xref: /freebsd/contrib/netcat/socks.c (revision 87569f75a91f298c52a71823c04d41cf53c88889)
1 /*	$OpenBSD: socks.c,v 1.9 2004/10/17 03:13:55 djm Exp $	*/
2 
3 /*
4  * Copyright (c) 1999 Niklas Hallqvist.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 
32 #include <err.h>
33 #include <errno.h>
34 #include <netdb.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 
40 #define SOCKS_PORT	"1080"
41 #define HTTP_PROXY_PORT	"3128"
42 #define HTTP_MAXHDRS	64
43 #define SOCKS_V5	5
44 #define SOCKS_V4	4
45 #define SOCKS_NOAUTH	0
46 #define SOCKS_NOMETHOD	0xff
47 #define SOCKS_CONNECT	1
48 #define SOCKS_IPV4	1
49 
50 
51 int	remote_connect(char *, char *, struct addrinfo);
52 int	socks_connect(char *host, char *port, struct addrinfo hints,
53 	    char *proxyhost, char *proxyport, struct addrinfo proxyhints,
54 	    int socksv);
55 
56 static in_addr_t
57 decode_addr(const char *s)
58 {
59 	struct hostent *hp = gethostbyname (s);
60 	struct in_addr retval;
61 
62 	if (hp)
63 		return *(in_addr_t *)hp->h_addr_list[0];
64 	if (inet_aton (s, &retval))
65 		return retval.s_addr;
66 	errx (1, "cannot decode address \"%s\"", s);
67 }
68 
69 static in_port_t
70 decode_port(const char *s)
71 {
72 	struct servent *sp;
73 	in_port_t port;
74 	char *p;
75 
76 	port = strtol (s, &p, 10);
77 	if (s == p) {
78 		sp = getservbyname (s, "tcp");
79 		if (sp)
80 			return sp->s_port;
81 	}
82 	if (*s != '\0' && *p == '\0')
83 		return htons (port);
84 	errx (1, "cannot decode port \"%s\"", s);
85 }
86 
87 static int
88 proxy_read_line(int fd, char *buf, int bufsz)
89 {
90 	int r, off;
91 
92 	for(off = 0;;) {
93 		if (off >= bufsz)
94 			errx(1, "proxy read too long");
95 		if ((r = read(fd, buf + off, 1)) <= 0) {
96 			if (r == -1 && errno == EINTR)
97 				continue;
98 			err(1, "proxy read");
99 		}
100 		/* Skip CR */
101 		if (buf[off] == '\r')
102 			continue;
103 		if (buf[off] == '\n') {
104 			buf[off] = '\0';
105 			break;
106 		}
107 		off++;
108 	}
109 	return (off);
110 }
111 
112 int
113 socks_connect(char *host, char *port, struct addrinfo hints,
114     char *proxyhost, char *proxyport, struct addrinfo proxyhints,
115     int socksv)
116 {
117 	int proxyfd, r;
118 	unsigned char buf[1024];
119 	ssize_t cnt;
120 	in_addr_t serveraddr;
121 	in_port_t serverport;
122 
123 	if (proxyport == NULL)
124 		proxyport = (socksv == -1) ? HTTP_PROXY_PORT : SOCKS_PORT;
125 
126 	proxyfd = remote_connect(proxyhost, proxyport, proxyhints);
127 
128 	if (proxyfd < 0)
129 		return -1;
130 
131 	serveraddr = decode_addr (host);
132 	serverport = decode_port (port);
133 
134 	if (socksv == 5) {
135 		/* Version 5, one method: no authentication */
136 		buf[0] = SOCKS_V5;
137 		buf[1] = 1;
138 		buf[2] = SOCKS_NOAUTH;
139 		cnt = write (proxyfd, buf, 3);
140 		if (cnt == -1)
141 			err (1, "write failed");
142 		if (cnt != 3)
143 			errx (1, "short write, %d (expected 3)", cnt);
144 
145 		read (proxyfd, buf, 2);
146 		if (buf[1] == SOCKS_NOMETHOD)
147 			errx (1, "authentication method negotiation failed");
148 
149 		/* Version 5, connect: IPv4 address */
150 		buf[0] = SOCKS_V5;
151 		buf[1] = SOCKS_CONNECT;
152 		buf[2] = 0;
153 		buf[3] = SOCKS_IPV4;
154 		memcpy (buf + 4, &serveraddr, sizeof serveraddr);
155 		memcpy (buf + 8, &serverport, sizeof serverport);
156 
157 		/* XXX Handle short writes better */
158 		cnt = write (proxyfd, buf, 10);
159 		if (cnt == -1)
160 			err (1, "write failed");
161 		if (cnt != 10)
162 			errx (1, "short write, %d (expected 10)", cnt);
163 
164 		/* XXX Handle short reads better */
165 		cnt = read (proxyfd, buf, sizeof buf);
166 		if (cnt == -1)
167 			err (1, "read failed");
168 		if (cnt != 10)
169 			errx (1, "unexpected reply size %d (expected 10)", cnt);
170 		if (buf[1] != 0)
171 			errx (1, "connection failed, SOCKS error %d", buf[1]);
172 	} else if (socksv == 4) {
173 		/* Version 4 */
174 		buf[0] = SOCKS_V4;
175 		buf[1] = SOCKS_CONNECT;	/* connect */
176 		memcpy (buf + 2, &serverport, sizeof serverport);
177 		memcpy (buf + 4, &serveraddr, sizeof serveraddr);
178 		buf[8] = 0;	/* empty username */
179 
180 		cnt = write (proxyfd, buf, 9);
181 		if (cnt == -1)
182 			err (1, "write failed");
183 		if (cnt != 9)
184 			errx (1, "short write, %d (expected 9)", cnt);
185 
186 		/* XXX Handle short reads better */
187 		cnt = read (proxyfd, buf, 8);
188 		if (cnt == -1)
189 			err (1, "read failed");
190 		if (cnt != 8)
191 			errx (1, "unexpected reply size %d (expected 8)", cnt);
192 		if (buf[1] != 90)
193 			errx (1, "connection failed, SOCKS error %d", buf[1]);
194 	} else if (socksv == -1) {
195 		/* HTTP proxy CONNECT */
196 
197 		/* Disallow bad chars in hostname */
198 		if (strcspn(host, "\r\n\t []:") != strlen(host))
199 			errx (1, "Invalid hostname");
200 
201 		/* Try to be sane about numeric IPv6 addresses */
202 		if (strchr(host, ':') != NULL) {
203 			r = snprintf(buf, sizeof(buf),
204 			    "CONNECT [%s]:%d HTTP/1.0\r\n\r\n",
205 			    host, ntohs(serverport));
206 		} else {
207 			r = snprintf(buf, sizeof(buf),
208 			    "CONNECT %s:%d HTTP/1.0\r\n\r\n",
209 			    host, ntohs(serverport));
210 		}
211 		if (r == -1 || r >= sizeof(buf))
212 			errx (1, "hostname too long");
213 		r = strlen(buf);
214 
215 		/* XXX atomicio */
216 		cnt = write (proxyfd, buf, r);
217 		if (cnt == -1)
218 			err (1, "write failed");
219 		if (cnt != r)
220 			errx (1, "short write, %d (expected %d)", cnt, r);
221 
222 		/* Read reply */
223 		for (r = 0; r < HTTP_MAXHDRS; r++) {
224 			proxy_read_line(proxyfd, buf, sizeof(buf));
225 			if (r == 0 && strncmp(buf, "HTTP/1.0 200 ", 12) != 0)
226 				errx (1, "Proxy error: \"%s\"", buf);
227 			/* Discard headers until we hit an empty line */
228 			if (*buf == '\0')
229 				break;
230 		}
231 	} else
232 		errx (1, "Unknown proxy protocol %d", socksv);
233 
234 	return proxyfd;
235 }
236