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

source code of gtk/subprojects/glib/gio/gsocks5proxy.c