• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
73 struct _GSocks5Proxy
74 {
75   GObject parent;
76 };
77 
78 struct _GSocks5ProxyClass
79 {
80   GObjectClass parent_class;
81 };
82 
83 static void g_socks5_proxy_iface_init (GProxyInterface *proxy_iface);
84 
85 #define g_socks5_proxy_get_type _g_socks5_proxy_get_type
86 G_DEFINE_TYPE_WITH_CODE (GSocks5Proxy, g_socks5_proxy, G_TYPE_OBJECT,
87 			 G_IMPLEMENT_INTERFACE (G_TYPE_PROXY,
88 						g_socks5_proxy_iface_init)
89 			 _g_io_modules_ensure_extension_points_registered ();
90 			 g_io_extension_point_implement (G_PROXY_EXTENSION_POINT_NAME,
91 							 g_define_type_id,
92 							 "socks5",
93 							 0))
94 
95 static void
g_socks5_proxy_finalize(GObject * object)96 g_socks5_proxy_finalize (GObject *object)
97 {
98   /* must chain up */
99   G_OBJECT_CLASS (g_socks5_proxy_parent_class)->finalize (object);
100 }
101 
102 static void
g_socks5_proxy_init(GSocks5Proxy * proxy)103 g_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
115 static gint
set_nego_msg(guint8 * msg,gboolean has_auth)116 set_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
144 static gboolean
parse_nego_reply(const guint8 * data,gboolean has_auth,gboolean * must_auth,GError ** error)145 parse_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 (error, G_IO_ERROR, 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 (error, G_IO_ERROR, 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 (error, G_IO_ERROR, 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 (error, G_IO_ERROR, 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
201 static gint
set_auth_msg(guint8 * msg,const gchar * username,const gchar * password,GError ** error)202 set_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 (username);
213 
214   if (password)
215     plen = strlen (password);
216 
217   if (ulen > SOCKS5_MAX_LEN || plen > SOCKS5_MAX_LEN)
218     {
219       g_set_error_literal (error, G_IO_ERROR, 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 (msg + len, username, ulen);
230 
231   len += ulen;
232   msg[len++] = plen;
233 
234   if (plen > 0)
235     memcpy (msg + len, password, plen);
236 
237   len += plen;
238 
239   return len;
240 }
241 
242 
243 static gboolean
check_auth_status(const guint8 * data,GError ** error)244 check_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 (error, G_IO_ERROR, 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
267 static gint
set_connect_msg(guint8 * msg,const gchar * hostname,guint16 port,GError ** error)268 set_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 (hostname);
282       gsize addr_len = g_inet_address_get_native_size (addr);
283 
284       /* We are cheating for simplicity, here's the logic:
285        *   1 = IPV4 = 4 bytes / 4
286        *   4 = IPV6 = 16 bytes / 4 */
287       msg[len++] = addr_len / 4;
288       memcpy (msg + len, g_inet_address_to_bytes (addr), addr_len);
289       len += addr_len;
290 
291       g_object_unref (addr);
292     }
293   else
294     {
295       gsize host_len = strlen (hostname);
296 
297       if (host_len > SOCKS5_MAX_LEN)
298 	{
299 	  g_set_error (error, G_IO_ERROR, 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 (msg + len, hostname, host_len);
308       len += host_len;
309     }
310 
311     {
312       guint16 hp = g_htons (port);
313       memcpy (msg + len, &hp, 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
332 static gboolean
parse_connect_reply(const guint8 * data,gint * atype,GError ** error)333 parse_connect_reply (const guint8 *data, gint *atype, GError **error)
334 {
335   if (data[0] != SOCKS5_VERSION)
336     {
337       g_set_error_literal (error, G_IO_ERROR, 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 (error, G_IO_ERROR, 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 (error, G_IO_ERROR, 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 (error, G_IO_ERROR, 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 (error, G_IO_ERROR, 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 (error, G_IO_ERROR, 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 (error, G_IO_ERROR, 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 (error, G_IO_ERROR, 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 (error, G_IO_ERROR, 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 (error, G_IO_ERROR, 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 (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
412 			     _("Unknown SOCKSv5 proxy error."));
413 	return FALSE;
414 	break;
415     }
416 
417   return TRUE;
418 }
419 
420 static GIOStream *
g_socks5_proxy_connect(GProxy * proxy,GIOStream * io_stream,GProxyAddress * proxy_address,GCancellable * cancellable,GError ** error)421 g_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_address);
436   port = g_proxy_address_get_destination_port (proxy_address);
437   username = g_proxy_address_get_username (proxy_address);
438   password = g_proxy_address_get_password (proxy_address);
439 
440   has_auth = username || password;
441 
442   in = g_io_stream_get_input_stream (io_stream);
443   out = g_io_stream_get_output_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 (out, msg, 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 (in, data, sizeof (data), NULL,
463 				    cancellable, error))
464 	goto error;
465 
466       if (!parse_nego_reply (data, has_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 (out, msg, len, NULL,
480 					  cancellable, error))
481 	    goto error;
482 
483 	  if (!g_input_stream_read_all (in, data, 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 (out, msg, 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 (in, data, 4, NULL,
513 				    cancellable, error))
514 	goto error;
515 
516       if (!parse_connect_reply (data, &atype, error))
517 	goto error;
518 
519       switch (atype)
520 	{
521 	  case SOCKS5_ATYP_IPV4:
522 	    if (!g_input_stream_read_all (in, data, 6, NULL,
523 					  cancellable, error))
524 	      goto error;
525 	    break;
526 
527 	  case SOCKS5_ATYP_IPV6:
528 	    if (!g_input_stream_read_all (in, data, 18, NULL,
529 					  cancellable, error))
530 	      goto error;
531 	    break;
532 
533 	  case SOCKS5_ATYP_DOMAINNAME:
534 	    if (!g_input_stream_read_all (in, data, 1, NULL,
535 					  cancellable, error))
536 	      goto error;
537 	    if (!g_input_stream_read_all (in, data, data[0] + 2, NULL,
538 					  cancellable, error))
539 	      goto error;
540 	    break;
541 	}
542     }
543 
544   return g_object_ref (io_stream);
545 
546 error:
547   return NULL;
548 }
549 
550 
551 typedef struct
552 {
553   GIOStream *io_stream;
554   gchar *hostname;
555   guint16 port;
556   gchar *username;
557   gchar *password;
558   guint8 *buffer;
559   gssize length;
560   gssize offset;
561 } ConnectAsyncData;
562 
563 static void nego_msg_write_cb	      (GObject          *source,
564 				       GAsyncResult     *res,
565 				       gpointer          user_data);
566 static void nego_reply_read_cb	      (GObject          *source,
567 				       GAsyncResult     *res,
568 				       gpointer          user_data);
569 static void auth_msg_write_cb	      (GObject          *source,
570 				       GAsyncResult     *res,
571 				       gpointer          user_data);
572 static void auth_reply_read_cb	      (GObject          *source,
573 				       GAsyncResult     *result,
574 				       gpointer          user_data);
575 static void send_connect_msg	      (GTask            *task);
576 static void connect_msg_write_cb      (GObject          *source,
577 				       GAsyncResult     *result,
578 				       gpointer          user_data);
579 static void connect_reply_read_cb     (GObject          *source,
580 				       GAsyncResult     *result,
581 				       gpointer          user_data);
582 static void connect_addr_len_read_cb  (GObject          *source,
583 				       GAsyncResult     *result,
584 				       gpointer          user_data);
585 static void connect_addr_read_cb      (GObject          *source,
586 				       GAsyncResult     *result,
587 				       gpointer          user_data);
588 
589 static void
free_connect_data(ConnectAsyncData * data)590 free_connect_data (ConnectAsyncData *data)
591 {
592   g_object_unref (data->io_stream);
593 
594   g_free (data->hostname);
595   g_free (data->username);
596   g_free (data->password);
597   g_free (data->buffer);
598 
599   g_slice_free (ConnectAsyncData, data);
600 }
601 
602 static void
do_read(GAsyncReadyCallback callback,GTask * task,ConnectAsyncData * data)603 do_read (GAsyncReadyCallback callback, GTask *task, ConnectAsyncData *data)
604 {
605    GInputStream *in;
606    in = g_io_stream_get_input_stream (data->io_stream);
607    g_input_stream_read_async (in,
608 			      data->buffer + data->offset,
609 			      data->length - data->offset,
610 			      g_task_get_priority (task),
611 			      g_task_get_cancellable (task),
612 			      callback, task);
613 }
614 
615 static void
do_write(GAsyncReadyCallback callback,GTask * task,ConnectAsyncData * data)616 do_write (GAsyncReadyCallback callback, GTask *task, ConnectAsyncData *data)
617 {
618   GOutputStream *out;
619   out = g_io_stream_get_output_stream (data->io_stream);
620   g_output_stream_write_async (out,
621 			       data->buffer + data->offset,
622 			       data->length - data->offset,
623 			       g_task_get_priority (task),
624 			       g_task_get_cancellable (task),
625 			       callback, task);
626 }
627 
628 static void
g_socks5_proxy_connect_async(GProxy * proxy,GIOStream * io_stream,GProxyAddress * proxy_address,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)629 g_socks5_proxy_connect_async (GProxy               *proxy,
630 			      GIOStream            *io_stream,
631 			      GProxyAddress        *proxy_address,
632 			      GCancellable         *cancellable,
633 			      GAsyncReadyCallback   callback,
634 			      gpointer              user_data)
635 {
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 (proxy, cancellable, callback, user_data);
643   g_task_set_source_tag (task, g_socks5_proxy_connect_async);
644   g_task_set_task_data (task, data, (GDestroyNotify) free_connect_data);
645 
646   g_object_get (G_OBJECT (proxy_address),
647 		"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 (data->buffer,
655 			       data->username || data->password);
656   data->offset = 0;
657 
658   do_write (nego_msg_write_cb, task, data);
659 }
660 
661 
662 static void
nego_msg_write_cb(GObject * source,GAsyncResult * res,gpointer user_data)663 nego_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 					  res, &error);
674 
675   if (written < 0)
676     {
677       g_task_return_error (task, error);
678       g_object_unref (task);
679       return;
680     }
681 
682   data->offset += written;
683 
684   if (data->offset == data->length)
685     {
686       g_free (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 (nego_reply_read_cb, task, data);
693     }
694   else
695     {
696       do_write (nego_msg_write_cb, task, data);
697     }
698 }
699 
700 static void
nego_reply_read_cb(GObject * source,GAsyncResult * res,gpointer user_data)701 nego_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 				     res, &error);
712 
713   if (read < 0)
714     {
715       g_task_return_error (task, error);
716       g_object_unref (task);
717       return;
718     }
719 
720   if (read == 0)
721     {
722       g_task_return_new_error (task,
723                                G_IO_ERROR,
724                                G_IO_ERROR_CONNECTION_CLOSED,
725                                "Connection to SOCKSv5 proxy server lost");
726       g_object_unref (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->buffer, has_auth, &must_auth, &error))
739 	{
740 	  g_task_return_error (task, error);
741 	  g_object_unref (task);
742 	  return;
743 	}
744 
745       if (must_auth)
746 	{
747 	  g_free (data->buffer);
748 
749 	  data->buffer = g_malloc0 (SOCKS5_AUTH_MSG_LEN);
750 	  data->length = set_auth_msg (data->buffer,
751 				       data->username,
752 				       data->password,
753 				       &error);
754 	  data->offset = 0;
755 
756 	  if (data->length < 0)
757 	    {
758 	      g_task_return_error (task, error);
759 	      g_object_unref (task);
760 	      return;
761 	    }
762 
763 	  do_write (auth_msg_write_cb, task, data);
764 	}
765       else
766 	{
767 	  send_connect_msg (task);
768 	}
769     }
770   else
771     {
772       do_read (nego_reply_read_cb, task, data);
773     }
774 }
775 
776 static void
auth_msg_write_cb(GObject * source,GAsyncResult * result,gpointer user_data)777 auth_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);
788 
789   if (written < 0)
790     {
791       g_task_return_error (task, error);
792       g_object_unref (task);
793       return;
794     }
795 
796   data->offset += written;
797 
798   if (data->offset == data->length)
799     {
800       g_free (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 (auth_reply_read_cb, task, data);
807     }
808   else
809     {
810       do_write (auth_msg_write_cb, task, data);
811     }
812 }
813 
814 static void
auth_reply_read_cb(GObject * source,GAsyncResult * result,gpointer user_data)815 auth_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);
826 
827   if (read < 0)
828     {
829       g_task_return_error (task, error);
830       g_object_unref (task);
831       return;
832     }
833 
834   if (read == 0)
835     {
836       g_task_return_new_error (task,
837                                G_IO_ERROR,
838                                G_IO_ERROR_CONNECTION_CLOSED,
839                                "Connection to SOCKSv5 proxy server lost");
840       g_object_unref (task);
841       return;
842     }
843 
844   data->offset += read;
845 
846   if (data->offset == data->length)
847     {
848       if (!check_auth_status (data->buffer, &error))
849 	{
850 	  g_task_return_error (task, error);
851 	  g_object_unref (task);
852 	  return;
853 	}
854 
855       send_connect_msg (task);
856     }
857   else
858     {
859       do_read (auth_reply_read_cb, task, data);
860     }
861 }
862 
863 static void
send_connect_msg(GTask * task)864 send_connect_msg (GTask *task)
865 {
866   ConnectAsyncData *data = g_task_get_task_data (task);
867   GError *error = NULL;
868 
869   g_free (data->buffer);
870 
871   data->buffer = g_malloc0 (SOCKS5_CONN_MSG_LEN);
872   data->length = set_connect_msg (data->buffer,
873 				  data->hostname,
874 				  data->port,
875 				  &error);
876   data->offset = 0;
877 
878   if (data->length < 0)
879     {
880       g_task_return_error (task, error);
881       g_object_unref (task);
882       return;
883     }
884 
885   do_write (connect_msg_write_cb, task, data);
886 }
887 
888 static void
connect_msg_write_cb(GObject * source,GAsyncResult * result,gpointer user_data)889 connect_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);
900 
901   if (written < 0)
902     {
903       g_task_return_error (task, error);
904       g_object_unref (task);
905       return;
906     }
907 
908   data->offset += written;
909 
910   if (data->offset == data->length)
911     {
912       g_free (data->buffer);
913 
914       data->buffer = g_malloc0 (SOCKS5_CONN_REP_LEN);
915       data->length = 4;
916       data->offset = 0;
917 
918       do_read (connect_reply_read_cb, task, data);
919     }
920   else
921     {
922       do_write (connect_msg_write_cb, task, data);
923     }
924 }
925 
926 static void
connect_reply_read_cb(GObject * source,GAsyncResult * result,gpointer user_data)927 connect_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);
938 
939   if (read < 0)
940     {
941       g_task_return_error (task, error);
942       g_object_unref (task);
943       return;
944     }
945 
946   if (read == 0)
947     {
948       g_task_return_new_error (task,
949                                G_IO_ERROR,
950                                G_IO_ERROR_CONNECTION_CLOSED,
951                                "Connection to SOCKSv5 proxy server lost");
952       g_object_unref (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->buffer, &atype, &error))
963 	{
964 	  g_task_return_error (task, error);
965 	  g_object_unref (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 (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 (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 (connect_addr_len_read_cb, task, data);
987 	    break;
988 	}
989     }
990   else
991     {
992       do_read (connect_reply_read_cb, task, data);
993     }
994 }
995 
996 static void
connect_addr_len_read_cb(GObject * source,GAsyncResult * result,gpointer user_data)997 connect_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);
1008 
1009   if (read < 0)
1010     {
1011       g_task_return_error (task, error);
1012       g_object_unref (task);
1013       return;
1014     }
1015 
1016   if (read == 0)
1017     {
1018       g_task_return_new_error (task,
1019                                G_IO_ERROR,
1020                                G_IO_ERROR_CONNECTION_CLOSED,
1021                                "Connection to SOCKSv5 proxy server lost");
1022       g_object_unref (task);
1023       return;
1024     }
1025 
1026   data->length = data->buffer[0] + 2;
1027   data->offset = 0;
1028 
1029   do_read (connect_addr_read_cb, task, data);
1030 }
1031 
1032 static void
connect_addr_read_cb(GObject * source,GAsyncResult * result,gpointer user_data)1033 connect_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);
1044 
1045   if (read < 0)
1046     {
1047       g_task_return_error (task, error);
1048       g_object_unref (task);
1049       return;
1050     }
1051 
1052   if (read == 0)
1053     {
1054       g_task_return_new_error (task,
1055                                G_IO_ERROR,
1056                                G_IO_ERROR_CONNECTION_CLOSED,
1057                                "Connection to SOCKSv5 proxy server lost");
1058       g_object_unref (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), g_object_unref);
1067       g_object_unref (task);
1068       return;
1069     }
1070   else
1071     {
1072       do_read (connect_reply_read_cb, task, data);
1073     }
1074 }
1075 
1076 static GIOStream *
g_socks5_proxy_connect_finish(GProxy * proxy,GAsyncResult * result,GError ** error)1077 g_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 
1084 static gboolean
g_socks5_proxy_supports_hostname(GProxy * proxy)1085 g_socks5_proxy_supports_hostname (GProxy *proxy)
1086 {
1087   return TRUE;
1088 }
1089 
1090 static void
g_socks5_proxy_class_init(GSocks5ProxyClass * class)1091 g_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 
1099 static void
g_socks5_proxy_iface_init(GProxyInterface * proxy_iface)1100 g_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