xref: /freebsd/crypto/openssh/regress/netcat.c (revision 47dd1d1b619cc035b82b49a91a25544309ff95ae)
1bc5531deSDag-Erling Smørgrav /* $OpenBSD: netcat.c,v 1.126 2014/10/30 16:08:31 tedu Exp $ */
2bc5531deSDag-Erling Smørgrav /*
3bc5531deSDag-Erling Smørgrav  * Copyright (c) 2001 Eric Jackson <ericj@monkey.org>
4bc5531deSDag-Erling Smørgrav  *
5bc5531deSDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
6bc5531deSDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
7bc5531deSDag-Erling Smørgrav  * are met:
8bc5531deSDag-Erling Smørgrav  *
9bc5531deSDag-Erling Smørgrav  * 1. Redistributions of source code must retain the above copyright
10bc5531deSDag-Erling Smørgrav  *   notice, this list of conditions and the following disclaimer.
11bc5531deSDag-Erling Smørgrav  * 2. Redistributions in binary form must reproduce the above copyright
12bc5531deSDag-Erling Smørgrav  *   notice, this list of conditions and the following disclaimer in the
13bc5531deSDag-Erling Smørgrav  *   documentation and/or other materials provided with the distribution.
14bc5531deSDag-Erling Smørgrav  * 3. The name of the author may not be used to endorse or promote products
15bc5531deSDag-Erling Smørgrav  *   derived from this software without specific prior written permission.
16bc5531deSDag-Erling Smørgrav  *
17bc5531deSDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18bc5531deSDag-Erling Smørgrav  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19bc5531deSDag-Erling Smørgrav  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20bc5531deSDag-Erling Smørgrav  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21bc5531deSDag-Erling Smørgrav  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22bc5531deSDag-Erling Smørgrav  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23bc5531deSDag-Erling Smørgrav  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24bc5531deSDag-Erling Smørgrav  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25bc5531deSDag-Erling Smørgrav  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26bc5531deSDag-Erling Smørgrav  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27bc5531deSDag-Erling Smørgrav  */
28bc5531deSDag-Erling Smørgrav 
29bc5531deSDag-Erling Smørgrav /*
30bc5531deSDag-Erling Smørgrav  * Re-written nc(1) for OpenBSD. Original implementation by
31bc5531deSDag-Erling Smørgrav  * *Hobbit* <hobbit@avian.org>.
32bc5531deSDag-Erling Smørgrav  */
33bc5531deSDag-Erling Smørgrav 
34bc5531deSDag-Erling Smørgrav #include "includes.h"
35bc5531deSDag-Erling Smørgrav 
36bc5531deSDag-Erling Smørgrav #include <sys/types.h>
37bc5531deSDag-Erling Smørgrav #include <sys/socket.h>
38bc5531deSDag-Erling Smørgrav #include <sys/time.h>
39bc5531deSDag-Erling Smørgrav #include <sys/uio.h>
40bc5531deSDag-Erling Smørgrav #include <sys/un.h>
41bc5531deSDag-Erling Smørgrav 
42bc5531deSDag-Erling Smørgrav #include <netinet/in.h>
43bc5531deSDag-Erling Smørgrav #include <netinet/tcp.h>
44bc5531deSDag-Erling Smørgrav #include <netinet/ip.h>
45bc5531deSDag-Erling Smørgrav 
46bc5531deSDag-Erling Smørgrav #include <errno.h>
47bc5531deSDag-Erling Smørgrav #include <netdb.h>
48bc5531deSDag-Erling Smørgrav #include <stdarg.h>
49bc5531deSDag-Erling Smørgrav #include <stdio.h>
50bc5531deSDag-Erling Smørgrav #include <stdlib.h>
51bc5531deSDag-Erling Smørgrav #include <string.h>
52bc5531deSDag-Erling Smørgrav #include <unistd.h>
53bc5531deSDag-Erling Smørgrav #include <fcntl.h>
54bc5531deSDag-Erling Smørgrav #include <limits.h>
55bc5531deSDag-Erling Smørgrav #include "atomicio.h"
56bc5531deSDag-Erling Smørgrav 
57bc5531deSDag-Erling Smørgrav #ifdef HAVE_POLL_H
58bc5531deSDag-Erling Smørgrav #include <poll.h>
59bc5531deSDag-Erling Smørgrav #else
60bc5531deSDag-Erling Smørgrav # ifdef HAVE_SYS_POLL_H
61bc5531deSDag-Erling Smørgrav #  include <sys/poll.h>
62bc5531deSDag-Erling Smørgrav # endif
63bc5531deSDag-Erling Smørgrav #endif
64076ad2f8SDag-Erling Smørgrav #ifdef HAVE_ERR_H
65076ad2f8SDag-Erling Smørgrav # include <err.h>
66076ad2f8SDag-Erling Smørgrav #endif
67bc5531deSDag-Erling Smørgrav 
68557f75e5SDag-Erling Smørgrav /* Telnet options from arpa/telnet.h */
69557f75e5SDag-Erling Smørgrav #define IAC	255
70557f75e5SDag-Erling Smørgrav #define DONT	254
71557f75e5SDag-Erling Smørgrav #define DO	253
72557f75e5SDag-Erling Smørgrav #define WONT	252
73557f75e5SDag-Erling Smørgrav #define WILL	251
74557f75e5SDag-Erling Smørgrav 
75bc5531deSDag-Erling Smørgrav #ifndef SUN_LEN
76bc5531deSDag-Erling Smørgrav #define SUN_LEN(su) \
77bc5531deSDag-Erling Smørgrav 	(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
78bc5531deSDag-Erling Smørgrav #endif
79bc5531deSDag-Erling Smørgrav 
80bc5531deSDag-Erling Smørgrav #define PORT_MAX	65535
81bc5531deSDag-Erling Smørgrav #define PORT_MAX_LEN	6
82bc5531deSDag-Erling Smørgrav #define UNIX_DG_TMP_SOCKET_SIZE	19
83bc5531deSDag-Erling Smørgrav 
84bc5531deSDag-Erling Smørgrav #define POLL_STDIN 0
85bc5531deSDag-Erling Smørgrav #define POLL_NETOUT 1
86bc5531deSDag-Erling Smørgrav #define POLL_NETIN 2
87bc5531deSDag-Erling Smørgrav #define POLL_STDOUT 3
88bc5531deSDag-Erling Smørgrav #define BUFSIZE 16384
89bc5531deSDag-Erling Smørgrav 
90bc5531deSDag-Erling Smørgrav /* Command Line Options */
91bc5531deSDag-Erling Smørgrav int	dflag;					/* detached, no stdin */
92bc5531deSDag-Erling Smørgrav int	Fflag;					/* fdpass sock to stdout */
93bc5531deSDag-Erling Smørgrav unsigned int iflag;				/* Interval Flag */
94bc5531deSDag-Erling Smørgrav int	kflag;					/* More than one connect */
95bc5531deSDag-Erling Smørgrav int	lflag;					/* Bind to local port */
96bc5531deSDag-Erling Smørgrav int	Nflag;					/* shutdown() network socket */
97bc5531deSDag-Erling Smørgrav int	nflag;					/* Don't do name look up */
98bc5531deSDag-Erling Smørgrav char   *Pflag;					/* Proxy username */
99bc5531deSDag-Erling Smørgrav char   *pflag;					/* Localport flag */
100bc5531deSDag-Erling Smørgrav int	rflag;					/* Random ports flag */
101bc5531deSDag-Erling Smørgrav char   *sflag;					/* Source Address */
102bc5531deSDag-Erling Smørgrav int	tflag;					/* Telnet Emulation */
103bc5531deSDag-Erling Smørgrav int	uflag;					/* UDP - Default to TCP */
104bc5531deSDag-Erling Smørgrav int	vflag;					/* Verbosity */
105bc5531deSDag-Erling Smørgrav int	xflag;					/* Socks proxy */
106bc5531deSDag-Erling Smørgrav int	zflag;					/* Port Scan Flag */
107bc5531deSDag-Erling Smørgrav int	Dflag;					/* sodebug */
108bc5531deSDag-Erling Smørgrav int	Iflag;					/* TCP receive buffer size */
109bc5531deSDag-Erling Smørgrav int	Oflag;					/* TCP send buffer size */
110bc5531deSDag-Erling Smørgrav int	Sflag;					/* TCP MD5 signature option */
111bc5531deSDag-Erling Smørgrav int	Tflag = -1;				/* IP Type of Service */
112bc5531deSDag-Erling Smørgrav int	rtableid = -1;
113bc5531deSDag-Erling Smørgrav 
114bc5531deSDag-Erling Smørgrav int timeout = -1;
115bc5531deSDag-Erling Smørgrav int family = AF_UNSPEC;
116bc5531deSDag-Erling Smørgrav char *portlist[PORT_MAX+1];
117bc5531deSDag-Erling Smørgrav char *unix_dg_tmp_socket;
118bc5531deSDag-Erling Smørgrav 
119bc5531deSDag-Erling Smørgrav void	atelnet(int, unsigned char *, unsigned int);
120bc5531deSDag-Erling Smørgrav void	build_ports(char *);
121bc5531deSDag-Erling Smørgrav void	help(void);
122bc5531deSDag-Erling Smørgrav int	local_listen(char *, char *, struct addrinfo);
123bc5531deSDag-Erling Smørgrav void	readwrite(int);
124bc5531deSDag-Erling Smørgrav void	fdpass(int nfd) __attribute__((noreturn));
125bc5531deSDag-Erling Smørgrav int	remote_connect(const char *, const char *, struct addrinfo);
126bc5531deSDag-Erling Smørgrav int	timeout_connect(int, const struct sockaddr *, socklen_t);
127bc5531deSDag-Erling Smørgrav int	socks_connect(const char *, const char *, struct addrinfo,
128bc5531deSDag-Erling Smørgrav 	    const char *, const char *, struct addrinfo, int, const char *);
129bc5531deSDag-Erling Smørgrav int	udptest(int);
130bc5531deSDag-Erling Smørgrav int	unix_bind(char *);
131bc5531deSDag-Erling Smørgrav int	unix_connect(char *);
132bc5531deSDag-Erling Smørgrav int	unix_listen(char *);
133bc5531deSDag-Erling Smørgrav void	set_common_sockopts(int);
134bc5531deSDag-Erling Smørgrav int	map_tos(char *, int *);
135bc5531deSDag-Erling Smørgrav void	report_connect(const struct sockaddr *, socklen_t);
136bc5531deSDag-Erling Smørgrav void	usage(int);
137bc5531deSDag-Erling Smørgrav ssize_t drainbuf(int, unsigned char *, size_t *);
138bc5531deSDag-Erling Smørgrav ssize_t fillbuf(int, unsigned char *, size_t *);
139bc5531deSDag-Erling Smørgrav 
140bc5531deSDag-Erling Smørgrav 
141bc5531deSDag-Erling Smørgrav int
142bc5531deSDag-Erling Smørgrav main(int argc, char *argv[])
143bc5531deSDag-Erling Smørgrav {
144bc5531deSDag-Erling Smørgrav 	int ch, s, ret, socksv;
145bc5531deSDag-Erling Smørgrav 	char *host, *uport;
146bc5531deSDag-Erling Smørgrav 	struct addrinfo hints;
147bc5531deSDag-Erling Smørgrav 	struct servent *sv;
148bc5531deSDag-Erling Smørgrav 	socklen_t len;
149bc5531deSDag-Erling Smørgrav 	struct sockaddr_storage cliaddr;
150bc5531deSDag-Erling Smørgrav 	char *proxy = NULL;
151bc5531deSDag-Erling Smørgrav 	const char *errstr, *proxyhost = "", *proxyport = NULL;
152bc5531deSDag-Erling Smørgrav 	struct addrinfo proxyhints;
153bc5531deSDag-Erling Smørgrav 	char unix_dg_tmp_socket_buf[UNIX_DG_TMP_SOCKET_SIZE];
154bc5531deSDag-Erling Smørgrav 
155bc5531deSDag-Erling Smørgrav 	ret = 1;
156bc5531deSDag-Erling Smørgrav 	s = 0;
157bc5531deSDag-Erling Smørgrav 	socksv = 5;
158bc5531deSDag-Erling Smørgrav 	host = NULL;
159bc5531deSDag-Erling Smørgrav 	uport = NULL;
160bc5531deSDag-Erling Smørgrav 	sv = NULL;
161bc5531deSDag-Erling Smørgrav 
162bc5531deSDag-Erling Smørgrav 	while ((ch = getopt(argc, argv,
163bc5531deSDag-Erling Smørgrav 	    "46DdFhI:i:klNnO:P:p:rSs:tT:UuV:vw:X:x:z")) != -1) {
164bc5531deSDag-Erling Smørgrav 		switch (ch) {
165bc5531deSDag-Erling Smørgrav 		case '4':
166bc5531deSDag-Erling Smørgrav 			family = AF_INET;
167bc5531deSDag-Erling Smørgrav 			break;
168bc5531deSDag-Erling Smørgrav 		case '6':
169bc5531deSDag-Erling Smørgrav 			family = AF_INET6;
170bc5531deSDag-Erling Smørgrav 			break;
171bc5531deSDag-Erling Smørgrav 		case 'U':
172bc5531deSDag-Erling Smørgrav 			family = AF_UNIX;
173bc5531deSDag-Erling Smørgrav 			break;
174bc5531deSDag-Erling Smørgrav 		case 'X':
175bc5531deSDag-Erling Smørgrav 			if (strcasecmp(optarg, "connect") == 0)
176bc5531deSDag-Erling Smørgrav 				socksv = -1; /* HTTP proxy CONNECT */
177bc5531deSDag-Erling Smørgrav 			else if (strcmp(optarg, "4") == 0)
178bc5531deSDag-Erling Smørgrav 				socksv = 4; /* SOCKS v.4 */
179bc5531deSDag-Erling Smørgrav 			else if (strcmp(optarg, "5") == 0)
180bc5531deSDag-Erling Smørgrav 				socksv = 5; /* SOCKS v.5 */
181bc5531deSDag-Erling Smørgrav 			else
182bc5531deSDag-Erling Smørgrav 				errx(1, "unsupported proxy protocol");
183bc5531deSDag-Erling Smørgrav 			break;
184bc5531deSDag-Erling Smørgrav 		case 'd':
185bc5531deSDag-Erling Smørgrav 			dflag = 1;
186bc5531deSDag-Erling Smørgrav 			break;
187bc5531deSDag-Erling Smørgrav 		case 'F':
188bc5531deSDag-Erling Smørgrav 			Fflag = 1;
189bc5531deSDag-Erling Smørgrav 			break;
190bc5531deSDag-Erling Smørgrav 		case 'h':
191bc5531deSDag-Erling Smørgrav 			help();
192bc5531deSDag-Erling Smørgrav 			break;
193bc5531deSDag-Erling Smørgrav 		case 'i':
194bc5531deSDag-Erling Smørgrav 			iflag = strtonum(optarg, 0, UINT_MAX, &errstr);
195bc5531deSDag-Erling Smørgrav 			if (errstr)
196bc5531deSDag-Erling Smørgrav 				errx(1, "interval %s: %s", errstr, optarg);
197bc5531deSDag-Erling Smørgrav 			break;
198bc5531deSDag-Erling Smørgrav 		case 'k':
199bc5531deSDag-Erling Smørgrav 			kflag = 1;
200bc5531deSDag-Erling Smørgrav 			break;
201bc5531deSDag-Erling Smørgrav 		case 'l':
202bc5531deSDag-Erling Smørgrav 			lflag = 1;
203bc5531deSDag-Erling Smørgrav 			break;
204bc5531deSDag-Erling Smørgrav 		case 'N':
205bc5531deSDag-Erling Smørgrav 			Nflag = 1;
206bc5531deSDag-Erling Smørgrav 			break;
207bc5531deSDag-Erling Smørgrav 		case 'n':
208bc5531deSDag-Erling Smørgrav 			nflag = 1;
209bc5531deSDag-Erling Smørgrav 			break;
210bc5531deSDag-Erling Smørgrav 		case 'P':
211bc5531deSDag-Erling Smørgrav 			Pflag = optarg;
212bc5531deSDag-Erling Smørgrav 			break;
213bc5531deSDag-Erling Smørgrav 		case 'p':
214bc5531deSDag-Erling Smørgrav 			pflag = optarg;
215bc5531deSDag-Erling Smørgrav 			break;
216bc5531deSDag-Erling Smørgrav 		case 'r':
217bc5531deSDag-Erling Smørgrav 			rflag = 1;
218bc5531deSDag-Erling Smørgrav 			break;
219bc5531deSDag-Erling Smørgrav 		case 's':
220bc5531deSDag-Erling Smørgrav 			sflag = optarg;
221bc5531deSDag-Erling Smørgrav 			break;
222bc5531deSDag-Erling Smørgrav 		case 't':
223bc5531deSDag-Erling Smørgrav 			tflag = 1;
224bc5531deSDag-Erling Smørgrav 			break;
225bc5531deSDag-Erling Smørgrav 		case 'u':
226bc5531deSDag-Erling Smørgrav 			uflag = 1;
227bc5531deSDag-Erling Smørgrav 			break;
228bc5531deSDag-Erling Smørgrav #ifdef SO_RTABLE
229bc5531deSDag-Erling Smørgrav 		case 'V':
230bc5531deSDag-Erling Smørgrav 			rtableid = (int)strtonum(optarg, 0,
231bc5531deSDag-Erling Smørgrav 			    RT_TABLEID_MAX, &errstr);
232bc5531deSDag-Erling Smørgrav 			if (errstr)
233bc5531deSDag-Erling Smørgrav 				errx(1, "rtable %s: %s", errstr, optarg);
234bc5531deSDag-Erling Smørgrav 			break;
235bc5531deSDag-Erling Smørgrav #endif
236bc5531deSDag-Erling Smørgrav 		case 'v':
237bc5531deSDag-Erling Smørgrav 			vflag = 1;
238bc5531deSDag-Erling Smørgrav 			break;
239bc5531deSDag-Erling Smørgrav 		case 'w':
240bc5531deSDag-Erling Smørgrav 			timeout = strtonum(optarg, 0, INT_MAX / 1000, &errstr);
241bc5531deSDag-Erling Smørgrav 			if (errstr)
242bc5531deSDag-Erling Smørgrav 				errx(1, "timeout %s: %s", errstr, optarg);
243bc5531deSDag-Erling Smørgrav 			timeout *= 1000;
244bc5531deSDag-Erling Smørgrav 			break;
245bc5531deSDag-Erling Smørgrav 		case 'x':
246bc5531deSDag-Erling Smørgrav 			xflag = 1;
247bc5531deSDag-Erling Smørgrav 			if ((proxy = strdup(optarg)) == NULL)
248bc5531deSDag-Erling Smørgrav 				errx(1, "strdup");
249bc5531deSDag-Erling Smørgrav 			break;
250bc5531deSDag-Erling Smørgrav 		case 'z':
251bc5531deSDag-Erling Smørgrav 			zflag = 1;
252bc5531deSDag-Erling Smørgrav 			break;
253bc5531deSDag-Erling Smørgrav 		case 'D':
254bc5531deSDag-Erling Smørgrav 			Dflag = 1;
255bc5531deSDag-Erling Smørgrav 			break;
256bc5531deSDag-Erling Smørgrav 		case 'I':
257bc5531deSDag-Erling Smørgrav 			Iflag = strtonum(optarg, 1, 65536 << 14, &errstr);
258bc5531deSDag-Erling Smørgrav 			if (errstr != NULL)
259bc5531deSDag-Erling Smørgrav 				errx(1, "TCP receive window %s: %s",
260bc5531deSDag-Erling Smørgrav 				    errstr, optarg);
261bc5531deSDag-Erling Smørgrav 			break;
262bc5531deSDag-Erling Smørgrav 		case 'O':
263bc5531deSDag-Erling Smørgrav 			Oflag = strtonum(optarg, 1, 65536 << 14, &errstr);
264bc5531deSDag-Erling Smørgrav 			if (errstr != NULL)
265bc5531deSDag-Erling Smørgrav 				errx(1, "TCP send window %s: %s",
266bc5531deSDag-Erling Smørgrav 				    errstr, optarg);
267bc5531deSDag-Erling Smørgrav 			break;
268bc5531deSDag-Erling Smørgrav 		case 'S':
269bc5531deSDag-Erling Smørgrav 			Sflag = 1;
270bc5531deSDag-Erling Smørgrav 			break;
271bc5531deSDag-Erling Smørgrav 		case 'T':
272bc5531deSDag-Erling Smørgrav 			errstr = NULL;
273bc5531deSDag-Erling Smørgrav 			errno = 0;
274bc5531deSDag-Erling Smørgrav 			if (map_tos(optarg, &Tflag))
275bc5531deSDag-Erling Smørgrav 				break;
276bc5531deSDag-Erling Smørgrav 			if (strlen(optarg) > 1 && optarg[0] == '0' &&
277bc5531deSDag-Erling Smørgrav 			    optarg[1] == 'x')
278bc5531deSDag-Erling Smørgrav 				Tflag = (int)strtol(optarg, NULL, 16);
279bc5531deSDag-Erling Smørgrav 			else
280bc5531deSDag-Erling Smørgrav 				Tflag = (int)strtonum(optarg, 0, 255,
281bc5531deSDag-Erling Smørgrav 				    &errstr);
282bc5531deSDag-Erling Smørgrav 			if (Tflag < 0 || Tflag > 255 || errstr || errno)
283bc5531deSDag-Erling Smørgrav 				errx(1, "illegal tos value %s", optarg);
284bc5531deSDag-Erling Smørgrav 			break;
285bc5531deSDag-Erling Smørgrav 		default:
286bc5531deSDag-Erling Smørgrav 			usage(1);
287bc5531deSDag-Erling Smørgrav 		}
288bc5531deSDag-Erling Smørgrav 	}
289bc5531deSDag-Erling Smørgrav 	argc -= optind;
290bc5531deSDag-Erling Smørgrav 	argv += optind;
291bc5531deSDag-Erling Smørgrav 
292bc5531deSDag-Erling Smørgrav 	/* Cruft to make sure options are clean, and used properly. */
293bc5531deSDag-Erling Smørgrav 	if (argv[0] && !argv[1] && family == AF_UNIX) {
294bc5531deSDag-Erling Smørgrav 		host = argv[0];
295bc5531deSDag-Erling Smørgrav 		uport = NULL;
296bc5531deSDag-Erling Smørgrav 	} else if (argv[0] && !argv[1]) {
297bc5531deSDag-Erling Smørgrav 		if  (!lflag)
298bc5531deSDag-Erling Smørgrav 			usage(1);
299bc5531deSDag-Erling Smørgrav 		uport = argv[0];
300bc5531deSDag-Erling Smørgrav 		host = NULL;
301bc5531deSDag-Erling Smørgrav 	} else if (argv[0] && argv[1]) {
302bc5531deSDag-Erling Smørgrav 		host = argv[0];
303bc5531deSDag-Erling Smørgrav 		uport = argv[1];
304bc5531deSDag-Erling Smørgrav 	} else
305bc5531deSDag-Erling Smørgrav 		usage(1);
306bc5531deSDag-Erling Smørgrav 
307bc5531deSDag-Erling Smørgrav 	if (lflag && sflag)
308bc5531deSDag-Erling Smørgrav 		errx(1, "cannot use -s and -l");
309bc5531deSDag-Erling Smørgrav 	if (lflag && pflag)
310bc5531deSDag-Erling Smørgrav 		errx(1, "cannot use -p and -l");
311bc5531deSDag-Erling Smørgrav 	if (lflag && zflag)
312bc5531deSDag-Erling Smørgrav 		errx(1, "cannot use -z and -l");
313bc5531deSDag-Erling Smørgrav 	if (!lflag && kflag)
314bc5531deSDag-Erling Smørgrav 		errx(1, "must use -l with -k");
315bc5531deSDag-Erling Smørgrav 
316bc5531deSDag-Erling Smørgrav 	/* Get name of temporary socket for unix datagram client */
317bc5531deSDag-Erling Smørgrav 	if ((family == AF_UNIX) && uflag && !lflag) {
318bc5531deSDag-Erling Smørgrav 		if (sflag) {
319bc5531deSDag-Erling Smørgrav 			unix_dg_tmp_socket = sflag;
320bc5531deSDag-Erling Smørgrav 		} else {
321bc5531deSDag-Erling Smørgrav 			strlcpy(unix_dg_tmp_socket_buf, "/tmp/nc.XXXXXXXXXX",
322bc5531deSDag-Erling Smørgrav 				UNIX_DG_TMP_SOCKET_SIZE);
323bc5531deSDag-Erling Smørgrav 			if (mktemp(unix_dg_tmp_socket_buf) == NULL)
324bc5531deSDag-Erling Smørgrav 				err(1, "mktemp");
325bc5531deSDag-Erling Smørgrav 			unix_dg_tmp_socket = unix_dg_tmp_socket_buf;
326bc5531deSDag-Erling Smørgrav 		}
327bc5531deSDag-Erling Smørgrav 	}
328bc5531deSDag-Erling Smørgrav 
329bc5531deSDag-Erling Smørgrav 	/* Initialize addrinfo structure. */
330bc5531deSDag-Erling Smørgrav 	if (family != AF_UNIX) {
331bc5531deSDag-Erling Smørgrav 		memset(&hints, 0, sizeof(struct addrinfo));
332bc5531deSDag-Erling Smørgrav 		hints.ai_family = family;
333bc5531deSDag-Erling Smørgrav 		hints.ai_socktype = uflag ? SOCK_DGRAM : SOCK_STREAM;
334bc5531deSDag-Erling Smørgrav 		hints.ai_protocol = uflag ? IPPROTO_UDP : IPPROTO_TCP;
335bc5531deSDag-Erling Smørgrav 		if (nflag)
336bc5531deSDag-Erling Smørgrav 			hints.ai_flags |= AI_NUMERICHOST;
337bc5531deSDag-Erling Smørgrav 	}
338bc5531deSDag-Erling Smørgrav 
339bc5531deSDag-Erling Smørgrav 	if (xflag) {
340bc5531deSDag-Erling Smørgrav 		if (uflag)
341bc5531deSDag-Erling Smørgrav 			errx(1, "no proxy support for UDP mode");
342bc5531deSDag-Erling Smørgrav 
343bc5531deSDag-Erling Smørgrav 		if (lflag)
344bc5531deSDag-Erling Smørgrav 			errx(1, "no proxy support for listen");
345bc5531deSDag-Erling Smørgrav 
346bc5531deSDag-Erling Smørgrav 		if (family == AF_UNIX)
347bc5531deSDag-Erling Smørgrav 			errx(1, "no proxy support for unix sockets");
348bc5531deSDag-Erling Smørgrav 
349bc5531deSDag-Erling Smørgrav 		/* XXX IPv6 transport to proxy would probably work */
350bc5531deSDag-Erling Smørgrav 		if (family == AF_INET6)
351bc5531deSDag-Erling Smørgrav 			errx(1, "no proxy support for IPv6");
352bc5531deSDag-Erling Smørgrav 
353bc5531deSDag-Erling Smørgrav 		if (sflag)
354bc5531deSDag-Erling Smørgrav 			errx(1, "no proxy support for local source address");
355bc5531deSDag-Erling Smørgrav 
356bc5531deSDag-Erling Smørgrav 		proxyhost = strsep(&proxy, ":");
357bc5531deSDag-Erling Smørgrav 		proxyport = proxy;
358bc5531deSDag-Erling Smørgrav 
359bc5531deSDag-Erling Smørgrav 		memset(&proxyhints, 0, sizeof(struct addrinfo));
360bc5531deSDag-Erling Smørgrav 		proxyhints.ai_family = family;
361bc5531deSDag-Erling Smørgrav 		proxyhints.ai_socktype = SOCK_STREAM;
362bc5531deSDag-Erling Smørgrav 		proxyhints.ai_protocol = IPPROTO_TCP;
363bc5531deSDag-Erling Smørgrav 		if (nflag)
364bc5531deSDag-Erling Smørgrav 			proxyhints.ai_flags |= AI_NUMERICHOST;
365bc5531deSDag-Erling Smørgrav 	}
366bc5531deSDag-Erling Smørgrav 
367bc5531deSDag-Erling Smørgrav 	if (lflag) {
368bc5531deSDag-Erling Smørgrav 		int connfd;
369bc5531deSDag-Erling Smørgrav 		ret = 0;
370bc5531deSDag-Erling Smørgrav 
371bc5531deSDag-Erling Smørgrav 		if (family == AF_UNIX) {
372bc5531deSDag-Erling Smørgrav 			if (uflag)
373bc5531deSDag-Erling Smørgrav 				s = unix_bind(host);
374bc5531deSDag-Erling Smørgrav 			else
375bc5531deSDag-Erling Smørgrav 				s = unix_listen(host);
376bc5531deSDag-Erling Smørgrav 		}
377bc5531deSDag-Erling Smørgrav 
378bc5531deSDag-Erling Smørgrav 		/* Allow only one connection at a time, but stay alive. */
379bc5531deSDag-Erling Smørgrav 		for (;;) {
380bc5531deSDag-Erling Smørgrav 			if (family != AF_UNIX)
381bc5531deSDag-Erling Smørgrav 				s = local_listen(host, uport, hints);
382bc5531deSDag-Erling Smørgrav 			if (s < 0)
383bc5531deSDag-Erling Smørgrav 				err(1, "local_listen");
384bc5531deSDag-Erling Smørgrav 			/*
385bc5531deSDag-Erling Smørgrav 			 * For UDP and -k, don't connect the socket, let it
386bc5531deSDag-Erling Smørgrav 			 * receive datagrams from multiple socket pairs.
387bc5531deSDag-Erling Smørgrav 			 */
388bc5531deSDag-Erling Smørgrav 			if (uflag && kflag)
389bc5531deSDag-Erling Smørgrav 				readwrite(s);
390bc5531deSDag-Erling Smørgrav 			/*
391bc5531deSDag-Erling Smørgrav 			 * For UDP and not -k, we will use recvfrom() initially
392bc5531deSDag-Erling Smørgrav 			 * to wait for a caller, then use the regular functions
393bc5531deSDag-Erling Smørgrav 			 * to talk to the caller.
394bc5531deSDag-Erling Smørgrav 			 */
395bc5531deSDag-Erling Smørgrav 			else if (uflag && !kflag) {
396bc5531deSDag-Erling Smørgrav 				int rv, plen;
397bc5531deSDag-Erling Smørgrav 				char buf[16384];
398bc5531deSDag-Erling Smørgrav 				struct sockaddr_storage z;
399bc5531deSDag-Erling Smørgrav 
400bc5531deSDag-Erling Smørgrav 				len = sizeof(z);
401bc5531deSDag-Erling Smørgrav 				plen = 2048;
402bc5531deSDag-Erling Smørgrav 				rv = recvfrom(s, buf, plen, MSG_PEEK,
403bc5531deSDag-Erling Smørgrav 				    (struct sockaddr *)&z, &len);
404bc5531deSDag-Erling Smørgrav 				if (rv < 0)
405bc5531deSDag-Erling Smørgrav 					err(1, "recvfrom");
406bc5531deSDag-Erling Smørgrav 
407bc5531deSDag-Erling Smørgrav 				rv = connect(s, (struct sockaddr *)&z, len);
408bc5531deSDag-Erling Smørgrav 				if (rv < 0)
409bc5531deSDag-Erling Smørgrav 					err(1, "connect");
410bc5531deSDag-Erling Smørgrav 
411bc5531deSDag-Erling Smørgrav 				if (vflag)
412bc5531deSDag-Erling Smørgrav 					report_connect((struct sockaddr *)&z, len);
413bc5531deSDag-Erling Smørgrav 
414bc5531deSDag-Erling Smørgrav 				readwrite(s);
415bc5531deSDag-Erling Smørgrav 			} else {
416bc5531deSDag-Erling Smørgrav 				len = sizeof(cliaddr);
417bc5531deSDag-Erling Smørgrav 				connfd = accept(s, (struct sockaddr *)&cliaddr,
418bc5531deSDag-Erling Smørgrav 				    &len);
419bc5531deSDag-Erling Smørgrav 				if (connfd == -1) {
420bc5531deSDag-Erling Smørgrav 					/* For now, all errnos are fatal */
421bc5531deSDag-Erling Smørgrav 					err(1, "accept");
422bc5531deSDag-Erling Smørgrav 				}
423bc5531deSDag-Erling Smørgrav 				if (vflag)
424bc5531deSDag-Erling Smørgrav 					report_connect((struct sockaddr *)&cliaddr, len);
425bc5531deSDag-Erling Smørgrav 
426bc5531deSDag-Erling Smørgrav 				readwrite(connfd);
427bc5531deSDag-Erling Smørgrav 				close(connfd);
428bc5531deSDag-Erling Smørgrav 			}
429bc5531deSDag-Erling Smørgrav 
430bc5531deSDag-Erling Smørgrav 			if (family != AF_UNIX)
431bc5531deSDag-Erling Smørgrav 				close(s);
432bc5531deSDag-Erling Smørgrav 			else if (uflag) {
433bc5531deSDag-Erling Smørgrav 				if (connect(s, NULL, 0) < 0)
434bc5531deSDag-Erling Smørgrav 					err(1, "connect");
435bc5531deSDag-Erling Smørgrav 			}
436bc5531deSDag-Erling Smørgrav 
437bc5531deSDag-Erling Smørgrav 			if (!kflag)
438bc5531deSDag-Erling Smørgrav 				break;
439bc5531deSDag-Erling Smørgrav 		}
440bc5531deSDag-Erling Smørgrav 	} else if (family == AF_UNIX) {
441bc5531deSDag-Erling Smørgrav 		ret = 0;
442bc5531deSDag-Erling Smørgrav 
443bc5531deSDag-Erling Smørgrav 		if ((s = unix_connect(host)) > 0 && !zflag) {
444bc5531deSDag-Erling Smørgrav 			readwrite(s);
445bc5531deSDag-Erling Smørgrav 			close(s);
446bc5531deSDag-Erling Smørgrav 		} else
447bc5531deSDag-Erling Smørgrav 			ret = 1;
448bc5531deSDag-Erling Smørgrav 
449bc5531deSDag-Erling Smørgrav 		if (uflag)
450bc5531deSDag-Erling Smørgrav 			unlink(unix_dg_tmp_socket);
451bc5531deSDag-Erling Smørgrav 		exit(ret);
452bc5531deSDag-Erling Smørgrav 
453bc5531deSDag-Erling Smørgrav 	} else {
454bc5531deSDag-Erling Smørgrav 		int i = 0;
455bc5531deSDag-Erling Smørgrav 
456bc5531deSDag-Erling Smørgrav 		/* Construct the portlist[] array. */
457bc5531deSDag-Erling Smørgrav 		build_ports(uport);
458bc5531deSDag-Erling Smørgrav 
459bc5531deSDag-Erling Smørgrav 		/* Cycle through portlist, connecting to each port. */
460bc5531deSDag-Erling Smørgrav 		for (i = 0; portlist[i] != NULL; i++) {
461bc5531deSDag-Erling Smørgrav 			if (s)
462bc5531deSDag-Erling Smørgrav 				close(s);
463bc5531deSDag-Erling Smørgrav 
464bc5531deSDag-Erling Smørgrav 			if (xflag)
465bc5531deSDag-Erling Smørgrav 				s = socks_connect(host, portlist[i], hints,
466bc5531deSDag-Erling Smørgrav 				    proxyhost, proxyport, proxyhints, socksv,
467bc5531deSDag-Erling Smørgrav 				    Pflag);
468bc5531deSDag-Erling Smørgrav 			else
469bc5531deSDag-Erling Smørgrav 				s = remote_connect(host, portlist[i], hints);
470bc5531deSDag-Erling Smørgrav 
471bc5531deSDag-Erling Smørgrav 			if (s < 0)
472bc5531deSDag-Erling Smørgrav 				continue;
473bc5531deSDag-Erling Smørgrav 
474bc5531deSDag-Erling Smørgrav 			ret = 0;
475bc5531deSDag-Erling Smørgrav 			if (vflag || zflag) {
476bc5531deSDag-Erling Smørgrav 				/* For UDP, make sure we are connected. */
477bc5531deSDag-Erling Smørgrav 				if (uflag) {
478bc5531deSDag-Erling Smørgrav 					if (udptest(s) == -1) {
479bc5531deSDag-Erling Smørgrav 						ret = 1;
480bc5531deSDag-Erling Smørgrav 						continue;
481bc5531deSDag-Erling Smørgrav 					}
482bc5531deSDag-Erling Smørgrav 				}
483bc5531deSDag-Erling Smørgrav 
484bc5531deSDag-Erling Smørgrav 				/* Don't look up port if -n. */
485bc5531deSDag-Erling Smørgrav 				if (nflag)
486bc5531deSDag-Erling Smørgrav 					sv = NULL;
487bc5531deSDag-Erling Smørgrav 				else {
488bc5531deSDag-Erling Smørgrav 					sv = getservbyport(
489bc5531deSDag-Erling Smørgrav 					    ntohs(atoi(portlist[i])),
490bc5531deSDag-Erling Smørgrav 					    uflag ? "udp" : "tcp");
491bc5531deSDag-Erling Smørgrav 				}
492bc5531deSDag-Erling Smørgrav 
493bc5531deSDag-Erling Smørgrav 				fprintf(stderr,
494bc5531deSDag-Erling Smørgrav 				    "Connection to %s %s port [%s/%s] "
495bc5531deSDag-Erling Smørgrav 				    "succeeded!\n", host, portlist[i],
496bc5531deSDag-Erling Smørgrav 				    uflag ? "udp" : "tcp",
497bc5531deSDag-Erling Smørgrav 				    sv ? sv->s_name : "*");
498bc5531deSDag-Erling Smørgrav 			}
499bc5531deSDag-Erling Smørgrav 			if (Fflag)
500bc5531deSDag-Erling Smørgrav 				fdpass(s);
501bc5531deSDag-Erling Smørgrav 			else if (!zflag)
502bc5531deSDag-Erling Smørgrav 				readwrite(s);
503bc5531deSDag-Erling Smørgrav 		}
504bc5531deSDag-Erling Smørgrav 	}
505bc5531deSDag-Erling Smørgrav 
506bc5531deSDag-Erling Smørgrav 	if (s)
507bc5531deSDag-Erling Smørgrav 		close(s);
508bc5531deSDag-Erling Smørgrav 
509bc5531deSDag-Erling Smørgrav 	exit(ret);
510bc5531deSDag-Erling Smørgrav }
511bc5531deSDag-Erling Smørgrav 
512bc5531deSDag-Erling Smørgrav /*
513bc5531deSDag-Erling Smørgrav  * unix_bind()
514bc5531deSDag-Erling Smørgrav  * Returns a unix socket bound to the given path
515bc5531deSDag-Erling Smørgrav  */
516bc5531deSDag-Erling Smørgrav int
517bc5531deSDag-Erling Smørgrav unix_bind(char *path)
518bc5531deSDag-Erling Smørgrav {
519bc5531deSDag-Erling Smørgrav 	struct sockaddr_un sun_sa;
520bc5531deSDag-Erling Smørgrav 	int s;
521bc5531deSDag-Erling Smørgrav 
522bc5531deSDag-Erling Smørgrav 	/* Create unix domain socket. */
523bc5531deSDag-Erling Smørgrav 	if ((s = socket(AF_UNIX, uflag ? SOCK_DGRAM : SOCK_STREAM,
524bc5531deSDag-Erling Smørgrav 	     0)) < 0)
525bc5531deSDag-Erling Smørgrav 		return (-1);
526bc5531deSDag-Erling Smørgrav 
527bc5531deSDag-Erling Smørgrav 	memset(&sun_sa, 0, sizeof(struct sockaddr_un));
528bc5531deSDag-Erling Smørgrav 	sun_sa.sun_family = AF_UNIX;
529bc5531deSDag-Erling Smørgrav 
530bc5531deSDag-Erling Smørgrav 	if (strlcpy(sun_sa.sun_path, path, sizeof(sun_sa.sun_path)) >=
531bc5531deSDag-Erling Smørgrav 	    sizeof(sun_sa.sun_path)) {
532bc5531deSDag-Erling Smørgrav 		close(s);
533bc5531deSDag-Erling Smørgrav 		errno = ENAMETOOLONG;
534bc5531deSDag-Erling Smørgrav 		return (-1);
535bc5531deSDag-Erling Smørgrav 	}
536bc5531deSDag-Erling Smørgrav 
537bc5531deSDag-Erling Smørgrav 	if (bind(s, (struct sockaddr *)&sun_sa, SUN_LEN(&sun_sa)) < 0) {
538bc5531deSDag-Erling Smørgrav 		close(s);
539bc5531deSDag-Erling Smørgrav 		return (-1);
540bc5531deSDag-Erling Smørgrav 	}
541bc5531deSDag-Erling Smørgrav 	return (s);
542bc5531deSDag-Erling Smørgrav }
543bc5531deSDag-Erling Smørgrav 
544bc5531deSDag-Erling Smørgrav /*
545bc5531deSDag-Erling Smørgrav  * unix_connect()
546bc5531deSDag-Erling Smørgrav  * Returns a socket connected to a local unix socket. Returns -1 on failure.
547bc5531deSDag-Erling Smørgrav  */
548bc5531deSDag-Erling Smørgrav int
549bc5531deSDag-Erling Smørgrav unix_connect(char *path)
550bc5531deSDag-Erling Smørgrav {
551bc5531deSDag-Erling Smørgrav 	struct sockaddr_un sun_sa;
552bc5531deSDag-Erling Smørgrav 	int s;
553bc5531deSDag-Erling Smørgrav 
554bc5531deSDag-Erling Smørgrav 	if (uflag) {
555bc5531deSDag-Erling Smørgrav 		if ((s = unix_bind(unix_dg_tmp_socket)) < 0)
556bc5531deSDag-Erling Smørgrav 			return (-1);
557bc5531deSDag-Erling Smørgrav 	} else {
558bc5531deSDag-Erling Smørgrav 		if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
559bc5531deSDag-Erling Smørgrav 			return (-1);
560bc5531deSDag-Erling Smørgrav 	}
561bc5531deSDag-Erling Smørgrav 	(void)fcntl(s, F_SETFD, FD_CLOEXEC);
562bc5531deSDag-Erling Smørgrav 
563bc5531deSDag-Erling Smørgrav 	memset(&sun_sa, 0, sizeof(struct sockaddr_un));
564bc5531deSDag-Erling Smørgrav 	sun_sa.sun_family = AF_UNIX;
565bc5531deSDag-Erling Smørgrav 
566bc5531deSDag-Erling Smørgrav 	if (strlcpy(sun_sa.sun_path, path, sizeof(sun_sa.sun_path)) >=
567bc5531deSDag-Erling Smørgrav 	    sizeof(sun_sa.sun_path)) {
568bc5531deSDag-Erling Smørgrav 		close(s);
569bc5531deSDag-Erling Smørgrav 		errno = ENAMETOOLONG;
570bc5531deSDag-Erling Smørgrav 		return (-1);
571bc5531deSDag-Erling Smørgrav 	}
572bc5531deSDag-Erling Smørgrav 	if (connect(s, (struct sockaddr *)&sun_sa, SUN_LEN(&sun_sa)) < 0) {
573bc5531deSDag-Erling Smørgrav 		close(s);
574bc5531deSDag-Erling Smørgrav 		return (-1);
575bc5531deSDag-Erling Smørgrav 	}
576bc5531deSDag-Erling Smørgrav 	return (s);
577bc5531deSDag-Erling Smørgrav 
578bc5531deSDag-Erling Smørgrav }
579bc5531deSDag-Erling Smørgrav 
580bc5531deSDag-Erling Smørgrav /*
581bc5531deSDag-Erling Smørgrav  * unix_listen()
582bc5531deSDag-Erling Smørgrav  * Create a unix domain socket, and listen on it.
583bc5531deSDag-Erling Smørgrav  */
584bc5531deSDag-Erling Smørgrav int
585bc5531deSDag-Erling Smørgrav unix_listen(char *path)
586bc5531deSDag-Erling Smørgrav {
587bc5531deSDag-Erling Smørgrav 	int s;
588bc5531deSDag-Erling Smørgrav 	if ((s = unix_bind(path)) < 0)
589bc5531deSDag-Erling Smørgrav 		return (-1);
590bc5531deSDag-Erling Smørgrav 
591bc5531deSDag-Erling Smørgrav 	if (listen(s, 5) < 0) {
592bc5531deSDag-Erling Smørgrav 		close(s);
593bc5531deSDag-Erling Smørgrav 		return (-1);
594bc5531deSDag-Erling Smørgrav 	}
595bc5531deSDag-Erling Smørgrav 	return (s);
596bc5531deSDag-Erling Smørgrav }
597bc5531deSDag-Erling Smørgrav 
598bc5531deSDag-Erling Smørgrav /*
599bc5531deSDag-Erling Smørgrav  * remote_connect()
600bc5531deSDag-Erling Smørgrav  * Returns a socket connected to a remote host. Properly binds to a local
601bc5531deSDag-Erling Smørgrav  * port or source address if needed. Returns -1 on failure.
602bc5531deSDag-Erling Smørgrav  */
603bc5531deSDag-Erling Smørgrav int
604bc5531deSDag-Erling Smørgrav remote_connect(const char *host, const char *port, struct addrinfo hints)
605bc5531deSDag-Erling Smørgrav {
606bc5531deSDag-Erling Smørgrav 	struct addrinfo *res, *res0;
607bc5531deSDag-Erling Smørgrav 	int s, error;
608bc5531deSDag-Erling Smørgrav #if defined(SO_RTABLE) || defined(SO_BINDANY)
609bc5531deSDag-Erling Smørgrav 	int on = 1;
610bc5531deSDag-Erling Smørgrav #endif
611bc5531deSDag-Erling Smørgrav 
612bc5531deSDag-Erling Smørgrav 	if ((error = getaddrinfo(host, port, &hints, &res)))
613bc5531deSDag-Erling Smørgrav 		errx(1, "getaddrinfo: %s", gai_strerror(error));
614bc5531deSDag-Erling Smørgrav 
615bc5531deSDag-Erling Smørgrav 	res0 = res;
616bc5531deSDag-Erling Smørgrav 	do {
617bc5531deSDag-Erling Smørgrav 		if ((s = socket(res0->ai_family, res0->ai_socktype,
618bc5531deSDag-Erling Smørgrav 		    res0->ai_protocol)) < 0)
619bc5531deSDag-Erling Smørgrav 			continue;
620bc5531deSDag-Erling Smørgrav 
621bc5531deSDag-Erling Smørgrav #ifdef SO_RTABLE
622bc5531deSDag-Erling Smørgrav 		if (rtableid >= 0 && (setsockopt(s, SOL_SOCKET, SO_RTABLE,
623bc5531deSDag-Erling Smørgrav 		    &rtableid, sizeof(rtableid)) == -1))
624bc5531deSDag-Erling Smørgrav 			err(1, "setsockopt SO_RTABLE");
625bc5531deSDag-Erling Smørgrav #endif
626bc5531deSDag-Erling Smørgrav 		/* Bind to a local port or source address if specified. */
627bc5531deSDag-Erling Smørgrav 		if (sflag || pflag) {
628bc5531deSDag-Erling Smørgrav 			struct addrinfo ahints, *ares;
629bc5531deSDag-Erling Smørgrav 
630bc5531deSDag-Erling Smørgrav #ifdef SO_BINDANY
631bc5531deSDag-Erling Smørgrav 			/* try SO_BINDANY, but don't insist */
632bc5531deSDag-Erling Smørgrav 			setsockopt(s, SOL_SOCKET, SO_BINDANY, &on, sizeof(on));
633bc5531deSDag-Erling Smørgrav #endif
634bc5531deSDag-Erling Smørgrav 			memset(&ahints, 0, sizeof(struct addrinfo));
635bc5531deSDag-Erling Smørgrav 			ahints.ai_family = res0->ai_family;
636bc5531deSDag-Erling Smørgrav 			ahints.ai_socktype = uflag ? SOCK_DGRAM : SOCK_STREAM;
637bc5531deSDag-Erling Smørgrav 			ahints.ai_protocol = uflag ? IPPROTO_UDP : IPPROTO_TCP;
638bc5531deSDag-Erling Smørgrav 			ahints.ai_flags = AI_PASSIVE;
639bc5531deSDag-Erling Smørgrav 			if ((error = getaddrinfo(sflag, pflag, &ahints, &ares)))
640bc5531deSDag-Erling Smørgrav 				errx(1, "getaddrinfo: %s", gai_strerror(error));
641bc5531deSDag-Erling Smørgrav 
642bc5531deSDag-Erling Smørgrav 			if (bind(s, (struct sockaddr *)ares->ai_addr,
643bc5531deSDag-Erling Smørgrav 			    ares->ai_addrlen) < 0)
644bc5531deSDag-Erling Smørgrav 				err(1, "bind failed");
645bc5531deSDag-Erling Smørgrav 			freeaddrinfo(ares);
646bc5531deSDag-Erling Smørgrav 		}
647bc5531deSDag-Erling Smørgrav 
648bc5531deSDag-Erling Smørgrav 		set_common_sockopts(s);
649bc5531deSDag-Erling Smørgrav 
650bc5531deSDag-Erling Smørgrav 		if (timeout_connect(s, res0->ai_addr, res0->ai_addrlen) == 0)
651bc5531deSDag-Erling Smørgrav 			break;
652bc5531deSDag-Erling Smørgrav 		else if (vflag)
653bc5531deSDag-Erling Smørgrav 			warn("connect to %s port %s (%s) failed", host, port,
654bc5531deSDag-Erling Smørgrav 			    uflag ? "udp" : "tcp");
655bc5531deSDag-Erling Smørgrav 
656bc5531deSDag-Erling Smørgrav 		close(s);
657bc5531deSDag-Erling Smørgrav 		s = -1;
658bc5531deSDag-Erling Smørgrav 	} while ((res0 = res0->ai_next) != NULL);
659bc5531deSDag-Erling Smørgrav 
660bc5531deSDag-Erling Smørgrav 	freeaddrinfo(res);
661bc5531deSDag-Erling Smørgrav 
662bc5531deSDag-Erling Smørgrav 	return (s);
663bc5531deSDag-Erling Smørgrav }
664bc5531deSDag-Erling Smørgrav 
665bc5531deSDag-Erling Smørgrav int
666bc5531deSDag-Erling Smørgrav timeout_connect(int s, const struct sockaddr *name, socklen_t namelen)
667bc5531deSDag-Erling Smørgrav {
668bc5531deSDag-Erling Smørgrav 	struct pollfd pfd;
669bc5531deSDag-Erling Smørgrav 	socklen_t optlen;
670bc5531deSDag-Erling Smørgrav 	int flags = 0, optval;
671bc5531deSDag-Erling Smørgrav 	int ret;
672bc5531deSDag-Erling Smørgrav 
673bc5531deSDag-Erling Smørgrav 	if (timeout != -1) {
674bc5531deSDag-Erling Smørgrav 		flags = fcntl(s, F_GETFL, 0);
675bc5531deSDag-Erling Smørgrav 		if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1)
676bc5531deSDag-Erling Smørgrav 			err(1, "set non-blocking mode");
677bc5531deSDag-Erling Smørgrav 	}
678bc5531deSDag-Erling Smørgrav 
679bc5531deSDag-Erling Smørgrav 	if ((ret = connect(s, name, namelen)) != 0 && errno == EINPROGRESS) {
680bc5531deSDag-Erling Smørgrav 		pfd.fd = s;
681bc5531deSDag-Erling Smørgrav 		pfd.events = POLLOUT;
682bc5531deSDag-Erling Smørgrav 		if ((ret = poll(&pfd, 1, timeout)) == 1) {
683bc5531deSDag-Erling Smørgrav 			optlen = sizeof(optval);
684bc5531deSDag-Erling Smørgrav 			if ((ret = getsockopt(s, SOL_SOCKET, SO_ERROR,
685bc5531deSDag-Erling Smørgrav 			    &optval, &optlen)) == 0) {
686bc5531deSDag-Erling Smørgrav 				errno = optval;
687bc5531deSDag-Erling Smørgrav 				ret = optval == 0 ? 0 : -1;
688bc5531deSDag-Erling Smørgrav 			}
689bc5531deSDag-Erling Smørgrav 		} else if (ret == 0) {
690bc5531deSDag-Erling Smørgrav 			errno = ETIMEDOUT;
691bc5531deSDag-Erling Smørgrav 			ret = -1;
692bc5531deSDag-Erling Smørgrav 		} else
693bc5531deSDag-Erling Smørgrav 			err(1, "poll failed");
694bc5531deSDag-Erling Smørgrav 	}
695bc5531deSDag-Erling Smørgrav 
696bc5531deSDag-Erling Smørgrav 	if (timeout != -1 && fcntl(s, F_SETFL, flags) == -1)
697bc5531deSDag-Erling Smørgrav 		err(1, "restoring flags");
698bc5531deSDag-Erling Smørgrav 
699bc5531deSDag-Erling Smørgrav 	return (ret);
700bc5531deSDag-Erling Smørgrav }
701bc5531deSDag-Erling Smørgrav 
702bc5531deSDag-Erling Smørgrav /*
703bc5531deSDag-Erling Smørgrav  * local_listen()
704bc5531deSDag-Erling Smørgrav  * Returns a socket listening on a local port, binds to specified source
705bc5531deSDag-Erling Smørgrav  * address. Returns -1 on failure.
706bc5531deSDag-Erling Smørgrav  */
707bc5531deSDag-Erling Smørgrav int
708bc5531deSDag-Erling Smørgrav local_listen(char *host, char *port, struct addrinfo hints)
709bc5531deSDag-Erling Smørgrav {
710bc5531deSDag-Erling Smørgrav 	struct addrinfo *res, *res0;
711bc5531deSDag-Erling Smørgrav 	int s, ret, x = 1;
712bc5531deSDag-Erling Smørgrav 	int error;
713bc5531deSDag-Erling Smørgrav 
714bc5531deSDag-Erling Smørgrav 	/* Allow nodename to be null. */
715bc5531deSDag-Erling Smørgrav 	hints.ai_flags |= AI_PASSIVE;
716bc5531deSDag-Erling Smørgrav 
717bc5531deSDag-Erling Smørgrav 	/*
718bc5531deSDag-Erling Smørgrav 	 * In the case of binding to a wildcard address
719bc5531deSDag-Erling Smørgrav 	 * default to binding to an ipv4 address.
720bc5531deSDag-Erling Smørgrav 	 */
721bc5531deSDag-Erling Smørgrav 	if (host == NULL && hints.ai_family == AF_UNSPEC)
722bc5531deSDag-Erling Smørgrav 		hints.ai_family = AF_INET;
723bc5531deSDag-Erling Smørgrav 
724bc5531deSDag-Erling Smørgrav 	if ((error = getaddrinfo(host, port, &hints, &res)))
725bc5531deSDag-Erling Smørgrav 		errx(1, "getaddrinfo: %s", gai_strerror(error));
726bc5531deSDag-Erling Smørgrav 
727bc5531deSDag-Erling Smørgrav 	res0 = res;
728bc5531deSDag-Erling Smørgrav 	do {
729bc5531deSDag-Erling Smørgrav 		if ((s = socket(res0->ai_family, res0->ai_socktype,
730bc5531deSDag-Erling Smørgrav 		    res0->ai_protocol)) < 0)
731bc5531deSDag-Erling Smørgrav 			continue;
732bc5531deSDag-Erling Smørgrav 
733bc5531deSDag-Erling Smørgrav #ifdef SO_RTABLE
734bc5531deSDag-Erling Smørgrav 		if (rtableid >= 0 && (setsockopt(s, SOL_SOCKET, SO_RTABLE,
735bc5531deSDag-Erling Smørgrav 		    &rtableid, sizeof(rtableid)) == -1))
736bc5531deSDag-Erling Smørgrav 			err(1, "setsockopt SO_RTABLE");
737bc5531deSDag-Erling Smørgrav #endif
738bc5531deSDag-Erling Smørgrav #ifdef SO_REUSEPORT
739bc5531deSDag-Erling Smørgrav 		ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof(x));
740bc5531deSDag-Erling Smørgrav 		if (ret == -1)
741*47dd1d1bSDag-Erling Smørgrav 			err(1, "setsockopt SO_REUSEPORT");
742*47dd1d1bSDag-Erling Smørgrav #endif
743*47dd1d1bSDag-Erling Smørgrav #ifdef SO_REUSEADDR
744*47dd1d1bSDag-Erling Smørgrav 		ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
745*47dd1d1bSDag-Erling Smørgrav 		if (ret == -1)
746*47dd1d1bSDag-Erling Smørgrav 			err(1, "setsockopt SO_REUSEADDR");
747bc5531deSDag-Erling Smørgrav #endif
748bc5531deSDag-Erling Smørgrav 		set_common_sockopts(s);
749bc5531deSDag-Erling Smørgrav 
750bc5531deSDag-Erling Smørgrav 		if (bind(s, (struct sockaddr *)res0->ai_addr,
751bc5531deSDag-Erling Smørgrav 		    res0->ai_addrlen) == 0)
752bc5531deSDag-Erling Smørgrav 			break;
753bc5531deSDag-Erling Smørgrav 
754bc5531deSDag-Erling Smørgrav 		close(s);
755bc5531deSDag-Erling Smørgrav 		s = -1;
756bc5531deSDag-Erling Smørgrav 	} while ((res0 = res0->ai_next) != NULL);
757bc5531deSDag-Erling Smørgrav 
758bc5531deSDag-Erling Smørgrav 	if (!uflag && s != -1) {
759bc5531deSDag-Erling Smørgrav 		if (listen(s, 1) < 0)
760bc5531deSDag-Erling Smørgrav 			err(1, "listen");
761bc5531deSDag-Erling Smørgrav 	}
762bc5531deSDag-Erling Smørgrav 
763bc5531deSDag-Erling Smørgrav 	freeaddrinfo(res);
764bc5531deSDag-Erling Smørgrav 
765bc5531deSDag-Erling Smørgrav 	return (s);
766bc5531deSDag-Erling Smørgrav }
767bc5531deSDag-Erling Smørgrav 
768bc5531deSDag-Erling Smørgrav /*
769bc5531deSDag-Erling Smørgrav  * readwrite()
770bc5531deSDag-Erling Smørgrav  * Loop that polls on the network file descriptor and stdin.
771bc5531deSDag-Erling Smørgrav  */
772bc5531deSDag-Erling Smørgrav void
773bc5531deSDag-Erling Smørgrav readwrite(int net_fd)
774bc5531deSDag-Erling Smørgrav {
775bc5531deSDag-Erling Smørgrav 	struct pollfd pfd[4];
776bc5531deSDag-Erling Smørgrav 	int stdin_fd = STDIN_FILENO;
777bc5531deSDag-Erling Smørgrav 	int stdout_fd = STDOUT_FILENO;
778bc5531deSDag-Erling Smørgrav 	unsigned char netinbuf[BUFSIZE];
779bc5531deSDag-Erling Smørgrav 	size_t netinbufpos = 0;
780bc5531deSDag-Erling Smørgrav 	unsigned char stdinbuf[BUFSIZE];
781bc5531deSDag-Erling Smørgrav 	size_t stdinbufpos = 0;
782bc5531deSDag-Erling Smørgrav 	int n, num_fds;
783bc5531deSDag-Erling Smørgrav 	ssize_t ret;
784bc5531deSDag-Erling Smørgrav 
785bc5531deSDag-Erling Smørgrav 	/* don't read from stdin if requested */
786bc5531deSDag-Erling Smørgrav 	if (dflag)
787bc5531deSDag-Erling Smørgrav 		stdin_fd = -1;
788bc5531deSDag-Erling Smørgrav 
789bc5531deSDag-Erling Smørgrav 	/* stdin */
790bc5531deSDag-Erling Smørgrav 	pfd[POLL_STDIN].fd = stdin_fd;
791bc5531deSDag-Erling Smørgrav 	pfd[POLL_STDIN].events = POLLIN;
792bc5531deSDag-Erling Smørgrav 
793bc5531deSDag-Erling Smørgrav 	/* network out */
794bc5531deSDag-Erling Smørgrav 	pfd[POLL_NETOUT].fd = net_fd;
795bc5531deSDag-Erling Smørgrav 	pfd[POLL_NETOUT].events = 0;
796bc5531deSDag-Erling Smørgrav 
797bc5531deSDag-Erling Smørgrav 	/* network in */
798bc5531deSDag-Erling Smørgrav 	pfd[POLL_NETIN].fd = net_fd;
799bc5531deSDag-Erling Smørgrav 	pfd[POLL_NETIN].events = POLLIN;
800bc5531deSDag-Erling Smørgrav 
801bc5531deSDag-Erling Smørgrav 	/* stdout */
802bc5531deSDag-Erling Smørgrav 	pfd[POLL_STDOUT].fd = stdout_fd;
803bc5531deSDag-Erling Smørgrav 	pfd[POLL_STDOUT].events = 0;
804bc5531deSDag-Erling Smørgrav 
805bc5531deSDag-Erling Smørgrav 	while (1) {
806bc5531deSDag-Erling Smørgrav 		/* both inputs are gone, buffers are empty, we are done */
807bc5531deSDag-Erling Smørgrav 		if (pfd[POLL_STDIN].fd == -1 && pfd[POLL_NETIN].fd == -1
808bc5531deSDag-Erling Smørgrav 		    && stdinbufpos == 0 && netinbufpos == 0) {
809bc5531deSDag-Erling Smørgrav 			close(net_fd);
810bc5531deSDag-Erling Smørgrav 			return;
811bc5531deSDag-Erling Smørgrav 		}
812bc5531deSDag-Erling Smørgrav 		/* both outputs are gone, we can't continue */
813bc5531deSDag-Erling Smørgrav 		if (pfd[POLL_NETOUT].fd == -1 && pfd[POLL_STDOUT].fd == -1) {
814bc5531deSDag-Erling Smørgrav 			close(net_fd);
815bc5531deSDag-Erling Smørgrav 			return;
816bc5531deSDag-Erling Smørgrav 		}
817bc5531deSDag-Erling Smørgrav 		/* listen and net in gone, queues empty, done */
818bc5531deSDag-Erling Smørgrav 		if (lflag && pfd[POLL_NETIN].fd == -1
819bc5531deSDag-Erling Smørgrav 		    && stdinbufpos == 0 && netinbufpos == 0) {
820bc5531deSDag-Erling Smørgrav 			close(net_fd);
821bc5531deSDag-Erling Smørgrav 			return;
822bc5531deSDag-Erling Smørgrav 		}
823bc5531deSDag-Erling Smørgrav 
824bc5531deSDag-Erling Smørgrav 		/* help says -i is for "wait between lines sent". We read and
825bc5531deSDag-Erling Smørgrav 		 * write arbitrary amounts of data, and we don't want to start
826bc5531deSDag-Erling Smørgrav 		 * scanning for newlines, so this is as good as it gets */
827bc5531deSDag-Erling Smørgrav 		if (iflag)
828bc5531deSDag-Erling Smørgrav 			sleep(iflag);
829bc5531deSDag-Erling Smørgrav 
830bc5531deSDag-Erling Smørgrav 		/* poll */
831bc5531deSDag-Erling Smørgrav 		num_fds = poll(pfd, 4, timeout);
832bc5531deSDag-Erling Smørgrav 
833bc5531deSDag-Erling Smørgrav 		/* treat poll errors */
834bc5531deSDag-Erling Smørgrav 		if (num_fds == -1) {
835bc5531deSDag-Erling Smørgrav 			close(net_fd);
836bc5531deSDag-Erling Smørgrav 			err(1, "polling error");
837bc5531deSDag-Erling Smørgrav 		}
838bc5531deSDag-Erling Smørgrav 
839bc5531deSDag-Erling Smørgrav 		/* timeout happened */
840bc5531deSDag-Erling Smørgrav 		if (num_fds == 0)
841bc5531deSDag-Erling Smørgrav 			return;
842bc5531deSDag-Erling Smørgrav 
843bc5531deSDag-Erling Smørgrav 		/* treat socket error conditions */
844bc5531deSDag-Erling Smørgrav 		for (n = 0; n < 4; n++) {
845bc5531deSDag-Erling Smørgrav 			if (pfd[n].revents & (POLLERR|POLLNVAL)) {
846bc5531deSDag-Erling Smørgrav 				pfd[n].fd = -1;
847bc5531deSDag-Erling Smørgrav 			}
848bc5531deSDag-Erling Smørgrav 		}
849bc5531deSDag-Erling Smørgrav 		/* reading is possible after HUP */
850bc5531deSDag-Erling Smørgrav 		if (pfd[POLL_STDIN].events & POLLIN &&
851bc5531deSDag-Erling Smørgrav 		    pfd[POLL_STDIN].revents & POLLHUP &&
852bc5531deSDag-Erling Smørgrav 		    ! (pfd[POLL_STDIN].revents & POLLIN))
853bc5531deSDag-Erling Smørgrav 				pfd[POLL_STDIN].fd = -1;
854bc5531deSDag-Erling Smørgrav 
855bc5531deSDag-Erling Smørgrav 		if (pfd[POLL_NETIN].events & POLLIN &&
856bc5531deSDag-Erling Smørgrav 		    pfd[POLL_NETIN].revents & POLLHUP &&
857bc5531deSDag-Erling Smørgrav 		    ! (pfd[POLL_NETIN].revents & POLLIN))
858bc5531deSDag-Erling Smørgrav 				pfd[POLL_NETIN].fd = -1;
859bc5531deSDag-Erling Smørgrav 
860bc5531deSDag-Erling Smørgrav 		if (pfd[POLL_NETOUT].revents & POLLHUP) {
861bc5531deSDag-Erling Smørgrav 			if (Nflag)
862bc5531deSDag-Erling Smørgrav 				shutdown(pfd[POLL_NETOUT].fd, SHUT_WR);
863bc5531deSDag-Erling Smørgrav 			pfd[POLL_NETOUT].fd = -1;
864bc5531deSDag-Erling Smørgrav 		}
865bc5531deSDag-Erling Smørgrav 		/* if HUP, stop watching stdout */
866bc5531deSDag-Erling Smørgrav 		if (pfd[POLL_STDOUT].revents & POLLHUP)
867bc5531deSDag-Erling Smørgrav 			pfd[POLL_STDOUT].fd = -1;
868bc5531deSDag-Erling Smørgrav 		/* if no net out, stop watching stdin */
869bc5531deSDag-Erling Smørgrav 		if (pfd[POLL_NETOUT].fd == -1)
870bc5531deSDag-Erling Smørgrav 			pfd[POLL_STDIN].fd = -1;
871bc5531deSDag-Erling Smørgrav 		/* if no stdout, stop watching net in */
872bc5531deSDag-Erling Smørgrav 		if (pfd[POLL_STDOUT].fd == -1) {
873bc5531deSDag-Erling Smørgrav 			if (pfd[POLL_NETIN].fd != -1)
874bc5531deSDag-Erling Smørgrav 				shutdown(pfd[POLL_NETIN].fd, SHUT_RD);
875bc5531deSDag-Erling Smørgrav 			pfd[POLL_NETIN].fd = -1;
876bc5531deSDag-Erling Smørgrav 		}
877bc5531deSDag-Erling Smørgrav 
878bc5531deSDag-Erling Smørgrav 		/* try to read from stdin */
879bc5531deSDag-Erling Smørgrav 		if (pfd[POLL_STDIN].revents & POLLIN && stdinbufpos < BUFSIZE) {
880bc5531deSDag-Erling Smørgrav 			ret = fillbuf(pfd[POLL_STDIN].fd, stdinbuf,
881bc5531deSDag-Erling Smørgrav 			    &stdinbufpos);
882bc5531deSDag-Erling Smørgrav 			/* error or eof on stdin - remove from pfd */
883bc5531deSDag-Erling Smørgrav 			if (ret == 0 || ret == -1)
884bc5531deSDag-Erling Smørgrav 				pfd[POLL_STDIN].fd = -1;
885bc5531deSDag-Erling Smørgrav 			/* read something - poll net out */
886bc5531deSDag-Erling Smørgrav 			if (stdinbufpos > 0)
887bc5531deSDag-Erling Smørgrav 				pfd[POLL_NETOUT].events = POLLOUT;
888bc5531deSDag-Erling Smørgrav 			/* filled buffer - remove self from polling */
889bc5531deSDag-Erling Smørgrav 			if (stdinbufpos == BUFSIZE)
890bc5531deSDag-Erling Smørgrav 				pfd[POLL_STDIN].events = 0;
891bc5531deSDag-Erling Smørgrav 		}
892bc5531deSDag-Erling Smørgrav 		/* try to write to network */
893bc5531deSDag-Erling Smørgrav 		if (pfd[POLL_NETOUT].revents & POLLOUT && stdinbufpos > 0) {
894bc5531deSDag-Erling Smørgrav 			ret = drainbuf(pfd[POLL_NETOUT].fd, stdinbuf,
895bc5531deSDag-Erling Smørgrav 			    &stdinbufpos);
896bc5531deSDag-Erling Smørgrav 			if (ret == -1)
897bc5531deSDag-Erling Smørgrav 				pfd[POLL_NETOUT].fd = -1;
898bc5531deSDag-Erling Smørgrav 			/* buffer empty - remove self from polling */
899bc5531deSDag-Erling Smørgrav 			if (stdinbufpos == 0)
900bc5531deSDag-Erling Smørgrav 				pfd[POLL_NETOUT].events = 0;
901bc5531deSDag-Erling Smørgrav 			/* buffer no longer full - poll stdin again */
902bc5531deSDag-Erling Smørgrav 			if (stdinbufpos < BUFSIZE)
903bc5531deSDag-Erling Smørgrav 				pfd[POLL_STDIN].events = POLLIN;
904bc5531deSDag-Erling Smørgrav 		}
905bc5531deSDag-Erling Smørgrav 		/* try to read from network */
906bc5531deSDag-Erling Smørgrav 		if (pfd[POLL_NETIN].revents & POLLIN && netinbufpos < BUFSIZE) {
907bc5531deSDag-Erling Smørgrav 			ret = fillbuf(pfd[POLL_NETIN].fd, netinbuf,
908bc5531deSDag-Erling Smørgrav 			    &netinbufpos);
909bc5531deSDag-Erling Smørgrav 			if (ret == -1)
910bc5531deSDag-Erling Smørgrav 				pfd[POLL_NETIN].fd = -1;
911bc5531deSDag-Erling Smørgrav 			/* eof on net in - remove from pfd */
912bc5531deSDag-Erling Smørgrav 			if (ret == 0) {
913bc5531deSDag-Erling Smørgrav 				shutdown(pfd[POLL_NETIN].fd, SHUT_RD);
914bc5531deSDag-Erling Smørgrav 				pfd[POLL_NETIN].fd = -1;
915bc5531deSDag-Erling Smørgrav 			}
916bc5531deSDag-Erling Smørgrav 			/* read something - poll stdout */
917bc5531deSDag-Erling Smørgrav 			if (netinbufpos > 0)
918bc5531deSDag-Erling Smørgrav 				pfd[POLL_STDOUT].events = POLLOUT;
919bc5531deSDag-Erling Smørgrav 			/* filled buffer - remove self from polling */
920bc5531deSDag-Erling Smørgrav 			if (netinbufpos == BUFSIZE)
921bc5531deSDag-Erling Smørgrav 				pfd[POLL_NETIN].events = 0;
922bc5531deSDag-Erling Smørgrav 			/* handle telnet */
923bc5531deSDag-Erling Smørgrav 			if (tflag)
924bc5531deSDag-Erling Smørgrav 				atelnet(pfd[POLL_NETIN].fd, netinbuf,
925bc5531deSDag-Erling Smørgrav 				    netinbufpos);
926bc5531deSDag-Erling Smørgrav 		}
927bc5531deSDag-Erling Smørgrav 		/* try to write to stdout */
928bc5531deSDag-Erling Smørgrav 		if (pfd[POLL_STDOUT].revents & POLLOUT && netinbufpos > 0) {
929bc5531deSDag-Erling Smørgrav 			ret = drainbuf(pfd[POLL_STDOUT].fd, netinbuf,
930bc5531deSDag-Erling Smørgrav 			    &netinbufpos);
931bc5531deSDag-Erling Smørgrav 			if (ret == -1)
932bc5531deSDag-Erling Smørgrav 				pfd[POLL_STDOUT].fd = -1;
933bc5531deSDag-Erling Smørgrav 			/* buffer empty - remove self from polling */
934bc5531deSDag-Erling Smørgrav 			if (netinbufpos == 0)
935bc5531deSDag-Erling Smørgrav 				pfd[POLL_STDOUT].events = 0;
936bc5531deSDag-Erling Smørgrav 			/* buffer no longer full - poll net in again */
937bc5531deSDag-Erling Smørgrav 			if (netinbufpos < BUFSIZE)
938bc5531deSDag-Erling Smørgrav 				pfd[POLL_NETIN].events = POLLIN;
939bc5531deSDag-Erling Smørgrav 		}
940bc5531deSDag-Erling Smørgrav 
941bc5531deSDag-Erling Smørgrav 		/* stdin gone and queue empty? */
942bc5531deSDag-Erling Smørgrav 		if (pfd[POLL_STDIN].fd == -1 && stdinbufpos == 0) {
943bc5531deSDag-Erling Smørgrav 			if (pfd[POLL_NETOUT].fd != -1 && Nflag)
944bc5531deSDag-Erling Smørgrav 				shutdown(pfd[POLL_NETOUT].fd, SHUT_WR);
945bc5531deSDag-Erling Smørgrav 			pfd[POLL_NETOUT].fd = -1;
946bc5531deSDag-Erling Smørgrav 		}
947bc5531deSDag-Erling Smørgrav 		/* net in gone and queue empty? */
948bc5531deSDag-Erling Smørgrav 		if (pfd[POLL_NETIN].fd == -1 && netinbufpos == 0) {
949bc5531deSDag-Erling Smørgrav 			pfd[POLL_STDOUT].fd = -1;
950bc5531deSDag-Erling Smørgrav 		}
951bc5531deSDag-Erling Smørgrav 	}
952bc5531deSDag-Erling Smørgrav }
953bc5531deSDag-Erling Smørgrav 
954bc5531deSDag-Erling Smørgrav ssize_t
955bc5531deSDag-Erling Smørgrav drainbuf(int fd, unsigned char *buf, size_t *bufpos)
956bc5531deSDag-Erling Smørgrav {
957bc5531deSDag-Erling Smørgrav 	ssize_t n;
958bc5531deSDag-Erling Smørgrav 	ssize_t adjust;
959bc5531deSDag-Erling Smørgrav 
960bc5531deSDag-Erling Smørgrav 	n = write(fd, buf, *bufpos);
961bc5531deSDag-Erling Smørgrav 	/* don't treat EAGAIN, EINTR as error */
962bc5531deSDag-Erling Smørgrav 	if (n == -1 && (errno == EAGAIN || errno == EINTR))
963bc5531deSDag-Erling Smørgrav 		n = -2;
964bc5531deSDag-Erling Smørgrav 	if (n <= 0)
965bc5531deSDag-Erling Smørgrav 		return n;
966bc5531deSDag-Erling Smørgrav 	/* adjust buffer */
967bc5531deSDag-Erling Smørgrav 	adjust = *bufpos - n;
968bc5531deSDag-Erling Smørgrav 	if (adjust > 0)
969bc5531deSDag-Erling Smørgrav 		memmove(buf, buf + n, adjust);
970bc5531deSDag-Erling Smørgrav 	*bufpos -= n;
971bc5531deSDag-Erling Smørgrav 	return n;
972bc5531deSDag-Erling Smørgrav }
973bc5531deSDag-Erling Smørgrav 
974bc5531deSDag-Erling Smørgrav 
975bc5531deSDag-Erling Smørgrav ssize_t
976bc5531deSDag-Erling Smørgrav fillbuf(int fd, unsigned char *buf, size_t *bufpos)
977bc5531deSDag-Erling Smørgrav {
978bc5531deSDag-Erling Smørgrav 	size_t num = BUFSIZE - *bufpos;
979bc5531deSDag-Erling Smørgrav 	ssize_t n;
980bc5531deSDag-Erling Smørgrav 
981bc5531deSDag-Erling Smørgrav 	n = read(fd, buf + *bufpos, num);
982bc5531deSDag-Erling Smørgrav 	/* don't treat EAGAIN, EINTR as error */
983bc5531deSDag-Erling Smørgrav 	if (n == -1 && (errno == EAGAIN || errno == EINTR))
984bc5531deSDag-Erling Smørgrav 		n = -2;
985bc5531deSDag-Erling Smørgrav 	if (n <= 0)
986bc5531deSDag-Erling Smørgrav 		return n;
987bc5531deSDag-Erling Smørgrav 	*bufpos += n;
988bc5531deSDag-Erling Smørgrav 	return n;
989bc5531deSDag-Erling Smørgrav }
990bc5531deSDag-Erling Smørgrav 
991bc5531deSDag-Erling Smørgrav /*
992bc5531deSDag-Erling Smørgrav  * fdpass()
993bc5531deSDag-Erling Smørgrav  * Pass the connected file descriptor to stdout and exit.
994bc5531deSDag-Erling Smørgrav  */
995bc5531deSDag-Erling Smørgrav void
996bc5531deSDag-Erling Smørgrav fdpass(int nfd)
997bc5531deSDag-Erling Smørgrav {
998bc5531deSDag-Erling Smørgrav #if defined(HAVE_SENDMSG) && (defined(HAVE_ACCRIGHTS_IN_MSGHDR) || defined(HAVE_CONTROL_IN_MSGHDR))
999bc5531deSDag-Erling Smørgrav 	struct msghdr msg;
1000bc5531deSDag-Erling Smørgrav #ifndef HAVE_ACCRIGHTS_IN_MSGHDR
1001bc5531deSDag-Erling Smørgrav 	union {
1002bc5531deSDag-Erling Smørgrav 		struct cmsghdr hdr;
1003bc5531deSDag-Erling Smørgrav 		char buf[CMSG_SPACE(sizeof(int))];
1004bc5531deSDag-Erling Smørgrav 	} cmsgbuf;
1005bc5531deSDag-Erling Smørgrav 	struct cmsghdr *cmsg;
1006bc5531deSDag-Erling Smørgrav #endif
1007bc5531deSDag-Erling Smørgrav 	struct iovec vec;
1008bc5531deSDag-Erling Smørgrav 	char ch = '\0';
1009bc5531deSDag-Erling Smørgrav 	struct pollfd pfd;
1010bc5531deSDag-Erling Smørgrav 	ssize_t r;
1011bc5531deSDag-Erling Smørgrav 
1012bc5531deSDag-Erling Smørgrav 	memset(&msg, 0, sizeof(msg));
1013bc5531deSDag-Erling Smørgrav #ifdef HAVE_ACCRIGHTS_IN_MSGHDR
1014bc5531deSDag-Erling Smørgrav 	msg.msg_accrights = (caddr_t)&nfd;
1015bc5531deSDag-Erling Smørgrav 	msg.msg_accrightslen = sizeof(nfd);
1016bc5531deSDag-Erling Smørgrav #else
1017bc5531deSDag-Erling Smørgrav 	memset(&cmsgbuf, 0, sizeof(cmsgbuf));
1018bc5531deSDag-Erling Smørgrav 	msg.msg_control = (caddr_t)&cmsgbuf.buf;
1019bc5531deSDag-Erling Smørgrav 	msg.msg_controllen = sizeof(cmsgbuf.buf);
1020bc5531deSDag-Erling Smørgrav 	cmsg = CMSG_FIRSTHDR(&msg);
1021bc5531deSDag-Erling Smørgrav 	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
1022bc5531deSDag-Erling Smørgrav 	cmsg->cmsg_level = SOL_SOCKET;
1023bc5531deSDag-Erling Smørgrav 	cmsg->cmsg_type = SCM_RIGHTS;
1024bc5531deSDag-Erling Smørgrav 	*(int *)CMSG_DATA(cmsg) = nfd;
1025bc5531deSDag-Erling Smørgrav #endif
1026bc5531deSDag-Erling Smørgrav 
1027bc5531deSDag-Erling Smørgrav 	vec.iov_base = &ch;
1028bc5531deSDag-Erling Smørgrav 	vec.iov_len = 1;
1029bc5531deSDag-Erling Smørgrav 	msg.msg_iov = &vec;
1030bc5531deSDag-Erling Smørgrav 	msg.msg_iovlen = 1;
1031bc5531deSDag-Erling Smørgrav 
1032bc5531deSDag-Erling Smørgrav 	bzero(&pfd, sizeof(pfd));
1033bc5531deSDag-Erling Smørgrav 	pfd.fd = STDOUT_FILENO;
1034bc5531deSDag-Erling Smørgrav 	for (;;) {
1035bc5531deSDag-Erling Smørgrav 		r = sendmsg(STDOUT_FILENO, &msg, 0);
1036bc5531deSDag-Erling Smørgrav 		if (r == -1) {
1037bc5531deSDag-Erling Smørgrav 			if (errno == EAGAIN || errno == EINTR) {
1038bc5531deSDag-Erling Smørgrav 				pfd.events = POLLOUT;
1039bc5531deSDag-Erling Smørgrav 				if (poll(&pfd, 1, -1) == -1)
1040bc5531deSDag-Erling Smørgrav 					err(1, "poll");
1041bc5531deSDag-Erling Smørgrav 				continue;
1042bc5531deSDag-Erling Smørgrav 			}
1043bc5531deSDag-Erling Smørgrav 			err(1, "sendmsg");
1044bc5531deSDag-Erling Smørgrav 		} else if (r == -1)
1045bc5531deSDag-Erling Smørgrav 			errx(1, "sendmsg: unexpected return value %zd", r);
1046bc5531deSDag-Erling Smørgrav 		else
1047bc5531deSDag-Erling Smørgrav 			break;
1048bc5531deSDag-Erling Smørgrav 	}
1049bc5531deSDag-Erling Smørgrav 	exit(0);
1050bc5531deSDag-Erling Smørgrav #else
1051bc5531deSDag-Erling Smørgrav 	errx(1, "%s: file descriptor passing not supported", __func__);
1052bc5531deSDag-Erling Smørgrav #endif
1053bc5531deSDag-Erling Smørgrav }
1054bc5531deSDag-Erling Smørgrav 
1055bc5531deSDag-Erling Smørgrav /* Deal with RFC 854 WILL/WONT DO/DONT negotiation. */
1056bc5531deSDag-Erling Smørgrav void
1057bc5531deSDag-Erling Smørgrav atelnet(int nfd, unsigned char *buf, unsigned int size)
1058bc5531deSDag-Erling Smørgrav {
1059bc5531deSDag-Erling Smørgrav 	unsigned char *p, *end;
1060bc5531deSDag-Erling Smørgrav 	unsigned char obuf[4];
1061bc5531deSDag-Erling Smørgrav 
1062bc5531deSDag-Erling Smørgrav 	if (size < 3)
1063bc5531deSDag-Erling Smørgrav 		return;
1064bc5531deSDag-Erling Smørgrav 	end = buf + size - 2;
1065bc5531deSDag-Erling Smørgrav 
1066bc5531deSDag-Erling Smørgrav 	for (p = buf; p < end; p++) {
1067bc5531deSDag-Erling Smørgrav 		if (*p != IAC)
1068bc5531deSDag-Erling Smørgrav 			continue;
1069bc5531deSDag-Erling Smørgrav 
1070bc5531deSDag-Erling Smørgrav 		obuf[0] = IAC;
1071bc5531deSDag-Erling Smørgrav 		p++;
1072bc5531deSDag-Erling Smørgrav 		if ((*p == WILL) || (*p == WONT))
1073bc5531deSDag-Erling Smørgrav 			obuf[1] = DONT;
1074bc5531deSDag-Erling Smørgrav 		else if ((*p == DO) || (*p == DONT))
1075bc5531deSDag-Erling Smørgrav 			obuf[1] = WONT;
1076bc5531deSDag-Erling Smørgrav 		else
1077bc5531deSDag-Erling Smørgrav 			continue;
1078bc5531deSDag-Erling Smørgrav 
1079bc5531deSDag-Erling Smørgrav 		p++;
1080bc5531deSDag-Erling Smørgrav 		obuf[2] = *p;
1081bc5531deSDag-Erling Smørgrav 		if (atomicio(vwrite, nfd, obuf, 3) != 3)
1082bc5531deSDag-Erling Smørgrav 			warn("Write Error!");
1083bc5531deSDag-Erling Smørgrav 	}
1084bc5531deSDag-Erling Smørgrav }
1085bc5531deSDag-Erling Smørgrav 
1086bc5531deSDag-Erling Smørgrav /*
1087bc5531deSDag-Erling Smørgrav  * build_ports()
1088bc5531deSDag-Erling Smørgrav  * Build an array of ports in portlist[], listing each port
1089bc5531deSDag-Erling Smørgrav  * that we should try to connect to.
1090bc5531deSDag-Erling Smørgrav  */
1091bc5531deSDag-Erling Smørgrav void
1092bc5531deSDag-Erling Smørgrav build_ports(char *p)
1093bc5531deSDag-Erling Smørgrav {
1094bc5531deSDag-Erling Smørgrav 	const char *errstr;
1095bc5531deSDag-Erling Smørgrav 	char *n;
1096bc5531deSDag-Erling Smørgrav 	int hi, lo, cp;
1097bc5531deSDag-Erling Smørgrav 	int x = 0;
1098bc5531deSDag-Erling Smørgrav 
1099bc5531deSDag-Erling Smørgrav 	if ((n = strchr(p, '-')) != NULL) {
1100bc5531deSDag-Erling Smørgrav 		*n = '\0';
1101bc5531deSDag-Erling Smørgrav 		n++;
1102bc5531deSDag-Erling Smørgrav 
1103bc5531deSDag-Erling Smørgrav 		/* Make sure the ports are in order: lowest->highest. */
1104bc5531deSDag-Erling Smørgrav 		hi = strtonum(n, 1, PORT_MAX, &errstr);
1105bc5531deSDag-Erling Smørgrav 		if (errstr)
1106bc5531deSDag-Erling Smørgrav 			errx(1, "port number %s: %s", errstr, n);
1107bc5531deSDag-Erling Smørgrav 		lo = strtonum(p, 1, PORT_MAX, &errstr);
1108bc5531deSDag-Erling Smørgrav 		if (errstr)
1109bc5531deSDag-Erling Smørgrav 			errx(1, "port number %s: %s", errstr, p);
1110bc5531deSDag-Erling Smørgrav 
1111bc5531deSDag-Erling Smørgrav 		if (lo > hi) {
1112bc5531deSDag-Erling Smørgrav 			cp = hi;
1113bc5531deSDag-Erling Smørgrav 			hi = lo;
1114bc5531deSDag-Erling Smørgrav 			lo = cp;
1115bc5531deSDag-Erling Smørgrav 		}
1116bc5531deSDag-Erling Smørgrav 
1117bc5531deSDag-Erling Smørgrav 		/* Load ports sequentially. */
1118bc5531deSDag-Erling Smørgrav 		for (cp = lo; cp <= hi; cp++) {
1119bc5531deSDag-Erling Smørgrav 			portlist[x] = calloc(1, PORT_MAX_LEN);
1120bc5531deSDag-Erling Smørgrav 			if (portlist[x] == NULL)
1121bc5531deSDag-Erling Smørgrav 				errx(1, "calloc");
1122bc5531deSDag-Erling Smørgrav 			snprintf(portlist[x], PORT_MAX_LEN, "%d", cp);
1123bc5531deSDag-Erling Smørgrav 			x++;
1124bc5531deSDag-Erling Smørgrav 		}
1125bc5531deSDag-Erling Smørgrav 
1126bc5531deSDag-Erling Smørgrav 		/* Randomly swap ports. */
1127bc5531deSDag-Erling Smørgrav 		if (rflag) {
1128bc5531deSDag-Erling Smørgrav 			int y;
1129bc5531deSDag-Erling Smørgrav 			char *c;
1130bc5531deSDag-Erling Smørgrav 
1131bc5531deSDag-Erling Smørgrav 			for (x = 0; x <= (hi - lo); x++) {
1132bc5531deSDag-Erling Smørgrav 				y = (arc4random() & 0xFFFF) % (hi - lo);
1133bc5531deSDag-Erling Smørgrav 				c = portlist[x];
1134bc5531deSDag-Erling Smørgrav 				portlist[x] = portlist[y];
1135bc5531deSDag-Erling Smørgrav 				portlist[y] = c;
1136bc5531deSDag-Erling Smørgrav 			}
1137bc5531deSDag-Erling Smørgrav 		}
1138bc5531deSDag-Erling Smørgrav 	} else {
1139bc5531deSDag-Erling Smørgrav 		hi = strtonum(p, 1, PORT_MAX, &errstr);
1140bc5531deSDag-Erling Smørgrav 		if (errstr)
1141bc5531deSDag-Erling Smørgrav 			errx(1, "port number %s: %s", errstr, p);
1142bc5531deSDag-Erling Smørgrav 		portlist[0] = strdup(p);
1143bc5531deSDag-Erling Smørgrav 		if (portlist[0] == NULL)
1144bc5531deSDag-Erling Smørgrav 			errx(1, "strdup");
1145bc5531deSDag-Erling Smørgrav 	}
1146bc5531deSDag-Erling Smørgrav }
1147bc5531deSDag-Erling Smørgrav 
1148bc5531deSDag-Erling Smørgrav /*
1149bc5531deSDag-Erling Smørgrav  * udptest()
1150bc5531deSDag-Erling Smørgrav  * Do a few writes to see if the UDP port is there.
1151bc5531deSDag-Erling Smørgrav  * Fails once PF state table is full.
1152bc5531deSDag-Erling Smørgrav  */
1153bc5531deSDag-Erling Smørgrav int
1154bc5531deSDag-Erling Smørgrav udptest(int s)
1155bc5531deSDag-Erling Smørgrav {
1156bc5531deSDag-Erling Smørgrav 	int i, ret;
1157bc5531deSDag-Erling Smørgrav 
1158bc5531deSDag-Erling Smørgrav 	for (i = 0; i <= 3; i++) {
1159bc5531deSDag-Erling Smørgrav 		if (write(s, "X", 1) == 1)
1160bc5531deSDag-Erling Smørgrav 			ret = 1;
1161bc5531deSDag-Erling Smørgrav 		else
1162bc5531deSDag-Erling Smørgrav 			ret = -1;
1163bc5531deSDag-Erling Smørgrav 	}
1164bc5531deSDag-Erling Smørgrav 	return (ret);
1165bc5531deSDag-Erling Smørgrav }
1166bc5531deSDag-Erling Smørgrav 
1167bc5531deSDag-Erling Smørgrav void
1168bc5531deSDag-Erling Smørgrav set_common_sockopts(int s)
1169bc5531deSDag-Erling Smørgrav {
1170bc5531deSDag-Erling Smørgrav 	int x = 1;
1171bc5531deSDag-Erling Smørgrav 
1172bc5531deSDag-Erling Smørgrav #ifdef TCP_MD5SIG
1173bc5531deSDag-Erling Smørgrav 	if (Sflag) {
1174bc5531deSDag-Erling Smørgrav 		if (setsockopt(s, IPPROTO_TCP, TCP_MD5SIG,
1175bc5531deSDag-Erling Smørgrav 			&x, sizeof(x)) == -1)
1176bc5531deSDag-Erling Smørgrav 			err(1, "setsockopt");
1177bc5531deSDag-Erling Smørgrav 	}
1178bc5531deSDag-Erling Smørgrav #endif
1179bc5531deSDag-Erling Smørgrav 	if (Dflag) {
1180bc5531deSDag-Erling Smørgrav 		if (setsockopt(s, SOL_SOCKET, SO_DEBUG,
1181bc5531deSDag-Erling Smørgrav 			&x, sizeof(x)) == -1)
1182bc5531deSDag-Erling Smørgrav 			err(1, "setsockopt");
1183bc5531deSDag-Erling Smørgrav 	}
1184bc5531deSDag-Erling Smørgrav 	if (Tflag != -1) {
1185bc5531deSDag-Erling Smørgrav 		if (setsockopt(s, IPPROTO_IP, IP_TOS,
1186bc5531deSDag-Erling Smørgrav 		    &Tflag, sizeof(Tflag)) == -1)
1187bc5531deSDag-Erling Smørgrav 			err(1, "set IP ToS");
1188bc5531deSDag-Erling Smørgrav 	}
1189bc5531deSDag-Erling Smørgrav 	if (Iflag) {
1190bc5531deSDag-Erling Smørgrav 		if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
1191bc5531deSDag-Erling Smørgrav 		    &Iflag, sizeof(Iflag)) == -1)
1192bc5531deSDag-Erling Smørgrav 			err(1, "set TCP receive buffer size");
1193bc5531deSDag-Erling Smørgrav 	}
1194bc5531deSDag-Erling Smørgrav 	if (Oflag) {
1195bc5531deSDag-Erling Smørgrav 		if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
1196bc5531deSDag-Erling Smørgrav 		    &Oflag, sizeof(Oflag)) == -1)
1197bc5531deSDag-Erling Smørgrav 			err(1, "set TCP send buffer size");
1198bc5531deSDag-Erling Smørgrav 	}
1199bc5531deSDag-Erling Smørgrav }
1200bc5531deSDag-Erling Smørgrav 
1201bc5531deSDag-Erling Smørgrav int
1202bc5531deSDag-Erling Smørgrav map_tos(char *s, int *val)
1203bc5531deSDag-Erling Smørgrav {
1204bc5531deSDag-Erling Smørgrav 	/* DiffServ Codepoints and other TOS mappings */
1205bc5531deSDag-Erling Smørgrav 	const struct toskeywords {
1206bc5531deSDag-Erling Smørgrav 		const char	*keyword;
1207bc5531deSDag-Erling Smørgrav 		int		 val;
1208bc5531deSDag-Erling Smørgrav 	} *t, toskeywords[] = {
1209bc5531deSDag-Erling Smørgrav 		{ "af11",		IPTOS_DSCP_AF11 },
1210bc5531deSDag-Erling Smørgrav 		{ "af12",		IPTOS_DSCP_AF12 },
1211bc5531deSDag-Erling Smørgrav 		{ "af13",		IPTOS_DSCP_AF13 },
1212bc5531deSDag-Erling Smørgrav 		{ "af21",		IPTOS_DSCP_AF21 },
1213bc5531deSDag-Erling Smørgrav 		{ "af22",		IPTOS_DSCP_AF22 },
1214bc5531deSDag-Erling Smørgrav 		{ "af23",		IPTOS_DSCP_AF23 },
1215bc5531deSDag-Erling Smørgrav 		{ "af31",		IPTOS_DSCP_AF31 },
1216bc5531deSDag-Erling Smørgrav 		{ "af32",		IPTOS_DSCP_AF32 },
1217bc5531deSDag-Erling Smørgrav 		{ "af33",		IPTOS_DSCP_AF33 },
1218bc5531deSDag-Erling Smørgrav 		{ "af41",		IPTOS_DSCP_AF41 },
1219bc5531deSDag-Erling Smørgrav 		{ "af42",		IPTOS_DSCP_AF42 },
1220bc5531deSDag-Erling Smørgrav 		{ "af43",		IPTOS_DSCP_AF43 },
1221bc5531deSDag-Erling Smørgrav 		{ "critical",		IPTOS_PREC_CRITIC_ECP },
1222bc5531deSDag-Erling Smørgrav 		{ "cs0",		IPTOS_DSCP_CS0 },
1223bc5531deSDag-Erling Smørgrav 		{ "cs1",		IPTOS_DSCP_CS1 },
1224bc5531deSDag-Erling Smørgrav 		{ "cs2",		IPTOS_DSCP_CS2 },
1225bc5531deSDag-Erling Smørgrav 		{ "cs3",		IPTOS_DSCP_CS3 },
1226bc5531deSDag-Erling Smørgrav 		{ "cs4",		IPTOS_DSCP_CS4 },
1227bc5531deSDag-Erling Smørgrav 		{ "cs5",		IPTOS_DSCP_CS5 },
1228bc5531deSDag-Erling Smørgrav 		{ "cs6",		IPTOS_DSCP_CS6 },
1229bc5531deSDag-Erling Smørgrav 		{ "cs7",		IPTOS_DSCP_CS7 },
1230bc5531deSDag-Erling Smørgrav 		{ "ef",			IPTOS_DSCP_EF },
1231bc5531deSDag-Erling Smørgrav 		{ "inetcontrol",	IPTOS_PREC_INTERNETCONTROL },
1232bc5531deSDag-Erling Smørgrav 		{ "lowdelay",		IPTOS_LOWDELAY },
1233bc5531deSDag-Erling Smørgrav 		{ "netcontrol",		IPTOS_PREC_NETCONTROL },
1234bc5531deSDag-Erling Smørgrav 		{ "reliability",	IPTOS_RELIABILITY },
1235bc5531deSDag-Erling Smørgrav 		{ "throughput",		IPTOS_THROUGHPUT },
1236bc5531deSDag-Erling Smørgrav 		{ NULL, 		-1 },
1237bc5531deSDag-Erling Smørgrav 	};
1238bc5531deSDag-Erling Smørgrav 
1239bc5531deSDag-Erling Smørgrav 	for (t = toskeywords; t->keyword != NULL; t++) {
1240bc5531deSDag-Erling Smørgrav 		if (strcmp(s, t->keyword) == 0) {
1241bc5531deSDag-Erling Smørgrav 			*val = t->val;
1242bc5531deSDag-Erling Smørgrav 			return (1);
1243bc5531deSDag-Erling Smørgrav 		}
1244bc5531deSDag-Erling Smørgrav 	}
1245bc5531deSDag-Erling Smørgrav 
1246bc5531deSDag-Erling Smørgrav 	return (0);
1247bc5531deSDag-Erling Smørgrav }
1248bc5531deSDag-Erling Smørgrav 
1249bc5531deSDag-Erling Smørgrav void
1250bc5531deSDag-Erling Smørgrav report_connect(const struct sockaddr *sa, socklen_t salen)
1251bc5531deSDag-Erling Smørgrav {
1252bc5531deSDag-Erling Smørgrav 	char remote_host[NI_MAXHOST];
1253bc5531deSDag-Erling Smørgrav 	char remote_port[NI_MAXSERV];
1254bc5531deSDag-Erling Smørgrav 	int herr;
1255bc5531deSDag-Erling Smørgrav 	int flags = NI_NUMERICSERV;
1256bc5531deSDag-Erling Smørgrav 
1257bc5531deSDag-Erling Smørgrav 	if (nflag)
1258bc5531deSDag-Erling Smørgrav 		flags |= NI_NUMERICHOST;
1259bc5531deSDag-Erling Smørgrav 
1260bc5531deSDag-Erling Smørgrav 	if ((herr = getnameinfo(sa, salen,
1261bc5531deSDag-Erling Smørgrav 	    remote_host, sizeof(remote_host),
1262bc5531deSDag-Erling Smørgrav 	    remote_port, sizeof(remote_port),
1263bc5531deSDag-Erling Smørgrav 	    flags)) != 0) {
1264bc5531deSDag-Erling Smørgrav 		if (herr == EAI_SYSTEM)
1265bc5531deSDag-Erling Smørgrav 			err(1, "getnameinfo");
1266bc5531deSDag-Erling Smørgrav 		else
1267bc5531deSDag-Erling Smørgrav 			errx(1, "getnameinfo: %s", gai_strerror(herr));
1268bc5531deSDag-Erling Smørgrav 	}
1269bc5531deSDag-Erling Smørgrav 
1270bc5531deSDag-Erling Smørgrav 	fprintf(stderr,
1271bc5531deSDag-Erling Smørgrav 	    "Connection from %s %s "
1272bc5531deSDag-Erling Smørgrav 	    "received!\n", remote_host, remote_port);
1273bc5531deSDag-Erling Smørgrav }
1274bc5531deSDag-Erling Smørgrav 
1275bc5531deSDag-Erling Smørgrav void
1276bc5531deSDag-Erling Smørgrav help(void)
1277bc5531deSDag-Erling Smørgrav {
1278bc5531deSDag-Erling Smørgrav 	usage(0);
1279bc5531deSDag-Erling Smørgrav 	fprintf(stderr, "\tCommand Summary:\n\
1280bc5531deSDag-Erling Smørgrav 	\t-4		Use IPv4\n\
1281bc5531deSDag-Erling Smørgrav 	\t-6		Use IPv6\n\
1282bc5531deSDag-Erling Smørgrav 	\t-D		Enable the debug socket option\n\
1283bc5531deSDag-Erling Smørgrav 	\t-d		Detach from stdin\n\
1284bc5531deSDag-Erling Smørgrav 	\t-F		Pass socket fd\n\
1285bc5531deSDag-Erling Smørgrav 	\t-h		This help text\n\
1286bc5531deSDag-Erling Smørgrav 	\t-I length	TCP receive buffer length\n\
1287bc5531deSDag-Erling Smørgrav 	\t-i secs\t	Delay interval for lines sent, ports scanned\n\
1288bc5531deSDag-Erling Smørgrav 	\t-k		Keep inbound sockets open for multiple connects\n\
1289bc5531deSDag-Erling Smørgrav 	\t-l		Listen mode, for inbound connects\n\
1290bc5531deSDag-Erling Smørgrav 	\t-N		Shutdown the network socket after EOF on stdin\n\
1291bc5531deSDag-Erling Smørgrav 	\t-n		Suppress name/port resolutions\n\
1292bc5531deSDag-Erling Smørgrav 	\t-O length	TCP send buffer length\n\
1293bc5531deSDag-Erling Smørgrav 	\t-P proxyuser\tUsername for proxy authentication\n\
1294bc5531deSDag-Erling Smørgrav 	\t-p port\t	Specify local port for remote connects\n\
1295bc5531deSDag-Erling Smørgrav 	\t-r		Randomize remote ports\n\
1296bc5531deSDag-Erling Smørgrav 	\t-S		Enable the TCP MD5 signature option\n\
1297bc5531deSDag-Erling Smørgrav 	\t-s addr\t	Local source address\n\
1298bc5531deSDag-Erling Smørgrav 	\t-T toskeyword\tSet IP Type of Service\n\
1299bc5531deSDag-Erling Smørgrav 	\t-t		Answer TELNET negotiation\n\
1300bc5531deSDag-Erling Smørgrav 	\t-U		Use UNIX domain socket\n\
1301bc5531deSDag-Erling Smørgrav 	\t-u		UDP mode\n\
1302bc5531deSDag-Erling Smørgrav 	\t-V rtable	Specify alternate routing table\n\
1303bc5531deSDag-Erling Smørgrav 	\t-v		Verbose\n\
1304bc5531deSDag-Erling Smørgrav 	\t-w secs\t	Timeout for connects and final net reads\n\
1305bc5531deSDag-Erling Smørgrav 	\t-X proto	Proxy protocol: \"4\", \"5\" (SOCKS) or \"connect\"\n\
1306bc5531deSDag-Erling Smørgrav 	\t-x addr[:port]\tSpecify proxy address and port\n\
1307bc5531deSDag-Erling Smørgrav 	\t-z		Zero-I/O mode [used for scanning]\n\
1308bc5531deSDag-Erling Smørgrav 	Port numbers can be individual or ranges: lo-hi [inclusive]\n");
1309bc5531deSDag-Erling Smørgrav 	exit(1);
1310bc5531deSDag-Erling Smørgrav }
1311bc5531deSDag-Erling Smørgrav 
1312bc5531deSDag-Erling Smørgrav void
1313bc5531deSDag-Erling Smørgrav usage(int ret)
1314bc5531deSDag-Erling Smørgrav {
1315bc5531deSDag-Erling Smørgrav 	fprintf(stderr,
1316bc5531deSDag-Erling Smørgrav 	    "usage: nc [-46DdFhklNnrStUuvz] [-I length] [-i interval] [-O length]\n"
1317bc5531deSDag-Erling Smørgrav 	    "\t  [-P proxy_username] [-p source_port] [-s source] [-T ToS]\n"
1318bc5531deSDag-Erling Smørgrav 	    "\t  [-V rtable] [-w timeout] [-X proxy_protocol]\n"
1319bc5531deSDag-Erling Smørgrav 	    "\t  [-x proxy_address[:port]] [destination] [port]\n");
1320bc5531deSDag-Erling Smørgrav 	if (ret)
1321bc5531deSDag-Erling Smørgrav 		exit(1);
1322bc5531deSDag-Erling Smørgrav }
1323bc5531deSDag-Erling Smørgrav 
1324bc5531deSDag-Erling Smørgrav /* *** src/usr.bin/nc/socks.c *** */
1325bc5531deSDag-Erling Smørgrav 
1326bc5531deSDag-Erling Smørgrav 
1327bc5531deSDag-Erling Smørgrav /*	$OpenBSD: socks.c,v 1.20 2012/03/08 09:56:28 espie Exp $	*/
1328bc5531deSDag-Erling Smørgrav 
1329bc5531deSDag-Erling Smørgrav /*
1330bc5531deSDag-Erling Smørgrav  * Copyright (c) 1999 Niklas Hallqvist.  All rights reserved.
1331bc5531deSDag-Erling Smørgrav  * Copyright (c) 2004, 2005 Damien Miller.  All rights reserved.
1332bc5531deSDag-Erling Smørgrav  *
1333bc5531deSDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
1334bc5531deSDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
1335bc5531deSDag-Erling Smørgrav  * are met:
1336bc5531deSDag-Erling Smørgrav  * 1. Redistributions of source code must retain the above copyright
1337bc5531deSDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer.
1338bc5531deSDag-Erling Smørgrav  * 2. Redistributions in binary form must reproduce the above copyright
1339bc5531deSDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer in the
1340bc5531deSDag-Erling Smørgrav  *    documentation and/or other materials provided with the distribution.
1341bc5531deSDag-Erling Smørgrav  *
1342bc5531deSDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1343bc5531deSDag-Erling Smørgrav  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1344bc5531deSDag-Erling Smørgrav  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1345bc5531deSDag-Erling Smørgrav  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1346bc5531deSDag-Erling Smørgrav  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1347bc5531deSDag-Erling Smørgrav  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1348bc5531deSDag-Erling Smørgrav  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1349bc5531deSDag-Erling Smørgrav  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1350bc5531deSDag-Erling Smørgrav  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1351bc5531deSDag-Erling Smørgrav  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1352bc5531deSDag-Erling Smørgrav  */
1353bc5531deSDag-Erling Smørgrav 
1354bc5531deSDag-Erling Smørgrav #include <sys/types.h>
1355bc5531deSDag-Erling Smørgrav #include <sys/socket.h>
1356bc5531deSDag-Erling Smørgrav #include <netinet/in.h>
1357bc5531deSDag-Erling Smørgrav #include <arpa/inet.h>
1358bc5531deSDag-Erling Smørgrav 
1359bc5531deSDag-Erling Smørgrav #include <errno.h>
1360bc5531deSDag-Erling Smørgrav #include <netdb.h>
1361bc5531deSDag-Erling Smørgrav #include <stdio.h>
1362bc5531deSDag-Erling Smørgrav #include <stdlib.h>
1363bc5531deSDag-Erling Smørgrav #include <string.h>
1364bc5531deSDag-Erling Smørgrav #include <unistd.h>
1365bc5531deSDag-Erling Smørgrav #include <resolv.h>
1366bc5531deSDag-Erling Smørgrav 
1367bc5531deSDag-Erling Smørgrav #define SOCKS_PORT	"1080"
1368bc5531deSDag-Erling Smørgrav #define HTTP_PROXY_PORT	"3128"
1369bc5531deSDag-Erling Smørgrav #define HTTP_MAXHDRS	64
1370bc5531deSDag-Erling Smørgrav #define SOCKS_V5	5
1371bc5531deSDag-Erling Smørgrav #define SOCKS_V4	4
1372bc5531deSDag-Erling Smørgrav #define SOCKS_NOAUTH	0
1373bc5531deSDag-Erling Smørgrav #define SOCKS_NOMETHOD	0xff
1374bc5531deSDag-Erling Smørgrav #define SOCKS_CONNECT	1
1375bc5531deSDag-Erling Smørgrav #define SOCKS_IPV4	1
1376bc5531deSDag-Erling Smørgrav #define SOCKS_DOMAIN	3
1377bc5531deSDag-Erling Smørgrav #define SOCKS_IPV6	4
1378bc5531deSDag-Erling Smørgrav 
1379bc5531deSDag-Erling Smørgrav int	remote_connect(const char *, const char *, struct addrinfo);
1380bc5531deSDag-Erling Smørgrav int	socks_connect(const char *, const char *, struct addrinfo,
1381bc5531deSDag-Erling Smørgrav 	    const char *, const char *, struct addrinfo, int,
1382bc5531deSDag-Erling Smørgrav 	    const char *);
1383bc5531deSDag-Erling Smørgrav 
1384bc5531deSDag-Erling Smørgrav static int
1385bc5531deSDag-Erling Smørgrav decode_addrport(const char *h, const char *p, struct sockaddr *addr,
1386bc5531deSDag-Erling Smørgrav     socklen_t addrlen, int v4only, int numeric)
1387bc5531deSDag-Erling Smørgrav {
1388bc5531deSDag-Erling Smørgrav 	int r;
1389bc5531deSDag-Erling Smørgrav 	struct addrinfo hints, *res;
1390bc5531deSDag-Erling Smørgrav 
1391bc5531deSDag-Erling Smørgrav 	bzero(&hints, sizeof(hints));
1392bc5531deSDag-Erling Smørgrav 	hints.ai_family = v4only ? PF_INET : PF_UNSPEC;
1393bc5531deSDag-Erling Smørgrav 	hints.ai_flags = numeric ? AI_NUMERICHOST : 0;
1394bc5531deSDag-Erling Smørgrav 	hints.ai_socktype = SOCK_STREAM;
1395bc5531deSDag-Erling Smørgrav 	r = getaddrinfo(h, p, &hints, &res);
1396bc5531deSDag-Erling Smørgrav 	/* Don't fatal when attempting to convert a numeric address */
1397bc5531deSDag-Erling Smørgrav 	if (r != 0) {
1398bc5531deSDag-Erling Smørgrav 		if (!numeric) {
1399bc5531deSDag-Erling Smørgrav 			errx(1, "getaddrinfo(\"%.64s\", \"%.64s\"): %s", h, p,
1400bc5531deSDag-Erling Smørgrav 			    gai_strerror(r));
1401bc5531deSDag-Erling Smørgrav 		}
1402bc5531deSDag-Erling Smørgrav 		return (-1);
1403bc5531deSDag-Erling Smørgrav 	}
1404bc5531deSDag-Erling Smørgrav 	if (addrlen < res->ai_addrlen) {
1405bc5531deSDag-Erling Smørgrav 		freeaddrinfo(res);
1406bc5531deSDag-Erling Smørgrav 		errx(1, "internal error: addrlen < res->ai_addrlen");
1407bc5531deSDag-Erling Smørgrav 	}
1408bc5531deSDag-Erling Smørgrav 	memcpy(addr, res->ai_addr, res->ai_addrlen);
1409bc5531deSDag-Erling Smørgrav 	freeaddrinfo(res);
1410bc5531deSDag-Erling Smørgrav 	return (0);
1411bc5531deSDag-Erling Smørgrav }
1412bc5531deSDag-Erling Smørgrav 
1413bc5531deSDag-Erling Smørgrav static int
1414bc5531deSDag-Erling Smørgrav proxy_read_line(int fd, char *buf, size_t bufsz)
1415bc5531deSDag-Erling Smørgrav {
1416bc5531deSDag-Erling Smørgrav 	size_t off;
1417bc5531deSDag-Erling Smørgrav 
1418bc5531deSDag-Erling Smørgrav 	for(off = 0;;) {
1419bc5531deSDag-Erling Smørgrav 		if (off >= bufsz)
1420bc5531deSDag-Erling Smørgrav 			errx(1, "proxy read too long");
1421bc5531deSDag-Erling Smørgrav 		if (atomicio(read, fd, buf + off, 1) != 1)
1422bc5531deSDag-Erling Smørgrav 			err(1, "proxy read");
1423bc5531deSDag-Erling Smørgrav 		/* Skip CR */
1424bc5531deSDag-Erling Smørgrav 		if (buf[off] == '\r')
1425bc5531deSDag-Erling Smørgrav 			continue;
1426bc5531deSDag-Erling Smørgrav 		if (buf[off] == '\n') {
1427bc5531deSDag-Erling Smørgrav 			buf[off] = '\0';
1428bc5531deSDag-Erling Smørgrav 			break;
1429bc5531deSDag-Erling Smørgrav 		}
1430bc5531deSDag-Erling Smørgrav 		off++;
1431bc5531deSDag-Erling Smørgrav 	}
1432bc5531deSDag-Erling Smørgrav 	return (off);
1433bc5531deSDag-Erling Smørgrav }
1434bc5531deSDag-Erling Smørgrav 
1435bc5531deSDag-Erling Smørgrav static const char *
1436bc5531deSDag-Erling Smørgrav getproxypass(const char *proxyuser, const char *proxyhost)
1437bc5531deSDag-Erling Smørgrav {
1438bc5531deSDag-Erling Smørgrav 	char prompt[512];
1439bc5531deSDag-Erling Smørgrav 	static char pw[256];
1440bc5531deSDag-Erling Smørgrav 
1441bc5531deSDag-Erling Smørgrav 	snprintf(prompt, sizeof(prompt), "Proxy password for %s@%s: ",
1442bc5531deSDag-Erling Smørgrav 	   proxyuser, proxyhost);
1443bc5531deSDag-Erling Smørgrav 	if (readpassphrase(prompt, pw, sizeof(pw), RPP_REQUIRE_TTY) == NULL)
1444bc5531deSDag-Erling Smørgrav 		errx(1, "Unable to read proxy passphrase");
1445bc5531deSDag-Erling Smørgrav 	return (pw);
1446bc5531deSDag-Erling Smørgrav }
1447bc5531deSDag-Erling Smørgrav 
1448bc5531deSDag-Erling Smørgrav int
1449bc5531deSDag-Erling Smørgrav socks_connect(const char *host, const char *port,
1450bc5531deSDag-Erling Smørgrav     struct addrinfo hints __attribute__ ((__unused__)),
1451bc5531deSDag-Erling Smørgrav     const char *proxyhost, const char *proxyport, struct addrinfo proxyhints,
1452bc5531deSDag-Erling Smørgrav     int socksv, const char *proxyuser)
1453bc5531deSDag-Erling Smørgrav {
1454bc5531deSDag-Erling Smørgrav 	int proxyfd, r, authretry = 0;
1455bc5531deSDag-Erling Smørgrav 	size_t hlen, wlen = 0;
1456bc5531deSDag-Erling Smørgrav 	unsigned char buf[1024];
1457bc5531deSDag-Erling Smørgrav 	size_t cnt;
1458bc5531deSDag-Erling Smørgrav 	struct sockaddr_storage addr;
1459bc5531deSDag-Erling Smørgrav 	struct sockaddr_in *in4 = (struct sockaddr_in *)&addr;
1460bc5531deSDag-Erling Smørgrav 	struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&addr;
1461bc5531deSDag-Erling Smørgrav 	in_port_t serverport;
1462bc5531deSDag-Erling Smørgrav 	const char *proxypass = NULL;
1463bc5531deSDag-Erling Smørgrav 
1464bc5531deSDag-Erling Smørgrav 	if (proxyport == NULL)
1465bc5531deSDag-Erling Smørgrav 		proxyport = (socksv == -1) ? HTTP_PROXY_PORT : SOCKS_PORT;
1466bc5531deSDag-Erling Smørgrav 
1467bc5531deSDag-Erling Smørgrav 	/* Abuse API to lookup port */
1468bc5531deSDag-Erling Smørgrav 	if (decode_addrport("0.0.0.0", port, (struct sockaddr *)&addr,
1469bc5531deSDag-Erling Smørgrav 	    sizeof(addr), 1, 1) == -1)
1470bc5531deSDag-Erling Smørgrav 		errx(1, "unknown port \"%.64s\"", port);
1471bc5531deSDag-Erling Smørgrav 	serverport = in4->sin_port;
1472bc5531deSDag-Erling Smørgrav 
1473bc5531deSDag-Erling Smørgrav  again:
1474bc5531deSDag-Erling Smørgrav 	if (authretry++ > 3)
1475bc5531deSDag-Erling Smørgrav 		errx(1, "Too many authentication failures");
1476bc5531deSDag-Erling Smørgrav 
1477bc5531deSDag-Erling Smørgrav 	proxyfd = remote_connect(proxyhost, proxyport, proxyhints);
1478bc5531deSDag-Erling Smørgrav 
1479bc5531deSDag-Erling Smørgrav 	if (proxyfd < 0)
1480bc5531deSDag-Erling Smørgrav 		return (-1);
1481bc5531deSDag-Erling Smørgrav 
1482bc5531deSDag-Erling Smørgrav 	if (socksv == 5) {
1483bc5531deSDag-Erling Smørgrav 		if (decode_addrport(host, port, (struct sockaddr *)&addr,
1484bc5531deSDag-Erling Smørgrav 		    sizeof(addr), 0, 1) == -1)
1485bc5531deSDag-Erling Smørgrav 			addr.ss_family = 0; /* used in switch below */
1486bc5531deSDag-Erling Smørgrav 
1487bc5531deSDag-Erling Smørgrav 		/* Version 5, one method: no authentication */
1488bc5531deSDag-Erling Smørgrav 		buf[0] = SOCKS_V5;
1489bc5531deSDag-Erling Smørgrav 		buf[1] = 1;
1490bc5531deSDag-Erling Smørgrav 		buf[2] = SOCKS_NOAUTH;
1491bc5531deSDag-Erling Smørgrav 		cnt = atomicio(vwrite, proxyfd, buf, 3);
1492bc5531deSDag-Erling Smørgrav 		if (cnt != 3)
1493bc5531deSDag-Erling Smørgrav 			err(1, "write failed (%zu/3)", cnt);
1494bc5531deSDag-Erling Smørgrav 
1495bc5531deSDag-Erling Smørgrav 		cnt = atomicio(read, proxyfd, buf, 2);
1496bc5531deSDag-Erling Smørgrav 		if (cnt != 2)
1497bc5531deSDag-Erling Smørgrav 			err(1, "read failed (%zu/3)", cnt);
1498bc5531deSDag-Erling Smørgrav 
1499bc5531deSDag-Erling Smørgrav 		if (buf[1] == SOCKS_NOMETHOD)
1500bc5531deSDag-Erling Smørgrav 			errx(1, "authentication method negotiation failed");
1501bc5531deSDag-Erling Smørgrav 
1502bc5531deSDag-Erling Smørgrav 		switch (addr.ss_family) {
1503bc5531deSDag-Erling Smørgrav 		case 0:
1504bc5531deSDag-Erling Smørgrav 			/* Version 5, connect: domain name */
1505bc5531deSDag-Erling Smørgrav 
1506bc5531deSDag-Erling Smørgrav 			/* Max domain name length is 255 bytes */
1507bc5531deSDag-Erling Smørgrav 			hlen = strlen(host);
1508bc5531deSDag-Erling Smørgrav 			if (hlen > 255)
1509bc5531deSDag-Erling Smørgrav 				errx(1, "host name too long for SOCKS5");
1510bc5531deSDag-Erling Smørgrav 			buf[0] = SOCKS_V5;
1511bc5531deSDag-Erling Smørgrav 			buf[1] = SOCKS_CONNECT;
1512bc5531deSDag-Erling Smørgrav 			buf[2] = 0;
1513bc5531deSDag-Erling Smørgrav 			buf[3] = SOCKS_DOMAIN;
1514bc5531deSDag-Erling Smørgrav 			buf[4] = hlen;
1515bc5531deSDag-Erling Smørgrav 			memcpy(buf + 5, host, hlen);
1516bc5531deSDag-Erling Smørgrav 			memcpy(buf + 5 + hlen, &serverport, sizeof serverport);
1517bc5531deSDag-Erling Smørgrav 			wlen = 7 + hlen;
1518bc5531deSDag-Erling Smørgrav 			break;
1519bc5531deSDag-Erling Smørgrav 		case AF_INET:
1520bc5531deSDag-Erling Smørgrav 			/* Version 5, connect: IPv4 address */
1521bc5531deSDag-Erling Smørgrav 			buf[0] = SOCKS_V5;
1522bc5531deSDag-Erling Smørgrav 			buf[1] = SOCKS_CONNECT;
1523bc5531deSDag-Erling Smørgrav 			buf[2] = 0;
1524bc5531deSDag-Erling Smørgrav 			buf[3] = SOCKS_IPV4;
1525bc5531deSDag-Erling Smørgrav 			memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr);
1526bc5531deSDag-Erling Smørgrav 			memcpy(buf + 8, &in4->sin_port, sizeof in4->sin_port);
1527bc5531deSDag-Erling Smørgrav 			wlen = 10;
1528bc5531deSDag-Erling Smørgrav 			break;
1529bc5531deSDag-Erling Smørgrav 		case AF_INET6:
1530bc5531deSDag-Erling Smørgrav 			/* Version 5, connect: IPv6 address */
1531bc5531deSDag-Erling Smørgrav 			buf[0] = SOCKS_V5;
1532bc5531deSDag-Erling Smørgrav 			buf[1] = SOCKS_CONNECT;
1533bc5531deSDag-Erling Smørgrav 			buf[2] = 0;
1534bc5531deSDag-Erling Smørgrav 			buf[3] = SOCKS_IPV6;
1535bc5531deSDag-Erling Smørgrav 			memcpy(buf + 4, &in6->sin6_addr, sizeof in6->sin6_addr);
1536bc5531deSDag-Erling Smørgrav 			memcpy(buf + 20, &in6->sin6_port,
1537bc5531deSDag-Erling Smørgrav 			    sizeof in6->sin6_port);
1538bc5531deSDag-Erling Smørgrav 			wlen = 22;
1539bc5531deSDag-Erling Smørgrav 			break;
1540bc5531deSDag-Erling Smørgrav 		default:
1541bc5531deSDag-Erling Smørgrav 			errx(1, "internal error: silly AF");
1542bc5531deSDag-Erling Smørgrav 		}
1543bc5531deSDag-Erling Smørgrav 
1544bc5531deSDag-Erling Smørgrav 		cnt = atomicio(vwrite, proxyfd, buf, wlen);
1545bc5531deSDag-Erling Smørgrav 		if (cnt != wlen)
1546bc5531deSDag-Erling Smørgrav 			err(1, "write failed (%zu/%zu)", cnt, wlen);
1547bc5531deSDag-Erling Smørgrav 
1548bc5531deSDag-Erling Smørgrav 		cnt = atomicio(read, proxyfd, buf, 4);
1549bc5531deSDag-Erling Smørgrav 		if (cnt != 4)
1550bc5531deSDag-Erling Smørgrav 			err(1, "read failed (%zu/4)", cnt);
1551bc5531deSDag-Erling Smørgrav 		if (buf[1] != 0)
1552bc5531deSDag-Erling Smørgrav 			errx(1, "connection failed, SOCKS error %d", buf[1]);
1553bc5531deSDag-Erling Smørgrav 		switch (buf[3]) {
1554bc5531deSDag-Erling Smørgrav 		case SOCKS_IPV4:
1555bc5531deSDag-Erling Smørgrav 			cnt = atomicio(read, proxyfd, buf + 4, 6);
1556bc5531deSDag-Erling Smørgrav 			if (cnt != 6)
1557bc5531deSDag-Erling Smørgrav 				err(1, "read failed (%zu/6)", cnt);
1558bc5531deSDag-Erling Smørgrav 			break;
1559bc5531deSDag-Erling Smørgrav 		case SOCKS_IPV6:
1560bc5531deSDag-Erling Smørgrav 			cnt = atomicio(read, proxyfd, buf + 4, 18);
1561bc5531deSDag-Erling Smørgrav 			if (cnt != 18)
1562bc5531deSDag-Erling Smørgrav 				err(1, "read failed (%zu/18)", cnt);
1563bc5531deSDag-Erling Smørgrav 			break;
1564bc5531deSDag-Erling Smørgrav 		default:
1565bc5531deSDag-Erling Smørgrav 			errx(1, "connection failed, unsupported address type");
1566bc5531deSDag-Erling Smørgrav 		}
1567bc5531deSDag-Erling Smørgrav 	} else if (socksv == 4) {
1568bc5531deSDag-Erling Smørgrav 		/* This will exit on lookup failure */
1569bc5531deSDag-Erling Smørgrav 		decode_addrport(host, port, (struct sockaddr *)&addr,
1570bc5531deSDag-Erling Smørgrav 		    sizeof(addr), 1, 0);
1571bc5531deSDag-Erling Smørgrav 
1572bc5531deSDag-Erling Smørgrav 		/* Version 4 */
1573bc5531deSDag-Erling Smørgrav 		buf[0] = SOCKS_V4;
1574bc5531deSDag-Erling Smørgrav 		buf[1] = SOCKS_CONNECT;	/* connect */
1575bc5531deSDag-Erling Smørgrav 		memcpy(buf + 2, &in4->sin_port, sizeof in4->sin_port);
1576bc5531deSDag-Erling Smørgrav 		memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr);
1577bc5531deSDag-Erling Smørgrav 		buf[8] = 0;	/* empty username */
1578bc5531deSDag-Erling Smørgrav 		wlen = 9;
1579bc5531deSDag-Erling Smørgrav 
1580bc5531deSDag-Erling Smørgrav 		cnt = atomicio(vwrite, proxyfd, buf, wlen);
1581bc5531deSDag-Erling Smørgrav 		if (cnt != wlen)
1582bc5531deSDag-Erling Smørgrav 			err(1, "write failed (%zu/%zu)", cnt, wlen);
1583bc5531deSDag-Erling Smørgrav 
1584bc5531deSDag-Erling Smørgrav 		cnt = atomicio(read, proxyfd, buf, 8);
1585bc5531deSDag-Erling Smørgrav 		if (cnt != 8)
1586bc5531deSDag-Erling Smørgrav 			err(1, "read failed (%zu/8)", cnt);
1587bc5531deSDag-Erling Smørgrav 		if (buf[1] != 90)
1588bc5531deSDag-Erling Smørgrav 			errx(1, "connection failed, SOCKS error %d", buf[1]);
1589bc5531deSDag-Erling Smørgrav 	} else if (socksv == -1) {
1590bc5531deSDag-Erling Smørgrav 		/* HTTP proxy CONNECT */
1591bc5531deSDag-Erling Smørgrav 
1592bc5531deSDag-Erling Smørgrav 		/* Disallow bad chars in hostname */
1593bc5531deSDag-Erling Smørgrav 		if (strcspn(host, "\r\n\t []:") != strlen(host))
1594bc5531deSDag-Erling Smørgrav 			errx(1, "Invalid hostname");
1595bc5531deSDag-Erling Smørgrav 
1596bc5531deSDag-Erling Smørgrav 		/* Try to be sane about numeric IPv6 addresses */
1597bc5531deSDag-Erling Smørgrav 		if (strchr(host, ':') != NULL) {
1598bc5531deSDag-Erling Smørgrav 			r = snprintf(buf, sizeof(buf),
1599bc5531deSDag-Erling Smørgrav 			    "CONNECT [%s]:%d HTTP/1.0\r\n",
1600bc5531deSDag-Erling Smørgrav 			    host, ntohs(serverport));
1601bc5531deSDag-Erling Smørgrav 		} else {
1602bc5531deSDag-Erling Smørgrav 			r = snprintf(buf, sizeof(buf),
1603bc5531deSDag-Erling Smørgrav 			    "CONNECT %s:%d HTTP/1.0\r\n",
1604bc5531deSDag-Erling Smørgrav 			    host, ntohs(serverport));
1605bc5531deSDag-Erling Smørgrav 		}
1606bc5531deSDag-Erling Smørgrav 		if (r == -1 || (size_t)r >= sizeof(buf))
1607bc5531deSDag-Erling Smørgrav 			errx(1, "hostname too long");
1608bc5531deSDag-Erling Smørgrav 		r = strlen(buf);
1609bc5531deSDag-Erling Smørgrav 
1610bc5531deSDag-Erling Smørgrav 		cnt = atomicio(vwrite, proxyfd, buf, r);
1611bc5531deSDag-Erling Smørgrav 		if (cnt != (size_t)r)
1612bc5531deSDag-Erling Smørgrav 			err(1, "write failed (%zu/%d)", cnt, r);
1613bc5531deSDag-Erling Smørgrav 
1614bc5531deSDag-Erling Smørgrav 		if (authretry > 1) {
1615bc5531deSDag-Erling Smørgrav 			char resp[1024];
1616bc5531deSDag-Erling Smørgrav 
1617bc5531deSDag-Erling Smørgrav 			proxypass = getproxypass(proxyuser, proxyhost);
1618bc5531deSDag-Erling Smørgrav 			r = snprintf(buf, sizeof(buf), "%s:%s",
1619bc5531deSDag-Erling Smørgrav 			    proxyuser, proxypass);
1620bc5531deSDag-Erling Smørgrav 			if (r == -1 || (size_t)r >= sizeof(buf) ||
1621bc5531deSDag-Erling Smørgrav 			    b64_ntop(buf, strlen(buf), resp,
1622bc5531deSDag-Erling Smørgrav 			    sizeof(resp)) == -1)
1623bc5531deSDag-Erling Smørgrav 				errx(1, "Proxy username/password too long");
1624bc5531deSDag-Erling Smørgrav 			r = snprintf(buf, sizeof(buf), "Proxy-Authorization: "
1625bc5531deSDag-Erling Smørgrav 			    "Basic %s\r\n", resp);
1626bc5531deSDag-Erling Smørgrav 			if (r == -1 || (size_t)r >= sizeof(buf))
1627bc5531deSDag-Erling Smørgrav 				errx(1, "Proxy auth response too long");
1628bc5531deSDag-Erling Smørgrav 			r = strlen(buf);
1629bc5531deSDag-Erling Smørgrav 			if ((cnt = atomicio(vwrite, proxyfd, buf, r)) != (size_t)r)
1630bc5531deSDag-Erling Smørgrav 				err(1, "write failed (%zu/%d)", cnt, r);
1631bc5531deSDag-Erling Smørgrav 		}
1632bc5531deSDag-Erling Smørgrav 
1633bc5531deSDag-Erling Smørgrav 		/* Terminate headers */
1634bc5531deSDag-Erling Smørgrav 		if ((r = atomicio(vwrite, proxyfd, "\r\n", 2)) != 2)
1635bc5531deSDag-Erling Smørgrav 			err(1, "write failed (2/%d)", r);
1636bc5531deSDag-Erling Smørgrav 
1637bc5531deSDag-Erling Smørgrav 		/* Read status reply */
1638bc5531deSDag-Erling Smørgrav 		proxy_read_line(proxyfd, buf, sizeof(buf));
1639bc5531deSDag-Erling Smørgrav 		if (proxyuser != NULL &&
1640bc5531deSDag-Erling Smørgrav 		    strncmp(buf, "HTTP/1.0 407 ", 12) == 0) {
1641bc5531deSDag-Erling Smørgrav 			if (authretry > 1) {
1642bc5531deSDag-Erling Smørgrav 				fprintf(stderr, "Proxy authentication "
1643bc5531deSDag-Erling Smørgrav 				    "failed\n");
1644bc5531deSDag-Erling Smørgrav 			}
1645bc5531deSDag-Erling Smørgrav 			close(proxyfd);
1646bc5531deSDag-Erling Smørgrav 			goto again;
1647bc5531deSDag-Erling Smørgrav 		} else if (strncmp(buf, "HTTP/1.0 200 ", 12) != 0 &&
1648bc5531deSDag-Erling Smørgrav 		    strncmp(buf, "HTTP/1.1 200 ", 12) != 0)
1649bc5531deSDag-Erling Smørgrav 			errx(1, "Proxy error: \"%s\"", buf);
1650bc5531deSDag-Erling Smørgrav 
1651bc5531deSDag-Erling Smørgrav 		/* Headers continue until we hit an empty line */
1652bc5531deSDag-Erling Smørgrav 		for (r = 0; r < HTTP_MAXHDRS; r++) {
1653bc5531deSDag-Erling Smørgrav 			proxy_read_line(proxyfd, buf, sizeof(buf));
1654bc5531deSDag-Erling Smørgrav 			if (*buf == '\0')
1655bc5531deSDag-Erling Smørgrav 				break;
1656bc5531deSDag-Erling Smørgrav 		}
1657bc5531deSDag-Erling Smørgrav 		if (*buf != '\0')
1658bc5531deSDag-Erling Smørgrav 			errx(1, "Too many proxy headers received");
1659bc5531deSDag-Erling Smørgrav 	} else
1660bc5531deSDag-Erling Smørgrav 		errx(1, "Unknown proxy protocol %d", socksv);
1661bc5531deSDag-Erling Smørgrav 
1662bc5531deSDag-Erling Smørgrav 	return (proxyfd);
1663bc5531deSDag-Erling Smørgrav }
1664bc5531deSDag-Erling Smørgrav 
1665