From 1f92d60e9793da1cc372244750f3ba29cbccecd3 Mon Sep 17 00:00:00 2001 From: Waldemar Brodkorb Date: Mon, 21 Sep 2020 13:44:14 +0200 Subject: add netcat-openbsd and libbsd --- package/libbsd/Makefile | 25 + package/netcat-openbsd/Makefile | 30 + package/netcat-openbsd/src/Makefile | 20 + package/netcat-openbsd/src/atomicio.c | 67 + package/netcat-openbsd/src/atomicio.h | 39 + package/netcat-openbsd/src/nc.1 | 582 +++++++++ package/netcat-openbsd/src/netcat.c | 2305 +++++++++++++++++++++++++++++++++ package/netcat-openbsd/src/socks.c | 400 ++++++ 8 files changed, 3468 insertions(+) create mode 100644 package/libbsd/Makefile create mode 100644 package/netcat-openbsd/Makefile create mode 100644 package/netcat-openbsd/src/Makefile create mode 100644 package/netcat-openbsd/src/atomicio.c create mode 100644 package/netcat-openbsd/src/atomicio.h create mode 100644 package/netcat-openbsd/src/nc.1 create mode 100644 package/netcat-openbsd/src/netcat.c create mode 100644 package/netcat-openbsd/src/socks.c (limited to 'package') diff --git a/package/libbsd/Makefile b/package/libbsd/Makefile new file mode 100644 index 000000000..b1dfbe191 --- /dev/null +++ b/package/libbsd/Makefile @@ -0,0 +1,25 @@ +# This file is part of the OpenADK project. OpenADK is copyrighted +# material, please see the LICENCE file in the top-level directory. + +include $(ADK_TOPDIR)/rules.mk + +PKG_NAME:= libbsd +PKG_VERSION:= 0.10.0 +PKG_RELEASE:= 1 +PKG_HASH:= 34b8adc726883d0e85b3118fa13605e179a62b31ba51f676136ecb2d0bc1a887 +PKG_DESCR:= bsd helper functions +PKG_SECTION:= libs/misc +PKG_URL:= https://libbsd.freedesktop.org/ +PKG_SITES:= https://libbsd.freedesktop.org/releases/ +PKG_OPTS:= dev + +include $(ADK_TOPDIR)/mk/package.mk + +$(eval $(call PKG_template,LIBBSD,libbsd,$(PKG_VERSION)-$(PKG_RELEASE),$(PKG_DEPENDS),$(PKG_DESCR),$(PKG_SECTION),$(PKG_OPTS))) + +libbsd-install: + $(INSTALL_DIR) $(IDIR_LIBBSD)/usr/lib + $(CP) $(WRKINST)/usr/lib/libbsd*.so* \ + $(IDIR_LIBBSD)/usr/lib + +include $(ADK_TOPDIR)/mk/pkg-bottom.mk diff --git a/package/netcat-openbsd/Makefile b/package/netcat-openbsd/Makefile new file mode 100644 index 000000000..6a45186c6 --- /dev/null +++ b/package/netcat-openbsd/Makefile @@ -0,0 +1,30 @@ +# This file is part of the OpenADK project. OpenADK is copyrighted +# material, please see the LICENCE file in the top-level directory. + +include $(ADK_TOPDIR)/rules.mk + +PKG_NAME:= netcat-openbsd +PKG_VERSION:= 1.217 +PKG_RELEASE:= 1 +PKG_HASH:= fcb551d9987fd51d020c62b6d81df0c2bb17ce1887bbc3fda4d28313791cc0f5 +PKG_DESCR:= openbsd netcat +PKG_SECTION:= net/misc +PKG_BUILDDEP:= libbsd +PKG_DEPENDS:= libbsd +PKG_SITES:= http://deb.debian.org/debian/pool/main/n/netcat-openbsd/ + +NO_DISTFILES:= 1 + +include $(ADK_TOPDIR)/mk/package.mk + +$(eval $(call PKG_template,NETCAT_OPENBSD,netcat-openbsd,$(PKG_VERSION)-$(PKG_RELEASE),$(PKG_DEPENDS),$(PKG_DESCR),$(PKG_SECTION))) + +CONFIG_STYLE:= manual +INSTALL_STYLE:= manual + +netcat-openbsd-install: + $(INSTALL_DIR) $(IDIR_NETCAT_OPENBSD)/usr/bin + $(INSTALL_BIN) $(WRKBUILD)/nc \ + $(IDIR_NETCAT_OPENBSD)/usr/bin/netcat-openbsd + +include $(ADK_TOPDIR)/mk/pkg-bottom.mk diff --git a/package/netcat-openbsd/src/Makefile b/package/netcat-openbsd/src/Makefile new file mode 100644 index 000000000..8247cfd0c --- /dev/null +++ b/package/netcat-openbsd/src/Makefile @@ -0,0 +1,20 @@ +# $OpenBSD: Makefile,v 1.7 2015/09/11 21:07:01 beck Exp $ + +PROG= nc +SRCS= netcat.c atomicio.c socks.c + +PKG_CONFIG ?= pkg-config +LIBS= `$(PKG_CONFIG) --libs libbsd` -lresolv +OBJS= $(SRCS:.c=.o) +CFLAGS= -g -O2 +LDFLAGS= -Wl,--no-add-needed + +all: nc +nc: $(OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o nc + +$(OBJS): %.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -f $(OBJS) nc diff --git a/package/netcat-openbsd/src/atomicio.c b/package/netcat-openbsd/src/atomicio.c new file mode 100644 index 000000000..344ac63ab --- /dev/null +++ b/package/netcat-openbsd/src/atomicio.c @@ -0,0 +1,67 @@ +/* $OpenBSD: atomicio.c,v 1.11 2012/12/04 02:24:47 deraadt Exp $ */ +/* + * Copyright (c) 2006 Damien Miller. All rights reserved. + * Copyright (c) 2005 Anil Madhavapeddy. All rights reserved. + * 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. + */ + +#include +#include +#include + +#include "atomicio.h" + +/* + * ensure all of data on socket comes through. f==read || f==vwrite + */ +size_t +atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n) +{ + char *s = _s; + size_t pos = 0; + ssize_t res; + struct pollfd pfd; + + pfd.fd = fd; + pfd.events = f == read ? POLLIN : POLLOUT; + while (n > pos) { + res = (f) (fd, s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR) + continue; + if ((errno == EAGAIN) || (errno == ENOBUFS)) { + (void)poll(&pfd, 1, -1); + continue; + } + return 0; + case 0: + errno = EPIPE; + return pos; + default: + pos += (size_t)res; + } + } + return (pos); +} diff --git a/package/netcat-openbsd/src/atomicio.h b/package/netcat-openbsd/src/atomicio.h new file mode 100644 index 000000000..7bf5b2541 --- /dev/null +++ b/package/netcat-openbsd/src/atomicio.h @@ -0,0 +1,39 @@ +/* $OpenBSD: atomicio.h,v 1.2 2007/09/07 14:50:44 tobias Exp $ */ + +/* + * Copyright (c) 2006 Damien Miller. All rights reserved. + * 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. + */ + +#ifndef _ATOMICIO_H +#define _ATOMICIO_H + +/* + * 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 + +#endif /* _ATOMICIO_H */ diff --git a/package/netcat-openbsd/src/nc.1 b/package/netcat-openbsd/src/nc.1 new file mode 100644 index 000000000..a77a551e9 --- /dev/null +++ b/package/netcat-openbsd/src/nc.1 @@ -0,0 +1,582 @@ +.\" $OpenBSD: nc.1,v 1.95 2020/02/12 14:46:36 schwarze Exp $ +.\" +.\" Copyright (c) 1996 David Sacerdote +.\" 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. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" 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. +.\" +.Dd $Mdocdate: February 12 2020 $ +.Dt NC 1 +.Os +.Sh NAME +.Nm nc +.Nd arbitrary TCP and UDP connections and listens +.Sh SYNOPSIS +.Nm nc +.Op Fl 46bCDdFhklNnrStUuvZz +.Op Fl I Ar length +.Op Fl i Ar interval +.Op Fl M Ar ttl +.Op Fl m Ar minttl +.Op Fl O Ar length +.Op Fl P Ar proxy_username +.Op Fl p Ar source_port +.Op Fl q Ar seconds +.Op Fl s Ar sourceaddr +.Op Fl T Ar keyword +.Op Fl V Ar rtable +.Op Fl W Ar recvlimit +.Op Fl w Ar timeout +.Op Fl X Ar proxy_protocol +.Op Fl x Ar proxy_address Ns Op : Ns Ar port +.Op Ar destination +.Op Ar port +.Sh DESCRIPTION +The +.Nm +(or +.Nm netcat ) +utility is used for just about anything under the sun involving TCP, +UDP, or +.Ux Ns -domain +sockets. +It can open TCP connections, send UDP packets, listen on arbitrary +TCP and UDP ports, do port scanning, and deal with both IPv4 and +IPv6. +Unlike +.Xr telnet 1 , +.Nm +scripts nicely, and separates error messages onto standard error instead +of sending them to standard output, as +.Xr telnet 1 +does with some. +.Pp +Common uses include: +.Pp +.Bl -bullet -offset indent -compact +.It +simple TCP proxies +.It +shell-script based HTTP clients and servers +.It +network daemon testing +.It +a SOCKS or HTTP ProxyCommand for +.Xr ssh 1 +.It +and much, much more +.El +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl 4 +Use IPv4 addresses only. +.It Fl 6 +Use IPv6 addresses only. +.It Fl b +Allow broadcast. +.It Fl C +Send CRLF as line-ending. Each line feed (LF) character from the input +data is translated into CR+LF before being written to the socket. Line +feed characters that are already preceded with a carriage return (CR) +are not translated. Received data is not affected. +.It Fl D +Enable debugging on the socket. +.It Fl d +Do not attempt to read from stdin. +.It Fl F +Pass the first connected socket using +.Xr sendmsg 2 +to stdout and exit. +This is useful in conjunction with +.Fl X +to have +.Nm +perform connection setup with a proxy but then leave the rest of the +connection to another program (e.g.\& +.Xr ssh 1 +using the +.Xr ssh_config 5 +.Cm ProxyUseFdpass +option). +Cannot be used with +.Fl U . +.It Fl h +Print out the +.Nm +help text and exit. +.It Fl I Ar length +Specify the size of the TCP receive buffer. +.It Fl i Ar interval +Sleep for +.Ar interval +seconds between lines of text sent and received. +Also causes a delay time between connections to multiple ports. +.It Fl k +When a connection is completed, listen for another one. +Requires +.Fl l . +When used together with the +.Fl u +option, the server socket is not connected and it can receive UDP datagrams from +multiple hosts. +.It Fl l +Listen for an incoming connection rather than initiating a +connection to a remote host. +The +.Ar destination +and +.Ar port +to listen on can be specified either as non-optional arguments, or with +options +.Fl s +and +.Fl p +respectively. +Cannot be used together with +.Fl x +or +.Fl z . +Additionally, any timeouts specified with the +.Fl w +option are ignored. +.It Fl M Ar ttl +Set the TTL / hop limit of outgoing packets. +.It Fl m Ar minttl +Ask the kernel to drop incoming packets whose TTL / hop limit is under +.Ar minttl . +.It Fl N +.Xr shutdown 2 +the network socket after EOF on the input. +Some servers require this to finish their work. +.It Fl n +Do not perform domain name resolution. +If a name cannot be resolved without DNS, an error will be reported. +.It Fl O Ar length +Specify the size of the TCP send buffer. +.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 +Specify the source port +.Nm +should use, subject to privilege restrictions and availability. +.It Fl q Ar seconds +after EOF on stdin, wait the specified number of +.Ar seconds +and then quit. If +.Ar seconds +is negative, wait forever (default). Specifying a non-negative +.Ar seconds +implies +.Fl N . +.It Fl r +Choose source and/or destination ports randomly +instead of sequentially within a range or in the order that the system +assigns them. +.It Fl S +Enable the RFC 2385 TCP MD5 signature option. +.It Fl s Ar sourceaddr +Set the source address to send packets from, +which is useful on machines with multiple interfaces. +For +.Ux Ns -domain +datagram sockets, specifies the local temporary socket file +to create and use so that datagrams can be received. +Cannot be used together with +.Fl x . +.It Fl T Ar keyword +Change the IPv4 TOS/IPv6 traffic class value. +.Ar keyword +may be one of +.Cm critical , +.Cm inetcontrol , +.Cm lowcost , +.Cm lowdelay , +.Cm netcontrol , +.Cm throughput , +.Cm reliability , +or one of the DiffServ Code Points: +.Cm ef , +.Cm af11 No ... Cm af43 , +.Cm cs0 No ... Cm cs7 ; +or a number in either hex or decimal. +.It Fl t +Send RFC 854 DON'T and WON'T responses to RFC 854 DO and WILL requests. +This makes it possible to use +.Nm +to script telnet sessions. +.It Fl U +Use +.Ux Ns -domain +sockets. +Cannot be used together with +.Fl F +or +.Fl x . +.It Fl u +Use UDP instead of TCP. +Cannot be used together with +.Fl x . +For +.Ux Ns -domain +sockets, use a datagram socket instead of a stream socket. +If a +.Ux Ns -domain +socket is used, a temporary receiving socket is created in +.Pa /tmp +unless the +.Fl s +flag is given. +.It Fl V Ar rtable +Set the routing table to be used. +.It Fl v +Produce more verbose output. +.It Fl W Ar recvlimit +Terminate after receiving +.Ar recvlimit +packets from the network. +.It Fl w Ar timeout +Connections which cannot be established or are idle timeout after +.Ar timeout +seconds. +The +.Fl w +flag has no effect on the +.Fl l +option, i.e.\& +.Nm +will listen forever for a connection, with or without the +.Fl w +flag. +The default is no timeout. +.It Fl X Ar proxy_protocol +Use +.Ar proxy_protocol +when talking to the proxy server. +Supported protocols are +.Cm 4 +(SOCKS v.4), +.Cm 5 +(SOCKS v.5) +and +.Cm connect +(HTTPS proxy). +If the protocol is not specified, SOCKS version 5 is used. +.It Fl x Ar proxy_address Ns Op : Ns Ar port +Connect to +.Ar destination +using a proxy at +.Ar proxy_address +and +.Ar port . +If +.Ar port +is not specified, the well-known port for the proxy protocol is used (1080 +for SOCKS, 3128 for HTTPS). +An IPv6 address can be specified unambiguously by enclosing +.Ar proxy_address +in square brackets. +A proxy cannot be used with any of the options +.Fl lsuU . +.It Fl Z +DCCP mode. +.It Fl z +Only scan for listening daemons, without sending any data to them. +Cannot be used together with +.Fl l . +.El +.Pp +.Ar destination +can be a numerical IP address or a symbolic hostname +(unless the +.Fl n +option is given). +In general, a destination must be specified, +unless the +.Fl l +option is given +(in which case the local host is used). +For +.Ux Ns -domain +sockets, a destination is required and is the socket path to connect to +(or listen on if the +.Fl l +option is given). +.Pp +.Ar port +can be specified as a numeric port number or as a service name. +Port ranges may be specified as numeric port numbers of the form +.Ar nn Ns - Ns Ar mm . +In general, +a destination port must be specified, +unless the +.Fl U +option is given. +.Sh CLIENT/SERVER MODEL +It is quite simple to build a very basic client/server model using +.Nm . +On one console, start +.Nm +listening on a specific port for a connection. +For example: +.Pp +.Dl $ nc -l 1234 +.Pp +.Nm +is now listening on port 1234 for a connection. +On a second console +.Pq or a second machine , +connect to the machine and port being listened on: +.Pp +.Dl $ nc 127.0.0.1 1234 +.Pp +There should now be a connection between the ports. +Anything typed at the second console will be concatenated to the first, +and vice-versa. +After the connection has been set up, +.Nm +does not really care which side is being used as a +.Sq server +and which side is being used as a +.Sq client . +The connection may be terminated using an +.Dv EOF +.Pq Sq ^D . +.Pp +There is no +.Fl c +or +.Fl e +option in this netcat, but you still can execute a command after connection +being established by redirecting file descriptors. Be cautious here because +opening a port and let anyone connected execute arbitrary command on your +site is DANGEROUS. If you really need to do this, here is an example: +.Pp +On +.Sq server +side: +.Pp +.Dl $ rm -f /tmp/f; mkfifo /tmp/f +.Dl $ cat /tmp/f | /bin/sh -i 2>&1 | nc -l 127.0.0.1 1234 > /tmp/f +.Pp +On +.Sq client +side: +.Pp +.Dl $ nc host.example.com 1234 +.Dl $ (shell prompt from host.example.com) +.Pp +By doing this, you create a fifo at /tmp/f and make nc listen at port 1234 +of address 127.0.0.1 on +.Sq server +side, when a +.Sq client +establishes a connection successfully to that port, /bin/sh gets executed +on +.Sq server +side and the shell prompt is given to +.Sq client +side. +.Pp +When connection is terminated, +.Nm +quits as well. Use +.Fl k +if you want it keep listening, but if the command quits this option won't +restart it or keep +.Nm +running. Also don't forget to remove the file descriptor once you don't need +it anymore: +.Pp +.Dl $ rm -f /tmp/f +.Pp +.Sh DATA TRANSFER +The example in the previous section can be expanded to build a +basic data transfer model. +Any information input into one end of the connection will be output +to the other end, and input and output can be easily captured in order to +emulate file transfer. +.Pp +Start by using +.Nm +to listen on a specific port, with output captured into a file: +.Pp +.Dl $ nc -l 1234 \*(Gt filename.out +.Pp +Using a second machine, connect to the listening +.Nm +process, feeding it the file which is to be transferred: +.Pp +.Dl $ nc -N host.example.com 1234 \*(Lt filename.in +.Pp +After the file has been transferred, the connection will close automatically. +.Sh TALKING TO SERVERS +It is sometimes useful to talk to servers +.Dq by hand +rather than through a user interface. +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: +.Bd -literal -offset indent +$ printf "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 +.Xr sed 1 , +if necessary. +.Pp +More complicated examples can be built up when the user knows the format +of requests required by the server. +As another example, an email may be submitted to an SMTP server using: +.Bd -literal -offset indent +$ nc [\-C] localhost 25 \*(Lt\*(Lt EOF +HELO host.example.com +MAIL FROM:\*(Ltuser@host.example.com\*(Gt +RCPT TO:\*(Ltuser2@host.example.com\*(Gt +DATA +Body of email. +\&. +QUIT +EOF +.Ed +.Sh PORT SCANNING +It may be useful to know which ports are open and running services on +a target machine. +The +.Fl z +flag can be used to tell +.Nm +to report open ports, +rather than initiate a connection. Usually it's useful to turn on verbose +output to stderr by use this option in conjunction with +.Fl v +option. +.Pp +For example: +.Bd -literal -offset indent +$ nc \-zv 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 +.Pp +The port range was specified to limit the search to ports 20 \- 30, and is +scanned by increasing order (unless the +.Fl r +flag is set). +.Pp +You can also specify a list of ports to scan, for example: +.Bd -literal -offset indent +$ nc \-zv host.example.com http 20 22-23 +nc: connect to host.example.com 80 (tcp) failed: Connection refused +nc: connect to host.example.com 20 (tcp) failed: Connection refused +Connection to host.example.com port [tcp/ssh] succeeded! +nc: connect to host.example.com 23 (tcp) failed: Connection refused +.Ed +.Pp +The ports are scanned by the order you given (unless the +.Fl r +flag is set). +.Pp +Alternatively, it might be useful to know which server software +is running, and which versions. +This information is often contained within the greeting banners. +In order to retrieve these, it is necessary to first make a connection, +and then break the connection when the banner has been retrieved. +This can be accomplished by specifying a small timeout with the +.Fl w +flag, or perhaps by issuing a +.Qq Dv QUIT +command to the server: +.Bd -literal -offset indent +$ echo "QUIT" | nc host.example.com 20-30 +SSH-1.99-OpenSSH_3.6.1p2 +Protocol mismatch. +220 host.example.com IMS SMTP Receiver Version 0.84 Ready +.Ed +.Sh EXAMPLES +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 host.example.com 42 +.Pp +Open a UDP connection to port 53 of host.example.com: +.Pp +.Dl $ nc -u host.example.com 53 +.Pp +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 host.example.com 42 +.Pp +Create and listen on a +.Ux Ns -domain +stream socket: +.Pp +.Dl $ nc -lU /var/tmp/dsocket +.Pp +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 +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 ssh 1 +.Sh AUTHORS +Original implementation by +.An *Hobbit* Aq Mt hobbit@avian.org . +.br +Rewritten with IPv6 support by +.An Eric Jackson Aq Mt ericj@monkey.org . +.br +Modified for Debian port by Aron Xu +.Aq aron@debian.org . +.Sh CAVEATS +UDP port scans using the +.Fl uz +combination of flags will always report success irrespective of +the target machine's state. +However, +in conjunction with a traffic sniffer either on the target machine +or an intermediary device, +the +.Fl uz +combination could be useful for communications diagnostics. +Note that the amount of UDP traffic generated may be limited either +due to hardware resources and/or configuration settings. diff --git a/package/netcat-openbsd/src/netcat.c b/package/netcat-openbsd/src/netcat.c new file mode 100644 index 000000000..bd7344fb4 --- /dev/null +++ b/package/netcat-openbsd/src/netcat.c @@ -0,0 +1,2305 @@ +/* $OpenBSD: netcat.c,v 1.217 2020/02/12 14:46:36 schwarze Exp $ */ +/* + * Copyright (c) 2001 Eric Jackson + * Copyright (c) 2015 Bob Beck. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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. + */ + +/* + * Re-written nc(1) for OpenBSD. Original implementation by + * *Hobbit* . + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include + +#include +#include +#include +#include +#ifdef __linux__ +# include +#endif +#if defined(TCP_MD5SIG_EXT) && defined(TCP_MD5SIG_MAXKEYLEN) +# include +#endif + +#ifndef IPTOS_LOWDELAY +# define IPTOS_LOWDELAY 0x10 +# define IPTOS_THROUGHPUT 0x08 +# define IPTOS_RELIABILITY 0x04 +# define IPTOS_LOWCOST 0x02 +# define IPTOS_MINCOST IPTOS_LOWCOST +#endif /* IPTOS_LOWDELAY */ + +# ifndef IPTOS_DSCP_AF11 +# define IPTOS_DSCP_AF11 0x28 +# define IPTOS_DSCP_AF12 0x30 +# define IPTOS_DSCP_AF13 0x38 +# define IPTOS_DSCP_AF21 0x48 +# define IPTOS_DSCP_AF22 0x50 +# define IPTOS_DSCP_AF23 0x58 +# define IPTOS_DSCP_AF31 0x68 +# define IPTOS_DSCP_AF32 0x70 +# define IPTOS_DSCP_AF33 0x78 +# define IPTOS_DSCP_AF41 0x88 +# define IPTOS_DSCP_AF42 0x90 +# define IPTOS_DSCP_AF43 0x98 +# define IPTOS_DSCP_EF 0xb8 +#endif /* IPTOS_DSCP_AF11 */ + +#ifndef IPTOS_DSCP_CS0 +# define IPTOS_DSCP_CS0 0x00 +# define IPTOS_DSCP_CS1 0x20 +# define IPTOS_DSCP_CS2 0x40 +# define IPTOS_DSCP_CS3 0x60 +# define IPTOS_DSCP_CS4 0x80 +# define IPTOS_DSCP_CS5 0xa0 +# define IPTOS_DSCP_CS6 0xc0 +# define IPTOS_DSCP_CS7 0xe0 +#endif /* IPTOS_DSCP_CS0 */ + +#ifndef IPTOS_DSCP_EF +# define IPTOS_DSCP_EF 0xb8 +#endif /* IPTOS_DSCP_EF */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef TLS +# include +#endif +#include +#include +#include + +#include "atomicio.h" + +#define PORT_MAX 65535 +#define UNIX_DG_TMP_SOCKET_SIZE 19 + +#define POLL_STDIN 0 +#define POLL_NETOUT 1 +#define POLL_NETIN 2 +#define POLL_STDOUT 3 +#define BUFSIZE 16384 + +#ifdef TLS +# define TLS_NOVERIFY (1 << 1) +# define TLS_NONAME (1 << 2) +# define TLS_CCERT (1 << 3) +# define TLS_MUSTSTAPLE (1 << 4) +#endif + +#define CONNECTION_SUCCESS 0 +#define CONNECTION_FAILED 1 +#define CONNECTION_TIMEOUT 2 + +#define UDP_SCAN_TIMEOUT 3 /* Seconds */ + +/* Command Line Options */ +int bflag; /* Allow Broadcast */ +int dflag; /* detached, no stdin */ +int Fflag; /* fdpass sock to stdout */ +unsigned int iflag; /* Interval Flag */ +int kflag; /* More than one connect */ +int lflag; /* Bind to local port */ +int Nflag; /* shutdown() network socket */ +int nflag; /* Don't do name look up */ +char *Pflag; /* Proxy username */ +char *pflag; /* Localport flag */ +int qflag = -1; /* Quit after some secs */ +int rflag; /* Random ports flag */ +char *sflag; /* Source Address */ +int tflag; /* Telnet Emulation */ +int uflag; /* UDP - Default to TCP */ +int dccpflag; /* DCCP - Default to TCP */ +int vflag; /* Verbosity */ +int xflag; /* Socks proxy */ +int zflag; /* Port Scan Flag */ +int Dflag; /* sodebug */ +int Iflag; /* TCP receive buffer size */ +int Oflag; /* TCP send buffer size */ +int Sflag; /* TCP MD5 signature option */ +int Tflag = -1; /* IP Type of Service */ +int rtableid = -1; + +# if defined(TLS) +int usetls; /* use TLS */ +const char *Cflag; /* Public cert file */ +const char *Kflag; /* Private key file */ +const char *oflag; /* OCSP stapling file */ +const char *Rflag; /* Root CA file */ +int tls_cachanged; /* Using non-default CA file */ +int TLSopt; /* TLS options */ +char *tls_expectname; /* required name in peer cert */ +char *tls_expecthash; /* required hash of peer cert */ +char *tls_ciphers; /* TLS ciphers */ +char *tls_protocols; /* TLS protocols */ +FILE *Zflag; /* file to save peer cert */ +# else +int Cflag = 0; /* CRLF line-ending */ +# endif + +# if defined(TCP_MD5SIG_EXT) && defined(TCP_MD5SIG_MAXKEYLEN) +char Sflag_password[TCP_MD5SIG_MAXKEYLEN]; +# endif +int recvcount, recvlimit; +int timeout = -1; +int family = AF_UNSPEC; +char *portlist[PORT_MAX+1]; +char *unix_dg_tmp_socket; +int ttl = -1; +int minttl = -1; + +void atelnet(int, unsigned char *, unsigned int); +int strtoport(char *portstr, int udp); +void build_ports(char **); +void help(void) __attribute__((noreturn)); +int local_listen(const char *, const char *, struct addrinfo); +# if defined(TLS) +void readwrite(int, struct tls *); +# else +void readwrite(int); +# endif +void fdpass(int nfd) __attribute__((noreturn)); +int remote_connect(const char *, const char *, struct addrinfo, char *); +# if defined(TLS) +int timeout_tls(int, struct tls *, int (*)(struct tls *)); +# endif +int timeout_connect(int, const struct sockaddr *, socklen_t); +int socks_connect(const char *, const char *, struct addrinfo, + const char *, const char *, struct addrinfo, int, const char *); +int udptest(int); +int unix_bind(char *, int); +int unix_connect(char *); +int unix_listen(char *); +void set_common_sockopts(int, const struct sockaddr *); +int process_tos_opt(char *, int *); +# if defined(TLS) +int process_tls_opt(char *, int *); +void save_peer_cert(struct tls *_tls_ctx, FILE *_fp); +# endif +void report_sock(const char *, const struct sockaddr *, socklen_t, char *); +# if defined(TLS) +void report_tls(struct tls *tls_ctx, char * host); +# endif +void usage(int); +# if defined(TLS) +ssize_t drainbuf(int, unsigned char *, size_t *, struct tls *); +ssize_t fillbuf(int, unsigned char *, size_t *, struct tls *); +void tls_setup_client(struct tls *, int, char *); +struct tls *tls_setup_server(struct tls *, int, char *); +# else +ssize_t drainbuf(int, unsigned char *, size_t *, int); +ssize_t fillbuf(int, unsigned char *, size_t *); +# endif + +char *proto_name(int uflag, int dccpflag); +static int connect_with_timeout(int fd, const struct sockaddr *sa, + socklen_t salen, int ctimeout); + +static void quit(); + +int +main(int argc, char *argv[]) +{ + int ch, s = -1, ret, socksv; + char *host, **uport; + char ipaddr[NI_MAXHOST]; + struct addrinfo hints; + struct servent *sv; + socklen_t len; + union { + struct sockaddr_storage storage; + struct sockaddr_un forunix; + } cliaddr; + char *proxy = NULL, *proxyport = NULL; + const char *errstr; + struct addrinfo proxyhints; + char unix_dg_tmp_socket_buf[UNIX_DG_TMP_SOCKET_SIZE]; +# if defined(TLS) + struct tls_config *tls_cfg = NULL; + struct tls *tls_ctx = NULL; +# endif + uint32_t protocols; + + ret = 1; + socksv = 5; + host = NULL; + uport = NULL; + sv = NULL; +# if defined(TLS) + Rflag = tls_default_ca_cert_file(); +# endif + + signal(SIGPIPE, SIG_IGN); + + while ((ch = getopt(argc, argv, +# if defined(TLS) + "46bC:cDde:FH:hI:i:K:klM:m:NnO:o:P:p:q:R:rSs:T:tUuV:vW:w:X:x:Z:z")) +# else + "46bCDdFhI:i:klM:m:NnO:P:p:q:rSs:T:tUuV:vW:w:X:x:Zz")) +# endif + != -1) { + switch (ch) { + case '4': + family = AF_INET; + break; + case '6': + family = AF_INET6; + break; + case 'b': +# if defined(SO_BROADCAST) + bflag = 1; +# else + errx(1, "no broadcast frame support available"); +# endif + break; + case 'U': + family = AF_UNIX; + break; + case 'X': + if (strcasecmp(optarg, "connect") == 0) + socksv = -1; /* HTTP proxy CONNECT */ + else if (strcmp(optarg, "4") == 0) + socksv = 4; /* SOCKS v.4 */ + else if (strcmp(optarg, "5") == 0) + socksv = 5; /* SOCKS v.5 */ + else + errx(1, "unsupported proxy protocol"); + break; +# if defined(TLS) + case 'C': + Cflag = optarg; + break; + case 'c': + usetls = 1; + break; +# else + case 'C': + Cflag = 1; + break; +# endif + case 'd': + dflag = 1; + break; +# if defined(TLS) + case 'e': + tls_expectname = optarg; + break; +# endif + case 'F': + Fflag = 1; + break; +# if defined(TLS) + case 'H': + tls_expecthash = optarg; + break; +# endif + case 'h': + help(); + break; + case 'i': + iflag = strtonum(optarg, 0, UINT_MAX, &errstr); + if (errstr) + errx(1, "interval %s: %s", errstr, optarg); + break; +# if defined(TLS) + case 'K': + Kflag = optarg; + break; +# endif + case 'k': + kflag = 1; + break; + case 'l': + lflag = 1; + break; + case 'M': + ttl = strtonum(optarg, 0, 255, &errstr); + if (errstr) + errx(1, "ttl is %s", errstr); + break; + case 'm': + minttl = strtonum(optarg, 0, 255, &errstr); + if (errstr) + errx(1, "minttl is %s", errstr); + break; + case 'N': + Nflag = 1; + break; + case 'n': + nflag = 1; + break; + case 'P': + Pflag = optarg; + break; + case 'p': + pflag = optarg; + break; + case 'q': + qflag = strtonum(optarg, INT_MIN, INT_MAX, &errstr); + if (errstr) + errx(1, "quit timer %s: %s", errstr, optarg); + if (qflag >= 0) + Nflag = 1; + break; +# if defined(TLS) + case 'R': + tls_cachanged = 1; + Rflag = optarg; + break; +# endif + case 'r': + rflag = 1; + break; + case 's': + sflag = optarg; + break; + case 't': + tflag = 1; + break; + case 'u': + uflag = 1; + break; + case 'Z': +# if defined(IPPROTO_DCCP) && defined(SOCK_DCCP) + dccpflag = 1; +# else + errx(1, "no DCCP support available"); +# endif + break; + case 'V': +# if defined(RT_TABLEID_MAX) + rtableid = (int)strtonum(optarg, 0, + RT_TABLEID_MAX, &errstr); + if (errstr) + errx(1, "rtable %s: %s", errstr, optarg); +# else + errx(1, "no alternate routing table support available"); +# endif + break; + case 'v': + vflag = 1; + break; + case 'W': + recvlimit = strtonum(optarg, 1, INT_MAX, &errstr); + if (errstr) + errx(1, "receive limit %s: %s", errstr, optarg); + break; + case 'w': + timeout = strtonum(optarg, 0, INT_MAX / 1000, &errstr); + if (errstr) + errx(1, "timeout %s: %s", errstr, optarg); + timeout *= 1000; + break; + case 'x': + xflag = 1; + if ((proxy = strdup(optarg)) == NULL) + err(1, NULL); + break; +# if defined(TLS) + case 'Z': + if (strcmp(optarg, "-") == 0) + Zflag = stderr; + else if ((Zflag = fopen(optarg, "w")) == NULL) + err(1, "can't open %s", optarg); + break; +# endif + case 'z': + zflag = 1; + break; + case 'D': + Dflag = 1; + break; + case 'I': + Iflag = strtonum(optarg, 1, 65536 << 14, &errstr); + if (errstr != NULL) + errx(1, "TCP receive window %s: %s", + errstr, optarg); + break; + case 'O': + Oflag = strtonum(optarg, 1, 65536 << 14, &errstr); + if (errstr != NULL) + errx(1, "TCP send window %s: %s", + errstr, optarg); + break; +# if defined(TLS) + case 'o': + oflag = optarg; + break; +# endif + case 'S': +# if defined(TCP_MD5SIG_EXT) && defined(TCP_MD5SIG_MAXKEYLEN) + if (readpassphrase("TCP MD5SIG password: ", + Sflag_password, TCP_MD5SIG_MAXKEYLEN, RPP_REQUIRE_TTY) == NULL) + errx(1, "Unable to read TCP MD5SIG password"); + Sflag = 1; +# else + errx(1, "no TCP MD5 signature support available"); +# endif + break; + case 'T': + errstr = NULL; + errno = 0; +# if defined(TLS) + if (process_tls_opt(optarg, &TLSopt)) + break; +# endif + if (process_tos_opt(optarg, &Tflag)) + break; + if (strlen(optarg) > 1 && optarg[0] == '0' && + optarg[1] == 'x') + Tflag = (int)strtol(optarg, NULL, 16); + else + Tflag = (int)strtonum(optarg, 0, 255, + &errstr); + if (Tflag < 0 || Tflag > 255 || errstr || errno) +# if defined(TLS) + errx(1, "illegal tos/tls value %s", optarg); +# else + errx(1, "illegal tos value %s", optarg); +# endif + break; + default: + usage(1); + } + } + argc -= optind; + argv += optind; + +# if defined(RT_TABLEID_MAX) + if (rtableid >= 0) + if (setrtable(rtableid) == -1) + err(1, "setrtable"); +# endif + + /* Cruft to make sure options are clean, and used properly. */ + if (argc == 0 && lflag) { + uport = &pflag; + host = sflag; + } else if (argc == 1 && !pflag && + /* `nc -l 12345` or `nc -U bar` or `nc -uU -s foo bar` */ + (!sflag || (family == AF_UNIX && uflag && !lflag))) { + if (family == AF_UNIX) { + host = argv[0]; + uport = NULL; + } else if (lflag) { + host = NULL; + uport = argv; + } + } else if (argc >= 2) { + if (lflag && (pflag || sflag || argc > 2)) + usage(1); /* conflict */ + host = argv[0]; + uport = &argv[1]; + } else + usage(1); + + if (family == AF_UNIX) { +# if defined(IPPROTO_DCCP) && defined(SOCK_DCCP) + if (dccpflag) + errx(1, "cannot use -Z and -U"); +# endif + if (uport && *uport) + errx(1, "cannot use port with -U"); + if (!host) + errx(1, "missing socket pathname"); + } else if (!uport || !*uport) + errx(1, "missing port number"); + + if (lflag && zflag) + errx(1, "cannot use -z and -l"); + +# if defined(TLS) + if (usetls) { + if (Cflag && unveil(Cflag, "r") == -1) + err(1, "unveil"); + if (unveil(Rflag, "r") == -1) + err(1, "unveil"); + if (Kflag && unveil(Kflag, "r") == -1) + err(1, "unveil"); + if (oflag && unveil(oflag, "r") == -1) + err(1, "unveil"); + } else if (family == AF_UNIX && uflag && lflag && !kflag) { + /* + * After recvfrom(2) from client, the server connects + * to the client socket. As the client path is determined + * during runtime, we cannot unveil(2). + */ + } else { + if (family == AF_UNIX) { + if (unveil(host, "rwc") == -1) + err(1, "unveil"); + if (uflag && !kflag) { + if (sflag) { + if (unveil(sflag, "rwc") == -1) + err(1, "unveil"); + } else { + if (unveil("/tmp", "rwc") == -1) + err(1, "unveil"); + } + } + } else { + /* no filesystem visibility */ + if (unveil("/", "") == -1) + err(1, "unveil"); + } + } +# endif + + if (!lflag && kflag) + errx(1, "must use -l with -k"); +# if defined(TLS) + if (uflag && usetls) + errx(1, "cannot use -c and -u"); + if ((family == AF_UNIX) && usetls) + errx(1, "cannot use -c and -U"); +# endif + if ((family == AF_UNIX) && Fflag) + errx(1, "cannot use -F and -U"); +# if defined(TLS) + if (Fflag && usetls) + errx(1, "cannot use -c and -F"); + if (TLSopt && !usetls) + errx(1, "you must specify -c to use TLS options"); + if (Cflag && !usetls) + errx(1, "you must specify -c to use -C"); + if (Kflag && !usetls) + errx(1, "you must specify -c to use -K"); + if (Zflag && !usetls) + errx(1, "you must specify -c to use -Z"); + if (oflag && !Cflag) + errx(1, "you must specify -C to use -o"); + if (tls_cachanged && !usetls) + errx(1, "you must specify -c to use -R"); + if (tls_expecthash && !usetls) + errx(1, "you must specify -c to use -H"); + if (tls_expectname && !usetls) + errx(1, "you must specify -c to use -e"); +# endif + + /* Get name of temporary socket for unix datagram client */ + if ((family == AF_UNIX) && uflag && !lflag) { + if (sflag) { + unix_dg_tmp_socket = sflag; + } else { + strlcpy(unix_dg_tmp_socket_buf, "/tmp/nc.XXXXXXXXXX", + UNIX_DG_TMP_SOCKET_SIZE); + if (mkstemp(unix_dg_tmp_socket_buf) == -1) + err(1, "mkstemp"); + unix_dg_tmp_socket = unix_dg_tmp_socket_buf; + } + } + + /* Initialize addrinfo structure. */ + if (family != AF_UNIX) { + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = family; + if (uflag) { + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + } +# if defined(IPPROTO_DCCP) && defined(SOCK_DCCP) + else if (dccpflag) { + hints.ai_socktype = SOCK_DCCP; + hints.ai_protocol = IPPROTO_DCCP; + } +# endif + else { + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + } + if (nflag) + hints.ai_flags |= AI_NUMERICHOST; + } + + if (xflag) { + if (uflag) + errx(1, "no proxy support for UDP mode"); +# if defined(IPPROTO_DCCP) && defined(SOCK_DCCP) + if (dccpflag) + errx(1, "no proxy support for DCCP mode"); +# endif + if (lflag) + errx(1, "no proxy support for listen"); + + if (family == AF_UNIX) + errx(1, "no proxy support for unix sockets"); + + if (sflag) + errx(1, "no proxy support for local source address"); + + if (*proxy == '[') { + ++proxy; + proxyport = strchr(proxy, ']'); + if (proxyport == NULL) + errx(1, "missing closing bracket in proxy"); + *proxyport++ = '\0'; + if (*proxyport == '\0') + /* Use default proxy port. */ + proxyport = NULL; + else { + if (*proxyport == ':') + ++proxyport; + else + errx(1, "garbage proxy port delimiter"); + } + } else { + proxyport = strrchr(proxy, ':'); + if (proxyport != NULL) + *proxyport++ = '\0'; + } + + memset(&proxyhints, 0, sizeof(struct addrinfo)); + proxyhints.ai_family = family; + proxyhints.ai_socktype = SOCK_STREAM; + proxyhints.ai_protocol = IPPROTO_TCP; + if (nflag) + proxyhints.ai_flags |= AI_NUMERICHOST; + } + +# if defined(TLS) + if (usetls) { + if ((tls_cfg = tls_config_new()) == NULL) + errx(1, "unable to allocate TLS config"); + if (Rflag && tls_config_set_ca_file(tls_cfg, Rflag) == -1) + errx(1, "%s", tls_config_error(tls_cfg)); + if (Cflag && tls_config_set_cert_file(tls_cfg, Cflag) == -1) + errx(1, "%s", tls_config_error(tls_cfg)); + if (Kflag && tls_config_set_key_file(tls_cfg, Kflag) == -1) + errx(1, "%s", tls_config_error(tls_cfg)); + if (oflag && tls_config_set_ocsp_staple_file(tls_cfg, oflag) == -1) + errx(1, "%s", tls_config_error(tls_cfg)); + if (tls_config_parse_protocols(&protocols, tls_protocols) == -1) + errx(1, "invalid TLS protocols `%s'", tls_protocols); + if (tls_config_set_protocols(tls_cfg, protocols) == -1) + errx(1, "%s", tls_config_error(tls_cfg)); + if (tls_config_set_ciphers(tls_cfg, tls_ciphers) == -1) + errx(1, "%s", tls_config_error(tls_cfg)); + if (!lflag && (TLSopt & TLS_CCERT)) + errx(1, "clientcert is only valid with -l"); + if (TLSopt & TLS_NONAME) + tls_config_insecure_noverifyname(tls_cfg); + if (TLSopt & TLS_NOVERIFY) { + if (tls_expecthash != NULL) + errx(1, "-H and -T noverify may not be used " + "together"); + tls_config_insecure_noverifycert(tls_cfg); + } + if (TLSopt & TLS_MUSTSTAPLE) + tls_config_ocsp_require_stapling(tls_cfg); + + if (Pflag) { + if (pledge("stdio inet dns tty", NULL) == -1) + err(1, "pledge"); + } else if (pledge("stdio inet dns", NULL) == -1) + err(1, "pledge"); + } +# endif + if (lflag) { + ret = 0; + + if (family == AF_UNIX) { + if (uflag) + s = unix_bind(host, 0); + else + s = unix_listen(host); + } else + s = local_listen(host, *uport, hints); + if (s < 0) + err(1, NULL); + +# if defined(TLS) + if (usetls) { + tls_config_verify_client_optional(tls_cfg); + if ((tls_ctx = tls_server()) == NULL) + errx(1, "tls server creation failed"); + if (tls_configure(tls_ctx, tls_cfg) == -1) + errx(1, "tls configuration failed (%s)", + tls_error(tls_ctx)); + } +# endif + /* Allow only one connection at a time, but stay alive. */ + for (;;) { + if (uflag && kflag) { + /* + * For UDP and -k, don't connect the socket, + * let it receive datagrams from multiple + * socket pairs. + */ +# if defined(TLS) + readwrite(s, NULL); +# else + readwrite(s); +# endif + } else if (uflag && !kflag) { + /* + * For UDP and not -k, we will use recvfrom() + * initially to wait for a caller, then use + * the regular functions to talk to the caller. + */ + int rv; + char buf[2048]; + struct sockaddr_storage z; + + len = sizeof(z); + rv = recvfrom(s, buf, sizeof(buf), MSG_PEEK, + (struct sockaddr *)&z, &len); + if (rv == -1) + err(1, "recvfrom"); + + rv = connect(s, (struct sockaddr *)&z, len); + if (rv == -1) + err(1, "connect"); + + if (vflag) + report_sock("Connection received", + (struct sockaddr *)&z, len, + family == AF_UNIX ? host : NULL); + +# if defined(TLS) + readwrite(s, NULL); + } else { + struct tls *tls_cctx = NULL; +# else + readwrite(s); + } else { +# endif + int connfd; + + len = sizeof(cliaddr); + connfd = accept4(s, (struct sockaddr *)&cliaddr, + &len, SOCK_NONBLOCK); + if (connfd == -1) { + /* For now, all errnos are fatal */ + err(1, "accept"); + } + if (vflag) + report_sock("Connection received", + (struct sockaddr *)&cliaddr, len, + family == AF_UNIX ? host : NULL); +# if defined(TLS) + if ((usetls) && + (tls_cctx = tls_setup_server(tls_ctx, connfd, host))) + readwrite(connfd, tls_cctx); + if (!usetls) + readwrite(connfd, NULL); + if (tls_cctx) + timeout_tls(s, tls_cctx, tls_close); + close(connfd); + tls_free(tls_cctx); +# else + readwrite(connfd); + close(connfd); +# endif + } + if (family == AF_UNIX && uflag) { + if (connect(s, NULL, 0) == -1) + err(1, "connect"); + } + + if (!kflag) { + if (s != -1) + close(s); + break; + } + } + } else if (family == AF_UNIX) { + ret = 0; + + if ((s = unix_connect(host)) > 0) { + if (!zflag) +# if defined(TLS) + readwrite(s, NULL); +# else + readwrite(s); +# endif + close(s); + } else { + warn("%s", host); + ret = 1; + } + + if (uflag) + unlink(unix_dg_tmp_socket); + return ret; + + } else { + int i = 0; + + /* Construct the portlist[] array. */ + build_ports(uport); + + /* Cycle through portlist, connecting to each port. */ + for (s = -1, i = 0; portlist[i] != NULL; i++) { + if (s != -1) + close(s); +# if defined(TLS) + tls_free(tls_ctx); + tls_ctx = NULL; + + if (usetls) { + if ((tls_ctx = tls_client()) == NULL) + errx(1, "tls client creation failed"); + if (tls_configure(tls_ctx, tls_cfg) == -1) + errx(1, "tls configuration failed (%s)", + tls_error(tls_ctx)); + } +# endif + if (xflag) + s = socks_connect(host, portlist[i], hints, + proxy, proxyport, proxyhints, socksv, + Pflag); + else + s = remote_connect(host, portlist[i], hints, + ipaddr); + + if (s == -1) + continue; + + ret = 0; + if (vflag) { + /* For UDP, make sure we are connected. */ + if (uflag) { + if (udptest(s) == -1) { + ret = 1; + continue; + } + } + + char *proto = proto_name(uflag, dccpflag); + /* Don't look up port if -n. */ + if (nflag) + sv = NULL; + else { + sv = getservbyport( + ntohs(atoi(portlist[i])), + proto); + } + + fprintf(stderr, "Connection to %s", host); + + /* + * if we aren't connecting thru a proxy and + * there is something to report, print IP + */ + if (!nflag && !xflag + && (strcmp(host, ipaddr) != 0)) + fprintf(stderr, " (%s)", ipaddr); + + fprintf(stderr, " %s port [%s/%s] succeeded!\n", + portlist[i], proto, + sv ? sv->s_name : "*"); + } + if (Fflag) + fdpass(s); +# if defined(TLS) + else { + if (usetls) + tls_setup_client(tls_ctx, s, host); + if (!zflag) + readwrite(s, tls_ctx); + if (tls_ctx) + timeout_tls(s, tls_ctx, tls_close); + } +# else + else if (!zflag) + readwrite(s); +# endif + } + } + + if (s != -1) + close(s); +# if defined(TLS) + tls_free(tls_ctx); + tls_config_free(tls_cfg); +# endif + + return ret; +} + +/* + * unix_bind() + * Returns a unix socket bound to the given path + */ +int +unix_bind(char *path, int flags) +{ + struct sockaddr_un s_un; + int s, save_errno; + + /* Create unix domain socket. */ + if ((s = socket(AF_UNIX, flags | (uflag ? SOCK_DGRAM : SOCK_STREAM), + 0)) == -1) + return -1; + + unlink(path); + + memset(&s_un, 0, sizeof(struct sockaddr_un)); + s_un.sun_family = AF_UNIX; + + if (strlcpy(s_un.sun_path, path, sizeof(s_un.sun_path)) >= + sizeof(s_un.sun_path)) { + close(s); + errno = ENAMETOOLONG; + return -1; + } + + if (bind(s, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { + save_errno = errno; + close(s); + errno = save_errno; + return -1; + } + if (vflag) + report_sock("Bound", NULL, 0, path); + + return s; +} + +# if defined(TLS) +int +timeout_tls(int s, struct tls *tls_ctx, int (*func)(struct tls *)) +{ + struct pollfd pfd; + int ret; + + while ((ret = (*func)(tls_ctx)) != 0) { + if (ret == TLS_WANT_POLLIN) + pfd.events = POLLIN; + else if (ret == TLS_WANT_POLLOUT) + pfd.events = POLLOUT; + else + break; + pfd.fd = s; + if ((ret = poll(&pfd, 1, timeout)) == 1) + continue; + else if (ret == 0) { + errno = ETIMEDOUT; + ret = -1; + break; + } else + err(1, "poll failed"); + } + + return ret; +} + +void +tls_setup_client(struct tls *tls_ctx, int s, char *host) +{ + const char *errstr; + + if (tls_connect_socket(tls_ctx, s, + tls_expectname ? tls_expectname : host) == -1) { + errx(1, "tls connection failed (%s)", + tls_error(tls_ctx)); + } + if (timeout_tls(s, tls_ctx, tls_handshake) == -1) { + if ((errstr = tls_error(tls_ctx)) == NULL) + errstr = strerror(errno); + errx(1, "tls handshake failed (%s)", errstr); + } + if (vflag) + report_tls(tls_ctx, host); + if (tls_expecthash && (tls_peer_cert_hash(tls_ctx) == NULL || + strcmp(tls_expecthash, tls_peer_cert_hash(tls_ctx)) != 0)) + errx(1, "peer certificate is not %s", tls_expecthash); + if (Zflag) { + save_peer_cert(tls_ctx, Zflag); + if (Zflag != stderr && (fclose(Zflag) != 0)) + err(1, "fclose failed saving peer cert"); + } +} + +struct tls * +tls_setup_server(struct tls *tls_ctx, int connfd, char *host) +{ + struct tls *tls_cctx; + const char *errstr; + + if (tls_accept_socket(tls_ctx, &tls_cctx, connfd) == -1) { + warnx("tls accept failed (%s)", tls_error(tls_ctx)); + } else if (timeout_tls(connfd, tls_cctx, tls_handshake) == -1) { + if ((errstr = tls_error(tls_cctx)) == NULL) + errstr = strerror(errno); + warnx("tls handshake failed (%s)", errstr); + } else { + int gotcert = tls_peer_cert_provided(tls_cctx); + + if (vflag && gotcert) + report_tls(tls_cctx, host); + if ((TLSopt & TLS_CCERT) && !gotcert) + warnx("No client certificate provided"); + else if (gotcert && tls_expecthash && + (tls_peer_cert_hash(tls_cctx) == NULL || + strcmp(tls_expecthash, tls_peer_cert_hash(tls_cctx)) != 0)) + warnx("peer certificate is not %s", tls_expecthash); + else if (gotcert && tls_expectname && + (!tls_peer_cert_contains_name(tls_cctx, tls_expectname))) + warnx("name (%s) not found in client cert", + tls_expectname); + else { + return tls_cctx; + } + } + return NULL; +} +# endif + +/* + * unix_connect() + * Returns a socket connected to a local unix socket. Returns -1 on failure. + */ +int +unix_connect(char *path) +{ + struct sockaddr_un s_un; + int s, save_errno; + + if (uflag) { + if ((s = unix_bind(unix_dg_tmp_socket, SOCK_CLOEXEC)) == -1) + return -1; + } else { + if ((s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) { + errx(1, "create unix socket failed"); + return -1; + } + } + + memset(&s_un, 0, sizeof(struct sockaddr_un)); + s_un.sun_family = AF_UNIX; + + if (strlcpy(s_un.sun_path, path, sizeof(s_un.sun_path)) >= + sizeof(s_un.sun_path)) { + close(s); + errno = ENAMETOOLONG; + warn("unix connect abandoned"); + return -1; + } + if (connect(s, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { + save_errno = errno; + warn("unix connect failed"); + close(s); + errno = save_errno; + return -1; + } + return s; + +} + +/* + * unix_listen() + * Create a unix domain socket, and listen on it. + */ +int +unix_listen(char *path) +{ + int s; + + if ((s = unix_bind(path, 0)) == -1) + return -1; + if (listen(s, 5) == -1) { + close(s); + return -1; + } + if (vflag) + report_sock("Listening", NULL, 0, path); + + return s; +} + +char *proto_name(int uflag, int dccpflag) { + + char *proto = NULL; + if (uflag) { + proto = "udp"; + } +# if defined(IPPROTO_DCCP) && defined(SOCK_DCCP) + else if (dccpflag) { + proto = "dccp"; + } +# endif + else { + proto = "tcp"; + } + + return proto; +} + +/* + * remote_connect() + * Returns a socket connected to a remote host. Properly binds to a local + * port or source address if needed. Returns -1 on failure. + */ +int +remote_connect(const char *host, const char *port, struct addrinfo hints, + char *ipaddr) +{ + struct addrinfo *res, *res0; + int s = -1, error, herr, on = 1, save_errno; + + if ((error = getaddrinfo(host, port, &hints, &res0))) + errx(1, "getaddrinfo for host \"%s\" port %s: %s", host, + port, gai_strerror(error)); + + for (res = res0; res; res = res->ai_next) { + if ((s = socket(res->ai_family, res->ai_socktype | + SOCK_NONBLOCK, res->ai_protocol)) == -1) + continue; + + /* Bind to a local port or source address if specified. */ + if (sflag || pflag) { + struct addrinfo ahints, *ares; + +# if defined (SO_BINDANY) + /* try SO_BINDANY, but don't insist */ + setsockopt(s, SOL_SOCKET, SO_BINDANY, &on, sizeof(on)); +# endif + memset(&ahints, 0, sizeof(struct addrinfo)); + ahints.ai_family = res->ai_family; + if (uflag) { + ahints.ai_socktype = SOCK_DGRAM; + ahints.ai_protocol = IPPROTO_UDP; + + } +# if defined(IPPROTO_DCCP) && defined(SOCK_DCCP) + else if (dccpflag) { + hints.ai_socktype = SOCK_DCCP; + hints.ai_protocol = IPPROTO_DCCP; + } +# endif + else { + ahints.ai_socktype = SOCK_STREAM; + ahints.ai_protocol = IPPROTO_TCP; + } + ahints.ai_flags = AI_PASSIVE; + if ((error = getaddrinfo(sflag, pflag, &ahints, &ares))) + errx(1, "getaddrinfo: %s", gai_strerror(error)); + + if (bind(s, (struct sockaddr *)ares->ai_addr, + ares->ai_addrlen) == -1) + err(1, "bind failed"); + freeaddrinfo(ares); + } + + set_common_sockopts(s, res->ai_addr); + + if (ipaddr != NULL) { + herr = getnameinfo(res->ai_addr, res->ai_addrlen, + ipaddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + switch (herr) { + case 0: + break; + case EAI_SYSTEM: + err(1, "getnameinfo"); + default: + errx(1, "getnameinfo: %s", gai_strerror(herr)); + } + } + + if ((error = connect_with_timeout(s, res->ai_addr, res->ai_addrlen, + timeout)) == CONNECTION_SUCCESS) + break; + + char *proto = proto_name(uflag, dccpflag); + + if (vflag) { + /* only print IP if there is something to report */ + if (nflag || ipaddr == NULL || + (strncmp(host, ipaddr, NI_MAXHOST) == 0)) + warn("connect to %s port %s (%s) %s", host, + port, proto, + error == CONNECTION_TIMEOUT ? "timed out" : "failed"); + else + warn("connect to %s (%s) port %s (%s) %s", + host, ipaddr, port, proto, + error == CONNECTION_TIMEOUT ? "timed out" : "failed"); + } + + save_errno = errno; + close(s); + errno = save_errno; + s = -1; + } + + freeaddrinfo(res0); + + return s; +} + +int +timeout_connect(int s, const struct sockaddr *name, socklen_t namelen) +{ + struct pollfd pfd; + socklen_t optlen; + int optval; + int ret; + + if ((ret = connect(s, name, namelen)) != 0 && errno == EINPROGRESS) { + pfd.fd = s; + pfd.events = POLLOUT; + if ((ret = poll(&pfd, 1, timeout)) == 1) { + optlen = sizeof(optval); + if ((ret = getsockopt(s, SOL_SOCKET, SO_ERROR, + &optval, &optlen)) == 0) { + errno = optval; + ret = optval == 0 ? 0 : -1; + } + } else if (ret == 0) { + errno = ETIMEDOUT; + ret = -1; + } else + err(1, "poll failed"); + } + + return ret; +} + +static int connect_with_timeout(int fd, const struct sockaddr *sa, + socklen_t salen, int ctimeout) +{ + int err; + struct timeval tv, *tvp = NULL; + fd_set connect_fdset; + socklen_t len; + int orig_flags; + + orig_flags = fcntl(fd, F_GETFL, 0); + if (fcntl(fd, F_SETFL, orig_flags | O_NONBLOCK) < 0 ) { + warn("can't set O_NONBLOCK - timeout not available"); + if (connect(fd, sa, salen) == 0) + return CONNECTION_SUCCESS; + else + return CONNECTION_FAILED; + } + + /* set connect timeout */ + if (ctimeout > 0) { + tv.tv_sec = (time_t)ctimeout/1000; + tv.tv_usec = 0; + tvp = &tv; + } + + /* attempt the connection */ + err = connect(fd, sa, salen); + if (err != 0 && errno == EINPROGRESS) { + /* connection is proceeding + * it is complete (or failed) when select returns */ + + /* initialize connect_fdset */ + FD_ZERO(&connect_fdset); + FD_SET(fd, &connect_fdset); + + /* call select */ + do { + err = select(fd + 1, NULL, &connect_fdset, + NULL, tvp); + } while (err < 0 && errno == EINTR); + + /* select error */ + if (err < 0) + errx(1,"select error: %s", strerror(errno)); + /* we have reached a timeout */ + if (err == 0) + return CONNECTION_TIMEOUT; + /* select returned successfully, but we must test socket + * error for result */ + len = sizeof(err); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) + errx(1, "getsockopt error: %s", strerror(errno)); + /* setup errno according to the result returned by + * getsockopt */ + if (err != 0) + errno = err; + } + + /* return aborted if an error occured, and valid otherwise */ + fcntl(fd, F_SETFL, orig_flags); + return (err != 0)? CONNECTION_FAILED : CONNECTION_SUCCESS; +} + +/* + * local_listen() + * Returns a socket listening on a local port, binds to specified source + * address. Returns -1 on failure. + */ +int +local_listen(const char *host, const char *port, struct addrinfo hints) +{ + struct addrinfo *res, *res0; + int s = -1, ret, x = 1, save_errno; + int error; + + /* Allow nodename to be null. */ + hints.ai_flags |= AI_PASSIVE; + + /* + * In the case of binding to a wildcard address + * default to binding to an ipv4 address. + */ + if (host == NULL && hints.ai_family == AF_UNSPEC) + hints.ai_family = AF_INET; + + if ((error = getaddrinfo(host, port, &hints, &res0))) + errx(1, "getaddrinfo: %s", gai_strerror(error)); + + for (res = res0; res; res = res->ai_next) { + if ((s = socket(res->ai_family, res->ai_socktype, + res->ai_protocol)) == -1) + continue; + + ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); + if (ret == -1) + err(1, NULL); + +# if defined(SO_REUSEPORT) + ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof(x)); + if (ret == -1) + err(1, NULL); +# endif + + set_common_sockopts(s, res->ai_addr); + + if (bind(s, (struct sockaddr *)res->ai_addr, + res->ai_addrlen) == 0) + break; + + save_errno = errno; + close(s); + errno = save_errno; + s = -1; + } + + if (!uflag && s != -1) { + if (listen(s, 1) == -1) + err(1, "listen"); + } + if (vflag && s != -1) { + struct sockaddr_storage ss; + socklen_t len; + + len = sizeof(ss); + if (getsockname(s, (struct sockaddr *)&ss, &len) == -1) + err(1, "getsockname"); + report_sock(uflag ? "Bound" : "Listening", + (struct sockaddr *)&ss, len, NULL); + } + + freeaddrinfo(res0); + + return s; +} + +/* + * readwrite() + * Loop that polls on the network file descriptor and stdin. + */ +void +# if defined(TLS) +readwrite(int net_fd, struct tls *tls_ctx) +# else +readwrite(int net_fd) +# endif +{ + struct pollfd pfd[4]; + int stdin_fd = STDIN_FILENO; + int stdout_fd = STDOUT_FILENO; + unsigned char netinbuf[BUFSIZE]; + size_t netinbufpos = 0; + unsigned char stdinbuf[BUFSIZE]; + size_t stdinbufpos = 0; + int n, num_fds; + ssize_t ret; + + /* don't read from stdin if requested */ + if (dflag) + stdin_fd = -1; + + /* stdin */ + pfd[POLL_STDIN].fd = stdin_fd; + pfd[POLL_STDIN].events = POLLIN; + + /* network out */ + pfd[POLL_NETOUT].fd = net_fd; + pfd[POLL_NETOUT].events = 0; + + /* network in */ + pfd[POLL_NETIN].fd = net_fd; + pfd[POLL_NETIN].events = POLLIN; + + /* stdout */ + pfd[POLL_STDOUT].fd = stdout_fd; + pfd[POLL_STDOUT].events = 0; + + while (1) { + /* both inputs are gone, buffers are empty, we are done */ + if (pfd[POLL_STDIN].fd == -1 && pfd[POLL_NETIN].fd == -1 && + stdinbufpos == 0 && netinbufpos == 0) { + if (qflag <= 0) + return; + goto delay_exit; + } + /* both outputs are gone, we can't continue */ + if (pfd[POLL_NETOUT].fd == -1 && pfd[POLL_STDOUT].fd == -1) { + if (qflag <= 0) + return; + goto delay_exit; + } + /* listen and net in gone, queues empty, done */ + if (lflag && pfd[POLL_NETIN].fd == -1 && + stdinbufpos == 0 && netinbufpos == 0) { + if (qflag <= 0) + return; +delay_exit: + close(net_fd); + signal(SIGALRM, quit); + alarm(qflag); + } + + /* poll */ + num_fds = poll(pfd, 4, timeout); + + /* treat poll errors */ + if (num_fds == -1) + err(1, "polling error"); + + /* timeout happened */ + if (num_fds == 0) + return; + + /* treat socket error conditions */ + for (n = 0; n < 4; n++) { + if (pfd[n].revents & (POLLERR|POLLNVAL)) { + pfd[n].fd = -1; + } + } + /* reading is possible after HUP */ + if (pfd[POLL_STDIN].events & POLLIN && + pfd[POLL_STDIN].revents & POLLHUP && + !(pfd[POLL_STDIN].revents & POLLIN)) + pfd[POLL_STDIN].fd = -1; + + if (pfd[POLL_NETIN].events & POLLIN && + pfd[POLL_NETIN].revents & POLLHUP && + !(pfd[POLL_NETIN].revents & POLLIN)) + pfd[POLL_NETIN].fd = -1; + + if (pfd[POLL_NETOUT].revents & POLLHUP) { + if (Nflag) + shutdown(pfd[POLL_NETOUT].fd, SHUT_WR); + pfd[POLL_NETOUT].fd = -1; + } + /* if HUP, stop watching stdout */ + if (pfd[POLL_STDOUT].revents & POLLHUP) + pfd[POLL_STDOUT].fd = -1; + /* if no net out, stop watching stdin */ + if (pfd[POLL_NETOUT].fd == -1) + pfd[POLL_STDIN].fd = -1; + /* if no stdout, stop watching net in */ + if (pfd[POLL_STDOUT].fd == -1) { + if (pfd[POLL_NETIN].fd != -1) + shutdown(pfd[POLL_NETIN].fd, SHUT_RD); + pfd[POLL_NETIN].fd = -1; + } + + /* try to read from stdin */ + if (pfd[POLL_STDIN].revents & POLLIN && stdinbufpos < BUFSIZE) { + ret = fillbuf(pfd[POLL_STDIN].fd, stdinbuf, +# if defined(TLS) + &stdinbufpos, NULL); + if (ret == TLS_WANT_POLLIN) + pfd[POLL_STDIN].events = POLLIN; + else if (ret == TLS_WANT_POLLOUT) + pfd[POLL_STDIN].events = POLLOUT; + else +# else + &stdinbufpos); +# endif + if (ret == 0 || ret == -1) + pfd[POLL_STDIN].fd = -1; + /* read something - poll net out */ + if (stdinbufpos > 0) + pfd[POLL_NETOUT].events = POLLOUT; + /* filled buffer - remove self from polling */ + if (stdinbufpos == BUFSIZE) + pfd[POLL_STDIN].events = 0; + } + /* try to write to network */ + if (pfd[POLL_NETOUT].revents & POLLOUT && stdinbufpos > 0) { + ret = drainbuf(pfd[POLL_NETOUT].fd, stdinbuf, +# if defined(TLS) + &stdinbufpos, tls_ctx); + if (ret == TLS_WANT_POLLIN) + pfd[POLL_NETOUT].events = POLLIN; + else if (ret == TLS_WANT_POLLOUT) + pfd[POLL_NETOUT].events = POLLOUT; + else +# else + &stdinbufpos, (iflag || Cflag) ? 1 : 0); +# endif + if (ret == -1) + pfd[POLL_NETOUT].fd = -1; + /* buffer empty - remove self from polling */ + if (stdinbufpos == 0) + pfd[POLL_NETOUT].events = 0; + /* buffer no longer full - poll stdin again */ + if (stdinbufpos < BUFSIZE) + pfd[POLL_STDIN].events = POLLIN; + } + /* try to read from network */ + if (pfd[POLL_NETIN].revents & POLLIN && netinbufpos < BUFSIZE) { + ret = fillbuf(pfd[POLL_NETIN].fd, netinbuf, +# if defined(TLS) + &netinbufpos, tls_ctx); + if (ret == TLS_WANT_POLLIN) + pfd[POLL_NETIN].events = POLLIN; + else if (ret == TLS_WANT_POLLOUT) + pfd[POLL_NETIN].events = POLLOUT; + else +# else + &netinbufpos); +# endif + if (ret == -1) + pfd[POLL_NETIN].fd = -1; + /* eof on net in - remove from pfd */ + if (ret == 0) { + shutdown(pfd[POLL_NETIN].fd, SHUT_RD); + pfd[POLL_NETIN].fd = -1; + } + if (recvlimit > 0 && ++recvcount >= recvlimit) { + if (pfd[POLL_NETIN].fd != -1) + shutdown(pfd[POLL_NETIN].fd, SHUT_RD); + pfd[POLL_NETIN].fd = -1; + pfd[POLL_STDIN].fd = -1; + } + /* read something - poll stdout */ + if (netinbufpos > 0) + pfd[POLL_STDOUT].events = POLLOUT; + /* filled buffer - remove self from polling */ + if (netinbufpos == BUFSIZE) + pfd[POLL_NETIN].events = 0; + /* handle telnet */ + if (tflag) + atelnet(pfd[POLL_NETIN].fd, netinbuf, + netinbufpos); + } + /* try to write to stdout */ + if (pfd[POLL_STDOUT].revents & POLLOUT && netinbufpos > 0) { + ret = drainbuf(pfd[POLL_STDOUT].fd, netinbuf, +# if defined(TLS) + &netinbufpos, NULL); + if (ret == TLS_WANT_POLLIN) + pfd[POLL_STDOUT].events = POLLIN; + else if (ret == TLS_WANT_POLLOUT) + pfd[POLL_STDOUT].events = POLLOUT; + else +# else + &netinbufpos, 0); +# endif + if (ret == -1) + pfd[POLL_STDOUT].fd = -1; + /* buffer empty - remove self from polling */ + if (netinbufpos == 0) + pfd[POLL_STDOUT].events = 0; + /* buffer no longer full - poll net in again */ + if (netinbufpos < BUFSIZE) + pfd[POLL_NETIN].events = POLLIN; + } + + /* stdin gone and queue empty? */ + if (pfd[POLL_STDIN].fd == -1 && stdinbufpos == 0) { + if (pfd[POLL_NETOUT].fd != -1 && Nflag) + shutdown(pfd[POLL_NETOUT].fd, SHUT_WR); + pfd[POLL_NETOUT].fd = -1; + /* #817050: handle UDP sockets and kflag */ + if ((lflag || uflag) && pfd[POLL_NETIN].fd != -1 && + qflag >= 0 && netinbufpos == 0) { + shutdown(pfd[POLL_NETIN].fd, SHUT_RD); + pfd[POLL_NETIN].fd = -1; + kflag = 0; + } + } + /* net in gone and queue empty? */ + if (pfd[POLL_NETIN].fd == -1 && netinbufpos == 0) { + pfd[POLL_STDOUT].fd = -1; + } + } +} + +ssize_t +drainbuf(int fd, unsigned char *buf, size_t *bufpos, int oneline) +{ + ssize_t n, r; + ssize_t adjust; + unsigned char *lf = NULL; + + if (oneline) + lf = memchr(buf, '\n', *bufpos); + if (lf == NULL) { + n = *bufpos; + oneline = 0; + } + else if (Cflag && (lf == buf || buf[lf - buf - 1] != '\r')) { + n = lf - buf; + oneline = 2; + } + else + n = lf - buf + 1; + if (n > 0) + n = write(fd, buf, n); + + /* don't treat EAGAIN, EINTR as error */ + if (n == -1 && (errno == EAGAIN || errno == EINTR)) + n = -2; + if (oneline == 2 && n >= 0) + n++; + if (n <= 0) + return n; + + if (oneline == 2 && (r = atomicio(vwrite, fd, "\r\n", 2)) != 2) + err(1, "write failed (%zu/2)", r); + if (oneline > 0 && iflag) + sleep(iflag); + + /* adjust buffer */ + adjust = *bufpos - n; + if (adjust > 0) + memmove(buf, buf + n, adjust); + *bufpos -= n; + return n; +} + +ssize_t +# if defined(TLS) +fillbuf(int fd, unsigned char *buf, size_t *bufpos, struct tls *tls) +# else +fillbuf(int fd, unsigned char *buf, size_t *bufpos) +# endif +{ + size_t num = BUFSIZE - *bufpos; + ssize_t n; + +# if defined(TLS) + if (tls) { + n = tls_read(tls, buf + *bufpos, num); + if (n == -1) + errx(1, "tls read failed (%s)", tls_error(tls)); + } else { +# endif + n = read(fd, buf + *bufpos, num); + /* don't treat EAGAIN, EINTR as error */ + if (n == -1 && (errno == EAGAIN || errno == EINTR)) +# if defined(TLS) + n = TLS_WANT_POLLIN; + } +# else + n = -2; +# endif + if (n <= 0) + return n; + *bufpos += n; + return n; +} + +/* + * fdpass() + * Pass the connected file descriptor to stdout and exit. + */ +void +fdpass(int nfd) +{ + struct msghdr mh; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(int))]; + } cmsgbuf; + struct cmsghdr *cmsg; + struct iovec iov; + char c = '\0'; + ssize_t r; + struct pollfd pfd; + + /* Avoid obvious stupidity */ + if (isatty(STDOUT_FILENO)) + errx(1, "Cannot pass file descriptor to tty"); + + memset(&mh, 0, sizeof(mh)); + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); + memset(&iov, 0, sizeof(iov)); + + mh.msg_control = (caddr_t)&cmsgbuf.buf; + mh.msg_controllen = sizeof(cmsgbuf.buf); + cmsg = CMSG_FIRSTHDR(&mh); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *(int *)CMSG_DATA(cmsg) = nfd; + + iov.iov_base = &c; + iov.iov_len = 1; + mh.msg_iov = &iov; + mh.msg_iovlen = 1; + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = STDOUT_FILENO; + pfd.events = POLLOUT; + for (;;) { + r = sendmsg(STDOUT_FILENO, &mh, 0); + if (r == -1) { + if (errno == EAGAIN || errno == EINTR) { + if (poll(&pfd, 1, -1) == -1) + err(1, "poll"); + continue; + } + err(1, "sendmsg"); + } else if (r != 1) + errx(1, "sendmsg: unexpected return value %zd", r); + else + break; + } + exit(0); +} + +/* Deal with RFC 854 WILL/WONT DO/DONT negotiation. */ +void +atelnet(int nfd, unsigned char *buf, unsigned int size) +{ + unsigned char *p, *end; + unsigned char obuf[4]; + + if (size < 3) + return; + end = buf + size - 2; + + for (p = buf; p < end; p++) { + if (*p != IAC) + continue; + + obuf[0] = IAC; + p++; + if ((*p == WILL) || (*p == WONT)) + obuf[1] = DONT; + else if ((*p == DO) || (*p == DONT)) + obuf[1] = WONT; + else + continue; + + p++; + obuf[2] = *p; + if (atomicio(vwrite, nfd, obuf, 3) != 3) + warn("Write Error!"); + } +} + + +int +strtoport(char *portstr, int udp) +{ + struct servent *entry; + const char *errstr; + char *proto; + int port = -1; + + proto = udp ? "udp" : "tcp"; + + port = strtonum(portstr, 1, PORT_MAX, &errstr); + if (errstr == NULL) + return port; + if (errno != EINVAL) + errx(1, "port number %s: %s", errstr, portstr); + if ((entry = getservbyname(portstr, proto)) == NULL) + errx(1, "service \"%s\" unknown", portstr); + return ntohs(entry->s_port); +} + +/* + * build_ports() + * Build an array of ports in portlist[], listing each port + * that we should try to connect to. + */ +void +build_ports(char **p) +{ + struct servent *sv; + char *n; + int hi, lo, cp; + int x = 0; + int i; + + char *proto = proto_name(uflag, dccpflag); + for (i = 0; p[i] != NULL; i++) { + sv = getservbyname(p[i], proto); + if (sv) { + if (asprintf(&portlist[x], "%d", ntohs(sv->s_port)) < 0) + err(1, "asprintf"); + x++; + } else if (isdigit((unsigned char)*p[i]) && (n = strchr(p[i], '-')) != NULL) { + *n = '\0'; + n++; + + /* Make sure the ports are in order: lowest->highest. */ + hi = strtoport(n, uflag); + lo = strtoport(p[i], uflag); + if (lo > hi) { + cp = hi; + hi = lo; + lo = cp; + } + + /* Load ports sequentially. */ + for (cp = lo; cp <= hi; cp++) { + if (asprintf(&portlist[x], "%d", cp) == -1) + err(1, "asprintf"); + x++; + } + } else { + hi = strtoport(p[i], uflag); + if (asprintf(&portlist[x], "%d", hi) < 0) + err(1, "asprintf"); + x++; + } + } + + /* + * Initialize portlist with a random permutation using + * Fisher–Yates shuffle. + */ + if (rflag) { + for (i = x-1; i > 0; i--) { + cp = arc4random_uniform(i+1); + if (cp != i) { + n = portlist[i]; + portlist[i] = portlist[cp]; + portlist[cp] = n; + } + } + } +} + +/* + * udptest() + * Do a few writes to see if the UDP port is there. + * Fails once PF state table is full. + */ +int +udptest(int s) +{ + int i, t; + + if ((write(s, "X", 1) != 1) || + ((write(s, "X", 1) != 1) && (errno == ECONNREFUSED))) + return -1; + + /* Give the remote host some time to reply. */ + for (i = 0, t = (timeout == -1) ? UDP_SCAN_TIMEOUT : (timeout / 1000); + i < t; i++) { + sleep(1); + if ((write(s, "X", 1) != 1) && (errno == ECONNREFUSED)) + return -1; + } + return 1; +} + +void +set_common_sockopts(int s, const struct sockaddr* sa) +{ + int x = 1; + int af = sa->sa_family; + +# if defined(SO_BROADCAST) + if (bflag) { + /* allow datagram sockets to send packets to a broadcast address + * (this option has no effect on stream-oriented sockets) */ + if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, + &x, sizeof(x)) == -1) + err(1, NULL); + } +# endif +# if defined(TCP_MD5SIG_EXT) && defined(TCP_MD5SIG_MAXKEYLEN) + if (Sflag) { + struct tcp_md5sig sig; + memset(&sig, 0, sizeof(sig)); + memcpy(&sig.tcpm_addr, sa, sizeof(struct sockaddr_storage)); + sig.tcpm_keylen = TCP_MD5SIG_MAXKEYLEN < strlen(Sflag_password) + ? TCP_MD5SIG_MAXKEYLEN + : strlen(Sflag_password); + memcpy(sig.tcpm_key, Sflag_password, sig.tcpm_keylen); + sig.tcpm_flags = TCP_MD5SIG_FLAG_PREFIX; + if (setsockopt(s, IPPROTO_TCP, TCP_MD5SIG_EXT, + &sig, sizeof(sig)) == -1) + err(1, NULL); + } +# endif + if (Dflag) { + if (setsockopt(s, SOL_SOCKET, SO_DEBUG, + &x, sizeof(x)) == -1) + err(1, NULL); + } + if (Tflag != -1) { + if (af == AF_INET && setsockopt(s, IPPROTO_IP, + IP_TOS, &Tflag, sizeof(Tflag)) == -1) + err(1, "set IP ToS"); + +#if defined(IPV6_TCLASS) + else if (af == AF_INET6 && setsockopt(s, IPPROTO_IPV6, + IPV6_TCLASS, &Tflag, sizeof(Tflag)) == -1) + err(1, "set IPv6 traffic class"); +#else + else if (af == AF_INET6) + errx(1, "can't set IPv6 traffic class (unavailable)"); +#endif + } + if (Iflag) { + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, + &Iflag, sizeof(Iflag)) == -1) + err(1, "set TCP receive buffer size"); + } + if (Oflag) { + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, + &Oflag, sizeof(Oflag)) == -1) + err(1, "set TCP send buffer size"); + } + + if (ttl != -1) { + if (af == AF_INET && setsockopt(s, IPPROTO_IP, + IP_TTL, &ttl, sizeof(ttl))) + err(1, "set IP TTL"); + +#if defined(IPV6_UNICAST_HOPS) + else if (af == AF_INET6 && setsockopt(s, IPPROTO_IPV6, + IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) + err(1, "set IPv6 unicast hops"); +#else + else if (af == AF_INET6) + errx(1, "can't set IPv6 unicast hops (unavailable)"); +#endif + } + + if (minttl != -1) { +#if defined(IP_MINTTL) + if (af == AF_INET && setsockopt(s, IPPROTO_IP, + IP_MINTTL, &minttl, sizeof(minttl))) + err(1, "set IP min TTL"); +#else + if (af == AF_INET) + errx(1, "can't set IP min TTL (unavailable)"); +#endif + +#if defined(IPV6_MINHOPCOUNT) + else if (af == AF_INET6 && setsockopt(s, IPPROTO_IPV6, + IPV6_MINHOPCOUNT, &minttl, sizeof(minttl))) + err(1, "set IPv6 min hop count"); +#else + else if (af == AF_INET6) + errx(1, "can't set IPv6 min hop count (unavailable)"); +#endif + } +} + +int +process_tos_opt(char *s, int *val) +{ + /* DiffServ Codepoints and other TOS mappings */ + const struct toskeywords { + const char *keyword; + int val; + } *t, toskeywords[] = { + { "af11", IPTOS_DSCP_AF11 }, + { "af12", IPTOS_DSCP_AF12 }, + { "af13", IPTOS_DSCP_AF13 }, + { "af21", IPTOS_DSCP_AF21 }, + { "af22", IPTOS_DSCP_AF22 }, + { "af23", IPTOS_DSCP_AF23 }, + { "af31", IPTOS_DSCP_AF31 }, + { "af32", IPTOS_DSCP_AF32 }, + { "af33", IPTOS_DSCP_AF33 }, + { "af41", IPTOS_DSCP_AF41 }, + { "af42", IPTOS_DSCP_AF42 }, + { "af43", IPTOS_DSCP_AF43 }, + { "critical", IPTOS_PREC_CRITIC_ECP }, + { "cs0", IPTOS_DSCP_CS0 }, + { "cs1", IPTOS_DSCP_CS1 }, + { "cs2", IPTOS_DSCP_CS2 }, + { "cs3", IPTOS_DSCP_CS3 }, + { "cs4", IPTOS_DSCP_CS4 }, + { "cs5", IPTOS_DSCP_CS5 }, + { "cs6", IPTOS_DSCP_CS6 }, + { "cs7", IPTOS_DSCP_CS7 }, + { "ef", IPTOS_DSCP_EF }, + { "inetcontrol", IPTOS_PREC_INTERNETCONTROL }, + { "lowcost", IPTOS_LOWCOST }, + { "lowdelay", IPTOS_LOWDELAY }, + { "netcontrol", IPTOS_PREC_NETCONTROL }, + { "reliability", IPTOS_RELIABILITY }, + { "throughput", IPTOS_THROUGHPUT }, + { NULL, -1 }, + }; + + for (t = toskeywords; t->keyword != NULL; t++) { + if (strcmp(s, t->keyword) == 0) { + *val = t->val; + return 1; + } + } + + return 0; +} + +# if defined(TLS) +int +process_tls_opt(char *s, int *flags) +{ + size_t len; + char *v; + + const struct tlskeywords { + const char *keyword; + int flag; + char **value; + } *t, tlskeywords[] = { + { "ciphers", -1, &tls_ciphers }, + { "clientcert", TLS_CCERT, NULL }, + { "muststaple", TLS_MUSTSTAPLE, NULL }, + { "noverify", TLS_NOVERIFY, NULL }, + { "noname", TLS_NONAME, NULL }, + { "protocols", -1, &tls_protocols }, + { NULL, -1, NULL }, + }; + + len = strlen(s); + if ((v = strchr(s, '=')) != NULL) { + len = v - s; + v++; + } + + for (t = tlskeywords; t->keyword != NULL; t++) { + if (strlen(t->keyword) == len && + strncmp(s, t->keyword, len) == 0) { + if (t->value != NULL) { + if (v == NULL) + errx(1, "invalid tls value `%s'", s); + *t->value = v; + } else { + *flags |= t->flag; + } + return 1; + } + } + return 0; +} + +void +save_peer_cert(struct tls *tls_ctx, FILE *fp) +{ + const char *pem; + size_t plen; + + if ((pem = tls_peer_cert_chain_pem(tls_ctx, &plen)) == NULL) + errx(1, "Can't get peer certificate"); + if (fprintf(fp, "%.*s", (int)plen, pem) < 0) + err(1, "unable to save peer cert"); + if (fflush(fp) != 0) + err(1, "unable to flush peer cert"); +} + +void +report_tls(struct tls * tls_ctx, char * host) +{ + time_t t; + const char *ocsp_url; + + fprintf(stderr, "TLS handshake negotiated %s/%s with host %s\n", + tls_conn_version(tls_ctx), tls_conn_cipher(tls_ctx), host); + fprintf(stderr, "Peer name: %s\n", + tls_expectname ? tls_expectname : host); + if (tls_peer_cert_subject(tls_ctx)) + fprintf(stderr, "Subject: %s\n", + tls_peer_cert_subject(tls_ctx)); + if (tls_peer_cert_issuer(tls_ctx)) + fprintf(stderr, "Issuer: %s\n", + tls_peer_cert_issuer(tls_ctx)); + if ((t = tls_peer_cert_notbefore(tls_ctx)) != -1) + fprintf(stderr, "Valid From: %s", ctime(&t)); + if ((t = tls_peer_cert_notafter(tls_ctx)) != -1) + fprintf(stderr, "Valid Until: %s", ctime(&t)); + if (tls_peer_cert_hash(tls_ctx)) + fprintf(stderr, "Cert Hash: %s\n", + tls_peer_cert_hash(tls_ctx)); + ocsp_url = tls_peer_ocsp_url(tls_ctx); + if (ocsp_url != NULL) + fprintf(stderr, "OCSP URL: %s\n", ocsp_url); + switch (tls_peer_ocsp_response_status(tls_ctx)) { + case TLS_OCSP_RESPONSE_SUCCESSFUL: + fprintf(stderr, "OCSP Stapling: %s\n", + tls_peer_ocsp_result(tls_ctx) == NULL ? "" : + tls_peer_ocsp_result(tls_ctx)); + fprintf(stderr, + " response_status=%d cert_status=%d crl_reason=%d\n", + tls_peer_ocsp_response_status(tls_ctx), + tls_peer_ocsp_cert_status(tls_ctx), + tls_peer_ocsp_crl_reason(tls_ctx)); + t = tls_peer_ocsp_this_update(tls_ctx); + fprintf(stderr, " this update: %s", + t != -1 ? ctime(&t) : "\n"); + t = tls_peer_ocsp_next_update(tls_ctx); + fprintf(stderr, " next update: %s", + t != -1 ? ctime(&t) : "\n"); + t = tls_peer_ocsp_revocation_time(tls_ctx); + fprintf(stderr, " revocation: %s", + t != -1 ? ctime(&t) : "\n"); + break; + case -1: + break; + default: + fprintf(stderr, "OCSP Stapling: failure - response_status %d (%s)\n", + tls_peer_ocsp_response_status(tls_ctx), + tls_peer_ocsp_result(tls_ctx) == NULL ? "" : + tls_peer_ocsp_result(tls_ctx)); + break; + + } +} +# endif + +void +report_sock(const char *msg, const struct sockaddr *sa, socklen_t salen, + char *path) +{ + char host[NI_MAXHOST], port[NI_MAXSERV]; + int herr; + int flags = NI_NUMERICSERV; + + if (path != NULL) { + fprintf(stderr, "%s on %s\n", msg, path); + return; + } + + if (nflag) + flags |= NI_NUMERICHOST; + + herr = getnameinfo(sa, salen, host, sizeof(host), port, sizeof(port), + flags); + switch (herr) { + case 0: + break; + case EAI_SYSTEM: + warn("getnameinfo"); + return; + default: + warnx("getnameinfo: %s", gai_strerror(herr)); + return; + } + + fprintf(stderr, "%s on %s %s\n", msg, host, port); +} + +void +help(void) +{ +# if defined(DEBIAN_VERSION) + fprintf(stderr, "OpenBSD netcat (Debian patchlevel " DEBIAN_VERSION ")\n"); +# endif + usage(0); + fprintf(stderr, "\tCommand Summary:\n\ + \t-4 Use IPv4\n\ + \t-6 Use IPv6\n\ + \t-b Allow broadcast\n\ + \t-C Send CRLF as line-ending\n\ + \t-D Enable the debug socket option\n\ + \t-d Detach from stdin\n\ + \t-F Pass socket fd\n\ + \t-h This help text\n\ + \t-I length TCP receive buffer length\n\ + \t-i interval Delay interval for lines sent, ports scanned\n\ + \t-k Keep inbound sockets open for multiple connects\n\ + \t-l Listen mode, for inbound connects\n\ + \t-M ttl Outgoing TTL / Hop Limit\n\ + \t-m minttl Minimum incoming TTL / Hop Limit\n\ + \t-N Shutdown the network socket after EOF on stdin\n\ + \t-n Suppress name/port resolutions\n\ + \t-O length TCP send buffer length\n\ + \t-P proxyuser\tUsername for proxy authentication\n\ + \t-p port\t Specify local port for remote connects\n\ + \t-q secs\t quit after EOF on stdin and delay of secs\n\ + \t-r Randomize remote ports\n\ + \t-S Enable the TCP MD5 signature option\n\ + \t-s sourceaddr Local source address\n\ + \t-T keyword TOS value\n\ + \t-t Answer TELNET negotiation\n\ + \t-U Use UNIX domain socket\n\ + \t-u UDP mode\n\ + \t-V rtable Specify alternate routing table\n\ + \t-v Verbose\n\ + \t-W recvlimit Terminate after receiving a number of packets\n\ + \t-w timeout Timeout for connects and final net reads\n\ + \t-X proto Proxy protocol: \"4\", \"5\" (SOCKS) or \"connect\"\n\ + \t-x addr[:port]\tSpecify proxy address and port\n\ + \t-Z DCCP mode\n\ + \t-z Zero-I/O mode [used for scanning]\n\ + Port numbers can be individual or ranges: lo-hi [inclusive]\n"); + exit(0); +} + +void +usage(int ret) +{ + fprintf(stderr, + "usage: nc [-46CDdFhklNnrStUuvZz] [-I length] [-i interval] [-M ttl]\n" + "\t [-m minttl] [-O length] [-P proxy_username] [-p source_port]\n" + "\t [-q seconds] [-s sourceaddr] [-T keyword] [-V rtable] [-W recvlimit]\n" + "\t [-w timeout] [-X proxy_protocol] [-x proxy_address[:port]]\n" + "\t [destination] [port]\n"); + if (ret) + exit(1); +} + +/* + * quit() + * handler for a "-q" timeout (exit 0 instead of 1) + */ +static void quit() +{ + exit(0); +} diff --git a/package/netcat-openbsd/src/socks.c b/package/netcat-openbsd/src/socks.c new file mode 100644 index 000000000..58fd5765b --- /dev/null +++ b/package/netcat-openbsd/src/socks.c @@ -0,0 +1,400 @@ +/* $OpenBSD: socks.c,v 1.30 2019/11/04 17:33:28 millert 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 + * 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. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "atomicio.h" + +#define SOCKS_PORT "1080" +#define HTTP_PROXY_PORT "3128" +#define HTTP_MAXHDRS 64 +#define SOCKS_V5 5 +#define SOCKS_V4 4 +#define SOCKS_NOAUTH 0 +#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, char *); +int socks_connect(const char *, const char *, struct addrinfo, + const char *, const char *, struct addrinfo, int, + const char *); + +static int +decode_addrport(const char *h, const char *p, struct sockaddr *addr, + socklen_t addrlen, int v4only, int numeric) +{ + int r; + struct addrinfo hints, *res; + + memset(&hints, 0, 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"); + } + memcpy(addr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + return (0); +} + +static int +proxy_read_line(int fd, char *buf, size_t bufsz) +{ + size_t off; + + for(off = 0;;) { + if (off >= bufsz) + errx(1, "proxy read too long"); + if (atomicio(read, fd, buf + off, 1) != 1) + err(1, "proxy read"); + /* Skip CR */ + if (buf[off] == '\r') + continue; + if (buf[off] == '\n') { + buf[off] = '\0'; + break; + } + off++; + } + return (off); +} + +static void +getproxypass(const char *proxyuser, const char *proxyhost, + char *pw, size_t pwlen) +{ + char prompt[512]; + + snprintf(prompt, sizeof(prompt), "Proxy password for %s@%s: ", + proxyuser, proxyhost); + if (readpassphrase(prompt, pw, pwlen, RPP_REQUIRE_TTY) == NULL) + errx(1, "Unable to read proxy passphrase"); +} + +/* + * Error strings adapted from the generally accepted SOCKSv4 spec: + * + * http://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4.protocol + */ +static const char * +socks4_strerror(int e) +{ + switch (e) { + case 90: + return "Succeeded"; + case 91: + return "Request rejected or failed"; + case 92: + return "SOCKS server cannot connect to identd on the client"; + case 93: + return "Client program and identd report different user-ids"; + default: + return "Unknown error"; + } +} + +/* + * Error strings taken almost directly from RFC 1928. + */ +static const char * +socks5_strerror(int e) +{ + switch (e) { + case 0: + return "Succeeded"; + case 1: + return "General SOCKS server failure"; + case 2: + return "Connection not allowed by ruleset"; + case 3: + return "Network unreachable"; + case 4: + return "Host unreachable"; + case 5: + return "Connection refused"; + case 6: + return "TTL expired"; + case 7: + return "Command not supported"; + case 8: + return "Address type not supported"; + default: + return "Unknown error"; + } +} + +int +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, authretry = 0; + size_t hlen, wlen; + unsigned char buf[1024]; + 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; + + 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, NULL); + + if (proxyfd < 0) + 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 = atomicio(vwrite, proxyfd, buf, 3); + if (cnt != 3) + err(1, "write failed (%zu/3)", (size_t)cnt); + + cnt = atomicio(read, proxyfd, buf, 2); + if (cnt != 2) + err(1, "read failed (%zu/3)", (size_t)cnt); + + if (buf[1] == SOCKS_NOMETHOD) + errx(1, "authentication method negotiation failed"); + + 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"); + } + + cnt = atomicio(vwrite, proxyfd, buf, wlen); + if (cnt != wlen) + err(1, "write failed (%zu/%zu)", (size_t)cnt, (size_t)wlen); + + cnt = atomicio(read, proxyfd, buf, 4); + if (cnt != 4) + err(1, "read failed (%zu/4)", (size_t)cnt); + if (buf[1] != 0) { + errx(1, "connection failed, SOCKSv5 error: %s", + socks5_strerror(buf[1])); + } + switch (buf[3]) { + case SOCKS_IPV4: + cnt = atomicio(read, proxyfd, buf + 4, 6); + if (cnt != 6) + err(1, "read failed (%zu/6)", (size_t)cnt); + break; + case SOCKS_IPV6: + cnt = atomicio(read, proxyfd, buf + 4, 18); + if (cnt != 18) + err(1, "read failed (%zu/18)", (size_t)cnt); + break; + default: + errx(1, "connection failed, unsupported address type"); + } + } 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, &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 = atomicio(vwrite, proxyfd, buf, wlen); + if (cnt != wlen) + err(1, "write failed (%zu/%zu)", (size_t)cnt, (size_t)wlen); + + cnt = atomicio(read, proxyfd, buf, 8); + if (cnt != 8) + err(1, "read failed (%zu/8)", (size_t)cnt); + if (buf[1] != 90) { + errx(1, "connection failed, SOCKSv4 error: %s", + socks4_strerror(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"); + + /* Try to be sane about numeric IPv6 addresses */ + if (strchr(host, ':') != NULL) { + r = snprintf((char*)buf, sizeof(buf), + "CONNECT [%s]:%d HTTP/1.0\r\n", + host, ntohs(serverport)); + } else { + r = snprintf((char*)buf, sizeof(buf), + "CONNECT %s:%d HTTP/1.0\r\n", + host, ntohs(serverport)); + } + if (r < 0 || (size_t)r >= sizeof(buf)) + errx(1, "hostname too long"); + r = strlen((char*)buf); + + cnt = atomicio(vwrite, proxyfd, buf, r); + if (cnt != r) + err(1, "write failed (%zu/%d)", (size_t)cnt, (int)r); + + if (authretry > 1) { + char proxypass[256]; + char resp[1024]; + + getproxypass(proxyuser, proxyhost, + proxypass, sizeof proxypass); + r = snprintf((char*)buf, sizeof(buf), "%s:%s", + proxyuser, proxypass); + explicit_bzero(proxypass, sizeof proxypass); + if (r == -1 || (size_t)r >= sizeof(buf) || + b64_ntop(buf, strlen((char*)buf), resp, + sizeof(resp)) == -1) + errx(1, "Proxy username/password too long"); + r = snprintf((char*)buf, sizeof(buf), "Proxy-Authorization: " + "Basic %s\r\n", resp); + if (r < 0 || (size_t)r >= sizeof(buf)) + errx(1, "Proxy auth response too long"); + r = strlen((char*)buf); + if ((cnt = atomicio(vwrite, proxyfd, buf, r)) != r) + err(1, "write failed (%zu/%d)", (size_t)cnt, r); + explicit_bzero(proxypass, sizeof proxypass); + explicit_bzero(buf, sizeof buf); + } + + /* Terminate headers */ + if ((cnt = atomicio(vwrite, proxyfd, "\r\n", 2)) != 2) + err(1, "write failed (%zu/2)", cnt); + + /* Read status reply */ + proxy_read_line(proxyfd, (char*)buf, sizeof(buf)); + if (proxyuser != NULL && + (strncmp((char*)buf, "HTTP/1.0 407 ", 12) == 0 || + strncmp((char*)buf, "HTTP/1.1 407 ", 12) == 0)) { + if (authretry > 1) { + fprintf(stderr, "Proxy authentication " + "failed\n"); + } + close(proxyfd); + goto again; + } else if (strncmp((char*)buf, "HTTP/1.0 200 ", 12) != 0 && + strncmp((char*)buf, "HTTP/1.1 200 ", 12) != 0) + errx(1, "Proxy error: \"%s\"", buf); + + /* Headers continue until we hit an empty line */ + for (r = 0; r < HTTP_MAXHDRS; r++) { + proxy_read_line(proxyfd, (char*)buf, sizeof(buf)); + if (*buf == '\0') + break; + } + if (*buf != '\0') + errx(1, "Too many proxy headers received"); + } else + errx(1, "Unknown proxy protocol %d", socksv); + + return (proxyfd); +} -- cgit v1.2.3