diff options
Diffstat (limited to 'src/proxy-polarssl.c')
-rw-r--r-- | src/proxy-polarssl.c | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/src/proxy-polarssl.c b/src/proxy-polarssl.c new file mode 100644 index 0000000..04f7f6d --- /dev/null +++ b/src/proxy-polarssl.c @@ -0,0 +1,360 @@ +/* + * proxy-polarssl.c - Net stack layer for SOCKS4a/5 proxy connections + * + * Based on proxy-bio.c - Original copyright (c) 2012 The Chromium OS Authors. + * + * This file was adapted by Paul Bakker <p.j.bakker@offspark.com> + * All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * This file implements a SOCKS4a/SOCKS5 net layer as used by PolarSSL. + */ + +#include "config.h" + +#include <arpa/inet.h> +#include <assert.h> +#ifndef __USE_MISC +#define __USE_MISC +#endif +#ifndef __USE_POSIX +#define __USE_POSIX +#endif +#include <netdb.h> +#include <stdint.h> +#include <stdio.h> + +#ifndef HAVE_STRNLEN +#include "src/common/strnlen.h" +#endif + +#include "src/proxy-polarssl.h" +#include "src/util.h" + +int socks4a_connect(proxy_polarssl_ctx *ctx) +{ + int r; + unsigned char buf[NI_MAXHOST + 16]; + uint16_t port_n; + size_t sz = 0; + + if (!ctx) + return 0; + + verb("V: proxy4: connecting %s:%d", ctx->host, ctx->port); + + port_n = htons(ctx->port); + + /* + * Packet layout: + * 1b: Version (must be 0x04) + * 1b: command (0x01 is connect) + * 2b: port number, big-endian + * 4b: 0x00, 0x00, 0x00, 0x01 (bogus IPv4 addr) + * 1b: 0x00 (empty 'userid' field) + * nb: hostname, null-terminated + */ + buf[0] = 0x04; + buf[1] = 0x01; + sz += 2; + + memcpy(buf + 2, &port_n, sizeof(port_n)); + sz += sizeof(port_n); + + buf[4] = 0x00; + buf[5] = 0x00; + buf[6] = 0x00; + buf[7] = 0x01; + sz += 4; + + buf[8] = 0x00; + sz += 1; + + memcpy(buf + sz, ctx->host, strlen(ctx->host) + 1); + sz += strlen(ctx->host) + 1; + + r = ctx->f_send(ctx->p_send, buf, sz); + if (r != sz) + return 0; + + /* server reply: 1 + 1 + 2 + 4 */ + r = ctx->f_recv(ctx->p_recv, buf, 8); + if (r != 8) + return 0; + + if (buf[1] == 0x5a) { + verb("V: proxy4: connected"); + ctx->connected = 1; + return 1; + } + return 0; +} + +int socks5_connect(proxy_polarssl_ctx *ctx) +{ + unsigned char buf[NI_MAXHOST + 16]; + int r; + uint16_t port_n; + size_t sz = 0; + + if (!ctx) + return 0; + + /* the length for SOCKS addresses is only one byte. */ + if (strnlen(ctx->host, UINT8_MAX + 1) == UINT8_MAX + 1) + return 0; + + verb("V: proxy5: connecting %s:%d", ctx->host, ctx->port); + + port_n = htons(ctx->port); + + /* + * Hello packet layout: + * 1b: Version + * 1b: auth methods + * nb: method types + * + * We support only one method (no auth, 0x00). Others listed in RFC + * 1928. + */ + buf[0] = 0x05; + buf[1] = 0x01; + buf[2] = 0x00; + + r = ctx->f_send(ctx->p_send, buf, 3); + if (r != 3) + return 0; + + r = ctx->f_recv(ctx->p_recv, buf, 2); + if (r != 2) + return 0; + + if (buf[0] != 0x05 || buf[1] != 0x00) { + verb("V: proxy5: auth error %02x %02x", buf[0], buf[1]); + return 0; + } + + /* + * Connect packet layout: + * 1b: version + * 1b: command (0x01 is connect) + * 1b: reserved, 0x00 + * 1b: addr type (0x03 is domain name) + * nb: addr len (1b) + addr bytes, no null termination + * 2b: port, network byte order + */ + buf[0] = 0x05; + buf[1] = 0x01; + buf[2] = 0x00; + buf[3] = 0x03; + buf[4] = strlen(ctx->host); + sz += 5; + memcpy(buf + 5, ctx->host, strlen(ctx->host)); + sz += strlen(ctx->host); + memcpy(buf + sz, &port_n, sizeof(port_n)); + sz += sizeof(port_n); + + r = ctx->f_send(ctx->p_send, buf, sz); + if (r != sz) + return 0; + + /* + * Server's response: + * 1b: version + * 1b: status (0x00 is okay) + * 1b: reserved, 0x00 + * 1b: addr type (0x03 is domain name, 0x01 ipv4) + * nb: addr len (1b) + addr bytes, no null termination + * 2b: port, network byte order + */ + + /* grab up through the addr type */ + r = ctx->f_recv(ctx->p_recv, buf, 4); + if (r != 4) + return 0; + + if (buf[0] != 0x05 || buf[1] != 0x00) { + verb("V: proxy5: connect error %02x %02x", buf[0], buf[1]); + return 0; + } + + if (buf[3] == 0x03) { + unsigned int len; + r = ctx->f_recv(ctx->p_recv, buf + 4, 1); + if (r != 1) + return 0; + /* host (buf[4] bytes) + port (2 bytes) */ + len = buf[4] + 2; + while (len) { + r = ctx->f_recv(ctx->p_recv, buf + 5, min(len, sizeof(buf))); + if (r <= 0) + return 0; + len -= min(len, r); + } + } else if (buf[3] == 0x01) { + /* 4 bytes ipv4 addr, 2 bytes port */ + r = ctx->f_recv(ctx->p_recv, buf + 4, 6); + if (r != 6) + return 0; + } + + verb("V: proxy5: connected"); + ctx->connected = 1; + return 1; +} + +/* SSL socket BIOs don't support BIO_gets, so... */ +int sock_gets(proxy_polarssl_ctx *ctx, char *buf, size_t sz) +{ + unsigned char c; + while (ctx->f_recv(ctx->p_recv, &c, 1) > 0 && sz > 1) { + *buf++ = c; + sz--; + if (c == '\n') { + *buf = '\0'; + return 0; + } + } + return 1; +} + +int http_connect(proxy_polarssl_ctx *ctx) +{ + int r; + char buf[4096]; + int retcode; + + snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.1\r\n", + ctx->host, ctx->port); + r = ctx->f_send(ctx->p_send, (unsigned char *) buf, strlen(buf)); + if (r != strlen(buf)) + return 0; + /* required by RFC 2616 14.23 */ + snprintf(buf, sizeof(buf), "Host: %s:%d\r\n", ctx->host, ctx->port); + r = ctx->f_send(ctx->p_send, (unsigned char *) buf, strlen(buf)); + if (r != strlen(buf)) + return 0; + strcpy(buf, "\r\n"); + r = ctx->f_send(ctx->p_send, (unsigned char *) buf, strlen(buf)); + if (r != strlen(buf)) + return 0; + + r = sock_gets(ctx, buf, sizeof(buf)); + if (r) + return 0; + /* use %*s to ignore the version */ + if (sscanf(buf, "HTTP/%*s %d", &retcode) != 1) + return 0; + + if (retcode < 200 || retcode > 299) + return 0; + while (!(r = sock_gets(ctx, buf, sizeof(buf)))) { + if (!strcmp(buf, "\r\n")) { + /* Done with the header */ + ctx->connected = 1; + return 1; + } + } + return 0; +} + +int API proxy_polarssl_init(proxy_polarssl_ctx *ctx) +{ + if (!ctx) + return 0; + + memset(ctx, 0, sizeof(proxy_polarssl_ctx)); + return 1; +} + +void API proxy_polarssl_set_bio(proxy_polarssl_ctx *ctx, + int (*f_recv)(void *, unsigned char *, size_t), void *p_recv, + int (*f_send)(void *, const unsigned char *, size_t), void *p_send) +{ + if (!ctx) + return; + + ctx->f_recv = f_recv; + ctx->p_recv = p_recv; + ctx->f_send = f_send; + ctx->p_send = p_send; +} + +int API proxy_polarssl_free(proxy_polarssl_ctx *ctx) +{ + if (!ctx) + return 0; + + if (ctx->host) + { + free(ctx->host); + ctx->host = NULL; + } + + return 1; +} + +int API proxy_polarssl_set_scheme(proxy_polarssl_ctx *ctx, const char *scheme) +{ + if (!strcmp(scheme, "socks5")) + ctx->f_connect = socks5_connect; + else if (!strcmp(scheme, "socks4")) + ctx->f_connect = socks4a_connect; + else if (!strcmp(scheme, "http")) + ctx->f_connect = http_connect; + else + return 1; + return 0; +} + +int API proxy_polarssl_set_host(proxy_polarssl_ctx *ctx, const char *host) +{ + if (strnlen(host, NI_MAXHOST) == NI_MAXHOST) + return 1; + ctx->host = strdup(host); + return 0; +} + +void API proxy_polarssl_set_port(proxy_polarssl_ctx *ctx, uint16_t port) +{ + ctx->port = port; +} + +int API proxy_polarssl_recv(void *ctx, unsigned char *data, size_t len) +{ + proxy_polarssl_ctx *proxy = (proxy_polarssl_ctx *) ctx; + int r; + + if (!ctx) + return -1; + + if (!proxy->connected) + { + r = proxy->f_connect(ctx); + if (r) + return (r); + } + + return proxy->f_recv(proxy->p_recv, data, len); +} + + +int API proxy_polarssl_send(void *ctx, const unsigned char *data, size_t len) +{ + proxy_polarssl_ctx *proxy = (proxy_polarssl_ctx *) ctx; + int r; + + if (!ctx) + return -1; + + if (!proxy->connected) + { + r = proxy->f_connect(ctx); + if (r) + return (r); + } + + return proxy->f_send(proxy->p_send, data, len); +} |