Coverage Report

Created: 2025-06-13 06:55

/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
 * 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 gint
118
set_nego_msg (guint8 *msg, gboolean has_auth)
119
0
{
120
0
  gint 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 gint
204
set_auth_msg (guint8    *msg,
205
        const gchar *username,
206
        const gchar *password,
207
        GError **error)
208
0
{
209
0
  gint len = 0;
210
0
  gint ulen = 0; /* username length */
211
0
  gint plen = 0; /* Password length */
212
213
0
  if (username)
214
0
    ulen = strlen (username);
215
216
0
  if (password)
217
0
    plen = strlen (password);
218
219
0
  if (ulen > SOCKS5_MAX_LEN || plen > SOCKS5_MAX_LEN)
220
0
    {
221
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
222
0
         _("Username or password is too long for SOCKSv5 "
223
0
           "protocol."));
224
0
      return -1;
225
0
    }
226
227
0
  msg[len++] = SOCKS5_AUTH_VERSION;
228
0
  msg[len++] = ulen;
229
230
0
  if (ulen > 0)
231
0
    memcpy (msg + len, username, ulen);
232
233
0
  len += ulen;
234
0
  msg[len++] = plen;
235
236
0
  if (plen > 0)
237
0
    memcpy (msg + len, password, plen);
238
239
0
  len += plen;
240
241
0
  return len;
242
0
}
243
244
245
static gboolean
246
check_auth_status (const guint8 *data, GError **error)
247
0
{
248
0
  if (data[0] != SOCKS5_AUTH_VERSION
249
0
      || data[1] != SOCKS5_REP_SUCCEEDED)
250
0
    {
251
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_AUTH_FAILED,
252
0
         _("SOCKSv5 authentication failed due to wrong "
253
0
           "username or password."));
254
0
      return FALSE;
255
0
    }
256
0
  return TRUE;
257
0
}
258
259
/*
260
 * +----+-----+-------+------+----------+----------+
261
 * |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
262
 * +----+-----+-------+------+----------+----------+
263
 * | 1  |  1  | X'00' |  1   | Variable |    2     |
264
 * +----+-----+-------+------+----------+----------+
265
 * DST.ADDR is a string with first byte being the size. So DST.ADDR may not be
266
 * longer then 256 bytes.
267
 */
268
0
#define SOCKS5_CONN_MSG_LEN   262
269
static gint
270
set_connect_msg (guint8       *msg,
271
     const gchar *hostname,
272
     guint16      port,
273
     GError     **error)
274
0
{
275
0
  guint len = 0;
276
277
0
  msg[len++] = SOCKS5_VERSION;
278
0
  msg[len++] = SOCKS5_CMD_CONNECT;
279
0
  msg[len++] = SOCKS5_RESERVED;
280
281
0
  if (g_hostname_is_ip_address (hostname))
282
0
    {
283
0
      GInetAddress *addr = g_inet_address_new_from_string (hostname);
284
0
      gsize addr_len = g_inet_address_get_native_size (addr);
285
286
      /* We are cheating for simplicity, here's the logic:
287
       *   1 = IPV4 = 4 bytes / 4
288
       *   4 = IPV6 = 16 bytes / 4 */
289
0
      msg[len++] = addr_len / 4;
290
0
      memcpy (msg + len, g_inet_address_to_bytes (addr), addr_len);
291
0
      len += addr_len;
292
293
0
      g_object_unref (addr);
294
0
    }
295
0
  else
296
0
    {
297
0
      gsize host_len = strlen (hostname);
298
299
0
      if (host_len > SOCKS5_MAX_LEN)
300
0
  {
301
0
    g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
302
0
           _("Hostname “%s” is too long for SOCKSv5 protocol"),
303
0
           hostname);
304
0
    return -1;
305
0
  }
306
307
0
      msg[len++] = SOCKS5_ATYP_DOMAINNAME;
308
0
      msg[len++] = (guint8) host_len;
309
0
      memcpy (msg + len, hostname, host_len);
310
0
      len += host_len;
311
0
    }
312
313
0
    {
314
0
      guint16 hp = g_htons (port);
315
0
      memcpy (msg + len, &hp, 2);
316
0
      len += 2;
317
0
    }
318
319
0
  return len;
320
0
}
321
322
/*
323
 * +----+-----+-------+------+----------+----------+
324
 * |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
325
 * +----+-----+-------+------+----------+----------+
326
 * | 1  |  1  | X'00' |  1   | Variable |    2     |
327
 * +----+-----+-------+------+----------+----------+
328
 * This reply need to be read by small part to determine size. Buffer
329
 * size is determined in function of the biggest part to read.
330
 *
331
 * The parser only requires 4 bytes.
332
 */
333
0
#define SOCKS5_CONN_REP_LEN   257
334
static gboolean
335
parse_connect_reply (const guint8 *data, gint *atype, GError **error)
336
0
{
337
0
  if (data[0] != SOCKS5_VERSION)
338
0
    {
339
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
340
0
         _("The server is not a SOCKSv5 proxy server."));
341
0
      return FALSE;
342
0
    }
343
344
0
  switch (data[1])
345
0
    {
346
0
      case SOCKS5_REP_SUCCEEDED:
347
0
  if (data[2] != SOCKS5_RESERVED)
348
0
    {
349
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
350
0
         _("The server is not a SOCKSv5 proxy server."));
351
0
      return FALSE;
352
0
    }
353
354
0
  switch (data[3])
355
0
    {
356
0
    case SOCKS5_ATYP_IPV4:
357
0
    case SOCKS5_ATYP_IPV6:
358
0
    case SOCKS5_ATYP_DOMAINNAME:
359
0
      *atype = data[3];
360
0
      break;
361
362
0
    default:
363
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
364
0
         _("The SOCKSv5 proxy server uses unknown address type."));
365
0
      return FALSE;
366
0
    }
367
0
  break;
368
369
0
      case SOCKS5_REP_SRV_FAILURE:
370
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
371
0
           _("Internal SOCKSv5 proxy server error."));
372
0
  return FALSE;
373
0
  break;
374
375
0
      case SOCKS5_REP_NOT_ALLOWED:
376
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_NOT_ALLOWED,
377
0
           _("SOCKSv5 connection not allowed by ruleset."));
378
0
  return FALSE;
379
0
  break;
380
381
0
      case SOCKS5_REP_TTL_EXPIRED:
382
0
      case SOCKS5_REP_HOST_UNREACH:
383
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
384
0
           _("Host unreachable through SOCKSv5 server."));
385
0
  return FALSE;
386
0
  break;
387
388
0
      case SOCKS5_REP_NET_UNREACH:
389
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
390
0
           _("Network unreachable through SOCKSv5 proxy."));
391
0
  return FALSE;
392
0
  break;
393
394
0
      case SOCKS5_REP_REFUSED:
395
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED,
396
0
           _("Connection refused through SOCKSv5 proxy."));
397
0
  return FALSE;
398
0
  break;
399
400
0
      case SOCKS5_REP_CMD_NOT_SUP:
401
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
402
0
           _("SOCKSv5 proxy does not support “connect” command."));
403
0
  return FALSE;
404
0
  break;
405
406
0
      case SOCKS5_REP_ATYPE_NOT_SUP:
407
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
408
0
           _("SOCKSv5 proxy does not support provided address type."));
409
0
  return FALSE;
410
0
  break;
411
412
0
      default: /* Unknown error */
413
0
  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
414
0
           _("Unknown SOCKSv5 proxy error."));
415
0
  return FALSE;
416
0
  break;
417
0
    }
418
419
0
  return TRUE;
420
0
}
421
422
static GIOStream *
423
g_socks5_proxy_connect (GProxy            *proxy,
424
      GIOStream         *io_stream,
425
      GProxyAddress     *proxy_address,
426
      GCancellable      *cancellable,
427
      GError          **error)
428
0
{
429
0
  gboolean has_auth;
430
0
  GInputStream *in;
431
0
  GOutputStream *out;
432
0
  const gchar *hostname;
433
0
  guint16 port;
434
0
  const gchar *username;
435
0
  const gchar *password;
436
437
0
  hostname = g_proxy_address_get_destination_hostname (proxy_address);
438
0
  port = g_proxy_address_get_destination_port (proxy_address);
439
0
  username = g_proxy_address_get_username (proxy_address);
440
0
  password = g_proxy_address_get_password (proxy_address);
441
442
0
  has_auth = username || password;
443
  
444
0
  in = g_io_stream_get_input_stream (io_stream);
445
0
  out = g_io_stream_get_output_stream (io_stream);
446
447
  /* Send SOCKS5 handshake */
448
0
    {
449
0
      guint8 msg[SOCKS5_NEGO_MSG_LEN];
450
0
      gint len;
451
452
0
      len = set_nego_msg (msg, has_auth);
453
454
0
      if (!g_output_stream_write_all (out, msg, len, NULL,
455
0
              cancellable, error))
456
0
  goto error;
457
0
    }
458
459
  /* Receive SOCKS5 response and reply with authentication if required */
460
0
    {
461
0
      guint8 data[SOCKS5_NEGO_REP_LEN];
462
0
      gboolean must_auth = FALSE;
463
464
0
      if (!g_input_stream_read_all (in, data, sizeof (data), NULL,
465
0
            cancellable, error))
466
0
  goto error;
467
468
0
      if (!parse_nego_reply (data, has_auth, &must_auth, error))
469
0
    goto error;
470
471
0
      if (must_auth)
472
0
  {
473
0
    guint8 msg[SOCKS5_AUTH_MSG_LEN];
474
0
    gint len;
475
476
0
    len = set_auth_msg (msg, username, password, error);
477
478
0
    if (len < 0)
479
0
      goto error;
480
    
481
0
    if (!g_output_stream_write_all (out, msg, len, NULL,
482
0
            cancellable, error))
483
0
      goto error;
484
485
0
    if (!g_input_stream_read_all (in, data, sizeof (data), NULL,
486
0
          cancellable, error))
487
0
      goto error;
488
489
0
    if (!check_auth_status (data, error))
490
0
      goto error;
491
0
  }
492
0
    }
493
494
  /* Send SOCKS5 connection request */
495
0
    {
496
0
      guint8 msg[SOCKS5_CONN_MSG_LEN];
497
0
      gint len;
498
      
499
0
      len = set_connect_msg (msg, hostname, port, error);
500
501
0
      if (len < 0)
502
0
  goto error;
503
504
0
      if (!g_output_stream_write_all (out, msg, len, NULL,
505
0
              cancellable, error))
506
0
  goto error;
507
0
    }
508
509
  /* Read SOCKS5 response */
510
0
    {
511
0
      guint8 data[SOCKS5_CONN_REP_LEN];
512
0
      gint atype;
513
514
0
      if (!g_input_stream_read_all (in, data, 4 /* VER, REP, RSV, ATYP */, NULL,
515
0
            cancellable, error))
516
0
  goto error;
517
518
0
      if (!parse_connect_reply (data, &atype, error))
519
0
  goto error;
520
521
0
      switch (atype)
522
0
  {
523
0
    case SOCKS5_ATYP_IPV4:
524
0
      if (!g_input_stream_read_all (in, data,
525
0
            4 /* IPv4 length */ + 2 /* port */,
526
0
            NULL, cancellable, error))
527
0
        goto error;
528
0
      break;
529
530
0
    case SOCKS5_ATYP_IPV6:
531
0
      if (!g_input_stream_read_all (in, data,
532
0
            16 /* IPv6 length */ + 2 /* port */,
533
0
            NULL, cancellable, error))
534
0
        goto error;
535
0
      break;
536
537
0
    case SOCKS5_ATYP_DOMAINNAME:
538
0
      if (!g_input_stream_read_all (in, data, 1 /* domain name length */,
539
0
            NULL, cancellable, error))
540
0
        goto error;
541
0
      if (!g_input_stream_read_all (in, data,
542
0
            data[0] /* domain name length */ + 2 /* port */,
543
0
            NULL, cancellable, error))
544
0
        goto error;
545
0
      break;
546
0
  }
547
0
    }
548
549
0
  return g_object_ref (io_stream);
550
551
0
error:
552
0
  return NULL;
553
0
}
554
555
556
typedef struct
557
{
558
  GIOStream *io_stream;
559
  gchar *hostname;
560
  guint16 port;
561
  gchar *username;
562
  gchar *password;
563
  guint8 *buffer;
564
  gssize length;
565
  gssize offset;
566
} ConnectAsyncData;
567
568
static void nego_msg_write_cb       (GObject          *source,
569
               GAsyncResult     *res,
570
               gpointer          user_data);
571
static void nego_reply_read_cb        (GObject          *source,
572
               GAsyncResult     *res,
573
               gpointer          user_data);
574
static void auth_msg_write_cb       (GObject          *source,
575
               GAsyncResult     *res,
576
               gpointer          user_data);
577
static void auth_reply_read_cb        (GObject          *source,
578
               GAsyncResult     *result,
579
               gpointer          user_data);
580
static void send_connect_msg        (GTask            *task);
581
static void connect_msg_write_cb      (GObject          *source,
582
               GAsyncResult     *result,
583
               gpointer          user_data);
584
static void connect_reply_read_cb     (GObject          *source,
585
               GAsyncResult     *result,
586
               gpointer          user_data);
587
static void connect_addr_len_read_cb  (GObject          *source,
588
               GAsyncResult     *result,
589
               gpointer          user_data);
590
static void connect_addr_read_cb      (GObject          *source,
591
               GAsyncResult     *result,
592
               gpointer          user_data);
593
594
static void
595
free_connect_data (ConnectAsyncData *data)
596
0
{
597
0
  g_object_unref (data->io_stream);
598
599
0
  g_free (data->hostname);
600
0
  g_free (data->username);
601
0
  g_free (data->password);
602
0
  g_free (data->buffer);
603
604
0
  g_slice_free (ConnectAsyncData, data);
605
0
}
606
607
static void
608
do_read (GAsyncReadyCallback callback, GTask *task, ConnectAsyncData *data)
609
0
{
610
0
   GInputStream *in;
611
0
   in = g_io_stream_get_input_stream (data->io_stream);
612
0
   g_input_stream_read_async (in,
613
0
            data->buffer + data->offset,
614
0
            data->length - data->offset,
615
0
            g_task_get_priority (task),
616
0
            g_task_get_cancellable (task),
617
0
            callback, task);
618
0
}
619
620
static void
621
do_write (GAsyncReadyCallback callback, GTask *task, ConnectAsyncData *data)
622
0
{
623
0
  GOutputStream *out;
624
0
  out = g_io_stream_get_output_stream (data->io_stream);
625
0
  g_output_stream_write_async (out,
626
0
             data->buffer + data->offset,
627
0
             data->length - data->offset,
628
0
             g_task_get_priority (task),
629
0
             g_task_get_cancellable (task),
630
0
             callback, task);
631
0
}
632
633
static void
634
g_socks5_proxy_connect_async (GProxy               *proxy,
635
            GIOStream            *io_stream,
636
            GProxyAddress        *proxy_address,
637
            GCancellable         *cancellable,
638
            GAsyncReadyCallback   callback,
639
            gpointer              user_data)
640
0
{
641
0
  GTask *task;
642
0
  ConnectAsyncData *data;
643
644
0
  data = g_slice_new0 (ConnectAsyncData);
645
0
  data->io_stream = g_object_ref (io_stream);
646
647
0
  task = g_task_new (proxy, cancellable, callback, user_data);
648
0
  g_task_set_source_tag (task, g_socks5_proxy_connect_async);
649
0
  g_task_set_task_data (task, data, (GDestroyNotify) free_connect_data);
650
651
0
  g_object_get (G_OBJECT (proxy_address),
652
0
    "destination-hostname", &data->hostname,
653
0
    "destination-port", &data->port,
654
0
    "username", &data->username,
655
0
    "password", &data->password,
656
0
    NULL);
657
658
0
  data->buffer = g_malloc0 (SOCKS5_NEGO_MSG_LEN);
659
0
  data->length = set_nego_msg (data->buffer,
660
0
             data->username || data->password);
661
0
  data->offset = 0;
662
663
0
  do_write (nego_msg_write_cb, task, data);
664
0
}
665
666
667
static void
668
nego_msg_write_cb (GObject      *source,
669
       GAsyncResult *res,
670
       gpointer      user_data)
671
0
{
672
0
  GTask *task = user_data;
673
0
  ConnectAsyncData *data = g_task_get_task_data (task);
674
0
  GError *error = NULL;
675
0
  gssize written;
676
677
0
  written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
678
0
            res, &error);
679
680
0
  if (written < 0)
681
0
    {
682
0
      g_task_return_error (task, error);
683
0
      g_object_unref (task);
684
0
      return;
685
0
    }
686
687
0
  data->offset += written;
688
689
0
  if (data->offset == data->length)
690
0
    {
691
0
      g_free (data->buffer);
692
693
0
      data->buffer = g_malloc0 (SOCKS5_NEGO_REP_LEN);
694
0
      data->length = SOCKS5_NEGO_REP_LEN;
695
0
      data->offset = 0;
696
697
0
      do_read (nego_reply_read_cb, task, data);
698
0
    }
699
0
  else
700
0
    {
701
0
      do_write (nego_msg_write_cb, task, data);
702
0
    }
703
0
}
704
705
static void
706
nego_reply_read_cb (GObject      *source,
707
        GAsyncResult *res,
708
        gpointer      user_data)
709
0
{
710
0
  GTask *task = user_data;
711
0
  ConnectAsyncData *data = g_task_get_task_data (task);
712
0
  GError *error = NULL;
713
0
  gssize read;
714
715
0
  read = g_input_stream_read_finish (G_INPUT_STREAM (source),
716
0
             res, &error);
717
718
0
  if (read < 0)
719
0
    {
720
0
      g_task_return_error (task, error);
721
0
      g_object_unref (task);
722
0
      return;
723
0
    }
724
725
0
  if (read == 0)
726
0
    {
727
0
      g_task_return_new_error (task,
728
0
                               G_IO_ERROR,
729
0
                               G_IO_ERROR_CONNECTION_CLOSED,
730
0
                               "Connection to SOCKSv5 proxy server lost");
731
0
      g_object_unref (task);
732
0
      return;
733
0
    }
734
735
0
  data->offset += read;
736
  
737
0
  if (data->offset == data->length)
738
0
    {
739
0
      gboolean must_auth = FALSE;
740
0
      gboolean has_auth = data->username || data->password;
741
742
0
      if (!parse_nego_reply (data->buffer, has_auth, &must_auth, &error))
743
0
  {
744
0
    g_task_return_error (task, error);
745
0
    g_object_unref (task);
746
0
    return;
747
0
  }
748
749
0
      if (must_auth)
750
0
  {
751
0
    g_free (data->buffer);
752
753
0
    data->buffer = g_malloc0 (SOCKS5_AUTH_MSG_LEN);
754
0
    data->length = set_auth_msg (data->buffer,
755
0
               data->username,
756
0
               data->password,
757
0
               &error);
758
0
    data->offset = 0;
759
760
0
    if (data->length < 0)
761
0
      {
762
0
        g_task_return_error (task, error);
763
0
        g_object_unref (task);
764
0
        return;
765
0
      }
766
    
767
0
    do_write (auth_msg_write_cb, task, data);
768
0
  }
769
0
      else
770
0
  {
771
0
    send_connect_msg (task);
772
0
  }
773
0
    }
774
0
  else
775
0
    {
776
0
      do_read (nego_reply_read_cb, task, data);
777
0
    }
778
0
}
779
780
static void
781
auth_msg_write_cb (GObject      *source,
782
       GAsyncResult *result,
783
       gpointer      user_data)
784
0
{
785
0
  GTask *task = user_data;
786
0
  ConnectAsyncData *data = g_task_get_task_data (task);
787
0
  GError *error = NULL;
788
0
  gssize written;
789
790
0
  written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
791
0
            result, &error);
792
  
793
0
  if (written < 0)
794
0
    {
795
0
      g_task_return_error (task, error);
796
0
      g_object_unref (task);
797
0
      return;
798
0
    }
799
800
0
  data->offset += written;
801
802
0
  if (data->offset == data->length)
803
0
    {
804
0
      g_free (data->buffer);
805
806
0
      data->buffer = g_malloc0 (SOCKS5_NEGO_REP_LEN);
807
0
      data->length = SOCKS5_NEGO_REP_LEN;
808
0
      data->offset = 0;
809
810
0
      do_read (auth_reply_read_cb, task, data);
811
0
    }
812
0
  else
813
0
    {
814
0
      do_write (auth_msg_write_cb, task, data);
815
0
    }
816
0
}
817
818
static void
819
auth_reply_read_cb (GObject      *source,
820
        GAsyncResult *result,
821
        gpointer      user_data)
822
0
{
823
0
  GTask *task = user_data;
824
0
  ConnectAsyncData *data = g_task_get_task_data (task);
825
0
  GError *error = NULL;
826
0
  gssize read;
827
828
0
  read = g_input_stream_read_finish (G_INPUT_STREAM (source),
829
0
             result, &error);
830
831
0
  if (read < 0)
832
0
    {
833
0
      g_task_return_error (task, error);
834
0
      g_object_unref (task);
835
0
      return;
836
0
    }
837
838
0
  if (read == 0)
839
0
    {
840
0
      g_task_return_new_error (task,
841
0
                               G_IO_ERROR,
842
0
                               G_IO_ERROR_CONNECTION_CLOSED,
843
0
                               "Connection to SOCKSv5 proxy server lost");
844
0
      g_object_unref (task);
845
0
      return;
846
0
    }
847
848
0
  data->offset += read;
849
850
0
  if (data->offset == data->length)
851
0
    {
852
0
      if (!check_auth_status (data->buffer, &error))
853
0
  {
854
0
    g_task_return_error (task, error);
855
0
    g_object_unref (task);
856
0
    return;
857
0
  }
858
  
859
0
      send_connect_msg (task);
860
0
    }
861
0
  else
862
0
    {
863
0
      do_read (auth_reply_read_cb, task, data);
864
0
    }
865
0
}
866
867
static void
868
send_connect_msg (GTask *task)
869
0
{
870
0
  ConnectAsyncData *data = g_task_get_task_data (task);
871
0
  GError *error = NULL;
872
873
0
  g_free (data->buffer);
874
875
0
  data->buffer = g_malloc0 (SOCKS5_CONN_MSG_LEN);
876
0
  data->length = set_connect_msg (data->buffer,
877
0
          data->hostname,
878
0
          data->port,
879
0
          &error);
880
0
  data->offset = 0;
881
  
882
0
  if (data->length < 0)
883
0
    {
884
0
      g_task_return_error (task, error);
885
0
      g_object_unref (task);
886
0
      return;
887
0
    }
888
889
0
  do_write (connect_msg_write_cb, task, data);
890
0
}
891
892
static void
893
connect_msg_write_cb (GObject      *source,
894
          GAsyncResult *result,
895
          gpointer      user_data)
896
0
{
897
0
  GTask *task = user_data;
898
0
  ConnectAsyncData *data = g_task_get_task_data (task);
899
0
  GError *error = NULL;
900
0
  gssize written;
901
902
0
  written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
903
0
            result, &error);
904
  
905
0
  if (written < 0)
906
0
    {
907
0
      g_task_return_error (task, error);
908
0
      g_object_unref (task);
909
0
      return;
910
0
    }
911
912
0
  data->offset += written;
913
914
0
  if (data->offset == data->length)
915
0
    {
916
0
      g_free (data->buffer);
917
918
0
      data->buffer = g_malloc0 (SOCKS5_CONN_REP_LEN);
919
0
      data->length = 4;
920
0
      data->offset = 0;
921
922
0
      do_read (connect_reply_read_cb, task, data);
923
0
    }
924
0
  else
925
0
    {
926
0
      do_write (connect_msg_write_cb, task, data);
927
0
    }
928
0
}
929
930
static void
931
connect_reply_read_cb (GObject       *source,
932
           GAsyncResult  *result,
933
           gpointer       user_data)
934
0
{
935
0
  GTask *task = user_data;
936
0
  ConnectAsyncData *data = g_task_get_task_data (task);
937
0
  GError *error = NULL;
938
0
  gssize read;
939
940
0
  read = g_input_stream_read_finish (G_INPUT_STREAM (source),
941
0
             result, &error);
942
943
0
  if (read < 0)
944
0
    {
945
0
      g_task_return_error (task, error);
946
0
      g_object_unref (task);
947
0
      return;
948
0
    }
949
950
0
  if (read == 0)
951
0
    {
952
0
      g_task_return_new_error (task,
953
0
                               G_IO_ERROR,
954
0
                               G_IO_ERROR_CONNECTION_CLOSED,
955
0
                               "Connection to SOCKSv5 proxy server lost");
956
0
      g_object_unref (task);
957
0
      return;
958
0
    }
959
960
0
  data->offset += read;
961
962
0
  if (data->offset == data->length)
963
0
    {
964
0
      gint atype;
965
966
0
      if (!parse_connect_reply (data->buffer, &atype, &error))
967
0
  {
968
0
    g_task_return_error (task, error);
969
0
    g_object_unref (task);
970
0
    return;
971
0
  }
972
973
0
      switch (atype)
974
0
  {
975
0
    case SOCKS5_ATYP_IPV4:
976
0
      data->length = 6;
977
0
      data->offset = 0;
978
0
      do_read (connect_addr_read_cb, task, data);
979
0
      break;
980
981
0
    case SOCKS5_ATYP_IPV6:
982
0
      data->length = 18;
983
0
      data->offset = 0;
984
0
      do_read (connect_addr_read_cb, task, data);
985
0
      break;
986
987
0
    case SOCKS5_ATYP_DOMAINNAME:
988
0
      data->length = 1;
989
0
      data->offset = 0;
990
0
      do_read (connect_addr_len_read_cb, task, data);
991
0
      break;
992
0
  }
993
0
    }
994
0
  else
995
0
    {
996
0
      do_read (connect_reply_read_cb, task, data);
997
0
    }
998
0
}
999
1000
static void
1001
connect_addr_len_read_cb (GObject      *source,
1002
        GAsyncResult *result,
1003
        gpointer      user_data)
1004
0
{
1005
0
  GTask *task = user_data;
1006
0
  ConnectAsyncData *data = g_task_get_task_data (task);
1007
0
  GError *error = NULL;
1008
0
  gssize read;
1009
1010
0
  read = g_input_stream_read_finish (G_INPUT_STREAM (source),
1011
0
             result, &error);
1012
1013
0
  if (read < 0)
1014
0
    {
1015
0
      g_task_return_error (task, error);
1016
0
      g_object_unref (task);
1017
0
      return;
1018
0
    }
1019
1020
0
  if (read == 0)
1021
0
    {
1022
0
      g_task_return_new_error (task,
1023
0
                               G_IO_ERROR,
1024
0
                               G_IO_ERROR_CONNECTION_CLOSED,
1025
0
                               "Connection to SOCKSv5 proxy server lost");
1026
0
      g_object_unref (task);
1027
0
      return;
1028
0
    }
1029
1030
0
  data->length = data->buffer[0] + 2;
1031
0
  data->offset = 0;
1032
1033
0
  do_read (connect_addr_read_cb, task, data);
1034
0
}
1035
1036
static void
1037
connect_addr_read_cb (GObject      *source,
1038
          GAsyncResult *result,
1039
          gpointer      user_data)
1040
0
{
1041
0
  GTask *task = user_data;
1042
0
  ConnectAsyncData *data = g_task_get_task_data (task);
1043
0
  GError *error = NULL;
1044
0
  gssize read;
1045
1046
0
  read = g_input_stream_read_finish (G_INPUT_STREAM (source),
1047
0
             result, &error);
1048
1049
0
  if (read < 0)
1050
0
    {
1051
0
      g_task_return_error (task, error);
1052
0
      g_object_unref (task);
1053
0
      return;
1054
0
    }
1055
1056
0
  if (read == 0)
1057
0
    {
1058
0
      g_task_return_new_error (task,
1059
0
                               G_IO_ERROR,
1060
0
                               G_IO_ERROR_CONNECTION_CLOSED,
1061
0
                               "Connection to SOCKSv5 proxy server lost");
1062
0
      g_object_unref (task);
1063
0
      return;
1064
0
    }
1065
1066
0
  data->offset += read;
1067
1068
0
  if (data->offset == data->length)
1069
0
    {
1070
0
      g_task_return_pointer (task, g_object_ref (data->io_stream), g_object_unref);
1071
0
      g_object_unref (task);
1072
0
      return;
1073
0
    }
1074
0
  else
1075
0
    {
1076
0
      do_read (connect_reply_read_cb, task, data);
1077
0
    }
1078
0
}
1079
1080
static GIOStream *
1081
g_socks5_proxy_connect_finish (GProxy       *proxy,
1082
             GAsyncResult *result,
1083
             GError      **error)
1084
0
{
1085
0
  return g_task_propagate_pointer (G_TASK (result), error);
1086
0
}
1087
1088
static gboolean
1089
g_socks5_proxy_supports_hostname (GProxy *proxy)
1090
0
{
1091
0
  return TRUE;
1092
0
}
1093
1094
static void
1095
g_socks5_proxy_class_init (GSocks5ProxyClass *class)
1096
0
{
1097
0
  GObjectClass *object_class;
1098
1099
0
  object_class = (GObjectClass *) class;
1100
0
  object_class->finalize = g_socks5_proxy_finalize;
1101
0
}
1102
1103
static void
1104
g_socks5_proxy_iface_init (GProxyInterface *proxy_iface)
1105
0
{
1106
0
  proxy_iface->connect  = g_socks5_proxy_connect;
1107
0
  proxy_iface->connect_async = g_socks5_proxy_connect_async;
1108
0
  proxy_iface->connect_finish = g_socks5_proxy_connect_finish;
1109
0
  proxy_iface->supports_hostname = g_socks5_proxy_supports_hostname;
1110
0
}