1 /* $OpenBSD: socks.c,v 1.9 2004/10/17 03:13:55 djm Exp $ */ 2 3 /* 4 * Copyright (c) 1999 Niklas Hallqvist. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/socket.h> 29 #include <netinet/in.h> 30 #include <arpa/inet.h> 31 32 #include <err.h> 33 #include <errno.h> 34 #include <netdb.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 40 #define SOCKS_PORT "1080" 41 #define HTTP_PROXY_PORT "3128" 42 #define HTTP_MAXHDRS 64 43 #define SOCKS_V5 5 44 #define SOCKS_V4 4 45 #define SOCKS_NOAUTH 0 46 #define SOCKS_NOMETHOD 0xff 47 #define SOCKS_CONNECT 1 48 #define SOCKS_IPV4 1 49 50 51 int remote_connect(char *, char *, struct addrinfo); 52 int socks_connect(char *host, char *port, struct addrinfo hints, 53 char *proxyhost, char *proxyport, struct addrinfo proxyhints, 54 int socksv); 55 56 static in_addr_t 57 decode_addr(const char *s) 58 { 59 struct hostent *hp = gethostbyname (s); 60 struct in_addr retval; 61 62 if (hp) 63 return *(in_addr_t *)hp->h_addr_list[0]; 64 if (inet_aton (s, &retval)) 65 return retval.s_addr; 66 errx (1, "cannot decode address \"%s\"", s); 67 } 68 69 static in_port_t 70 decode_port(const char *s) 71 { 72 struct servent *sp; 73 in_port_t port; 74 char *p; 75 76 port = strtol (s, &p, 10); 77 if (s == p) { 78 sp = getservbyname (s, "tcp"); 79 if (sp) 80 return sp->s_port; 81 } 82 if (*s != '\0' && *p == '\0') 83 return htons (port); 84 errx (1, "cannot decode port \"%s\"", s); 85 } 86 87 static int 88 proxy_read_line(int fd, char *buf, int bufsz) 89 { 90 int r, off; 91 92 for(off = 0;;) { 93 if (off >= bufsz) 94 errx(1, "proxy read too long"); 95 if ((r = read(fd, buf + off, 1)) <= 0) { 96 if (r == -1 && errno == EINTR) 97 continue; 98 err(1, "proxy read"); 99 } 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 int 113 socks_connect(char *host, char *port, struct addrinfo hints, 114 char *proxyhost, char *proxyport, struct addrinfo proxyhints, 115 int socksv) 116 { 117 int proxyfd, r; 118 unsigned char buf[1024]; 119 ssize_t cnt; 120 in_addr_t serveraddr; 121 in_port_t serverport; 122 123 if (proxyport == NULL) 124 proxyport = (socksv == -1) ? HTTP_PROXY_PORT : SOCKS_PORT; 125 126 proxyfd = remote_connect(proxyhost, proxyport, proxyhints); 127 128 if (proxyfd < 0) 129 return -1; 130 131 serveraddr = decode_addr (host); 132 serverport = decode_port (port); 133 134 if (socksv == 5) { 135 /* Version 5, one method: no authentication */ 136 buf[0] = SOCKS_V5; 137 buf[1] = 1; 138 buf[2] = SOCKS_NOAUTH; 139 cnt = write (proxyfd, buf, 3); 140 if (cnt == -1) 141 err (1, "write failed"); 142 if (cnt != 3) 143 errx (1, "short write, %d (expected 3)", cnt); 144 145 read (proxyfd, buf, 2); 146 if (buf[1] == SOCKS_NOMETHOD) 147 errx (1, "authentication method negotiation failed"); 148 149 /* Version 5, connect: IPv4 address */ 150 buf[0] = SOCKS_V5; 151 buf[1] = SOCKS_CONNECT; 152 buf[2] = 0; 153 buf[3] = SOCKS_IPV4; 154 memcpy (buf + 4, &serveraddr, sizeof serveraddr); 155 memcpy (buf + 8, &serverport, sizeof serverport); 156 157 /* XXX Handle short writes better */ 158 cnt = write (proxyfd, buf, 10); 159 if (cnt == -1) 160 err (1, "write failed"); 161 if (cnt != 10) 162 errx (1, "short write, %d (expected 10)", cnt); 163 164 /* XXX Handle short reads better */ 165 cnt = read (proxyfd, buf, sizeof buf); 166 if (cnt == -1) 167 err (1, "read failed"); 168 if (cnt != 10) 169 errx (1, "unexpected reply size %d (expected 10)", cnt); 170 if (buf[1] != 0) 171 errx (1, "connection failed, SOCKS error %d", buf[1]); 172 } else if (socksv == 4) { 173 /* Version 4 */ 174 buf[0] = SOCKS_V4; 175 buf[1] = SOCKS_CONNECT; /* connect */ 176 memcpy (buf + 2, &serverport, sizeof serverport); 177 memcpy (buf + 4, &serveraddr, sizeof serveraddr); 178 buf[8] = 0; /* empty username */ 179 180 cnt = write (proxyfd, buf, 9); 181 if (cnt == -1) 182 err (1, "write failed"); 183 if (cnt != 9) 184 errx (1, "short write, %d (expected 9)", cnt); 185 186 /* XXX Handle short reads better */ 187 cnt = read (proxyfd, buf, 8); 188 if (cnt == -1) 189 err (1, "read failed"); 190 if (cnt != 8) 191 errx (1, "unexpected reply size %d (expected 8)", cnt); 192 if (buf[1] != 90) 193 errx (1, "connection failed, SOCKS error %d", buf[1]); 194 } else if (socksv == -1) { 195 /* HTTP proxy CONNECT */ 196 197 /* Disallow bad chars in hostname */ 198 if (strcspn(host, "\r\n\t []:") != strlen(host)) 199 errx (1, "Invalid hostname"); 200 201 /* Try to be sane about numeric IPv6 addresses */ 202 if (strchr(host, ':') != NULL) { 203 r = snprintf(buf, sizeof(buf), 204 "CONNECT [%s]:%d HTTP/1.0\r\n\r\n", 205 host, ntohs(serverport)); 206 } else { 207 r = snprintf(buf, sizeof(buf), 208 "CONNECT %s:%d HTTP/1.0\r\n\r\n", 209 host, ntohs(serverport)); 210 } 211 if (r == -1 || r >= sizeof(buf)) 212 errx (1, "hostname too long"); 213 r = strlen(buf); 214 215 /* XXX atomicio */ 216 cnt = write (proxyfd, buf, r); 217 if (cnt == -1) 218 err (1, "write failed"); 219 if (cnt != r) 220 errx (1, "short write, %d (expected %d)", cnt, r); 221 222 /* Read reply */ 223 for (r = 0; r < HTTP_MAXHDRS; r++) { 224 proxy_read_line(proxyfd, buf, sizeof(buf)); 225 if (r == 0 && strncmp(buf, "HTTP/1.0 200 ", 12) != 0) 226 errx (1, "Proxy error: \"%s\"", buf); 227 /* Discard headers until we hit an empty line */ 228 if (*buf == '\0') 229 break; 230 } 231 } else 232 errx (1, "Unknown proxy protocol %d", socksv); 233 234 return proxyfd; 235 } 236