fcf596178b
Small summary ------------- o Almost all IPsec releated code was moved into sys/netipsec. o New kernel modules added: ipsec.ko and tcpmd5.ko. New kernel option IPSEC_SUPPORT added. It enables support for loading and unloading of ipsec.ko and tcpmd5.ko kernel modules. o IPSEC_NAT_T option was removed. Now NAT-T support is enabled by default. The UDP_ENCAP_ESPINUDP_NON_IKE encapsulation type support was removed. Added TCP/UDP checksum handling for inbound packets that were decapsulated by transport mode SAs. setkey(8) modified to show run-time NAT-T configuration of SA. o New network pseudo interface if_ipsec(4) added. For now it is build as part of ipsec.ko module (or with IPSEC kernel). It implements IPsec virtual tunnels to create route-based VPNs. o The network stack now invokes IPsec functions using special methods. The only one header file <netipsec/ipsec_support.h> should be included to declare all the needed things to work with IPsec. o All IPsec protocols handlers (ESP/AH/IPCOMP protosw) were removed. Now these protocols are handled directly via IPsec methods. o TCP_SIGNATURE support was reworked to be more close to RFC. o PF_KEY SADB was reworked: - now all security associations stored in the single SPI namespace, and all SAs MUST have unique SPI. - several hash tables added to speed up lookups in SADB. - SADB now uses rmlock to protect access, and concurrent threads can do SA lookups in the same time. - many PF_KEY message handlers were reworked to reflect changes in SADB. - SADB_UPDATE message was extended to support new PF_KEY headers: SADB_X_EXT_NEW_ADDRESS_SRC and SADB_X_EXT_NEW_ADDRESS_DST. They can be used by IKE daemon to change SA addresses. o ipsecrequest and secpolicy structures were cardinally changed to avoid locking protection for ipsecrequest. Now we support only limited number (4) of bundled SAs, but they are supported for both INET and INET6. o INPCB security policy cache was introduced. Each PCB now caches used security policies to avoid SP lookup for each packet. o For inbound security policies added the mode, when the kernel does check for full history of applied IPsec transforms. o References counting rules for security policies and security associations were changed. The proper SA locking added into xform code. o xform code was also changed. Now it is possible to unregister xforms. tdb_xxx structures were changed and renamed to reflect changes in SADB/SPDB, and changed rules for locking and refcounting. Reviewed by: gnn, wblock Obtained from: Yandex LLC Relnotes: yes Sponsored by: Yandex LLC Differential Revision: https://reviews.freebsd.org/D9352
1397 lines
32 KiB
C
1397 lines
32 KiB
C
/* $OpenBSD: netcat.c,v 1.130 2015/07/26 19:12:28 chl 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/uio.h>
|
|
#include <sys/un.h>
|
|
|
|
#include <netinet/in.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 <fcntl.h>
|
|
#include <limits.h>
|
|
#include <netdb.h>
|
|
#include <poll.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.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
|
|
|
|
#define POLL_STDIN 0
|
|
#define POLL_NETOUT 1
|
|
#define POLL_NETIN 2
|
|
#define POLL_STDOUT 3
|
|
#define BUFSIZE 16384
|
|
|
|
/* Command Line Options */
|
|
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 */
|
|
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 */
|
|
int rtableid = -1;
|
|
|
|
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);
|
|
void fdpass(int nfd) __attribute__((noreturn));
|
|
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);
|
|
int map_tos(char *, int *);
|
|
void report_connect(const struct sockaddr *, socklen_t);
|
|
void usage(int);
|
|
ssize_t drainbuf(int, unsigned char *, size_t *);
|
|
ssize_t fillbuf(int, unsigned char *, size_t *);
|
|
|
|
#ifdef IPSEC
|
|
void add_ipsec_policy(int, 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;
|
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
while ((ch = getopt_long(argc, argv,
|
|
"46DdEe:FhI: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 'F':
|
|
Fflag = 1;
|
|
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 = (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 (Fflag)
|
|
fdpass(s);
|
|
else 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, FD_CLOEXEC);
|
|
|
|
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;
|
|
|
|
if (rtableid >= 0 && (setsockopt(s, SOL_SOCKET, SO_SETFIB,
|
|
&rtableid, sizeof(rtableid)) == -1))
|
|
err(1, "setsockopt SO_SETFIB");
|
|
|
|
/* 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)
|
|
err(1, "bind failed");
|
|
freeaddrinfo(ares);
|
|
}
|
|
|
|
set_common_sockopts(s, res0->ai_family);
|
|
|
|
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 >= 0 && (setsockopt(s, SOL_SOCKET, SO_SETFIB,
|
|
&rtableid, sizeof(rtableid)) == -1))
|
|
err(1, "setsockopt SO_SETFIB");
|
|
|
|
ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof(x));
|
|
if (ret == -1)
|
|
err(1, NULL);
|
|
|
|
if (FreeBSD_Oflag) {
|
|
if (setsockopt(s, IPPROTO_TCP, TCP_NOOPT,
|
|
&FreeBSD_Oflag, sizeof(FreeBSD_Oflag)) == -1)
|
|
err(1, "disable TCP options");
|
|
}
|
|
|
|
set_common_sockopts(s, res0->ai_family);
|
|
|
|
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 net_fd)
|
|
{
|
|
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) {
|
|
close(net_fd);
|
|
return;
|
|
}
|
|
/* both outputs are gone, we can't continue */
|
|
if (pfd[POLL_NETOUT].fd == -1 && pfd[POLL_STDOUT].fd == -1) {
|
|
close(net_fd);
|
|
return;
|
|
}
|
|
/* listen and net in gone, queues empty, done */
|
|
if (lflag && pfd[POLL_NETIN].fd == -1
|
|
&& stdinbufpos == 0 && netinbufpos == 0) {
|
|
close(net_fd);
|
|
return;
|
|
}
|
|
|
|
/* help says -i is for "wait between lines sent". We read and
|
|
* write arbitrary amounts of data, and we don't want to start
|
|
* scanning for newlines, so this is as good as it gets */
|
|
if (iflag)
|
|
sleep(iflag);
|
|
|
|
/* poll */
|
|
num_fds = poll(pfd, 4, timeout);
|
|
|
|
/* treat poll errors */
|
|
if (num_fds == -1) {
|
|
close(net_fd);
|
|
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,
|
|
&stdinbufpos);
|
|
/* error or eof on stdin - remove from pfd */
|
|
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,
|
|
&stdinbufpos);
|
|
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,
|
|
&netinbufpos);
|
|
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;
|
|
}
|
|
/* 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,
|
|
&netinbufpos);
|
|
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;
|
|
}
|
|
/* 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)
|
|
{
|
|
ssize_t n;
|
|
ssize_t adjust;
|
|
|
|
n = write(fd, buf, *bufpos);
|
|
/* don't treat EAGAIN, EINTR as error */
|
|
if (n == -1 && (errno == EAGAIN || errno == EINTR))
|
|
n = -2;
|
|
if (n <= 0)
|
|
return n;
|
|
/* adjust buffer */
|
|
adjust = *bufpos - n;
|
|
if (adjust > 0)
|
|
memmove(buf, buf + n, adjust);
|
|
*bufpos -= n;
|
|
return n;
|
|
}
|
|
|
|
|
|
ssize_t
|
|
fillbuf(int fd, unsigned char *buf, size_t *bufpos)
|
|
{
|
|
size_t num = BUFSIZE - *bufpos;
|
|
ssize_t n;
|
|
|
|
n = read(fd, buf + *bufpos, num);
|
|
/* don't treat EAGAIN, EINTR as error */
|
|
if (n == -1 && (errno == EAGAIN || errno == EINTR))
|
|
n = -2;
|
|
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");
|
|
|
|
bzero(&mh, sizeof(mh));
|
|
bzero(&cmsgbuf, sizeof(cmsgbuf));
|
|
bzero(&iov, 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;
|
|
|
|
bzero(&pfd, 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!");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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 af)
|
|
{
|
|
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) {
|
|
int proto, option;
|
|
|
|
if (af == AF_INET6) {
|
|
proto = IPPROTO_IPV6;
|
|
option = IPV6_TCLASS;
|
|
} else {
|
|
proto = IPPROTO_IP;
|
|
option = IP_TOS;
|
|
}
|
|
|
|
if (setsockopt(s, proto, option, &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");
|
|
}
|
|
#ifdef IPSEC
|
|
if (ipsec_policy[0] != NULL)
|
|
add_ipsec_policy(s, af, ipsec_policy[0]);
|
|
if (ipsec_policy[1] != NULL)
|
|
add_ipsec_policy(s, af, ipsec_policy[1]);
|
|
#endif
|
|
}
|
|
|
|
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-F Pass socket fd\n\
|
|
\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, int af, 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());
|
|
if (af == AF_INET)
|
|
e = setsockopt(s, IPPROTO_IP, IP_IPSEC_POLICY, raw,
|
|
ipsec_get_policylen(raw));
|
|
if (af == AF_INET6)
|
|
e = setsockopt(s, IPPROTO_IPV6, IPV6_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 [-46DdEFhklNnrStUuvz] [-e policy] [-I length] [-i interval] [-O length]\n"
|
|
#else
|
|
"usage: nc [-46DdFhklNnrStUuvz] [-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);
|
|
}
|