Branch data Line data Source code
1 : : /* GIO - GLib Input, Output and Streaming Library
2 : : *
3 : : * Copyright (C) 2008, 2010 Collabora, Ltd.
4 : : * Copyright (C) 2008 Nokia Corporation. All rights reserved.
5 : : *
6 : : * SPDX-License-Identifier: LGPL-2.1-or-later
7 : : *
8 : : * This library is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU Lesser General Public
10 : : * License as published by the Free Software Foundation; either
11 : : * version 2.1 of the License, or (at your option) any later version.
12 : : *
13 : : * This library is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : : * Lesser General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU Lesser General
19 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 : : *
21 : : * Author: Youness Alaoui <youness.alaoui@collabora.co.uk
22 : : *
23 : : * Contributors:
24 : : * Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
25 : : */
26 : :
27 : : #include "config.h"
28 : :
29 : : #include "gsocks5proxy.h"
30 : :
31 : : #include <string.h>
32 : :
33 : : #include "giomodule.h"
34 : : #include "giomodule-priv.h"
35 : : #include "giostream.h"
36 : : #include "ginetaddress.h"
37 : : #include "ginputstream.h"
38 : : #include "glibintl.h"
39 : : #include "goutputstream.h"
40 : : #include "gproxy.h"
41 : : #include "gproxyaddress.h"
42 : : #include "gtask.h"
43 : :
44 : : #define SOCKS5_VERSION 0x05
45 : :
46 : : #define SOCKS5_CMD_CONNECT 0x01
47 : : #define SOCKS5_CMD_BIND 0x02
48 : : #define SOCKS5_CMD_UDP_ASSOCIATE 0x03
49 : :
50 : : #define SOCKS5_ATYP_IPV4 0x01
51 : : #define SOCKS5_ATYP_DOMAINNAME 0x03
52 : : #define SOCKS5_ATYP_IPV6 0x04
53 : :
54 : : #define SOCKS5_AUTH_VERSION 0x01
55 : :
56 : : #define SOCKS5_AUTH_NONE 0x00
57 : : #define SOCKS5_AUTH_GSSAPI 0x01
58 : : #define SOCKS5_AUTH_USR_PASS 0x02
59 : : #define SOCKS5_AUTH_NO_ACCEPT 0xff
60 : :
61 : : #define SOCKS5_MAX_LEN 255
62 : : #define SOCKS5_RESERVED 0x00
63 : :
64 : : #define SOCKS5_REP_SUCCEEDED 0x00
65 : : #define SOCKS5_REP_SRV_FAILURE 0x01
66 : : #define SOCKS5_REP_NOT_ALLOWED 0x02
67 : : #define SOCKS5_REP_NET_UNREACH 0x03
68 : : #define SOCKS5_REP_HOST_UNREACH 0x04
69 : : #define SOCKS5_REP_REFUSED 0x05
70 : : #define SOCKS5_REP_TTL_EXPIRED 0x06
71 : : #define SOCKS5_REP_CMD_NOT_SUP 0x07
72 : : #define SOCKS5_REP_ATYPE_NOT_SUP 0x08
73 : :
74 : :
75 : : struct _GSocks5Proxy
76 : : {
77 : : GObject parent;
78 : : };
79 : :
80 : : struct _GSocks5ProxyClass
81 : : {
82 : : GObjectClass parent_class;
83 : : };
84 : :
85 : : static void g_socks5_proxy_iface_init (GProxyInterface *proxy_iface);
86 : :
87 : : #define g_socks5_proxy_get_type _g_socks5_proxy_get_type
88 [ + - + - : 228 : G_DEFINE_TYPE_WITH_CODE (GSocks5Proxy, g_socks5_proxy, G_TYPE_OBJECT,
+ - ]
89 : : G_IMPLEMENT_INTERFACE (G_TYPE_PROXY,
90 : : g_socks5_proxy_iface_init)
91 : : _g_io_modules_ensure_extension_points_registered ();
92 : : g_io_extension_point_implement (G_PROXY_EXTENSION_POINT_NAME,
93 : : g_define_type_id,
94 : : "socks5",
95 : : 0))
96 : :
97 : : static void
98 : 0 : g_socks5_proxy_finalize (GObject *object)
99 : : {
100 : : /* must chain up */
101 : 0 : G_OBJECT_CLASS (g_socks5_proxy_parent_class)->finalize (object);
102 : 0 : }
103 : :
104 : : static void
105 : 0 : g_socks5_proxy_init (GSocks5Proxy *proxy)
106 : : {
107 : 0 : }
108 : :
109 : : /*
110 : : * +----+----------+----------+
111 : : * |VER | NMETHODS | METHODS |
112 : : * +----+----------+----------+
113 : : * | 1 | 1 | 1 to 255 |
114 : : * +----+----------+----------+
115 : : */
116 : : #define SOCKS5_NEGO_MSG_LEN 4
117 : : static gint
118 : 0 : set_nego_msg (guint8 *msg, gboolean has_auth)
119 : : {
120 : 0 : gint len = 3;
121 : :
122 : 0 : msg[0] = SOCKS5_VERSION;
123 : 0 : msg[1] = 0x01; /* number of methods supported */
124 : 0 : msg[2] = SOCKS5_AUTH_NONE;
125 : :
126 : : /* add support for authentication method */
127 [ # # ]: 0 : if (has_auth)
128 : : {
129 : 0 : msg[1] = 0x02; /* number of methods supported */
130 : 0 : msg[3] = SOCKS5_AUTH_USR_PASS;
131 : 0 : len++;
132 : : }
133 : :
134 : 0 : return len;
135 : : }
136 : :
137 : :
138 : : /*
139 : : * +----+--------+
140 : : * |VER | METHOD |
141 : : * +----+--------+
142 : : * | 1 | 1 |
143 : : * +----+--------+
144 : : */
145 : : #define SOCKS5_NEGO_REP_LEN 2
146 : : static gboolean
147 : 0 : parse_nego_reply (const guint8 *data,
148 : : gboolean has_auth,
149 : : gboolean *must_auth,
150 : : GError **error)
151 : : {
152 [ # # ]: 0 : if (data[0] != SOCKS5_VERSION)
153 : : {
154 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
155 : : _("The server is not a SOCKSv5 proxy server."));
156 : 0 : return FALSE;
157 : : }
158 : :
159 [ # # # # ]: 0 : switch (data[1])
160 : : {
161 : 0 : case SOCKS5_AUTH_NONE:
162 : 0 : *must_auth = FALSE;
163 : 0 : break;
164 : :
165 : 0 : case SOCKS5_AUTH_USR_PASS:
166 [ # # ]: 0 : if (!has_auth)
167 : : {
168 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_NEED_AUTH,
169 : : _("The SOCKSv5 proxy requires authentication."));
170 : 0 : return FALSE;
171 : : }
172 : 0 : *must_auth = TRUE;
173 : 0 : break;
174 : :
175 : 0 : case SOCKS5_AUTH_NO_ACCEPT:
176 [ # # ]: 0 : if (!has_auth)
177 : : {
178 : : /* The server has said it accepts none of our authentication methods,
179 : : * but given the slightly odd implementation of set_nego_msg(), we
180 : : * actually only gave it the choice of %SOCKS5_AUTH_NONE, since the
181 : : * caller specified no username or password.
182 : : * Return %G_IO_ERROR_PROXY_NEED_AUTH so the caller knows that if
183 : : * they specify a username and password and try again, authentication
184 : : * might succeed (since we’ll send %SOCKS5_AUTH_USR_PASS next time). */
185 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_NEED_AUTH,
186 : : _("The SOCKSv5 proxy requires authentication."));
187 : 0 : return FALSE;
188 : : }
189 : : G_GNUC_FALLTHROUGH;
190 : : case SOCKS5_AUTH_GSSAPI:
191 : : default:
192 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_AUTH_FAILED,
193 : : _("The SOCKSv5 proxy requires an authentication "
194 : : "method that is not supported by GLib."));
195 : 0 : return FALSE;
196 : : break;
197 : : }
198 : :
199 : 0 : return TRUE;
200 : : }
201 : :
202 : : #define SOCKS5_AUTH_MSG_LEN 515
203 : : static gint
204 : 0 : set_auth_msg (guint8 *msg,
205 : : const gchar *username,
206 : : const gchar *password,
207 : : GError **error)
208 : : {
209 : 0 : gint len = 0;
210 : 0 : gint ulen = 0; /* username length */
211 : 0 : gint plen = 0; /* Password length */
212 : :
213 [ # # ]: 0 : if (username)
214 : 0 : ulen = strlen (username);
215 : :
216 [ # # ]: 0 : if (password)
217 : 0 : plen = strlen (password);
218 : :
219 [ # # # # ]: 0 : if (ulen > SOCKS5_MAX_LEN || plen > SOCKS5_MAX_LEN)
220 : : {
221 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
222 : : _("Username or password is too long for SOCKSv5 "
223 : : "protocol."));
224 : 0 : return -1;
225 : : }
226 : :
227 : 0 : msg[len++] = SOCKS5_AUTH_VERSION;
228 : 0 : msg[len++] = ulen;
229 : :
230 [ # # ]: 0 : if (ulen > 0)
231 : 0 : memcpy (msg + len, username, ulen);
232 : :
233 : 0 : len += ulen;
234 : 0 : msg[len++] = plen;
235 : :
236 [ # # ]: 0 : if (plen > 0)
237 : 0 : memcpy (msg + len, password, plen);
238 : :
239 : 0 : len += plen;
240 : :
241 : 0 : return len;
242 : : }
243 : :
244 : :
245 : : static gboolean
246 : 0 : check_auth_status (const guint8 *data, GError **error)
247 : : {
248 [ # # ]: 0 : if (data[0] != SOCKS5_AUTH_VERSION
249 [ # # ]: 0 : || data[1] != SOCKS5_REP_SUCCEEDED)
250 : : {
251 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_AUTH_FAILED,
252 : : _("SOCKSv5 authentication failed due to wrong "
253 : : "username or password."));
254 : 0 : return FALSE;
255 : : }
256 : 0 : return TRUE;
257 : : }
258 : :
259 : : /*
260 : : * +----+-----+-------+------+----------+----------+
261 : : * |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
262 : : * +----+-----+-------+------+----------+----------+
263 : : * | 1 | 1 | X'00' | 1 | Variable | 2 |
264 : : * +----+-----+-------+------+----------+----------+
265 : : * DST.ADDR is a string with first byte being the size. So DST.ADDR may not be
266 : : * longer then 256 bytes.
267 : : */
268 : : #define SOCKS5_CONN_MSG_LEN 262
269 : : static gint
270 : 0 : set_connect_msg (guint8 *msg,
271 : : const gchar *hostname,
272 : : guint16 port,
273 : : GError **error)
274 : : {
275 : 0 : guint len = 0;
276 : :
277 : 0 : msg[len++] = SOCKS5_VERSION;
278 : 0 : msg[len++] = SOCKS5_CMD_CONNECT;
279 : 0 : msg[len++] = SOCKS5_RESERVED;
280 : :
281 [ # # ]: 0 : if (g_hostname_is_ip_address (hostname))
282 : : {
283 : 0 : GInetAddress *addr = g_inet_address_new_from_string (hostname);
284 : 0 : gsize addr_len = g_inet_address_get_native_size (addr);
285 : :
286 : : /* We are cheating for simplicity, here's the logic:
287 : : * 1 = IPV4 = 4 bytes / 4
288 : : * 4 = IPV6 = 16 bytes / 4 */
289 : 0 : msg[len++] = addr_len / 4;
290 : 0 : memcpy (msg + len, g_inet_address_to_bytes (addr), addr_len);
291 : 0 : len += addr_len;
292 : :
293 : 0 : g_object_unref (addr);
294 : : }
295 : : else
296 : : {
297 : 0 : gsize host_len = strlen (hostname);
298 : :
299 [ # # ]: 0 : if (host_len > SOCKS5_MAX_LEN)
300 : : {
301 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
302 : : _("Hostname “%s” is too long for SOCKSv5 protocol"),
303 : : hostname);
304 : 0 : return -1;
305 : : }
306 : :
307 : 0 : msg[len++] = SOCKS5_ATYP_DOMAINNAME;
308 : 0 : msg[len++] = (guint8) host_len;
309 : 0 : memcpy (msg + len, hostname, host_len);
310 : 0 : len += host_len;
311 : : }
312 : :
313 : : {
314 : 0 : guint16 hp = g_htons (port);
315 : 0 : memcpy (msg + len, &hp, 2);
316 : 0 : len += 2;
317 : : }
318 : :
319 : 0 : return len;
320 : : }
321 : :
322 : : /*
323 : : * +----+-----+-------+------+----------+----------+
324 : : * |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
325 : : * +----+-----+-------+------+----------+----------+
326 : : * | 1 | 1 | X'00' | 1 | Variable | 2 |
327 : : * +----+-----+-------+------+----------+----------+
328 : : * This reply need to be read by small part to determine size. Buffer
329 : : * size is determined in function of the biggest part to read.
330 : : *
331 : : * The parser only requires 4 bytes.
332 : : */
333 : : #define SOCKS5_CONN_REP_LEN 257
334 : : static gboolean
335 : 0 : parse_connect_reply (const guint8 *data, gint *atype, GError **error)
336 : : {
337 [ # # ]: 0 : if (data[0] != SOCKS5_VERSION)
338 : : {
339 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
340 : : _("The server is not a SOCKSv5 proxy server."));
341 : 0 : return FALSE;
342 : : }
343 : :
344 [ # # # # : 0 : switch (data[1])
# # # #
# ]
345 : : {
346 : 0 : case SOCKS5_REP_SUCCEEDED:
347 [ # # ]: 0 : if (data[2] != SOCKS5_RESERVED)
348 : : {
349 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
350 : : _("The server is not a SOCKSv5 proxy server."));
351 : 0 : return FALSE;
352 : : }
353 : :
354 [ # # ]: 0 : switch (data[3])
355 : : {
356 : 0 : case SOCKS5_ATYP_IPV4:
357 : : case SOCKS5_ATYP_IPV6:
358 : : case SOCKS5_ATYP_DOMAINNAME:
359 : 0 : *atype = data[3];
360 : 0 : break;
361 : :
362 : 0 : default:
363 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
364 : : _("The SOCKSv5 proxy server uses unknown address type."));
365 : 0 : return FALSE;
366 : : }
367 : 0 : break;
368 : :
369 : 0 : case SOCKS5_REP_SRV_FAILURE:
370 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
371 : : _("Internal SOCKSv5 proxy server error."));
372 : 0 : return FALSE;
373 : : break;
374 : :
375 : 0 : case SOCKS5_REP_NOT_ALLOWED:
376 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_NOT_ALLOWED,
377 : : _("SOCKSv5 connection not allowed by ruleset."));
378 : 0 : return FALSE;
379 : : break;
380 : :
381 : 0 : case SOCKS5_REP_TTL_EXPIRED:
382 : : case SOCKS5_REP_HOST_UNREACH:
383 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE,
384 : : _("Host unreachable through SOCKSv5 server."));
385 : 0 : return FALSE;
386 : : break;
387 : :
388 : 0 : case SOCKS5_REP_NET_UNREACH:
389 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
390 : : _("Network unreachable through SOCKSv5 proxy."));
391 : 0 : return FALSE;
392 : : break;
393 : :
394 : 0 : case SOCKS5_REP_REFUSED:
395 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED,
396 : : _("Connection refused through SOCKSv5 proxy."));
397 : 0 : return FALSE;
398 : : break;
399 : :
400 : 0 : case SOCKS5_REP_CMD_NOT_SUP:
401 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
402 : : _("SOCKSv5 proxy does not support “connect” command."));
403 : 0 : return FALSE;
404 : : break;
405 : :
406 : 0 : case SOCKS5_REP_ATYPE_NOT_SUP:
407 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
408 : : _("SOCKSv5 proxy does not support provided address type."));
409 : 0 : return FALSE;
410 : : break;
411 : :
412 : 0 : default: /* Unknown error */
413 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
414 : : _("Unknown SOCKSv5 proxy error."));
415 : 0 : return FALSE;
416 : : break;
417 : : }
418 : :
419 : 0 : return TRUE;
420 : : }
421 : :
422 : : static GIOStream *
423 : 0 : g_socks5_proxy_connect (GProxy *proxy,
424 : : GIOStream *io_stream,
425 : : GProxyAddress *proxy_address,
426 : : GCancellable *cancellable,
427 : : GError **error)
428 : : {
429 : : gboolean has_auth;
430 : : GInputStream *in;
431 : : GOutputStream *out;
432 : : const gchar *hostname;
433 : : guint16 port;
434 : : const gchar *username;
435 : : const gchar *password;
436 : :
437 : 0 : hostname = g_proxy_address_get_destination_hostname (proxy_address);
438 : 0 : port = g_proxy_address_get_destination_port (proxy_address);
439 : 0 : username = g_proxy_address_get_username (proxy_address);
440 : 0 : password = g_proxy_address_get_password (proxy_address);
441 : :
442 [ # # # # ]: 0 : has_auth = username || password;
443 : :
444 : 0 : in = g_io_stream_get_input_stream (io_stream);
445 : 0 : out = g_io_stream_get_output_stream (io_stream);
446 : :
447 : : /* Send SOCKS5 handshake */
448 : : {
449 : : guint8 msg[SOCKS5_NEGO_MSG_LEN];
450 : : gint len;
451 : :
452 : 0 : len = set_nego_msg (msg, has_auth);
453 : :
454 [ # # ]: 0 : if (!g_output_stream_write_all (out, msg, len, NULL,
455 : : cancellable, error))
456 : 0 : goto error;
457 : : }
458 : :
459 : : /* Receive SOCKS5 response and reply with authentication if required */
460 : : {
461 : : guint8 data[SOCKS5_NEGO_REP_LEN];
462 : 0 : gboolean must_auth = FALSE;
463 : :
464 [ # # ]: 0 : if (!g_input_stream_read_all (in, data, sizeof (data), NULL,
465 : : cancellable, error))
466 : 0 : goto error;
467 : :
468 [ # # ]: 0 : if (!parse_nego_reply (data, has_auth, &must_auth, error))
469 : 0 : goto error;
470 : :
471 [ # # ]: 0 : if (must_auth)
472 : : {
473 : : guint8 msg[SOCKS5_AUTH_MSG_LEN];
474 : : gint len;
475 : :
476 : 0 : len = set_auth_msg (msg, username, password, error);
477 : :
478 [ # # ]: 0 : if (len < 0)
479 : 0 : goto error;
480 : :
481 [ # # ]: 0 : if (!g_output_stream_write_all (out, msg, len, NULL,
482 : : cancellable, error))
483 : 0 : goto error;
484 : :
485 [ # # ]: 0 : if (!g_input_stream_read_all (in, data, sizeof (data), NULL,
486 : : cancellable, error))
487 : 0 : goto error;
488 : :
489 [ # # ]: 0 : if (!check_auth_status (data, error))
490 : 0 : goto error;
491 : : }
492 : : }
493 : :
494 : : /* Send SOCKS5 connection request */
495 : : {
496 : : guint8 msg[SOCKS5_CONN_MSG_LEN];
497 : : gint len;
498 : :
499 : 0 : len = set_connect_msg (msg, hostname, port, error);
500 : :
501 [ # # ]: 0 : if (len < 0)
502 : 0 : goto error;
503 : :
504 [ # # ]: 0 : if (!g_output_stream_write_all (out, msg, len, NULL,
505 : : cancellable, error))
506 : 0 : goto error;
507 : : }
508 : :
509 : : /* Read SOCKS5 response */
510 : : {
511 : : guint8 data[SOCKS5_CONN_REP_LEN];
512 : : gint atype;
513 : :
514 [ # # ]: 0 : if (!g_input_stream_read_all (in, data, 4 /* VER, REP, RSV, ATYP */, NULL,
515 : : cancellable, error))
516 : 0 : goto error;
517 : :
518 [ # # ]: 0 : if (!parse_connect_reply (data, &atype, error))
519 : 0 : goto error;
520 : :
521 [ # # # # ]: 0 : switch (atype)
522 : : {
523 : 0 : case SOCKS5_ATYP_IPV4:
524 [ # # ]: 0 : if (!g_input_stream_read_all (in, data,
525 : : 4 /* IPv4 length */ + 2 /* port */,
526 : : NULL, cancellable, error))
527 : 0 : goto error;
528 : 0 : break;
529 : :
530 : 0 : case SOCKS5_ATYP_IPV6:
531 [ # # ]: 0 : if (!g_input_stream_read_all (in, data,
532 : : 16 /* IPv6 length */ + 2 /* port */,
533 : : NULL, cancellable, error))
534 : 0 : goto error;
535 : 0 : break;
536 : :
537 : 0 : case SOCKS5_ATYP_DOMAINNAME:
538 [ # # ]: 0 : if (!g_input_stream_read_all (in, data, 1 /* domain name length */,
539 : : NULL, cancellable, error))
540 : 0 : goto error;
541 [ # # ]: 0 : if (!g_input_stream_read_all (in, data,
542 : 0 : data[0] /* domain name length */ + 2 /* port */,
543 : : NULL, cancellable, error))
544 : 0 : goto error;
545 : 0 : break;
546 : : }
547 : : }
548 : :
549 : 0 : return g_object_ref (io_stream);
550 : :
551 : 0 : error:
552 : 0 : return NULL;
553 : : }
554 : :
555 : :
556 : : typedef struct
557 : : {
558 : : GIOStream *io_stream;
559 : : gchar *hostname;
560 : : guint16 port;
561 : : gchar *username;
562 : : gchar *password;
563 : : guint8 *buffer;
564 : : gssize length;
565 : : gssize offset;
566 : : } ConnectAsyncData;
567 : :
568 : : static void nego_msg_write_cb (GObject *source,
569 : : GAsyncResult *res,
570 : : gpointer user_data);
571 : : static void nego_reply_read_cb (GObject *source,
572 : : GAsyncResult *res,
573 : : gpointer user_data);
574 : : static void auth_msg_write_cb (GObject *source,
575 : : GAsyncResult *res,
576 : : gpointer user_data);
577 : : static void auth_reply_read_cb (GObject *source,
578 : : GAsyncResult *result,
579 : : gpointer user_data);
580 : : static void send_connect_msg (GTask *task);
581 : : static void connect_msg_write_cb (GObject *source,
582 : : GAsyncResult *result,
583 : : gpointer user_data);
584 : : static void connect_reply_read_cb (GObject *source,
585 : : GAsyncResult *result,
586 : : gpointer user_data);
587 : : static void connect_addr_len_read_cb (GObject *source,
588 : : GAsyncResult *result,
589 : : gpointer user_data);
590 : : static void connect_addr_read_cb (GObject *source,
591 : : GAsyncResult *result,
592 : : gpointer user_data);
593 : :
594 : : static void
595 : 0 : free_connect_data (ConnectAsyncData *data)
596 : : {
597 : 0 : g_object_unref (data->io_stream);
598 : :
599 : 0 : g_free (data->hostname);
600 : 0 : g_free (data->username);
601 : 0 : g_free (data->password);
602 : 0 : g_free (data->buffer);
603 : :
604 : 0 : g_slice_free (ConnectAsyncData, data);
605 : 0 : }
606 : :
607 : : static void
608 : 0 : do_read (GAsyncReadyCallback callback, GTask *task, ConnectAsyncData *data)
609 : : {
610 : : GInputStream *in;
611 : 0 : in = g_io_stream_get_input_stream (data->io_stream);
612 : 0 : g_input_stream_read_async (in,
613 : 0 : data->buffer + data->offset,
614 : 0 : data->length - data->offset,
615 : : g_task_get_priority (task),
616 : : g_task_get_cancellable (task),
617 : : callback, task);
618 : 0 : }
619 : :
620 : : static void
621 : 0 : do_write (GAsyncReadyCallback callback, GTask *task, ConnectAsyncData *data)
622 : : {
623 : : GOutputStream *out;
624 : 0 : out = g_io_stream_get_output_stream (data->io_stream);
625 : 0 : g_output_stream_write_async (out,
626 : 0 : data->buffer + data->offset,
627 : 0 : data->length - data->offset,
628 : : g_task_get_priority (task),
629 : : g_task_get_cancellable (task),
630 : : callback, task);
631 : 0 : }
632 : :
633 : : static void
634 : 0 : g_socks5_proxy_connect_async (GProxy *proxy,
635 : : GIOStream *io_stream,
636 : : GProxyAddress *proxy_address,
637 : : GCancellable *cancellable,
638 : : GAsyncReadyCallback callback,
639 : : gpointer user_data)
640 : : {
641 : : GTask *task;
642 : : ConnectAsyncData *data;
643 : :
644 : 0 : data = g_slice_new0 (ConnectAsyncData);
645 : 0 : data->io_stream = g_object_ref (io_stream);
646 : :
647 : 0 : task = g_task_new (proxy, cancellable, callback, user_data);
648 [ # # ]: 0 : g_task_set_source_tag (task, g_socks5_proxy_connect_async);
649 : 0 : g_task_set_task_data (task, data, (GDestroyNotify) free_connect_data);
650 : :
651 : 0 : g_object_get (G_OBJECT (proxy_address),
652 : : "destination-hostname", &data->hostname,
653 : : "destination-port", &data->port,
654 : : "username", &data->username,
655 : : "password", &data->password,
656 : : NULL);
657 : :
658 : 0 : data->buffer = g_malloc0 (SOCKS5_NEGO_MSG_LEN);
659 : 0 : data->length = set_nego_msg (data->buffer,
660 [ # # # # ]: 0 : data->username || data->password);
661 : 0 : data->offset = 0;
662 : :
663 : 0 : do_write (nego_msg_write_cb, task, data);
664 : 0 : }
665 : :
666 : :
667 : : static void
668 : 0 : nego_msg_write_cb (GObject *source,
669 : : GAsyncResult *res,
670 : : gpointer user_data)
671 : : {
672 : 0 : GTask *task = user_data;
673 : 0 : ConnectAsyncData *data = g_task_get_task_data (task);
674 : 0 : GError *error = NULL;
675 : : gssize written;
676 : :
677 : 0 : written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
678 : : res, &error);
679 : :
680 [ # # ]: 0 : if (written < 0)
681 : : {
682 : 0 : g_task_return_error (task, error);
683 : 0 : g_object_unref (task);
684 : 0 : return;
685 : : }
686 : :
687 : 0 : data->offset += written;
688 : :
689 [ # # ]: 0 : if (data->offset == data->length)
690 : : {
691 : 0 : g_free (data->buffer);
692 : :
693 : 0 : data->buffer = g_malloc0 (SOCKS5_NEGO_REP_LEN);
694 : 0 : data->length = SOCKS5_NEGO_REP_LEN;
695 : 0 : data->offset = 0;
696 : :
697 : 0 : do_read (nego_reply_read_cb, task, data);
698 : : }
699 : : else
700 : : {
701 : 0 : do_write (nego_msg_write_cb, task, data);
702 : : }
703 : : }
704 : :
705 : : static void
706 : 0 : nego_reply_read_cb (GObject *source,
707 : : GAsyncResult *res,
708 : : gpointer user_data)
709 : : {
710 : 0 : GTask *task = user_data;
711 : 0 : ConnectAsyncData *data = g_task_get_task_data (task);
712 : 0 : GError *error = NULL;
713 : : gssize read;
714 : :
715 : 0 : read = g_input_stream_read_finish (G_INPUT_STREAM (source),
716 : : res, &error);
717 : :
718 [ # # ]: 0 : if (read < 0)
719 : : {
720 : 0 : g_task_return_error (task, error);
721 : 0 : g_object_unref (task);
722 : 0 : return;
723 : : }
724 : :
725 [ # # ]: 0 : if (read == 0)
726 : : {
727 : 0 : g_task_return_new_error_literal (task,
728 : : G_IO_ERROR,
729 : : G_IO_ERROR_CONNECTION_CLOSED,
730 : : "Connection to SOCKSv5 proxy server lost");
731 : 0 : g_object_unref (task);
732 : 0 : return;
733 : : }
734 : :
735 : 0 : data->offset += read;
736 : :
737 [ # # ]: 0 : if (data->offset == data->length)
738 : : {
739 : 0 : gboolean must_auth = FALSE;
740 [ # # # # ]: 0 : gboolean has_auth = data->username || data->password;
741 : :
742 [ # # ]: 0 : if (!parse_nego_reply (data->buffer, has_auth, &must_auth, &error))
743 : : {
744 : 0 : g_task_return_error (task, error);
745 : 0 : g_object_unref (task);
746 : 0 : return;
747 : : }
748 : :
749 [ # # ]: 0 : if (must_auth)
750 : : {
751 : 0 : g_free (data->buffer);
752 : :
753 : 0 : data->buffer = g_malloc0 (SOCKS5_AUTH_MSG_LEN);
754 : 0 : data->length = set_auth_msg (data->buffer,
755 : 0 : data->username,
756 : 0 : data->password,
757 : : &error);
758 : 0 : data->offset = 0;
759 : :
760 [ # # ]: 0 : if (data->length < 0)
761 : : {
762 : 0 : g_task_return_error (task, error);
763 : 0 : g_object_unref (task);
764 : 0 : return;
765 : : }
766 : :
767 : 0 : do_write (auth_msg_write_cb, task, data);
768 : : }
769 : : else
770 : : {
771 : 0 : send_connect_msg (task);
772 : : }
773 : : }
774 : : else
775 : : {
776 : 0 : do_read (nego_reply_read_cb, task, data);
777 : : }
778 : : }
779 : :
780 : : static void
781 : 0 : auth_msg_write_cb (GObject *source,
782 : : GAsyncResult *result,
783 : : gpointer user_data)
784 : : {
785 : 0 : GTask *task = user_data;
786 : 0 : ConnectAsyncData *data = g_task_get_task_data (task);
787 : 0 : GError *error = NULL;
788 : : gssize written;
789 : :
790 : 0 : written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
791 : : result, &error);
792 : :
793 [ # # ]: 0 : if (written < 0)
794 : : {
795 : 0 : g_task_return_error (task, error);
796 : 0 : g_object_unref (task);
797 : 0 : return;
798 : : }
799 : :
800 : 0 : data->offset += written;
801 : :
802 [ # # ]: 0 : if (data->offset == data->length)
803 : : {
804 : 0 : g_free (data->buffer);
805 : :
806 : 0 : data->buffer = g_malloc0 (SOCKS5_NEGO_REP_LEN);
807 : 0 : data->length = SOCKS5_NEGO_REP_LEN;
808 : 0 : data->offset = 0;
809 : :
810 : 0 : do_read (auth_reply_read_cb, task, data);
811 : : }
812 : : else
813 : : {
814 : 0 : do_write (auth_msg_write_cb, task, data);
815 : : }
816 : : }
817 : :
818 : : static void
819 : 0 : auth_reply_read_cb (GObject *source,
820 : : GAsyncResult *result,
821 : : gpointer user_data)
822 : : {
823 : 0 : GTask *task = user_data;
824 : 0 : ConnectAsyncData *data = g_task_get_task_data (task);
825 : 0 : GError *error = NULL;
826 : : gssize read;
827 : :
828 : 0 : read = g_input_stream_read_finish (G_INPUT_STREAM (source),
829 : : result, &error);
830 : :
831 [ # # ]: 0 : if (read < 0)
832 : : {
833 : 0 : g_task_return_error (task, error);
834 : 0 : g_object_unref (task);
835 : 0 : return;
836 : : }
837 : :
838 [ # # ]: 0 : if (read == 0)
839 : : {
840 : 0 : g_task_return_new_error_literal (task,
841 : : G_IO_ERROR,
842 : : G_IO_ERROR_CONNECTION_CLOSED,
843 : : "Connection to SOCKSv5 proxy server lost");
844 : 0 : g_object_unref (task);
845 : 0 : return;
846 : : }
847 : :
848 : 0 : data->offset += read;
849 : :
850 [ # # ]: 0 : if (data->offset == data->length)
851 : : {
852 [ # # ]: 0 : if (!check_auth_status (data->buffer, &error))
853 : : {
854 : 0 : g_task_return_error (task, error);
855 : 0 : g_object_unref (task);
856 : 0 : return;
857 : : }
858 : :
859 : 0 : send_connect_msg (task);
860 : : }
861 : : else
862 : : {
863 : 0 : do_read (auth_reply_read_cb, task, data);
864 : : }
865 : : }
866 : :
867 : : static void
868 : 0 : send_connect_msg (GTask *task)
869 : : {
870 : 0 : ConnectAsyncData *data = g_task_get_task_data (task);
871 : 0 : GError *error = NULL;
872 : :
873 : 0 : g_free (data->buffer);
874 : :
875 : 0 : data->buffer = g_malloc0 (SOCKS5_CONN_MSG_LEN);
876 : 0 : data->length = set_connect_msg (data->buffer,
877 : 0 : data->hostname,
878 : 0 : data->port,
879 : : &error);
880 : 0 : data->offset = 0;
881 : :
882 [ # # ]: 0 : if (data->length < 0)
883 : : {
884 : 0 : g_task_return_error (task, error);
885 : 0 : g_object_unref (task);
886 : 0 : return;
887 : : }
888 : :
889 : 0 : do_write (connect_msg_write_cb, task, data);
890 : : }
891 : :
892 : : static void
893 : 0 : connect_msg_write_cb (GObject *source,
894 : : GAsyncResult *result,
895 : : gpointer user_data)
896 : : {
897 : 0 : GTask *task = user_data;
898 : 0 : ConnectAsyncData *data = g_task_get_task_data (task);
899 : 0 : GError *error = NULL;
900 : : gssize written;
901 : :
902 : 0 : written = g_output_stream_write_finish (G_OUTPUT_STREAM (source),
903 : : result, &error);
904 : :
905 [ # # ]: 0 : if (written < 0)
906 : : {
907 : 0 : g_task_return_error (task, error);
908 : 0 : g_object_unref (task);
909 : 0 : return;
910 : : }
911 : :
912 : 0 : data->offset += written;
913 : :
914 [ # # ]: 0 : if (data->offset == data->length)
915 : : {
916 : 0 : g_free (data->buffer);
917 : :
918 : 0 : data->buffer = g_malloc0 (SOCKS5_CONN_REP_LEN);
919 : 0 : data->length = 4;
920 : 0 : data->offset = 0;
921 : :
922 : 0 : do_read (connect_reply_read_cb, task, data);
923 : : }
924 : : else
925 : : {
926 : 0 : do_write (connect_msg_write_cb, task, data);
927 : : }
928 : : }
929 : :
930 : : static void
931 : 0 : connect_reply_read_cb (GObject *source,
932 : : GAsyncResult *result,
933 : : gpointer user_data)
934 : : {
935 : 0 : GTask *task = user_data;
936 : 0 : ConnectAsyncData *data = g_task_get_task_data (task);
937 : 0 : GError *error = NULL;
938 : : gssize read;
939 : :
940 : 0 : read = g_input_stream_read_finish (G_INPUT_STREAM (source),
941 : : result, &error);
942 : :
943 [ # # ]: 0 : if (read < 0)
944 : : {
945 : 0 : g_task_return_error (task, error);
946 : 0 : g_object_unref (task);
947 : 0 : return;
948 : : }
949 : :
950 [ # # ]: 0 : if (read == 0)
951 : : {
952 : 0 : g_task_return_new_error_literal (task,
953 : : G_IO_ERROR,
954 : : G_IO_ERROR_CONNECTION_CLOSED,
955 : : "Connection to SOCKSv5 proxy server lost");
956 : 0 : g_object_unref (task);
957 : 0 : return;
958 : : }
959 : :
960 : 0 : data->offset += read;
961 : :
962 [ # # ]: 0 : if (data->offset == data->length)
963 : : {
964 : : gint atype;
965 : :
966 [ # # ]: 0 : if (!parse_connect_reply (data->buffer, &atype, &error))
967 : : {
968 : 0 : g_task_return_error (task, error);
969 : 0 : g_object_unref (task);
970 : 0 : return;
971 : : }
972 : :
973 [ # # # # ]: 0 : switch (atype)
974 : : {
975 : 0 : case SOCKS5_ATYP_IPV4:
976 : 0 : data->length = 6;
977 : 0 : data->offset = 0;
978 : 0 : do_read (connect_addr_read_cb, task, data);
979 : 0 : break;
980 : :
981 : 0 : case SOCKS5_ATYP_IPV6:
982 : 0 : data->length = 18;
983 : 0 : data->offset = 0;
984 : 0 : do_read (connect_addr_read_cb, task, data);
985 : 0 : break;
986 : :
987 : 0 : case SOCKS5_ATYP_DOMAINNAME:
988 : 0 : data->length = 1;
989 : 0 : data->offset = 0;
990 : 0 : do_read (connect_addr_len_read_cb, task, data);
991 : 0 : break;
992 : : }
993 : : }
994 : : else
995 : : {
996 : 0 : do_read (connect_reply_read_cb, task, data);
997 : : }
998 : : }
999 : :
1000 : : static void
1001 : 0 : connect_addr_len_read_cb (GObject *source,
1002 : : GAsyncResult *result,
1003 : : gpointer user_data)
1004 : : {
1005 : 0 : GTask *task = user_data;
1006 : 0 : ConnectAsyncData *data = g_task_get_task_data (task);
1007 : 0 : GError *error = NULL;
1008 : : gssize read;
1009 : :
1010 : 0 : read = g_input_stream_read_finish (G_INPUT_STREAM (source),
1011 : : result, &error);
1012 : :
1013 [ # # ]: 0 : if (read < 0)
1014 : : {
1015 : 0 : g_task_return_error (task, error);
1016 : 0 : g_object_unref (task);
1017 : 0 : return;
1018 : : }
1019 : :
1020 [ # # ]: 0 : if (read == 0)
1021 : : {
1022 : 0 : g_task_return_new_error_literal (task,
1023 : : G_IO_ERROR,
1024 : : G_IO_ERROR_CONNECTION_CLOSED,
1025 : : "Connection to SOCKSv5 proxy server lost");
1026 : 0 : g_object_unref (task);
1027 : 0 : return;
1028 : : }
1029 : :
1030 : 0 : data->length = data->buffer[0] + 2;
1031 : 0 : data->offset = 0;
1032 : :
1033 : 0 : do_read (connect_addr_read_cb, task, data);
1034 : : }
1035 : :
1036 : : static void
1037 : 0 : connect_addr_read_cb (GObject *source,
1038 : : GAsyncResult *result,
1039 : : gpointer user_data)
1040 : : {
1041 : 0 : GTask *task = user_data;
1042 : 0 : ConnectAsyncData *data = g_task_get_task_data (task);
1043 : 0 : GError *error = NULL;
1044 : : gssize read;
1045 : :
1046 : 0 : read = g_input_stream_read_finish (G_INPUT_STREAM (source),
1047 : : result, &error);
1048 : :
1049 [ # # ]: 0 : if (read < 0)
1050 : : {
1051 : 0 : g_task_return_error (task, error);
1052 : 0 : g_object_unref (task);
1053 : 0 : return;
1054 : : }
1055 : :
1056 [ # # ]: 0 : if (read == 0)
1057 : : {
1058 : 0 : g_task_return_new_error_literal (task,
1059 : : G_IO_ERROR,
1060 : : G_IO_ERROR_CONNECTION_CLOSED,
1061 : : "Connection to SOCKSv5 proxy server lost");
1062 : 0 : g_object_unref (task);
1063 : 0 : return;
1064 : : }
1065 : :
1066 : 0 : data->offset += read;
1067 : :
1068 [ # # ]: 0 : if (data->offset == data->length)
1069 : : {
1070 : 0 : g_task_return_pointer (task, g_object_ref (data->io_stream), g_object_unref);
1071 : 0 : g_object_unref (task);
1072 : 0 : return;
1073 : : }
1074 : : else
1075 : : {
1076 : 0 : do_read (connect_reply_read_cb, task, data);
1077 : : }
1078 : : }
1079 : :
1080 : : static GIOStream *
1081 : 0 : g_socks5_proxy_connect_finish (GProxy *proxy,
1082 : : GAsyncResult *result,
1083 : : GError **error)
1084 : : {
1085 : 0 : return g_task_propagate_pointer (G_TASK (result), error);
1086 : : }
1087 : :
1088 : : static gboolean
1089 : 0 : g_socks5_proxy_supports_hostname (GProxy *proxy)
1090 : : {
1091 : 0 : return TRUE;
1092 : : }
1093 : :
1094 : : static void
1095 : 0 : g_socks5_proxy_class_init (GSocks5ProxyClass *class)
1096 : : {
1097 : : GObjectClass *object_class;
1098 : :
1099 : 0 : object_class = (GObjectClass *) class;
1100 : 0 : object_class->finalize = g_socks5_proxy_finalize;
1101 : 0 : }
1102 : :
1103 : : static void
1104 : 0 : g_socks5_proxy_iface_init (GProxyInterface *proxy_iface)
1105 : : {
1106 : 0 : proxy_iface->connect = g_socks5_proxy_connect;
1107 : 0 : proxy_iface->connect_async = g_socks5_proxy_connect_async;
1108 : 0 : proxy_iface->connect_finish = g_socks5_proxy_connect_finish;
1109 : 0 : proxy_iface->supports_hostname = g_socks5_proxy_supports_hostname;
1110 : 0 : }
|