Coverage Report

Created: 2025-07-23 08:13

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