Coverage Report

Created: 2025-06-13 06:55

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