aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/ecore/src/lib/ecore_con/ecore_con_socks.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/ecore/src/lib/ecore_con/ecore_con_socks.c')
-rw-r--r--libraries/ecore/src/lib/ecore_con/ecore_con_socks.c496
1 files changed, 496 insertions, 0 deletions
diff --git a/libraries/ecore/src/lib/ecore_con/ecore_con_socks.c b/libraries/ecore/src/lib/ecore_con/ecore_con_socks.c
new file mode 100644
index 0000000..aecaff0
--- /dev/null
+++ b/libraries/ecore/src/lib/ecore_con/ecore_con_socks.c
@@ -0,0 +1,496 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <stdio.h>
6#include <string.h>
7#include <sys/types.h>
8#include <sys/stat.h>
9#include <errno.h>
10#include <unistd.h>
11#include <fcntl.h>
12
13#ifdef HAVE_NETINET_TCP_H
14# include <netinet/tcp.h>
15#endif
16
17#ifdef HAVE_NET_IF_H
18# include <net/if.h>
19#endif
20
21/* if net/if.h is not found or if an older versions of net/if.h is provided
22 which does not define IF_NAMESIZE. We must define it ourselves */
23#ifndef IF_NAMESIZE
24# ifdef IFNAMSIZ
25# define IF_NAMESIZE IFNAMSIZ
26# else
27# define IF_NAMESIZE 16
28# endif
29#endif
30
31#ifdef HAVE_NETINET_IN_H
32# include <netinet/in.h>
33#endif
34
35#ifdef HAVE_ARPA_INET_H
36# include <arpa/inet.h>
37#endif
38
39#ifdef HAVE_SYS_SOCKET_H
40# include <sys/socket.h>
41#endif
42
43#ifdef HAVE_SYS_UN_H
44# include <sys/un.h>
45#endif
46
47#ifdef HAVE_WS2TCPIP_H
48# include <ws2tcpip.h>
49#endif
50
51#ifdef HAVE_EVIL
52# include <Evil.h>
53#endif
54
55#include "Ecore.h"
56#include "ecore_private.h"
57#include "Ecore_Con.h"
58#include "ecore_con_private.h"
59
60#define _ecore_con_server_kill(svr) do { \
61 DBG("KILL %p", (svr)); \
62 _ecore_con_server_kill((svr)); \
63} while (0)
64
65Eina_List *ecore_con_socks_proxies = NULL;
66
67static Ecore_Con_Socks *
68_ecore_con_socks_find(unsigned char version, const char *ip, int port, const char *username)
69{
70 Eina_List *l;
71 Ecore_Con_Socks *ecs;
72
73 if (!ecore_con_socks_proxies) return NULL;
74
75 EINA_LIST_FOREACH(ecore_con_socks_proxies, l, ecs)
76 {
77 if (ecs->version != version) continue;
78 if (strcmp(ecs->ip, ip)) continue;
79 if ((port != -1) && (port != ecs->port)) continue;
80 if (username && strcmp(ecs->username, username)) continue;
81 return ecs;
82 }
83 return NULL;
84}
85
86static void
87_ecore_con_socks_free(Ecore_Con_Socks *ecs)
88{
89 ECORE_CON_SOCKS_CAST_ELSE(ecs) return;
90
91 if (_ecore_con_proxy_once == ecs) _ecore_con_proxy_once = NULL;
92 if (_ecore_con_proxy_global == ecs) _ecore_con_proxy_global = NULL;
93 eina_stringshare_del(ecs->ip);
94 eina_stringshare_del(ecs->username);
95 free(ecs);
96}
97/////////////////////////////////////////////////////////////////////////////////////
98void
99ecore_con_socks_shutdown(void)
100{
101 Ecore_Con_Socks *ecs;
102 EINA_LIST_FREE(ecore_con_socks_proxies, ecs)
103 _ecore_con_socks_free(ecs);
104 _ecore_con_proxy_once = NULL;
105 _ecore_con_proxy_global = NULL;
106}
107
108void
109ecore_con_socks_read(Ecore_Con_Server *svr, unsigned char *buf, int num)
110{
111 const unsigned char *data;
112 ECORE_CON_SOCKS_CAST_ELSE(svr->ecs) return;
113
114 if (svr->ecs_state != ECORE_CON_SOCKS_STATE_READ) return;
115
116 if (v4)
117 {
118 DBG("SOCKS: %d bytes", num);
119 if (num < 8)
120 {
121 if (!svr->ecs_recvbuf) svr->ecs_recvbuf = eina_binbuf_new();
122 if (!svr->ecs_recvbuf) goto error;
123 eina_binbuf_append_length(svr->ecs_recvbuf, buf, num);
124 /* the slowest connection on earth */
125 if (eina_binbuf_length_get(svr->ecs_recvbuf) != 8) return;
126 data = eina_binbuf_string_get(svr->ecs_recvbuf);
127 }
128 else if (num > 8) goto error;
129 else
130 data = buf;
131
132 /* http://ufasoft.com/doc/socks4_protocol.htm */
133 if (data[0]) goto error;
134 switch (data[1])
135 {
136 case 90:
137 /* success! */
138 break;
139 case 91:
140 ecore_con_event_server_error(svr, "proxy request rejected or failed");
141 goto error;
142 case 92:
143 ecore_con_event_server_error(svr, "proxying SOCKS server could not perform authentication");
144 goto error;
145 case 93:
146 ecore_con_event_server_error(svr, "proxy request authentication rejected");
147 goto error;
148 default:
149 ecore_con_event_server_error(svr, "garbage data from proxy");
150 goto error;
151 }
152 if (svr->ecs->bind)
153 {
154 unsigned int nport;
155 char naddr[IF_NAMESIZE];
156
157 memcpy(&nport, &data[2], 2);
158 svr->proxyport = ntohl(nport);
159
160 if (!inet_ntop(AF_INET, &data[4], naddr, sizeof(naddr))) goto error;
161 svr->proxyip = eina_stringshare_add(naddr);
162 ecore_con_event_proxy_bind(svr);
163 }
164 svr->ecs_state = ECORE_CON_SOCKS_STATE_DONE;
165 INF("PROXY CONNECTED");
166 if (svr->ecs_recvbuf) eina_binbuf_free(svr->ecs_recvbuf);
167 svr->ecs_recvbuf = NULL;
168 svr->ecs_buf_offset = svr->ecs_addrlen = 0;
169 memset(svr->ecs_addr, 0, sizeof(svr->ecs_addr));
170 if (!svr->ssl_state)
171 ecore_con_event_server_add(svr);
172 if (svr->ssl_state || (svr->buf && eina_binbuf_length_get(svr->buf)))
173 ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_READ | ECORE_FD_WRITE);
174 }
175 return;
176error:
177 _ecore_con_server_kill(svr);
178}
179
180Eina_Bool
181ecore_con_socks_svr_init(Ecore_Con_Server *svr)
182{
183 unsigned char *sbuf;
184 ECORE_CON_SOCKS_CAST_ELSE(svr->ecs) return EINA_FALSE;
185
186 if (!svr->ip) return EINA_FALSE;
187 if (svr->ecs_buf) return EINA_FALSE;
188 if (svr->ecs_state != ECORE_CON_SOCKS_STATE_INIT) return EINA_FALSE;
189 ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_WRITE);
190 if (v4)
191 {
192 size_t addrlen, buflen, ulen = 1;
193 addrlen = svr->ecs->lookup ? strlen(svr->name) + 1: 0;
194 if (svr->ecs->username) ulen += strlen(svr->ecs->username);
195 buflen = sizeof(char) * (8 + ulen + addrlen);
196 sbuf = malloc(buflen);
197 if (!sbuf)
198 {
199 ecore_con_event_server_error(svr, "Memory allocation failure!");
200 _ecore_con_server_kill(svr);
201 return EINA_FALSE;
202 }
203 /* http://en.wikipedia.org/wiki/SOCKS */
204 sbuf[0] = 4;
205 sbuf[1] = v4->bind ? 2 : 1;
206 sbuf[2] = svr->port >> 8;
207 sbuf[3] = svr->port & 0xff;
208 if (addrlen)
209 {
210 sbuf[4] = sbuf[5] = sbuf[6] = 0;
211 sbuf[7] = 1;
212 }
213 else
214 memcpy(sbuf + 4, svr->ecs_addr, 4);
215 if (svr->ecs->username)
216 memcpy(sbuf + 8, svr->ecs->username, ulen);
217 else
218 sbuf[8] = 0;
219 if (addrlen) memcpy(sbuf + 8 + ulen, svr->name, addrlen);
220
221 svr->ecs_buf = eina_binbuf_manage_new_length(sbuf, buflen);
222 }
223 return EINA_TRUE;
224}
225
226void
227ecore_con_socks_dns_cb(const char *canonname __UNUSED__, const char *ip, struct sockaddr *addr, int addrlen, Ecore_Con_Server *svr)
228{
229 svr->ip = eina_stringshare_add(ip);
230 svr->ecs_addrlen = addrlen;
231 svr->ecs_state++;
232 if (addr->sa_family == AF_INET)
233 memcpy(svr->ecs_addr, &((struct sockaddr_in *)addr)->sin_addr.s_addr, 4);
234#ifdef HAVE_IPV6
235 else
236 memcpy(svr->ecs_addr, &((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, addrlen);
237#endif
238 ecore_con_socks_svr_init(svr);
239}
240
241void
242ecore_con_socks_init(void)
243{
244 const char *socks;
245 char *u, *h, *p, *l;
246 char buf[64];
247 int port, lookup = 0;
248 Ecore_Con_Socks *ecs;
249 unsigned char addr[sizeof(struct in_addr)];
250
251 /* ECORE_CON_SOCKS_V4=user@host:port:[1|0] */
252 socks = getenv("ECORE_CON_SOCKS_V4");
253 if ((!socks) || (!socks[0]) || (strlen(socks) > 64)) return;
254 strncpy(buf, socks, sizeof(buf));
255 h = strchr(buf, '@');
256 u = NULL;
257 /* username */
258 if (h && (h - buf > 0)) *h++ = 0, u = buf;
259 else h = buf;
260
261 /* host ip; I ain't resolvin shit here */
262 p = strchr(h, ':');
263 if (!p) return;
264 *p++ = 0;
265 if (!inet_pton(AF_INET, h, addr)) return;
266
267 errno = 0;
268 port = strtol(p, &l, 10);
269 if (errno || (port < 0) || (port > 65535)) return;
270 if (l && (l[0] == ':'))
271 lookup = (l[1] == '1');
272 ecs = ecore_con_socks4_remote_add(h, port, u);
273 if (!ecs) return;
274 ecore_con_socks4_lookup_set(ecs, lookup);
275 ecore_con_socks_apply_always(ecs);
276 INF("Added global proxy server %s%s%s:%d - DNS lookup %s",
277 u ?: "", u ? "@" : "", h, port, lookup ? "ENABLED" : "DISABLED");
278}
279
280/////////////////////////////////////////////////////////////////////////////////////
281
282/**
283 * @defgroup Ecore_Con_Socks_Group Ecore Connection SOCKS functions
284 * @{
285 */
286
287/**
288 * Add a SOCKS v4 proxy to the proxy list
289 *
290 * Use this to create (or return, if previously added) a SOCKS proxy
291 * object which can be used by any ecore_con servers.
292 * @param ip The ip address of the proxy (NOT DOMAIN NAME. IP ADDRESS.)
293 * @param port The port to connect to on the proxy
294 * @param username The username to use for the proxy (OPTIONAL)
295 * @return An allocated proxy object, or NULL on failure
296 * @note This object NEVER needs to be explicitly freed.
297 * @since 1.2
298 */
299EAPI Ecore_Con_Socks *
300ecore_con_socks4_remote_add(const char *ip, int port, const char *username)
301{
302 Ecore_Con_Socks *ecs;
303
304 if ((!ip) || (!ip[0]) || (port < 0) || (port > 65535)) return NULL;
305
306 ecs = _ecore_con_socks_find(4, ip, port, username);
307 if (ecs) return ecs;
308
309 ecs = calloc(1, sizeof(Ecore_Con_Socks_v4));
310 if (!ecs) return NULL;
311
312 ecs->version = 4;
313 ecs->ip = eina_stringshare_add(ip);
314 ecs->port = port;
315 ecs->username = eina_stringshare_add(username);
316 ecore_con_socks_proxies = eina_list_append(ecore_con_socks_proxies, ecs);
317 return ecs;
318}
319
320/**
321 * Set DNS lookup mode on an existing SOCKS v4 proxy
322 *
323 * According to RFC, SOCKS v4 does not require that a proxy perform
324 * its own DNS lookups for addresses. SOCKS v4a specifies the protocol
325 * for this. If you want to enable remote DNS lookup and are sure that your
326 * proxy supports it, use this function.
327 * @param ecs The proxy object
328 * @param enable If true, the proxy will perform the dns lookup
329 * @note By default, this setting is DISABLED.
330 * @since 1.2
331 */
332EAPI void
333ecore_con_socks4_lookup_set(Ecore_Con_Socks *ecs, Eina_Bool enable)
334{
335 ECORE_CON_SOCKS_CAST_ELSE(ecs) return;
336 if (v4) v4->lookup = !!enable;
337}
338
339/**
340 * Get DNS lookup mode on an existing SOCKS v4 proxy
341 *
342 * According to RFC, SOCKS v4 does not require that a proxy perform
343 * its own DNS lookups for addresses. SOCKS v4a specifies the protocol
344 * for this. This function returns whether lookups are enabled on a proxy object.
345 * @param ecs The proxy object
346 * @return If true, the proxy will perform the dns lookup
347 * @note By default, this setting is DISABLED.
348 * @since 1.2
349 */
350EAPI Eina_Bool
351ecore_con_socks4_lookup_get(Ecore_Con_Socks *ecs)
352{
353 ECORE_CON_SOCKS_CAST_ELSE(ecs) return EINA_FALSE;
354 return v4 ? v4->lookup : EINA_FALSE;
355}
356
357/**
358 * Find a SOCKS v4 proxy in the proxy list
359 *
360 * Use this to determine if a SOCKS proxy was previously added by checking
361 * the proxy list against the parameters given.
362 * @param ip The ip address of the proxy (NOT DOMAIN NAME. IP ADDRESS.)
363 * @param port The port to connect to on the proxy, or -1 to match the first proxy with @p ip
364 * @param username The username used for the proxy (OPTIONAL)
365 * @return true only if a proxy exists matching the given params
366 * @note This function matches slightly more loosely than ecore_con_socks4_remote_add(), and
367 * ecore_con_socks4_remote_add() should be used to return the actual object.
368 * @since 1.2
369 */
370EAPI Eina_Bool
371ecore_con_socks4_remote_exists(const char *ip, int port, const char *username)
372{
373 if ((!ip) || (!ip[0]) || (port < -1) || (port > 65535) || (username && (!username[0])))
374 return EINA_FALSE;
375 return !!_ecore_con_socks_find(4, ip, port, username);
376}
377
378/**
379 * Remove a SOCKS v4 proxy from the proxy list and delete it
380 *
381 * Use this to remove a SOCKS proxy from the proxy list by checking
382 * the list against the parameters given. The proxy will then be deleted.
383 * @param ip The ip address of the proxy (NOT DOMAIN NAME. IP ADDRESS.)
384 * @param port The port to connect to on the proxy, or -1 to match the first proxy with @p ip
385 * @param username The username used for the proxy (OPTIONAL)
386 * @note This function matches in the same way as ecore_con_socks4_remote_exists().
387 * @warning Be aware that deleting a proxy which is being used WILL ruin your life.
388 * @since 1.2
389 */
390EAPI void
391ecore_con_socks4_remote_del(const char *ip, int port, const char *username)
392{
393 Ecore_Con_Socks_v4 *v4;
394
395 if ((!ip) || (!ip[0]) || (port < -1) || (port > 65535) || (username && (!username[0]))) return;
396 if (!ecore_con_socks_proxies) return;
397
398 v4 = (Ecore_Con_Socks_v4*)_ecore_con_socks_find(4, ip, port, username);
399 if (!v4) return;
400 ecore_con_socks_proxies = eina_list_remove(ecore_con_socks_proxies, v4);
401 _ecore_con_socks_free((Ecore_Con_Socks*)v4);
402}
403
404/**
405 * Enable bind mode on a SOCKS proxy
406 *
407 * Use this function to enable binding a remote port for use with a remote server.
408 * For more information, see http://ufasoft.com/doc/socks4_protocol.htm
409 * @param ecs The proxy object
410 * @param is_bind If true, the connection established will be a port binding
411 * @warning Be aware that changing the operation mode of an active proxy may result in undefined behavior
412 * @since 1.2
413 */
414EAPI void
415ecore_con_socks_bind_set(Ecore_Con_Socks *ecs, Eina_Bool is_bind)
416{
417 EINA_SAFETY_ON_NULL_RETURN(ecs);
418 ecs->bind = !!is_bind;
419}
420
421/**
422 * Return bind mode of a SOCKS proxy
423 *
424 * Use this function to return bind mode of a proxy (binding a remote port for use with a remote server).
425 * For more information, see http://ufasoft.com/doc/socks4_protocol.htm
426 * @param ecs The proxy object
427 * @return If true, the connection established will be a port binding
428 * @since 1.2
429 */
430EAPI Eina_Bool
431ecore_con_socks_bind_get(Ecore_Con_Socks *ecs)
432{
433 EINA_SAFETY_ON_NULL_RETURN_VAL(ecs, EINA_FALSE);
434 return ecs->bind;
435}
436
437EAPI unsigned int
438ecore_con_socks_version_get(Ecore_Con_Socks *ecs)
439{
440 EINA_SAFETY_ON_NULL_RETURN_VAL(ecs, 0);
441 return ecs->version;
442}
443
444/**
445 * Remove a SOCKS v4 proxy from the proxy list and delete it
446 *
447 * Use this to remove a SOCKS proxy from the proxy list by directly deleting the object given.
448 * @param ecs The proxy object to delete
449 * @warning Be aware that deleting a proxy which is being used WILL ruin your life.
450 * @since 1.2
451 */
452EAPI void
453ecore_con_socks_remote_del(Ecore_Con_Socks *ecs)
454{
455 EINA_SAFETY_ON_NULL_RETURN(ecs);
456 if (!ecore_con_socks_proxies) return;
457
458 ecore_con_socks_proxies = eina_list_remove(ecore_con_socks_proxies, ecs);
459 _ecore_con_socks_free(ecs);
460}
461
462/**
463 * Set a proxy object to be used with the next server created with ecore_con_server_connect()
464 *
465 * This function sets a proxy for the next ecore_con connection. After the next server is created,
466 * the proxy will NEVER be applied again unless explicitly enabled.
467 * @param ecs The proxy object
468 * @see ecore_con_socks_apply_always()
469 * @since 1.2
470 */
471EAPI void
472ecore_con_socks_apply_once(Ecore_Con_Socks *ecs)
473{
474 _ecore_con_proxy_once = ecs;
475}
476
477/**
478 * Set a proxy object to be used with all servers created with ecore_con_server_connect()
479 *
480 * This function sets a proxy for all ecore_con connections. It will always be used.
481 * @param ecs The proxy object
482 * @see ecore_con_socks_apply_once()
483 * @since 1.2
484 * @note ecore-con supports setting this through environment variables like so:
485 * ECORE_CON_SOCKS_V4=[user@]server:port:lookup
486 * user is the OPTIONAL string that would be passed to the proxy as the username
487 * server is the IP_ADDRESS of the proxy server
488 * port is the port to connect to on the proxy server
489 * lookup is 1 if the proxy should perform all DNS lookups, otherwise 0 or omitted
490 */
491EAPI void
492ecore_con_socks_apply_always(Ecore_Con_Socks *ecs)
493{
494 _ecore_con_proxy_global = ecs;
495}
496/** @} */