freebsd-dev/contrib/netcat/netcat.c
Xin LI 0772266e63 MFV r249496,249498. The most visible change is that we no longer shuts
down the connection when stdin closes, by default.  This matches Hobbit's
original netcat and GNU netcat.

Old behavior can be restored with the new -N flag.

MFC after:	2 weeks
2013-04-15 05:31:59 +00:00

1159 lines
26 KiB
C

/* $OpenBSD: netcat.c,v 1.111 2013/03/20 09:27:56 sthen Exp $ */
/*
* Copyright (c) 2001 Eric Jackson <ericj@monkey.org>
*
* 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.
*
* $FreeBSD$
*/
/*
* Re-written nc(1) for OpenBSD. Original implementation by
* *Hobbit* <hobbit@avian.org>.
*/
#include <sys/limits.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#ifdef IPSEC
#include <netipsec/ipsec.h>
#endif
#include <netinet/tcp.h>
#include <netinet/ip.h>
#include <arpa/telnet.h>
#include <err.h>
#include <errno.h>
#include <getopt.h>
#include <netdb.h>
#include <poll.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include "atomicio.h"
#ifndef SUN_LEN
#define SUN_LEN(su) \
(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
#endif
#define PORT_MAX 65535
#define PORT_MAX_LEN 6
#define UNIX_DG_TMP_SOCKET_SIZE 19
/* Command Line Options */
int dflag; /* detached, no stdin */
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 */
int FreeBSD_Oflag; /* Do not use TCP options */
char *Pflag; /* Proxy username */
char *pflag; /* Localport flag */
int rflag; /* Random ports flag */
char *sflag; /* Source Address */
int tflag; /* Telnet Emulation */
int uflag; /* UDP - 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 */
u_int rtableid;
int timeout = -1;
int family = AF_UNSPEC;
char *portlist[PORT_MAX+1];
char *unix_dg_tmp_socket;
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(const char *, const char *, struct addrinfo);
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 unix_connect(char *);
int unix_listen(char *);
void set_common_sockopts(int);
int map_tos(char *, int *);
void report_connect(const struct sockaddr *, socklen_t);
void usage(int);
#ifdef IPSEC
void add_ipsec_policy(int, char *);
char *ipsec_policy[2];
#endif
int
main(int argc, char *argv[])
{
int ch, s, ret, socksv, ipsec_count;
int numfibs;
size_t intsize = sizeof(int);
char *host, *uport;
struct addrinfo hints;
struct servent *sv;
socklen_t len;
struct sockaddr_storage cliaddr;
char *proxy;
const char *errstr, *proxyhost = "", *proxyport = NULL;
struct addrinfo proxyhints;
char unix_dg_tmp_socket_buf[UNIX_DG_TMP_SOCKET_SIZE];
struct option longopts[] = {
{ "no-tcpopt", no_argument, &FreeBSD_Oflag, 1 },
{ NULL, 0, NULL, 0 }
};
ret = 1;
ipsec_count = 0;
s = 0;
socksv = 5;
host = NULL;
uport = NULL;
sv = NULL;
while ((ch = getopt_long(argc, argv,
"46DdEe:hI:i:klNnoO:P:p:rSs:tT:UuV:vw:X:x:z",
longopts, NULL)) != -1) {
switch (ch) {
case '4':
family = AF_INET;
break;
case '6':
family = AF_INET6;
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;
case 'd':
dflag = 1;
break;
case 'e':
#ifdef IPSEC
ipsec_policy[ipsec_count++ % 2] = optarg;
#else
errx(1, "IPsec support unavailable.");
#endif
break;
case 'E':
#ifdef IPSEC
ipsec_policy[0] = "in ipsec esp/transport//require";
ipsec_policy[1] = "out ipsec esp/transport//require";
#else
errx(1, "IPsec support unavailable.");
#endif
break;
case 'h':
help();
break;
case 'i':
iflag = strtonum(optarg, 0, UINT_MAX, &errstr);
if (errstr)
errx(1, "interval %s: %s", errstr, optarg);
break;
case 'k':
kflag = 1;
break;
case 'l':
lflag = 1;
break;
case 'N':
Nflag = 1;
break;
case 'n':
nflag = 1;
break;
case 'o':
fprintf(stderr, "option -o is deprecated.\n");
break;
case 'P':
Pflag = optarg;
break;
case 'p':
pflag = optarg;
break;
case 'r':
rflag = 1;
break;
case 's':
sflag = optarg;
break;
case 't':
tflag = 1;
break;
case 'u':
uflag = 1;
break;
case 'V':
if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1)
errx(1, "Multiple FIBS not supported");
rtableid = (unsigned int)strtonum(optarg, 0,
numfibs - 1, &errstr);
if (errstr)
errx(1, "rtable %s: %s", errstr, optarg);
break;
case 'v':
vflag = 1;
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;
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) {
if (strcmp(errstr, "invalid") != 0)
errx(1, "TCP send window %s: %s",
errstr, optarg);
}
break;
case 'S':
Sflag = 1;
break;
case 'T':
errstr = NULL;
errno = 0;
if (map_tos(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)
errx(1, "illegal tos value %s", optarg);
break;
default:
usage(1);
}
}
argc -= optind;
argv += optind;
/* Cruft to make sure options are clean, and used properly. */
if (argv[0] && !argv[1] && family == AF_UNIX) {
host = argv[0];
uport = NULL;
} else if (argv[0] && !argv[1]) {
if (!lflag)
usage(1);
uport = argv[0];
host = NULL;
} else if (argv[0] && argv[1]) {
host = argv[0];
uport = argv[1];
} else
usage(1);
if (lflag && sflag)
errx(1, "cannot use -s and -l");
if (lflag && pflag)
errx(1, "cannot use -p and -l");
if (lflag && zflag)
errx(1, "cannot use -z and -l");
if (!lflag && kflag)
errx(1, "must use -l with -k");
/* 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 (mktemp(unix_dg_tmp_socket_buf) == NULL)
err(1, "mktemp");
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;
hints.ai_socktype = uflag ? SOCK_DGRAM : SOCK_STREAM;
hints.ai_protocol = uflag ? IPPROTO_UDP : IPPROTO_TCP;
if (nflag)
hints.ai_flags |= AI_NUMERICHOST;
}
if (xflag) {
if (uflag)
errx(1, "no proxy support for UDP mode");
if (lflag)
errx(1, "no proxy support for listen");
if (family == AF_UNIX)
errx(1, "no proxy support for unix sockets");
/* XXX IPv6 transport to proxy would probably work */
if (family == AF_INET6)
errx(1, "no proxy support for IPv6");
if (sflag)
errx(1, "no proxy support for local source address");
proxyhost = strsep(&proxy, ":");
proxyport = proxy;
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 (lflag) {
int connfd;
ret = 0;
if (family == AF_UNIX) {
if (uflag)
s = unix_bind(host);
else
s = unix_listen(host);
}
/* Allow only one connection at a time, but stay alive. */
for (;;) {
if (family != AF_UNIX)
s = local_listen(host, uport, hints);
if (s < 0)
err(1, NULL);
/*
* For UDP and -k, don't connect the socket, let it
* receive datagrams from multiple socket pairs.
*/
if (uflag && kflag)
readwrite(s);
/*
* 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.
*/
else if (uflag && !kflag) {
int rv, plen;
char buf[16384];
struct sockaddr_storage z;
len = sizeof(z);
plen = 2048;
rv = recvfrom(s, buf, plen, MSG_PEEK,
(struct sockaddr *)&z, &len);
if (rv < 0)
err(1, "recvfrom");
rv = connect(s, (struct sockaddr *)&z, len);
if (rv < 0)
err(1, "connect");
if (vflag)
report_connect((struct sockaddr *)&z, len);
readwrite(s);
} else {
len = sizeof(cliaddr);
connfd = accept(s, (struct sockaddr *)&cliaddr,
&len);
if (connfd == -1) {
/* For now, all errnos are fatal */
err(1, "accept");
}
if (vflag)
report_connect((struct sockaddr *)&cliaddr, len);
readwrite(connfd);
close(connfd);
}
if (family != AF_UNIX)
close(s);
else if (uflag) {
if (connect(s, NULL, 0) < 0)
err(1, "connect");
}
if (!kflag)
break;
}
} else if (family == AF_UNIX) {
ret = 0;
if ((s = unix_connect(host)) > 0 && !zflag) {
readwrite(s);
close(s);
} else
ret = 1;
if (uflag)
unlink(unix_dg_tmp_socket);
exit(ret);
} else {
int i = 0;
/* Construct the portlist[] array. */
build_ports(uport);
/* Cycle through portlist, connecting to each port. */
for (i = 0; portlist[i] != NULL; i++) {
if (s)
close(s);
if (xflag)
s = socks_connect(host, portlist[i], hints,
proxyhost, proxyport, proxyhints, socksv,
Pflag);
else
s = remote_connect(host, portlist[i], hints);
if (s < 0)
continue;
ret = 0;
if (vflag || zflag) {
/* For UDP, make sure we are connected. */
if (uflag) {
if (udptest(s) == -1) {
ret = 1;
continue;
}
}
/* Don't look up port if -n. */
if (nflag)
sv = NULL;
else {
sv = getservbyport(
ntohs(atoi(portlist[i])),
uflag ? "udp" : "tcp");
}
fprintf(stderr,
"Connection to %s %s port [%s/%s] "
"succeeded!\n", host, portlist[i],
uflag ? "udp" : "tcp",
sv ? sv->s_name : "*");
}
if (!zflag)
readwrite(s);
}
}
if (s)
close(s);
exit(ret);
}
/*
* unix_bind()
* Returns a unix socket bound to the given path
*/
int
unix_bind(char *path)
{
struct sockaddr_un sun;
int s;
/* Create unix domain socket. */
if ((s = socket(AF_UNIX, uflag ? SOCK_DGRAM : SOCK_STREAM,
0)) < 0)
return (-1);
memset(&sun, 0, sizeof(struct sockaddr_un));
sun.sun_family = AF_UNIX;
if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
sizeof(sun.sun_path)) {
close(s);
errno = ENAMETOOLONG;
return (-1);
}
if (bind(s, (struct sockaddr *)&sun, SUN_LEN(&sun)) < 0) {
close(s);
return (-1);
}
return (s);
}
/*
* unix_connect()
* Returns a socket connected to a local unix socket. Returns -1 on failure.
*/
int
unix_connect(char *path)
{
struct sockaddr_un sun;
int s;
if (uflag) {
if ((s = unix_bind(unix_dg_tmp_socket)) < 0)
return (-1);
} else {
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
return (-1);
}
(void)fcntl(s, F_SETFD, 1);
memset(&sun, 0, sizeof(struct sockaddr_un));
sun.sun_family = AF_UNIX;
if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
sizeof(sun.sun_path)) {
close(s);
errno = ENAMETOOLONG;
return (-1);
}
if (connect(s, (struct sockaddr *)&sun, SUN_LEN(&sun)) < 0) {
close(s);
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)
return (-1);
if (listen(s, 5) < 0) {
close(s);
return (-1);
}
return (s);
}
/*
* 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)
{
struct addrinfo *res, *res0;
int s, error, on = 1;
if ((error = getaddrinfo(host, port, &hints, &res)))
errx(1, "getaddrinfo: %s", gai_strerror(error));
res0 = res;
do {
if ((s = socket(res0->ai_family, res0->ai_socktype,
res0->ai_protocol)) < 0)
continue;
#ifdef IPSEC
if (ipsec_policy[0] != NULL)
add_ipsec_policy(s, ipsec_policy[0]);
if (ipsec_policy[1] != NULL)
add_ipsec_policy(s, ipsec_policy[1]);
#endif
if (rtableid) {
if (setsockopt(s, SOL_SOCKET, SO_SETFIB, &rtableid,
sizeof(rtableid)) == -1)
err(1, "setsockopt(.., SO_SETFIB, %u, ..)",
rtableid);
}
/* Bind to a local port or source address if specified. */
if (sflag || pflag) {
struct addrinfo ahints, *ares;
/* try IP_BINDANY, but don't insist */
setsockopt(s, IPPROTO_IP, IP_BINDANY, &on, sizeof(on));
memset(&ahints, 0, sizeof(struct addrinfo));
ahints.ai_family = res0->ai_family;
ahints.ai_socktype = uflag ? SOCK_DGRAM : SOCK_STREAM;
ahints.ai_protocol = uflag ? IPPROTO_UDP : 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) < 0)
errx(1, "bind failed: %s", strerror(errno));
freeaddrinfo(ares);
}
set_common_sockopts(s);
if (timeout_connect(s, res0->ai_addr, res0->ai_addrlen) == 0)
break;
else if (vflag)
warn("connect to %s port %s (%s) failed", host, port,
uflag ? "udp" : "tcp");
close(s);
s = -1;
} while ((res0 = res0->ai_next) != NULL);
freeaddrinfo(res);
return (s);
}
int
timeout_connect(int s, const struct sockaddr *name, socklen_t namelen)
{
struct pollfd pfd;
socklen_t optlen;
int flags, optval;
int ret;
if (timeout != -1) {
flags = fcntl(s, F_GETFL, 0);
if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1)
err(1, "set non-blocking mode");
}
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");
}
if (timeout != -1 && fcntl(s, F_SETFL, flags) == -1)
err(1, "restoring flags");
return (ret);
}
/*
* local_listen()
* Returns a socket listening on a local port, binds to specified source
* address. Returns -1 on failure.
*/
int
local_listen(char *host, char *port, struct addrinfo hints)
{
struct addrinfo *res, *res0;
int s, ret, x = 1;
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, &res)))
errx(1, "getaddrinfo: %s", gai_strerror(error));
res0 = res;
do {
if ((s = socket(res0->ai_family, res0->ai_socktype,
res0->ai_protocol)) < 0)
continue;
if (rtableid) {
ret = setsockopt(s, SOL_SOCKET, SO_SETFIB, &rtableid,
sizeof(rtableid));
if (ret == -1)
err(1, "setsockopt(.., SO_SETFIB, %u, ..)",
rtableid);
}
ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof(x));
if (ret == -1)
err(1, NULL);
#ifdef IPSEC
if (ipsec_policy[0] != NULL)
add_ipsec_policy(s, ipsec_policy[0]);
if (ipsec_policy[1] != NULL)
add_ipsec_policy(s, ipsec_policy[1]);
#endif
if (FreeBSD_Oflag) {
if (setsockopt(s, IPPROTO_TCP, TCP_NOOPT,
&FreeBSD_Oflag, sizeof(FreeBSD_Oflag)) == -1)
err(1, "disable TCP options");
}
if (bind(s, (struct sockaddr *)res0->ai_addr,
res0->ai_addrlen) == 0)
break;
close(s);
s = -1;
} while ((res0 = res0->ai_next) != NULL);
if (!uflag && s != -1) {
if (listen(s, 1) < 0)
err(1, "listen");
}
freeaddrinfo(res);
return (s);
}
/*
* readwrite()
* Loop that polls on the network file descriptor and stdin.
*/
void
readwrite(int nfd)
{
struct pollfd pfd[2];
unsigned char buf[16384];
int n, wfd = fileno(stdin);
int lfd = fileno(stdout);
int plen;
plen = 2048;
/* Setup Network FD */
pfd[0].fd = nfd;
pfd[0].events = POLLIN;
/* Set up STDIN FD. */
pfd[1].fd = wfd;
pfd[1].events = POLLIN;
while (pfd[0].fd != -1) {
if (iflag)
sleep(iflag);
if ((n = poll(pfd, 2 - dflag, timeout)) < 0) {
close(nfd);
err(1, "Polling Error");
}
if (n == 0)
return;
if (pfd[0].revents & POLLIN) {
if ((n = read(nfd, buf, plen)) < 0)
return;
else if (n == 0) {
shutdown(nfd, SHUT_RD);
pfd[0].fd = -1;
pfd[0].events = 0;
} else {
if (tflag)
atelnet(nfd, buf, n);
if (atomicio(vwrite, lfd, buf, n) != n)
return;
}
}
if (!dflag && pfd[1].revents & POLLIN) {
if ((n = read(wfd, buf, plen)) < 0)
return;
else if (n == 0) {
if (Nflag)
shutdown(nfd, SHUT_WR);
pfd[1].fd = -1;
pfd[1].events = 0;
} else {
if (atomicio(vwrite, nfd, buf, n) != n)
return;
}
}
}
}
/* 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!");
}
}
/*
* build_ports()
* Build an array of ports in portlist[], listing each port
* that we should try to connect to.
*/
void
build_ports(char *p)
{
const char *errstr;
char *n;
int hi, lo, cp;
int x = 0;
if ((n = strchr(p, '-')) != NULL) {
*n = '\0';
n++;
/* Make sure the ports are in order: lowest->highest. */
hi = strtonum(n, 1, PORT_MAX, &errstr);
if (errstr)
errx(1, "port number %s: %s", errstr, n);
lo = strtonum(p, 1, PORT_MAX, &errstr);
if (errstr)
errx(1, "port number %s: %s", errstr, p);
if (lo > hi) {
cp = hi;
hi = lo;
lo = cp;
}
/* Load ports sequentially. */
for (cp = lo; cp <= hi; cp++) {
portlist[x] = calloc(1, PORT_MAX_LEN);
if (portlist[x] == NULL)
err(1, NULL);
snprintf(portlist[x], PORT_MAX_LEN, "%d", cp);
x++;
}
/* Randomly swap ports. */
if (rflag) {
int y;
char *c;
for (x = 0; x <= (hi - lo); x++) {
y = (arc4random() & 0xFFFF) % (hi - lo);
c = portlist[x];
portlist[x] = portlist[y];
portlist[y] = c;
}
}
} else {
hi = strtonum(p, 1, PORT_MAX, &errstr);
if (errstr)
errx(1, "port number %s: %s", errstr, p);
portlist[0] = strdup(p);
if (portlist[0] == NULL)
err(1, NULL);
}
}
/*
* 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, ret;
for (i = 0; i <= 3; i++) {
if (write(s, "X", 1) == 1)
ret = 1;
else
ret = -1;
}
return (ret);
}
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 (Tflag != -1) {
if (setsockopt(s, IPPROTO_IP, IP_TOS,
&Tflag, sizeof(Tflag)) == -1)
err(1, "set IP ToS");
}
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 (FreeBSD_Oflag) {
if (setsockopt(s, IPPROTO_TCP, TCP_NOOPT,
&FreeBSD_Oflag, sizeof(FreeBSD_Oflag)) == -1)
err(1, "disable TCP options");
}
}
int
map_tos(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 },
{ "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);
}
void
report_connect(const struct sockaddr *sa, socklen_t salen)
{
char remote_host[NI_MAXHOST];
char remote_port[NI_MAXSERV];
int herr;
int flags = NI_NUMERICSERV;
if (nflag)
flags |= NI_NUMERICHOST;
if ((herr = getnameinfo(sa, salen,
remote_host, sizeof(remote_host),
remote_port, sizeof(remote_port),
flags)) != 0) {
if (herr == EAI_SYSTEM)
err(1, "getnameinfo");
else
errx(1, "getnameinfo: %s", gai_strerror(herr));
}
fprintf(stderr,
"Connection from %s %s "
"received!\n", remote_host, remote_port);
}
void
help(void)
{
usage(0);
fprintf(stderr, "\tCommand Summary:\n\
\t-4 Use IPv4\n\
\t-6 Use IPv6\n\
\t-D Enable the debug socket option\n\
\t-d Detach from stdin\n");
#ifdef IPSEC
fprintf(stderr, "\
\t-E Use IPsec ESP\n\
\t-e policy Use specified IPsec policy\n");
#endif
fprintf(stderr, "\
\t-h This help text\n\
\t-I length TCP receive buffer length\n\
\t-i secs\t 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-N Shutdown the network socket after EOF on stdin\n\
\t-n Suppress name/port resolutions\n\
\t--no-tcpopt Disable TCP options\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-r Randomize remote ports\n\
\t-S Enable the TCP MD5 signature option\n\
\t-s addr\t Local source address\n\
\t-T toskeyword\tSet IP Type of Service\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 secs\t 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 Zero-I/O mode [used for scanning]\n\
Port numbers can be individual or ranges: lo-hi [inclusive]\n");
#ifdef IPSEC
fprintf(stderr, "See ipsec_set_policy(3) for -e argument format\n");
#endif
exit(1);
}
#ifdef IPSEC
void
add_ipsec_policy(int s, char *policy)
{
char *raw;
int e;
raw = ipsec_set_policy(policy, strlen(policy));
if (raw == NULL)
errx(1, "ipsec_set_policy `%s': %s", policy,
ipsec_strerror());
e = setsockopt(s, IPPROTO_IP, IP_IPSEC_POLICY, raw,
ipsec_get_policylen(raw));
if (e < 0)
err(1, "ipsec policy cannot be configured");
free(raw);
if (vflag)
fprintf(stderr, "ipsec policy configured: `%s'\n", policy);
return;
}
#endif /* IPSEC */
void
usage(int ret)
{
fprintf(stderr,
#ifdef IPSEC
"usage: nc [-46DdEhklNnrStUuvz] [-e policy] [-I length] [-i interval] [-O length]\n"
#else
"usage: nc [-46DdhklNnrStUuvz] [-I length] [-i interval] [-O length]\n"
#endif
"\t [-P proxy_username] [-p source_port] [-s source] [-T ToS]\n"
"\t [-V rtable] [-w timeout] [-X proxy_protocol]\n"
"\t [-x proxy_address[:port]] [destination] [port]\n");
if (ret)
exit(1);
}