From 0ecb713fdc028bafbe437f0b61e0bc231a406ee5 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 2 Jan 2010 13:17:48 +0000 Subject: [PATCH] Add SOCKS5 support Signed-off-by: David Woodhouse --- http.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- main.c | 19 ++++-- openconnect.h | 3 +- ssl.c | 18 ++++-- 4 files changed, 195 insertions(+), 28 deletions(-) diff --git a/http.c b/http.c index 013a19a..788399a 100644 --- a/http.c +++ b/http.c @@ -743,11 +743,154 @@ static int proxy_gets(int fd, char *buf, size_t len) return i ?: ret; } +static int proxy_write(int fd, unsigned char *buf, size_t len) +{ + size_t count; + + for (count = 0; count < len; ) { + int i = write(fd, buf + count, len - count); + if (i < 0) + return -errno; + + count += i; + } + return 0; +} -int process_http_proxy(struct openconnect_info *vpninfo, int ssl_sock) +static int proxy_read(int fd, unsigned char *buf, size_t len) +{ + size_t count; + + for (count = 0; count < len; ) { + int i = read(fd, buf + count, len - count); + if (i < 0) + return -errno; + + count += i; + } + return 0; +} + +static const char *socks_errors[] = { + "request granted", + "general failure", + "connection not allowed by ruleset", + "network unreachable", + "host unreachable", + "connection refused by destination host", + "TTL expired", + "command not supported / protocol error", + "address type not supported" +}; + +static int process_socks_proxy(struct openconnect_info *vpninfo, int ssl_sock) +{ + unsigned char buf[1024]; + int i; + + buf[0] = 5; /* SOCKS version */ + buf[1] = 1; /* # auth methods */ + buf[2] = 0; /* No auth supported */ + + if ((i = proxy_write(ssl_sock, buf, 3))) { + vpninfo->progress(vpninfo, PRG_ERR, + "Error writing auth request to SOCKS proxy: %s\n", + strerror(-i)); + return i; + } + + if ((i = proxy_read(ssl_sock, buf, 2))) { + vpninfo->progress(vpninfo, PRG_ERR, + "Error reading auth response from SOCKS proxy: %s\n", + strerror(-i)); + return i; + } + if (buf[0] != 5) { + vpninfo->progress(vpninfo, PRG_ERR, + "Unexpected auth response from SOCKS proxy: %02x %02x\n", + buf[0], buf[1]); + return -EIO; + } + if (buf[1]) { + socks_err: + if (buf[1] < sizeof(socks_errors) / sizeof(socks_errors[0])) + vpninfo->progress(vpninfo, PRG_ERR, + "SOCKS proxy error %02x: %s\n", + buf[1], socks_errors[buf[1]]); + else + vpninfo->progress(vpninfo, PRG_ERR, + "SOCKS proxy error %02x\n", + buf[1]); + return -EIO; + } + + vpninfo->progress(vpninfo, PRG_INFO, "Requesting SOCKS proxy connection to %s:%d\n", + vpninfo->hostname, vpninfo->port); + + buf[0] = 5; /* SOCKS version */ + buf[1] = 1; /* CONNECT */ + buf[2] = 0; /* Reserved */ + buf[3] = 3; /* Address type is domain name */ + buf[4] = strlen(vpninfo->hostname); + strcpy((char *)buf + 5, vpninfo->hostname); + i = strlen(vpninfo->hostname) + 5; + buf[i++] = vpninfo->port >> 8; + buf[i++] = vpninfo->port & 0xff; + + if ((i = proxy_write(ssl_sock, buf, i))) { + vpninfo->progress(vpninfo, PRG_ERR, + "Error writing connect request to SOCKS proxy: %s\n", + strerror(-i)); + return i; + } + /* Read 5 bytes -- up to and including the first byte of the returned + address (which might be the length byte of a domain name) */ + if ((i = proxy_read(ssl_sock, buf, 5))) { + vpninfo->progress(vpninfo, PRG_ERR, + "Error reading connect response from SOCKS proxy: %s\n", + strerror(-i)); + return i; + } + if (buf[0] != 5) { + vpninfo->progress(vpninfo, PRG_ERR, + "Unexpected connect response from SOCKS proxy: %02x %02x...\n", + buf[0], buf[1]); + return -EIO; + } + if (buf[1]) + goto socks_err; + + /* Connect responses contain an address */ + switch(buf[3]) { + case 1: /* Legacy IP */ + i = 5; + break; + case 3: /* Domain name */ + i = buf[4] + 2; + break; + case 4: /* IPv6 */ + i = 17; + break; + default: + vpninfo->progress(vpninfo, PRG_ERR, + "Unexpected address type %02x in SOCKS connect response\n", + buf[3]); + return -EIO; + } + + if ((i = proxy_read(ssl_sock, buf, i))) { + vpninfo->progress(vpninfo, PRG_ERR, + "Error reading connect response from SOCKS proxy: %s\n", + strerror(-i)); + return i; + } + return 0; +} + +static int process_http_proxy(struct openconnect_info *vpninfo, int ssl_sock) { char buf[MAX_BUF_LEN]; - int count, buflen, result; + int buflen, result; sprintf(buf, "CONNECT %s:%d HTTP/1.1\r\n", vpninfo->hostname, vpninfo->port); sprintf(buf + strlen(buf), "Host: %s\r\n", vpninfo->hostname); @@ -757,19 +900,14 @@ int process_http_proxy(struct openconnect_info *vpninfo, int ssl_sock) sprintf(buf + strlen(buf), "Accept-Encoding: identity\r\n"); sprintf(buf + strlen(buf), "\r\n"); - vpninfo->progress(vpninfo, PRG_INFO, "Requesting proxy connection to %s:%d\n", + vpninfo->progress(vpninfo, PRG_INFO, "Requesting HTTP proxy connection to %s:%d\n", vpninfo->hostname, vpninfo->port); - buflen = strlen(buf); - for (count = 0; count < buflen; ) { - int i = write(ssl_sock, buf + count, buflen - count); - if (i < 0) { - i = -errno; - vpninfo->progress(vpninfo, PRG_ERR, "Sending proxy request failed: %s\n", - strerror(errno)); - return i; - } - count += i; + if (proxy_write(ssl_sock, (unsigned char *)buf, strlen(buf))) { + result = -errno; + vpninfo->progress(vpninfo, PRG_ERR, "Sending proxy request failed: %s\n", + strerror(errno)); + return result; } if (proxy_gets(ssl_sock, buf, sizeof(buf)) < 0) { @@ -790,8 +928,8 @@ int process_http_proxy(struct openconnect_info *vpninfo, int ssl_sock) return -EIO; } - while ((count = proxy_gets(ssl_sock, buf, sizeof(buf)))) { - if (count < 0) { + while ((buflen = proxy_gets(ssl_sock, buf, sizeof(buf)))) { + if (buflen < 0) { vpninfo->progress(vpninfo, PRG_ERR, "Failed to read proxy response\n"); return -EIO; } @@ -802,3 +940,18 @@ int process_http_proxy(struct openconnect_info *vpninfo, int ssl_sock) return 0; } + +int process_proxy(struct openconnect_info *vpninfo, int ssl_sock) +{ + if (!vpninfo->proxy_type || !strcmp(vpninfo->proxy_type, "http")) + return process_http_proxy(vpninfo, ssl_sock); + + if (!strcmp(vpninfo->proxy_type, "socks") || + !strcmp(vpninfo->proxy_type, "socks5")) + return process_socks_proxy(vpninfo, ssl_sock); + + vpninfo->progress(vpninfo, PRG_ERR, "Unknown proxy type '%s'\n", + vpninfo->proxy_type); + return -EIO; +} + diff --git a/main.c b/main.c index 25921b9..91d7cab 100644 --- a/main.c +++ b/main.c @@ -308,15 +308,22 @@ int main(int argc, char **argv) break; case 'P': { char *url = strdup(optarg); - char *scheme; - autoproxy = 0; - parse_url(url, &scheme, &vpninfo->proxy, &vpninfo->proxy_port, NULL, 80); - if (scheme && strcmp(scheme, "http")) { - fprintf(stderr, "Non-http proxy not supported\n"); + free(vpninfo->proxy_type); + vpninfo->proxy_type = NULL; + free(vpninfo->proxy); + vpninfo->proxy = NULL; + + parse_url(url, &vpninfo->proxy_type, &vpninfo->proxy, + &vpninfo->proxy_port, NULL, 80); + if (vpninfo->proxy_type && + strcmp(vpninfo->proxy_type, "http") && + strcmp(vpninfo->proxy_type, "socks") && + strcmp(vpninfo->proxy_type, "socks5")) { + fprintf(stderr, "Only http or socks[5] proxy scheme supported\n"); exit(1); } - free(scheme); + autoproxy = 0; free(url); break; } diff --git a/openconnect.h b/openconnect.h index 77c8a05..332dbbf 100644 --- a/openconnect.h +++ b/openconnect.h @@ -150,6 +150,7 @@ struct openconnect_info { #ifdef OPENCONNECT_LIBPROXY pxProxyFactory *proxy_factory; #endif + char *proxy_type; char *proxy; int proxy_port; @@ -330,7 +331,7 @@ int parse_xml_response(struct openconnect_info *vpninfo, char *response, /* http.c */ int openconnect_obtain_cookie(struct openconnect_info *vpninfo); char *openconnect_create_useragent(char *base); -int process_http_proxy(struct openconnect_info *vpninfo, int ssl_sock); +int process_proxy(struct openconnect_info *vpninfo, int ssl_sock); int parse_url(char *url, char **res_proto, char **res_host, int *res_port, char **res_path, int default_port); diff --git a/ssl.c b/ssl.c index 6696254..f95f99c 100644 --- a/ssl.c +++ b/ssl.c @@ -516,6 +516,8 @@ int openconnect_open_https(struct openconnect_info *vpninfo) char **proxies; int i = 0; + free(vpninfo->proxy_type); + vpninfo->proxy_type = NULL; free(vpninfo->proxy); vpninfo->proxy = NULL; @@ -530,16 +532,20 @@ int openconnect_open_https(struct openconnect_info *vpninfo) url); while (proxies && proxies[i]) { - if (!vpninfo->proxy && !strncmp(proxies[i], "http://", 7)) - parse_url(proxies[i], NULL, &vpninfo->proxy, - &vpninfo->proxy_port, NULL, 0); + if (!vpninfo->proxy && + (!strncmp(proxies[i], "http://", 7) || + !strncmp(proxies[i], "socks://", 8) || + !strncmp(proxies[i], "socks5://", 9))) + parse_url(proxies[i], &vpninfo->proxy_type, + &vpninfo->proxy, &vpninfo->proxy_port, + NULL, 0); i++; } free(url); free(proxies); if (vpninfo->proxy) - vpninfo->progress(vpninfo, PRG_TRACE, "Proxy from libproxy: http://%s:%d/\n", - vpninfo->proxy, vpninfo->port); + vpninfo->progress(vpninfo, PRG_TRACE, "Proxy from libproxy: %s://%s:%d/\n", + vpninfo->proxy_type, vpninfo->proxy, vpninfo->port); } #endif if (vpninfo->proxy) { @@ -610,7 +616,7 @@ int openconnect_open_https(struct openconnect_info *vpninfo) fcntl(ssl_sock, F_SETFD, FD_CLOEXEC); if (vpninfo->proxy) { - err = process_http_proxy(vpninfo, ssl_sock); + err = process_proxy(vpninfo, ssl_sock); if (err) { close(ssl_sock); return err; -- 2.7.4