1 /* $OpenBSD: socks.c,v 1.24 2016/06/27 14:43:04 deraadt Exp $ */
4 * Copyright (c) 1999 Niklas Hallqvist. All rights reserved.
5 * Copyright (c) 2004, 2005 Damien Miller. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
41 #include <readpassphrase.h>
44 #define SOCKS_PORT "1080"
45 #define HTTP_PROXY_PORT "3128"
46 #define HTTP_MAXHDRS 64
49 #define SOCKS_NOAUTH 0
50 #define SOCKS_NOMETHOD 0xff
51 #define SOCKS_CONNECT 1
53 #define SOCKS_DOMAIN 3
56 int remote_connect(const char *, const char *, struct addrinfo
);
57 int socks_connect(const char *, const char *, struct addrinfo
,
58 const char *, const char *, struct addrinfo
, int,
62 decode_addrport(const char *h
, const char *p
, struct sockaddr
*addr
,
63 socklen_t addrlen
, int v4only
, int numeric
)
66 struct addrinfo hints
, *res
;
68 bzero(&hints
, sizeof(hints
));
69 hints
.ai_family
= v4only
? PF_INET
: PF_UNSPEC
;
70 hints
.ai_flags
= numeric
? AI_NUMERICHOST
: 0;
71 hints
.ai_socktype
= SOCK_STREAM
;
72 r
= getaddrinfo(h
, p
, &hints
, &res
);
73 /* Don't fatal when attempting to convert a numeric address */
76 errx(1, "getaddrinfo(\"%.64s\", \"%.64s\"): %s", h
, p
,
81 if (addrlen
< res
->ai_addrlen
) {
83 errx(1, "internal error: addrlen < res->ai_addrlen");
85 memcpy(addr
, res
->ai_addr
, res
->ai_addrlen
);
91 proxy_read_line(int fd
, char *buf
, size_t bufsz
)
97 errx(1, "proxy read too long");
98 if (atomicio(read
, fd
, buf
+ off
, 1) != 1)
101 if (buf
[off
] == '\r')
103 if (buf
[off
] == '\n') {
113 getproxypass(const char *proxyuser
, const char *proxyhost
)
118 snprintf(prompt
, sizeof(prompt
), "Proxy password for %s@%s: ",
119 proxyuser
, proxyhost
);
120 if (readpassphrase(prompt
, pw
, sizeof(pw
), RPP_REQUIRE_TTY
) == NULL
)
121 errx(1, "Unable to read proxy passphrase");
126 * Error strings adapted from the generally accepted SOCKSv4 spec:
128 * http://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4.protocol
131 socks4_strerror(int e
)
137 return "Request rejected or failed";
139 return "SOCKS server cannot connect to identd on the client";
141 return "Client program and identd report different user-ids";
143 return "Unknown error";
148 * Error strings taken almost directly from RFC 1928.
151 socks5_strerror(int e
)
157 return "General SOCKS server failure";
159 return "Connection not allowed by ruleset";
161 return "Network unreachable";
163 return "Host unreachable";
165 return "Connection refused";
167 return "TTL expired";
169 return "Command not supported";
171 return "Address type not supported";
173 return "Unknown error";
178 socks_connect(const char *host
, const char *port
,
179 struct addrinfo hints
__attribute__ ((__unused__
)),
180 const char *proxyhost
, const char *proxyport
, struct addrinfo proxyhints
,
181 int socksv
, const char *proxyuser
)
183 int proxyfd
, r
, authretry
= 0;
185 unsigned char buf
[1024];
187 struct sockaddr_storage addr
;
188 struct sockaddr_in
*in4
= (struct sockaddr_in
*)&addr
;
189 struct sockaddr_in6
*in6
= (struct sockaddr_in6
*)&addr
;
190 in_port_t serverport
;
191 const char *proxypass
= NULL
;
193 if (proxyport
== NULL
)
194 proxyport
= (socksv
== -1) ? HTTP_PROXY_PORT
: SOCKS_PORT
;
196 /* Abuse API to lookup port */
197 if (decode_addrport("0.0.0.0", port
, (struct sockaddr
*)&addr
,
198 sizeof(addr
), 1, 1) == -1)
199 errx(1, "unknown port \"%.64s\"", port
);
200 serverport
= in4
->sin_port
;
204 errx(1, "Too many authentication failures");
206 proxyfd
= remote_connect(proxyhost
, proxyport
, proxyhints
);
212 if (decode_addrport(host
, port
, (struct sockaddr
*)&addr
,
213 sizeof(addr
), 0, 1) == -1)
214 addr
.ss_family
= 0; /* used in switch below */
216 /* Version 5, one method: no authentication */
219 buf
[2] = SOCKS_NOAUTH
;
220 cnt
= atomicio(vwrite
, proxyfd
, buf
, 3);
222 err(1, "write failed (%zu/3)", cnt
);
224 cnt
= atomicio(read
, proxyfd
, buf
, 2);
226 err(1, "read failed (%zu/3)", cnt
);
228 if (buf
[1] == SOCKS_NOMETHOD
)
229 errx(1, "authentication method negotiation failed");
231 switch (addr
.ss_family
) {
233 /* Version 5, connect: domain name */
235 /* Max domain name length is 255 bytes */
238 errx(1, "host name too long for SOCKS5");
240 buf
[1] = SOCKS_CONNECT
;
242 buf
[3] = SOCKS_DOMAIN
;
244 memcpy(buf
+ 5, host
, hlen
);
245 memcpy(buf
+ 5 + hlen
, &serverport
, sizeof serverport
);
249 /* Version 5, connect: IPv4 address */
251 buf
[1] = SOCKS_CONNECT
;
254 memcpy(buf
+ 4, &in4
->sin_addr
, sizeof in4
->sin_addr
);
255 memcpy(buf
+ 8, &in4
->sin_port
, sizeof in4
->sin_port
);
259 /* Version 5, connect: IPv6 address */
261 buf
[1] = SOCKS_CONNECT
;
264 memcpy(buf
+ 4, &in6
->sin6_addr
, sizeof in6
->sin6_addr
);
265 memcpy(buf
+ 20, &in6
->sin6_port
,
266 sizeof in6
->sin6_port
);
270 errx(1, "internal error: silly AF");
273 cnt
= atomicio(vwrite
, proxyfd
, buf
, wlen
);
275 err(1, "write failed (%zu/%zu)", cnt
, wlen
);
277 cnt
= atomicio(read
, proxyfd
, buf
, 4);
279 err(1, "read failed (%zu/4)", cnt
);
281 errx(1, "connection failed, SOCKSv5 error: %s",
282 socks5_strerror(buf
[1]));
286 cnt
= atomicio(read
, proxyfd
, buf
+ 4, 6);
288 err(1, "read failed (%zu/6)", cnt
);
291 cnt
= atomicio(read
, proxyfd
, buf
+ 4, 18);
293 err(1, "read failed (%zu/18)", cnt
);
296 errx(1, "connection failed, unsupported address type");
298 } else if (socksv
== 4) {
299 /* This will exit on lookup failure */
300 decode_addrport(host
, port
, (struct sockaddr
*)&addr
,
305 buf
[1] = SOCKS_CONNECT
; /* connect */
306 memcpy(buf
+ 2, &in4
->sin_port
, sizeof in4
->sin_port
);
307 memcpy(buf
+ 4, &in4
->sin_addr
, sizeof in4
->sin_addr
);
308 buf
[8] = 0; /* empty username */
311 cnt
= atomicio(vwrite
, proxyfd
, buf
, wlen
);
313 err(1, "write failed (%zu/%zu)", cnt
, wlen
);
315 cnt
= atomicio(read
, proxyfd
, buf
, 8);
317 err(1, "read failed (%zu/8)", cnt
);
319 errx(1, "connection failed, SOCKSv4 error: %s",
320 socks4_strerror(buf
[1]));
322 } else if (socksv
== -1) {
323 /* HTTP proxy CONNECT */
325 /* Disallow bad chars in hostname */
326 if (strcspn(host
, "\r\n\t []:") != strlen(host
))
327 errx(1, "Invalid hostname");
329 /* Try to be sane about numeric IPv6 addresses */
330 if (strchr(host
, ':') != NULL
) {
331 r
= snprintf(buf
, sizeof(buf
),
332 "CONNECT [%s]:%d HTTP/1.0\r\n",
333 host
, ntohs(serverport
));
335 r
= snprintf(buf
, sizeof(buf
),
336 "CONNECT %s:%d HTTP/1.0\r\n",
337 host
, ntohs(serverport
));
339 if (r
== -1 || (size_t)r
>= sizeof(buf
))
340 errx(1, "hostname too long");
343 cnt
= atomicio(vwrite
, proxyfd
, buf
, r
);
345 err(1, "write failed (%zu/%d)", cnt
, r
);
350 proxypass
= getproxypass(proxyuser
, proxyhost
);
351 r
= snprintf(buf
, sizeof(buf
), "%s:%s",
352 proxyuser
, proxypass
);
353 if (r
== -1 || (size_t)r
>= sizeof(buf
) ||
354 b64_ntop(buf
, strlen(buf
), resp
,
356 errx(1, "Proxy username/password too long");
357 r
= snprintf(buf
, sizeof(buf
), "Proxy-Authorization: "
358 "Basic %s\r\n", resp
);
359 if (r
== -1 || (size_t)r
>= sizeof(buf
))
360 errx(1, "Proxy auth response too long");
362 if ((cnt
= atomicio(vwrite
, proxyfd
, buf
, r
)) != r
)
363 err(1, "write failed (%zu/%d)", cnt
, r
);
366 /* Terminate headers */
367 if ((cnt
= atomicio(vwrite
, proxyfd
, "\r\n", 2)) != 2)
368 err(1, "write failed (%zu/2)", cnt
);
370 /* Read status reply */
371 proxy_read_line(proxyfd
, buf
, sizeof(buf
));
372 if (proxyuser
!= NULL
&&
373 strncmp(buf
, "HTTP/1.0 407 ", 12) == 0) {
375 fprintf(stderr
, "Proxy authentication "
380 } else if (strncmp(buf
, "HTTP/1.0 200 ", 12) != 0 &&
381 strncmp(buf
, "HTTP/1.1 200 ", 12) != 0)
382 errx(1, "Proxy error: \"%s\"", buf
);
384 /* Headers continue until we hit an empty line */
385 for (r
= 0; r
< HTTP_MAXHDRS
; r
++) {
386 proxy_read_line(proxyfd
, buf
, sizeof(buf
));
391 errx(1, "Too many proxy headers received");
393 errx(1, "Unknown proxy protocol %d", socksv
);