forked from enlightenment/efl
937 lines
30 KiB
C
937 lines
30 KiB
C
#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>
|
|
|
|
#include <arpa/inet.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
#include <sys/un.h>
|
|
#include <sys/socket.h>
|
|
|
|
#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"
|
|
|
|
/* 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)); \
|
|
} while (0)
|
|
|
|
#define ECORE_CON_SOCKS_VERSION_CHECK(X) do { \
|
|
if (!(X) || ((X)->version < 4) || ((X)->version > 5)) \
|
|
return; \
|
|
} while (0)
|
|
#define ECORE_CON_SOCKS_VERSION_CHECK_RETURN(X, ret) do { \
|
|
if (!(X) || ((X)->version < 4) || ((X)->version > 5)) \
|
|
return (ret); \
|
|
} while (0)
|
|
|
|
#define ECORE_CON_SOCKS_CAST(X) \
|
|
Ecore_Con_Socks_v4 *v4 = NULL; \
|
|
Ecore_Con_Socks_v5 *v5 = NULL; \
|
|
if ((X) && ((X)->version == 4)) \
|
|
v4 = (Ecore_Con_Socks_v4 *)(X); \
|
|
else if ((X) && ((X)->version == 5)) \
|
|
v5 = (Ecore_Con_Socks_v5 *)(X);
|
|
|
|
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, size_t ulen, const char *password, size_t plen)
|
|
{
|
|
Eina_List *l;
|
|
Ecore_Con_Socks_v5 *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 (ulen != ecs->ulen) continue;
|
|
if (username && strcmp(ecs->username, username)) continue;
|
|
if (version == 5)
|
|
{
|
|
if (plen != ecs->plen) continue;
|
|
if (password && strcmp(ecs->password, password)) continue;
|
|
}
|
|
return (Ecore_Con_Socks*)ecs;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
_ecore_con_socks_free(Ecore_Con_Socks *ecs)
|
|
{
|
|
ECORE_CON_SOCKS_VERSION_CHECK(ecs);
|
|
|
|
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);
|
|
}
|
|
|
|
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 EINA_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)
|
|
{
|
|
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)
|
|
{
|
|
ECORE_CON_SOCKS_VERSION_CHECK(svr->ecs);
|
|
ECORE_CON_SOCKS_CAST(svr->ecs);
|
|
|
|
if (svr->ecs_state < ECORE_CON_PROXY_STATE_READ) return;
|
|
|
|
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)
|
|
{
|
|
ECORE_CON_SOCKS_VERSION_CHECK_RETURN(svr->ecs, EINA_FALSE);
|
|
ECORE_CON_SOCKS_CAST(svr->ecs);
|
|
|
|
if (!svr->ip) return EINA_FALSE;
|
|
if (svr->ecs_buf) 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) 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 EINA_UNUSED, const char *ip, struct sockaddr *addr, int addrlen EINA_UNUSED, Ecore_Con_Server *svr)
|
|
{
|
|
svr->ip = eina_stringshare_add(ip);
|
|
svr->ecs_state++;
|
|
if (addr->sa_family == AF_INET)
|
|
{
|
|
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, 16);
|
|
svr->ecs_addrlen = 16;
|
|
}
|
|
#endif
|
|
ecore_con_socks_svr_init(svr);
|
|
}
|
|
|
|
void
|
|
ecore_con_socks_init(void)
|
|
{
|
|
const char *socks;
|
|
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] */
|
|
socks = getenv("ECORE_CON_SOCKS_V4");
|
|
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, '@');
|
|
/* 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))
|
|
#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');
|
|
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_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");
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* @defgroup Ecore_Con_Socks_Group Ecore Connection SOCKS functions
|
|
* @ingroup Ecore_Con_Group
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* 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;
|
|
size_t ulen = 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;
|
|
}
|
|
ecs = _ecore_con_socks_find(4, ip, port, username, ulen, NULL, 0);
|
|
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);
|
|
ecs->ulen = ulen;
|
|
ecore_con_socks_proxies = eina_list_append(ecore_con_socks_proxies, ecs);
|
|
return ecs;
|
|
}
|
|
|
|
/**
|
|
* 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, username ? strlen(username) : 0, NULL, 0);
|
|
}
|
|
|
|
/**
|
|
* 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, 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, password ? strlen(password) : 0);
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
* @param password The password 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, password ? strlen(password) : 0);
|
|
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_VERSION_CHECK(ecs);
|
|
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_VERSION_CHECK_RETURN(ecs, EINA_FALSE);
|
|
return ecs->lookup;
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
ECORE_CON_SOCKS_VERSION_CHECK(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);
|
|
ECORE_CON_SOCKS_VERSION_CHECK_RETURN(ecs, EINA_FALSE);
|
|
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)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(ecs, 0);
|
|
ECORE_CON_SOCKS_VERSION_CHECK_RETURN(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
|
|
* 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
|
|
* 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;
|
|
}
|
|
/** @} */
|