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