SOCKS proxying

Posting here, in case I ever wonder how SOCKS works again.

/* socks.c: SOCKS */
#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in.h>

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>

#include <netdb.h>

#define BUFSIZE 1024

static int socksInit(const char* socksHost, const char* socksPort);
static int socksConnect(int fd, const char* host, int port);
static int socksRead(int fd, char* buffer, int length);
static int socksWrite(int fd, const char* buffer, int length);
static int socksClose(int fd);

int
main(int argc, char** argv) {
	int fd = -1;
	char* socksHost;
	char* socksPort;
	char buffer[BUFSIZE];
	int ret;

	if(argc < 2) {
		fprintf(stderr, "Usage: %s [servername] [port]\n", argv[0]);
		return 1;
	}

	socksHost = argv[1];
	if(argc > 2)
		socksPort = argv[2];
	else
		socksPort = "1080";

	fd = socksInit(socksHost, socksPort);

	if(fd == -1) {
		fprintf(stderr, "Failed to connect\n");
		return 1;
	}

	socksConnect(fd, "checkip.dyndns.org", 80);

	ret = snprintf(buffer, BUFSIZE, "GET / HTTP/1.1\r\nHost: checkip.dyndns.org\r\nConnection: Close\r\n\r\n");
	ret = socksWrite(fd, buffer, ret);
	ret = socksRead(fd, buffer, BUFSIZE);
	buffer[ret] = '\0';
	fprintf(stderr, "%s\n", buffer);

	socksClose(fd);
}

int
socksInit(const char* socksHost, const char* socksPort) {
	int optval, error;
	struct addrinfo hint;
	struct addrinfo* results;
	struct addrinfo* result;
	struct sockaddr_in6 addr; 
	int fd;

	bzero(&hint, sizeof(hint));
	hint.ai_family = PF_UNSPEC;
	hint.ai_socktype = SOCK_STREAM;
	hint.ai_protocol = IPPROTO_TCP;
	hint.ai_flags = AI_ADDRCONFIG;
	if((error = getaddrinfo(socksHost, socksPort, &hint, &results))) {
		fprintf(stderr, "getaddrinfo() error: %s\n", gai_strerror(error));
		return 1;
	}

	for(result = results; result; result = result->ai_next) {
		fd = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
		if(fd == -1) {
			continue;
		}

		optval = 1;
		if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) {
			perror("setsockopt(SO_REUSEADDR)");
			close(fd);
			return 1;
		}

		if(result->ai_family == AF_INET6) {
			optval = 0;
			if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval)) == -1) {
				perror("setsockopt(IPV6_V6ONLY)");
				close(fd);
				return 1;
			}
		}	

		if(connect(fd, result->ai_addr, result->ai_addrlen) == -1) {
			close(fd);
			fd = -1;
			continue;
		}
		break;
	}

	return fd;
}

int
socksConnect(int fd, const char* host, int port)
{
	char buffer[BUFSIZE];
	int ret;

	buffer[0] = 5;
	buffer[1] = 1;
	buffer[2] = 0;

	ret = write(fd, buffer, 3);
	ret = read(fd, buffer, BUFSIZE);

	if(buffer[1] != 0) {
		return -1;
	}

	ret = strlen(host);

	buffer[0] = 5;
	buffer[1] = 1;
	buffer[2] = 0;
	buffer[3] = 3;
	buffer[4] = ret;
	memcpy(&buffer[5], host, ret);
	*((uint16_t*)(&buffer[4+ret+1])) = htons(port);

	ret = write(fd, buffer, 5+ret+sizeof(uint16_t));
	ret = read(fd, buffer, BUFSIZE);

	return buffer[1];
}

int
socksRead(int fd, char* buffer, int length) {
	return read(fd, buffer, length);
}


int
socksWrite(int fd, const char* buffer, int length) {
	return write(fd, buffer, length);
}

int socksClose(int fd) {
	return close(fd);
}
/* sslsocks.c: SSL over SOCKS */
#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in.h>

#include <openssl/ssl.h>

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>

#include <netdb.h>

#define BUFSIZE 1024

static int socksInit(const char* socksHost, const char* socksPort);
static int socksConnect(int fd, const char* host, int port);
static int socksRead(int fd, char* buffer, int length);
static int socksWrite(int fd, const char* buffer, int length);
static int socksClose(int fd);

static void startSSL(int fd);

int
main(int argc, char** argv) {
	int fd = -1;
	char* socksHost;
	char* socksPort;

	if(argc < 2) {
		fprintf(stderr, "Usage: %s [servername] [port]\n", argv[0]);
		return 1;
	}

	socksHost = argv[1];
	if(argc > 2)
		socksPort = argv[2];
	else
		socksPort = "1080";

	fd = socksInit(socksHost, socksPort);

	if(fd == -1) {
		fprintf(stderr, "Failed to connect\n");
		return 1;
	}

	socksConnect(fd, "www.duckduckgo.com", 443);

	startSSL(fd);

	socksClose(fd);
}

void
startSSL(int fd)
{
	SSL_CTX *ctxSSL;
	SSL* ssl;
	char buffer[BUFSIZE];
	int ret;

	SSL_library_init();
	
	if(!(ctxSSL = SSL_CTX_new(SSLv3_client_method()))) {
		fprintf(stderr, "SSL_CTX_new() failed.\n");
		return;
	}

	if(!(ssl = SSL_new(ctxSSL))) {
		fprintf(stderr, "SSL_new() failed.\n");
		return;
	}

	SSL_set_fd(ssl, fd);
	SSL_connect(ssl);

	ret = snprintf(buffer, BUFSIZE, "HEAD / HTTP/1.1\r\nHost: www.duckduckgo.com\r\nConnection: Close\r\n\r\n");

	ret = SSL_write(ssl, buffer, ret);
	ret = SSL_read(ssl, buffer, BUFSIZE);

	buffer[ret] = '\0';
	fprintf(stderr, "%s\n", buffer);

	SSL_shutdown(ssl);
}

int
socksInit(const char* socksHost, const char* socksPort) {
	int optval, error;
	struct addrinfo hint;
	struct addrinfo* results;
	struct addrinfo* result;
	struct sockaddr_in6 addr; 
	int fd;

	bzero(&hint, sizeof(hint));
	hint.ai_family = PF_UNSPEC;
	hint.ai_socktype = SOCK_STREAM;
	hint.ai_protocol = IPPROTO_TCP;
	hint.ai_flags = AI_ADDRCONFIG;
	if((error = getaddrinfo(socksHost, socksPort, &hint, &results))) {
		fprintf(stderr, "getaddrinfo() error: %s\n", gai_strerror(error));
		return 1;
	}

	for(result = results; result; result = result->ai_next) {
		fd = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
		if(fd == -1) {
			continue;
		}

		optval = 1;
		if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) {
			perror("setsockopt(SO_REUSEADDR)");
			close(fd);
			return 1;
		}

		if(result->ai_family == AF_INET6) {
			optval = 0;
			if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval)) == -1) {
				perror("setsockopt(IPV6_V6ONLY)");
				close(fd);
				return 1;
			}
		}	

		if(connect(fd, result->ai_addr, result->ai_addrlen) == -1) {
			close(fd);
			fd = -1;
			continue;
		}
		break;
	}

	return fd;
}

int
socksConnect(int fd, const char* host, int port)
{
	char buffer[BUFSIZE];
	int ret;

	buffer[0] = 5;
	buffer[1] = 1;
	buffer[2] = 0;

	ret = write(fd, buffer, 3);
	ret = read(fd, buffer, BUFSIZE);

	if(buffer[1] != 0) {
		return -1;
	}

	ret = strlen(host);

	buffer[0] = 5;
	buffer[1] = 1;
	buffer[2] = 0;
	buffer[3] = 3;
	buffer[4] = ret;
	memcpy(&buffer[5], host, ret);
	*((uint16_t*)(&buffer[4+ret+1])) = htons(port);

	ret = write(fd, buffer, 5+ret+sizeof(uint16_t));
	ret = read(fd, buffer, BUFSIZE);

	return buffer[1];
}

int
socksRead(int fd, char* buffer, int length) {
	return read(fd, buffer, length);
}


int
socksWrite(int fd, const char* buffer, int length) {
	return write(fd, buffer, length);
}

int socksClose(int fd) {
	return close(fd);
}

Outputs:

chateau.d.if!abbe [~/progs/bsd] % ./socks localhost 9050
HTTP/1.1 200 OK
Content-Type: text/html
Server: DynDNS-CheckIP/1.0
Connection: close
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 107

<html><head><title>Current IP Check</title></head><body>Current IP Address: 109.163.233.201</body></html>

chateau.d.if!abbe [~/progs/bsd] % ./sslsocks localhost 9050      
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 07 Jun 2012 17:06:19 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 4593
Last-Modified: Wed, 06 Jun 2012 23:45:15 GMT
Connection: close
Expires: Thu, 07 Jun 2012 23:06:19 GMT
Cache-Control: max-age=21600
Accept-Ranges: bytes