1 /* $OpenBSD: socks.c,v 1.30 2019/11/04 17:33:28 millert 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 #include <sys/types.h> 29 #include <sys/socket.h> 30 #include <netinet/in.h> 31 #include <arpa/inet.h> 32 33 #include <err.h> 34 #include <errno.h> 35 #include <netdb.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 #include <resolv.h> 41 #include <readpassphrase.h> 42 #include "atomicio.h" 43 44 #define SOCKS_PORT "1080" 45 #define HTTP_PROXY_PORT "3128" 46 #define HTTP_MAXHDRS 64 47 #define SOCKS_V5 5 48 #define SOCKS_V4 4 49 #define SOCKS_NOAUTH 0 50 #define SOCKS_NOMETHOD 0xff 51 #define SOCKS_CONNECT 1 52 #define SOCKS_IPV4 1 53 #define SOCKS_DOMAIN 3 54 #define SOCKS_IPV6 4 55 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, 59 const char *); 60 61 static int 62 decode_addrport(const char *h, const char *p, struct sockaddr *addr, 63 socklen_t addrlen, int v4only, int numeric) 64 { 65 int r; 66 struct addrinfo hints, *res; 67 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 */ 74 if (r != 0) { 75 if (!numeric) { 76 errx(1, "getaddrinfo(\"%.64s\", \"%.64s\"): %s", h, p, 77 gai_strerror(r)); 78 } 79 return (-1); 80 } 81 if (addrlen < res->ai_addrlen) { 82 freeaddrinfo(res); 83 errx(1, "internal error: addrlen < res->ai_addrlen"); 84 } 85 memcpy(addr, res->ai_addr, res->ai_addrlen); 86 freeaddrinfo(res); 87 return (0); 88 } 89 90 static int 91 proxy_read_line(int fd, char *buf, size_t bufsz) 92 { 93 size_t off; 94 95 for(off = 0;;) { 96 if (off >= bufsz) 97 errx(1, "proxy read too long"); 98 if (atomicio(read, fd, buf + off, 1) != 1) 99 err(1, "proxy read"); 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 static void 113 getproxypass(const char *proxyuser, const char *proxyhost, 114 char *pw, size_t pwlen) 115 { 116 char prompt[512]; 117 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"); 122 } 123 124 /* 125 * Error strings adapted from the generally accepted SOCKSv4 spec: 126 * 127 * http://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4.protocol 128 */ 129 static const char * 130 socks4_strerror(int e) 131 { 132 switch (e) { 133 case 90: 134 return "Succeeded"; 135 case 91: 136 return "Request rejected or failed"; 137 case 92: 138 return "SOCKS server cannot connect to identd on the client"; 139 case 93: 140 return "Client program and identd report different user-ids"; 141 default: 142 return "Unknown error"; 143 } 144 } 145 146 /* 147 * Error strings taken almost directly from RFC 1928. 148 */ 149 static const char * 150 socks5_strerror(int e) 151 { 152 switch (e) { 153 case 0: 154 return "Succeeded"; 155 case 1: 156 return "General SOCKS server failure"; 157 case 2: 158 return "Connection not allowed by ruleset"; 159 case 3: 160 return "Network unreachable"; 161 case 4: 162 return "Host unreachable"; 163 case 5: 164 return "Connection refused"; 165 case 6: 166 return "TTL expired"; 167 case 7: 168 return "Command not supported"; 169 case 8: 170 return "Address type not supported"; 171 default: 172 return "Unknown error"; 173 } 174 } 175 176 int 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) 181 { 182 int proxyfd, r, authretry = 0; 183 size_t hlen, wlen; 184 unsigned char buf[1024]; 185 size_t cnt; 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; 190 191 if (proxyport == NULL) 192 proxyport = (socksv == -1) ? HTTP_PROXY_PORT : SOCKS_PORT; 193 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; 199 200 again: 201 if (authretry++ > 3) 202 errx(1, "Too many authentication failures"); 203 204 proxyfd = remote_connect(proxyhost, proxyport, proxyhints, NULL); 205 206 if (proxyfd < 0) 207 return (-1); 208 209 if (socksv == 5) { 210 if (decode_addrport(host, port, (struct sockaddr *)&addr, 211 sizeof(addr), 0, 1) == -1) 212 addr.ss_family = 0; /* used in switch below */ 213 214 /* Version 5, one method: no authentication */ 215 buf[0] = SOCKS_V5; 216 buf[1] = 1; 217 buf[2] = SOCKS_NOAUTH; 218 cnt = atomicio(vwrite, proxyfd, buf, 3); 219 if (cnt != 3) 220 err(1, "write failed (%zu/3)", cnt); 221 222 cnt = atomicio(read, proxyfd, buf, 2); 223 if (cnt != 2) 224 err(1, "read failed (%zu/3)", cnt); 225 226 if (buf[1] == SOCKS_NOMETHOD) 227 errx(1, "authentication method negotiation failed"); 228 229 switch (addr.ss_family) { 230 case 0: 231 /* Version 5, connect: domain name */ 232 233 /* Max domain name length is 255 bytes */ 234 hlen = strlen(host); 235 if (hlen > 255) 236 errx(1, "host name too long for SOCKS5"); 237 buf[0] = SOCKS_V5; 238 buf[1] = SOCKS_CONNECT; 239 buf[2] = 0; 240 buf[3] = SOCKS_DOMAIN; 241 buf[4] = hlen; 242 memcpy(buf + 5, host, hlen); 243 memcpy(buf + 5 + hlen, &serverport, sizeof serverport); 244 wlen = 7 + hlen; 245 break; 246 case AF_INET: 247 /* Version 5, connect: IPv4 address */ 248 buf[0] = SOCKS_V5; 249 buf[1] = SOCKS_CONNECT; 250 buf[2] = 0; 251 buf[3] = SOCKS_IPV4; 252 memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr); 253 memcpy(buf + 8, &in4->sin_port, sizeof in4->sin_port); 254 wlen = 10; 255 break; 256 case AF_INET6: 257 /* Version 5, connect: IPv6 address */ 258 buf[0] = SOCKS_V5; 259 buf[1] = SOCKS_CONNECT; 260 buf[2] = 0; 261 buf[3] = SOCKS_IPV6; 262 memcpy(buf + 4, &in6->sin6_addr, sizeof in6->sin6_addr); 263 memcpy(buf + 20, &in6->sin6_port, 264 sizeof in6->sin6_port); 265 wlen = 22; 266 break; 267 default: 268 errx(1, "internal error: silly AF"); 269 } 270 271 cnt = atomicio(vwrite, proxyfd, buf, wlen); 272 if (cnt != wlen) 273 err(1, "write failed (%zu/%zu)", cnt, wlen); 274 275 cnt = atomicio(read, proxyfd, buf, 4); 276 if (cnt != 4) 277 err(1, "read failed (%zu/4)", cnt); 278 if (buf[1] != 0) { 279 errx(1, "connection failed, SOCKSv5 error: %s", 280 socks5_strerror(buf[1])); 281 } 282 switch (buf[3]) { 283 case SOCKS_IPV4: 284 cnt = atomicio(read, proxyfd, buf + 4, 6); 285 if (cnt != 6) 286 err(1, "read failed (%zu/6)", cnt); 287 break; 288 case SOCKS_IPV6: 289 cnt = atomicio(read, proxyfd, buf + 4, 18); 290 if (cnt != 18) 291 err(1, "read failed (%zu/18)", cnt); 292 break; 293 default: 294 errx(1, "connection failed, unsupported address type"); 295 } 296 } else if (socksv == 4) { 297 /* This will exit on lookup failure */ 298 decode_addrport(host, port, (struct sockaddr *)&addr, 299 sizeof(addr), 1, 0); 300 301 /* Version 4 */ 302 buf[0] = SOCKS_V4; 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 */ 307 wlen = 9; 308 309 cnt = atomicio(vwrite, proxyfd, buf, wlen); 310 if (cnt != wlen) 311 err(1, "write failed (%zu/%zu)", cnt, wlen); 312 313 cnt = atomicio(read, proxyfd, buf, 8); 314 if (cnt != 8) 315 err(1, "read failed (%zu/8)", cnt); 316 if (buf[1] != 90) { 317 errx(1, "connection failed, SOCKSv4 error: %s", 318 socks4_strerror(buf[1])); 319 } 320 } else if (socksv == -1) { 321 /* HTTP proxy CONNECT */ 322 323 /* Disallow bad chars in hostname */ 324 if (strcspn(host, "\r\n\t []:") != strlen(host)) 325 errx(1, "Invalid hostname"); 326 327 /* Try to be sane about numeric IPv6 addresses */ 328 if (strchr(host, ':') != NULL) { 329 r = snprintf(buf, sizeof(buf), 330 "CONNECT [%s]:%d HTTP/1.0\r\n", 331 host, ntohs(serverport)); 332 } else { 333 r = snprintf(buf, sizeof(buf), 334 "CONNECT %s:%d HTTP/1.0\r\n", 335 host, ntohs(serverport)); 336 } 337 if (r < 0 || (size_t)r >= sizeof(buf)) 338 errx(1, "hostname too long"); 339 r = strlen(buf); 340 341 cnt = atomicio(vwrite, proxyfd, buf, r); 342 if (cnt != r) 343 err(1, "write failed (%zu/%d)", cnt, r); 344 345 if (authretry > 1) { 346 char proxypass[256]; 347 char resp[1024]; 348 349 getproxypass(proxyuser, proxyhost, 350 proxypass, sizeof proxypass); 351 r = snprintf(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(buf), resp, 356 sizeof(resp)) == -1) 357 errx(1, "Proxy username/password too long"); 358 r = snprintf(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(buf); 363 if ((cnt = atomicio(vwrite, proxyfd, buf, r)) != r) 364 err(1, "write failed (%zu/%d)", cnt, r); 365 explicit_bzero(proxypass, sizeof proxypass); 366 explicit_bzero(buf, sizeof buf); 367 } 368 369 /* Terminate headers */ 370 if ((cnt = atomicio(vwrite, proxyfd, "\r\n", 2)) != 2) 371 err(1, "write failed (%zu/2)", cnt); 372 373 /* Read status reply */ 374 proxy_read_line(proxyfd, buf, sizeof(buf)); 375 if (proxyuser != NULL && 376 (strncmp(buf, "HTTP/1.0 407 ", 12) == 0 || 377 strncmp(buf, "HTTP/1.1 407 ", 12) == 0)) { 378 if (authretry > 1) { 379 fprintf(stderr, "Proxy authentication " 380 "failed\n"); 381 } 382 close(proxyfd); 383 goto again; 384 } else if (strncmp(buf, "HTTP/1.0 200 ", 12) != 0 && 385 strncmp(buf, "HTTP/1.1 200 ", 12) != 0) 386 errx(1, "Proxy error: \"%s\"", buf); 387 388 /* Headers continue until we hit an empty line */ 389 for (r = 0; r < HTTP_MAXHDRS; r++) { 390 proxy_read_line(proxyfd, buf, sizeof(buf)); 391 if (*buf == '\0') 392 break; 393 } 394 if (*buf != '\0') 395 errx(1, "Too many proxy headers received"); 396 } else 397 errx(1, "Unknown proxy protocol %d", socksv); 398 399 return (proxyfd); 400 } 401