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