Tizen 2.1 base
[framework/uifw/ecore.git] / src / lib / ecore_con / ecore_con_socks.c
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_SYS_SOCKET_H
14 # include <sys/socket.h>
15 #endif
16
17 #ifdef HAVE_NETINET_TCP_H
18 # include <netinet/tcp.h>
19 #endif
20
21 #ifdef HAVE_NET_IF_H
22 # include <net/if.h>
23 #endif
24
25 /* if net/if.h is not found or if an older versions of net/if.h is provided
26    which does not define IF_NAMESIZE. We must define it ourselves */
27 #ifndef IF_NAMESIZE
28 #  ifdef IFNAMSIZ
29 #    define IF_NAMESIZE IFNAMSIZ
30 #  else
31 #    define IF_NAMESIZE 16
32 #  endif
33 #endif
34
35 #ifdef HAVE_NETINET_IN_H
36 # include <netinet/in.h>
37 #endif
38
39 #ifdef HAVE_ARPA_INET_H
40 # include <arpa/inet.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 /* http://tools.ietf.org/html/rfc1928
61           o  X'00' NO AUTHENTICATION REQUIRED
62           o  X'01' GSSAPI
63           o  X'02' USERNAME/PASSWORD
64           o  X'03' to X'7F' IANA ASSIGNED
65           o  X'80' to X'FE' RESERVED FOR PRIVATE METHODS
66           o  X'FF' NO ACCEPTABLE METHODS
67 */
68 #define ECORE_CON_SOCKS_V5_METHOD_NONE 0
69 #define ECORE_CON_SOCKS_V5_METHOD_GSSAPI 1
70 #define ECORE_CON_SOCKS_V5_METHOD_USERPASS 2
71
72 static int ECORE_CON_SOCKS_V5_METHODS[] =
73 {
74    ECORE_CON_SOCKS_V5_METHOD_NONE,
75 //   ECORE_CON_SOCKS_V5_METHOD_GSSAPI, TODO
76    ECORE_CON_SOCKS_V5_METHOD_USERPASS
77 };
78
79 #define ECORE_CON_SOCKS_V5_TOTAL_METHODS sizeof(ECORE_CON_SOCKS_V5_METHODS)
80
81 #define _ecore_con_server_kill(svr) do { \
82    DBG("KILL %p", (svr)); \
83    _ecore_con_server_kill((svr)); \
84 } while (0)
85
86 Eina_List *ecore_con_socks_proxies = NULL;
87
88 static Ecore_Con_Socks *
89 _ecore_con_socks_find(unsigned char version, const char *ip, int port, const char *username, size_t ulen, const char *password, size_t plen)
90 {
91    Eina_List *l;
92    Ecore_Con_Socks_v5 *ecs;
93
94    if (!ecore_con_socks_proxies) return NULL;
95
96    EINA_LIST_FOREACH(ecore_con_socks_proxies, l, ecs)
97      {
98         if (ecs->version != version) continue;
99         if (strcmp(ecs->ip, ip)) continue;
100         if ((port != -1) && (port != ecs->port)) continue;
101         if (ulen != ecs->ulen) continue;
102         if (username && strcmp(ecs->username, username)) continue;
103         if (version == 5)
104           {
105              if (plen != ecs->plen) continue;
106              if (password && strcmp(ecs->password, password)) continue;
107           }
108         return (Ecore_Con_Socks*)ecs;
109      }
110    return NULL;
111 }
112
113 static void
114 _ecore_con_socks_free(Ecore_Con_Socks *ecs)
115 {
116    ECORE_CON_SOCKS_CAST_ELSE(ecs) return;
117
118    if (_ecore_con_proxy_once == ecs) _ecore_con_proxy_once = NULL;
119    if (_ecore_con_proxy_global == ecs) _ecore_con_proxy_global = NULL;
120    eina_stringshare_del(ecs->ip);
121    eina_stringshare_del(ecs->username);
122    free(ecs);
123 }
124
125 static Eina_Bool
126 _ecore_con_socks_svr_init_v4(Ecore_Con_Server *svr, Ecore_Con_Socks_v4 *v4)
127 {
128    size_t addrlen, buflen, ulen = 1;
129    unsigned char *sbuf;
130
131    addrlen = v4->lookup ? strlen(svr->name) + 1 : 0;
132    if (v4->username) ulen += v4->ulen;
133    buflen = sizeof(char) * (8  + ulen + addrlen);
134    sbuf = malloc(buflen);
135    if (!sbuf)
136      {
137         ecore_con_event_server_error(svr, "Memory allocation failure!");
138         _ecore_con_server_kill(svr);
139         return EINA_FALSE;
140      }
141    /* http://en.wikipedia.org/wiki/SOCKS */
142    sbuf[0] = 4;
143    sbuf[1] = v4->bind ? 2 : 1;
144    sbuf[2] = svr->port >> 8;
145    sbuf[3] = svr->port & 0xff;
146    if (addrlen)
147      {
148         sbuf[4] = sbuf[5] = sbuf[6] = 0;
149         sbuf[7] = 1;
150      }
151    else
152      /* SOCKSv4 only handles IPV4, so addrlen is always 4 */
153      memcpy(sbuf + 4, svr->ecs_addr, 4);
154    if (v4->username)
155      memcpy(sbuf + 8, v4->username, ulen);
156    else
157      sbuf[8] = 0;
158    if (addrlen) memcpy(sbuf + 8 + ulen, svr->name, addrlen);
159
160    svr->ecs_buf = eina_binbuf_manage_new_length(sbuf, buflen);
161    return EINA_TRUE;
162 }
163
164 static Eina_Bool
165 _ecore_con_socks_svr_init_v5(Ecore_Con_Server *svr, Ecore_Con_Socks_v5 *v5)
166 {
167    size_t buflen;
168    unsigned int x;
169    unsigned char *sbuf;
170
171    if (v5->username)
172      buflen = sizeof(char) * (2  + ECORE_CON_SOCKS_V5_TOTAL_METHODS);
173    else
174      buflen = 3;
175    sbuf = malloc(buflen);
176    if (!sbuf)
177      {
178         ecore_con_event_server_error(svr, "Memory allocation failure!");
179         _ecore_con_server_kill(svr);
180         return EINA_FALSE;
181      }
182    /* http://en.wikipedia.org/wiki/SOCKS
183     * http://tools.ietf.org/html/rfc1928
184     */
185    sbuf[0] = 5;
186    if (v5->username)
187      {
188        sbuf[1] = ECORE_CON_SOCKS_V5_TOTAL_METHODS;
189        for (x = 2; x < 2 + ECORE_CON_SOCKS_V5_TOTAL_METHODS; x++)
190          sbuf[x] = ECORE_CON_SOCKS_V5_METHODS[x - 2];
191      }
192    else
193      {
194         sbuf[1] = 1;
195         sbuf[2] = ECORE_CON_SOCKS_V5_METHOD_NONE;
196      }
197
198    svr->ecs_buf = eina_binbuf_manage_new_length(sbuf, buflen);
199    return EINA_TRUE;
200 }
201
202 #define ECORE_CON_SOCKS_READ(EXACT) \
203           if (num < EXACT) \
204             { \
205                if (!svr->ecs_recvbuf) svr->ecs_recvbuf = eina_binbuf_new(); \
206                if (!svr->ecs_recvbuf) goto error; \
207                eina_binbuf_append_length(svr->ecs_recvbuf, buf, num); \
208                /* the slowest connection on earth */ \
209                if (eina_binbuf_length_get(svr->ecs_recvbuf) != EXACT) return; \
210                data = eina_binbuf_string_get(svr->ecs_recvbuf); \
211             } \
212           else if (num > EXACT) goto error; \
213           else \
214             data = buf
215
216 static void
217 _ecore_con_socks_read_v4(Ecore_Con_Server *svr, Ecore_Con_Socks_v4 *v4 __UNUSED__, const unsigned char *buf, unsigned int num)
218 {
219    const unsigned char *data;
220    DBG("SOCKS: %d bytes", num);
221    ECORE_CON_SOCKS_READ(8);
222
223 /* http://ufasoft.com/doc/socks4_protocol.htm */
224    if (data[0]) goto error;
225    switch (data[1])
226      {
227       case 90:
228         /* success! */
229         break;
230       case 91:
231         ecore_con_event_server_error(svr, "proxy request rejected or failed");
232         goto error;
233       case 92:
234         ecore_con_event_server_error(svr, "proxying SOCKS server could not perform authentication");
235         goto error;
236       case 93:
237         ecore_con_event_server_error(svr, "proxy request authentication rejected");
238         goto error;
239       default:
240         ecore_con_event_server_error(svr, "garbage data from proxy");
241         goto error;
242      }
243    if (svr->ecs->bind)
244      {
245         unsigned int nport;
246         char naddr[IF_NAMESIZE];
247
248         memcpy(&nport, &data[2], 2);
249         svr->proxyport = ntohl(nport);
250
251         if (!inet_ntop(AF_INET, &data[4], naddr, sizeof(naddr))) goto error;
252         svr->proxyip = eina_stringshare_add(naddr);
253         ecore_con_event_proxy_bind(svr);
254      }
255    svr->ecs_state = ECORE_CON_PROXY_STATE_DONE;
256    INF("PROXY CONNECTED");
257    if (svr->ecs_recvbuf) eina_binbuf_free(svr->ecs_recvbuf);
258    svr->ecs_recvbuf = NULL;
259    svr->ecs_buf_offset = svr->ecs_addrlen = 0;
260    memset(svr->ecs_addr, 0, sizeof(svr->ecs_addr));
261    if (!svr->ssl_state)
262      ecore_con_event_server_add(svr);
263    if (svr->ssl_state || (svr->buf && eina_binbuf_length_get(svr->buf)))
264      ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_READ | ECORE_FD_WRITE);
265    return;
266 error:
267    _ecore_con_server_kill(svr);
268 }
269
270 static Eina_Bool
271 _ecore_con_socks_auth_v5(Ecore_Con_Server *svr, Ecore_Con_Socks_v5 *v5)
272 {
273    size_t size;
274    unsigned char *data;
275    switch (v5->method)
276      {
277         case ECORE_CON_SOCKS_V5_METHOD_NONE:
278           svr->ecs_state = ECORE_CON_PROXY_STATE_REQUEST;
279           return EINA_TRUE;
280         case ECORE_CON_SOCKS_V5_METHOD_GSSAPI:
281           return EINA_TRUE;
282         case ECORE_CON_SOCKS_V5_METHOD_USERPASS:
283           if (!v5->username) return EINA_FALSE;
284           if (!v5->password) v5->plen = 1;
285           /* http://tools.ietf.org/html/rfc1929 */
286           size = sizeof(char) * (3 + v5->ulen + v5->plen);
287           data = malloc(size);
288           if (!data) break;
289           data[0] = 1;
290           data[1] = v5->ulen;
291           memcpy(&data[2], v5->username, v5->ulen);
292           data[1 + v5->ulen] = v5->plen;
293           if (v5->password)
294             memcpy(&data[2 + v5->ulen], v5->password, v5->plen);
295           else
296             data[2 + v5->ulen] = 0;
297           svr->ecs_buf = eina_binbuf_manage_new_length(data, size);
298           return EINA_TRUE;
299         default:
300           break;
301      }
302    return EINA_FALSE;
303 }
304
305 static void
306 _ecore_con_socks_read_v5(Ecore_Con_Server *svr, Ecore_Con_Socks_v5 *v5, const unsigned char *buf, unsigned int num)
307 {
308    const unsigned char *data;
309
310    DBG("SOCKS: %d bytes", num);
311    switch (svr->ecs_state)
312      {
313
314         case ECORE_CON_PROXY_STATE_READ:
315           ECORE_CON_SOCKS_READ(2);
316           /* http://en.wikipedia.org/wiki/SOCKS */
317           if (data[0] != 5) goto error;
318           if (data[1] == 0xFF)
319             {
320                ecore_con_event_server_error(svr, "proxy authentication methods rejected");
321                goto error;
322             }
323           v5->method = data[1];
324           if (!_ecore_con_socks_auth_v5(svr, v5)) goto error;
325           if (svr->ecs_state == ECORE_CON_PROXY_STATE_REQUEST)
326             {
327                /* run again to skip auth reading */
328                _ecore_con_socks_read_v5(svr, v5, NULL, 0);
329                return;
330             }
331           ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_WRITE);
332           svr->ecs_state = ECORE_CON_PROXY_STATE_AUTH;
333           break;
334         case ECORE_CON_PROXY_STATE_AUTH:
335           ECORE_CON_SOCKS_READ(2);
336           switch (v5->method)
337             {
338              case ECORE_CON_SOCKS_V5_METHOD_NONE:
339                CRIT("HOW DID THIS HAPPEN?????????");
340                goto error;
341              case ECORE_CON_SOCKS_V5_METHOD_GSSAPI:
342                /* TODO: this */
343                break;
344              case ECORE_CON_SOCKS_V5_METHOD_USERPASS:
345                if (data[0] != 1)
346                  {
347                     ecore_con_event_server_error(svr, "protocol error");
348                     goto error; /* wrong version */
349                  }
350                if (data[1])
351                  {
352                     ecore_con_event_server_error(svr, "proxy request authentication rejected");
353                     goto error;
354                  }
355              default:
356                break;
357             }
358         case ECORE_CON_PROXY_STATE_REQUEST:
359           {
360              size_t addrlen, buflen;
361              unsigned char *sbuf;
362              addrlen = v5->lookup ? strlen(svr->name) + 1 : (unsigned int)svr->ecs_addrlen;
363              buflen = sizeof(char) * (6 + addrlen);
364              sbuf = malloc(buflen);
365              if (!sbuf)
366                {
367                   ecore_con_event_server_error(svr, "Memory allocation failure!");
368                   goto error;
369                }
370               sbuf[0] = 5;
371               sbuf[1] = v5->bind ? 2 : 1; /* TODO: 0x03 for UDP port association */
372               sbuf[2] = 0;
373               if (v5->lookup) /* domain name */
374                 {
375                    sbuf[3] = 3;
376                    sbuf[4] = addrlen - 1;
377                    memcpy(sbuf + 5, svr->name, addrlen - 1);
378                 }
379               else
380                 {
381                    sbuf[3] = (svr->ecs_addrlen == 4) ? 1 : 4;
382                    memcpy(sbuf + 4, svr->ecs_addr, addrlen);
383                 }
384               sbuf[addrlen + 4] = svr->port >> 8;
385               sbuf[addrlen + 5] = svr->port & 0xff;
386
387               svr->ecs_buf = eina_binbuf_manage_new_length(sbuf, buflen);
388               ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_WRITE);
389               break;
390           }
391         case ECORE_CON_PROXY_STATE_CONFIRM:
392           {
393              /* this is ugly because we have to read an exact number of bytes,
394               * but we don't know what that number is until we've already read
395               * at least 5 bytes to determine the length of the unknown stream.
396               * yep.
397               */
398              size_t to_read, len = svr->ecs_recvbuf ? eina_binbuf_length_get(svr->ecs_recvbuf) : 0;
399              if (num + len < 5)
400                {
401                   /* guarantees we get called again */
402                   ECORE_CON_SOCKS_READ(5);
403                }
404              if (len >= 5)
405                {
406                   data = eina_binbuf_string_get(svr->ecs_recvbuf);
407                   data += 3;
408                }
409              else
410                data = buf + 3 - len;
411              switch (data[0])
412                {
413                   case 1:
414                     to_read = 4;
415                     break;
416                   case 3:
417                     to_read = data[1] + 1;
418                     break;
419                   case 4:
420                     to_read = 16;
421                     /* lazy debugging stub comment */
422                     break;
423                   default:
424                     ecore_con_event_server_error(svr, "protocol error");
425                     goto error;
426                }
427              /* at this point, we finally know exactly how much we need to read */
428              ECORE_CON_SOCKS_READ(6 + to_read);
429
430              if (data[0] != 5)
431                {
432                   ecore_con_event_server_error(svr, "protocol error");
433                   goto error; /* wrong version */
434                }
435              switch (data[1])
436                {
437                   case 0:
438                     break;
439                   case 1:
440                     ecore_con_event_server_error(svr, "general proxy failure");
441                     goto error;
442                   case 2:
443                     ecore_con_event_server_error(svr, "connection not allowed by ruleset");
444                     goto error;
445                   case 3:
446                     ecore_con_event_server_error(svr, "network unreachable");
447                     goto error;
448                   case 4:
449                     ecore_con_event_server_error(svr, "host unreachable");
450                     goto error;
451                   case 5:
452                     ecore_con_event_server_error(svr, "connection refused by destination host");
453                     goto error;
454                   case 6:
455                     ecore_con_event_server_error(svr, "TTL expired");
456                     goto error;
457                   case 7:
458                     ecore_con_event_server_error(svr, "command not supported / protocol error");
459                     goto error;
460                   case 8:
461                     ecore_con_event_server_error(svr, "address type not supported");
462                   default:
463                     goto error;
464                }
465              if (data[2])
466                {
467                   ecore_con_event_server_error(svr, "protocol error");
468                   goto error;
469                }
470              memset(svr->ecs_addr, 0, sizeof(svr->ecs_addr));
471              if (!svr->ssl_state)
472                ecore_con_event_server_add(svr);
473              if (svr->ssl_state || (svr->buf && eina_binbuf_length_get(svr->buf)))
474                ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_READ | ECORE_FD_WRITE);
475              svr->ecs_buf_offset = svr->ecs_addrlen = 0;
476              svr->ecs_state = ECORE_CON_PROXY_STATE_DONE;
477              INF("PROXY CONNECTED");
478              break;
479           }
480       default:
481         break;
482      }
483    if (svr->ecs_recvbuf) eina_binbuf_free(svr->ecs_recvbuf);
484    svr->ecs_recvbuf = NULL;
485    
486    return;
487 error:
488    _ecore_con_server_kill(svr);
489 }
490
491 /////////////////////////////////////////////////////////////////////////////////////
492 void
493 ecore_con_socks_shutdown(void)
494 {
495    Ecore_Con_Socks *ecs;
496    EINA_LIST_FREE(ecore_con_socks_proxies, ecs)
497      _ecore_con_socks_free(ecs);
498    _ecore_con_proxy_once = NULL;
499    _ecore_con_proxy_global = NULL;
500 }
501
502 void
503 ecore_con_socks_read(Ecore_Con_Server *svr, unsigned char *buf, int num)
504 {
505    ECORE_CON_SOCKS_CAST_ELSE(svr->ecs) return;
506
507    if (svr->ecs_state < ECORE_CON_PROXY_STATE_READ) return;
508
509    if (v4) _ecore_con_socks_read_v4(svr, v4, buf, (unsigned int)num);
510    else _ecore_con_socks_read_v5(svr, v5, buf, (unsigned int)num);
511 }
512
513 Eina_Bool
514 ecore_con_socks_svr_init(Ecore_Con_Server *svr)
515 {
516    ECORE_CON_SOCKS_CAST_ELSE(svr->ecs) return EINA_FALSE;
517
518    if (!svr->ip) return EINA_FALSE;
519    if (svr->ecs_buf) return EINA_FALSE;
520    if (svr->ecs_state != ECORE_CON_PROXY_STATE_INIT) return EINA_FALSE;
521    ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_WRITE);
522    if (v4) return _ecore_con_socks_svr_init_v4(svr, v4);
523    return _ecore_con_socks_svr_init_v5(svr, v5);
524 }
525
526 void
527 ecore_con_socks_dns_cb(const char *canonname __UNUSED__, const char *ip, struct sockaddr *addr, int addrlen __UNUSED__, Ecore_Con_Server *svr)
528 {
529    svr->ip = eina_stringshare_add(ip);
530    svr->ecs_state++;
531    if (addr->sa_family == AF_INET)
532      {
533         memcpy(svr->ecs_addr, &((struct sockaddr_in *)addr)->sin_addr.s_addr, 4);
534         svr->ecs_addrlen = 4;
535      }
536 #ifdef HAVE_IPV6
537    else
538      {
539         memcpy(svr->ecs_addr, &((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, 16);
540         svr->ecs_addrlen = 16;
541      }
542 #endif
543    ecore_con_socks_svr_init(svr);
544 }
545
546 void
547 ecore_con_socks_init(void)
548 {
549    const char *socks;
550    char *h, *p, *l, *u = NULL;
551    char buf[512];
552    int port, lookup = 0;
553    Eina_Bool v5 = EINA_FALSE;
554    Ecore_Con_Socks *ecs;
555    unsigned char addr[sizeof(struct in_addr)];
556 #ifdef HAVE_IPV6
557    unsigned char addr6[sizeof(struct in6_addr)];
558 #endif
559
560    /* ECORE_CON_SOCKS_V4=[user@]host-port:[1|0] */
561    socks = getenv("ECORE_CON_SOCKS_V4");
562    if (!socks)
563      {
564         /* ECORE_CON_SOCKS_V5=[user@]host-port:[1|0] */
565         socks = getenv("ECORE_CON_SOCKS_V5");
566         v5 = EINA_TRUE;
567      }
568    if ((!socks) || (!socks[0]) || (strlen(socks) > 512)) return;
569    strncpy(buf, socks, sizeof(buf));
570    h = strchr(buf, '@');
571    /* username */
572    if (h && (h - buf > 0)) *h++ = 0, u = buf;
573    else h = buf;
574
575    /* host ip; I ain't resolvin shit here */
576    p = strchr(h, '-');
577    if (!p) return;
578    *p++ = 0;
579    if (!inet_pton(AF_INET, h, addr))
580 #ifdef HAVE_IPV6
581      {
582         if (!v5) return;
583         if (!inet_pton(AF_INET6, h, addr6))
584           return;
585      }
586 #else
587      return;
588 #endif
589
590    errno = 0;
591    port = strtol(p, &l, 10);
592    if (errno || (port < 0) || (port > 65535)) return;
593    if (l && (l[0] == ':'))
594      lookup = (l[1] == '1');
595    if (v5)
596      ecs = ecore_con_socks5_remote_add(h, port, u, NULL);
597    else
598      ecs = ecore_con_socks4_remote_add(h, port, u);
599    if (!ecs) return;
600    ecore_con_socks_lookup_set(ecs, lookup);
601    ecore_con_socks_apply_always(ecs);
602    INF("Added global proxy server %s%s%s:%d - DNS lookup %s",
603        u ?: "", u ? "@" : "", h, port, lookup ? "ENABLED" : "DISABLED");
604 }
605
606 /////////////////////////////////////////////////////////////////////////////////////
607
608 /**
609  * @defgroup Ecore_Con_Socks_Group Ecore Connection SOCKS functions
610  * @{
611  */
612
613 /**
614  * Add a SOCKS v4 proxy to the proxy list
615  *
616  * Use this to create (or return, if previously added) a SOCKS proxy
617  * object which can be used by any ecore_con servers.
618  * @param ip The ip address of the proxy (NOT DOMAIN NAME. IP ADDRESS.)
619  * @param port The port to connect to on the proxy
620  * @param username The username to use for the proxy (OPTIONAL)
621  * @return An allocated proxy object, or NULL on failure
622  * @note This object NEVER needs to be explicitly freed.
623  * @since 1.2
624  */
625 EAPI Ecore_Con_Socks *
626 ecore_con_socks4_remote_add(const char *ip, int port, const char *username)
627 {
628    Ecore_Con_Socks *ecs;
629    size_t ulen = 0;
630
631    if ((!ip) || (!ip[0]) || (port < 0) || (port > 65535)) return NULL;
632
633    if (username)
634      {
635         ulen = strlen(username);
636         /* max length for protocol */
637         if ((!ulen) || (ulen > 255)) return NULL;
638      }
639    ecs = _ecore_con_socks_find(4, ip, port, username, ulen, NULL, 0);
640    if (ecs) return ecs;
641
642    ecs = calloc(1, sizeof(Ecore_Con_Socks_v4));
643    if (!ecs) return NULL;
644
645    ecs->version = 4;
646    ecs->ip = eina_stringshare_add(ip);
647    ecs->port = port;
648    ecs->username = eina_stringshare_add(username);
649    ecs->ulen = ulen;
650    ecore_con_socks_proxies = eina_list_append(ecore_con_socks_proxies, ecs);
651    return ecs;
652 }
653
654 /**
655  * Find a SOCKS v4 proxy in the proxy list
656  *
657  * Use this to determine if a SOCKS proxy was previously added by checking
658  * the proxy list against the parameters given.
659  * @param ip The ip address of the proxy (NOT DOMAIN NAME. IP ADDRESS.)
660  * @param port The port to connect to on the proxy, or -1 to match the first proxy with @p ip
661  * @param username The username used for the proxy (OPTIONAL)
662  * @return true only if a proxy exists matching the given params
663  * @note This function matches slightly more loosely than ecore_con_socks4_remote_add(), and
664  * ecore_con_socks4_remote_add() should be used to return the actual object.
665  * @since 1.2
666  */
667 EAPI Eina_Bool
668 ecore_con_socks4_remote_exists(const char *ip, int port, const char *username)
669 {
670    if ((!ip) || (!ip[0]) || (port < -1) || (port > 65535) || (username && (!username[0])))
671      return EINA_FALSE;
672    return !!_ecore_con_socks_find(4, ip, port, username, username ? strlen(username) : 0, NULL, 0);
673 }
674
675 /**
676  * Remove a SOCKS v4 proxy from the proxy list and delete it
677  *
678  * Use this to remove a SOCKS proxy from the proxy list by checking
679  * the list against the parameters given. The proxy will then be deleted.
680  * @param ip The ip address of the proxy (NOT DOMAIN NAME. IP ADDRESS.)
681  * @param port The port to connect to on the proxy, or -1 to match the first proxy with @p ip
682  * @param username The username used for the proxy (OPTIONAL)
683  * @note This function matches in the same way as ecore_con_socks4_remote_exists().
684  * @warning Be aware that deleting a proxy which is being used WILL ruin your life.
685  * @since 1.2
686  */
687 EAPI void
688 ecore_con_socks4_remote_del(const char *ip, int port, const char *username)
689 {
690    Ecore_Con_Socks_v4 *v4;
691
692    if ((!ip) || (!ip[0]) || (port < -1) || (port > 65535) || (username && (!username[0]))) return;
693    if (!ecore_con_socks_proxies) return;
694
695    v4 = (Ecore_Con_Socks_v4*)_ecore_con_socks_find(4, ip, port, username, username ? strlen(username) : 0, NULL, 0);
696    if (!v4) return;
697    ecore_con_socks_proxies = eina_list_remove(ecore_con_socks_proxies, v4);
698    _ecore_con_socks_free((Ecore_Con_Socks*)v4);
699 }
700 /**
701  * Add a SOCKS v5 proxy to the proxy list
702  *
703  * Use this to create (or return, if previously added) a SOCKS proxy
704  * object which can be used by any ecore_con servers.
705  * @param ip The ip address of the proxy (NOT DOMAIN NAME. IP ADDRESS.)
706  * @param port The port to connect to on the proxy
707  * @param username The username to use for the proxy (OPTIONAL)
708  * @param password The password to use for the proxy (OPTIONAL)
709  * @return An allocated proxy object, or NULL on failure
710  * @note This object NEVER needs to be explicitly freed.
711  * @since 1.2
712  */
713 EAPI Ecore_Con_Socks *
714 ecore_con_socks5_remote_add(const char *ip, int port, const char *username, const char *password)
715 {
716    Ecore_Con_Socks_v5 *ecs5;
717    size_t ulen = 0, plen = 0;
718
719    if ((!ip) || (!ip[0]) || (port < 0) || (port > 65535)) return NULL;
720
721    if (username)
722      {
723         ulen = strlen(username);
724         /* max length for protocol */
725         if ((!ulen) || (ulen > 255)) return NULL;
726      }
727    if (password)
728      {
729         plen = strlen(password);
730         /* max length for protocol */
731         if ((!plen) || (plen > 255)) return NULL;
732      }
733    ecs5 = (Ecore_Con_Socks_v5*)_ecore_con_socks_find(5, ip, port, username, ulen, password, plen);
734    if (ecs5) return (Ecore_Con_Socks*)ecs5;
735
736    ecs5 = calloc(1, sizeof(Ecore_Con_Socks_v5));
737    if (!ecs5) return NULL;
738
739    ecs5->version = 5;
740    ecs5->ip = eina_stringshare_add(ip);
741    ecs5->port = port;
742    ecs5->username = eina_stringshare_add(username);
743    ecs5->ulen = ulen;
744    ecs5->password = eina_stringshare_add(password);
745    ecs5->plen = plen;
746    ecore_con_socks_proxies = eina_list_append(ecore_con_socks_proxies, ecs5);
747    return (Ecore_Con_Socks*)ecs5;
748 }
749
750 /**
751  * Find a SOCKS v5 proxy in the proxy list
752  *
753  * Use this to determine if a SOCKS proxy was previously added by checking
754  * the proxy list against the parameters given.
755  * @param ip The ip address of the proxy (NOT DOMAIN NAME. IP ADDRESS.)
756  * @param port The port to connect to on the proxy, or -1 to match the first proxy with @p ip
757  * @param username The username used for the proxy (OPTIONAL)
758  * @param password The password used for the proxy (OPTIONAL)
759  * @return true only if a proxy exists matching the given params
760  * @note This function matches slightly more loosely than ecore_con_socks5_remote_add(), and
761  * ecore_con_socks5_remote_add() should be used to return the actual object.
762  * @since 1.2
763  */
764 EAPI Eina_Bool
765 ecore_con_socks5_remote_exists(const char *ip, int port, const char *username, const char *password)
766 {
767    if ((!ip) || (!ip[0]) || (port < -1) || (port > 65535) || (username && (!username[0])) || (password && (!password[0])))
768      return EINA_FALSE;
769    return !!_ecore_con_socks_find(5, ip, port, username, username ? strlen(username) : 0, password, password ? strlen(password) : 0);
770 }
771
772 /**
773  * Remove a SOCKS v5 proxy from the proxy list and delete it
774  *
775  * Use this to remove a SOCKS proxy from the proxy list by checking
776  * the list against the parameters given. The proxy will then be deleted.
777  * @param ip The ip address of the proxy (NOT DOMAIN NAME. IP ADDRESS.)
778  * @param port The port to connect to on the proxy, or -1 to match the first proxy with @p ip
779  * @param username The username used for the proxy (OPTIONAL)
780  * @param password The password used for the proxy (OPTIONAL)
781  * @note This function matches in the same way as ecore_con_socks4_remote_exists().
782  * @warning Be aware that deleting a proxy which is being used WILL ruin your life.
783  * @since 1.2
784  */
785 EAPI void
786 ecore_con_socks5_remote_del(const char *ip, int port, const char *username, const char *password)
787 {
788    Ecore_Con_Socks_v5 *v5;
789
790    if ((!ip) || (!ip[0]) || (port < -1) || (port > 65535) || (username && (!username[0])) || (password && (!password[0])))
791      return;
792    if (!ecore_con_socks_proxies) return;
793
794    v5 = (Ecore_Con_Socks_v5*)_ecore_con_socks_find(5, ip, port, username, username ? strlen(username) : 0, password, password ? strlen(password) : 0);
795    if (!v5) return;
796    ecore_con_socks_proxies = eina_list_remove(ecore_con_socks_proxies, v5);
797    _ecore_con_socks_free((Ecore_Con_Socks*)v5);
798 }
799
800 /**
801  * Set DNS lookup mode on an existing SOCKS proxy
802  *
803  * According to RFC, SOCKS v4 does not require that a proxy perform
804  * its own DNS lookups for addresses. SOCKS v4a specifies the protocol
805  * for this. SOCKS v5 allows DNS lookups.
806  * If you want to enable remote DNS lookup and are sure that your
807  * proxy supports it, use this function.
808  * @param ecs The proxy object
809  * @param enable If true, the proxy will perform the dns lookup
810  * @note By default, this setting is DISABLED.
811  * @since 1.2
812  */
813 EAPI void
814 ecore_con_socks_lookup_set(Ecore_Con_Socks *ecs, Eina_Bool enable)
815 {
816    ECORE_CON_SOCKS_CAST_ELSE(ecs) return;
817    ecs->lookup = !!enable;
818 }
819
820 /**
821  * Get DNS lookup mode on an existing SOCKS proxy
822  *
823  * According to RFC, SOCKS v4 does not require that a proxy perform
824  * its own DNS lookups for addresses. SOCKS v4a specifies the protocol
825  * for this. SOCKS v5 allows DNS lookups.
826  * This function returns whether lookups are enabled on a proxy object.
827  * @param ecs The proxy object
828  * @return If true, the proxy will perform the dns lookup
829  * @note By default, this setting is DISABLED.
830  * @since 1.2
831  */
832 EAPI Eina_Bool
833 ecore_con_socks_lookup_get(Ecore_Con_Socks *ecs)
834 {
835    ECORE_CON_SOCKS_CAST_ELSE(ecs) return EINA_FALSE;
836    return ecs->lookup;
837 }
838
839 /**
840  * Enable bind mode on a SOCKS proxy
841  *
842  * Use this function to enable binding a remote port for use with a remote server.
843  * For more information, see http://ufasoft.com/doc/socks4_protocol.htm
844  * @param ecs The proxy object
845  * @param is_bind If true, the connection established will be a port binding
846  * @warning Be aware that changing the operation mode of an active proxy may result in undefined behavior
847  * @since 1.2
848  */
849 EAPI void
850 ecore_con_socks_bind_set(Ecore_Con_Socks *ecs, Eina_Bool is_bind)
851 {
852    EINA_SAFETY_ON_NULL_RETURN(ecs);
853    ecs->bind = !!is_bind;
854 }
855
856 /**
857  * Return bind mode of a SOCKS proxy
858  *
859  * Use this function to return bind mode of a proxy (binding a remote port for use with a remote server).
860  * For more information, see http://ufasoft.com/doc/socks4_protocol.htm
861  * @param ecs The proxy object
862  * @return If true, the connection established will be a port binding
863  * @since 1.2
864  */
865 EAPI Eina_Bool
866 ecore_con_socks_bind_get(Ecore_Con_Socks *ecs)
867 {
868    EINA_SAFETY_ON_NULL_RETURN_VAL(ecs, EINA_FALSE);
869    return ecs->bind;
870 }
871
872 /**
873  * Return SOCKS version of a SOCKS proxy
874  *
875  * Use this function to return the SOCKS protocol version of a proxy
876  * @param ecs The proxy object
877  * @return 0 on error, else 4/5
878  * @since 1.2
879  */
880 EAPI unsigned int
881 ecore_con_socks_version_get(Ecore_Con_Socks *ecs)
882 {
883    EINA_SAFETY_ON_NULL_RETURN_VAL(ecs, 0);
884    return ecs->version;
885 }
886
887 /**
888  * Remove a SOCKS v4 proxy from the proxy list and delete it
889  *
890  * Use this to remove a SOCKS proxy from the proxy list by directly deleting the object given.
891  * @param ecs The proxy object to delete
892  * @warning Be aware that deleting a proxy which is being used WILL ruin your life.
893  * @since 1.2
894  */
895 EAPI void
896 ecore_con_socks_remote_del(Ecore_Con_Socks *ecs)
897 {
898    EINA_SAFETY_ON_NULL_RETURN(ecs);
899    if (!ecore_con_socks_proxies) return;
900
901    ecore_con_socks_proxies = eina_list_remove(ecore_con_socks_proxies, ecs);
902    _ecore_con_socks_free(ecs);
903 }
904
905 /**
906  * Set a proxy object to be used with the next server created with ecore_con_server_connect()
907  *
908  * This function sets a proxy for the next ecore_con connection. After the next server is created,
909  * the proxy will NEVER be applied again unless explicitly enabled.
910  * @param ecs The proxy object
911  * @see ecore_con_socks_apply_always()
912  * @since 1.2
913  */
914 EAPI void
915 ecore_con_socks_apply_once(Ecore_Con_Socks *ecs)
916 {
917    _ecore_con_proxy_once = ecs;
918 }
919
920 /**
921  * Set a proxy object to be used with all servers created with ecore_con_server_connect()
922  *
923  * This function sets a proxy for all ecore_con connections. It will always be used.
924  * @param ecs The proxy object
925  * @see ecore_con_socks_apply_once()
926  * @since 1.2
927  * @note ecore-con supports setting this through environment variables like so:
928  *   ECORE_CON_SOCKS_V4=[user@]server-port:lookup
929  *   ECORE_CON_SOCKS_V5=[user@]server-port:lookup
930  * user is the OPTIONAL string that would be passed to the proxy as the username
931  * server is the IP_ADDRESS of the proxy server
932  * port is the port to connect to on the proxy server
933  * lookup is 1 if the proxy should perform all DNS lookups, otherwise 0 or omitted
934  */
935 EAPI void
936 ecore_con_socks_apply_always(Ecore_Con_Socks *ecs)
937 {
938    _ecore_con_proxy_global = ecs;
939 }
940 /** @} */