jingle-s5b/socks5-proto.c
changeset 168 f699f6b47613
equal deleted inserted replaced
167:97f93fa3cd95 168:f699f6b47613
       
     1 /*
       
     2  * socks5-proto.c
       
     3  *
       
     4  * Based on the gio socks5 implementation that is not yet included in
       
     5  * the latest stable glib release.
       
     6  * Original Authors:
       
     7  * Youness Alaoui <youness.alaoui@collabora.co.uk>
       
     8  * Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
       
     9  *
       
    10  * Copyrigth (C) 2010 Nicolas Cornu <nicolas.cornu@ensi-bourges.fr>
       
    11  *
       
    12  * This program is free software; you can redistribute it and/or modify
       
    13  * it under the terms of the GNU General Public License as published by
       
    14  * the Free Software Foundation; either version 2 of the License, or (at
       
    15  * your option) any later version.
       
    16  *
       
    17  * This program is distributed in the hope that it will be useful, but
       
    18  * WITHOUT ANY WARRANTY; without even the implied warranty of
       
    19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    20  * General Public License for more details.
       
    21  *
       
    22  * You should have received a copy of the GNU General Public License
       
    23  * along with this program; if not, write to the Free Software
       
    24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
       
    25  * USA
       
    26  */
       
    27 
       
    28 #include <glib.h>
       
    29 #include <gio/gio.h>
       
    30 
       
    31 #include <string.h>
       
    32 
       
    33 #include "socks5-proto.h"
       
    34 
       
    35 
       
    36 #define SOCKS5_VERSION            0x05
       
    37 
       
    38 #define SOCKS5_CMD_CONNECT        0x01
       
    39 #define SOCKS5_CMD_BIND           0x02
       
    40 #define SOCKS5_CMD_UDP_ASSOCIATE  0x03
       
    41 
       
    42 #define SOCKS5_ATYP_IPV4          0x01
       
    43 #define SOCKS5_ATYP_DOMAINNAME    0x03
       
    44 #define SOCKS5_ATYP_IPV6          0x04
       
    45 
       
    46 #define SOCKS5_AUTH_VERSION       0x01
       
    47 
       
    48 #define SOCKS5_AUTH_NONE          0x00
       
    49 #define SOCKS5_AUTH_GSSAPI        0x01
       
    50 #define SOCKS5_AUTH_USR_PASS      0x02
       
    51 #define SOCKS5_AUTH_NO_ACCEPT     0xff
       
    52 
       
    53 #define SOCKS5_MAX_LEN            255
       
    54 #define SOCKS5_RESERVED           0x00
       
    55 
       
    56 #define SOCKS5_REP_SUCCEEDED      0x00
       
    57 #define SOCKS5_REP_SRV_FAILURE    0x01
       
    58 #define SOCKS5_REP_NOT_ALLOWED    0x02
       
    59 #define SOCKS5_REP_NET_UNREACH    0x03
       
    60 #define SOCKS5_REP_HOST_UNREACH   0x04
       
    61 #define SOCKS5_REP_REFUSED        0x05
       
    62 #define SOCKS5_REP_TTL_EXPIRED    0x06
       
    63 #define SOCKS5_REP_CMD_NOT_SUP    0x07
       
    64 #define SOCKS5_REP_ATYPE_NOT_SUP  0x08
       
    65 
       
    66 #define S5B_DST_PORT              0
       
    67 
       
    68 
       
    69 /*
       
    70  * +----+----------+----------+
       
    71  * |VER | NMETHODS | METHODS  |
       
    72  * +----+----------+----------+
       
    73  * | 1  |    1     | 1 to 255 |
       
    74  * +----+----------+----------+
       
    75  */
       
    76 #define SOCKS5_NEGO_MSG_LEN       4
       
    77 static gint
       
    78 client_set_nego_msg (guint8 *msg, gboolean has_auth)
       
    79 {
       
    80   gint len = 3;
       
    81 
       
    82   msg[0] = SOCKS5_VERSION;
       
    83   msg[1] = 0x01; /* number of methods supported */
       
    84   msg[2] = SOCKS5_AUTH_NONE;
       
    85 
       
    86   /* add support for authentication method */
       
    87   if (has_auth) {
       
    88     msg[1] = 0x02; /* number of methods supported */
       
    89     msg[3] = SOCKS5_AUTH_USR_PASS;
       
    90     len++;
       
    91   }
       
    92 
       
    93   return len;
       
    94 }
       
    95 
       
    96 /*
       
    97  * +----+--------+
       
    98  * |VER | METHOD |
       
    99  * +----+--------+
       
   100  * | 1  |   1    |
       
   101  * +----+--------+
       
   102  */
       
   103 static gint
       
   104 server_set_nego_reply_msg (guint8 *msg, guint8 method)
       
   105 {
       
   106   gint len = 2;
       
   107 
       
   108   msg[0] = SOCKS5_VERSION;
       
   109   msg[1] = method; /* selected method */
       
   110 
       
   111   return len;
       
   112 }
       
   113 
       
   114 
       
   115 /*
       
   116  * +----+--------+
       
   117  * |VER | METHOD |
       
   118  * +----+--------+
       
   119  * | 1  |   1    |
       
   120  * +----+--------+
       
   121  */
       
   122 #define SOCKS5_NEGO_REP_LEN       2
       
   123 static gboolean
       
   124 client_parse_nego_reply (const guint8 *data,
       
   125                          gboolean     has_auth,
       
   126                          gboolean    *must_auth,
       
   127                          GError     **error)
       
   128 {
       
   129   if (data[0] != SOCKS5_VERSION) {
       
   130     g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_FAILED,
       
   131                          "The server is not a SOCKSv5 proxy server.");
       
   132     return FALSE;
       
   133   }
       
   134 
       
   135   switch (data[1]) {
       
   136     case SOCKS5_AUTH_NONE:
       
   137       *must_auth = FALSE;
       
   138       break;
       
   139 
       
   140     case SOCKS5_AUTH_USR_PASS:
       
   141       if (!has_auth) {
       
   142         g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_NEED_AUTH,
       
   143                              "The SOCKSv5 proxy requires authentication.");
       
   144         return FALSE;
       
   145       }
       
   146       *must_auth = TRUE;
       
   147       break;
       
   148 
       
   149     case SOCKS5_AUTH_GSSAPI:
       
   150     case SOCKS5_AUTH_NO_ACCEPT:
       
   151     default:
       
   152       g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_AUTH_FAILED,
       
   153                            "The SOCKSv5 require an authentication method that is not "
       
   154                            "supported.");
       
   155       return FALSE;
       
   156       break;
       
   157   }
       
   158 
       
   159   return TRUE;
       
   160 }
       
   161 
       
   162 
       
   163 /*
       
   164  * +----+----------+----------+
       
   165  * |VER | NMETHODS | METHODS  |
       
   166  * +----+----------+----------+
       
   167  * | 1  |    1     | 1 to 255 |
       
   168  * +----+----------+----------+
       
   169  */
       
   170 static gboolean
       
   171 server_parse_nego_init (const guint8 *data,
       
   172                         gssize        datalen,
       
   173                         GError      **error)
       
   174 {
       
   175   guint i;
       
   176 
       
   177   if (data[0] != SOCKS5_VERSION) {
       
   178     g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_FAILED,
       
   179                          "The client is not a SOCKSv5 client.");
       
   180     return FALSE;
       
   181   }
       
   182 
       
   183   for (i = 2; i < datalen && i < data[1]; ++i) {
       
   184     guint8 method = data[i];
       
   185     if (method == 0x00)
       
   186       return TRUE;
       
   187   }
       
   188   
       
   189   g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_AUTH_FAILED,
       
   190                        "The client did not offer any authentication method "
       
   191                        "we support.");
       
   192   return FALSE;
       
   193 }
       
   194 
       
   195 
       
   196 #define SOCKS5_AUTH_MSG_LEN       515
       
   197 static gint
       
   198 set_auth_msg (guint8      *msg,
       
   199               const gchar *username,
       
   200               const gchar *password,
       
   201               GError **error)
       
   202 {
       
   203   gint len = 0;
       
   204   gint ulen = 0; /* username length */
       
   205   gint plen = 0; /* Password length */
       
   206 
       
   207   if (username)
       
   208     ulen = strlen (username);
       
   209 
       
   210   if (password)
       
   211     plen = strlen (password);
       
   212 
       
   213   if (ulen > SOCKS5_MAX_LEN || plen > SOCKS5_MAX_LEN) {
       
   214     g_set_error (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_FAILED,
       
   215                  "Username or password is too long for SOCKSv5 "
       
   216                  "protocol (max. is %i).",
       
   217                  SOCKS5_MAX_LEN);
       
   218     return FALSE;
       
   219   }
       
   220 
       
   221   msg[len++] = SOCKS5_AUTH_VERSION;
       
   222   msg[len++] = ulen;
       
   223 
       
   224   if (ulen > 0)
       
   225     memcpy (msg + len, username, ulen);
       
   226 
       
   227   len += ulen;
       
   228   msg[len++] = plen;
       
   229 
       
   230   if (plen > 0)
       
   231     memcpy (msg + len, password, plen);
       
   232 
       
   233   len += plen;
       
   234 
       
   235   return len;
       
   236 }
       
   237 
       
   238 
       
   239 static gboolean
       
   240 check_auth_status (const guint8 *data, GError **error)
       
   241 {
       
   242   if (data[0] != SOCKS5_VERSION
       
   243       || data[1] != SOCKS5_REP_SUCCEEDED) {
       
   244     g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_AUTH_FAILED,
       
   245                          "SOCKSv5 authentication failed due to wrong "
       
   246                          "username or password.");
       
   247     return FALSE;
       
   248   }
       
   249   return TRUE;
       
   250 }
       
   251 
       
   252 /*
       
   253  * +----+-----+-------+------+----------+----------+
       
   254  * |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
       
   255  * +----+-----+-------+------+----------+----------+
       
   256  * | 1  |  1  | X'00' |  1   | Variable |    2     |
       
   257  * +----+-----+-------+------+----------+----------+
       
   258  * DST.ADDR is a string with first byte being the size. So DST.ADDR may not be
       
   259  * longer then 256 bytes.
       
   260  */
       
   261 #define SOCKS5_CONN_MSG_LEN       262
       
   262 static gint
       
   263 client_set_connect_msg (guint8       *msg,
       
   264                         const gchar *hostname,
       
   265                         guint16      port,
       
   266                         GError     **error)
       
   267 {
       
   268   guint len = 0;
       
   269 
       
   270   msg[len++] = SOCKS5_VERSION;
       
   271   msg[len++] = SOCKS5_CMD_CONNECT;
       
   272   msg[len++] = SOCKS5_RESERVED;
       
   273 
       
   274   gsize host_len = strlen (hostname);
       
   275 
       
   276   if (host_len > SOCKS5_MAX_LEN) {
       
   277     g_set_error (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_FAILED,
       
   278                  "Hostname '%s' too long for SOCKSv5 protocol "
       
   279                  "(maximum is %i bytes)",
       
   280                  hostname, SOCKS5_MAX_LEN);
       
   281     return -1;
       
   282   }
       
   283 
       
   284   msg[len++] = SOCKS5_ATYP_DOMAINNAME;
       
   285   msg[len++] = (guint8) host_len;
       
   286   memcpy (msg + len, hostname, host_len);
       
   287   len += host_len;
       
   288 
       
   289   {
       
   290     guint16 hp = g_htons (port);
       
   291     memcpy (msg + len, &hp, 2);
       
   292     len += 2;
       
   293   }
       
   294 
       
   295   return len;
       
   296 }
       
   297 
       
   298 static guint
       
   299 server_parse_connect_msg (const guint8 *data, gint *atype, GError **error)
       
   300 {
       
   301   if (data[0] != SOCKS5_VERSION) {
       
   302     g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_FAILED,
       
   303                          "The client is not a SOCKSv5 client.");
       
   304     return FALSE;
       
   305   }
       
   306 
       
   307   if (data[1] != SOCKS5_CMD_CONNECT) {
       
   308     g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_FAILED,
       
   309                          "The server only supports the CONNECT command.");
       
   310   }
       
   311 
       
   312   switch (data[3]) {
       
   313     case SOCKS5_ATYP_IPV4:
       
   314       *atype = SOCKS5_ATYP_IPV4;
       
   315       break;
       
   316     case SOCKS5_ATYP_DOMAINNAME:
       
   317       *atype = SOCKS5_ATYP_DOMAINNAME;
       
   318       break;
       
   319     case SOCKS5_ATYP_IPV6:
       
   320       *atype = SOCKS5_ATYP_IPV6;
       
   321       break;
       
   322     default:
       
   323       g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_FAILED,
       
   324                            "The client sent an invalid address type.");
       
   325       return FALSE;
       
   326   }
       
   327 
       
   328   return TRUE;
       
   329 }
       
   330 
       
   331 /*
       
   332  * +----+-----+-------+------+----------+----------+
       
   333  * |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
       
   334  * +----+-----+-------+------+----------+----------+
       
   335  * | 1  |  1  | X'00' |  1   | Variable |    2     |
       
   336  * +----+-----+-------+------+----------+----------+
       
   337  * This reply need to be read by small part to determin size. Buffer
       
   338  * size is determined in function of the biggest part to read.
       
   339  *
       
   340  * The parser only requires 4 bytes.
       
   341  */
       
   342 #define SOCKS5_CONN_REP_LEN       255
       
   343 static gboolean
       
   344 client_parse_connect_reply (const guint8 *data, gint *atype, GError **error)
       
   345 {
       
   346   if (data[0] != SOCKS5_VERSION) {
       
   347     g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_FAILED,
       
   348                          "The server is not a SOCKSv5 proxy server.");
       
   349     return FALSE;
       
   350   }
       
   351 
       
   352   switch (data[1]) {
       
   353     case SOCKS5_REP_SUCCEEDED:
       
   354       if (data[2] != SOCKS5_RESERVED) {
       
   355         g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_FAILED,
       
   356                              "The server is not a SOCKSv5 proxy server.");
       
   357         return FALSE;
       
   358       }
       
   359 
       
   360       switch (data[3]) {
       
   361         case SOCKS5_ATYP_IPV4:
       
   362         case SOCKS5_ATYP_IPV6:
       
   363         case SOCKS5_ATYP_DOMAINNAME:
       
   364           *atype = data[3];
       
   365           break;
       
   366 
       
   367         default:
       
   368           g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_FAILED,
       
   369                                "The SOCKSv5 proxy server uses unkown address type.");
       
   370           return FALSE;
       
   371       }
       
   372       break;
       
   373 
       
   374     case SOCKS5_REP_SRV_FAILURE:
       
   375       g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_FAILED,
       
   376                            "Internal SOCKSv5 proxy server error.");
       
   377       return FALSE;
       
   378       break;
       
   379 
       
   380     case SOCKS5_REP_NOT_ALLOWED:
       
   381       g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_NOT_ALLOWED,
       
   382                            "SOCKSv5 connection not allowed by ruleset.");
       
   383       return FALSE;
       
   384       break;
       
   385 
       
   386     case SOCKS5_REP_TTL_EXPIRED:
       
   387     case SOCKS5_REP_HOST_UNREACH:
       
   388       g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_HOST_UNREACHABLE,
       
   389                            "Host unreachable through SOCKSv5 server.");
       
   390       return FALSE;
       
   391       break;
       
   392 
       
   393     case SOCKS5_REP_NET_UNREACH:
       
   394       g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_NETWORK_UNREACHABLE,
       
   395                            "Network unreachable through SOCKSv5 proxy.");
       
   396       return FALSE;
       
   397       break;
       
   398 
       
   399     case SOCKS5_REP_REFUSED:
       
   400       g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_CONNECTION_REFUSED,
       
   401                            "Connection refused through SOCKSv5 proxy.");
       
   402       return FALSE;
       
   403       break;
       
   404 
       
   405     case SOCKS5_REP_CMD_NOT_SUP:
       
   406       g_set_error_literal (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_FAILED,
       
   407                            "SOCKSv5 proxy does not support 'connect' command.");
       
   408       return FALSE;
       
   409       break;
       
   410 
       
   411     case SOCKS5_REP_ATYPE_NOT_SUP:
       
   412       g_set_error_literal (error, G_IO_ERROR, S5B_SOCKS5_ERROR_FAILED,
       
   413                            "SOCKSv5 proxy does not support provided address type.");
       
   414       return FALSE;
       
   415       break;
       
   416 
       
   417     default: /* Unknown error */
       
   418       g_set_error_literal (error, G_IO_ERROR, S5B_SOCKS5_ERROR_FAILED,
       
   419                            "Unkown SOCKSv5 proxy error.");
       
   420       return FALSE;
       
   421       break;
       
   422   }
       
   423 
       
   424   return TRUE;
       
   425 }
       
   426 
       
   427 static gint
       
   428 server_set_connect_reply (guint8 *msg,
       
   429                           guint8 rep,
       
   430                           guint8 atype,
       
   431                           const guint8 *bndaddr,
       
   432                           guint16 bndport)
       
   433 {
       
   434   guint len = 0, host_len;
       
   435 
       
   436   msg[len++] = SOCKS5_VERSION;
       
   437   msg[len++] = rep;
       
   438   if (rep != 0)
       
   439     return len;
       
   440 
       
   441   len++;
       
   442   msg[len++] = atype;
       
   443   switch (atype) {
       
   444     case SOCKS5_ATYP_IPV4:
       
   445       // bndaddr should already be in network byte order
       
   446       msg[len++] = SOCKS5_ATYP_IPV4;
       
   447       memcpy (msg + len, bndaddr, 4);
       
   448       len += 4;
       
   449       break;
       
   450     case SOCKS5_ATYP_DOMAINNAME:
       
   451       host_len = strlen((gchar* )bndaddr);
       
   452       msg[len++] = SOCKS5_ATYP_DOMAINNAME;
       
   453       msg[len++] = (guint8) host_len;
       
   454       memcpy (msg + len, bndaddr, host_len);
       
   455       len += host_len;
       
   456       break;
       
   457     case SOCKS5_ATYP_IPV6:
       
   458       msg[len++] = SOCKS5_ATYP_IPV6;
       
   459       memcpy (msg + len, bndaddr, 16);
       
   460       len += 16;
       
   461       break;
       
   462     default:
       
   463       return 0;
       
   464   }
       
   465 
       
   466   {
       
   467     guint16 hp = g_htons (bndport);
       
   468     memcpy (msg + len, &hp, 2);
       
   469     len += 2;
       
   470   }
       
   471 
       
   472   return len;
       
   473 }
       
   474 
       
   475 typedef struct
       
   476 {
       
   477   GSimpleAsyncResult *simple;
       
   478   GIOStream *io_stream;
       
   479   GInetSocketAddress *external_address;
       
   480   gchar *hostname;
       
   481   //guint16 port; // port is always 0
       
   482   gchar *username;
       
   483   gchar *password;
       
   484   guint8 *buffer;
       
   485   gssize length;
       
   486   gssize offset;
       
   487   GCancellable *cancellable;
       
   488 } ConnectAsyncData;
       
   489 
       
   490 static void nego_msg_write_cb              (GObject          *source,
       
   491                                             GAsyncResult     *res,
       
   492                                             gpointer          user_data);
       
   493 static void nego_msg_read_cb               (GObject          *source,
       
   494                                             GAsyncResult     *res,
       
   495                                             gpointer          user_data);
       
   496 static void no_supported_method_cb         (GObject      *source,
       
   497                                             GAsyncResult *res,
       
   498                                             gpointer      user_data);
       
   499 static void nego_reply_read_cb             (GObject          *source,
       
   500                                             GAsyncResult     *res,
       
   501                                             gpointer          user_data);
       
   502 static void nego_reply_write_cb            (GObject      *source,
       
   503                                             GAsyncResult *result,
       
   504                                             gpointer      user_data);
       
   505 static void auth_msg_write_cb              (GObject          *source,
       
   506                                            GAsyncResult     *res,
       
   507                                             gpointer          user_data);
       
   508 static void auth_reply_read_cb             (GObject          *source,
       
   509                                             GAsyncResult     *result,
       
   510                                             gpointer          user_data);
       
   511 static void send_connect_msg               (ConnectAsyncData *data);
       
   512 static void connect_msg_write_cb           (GObject          *source,
       
   513                                             GAsyncResult     *result,
       
   514                                             gpointer          user_data);
       
   515 static void connect_msg_read_cb            (GObject      *source,
       
   516                                             GAsyncResult *result,
       
   517                                             gpointer      user_data);
       
   518 static void connect_addr_len_read_cb       (GObject      *source,
       
   519                                             GAsyncResult *result,
       
   520                                             gpointer      user_data);
       
   521 static void connect_addr_read_cb           (GObject      *source,
       
   522                                             GAsyncResult *result,
       
   523                                             gpointer      user_data);
       
   524 static void connect_reply_write_cb         (GObject      *source,
       
   525                                             GAsyncResult *result,
       
   526                                             gpointer      user_data);
       
   527 static void connect_reply_read_cb          (GObject          *source,
       
   528                                             GAsyncResult     *result,
       
   529                                             gpointer          user_data);
       
   530 static void connect_reply_addr_len_read_cb (GObject          *source,
       
   531                                             GAsyncResult     *result,
       
   532                                             gpointer          user_data);
       
   533 static void connect_reply_addr_read_cb     (GObject          *source,
       
   534                                             GAsyncResult     *result,
       
   535                                             gpointer          user_data);
       
   536 
       
   537 static void
       
   538 free_connect_data (ConnectAsyncData *data)
       
   539 {
       
   540   if (data->io_stream)
       
   541     g_object_unref (data->io_stream);
       
   542 
       
   543   if (data->external_address)
       
   544     g_object_unref(data->external_address);
       
   545 
       
   546   g_free (data->hostname);
       
   547   g_free (data->username);
       
   548   g_free (data->password);
       
   549   
       
   550   if (data->cancellable)
       
   551     g_object_unref (data->cancellable);
       
   552 
       
   553   g_slice_free (ConnectAsyncData, data);
       
   554 }
       
   555 
       
   556 static void
       
   557 complete_async_from_error (ConnectAsyncData *data, GError *error)
       
   558 {
       
   559   GSimpleAsyncResult *simple = data->simple;
       
   560   g_simple_async_result_set_from_error (data->simple,
       
   561                                         error);
       
   562   g_error_free (error);
       
   563   g_simple_async_result_set_op_res_gpointer (simple, NULL, NULL);
       
   564   g_simple_async_result_complete (simple);
       
   565   g_object_unref (simple);
       
   566 }
       
   567 
       
   568 static void
       
   569 do_read (GAsyncReadyCallback callback, ConnectAsyncData *data)
       
   570 {
       
   571    GInputStream *in;
       
   572    in = g_io_stream_get_input_stream (data->io_stream);
       
   573    g_input_stream_read_async (in,
       
   574                               data->buffer + data->offset,
       
   575                               data->length - data->offset,
       
   576                               G_PRIORITY_DEFAULT, data->cancellable,
       
   577                               callback, data);
       
   578 }
       
   579 
       
   580 static void
       
   581 do_write (GAsyncReadyCallback callback, ConnectAsyncData *data)
       
   582 {
       
   583   GOutputStream *out;
       
   584   out = g_io_stream_get_output_stream (data->io_stream);
       
   585   g_output_stream_write_async (out,
       
   586                                data->buffer + data->offset,
       
   587                                data->length - data->offset,
       
   588                                G_PRIORITY_DEFAULT, data->cancellable,
       
   589                                callback, data);
       
   590 }
       
   591 
       
   592 /**
       
   593  * @param hostname  with SOCKS5 Bytestreams, the hostname (dst.addr) actually
       
   594  *                  contains SHA1 Hash of: (SID + Requester JID + Target JID)
       
   595  * 
       
   596  * Function called when we act as a client and want to negociate
       
   597  * with a SOCKS5 server.
       
   598  */
       
   599 void
       
   600 socks5_client_nego (GIOStream            *io_stream,
       
   601                     gchar                *hostname,
       
   602                     GCancellable         *cancellable,
       
   603                     GAsyncReadyCallback   callback,
       
   604                     gpointer              user_data)
       
   605 {
       
   606   GSimpleAsyncResult *simple;
       
   607   ConnectAsyncData *data;
       
   608 
       
   609   simple = g_simple_async_result_new (NULL,
       
   610                                       callback, user_data,
       
   611                                       socks5_client_nego);
       
   612 
       
   613   data = g_slice_new0 (ConnectAsyncData);
       
   614 
       
   615   data->simple = simple;
       
   616   data->io_stream = g_object_ref (io_stream);
       
   617 
       
   618   if (cancellable)
       
   619     data->cancellable = g_object_ref (cancellable);
       
   620 
       
   621   data->hostname = g_strdup(hostname);
       
   622 
       
   623   g_simple_async_result_set_op_res_gpointer (simple, data, 
       
   624                                              (GDestroyNotify) free_connect_data);
       
   625 
       
   626   data->buffer = g_malloc0 (SOCKS5_NEGO_MSG_LEN);
       
   627   data->length = client_set_nego_msg (data->buffer,
       
   628                                       data->username || data->password);
       
   629   data->offset = 0;
       
   630 
       
   631   do_write (nego_msg_write_cb, data);
       
   632 }
       
   633 
       
   634 /**
       
   635  * Function called when we act as a server and want to negociate
       
   636  * with a client.
       
   637  */
       
   638 #define SOCKS5_NEGO_MSG_MAX_LEN       257 // 1 + 1 + 255
       
   639 void
       
   640 socks5_server_nego (GIOStream            *io_stream,
       
   641                     gchar                *allowed_hostname,
       
   642                     GInetSocketAddress   *external_address,
       
   643                     GCancellable         *cancellable,
       
   644                     GAsyncReadyCallback   callback,
       
   645                     gpointer              user_data)
       
   646 {
       
   647   GSimpleAsyncResult *simple;
       
   648   ConnectAsyncData *data;
       
   649 
       
   650   simple = g_simple_async_result_new (NULL,
       
   651                                       callback, user_data,
       
   652                                       socks5_server_nego);
       
   653 
       
   654   data = g_slice_new0 (ConnectAsyncData);
       
   655   data->simple = simple;
       
   656   data->io_stream = g_object_ref (io_stream);
       
   657   data->external_address = g_object_ref (external_address);
       
   658 
       
   659   if (cancellable)
       
   660     data->cancellable = g_object_ref (cancellable);
       
   661 
       
   662   data->hostname = g_strdup(allowed_hostname);
       
   663 
       
   664   g_simple_async_result_set_op_res_gpointer (simple, data, 
       
   665                                              (GDestroyNotify) free_connect_data);
       
   666 
       
   667   data->buffer = g_malloc0 (SOCKS5_NEGO_MSG_LEN);
       
   668   data->length = SOCKS5_NEGO_MSG_MAX_LEN;
       
   669   data->offset = 0;
       
   670 
       
   671   do_read (nego_msg_read_cb, data);
       
   672 }
       
   673 
       
   674 
       
   675 static void
       
   676 nego_msg_write_cb (GObject      *source,
       
   677                    GAsyncResult *res,
       
   678                    gpointer      user_data)
       
   679 {
       
   680   GError *error = NULL;
       
   681   ConnectAsyncData *data = user_data;
       
   682   gssize written;
       
   683 
       
   684   written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
       
   685                                           res, &error);
       
   686 
       
   687   if (written < 0) {
       
   688     complete_async_from_error (data, error);
       
   689     return;
       
   690   }
       
   691 
       
   692   data->offset += written;
       
   693 
       
   694   if (data->offset == data->length) {
       
   695     g_free (data->buffer);
       
   696 
       
   697     data->buffer = g_malloc0 (SOCKS5_NEGO_REP_LEN);
       
   698     data->length = SOCKS5_NEGO_REP_LEN;
       
   699     data->offset = 0;
       
   700 
       
   701     do_read (nego_reply_read_cb, data);
       
   702   } else {
       
   703     do_write (nego_msg_write_cb, data);
       
   704   }
       
   705 }
       
   706 
       
   707 static void
       
   708 nego_msg_read_cb (GObject      *source,
       
   709                   GAsyncResult *res,
       
   710                   gpointer      user_data)
       
   711 {
       
   712   GError *error = NULL;
       
   713   ConnectAsyncData *data = user_data;
       
   714   gssize read;
       
   715 
       
   716   read = g_input_stream_read_finish (G_INPUT_STREAM (source),
       
   717                                      res, &error);
       
   718 
       
   719   if (read < 0) {
       
   720     complete_async_from_error (data, error);
       
   721     return;
       
   722   }
       
   723 
       
   724   data->offset += read;
       
   725 
       
   726   /* if the client already send at least NMETHODS, no need to
       
   727    * read until data->length, which is the max size */
       
   728   if ((data->offset >= 2 && data->offset-2 > data->buffer[2]) ||
       
   729       data->offset == data->length) {
       
   730     GError *error;
       
   731 
       
   732     if (!server_parse_nego_init (data->buffer, data->offset, &error)) {
       
   733       if (g_error_matches (error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_AUTH_FAILED)) {
       
   734         /* "If the selected METHOD is X'FF', none of the methods listed by the
       
   735            client are acceptable, and the client MUST close the connection." */
       
   736         g_free (data->buffer);
       
   737         data->buffer = g_malloc0(SOCKS5_NEGO_REP_LEN);
       
   738         data->length = server_set_nego_reply_msg(data->buffer, SOCKS5_AUTH_NO_ACCEPT);
       
   739         data->offset = 0;
       
   740         do_write (no_supported_method_cb, data);
       
   741         return;
       
   742 	  }
       
   743       complete_async_from_error (data, error);
       
   744       return;
       
   745 	}
       
   746 
       
   747     g_free (data->buffer);
       
   748     data->buffer = g_malloc0 (SOCKS5_NEGO_REP_LEN);
       
   749     data->length = server_set_nego_reply_msg (data->buffer, SOCKS5_AUTH_NONE);
       
   750     data->offset = 0;
       
   751 
       
   752     do_write (nego_reply_write_cb, data);
       
   753   } else {
       
   754     do_read (nego_msg_read_cb, data);
       
   755   }
       
   756 }
       
   757 
       
   758 static void
       
   759 no_supported_method_cb (GObject      *source,
       
   760                         GAsyncResult *res,
       
   761                         gpointer      user_data)
       
   762 {
       
   763   GError *error = NULL;
       
   764   ConnectAsyncData *data = user_data;
       
   765   gssize written;
       
   766 
       
   767   written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
       
   768                                           res, &error);
       
   769 
       
   770   if (written < 0) {
       
   771     complete_async_from_error (data, error);
       
   772     return;
       
   773   }
       
   774 
       
   775   data->offset += written;
       
   776 
       
   777   if (data->offset == data->length) {
       
   778     GError *error;
       
   779     error = g_error_new_literal (S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_AUTH_FAILED,
       
   780                                  "The client did not offer any authentication "
       
   781                                   "method we support.");
       
   782     complete_async_from_error (data, error);
       
   783   } else {
       
   784     do_write(no_supported_method_cb, data);
       
   785   }
       
   786 }
       
   787 
       
   788 static void
       
   789 nego_reply_read_cb (GObject      *source,
       
   790                     GAsyncResult *res,
       
   791                     gpointer      user_data)
       
   792 {
       
   793   GError *error = NULL;
       
   794   ConnectAsyncData *data = user_data;
       
   795   gssize read;
       
   796 
       
   797   read = g_input_stream_read_finish (G_INPUT_STREAM (source),
       
   798                                      res, &error);
       
   799 
       
   800   if (read < 0) {
       
   801     complete_async_from_error (data, error);
       
   802     return;
       
   803   }
       
   804 
       
   805   data->offset += read;
       
   806   
       
   807   if (data->offset == data->length) {
       
   808     GError *error;
       
   809     gboolean must_auth = FALSE;
       
   810     gboolean has_auth = data->username || data->password;
       
   811 
       
   812     if (!client_parse_nego_reply (data->buffer, has_auth, &must_auth, &error)) {
       
   813       complete_async_from_error (data, error);
       
   814       return;
       
   815     }
       
   816 
       
   817     if (must_auth) {
       
   818       g_free (data->buffer);
       
   819 
       
   820       data->buffer = g_malloc0 (SOCKS5_AUTH_MSG_LEN);
       
   821       data->length = set_auth_msg (data->buffer,
       
   822                                    data->username,
       
   823                                    data->password,
       
   824                                    &error);
       
   825       data->offset = 0;
       
   826 
       
   827       if (data->length < 0) {
       
   828         complete_async_from_error (data, error);
       
   829         return;
       
   830       }
       
   831           
       
   832       do_write (auth_msg_write_cb, data);
       
   833     } else {
       
   834       send_connect_msg (data);
       
   835     }
       
   836   } else {
       
   837     do_read (nego_reply_read_cb, data);
       
   838   }
       
   839 }
       
   840 
       
   841 static void
       
   842 nego_reply_write_cb (GObject      *source,
       
   843                      GAsyncResult *res,
       
   844                      gpointer      user_data)
       
   845 {
       
   846   GError *error = NULL;
       
   847   ConnectAsyncData *data = user_data;
       
   848   gssize written;
       
   849 
       
   850   written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
       
   851                                           res, &error);
       
   852 
       
   853   if (written < 0) {
       
   854     complete_async_from_error (data, error);
       
   855     return;
       
   856   }
       
   857 
       
   858   data->offset += written;
       
   859 
       
   860   if (data->offset == data->length) {
       
   861     g_free (data->buffer);
       
   862 
       
   863     data->buffer = g_malloc0 (SOCKS5_CONN_MSG_LEN);
       
   864     data->length = 4;
       
   865     data->offset = 0;
       
   866 
       
   867     do_read (connect_msg_read_cb, data);
       
   868   } else {
       
   869     do_write (nego_reply_write_cb, data);
       
   870   }
       
   871 }
       
   872 
       
   873 static void
       
   874 auth_msg_write_cb (GObject      *source,
       
   875                    GAsyncResult *result,
       
   876                    gpointer      user_data)
       
   877 {
       
   878   GError *error = NULL;
       
   879   ConnectAsyncData *data = user_data;
       
   880   gssize written;
       
   881 
       
   882   written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
       
   883                                           result, &error);
       
   884   
       
   885   if (written < 0) {
       
   886     complete_async_from_error (data, error);
       
   887     return;
       
   888   }
       
   889 
       
   890   data->offset += written;
       
   891 
       
   892   if (data->offset == data->length) {
       
   893     g_free (data->buffer);
       
   894 
       
   895     data->buffer = g_malloc0 (SOCKS5_NEGO_REP_LEN);
       
   896     data->length = SOCKS5_NEGO_REP_LEN;
       
   897     data->offset = 0;
       
   898 
       
   899     do_read (auth_reply_read_cb, data);
       
   900   } else {
       
   901     do_write (auth_msg_write_cb, data);
       
   902   }
       
   903 }
       
   904 
       
   905 static void
       
   906 auth_reply_read_cb (GObject      *source,
       
   907                     GAsyncResult *result,
       
   908                     gpointer      user_data)
       
   909 {
       
   910   GError *error = NULL;
       
   911   ConnectAsyncData *data = user_data;
       
   912   gssize read;
       
   913 
       
   914   read = g_input_stream_read_finish (G_INPUT_STREAM (source),
       
   915                                      result, &error);
       
   916 
       
   917   if (read < 0) {
       
   918     complete_async_from_error (data, error);
       
   919     return;
       
   920   }
       
   921 
       
   922   data->offset += read;
       
   923 
       
   924   if (data->offset == data->length) {
       
   925     if (!check_auth_status (data->buffer, &error)) {
       
   926       complete_async_from_error (data, error);
       
   927       return;
       
   928     }
       
   929 
       
   930     send_connect_msg (data);
       
   931   } else {
       
   932     do_read (auth_reply_read_cb, data);
       
   933   }
       
   934 }
       
   935 
       
   936 static void
       
   937 send_connect_msg (ConnectAsyncData *data)
       
   938 {
       
   939   GError *error = NULL;
       
   940 
       
   941   g_free (data->buffer);
       
   942 
       
   943   data->buffer = g_malloc0 (SOCKS5_CONN_MSG_LEN);
       
   944   data->length = client_set_connect_msg (data->buffer,
       
   945                                          data->hostname,
       
   946                                          S5B_DST_PORT,
       
   947                                          &error);
       
   948   data->offset = 0;
       
   949   
       
   950   if (data->length < 0) {
       
   951     complete_async_from_error (data, error);
       
   952     return;
       
   953   }
       
   954 
       
   955   do_write (connect_msg_write_cb, data);
       
   956 }
       
   957 
       
   958 static void
       
   959 connect_msg_write_cb (GObject      *source,
       
   960                       GAsyncResult *result,
       
   961                       gpointer      user_data)
       
   962 {
       
   963   GError *error = NULL;
       
   964   ConnectAsyncData *data = user_data;
       
   965   gssize written;
       
   966 
       
   967   written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
       
   968                                           result, &error);
       
   969   
       
   970   if (written < 0) {
       
   971     complete_async_from_error (data, error);
       
   972     return;
       
   973   }
       
   974 
       
   975   data->offset += written;
       
   976 
       
   977   if (data->offset == data->length) {
       
   978     g_free (data->buffer);
       
   979 
       
   980     data->buffer = g_malloc0 (SOCKS5_CONN_REP_LEN);
       
   981     data->length = 4;
       
   982     data->offset = 0;
       
   983 
       
   984     do_read (connect_reply_read_cb, data);
       
   985   } else {
       
   986     do_write (connect_msg_write_cb, data);
       
   987   }
       
   988 }
       
   989 
       
   990 static void
       
   991 connect_msg_read_cb (GObject      *source,
       
   992                      GAsyncResult *result,
       
   993                      gpointer      user_data)
       
   994 {
       
   995   GError *error = NULL;
       
   996   ConnectAsyncData *data = user_data;
       
   997   gssize read;
       
   998 
       
   999   read = g_input_stream_read_finish (G_INPUT_STREAM (source),
       
  1000                                      result, &error);
       
  1001 
       
  1002   if (read < 0) {
       
  1003     complete_async_from_error (data, error);
       
  1004     return;
       
  1005   }
       
  1006 
       
  1007   data->offset += read;
       
  1008 
       
  1009   if (data->offset == data->length) {
       
  1010     gint atype;
       
  1011 
       
  1012     if (!server_parse_connect_msg (data->buffer, &atype, &error)) {
       
  1013       complete_async_from_error (data, error);
       
  1014       return;
       
  1015     }
       
  1016 
       
  1017     switch (atype) {
       
  1018       case SOCKS5_ATYP_DOMAINNAME:
       
  1019         data->length = 1;
       
  1020         data->offset = 0;
       
  1021         do_read (connect_addr_len_read_cb, data);
       
  1022         break;
       
  1023       default:
       
  1024         g_set_error_literal (&error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_FAILED,
       
  1025                              "This SOCKSv5 server implementation only support "
       
  1026                              "DOMAINNAME addresses.");
       
  1027         complete_async_from_error (data, error);
       
  1028         return;
       
  1029     }
       
  1030   } else {
       
  1031     do_read (connect_msg_read_cb, data);
       
  1032   }
       
  1033 }
       
  1034 
       
  1035 static void
       
  1036 connect_addr_len_read_cb (GObject      *source,
       
  1037                           GAsyncResult *result,
       
  1038                           gpointer      user_data)
       
  1039 {
       
  1040   GError *error = NULL;
       
  1041   ConnectAsyncData *data = user_data;
       
  1042   gssize read;
       
  1043 
       
  1044   read = g_input_stream_read_finish (G_INPUT_STREAM (source),
       
  1045                                      result, &error);
       
  1046 
       
  1047   if (read < 0) {
       
  1048     complete_async_from_error (data, error);
       
  1049     return;
       
  1050   }
       
  1051 
       
  1052   data->length = data->buffer[0] + 2;
       
  1053   data->offset = 0;
       
  1054 
       
  1055   do_read (connect_addr_read_cb, data);
       
  1056 }
       
  1057 
       
  1058 static void
       
  1059 connect_addr_read_cb (GObject      *source,
       
  1060                       GAsyncResult *result,
       
  1061                       gpointer      user_data)
       
  1062 {
       
  1063   GError *error = NULL;
       
  1064   ConnectAsyncData *data = user_data;
       
  1065   gssize read;
       
  1066 
       
  1067   read = g_input_stream_read_finish (G_INPUT_STREAM (source),
       
  1068                                      result, &error);
       
  1069 
       
  1070   if (read < 0) {
       
  1071     complete_async_from_error (data, error);
       
  1072     return;
       
  1073   }
       
  1074 
       
  1075   data->offset += read;
       
  1076 
       
  1077   if (data->offset == data->length) {
       
  1078     gchar *hostname = g_strndup ((gchar *)data->buffer, data->length-2);
       
  1079     if (!g_strcmp0 (data->hostname, hostname)) {
       
  1080       guint8 atype;
       
  1081       GInetAddress *address = g_inet_socket_address_get_address (data->external_address);
       
  1082       g_free(data->buffer);
       
  1083       if (g_inet_address_get_family (address) == G_SOCKET_FAMILY_IPV4)
       
  1084         atype = SOCKS5_ATYP_IPV4;
       
  1085       else
       
  1086         atype = SOCKS5_ATYP_IPV6;
       
  1087 
       
  1088       data->buffer = g_malloc0 (SOCKS5_CONN_REP_LEN);
       
  1089       data->length = server_set_connect_reply (data->buffer, SOCKS5_REP_SUCCEEDED,
       
  1090                                                atype, g_inet_address_to_bytes(address),
       
  1091                                                g_inet_socket_address_get_port(data->external_address));
       
  1092       data->offset = 0;
       
  1093 
       
  1094       do_write (connect_reply_write_cb, data);
       
  1095 	} else {
       
  1096       // wrong hostname ! we should respond: X'02' connection not allowed by ruleset
       
  1097       g_set_error_literal (&error, S5B_SOCKS5_ERROR, S5B_SOCKS5_ERROR_NOT_ALLOWED,
       
  1098                            "The client gave an invalid hostname");
       
  1099       complete_async_from_error (data, error);
       
  1100 	}
       
  1101 	g_free (hostname);
       
  1102   } else {
       
  1103     do_read (connect_reply_read_cb, data);
       
  1104   }
       
  1105 }
       
  1106 
       
  1107 static void
       
  1108 connect_reply_write_cb (GObject      *source,
       
  1109                         GAsyncResult *result,
       
  1110                         gpointer      user_data)
       
  1111 {
       
  1112   GError *error = NULL;
       
  1113   ConnectAsyncData *data = user_data;
       
  1114   gssize written;
       
  1115 
       
  1116   written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
       
  1117                                           result, &error);
       
  1118   
       
  1119   if (written < 0) {
       
  1120     complete_async_from_error (data, error);
       
  1121     return;
       
  1122   }
       
  1123 
       
  1124   data->offset += written;
       
  1125 
       
  1126   if (data->offset == data->length) {
       
  1127     GSimpleAsyncResult *simple = data->simple;
       
  1128     g_simple_async_result_complete (simple);
       
  1129     g_object_unref (simple);
       
  1130   } else {
       
  1131     do_write (connect_reply_write_cb, data);
       
  1132   }
       
  1133 }
       
  1134 
       
  1135 static void
       
  1136 connect_reply_read_cb (GObject       *source,
       
  1137                        GAsyncResult  *result,
       
  1138                        gpointer       user_data)
       
  1139 {
       
  1140   GError *error = NULL;
       
  1141   ConnectAsyncData *data = user_data;
       
  1142   gssize read;
       
  1143 
       
  1144   read = g_input_stream_read_finish (G_INPUT_STREAM (source),
       
  1145                                      result, &error);
       
  1146 
       
  1147   if (read < 0) {
       
  1148     complete_async_from_error (data, error);
       
  1149     return;
       
  1150   }
       
  1151 
       
  1152   data->offset += read;
       
  1153 
       
  1154   if (data->offset == data->length) {
       
  1155     gint atype;
       
  1156 
       
  1157     if (!client_parse_connect_reply (data->buffer, &atype, &error)) {
       
  1158       complete_async_from_error (data, error);
       
  1159       return;
       
  1160     }
       
  1161 
       
  1162     switch (atype) {
       
  1163       case SOCKS5_ATYP_IPV4:
       
  1164         data->length = 6;
       
  1165         data->offset = 0;
       
  1166         do_read (connect_reply_addr_read_cb, data);
       
  1167             break;
       
  1168 
       
  1169       case SOCKS5_ATYP_IPV6:
       
  1170         data->length = 18;
       
  1171         data->offset = 0;
       
  1172         do_read (connect_reply_addr_read_cb, data);
       
  1173         break;
       
  1174 
       
  1175       case SOCKS5_ATYP_DOMAINNAME:
       
  1176         data->length = 1;
       
  1177         data->offset = 0;
       
  1178         do_read (connect_reply_addr_len_read_cb, data);
       
  1179         break;
       
  1180     }
       
  1181   } else {
       
  1182     do_read (connect_reply_read_cb, data);
       
  1183   }
       
  1184 }
       
  1185 
       
  1186 static void
       
  1187 connect_reply_addr_len_read_cb (GObject      *source,
       
  1188                                 GAsyncResult *result,
       
  1189                                 gpointer      user_data)
       
  1190 {
       
  1191   GError *error = NULL;
       
  1192   ConnectAsyncData *data = user_data;
       
  1193   gssize read;
       
  1194 
       
  1195   read = g_input_stream_read_finish (G_INPUT_STREAM (source),
       
  1196                                      result, &error);
       
  1197 
       
  1198   if (read < 0) {
       
  1199     complete_async_from_error (data, error);
       
  1200     return;
       
  1201   }
       
  1202 
       
  1203   data->length = data->buffer[0] + 2;
       
  1204   data->offset = 0;
       
  1205 
       
  1206   do_read (connect_addr_read_cb, data);
       
  1207 }
       
  1208 
       
  1209 static void
       
  1210 connect_reply_addr_read_cb (GObject      *source,
       
  1211                             GAsyncResult *result,
       
  1212                             gpointer      user_data)
       
  1213 {
       
  1214   GError *error = NULL;
       
  1215   ConnectAsyncData *data = user_data;
       
  1216   gssize read;
       
  1217 
       
  1218   read = g_input_stream_read_finish (G_INPUT_STREAM (source),
       
  1219                                      result, &error);
       
  1220 
       
  1221   if (read < 0) {
       
  1222     complete_async_from_error (data, error);
       
  1223     return;
       
  1224   }
       
  1225 
       
  1226   data->offset += read;
       
  1227 
       
  1228   if (data->offset == data->length) {
       
  1229     GSimpleAsyncResult *simple = data->simple;
       
  1230     g_simple_async_result_complete (simple);
       
  1231     g_object_unref (simple);
       
  1232   } else {
       
  1233     do_read (connect_reply_read_cb, data);
       
  1234   }
       
  1235 }
       
  1236 
       
  1237 GIOStream *
       
  1238 g_socks5_proxy_connect_finish (GAsyncResult *result,
       
  1239                                GError      **error)
       
  1240 {
       
  1241   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
       
  1242   ConnectAsyncData *data = g_simple_async_result_get_op_res_gpointer (simple);
       
  1243 
       
  1244   if (g_simple_async_result_propagate_error (simple, error))
       
  1245     return NULL;
       
  1246 
       
  1247   return g_object_ref (data->io_stream);
       
  1248 }
       
  1249 
       
  1250 GQuark s5b_proxy_error_quark(void)
       
  1251 {
       
  1252   return g_quark_from_string("S5B_SOCKS5_ERROR");
       
  1253 }