1241906Sdelphij/* $OpenBSD: socks.c,v 1.20 2012/03/08 09:56:28 espie Exp $ */ 2141261Sdelphij 3141261Sdelphij/* 4141261Sdelphij * Copyright (c) 1999 Niklas Hallqvist. All rights reserved. 5158795Sdelphij * Copyright (c) 2004, 2005 Damien Miller. All rights reserved. 6141261Sdelphij * 7141261Sdelphij * Redistribution and use in source and binary forms, with or without 8141261Sdelphij * modification, are permitted provided that the following conditions 9141261Sdelphij * are met: 10141261Sdelphij * 1. Redistributions of source code must retain the above copyright 11141261Sdelphij * notice, this list of conditions and the following disclaimer. 12141261Sdelphij * 2. Redistributions in binary form must reproduce the above copyright 13141261Sdelphij * notice, this list of conditions and the following disclaimer in the 14141261Sdelphij * documentation and/or other materials provided with the distribution. 15141261Sdelphij * 16141261Sdelphij * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17141261Sdelphij * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18141261Sdelphij * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19141261Sdelphij * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20141261Sdelphij * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21141261Sdelphij * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22141261Sdelphij * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23141261Sdelphij * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24141261Sdelphij * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25141261Sdelphij * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26141261Sdelphij */ 27141261Sdelphij 28141261Sdelphij#include <sys/types.h> 29141261Sdelphij#include <sys/socket.h> 30141261Sdelphij#include <netinet/in.h> 31141261Sdelphij#include <arpa/inet.h> 32141261Sdelphij 33141261Sdelphij#include <err.h> 34141261Sdelphij#include <errno.h> 35141261Sdelphij#include <netdb.h> 36141261Sdelphij#include <stdio.h> 37141261Sdelphij#include <stdlib.h> 38141261Sdelphij#include <string.h> 39141261Sdelphij#include <unistd.h> 40158795Sdelphij#include <resolv.h> 41158795Sdelphij#include <readpassphrase.h> 42158795Sdelphij#include "atomicio.h" 43141261Sdelphij 44141261Sdelphij#define SOCKS_PORT "1080" 45141261Sdelphij#define HTTP_PROXY_PORT "3128" 46141261Sdelphij#define HTTP_MAXHDRS 64 47141261Sdelphij#define SOCKS_V5 5 48141261Sdelphij#define SOCKS_V4 4 49141261Sdelphij#define SOCKS_NOAUTH 0 50141261Sdelphij#define SOCKS_NOMETHOD 0xff 51141261Sdelphij#define SOCKS_CONNECT 1 52141261Sdelphij#define SOCKS_IPV4 1 53158795Sdelphij#define SOCKS_DOMAIN 3 54158795Sdelphij#define SOCKS_IPV6 4 55141261Sdelphij 56158795Sdelphijint remote_connect(const char *, const char *, struct addrinfo); 57158795Sdelphijint socks_connect(const char *, const char *, struct addrinfo, 58158795Sdelphij const char *, const char *, struct addrinfo, int, 59158795Sdelphij const char *); 60141261Sdelphij 61158795Sdelphijstatic int 62158795Sdelphijdecode_addrport(const char *h, const char *p, struct sockaddr *addr, 63158795Sdelphij socklen_t addrlen, int v4only, int numeric) 64141261Sdelphij{ 65158795Sdelphij int r; 66158795Sdelphij struct addrinfo hints, *res; 67141261Sdelphij 68158795Sdelphij bzero(&hints, sizeof(hints)); 69158795Sdelphij hints.ai_family = v4only ? PF_INET : PF_UNSPEC; 70158795Sdelphij hints.ai_flags = numeric ? AI_NUMERICHOST : 0; 71158795Sdelphij hints.ai_socktype = SOCK_STREAM; 72158795Sdelphij r = getaddrinfo(h, p, &hints, &res); 73158795Sdelphij /* Don't fatal when attempting to convert a numeric address */ 74158795Sdelphij if (r != 0) { 75158795Sdelphij if (!numeric) { 76158795Sdelphij errx(1, "getaddrinfo(\"%.64s\", \"%.64s\"): %s", h, p, 77158795Sdelphij gai_strerror(r)); 78158795Sdelphij } 79158795Sdelphij return (-1); 80141261Sdelphij } 81158795Sdelphij if (addrlen < res->ai_addrlen) { 82158795Sdelphij freeaddrinfo(res); 83158795Sdelphij errx(1, "internal error: addrlen < res->ai_addrlen"); 84158795Sdelphij } 85158795Sdelphij memcpy(addr, res->ai_addr, res->ai_addrlen); 86158795Sdelphij freeaddrinfo(res); 87158795Sdelphij return (0); 88141261Sdelphij} 89141261Sdelphij 90141261Sdelphijstatic int 91158795Sdelphijproxy_read_line(int fd, char *buf, size_t bufsz) 92141261Sdelphij{ 93158795Sdelphij size_t off; 94141261Sdelphij 95141261Sdelphij for(off = 0;;) { 96141261Sdelphij if (off >= bufsz) 97141261Sdelphij errx(1, "proxy read too long"); 98158795Sdelphij if (atomicio(read, fd, buf + off, 1) != 1) 99141261Sdelphij err(1, "proxy read"); 100141261Sdelphij /* Skip CR */ 101141261Sdelphij if (buf[off] == '\r') 102141261Sdelphij continue; 103141261Sdelphij if (buf[off] == '\n') { 104141261Sdelphij buf[off] = '\0'; 105141261Sdelphij break; 106141261Sdelphij } 107141261Sdelphij off++; 108141261Sdelphij } 109141261Sdelphij return (off); 110141261Sdelphij} 111141261Sdelphij 112158795Sdelphijstatic const char * 113158795Sdelphijgetproxypass(const char *proxyuser, const char *proxyhost) 114158795Sdelphij{ 115158795Sdelphij char prompt[512]; 116158795Sdelphij static char pw[256]; 117158795Sdelphij 118158795Sdelphij snprintf(prompt, sizeof(prompt), "Proxy password for %s@%s: ", 119158795Sdelphij proxyuser, proxyhost); 120158795Sdelphij if (readpassphrase(prompt, pw, sizeof(pw), RPP_REQUIRE_TTY) == NULL) 121158795Sdelphij errx(1, "Unable to read proxy passphrase"); 122158795Sdelphij return (pw); 123158795Sdelphij} 124158795Sdelphij 125141261Sdelphijint 126158795Sdelphijsocks_connect(const char *host, const char *port, 127158795Sdelphij struct addrinfo hints __attribute__ ((__unused__)), 128158795Sdelphij const char *proxyhost, const char *proxyport, struct addrinfo proxyhints, 129158795Sdelphij int socksv, const char *proxyuser) 130141261Sdelphij{ 131158795Sdelphij int proxyfd, r, authretry = 0; 132158795Sdelphij size_t hlen, wlen; 133141261Sdelphij unsigned char buf[1024]; 134158795Sdelphij size_t cnt; 135158795Sdelphij struct sockaddr_storage addr; 136158795Sdelphij struct sockaddr_in *in4 = (struct sockaddr_in *)&addr; 137158795Sdelphij struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&addr; 138141261Sdelphij in_port_t serverport; 139158795Sdelphij const char *proxypass = NULL; 140141261Sdelphij 141141261Sdelphij if (proxyport == NULL) 142141261Sdelphij proxyport = (socksv == -1) ? HTTP_PROXY_PORT : SOCKS_PORT; 143141261Sdelphij 144158795Sdelphij /* Abuse API to lookup port */ 145158795Sdelphij if (decode_addrport("0.0.0.0", port, (struct sockaddr *)&addr, 146158795Sdelphij sizeof(addr), 1, 1) == -1) 147158795Sdelphij errx(1, "unknown port \"%.64s\"", port); 148158795Sdelphij serverport = in4->sin_port; 149158795Sdelphij 150158795Sdelphij again: 151158795Sdelphij if (authretry++ > 3) 152158795Sdelphij errx(1, "Too many authentication failures"); 153158795Sdelphij 154141261Sdelphij proxyfd = remote_connect(proxyhost, proxyport, proxyhints); 155141261Sdelphij 156141261Sdelphij if (proxyfd < 0) 157158795Sdelphij return (-1); 158141261Sdelphij 159158795Sdelphij if (socksv == 5) { 160158795Sdelphij if (decode_addrport(host, port, (struct sockaddr *)&addr, 161158795Sdelphij sizeof(addr), 0, 1) == -1) 162158795Sdelphij addr.ss_family = 0; /* used in switch below */ 163141261Sdelphij 164141261Sdelphij /* Version 5, one method: no authentication */ 165141261Sdelphij buf[0] = SOCKS_V5; 166141261Sdelphij buf[1] = 1; 167141261Sdelphij buf[2] = SOCKS_NOAUTH; 168158795Sdelphij cnt = atomicio(vwrite, proxyfd, buf, 3); 169141261Sdelphij if (cnt != 3) 170214047Sdelphij err(1, "write failed (%zu/3)", cnt); 171141261Sdelphij 172158795Sdelphij cnt = atomicio(read, proxyfd, buf, 2); 173158795Sdelphij if (cnt != 2) 174214047Sdelphij err(1, "read failed (%zu/3)", cnt); 175158795Sdelphij 176141261Sdelphij if (buf[1] == SOCKS_NOMETHOD) 177158795Sdelphij errx(1, "authentication method negotiation failed"); 178141261Sdelphij 179158795Sdelphij switch (addr.ss_family) { 180158795Sdelphij case 0: 181158795Sdelphij /* Version 5, connect: domain name */ 182141261Sdelphij 183158795Sdelphij /* Max domain name length is 255 bytes */ 184158795Sdelphij hlen = strlen(host); 185158795Sdelphij if (hlen > 255) 186158795Sdelphij errx(1, "host name too long for SOCKS5"); 187158795Sdelphij buf[0] = SOCKS_V5; 188158795Sdelphij buf[1] = SOCKS_CONNECT; 189158795Sdelphij buf[2] = 0; 190158795Sdelphij buf[3] = SOCKS_DOMAIN; 191158795Sdelphij buf[4] = hlen; 192158795Sdelphij memcpy(buf + 5, host, hlen); 193158795Sdelphij memcpy(buf + 5 + hlen, &serverport, sizeof serverport); 194158795Sdelphij wlen = 7 + hlen; 195158795Sdelphij break; 196158795Sdelphij case AF_INET: 197158795Sdelphij /* Version 5, connect: IPv4 address */ 198158795Sdelphij buf[0] = SOCKS_V5; 199158795Sdelphij buf[1] = SOCKS_CONNECT; 200158795Sdelphij buf[2] = 0; 201158795Sdelphij buf[3] = SOCKS_IPV4; 202158795Sdelphij memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr); 203158795Sdelphij memcpy(buf + 8, &in4->sin_port, sizeof in4->sin_port); 204158795Sdelphij wlen = 10; 205158795Sdelphij break; 206158795Sdelphij case AF_INET6: 207158795Sdelphij /* Version 5, connect: IPv6 address */ 208158795Sdelphij buf[0] = SOCKS_V5; 209158795Sdelphij buf[1] = SOCKS_CONNECT; 210158795Sdelphij buf[2] = 0; 211158795Sdelphij buf[3] = SOCKS_IPV6; 212158795Sdelphij memcpy(buf + 4, &in6->sin6_addr, sizeof in6->sin6_addr); 213158795Sdelphij memcpy(buf + 20, &in6->sin6_port, 214158795Sdelphij sizeof in6->sin6_port); 215158795Sdelphij wlen = 22; 216158795Sdelphij break; 217158795Sdelphij default: 218158795Sdelphij errx(1, "internal error: silly AF"); 219158795Sdelphij } 220141261Sdelphij 221158795Sdelphij cnt = atomicio(vwrite, proxyfd, buf, wlen); 222158795Sdelphij if (cnt != wlen) 223214047Sdelphij err(1, "write failed (%zu/%zu)", cnt, wlen); 224158795Sdelphij 225221793Sdelphij cnt = atomicio(read, proxyfd, buf, 4); 226221793Sdelphij if (cnt != 4) 227221793Sdelphij err(1, "read failed (%zu/4)", cnt); 228141261Sdelphij if (buf[1] != 0) 229158795Sdelphij errx(1, "connection failed, SOCKS error %d", buf[1]); 230221793Sdelphij switch (buf[3]) { 231221793Sdelphij case SOCKS_IPV4: 232221793Sdelphij cnt = atomicio(read, proxyfd, buf + 4, 6); 233221793Sdelphij if (cnt != 6) 234241906Sdelphij err(1, "read failed (%zu/6)", cnt); 235221793Sdelphij break; 236221793Sdelphij case SOCKS_IPV6: 237221793Sdelphij cnt = atomicio(read, proxyfd, buf + 4, 18); 238221793Sdelphij if (cnt != 18) 239241906Sdelphij err(1, "read failed (%zu/18)", cnt); 240221793Sdelphij break; 241221793Sdelphij default: 242221793Sdelphij errx(1, "connection failed, unsupported address type"); 243221793Sdelphij } 244141261Sdelphij } else if (socksv == 4) { 245158795Sdelphij /* This will exit on lookup failure */ 246158795Sdelphij decode_addrport(host, port, (struct sockaddr *)&addr, 247158795Sdelphij sizeof(addr), 1, 0); 248158795Sdelphij 249141261Sdelphij /* Version 4 */ 250141261Sdelphij buf[0] = SOCKS_V4; 251141261Sdelphij buf[1] = SOCKS_CONNECT; /* connect */ 252158795Sdelphij memcpy(buf + 2, &in4->sin_port, sizeof in4->sin_port); 253158795Sdelphij memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr); 254141261Sdelphij buf[8] = 0; /* empty username */ 255158795Sdelphij wlen = 9; 256141261Sdelphij 257158795Sdelphij cnt = atomicio(vwrite, proxyfd, buf, wlen); 258158795Sdelphij if (cnt != wlen) 259214047Sdelphij err(1, "write failed (%zu/%zu)", cnt, wlen); 260141261Sdelphij 261158795Sdelphij cnt = atomicio(read, proxyfd, buf, 8); 262141261Sdelphij if (cnt != 8) 263214047Sdelphij err(1, "read failed (%zu/8)", cnt); 264141261Sdelphij if (buf[1] != 90) 265158795Sdelphij errx(1, "connection failed, SOCKS error %d", buf[1]); 266141261Sdelphij } else if (socksv == -1) { 267141261Sdelphij /* HTTP proxy CONNECT */ 268141261Sdelphij 269141261Sdelphij /* Disallow bad chars in hostname */ 270141261Sdelphij if (strcspn(host, "\r\n\t []:") != strlen(host)) 271158795Sdelphij errx(1, "Invalid hostname"); 272141261Sdelphij 273141261Sdelphij /* Try to be sane about numeric IPv6 addresses */ 274141261Sdelphij if (strchr(host, ':') != NULL) { 275141261Sdelphij r = snprintf(buf, sizeof(buf), 276158795Sdelphij "CONNECT [%s]:%d HTTP/1.0\r\n", 277141261Sdelphij host, ntohs(serverport)); 278141261Sdelphij } else { 279141261Sdelphij r = snprintf(buf, sizeof(buf), 280158795Sdelphij "CONNECT %s:%d HTTP/1.0\r\n", 281141261Sdelphij host, ntohs(serverport)); 282141261Sdelphij } 283158795Sdelphij if (r == -1 || (size_t)r >= sizeof(buf)) 284158795Sdelphij errx(1, "hostname too long"); 285141261Sdelphij r = strlen(buf); 286141261Sdelphij 287158795Sdelphij cnt = atomicio(vwrite, proxyfd, buf, r); 288141261Sdelphij if (cnt != r) 289214047Sdelphij err(1, "write failed (%zu/%d)", cnt, r); 290141261Sdelphij 291158795Sdelphij if (authretry > 1) { 292158795Sdelphij char resp[1024]; 293158795Sdelphij 294158795Sdelphij proxypass = getproxypass(proxyuser, proxyhost); 295158795Sdelphij r = snprintf(buf, sizeof(buf), "%s:%s", 296158795Sdelphij proxyuser, proxypass); 297158795Sdelphij if (r == -1 || (size_t)r >= sizeof(buf) || 298158795Sdelphij b64_ntop(buf, strlen(buf), resp, 299158795Sdelphij sizeof(resp)) == -1) 300158795Sdelphij errx(1, "Proxy username/password too long"); 301158795Sdelphij r = snprintf(buf, sizeof(buf), "Proxy-Authorization: " 302158795Sdelphij "Basic %s\r\n", resp); 303158795Sdelphij if (r == -1 || (size_t)r >= sizeof(buf)) 304158795Sdelphij errx(1, "Proxy auth response too long"); 305158795Sdelphij r = strlen(buf); 306158795Sdelphij if ((cnt = atomicio(vwrite, proxyfd, buf, r)) != r) 307214047Sdelphij err(1, "write failed (%zu/%d)", cnt, r); 308158795Sdelphij } 309158795Sdelphij 310158795Sdelphij /* Terminate headers */ 311158795Sdelphij if ((r = atomicio(vwrite, proxyfd, "\r\n", 2)) != 2) 312158795Sdelphij err(1, "write failed (2/%d)", r); 313158795Sdelphij 314158795Sdelphij /* Read status reply */ 315158795Sdelphij proxy_read_line(proxyfd, buf, sizeof(buf)); 316158795Sdelphij if (proxyuser != NULL && 317158795Sdelphij strncmp(buf, "HTTP/1.0 407 ", 12) == 0) { 318158795Sdelphij if (authretry > 1) { 319158795Sdelphij fprintf(stderr, "Proxy authentication " 320158795Sdelphij "failed\n"); 321158795Sdelphij } 322158795Sdelphij close(proxyfd); 323158795Sdelphij goto again; 324167961Sdelphij } else if (strncmp(buf, "HTTP/1.0 200 ", 12) != 0 && 325167961Sdelphij strncmp(buf, "HTTP/1.1 200 ", 12) != 0) 326158795Sdelphij errx(1, "Proxy error: \"%s\"", buf); 327158795Sdelphij 328158795Sdelphij /* Headers continue until we hit an empty line */ 329141261Sdelphij for (r = 0; r < HTTP_MAXHDRS; r++) { 330141261Sdelphij proxy_read_line(proxyfd, buf, sizeof(buf)); 331141261Sdelphij if (*buf == '\0') 332141261Sdelphij break; 333141261Sdelphij } 334158795Sdelphij if (*buf != '\0') 335158795Sdelphij errx(1, "Too many proxy headers received"); 336141261Sdelphij } else 337158795Sdelphij errx(1, "Unknown proxy protocol %d", socksv); 338141261Sdelphij 339158795Sdelphij return (proxyfd); 340141261Sdelphij} 341