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