just kidding before, had to break svn at least once today
authordiscomfitor <discomfitor@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Tue, 6 Dec 2011 03:41:16 +0000 (03:41 +0000)
committerdiscomfitor <discomfitor@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Tue, 6 Dec 2011 03:41:16 +0000 (03:41 +0000)
git-svn-id: http://svn.enlightenment.org/svn/e/trunk/ecore@65935 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

src/lib/ecore_con/ecore_con_socks.c [new file with mode: 0644]

diff --git a/src/lib/ecore_con/ecore_con_socks.c b/src/lib/ecore_con/ecore_con_socks.c
new file mode 100644 (file)
index 0000000..d89a3e8
--- /dev/null
@@ -0,0 +1,469 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifdef HAVE_NETINET_TCP_H
+# include <netinet/tcp.h>
+#endif
+
+#ifdef HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_UN_H
+# include <sys/un.h>
+#endif
+
+#ifdef HAVE_WS2TCPIP_H
+# include <ws2tcpip.h>
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "Ecore.h"
+#include "ecore_private.h"
+#include "Ecore_Con.h"
+#include "ecore_con_private.h"
+
+Eina_List *ecore_con_socks_proxies = NULL;
+
+static Ecore_Con_Socks *
+_ecore_con_socks_find(unsigned char version, const char *ip, int port, const char *username)
+{
+   Eina_List *l;
+   Ecore_Con_Socks *ecs;
+   if (!ecore_con_socks_proxies) return NULL;
+
+   EINA_LIST_FOREACH(ecore_con_socks_proxies, l, ecs)
+     {
+        if (ecs->version != version) continue;
+        if (strcmp(ecs->ip, ip)) continue;
+        if ((port != -1) && (port != ecs->port)) continue;
+        if (username && strcmp(ecs->username, username)) continue;
+        return ecs;
+     }
+   return NULL;
+}
+
+static void
+_ecore_con_socks_free(Ecore_Con_Socks *ecs)
+{
+   ECORE_CON_SOCKS_CAST_ELSE(ecs) return;
+
+   if (_ecore_con_proxy_once == ecs) _ecore_con_proxy_once = NULL;
+   if (_ecore_con_proxy_global == ecs) _ecore_con_proxy_global = NULL;
+   eina_stringshare_del(ecs->ip);
+   eina_stringshare_del(ecs->username);
+   free(ecs);
+}
+/////////////////////////////////////////////////////////////////////////////////////
+void
+ecore_con_socks_shutdown(void)
+{
+   Ecore_Con_Socks *ecs;
+   EINA_LIST_FREE(ecore_con_socks_proxies, ecs)
+     _ecore_con_socks_free(ecs);
+   _ecore_con_proxy_once = NULL;
+   _ecore_con_proxy_global = NULL;
+}
+
+void
+ecore_con_socks_read(Ecore_Con_Server *svr, unsigned char *buf, int num)
+{
+   const unsigned char *data;
+   ECORE_CON_SOCKS_CAST_ELSE(svr->ecs) return;
+
+   if (svr->ecs_state != ECORE_CON_SOCKS_STATE_READ) return;
+
+   if (v4)
+     {
+        DBG("SOCKS: %d bytes", num);
+        if (num < 8)
+          {
+             if (!svr->ecs_recvbuf) svr->ecs_recvbuf = eina_binbuf_new();
+             if (!svr->ecs_recvbuf) goto error;
+             eina_binbuf_append_length(svr->ecs_recvbuf, buf, num);
+             /* the slowest connection on earth */
+             if (eina_binbuf_length_get(svr->ecs_recvbuf) != 8) return;
+             data = eina_binbuf_string_get(svr->ecs_recvbuf);
+          }
+        else if (num > 8) goto error;
+        else
+          data = buf;
+
+     /* http://ufasoft.com/doc/socks4_protocol.htm */
+        if (data[0]) goto error;
+        switch (data[1])
+          {
+           case 90:
+             /* success! */
+             break;
+           case 91:
+             ecore_con_event_server_error(svr, "proxy request rejected or failed");
+             goto error;
+           case 92:
+             ecore_con_event_server_error(svr, "proxying SOCKS server could not perform authentication");
+             goto error;
+           case 93:
+             ecore_con_event_server_error(svr, "proxy request authentication rejected");
+             goto error;
+           default:
+             ecore_con_event_server_error(svr, "garbage data from proxy");
+             goto error;
+          }
+        if (svr->ecs->bind)
+          {
+             unsigned int nport;
+             char naddr[IF_NAMESIZE];
+
+             memcpy(&nport, &data[2], 2);
+             svr->proxyport = ntohl(nport);
+
+             if (!inet_ntop(AF_INET, &data[4], naddr, sizeof(naddr))) goto error;
+             svr->proxyip = eina_stringshare_add(naddr);
+             ecore_con_event_proxy_bind(svr);
+          }
+        svr->ecs_state = ECORE_CON_SOCKS_STATE_DONE;
+        INF("PROXY STATE++");
+        if (svr->ecs_recvbuf) eina_binbuf_free(svr->ecs_recvbuf);
+        svr->ecs_recvbuf = NULL;
+        ecore_con_event_server_add(svr);
+     }
+   return;
+error:
+   _ecore_con_server_kill(svr);
+}
+
+Eina_Bool
+ecore_con_socks_svr_init(Ecore_Con_Server *svr)
+{
+   unsigned char *sbuf;
+   ECORE_CON_SOCKS_CAST_ELSE(svr->ecs) return EINA_FALSE;
+
+   if (!svr->ip) return EINA_FALSE;
+   if (svr->ecs_buf) return EINA_FALSE;
+   if (svr->handshaking && svr->ssl_state) return EINA_FALSE;
+   if (svr->ecs_state != ECORE_CON_SOCKS_STATE_INIT) return EINA_FALSE;
+   ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_WRITE);
+   if (v4)
+     {
+        size_t addrlen, ulen = 1;
+        addrlen = svr->ecs->lookup ? strlen(svr->name) + 1: 0;
+        if (svr->ecs->username) ulen += strlen(svr->ecs->username);
+        sbuf = alloca(sizeof(char) * (8  + ulen + addrlen));
+        /* http://en.wikipedia.org/wiki/SOCKS */
+        sbuf[0] = 4;
+        sbuf[1] = v4->bind ? 2 : 1;
+        sbuf[2] = svr->port >> 8;
+        sbuf[3] = svr->port & 0xff;
+        if (addrlen)
+          {
+             sbuf[4] = sbuf[5] = sbuf[6] = 0;
+             sbuf[7] = 1;
+          }
+        else
+          memcpy(sbuf + 4, svr->ecs_addr, 4);
+        if (svr->ecs->username)
+          memcpy(sbuf + 8, svr->ecs->username, ulen);
+        else
+          sbuf[8] = 0;
+        if (addrlen) memcpy(sbuf + 8 + ulen, svr->name, addrlen);
+        
+        svr->ecs_buf = eina_binbuf_new();
+        eina_binbuf_append_length(svr->ecs_buf, sbuf, 8 + ulen + addrlen);
+     }
+   return EINA_TRUE;
+}
+
+void
+ecore_con_socks_dns_cb(const char *canonname __UNUSED__, const char *ip, struct sockaddr *addr, int addrlen, Ecore_Con_Server *svr)
+{
+   svr->ip = eina_stringshare_add(ip);
+   svr->ecs_addrlen = addrlen;
+   svr->ecs_state++;
+   if (addr->sa_family == AF_INET)
+     memcpy(svr->ecs_addr, &((struct sockaddr_in *)addr)->sin_addr.s_addr, 4);
+#ifdef HAVE_IPV6
+   else
+     memcpy(svr->ecs_addr, &((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, addrlen);
+#endif
+   ecore_con_socks_svr_init(svr);
+}
+
+void
+ecore_con_socks_init(void)
+{
+   const char *socks;
+   char *u, *h, *p, *l;
+   char buf[64];
+   int port, lookup = 0;
+   Ecore_Con_Socks *ecs;
+   unsigned char addr[sizeof(struct in_addr)];
+
+   /* ECORE_CON_SOCKS_V4=user@host:port:[1|0] */
+   socks = getenv("ECORE_CON_SOCKS_V4");
+   if ((!socks) || (!socks[0]) || (strlen(socks) > 64)) return;
+   strncpy(buf, socks, sizeof(buf));
+   h = strchr(buf, '@');
+   u = NULL;
+   /* username */
+   if (h && (h - buf > 0)) *h++ = 0, u = buf;
+   else h = buf;
+   
+   /* host ip; I ain't resolvin shit here */
+   p = strchr(h, ':');
+   if (!p) return;
+   *p++ = 0;
+   if (!inet_pton(AF_INET, h, addr)) return;
+   
+   errno = 0;
+   port = strtol(p, &l, 10);
+   if (errno || (port < 0) || (port > 65535)) return;
+   if (l && (l[0] == ':'))
+     lookup = (l[1] == '1');
+   ecs = ecore_con_socks4_remote_add(h, port, u);
+   if (!ecs) return;
+   ecore_con_socks4_lookup_set(ecs, lookup);
+   ecore_con_socks_apply_always(ecs);
+   INF("Added global proxy server %s%s%s:%d - DNS lookup %s",
+       u ?: "", u ? "@" : "", h, port, lookup ? "ENABLED" : "DISABLED");
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @defgroup Ecore_Con_Socks_Group Ecore Connection SOCKS functions
+ * @{
+ */
+
+/**
+ * Add a SOCKS v4 proxy to the proxy list
+ *
+ * Use this to create (or return, if previously added) a SOCKS proxy
+ * object which can be used by any ecore_con servers.
+ * @param ip The ip address of the proxy (NOT DOMAIN NAME. IP ADDRESS.)
+ * @param port The port to connect to on the proxy
+ * @param username The username to use for the proxy (OPTIONAL)
+ * @return An allocated proxy object, or NULL on failure
+ * @note This object NEVER needs to be explicitly freed.
+ * @since 1.2
+ */
+EAPI Ecore_Con_Socks *
+ecore_con_socks4_remote_add(const char *ip, int port, const char *username)
+{
+   Ecore_Con_Socks *ecs;
+
+   if ((!ip) || (!ip[0]) || (port < 0) || (port > 65535)) return NULL;
+
+   ecs = _ecore_con_socks_find(4, ip, port, username);
+   if (ecs) return ecs;
+
+   ecs = calloc(1, sizeof(Ecore_Con_Socks_v4));
+   if (!ecs) return NULL;
+
+   ecs->version = 4;
+   ecs->ip = eina_stringshare_add(ip);
+   ecs->port = port;
+   ecs->username = eina_stringshare_add(username);
+   ecore_con_socks_proxies = eina_list_append(ecore_con_socks_proxies, ecs);
+   return ecs;
+}
+
+/**
+ * Set DNS lookup mode on an existing SOCKS v4 proxy
+ *
+ * According to RFC, SOCKS v4 does not require that a proxy perform
+ * its own DNS lookups for addresses. SOCKS v4a specifies the protocol
+ * for this. If you want to enable remote DNS lookup and are sure that your
+ * proxy supports it, use this function.
+ * @param ecs The proxy object
+ * @param enable If true, the proxy will perform the dns lookup
+ * @note By default, this setting is DISABLED.
+ */
+EAPI void
+ecore_con_socks4_lookup_set(Ecore_Con_Socks *ecs, Eina_Bool enable)
+{
+   ECORE_CON_SOCKS_CAST_ELSE(ecs) return;
+   v4->lookup = !!enable;
+}
+
+/**
+ * Get DNS lookup mode on an existing SOCKS v4 proxy
+ *
+ * According to RFC, SOCKS v4 does not require that a proxy perform
+ * its own DNS lookups for addresses. SOCKS v4a specifies the protocol
+ * for this. This function returns whether lookups are enabled on a proxy object.
+ * @param ecs The proxy object
+ * @return If true, the proxy will perform the dns lookup
+ * @note By default, this setting is DISABLED.
+ */
+EAPI Eina_Bool
+ecore_con_socks4_lookup_get(Ecore_Con_Socks *ecs)
+{
+   ECORE_CON_SOCKS_CAST_ELSE(ecs) return EINA_FALSE;
+   return v4 ? v4->lookup : EINA_FALSE;
+}
+
+/**
+ * Find a SOCKS v4 proxy in the proxy list
+ *
+ * Use this to determine if a SOCKS proxy was previously added by checking
+ * the proxy list against the parameters given.
+ * @param ip The ip address of the proxy (NOT DOMAIN NAME. IP ADDRESS.)
+ * @param port The port to connect to on the proxy, or -1 to match the first proxy with @p ip
+ * @param username The username used for the proxy (OPTIONAL)
+ * @return true only if a proxy exists matching the given params
+ * @note This function matches slightly more loosely than ecore_con_socks4_remote_add(), and
+ * ecore_con_socks4_remote_add() should be used to return the actual object.
+ * @since 1.2
+ */
+EAPI Eina_Bool
+ecore_con_socks4_remote_exists(const char *ip, int port, const char *username)
+{
+   if ((!ip) || (!ip[0]) || (port < -1) || (port > 65535) || (username && (!username[0])))
+     return EINA_FALSE;
+   return !!_ecore_con_socks_find(4, ip, port, username);
+}
+
+/**
+ * Remove a SOCKS v4 proxy from the proxy list and delete it
+ *
+ * Use this to remove a SOCKS proxy from the proxy list by checking
+ * the list against the parameters given. The proxy will then be deleted.
+ * @param ip The ip address of the proxy (NOT DOMAIN NAME. IP ADDRESS.)
+ * @param port The port to connect to on the proxy, or -1 to match the first proxy with @p ip
+ * @param username The username used for the proxy (OPTIONAL)
+ * @note This function matches in the same way as ecore_con_socks4_remote_exists().
+ * @warning Be aware that deleting a proxy which is being used WILL ruin your life.
+ * @since 1.2
+ */
+EAPI void
+ecore_con_socks4_remote_del(const char *ip, int port, const char *username)
+{
+   Ecore_Con_Socks_v4 *v4;
+
+   if ((!ip) || (!ip[0]) || (port < -1) || (port > 65535) || (username && (!username[0]))) return;
+   if (!ecore_con_socks_proxies) return;
+
+   v4 = (Ecore_Con_Socks_v4*)_ecore_con_socks_find(4, ip, port, username);
+   if (!v4) return;
+   ecore_con_socks_proxies = eina_list_remove(ecore_con_socks_proxies, v4);
+   _ecore_con_socks_free((Ecore_Con_Socks*)v4);
+}
+
+/**
+ * Enable bind mode on a SOCKS proxy
+ *
+ * Use this function to enable binding a remote port for use with a remote server.
+ * For more information, see http://ufasoft.com/doc/socks4_protocol.htm
+ * @param ecs The proxy object
+ * @param is_bind If true, the connection established will be a port binding
+ * @warning Be aware that changing the operation mode of an active proxy may result in undefined behavior
+ * @since 1.2
+ */
+EAPI void
+ecore_con_socks_bind_set(Ecore_Con_Socks *ecs, Eina_Bool is_bind)
+{
+   EINA_SAFETY_ON_NULL_RETURN(ecs);
+   ecs->bind = !!is_bind;
+}
+
+/**
+ * Return bind mode of a SOCKS proxy
+ *
+ * Use this function to return bind mode of a proxy (binding a remote port for use with a remote server).
+ * For more information, see http://ufasoft.com/doc/socks4_protocol.htm
+ * @param ecs The proxy object
+ * @return If true, the connection established will be a port binding
+ * @since 1.2
+ */
+EAPI Eina_Bool
+ecore_con_socks_bind_get(Ecore_Con_Socks *ecs)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ecs, EINA_FALSE);
+   return ecs->bind;
+}
+
+EAPI unsigned int
+ecore_con_socks_version_get(Ecore_Con_Socks *ecs)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ecs, 0);
+   return ecs->version;
+}
+
+/**
+ * Remove a SOCKS v4 proxy from the proxy list and delete it
+ *
+ * Use this to remove a SOCKS proxy from the proxy list by directly deleting the object given.
+ * @param ecs The proxy object to delete
+ * @warning Be aware that deleting a proxy which is being used WILL ruin your life.
+ * @since 1.2
+ */
+EAPI void
+ecore_con_socks_remote_del(Ecore_Con_Socks *ecs)
+{
+   EINA_SAFETY_ON_NULL_RETURN(ecs);
+   if (!ecore_con_socks_proxies) return;
+
+   ecore_con_socks_proxies = eina_list_remove(ecore_con_socks_proxies, ecs);
+   _ecore_con_socks_free(ecs);
+}
+
+/**
+ * Set a proxy object to be used with the next server created with ecore_con_server_connect()
+ *
+ * This function sets a proxy for the next ecore_con connection. After the next server is created,
+ * the proxy will NEVER be applied again unless explicitly enabled.
+ * @param ecs The proxy object
+ * @see ecore_con_socks_apply_always()
+ * @since 1.2
+ */
+EAPI void
+ecore_con_socks_apply_once(Ecore_Con_Socks *ecs)
+{
+   _ecore_con_proxy_once = ecs;
+}
+
+/**
+ * Set a proxy object to be used with all servers created with ecore_con_server_connect()
+ *
+ * This function sets a proxy for all ecore_con connections. It will always be used.
+ * @param ecs The proxy object
+ * @see ecore_con_socks_apply_once()
+ * @since 1.2
+ * @note ecore-con supports setting this through environment variables like so:
+ *   ECORE_CON_SOCKS_V4=[user@]server:port:lookup
+ * user is the OPTIONAL string that would be passed to the proxy as the username
+ * server is the IP_ADDRESS of the proxy server
+ * port is the port to connect to on the proxy server
+ * lookup is 1 if the proxy should perform all DNS lookups, otherwise 0 or omitted
+ */
+EAPI void
+ecore_con_socks_apply_always(Ecore_Con_Socks *ecs)
+{
+   _ecore_con_proxy_global = ecs;
+}
+/** @} */