/*
  httpstunnel.c

  quick hack to tunnel thru HTTPS firewall proxies.
  service on remote host should be on port 443.

  Dug Song <dugsong@monkey.org>
*/

#include <sys/param.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <err.h>

int lsock, ssock, csock;

static u_long
resolve_host(char *host)
{
	u_long addr;
	struct hostent *hp;
	
	if (host == NULL) return (0);
	
	if ((addr = inet_addr(host)) == -1) {
		if ((hp = gethostbyname(host)) == NULL)
			return (0);
		memcpy((char *)&addr, hp->h_addr, sizeof(addr));
	}
	return (addr);
}

int
main(int argc, char *argv[])
{
	struct sockaddr_in lsin, ssin;
	int slen, rlen, vers, code, one = 1;
	fd_set fdsr, fdse;
	char buf[4096];
	
	if (argc != 6) {
		fprintf(stderr, "Usage: %s <local-port> <proxy-host> "
			"<proxy-port> <remote-host> <remote-port>\n", argv[0]);
		exit(1);
	}
	slen = sizeof(lsin);
	
	memset((char *) &lsin, 0, slen);
	lsin.sin_family = AF_INET;
	lsin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	lsin.sin_port = htons(atoi(argv[1]));
	
	ssin = lsin;
	ssin.sin_addr.s_addr = resolve_host(argv[2]);
	ssin.sin_port = htons(atoi(argv[3]));
	
	lsock = socket(AF_INET, SOCK_STREAM, 0);
	
	if (bind(lsock, (struct sockaddr *)&lsin, slen) < 0)
		err(1, "bind");

	if (listen(lsock, 0))
		err(1, "listen");
	
	warnx("Waiting for connection...");
	
	if ((csock = accept(lsock, (struct sockaddr *)&lsin, &slen)) < 0)
		err(1, "accept");
	
	if (setsockopt(csock, SOL_SOCKET, SO_KEEPALIVE,
		       (void *)&one, sizeof(one)) < 0)
		err(1, "setsockopt");
	
	if ((ssock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		err(1, "socket");

	if (connect(ssock, (struct sockaddr *)&ssin, slen) < 0)
		err(1, "connect");
	
	if (setsockopt(ssock, SOL_SOCKET, SO_KEEPALIVE,
		       (void *)&one, sizeof(one)) < 0)
		err(1, "setsockopt");
	
	slen = snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n\r\n",
		       argv[4], atoi(argv[5]));
	
	if (write(ssock, buf, slen) != slen)
		err(1, "write");

	while ((rlen = read(ssock, buf, sizeof(buf))) >= 0) {
		if (rlen && sscanf(buf, "HTTP/1.%d %d", &vers, &code) == 2) {
			if (code != 200)
				errx(1, "Proxy requires authentication!");
			warnx("Proxy accepted us!");
			break;
		}
	}
	warnx("Transferring data.");
	
	for (;;) {
		FD_ZERO(&fdsr); FD_ZERO(&fdse);
		FD_SET(csock, &fdsr); FD_SET(csock, &fdse);
		FD_SET(ssock, &fdsr); FD_SET(ssock, &fdse);
    
		if (select(MAX(csock, ssock) + 1,
			   &fdsr, NULL, &fdse, NULL) == -1) {
			perror("select");
			break;
		}
		if (FD_ISSET(csock, &fdsr) || FD_ISSET(csock, &fdse)) {
			if ((rlen = read(csock, buf, sizeof(buf))) <= 0)
				break;
			if (write(ssock, buf, rlen) <= 0)
				break;
		}
		else if (FD_ISSET(ssock, &fdsr) || FD_ISSET(ssock, &fdse)) {
			if ((rlen = read(ssock, buf, sizeof(buf))) <= 0)
				break;
			if (write(csock, buf, rlen) <= 0)
				break;
		}
	}
	/* NOTREACHED */
	
	exit(0);
}
