diff options
author | delphij <delphij@FreeBSD.org> | 2006-05-21 15:49:27 +0000 |
---|---|---|
committer | delphij <delphij@FreeBSD.org> | 2006-05-21 15:49:27 +0000 |
commit | ae5f19e683f661b5fdb91368ed13625b8184eca7 (patch) | |
tree | 243e38414b80cb75b334161d99a0c8e89e6273c1 /contrib/netcat/socks.c | |
parent | fe20bac494132d72a50195b9fac6ff1258b6f32f (diff) | |
download | FreeBSD-src-ae5f19e683f661b5fdb91368ed13625b8184eca7.zip FreeBSD-src-ae5f19e683f661b5fdb91368ed13625b8184eca7.tar.gz |
Import netcat from OpenBSD 3.9-RELEASE.
Diffstat (limited to 'contrib/netcat/socks.c')
-rw-r--r-- | contrib/netcat/socks.c | 296 |
1 files changed, 193 insertions, 103 deletions
diff --git a/contrib/netcat/socks.c b/contrib/netcat/socks.c index e7d35b6..daed997 100644 --- a/contrib/netcat/socks.c +++ b/contrib/netcat/socks.c @@ -1,7 +1,8 @@ -/* $OpenBSD: socks.c,v 1.9 2004/10/17 03:13:55 djm Exp $ */ +/* $OpenBSD: socks.c,v 1.16 2006/01/25 23:21:37 djm Exp $ */ /* * Copyright (c) 1999 Niklas Hallqvist. All rights reserved. + * Copyright (c) 2004, 2005 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,6 +37,9 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <resolv.h> +#include <readpassphrase.h> +#include "atomicio.h" #define SOCKS_PORT "1080" #define HTTP_PROXY_PORT "3128" @@ -46,57 +50,53 @@ #define SOCKS_NOMETHOD 0xff #define SOCKS_CONNECT 1 #define SOCKS_IPV4 1 +#define SOCKS_DOMAIN 3 +#define SOCKS_IPV6 4 +int remote_connect(const char *, const char *, struct addrinfo); +int socks_connect(const char *, const char *, struct addrinfo, + const char *, const char *, struct addrinfo, int, + const char *); -int remote_connect(char *, char *, struct addrinfo); -int socks_connect(char *host, char *port, struct addrinfo hints, - char *proxyhost, char *proxyport, struct addrinfo proxyhints, - int socksv); - -static in_addr_t -decode_addr(const char *s) +static int +decode_addrport(const char *h, const char *p, struct sockaddr *addr, + socklen_t addrlen, int v4only, int numeric) { - struct hostent *hp = gethostbyname (s); - struct in_addr retval; - - if (hp) - return *(in_addr_t *)hp->h_addr_list[0]; - if (inet_aton (s, &retval)) - return retval.s_addr; - errx (1, "cannot decode address \"%s\"", s); -} + int r; + struct addrinfo hints, *res; -static in_port_t -decode_port(const char *s) -{ - struct servent *sp; - in_port_t port; - char *p; - - port = strtol (s, &p, 10); - if (s == p) { - sp = getservbyname (s, "tcp"); - if (sp) - return sp->s_port; + bzero(&hints, sizeof(hints)); + hints.ai_family = v4only ? PF_INET : PF_UNSPEC; + hints.ai_flags = numeric ? AI_NUMERICHOST : 0; + hints.ai_socktype = SOCK_STREAM; + r = getaddrinfo(h, p, &hints, &res); + /* Don't fatal when attempting to convert a numeric address */ + if (r != 0) { + if (!numeric) { + errx(1, "getaddrinfo(\"%.64s\", \"%.64s\"): %s", h, p, + gai_strerror(r)); + } + return (-1); + } + if (addrlen < res->ai_addrlen) { + freeaddrinfo(res); + errx(1, "internal error: addrlen < res->ai_addrlen"); } - if (*s != '\0' && *p == '\0') - return htons (port); - errx (1, "cannot decode port \"%s\"", s); + memcpy(addr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + return (0); } static int -proxy_read_line(int fd, char *buf, int bufsz) +proxy_read_line(int fd, char *buf, size_t bufsz) { - int r, off; + size_t off; for(off = 0;;) { if (off >= bufsz) errx(1, "proxy read too long"); - if ((r = read(fd, buf + off, 1)) <= 0) { - if (r == -1 && errno == EINTR) - continue; + if (atomicio(read, fd, buf + off, 1) != 1) err(1, "proxy read"); - } /* Skip CR */ if (buf[off] == '\r') continue; @@ -109,127 +109,217 @@ proxy_read_line(int fd, char *buf, int bufsz) return (off); } +static const char * +getproxypass(const char *proxyuser, const char *proxyhost) +{ + char prompt[512]; + static char pw[256]; + + snprintf(prompt, sizeof(prompt), "Proxy password for %s@%s: ", + proxyuser, proxyhost); + if (readpassphrase(prompt, pw, sizeof(pw), RPP_REQUIRE_TTY) == NULL) + errx(1, "Unable to read proxy passphrase"); + return (pw); +} + int -socks_connect(char *host, char *port, struct addrinfo hints, - char *proxyhost, char *proxyport, struct addrinfo proxyhints, - int socksv) +socks_connect(const char *host, const char *port, + struct addrinfo hints __attribute__ ((__unused__)), + const char *proxyhost, const char *proxyport, struct addrinfo proxyhints, + int socksv, const char *proxyuser) { - int proxyfd, r; + int proxyfd, r, authretry = 0; + size_t hlen, wlen; unsigned char buf[1024]; - ssize_t cnt; - in_addr_t serveraddr; + size_t cnt; + struct sockaddr_storage addr; + struct sockaddr_in *in4 = (struct sockaddr_in *)&addr; + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&addr; in_port_t serverport; + const char *proxypass = NULL; if (proxyport == NULL) proxyport = (socksv == -1) ? HTTP_PROXY_PORT : SOCKS_PORT; + /* Abuse API to lookup port */ + if (decode_addrport("0.0.0.0", port, (struct sockaddr *)&addr, + sizeof(addr), 1, 1) == -1) + errx(1, "unknown port \"%.64s\"", port); + serverport = in4->sin_port; + + again: + if (authretry++ > 3) + errx(1, "Too many authentication failures"); + proxyfd = remote_connect(proxyhost, proxyport, proxyhints); if (proxyfd < 0) - return -1; - - serveraddr = decode_addr (host); - serverport = decode_port (port); + return (-1); if (socksv == 5) { + if (decode_addrport(host, port, (struct sockaddr *)&addr, + sizeof(addr), 0, 1) == -1) + addr.ss_family = 0; /* used in switch below */ + /* Version 5, one method: no authentication */ buf[0] = SOCKS_V5; buf[1] = 1; buf[2] = SOCKS_NOAUTH; - cnt = write (proxyfd, buf, 3); - if (cnt == -1) - err (1, "write failed"); + cnt = atomicio(vwrite, proxyfd, buf, 3); if (cnt != 3) - errx (1, "short write, %d (expected 3)", cnt); + err(1, "write failed (%d/3)", cnt); + + cnt = atomicio(read, proxyfd, buf, 2); + if (cnt != 2) + err(1, "read failed (%d/3)", cnt); - read (proxyfd, buf, 2); if (buf[1] == SOCKS_NOMETHOD) - errx (1, "authentication method negotiation failed"); + errx(1, "authentication method negotiation failed"); - /* Version 5, connect: IPv4 address */ - buf[0] = SOCKS_V5; - buf[1] = SOCKS_CONNECT; - buf[2] = 0; - buf[3] = SOCKS_IPV4; - memcpy (buf + 4, &serveraddr, sizeof serveraddr); - memcpy (buf + 8, &serverport, sizeof serverport); - - /* XXX Handle short writes better */ - cnt = write (proxyfd, buf, 10); - if (cnt == -1) - err (1, "write failed"); - if (cnt != 10) - errx (1, "short write, %d (expected 10)", cnt); + switch (addr.ss_family) { + case 0: + /* Version 5, connect: domain name */ + + /* Max domain name length is 255 bytes */ + hlen = strlen(host); + if (hlen > 255) + errx(1, "host name too long for SOCKS5"); + buf[0] = SOCKS_V5; + buf[1] = SOCKS_CONNECT; + buf[2] = 0; + buf[3] = SOCKS_DOMAIN; + buf[4] = hlen; + memcpy(buf + 5, host, hlen); + memcpy(buf + 5 + hlen, &serverport, sizeof serverport); + wlen = 7 + hlen; + break; + case AF_INET: + /* Version 5, connect: IPv4 address */ + buf[0] = SOCKS_V5; + buf[1] = SOCKS_CONNECT; + buf[2] = 0; + buf[3] = SOCKS_IPV4; + memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr); + memcpy(buf + 8, &in4->sin_port, sizeof in4->sin_port); + wlen = 10; + break; + case AF_INET6: + /* Version 5, connect: IPv6 address */ + buf[0] = SOCKS_V5; + buf[1] = SOCKS_CONNECT; + buf[2] = 0; + buf[3] = SOCKS_IPV6; + memcpy(buf + 4, &in6->sin6_addr, sizeof in6->sin6_addr); + memcpy(buf + 20, &in6->sin6_port, + sizeof in6->sin6_port); + wlen = 22; + break; + default: + errx(1, "internal error: silly AF"); + } - /* XXX Handle short reads better */ - cnt = read (proxyfd, buf, sizeof buf); - if (cnt == -1) - err (1, "read failed"); + cnt = atomicio(vwrite, proxyfd, buf, wlen); + if (cnt != wlen) + err(1, "write failed (%d/%d)", cnt, wlen); + + cnt = atomicio(read, proxyfd, buf, 10); if (cnt != 10) - errx (1, "unexpected reply size %d (expected 10)", cnt); + err(1, "read failed (%d/10)", cnt); if (buf[1] != 0) - errx (1, "connection failed, SOCKS error %d", buf[1]); + errx(1, "connection failed, SOCKS error %d", buf[1]); } else if (socksv == 4) { + /* This will exit on lookup failure */ + decode_addrport(host, port, (struct sockaddr *)&addr, + sizeof(addr), 1, 0); + /* Version 4 */ buf[0] = SOCKS_V4; buf[1] = SOCKS_CONNECT; /* connect */ - memcpy (buf + 2, &serverport, sizeof serverport); - memcpy (buf + 4, &serveraddr, sizeof serveraddr); + memcpy(buf + 2, &in4->sin_port, sizeof in4->sin_port); + memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr); buf[8] = 0; /* empty username */ + wlen = 9; - cnt = write (proxyfd, buf, 9); - if (cnt == -1) - err (1, "write failed"); - if (cnt != 9) - errx (1, "short write, %d (expected 9)", cnt); + cnt = atomicio(vwrite, proxyfd, buf, wlen); + if (cnt != wlen) + err(1, "write failed (%d/%d)", cnt, wlen); - /* XXX Handle short reads better */ - cnt = read (proxyfd, buf, 8); - if (cnt == -1) - err (1, "read failed"); + cnt = atomicio(read, proxyfd, buf, 8); if (cnt != 8) - errx (1, "unexpected reply size %d (expected 8)", cnt); + err(1, "read failed (%d/8)", cnt); if (buf[1] != 90) - errx (1, "connection failed, SOCKS error %d", buf[1]); + errx(1, "connection failed, SOCKS error %d", buf[1]); } else if (socksv == -1) { /* HTTP proxy CONNECT */ /* Disallow bad chars in hostname */ if (strcspn(host, "\r\n\t []:") != strlen(host)) - errx (1, "Invalid hostname"); + errx(1, "Invalid hostname"); /* Try to be sane about numeric IPv6 addresses */ if (strchr(host, ':') != NULL) { r = snprintf(buf, sizeof(buf), - "CONNECT [%s]:%d HTTP/1.0\r\n\r\n", + "CONNECT [%s]:%d HTTP/1.0\r\n", host, ntohs(serverport)); } else { r = snprintf(buf, sizeof(buf), - "CONNECT %s:%d HTTP/1.0\r\n\r\n", + "CONNECT %s:%d HTTP/1.0\r\n", host, ntohs(serverport)); } - if (r == -1 || r >= sizeof(buf)) - errx (1, "hostname too long"); + if (r == -1 || (size_t)r >= sizeof(buf)) + errx(1, "hostname too long"); r = strlen(buf); - /* XXX atomicio */ - cnt = write (proxyfd, buf, r); - if (cnt == -1) - err (1, "write failed"); + cnt = atomicio(vwrite, proxyfd, buf, r); if (cnt != r) - errx (1, "short write, %d (expected %d)", cnt, r); + err(1, "write failed (%d/%d)", cnt, r); + + if (authretry > 1) { + char resp[1024]; + + proxypass = getproxypass(proxyuser, proxyhost); + r = snprintf(buf, sizeof(buf), "%s:%s", + proxyuser, proxypass); + if (r == -1 || (size_t)r >= sizeof(buf) || + b64_ntop(buf, strlen(buf), resp, + sizeof(resp)) == -1) + errx(1, "Proxy username/password too long"); + r = snprintf(buf, sizeof(buf), "Proxy-Authorization: " + "Basic %s\r\n", resp); + if (r == -1 || (size_t)r >= sizeof(buf)) + errx(1, "Proxy auth response too long"); + r = strlen(buf); + if ((cnt = atomicio(vwrite, proxyfd, buf, r)) != r) + err(1, "write failed (%d/%d)", cnt, r); + } + + /* Terminate headers */ + if ((r = atomicio(vwrite, proxyfd, "\r\n", 2)) != 2) + err(1, "write failed (2/%d)", r); + + /* Read status reply */ + proxy_read_line(proxyfd, buf, sizeof(buf)); + if (proxyuser != NULL && + strncmp(buf, "HTTP/1.0 407 ", 12) == 0) { + if (authretry > 1) { + fprintf(stderr, "Proxy authentication " + "failed\n"); + } + close(proxyfd); + goto again; + } else if (strncmp(buf, "HTTP/1.0 200 ", 12) != 0) + errx(1, "Proxy error: \"%s\"", buf); - /* Read reply */ + /* Headers continue until we hit an empty line */ for (r = 0; r < HTTP_MAXHDRS; r++) { proxy_read_line(proxyfd, buf, sizeof(buf)); - if (r == 0 && strncmp(buf, "HTTP/1.0 200 ", 12) != 0) - errx (1, "Proxy error: \"%s\"", buf); - /* Discard headers until we hit an empty line */ if (*buf == '\0') break; } + if (*buf != '\0') + errx(1, "Too many proxy headers received"); } else - errx (1, "Unknown proxy protocol %d", socksv); + errx(1, "Unknown proxy protocol %d", socksv); - return proxyfd; + return (proxyfd); } |