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