Coverage Report

Created: 2025-07-01 07:09

/src/glib/gio/gsocks4aproxy.c
Line
Count
Source (jump to first uncovered line)
1
 /* GIO - GLib Input, Output and Streaming Library
2
 *
3
 * Copyright (C) 2010 Collabora, Ltd.
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General
16
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17
 *
18
 * Author: Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
19
 */
20
21
#include "config.h"
22
23
#include "gsocks4aproxy.h"
24
25
#include <string.h>
26
27
#include "giomodule.h"
28
#include "giomodule-priv.h"
29
#include "giostream.h"
30
#include "ginetaddress.h"
31
#include "ginputstream.h"
32
#include "glibintl.h"
33
#include "goutputstream.h"
34
#include "gproxy.h"
35
#include "gproxyaddress.h"
36
#include "gtask.h"
37
38
0
#define SOCKS4_VERSION      4
39
40
0
#define SOCKS4_CMD_CONNECT    1
41
#define SOCKS4_CMD_BIND     2
42
43
0
#define SOCKS4_MAX_LEN      255
44
45
0
#define SOCKS4_REP_VERSION    0
46
0
#define SOCKS4_REP_GRANTED    90
47
#define SOCKS4_REP_REJECTED       91
48
#define SOCKS4_REP_NO_IDENT       92
49
#define SOCKS4_REP_BAD_IDENT      93
50
51
static void g_socks4a_proxy_iface_init (GProxyInterface *proxy_iface);
52
53
#define g_socks4a_proxy_get_type _g_socks4a_proxy_get_type
54
G_DEFINE_TYPE_WITH_CODE (GSocks4aProxy, g_socks4a_proxy, G_TYPE_OBJECT,
55
       G_IMPLEMENT_INTERFACE (G_TYPE_PROXY,
56
            g_socks4a_proxy_iface_init)
57
       _g_io_modules_ensure_extension_points_registered ();
58
       g_io_extension_point_implement (G_PROXY_EXTENSION_POINT_NAME,
59
               g_define_type_id,
60
               "socks4a",
61
               0))
62
63
static void
64
g_socks4a_proxy_finalize (GObject *object)
65
0
{
66
  /* must chain up */
67
0
  G_OBJECT_CLASS (g_socks4a_proxy_parent_class)->finalize (object);
68
0
}
69
70
static void
71
g_socks4a_proxy_init (GSocks4aProxy *proxy)
72
0
{
73
0
  proxy->supports_hostname = TRUE;
74
0
}
75
76
/*                                                             |-> SOCKSv4a only
77
 * +----+----+----+----+----+----+----+----+----+----+....+----+------+....+------+
78
 * | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL| HOST |    | NULL |
79
 * +----+----+----+----+----+----+----+----+----+----+....+----+------+....+------+
80
 *    1    1      2              4           variable       1    variable
81
 */
82
0
#define SOCKS4_CONN_MSG_LEN     (9 + SOCKS4_MAX_LEN * 2)
83
static gint
84
set_connect_msg (guint8      *msg,
85
     const gchar *hostname,
86
     guint16      port,
87
     const char  *username,
88
     GError     **error)
89
0
{
90
0
  GInetAddress *addr;
91
0
  guint len = 0;
92
0
  gsize addr_len;
93
0
  gboolean is_ip;
94
0
  const gchar *ip;
95
96
0
  msg[len++] = SOCKS4_VERSION;
97
0
  msg[len++] = SOCKS4_CMD_CONNECT;
98
99
0
    {
100
0
      guint16 hp = g_htons (port);
101
0
      memcpy (msg + len, &hp, 2);
102
0
      len += 2;
103
0
    }
104
105
0
  is_ip = g_hostname_is_ip_address (hostname);
106
107
0
  if (is_ip)
108
0
    ip = hostname;
109
0
  else
110
0
    ip = "0.0.0.1";
111
    
112
0
  addr = g_inet_address_new_from_string (ip);
113
0
  addr_len = g_inet_address_get_native_size (addr);
114
115
0
  if (addr_len != 4)
116
0
    {
117
0
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
118
0
      _("SOCKSv4 does not support IPv6 address ā€œ%sā€"),
119
0
      ip);
120
0
      g_object_unref (addr);
121
0
      return -1;
122
0
    }
123
124
0
  memcpy (msg + len, g_inet_address_to_bytes (addr), addr_len);
125
0
  len += addr_len;
126
127
0
  g_object_unref (addr);
128
129
0
  if (username)
130
0
    {
131
0
      gsize user_len = strlen (username);
132
133
0
      if (user_len > SOCKS4_MAX_LEN)
134
0
  {
135
0
    g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
136
0
             _("Username is too long for SOCKSv4 protocol"));
137
0
    return -1;
138
0
  }
139
140
0
      memcpy (msg + len, username, user_len);
141
0
      len += user_len;
142
0
    }
143
144
0
  msg[len++] = '\0';
145
146
0
  if (!is_ip)
147
0
    {
148
0
      gsize host_len = strlen (hostname);
149
150
0
      if (host_len > SOCKS4_MAX_LEN)
151
0
  {
152
0
    g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
153
0
           _("Hostname ā€œ%sā€ is too long for SOCKSv4 protocol"),
154
0
           hostname);
155
0
    return -1;
156
0
  }
157
158
0
      memcpy (msg + len, hostname, host_len);
159
0
      len += host_len;
160
0
      msg[len++] = '\0';
161
0
    }
162
163
0
  return len;
164
0
}
165
166
/*
167
 * +----+----+----+----+----+----+----+----+
168
 * | VN | CD | DSTPORT |      DSTIP        |
169
 * +----+----+----+----+----+----+----+----+
170
 *    1    1      2              4
171
 */
172
0
#define SOCKS4_CONN_REP_LEN   8
173
static gboolean
174
parse_connect_reply (const guint8 *data, GError **error)
175
0
{
176
0
  if (data[0] != SOCKS4_REP_VERSION)
177
0
    {
178
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
179
0
         _("The server is not a SOCKSv4 proxy server."));
180
0
      return FALSE;
181
0
    }
182
183
0
  if (data[1] != SOCKS4_REP_GRANTED)
184
0
    {
185
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
186
0
         _("Connection through SOCKSv4 server was rejected"));
187
0
      return FALSE;
188
0
    }
189
190
0
  return TRUE;
191
0
}
192
193
static GIOStream *
194
g_socks4a_proxy_connect (GProxy            *proxy,
195
       GIOStream         *io_stream,
196
       GProxyAddress     *proxy_address,
197
       GCancellable      *cancellable,
198
       GError           **error)
199
0
{
200
0
  GInputStream *in;
201
0
  GOutputStream *out;
202
0
  const gchar *hostname;
203
0
  guint16 port;
204
0
  const gchar *username;
205
206
0
  hostname = g_proxy_address_get_destination_hostname (proxy_address);
207
0
  port = g_proxy_address_get_destination_port (proxy_address);
208
0
  username = g_proxy_address_get_username (proxy_address);
209
210
0
  in = g_io_stream_get_input_stream (io_stream);
211
0
  out = g_io_stream_get_output_stream (io_stream);
212
213
  /* Send SOCKS4 connection request */
214
0
    {
215
0
      guint8 msg[SOCKS4_CONN_MSG_LEN];
216
0
      gint len;
217
      
218
0
      len = set_connect_msg (msg, hostname, port, username, error);
219
220
0
      if (len < 0)
221
0
  goto error;
222
223
0
      if (!g_output_stream_write_all (out, msg, len, NULL,
224
0
              cancellable, error))
225
0
  goto error;
226
0
    }
227
228
  /* Read SOCKS4 response */
229
0
    {
230
0
      guint8 data[SOCKS4_CONN_REP_LEN];
231
232
0
      if (!g_input_stream_read_all (in, data, SOCKS4_CONN_REP_LEN, NULL,
233
0
            cancellable, error))
234
0
  goto error;
235
236
0
      if (!parse_connect_reply (data, error))
237
0
  goto error;
238
0
    }
239
240
0
  return g_object_ref (io_stream);
241
242
0
error:
243
0
  return NULL;
244
0
}
245
246
247
typedef struct
248
{
249
  GIOStream *io_stream;
250
251
  /* For connecting */
252
  guint8 *buffer;
253
  gssize length;
254
  gssize offset;
255
256
} ConnectAsyncData;
257
258
static void connect_msg_write_cb      (GObject          *source,
259
               GAsyncResult     *result,
260
               gpointer          user_data);
261
static void connect_reply_read_cb     (GObject          *source,
262
               GAsyncResult     *result,
263
               gpointer          user_data);
264
265
static void
266
free_connect_data (ConnectAsyncData *data)
267
0
{
268
0
  g_object_unref (data->io_stream);
269
0
  g_slice_free (ConnectAsyncData, data);
270
0
}
271
272
static void
273
do_read (GAsyncReadyCallback callback, GTask *task, ConnectAsyncData *data)
274
0
{
275
0
   GInputStream *in;
276
0
   in = g_io_stream_get_input_stream (data->io_stream);
277
0
   g_input_stream_read_async (in,
278
0
            data->buffer + data->offset,
279
0
            data->length - data->offset,
280
0
            g_task_get_priority (task),
281
0
            g_task_get_cancellable (task),
282
0
            callback, task);
283
0
}
284
285
static void
286
do_write (GAsyncReadyCallback callback, GTask *task, ConnectAsyncData *data)
287
0
{
288
0
  GOutputStream *out;
289
0
  out = g_io_stream_get_output_stream (data->io_stream);
290
0
  g_output_stream_write_async (out,
291
0
             data->buffer + data->offset,
292
0
             data->length - data->offset,
293
0
             g_task_get_priority (task),
294
0
             g_task_get_cancellable (task),
295
0
             callback, task);
296
0
}
297
298
299
300
static void
301
g_socks4a_proxy_connect_async (GProxy               *proxy,
302
             GIOStream            *io_stream,
303
             GProxyAddress        *proxy_address,
304
             GCancellable         *cancellable,
305
             GAsyncReadyCallback   callback,
306
             gpointer              user_data)
307
0
{
308
0
  GError *error = NULL;
309
0
  GTask *task;
310
0
  ConnectAsyncData *data;
311
0
  const gchar *hostname;
312
0
  guint16 port;
313
0
  const gchar *username;
314
315
0
  data = g_slice_new0 (ConnectAsyncData);
316
0
  data->io_stream = g_object_ref (io_stream);
317
318
0
  task = g_task_new (proxy, cancellable, callback, user_data);
319
0
  g_task_set_source_tag (task, g_socks4a_proxy_connect_async);
320
0
  g_task_set_task_data (task, data, (GDestroyNotify) free_connect_data);
321
322
0
  hostname = g_proxy_address_get_destination_hostname (proxy_address);
323
0
  port = g_proxy_address_get_destination_port (proxy_address);
324
0
  username = g_proxy_address_get_username (proxy_address); 
325
326
0
  data->buffer = g_malloc0 (SOCKS4_CONN_MSG_LEN);
327
0
  data->length = set_connect_msg (data->buffer,
328
0
          hostname, port, username,
329
0
          &error);
330
0
  data->offset = 0;
331
332
0
  if (data->length < 0)
333
0
    {
334
0
      g_task_return_error (task, error);
335
0
      g_object_unref (task);
336
0
    }
337
0
  else
338
0
    {
339
0
      do_write (connect_msg_write_cb, task, data);
340
0
    }
341
0
}
342
343
static void
344
connect_msg_write_cb (GObject      *source,
345
          GAsyncResult *result,
346
          gpointer      user_data)
347
0
{
348
0
  GTask *task = user_data;
349
0
  ConnectAsyncData *data = g_task_get_task_data (task);
350
0
  GError *error = NULL;
351
0
  gssize written;
352
353
0
  written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
354
0
            result, &error);
355
  
356
0
  if (written < 0)
357
0
    {
358
0
      g_task_return_error (task, error);
359
0
      g_object_unref (task);
360
0
      return;
361
0
    }
362
363
0
  data->offset += written;
364
365
0
  if (data->offset == data->length)
366
0
    {
367
0
      g_free (data->buffer);
368
369
0
      data->buffer = g_malloc0 (SOCKS4_CONN_REP_LEN);
370
0
      data->length = SOCKS4_CONN_REP_LEN;
371
0
      data->offset = 0;
372
373
0
      do_read (connect_reply_read_cb, task, data);
374
0
    }
375
0
  else
376
0
    {
377
0
      do_write (connect_msg_write_cb, task, data);
378
0
    }
379
0
}
380
381
static void
382
connect_reply_read_cb (GObject       *source,
383
           GAsyncResult  *result,
384
           gpointer       user_data)
385
0
{
386
0
  GTask *task = user_data;
387
0
  ConnectAsyncData *data = g_task_get_task_data (task);
388
0
  GError *error = NULL;
389
0
  gssize read;
390
391
0
  read = g_input_stream_read_finish (G_INPUT_STREAM (source),
392
0
             result, &error);
393
394
0
  if (read < 0)
395
0
    {
396
0
      g_task_return_error (task, error);
397
0
      g_object_unref (task);
398
0
      return;
399
0
    }
400
401
0
  data->offset += read;
402
403
0
  if (data->offset == data->length)
404
0
    {
405
0
      if (!parse_connect_reply (data->buffer, &error))
406
0
  {
407
0
    g_task_return_error (task, error);
408
0
    g_object_unref (task);
409
0
    return;
410
0
  }
411
0
      else
412
0
  {
413
0
    g_task_return_pointer (task, g_object_ref (data->io_stream), g_object_unref);
414
0
    g_object_unref (task);
415
0
    return;
416
0
  }
417
0
    }
418
0
  else
419
0
    {
420
0
      do_read (connect_reply_read_cb, task, data);
421
0
    }
422
0
}
423
424
static GIOStream *g_socks4a_proxy_connect_finish (GProxy       *proxy,
425
              GAsyncResult *result,
426
              GError      **error);
427
428
static GIOStream *
429
g_socks4a_proxy_connect_finish (GProxy       *proxy,
430
              GAsyncResult *result,
431
              GError      **error)
432
0
{
433
0
  return g_task_propagate_pointer (G_TASK (result), error);
434
0
}
435
436
static gboolean
437
g_socks4a_proxy_supports_hostname (GProxy *proxy)
438
0
{
439
0
  return G_SOCKS4A_PROXY (proxy)->supports_hostname;
440
0
}
441
442
static void
443
g_socks4a_proxy_class_init (GSocks4aProxyClass *class)
444
0
{
445
0
  GObjectClass *object_class;
446
447
0
  object_class = (GObjectClass *) class;
448
0
  object_class->finalize = g_socks4a_proxy_finalize;
449
0
}
450
451
static void
452
g_socks4a_proxy_iface_init (GProxyInterface *proxy_iface)
453
0
{
454
0
  proxy_iface->connect  = g_socks4a_proxy_connect;
455
0
  proxy_iface->connect_async = g_socks4a_proxy_connect_async;
456
0
  proxy_iface->connect_finish = g_socks4a_proxy_connect_finish;
457
0
  proxy_iface->supports_hostname = g_socks4a_proxy_supports_hostname;
458
0
}