nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 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, see <http://www.gnu.org/licenses/>. |
||
17 | * |
||
18 | * Author: Nicolas Dufresne <nicolas.dufresne@collabora.co.uk> |
||
19 | */ |
||
20 | |||
21 | #include "config.h" |
||
22 | |||
23 | #include "gsocks4aproxy.h" |
||
24 | |||
25 | #include <string.h> |
||
26 | |||
27 | #include "giomodule.h" |
||
28 | #include "giomodule-priv.h" |
||
29 | #include "giostream.h" |
||
30 | #include "ginetaddress.h" |
||
31 | #include "ginputstream.h" |
||
32 | #include "glibintl.h" |
||
33 | #include "goutputstream.h" |
||
34 | #include "gproxy.h" |
||
35 | #include "gproxyaddress.h" |
||
36 | #include "gtask.h" |
||
37 | |||
38 | #define SOCKS4_VERSION 4 |
||
39 | |||
40 | #define SOCKS4_CMD_CONNECT 1 |
||
41 | #define SOCKS4_CMD_BIND 2 |
||
42 | |||
43 | #define SOCKS4_MAX_LEN 255 |
||
44 | |||
45 | #define SOCKS4_REP_VERSION 0 |
||
46 | #define SOCKS4_REP_GRANTED 90 |
||
47 | #define SOCKS4_REP_REJECTED 91 |
||
48 | #define SOCKS4_REP_NO_IDENT 92 |
||
49 | #define SOCKS4_REP_BAD_IDENT 93 |
||
50 | |||
51 | static void g_socks4a_proxy_iface_init (GProxyInterface *proxy_iface); |
||
52 | |||
53 | #define g_socks4a_proxy_get_type _g_socks4a_proxy_get_type |
||
54 | G_DEFINE_TYPE_WITH_CODE (GSocks4aProxy, g_socks4a_proxy, G_TYPE_OBJECT, |
||
55 | G_IMPLEMENT_INTERFACE (G_TYPE_PROXY, |
||
56 | g_socks4a_proxy_iface_init) |
||
57 | _g_io_modules_ensure_extension_points_registered (); |
||
58 | g_io_extension_point_implement (G_PROXY_EXTENSION_POINT_NAME, |
||
59 | g_define_type_id, |
||
60 | "socks4a", |
||
61 | 0)) |
||
62 | |||
63 | static void |
||
64 | g_socks4a_proxy_finalize (GObject *object) |
||
65 | { |
||
66 | /* must chain up */ |
||
67 | G_OBJECT_CLASS (g_socks4a_proxy_parent_class)->finalize (object); |
||
68 | } |
||
69 | |||
70 | static void |
||
71 | g_socks4a_proxy_init (GSocks4aProxy *proxy) |
||
72 | { |
||
73 | proxy->supports_hostname = TRUE; |
||
74 | } |
||
75 | |||
76 | /* |-> SOCKSv4a only |
||
77 | * +----+----+----+----+----+----+----+----+----+----+....+----+------+....+------+ |
||
78 | * | VN | CD | DSTPORT | DSTIP | USERID |NULL| HOST | | NULL | |
||
79 | * +----+----+----+----+----+----+----+----+----+----+....+----+------+....+------+ |
||
80 | * 1 1 2 4 variable 1 variable |
||
81 | */ |
||
82 | #define SOCKS4_CONN_MSG_LEN (9 + SOCKS4_MAX_LEN * 2) |
||
83 | static gint |
||
84 | set_connect_msg (guint8 *msg, |
||
85 | const gchar *hostname, |
||
86 | guint16 port, |
||
87 | const char *username, |
||
88 | GError **error) |
||
89 | { |
||
90 | GInetAddress *addr; |
||
91 | guint len = 0; |
||
92 | gsize addr_len; |
||
93 | gboolean is_ip; |
||
94 | const gchar *ip; |
||
95 | |||
96 | msg[len++] = SOCKS4_VERSION; |
||
97 | msg[len++] = SOCKS4_CMD_CONNECT; |
||
98 | |||
99 | { |
||
100 | guint16 hp = g_htons (port); |
||
101 | memcpy (msg + len, &hp, 2); |
||
102 | len += 2; |
||
103 | } |
||
104 | |||
105 | is_ip = g_hostname_is_ip_address (hostname); |
||
106 | |||
107 | if (is_ip) |
||
108 | ip = hostname; |
||
109 | else |
||
110 | ip = "0.0.0.1"; |
||
111 | |||
112 | addr = g_inet_address_new_from_string (ip); |
||
113 | addr_len = g_inet_address_get_native_size (addr); |
||
114 | |||
115 | if (addr_len != 4) |
||
116 | { |
||
117 | g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED, |
||
118 | _("SOCKSv4 does not support IPv6 address '%s'"), |
||
119 | ip); |
||
120 | g_object_unref (addr); |
||
121 | return -1; |
||
122 | } |
||
123 | |||
124 | memcpy (msg + len, g_inet_address_to_bytes (addr), addr_len); |
||
125 | len += addr_len; |
||
126 | |||
127 | g_object_unref (addr); |
||
128 | |||
129 | if (username) |
||
130 | { |
||
131 | gsize user_len = strlen (username); |
||
132 | |||
133 | if (user_len > SOCKS4_MAX_LEN) |
||
134 | { |
||
135 | g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED, |
||
136 | _("Username is too long for SOCKSv4 protocol")); |
||
137 | return -1; |
||
138 | } |
||
139 | |||
140 | memcpy (msg + len, username, user_len); |
||
141 | len += user_len; |
||
142 | } |
||
143 | |||
144 | msg[len++] = '\0'; |
||
145 | |||
146 | if (!is_ip) |
||
147 | { |
||
148 | gsize host_len = strlen (hostname); |
||
149 | |||
150 | if (host_len > SOCKS4_MAX_LEN) |
||
151 | { |
||
152 | g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED, |
||
153 | _("Hostname '%s' is too long for SOCKSv4 protocol"), |
||
154 | hostname); |
||
155 | return -1; |
||
156 | } |
||
157 | |||
158 | memcpy (msg + len, hostname, host_len); |
||
159 | len += host_len; |
||
160 | msg[len++] = '\0'; |
||
161 | } |
||
162 | |||
163 | return len; |
||
164 | } |
||
165 | |||
166 | /* |
||
167 | * +----+----+----+----+----+----+----+----+ |
||
168 | * | VN | CD | DSTPORT | DSTIP | |
||
169 | * +----+----+----+----+----+----+----+----+ |
||
170 | * 1 1 2 4 |
||
171 | */ |
||
172 | #define SOCKS4_CONN_REP_LEN 8 |
||
173 | static gboolean |
||
174 | parse_connect_reply (const guint8 *data, GError **error) |
||
175 | { |
||
176 | if (data[0] != SOCKS4_REP_VERSION) |
||
177 | { |
||
178 | g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED, |
||
179 | _("The server is not a SOCKSv4 proxy server.")); |
||
180 | return FALSE; |
||
181 | } |
||
182 | |||
183 | if (data[1] != SOCKS4_REP_GRANTED) |
||
184 | { |
||
185 | g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED, |
||
186 | _("Connection through SOCKSv4 server was rejected")); |
||
187 | return FALSE; |
||
188 | } |
||
189 | |||
190 | return TRUE; |
||
191 | } |
||
192 | |||
193 | static GIOStream * |
||
194 | g_socks4a_proxy_connect (GProxy *proxy, |
||
195 | GIOStream *io_stream, |
||
196 | GProxyAddress *proxy_address, |
||
197 | GCancellable *cancellable, |
||
198 | GError **error) |
||
199 | { |
||
200 | GInputStream *in; |
||
201 | GOutputStream *out; |
||
202 | const gchar *hostname; |
||
203 | guint16 port; |
||
204 | const gchar *username; |
||
205 | |||
206 | hostname = g_proxy_address_get_destination_hostname (proxy_address); |
||
207 | port = g_proxy_address_get_destination_port (proxy_address); |
||
208 | username = g_proxy_address_get_username (proxy_address); |
||
209 | |||
210 | in = g_io_stream_get_input_stream (io_stream); |
||
211 | out = g_io_stream_get_output_stream (io_stream); |
||
212 | |||
213 | /* Send SOCKS4 connection request */ |
||
214 | { |
||
215 | guint8 msg[SOCKS4_CONN_MSG_LEN]; |
||
216 | gint len; |
||
217 | |||
218 | len = set_connect_msg (msg, hostname, port, username, error); |
||
219 | |||
220 | if (len < 0) |
||
221 | goto error; |
||
222 | |||
223 | if (!g_output_stream_write_all (out, msg, len, NULL, |
||
224 | cancellable, error)) |
||
225 | goto error; |
||
226 | } |
||
227 | |||
228 | /* Read SOCKS4 response */ |
||
229 | { |
||
230 | guint8 data[SOCKS4_CONN_REP_LEN]; |
||
231 | |||
232 | if (!g_input_stream_read_all (in, data, SOCKS4_CONN_REP_LEN, NULL, |
||
233 | cancellable, error)) |
||
234 | goto error; |
||
235 | |||
236 | if (!parse_connect_reply (data, error)) |
||
237 | goto error; |
||
238 | } |
||
239 | |||
240 | return g_object_ref (io_stream); |
||
241 | |||
242 | error: |
||
243 | return NULL; |
||
244 | } |
||
245 | |||
246 | |||
247 | typedef struct |
||
248 | { |
||
249 | GIOStream *io_stream; |
||
250 | |||
251 | /* For connecting */ |
||
252 | guint8 *buffer; |
||
253 | gssize length; |
||
254 | gssize offset; |
||
255 | |||
256 | } ConnectAsyncData; |
||
257 | |||
258 | static void connect_msg_write_cb (GObject *source, |
||
259 | GAsyncResult *result, |
||
260 | gpointer user_data); |
||
261 | static void connect_reply_read_cb (GObject *source, |
||
262 | GAsyncResult *result, |
||
263 | gpointer user_data); |
||
264 | |||
265 | static void |
||
266 | free_connect_data (ConnectAsyncData *data) |
||
267 | { |
||
268 | g_object_unref (data->io_stream); |
||
269 | g_slice_free (ConnectAsyncData, data); |
||
270 | } |
||
271 | |||
272 | static void |
||
273 | do_read (GAsyncReadyCallback callback, GTask *task, ConnectAsyncData *data) |
||
274 | { |
||
275 | GInputStream *in; |
||
276 | in = g_io_stream_get_input_stream (data->io_stream); |
||
277 | g_input_stream_read_async (in, |
||
278 | data->buffer + data->offset, |
||
279 | data->length - data->offset, |
||
280 | g_task_get_priority (task), |
||
281 | g_task_get_cancellable (task), |
||
282 | callback, task); |
||
283 | } |
||
284 | |||
285 | static void |
||
286 | do_write (GAsyncReadyCallback callback, GTask *task, ConnectAsyncData *data) |
||
287 | { |
||
288 | GOutputStream *out; |
||
289 | out = g_io_stream_get_output_stream (data->io_stream); |
||
290 | g_output_stream_write_async (out, |
||
291 | data->buffer + data->offset, |
||
292 | data->length - data->offset, |
||
293 | g_task_get_priority (task), |
||
294 | g_task_get_cancellable (task), |
||
295 | callback, task); |
||
296 | } |
||
297 | |||
298 | |||
299 | |||
300 | static void |
||
301 | g_socks4a_proxy_connect_async (GProxy *proxy, |
||
302 | GIOStream *io_stream, |
||
303 | GProxyAddress *proxy_address, |
||
304 | GCancellable *cancellable, |
||
305 | GAsyncReadyCallback callback, |
||
306 | gpointer user_data) |
||
307 | { |
||
308 | GError *error = NULL; |
||
309 | GTask *task; |
||
310 | ConnectAsyncData *data; |
||
311 | const gchar *hostname; |
||
312 | guint16 port; |
||
313 | const gchar *username; |
||
314 | |||
315 | data = g_slice_new0 (ConnectAsyncData); |
||
316 | data->io_stream = g_object_ref (io_stream); |
||
317 | |||
318 | task = g_task_new (proxy, cancellable, callback, user_data); |
||
319 | g_task_set_task_data (task, data, (GDestroyNotify) free_connect_data); |
||
320 | |||
321 | hostname = g_proxy_address_get_destination_hostname (proxy_address); |
||
322 | port = g_proxy_address_get_destination_port (proxy_address); |
||
323 | username = g_proxy_address_get_username (proxy_address); |
||
324 | |||
325 | data->buffer = g_malloc0 (SOCKS4_CONN_MSG_LEN); |
||
326 | data->length = set_connect_msg (data->buffer, |
||
327 | hostname, port, username, |
||
328 | &error); |
||
329 | data->offset = 0; |
||
330 | |||
331 | if (data->length < 0) |
||
332 | { |
||
333 | g_task_return_error (task, error); |
||
334 | g_object_unref (task); |
||
335 | } |
||
336 | else |
||
337 | { |
||
338 | do_write (connect_msg_write_cb, task, data); |
||
339 | } |
||
340 | } |
||
341 | |||
342 | static void |
||
343 | connect_msg_write_cb (GObject *source, |
||
344 | GAsyncResult *result, |
||
345 | gpointer user_data) |
||
346 | { |
||
347 | GTask *task = user_data; |
||
348 | ConnectAsyncData *data = g_task_get_task_data (task); |
||
349 | GError *error = NULL; |
||
350 | gssize written; |
||
351 | |||
352 | written = g_output_stream_write_finish (G_OUTPUT_STREAM (source), |
||
353 | result, &error); |
||
354 | |||
355 | if (written < 0) |
||
356 | { |
||
357 | g_task_return_error (task, error); |
||
358 | g_object_unref (task); |
||
359 | return; |
||
360 | } |
||
361 | |||
362 | data->offset += written; |
||
363 | |||
364 | if (data->offset == data->length) |
||
365 | { |
||
366 | g_free (data->buffer); |
||
367 | |||
368 | data->buffer = g_malloc0 (SOCKS4_CONN_REP_LEN); |
||
369 | data->length = SOCKS4_CONN_REP_LEN; |
||
370 | data->offset = 0; |
||
371 | |||
372 | do_read (connect_reply_read_cb, task, data); |
||
373 | } |
||
374 | else |
||
375 | { |
||
376 | do_write (connect_msg_write_cb, task, data); |
||
377 | } |
||
378 | } |
||
379 | |||
380 | static void |
||
381 | connect_reply_read_cb (GObject *source, |
||
382 | GAsyncResult *result, |
||
383 | gpointer user_data) |
||
384 | { |
||
385 | GTask *task = user_data; |
||
386 | ConnectAsyncData *data = g_task_get_task_data (task); |
||
387 | GError *error = NULL; |
||
388 | gssize read; |
||
389 | |||
390 | read = g_input_stream_read_finish (G_INPUT_STREAM (source), |
||
391 | result, &error); |
||
392 | |||
393 | if (read < 0) |
||
394 | { |
||
395 | g_task_return_error (task, error); |
||
396 | g_object_unref (task); |
||
397 | return; |
||
398 | } |
||
399 | |||
400 | data->offset += read; |
||
401 | |||
402 | if (data->offset == data->length) |
||
403 | { |
||
404 | if (!parse_connect_reply (data->buffer, &error)) |
||
405 | { |
||
406 | g_task_return_error (task, error); |
||
407 | g_object_unref (task); |
||
408 | return; |
||
409 | } |
||
410 | else |
||
411 | { |
||
412 | g_task_return_pointer (task, g_object_ref (data->io_stream), g_object_unref); |
||
413 | g_object_unref (task); |
||
414 | return; |
||
415 | } |
||
416 | } |
||
417 | else |
||
418 | { |
||
419 | do_read (connect_reply_read_cb, task, data); |
||
420 | } |
||
421 | } |
||
422 | |||
423 | static GIOStream *g_socks4a_proxy_connect_finish (GProxy *proxy, |
||
424 | GAsyncResult *result, |
||
425 | GError **error); |
||
426 | |||
427 | static GIOStream * |
||
428 | g_socks4a_proxy_connect_finish (GProxy *proxy, |
||
429 | GAsyncResult *result, |
||
430 | GError **error) |
||
431 | { |
||
432 | return g_task_propagate_pointer (G_TASK (result), error); |
||
433 | } |
||
434 | |||
435 | static gboolean |
||
436 | g_socks4a_proxy_supports_hostname (GProxy *proxy) |
||
437 | { |
||
438 | return G_SOCKS4A_PROXY (proxy)->supports_hostname; |
||
439 | } |
||
440 | |||
441 | static void |
||
442 | g_socks4a_proxy_class_init (GSocks4aProxyClass *class) |
||
443 | { |
||
444 | GObjectClass *object_class; |
||
445 | |||
446 | object_class = (GObjectClass *) class; |
||
447 | object_class->finalize = g_socks4a_proxy_finalize; |
||
448 | } |
||
449 | |||
450 | static void |
||
451 | g_socks4a_proxy_iface_init (GProxyInterface *proxy_iface) |
||
452 | { |
||
453 | proxy_iface->connect = g_socks4a_proxy_connect; |
||
454 | proxy_iface->connect_async = g_socks4a_proxy_connect_async; |
||
455 | proxy_iface->connect_finish = g_socks4a_proxy_connect_finish; |
||
456 | proxy_iface->supports_hostname = g_socks4a_proxy_supports_hostname; |
||
457 | } |