finally finish implementing SOCKS5 proxies in ecore-con.

SOCKS5 is different from SOCKS4 in that it supports password authentication mechanisms (GSSAPI is still on the todo) and IPV6, neither of which are possible with SOCKS4

NOTE THAT THE CMDLINE SYNTAX FOR AUTOSOCKSING HAS CHANGED!
 *   ECORE_CON_SOCKS_V4=[user@]server-port:lookup
 *   ECORE_CON_SOCKS_V5=[user@]server-port:lookup

also note that I did not implement autosocksing with password. it's just not safe.


SVN revision: 67959
This commit is contained in:
Mike Blumenkrantz 2012-02-15 05:53:50 +00:00
parent 567b6ac0e8
commit 89276be842
4 changed files with 630 additions and 184 deletions

View File

@ -723,10 +723,13 @@ EAPI Eina_Bool ecore_con_ssl_client_upgrade(Ecore_Con_Client *cl, Ecore_
*/
EAPI Ecore_Con_Socks *ecore_con_socks4_remote_add(const char *ip, int port, const char *username);
EAPI void ecore_con_socks4_lookup_set(Ecore_Con_Socks *ecs, Eina_Bool enable);
EAPI Eina_Bool ecore_con_socks4_lookup_get(Ecore_Con_Socks *ecs);
EAPI Eina_Bool ecore_con_socks4_remote_exists(const char *ip, int port, const char *username);
EAPI void ecore_con_socks4_remote_del(const char *ip, int port, const char *username);
EAPI Ecore_Con_Socks *ecore_con_socks5_remote_add(const char *ip, int port, const char *username, const char *password);
EAPI Eina_Bool ecore_con_socks5_remote_exists(const char *ip, int port, const char *username, const char *password);
EAPI void ecore_con_socks5_remote_del(const char *ip, int port, const char *username, const char *password);
EAPI void ecore_con_socks_lookup_set(Ecore_Con_Socks *ecs, Eina_Bool enable);
EAPI Eina_Bool ecore_con_socks_lookup_get(Ecore_Con_Socks *ecs);
EAPI void ecore_con_socks_bind_set(Ecore_Con_Socks *ecs, Eina_Bool is_bind);
EAPI Eina_Bool ecore_con_socks_bind_get(Ecore_Con_Socks *ecs);
EAPI unsigned int ecore_con_socks_version_get(Ecore_Con_Socks *ecs);

View File

@ -461,7 +461,7 @@ ecore_con_server_connect(Ecore_Con_Type compl_type,
(!ecore_con_lookup(svr->name, (Ecore_Con_Dns_Cb)ecore_con_socks_dns_cb, svr)))
goto error;
if (svr->ecs->lookup)
svr->ecs_state = ECORE_CON_SOCKS_STATE_RESOLVED;
svr->ecs_state = ECORE_CON_PROXY_STATE_RESOLVED;
}
}
if (ecore_con_ssl_server_prepare(svr, compl_type & ECORE_CON_SSL))
@ -1031,7 +1031,7 @@ ecore_con_event_server_del(Ecore_Con_Server *svr)
e->server = svr;
if (svr->ecs)
{
svr->ecs_state = svr->ecs->lookup ? ECORE_CON_SOCKS_STATE_RESOLVED : ECORE_CON_SOCKS_STATE_DONE;
svr->ecs_state = svr->ecs->lookup ? ECORE_CON_PROXY_STATE_RESOLVED : ECORE_CON_PROXY_STATE_DONE;
eina_stringshare_replace(&svr->proxyip, NULL);
svr->proxyport = 0;
}
@ -1971,9 +1971,9 @@ _ecore_con_cl_handler(void *data,
ecore_con_event_server_add(svr);
return ECORE_CALLBACK_RENEW;
}
if (svr->ecs && svr->ecs_state && (svr->ecs_state < ECORE_CON_SOCKS_STATE_READ) && (!svr->ecs_buf))
if (svr->ecs && svr->ecs_state && (svr->ecs_state < ECORE_CON_PROXY_STATE_READ) && (!svr->ecs_buf))
{
if (svr->ecs_state < ECORE_CON_SOCKS_STATE_INIT)
if (svr->ecs_state < ECORE_CON_PROXY_STATE_INIT)
{
INF("PROXY STATE++");
svr->ecs_state++;

View File

@ -56,7 +56,7 @@ extern int _ecore_con_log_dom;
typedef struct _Ecore_Con_Lookup Ecore_Con_Lookup;
typedef struct _Ecore_Con_Info Ecore_Con_Info;
typedef struct Ecore_Con_Socks_v4 Ecore_Con_Socks_v4;
typedef struct Ecore_Con_Socks Ecore_Con_Socks_v4;
typedef struct Ecore_Con_Socks_v5 Ecore_Con_Socks_v5;
typedef void (*Ecore_Con_Info_Cb)(void *data, Ecore_Con_Info *infos);
@ -83,13 +83,18 @@ typedef enum _Ecore_Con_Ssl_Handshake
ECORE_CON_SSL_STATE_INIT
} Ecore_Con_Ssl_State;
typedef enum Ecore_Con_Socks_State
{
ECORE_CON_SOCKS_STATE_DONE = 0,
ECORE_CON_SOCKS_STATE_RESOLVED,
ECORE_CON_SOCKS_STATE_INIT,
ECORE_CON_SOCKS_STATE_READ
} Ecore_Con_Socks_State;
typedef enum Ecore_Con_Proxy_State
{ /* named PROXY instead of SOCKS in case some handsome and enterprising
* developer decides to add HTTP CONNECT support
*/
ECORE_CON_PROXY_STATE_DONE = 0,
ECORE_CON_PROXY_STATE_RESOLVED,
ECORE_CON_PROXY_STATE_INIT,
ECORE_CON_PROXY_STATE_READ,
ECORE_CON_PROXY_STATE_AUTH,
ECORE_CON_PROXY_STATE_REQUEST,
ECORE_CON_PROXY_STATE_CONFIRM,
} Ecore_Con_Proxy_State;
struct _Ecore_Con_Client
{
@ -140,7 +145,7 @@ struct _Ecore_Con_Server
pid_t ppid;
/* socks */
Ecore_Con_Socks *ecs;
Ecore_Con_Socks_State ecs_state;
Ecore_Con_Proxy_State ecs_state;
int ecs_addrlen;
unsigned char ecs_addr[16];
unsigned int ecs_buf_offset;
@ -239,24 +244,14 @@ struct _Ecore_Con_Lookup
v5 = (Ecore_Con_Socks_v5*)(X); \
else
struct Ecore_Con_Socks
{
unsigned char version;
const char *ip;
int port;
const char *username;
Eina_Bool lookup : 1;
Eina_Bool bind : 1;
};
struct Ecore_Con_Socks_v4
struct Ecore_Con_Socks /* v4 */
{
unsigned char version;
const char *ip;
int port;
const char *username;
unsigned int ulen;
Eina_Bool lookup : 1;
Eina_Bool bind : 1;
};
@ -268,8 +263,13 @@ struct Ecore_Con_Socks_v5
const char *ip;
int port;
const char *username;
unsigned int ulen;
Eina_Bool lookup : 1;
Eina_Bool bind : 1;
/* v5 only */
unsigned char method;
const char *password;
unsigned int plen;
};
extern Ecore_Con_Socks *_ecore_con_proxy_once;

View File

@ -57,6 +57,27 @@
#include "Ecore_Con.h"
#include "ecore_con_private.h"
/* http://tools.ietf.org/html/rfc1928
o X'00' NO AUTHENTICATION REQUIRED
o X'01' GSSAPI
o X'02' USERNAME/PASSWORD
o X'03' to X'7F' IANA ASSIGNED
o X'80' to X'FE' RESERVED FOR PRIVATE METHODS
o X'FF' NO ACCEPTABLE METHODS
*/
#define ECORE_CON_SOCKS_V5_METHOD_NONE 0
#define ECORE_CON_SOCKS_V5_METHOD_GSSAPI 1
#define ECORE_CON_SOCKS_V5_METHOD_USERPASS 2
static int ECORE_CON_SOCKS_V5_METHODS[] =
{
ECORE_CON_SOCKS_V5_METHOD_NONE,
// ECORE_CON_SOCKS_V5_METHOD_GSSAPI, TODO
ECORE_CON_SOCKS_V5_METHOD_USERPASS
};
#define ECORE_CON_SOCKS_V5_TOTAL_METHODS sizeof(ECORE_CON_SOCKS_V5_METHODS)
#define _ecore_con_server_kill(svr) do { \
DBG("KILL %p", (svr)); \
_ecore_con_server_kill((svr)); \
@ -65,10 +86,10 @@
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)
_ecore_con_socks_find(unsigned char version, const char *ip, int port, const char *username, size_t ulen, const char *password, size_t plen)
{
Eina_List *l;
Ecore_Con_Socks *ecs;
Ecore_Con_Socks_v5 *ecs;
if (!ecore_con_socks_proxies) return NULL;
@ -77,8 +98,14 @@ _ecore_con_socks_find(unsigned char version, const char *ip, int port, const cha
if (ecs->version != version) continue;
if (strcmp(ecs->ip, ip)) continue;
if ((port != -1) && (port != ecs->port)) continue;
if (ulen != ecs->ulen) continue;
if (username && strcmp(ecs->username, username)) continue;
return ecs;
if (version == 5)
{
if (plen != ecs->plen) continue;
if (password && strcmp(ecs->password, password)) continue;
}
return (Ecore_Con_Socks*)ecs;
}
return NULL;
}
@ -94,6 +121,373 @@ _ecore_con_socks_free(Ecore_Con_Socks *ecs)
eina_stringshare_del(ecs->username);
free(ecs);
}
static Eina_Bool
_ecore_con_socks_svr_init_v4(Ecore_Con_Server *svr, Ecore_Con_Socks_v4 *v4)
{
size_t addrlen, buflen, ulen = 1;
unsigned char *sbuf;
addrlen = v4->lookup ? strlen(svr->name) + 1 : 0;
if (v4->username) ulen += v4->ulen;
buflen = sizeof(char) * (8 + ulen + addrlen);
sbuf = malloc(buflen);
if (!sbuf)
{
ecore_con_event_server_error(svr, "Memory allocation failure!");
_ecore_con_server_kill(svr);
return EINA_FALSE;
}
/* 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
/* SOCKSv4 only handles IPV4, so addrlen is always 4 */
memcpy(sbuf + 4, svr->ecs_addr, 4);
if (v4->username)
memcpy(sbuf + 8, v4->username, ulen);
else
sbuf[8] = 0;
if (addrlen) memcpy(sbuf + 8 + ulen, svr->name, addrlen);
svr->ecs_buf = eina_binbuf_manage_new_length(sbuf, buflen);
return EINA_TRUE;
}
static Eina_Bool
_ecore_con_socks_svr_init_v5(Ecore_Con_Server *svr, Ecore_Con_Socks_v5 *v5)
{
size_t buflen;
unsigned int x;
unsigned char *sbuf;
if (v5->username)
buflen = sizeof(char) * (2 + ECORE_CON_SOCKS_V5_TOTAL_METHODS);
else
buflen = 3;
sbuf = malloc(buflen);
if (!sbuf)
{
ecore_con_event_server_error(svr, "Memory allocation failure!");
_ecore_con_server_kill(svr);
return EINA_FALSE;
}
/* http://en.wikipedia.org/wiki/SOCKS
* http://tools.ietf.org/html/rfc1928
*/
sbuf[0] = 5;
if (v5->username)
{
sbuf[1] = ECORE_CON_SOCKS_V5_TOTAL_METHODS;
for (x = 2; x < 2 + ECORE_CON_SOCKS_V5_TOTAL_METHODS; x++)
sbuf[x] = ECORE_CON_SOCKS_V5_METHODS[x - 2];
}
else
{
sbuf[1] = 1;
sbuf[2] = ECORE_CON_SOCKS_V5_METHOD_NONE;
}
svr->ecs_buf = eina_binbuf_manage_new_length(sbuf, buflen);
return EINA_TRUE;
}
#define ECORE_CON_SOCKS_READ(EXACT) \
if (num < EXACT) \
{ \
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) != EXACT) return; \
data = eina_binbuf_string_get(svr->ecs_recvbuf); \
} \
else if (num > EXACT) goto error; \
else \
data = buf
static void
_ecore_con_socks_read_v4(Ecore_Con_Server *svr, Ecore_Con_Socks_v4 *v4 __UNUSED__, const unsigned char *buf, unsigned int num)
{
const unsigned char *data;
DBG("SOCKS: %d bytes", num);
ECORE_CON_SOCKS_READ(8);
/* 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_PROXY_STATE_DONE;
INF("PROXY CONNECTED");
if (svr->ecs_recvbuf) eina_binbuf_free(svr->ecs_recvbuf);
svr->ecs_recvbuf = NULL;
svr->ecs_buf_offset = svr->ecs_addrlen = 0;
memset(svr->ecs_addr, 0, sizeof(svr->ecs_addr));
if (!svr->ssl_state)
ecore_con_event_server_add(svr);
if (svr->ssl_state || (svr->buf && eina_binbuf_length_get(svr->buf)))
ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_READ | ECORE_FD_WRITE);
return;
error:
_ecore_con_server_kill(svr);
}
static Eina_Bool
_ecore_con_socks_auth_v5(Ecore_Con_Server *svr, Ecore_Con_Socks_v5 *v5)
{
size_t size;
unsigned char *data;
switch (v5->method)
{
case ECORE_CON_SOCKS_V5_METHOD_NONE:
svr->ecs_state = ECORE_CON_PROXY_STATE_REQUEST;
return EINA_TRUE;
case ECORE_CON_SOCKS_V5_METHOD_GSSAPI:
return EINA_TRUE;
case ECORE_CON_SOCKS_V5_METHOD_USERPASS:
if (!v5->username) return EINA_FALSE;
if (!v5->password) v5->plen = 1;
/* http://tools.ietf.org/html/rfc1929 */
size = sizeof(char) * (3 + v5->ulen + v5->plen);
data = malloc(size);
if (!data) break;
data[0] = 1;
data[1] = v5->ulen;
memcpy(&data[2], v5->username, v5->ulen);
data[1 + v5->ulen] = v5->plen;
if (v5->password)
memcpy(&data[2 + v5->ulen], v5->password, v5->plen);
else
data[2 + v5->ulen] = 0;
svr->ecs_buf = eina_binbuf_manage_new_length(data, size);
return EINA_TRUE;
default:
break;
}
return EINA_FALSE;
}
static void
_ecore_con_socks_read_v5(Ecore_Con_Server *svr, Ecore_Con_Socks_v5 *v5, const unsigned char *buf, unsigned int num)
{
const unsigned char *data;
DBG("SOCKS: %d bytes", num);
switch (svr->ecs_state)
{
case ECORE_CON_PROXY_STATE_READ:
ECORE_CON_SOCKS_READ(2);
/* http://en.wikipedia.org/wiki/SOCKS */
if (data[0] != 5) goto error;
if (data[1] == 0xFF)
{
ecore_con_event_server_error(svr, "proxy authentication methods rejected");
goto error;
}
v5->method = data[1];
if (!_ecore_con_socks_auth_v5(svr, v5)) goto error;
if (svr->ecs_state == ECORE_CON_PROXY_STATE_REQUEST)
{
/* run again to skip auth reading */
_ecore_con_socks_read_v5(svr, v5, NULL, 0);
return;
}
ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_WRITE);
svr->ecs_state = ECORE_CON_PROXY_STATE_AUTH;
break;
case ECORE_CON_PROXY_STATE_AUTH:
ECORE_CON_SOCKS_READ(2);
switch (v5->method)
{
case ECORE_CON_SOCKS_V5_METHOD_NONE:
CRIT("HOW DID THIS HAPPEN?????????");
goto error;
case ECORE_CON_SOCKS_V5_METHOD_GSSAPI:
/* TODO: this */
break;
case ECORE_CON_SOCKS_V5_METHOD_USERPASS:
if (data[0] != 1)
{
ecore_con_event_server_error(svr, "protocol error");
goto error; /* wrong version */
}
if (data[1])
{
ecore_con_event_server_error(svr, "proxy request authentication rejected");
goto error;
}
default:
break;
}
case ECORE_CON_PROXY_STATE_REQUEST:
{
size_t addrlen, buflen;
unsigned char *sbuf;
addrlen = v5->lookup ? strlen(svr->name) + 1 : (unsigned int)svr->ecs_addrlen;
buflen = sizeof(char) * (6 + addrlen);
sbuf = malloc(buflen);
if (!sbuf)
{
ecore_con_event_server_error(svr, "Memory allocation failure!");
goto error;
}
sbuf[0] = 5;
sbuf[1] = v5->bind ? 2 : 1; /* TODO: 0x03 for UDP port association */
sbuf[2] = 0;
if (v5->lookup) /* domain name */
{
sbuf[3] = 3;
sbuf[4] = addrlen - 1;
memcpy(sbuf + 5, svr->name, addrlen - 1);
}
else
{
sbuf[3] = (svr->ecs_addrlen == 4) ? 1 : 4;
memcpy(sbuf + 4, svr->ecs_addr, addrlen);
}
sbuf[addrlen + 4] = svr->port >> 8;
sbuf[addrlen + 5] = svr->port & 0xff;
svr->ecs_buf = eina_binbuf_manage_new_length(sbuf, buflen);
ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_WRITE);
break;
}
case ECORE_CON_PROXY_STATE_CONFIRM:
{
/* this is ugly because we have to read an exact number of bytes,
* but we don't know what that number is until we've already read
* at least 5 bytes to determine the length of the unknown stream.
* yep.
*/
size_t to_read, len = svr->ecs_recvbuf ? eina_binbuf_length_get(svr->ecs_recvbuf) : 0;
if (num + len < 5)
{
/* guarantees we get called again */
ECORE_CON_SOCKS_READ(5);
}
if (len >= 5)
{
data = eina_binbuf_string_get(svr->ecs_recvbuf);
data += 3;
}
else
data = buf + 3 - len;
switch (data[0])
{
case 1:
to_read = 4;
break;
case 3:
to_read = data[1] + 1;
break;
case 4:
to_read = 16;
/* lazy debugging stub comment */
break;
default:
ecore_con_event_server_error(svr, "protocol error");
goto error;
}
/* at this point, we finally know exactly how much we need to read */
ECORE_CON_SOCKS_READ(6 + to_read);
if (data[0] != 5)
{
ecore_con_event_server_error(svr, "protocol error");
goto error; /* wrong version */
}
switch (data[1])
{
case 0:
break;
case 1:
ecore_con_event_server_error(svr, "general proxy failure");
goto error;
case 2:
ecore_con_event_server_error(svr, "connection not allowed by ruleset");
goto error;
case 3:
ecore_con_event_server_error(svr, "network unreachable");
goto error;
case 4:
ecore_con_event_server_error(svr, "host unreachable");
goto error;
case 5:
ecore_con_event_server_error(svr, "connection refused by destination host");
goto error;
case 6:
ecore_con_event_server_error(svr, "TTL expired");
goto error;
case 7:
ecore_con_event_server_error(svr, "command not supported / protocol error");
goto error;
case 8:
ecore_con_event_server_error(svr, "address type not supported");
default:
goto error;
}
if (data[2])
{
ecore_con_event_server_error(svr, "protocol error");
goto error;
}
memset(svr->ecs_addr, 0, sizeof(svr->ecs_addr));
if (!svr->ssl_state)
ecore_con_event_server_add(svr);
if (svr->ssl_state || (svr->buf && eina_binbuf_length_get(svr->buf)))
ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_READ | ECORE_FD_WRITE);
svr->ecs_buf_offset = svr->ecs_addrlen = 0;
svr->ecs_state = ECORE_CON_PROXY_STATE_DONE;
INF("PROXY CONNECTED");
break;
}
default:
break;
}
if (svr->ecs_recvbuf) eina_binbuf_free(svr->ecs_recvbuf);
svr->ecs_recvbuf = NULL;
return;
error:
_ecore_con_server_kill(svr);
}
/////////////////////////////////////////////////////////////////////////////////////
void
ecore_con_socks_shutdown(void)
@ -108,132 +502,43 @@ ecore_con_socks_shutdown(void)
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 (svr->ecs_state < ECORE_CON_PROXY_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 CONNECTED");
if (svr->ecs_recvbuf) eina_binbuf_free(svr->ecs_recvbuf);
svr->ecs_recvbuf = NULL;
svr->ecs_buf_offset = svr->ecs_addrlen = 0;
memset(svr->ecs_addr, 0, sizeof(svr->ecs_addr));
if (!svr->ssl_state)
ecore_con_event_server_add(svr);
if (svr->ssl_state || (svr->buf && eina_binbuf_length_get(svr->buf)))
ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_READ | ECORE_FD_WRITE);
}
return;
error:
_ecore_con_server_kill(svr);
if (v4) _ecore_con_socks_read_v4(svr, v4, buf, (unsigned int)num);
else _ecore_con_socks_read_v5(svr, v5, buf, (unsigned int)num);
}
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->ecs_state != ECORE_CON_SOCKS_STATE_INIT) return EINA_FALSE;
if (svr->ecs_state != ECORE_CON_PROXY_STATE_INIT) return EINA_FALSE;
ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_WRITE);
if (v4)
{
size_t addrlen, buflen, ulen = 1;
addrlen = svr->ecs->lookup ? strlen(svr->name) + 1: 0;
if (svr->ecs->username) ulen += strlen(svr->ecs->username);
buflen = sizeof(char) * (8 + ulen + addrlen);
sbuf = malloc(buflen);
if (!sbuf)
{
ecore_con_event_server_error(svr, "Memory allocation failure!");
_ecore_con_server_kill(svr);
return EINA_FALSE;
}
/* 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_manage_new_length(sbuf, buflen);
}
return EINA_TRUE;
if (v4) return _ecore_con_socks_svr_init_v4(svr, v4);
return _ecore_con_socks_svr_init_v5(svr, v5);
}
void
ecore_con_socks_dns_cb(const char *canonname __UNUSED__, const char *ip, struct sockaddr *addr, int addrlen, Ecore_Con_Server *svr)
ecore_con_socks_dns_cb(const char *canonname __UNUSED__, const char *ip, struct sockaddr *addr, int addrlen __UNUSED__, 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);
{
memcpy(svr->ecs_addr, &((struct sockaddr_in *)addr)->sin_addr.s_addr, 4);
svr->ecs_addrlen = 4;
}
#ifdef HAVE_IPV6
else
memcpy(svr->ecs_addr, &((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, addrlen);
{
memcpy(svr->ecs_addr, &((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, 16);
svr->ecs_addrlen = 16;
}
#endif
ecore_con_socks_svr_init(svr);
}
@ -242,36 +547,57 @@ void
ecore_con_socks_init(void)
{
const char *socks;
char *u, *h, *p, *l;
char buf[64];
char *h, *p, *l, *u = NULL;
char buf[512];
int port, lookup = 0;
Eina_Bool v5 = EINA_FALSE;
Ecore_Con_Socks *ecs;
unsigned char addr[sizeof(struct in_addr)];
#ifdef HAVE_IPV6
unsigned char addr6[sizeof(struct in6_addr)];
#endif
/* ECORE_CON_SOCKS_V4=user@host:port:[1|0] */
/* ECORE_CON_SOCKS_V4=[user@]host:port-[1|0] */
socks = getenv("ECORE_CON_SOCKS_V4");
if ((!socks) || (!socks[0]) || (strlen(socks) > 64)) return;
if (!socks)
{
/* ECORE_CON_SOCKS_V5=[user@]host-port:[1|0] */
socks = getenv("ECORE_CON_SOCKS_V5");
v5 = EINA_TRUE;
}
if ((!socks) || (!socks[0]) || (strlen(socks) > 512)) 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, ':');
p = strchr(h, '-');
if (!p) return;
*p++ = 0;
if (!inet_pton(AF_INET, h, addr)) return;
if (!inet_pton(AF_INET, h, addr))
#ifdef HAVE_IPV6
{
if (!v5) return;
if (!inet_pton(AF_INET6, h, addr6))
return;
}
#else
return;
#endif
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 (v5)
ecs = ecore_con_socks5_remote_add(h, port, u, NULL);
else
ecs = ecore_con_socks4_remote_add(h, port, u);
if (!ecs) return;
ecore_con_socks4_lookup_set(ecs, lookup);
ecore_con_socks_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");
@ -300,10 +626,17 @@ EAPI Ecore_Con_Socks *
ecore_con_socks4_remote_add(const char *ip, int port, const char *username)
{
Ecore_Con_Socks *ecs;
size_t ulen = 0;
if ((!ip) || (!ip[0]) || (port < 0) || (port > 65535)) return NULL;
ecs = _ecore_con_socks_find(4, ip, port, username);
if (username)
{
ulen = strlen(username);
/* max length for protocol */
if ((!ulen) || (ulen > 255)) return NULL;
}
ecs = _ecore_con_socks_find(4, ip, port, username, ulen, NULL, 0);
if (ecs) return ecs;
ecs = calloc(1, sizeof(Ecore_Con_Socks_v4));
@ -313,47 +646,11 @@ ecore_con_socks4_remote_add(const char *ip, int port, const char *username)
ecs->ip = eina_stringshare_add(ip);
ecs->port = port;
ecs->username = eina_stringshare_add(username);
ecs->ulen = ulen;
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.
* @since 1.2
*/
EAPI void
ecore_con_socks4_lookup_set(Ecore_Con_Socks *ecs, Eina_Bool enable)
{
ECORE_CON_SOCKS_CAST_ELSE(ecs) return;
if (v4) 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.
* @since 1.2
*/
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
*
@ -372,7 +669,7 @@ 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);
return !!_ecore_con_socks_find(4, ip, port, username, username ? strlen(username) : 0, NULL, 0);
}
/**
@ -395,11 +692,148 @@ ecore_con_socks4_remote_del(const char *ip, int port, const char *username)
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);
v4 = (Ecore_Con_Socks_v4*)_ecore_con_socks_find(4, ip, port, username, username ? strlen(username) : 0, NULL, 0);
if (!v4) return;
ecore_con_socks_proxies = eina_list_remove(ecore_con_socks_proxies, v4);
_ecore_con_socks_free((Ecore_Con_Socks*)v4);
}
/**
* Add a SOCKS v5 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)
* @param password The password 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_socks5_remote_add(const char *ip, int port, const char *username, const char *password)
{
Ecore_Con_Socks_v5 *ecs5;
size_t ulen = 0, plen = 0;
if ((!ip) || (!ip[0]) || (port < 0) || (port > 65535)) return NULL;
if (username)
{
ulen = strlen(username);
/* max length for protocol */
if ((!ulen) || (ulen > 255)) return NULL;
}
if (password)
{
plen = strlen(password);
/* max length for protocol */
if ((!plen) || (plen > 255)) return NULL;
}
ecs5 = (Ecore_Con_Socks_v5*)_ecore_con_socks_find(5, ip, port, username, ulen, password, plen);
if (ecs5) return (Ecore_Con_Socks*)ecs5;
ecs5 = calloc(1, sizeof(Ecore_Con_Socks_v5));
if (!ecs5) return NULL;
ecs5->version = 5;
ecs5->ip = eina_stringshare_add(ip);
ecs5->port = port;
ecs5->username = eina_stringshare_add(username);
ecs5->ulen = ulen;
ecs5->password = eina_stringshare_add(password);
ecs5->plen = plen;
ecore_con_socks_proxies = eina_list_append(ecore_con_socks_proxies, ecs5);
return (Ecore_Con_Socks*)ecs5;
}
/**
* Find a SOCKS v5 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)
* @param password The password 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_socks5_remote_add(), and
* ecore_con_socks5_remote_add() should be used to return the actual object.
* @since 1.2
*/
EAPI Eina_Bool
ecore_con_socks5_remote_exists(const char *ip, int port, const char *username, const char *password)
{
if ((!ip) || (!ip[0]) || (port < -1) || (port > 65535) || (username && (!username[0])) || (password && (!password[0])))
return EINA_FALSE;
return !!_ecore_con_socks_find(5, ip, port, username, username ? strlen(username) : 0, password, strlen(password));
}
/**
* Remove a SOCKS v5 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_socks5_remote_del(const char *ip, int port, const char *username, const char *password)
{
Ecore_Con_Socks_v5 *v5;
if ((!ip) || (!ip[0]) || (port < -1) || (port > 65535) || (username && (!username[0])) || (password && (!password[0])))
return;
if (!ecore_con_socks_proxies) return;
v5 = (Ecore_Con_Socks_v5*)_ecore_con_socks_find(5, ip, port, username, username ? strlen(username) : 0, password, strlen(password));
if (!v5) return;
ecore_con_socks_proxies = eina_list_remove(ecore_con_socks_proxies, v5);
_ecore_con_socks_free((Ecore_Con_Socks*)v5);
}
/**
* Set DNS lookup mode on an existing SOCKS 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. SOCKS v5 allows DNS lookups.
* 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.
* @since 1.2
*/
EAPI void
ecore_con_socks_lookup_set(Ecore_Con_Socks *ecs, Eina_Bool enable)
{
ECORE_CON_SOCKS_CAST_ELSE(ecs) return;
ecs->lookup = !!enable;
}
/**
* Get DNS lookup mode on an existing SOCKS 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. SOCKS v5 allows DNS lookups.
* 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.
* @since 1.2
*/
EAPI Eina_Bool
ecore_con_socks_lookup_get(Ecore_Con_Socks *ecs)
{
ECORE_CON_SOCKS_CAST_ELSE(ecs) return EINA_FALSE;
return ecs->lookup;
}
/**
* Enable bind mode on a SOCKS proxy
@ -434,6 +868,14 @@ ecore_con_socks_bind_get(Ecore_Con_Socks *ecs)
return ecs->bind;
}
/**
* Return SOCKS version of a SOCKS proxy
*
* Use this function to return the SOCKS protocol version of a proxy
* @param ecs The proxy object
* @return 0 on error, else 4/5
* @since 1.2
*/
EAPI unsigned int
ecore_con_socks_version_get(Ecore_Con_Socks *ecs)
{
@ -482,7 +924,8 @@ ecore_con_socks_apply_once(Ecore_Con_Socks *ecs)
* @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
* ECORE_CON_SOCKS_V4=[user@]server-port:lookup
* ECORE_CON_SOCKS_V5=[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