Merge remote branch 'gvdb/master'
[platform/upstream/glib.git] / gio / gsocks4aproxy.c
1  /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) 2010 Collabora, Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
21  */
22
23 #include "config.h"
24
25 #include "gsocks4aproxy.h"
26
27 #include <string.h>
28
29 #include "gasyncresult.h"
30 #include "giomodule.h"
31 #include "giomodule-priv.h"
32 #include "giostream.h"
33 #include "ginetaddress.h"
34 #include "ginputstream.h"
35 #include "glibintl.h"
36 #include "goutputstream.h"
37 #include "gproxy.h"
38 #include "gproxyaddress.h"
39 #include "gsimpleasyncresult.h"
40
41 #define SOCKS4_VERSION            4
42
43 #define SOCKS4_CMD_CONNECT        1
44 #define SOCKS4_CMD_BIND           2
45
46 #define SOCKS4_MAX_LEN            255
47
48 #define SOCKS4_REP_VERSION        0
49 #define SOCKS4_REP_GRANTED        90
50 #define SOCKS4_REP_REJECTED       91
51 #define SOCKS4_REP_NO_IDENT       92
52 #define SOCKS4_REP_BAD_IDENT      93
53
54 static void g_socks4a_proxy_iface_init (GProxyInterface *proxy_iface);
55
56 #define g_socks4a_proxy_get_type _g_socks4a_proxy_get_type
57 G_DEFINE_TYPE_WITH_CODE (GSocks4aProxy, g_socks4a_proxy, G_TYPE_OBJECT,
58                          G_IMPLEMENT_INTERFACE (G_TYPE_PROXY,
59                                                 g_socks4a_proxy_iface_init)
60                          _g_io_modules_ensure_extension_points_registered ();
61                          g_io_extension_point_implement (G_PROXY_EXTENSION_POINT_NAME,
62                                                          g_define_type_id,
63                                                          "socks4a",
64                                                          0))
65
66 static void
67 g_socks4a_proxy_finalize (GObject *object)
68 {
69   /* must chain up */
70   G_OBJECT_CLASS (g_socks4a_proxy_parent_class)->finalize (object);
71 }
72
73 static void
74 g_socks4a_proxy_init (GSocks4aProxy *proxy)
75 {
76   proxy->supports_hostname = TRUE;
77 }
78
79 /*                                                             |-> SOCKSv4a only
80  * +----+----+----+----+----+----+----+----+----+----+....+----+------+....+------+
81  * | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL| HOST |    | NULL |
82  * +----+----+----+----+----+----+----+----+----+----+....+----+------+....+------+
83  *    1    1      2              4           variable       1    variable
84  */
85 #define SOCKS4_CONN_MSG_LEN         (9 + SOCKS4_MAX_LEN * 2)
86 static gint
87 set_connect_msg (guint8      *msg,
88                  const gchar *hostname,
89                  guint16      port,
90                  const char  *username,
91                  GError     **error)
92 {
93   GInetAddress *addr;
94   guint len = 0;
95   gsize addr_len;
96   gboolean is_ip;
97   const gchar *ip;
98
99   msg[len++] = SOCKS4_VERSION;
100   msg[len++] = SOCKS4_CMD_CONNECT;
101
102     {
103       guint16 hp = g_htons (port);
104       memcpy (msg + len, &hp, 2);
105       len += 2;
106     }
107
108   is_ip = g_hostname_is_ip_address (hostname);
109
110   if (is_ip)
111     ip = hostname;
112   else
113     ip = "0.0.0.1";
114     
115   addr = g_inet_address_new_from_string (ip);
116   addr_len = g_inet_address_get_native_size (addr);
117
118   if (addr_len != 4)
119     {
120       g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
121                   _("SOCKSv4 does not support IPv6 address '%s'"),
122                   ip);
123       g_object_unref (addr);
124       return -1;
125     }
126
127   memcpy (msg + len, g_inet_address_to_bytes (addr), addr_len);
128   len += addr_len;
129
130   g_object_unref (addr);
131
132   if (username)
133     {
134       gsize user_len = strlen (username);
135
136       if (user_len > SOCKS4_MAX_LEN)
137         {
138           g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
139                        _("SOCKSv4 implementation limits username to %i characters"),
140                        SOCKS4_MAX_LEN);
141           return -1;
142         }
143
144       memcpy (msg + len, username, user_len);
145       len += user_len;
146     }
147
148   msg[len++] = '\0';
149
150   if (!is_ip)
151     {
152       gsize host_len = strlen (hostname);
153
154       if (host_len > SOCKS4_MAX_LEN)
155         {
156           g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
157                        _("SOCKSv4a implementation limits hostname to %i characters"),
158                        SOCKS4_MAX_LEN);
159           return -1;
160         }
161
162       memcpy (msg + len, hostname, host_len);
163       len += host_len;
164       msg[len++] = '\0';
165     }
166
167   return len;
168 }
169
170 /*
171  * +----+----+----+----+----+----+----+----+
172  * | VN | CD | DSTPORT |      DSTIP        |
173  * +----+----+----+----+----+----+----+----+
174  *    1    1      2              4
175  */
176 #define SOCKS4_CONN_REP_LEN       8
177 static gboolean
178 parse_connect_reply (const guint8 *data, GError **error)
179 {
180   if (data[0] != SOCKS4_REP_VERSION)
181     {
182       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
183                            _("The server is not a SOCKSv4 proxy server."));
184       return FALSE;
185     }
186
187   if (data[1] != SOCKS4_REP_GRANTED)
188     {
189       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
190                            _("Connection through SOCKSv4 server was rejected"));
191       return FALSE;
192     }
193
194   return TRUE;
195 }
196
197 static GIOStream *
198 g_socks4a_proxy_connect (GProxy            *proxy,
199                          GIOStream         *io_stream,
200                          GProxyAddress     *proxy_address,
201                          GCancellable      *cancellable,
202                          GError           **error)
203 {
204   GInputStream *in;
205   GOutputStream *out;
206   const gchar *hostname;
207   guint16 port;
208   const gchar *username;
209
210   hostname = g_proxy_address_get_destination_hostname (proxy_address);
211   port = g_proxy_address_get_destination_port (proxy_address);
212   username = g_proxy_address_get_username (proxy_address);
213
214   in = g_io_stream_get_input_stream (io_stream);
215   out = g_io_stream_get_output_stream (io_stream);
216
217   /* Send SOCKS4 connection request */
218     {
219       guint8 msg[SOCKS4_CONN_MSG_LEN];
220       gint len;
221       
222       len = set_connect_msg (msg, hostname, port, username, error);
223
224       if (len < 0)
225         goto error;
226
227       if (!g_output_stream_write_all (out, msg, len, NULL,
228                                       cancellable, error))
229         goto error;
230     }
231
232   /* Read SOCKS4 response */
233     {
234       guint8 data[SOCKS4_CONN_REP_LEN];
235
236       if (!g_input_stream_read_all (in, data, SOCKS4_CONN_REP_LEN, NULL,
237                                     cancellable, error))
238         goto error;
239
240       if (!parse_connect_reply (data, error))
241         goto error;
242     }
243
244   return g_object_ref (io_stream);
245
246 error:
247   return NULL;
248 }
249
250
251 typedef struct
252 {
253   GSimpleAsyncResult *simple;
254   GIOStream *io_stream;
255   GProxyAddress *proxy_address;
256   GCancellable *cancellable;
257
258   /* For connecting */
259   guint8 *buffer;
260   gssize length;
261   gssize offset;
262
263 } ConnectAsyncData;
264
265 static void connect_msg_write_cb      (GObject          *source,
266                                        GAsyncResult     *result,
267                                        gpointer          user_data);
268 static void connect_reply_read_cb     (GObject          *source,
269                                        GAsyncResult     *result,
270                                        gpointer          user_data);
271
272 static void
273 free_connect_data (ConnectAsyncData *data)
274 {
275   if (data->io_stream)
276     g_object_unref (data->io_stream);
277   
278   if (data->proxy_address)
279     g_object_unref (data->proxy_address);
280
281   if (data->cancellable)
282     g_object_unref (data->cancellable);
283
284   g_slice_free (ConnectAsyncData, data);
285 }
286
287 static void
288 complete_async_from_error (ConnectAsyncData *data, GError *error)
289 {
290   GSimpleAsyncResult *simple = data->simple;
291   g_simple_async_result_set_from_error (data->simple,
292                                         error);
293   g_error_free (error);
294   g_simple_async_result_set_op_res_gpointer (simple, NULL, NULL);
295   g_simple_async_result_complete (simple);
296   g_object_unref (simple);
297 }
298
299 static void
300 do_read (GAsyncReadyCallback callback, ConnectAsyncData *data)
301 {
302    GInputStream *in;
303    in = g_io_stream_get_input_stream (data->io_stream);
304    g_input_stream_read_async (in,
305                               data->buffer + data->offset,
306                               data->length - data->offset,
307                               G_PRIORITY_DEFAULT, data->cancellable,
308                               callback, data);
309 }
310
311 static void
312 do_write (GAsyncReadyCallback callback, ConnectAsyncData *data)
313 {
314   GOutputStream *out;
315   out = g_io_stream_get_output_stream (data->io_stream);
316   g_output_stream_write_async (out,
317                                data->buffer + data->offset,
318                                data->length - data->offset,
319                                G_PRIORITY_DEFAULT, data->cancellable,
320                                callback, data);
321 }
322
323
324
325 static void
326 g_socks4a_proxy_connect_async (GProxy               *proxy,
327                                GIOStream            *io_stream,
328                                GProxyAddress        *proxy_address,
329                                GCancellable         *cancellable,
330                                GAsyncReadyCallback   callback,
331                                gpointer              user_data)
332 {
333   GError *error = NULL;
334   GSimpleAsyncResult *simple;
335   ConnectAsyncData *data;
336   const gchar *hostname;
337   guint16 port;
338   const gchar *username;
339
340   simple = g_simple_async_result_new (G_OBJECT (proxy),
341                                       callback, user_data,
342                                       g_socks4a_proxy_connect_async);
343
344   data = g_slice_new0 (ConnectAsyncData);
345
346   data->simple = simple;
347   data->io_stream = g_object_ref (io_stream);
348
349   if (cancellable)
350     data->cancellable = g_object_ref (cancellable);
351
352   g_simple_async_result_set_op_res_gpointer (simple, data, 
353                                              (GDestroyNotify) free_connect_data);
354
355   hostname = g_proxy_address_get_destination_hostname (proxy_address);
356   port = g_proxy_address_get_destination_port (proxy_address);
357   username = g_proxy_address_get_username (proxy_address); 
358
359   data->buffer = g_malloc0 (SOCKS4_CONN_MSG_LEN);
360   data->length = set_connect_msg (data->buffer,
361                                   hostname, port, username,
362                                   &error);
363   data->offset = 0;
364
365   if (data->length < 0)
366     {
367       g_simple_async_result_set_from_error (data->simple,
368                                             error);
369       g_error_free (error);
370       g_simple_async_result_set_op_res_gpointer (simple, NULL, NULL);
371       g_simple_async_result_complete_in_idle (simple);
372       g_object_unref (simple);
373     }
374   else
375     {
376       do_write (connect_msg_write_cb, data);
377     }
378 }
379
380 static void
381 connect_msg_write_cb (GObject      *source,
382                       GAsyncResult *result,
383                       gpointer      user_data)
384 {
385   GError *error = NULL;
386   ConnectAsyncData *data = user_data;
387   gssize written;
388
389   written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
390                                           result, &error);
391   
392   if (written < 0)
393     {
394       complete_async_from_error (data, error);
395       return;
396     }
397
398   data->offset += written;
399
400   if (data->offset == data->length)
401     {
402       g_free (data->buffer);
403
404       data->buffer = g_malloc0 (SOCKS4_CONN_REP_LEN);
405       data->length = SOCKS4_CONN_REP_LEN;
406       data->offset = 0;
407
408       do_read (connect_reply_read_cb, data);
409     }
410   else
411     {
412       do_write (connect_msg_write_cb, data);
413     }
414 }
415
416 static void
417 connect_reply_read_cb (GObject       *source,
418                        GAsyncResult  *result,
419                        gpointer       user_data)
420 {
421   GError *error = NULL;
422   ConnectAsyncData *data = user_data;
423   gssize read;
424
425   read = g_input_stream_read_finish (G_INPUT_STREAM (source),
426                                      result, &error);
427
428   if (read < 0)
429     {
430       complete_async_from_error (data, error);
431       return;
432     }
433
434   data->offset += read;
435
436   if (data->offset == data->length)
437     {
438       if (!parse_connect_reply (data->buffer, &error))
439         {
440           complete_async_from_error (data, error);
441         }
442       else
443         {
444           GSimpleAsyncResult *simple = data->simple;
445           g_simple_async_result_complete (simple);
446           g_object_unref (simple);
447         }
448     }
449   else
450     {
451       do_read (connect_reply_read_cb, data);
452     }
453 }
454
455 static GIOStream *g_socks4a_proxy_connect_finish (GProxy       *proxy,
456                                                   GAsyncResult *result,
457                                                   GError      **error);
458
459 static GIOStream *
460 g_socks4a_proxy_connect_finish (GProxy       *proxy,
461                                 GAsyncResult *result,
462                                 GError      **error)
463 {
464   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
465   ConnectAsyncData *data = g_simple_async_result_get_op_res_gpointer (simple);
466
467   if (g_simple_async_result_propagate_error (simple, error))
468     return NULL;
469
470   return g_object_ref (data->io_stream);
471 }
472
473 static gboolean
474 g_socks4a_proxy_supports_hostname (GProxy *proxy)
475 {
476   return G_SOCKS4A_PROXY (proxy)->supports_hostname;
477 }
478
479 static void
480 g_socks4a_proxy_class_init (GSocks4aProxyClass *class)
481 {
482   GObjectClass *object_class;
483
484   object_class = (GObjectClass *) class;
485   object_class->finalize = g_socks4a_proxy_finalize;
486 }
487
488 static void
489 g_socks4a_proxy_iface_init (GProxyInterface *proxy_iface)
490 {
491   proxy_iface->connect  = g_socks4a_proxy_connect;
492   proxy_iface->connect_async = g_socks4a_proxy_connect_async;
493   proxy_iface->connect_finish = g_socks4a_proxy_connect_finish;
494   proxy_iface->supports_hostname = g_socks4a_proxy_supports_hostname;
495 }