xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.bin/nc/netcat.c (revision 5830:6366d1b2d338)
15570Svk199839 /* $OpenBSD: netcat.c,v 1.89 2007/02/20 14:11:17 jmc Exp $ */
25570Svk199839 /*
35570Svk199839  * Copyright (c) 2001 Eric Jackson <ericj@monkey.org>
45570Svk199839  *
55570Svk199839  * Redistribution and use in source and binary forms, with or without
65570Svk199839  * modification, are permitted provided that the following conditions
75570Svk199839  * are met:
85570Svk199839  *
95570Svk199839  * 1. Redistributions of source code must retain the above copyright
105570Svk199839  *   notice, this list of conditions and the following disclaimer.
115570Svk199839  * 2. Redistributions in binary form must reproduce the above copyright
125570Svk199839  *   notice, this list of conditions and the following disclaimer in the
135570Svk199839  *   documentation and/or other materials provided with the distribution.
145570Svk199839  * 3. The name of the author may not be used to endorse or promote products
155570Svk199839  *   derived from this software without specific prior written permission.
165570Svk199839  *
175570Svk199839  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
185570Svk199839  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
195570Svk199839  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
205570Svk199839  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
215570Svk199839  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
225570Svk199839  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
235570Svk199839  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
245570Svk199839  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
255570Svk199839  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
265570Svk199839  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
275570Svk199839  */
285570Svk199839 
295570Svk199839 /*
305570Svk199839  * Re-written nc(1) for OpenBSD. Original implementation by
315570Svk199839  * *Hobbit* <hobbit@avian.org>.
325570Svk199839  */
335570Svk199839 
34*5830Svk199839 /*
35*5830Svk199839  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
36*5830Svk199839  * Use is subject to license terms.
37*5830Svk199839  */
38*5830Svk199839 
395570Svk199839 #pragma ident	"%Z%%M%	%I%	%E% SMI"
405570Svk199839 
415570Svk199839 #include <sys/types.h>
425570Svk199839 #include <sys/socket.h>
435570Svk199839 #include <sys/time.h>
445570Svk199839 #include <sys/un.h>
455570Svk199839 
465570Svk199839 #include <netinet/in.h>
475570Svk199839 #include <netinet/in_systm.h>
485570Svk199839 #include <netinet/tcp.h>
495570Svk199839 #include <netinet/ip.h>
505570Svk199839 #include <arpa/telnet.h>
515570Svk199839 
525570Svk199839 #include <err.h>
535570Svk199839 #include <errno.h>
545570Svk199839 #include <netdb.h>
555570Svk199839 #include <poll.h>
565570Svk199839 #include <stdarg.h>
575570Svk199839 #include <stdio.h>
585570Svk199839 #include <stdlib.h>
595570Svk199839 #include <string.h>
605570Svk199839 #include <unistd.h>
615570Svk199839 #include <fcntl.h>
625570Svk199839 #include <limits.h>
635570Svk199839 #include <signal.h>
645570Svk199839 
655570Svk199839 #include "atomicio.h"
665570Svk199839 #include "strtonum.h"
675570Svk199839 
685570Svk199839 #ifndef	SUN_LEN
695570Svk199839 #define	SUN_LEN(su) \
705570Svk199839 	(sizeof (*(su)) - sizeof ((su)->sun_path) + strlen((su)->sun_path))
715570Svk199839 #endif
725570Svk199839 
735570Svk199839 #define	PORT_MIN	1
745570Svk199839 #define	PORT_MAX	65535
755570Svk199839 #define	PORT_MAX_LEN	6
765570Svk199839 
775570Svk199839 /* Command Line Options */
785570Svk199839 int	dflag;		/* detached, no stdin */
795570Svk199839 unsigned int iflag;	/* Interval Flag */
805570Svk199839 int	kflag;		/* More than one connect */
815570Svk199839 int	lflag;		/* Bind to local port */
825570Svk199839 int	nflag;		/* Don't do name lookup */
835570Svk199839 char	*Pflag;		/* Proxy username */
845570Svk199839 char	*pflag;		/* Localport flag */
855570Svk199839 int	rflag;		/* Random ports flag */
865570Svk199839 char	*sflag;		/* Source Address */
875570Svk199839 int	tflag;		/* Telnet Emulation */
885570Svk199839 int	uflag;		/* UDP - Default to TCP */
895570Svk199839 int	vflag;		/* Verbosity */
905570Svk199839 int	xflag;		/* Socks proxy */
915570Svk199839 int	Xflag;		/* indicator of Socks version set */
925570Svk199839 int	zflag;		/* Port Scan Flag */
935570Svk199839 int	Dflag;		/* sodebug */
945570Svk199839 int	Tflag = -1;	/* IP Type of Service */
955570Svk199839 
965570Svk199839 int	timeout = -1;
975570Svk199839 int	family = AF_UNSPEC;
985570Svk199839 char	*portlist[PORT_MAX+1];
995570Svk199839 
1005570Svk199839 void	atelnet(int, unsigned char *, unsigned int);
1015570Svk199839 void	build_ports(char *);
1025570Svk199839 void	help(void);
1035570Svk199839 int	local_listen(char *, char *, struct addrinfo);
1045570Svk199839 void	readwrite(int);
1055570Svk199839 int	remote_connect(const char *, const char *, struct addrinfo);
1065570Svk199839 int	socks_connect(const char *, const char *,
1075570Svk199839 	    const char *, const char *, struct addrinfo, int, const char *);
1085570Svk199839 int	udptest(int);
1095570Svk199839 int	unix_connect(char *);
1105570Svk199839 int	unix_listen(char *);
1115570Svk199839 void	set_common_sockopts(int);
1125570Svk199839 int	parse_iptos(char *);
1135570Svk199839 void	usage(int);
114*5830Svk199839 char	*print_addr(char *, size_t, struct sockaddr *, int, int);
1155570Svk199839 
1165570Svk199839 int
1175570Svk199839 main(int argc, char *argv[])
1185570Svk199839 {
1195570Svk199839 	int ch, s, ret, socksv;
1205570Svk199839 	char *host, *uport, *proxy;
1215570Svk199839 	struct addrinfo hints;
1225570Svk199839 	struct servent *sv;
1235570Svk199839 	socklen_t len;
1245570Svk199839 	struct sockaddr_storage cliaddr;
1255570Svk199839 	const char *errstr, *proxyhost = "", *proxyport = NULL;
1265570Svk199839 	struct addrinfo proxyhints;
1275570Svk199839 
1285570Svk199839 	ret = 1;
1295570Svk199839 	s = 0;
1305570Svk199839 	socksv = 5;
1315570Svk199839 	host = NULL;
1325570Svk199839 	uport = NULL;
1335570Svk199839 	sv = NULL;
1345570Svk199839 
1355570Svk199839 	while ((ch = getopt(argc, argv,
1365570Svk199839 	    "46Ddhi:klnP:p:rs:T:tUuvw:X:x:z")) != -1) {
1375570Svk199839 		switch (ch) {
1385570Svk199839 		case '4':
1395570Svk199839 			family = AF_INET;
1405570Svk199839 			break;
1415570Svk199839 		case '6':
1425570Svk199839 			family = AF_INET6;
1435570Svk199839 			break;
1445570Svk199839 		case 'U':
1455570Svk199839 			family = AF_UNIX;
1465570Svk199839 			break;
1475570Svk199839 		case 'X':
1485570Svk199839 			Xflag = 1;
1495570Svk199839 			if (strcasecmp(optarg, "connect") == 0)
1505570Svk199839 				socksv = -1; /* HTTP proxy CONNECT */
1515570Svk199839 			else if (strcmp(optarg, "4") == 0)
1525570Svk199839 				socksv = 4; /* SOCKS v.4 */
1535570Svk199839 			else if (strcmp(optarg, "5") == 0)
1545570Svk199839 				socksv = 5; /* SOCKS v.5 */
1555570Svk199839 			else
1565570Svk199839 				errx(1, "unsupported proxy protocol");
1575570Svk199839 			break;
1585570Svk199839 		case 'd':
1595570Svk199839 			dflag = 1;
1605570Svk199839 			break;
1615570Svk199839 		case 'h':
1625570Svk199839 			help();
1635570Svk199839 			break;
1645570Svk199839 		case 'i':
1655570Svk199839 			iflag = strtonum(optarg, 0, UINT_MAX, &errstr);
1665570Svk199839 			if (errstr)
1675570Svk199839 				errx(1, "interval %s: %s", errstr, optarg);
1685570Svk199839 			break;
1695570Svk199839 		case 'k':
1705570Svk199839 			kflag = 1;
1715570Svk199839 			break;
1725570Svk199839 		case 'l':
1735570Svk199839 			lflag = 1;
1745570Svk199839 			break;
1755570Svk199839 		case 'n':
1765570Svk199839 			nflag = 1;
1775570Svk199839 			break;
1785570Svk199839 		case 'P':
1795570Svk199839 			Pflag = optarg;
1805570Svk199839 			break;
1815570Svk199839 		case 'p':
1825570Svk199839 			pflag = optarg;
1835570Svk199839 			break;
1845570Svk199839 		case 'r':
1855570Svk199839 			rflag = 1;
1865570Svk199839 			break;
1875570Svk199839 		case 's':
1885570Svk199839 			sflag = optarg;
1895570Svk199839 			break;
1905570Svk199839 		case 't':
1915570Svk199839 			tflag = 1;
1925570Svk199839 			break;
1935570Svk199839 		case 'u':
1945570Svk199839 			uflag = 1;
1955570Svk199839 			break;
1965570Svk199839 		case 'v':
1975570Svk199839 			vflag = 1;
1985570Svk199839 			break;
1995570Svk199839 		case 'w':
2005570Svk199839 			timeout = strtonum(optarg, 0, INT_MAX / 1000, &errstr);
2015570Svk199839 			if (errstr)
2025570Svk199839 				errx(1, "timeout %s: %s", errstr, optarg);
2035570Svk199839 			timeout *= 1000;
2045570Svk199839 			break;
2055570Svk199839 		case 'x':
2065570Svk199839 			xflag = 1;
2075570Svk199839 			if ((proxy = strdup(optarg)) == NULL)
2085570Svk199839 				err(1, NULL);
2095570Svk199839 			break;
2105570Svk199839 		case 'z':
2115570Svk199839 			zflag = 1;
2125570Svk199839 			break;
2135570Svk199839 		case 'D':
2145570Svk199839 			Dflag = 1;
2155570Svk199839 			break;
2165570Svk199839 		case 'T':
2175570Svk199839 			Tflag = parse_iptos(optarg);
2185570Svk199839 			break;
2195570Svk199839 		default:
2205570Svk199839 			usage(1);
2215570Svk199839 		}
2225570Svk199839 	}
2235570Svk199839 	argc -= optind;
2245570Svk199839 	argv += optind;
2255570Svk199839 
2265570Svk199839 	/* Cruft to make sure options are clean, and used properly. */
2275570Svk199839 	if (argv[0] && !argv[1] && family == AF_UNIX) {
2285570Svk199839 		if (uflag)
2295570Svk199839 			errx(1, "cannot use -u and -U");
2305570Svk199839 		host = argv[0];
2315570Svk199839 		uport = NULL;
2325570Svk199839 	} else if (argv[0] && !argv[1]) {
2335570Svk199839 		if (!lflag)
2345570Svk199839 			usage(1);
2355570Svk199839 		uport = argv[0];
2365570Svk199839 		host = NULL;
2375570Svk199839 	} else if (argv[0] && argv[1]) {
2385570Svk199839 		if (family == AF_UNIX)
2395570Svk199839 			usage(1);
2405570Svk199839 		host = argv[0];
2415570Svk199839 		uport = argv[1];
2425570Svk199839 	} else {
2435570Svk199839 		if (!(lflag && pflag))
2445570Svk199839 			usage(1);
2455570Svk199839 	}
2465570Svk199839 
2475570Svk199839 	if (lflag && sflag)
2485570Svk199839 		errx(1, "cannot use -s and -l");
2495570Svk199839 	if (lflag && rflag)
2505570Svk199839 		errx(1, "cannot use -r and -l");
2515570Svk199839 	if (lflag && pflag) {
2525570Svk199839 		if (uport)
2535570Svk199839 			usage(1);
2545570Svk199839 		uport = pflag;
2555570Svk199839 	}
2565570Svk199839 	if (lflag && zflag)
2575570Svk199839 		errx(1, "cannot use -z and -l");
2585570Svk199839 	if (!lflag && kflag)
2595570Svk199839 		errx(1, "must use -l with -k");
2605570Svk199839 	if (lflag && (Pflag || xflag || Xflag))
2615570Svk199839 		errx(1, "cannot use -l with -P, -X or -x");
2625570Svk199839 
2635570Svk199839 	/* Initialize addrinfo structure. */
2645570Svk199839 	if (family != AF_UNIX) {
2655570Svk199839 		(void) memset(&hints, 0, sizeof (struct addrinfo));
2665570Svk199839 		hints.ai_family = family;
2675570Svk199839 		hints.ai_socktype = uflag ? SOCK_DGRAM : SOCK_STREAM;
2685570Svk199839 		hints.ai_protocol = uflag ? IPPROTO_UDP : IPPROTO_TCP;
2695570Svk199839 		if (nflag)
2705570Svk199839 			hints.ai_flags |= AI_NUMERICHOST;
2715570Svk199839 	}
2725570Svk199839 
2735570Svk199839 	if (xflag) {
2745570Svk199839 		if (uflag)
2755570Svk199839 			errx(1, "no proxy support for UDP mode");
2765570Svk199839 
2775570Svk199839 		if (lflag)
2785570Svk199839 			errx(1, "no proxy support for listen");
2795570Svk199839 
2805570Svk199839 		if (family == AF_UNIX)
2815570Svk199839 			errx(1, "no proxy support for unix sockets");
2825570Svk199839 
2835570Svk199839 		if (family == AF_INET6)
2845570Svk199839 			errx(1, "no proxy support for IPv6");
2855570Svk199839 
2865570Svk199839 		if (sflag)
2875570Svk199839 			errx(1, "no proxy support for local source address");
2885570Svk199839 
2895570Svk199839 		if ((proxyhost = strtok(proxy, ":")) == NULL)
2905570Svk199839 			errx(1, "missing port specification");
2915570Svk199839 		proxyport = strtok(NULL, ":");
2925570Svk199839 
2935570Svk199839 		(void) memset(&proxyhints, 0, sizeof (struct addrinfo));
2945570Svk199839 		proxyhints.ai_family = family;
2955570Svk199839 		proxyhints.ai_socktype = SOCK_STREAM;
2965570Svk199839 		proxyhints.ai_protocol = IPPROTO_TCP;
2975570Svk199839 		if (nflag)
2985570Svk199839 			proxyhints.ai_flags |= AI_NUMERICHOST;
2995570Svk199839 	}
3005570Svk199839 
3015570Svk199839 	if (lflag) {
3025570Svk199839 		int connfd;
3035570Svk199839 		ret = 0;
3045570Svk199839 
3055570Svk199839 		if (family == AF_UNIX)
3065570Svk199839 			s = unix_listen(host);
3075570Svk199839 
3085570Svk199839 		/* Allow only one connection at a time, but stay alive. */
3095570Svk199839 		for (;;) {
3105570Svk199839 			if (family != AF_UNIX)
3115570Svk199839 				s = local_listen(host, uport, hints);
3125570Svk199839 			if (s < 0)
3135570Svk199839 				err(1, NULL);
3145570Svk199839 			/*
3155570Svk199839 			 * For UDP, we will use recvfrom() initially
3165570Svk199839 			 * to wait for a caller, then use the regular
3175570Svk199839 			 * functions to talk to the caller.
3185570Svk199839 			 */
3195570Svk199839 			if (uflag) {
3205570Svk199839 				int rv, plen;
3215570Svk199839 				char buf[8192];
3225570Svk199839 				struct sockaddr_storage z;
3235570Svk199839 
3245570Svk199839 				len = sizeof (z);
3255570Svk199839 				plen = 1024;
3265570Svk199839 				rv = recvfrom(s, buf, plen, MSG_PEEK,
3275570Svk199839 				    (struct sockaddr *)&z, &len);
3285570Svk199839 				if (rv < 0)
3295570Svk199839 					err(1, "recvfrom");
3305570Svk199839 
3315570Svk199839 				rv = connect(s, (struct sockaddr *)&z, len);
3325570Svk199839 				if (rv < 0)
3335570Svk199839 					err(1, "connect");
3345570Svk199839 
3355570Svk199839 				connfd = s;
3365570Svk199839 			} else {
3375570Svk199839 				len = sizeof (cliaddr);
3385570Svk199839 				connfd = accept(s, (struct sockaddr *)&cliaddr,
3395570Svk199839 				    &len);
340*5830Svk199839 				if ((connfd != -1) && vflag) {
341*5830Svk199839 					char ntop[NI_MAXHOST + NI_MAXSERV];
342*5830Svk199839 					(void) fprintf(stderr,
343*5830Svk199839 					    "Received connection from %s\n",
344*5830Svk199839 					    print_addr(ntop, sizeof (ntop),
345*5830Svk199839 					    (struct sockaddr *)&cliaddr, len,
346*5830Svk199839 					    nflag ? NI_NUMERICHOST : 0));
347*5830Svk199839 				}
3485570Svk199839 			}
3495570Svk199839 
3505570Svk199839 			readwrite(connfd);
3515570Svk199839 			(void) close(connfd);
3525570Svk199839 			if (family != AF_UNIX)
3535570Svk199839 				(void) close(s);
3545570Svk199839 
3555570Svk199839 			if (!kflag)
3565570Svk199839 				break;
3575570Svk199839 		}
3585570Svk199839 	} else if (family == AF_UNIX) {
3595570Svk199839 		ret = 0;
3605570Svk199839 
3615570Svk199839 		if ((s = unix_connect(host)) > 0 && !zflag) {
3625570Svk199839 			readwrite(s);
3635570Svk199839 			(void) close(s);
3645570Svk199839 		} else
3655570Svk199839 			ret = 1;
3665570Svk199839 
3675570Svk199839 		exit(ret);
3685570Svk199839 
3695570Svk199839 	} else {	/* AF_INET or AF_INET6 */
3705570Svk199839 		int i = 0;
3715570Svk199839 
3725570Svk199839 		/* Construct the portlist[] array. */
3735570Svk199839 		build_ports(uport);
3745570Svk199839 
3755570Svk199839 		/* Cycle through portlist, connecting to each port. */
3765570Svk199839 		for (i = 0; portlist[i] != NULL; i++) {
3775570Svk199839 			if (s)
3785570Svk199839 				(void) close(s);
3795570Svk199839 
3805570Svk199839 			if (xflag)
3815570Svk199839 				s = socks_connect(host, portlist[i],
3825570Svk199839 				    proxyhost, proxyport, proxyhints, socksv,
3835570Svk199839 				    Pflag);
3845570Svk199839 			else
3855570Svk199839 				s = remote_connect(host, portlist[i], hints);
3865570Svk199839 
3875570Svk199839 			if (s < 0)
3885570Svk199839 				continue;
3895570Svk199839 
3905570Svk199839 			ret = 0;
3915570Svk199839 			if (vflag || zflag) {
3925570Svk199839 				/* For UDP, make sure we are connected. */
3935570Svk199839 				if (uflag) {
3945570Svk199839 					if (udptest(s) == -1) {
3955570Svk199839 						ret = 1;
3965570Svk199839 						continue;
3975570Svk199839 					}
3985570Svk199839 				}
3995570Svk199839 
4005570Svk199839 				/* Don't look up port if -n. */
4015570Svk199839 				if (nflag)
4025570Svk199839 					sv = NULL;
4035570Svk199839 				else {
4045570Svk199839 					sv = getservbyport(
4055570Svk199839 					    ntohs(atoi(portlist[i])),
4065570Svk199839 					    uflag ? "udp" : "tcp");
4075570Svk199839 				}
4085570Svk199839 
409*5830Svk199839 				(void) fprintf(stderr, "Connection to %s %s "
4105570Svk199839 				    "port [%s/%s] succeeded!\n",
4115570Svk199839 				    host, portlist[i], uflag ? "udp" : "tcp",
4125570Svk199839 				    sv ? sv->s_name : "*");
4135570Svk199839 			}
4145570Svk199839 			if (!zflag)
4155570Svk199839 				readwrite(s);
4165570Svk199839 		}
4175570Svk199839 	}
4185570Svk199839 
4195570Svk199839 	if (s)
4205570Svk199839 		(void) close(s);
4215570Svk199839 
4225570Svk199839 	return (ret);
4235570Svk199839 }
4245570Svk199839 
4255570Svk199839 /*
426*5830Svk199839  * print IP address and (optionally) a port
427*5830Svk199839  */
428*5830Svk199839 char *
429*5830Svk199839 print_addr(char *ntop, size_t ntlen, struct sockaddr *addr, int len, int flags)
430*5830Svk199839 {
431*5830Svk199839 	char port[NI_MAXSERV];
432*5830Svk199839 	int e;
433*5830Svk199839 
434*5830Svk199839 	/* print port always as number */
435*5830Svk199839 	if ((e = getnameinfo(addr, len, ntop, ntlen,
436*5830Svk199839 	    port, sizeof (port), flags|NI_NUMERICSERV)) != 0) {
437*5830Svk199839 		return ((char *)gai_strerror(e));
438*5830Svk199839 	}
439*5830Svk199839 
440*5830Svk199839 	(void) snprintf(ntop, ntlen, "%s port %s", ntop, port);
441*5830Svk199839 
442*5830Svk199839 	return (ntop);
443*5830Svk199839 }
444*5830Svk199839 
445*5830Svk199839 /*
4465570Svk199839  * unix_connect()
4475570Svk199839  * Returns a socket connected to a local unix socket. Returns -1 on failure.
4485570Svk199839  */
4495570Svk199839 int
4505570Svk199839 unix_connect(char *path)
4515570Svk199839 {
4525570Svk199839 	struct sockaddr_un sunaddr;
4535570Svk199839 	int s;
4545570Svk199839 
4555570Svk199839 	if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
4565570Svk199839 		return (-1);
4575570Svk199839 
4585570Svk199839 	(void) memset(&sunaddr, 0, sizeof (struct sockaddr_un));
4595570Svk199839 	sunaddr.sun_family = AF_UNIX;
4605570Svk199839 
4615570Svk199839 	if (strlcpy(sunaddr.sun_path, path, sizeof (sunaddr.sun_path)) >=
4625570Svk199839 	    sizeof (sunaddr.sun_path)) {
4635570Svk199839 		(void) close(s);
4645570Svk199839 		errno = ENAMETOOLONG;
4655570Svk199839 		return (-1);
4665570Svk199839 	}
4675570Svk199839 	if (connect(s, (struct sockaddr *)&sunaddr, SUN_LEN(&sunaddr)) < 0) {
4685570Svk199839 		(void) close(s);
4695570Svk199839 		return (-1);
4705570Svk199839 	}
4715570Svk199839 	return (s);
4725570Svk199839 }
4735570Svk199839 
4745570Svk199839 /*
4755570Svk199839  * unix_listen()
4765570Svk199839  * Create a unix domain socket, and listen on it.
4775570Svk199839  */
4785570Svk199839 int
4795570Svk199839 unix_listen(char *path)
4805570Svk199839 {
4815570Svk199839 	struct sockaddr_un sunaddr;
4825570Svk199839 	int s;
4835570Svk199839 
4845570Svk199839 	/* Create unix domain socket. */
4855570Svk199839 	if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
4865570Svk199839 		return (-1);
4875570Svk199839 
4885570Svk199839 	(void) memset(&sunaddr, 0, sizeof (struct sockaddr_un));
4895570Svk199839 	sunaddr.sun_family = AF_UNIX;
4905570Svk199839 
4915570Svk199839 	if (strlcpy(sunaddr.sun_path, path, sizeof (sunaddr.sun_path)) >=
4925570Svk199839 	    sizeof (sunaddr.sun_path)) {
4935570Svk199839 		(void) close(s);
4945570Svk199839 		errno = ENAMETOOLONG;
4955570Svk199839 		return (-1);
4965570Svk199839 	}
4975570Svk199839 
4985570Svk199839 	if (bind(s, (struct sockaddr *)&sunaddr, SUN_LEN(&sunaddr)) < 0) {
4995570Svk199839 		(void) close(s);
5005570Svk199839 		return (-1);
5015570Svk199839 	}
5025570Svk199839 
5035570Svk199839 	if (listen(s, 5) < 0) {
5045570Svk199839 		(void) close(s);
5055570Svk199839 		return (-1);
5065570Svk199839 	}
5075570Svk199839 	return (s);
5085570Svk199839 }
5095570Svk199839 
5105570Svk199839 /*
5115570Svk199839  * remote_connect()
5125570Svk199839  * Returns a socket connected to a remote host. Properly binds to a local
5135570Svk199839  * port or source address if needed. Returns -1 on failure.
5145570Svk199839  */
5155570Svk199839 int
5165570Svk199839 remote_connect(const char *host, const char *port, struct addrinfo hints)
5175570Svk199839 {
5185570Svk199839 	struct addrinfo *res, *res0;
5195570Svk199839 	int s, error;
5205570Svk199839 
5215570Svk199839 	if ((error = getaddrinfo(host, port, &hints, &res)))
5225570Svk199839 		errx(1, "getaddrinfo: %s", gai_strerror(error));
5235570Svk199839 
5245570Svk199839 	res0 = res;
5255570Svk199839 	do {
5265570Svk199839 		if ((s = socket(res0->ai_family, res0->ai_socktype,
5275570Svk199839 		    res0->ai_protocol)) < 0) {
5285570Svk199839 			warn("failed to create socket");
5295570Svk199839 			continue;
5305570Svk199839 		}
5315570Svk199839 
5325570Svk199839 		/* Bind to a local port or source address if specified. */
5335570Svk199839 		if (sflag || pflag) {
5345570Svk199839 			struct addrinfo ahints, *ares;
5355570Svk199839 
5365570Svk199839 			(void) memset(&ahints, 0, sizeof (struct addrinfo));
5375570Svk199839 			ahints.ai_family = res0->ai_family;
5385570Svk199839 			ahints.ai_socktype = uflag ? SOCK_DGRAM : SOCK_STREAM;
5395570Svk199839 			ahints.ai_protocol = uflag ? IPPROTO_UDP : IPPROTO_TCP;
5405570Svk199839 			ahints.ai_flags = AI_PASSIVE;
5415570Svk199839 			if ((error = getaddrinfo(sflag, pflag, &ahints, &ares)))
5425570Svk199839 				errx(1, "getaddrinfo: %s", gai_strerror(error));
5435570Svk199839 
5445570Svk199839 			if (bind(s, (struct sockaddr *)ares->ai_addr,
5455570Svk199839 			    ares->ai_addrlen) < 0)
5465570Svk199839 				errx(1, "bind failed: %s", strerror(errno));
5475570Svk199839 			freeaddrinfo(ares);
548*5830Svk199839 
549*5830Svk199839 			if (vflag && !lflag) {
550*5830Svk199839 				if (sflag != NULL)
551*5830Svk199839 					(void) fprintf(stderr,
552*5830Svk199839 					    "Using source address: %s\n",
553*5830Svk199839 					    sflag);
554*5830Svk199839 				if (pflag != NULL)
555*5830Svk199839 					(void) fprintf(stderr,
556*5830Svk199839 					    "Using source port: %s\n", pflag);
557*5830Svk199839 			}
5585570Svk199839 		}
5595570Svk199839 
5605570Svk199839 		set_common_sockopts(s);
5615570Svk199839 
5625570Svk199839 		if (connect(s, res0->ai_addr, res0->ai_addrlen) == 0)
5635570Svk199839 			break;
564*5830Svk199839 		else if (vflag) {
565*5830Svk199839 			char ntop[NI_MAXHOST + NI_MAXSERV];
566*5830Svk199839 			warn("connect to %s [host %s] (%s) failed",
567*5830Svk199839 			    print_addr(ntop, sizeof (ntop),
568*5830Svk199839 			    res0->ai_addr, res0->ai_addrlen, NI_NUMERICHOST),
569*5830Svk199839 			    host, uflag ? "udp" : "tcp");
570*5830Svk199839 		}
5715570Svk199839 
5725570Svk199839 		(void) close(s);
5735570Svk199839 		s = -1;
5745570Svk199839 	} while ((res0 = res0->ai_next) != NULL);
5755570Svk199839 
5765570Svk199839 	freeaddrinfo(res);
5775570Svk199839 
5785570Svk199839 	return (s);
5795570Svk199839 }
5805570Svk199839 
5815570Svk199839 /*
5825570Svk199839  * local_listen()
5835570Svk199839  * Returns a socket listening on a local port, binds to specified source
5845570Svk199839  * address. Returns -1 on failure.
5855570Svk199839  */
5865570Svk199839 int
5875570Svk199839 local_listen(char *host, char *port, struct addrinfo hints)
5885570Svk199839 {
5895570Svk199839 	struct addrinfo *res, *res0;
5905570Svk199839 	int s, ret, x = 1;
5915570Svk199839 	int error;
5925570Svk199839 
5935570Svk199839 	/* Allow nodename to be null. */
5945570Svk199839 	hints.ai_flags |= AI_PASSIVE;
5955570Svk199839 
5965570Svk199839 	if ((error = getaddrinfo(host, port, &hints, &res)))
5975570Svk199839 		errx(1, "getaddrinfo: %s", gai_strerror(error));
5985570Svk199839 
5995570Svk199839 	res0 = res;
6005570Svk199839 	do {
6015570Svk199839 		if ((s = socket(res0->ai_family, res0->ai_socktype,
6025570Svk199839 		    res0->ai_protocol)) < 0) {
6035570Svk199839 			warn("failed to create socket");
6045570Svk199839 			continue;
6055570Svk199839 		}
6065570Svk199839 
6075570Svk199839 		ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &x, sizeof (x));
6085570Svk199839 		if (ret == -1)
6095570Svk199839 			err(1, NULL);
6105570Svk199839 
6115570Svk199839 		set_common_sockopts(s);
6125570Svk199839 
6135570Svk199839 		if (bind(s, (struct sockaddr *)res0->ai_addr,
6145570Svk199839 		    res0->ai_addrlen) == 0)
6155570Svk199839 			break;
6165570Svk199839 
6175570Svk199839 		(void) close(s);
6185570Svk199839 		s = -1;
6195570Svk199839 	} while ((res0 = res0->ai_next) != NULL);
6205570Svk199839 
6215570Svk199839 	if (!uflag && s != -1) {
6225570Svk199839 		if (listen(s, 1) < 0)
6235570Svk199839 			err(1, "listen");
6245570Svk199839 	}
6255570Svk199839 
6265570Svk199839 	freeaddrinfo(res);
6275570Svk199839 
6285570Svk199839 	return (s);
6295570Svk199839 }
6305570Svk199839 
6315570Svk199839 /*
6325570Svk199839  * readwrite()
6335570Svk199839  * Loop that polls on the network file descriptor and stdin.
6345570Svk199839  */
6355570Svk199839 void
6365570Svk199839 readwrite(int nfd)
6375570Svk199839 {
6385570Svk199839 	struct pollfd pfd[2];
6395570Svk199839 	unsigned char buf[8192];
6405570Svk199839 	int n, wfd = fileno(stdin);
6415570Svk199839 	int lfd = fileno(stdout);
6425570Svk199839 	int plen;
6435570Svk199839 
6445570Svk199839 	plen = 1024;
6455570Svk199839 
6465570Svk199839 	/* Setup Network FD */
6475570Svk199839 	pfd[0].fd = nfd;
6485570Svk199839 	pfd[0].events = POLLIN;
6495570Svk199839 
6505570Svk199839 	/* Set up STDIN FD. */
6515570Svk199839 	pfd[1].fd = wfd;
6525570Svk199839 	pfd[1].events = POLLIN;
6535570Svk199839 
6545570Svk199839 	while (pfd[0].fd != -1) {
6555570Svk199839 		if (iflag)
6565570Svk199839 			(void) sleep(iflag);
6575570Svk199839 
6585570Svk199839 		if ((n = poll(pfd, 2 - dflag, timeout)) < 0) {
6595570Svk199839 			(void) close(nfd);
6605570Svk199839 			err(1, "Polling Error");
6615570Svk199839 		}
6625570Svk199839 
6635570Svk199839 		if (n == 0)
6645570Svk199839 			return;
6655570Svk199839 
6665570Svk199839 		if (pfd[0].revents & (POLLIN|POLLHUP)) {
6675570Svk199839 			if ((n = read(nfd, buf, plen)) < 0)
6685570Svk199839 				return;
6695570Svk199839 			else if (n == 0) {
6705570Svk199839 				(void) shutdown(nfd, SHUT_RD);
6715570Svk199839 				pfd[0].fd = -1;
6725570Svk199839 				pfd[0].events = 0;
6735570Svk199839 			} else {
6745570Svk199839 				if (tflag)
6755570Svk199839 					atelnet(nfd, buf, n);
6765570Svk199839 				if (atomicio(vwrite, lfd, buf, n) != n)
6775570Svk199839 					return;
6785570Svk199839 			}
6795570Svk199839 		}
6805570Svk199839 
6815570Svk199839 		/*
6825570Svk199839 		 * handle the case of disconnected pipe: after pipe
6835570Svk199839 		 * is closed (indicated by POLLHUP) there may still
6845570Svk199839 		 * be some data lingering (POLLIN). After we read
6855570Svk199839 		 * the data, only POLLHUP remains, read() returns 0
6865570Svk199839 		 * and we are finished.
6875570Svk199839 		 */
6885570Svk199839 		if (!dflag && (pfd[1].revents & (POLLIN|POLLHUP))) {
6895570Svk199839 			if ((n = read(wfd, buf, plen)) < 0)
6905570Svk199839 				return;
6915570Svk199839 			else if (n == 0) {
6925570Svk199839 				(void) shutdown(nfd, SHUT_WR);
6935570Svk199839 				pfd[1].fd = -1;
6945570Svk199839 				pfd[1].events = 0;
6955570Svk199839 			} else {
6965570Svk199839 				if (atomicio(vwrite, nfd, buf, n) != n)
6975570Svk199839 					return;
6985570Svk199839 			}
6995570Svk199839 		}
7005570Svk199839 	}
7015570Svk199839 }
7025570Svk199839 
7035570Svk199839 /* Deal with RFC 854 WILL/WONT DO/DONT negotiation. */
7045570Svk199839 void
7055570Svk199839 atelnet(int nfd, unsigned char *buf, unsigned int size)
7065570Svk199839 {
7075570Svk199839 	unsigned char *p, *end;
7085570Svk199839 	unsigned char obuf[4];
7095570Svk199839 
7105570Svk199839 	end = buf + size;
7115570Svk199839 	obuf[0] = '\0';
7125570Svk199839 
7135570Svk199839 	for (p = buf; p < end; p++) {
7145570Svk199839 		if (*p != IAC)
7155570Svk199839 			break;
7165570Svk199839 
7175570Svk199839 		obuf[0] = IAC;
7185570Svk199839 		obuf[1] = 0;
7195570Svk199839 		p++;
7205570Svk199839 		/* refuse all options */
7215570Svk199839 		if ((*p == WILL) || (*p == WONT))
7225570Svk199839 			obuf[1] = DONT;
7235570Svk199839 		if ((*p == DO) || (*p == DONT))
7245570Svk199839 			obuf[1] = WONT;
7255570Svk199839 		if (obuf[1]) {
7265570Svk199839 			p++;
7275570Svk199839 			obuf[2] = *p;
7285570Svk199839 			obuf[3] = '\0';
7295570Svk199839 			if (atomicio(vwrite, nfd, obuf, 3) != 3)
7305570Svk199839 				warn("Write Error!");
7315570Svk199839 			obuf[0] = '\0';
7325570Svk199839 		}
7335570Svk199839 	}
7345570Svk199839 }
7355570Svk199839 
7365570Svk199839 /*
7375570Svk199839  * build_ports()
7385570Svk199839  * Build an array of ports in portlist[], listing each port
7395570Svk199839  * that we should try to connect to.
7405570Svk199839  */
7415570Svk199839 void
7425570Svk199839 build_ports(char *p)
7435570Svk199839 {
7445570Svk199839 	const char *errstr;
7455570Svk199839 	char *n;
7465570Svk199839 	int hi, lo, cp;
7475570Svk199839 	int x = 0;
7485570Svk199839 
7495570Svk199839 	if ((n = strchr(p, '-')) != NULL) {
7505570Svk199839 		if (lflag)
7515570Svk199839 			errx(1, "Cannot use -l with multiple ports!");
7525570Svk199839 
7535570Svk199839 		*n = '\0';
7545570Svk199839 		n++;
7555570Svk199839 
7565570Svk199839 		/* Make sure the ports are in order: lowest->highest. */
7575570Svk199839 		hi = strtonum(n, PORT_MIN, PORT_MAX, &errstr);
7585570Svk199839 		if (errstr)
7595570Svk199839 			errx(1, "port number %s: %s", errstr, n);
7605570Svk199839 		lo = strtonum(p, PORT_MIN, PORT_MAX, &errstr);
7615570Svk199839 		if (errstr)
7625570Svk199839 			errx(1, "port number %s: %s", errstr, p);
7635570Svk199839 
7645570Svk199839 		if (lo > hi) {
7655570Svk199839 			cp = hi;
7665570Svk199839 			hi = lo;
7675570Svk199839 			lo = cp;
7685570Svk199839 		}
7695570Svk199839 
7705570Svk199839 		/* Load ports sequentially. */
7715570Svk199839 		for (cp = lo; cp <= hi; cp++) {
7725570Svk199839 			portlist[x] = calloc(1, PORT_MAX_LEN);
7735570Svk199839 			if (portlist[x] == NULL)
7745570Svk199839 				err(1, NULL);
7755570Svk199839 			(void) snprintf(portlist[x], PORT_MAX_LEN, "%d", cp);
7765570Svk199839 			x++;
7775570Svk199839 		}
7785570Svk199839 
7795570Svk199839 		/* Randomly swap ports. */
7805570Svk199839 		if (rflag) {
7815570Svk199839 			int y;
7825570Svk199839 			char *c;
7835570Svk199839 
7845570Svk199839 			srandom(time((time_t *)0));
7855570Svk199839 
7865570Svk199839 			for (x = 0; x <= (hi - lo); x++) {
7875570Svk199839 				y = (random() & 0xFFFF) % (hi - lo);
7885570Svk199839 				c = portlist[x];
7895570Svk199839 				portlist[x] = portlist[y];
7905570Svk199839 				portlist[y] = c;
7915570Svk199839 			}
7925570Svk199839 		}
7935570Svk199839 	} else {
7945570Svk199839 		hi = strtonum(p, PORT_MIN, PORT_MAX, &errstr);
7955570Svk199839 		if (errstr)
7965570Svk199839 			errx(1, "port number %s: %s", errstr, p);
7975570Svk199839 		portlist[0] = calloc(1, PORT_MAX_LEN);
7985570Svk199839 		if (portlist[0] == NULL)
7995570Svk199839 			err(1, NULL);
8005570Svk199839 		portlist[0] = p;
8015570Svk199839 	}
8025570Svk199839 }
8035570Svk199839 
8045570Svk199839 /*
8055570Svk199839  * udptest()
8065570Svk199839  * Do a few writes to see if the UDP port is there.
8075570Svk199839  * XXX - Better way of doing this? Doesn't work for IPv6.
8085570Svk199839  * Also fails after around 100 ports checked.
8095570Svk199839  */
8105570Svk199839 int
8115570Svk199839 udptest(int s)
8125570Svk199839 {
8135570Svk199839 	int i, ret;
8145570Svk199839 
8155570Svk199839 	for (i = 0; i <= 3; i++) {
8165570Svk199839 		if (write(s, "X", 1) == 1)
8175570Svk199839 			ret = 1;
8185570Svk199839 		else
8195570Svk199839 			ret = -1;
8205570Svk199839 	}
8215570Svk199839 	return (ret);
8225570Svk199839 }
8235570Svk199839 
8245570Svk199839 void
8255570Svk199839 set_common_sockopts(int s)
8265570Svk199839 {
8275570Svk199839 	int x = 1;
8285570Svk199839 
8295570Svk199839 	if (Dflag) {
8305570Svk199839 		if (setsockopt(s, SOL_SOCKET, SO_DEBUG, &x, sizeof (x)) == -1)
8315570Svk199839 			err(1, NULL);
8325570Svk199839 	}
8335570Svk199839 	if (Tflag != -1) {
8345570Svk199839 		if (setsockopt(s, IPPROTO_IP, IP_TOS, &Tflag,
8355570Svk199839 		    sizeof (Tflag)) == -1)
8365570Svk199839 			err(1, "set IP ToS");
8375570Svk199839 	}
8385570Svk199839 }
8395570Svk199839 
8405570Svk199839 int
8415570Svk199839 parse_iptos(char *s)
8425570Svk199839 {
8435570Svk199839 	int tos = -1;
8445570Svk199839 
8455570Svk199839 	if (strcmp(s, "lowdelay") == 0)
8465570Svk199839 		return (IPTOS_LOWDELAY);
8475570Svk199839 	if (strcmp(s, "throughput") == 0)
8485570Svk199839 		return (IPTOS_THROUGHPUT);
8495570Svk199839 	if (strcmp(s, "reliability") == 0)
8505570Svk199839 		return (IPTOS_RELIABILITY);
8515570Svk199839 
8525570Svk199839 	if (sscanf(s, "0x%x", (unsigned int *) &tos) != 1 ||
8535570Svk199839 	    tos < 0 || tos > 0xff)
8545570Svk199839 		errx(1, "invalid IP Type of Service");
8555570Svk199839 	return (tos);
8565570Svk199839 }
8575570Svk199839 
8585570Svk199839 void
8595570Svk199839 help(void)
8605570Svk199839 {
8615570Svk199839 	usage(0);
8625570Svk199839 	(void) fprintf(stderr, "\tCommand Summary:\n\
8635570Svk199839 	\t-4		Use IPv4\n\
8645570Svk199839 	\t-6		Use IPv6\n\
8655570Svk199839 	\t-D		Enable the debug socket option\n\
8665570Svk199839 	\t-d		Detach from stdin\n\
8675570Svk199839 	\t-h		This help text\n\
8685570Svk199839 	\t-i secs\t	Delay interval for lines sent, ports scanned\n\
8695570Svk199839 	\t-k		Keep inbound sockets open for multiple connects\n\
8705570Svk199839 	\t-l		Listen mode, for inbound connects\n\
8715570Svk199839 	\t-n		Suppress name/port resolutions\n\
8725570Svk199839 	\t-P proxyuser\tUsername for proxy authentication\n\
8735570Svk199839 	\t-p port\t	Specify local port or listen port\n\
8745570Svk199839 	\t-r		Randomize remote ports\n\
8755570Svk199839 	\t-s addr\t	Local source address\n\
8765570Svk199839 	\t-T ToS\t	Set IP Type of Service\n\
8775570Svk199839 	\t-t		Answer TELNET negotiation\n\
8785570Svk199839 	\t-U		Use UNIX domain socket\n\
8795570Svk199839 	\t-u		UDP mode\n\
8805570Svk199839 	\t-v		Verbose\n\
8815570Svk199839 	\t-w secs\t	Timeout for connects and final net reads\n\
8825570Svk199839 	\t-X proto	Proxy protocol: \"4\", \"5\" (SOCKS) or \"connect\"\n\
8835570Svk199839 	\t-x addr[:port]\tSpecify proxy address and port\n\
8845570Svk199839 	\t-z		Zero-I/O mode [used for scanning]\n\
8855570Svk199839 	Port numbers can be individual or ranges: lo-hi [inclusive]\n");
8865570Svk199839 	exit(1);
8875570Svk199839 }
8885570Svk199839 
8895570Svk199839 void
8905570Svk199839 usage(int ret)
8915570Svk199839 {
8925570Svk199839 	(void) fprintf(stderr,
8935570Svk199839 	    "usage: nc [-46DdhklnrtUuvz] [-i interval] [-P proxy_username]"
8945570Svk199839 	    " [-p port]\n");
8955570Svk199839 	(void) fprintf(stderr,
8965570Svk199839 	    "\t  [-s source_ip_address] [-T ToS] [-w timeout]"
8975570Svk199839 	    " [-X proxy_protocol]\n");
8985570Svk199839 	(void) fprintf(stderr,
8995570Svk199839 	    "\t  [-x proxy_address[:port]] [hostname]"
9005570Svk199839 	    " [port[s]]\n");
9015570Svk199839 	if (ret)
9025570Svk199839 		exit(1);
9035570Svk199839 }
904