From fdd93b1e9545b66d1b3a2a1ec24d4c8613ee43fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Sat, 24 Jan 2015 23:59:21 +0100 Subject: [PATCH] Support SOCKSv5 proxy Support using a SOCKSv5 proxy specified as DISTCC_SOCKS_PROXY. The variable can either a hostname, a host:port pair or an absolute path to a UNIX socket. When SOCKS is used, the hostname is passed to the proxy and therefore the name resolution is done remotely. --- src/clinet.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 2 deletions(-) diff --git a/src/clinet.c b/src/clinet.c index 010a884..4773d8b 100644 --- a/src/clinet.c +++ b/src/clinet.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -42,8 +43,10 @@ #include #include +#include #include #include +#include #include @@ -161,7 +164,7 @@ out_failed: /** * Open a socket to a tcp remote host with the specified port. **/ -int dcc_connect_by_name(const char *host, int port, int *p_fd) +static int dcc_connect_by_name_real(const char *host, int port, int *p_fd) { struct addrinfo hints; struct addrinfo *res; @@ -201,7 +204,7 @@ int dcc_connect_by_name(const char *host, int port, int *p_fd) * * @todo Don't try for too long to connect. **/ -int dcc_connect_by_name(const char *host, int port, int *p_fd) +static int dcc_connect_by_name_real(const char *host, int port, int *p_fd) { struct sockaddr_in sock_out; struct hostent *hp; @@ -224,3 +227,133 @@ int dcc_connect_by_name(const char *host, int port, int *p_fd) } #endif /* not ENABLE_RFC2553 */ + +static int dcc_connect_via_socks5(const char *host, int port, int *p_fd, const char *proxy) +{ + int ret; + char *proxy_host, *proxy_it; + int proxy_port; + char buf[262]; + int host_length; + struct sockaddr_in addr_buf; + int skip_bytes; + + host_length = strlen(host); + if (host_length > 255) { + rs_log_error("hostname \"%s\" too long for SOCKSv5 (over 255 chars)", host); + return EXIT_CONNECT_FAILED; + } + + if (proxy[0] == '/') { /* UNIX socket */ + struct sockaddr_un unix_addr; + + if (strlen(proxy) >= sizeof(unix_addr.sun_path)) + { + rs_log_error("UNIX socket path \"%s\" too long", proxy); + return EXIT_CONNECT_FAILED; + } + + unix_addr.sun_family = AF_UNIX; + strcpy(unix_addr.sun_path, proxy); + + ret = dcc_connect_by_addr((struct sockaddr *) &unix_addr, + offsetof(struct sockaddr_un, sun_path) + strlen(proxy) + 1, + p_fd); + + } else { /* hostname? IP address? */ + proxy_host = strdup(proxy); + if (proxy_host == NULL) return EXIT_OUT_OF_MEMORY; + + proxy_it = strrchr(proxy_host, ':'); + if (proxy_it) { + *(proxy_it++) = 0; + proxy_port = atoi(proxy_it); + + if (proxy_port <= 0) { + rs_log_error("invalid proxy port \"%s\"", proxy_it); + free(proxy_host); + return EXIT_CONNECT_FAILED; + } + } + else + proxy_port = 1080; + + ret = dcc_connect_by_name_real(proxy_host, proxy_port, p_fd); + free(proxy_host); + } + + if (ret != 0) + return ret; + + /* connected to proxy, now identifier/method selection */ + buf[0] = 0x05; /* SOCKSv5 */ + buf[1] = 0x01; /* one method */ + buf[2] = 0x00; /* NO AUTHENTICATION REQUIRED */ + ret = dcc_writex(*p_fd, buf, 3); + if (ret != 0) + return ret; + + /* wait for method selection */ + ret = dcc_readx(*p_fd, buf, 2); + if (ret != 0) + return ret; + if (buf[0] != 0x05 || buf[1] != 0x00) { /* version, method */ + rs_log_error("invalid proxy reply (version 0x%02x, method 0x%02x)", + buf[0], buf[1]); + return EXIT_CONNECT_FAILED; + } + + /* send connect request */ + buf[0] = 0x05; /* SOCKSv5 */ + buf[1] = 0x01; /* CONNECT command */ + buf[2] = 0x00; /* reserved */ + buf[3] = 0x03; /* DOMAINNAME address type */ + buf[4] = host_length; + memcpy(&buf[5], host, host_length); + addr_buf.sin_port = htons(port); + memcpy(&buf[5 + host_length], &addr_buf.sin_port, 2); + ret = dcc_writex(*p_fd, buf, 7 + host_length); + if (ret != 0) + return ret; + + /* wait for the connection */ + /* read first 4 bytes of reply + 2 extra bytes we know will be there */ + ret = dcc_readx(*p_fd, buf, 6); + if (ret != 0) + return ret; + if (buf[0] != 0x05 || buf[2] != 0x00) { /* version, reserved */ + rs_log_error("invalid proxy reply (version 0x%02x, reserved 0x%02x)", + buf[0], buf[2]); + return EXIT_CONNECT_FAILED; + } + if (buf[1] != 0x00) { /* reply */ + rs_log_error("proxy connection failed, reason=0x%02x", buf[1]); + return EXIT_CONNECT_FAILED; + } + + /* now read the remaining (packet size - 6) bytes */ + switch (buf[3]) { /* address type */ + case 0x01: skip_bytes = 4; break; /* IPv4 */ + case 0x03: skip_bytes = buf[4] + 1; break; /* hostname with length field */ + case 0x04: skip_bytes = 16; break; /* IPv6 */ + default: + rs_log_error("invalid proxy reply (address type 0x%02x)", buf[3]); + return EXIT_CONNECT_FAILED; + } + ret = dcc_readx(*p_fd, buf, skip_bytes); + if (ret != 0) + return ret; + + return 0; +} + +int dcc_connect_by_name(const char *host, int port, int *p_fd) +{ + const char *proxy; + + proxy = getenv("DISTCC_SOCKS_PROXY"); + if (proxy) + return dcc_connect_via_socks5(host, port, p_fd, proxy); + else + return dcc_connect_by_name_real(host, port, p_fd); +} -- 2.2.2