From ae5f19e683f661b5fdb91368ed13625b8184eca7 Mon Sep 17 00:00:00 2001 From: delphij Date: Sun, 21 May 2006 15:49:27 +0000 Subject: Import netcat from OpenBSD 3.9-RELEASE. --- contrib/netcat/atomicio.c | 28 +++-- contrib/netcat/atomicio.h | 33 ++++++ contrib/netcat/nc.1 | 77 ++++++++---- contrib/netcat/netcat.c | 151 ++++++++++++++--------- contrib/netcat/socks.c | 296 ++++++++++++++++++++++++++++++---------------- 5 files changed, 393 insertions(+), 192 deletions(-) create mode 100644 contrib/netcat/atomicio.h (limited to 'contrib') diff --git a/contrib/netcat/atomicio.c b/contrib/netcat/atomicio.c index 9e73290..c3f2684 100644 --- a/contrib/netcat/atomicio.c +++ b/contrib/netcat/atomicio.c @@ -1,4 +1,7 @@ +/* $OpenBSD: atomicio.c,v 1.8 2006/02/11 19:31:18 otto Exp $ */ + /* + * Copyright (c) 2005 Anil Madhavapeddy. All rights served. * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. * All rights reserved. * @@ -25,20 +28,23 @@ #include #include - #include #include - -ssize_t atomicio(ssize_t (*f)(int, void *, size_t), int fd, void *_s, size_t n); +#include "atomicio.h" /* - * ensure all of data on socket comes through. f==read || f==write + * ensure all of data on socket comes through. f==read || f==vwrite */ -ssize_t -atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n) +size_t +atomicio(f, fd, _s, n) + ssize_t (*f) (int, void *, size_t); + int fd; + void *_s; + size_t n; { char *s = _s; - ssize_t res, pos = 0; + size_t pos = 0; + ssize_t res; while (n > pos) { res = (f) (fd, s + pos, n - pos); @@ -46,11 +52,13 @@ atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n) case -1: if (errno == EINTR || errno == EAGAIN) continue; + return 0; case 0: - return (res); + errno = EPIPE; + return pos; default: - pos += res; + pos += (size_t)res; } } - return (pos); + return pos; } diff --git a/contrib/netcat/atomicio.h b/contrib/netcat/atomicio.h new file mode 100644 index 0000000..551a056 --- /dev/null +++ b/contrib/netcat/atomicio.h @@ -0,0 +1,33 @@ +/* $OpenBSD: atomicio.h,v 1.1 2005/05/24 20:13:28 avsm Exp $ */ + +/* + * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Ensure all of data on socket comes through. f==read || f==vwrite + */ +size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t); + +#define vwrite (ssize_t (*)(int, void *, size_t))write diff --git a/contrib/netcat/nc.1 b/contrib/netcat/nc.1 index 472fa76..852ded8 100644 --- a/contrib/netcat/nc.1 +++ b/contrib/netcat/nc.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: nc.1,v 1.36 2005/01/07 10:11:31 jmc Exp $ +.\" $OpenBSD: nc.1,v 1.43 2006/01/31 09:34:12 jmc Exp $ .\" .\" Copyright (c) 1996 David Sacerdote .\" All rights reserved. @@ -36,8 +36,10 @@ .Bk -words .Op Fl 46DdhklnrStUuvz .Op Fl i Ar interval +.Op Fl P Ar proxy_username .Op Fl p Ar source_port .Op Fl s Ar source_ip_address +.Op Fl T Ar ToS .Op Fl w Ar timeout .Op Fl X Ar proxy_protocol .Oo Xo @@ -127,6 +129,10 @@ option are ignored. .It Fl n Do not do any DNS or service lookups on any specified addresses, hostnames or ports. +.It Fl P Ar proxy_username +Specifies a username to present to a proxy server that requires authentication. +If no username is specified then authentication will not be attempted. +Proxy authentication is only supported for HTTP CONNECT proxies at present. .It Fl p Ar source_port Specifies the source port .Nm @@ -145,6 +151,14 @@ Specifies the IP of the interface which is used to send the packets. It is an error to use this option in conjunction with the .Fl l option. +.It Fl T Ar ToS +Specifies IP Type of Service (ToS) for the connection. +Valid values are the tokens +.Dq lowdelay , +.Dq throughput , +.Dq reliability , +or an 8-bit hexadecimal value preceded by +.Dq 0x . .It Fl t Causes .Nm @@ -174,7 +188,7 @@ will listen forever for a connection, with or without the .Fl w flag. The default is no timeout. -.It Fl X Ar proxy_version +.It Fl X Ar proxy_protocol Requests that .Nm should use the specified protocol when talking to the proxy server. @@ -290,8 +304,9 @@ It can aid in troubleshooting, when it might be necessary to verify what data a server is sending in response to commands issued by the client. For example, to retrieve the home page of a web site: -.Pp -.Dl $ echo \&"GET\&" | nc host.example.com 80 +.Bd -literal -offset indent +$ echo -n "GET / HTTP/1.0\er\en\er\en" | nc host.example.com 80 +.Ed .Pp Note that this also displays the headers sent by the web server. They can be filtered, using a tool such as @@ -319,15 +334,11 @@ The .Fl z flag can be used to tell .Nm -not to initiate a connection, -together with the -.Fl v -.Pq verbose -flag, -to report open ports. +to report open ports, +rather than initiate a connection. For example: .Bd -literal -offset indent -$ nc -vz host.example.com 20-30 +$ nc -z host.example.com 20-30 Connection to host.example.com 22 port [tcp/ssh] succeeded! Connection to host.example.com 25 port [tcp/smtp] succeeded! .Ed @@ -351,37 +362,53 @@ Protocol mismatch. 220 host.example.com IMS SMTP Receiver Version 0.84 Ready .Ed .Sh EXAMPLES -Open a TCP connection to port 42 of hostname, using port 31337 as +Open a TCP connection to port 42 of host.example.com, using port 31337 as the source port, with a timeout of 5 seconds: .Pp -.Dl $ nc -p 31337 -w 5 hostname 42 +.Dl $ nc -p 31337 -w 5 host.example.com 42 .Pp -Open a UDP connection to port 53 of hostname: +Open a UDP connection to port 53 of host.example.com: .Pp -.Dl $ nc -u hostname 53 +.Dl $ nc -u host.example.com 53 .Pp -Open a TCP connection to port 42 of example.host using 10.1.2.3 as the +Open a TCP connection to port 42 of host.example.com using 10.1.2.3 as the IP for the local end of the connection: .Pp -.Dl $ nc -s 10.1.2.3 example.host 42 -.Pp -Send UDP packets to ports 20-30 of example.host, and report which ones -responded with an ICMP packet after three seconds: -.Pp -.Dl $ nc -uvz -w 3 hostname 20-30 +.Dl $ nc -s 10.1.2.3 host.example.com 42 .Pp Create and listen on a Unix Domain Socket: .Pp .Dl $ nc -lU /var/tmp/dsocket .Pp -Connect to port 42 of hostname via an HTTP proxy at 10.2.3.4, port 8080: +Connect to port 42 of host.example.com via an HTTP proxy at 10.2.3.4, +port 8080. +This example could also be used by +.Xr ssh 1 ; +see the +.Cm ProxyCommand +directive in +.Xr ssh_config 5 +for more information. +.Pp +.Dl $ nc -x10.2.3.4:8080 -Xconnect host.example.com 42 .Pp -.Dl $ nc -x10.2.3.4:8080 -Xconnect hostname 42 +The same example again, this time enabling proxy authentication with username +.Dq ruser +if the proxy requires it: +.Pp +.Dl $ nc -x10.2.3.4:8080 -Xconnect -Pruser host.example.com 42 .Sh SEE ALSO -.Xr cat 1 +.Xr cat 1 , +.Xr ssh 1 .Sh AUTHORS Original implementation by *Hobbit* .Aq hobbit@avian.org . .br Rewritten with IPv6 support by .An Eric Jackson Aq ericj@monkey.org . +.Sh CAVEATS +UDP port scans will always succeed +(i.e. report the port as open), +rendering the +.Fl uz +combination of flags relatively useless. diff --git a/contrib/netcat/netcat.c b/contrib/netcat/netcat.c index 3c610b9..bea1b79 100644 --- a/contrib/netcat/netcat.c +++ b/contrib/netcat/netcat.c @@ -1,4 +1,4 @@ -/* $OpenBSD: netcat.c,v 1.76 2004/12/10 16:51:31 hshoexer Exp $ */ +/* $OpenBSD: netcat.c,v 1.87 2006/02/01 21:33:14 otto Exp $ */ /* * Copyright (c) 2001 Eric Jackson * @@ -37,7 +37,9 @@ #include #include +#include #include +#include #include #include @@ -50,6 +52,8 @@ #include #include #include +#include +#include "atomicio.h" #ifndef SUN_LEN #define SUN_LEN(su) \ @@ -62,9 +66,11 @@ /* Command Line Options */ int dflag; /* detached, no stdin */ int iflag; /* Interval Flag */ +int jflag; /* use jumbo frames if we can */ int kflag; /* More than one connect */ int lflag; /* Bind to local port */ int nflag; /* Don't do name look up */ +char *Pflag; /* Proxy username */ char *pflag; /* Localport flag */ int rflag; /* Random ports flag */ char *sflag; /* Source Address */ @@ -75,23 +81,25 @@ int xflag; /* Socks proxy */ int zflag; /* Port Scan Flag */ int Dflag; /* sodebug */ int Sflag; /* TCP MD5 signature option */ +int Tflag = -1; /* IP Type of Service */ int timeout = -1; int family = AF_UNSPEC; char *portlist[PORT_MAX+1]; -ssize_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t); void atelnet(int, unsigned char *, unsigned int); void build_ports(char *); void help(void); int local_listen(char *, char *, struct addrinfo); void readwrite(int); -int remote_connect(char *, char *, struct addrinfo); -int socks_connect(char *, char *, struct addrinfo, char *, char *, - struct addrinfo, int); +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 udptest(int); int unix_connect(char *); int unix_listen(char *); +void set_common_sockopts(int); +int parse_iptos(char *); void usage(int); int @@ -104,7 +112,7 @@ main(int argc, char *argv[]) socklen_t len; struct sockaddr_storage cliaddr; char *proxy; - char *proxyhost = "", *proxyport = NULL; + const char *proxyhost = "", *proxyport = NULL; struct addrinfo proxyhints; ret = 1; @@ -115,7 +123,8 @@ main(int argc, char *argv[]) endp = NULL; sv = NULL; - while ((ch = getopt(argc, argv, "46Ddhi:klnp:rSs:tUuvw:X:x:z")) != -1) { + while ((ch = getopt(argc, argv, + "46Ddhi:jklnP:p:rSs:tT:Uuvw:X:x:z")) != -1) { switch (ch) { case '4': family = AF_INET; @@ -147,6 +156,9 @@ main(int argc, char *argv[]) if (iflag < 0 || *endp != '\0') errx(1, "interval cannot be negative"); break; + case 'j': + jflag = 1; + break; case 'k': kflag = 1; break; @@ -156,6 +168,9 @@ main(int argc, char *argv[]) case 'n': nflag = 1; break; + case 'P': + Pflag = optarg; + break; case 'p': pflag = optarg; break; @@ -196,6 +211,9 @@ main(int argc, char *argv[]) case 'S': Sflag = 1; break; + case 'T': + Tflag = parse_iptos(optarg); + break; default: usage(1); } @@ -286,12 +304,13 @@ main(int argc, char *argv[]) * functions to talk to the caller. */ if (uflag) { - int rv; - char buf[1024]; + int rv, plen; + char buf[8192]; struct sockaddr_storage z; len = sizeof(z); - rv = recvfrom(s, buf, sizeof(buf), MSG_PEEK, + plen = jflag ? 8192 : 1024; + rv = recvfrom(s, buf, plen, MSG_PEEK, (struct sockaddr *)&z, &len); if (rv < 0) err(1, "recvfrom"); @@ -302,6 +321,7 @@ main(int argc, char *argv[]) connfd = s; } else { + len = sizeof(cliaddr); connfd = accept(s, (struct sockaddr *)&cliaddr, &len); } @@ -338,7 +358,8 @@ main(int argc, char *argv[]) if (xflag) s = socks_connect(host, portlist[i], hints, - proxyhost, proxyport, proxyhints, socksv); + proxyhost, proxyport, proxyhints, socksv, + Pflag); else s = remote_connect(host, portlist[i], hints); @@ -452,10 +473,10 @@ unix_listen(char *path) * port or source address if needed. Returns -1 on failure. */ int -remote_connect(char *host, char *port, struct addrinfo hints) +remote_connect(const char *host, const char *port, struct addrinfo hints) { struct addrinfo *res, *res0; - int s, error, x = 1; + int s, error; if ((error = getaddrinfo(host, port, &hints, &res))) errx(1, "getaddrinfo: %s", gai_strerror(error)); @@ -470,13 +491,6 @@ remote_connect(char *host, char *port, struct addrinfo hints) if (sflag || pflag) { struct addrinfo ahints, *ares; - if (!(sflag && pflag)) { - if (!sflag) - sflag = NULL; - else - pflag = NULL; - } - memset(&ahints, 0, sizeof(struct addrinfo)); ahints.ai_family = res0->ai_family; ahints.ai_socktype = uflag ? SOCK_DGRAM : SOCK_STREAM; @@ -490,16 +504,8 @@ remote_connect(char *host, char *port, struct addrinfo hints) errx(1, "bind failed: %s", strerror(errno)); freeaddrinfo(ares); } - if (Sflag) { - if (setsockopt(s, IPPROTO_TCP, TCP_MD5SIG, - &x, sizeof(x)) == -1) - err(1, NULL); - } - if (Dflag) { - if (setsockopt(s, SOL_SOCKET, SO_DEBUG, - &x, sizeof(x)) == -1) - err(1, NULL); - } + + set_common_sockopts(s); if (connect(s, res0->ai_addr, res0->ai_addrlen) == 0) break; @@ -544,23 +550,14 @@ local_listen(char *host, char *port, struct addrinfo hints) res0 = res; do { if ((s = socket(res0->ai_family, res0->ai_socktype, - res0->ai_protocol)) == 0) + res0->ai_protocol)) < 0) continue; ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof(x)); if (ret == -1) err(1, NULL); - if (Sflag) { - ret = setsockopt(s, IPPROTO_TCP, TCP_MD5SIG, - &x, sizeof(x)); - if (ret == -1) - err(1, NULL); - } - if (Dflag) { - if (setsockopt(s, SOL_SOCKET, SO_DEBUG, - &x, sizeof(x)) == -1) - err(1, NULL); - } + + set_common_sockopts(s); if (bind(s, (struct sockaddr *)res0->ai_addr, res0->ai_addrlen) == 0) @@ -588,9 +585,12 @@ void readwrite(int nfd) { struct pollfd pfd[2]; - unsigned char buf[BUFSIZ]; - int wfd = fileno(stdin), n; + unsigned char buf[8192]; + int n, wfd = fileno(stdin); int lfd = fileno(stdout); + int plen; + + plen = jflag ? 8192 : 1024; /* Setup Network FD */ pfd[0].fd = nfd; @@ -613,7 +613,7 @@ readwrite(int nfd) return; if (pfd[0].revents & POLLIN) { - if ((n = read(nfd, buf, sizeof(buf))) < 0) + if ((n = read(nfd, buf, plen)) < 0) return; else if (n == 0) { shutdown(nfd, SHUT_RD); @@ -622,22 +622,20 @@ readwrite(int nfd) } else { if (tflag) atelnet(nfd, buf, n); - if (atomicio((ssize_t (*)(int, void *, size_t))write, - lfd, buf, n) != n) + if (atomicio(vwrite, lfd, buf, n) != n) return; } } if (!dflag && pfd[1].revents & POLLIN) { - if ((n = read(wfd, buf, sizeof(buf))) < 0) + if ((n = read(wfd, buf, plen)) < 0) return; else if (n == 0) { shutdown(nfd, SHUT_WR); pfd[1].fd = -1; pfd[1].events = 0; } else { - if (atomicio((ssize_t (*)(int, void *, size_t))write, - nfd, buf, n) != n) + if (atomicio(vwrite, nfd, buf, n) != n) return; } } @@ -668,9 +666,8 @@ atelnet(int nfd, unsigned char *buf, unsigned int size) p++; obuf[2] = *p; obuf[3] = '\0'; - if (atomicio((ssize_t (*)(int, void *, size_t))write, - nfd, obuf, 3) != 3) - warnx("Write Error!"); + if (atomicio(vwrite, nfd, obuf, 3) != 3) + warn("Write Error!"); obuf[0] = '\0'; } } @@ -762,6 +759,50 @@ udptest(int s) } void +set_common_sockopts(int s) +{ + int x = 1; + + if (Sflag) { + if (setsockopt(s, IPPROTO_TCP, TCP_MD5SIG, + &x, sizeof(x)) == -1) + err(1, NULL); + } + if (Dflag) { + if (setsockopt(s, SOL_SOCKET, SO_DEBUG, + &x, sizeof(x)) == -1) + err(1, NULL); + } + if (jflag) { + if (setsockopt(s, SOL_SOCKET, SO_JUMBO, + &x, sizeof(x)) == -1) + err(1, NULL); + } + if (Tflag != -1) { + if (setsockopt(s, IPPROTO_IP, IP_TOS, + &Tflag, sizeof(Tflag)) == -1) + err(1, "set IP ToS"); + } +} + +int +parse_iptos(char *s) +{ + int tos = -1; + + if (strcmp(s, "lowdelay") == 0) + return (IPTOS_LOWDELAY); + if (strcmp(s, "throughput") == 0) + return (IPTOS_THROUGHPUT); + if (strcmp(s, "reliability") == 0) + return (IPTOS_RELIABILITY); + + if (sscanf(s, "0x%x", &tos) != 1 || tos < 0 || tos > 0xff) + errx(1, "invalid IP Type of Service"); + return (tos); +} + +void help(void) { usage(0); @@ -775,10 +816,12 @@ help(void) \t-k Keep inbound sockets open for multiple connects\n\ \t-l Listen mode, for inbound connects\n\ \t-n Suppress name/port resolutions\n\ + \t-P proxyuser\tUsername for proxy authentication\n\ \t-p port\t Specify local port for remote connects\n\ \t-r Randomize remote ports\n\ \t-S Enable the TCP MD5 signature option\n\ \t-s addr\t Local source address\n\ + \t-T ToS\t Set IP Type of Service\n\ \t-t Answer TELNET negotiation\n\ \t-U Use UNIX domain socket\n\ \t-u UDP mode\n\ @@ -795,7 +838,7 @@ void usage(int ret) { fprintf(stderr, "usage: nc [-46DdhklnrStUuvz] [-i interval] [-p source_port]\n"); - fprintf(stderr, "\t [-s source_ip_address] [-w timeout] [-X proxy_version]\n"); + fprintf(stderr, "\t [-s source_ip_address] [-T ToS] [-w timeout] [-X proxy_version]\n"); fprintf(stderr, "\t [-x proxy_address[:port]] [hostname] [port[s]]\n"); if (ret) exit(1); 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 #include #include +#include +#include +#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); } -- cgit v1.1