360 lines
7.4 KiB
C
360 lines
7.4 KiB
C
/*
|
|
* 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);
|
|
}
|