Merge remote-tracking 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_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
139                                _("Username is too long for SOCKSv4 protocol"));
140           return -1;
141         }
142
143       memcpy (msg + len, username, user_len);
144       len += user_len;
145     }
146
147   msg[len++] = '\0';
148
149   if (!is_ip)
150     {
151       gsize host_len = strlen (hostname);
152
153       if (host_len > SOCKS4_MAX_LEN)
154         {
155           g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
156                        _("Hostname '%s' is too long for SOCKSv4 protocol"),
157                        hostname);
158           return -1;
159         }
160
161       memcpy (msg + len, hostname, host_len);
162       len += host_len;
163       msg[len++] = '\0';
164     }
165
166   return len;
167 }
168
169 /*
170  * +----+----+----+----+----+----+----+----+
171  * | VN | CD | DSTPORT |      DSTIP        |
172  * +----+----+----+----+----+----+----+----+
173  *    1    1      2              4
174  */
175 #define SOCKS4_CONN_REP_LEN       8
176 static gboolean
177 parse_connect_reply (const guint8 *data, GError **error)
178 {
179   if (data[0] != SOCKS4_REP_VERSION)
180     {
181       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
182                            _("The server is not a SOCKSv4 proxy server."));
183       return FALSE;
184     }
185
186   if (data[1] != SOCKS4_REP_GRANTED)
187     {
188       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
189                            _("Connection through SOCKSv4 server was rejected"));
190       return FALSE;
191     }
192
193   return TRUE;
194 }
195
196 static GIOStream *
197 g_socks4a_proxy_connect (GProxy            *proxy,
198                          GIOStream         *io_stream,
199                          GProxyAddress     *proxy_address,
200                          GCancellable      *cancellable,
201                          GError           **error)
202 {
203   GInputStream *in;
204   GOutputStream *out;
205   const gchar *hostname;
206   guint16 port;
207   const gchar *username;
208
209   hostname = g_proxy_address_get_destination_hostname (proxy_address);
210   port = g_proxy_address_get_destination_port (proxy_address);
211   username = g_proxy_address_get_username (proxy_address);
212
213   in = g_io_stream_get_input_stream (io_stream);
214   out = g_io_stream_get_output_stream (io_stream);
215
216   /* Send SOCKS4 connection request */
217     {
218       guint8 msg[SOCKS4_CONN_MSG_LEN];
219       gint len;
220       
221       len = set_connect_msg (msg, hostname, port, username, error);
222
223       if (len < 0)
224         goto error;
225
226       if (!g_output_stream_write_all (out, msg, len, NULL,
227                                       cancellable, error))
228         goto error;
229     }
230
231   /* Read SOCKS4 response */
232     {
233       guint8 data[SOCKS4_CONN_REP_LEN];
234
235       if (!g_input_stream_read_all (in, data, SOCKS4_CONN_REP_LEN, NULL,
236                                     cancellable, error))
237         goto error;
238
239       if (!parse_connect_reply (data, error))
240         goto error;
241     }
242
243   return g_object_ref (io_stream);
244
245 error:
246   return NULL;
247 }
248
249
250 typedef struct
251 {
252   GSimpleAsyncResult *simple;
253   GIOStream *io_stream;
254   GProxyAddress *proxy_address;
255   GCancellable *cancellable;
256
257   /* For connecting */
258   guint8 *buffer;
259   gssize length;
260   gssize offset;
261
262 } ConnectAsyncData;
263
264 static void connect_msg_write_cb      (GObject          *source,
265                                        GAsyncResult     *result,
266                                        gpointer          user_data);
267 static void connect_reply_read_cb     (GObject          *source,
268                                        GAsyncResult     *result,
269                                        gpointer          user_data);
270
271 static void
272 free_connect_data (ConnectAsyncData *data)
273 {
274   if (data->io_stream)
275     g_object_unref (data->io_stream);
276   
277   if (data->proxy_address)
278     g_object_unref (data->proxy_address);
279
280   if (data->cancellable)
281     g_object_unref (data->cancellable);
282
283   g_slice_free (ConnectAsyncData, data);
284 }
285
286 static void
287 complete_async_from_error (ConnectAsyncData *data, GError *error)
288 {
289   GSimpleAsyncResult *simple = data->simple;
290   g_simple_async_result_take_error (data->simple, error);
291   g_simple_async_result_set_op_res_gpointer (simple, NULL, NULL);
292   g_simple_async_result_complete (simple);
293   g_object_unref (simple);
294 }
295
296 static void
297 do_read (GAsyncReadyCallback callback, ConnectAsyncData *data)
298 {
299    GInputStream *in;
300    in = g_io_stream_get_input_stream (data->io_stream);
301    g_input_stream_read_async (in,
302                               data->buffer + data->offset,
303                               data->length - data->offset,
304                               G_PRIORITY_DEFAULT, data->cancellable,
305                               callback, data);
306 }
307
308 static void
309 do_write (GAsyncReadyCallback callback, ConnectAsyncData *data)
310 {
311   GOutputStream *out;
312   out = g_io_stream_get_output_stream (data->io_stream);
313   g_output_stream_write_async (out,
314                                data->buffer + data->offset,
315                                data->length - data->offset,
316                                G_PRIORITY_DEFAULT, data->cancellable,
317                                callback, data);
318 }
319
320
321
322 static void
323 g_socks4a_proxy_connect_async (GProxy               *proxy,
324                                GIOStream            *io_stream,
325                                GProxyAddress        *proxy_address,
326                                GCancellable         *cancellable,
327                                GAsyncReadyCallback   callback,
328                                gpointer              user_data)
329 {
330   GError *error = NULL;
331   GSimpleAsyncResult *simple;
332   ConnectAsyncData *data;
333   const gchar *hostname;
334   guint16 port;
335   const gchar *username;
336
337   simple = g_simple_async_result_new (G_OBJECT (proxy),
338                                       callback, user_data,
339                                       g_socks4a_proxy_connect_async);
340
341   data = g_slice_new0 (ConnectAsyncData);
342
343   data->simple = simple;
344   data->io_stream = g_object_ref (io_stream);
345
346   if (cancellable)
347     data->cancellable = g_object_ref (cancellable);
348
349   g_simple_async_result_set_op_res_gpointer (simple, data, 
350                                              (GDestroyNotify) free_connect_data);
351
352   hostname = g_proxy_address_get_destination_hostname (proxy_address);
353   port = g_proxy_address_get_destination_port (proxy_address);
354   username = g_proxy_address_get_username (proxy_address); 
355
356   data->buffer = g_malloc0 (SOCKS4_CONN_MSG_LEN);
357   data->length = set_connect_msg (data->buffer,
358                                   hostname, port, username,
359                                   &error);
360   data->offset = 0;
361
362   if (data->length < 0)
363     {
364       g_simple_async_result_take_error (data->simple, error);
365       g_simple_async_result_set_op_res_gpointer (simple, NULL, NULL);
366       g_simple_async_result_complete_in_idle (simple);
367       g_object_unref (simple);
368     }
369   else
370     {
371       do_write (connect_msg_write_cb, data);
372     }
373 }
374
375 static void
376 connect_msg_write_cb (GObject      *source,
377                       GAsyncResult *result,
378                       gpointer      user_data)
379 {
380   GError *error = NULL;
381   ConnectAsyncData *data = user_data;
382   gssize written;
383
384   written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
385                                           result, &error);
386   
387   if (written < 0)
388     {
389       complete_async_from_error (data, error);
390       return;
391     }
392
393   data->offset += written;
394
395   if (data->offset == data->length)
396     {
397       g_free (data->buffer);
398
399       data->buffer = g_malloc0 (SOCKS4_CONN_REP_LEN);
400       data->length = SOCKS4_CONN_REP_LEN;
401       data->offset = 0;
402
403       do_read (connect_reply_read_cb, data);
404     }
405   else
406     {
407       do_write (connect_msg_write_cb, data);
408     }
409 }
410
411 static void
412 connect_reply_read_cb (GObject       *source,
413                        GAsyncResult  *result,
414                        gpointer       user_data)
415 {
416   GError *error = NULL;
417   ConnectAsyncData *data = user_data;
418   gssize read;
419
420   read = g_input_stream_read_finish (G_INPUT_STREAM (source),
421                                      result, &error);
422
423   if (read < 0)
424     {
425       complete_async_from_error (data, error);
426       return;
427     }
428
429   data->offset += read;
430
431   if (data->offset == data->length)
432     {
433       if (!parse_connect_reply (data->buffer, &error))
434         {
435           complete_async_from_error (data, error);
436         }
437       else
438         {
439           GSimpleAsyncResult *simple = data->simple;
440           g_simple_async_result_complete (simple);
441           g_object_unref (simple);
442         }
443     }
444   else
445     {
446       do_read (connect_reply_read_cb, data);
447     }
448 }
449
450 static GIOStream *g_socks4a_proxy_connect_finish (GProxy       *proxy,
451                                                   GAsyncResult *result,
452                                                   GError      **error);
453
454 static GIOStream *
455 g_socks4a_proxy_connect_finish (GProxy       *proxy,
456                                 GAsyncResult *result,
457                                 GError      **error)
458 {
459   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
460   ConnectAsyncData *data = g_simple_async_result_get_op_res_gpointer (simple);
461
462   if (g_simple_async_result_propagate_error (simple, error))
463     return NULL;
464
465   return g_object_ref (data->io_stream);
466 }
467
468 static gboolean
469 g_socks4a_proxy_supports_hostname (GProxy *proxy)
470 {
471   return G_SOCKS4A_PROXY (proxy)->supports_hostname;
472 }
473
474 static void
475 g_socks4a_proxy_class_init (GSocks4aProxyClass *class)
476 {
477   GObjectClass *object_class;
478
479   object_class = (GObjectClass *) class;
480   object_class->finalize = g_socks4a_proxy_finalize;
481 }
482
483 static void
484 g_socks4a_proxy_iface_init (GProxyInterface *proxy_iface)
485 {
486   proxy_iface->connect  = g_socks4a_proxy_connect;
487   proxy_iface->connect_async = g_socks4a_proxy_connect_async;
488   proxy_iface->connect_finish = g_socks4a_proxy_connect_finish;
489   proxy_iface->supports_hostname = g_socks4a_proxy_supports_hostname;
490 }