g_unix_set_fd_nonblocking: New API to control file descriptor blocking state
[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_take_error (data->simple, error);
292   g_simple_async_result_set_op_res_gpointer (simple, NULL, NULL);
293   g_simple_async_result_complete (simple);
294   g_object_unref (simple);
295 }
296
297 static void
298 do_read (GAsyncReadyCallback callback, ConnectAsyncData *data)
299 {
300    GInputStream *in;
301    in = g_io_stream_get_input_stream (data->io_stream);
302    g_input_stream_read_async (in,
303                               data->buffer + data->offset,
304                               data->length - data->offset,
305                               G_PRIORITY_DEFAULT, data->cancellable,
306                               callback, data);
307 }
308
309 static void
310 do_write (GAsyncReadyCallback callback, ConnectAsyncData *data)
311 {
312   GOutputStream *out;
313   out = g_io_stream_get_output_stream (data->io_stream);
314   g_output_stream_write_async (out,
315                                data->buffer + data->offset,
316                                data->length - data->offset,
317                                G_PRIORITY_DEFAULT, data->cancellable,
318                                callback, data);
319 }
320
321
322
323 static void
324 g_socks4a_proxy_connect_async (GProxy               *proxy,
325                                GIOStream            *io_stream,
326                                GProxyAddress        *proxy_address,
327                                GCancellable         *cancellable,
328                                GAsyncReadyCallback   callback,
329                                gpointer              user_data)
330 {
331   GError *error = NULL;
332   GSimpleAsyncResult *simple;
333   ConnectAsyncData *data;
334   const gchar *hostname;
335   guint16 port;
336   const gchar *username;
337
338   simple = g_simple_async_result_new (G_OBJECT (proxy),
339                                       callback, user_data,
340                                       g_socks4a_proxy_connect_async);
341
342   data = g_slice_new0 (ConnectAsyncData);
343
344   data->simple = simple;
345   data->io_stream = g_object_ref (io_stream);
346
347   if (cancellable)
348     data->cancellable = g_object_ref (cancellable);
349
350   g_simple_async_result_set_op_res_gpointer (simple, data, 
351                                              (GDestroyNotify) free_connect_data);
352
353   hostname = g_proxy_address_get_destination_hostname (proxy_address);
354   port = g_proxy_address_get_destination_port (proxy_address);
355   username = g_proxy_address_get_username (proxy_address); 
356
357   data->buffer = g_malloc0 (SOCKS4_CONN_MSG_LEN);
358   data->length = set_connect_msg (data->buffer,
359                                   hostname, port, username,
360                                   &error);
361   data->offset = 0;
362
363   if (data->length < 0)
364     {
365       g_simple_async_result_take_error (data->simple, error);
366       g_simple_async_result_set_op_res_gpointer (simple, NULL, NULL);
367       g_simple_async_result_complete_in_idle (simple);
368       g_object_unref (simple);
369     }
370   else
371     {
372       do_write (connect_msg_write_cb, data);
373     }
374 }
375
376 static void
377 connect_msg_write_cb (GObject      *source,
378                       GAsyncResult *result,
379                       gpointer      user_data)
380 {
381   GError *error = NULL;
382   ConnectAsyncData *data = user_data;
383   gssize written;
384
385   written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
386                                           result, &error);
387   
388   if (written < 0)
389     {
390       complete_async_from_error (data, error);
391       return;
392     }
393
394   data->offset += written;
395
396   if (data->offset == data->length)
397     {
398       g_free (data->buffer);
399
400       data->buffer = g_malloc0 (SOCKS4_CONN_REP_LEN);
401       data->length = SOCKS4_CONN_REP_LEN;
402       data->offset = 0;
403
404       do_read (connect_reply_read_cb, data);
405     }
406   else
407     {
408       do_write (connect_msg_write_cb, data);
409     }
410 }
411
412 static void
413 connect_reply_read_cb (GObject       *source,
414                        GAsyncResult  *result,
415                        gpointer       user_data)
416 {
417   GError *error = NULL;
418   ConnectAsyncData *data = user_data;
419   gssize read;
420
421   read = g_input_stream_read_finish (G_INPUT_STREAM (source),
422                                      result, &error);
423
424   if (read < 0)
425     {
426       complete_async_from_error (data, error);
427       return;
428     }
429
430   data->offset += read;
431
432   if (data->offset == data->length)
433     {
434       if (!parse_connect_reply (data->buffer, &error))
435         {
436           complete_async_from_error (data, error);
437         }
438       else
439         {
440           GSimpleAsyncResult *simple = data->simple;
441           g_simple_async_result_complete (simple);
442           g_object_unref (simple);
443         }
444     }
445   else
446     {
447       do_read (connect_reply_read_cb, data);
448     }
449 }
450
451 static GIOStream *g_socks4a_proxy_connect_finish (GProxy       *proxy,
452                                                   GAsyncResult *result,
453                                                   GError      **error);
454
455 static GIOStream *
456 g_socks4a_proxy_connect_finish (GProxy       *proxy,
457                                 GAsyncResult *result,
458                                 GError      **error)
459 {
460   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
461   ConnectAsyncData *data = g_simple_async_result_get_op_res_gpointer (simple);
462
463   if (g_simple_async_result_propagate_error (simple, error))
464     return NULL;
465
466   return g_object_ref (data->io_stream);
467 }
468
469 static gboolean
470 g_socks4a_proxy_supports_hostname (GProxy *proxy)
471 {
472   return G_SOCKS4A_PROXY (proxy)->supports_hostname;
473 }
474
475 static void
476 g_socks4a_proxy_class_init (GSocks4aProxyClass *class)
477 {
478   GObjectClass *object_class;
479
480   object_class = (GObjectClass *) class;
481   object_class->finalize = g_socks4a_proxy_finalize;
482 }
483
484 static void
485 g_socks4a_proxy_iface_init (GProxyInterface *proxy_iface)
486 {
487   proxy_iface->connect  = g_socks4a_proxy_connect;
488   proxy_iface->connect_async = g_socks4a_proxy_connect_async;
489   proxy_iface->connect_finish = g_socks4a_proxy_connect_finish;
490   proxy_iface->supports_hostname = g_socks4a_proxy_supports_hostname;
491 }