Add support for droping packets when using TCP and the bandwidth is too slow for...
[sipe-libnice.git] / socket / socks5.c
blob22e435d8b7a04590a92e2969a69ed61f79ea8dc9
1 /*
2 * This file is part of the Nice GLib ICE library.
4 * (C) 2008 Collabora Ltd.
5 * Contact: Youness Alaoui
6 * (C) 2008 Nokia Corporation. All rights reserved.
8 * The contents of this file are subject to the Mozilla Public License Version
9 * 1.1 (the "License"); you may not use this file except in compliance with
10 * the License. You may obtain a copy of the License at
11 * http://www.mozilla.org/MPL/
13 * Software distributed under the License is distributed on an "AS IS" basis,
14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
15 * for the specific language governing rights and limitations under the
16 * License.
18 * The Original Code is the Nice GLib ICE library.
20 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
21 * Corporation. All Rights Reserved.
23 * Contributors:
24 * Youness Alaoui, Collabora Ltd.
26 * Alternatively, the contents of this file may be used under the terms of the
27 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
28 * case the provisions of LGPL are applicable instead of those above. If you
29 * wish to allow use of your version of this file only under the terms of the
30 * LGPL and not to allow others to use your version of this file under the
31 * MPL, indicate your decision by deleting the provisions above and replace
32 * them with the notice and other provisions required by the LGPL. If you do
33 * not delete the provisions above, a recipient may use your version of this
34 * file under either the MPL or the LGPL.
38 * Implementation of TCP relay socket interface using TCP Berkeley sockets. (See
39 * http://en.wikipedia.org/wiki/Berkeley_sockets.)
41 #ifdef HAVE_CONFIG_H
42 # include "config.h"
43 #endif
45 #include "socks5.h"
47 #include <string.h>
49 #ifndef G_OS_WIN32
50 #include <unistd.h>
51 #endif
53 typedef enum {
54 SOCKS_STATE_INIT,
55 SOCKS_STATE_AUTH,
56 SOCKS_STATE_CONNECT,
57 SOCKS_STATE_CONNECTED,
58 SOCKS_STATE_ERROR
59 } SocksState;
61 typedef struct {
62 SocksState state;
63 NiceSocket *base_socket;
64 NiceAddress addr;
65 gchar *username;
66 gchar *password;
67 GQueue send_queue;
68 } Socks5Priv;
71 struct to_be_sent {
72 guint length;
73 gchar *buf;
74 NiceAddress to;
78 static void socket_close (NiceSocket *sock);
79 static gint socket_recv (NiceSocket *sock, NiceAddress *from,
80 guint len, gchar *buf);
81 static gboolean socket_send (NiceSocket *sock, const NiceAddress *to,
82 guint len, const gchar *buf);
83 static gboolean socket_is_reliable (NiceSocket *sock);
85 static void add_to_be_sent (NiceSocket *sock, const NiceAddress *to,
86 const gchar *buf, guint len);
87 static void free_to_be_sent (struct to_be_sent *tbs);
90 NiceSocket *
91 nice_socks5_socket_new (NiceSocket *base_socket,
92 NiceAddress *addr, gchar *username, gchar *password)
94 Socks5Priv *priv;
95 NiceSocket *sock = NULL;
97 if (addr) {
98 sock = g_slice_new0 (NiceSocket);
99 sock->priv = priv = g_slice_new0 (Socks5Priv);
101 priv->base_socket = base_socket;
102 priv->addr = *addr;
103 priv->username = g_strdup (username);
104 priv->password = g_strdup (password);
106 sock->fileno = priv->base_socket->fileno;
107 sock->addr = priv->base_socket->addr;
108 sock->send = socket_send;
109 sock->recv = socket_recv;
110 sock->is_reliable = socket_is_reliable;
111 sock->close = socket_close;
113 /* Send SOCKS5 handshake */
115 gchar msg[4];
116 gint len = 3;
118 msg[0] = 0x05; /* SOCKS version */
119 msg[1] = 0x01; /* number of methods supported */
120 msg[2] = 0x00; /* no authentication method*/
122 g_debug ("user/pass : %s - %s", username, password);
123 /* add support for authentication method */
124 if (username || password) {
125 msg[1] = 0x02; /* number of methods supported */
126 msg[3] = 0x02; /* authentication method */
127 len++;
130 /* We send 'to' NULL because it will always be to an already connected
131 * TCP base socket, which ignores the destination */
132 nice_socket_send (priv->base_socket, NULL, len, msg);
133 priv->state = SOCKS_STATE_INIT;
137 return sock;
141 static void
142 socket_close (NiceSocket *sock)
144 Socks5Priv *priv = sock->priv;
146 if (priv->base_socket)
147 nice_socket_free (priv->base_socket);
149 if (priv->username)
150 g_free (priv->username);
152 if (priv->password)
153 g_free (priv->password);
155 g_queue_foreach (&priv->send_queue, (GFunc) free_to_be_sent, NULL);
156 g_queue_clear (&priv->send_queue);
158 g_slice_free(Socks5Priv, sock->priv);
162 static gint
163 socket_recv (NiceSocket *sock, NiceAddress *from, guint len, gchar *buf)
165 Socks5Priv *priv = sock->priv;
167 switch (priv->state) {
168 case SOCKS_STATE_CONNECTED:
169 if (priv->base_socket)
170 return nice_socket_recv (priv->base_socket, from, len, buf);
171 break;
172 case SOCKS_STATE_INIT:
174 gchar data[2];
175 gint ret = -1;
177 nice_debug ("Socks5 state Init");
179 if (priv->base_socket)
180 ret = nice_socket_recv (priv->base_socket, from, sizeof(data), data);
182 if (ret <= 0) {
183 return ret;
184 } else if(ret == sizeof(data)) {
185 if (data[0] == 0x05) {
186 if (data[1] == 0x02) {
187 gchar msg[515];
188 gint len = 0;
190 if (priv->username || priv->password) {
191 gint ulen = 0;
192 gint plen = 0;
194 if (priv->username)
195 ulen = strlen (priv->username);
197 if (priv->password)
198 plen = strlen (priv->password);
200 msg[len++] = 0x01; /* auth version */
201 msg[len++] = ulen; /* username length */
202 memcpy (msg + len, priv->username, ulen); /* Username */
203 len += ulen;
204 msg[len++] = plen; /* Password length */
205 memcpy (msg + len, priv->password, plen); /* Password */
206 len += plen;
208 nice_socket_send (priv->base_socket, NULL, len, msg);
209 priv->state = SOCKS_STATE_AUTH;
210 } else {
211 /* Authentication required but no auth info available */
212 goto error;
214 } else if (data[1] == 0x00) {
215 goto send_connect;
216 } else {
217 /* method not supported by socks server */
218 goto error;
220 } else {
221 /* invalid SOCKS server version */
222 goto error;
224 } else {
225 /* read error */
226 goto error;
229 break;
230 case SOCKS_STATE_AUTH:
232 gchar data[2];
233 gint ret = -1;
235 nice_debug ("Socks5 state auth");
236 if (priv->base_socket)
237 ret = nice_socket_recv (priv->base_socket, from, sizeof(data), data);
239 if (ret <= 0) {
240 return ret;
241 } else if(ret == sizeof(data)) {
242 if (data[0] == 0x01 && data[1] == 0x00) {
243 /* Authenticated */
244 goto send_connect;
245 } else {
246 /* Authentication failed */
247 goto error;
251 break;
252 case SOCKS_STATE_CONNECT:
254 gchar data[22];
255 gint ret = -1;
257 nice_debug ("Socks5 state connect");
258 if (priv->base_socket)
259 ret = nice_socket_recv (priv->base_socket, from, 4, data);
261 if (ret <= 0) {
262 return ret;
263 } else if(ret == 4) {
264 if (data[0] == 0x05) {
265 switch (data[1]) {
266 case 0x00:
267 if (data[2] == 0x00) {
268 struct to_be_sent *tbs = NULL;
269 switch (data[3]) {
270 case 0x01: /* IPV4 bound address */
271 ret = nice_socket_recv (priv->base_socket, from, 6, data);
272 if (ret != 6) {
273 /* Could not read server bound address */
274 goto error;
276 break;
277 case 0x04: /* IPV6 bound address */
278 ret = nice_socket_recv (priv->base_socket, from, 18, data);
279 if (ret != 18) {
280 /* Could not read server bound address */
281 goto error;
283 break;
284 default:
285 /* Unsupported address type */
286 goto error;
288 while ((tbs = g_queue_pop_head (&priv->send_queue))) {
289 nice_socket_send (priv->base_socket, &tbs->to,
290 tbs->length, tbs->buf);
291 g_free (tbs->buf);
292 g_slice_free (struct to_be_sent, tbs);
294 priv->state = SOCKS_STATE_CONNECTED;
295 } else {
296 /* Wrong reserved value */
297 goto error;
299 break;
300 case 0x01: /* general SOCKS server failure */
301 case 0x02: /* connection not allowed by ruleset */
302 case 0x03: /* Network unreachable */
303 case 0x04: /* Host unreachable */
304 case 0x05: /* Connection refused */
305 case 0x06: /* TTL expired */
306 case 0x07: /* Command not supported */
307 case 0x08: /* Address type not supported */
308 default: /* Unknown error */
309 goto error;
310 break;
312 } else {
313 /* Wrong server version */
314 goto error;
316 } else {
317 /* Invalid data received */
318 goto error;
321 break;
322 default:
323 /* Unknown status */
324 goto error;
327 return 0;
329 send_connect:
331 gchar msg[22];
332 gint len = 0;
333 struct sockaddr_storage name;
334 nice_address_copy_to_sockaddr(&priv->addr, (struct sockaddr *)&name);
336 msg[len++] = 0x05; /* SOCKS version */
337 msg[len++] = 0x01; /* connect command */
338 msg[len++] = 0x00; /* reserved */
339 if (name.ss_family == AF_INET) {
340 msg[len++] = 0x01; /* IPV4 address type */
341 /* Address */
342 memcpy (msg + len, &((struct sockaddr_in *) &name)->sin_addr, 4);
343 len += 4;
344 /* Port */
345 memcpy (msg + len, &((struct sockaddr_in *) &name)->sin_port, 2);
346 len += 2;
347 } else if (name.ss_family == AF_INET6) {
348 msg[len++] = 0x04; /* IPV6 address type */
349 /* Address */
350 memcpy (msg + len, &((struct sockaddr_in6 *) &name)->sin6_addr, 16);
351 len += 16;
352 /* Port */
353 memcpy (msg + len, &((struct sockaddr_in6 *) &name)->sin6_port, 2);
354 len += 2;
357 nice_socket_send (priv->base_socket, NULL, len, msg);
358 priv->state = SOCKS_STATE_CONNECT;
360 return 0;
362 error:
363 nice_debug ("Socks5 error");
364 if (priv->base_socket)
365 nice_socket_free (priv->base_socket);
366 priv->base_socket = NULL;
367 priv->state = SOCKS_STATE_ERROR;
369 return -1;
372 static gboolean
373 socket_send (NiceSocket *sock, const NiceAddress *to,
374 guint len, const gchar *buf)
376 Socks5Priv *priv = sock->priv;
378 if (priv->state == SOCKS_STATE_CONNECTED) {
379 if (priv->base_socket)
380 return nice_socket_send (priv->base_socket, to, len, buf);
381 else
382 return FALSE;
383 } else if (priv->state == SOCKS_STATE_ERROR) {
384 return FALSE;
385 } else {
386 add_to_be_sent (sock, to, buf, len);
388 return TRUE;
392 static gboolean
393 socket_is_reliable (NiceSocket *sock)
395 return TRUE;
399 static void
400 add_to_be_sent (NiceSocket *sock, const NiceAddress *to,
401 const gchar *buf, guint len)
403 Socks5Priv *priv = sock->priv;
404 struct to_be_sent *tbs = NULL;
406 if (len <= 0)
407 return;
409 tbs = g_slice_new0 (struct to_be_sent);
410 tbs->buf = g_memdup (buf, len);
411 tbs->length = len;
412 if (to)
413 tbs->to = *to;
414 g_queue_push_tail (&priv->send_queue, tbs);
419 static void
420 free_to_be_sent (struct to_be_sent *tbs)
422 g_free (tbs->buf);
423 g_slice_free (struct to_be_sent, tbs);