diff options
Diffstat (limited to '')
| -rw-r--r-- | src/usr.bin/nc/socks.c | 340 |
1 files changed, 0 insertions, 340 deletions
diff --git a/src/usr.bin/nc/socks.c b/src/usr.bin/nc/socks.c deleted file mode 100644 index f8adda463a..0000000000 --- a/src/usr.bin/nc/socks.c +++ /dev/null | |||
| @@ -1,340 +0,0 @@ | |||
| 1 | /* $OpenBSD: socks.c,v 1.20 2012/03/08 09:56:28 espie 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); | ||
| 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 | bzero(&hints, 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 const char * | ||
| 113 | getproxypass(const char *proxyuser, const char *proxyhost) | ||
| 114 | { | ||
| 115 | char prompt[512]; | ||
| 116 | static char pw[256]; | ||
| 117 | |||
| 118 | snprintf(prompt, sizeof(prompt), "Proxy password for %s@%s: ", | ||
| 119 | proxyuser, proxyhost); | ||
| 120 | if (readpassphrase(prompt, pw, sizeof(pw), RPP_REQUIRE_TTY) == NULL) | ||
| 121 | errx(1, "Unable to read proxy passphrase"); | ||
| 122 | return (pw); | ||
| 123 | } | ||
| 124 | |||
| 125 | int | ||
| 126 | socks_connect(const char *host, const char *port, | ||
| 127 | struct addrinfo hints __attribute__ ((__unused__)), | ||
| 128 | const char *proxyhost, const char *proxyport, struct addrinfo proxyhints, | ||
| 129 | int socksv, const char *proxyuser) | ||
| 130 | { | ||
| 131 | int proxyfd, r, authretry = 0; | ||
| 132 | size_t hlen, wlen; | ||
| 133 | unsigned char buf[1024]; | ||
| 134 | size_t cnt; | ||
| 135 | struct sockaddr_storage addr; | ||
| 136 | struct sockaddr_in *in4 = (struct sockaddr_in *)&addr; | ||
| 137 | struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&addr; | ||
| 138 | in_port_t serverport; | ||
| 139 | const char *proxypass = NULL; | ||
| 140 | |||
| 141 | if (proxyport == NULL) | ||
| 142 | proxyport = (socksv == -1) ? HTTP_PROXY_PORT : SOCKS_PORT; | ||
| 143 | |||
| 144 | /* Abuse API to lookup port */ | ||
| 145 | if (decode_addrport("0.0.0.0", port, (struct sockaddr *)&addr, | ||
| 146 | sizeof(addr), 1, 1) == -1) | ||
| 147 | errx(1, "unknown port \"%.64s\"", port); | ||
| 148 | serverport = in4->sin_port; | ||
| 149 | |||
| 150 | again: | ||
| 151 | if (authretry++ > 3) | ||
| 152 | errx(1, "Too many authentication failures"); | ||
| 153 | |||
| 154 | proxyfd = remote_connect(proxyhost, proxyport, proxyhints); | ||
| 155 | |||
| 156 | if (proxyfd < 0) | ||
| 157 | return (-1); | ||
| 158 | |||
| 159 | if (socksv == 5) { | ||
| 160 | if (decode_addrport(host, port, (struct sockaddr *)&addr, | ||
| 161 | sizeof(addr), 0, 1) == -1) | ||
| 162 | addr.ss_family = 0; /* used in switch below */ | ||
| 163 | |||
| 164 | /* Version 5, one method: no authentication */ | ||
| 165 | buf[0] = SOCKS_V5; | ||
| 166 | buf[1] = 1; | ||
| 167 | buf[2] = SOCKS_NOAUTH; | ||
| 168 | cnt = atomicio(vwrite, proxyfd, buf, 3); | ||
| 169 | if (cnt != 3) | ||
| 170 | err(1, "write failed (%zu/3)", cnt); | ||
| 171 | |||
| 172 | cnt = atomicio(read, proxyfd, buf, 2); | ||
| 173 | if (cnt != 2) | ||
| 174 | err(1, "read failed (%zu/3)", cnt); | ||
| 175 | |||
| 176 | if (buf[1] == SOCKS_NOMETHOD) | ||
| 177 | errx(1, "authentication method negotiation failed"); | ||
| 178 | |||
| 179 | switch (addr.ss_family) { | ||
| 180 | case 0: | ||
| 181 | /* Version 5, connect: domain name */ | ||
| 182 | |||
| 183 | /* Max domain name length is 255 bytes */ | ||
| 184 | hlen = strlen(host); | ||
| 185 | if (hlen > 255) | ||
| 186 | errx(1, "host name too long for SOCKS5"); | ||
| 187 | buf[0] = SOCKS_V5; | ||
| 188 | buf[1] = SOCKS_CONNECT; | ||
| 189 | buf[2] = 0; | ||
| 190 | buf[3] = SOCKS_DOMAIN; | ||
| 191 | buf[4] = hlen; | ||
| 192 | memcpy(buf + 5, host, hlen); | ||
| 193 | memcpy(buf + 5 + hlen, &serverport, sizeof serverport); | ||
| 194 | wlen = 7 + hlen; | ||
| 195 | break; | ||
| 196 | case AF_INET: | ||
| 197 | /* Version 5, connect: IPv4 address */ | ||
| 198 | buf[0] = SOCKS_V5; | ||
| 199 | buf[1] = SOCKS_CONNECT; | ||
| 200 | buf[2] = 0; | ||
| 201 | buf[3] = SOCKS_IPV4; | ||
| 202 | memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr); | ||
| 203 | memcpy(buf + 8, &in4->sin_port, sizeof in4->sin_port); | ||
| 204 | wlen = 10; | ||
| 205 | break; | ||
| 206 | case AF_INET6: | ||
| 207 | /* Version 5, connect: IPv6 address */ | ||
| 208 | buf[0] = SOCKS_V5; | ||
| 209 | buf[1] = SOCKS_CONNECT; | ||
| 210 | buf[2] = 0; | ||
| 211 | buf[3] = SOCKS_IPV6; | ||
| 212 | memcpy(buf + 4, &in6->sin6_addr, sizeof in6->sin6_addr); | ||
| 213 | memcpy(buf + 20, &in6->sin6_port, | ||
| 214 | sizeof in6->sin6_port); | ||
| 215 | wlen = 22; | ||
| 216 | break; | ||
| 217 | default: | ||
| 218 | errx(1, "internal error: silly AF"); | ||
| 219 | } | ||
| 220 | |||
| 221 | cnt = atomicio(vwrite, proxyfd, buf, wlen); | ||
| 222 | if (cnt != wlen) | ||
| 223 | err(1, "write failed (%zu/%zu)", cnt, wlen); | ||
| 224 | |||
| 225 | cnt = atomicio(read, proxyfd, buf, 4); | ||
| 226 | if (cnt != 4) | ||
| 227 | err(1, "read failed (%zu/4)", cnt); | ||
| 228 | if (buf[1] != 0) | ||
| 229 | errx(1, "connection failed, SOCKS error %d", buf[1]); | ||
| 230 | switch (buf[3]) { | ||
| 231 | case SOCKS_IPV4: | ||
| 232 | cnt = atomicio(read, proxyfd, buf + 4, 6); | ||
| 233 | if (cnt != 6) | ||
| 234 | err(1, "read failed (%zu/6)", cnt); | ||
| 235 | break; | ||
| 236 | case SOCKS_IPV6: | ||
| 237 | cnt = atomicio(read, proxyfd, buf + 4, 18); | ||
| 238 | if (cnt != 18) | ||
| 239 | err(1, "read failed (%zu/18)", cnt); | ||
| 240 | break; | ||
| 241 | default: | ||
| 242 | errx(1, "connection failed, unsupported address type"); | ||
| 243 | } | ||
| 244 | } else if (socksv == 4) { | ||
| 245 | /* This will exit on lookup failure */ | ||
| 246 | decode_addrport(host, port, (struct sockaddr *)&addr, | ||
| 247 | sizeof(addr), 1, 0); | ||
| 248 | |||
| 249 | /* Version 4 */ | ||
| 250 | buf[0] = SOCKS_V4; | ||
| 251 | buf[1] = SOCKS_CONNECT; /* connect */ | ||
| 252 | memcpy(buf + 2, &in4->sin_port, sizeof in4->sin_port); | ||
| 253 | memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr); | ||
| 254 | buf[8] = 0; /* empty username */ | ||
| 255 | wlen = 9; | ||
| 256 | |||
| 257 | cnt = atomicio(vwrite, proxyfd, buf, wlen); | ||
| 258 | if (cnt != wlen) | ||
| 259 | err(1, "write failed (%zu/%zu)", cnt, wlen); | ||
| 260 | |||
| 261 | cnt = atomicio(read, proxyfd, buf, 8); | ||
| 262 | if (cnt != 8) | ||
| 263 | err(1, "read failed (%zu/8)", cnt); | ||
| 264 | if (buf[1] != 90) | ||
| 265 | errx(1, "connection failed, SOCKS error %d", buf[1]); | ||
| 266 | } else if (socksv == -1) { | ||
| 267 | /* HTTP proxy CONNECT */ | ||
| 268 | |||
| 269 | /* Disallow bad chars in hostname */ | ||
| 270 | if (strcspn(host, "\r\n\t []:") != strlen(host)) | ||
| 271 | errx(1, "Invalid hostname"); | ||
| 272 | |||
| 273 | /* Try to be sane about numeric IPv6 addresses */ | ||
| 274 | if (strchr(host, ':') != NULL) { | ||
| 275 | r = snprintf(buf, sizeof(buf), | ||
| 276 | "CONNECT [%s]:%d HTTP/1.0\r\n", | ||
| 277 | host, ntohs(serverport)); | ||
| 278 | } else { | ||
| 279 | r = snprintf(buf, sizeof(buf), | ||
| 280 | "CONNECT %s:%d HTTP/1.0\r\n", | ||
| 281 | host, ntohs(serverport)); | ||
| 282 | } | ||
| 283 | if (r == -1 || (size_t)r >= sizeof(buf)) | ||
| 284 | errx(1, "hostname too long"); | ||
| 285 | r = strlen(buf); | ||
| 286 | |||
| 287 | cnt = atomicio(vwrite, proxyfd, buf, r); | ||
| 288 | if (cnt != r) | ||
| 289 | err(1, "write failed (%zu/%d)", cnt, r); | ||
| 290 | |||
| 291 | if (authretry > 1) { | ||
| 292 | char resp[1024]; | ||
| 293 | |||
| 294 | proxypass = getproxypass(proxyuser, proxyhost); | ||
| 295 | r = snprintf(buf, sizeof(buf), "%s:%s", | ||
| 296 | proxyuser, proxypass); | ||
| 297 | if (r == -1 || (size_t)r >= sizeof(buf) || | ||
| 298 | b64_ntop(buf, strlen(buf), resp, | ||
| 299 | sizeof(resp)) == -1) | ||
| 300 | errx(1, "Proxy username/password too long"); | ||
| 301 | r = snprintf(buf, sizeof(buf), "Proxy-Authorization: " | ||
| 302 | "Basic %s\r\n", resp); | ||
| 303 | if (r == -1 || (size_t)r >= sizeof(buf)) | ||
| 304 | errx(1, "Proxy auth response too long"); | ||
| 305 | r = strlen(buf); | ||
| 306 | if ((cnt = atomicio(vwrite, proxyfd, buf, r)) != r) | ||
| 307 | err(1, "write failed (%zu/%d)", cnt, r); | ||
| 308 | } | ||
| 309 | |||
| 310 | /* Terminate headers */ | ||
| 311 | if ((r = atomicio(vwrite, proxyfd, "\r\n", 2)) != 2) | ||
| 312 | err(1, "write failed (2/%d)", r); | ||
| 313 | |||
| 314 | /* Read status reply */ | ||
| 315 | proxy_read_line(proxyfd, buf, sizeof(buf)); | ||
| 316 | if (proxyuser != NULL && | ||
| 317 | strncmp(buf, "HTTP/1.0 407 ", 12) == 0) { | ||
| 318 | if (authretry > 1) { | ||
| 319 | fprintf(stderr, "Proxy authentication " | ||
| 320 | "failed\n"); | ||
| 321 | } | ||
| 322 | close(proxyfd); | ||
| 323 | goto again; | ||
| 324 | } else if (strncmp(buf, "HTTP/1.0 200 ", 12) != 0 && | ||
| 325 | strncmp(buf, "HTTP/1.1 200 ", 12) != 0) | ||
| 326 | errx(1, "Proxy error: \"%s\"", buf); | ||
| 327 | |||
| 328 | /* Headers continue until we hit an empty line */ | ||
| 329 | for (r = 0; r < HTTP_MAXHDRS; r++) { | ||
| 330 | proxy_read_line(proxyfd, buf, sizeof(buf)); | ||
| 331 | if (*buf == '\0') | ||
| 332 | break; | ||
| 333 | } | ||
| 334 | if (*buf != '\0') | ||
| 335 | errx(1, "Too many proxy headers received"); | ||
| 336 | } else | ||
| 337 | errx(1, "Unknown proxy protocol %d", socksv); | ||
| 338 | |||
| 339 | return (proxyfd); | ||
| 340 | } | ||
