103100a63Svk /*	$OpenBSD: socks.c,v 1.17 2006/09/25 04:51:20 ray Exp $	*/
203100a63Svk 
303100a63Svk /*
403100a63Svk  * Copyright (c) 1999 Niklas Hallqvist.  All rights reserved.
503100a63Svk  * Copyright (c) 2004, 2005 Damien Miller.  All rights reserved.
603100a63Svk  *
703100a63Svk  * Redistribution and use in source and binary forms, with or without
803100a63Svk  * modification, are permitted provided that the following conditions
903100a63Svk  * are met:
1003100a63Svk  * 1. Redistributions of source code must retain the above copyright
1103100a63Svk  *    notice, this list of conditions and the following disclaimer.
1203100a63Svk  * 2. Redistributions in binary form must reproduce the above copyright
1303100a63Svk  *    notice, this list of conditions and the following disclaimer in the
1403100a63Svk  *    documentation and/or other materials provided with the distribution.
1503100a63Svk  *
1603100a63Svk  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1703100a63Svk  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1803100a63Svk  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1903100a63Svk  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2003100a63Svk  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2103100a63Svk  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2203100a63Svk  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2303100a63Svk  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2403100a63Svk  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2503100a63Svk  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2603100a63Svk  */
2703100a63Svk 
2803100a63Svk #include <sys/types.h>
2903100a63Svk #include <sys/socket.h>
3003100a63Svk #include <netinet/in.h>
3103100a63Svk #include <arpa/inet.h>
3203100a63Svk 
3303100a63Svk #include <err.h>
3403100a63Svk #include <errno.h>
3503100a63Svk #include <netdb.h>
3603100a63Svk #include <stdio.h>
3703100a63Svk #include <stdlib.h>
3803100a63Svk #include <string.h>
3903100a63Svk #include <unistd.h>
4003100a63Svk #include <resolv.h>
4103100a63Svk #include <strings.h>
4203100a63Svk #include "atomicio.h"
4303100a63Svk 
4403100a63Svk #define	SOCKS_PORT	"1080"
4503100a63Svk #define	HTTP_PROXY_PORT	"3128"
4603100a63Svk #define	HTTP_MAXHDRS	64
4703100a63Svk #define	SOCKS_V5	5
4803100a63Svk #define	SOCKS_V4	4
4903100a63Svk #define	SOCKS_NOAUTH	0
5003100a63Svk #define	SOCKS_NOMETHOD	0xff
5103100a63Svk #define	SOCKS_CONNECT	1
5203100a63Svk #define	SOCKS_IPV4	1
5303100a63Svk #define	SOCKS_DOMAIN	3
5403100a63Svk #define	SOCKS_IPV6	4
5503100a63Svk 
5603100a63Svk #define	HTTP_10_407	"HTTP/1.0 407 "
5703100a63Svk #define	HTTP_10_200	"HTTP/1.0 200 "
5803100a63Svk #define	HTTP_11_200	"HTTP/1.1 200 "
5903100a63Svk 
6003100a63Svk int remote_connect(const char *, const char *, struct addrinfo);
6103100a63Svk int socks_connect(const char *, const char *,
6203100a63Svk 	    const char *, const char *, struct addrinfo, int,
6303100a63Svk 	    const char *);
6403100a63Svk 
6503100a63Svk /*
6603100a63Svk  * Convert string representation of host (h) and service/port (p) into
6703100a63Svk  * sockaddr structure and return 0 on success, -1 on failure.
6803100a63Svk  * Indicate whether the host address is IPv4 (v4only) and numeric.
6903100a63Svk  */
7003100a63Svk static int
decode_addrport(const char * h,const char * p,struct sockaddr * addr,socklen_t addrlen,int v4only,int numeric)7103100a63Svk decode_addrport(const char *h, const char *p, struct sockaddr *addr,
7203100a63Svk     socklen_t addrlen, int v4only, int numeric)
7303100a63Svk {
7403100a63Svk 	int r;
7503100a63Svk 	struct addrinfo hints, *res;
7603100a63Svk 
7703100a63Svk 	bzero(&hints, sizeof (hints));
7803100a63Svk 	hints.ai_family = v4only ? PF_INET : PF_UNSPEC;
7903100a63Svk 	hints.ai_flags = numeric ? AI_NUMERICHOST : 0;
8003100a63Svk 	hints.ai_socktype = SOCK_STREAM;
8103100a63Svk 	r = getaddrinfo(h, p, &hints, &res);
8203100a63Svk 	/* Don't fatal when attempting to convert a numeric address */
8303100a63Svk 	if (r != 0) {
8403100a63Svk 		if (!numeric) {
8503100a63Svk 			errx(1, "getaddrinfo(\"%.64s\", \"%.64s\"): %s", h, p,
8603100a63Svk 			    gai_strerror(r));
8703100a63Svk 		}
8803100a63Svk 		return (-1);
8903100a63Svk 	}
9003100a63Svk 	if (addrlen < res->ai_addrlen) {
9103100a63Svk 		freeaddrinfo(res);
9203100a63Svk 		errx(1, "internal error: addrlen < res->ai_addrlen");
9303100a63Svk 	}
9403100a63Svk 	(void) memcpy(addr, res->ai_addr, res->ai_addrlen);
9503100a63Svk 	freeaddrinfo(res);
9603100a63Svk 	return (0);
9703100a63Svk }
9803100a63Svk 
9903100a63Svk /*
10003100a63Svk  * Read single line from a descriptor into buffer up to bufsz bytes,
10103100a63Svk  * byte by byte. Returns length of the line (including ending NULL),
10203100a63Svk  * exits upon failure.
10303100a63Svk  */
10403100a63Svk static int
proxy_read_line(int fd,char * buf,size_t bufsz)10503100a63Svk proxy_read_line(int fd, char *buf, size_t bufsz)
10603100a63Svk {
10703100a63Svk 	size_t off;
10803100a63Svk 
10903100a63Svk 	for (off = 0; ; ) {
11003100a63Svk 		if (off >= bufsz)
11103100a63Svk 			errx(1, "proxy read too long");
11203100a63Svk 		if (atomicio(read, fd, buf + off, 1) != 1)
11303100a63Svk 			err(1, "proxy read");
11403100a63Svk 		/* Skip CR */
11503100a63Svk 		if (buf[off] == '\r')
11603100a63Svk 			continue;
11703100a63Svk 		if (buf[off] == '\n') {
11803100a63Svk 			buf[off] = '\0';
11903100a63Svk 			break;
12003100a63Svk 		}
12103100a63Svk 		/*
12203100a63Svk 		 * we rewite \r\n to NULL since socks_connect() relies
12303100a63Svk 		 * on *buf being zero in that case.
12403100a63Svk 		 */
12503100a63Svk 		off++;
12603100a63Svk 	}
12703100a63Svk 	return (off);
12803100a63Svk }
12903100a63Svk 
13003100a63Svk /*
13103100a63Svk  * Read proxy password from user and return it. The arguments are used
13203100a63Svk  * only for prompt construction.
13303100a63Svk  */
13403100a63Svk static const char *
getproxypass(const char * proxyuser,const char * proxyhost)13503100a63Svk getproxypass(const char *proxyuser, const char *proxyhost)
13603100a63Svk {
13703100a63Svk 	char prompt[512];
13803100a63Svk 	const char *pw;
13903100a63Svk 
14003100a63Svk 	(void) snprintf(prompt, sizeof (prompt), "Proxy password for %s@%s: ",
14103100a63Svk 	    proxyuser, proxyhost);
14203100a63Svk 	if ((pw = getpassphrase(prompt)) == NULL)
14303100a63Svk 		errx(1, "Unable to read proxy passphrase");
14403100a63Svk 	return (pw);
14503100a63Svk }
14603100a63Svk 
14703100a63Svk /* perform connection via proxy using SOCKSv[45] or HTTP proxy CONNECT */
14803100a63Svk int
socks_connect(const char * host,const char * port,const char * proxyhost,const char * proxyport,struct addrinfo proxyhints,int socksv,const char * proxyuser)14903100a63Svk socks_connect(const char *host, const char *port, const char *proxyhost,
15003100a63Svk     const char *proxyport, struct addrinfo proxyhints, int socksv,
15103100a63Svk     const char *proxyuser)
15203100a63Svk {
15303100a63Svk 	int proxyfd, r, authretry = 0;
15403100a63Svk 	size_t hlen, wlen;
15503100a63Svk 	char buf[1024];
15603100a63Svk 	size_t cnt;
15703100a63Svk 	struct sockaddr_storage addr;
15803100a63Svk 	struct sockaddr_in *in4 = (struct sockaddr_in *)&addr;
15903100a63Svk 	struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&addr;
16003100a63Svk 	in_port_t serverport;
16103100a63Svk 	const char *proxypass = NULL;
16203100a63Svk 
16303100a63Svk 	if (proxyport == NULL)
16403100a63Svk 		proxyport = (socksv == -1) ? HTTP_PROXY_PORT : SOCKS_PORT;
16503100a63Svk 
16603100a63Svk 	/* Abuse API to lookup port */
16703100a63Svk 	if (decode_addrport("0.0.0.0", port, (struct sockaddr *)&addr,
16803100a63Svk 	    sizeof (addr), 1, 1) == -1)
16903100a63Svk 		errx(1, "unknown port \"%.64s\"", port);
17003100a63Svk 	serverport = in4->sin_port;
17103100a63Svk 
17203100a63Svk again:
17303100a63Svk 	if (authretry++ > 3)
17403100a63Svk 		errx(1, "Too many authentication failures");
17503100a63Svk 
17603100a63Svk 	proxyfd = remote_connect(proxyhost, proxyport, proxyhints);
17703100a63Svk 
17803100a63Svk 	if (proxyfd < 0)
17903100a63Svk 		return (-1);
18003100a63Svk 
18103100a63Svk 	if (socksv == 5) {
18203100a63Svk 		if (decode_addrport(host, port, (struct sockaddr *)&addr,
18303100a63Svk 		    sizeof (addr), 0, 1) == -1)
18403100a63Svk 			addr.ss_family = 0; /* used in switch below */
18503100a63Svk 
18603100a63Svk 		/* Version 5, one method: no authentication */
18703100a63Svk 		buf[0] = SOCKS_V5;
18803100a63Svk 		buf[1] = 1;
18903100a63Svk 		buf[2] = SOCKS_NOAUTH;
19003100a63Svk 		cnt = atomicio(vwrite, proxyfd, buf, 3);
19103100a63Svk 		if (cnt != 3)
192*8361acf5SRichard Lowe 			err(1, "write failed (%zu/3)", cnt);
19303100a63Svk 
19403100a63Svk 		cnt = atomicio(read, proxyfd, buf, 2);
19503100a63Svk 		if (cnt != 2)
196*8361acf5SRichard Lowe 			err(1, "read failed (%zu/2)", cnt);
19703100a63Svk 
19803100a63Svk 		if ((unsigned char)buf[1] == SOCKS_NOMETHOD)
19903100a63Svk 			errx(1, "authentication method negotiation failed");
20003100a63Svk 
20103100a63Svk 		switch (addr.ss_family) {
20203100a63Svk 		case 0:
20303100a63Svk 			/* Version 5, connect: domain name */
20403100a63Svk 
20503100a63Svk 			/* Max domain name length is 255 bytes */
20603100a63Svk 			hlen = strlen(host);
20703100a63Svk 			if (hlen > 255)
20803100a63Svk 				errx(1, "host name too long for SOCKS5");
20903100a63Svk 			buf[0] = SOCKS_V5;
21003100a63Svk 			buf[1] = SOCKS_CONNECT;
21103100a63Svk 			buf[2] = 0;
21203100a63Svk 			buf[3] = SOCKS_DOMAIN;
21303100a63Svk 			buf[4] = hlen;
21403100a63Svk 			(void) memcpy(buf + 5, host, hlen);
21503100a63Svk 			(void) memcpy(buf + 5 + hlen, &serverport,
21603100a63Svk 			    sizeof (serverport));
21703100a63Svk 			wlen = 5 + hlen + sizeof (serverport);
21803100a63Svk 			break;
21903100a63Svk 		case AF_INET:
22003100a63Svk 			/* Version 5, connect: IPv4 address */
22103100a63Svk 			buf[0] = SOCKS_V5;
22203100a63Svk 			buf[1] = SOCKS_CONNECT;
22303100a63Svk 			buf[2] = 0;
22403100a63Svk 			buf[3] = SOCKS_IPV4;
22503100a63Svk 			(void) memcpy(buf + 4, &in4->sin_addr,
22603100a63Svk 			    sizeof (in4->sin_addr));
22703100a63Svk 			(void) memcpy(buf + 8, &in4->sin_port,
22803100a63Svk 			    sizeof (in4->sin_port));
22903100a63Svk 			wlen = 4 + sizeof (in4->sin_addr) +
23003100a63Svk 			    sizeof (in4->sin_port);
23103100a63Svk 			break;
23203100a63Svk 		case AF_INET6:
23303100a63Svk 			/* Version 5, connect: IPv6 address */
23403100a63Svk 			buf[0] = SOCKS_V5;
23503100a63Svk 			buf[1] = SOCKS_CONNECT;
23603100a63Svk 			buf[2] = 0;
23703100a63Svk 			buf[3] = SOCKS_IPV6;
23803100a63Svk 			(void) memcpy(buf + 4, &in6->sin6_addr,
23903100a63Svk 			    sizeof (in6->sin6_addr));
24003100a63Svk 			(void) memcpy(buf + 20, &in6->sin6_port,
24103100a63Svk 			    sizeof (in6->sin6_port));
24203100a63Svk 			wlen = 4 + sizeof (in6->sin6_addr) +
24303100a63Svk 			    sizeof (in6->sin6_port);
24403100a63Svk 			break;
24503100a63Svk 		default:
24603100a63Svk 			errx(1, "internal error: silly AF");
24703100a63Svk 		}
24803100a63Svk 
24903100a63Svk 		cnt = atomicio(vwrite, proxyfd, buf, wlen);
25003100a63Svk 		if (cnt != wlen)
251*8361acf5SRichard Lowe 			err(1, "write failed (%zu/%zu)", cnt, wlen);
25203100a63Svk 
25303100a63Svk 		/*
25403100a63Svk 		 * read proxy reply which is 4 byte "header", BND.ADDR
25503100a63Svk 		 * and BND.PORT according to RFC 1928, section 6. BND.ADDR
25603100a63Svk 		 * is 4 bytes in case of IPv4 which gives us 10 bytes in sum.
25703100a63Svk 		 */
25803100a63Svk 		cnt = atomicio(read, proxyfd, buf, 10);
25903100a63Svk 		if (cnt != 10)
260*8361acf5SRichard Lowe 			err(1, "read failed (%zu/10)", cnt);
26103100a63Svk 		if (buf[1] != 0)
26203100a63Svk 			errx(1, "connection failed, SOCKS error %d", buf[1]);
26303100a63Svk 	} else if (socksv == 4) {
26403100a63Svk 		/* This will exit on lookup failure */
26503100a63Svk 		(void) decode_addrport(host, port, (struct sockaddr *)&addr,
26603100a63Svk 		    sizeof (addr), 1, 0);
26703100a63Svk 
26803100a63Svk 		/* Version 4 */
26903100a63Svk 		buf[0] = SOCKS_V4;
27003100a63Svk 		buf[1] = SOCKS_CONNECT;	/* connect */
27103100a63Svk 		(void) memcpy(buf + 2, &in4->sin_port, sizeof (in4->sin_port));
27203100a63Svk 		(void) memcpy(buf + 4, &in4->sin_addr, sizeof (in4->sin_addr));
27303100a63Svk 		buf[8] = 0;	/* empty username */
27403100a63Svk 		wlen = 9;
27503100a63Svk 
27603100a63Svk 		cnt = atomicio(vwrite, proxyfd, buf, wlen);
27703100a63Svk 		if (cnt != wlen)
278*8361acf5SRichard Lowe 			err(1, "write failed (%zu/%zu)", cnt, wlen);
27903100a63Svk 
28003100a63Svk 		/*
28103100a63Svk 		 * SOCKSv4 proxy replies consists of 2 byte "header",
28203100a63Svk 		 * port number and numeric IPv4 address which gives 8 bytes.
28303100a63Svk 		 */
28403100a63Svk 		cnt = atomicio(read, proxyfd, buf, 8);
28503100a63Svk 		if (cnt != 8)
286*8361acf5SRichard Lowe 			err(1, "read failed (%zu/8)", cnt);
28703100a63Svk 		if (buf[1] != 90)
28803100a63Svk 			errx(1, "connection failed, SOCKS error %d", buf[1]);
28903100a63Svk 	} else if (socksv == -1) {
29003100a63Svk 		/* HTTP proxy CONNECT according to RFC 2817, section 5 */
29103100a63Svk 
29203100a63Svk 		/* Disallow bad chars in hostname */
29303100a63Svk 		if (strcspn(host, "\r\n\t []:") != strlen(host))
29403100a63Svk 			errx(1, "Invalid hostname");
29503100a63Svk 
29603100a63Svk 		/* Try to be sane about numeric IPv6 addresses */
29703100a63Svk 		if (strchr(host, ':') != NULL) {
29803100a63Svk 			r = snprintf(buf, sizeof (buf),
29903100a63Svk 			    "CONNECT [%s]:%d HTTP/1.0\r\n",
30003100a63Svk 			    host, ntohs(serverport));
30103100a63Svk 		} else {
30203100a63Svk 			r = snprintf(buf, sizeof (buf),
30303100a63Svk 			    "CONNECT %s:%d HTTP/1.0\r\n",
30403100a63Svk 			    host, ntohs(serverport));
30503100a63Svk 		}
30603100a63Svk 		if (r == -1 || (size_t)r >= sizeof (buf))
30703100a63Svk 			errx(1, "hostname too long");
30803100a63Svk 		r = strlen(buf);
30903100a63Svk 
31003100a63Svk 		cnt = atomicio(vwrite, proxyfd, buf, r);
31103100a63Svk 		if (cnt != r)
312*8361acf5SRichard Lowe 			err(1, "write failed (%zu/%d)", cnt, r);
31303100a63Svk 
31403100a63Svk 		if (authretry > 1) {
31503100a63Svk 			char resp[1024];
31603100a63Svk 
31703100a63Svk 			proxypass = getproxypass(proxyuser, proxyhost);
31803100a63Svk 			r = snprintf(buf, sizeof (buf), "%s:%s",
31903100a63Svk 			    proxyuser, proxypass);
32003100a63Svk 			free((void *)proxypass);
32103100a63Svk 			if (r == -1 || (size_t)r >= sizeof (buf) ||
32203100a63Svk 			    b64_ntop((unsigned char *)buf, strlen(buf), resp,
32303100a63Svk 			    sizeof (resp)) == -1)
32403100a63Svk 				errx(1, "Proxy username/password too long");
32503100a63Svk 			r = snprintf(buf, sizeof (buf), "Proxy-Authorization: "
32603100a63Svk 			    "Basic %s\r\n", resp);
32703100a63Svk 			if (r == -1 || (size_t)r >= sizeof (buf))
32803100a63Svk 				errx(1, "Proxy auth response too long");
32903100a63Svk 			r = strlen(buf);
33003100a63Svk 			if ((cnt = atomicio(vwrite, proxyfd, buf, r)) != r)
331*8361acf5SRichard Lowe 				err(1, "write failed (%zu/%d)", cnt, r);
33203100a63Svk 		}
33303100a63Svk 
33403100a63Svk 		/* Terminate headers */
33503100a63Svk 		if ((r = atomicio(vwrite, proxyfd, "\r\n", 2)) != 2)
33603100a63Svk 			err(1, "write failed (2/%d)", r);
33703100a63Svk 
33803100a63Svk 		/* Read status reply */
33903100a63Svk 		(void) proxy_read_line(proxyfd, buf, sizeof (buf));
34003100a63Svk 		if (proxyuser != NULL &&
34103100a63Svk 		    strncmp(buf, HTTP_10_407, strlen(HTTP_10_407)) == 0) {
34203100a63Svk 			if (authretry > 1) {
34303100a63Svk 				(void) fprintf(stderr, "Proxy authentication "
34403100a63Svk 				    "failed\n");
34503100a63Svk 			}
34603100a63Svk 			(void) close(proxyfd);
34703100a63Svk 			goto again;
34803100a63Svk 		} else if (strncmp(buf, HTTP_10_200,
34903100a63Svk 		    strlen(HTTP_10_200)) != 0 && strncmp(buf, HTTP_11_200,
35003100a63Svk 		    strlen(HTTP_11_200)) != 0)
35103100a63Svk 			errx(1, "Proxy error: \"%s\"", buf);
35203100a63Svk 
35303100a63Svk 		/* Headers continue until we hit an empty line */
35403100a63Svk 		for (r = 0; r < HTTP_MAXHDRS; r++) {
35503100a63Svk 			(void) proxy_read_line(proxyfd, buf, sizeof (buf));
35603100a63Svk 			if (*buf == '\0')
35703100a63Svk 				break;
35803100a63Svk 		}
35903100a63Svk 		if (*buf != '\0')
36003100a63Svk 			errx(1, "Too many proxy headers received");
36103100a63Svk 	} else
36203100a63Svk 		errx(1, "Unknown proxy protocol %d", socksv);
36303100a63Svk 
36403100a63Svk 	return (proxyfd);
36503100a63Svk }
366