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