1 /* $OpenBSD: socks.c,v 1.30 2019/11/04 17:33:28 millert 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 <bsd/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
, char *);
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 memset(&hints
, 0, 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
,
114 char *pw
, size_t pwlen
)
118 snprintf(prompt
, sizeof(prompt
), "Proxy password for %s@%s: ",
119 proxyuser
, proxyhost
);
120 if (readpassphrase(prompt
, pw
, pwlen
, RPP_REQUIRE_TTY
) == NULL
)
121 errx(1, "Unable to read proxy passphrase");
125 * Error strings adapted from the generally accepted SOCKSv4 spec:
127 * http://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4.protocol
130 socks4_strerror(int e
)
136 return "Request rejected or failed";
138 return "SOCKS server cannot connect to identd on the client";
140 return "Client program and identd report different user-ids";
142 return "Unknown error";
147 * Error strings taken almost directly from RFC 1928.
150 socks5_strerror(int e
)
156 return "General SOCKS server failure";
158 return "Connection not allowed by ruleset";
160 return "Network unreachable";
162 return "Host unreachable";
164 return "Connection refused";
166 return "TTL expired";
168 return "Command not supported";
170 return "Address type not supported";
172 return "Unknown error";
177 socks_connect(const char *host
, const char *port
,
178 struct addrinfo hints
__attribute__ ((__unused__
)),
179 const char *proxyhost
, const char *proxyport
, struct addrinfo proxyhints
,
180 int socksv
, const char *proxyuser
)
182 int proxyfd
, r
, authretry
= 0;
184 unsigned char buf
[1024];
186 struct sockaddr_storage addr
;
187 struct sockaddr_in
*in4
= (struct sockaddr_in
*)&addr
;
188 struct sockaddr_in6
*in6
= (struct sockaddr_in6
*)&addr
;
189 in_port_t serverport
;
191 if (proxyport
== NULL
)
192 proxyport
= (socksv
== -1) ? HTTP_PROXY_PORT
: SOCKS_PORT
;
194 /* Abuse API to lookup port */
195 if (decode_addrport("0.0.0.0", port
, (struct sockaddr
*)&addr
,
196 sizeof(addr
), 1, 1) == -1)
197 errx(1, "unknown port \"%.64s\"", port
);
198 serverport
= in4
->sin_port
;
202 errx(1, "Too many authentication failures");
204 proxyfd
= remote_connect(proxyhost
, proxyport
, proxyhints
, NULL
);
210 if (decode_addrport(host
, port
, (struct sockaddr
*)&addr
,
211 sizeof(addr
), 0, 1) == -1)
212 addr
.ss_family
= 0; /* used in switch below */
214 /* Version 5, one method: no authentication */
217 buf
[2] = SOCKS_NOAUTH
;
218 cnt
= atomicio(vwrite
, proxyfd
, buf
, 3);
220 err(1, "write failed (%zu/3)", (size_t)cnt
);
222 cnt
= atomicio(read
, proxyfd
, buf
, 2);
224 err(1, "read failed (%zu/3)", (size_t)cnt
);
226 if (buf
[1] == SOCKS_NOMETHOD
)
227 errx(1, "authentication method negotiation failed");
229 switch (addr
.ss_family
) {
231 /* Version 5, connect: domain name */
233 /* Max domain name length is 255 bytes */
236 errx(1, "host name too long for SOCKS5");
238 buf
[1] = SOCKS_CONNECT
;
240 buf
[3] = SOCKS_DOMAIN
;
242 memcpy(buf
+ 5, host
, hlen
);
243 memcpy(buf
+ 5 + hlen
, &serverport
, sizeof serverport
);
247 /* Version 5, connect: IPv4 address */
249 buf
[1] = SOCKS_CONNECT
;
252 memcpy(buf
+ 4, &in4
->sin_addr
, sizeof in4
->sin_addr
);
253 memcpy(buf
+ 8, &in4
->sin_port
, sizeof in4
->sin_port
);
257 /* Version 5, connect: IPv6 address */
259 buf
[1] = SOCKS_CONNECT
;
262 memcpy(buf
+ 4, &in6
->sin6_addr
, sizeof in6
->sin6_addr
);
263 memcpy(buf
+ 20, &in6
->sin6_port
,
264 sizeof in6
->sin6_port
);
268 errx(1, "internal error: silly AF");
271 cnt
= atomicio(vwrite
, proxyfd
, buf
, wlen
);
273 err(1, "write failed (%zu/%zu)", (size_t)cnt
, (size_t)wlen
);
275 cnt
= atomicio(read
, proxyfd
, buf
, 4);
277 err(1, "read failed (%zu/4)", (size_t)cnt
);
279 errx(1, "connection failed, SOCKSv5 error: %s",
280 socks5_strerror(buf
[1]));
284 cnt
= atomicio(read
, proxyfd
, buf
+ 4, 6);
286 err(1, "read failed (%zu/6)", (size_t)cnt
);
289 cnt
= atomicio(read
, proxyfd
, buf
+ 4, 18);
291 err(1, "read failed (%zu/18)", (size_t)cnt
);
294 errx(1, "connection failed, unsupported address type");
296 } else if (socksv
== 4) {
297 /* This will exit on lookup failure */
298 decode_addrport(host
, port
, (struct sockaddr
*)&addr
,
303 buf
[1] = SOCKS_CONNECT
; /* connect */
304 memcpy(buf
+ 2, &in4
->sin_port
, sizeof in4
->sin_port
);
305 memcpy(buf
+ 4, &in4
->sin_addr
, sizeof in4
->sin_addr
);
306 buf
[8] = 0; /* empty username */
309 cnt
= atomicio(vwrite
, proxyfd
, buf
, wlen
);
311 err(1, "write failed (%zu/%zu)", (size_t)cnt
, (size_t)wlen
);
313 cnt
= atomicio(read
, proxyfd
, buf
, 8);
315 err(1, "read failed (%zu/8)", (size_t)cnt
);
317 errx(1, "connection failed, SOCKSv4 error: %s",
318 socks4_strerror(buf
[1]));
320 } else if (socksv
== -1) {
321 /* HTTP proxy CONNECT */
323 /* Disallow bad chars in hostname */
324 if (strcspn(host
, "\r\n\t []:") != strlen(host
))
325 errx(1, "Invalid hostname");
327 /* Try to be sane about numeric IPv6 addresses */
328 if (strchr(host
, ':') != NULL
) {
329 r
= snprintf((char*)buf
, sizeof(buf
),
330 "CONNECT [%s]:%d HTTP/1.0\r\n",
331 host
, ntohs(serverport
));
333 r
= snprintf((char*)buf
, sizeof(buf
),
334 "CONNECT %s:%d HTTP/1.0\r\n",
335 host
, ntohs(serverport
));
337 if (r
< 0 || (size_t)r
>= sizeof(buf
))
338 errx(1, "hostname too long");
339 r
= strlen((char*)buf
);
341 cnt
= atomicio(vwrite
, proxyfd
, buf
, r
);
343 err(1, "write failed (%zu/%d)", (size_t)cnt
, (int)r
);
349 getproxypass(proxyuser
, proxyhost
,
350 proxypass
, sizeof proxypass
);
351 r
= snprintf((char*)buf
, sizeof(buf
), "%s:%s",
352 proxyuser
, proxypass
);
353 explicit_bzero(proxypass
, sizeof proxypass
);
354 if (r
== -1 || (size_t)r
>= sizeof(buf
) ||
355 b64_ntop(buf
, strlen((char*)buf
), resp
,
357 errx(1, "Proxy username/password too long");
358 r
= snprintf((char*)buf
, sizeof(buf
), "Proxy-Authorization: "
359 "Basic %s\r\n", resp
);
360 if (r
< 0 || (size_t)r
>= sizeof(buf
))
361 errx(1, "Proxy auth response too long");
362 r
= strlen((char*)buf
);
363 if ((cnt
= atomicio(vwrite
, proxyfd
, buf
, r
)) != r
)
364 err(1, "write failed (%zu/%d)", (size_t)cnt
, r
);
365 explicit_bzero(proxypass
, sizeof proxypass
);
366 explicit_bzero(buf
, sizeof buf
);
369 /* Terminate headers */
370 if ((cnt
= atomicio(vwrite
, proxyfd
, "\r\n", 2)) != 2)
371 err(1, "write failed (%zu/2)", cnt
);
373 /* Read status reply */
374 proxy_read_line(proxyfd
, (char*)buf
, sizeof(buf
));
375 if (proxyuser
!= NULL
&&
376 (strncmp((char*)buf
, "HTTP/1.0 407 ", 12) == 0 ||
377 strncmp((char*)buf
, "HTTP/1.1 407 ", 12) == 0)) {
379 fprintf(stderr
, "Proxy authentication "
384 } else if (strncmp((char*)buf
, "HTTP/1.0 200 ", 12) != 0 &&
385 strncmp((char*)buf
, "HTTP/1.1 200 ", 12) != 0)
386 errx(1, "Proxy error: \"%s\"", buf
);
388 /* Headers continue until we hit an empty line */
389 for (r
= 0; r
< HTTP_MAXHDRS
; r
++) {
390 proxy_read_line(proxyfd
, (char*)buf
, sizeof(buf
));
395 errx(1, "Too many proxy headers received");
397 errx(1, "Unknown proxy protocol %d", socksv
);