1 /* $OpenBSD: socks.c,v 1.17 2006/09/25 04:51:20 ray Exp $ */ 2 3 /* 4 * Copyright (c) 1999 Niklas Hallqvist. All rights reserved. 5 * Copyright (c) 2004, 2005 Damien Miller. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 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. 15 * 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. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 #include <sys/types.h> 31 #include <sys/socket.h> 32 #include <netinet/in.h> 33 #include <arpa/inet.h> 34 35 #include <err.h> 36 #include <errno.h> 37 #include <netdb.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include <resolv.h> 43 #include <strings.h> 44 #include "atomicio.h" 45 46 #define SOCKS_PORT "1080" 47 #define HTTP_PROXY_PORT "3128" 48 #define HTTP_MAXHDRS 64 49 #define SOCKS_V5 5 50 #define SOCKS_V4 4 51 #define SOCKS_NOAUTH 0 52 #define SOCKS_NOMETHOD 0xff 53 #define SOCKS_CONNECT 1 54 #define SOCKS_IPV4 1 55 #define SOCKS_DOMAIN 3 56 #define SOCKS_IPV6 4 57 58 #define HTTP_10_407 "HTTP/1.0 407 " 59 #define HTTP_10_200 "HTTP/1.0 200 " 60 #define HTTP_11_200 "HTTP/1.1 200 " 61 62 int remote_connect(const char *, const char *, struct addrinfo); 63 int socks_connect(const char *, const char *, 64 const char *, const char *, struct addrinfo, int, 65 const char *); 66 67 /* 68 * Convert string representation of host (h) and service/port (p) into 69 * sockaddr structure and return 0 on success, -1 on failure. 70 * Indicate whether the host address is IPv4 (v4only) and numeric. 71 */ 72 static int 73 decode_addrport(const char *h, const char *p, struct sockaddr *addr, 74 socklen_t addrlen, int v4only, int numeric) 75 { 76 int r; 77 struct addrinfo hints, *res; 78 79 bzero(&hints, sizeof (hints)); 80 hints.ai_family = v4only ? PF_INET : PF_UNSPEC; 81 hints.ai_flags = numeric ? AI_NUMERICHOST : 0; 82 hints.ai_socktype = SOCK_STREAM; 83 r = getaddrinfo(h, p, &hints, &res); 84 /* Don't fatal when attempting to convert a numeric address */ 85 if (r != 0) { 86 if (!numeric) { 87 errx(1, "getaddrinfo(\"%.64s\", \"%.64s\"): %s", h, p, 88 gai_strerror(r)); 89 } 90 return (-1); 91 } 92 if (addrlen < res->ai_addrlen) { 93 freeaddrinfo(res); 94 errx(1, "internal error: addrlen < res->ai_addrlen"); 95 } 96 (void) memcpy(addr, res->ai_addr, res->ai_addrlen); 97 freeaddrinfo(res); 98 return (0); 99 } 100 101 /* 102 * Read single line from a descriptor into buffer up to bufsz bytes, 103 * byte by byte. Returns length of the line (including ending NULL), 104 * exits upon failure. 105 */ 106 static int 107 proxy_read_line(int fd, char *buf, size_t bufsz) 108 { 109 size_t off; 110 111 for (off = 0; ; ) { 112 if (off >= bufsz) 113 errx(1, "proxy read too long"); 114 if (atomicio(read, fd, buf + off, 1) != 1) 115 err(1, "proxy read"); 116 /* Skip CR */ 117 if (buf[off] == '\r') 118 continue; 119 if (buf[off] == '\n') { 120 buf[off] = '\0'; 121 break; 122 } 123 /* 124 * we rewite \r\n to NULL since socks_connect() relies 125 * on *buf being zero in that case. 126 */ 127 off++; 128 } 129 return (off); 130 } 131 132 /* 133 * Read proxy password from user and return it. The arguments are used 134 * only for prompt construction. 135 */ 136 static const char * 137 getproxypass(const char *proxyuser, const char *proxyhost) 138 { 139 char prompt[512]; 140 const char *pw; 141 142 (void) snprintf(prompt, sizeof (prompt), "Proxy password for %s@%s: ", 143 proxyuser, proxyhost); 144 if ((pw = getpassphrase(prompt)) == NULL) 145 errx(1, "Unable to read proxy passphrase"); 146 return (pw); 147 } 148 149 /* perform connection via proxy using SOCKSv[45] or HTTP proxy CONNECT */ 150 int 151 socks_connect(const char *host, const char *port, const char *proxyhost, 152 const char *proxyport, struct addrinfo proxyhints, int socksv, 153 const char *proxyuser) 154 { 155 int proxyfd, r, authretry = 0; 156 size_t hlen, wlen; 157 char buf[1024]; 158 size_t cnt; 159 struct sockaddr_storage addr; 160 struct sockaddr_in *in4 = (struct sockaddr_in *)&addr; 161 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&addr; 162 in_port_t serverport; 163 const char *proxypass = NULL; 164 165 if (proxyport == NULL) 166 proxyport = (socksv == -1) ? HTTP_PROXY_PORT : SOCKS_PORT; 167 168 /* Abuse API to lookup port */ 169 if (decode_addrport("0.0.0.0", port, (struct sockaddr *)&addr, 170 sizeof (addr), 1, 1) == -1) 171 errx(1, "unknown port \"%.64s\"", port); 172 serverport = in4->sin_port; 173 174 again: 175 if (authretry++ > 3) 176 errx(1, "Too many authentication failures"); 177 178 proxyfd = remote_connect(proxyhost, proxyport, proxyhints); 179 180 if (proxyfd < 0) 181 return (-1); 182 183 if (socksv == 5) { 184 if (decode_addrport(host, port, (struct sockaddr *)&addr, 185 sizeof (addr), 0, 1) == -1) 186 addr.ss_family = 0; /* used in switch below */ 187 188 /* Version 5, one method: no authentication */ 189 buf[0] = SOCKS_V5; 190 buf[1] = 1; 191 buf[2] = SOCKS_NOAUTH; 192 cnt = atomicio(vwrite, proxyfd, buf, 3); 193 if (cnt != 3) 194 err(1, "write failed (%d/3)", cnt); 195 196 cnt = atomicio(read, proxyfd, buf, 2); 197 if (cnt != 2) 198 err(1, "read failed (%d/3)", cnt); 199 200 if ((unsigned char)buf[1] == SOCKS_NOMETHOD) 201 errx(1, "authentication method negotiation failed"); 202 203 switch (addr.ss_family) { 204 case 0: 205 /* Version 5, connect: domain name */ 206 207 /* Max domain name length is 255 bytes */ 208 hlen = strlen(host); 209 if (hlen > 255) 210 errx(1, "host name too long for SOCKS5"); 211 buf[0] = SOCKS_V5; 212 buf[1] = SOCKS_CONNECT; 213 buf[2] = 0; 214 buf[3] = SOCKS_DOMAIN; 215 buf[4] = hlen; 216 (void) memcpy(buf + 5, host, hlen); 217 (void) memcpy(buf + 5 + hlen, &serverport, 218 sizeof (serverport)); 219 wlen = 5 + hlen + sizeof (serverport); 220 break; 221 case AF_INET: 222 /* Version 5, connect: IPv4 address */ 223 buf[0] = SOCKS_V5; 224 buf[1] = SOCKS_CONNECT; 225 buf[2] = 0; 226 buf[3] = SOCKS_IPV4; 227 (void) memcpy(buf + 4, &in4->sin_addr, 228 sizeof (in4->sin_addr)); 229 (void) memcpy(buf + 8, &in4->sin_port, 230 sizeof (in4->sin_port)); 231 wlen = 4 + sizeof (in4->sin_addr) + 232 sizeof (in4->sin_port); 233 break; 234 case AF_INET6: 235 /* Version 5, connect: IPv6 address */ 236 buf[0] = SOCKS_V5; 237 buf[1] = SOCKS_CONNECT; 238 buf[2] = 0; 239 buf[3] = SOCKS_IPV6; 240 (void) memcpy(buf + 4, &in6->sin6_addr, 241 sizeof (in6->sin6_addr)); 242 (void) memcpy(buf + 20, &in6->sin6_port, 243 sizeof (in6->sin6_port)); 244 wlen = 4 + sizeof (in6->sin6_addr) + 245 sizeof (in6->sin6_port); 246 break; 247 default: 248 errx(1, "internal error: silly AF"); 249 } 250 251 cnt = atomicio(vwrite, proxyfd, buf, wlen); 252 if (cnt != wlen) 253 err(1, "write failed (%d/%d)", cnt, wlen); 254 255 /* 256 * read proxy reply which is 4 byte "header", BND.ADDR 257 * and BND.PORT according to RFC 1928, section 6. BND.ADDR 258 * is 4 bytes in case of IPv4 which gives us 10 bytes in sum. 259 */ 260 cnt = atomicio(read, proxyfd, buf, 10); 261 if (cnt != 10) 262 err(1, "read failed (%d/10)", cnt); 263 if (buf[1] != 0) 264 errx(1, "connection failed, SOCKS error %d", buf[1]); 265 } else if (socksv == 4) { 266 /* This will exit on lookup failure */ 267 (void) decode_addrport(host, port, (struct sockaddr *)&addr, 268 sizeof (addr), 1, 0); 269 270 /* Version 4 */ 271 buf[0] = SOCKS_V4; 272 buf[1] = SOCKS_CONNECT; /* connect */ 273 (void) memcpy(buf + 2, &in4->sin_port, sizeof (in4->sin_port)); 274 (void) memcpy(buf + 4, &in4->sin_addr, sizeof (in4->sin_addr)); 275 buf[8] = 0; /* empty username */ 276 wlen = 9; 277 278 cnt = atomicio(vwrite, proxyfd, buf, wlen); 279 if (cnt != wlen) 280 err(1, "write failed (%d/%d)", cnt, wlen); 281 282 /* 283 * SOCKSv4 proxy replies consists of 2 byte "header", 284 * port number and numeric IPv4 address which gives 8 bytes. 285 */ 286 cnt = atomicio(read, proxyfd, buf, 8); 287 if (cnt != 8) 288 err(1, "read failed (%d/8)", cnt); 289 if (buf[1] != 90) 290 errx(1, "connection failed, SOCKS error %d", buf[1]); 291 } else if (socksv == -1) { 292 /* HTTP proxy CONNECT according to RFC 2817, section 5 */ 293 294 /* Disallow bad chars in hostname */ 295 if (strcspn(host, "\r\n\t []:") != strlen(host)) 296 errx(1, "Invalid hostname"); 297 298 /* Try to be sane about numeric IPv6 addresses */ 299 if (strchr(host, ':') != NULL) { 300 r = snprintf(buf, sizeof (buf), 301 "CONNECT [%s]:%d HTTP/1.0\r\n", 302 host, ntohs(serverport)); 303 } else { 304 r = snprintf(buf, sizeof (buf), 305 "CONNECT %s:%d HTTP/1.0\r\n", 306 host, ntohs(serverport)); 307 } 308 if (r == -1 || (size_t)r >= sizeof (buf)) 309 errx(1, "hostname too long"); 310 r = strlen(buf); 311 312 cnt = atomicio(vwrite, proxyfd, buf, r); 313 if (cnt != r) 314 err(1, "write failed (%d/%d)", cnt, r); 315 316 if (authretry > 1) { 317 char resp[1024]; 318 319 proxypass = getproxypass(proxyuser, proxyhost); 320 r = snprintf(buf, sizeof (buf), "%s:%s", 321 proxyuser, proxypass); 322 free((void *)proxypass); 323 if (r == -1 || (size_t)r >= sizeof (buf) || 324 b64_ntop((unsigned char *)buf, strlen(buf), resp, 325 sizeof (resp)) == -1) 326 errx(1, "Proxy username/password too long"); 327 r = snprintf(buf, sizeof (buf), "Proxy-Authorization: " 328 "Basic %s\r\n", resp); 329 if (r == -1 || (size_t)r >= sizeof (buf)) 330 errx(1, "Proxy auth response too long"); 331 r = strlen(buf); 332 if ((cnt = atomicio(vwrite, proxyfd, buf, r)) != r) 333 err(1, "write failed (%d/%d)", cnt, r); 334 } 335 336 /* Terminate headers */ 337 if ((r = atomicio(vwrite, proxyfd, "\r\n", 2)) != 2) 338 err(1, "write failed (2/%d)", r); 339 340 /* Read status reply */ 341 (void) proxy_read_line(proxyfd, buf, sizeof (buf)); 342 if (proxyuser != NULL && 343 strncmp(buf, HTTP_10_407, strlen(HTTP_10_407)) == 0) { 344 if (authretry > 1) { 345 (void) fprintf(stderr, "Proxy authentication " 346 "failed\n"); 347 } 348 (void) close(proxyfd); 349 goto again; 350 } else if (strncmp(buf, HTTP_10_200, 351 strlen(HTTP_10_200)) != 0 && strncmp(buf, HTTP_11_200, 352 strlen(HTTP_11_200)) != 0) 353 errx(1, "Proxy error: \"%s\"", buf); 354 355 /* Headers continue until we hit an empty line */ 356 for (r = 0; r < HTTP_MAXHDRS; r++) { 357 (void) proxy_read_line(proxyfd, buf, sizeof (buf)); 358 if (*buf == '\0') 359 break; 360 } 361 if (*buf != '\0') 362 errx(1, "Too many proxy headers received"); 363 } else 364 errx(1, "Unknown proxy protocol %d", socksv); 365 366 return (proxyfd); 367 } 368