Coverage Report

Created: 2025-07-01 07:09

/src/glib/gio/gsocks5proxy.c
Line
Count
Source (jump to first uncovered line)
1
 /* GIO - GLib Input, Output and Streaming Library
2
 *
3
 * Copyright (C) 2008, 2010 Collabora, Ltd.
4
 * Copyright (C) 2008 Nokia Corporation. All rights reserved.
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General
17
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18
 *
19
 * Author:  Youness Alaoui <youness.alaoui@collabora.co.uk
20
 *
21
 * Contributors:
22
 *      Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
23
 */
24
25
#include "config.h"
26
27
#include "gsocks5proxy.h"
28
29
#include <string.h>
30
31
#include "giomodule.h"
32
#include "giomodule-priv.h"
33
#include "giostream.h"
34
#include "ginetaddress.h"
35
#include "ginputstream.h"
36
#include "glibintl.h"
37
#include "goutputstream.h"
38
#include "gproxy.h"
39
#include "gproxyaddress.h"
40
#include "gtask.h"
41
42
0
#define SOCKS5_VERSION      0x05
43
44
0
#define SOCKS5_CMD_CONNECT    0x01
45
#define SOCKS5_CMD_BIND     0x02
46
#define SOCKS5_CMD_UDP_ASSOCIATE  0x03
47
48
0
#define SOCKS5_ATYP_IPV4    0x01
49
0
#define SOCKS5_ATYP_DOMAINNAME    0x03
50
0
#define SOCKS5_ATYP_IPV6    0x04
51
52
0
#define SOCKS5_AUTH_VERSION   0x01
53
54
0
#define SOCKS5_AUTH_NONE    0x00
55
0
#define SOCKS5_AUTH_GSSAPI    0x01
56
0
#define SOCKS5_AUTH_USR_PASS    0x02
57
0
#define SOCKS5_AUTH_NO_ACCEPT   0xff
58
59
0
#define SOCKS5_MAX_LEN      255
60
0
#define SOCKS5_RESERVED     0x00
61
62
0
#define SOCKS5_REP_SUCCEEDED    0x00
63
0
#define SOCKS5_REP_SRV_FAILURE    0x01
64
0
#define SOCKS5_REP_NOT_ALLOWED    0x02
65
0
#define SOCKS5_REP_NET_UNREACH    0x03
66
0
#define SOCKS5_REP_HOST_UNREACH   0x04
67
0
#define SOCKS5_REP_REFUSED        0x05
68
0
#define SOCKS5_REP_TTL_EXPIRED    0x06
69
0
#define SOCKS5_REP_CMD_NOT_SUP    0x07
70
0
#define SOCKS5_REP_ATYPE_NOT_SUP  0x08
71
72
73
struct _GSocks5Proxy
74
{
75
  GObject parent;
76
};
77
78
struct _GSocks5ProxyClass
79
{
80
  GObjectClass parent_class;
81
};
82
83
static void g_socks5_proxy_iface_init (GProxyInterface *proxy_iface);
84
85
#define g_socks5_proxy_get_type _g_socks5_proxy_get_type
86
G_DEFINE_TYPE_WITH_CODE (GSocks5Proxy, g_socks5_proxy, G_TYPE_OBJECT,
87
       G_IMPLEMENT_INTERFACE (G_TYPE_PROXY,
88
            g_socks5_proxy_iface_init)
89
       _g_io_modules_ensure_extension_points_registered ();
90
       g_io_extension_point_implement (G_PROXY_EXTENSION_POINT_NAME,
91
               g_define_type_id,
92
               "socks5",
93
               0))
94
95
static void
96
g_socks5_proxy_finalize (GObject *object)
97
0
{
98
  /* must chain up */
99
0
  G_OBJECT_CLASS (g_socks5_proxy_parent_class)->finalize (object);
100
0
}
101
102
static void
103
g_socks5_proxy_init (GSocks5Proxy *proxy)
104
0
{
105
0
}
106
107
/*
108
 * +----+----------+----------+
109
 * |VER | NMETHODS | METHODS  |
110
 * +----+----------+----------+
111
 * | 1  |    1     | 1 to 255 |
112
 * +----+----------+----------+
113
 */
114
0
#define SOCKS5_NEGO_MSG_LEN   4
115
static gint
116
set_nego_msg (guint8 *msg, gboolean has_auth)
117
0
{
118
0
  gint len = 3;
119
120
0
  msg[0] = SOCKS5_VERSION;
121
0
  msg[1] = 0x01; /* number of methods supported */
122
0
  msg[2] = SOCKS5_AUTH_NONE;
123
124
  /* add support for authentication method */
125
0
  if (has_auth)
126
0
    {
127
0
      msg[1] = 0x02; /* number of methods supported */
128
0
      msg[3] = SOCKS5_AUTH_USR_PASS;
129
0
      len++;
130
0
    }
131
132
0
  return len;
133
0
}
134
135
136
/*
137
 * +----+--------+
138
 * |VER | METHOD |
139
 * +----+--------+
140
 * | 1  |   1    |
141
 * +----+--------+
142
 */
143
0
#define SOCKS5_NEGO_REP_LEN   2
144
static gboolean
145
parse_nego_reply (const guint8 *data,
146
      gboolean     has_auth,
147
      gboolean    *must_auth,
148
      GError     **error)
149
0
{
150
0
  if (data[0] != SOCKS5_VERSION)
151
0
    {
152
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
153
0
         _("The server is not a SOCKSv5 proxy server."));
154
0
      return FALSE;
155
0
    }
156
157
0
  switch (data[1])
158
0
    {
159
0
      case SOCKS5_AUTH_NONE:
160
0
  *must_auth = FALSE;
161
0
  break;
162
163
0
      case SOCKS5_AUTH_USR_PASS:
164
0
  if (!has_auth)
165
0
    {
166
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_NEED_AUTH,
167
0
         _("The SOCKSv5 proxy requires authentication."));
168
0
      return FALSE;
169
0
    }
170
0
  *must_auth = TRUE;
171
0
  break;
172
173
0
      case SOCKS5_AUTH_NO_ACCEPT:
174
0
        if (!has_auth)
175
0
          {
176
            /* The server has said it accepts none of our authentication methods,
177
             * but given the slightly odd implementation of set_nego_msg(), we
178
             * actually only gave it the choice of %SOCKS5_AUTH_NONE, since the
179
             * caller specified no username or password.
180
             * Return %G_IO_ERROR_PROXY_NEED_AUTH so the caller knows that if
181
             * they specify a username and password and try again, authentication
182
             * might succeed (since we’ll send %SOCKS5_AUTH_USR_PASS next time). */
183
0
            g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_NEED_AUTH,
184
0
                                 _("The SOCKSv5 proxy requires authentication."));
185
0
            return FALSE;
186
0
          }
187
0
        G_GNUC_FALLTHROUGH;
188
0
      case SOCKS5_AUTH_GSSAPI:
189
0
      default:
190
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_AUTH_FAILED,
191
0
           _("The SOCKSv5 proxy requires an authentication "
192
0
             "method that is not supported by GLib."));
193
0
  return FALSE;
194
0
  break;
195
0
    }
196
197
0
  return TRUE;
198
0
}
199
200
0
#define SOCKS5_AUTH_MSG_LEN       515
201
static gint
202
set_auth_msg (guint8    *msg,
203
        const gchar *username,
204
        const gchar *password,
205
        GError **error)
206
0
{
207
0
  gint len = 0;
208
0
  gint ulen = 0; /* username length */
209
0
  gint plen = 0; /* Password length */
210
211
0
  if (username)
212
0
    ulen = strlen (username);
213
214
0
  if (password)
215
0
    plen = strlen (password);
216
217
0
  if (ulen > SOCKS5_MAX_LEN || plen > SOCKS5_MAX_LEN)
218
0
    {
219
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
220
0
         _("Username or password is too long for SOCKSv5 "
221
0
           "protocol."));
222
0
      return -1;
223
0
    }
224
225
0
  msg[len++] = SOCKS5_AUTH_VERSION;
226
0
  msg[len++] = ulen;
227
228
0
  if (ulen > 0)
229
0
    memcpy (msg + len, username, ulen);
230
231
0
  len += ulen;
232
0
  msg[len++] = plen;
233
234
0
  if (plen > 0)
235
0
    memcpy (msg + len, password, plen);
236
237
0
  len += plen;
238
239
0
  return len;
240
0
}
241
242
243
static gboolean
244
check_auth_status (const guint8 *data, GError **error)
245
0
{
246
0
  if (data[0] != SOCKS5_AUTH_VERSION
247
0
      || data[1] != SOCKS5_REP_SUCCEEDED)
248
0
    {
249
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_AUTH_FAILED,
250
0
         _("SOCKSv5 authentication failed due to wrong "
251
0
           "username or password."));
252
0
      return FALSE;
253
0
    }
254
0
  return TRUE;
255
0
}
256
257
/*
258
 * +----+-----+-------+------+----------+----------+
259
 * |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
260
 * +----+-----+-------+------+----------+----------+
261
 * | 1  |  1  | X'00' |  1   | Variable |    2     |
262
 * +----+-----+-------+------+----------+----------+
263
 * DST.ADDR is a string with first byte being the size. So DST.ADDR may not be
264
 * longer then 256 bytes.
265
 */
266
0
#define SOCKS5_CONN_MSG_LEN   262
267
static gint
268
set_connect_msg (guint8       *msg,
269
     const gchar *hostname,
270
     guint16      port,
271
     GError     **error)
272
0
{
273
0
  guint len = 0;
274
275
0
  msg[len++] = SOCKS5_VERSION;
276
0
  msg[len++] = SOCKS5_CMD_CONNECT;
277
0
  msg[len++] = SOCKS5_RESERVED;
278
279
0
  if (g_hostname_is_ip_address (hostname))
280
0
    {
281
0
      GInetAddress *addr = g_inet_address_new_from_string (hostname);
282
0
      gsize addr_len = g_inet_address_get_native_size (addr);
283
284
      /* We are cheating for simplicity, here's the logic:
285
       *   1 = IPV4 = 4 bytes / 4
286
       *   4 = IPV6 = 16 bytes / 4 */
287
0
      msg[len++] = addr_len / 4;
288
0
      memcpy (msg + len, g_inet_address_to_bytes (addr), addr_len);
289
0
      len += addr_len;
290
291
0
      g_object_unref (addr);
292
0
    }
293
0
  else
294
0
    {
295
0
      gsize host_len = strlen (hostname);
296
297
0
      if (host_len > SOCKS5_MAX_LEN)
298
0
  {
299
0
    g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
300
0
           _("Hostname “%s” is too long for SOCKSv5 protocol"),
301
0
           hostname);
302
0
    return -1;
303
0
  }
304
305
0
      msg[len++] = SOCKS5_ATYP_DOMAINNAME;
306
0
      msg[len++] = (guint8) host_len;
307
0
      memcpy (msg + len, hostname, host_len);
308
0
      len += host_len;
309
0
    }
310
311
0
    {
312
0
      guint16 hp = g_htons (port);
313
0
      memcpy (msg + len, &hp, 2);
314
0
      len += 2;
315
0
    }
316
317
0
  return len;
318
0
}
319
320
/*
321
 * +----+-----+-------+------+----------+----------+
322
 * |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
323
 * +----+-----+-------+------+----------+----------+
324
 * | 1  |  1  | X'00' |  1   | Variable |    2     |
325
 * +----+-----+-------+------+----------+----------+
326
 * This reply need to be read by small part to determine size. Buffer
327
 * size is determined in function of the biggest part to read.
328
 *
329
 * The parser only requires 4 bytes.
330
 */
331
0
#define SOCKS5_CONN_REP_LEN   255
332
static gboolean
333
parse_connect_reply (const guint8 *data, gint *atype, GError **error)
334
0
{
335
0
  if (data[0] != SOCKS5_VERSION)
336
0
    {
337
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
338
0
         _("The server is not a SOCKSv5 proxy server."));
339
0
      return FALSE;
340
0
    }
341
342
0
  switch (data[1])
343
0
    {
344
0
      case SOCKS5_REP_SUCCEEDED:
345
0
  if (data[2] != SOCKS5_RESERVED)
346
0
    {
347
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
348
0
         _("The server is not a SOCKSv5 proxy server."));
349
0
      return FALSE;
350
0
    }
351
352
0
  switch (data[3])
353
0
    {
354
0
    case SOCKS5_ATYP_IPV4:
355
0
    case SOCKS5_ATYP_IPV6:
356
0
    case SOCKS5_ATYP_DOMAINNAME:
357
0
      *atype = data[3];
358
0
      break;
359
360
0
    default:
361
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
362
0
         _("The SOCKSv5 proxy server uses unknown address type."));
363
0
      return FALSE;
364
0
    }
365
0
  break;
366
367
0
      case SOCKS5_REP_SRV_FAILURE:
368
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
369
0
           _("Internal SOCKSv5 proxy server error."));
370
0
  return FALSE;
371
0
  break;
372
373
0
      case SOCKS5_REP_NOT_ALLOWED:
374
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_NOT_ALLOWED,
375
0
           _("SOCKSv5 connection not allowed by ruleset."));
376
0
  return FALSE;
377
0
  break;
378
379
0
      case SOCKS5_REP_TTL_EXPIRED:
380
0
      case SOCKS5_REP_HOST_UNREACH:
381
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
382
0
           _("Host unreachable through SOCKSv5 server."));
383
0
  return FALSE;
384
0
  break;
385
386
0
      case SOCKS5_REP_NET_UNREACH:
387
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
388
0
           _("Network unreachable through SOCKSv5 proxy."));
389
0
  return FALSE;
390
0
  break;
391
392
0
      case SOCKS5_REP_REFUSED:
393
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED,
394
0
           _("Connection refused through SOCKSv5 proxy."));
395
0
  return FALSE;
396
0
  break;
397
398
0
      case SOCKS5_REP_CMD_NOT_SUP:
399
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
400
0
           _("SOCKSv5 proxy does not support “connect” command."));
401
0
  return FALSE;
402
0
  break;
403
404
0
      case SOCKS5_REP_ATYPE_NOT_SUP:
405
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
406
0
           _("SOCKSv5 proxy does not support provided address type."));
407
0
  return FALSE;
408
0
  break;
409
410
0
      default: /* Unknown error */
411
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
412
0
           _("Unknown SOCKSv5 proxy error."));
413
0
  return FALSE;
414
0
  break;
415
0
    }
416
417
0
  return TRUE;
418
0
}
419
420
static GIOStream *
421
g_socks5_proxy_connect (GProxy            *proxy,
422
      GIOStream         *io_stream,
423
      GProxyAddress     *proxy_address,
424
      GCancellable      *cancellable,
425
      GError          **error)
426
0
{
427
0
  gboolean has_auth;
428
0
  GInputStream *in;
429
0
  GOutputStream *out;
430
0
  const gchar *hostname;
431
0
  guint16 port;
432
0
  const gchar *username;
433
0
  const gchar *password;
434
435
0
  hostname = g_proxy_address_get_destination_hostname (proxy_address);
436
0
  port = g_proxy_address_get_destination_port (proxy_address);
437
0
  username = g_proxy_address_get_username (proxy_address);
438
0
  password = g_proxy_address_get_password (proxy_address);
439
440
0
  has_auth = username || password;
441
  
442
0
  in = g_io_stream_get_input_stream (io_stream);
443
0
  out = g_io_stream_get_output_stream (io_stream);
444
445
  /* Send SOCKS5 handshake */
446
0
    {
447
0
      guint8 msg[SOCKS5_NEGO_MSG_LEN];
448
0
      gint len;
449
450
0
      len = set_nego_msg (msg, has_auth);
451
452
0
      if (!g_output_stream_write_all (out, msg, len, NULL,
453
0
              cancellable, error))
454
0
  goto error;
455
0
    }
456
457
  /* Receive SOCKS5 response and reply with authentication if required */
458
0
    {
459
0
      guint8 data[SOCKS5_NEGO_REP_LEN];
460
0
      gboolean must_auth = FALSE;
461
462
0
      if (!g_input_stream_read_all (in, data, sizeof (data), NULL,
463
0
            cancellable, error))
464
0
  goto error;
465
466
0
      if (!parse_nego_reply (data, has_auth, &must_auth, error))
467
0
    goto error;
468
469
0
      if (must_auth)
470
0
  {
471
0
    guint8 msg[SOCKS5_AUTH_MSG_LEN];
472
0
    gint len;
473
474
0
    len = set_auth_msg (msg, username, password, error);
475
476
0
    if (len < 0)
477
0
      goto error;
478
    
479
0
    if (!g_output_stream_write_all (out, msg, len, NULL,
480
0
            cancellable, error))
481
0
      goto error;
482
483
0
    if (!g_input_stream_read_all (in, data, sizeof (data), NULL,
484
0
          cancellable, error))
485
0
      goto error;
486
487
0
    if (!check_auth_status (data, error))
488
0
      goto error;
489
0
  }
490
0
    }
491
492
  /* Send SOCKS5 connection request */
493
0
    {
494
0
      guint8 msg[SOCKS5_CONN_MSG_LEN];
495
0
      gint len;
496
      
497
0
      len = set_connect_msg (msg, hostname, port, error);
498
499
0
      if (len < 0)
500
0
  goto error;
501
502
0
      if (!g_output_stream_write_all (out, msg, len, NULL,
503
0
              cancellable, error))
504
0
  goto error;
505
0
    }
506
507
  /* Read SOCKS5 response */
508
0
    {
509
0
      guint8 data[SOCKS5_CONN_REP_LEN];
510
0
      gint atype;
511
512
0
      if (!g_input_stream_read_all (in, data, 4, NULL,
513
0
            cancellable, error))
514
0
  goto error;
515
516
0
      if (!parse_connect_reply (data, &atype, error))
517
0
  goto error;
518
519
0
      switch (atype)
520
0
  {
521
0
    case SOCKS5_ATYP_IPV4:
522
0
      if (!g_input_stream_read_all (in, data, 6, NULL,
523
0
            cancellable, error))
524
0
        goto error;
525
0
      break;
526
527
0
    case SOCKS5_ATYP_IPV6:
528
0
      if (!g_input_stream_read_all (in, data, 18, NULL,
529
0
            cancellable, error))
530
0
        goto error;
531
0
      break;
532
533
0
    case SOCKS5_ATYP_DOMAINNAME:
534
0
      if (!g_input_stream_read_all (in, data, 1, NULL,
535
0
            cancellable, error))
536
0
        goto error;
537
0
      if (!g_input_stream_read_all (in, data, data[0] + 2, NULL,
538
0
            cancellable, error))
539
0
        goto error;
540
0
      break;
541
0
  }
542
0
    }
543
544
0
  return g_object_ref (io_stream);
545
546
0
error:
547
0
  return NULL;
548
0
}
549
550
551
typedef struct
552
{
553
  GIOStream *io_stream;
554
  gchar *hostname;
555
  guint16 port;
556
  gchar *username;
557
  gchar *password;
558
  guint8 *buffer;
559
  gssize length;
560
  gssize offset;
561
} ConnectAsyncData;
562
563
static void nego_msg_write_cb       (GObject          *source,
564
               GAsyncResult     *res,
565
               gpointer          user_data);
566
static void nego_reply_read_cb        (GObject          *source,
567
               GAsyncResult     *res,
568
               gpointer          user_data);
569
static void auth_msg_write_cb       (GObject          *source,
570
               GAsyncResult     *res,
571
               gpointer          user_data);
572
static void auth_reply_read_cb        (GObject          *source,
573
               GAsyncResult     *result,
574
               gpointer          user_data);
575
static void send_connect_msg        (GTask            *task);
576
static void connect_msg_write_cb      (GObject          *source,
577
               GAsyncResult     *result,
578
               gpointer          user_data);
579
static void connect_reply_read_cb     (GObject          *source,
580
               GAsyncResult     *result,
581
               gpointer          user_data);
582
static void connect_addr_len_read_cb  (GObject          *source,
583
               GAsyncResult     *result,
584
               gpointer          user_data);
585
static void connect_addr_read_cb      (GObject          *source,
586
               GAsyncResult     *result,
587
               gpointer          user_data);
588
589
static void
590
free_connect_data (ConnectAsyncData *data)
591
0
{
592
0
  g_object_unref (data->io_stream);
593
594
0
  g_free (data->hostname);
595
0
  g_free (data->username);
596
0
  g_free (data->password);
597
0
  g_free (data->buffer);
598
599
0
  g_slice_free (ConnectAsyncData, data);
600
0
}
601
602
static void
603
do_read (GAsyncReadyCallback callback, GTask *task, ConnectAsyncData *data)
604
0
{
605
0
   GInputStream *in;
606
0
   in = g_io_stream_get_input_stream (data->io_stream);
607
0
   g_input_stream_read_async (in,
608
0
            data->buffer + data->offset,
609
0
            data->length - data->offset,
610
0
            g_task_get_priority (task),
611
0
            g_task_get_cancellable (task),
612
0
            callback, task);
613
0
}
614
615
static void
616
do_write (GAsyncReadyCallback callback, GTask *task, ConnectAsyncData *data)
617
0
{
618
0
  GOutputStream *out;
619
0
  out = g_io_stream_get_output_stream (data->io_stream);
620
0
  g_output_stream_write_async (out,
621
0
             data->buffer + data->offset,
622
0
             data->length - data->offset,
623
0
             g_task_get_priority (task),
624
0
             g_task_get_cancellable (task),
625
0
             callback, task);
626
0
}
627
628
static void
629
g_socks5_proxy_connect_async (GProxy               *proxy,
630
            GIOStream            *io_stream,
631
            GProxyAddress        *proxy_address,
632
            GCancellable         *cancellable,
633
            GAsyncReadyCallback   callback,
634
            gpointer              user_data)
635
0
{
636
0
  GTask *task;
637
0
  ConnectAsyncData *data;
638
639
0
  data = g_slice_new0 (ConnectAsyncData);
640
0
  data->io_stream = g_object_ref (io_stream);
641
642
0
  task = g_task_new (proxy, cancellable, callback, user_data);
643
0
  g_task_set_source_tag (task, g_socks5_proxy_connect_async);
644
0
  g_task_set_task_data (task, data, (GDestroyNotify) free_connect_data);
645
646
0
  g_object_get (G_OBJECT (proxy_address),
647
0
    "destination-hostname", &data->hostname,
648
0
    "destination-port", &data->port,
649
0
    "username", &data->username,
650
0
    "password", &data->password,
651
0
    NULL);
652
653
0
  data->buffer = g_malloc0 (SOCKS5_NEGO_MSG_LEN);
654
0
  data->length = set_nego_msg (data->buffer,
655
0
             data->username || data->password);
656
0
  data->offset = 0;
657
658
0
  do_write (nego_msg_write_cb, task, data);
659
0
}
660
661
662
static void
663
nego_msg_write_cb (GObject      *source,
664
       GAsyncResult *res,
665
       gpointer      user_data)
666
0
{
667
0
  GTask *task = user_data;
668
0
  ConnectAsyncData *data = g_task_get_task_data (task);
669
0
  GError *error = NULL;
670
0
  gssize written;
671
672
0
  written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
673
0
            res, &error);
674
675
0
  if (written < 0)
676
0
    {
677
0
      g_task_return_error (task, error);
678
0
      g_object_unref (task);
679
0
      return;
680
0
    }
681
682
0
  data->offset += written;
683
684
0
  if (data->offset == data->length)
685
0
    {
686
0
      g_free (data->buffer);
687
688
0
      data->buffer = g_malloc0 (SOCKS5_NEGO_REP_LEN);
689
0
      data->length = SOCKS5_NEGO_REP_LEN;
690
0
      data->offset = 0;
691
692
0
      do_read (nego_reply_read_cb, task, data);
693
0
    }
694
0
  else
695
0
    {
696
0
      do_write (nego_msg_write_cb, task, data);
697
0
    }
698
0
}
699
700
static void
701
nego_reply_read_cb (GObject      *source,
702
        GAsyncResult *res,
703
        gpointer      user_data)
704
0
{
705
0
  GTask *task = user_data;
706
0
  ConnectAsyncData *data = g_task_get_task_data (task);
707
0
  GError *error = NULL;
708
0
  gssize read;
709
710
0
  read = g_input_stream_read_finish (G_INPUT_STREAM (source),
711
0
             res, &error);
712
713
0
  if (read < 0)
714
0
    {
715
0
      g_task_return_error (task, error);
716
0
      g_object_unref (task);
717
0
      return;
718
0
    }
719
720
0
  if (read == 0)
721
0
    {
722
0
      g_task_return_new_error (task,
723
0
                               G_IO_ERROR,
724
0
                               G_IO_ERROR_CONNECTION_CLOSED,
725
0
                               "Connection to SOCKSv5 proxy server lost");
726
0
      g_object_unref (task);
727
0
      return;
728
0
    }
729
730
0
  data->offset += read;
731
  
732
0
  if (data->offset == data->length)
733
0
    {
734
0
      GError *error = NULL;
735
0
      gboolean must_auth = FALSE;
736
0
      gboolean has_auth = data->username || data->password;
737
738
0
      if (!parse_nego_reply (data->buffer, has_auth, &must_auth, &error))
739
0
  {
740
0
    g_task_return_error (task, error);
741
0
    g_object_unref (task);
742
0
    return;
743
0
  }
744
745
0
      if (must_auth)
746
0
  {
747
0
    g_free (data->buffer);
748
749
0
    data->buffer = g_malloc0 (SOCKS5_AUTH_MSG_LEN);
750
0
    data->length = set_auth_msg (data->buffer,
751
0
               data->username,
752
0
               data->password,
753
0
               &error);
754
0
    data->offset = 0;
755
756
0
    if (data->length < 0)
757
0
      {
758
0
        g_task_return_error (task, error);
759
0
        g_object_unref (task);
760
0
        return;
761
0
      }
762
    
763
0
    do_write (auth_msg_write_cb, task, data);
764
0
  }
765
0
      else
766
0
  {
767
0
    send_connect_msg (task);
768
0
  }
769
0
    }
770
0
  else
771
0
    {
772
0
      do_read (nego_reply_read_cb, task, data);
773
0
    }
774
0
}
775
776
static void
777
auth_msg_write_cb (GObject      *source,
778
       GAsyncResult *result,
779
       gpointer      user_data)
780
0
{
781
0
  GTask *task = user_data;
782
0
  ConnectAsyncData *data = g_task_get_task_data (task);
783
0
  GError *error = NULL;
784
0
  gssize written;
785
786
0
  written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
787
0
            result, &error);
788
  
789
0
  if (written < 0)
790
0
    {
791
0
      g_task_return_error (task, error);
792
0
      g_object_unref (task);
793
0
      return;
794
0
    }
795
796
0
  data->offset += written;
797
798
0
  if (data->offset == data->length)
799
0
    {
800
0
      g_free (data->buffer);
801
802
0
      data->buffer = g_malloc0 (SOCKS5_NEGO_REP_LEN);
803
0
      data->length = SOCKS5_NEGO_REP_LEN;
804
0
      data->offset = 0;
805
806
0
      do_read (auth_reply_read_cb, task, data);
807
0
    }
808
0
  else
809
0
    {
810
0
      do_write (auth_msg_write_cb, task, data);
811
0
    }
812
0
}
813
814
static void
815
auth_reply_read_cb (GObject      *source,
816
        GAsyncResult *result,
817
        gpointer      user_data)
818
0
{
819
0
  GTask *task = user_data;
820
0
  ConnectAsyncData *data = g_task_get_task_data (task);
821
0
  GError *error = NULL;
822
0
  gssize read;
823
824
0
  read = g_input_stream_read_finish (G_INPUT_STREAM (source),
825
0
             result, &error);
826
827
0
  if (read < 0)
828
0
    {
829
0
      g_task_return_error (task, error);
830
0
      g_object_unref (task);
831
0
      return;
832
0
    }
833
834
0
  if (read == 0)
835
0
    {
836
0
      g_task_return_new_error (task,
837
0
                               G_IO_ERROR,
838
0
                               G_IO_ERROR_CONNECTION_CLOSED,
839
0
                               "Connection to SOCKSv5 proxy server lost");
840
0
      g_object_unref (task);
841
0
      return;
842
0
    }
843
844
0
  data->offset += read;
845
846
0
  if (data->offset == data->length)
847
0
    {
848
0
      if (!check_auth_status (data->buffer, &error))
849
0
  {
850
0
    g_task_return_error (task, error);
851
0
    g_object_unref (task);
852
0
    return;
853
0
  }
854
  
855
0
      send_connect_msg (task);
856
0
    }
857
0
  else
858
0
    {
859
0
      do_read (auth_reply_read_cb, task, data);
860
0
    }
861
0
}
862
863
static void
864
send_connect_msg (GTask *task)
865
0
{
866
0
  ConnectAsyncData *data = g_task_get_task_data (task);
867
0
  GError *error = NULL;
868
869
0
  g_free (data->buffer);
870
871
0
  data->buffer = g_malloc0 (SOCKS5_CONN_MSG_LEN);
872
0
  data->length = set_connect_msg (data->buffer,
873
0
          data->hostname,
874
0
          data->port,
875
0
          &error);
876
0
  data->offset = 0;
877
  
878
0
  if (data->length < 0)
879
0
    {
880
0
      g_task_return_error (task, error);
881
0
      g_object_unref (task);
882
0
      return;
883
0
    }
884
885
0
  do_write (connect_msg_write_cb, task, data);
886
0
}
887
888
static void
889
connect_msg_write_cb (GObject      *source,
890
          GAsyncResult *result,
891
          gpointer      user_data)
892
0
{
893
0
  GTask *task = user_data;
894
0
  ConnectAsyncData *data = g_task_get_task_data (task);
895
0
  GError *error = NULL;
896
0
  gssize written;
897
898
0
  written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
899
0
            result, &error);
900
  
901
0
  if (written < 0)
902
0
    {
903
0
      g_task_return_error (task, error);
904
0
      g_object_unref (task);
905
0
      return;
906
0
    }
907
908
0
  data->offset += written;
909
910
0
  if (data->offset == data->length)
911
0
    {
912
0
      g_free (data->buffer);
913
914
0
      data->buffer = g_malloc0 (SOCKS5_CONN_REP_LEN);
915
0
      data->length = 4;
916
0
      data->offset = 0;
917
918
0
      do_read (connect_reply_read_cb, task, data);
919
0
    }
920
0
  else
921
0
    {
922
0
      do_write (connect_msg_write_cb, task, data);
923
0
    }
924
0
}
925
926
static void
927
connect_reply_read_cb (GObject       *source,
928
           GAsyncResult  *result,
929
           gpointer       user_data)
930
0
{
931
0
  GTask *task = user_data;
932
0
  ConnectAsyncData *data = g_task_get_task_data (task);
933
0
  GError *error = NULL;
934
0
  gssize read;
935
936
0
  read = g_input_stream_read_finish (G_INPUT_STREAM (source),
937
0
             result, &error);
938
939
0
  if (read < 0)
940
0
    {
941
0
      g_task_return_error (task, error);
942
0
      g_object_unref (task);
943
0
      return;
944
0
    }
945
946
0
  if (read == 0)
947
0
    {
948
0
      g_task_return_new_error (task,
949
0
                               G_IO_ERROR,
950
0
                               G_IO_ERROR_CONNECTION_CLOSED,
951
0
                               "Connection to SOCKSv5 proxy server lost");
952
0
      g_object_unref (task);
953
0
      return;
954
0
    }
955
956
0
  data->offset += read;
957
958
0
  if (data->offset == data->length)
959
0
    {
960
0
      gint atype;
961
962
0
      if (!parse_connect_reply (data->buffer, &atype, &error))
963
0
  {
964
0
    g_task_return_error (task, error);
965
0
    g_object_unref (task);
966
0
    return;
967
0
  }
968
969
0
      switch (atype)
970
0
  {
971
0
    case SOCKS5_ATYP_IPV4:
972
0
      data->length = 6;
973
0
      data->offset = 0;
974
0
      do_read (connect_addr_read_cb, task, data);
975
0
      break;
976
977
0
    case SOCKS5_ATYP_IPV6:
978
0
      data->length = 18;
979
0
      data->offset = 0;
980
0
      do_read (connect_addr_read_cb, task, data);
981
0
      break;
982
983
0
    case SOCKS5_ATYP_DOMAINNAME:
984
0
      data->length = 1;
985
0
      data->offset = 0;
986
0
      do_read (connect_addr_len_read_cb, task, data);
987
0
      break;
988
0
  }
989
0
    }
990
0
  else
991
0
    {
992
0
      do_read (connect_reply_read_cb, task, data);
993
0
    }
994
0
}
995
996
static void
997
connect_addr_len_read_cb (GObject      *source,
998
        GAsyncResult *result,
999
        gpointer      user_data)
1000
0
{
1001
0
  GTask *task = user_data;
1002
0
  ConnectAsyncData *data = g_task_get_task_data (task);
1003
0
  GError *error = NULL;
1004
0
  gssize read;
1005
1006
0
  read = g_input_stream_read_finish (G_INPUT_STREAM (source),
1007
0
             result, &error);
1008
1009
0
  if (read < 0)
1010
0
    {
1011
0
      g_task_return_error (task, error);
1012
0
      g_object_unref (task);
1013
0
      return;
1014
0
    }
1015
1016
0
  if (read == 0)
1017
0
    {
1018
0
      g_task_return_new_error (task,
1019
0
                               G_IO_ERROR,
1020
0
                               G_IO_ERROR_CONNECTION_CLOSED,
1021
0
                               "Connection to SOCKSv5 proxy server lost");
1022
0
      g_object_unref (task);
1023
0
      return;
1024
0
    }
1025
1026
0
  data->length = data->buffer[0] + 2;
1027
0
  data->offset = 0;
1028
1029
0
  do_read (connect_addr_read_cb, task, data);
1030
0
}
1031
1032
static void
1033
connect_addr_read_cb (GObject      *source,
1034
          GAsyncResult *result,
1035
          gpointer      user_data)
1036
0
{
1037
0
  GTask *task = user_data;
1038
0
  ConnectAsyncData *data = g_task_get_task_data (task);
1039
0
  GError *error = NULL;
1040
0
  gssize read;
1041
1042
0
  read = g_input_stream_read_finish (G_INPUT_STREAM (source),
1043
0
             result, &error);
1044
1045
0
  if (read < 0)
1046
0
    {
1047
0
      g_task_return_error (task, error);
1048
0
      g_object_unref (task);
1049
0
      return;
1050
0
    }
1051
1052
0
  if (read == 0)
1053
0
    {
1054
0
      g_task_return_new_error (task,
1055
0
                               G_IO_ERROR,
1056
0
                               G_IO_ERROR_CONNECTION_CLOSED,
1057
0
                               "Connection to SOCKSv5 proxy server lost");
1058
0
      g_object_unref (task);
1059
0
      return;
1060
0
    }
1061
1062
0
  data->offset += read;
1063
1064
0
  if (data->offset == data->length)
1065
0
    {
1066
0
      g_task_return_pointer (task, g_object_ref (data->io_stream), g_object_unref);
1067
0
      g_object_unref (task);
1068
0
      return;
1069
0
    }
1070
0
  else
1071
0
    {
1072
0
      do_read (connect_reply_read_cb, task, data);
1073
0
    }
1074
0
}
1075
1076
static GIOStream *
1077
g_socks5_proxy_connect_finish (GProxy       *proxy,
1078
             GAsyncResult *result,
1079
             GError      **error)
1080
0
{
1081
0
  return g_task_propagate_pointer (G_TASK (result), error);
1082
0
}
1083
1084
static gboolean
1085
g_socks5_proxy_supports_hostname (GProxy *proxy)
1086
0
{
1087
0
  return TRUE;
1088
0
}
1089
1090
static void
1091
g_socks5_proxy_class_init (GSocks5ProxyClass *class)
1092
0
{
1093
0
  GObjectClass *object_class;
1094
1095
0
  object_class = (GObjectClass *) class;
1096
0
  object_class->finalize = g_socks5_proxy_finalize;
1097
0
}
1098
1099
static void
1100
g_socks5_proxy_iface_init (GProxyInterface *proxy_iface)
1101
0
{
1102
0
  proxy_iface->connect  = g_socks5_proxy_connect;
1103
0
  proxy_iface->connect_async = g_socks5_proxy_connect_async;
1104
0
  proxy_iface->connect_finish = g_socks5_proxy_connect_finish;
1105
0
  proxy_iface->supports_hostname = g_socks5_proxy_supports_hostname;
1106
0
}