1/* $OpenBSD: netcat.c,v 1.82 2005/07/24 09:33:56 marius Exp $ */
2/*
3 * Copyright (c) 2001 Eric Jackson <ericj@monkey.org>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *   notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *   notice, this list of conditions and the following disclaimer in the
13 *   documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *   derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * Re-written nc(1) for OpenBSD. Original implementation by
31 * *Hobbit* <hobbit@avian.org>.
32 */
33
34#include <sys/types.h>
35#include <sys/socket.h>
36#include <sys/time.h>
37#include <sys/un.h>
38#include <sys/event.h>
39#include <sys/ioctl.h>
40
41#include <net/if.h>
42#include <netinet/in.h>
43#include <netinet/ip.h>
44#include <netinet/tcp.h>
45#include <arpa/telnet.h>
46#include <arpa/inet.h>
47
48#include <err.h>
49#include <errno.h>
50#ifdef __APPLE__
51#include <limits.h>
52#endif /* __APPLE__ */
53#include <netdb.h>
54#include <poll.h>
55#include <stdarg.h>
56#include <stdio.h>
57#include <stdlib.h>
58#include <inttypes.h>
59#include <string.h>
60#include <unistd.h>
61#include <fcntl.h>
62#include "atomicio.h"
63
64#include <network/conninfo.h>
65
66#ifndef SUN_LEN
67#define SUN_LEN(su) \
68	(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
69#endif
70
71#define PORT_MAX	65535
72#define PORT_MAX_LEN	6
73
74/* Command Line Options */
75int	cflag;					/* CRLF line-ending */
76int	dflag;					/* detached, no stdin */
77int	iflag;					/* Interval Flag */
78#ifndef __APPLE__
79int	jflag;					/* use jumbo frames if we can */
80#endif /* !__APPLE__ */
81int	kflag;					/* More than one connect */
82int	lflag;					/* Bind to local port */
83int	nflag;					/* Don't do name look up */
84char   *pflag;					/* Localport flag */
85int	rflag;					/* Random ports flag */
86char   *sflag;					/* Source Address */
87int	tflag;					/* Telnet Emulation */
88int	uflag;					/* UDP - Default to TCP */
89int	vflag;					/* Verbosity */
90int	xflag;					/* Socks proxy */
91int	Oflag;					/* use connect vs. connectx */
92int	zflag;					/* Port Scan Flag */
93int	Dflag;					/* sodebug */
94#ifndef __APPLE__
95int	Sflag;					/* TCP MD5 signature option */
96#endif /* !__APPLE__ */
97
98#ifdef __APPLE__
99int	Aflag;					/* Set SO_RECV_ANYIF on socket */
100int	aflag;					/* Set SO_AWDL_UNRESTRICTED on socket */
101char	*boundif;				/* interface to bind to */
102int	ifscope;				/* idx of bound to interface */
103int	Cflag;					/* cellular connection OFF option */
104int	Eflag;					/* expensive connection OFF option */
105int	tclass = SO_TC_BE;			/* traffic class value */
106int	Kflag;					/* traffic class option */
107int	Fflag;					/* disable flow advisory for UDP if set */
108int	Gflag;					/* TCP connection timeout */
109int	tcp_conn_timeout;			/* Value of TCP connection timeout */
110int	Hflag;					/* TCP keep idle option */
111int	tcp_conn_keepidle;			/* Value of TCP keep idle interval in seconds */
112int	Iflag;					/* TCP keep intvl option */
113int	tcp_conn_keepintvl;			/* Value of TCP keep interval in seconds */
114int	Jflag;					/* TCP keep count option */
115int	tcp_conn_keepcnt;			/* Value of TCP keep count */
116int	Lflag;					/* TCP adaptive read timeout */
117int	Mflag;					/* MULTIPATH domain */
118int	Nflag;					/* TCP adaptive write timeout */
119int	oflag;					/* set options after connect/bind */
120int	tcp_conn_adaptive_rtimo;			/* Value of TCP adaptive timeout */
121int	tcp_conn_adaptive_wtimo;			/* Value of TCP adaptive timeout */
122#endif /* __APPLE__ */
123
124int srcroute = 0;				/* Source routing IPv4/IPv6 options */
125char *srcroute_hosts = NULL;
126
127int use_flowadv = 1;
128int timeout = -1;
129int family = AF_UNSPEC;
130char *portlist[PORT_MAX+1];
131
132void	atelnet(int, unsigned char *, unsigned int);
133void	build_ports(char *);
134void	help(void);
135int	local_listen(char *, char *, struct addrinfo);
136void	readwrite(int);
137int	remote_connect(const char *, const char *, struct addrinfo);
138int	remote_connectx(const char *, const char *, struct addrinfo);
139int	socks_connect(const char *, const char *, struct addrinfo, const char *, const char *,
140	struct addrinfo, int);
141int	udptest(int);
142int	unix_connect(char *);
143int	unix_listen(char *);
144void    set_common_sockopts(int);
145void	usage(int);
146int	showconninfo(int, connid_t);
147void	showmpinfo(int);
148
149extern int sourceroute(struct addrinfo *, char *, char **, int *, int *, int *);
150
151int
152main(int argc, char *argv[])
153{
154	int ch, s, ret, socksv;
155	char *host, *uport, *endp;
156	struct addrinfo hints;
157	struct servent *sv;
158	socklen_t len;
159	struct sockaddr_storage cliaddr;
160	char *proxy;
161	const char *proxyhost = "", *proxyport = NULL;
162	struct addrinfo proxyhints;
163
164	ret = 1;
165	s = 0;
166	socksv = 5;
167	host = NULL;
168	uport = NULL;
169	endp = NULL;
170	sv = NULL;
171
172	while ((ch = getopt(argc, argv,
173	    "46AacDCb:dEhi:jFG:H:I:J:K:L:klMnN:Oop:rSs:tUuvw:X:x:z")) != -1) {
174		switch (ch) {
175		case '4':
176			family = AF_INET;
177			break;
178		case '6':
179			family = AF_INET6;
180			break;
181		case 'A':
182			Aflag = 1;
183			break;
184		case 'a':
185			aflag = 1;
186			break;
187		case 'U':
188			family = AF_UNIX;
189			break;
190		case 'M':
191			Mflag = 1;
192			break;
193		case 'X':
194			if (strcasecmp(optarg, "connect") == 0)
195				socksv = -1; /* HTTP proxy CONNECT */
196			else if (strcmp(optarg, "4") == 0)
197				socksv = 4; /* SOCKS v.4 */
198			else if (strcmp(optarg, "5") == 0)
199				socksv = 5; /* SOCKS v.5 */
200			else
201				errx(1, "unsupported proxy protocol");
202			break;
203		case 'c':
204			cflag = 1;
205			break;
206#ifdef __APPLE__
207		case 'C':
208			Cflag = 1;
209			break;
210
211		case 'E':
212			Eflag = 1;
213			break;
214		case 'b':
215			boundif = optarg;
216			if ((ifscope = if_nametoindex(boundif)) == 0)
217				errx(1, "bad interface name");
218			break;
219#endif /* __APPLE__ */
220		case 'd':
221			dflag = 1;
222			break;
223		case 'h':
224			help();
225			break;
226		case 'i':
227			iflag = (int)strtoul(optarg, &endp, 10);
228			if (iflag < 0 || *endp != '\0')
229				errx(1, "interval cannot be negative");
230			break;
231#ifndef __APPLE__
232		case 'j':
233			jflag = 1;
234			break;
235#endif /* !__APPLE__ */
236		case 'k':
237			kflag = 1;
238			break;
239#ifdef __APPLE__
240		case 'K':
241			Kflag = 1;
242			tclass = (int)strtoul(optarg, &endp, 10);
243			if (tclass < 0 || *endp != '\0')
244				errx(1, "invalid traffic class");
245			break;
246		case 'F':
247			Fflag = 1;
248			use_flowadv = 0;
249			break;
250		case 'G':
251			Gflag = 1;
252			tcp_conn_timeout = strtoumax(optarg, &endp, 10);
253			if (tcp_conn_timeout < 0 || *endp != '\0')
254				errx(1, "invalid tcp connection timeout");
255			break;
256		case 'H':
257			Hflag = 1;
258			tcp_conn_keepidle = strtoumax(optarg, &endp, 10);
259			if (tcp_conn_keepidle < 0 || *endp != '\0')
260				errx(1, "invalid tcp keep idle interval");
261			break;
262		case 'I':
263			Iflag = 1;
264			tcp_conn_keepintvl = strtoumax(optarg, &endp, 10);
265			if (tcp_conn_keepintvl < 0 || *endp != '\0')
266				errx(1, "invalid tcp keep interval");
267			break;
268		case 'J':
269			Jflag = 1;
270			tcp_conn_keepcnt = strtoumax(optarg, &endp, 10);
271			if (tcp_conn_keepcnt < 0 || *endp != '\0')
272				errx(1, "invalid tcp keep count");
273			break;
274		case 'L':
275			Lflag = 1;
276			tcp_conn_adaptive_rtimo = strtoumax(optarg, &endp, 10);
277			if (tcp_conn_adaptive_rtimo < 0 || *endp != '\0')
278				errx(1, "invalid tcp adaptive read timeout value");
279			break;
280		case 'N':
281			Nflag = 1;
282			tcp_conn_adaptive_wtimo = strtoumax(optarg, &endp, 10);
283			if (tcp_conn_adaptive_wtimo < 0 || *endp != '\0')
284				errx(1, "invalid tcp adaptive write timeout value");
285			break;
286
287#endif /* __APPLE__ */
288		case 'l':
289			lflag = 1;
290			break;
291		case 'n':
292			nflag = 1;
293			break;
294		case 'o':
295			oflag = 1;
296			break;
297		case 'O':
298			Oflag = 1;
299			break;
300		case 'p':
301			pflag = optarg;
302			break;
303		case 'r':
304			rflag = 1;
305			break;
306		case 's':
307			sflag = optarg;
308			break;
309		case 't':
310			tflag = 1;
311			break;
312		case 'u':
313			uflag = 1;
314			break;
315		case 'v':
316			vflag = 1;
317			break;
318		case 'w':
319			timeout = (int)strtoul(optarg, &endp, 10);
320			if (timeout < 0 || *endp != '\0')
321				errx(1, "timeout cannot be negative");
322			if (timeout >= (INT_MAX / 1000))
323				errx(1, "timeout too large");
324#ifndef USE_SELECT
325			timeout *= 1000;
326#endif
327			break;
328		case 'x':
329			xflag = 1;
330			if ((proxy = strdup(optarg)) == NULL)
331				err(1, NULL);
332			break;
333		case 'z':
334			zflag = 1;
335			break;
336		case 'D':
337			Dflag = 1;
338			break;
339#ifndef __APPLE__
340		case 'S':
341			Sflag = 1;
342			break;
343#endif /* !__APPLE__ */
344		default:
345			usage(1);
346		}
347	}
348	argc -= optind;
349	argv += optind;
350
351	/* Cruft to make sure options are clean, and used properly. */
352	if (argv[0] && !argv[1] && family == AF_UNIX) {
353		if (uflag)
354			errx(1, "cannot use -u and -U");
355		if (Mflag)
356			errx(1, "cannot use -M and -U");
357		host = argv[0];
358		uport = NULL;
359	} else if (argv[0] && !argv[1]) {
360		if  (!lflag)
361			usage(1);
362		uport = argv[0];
363		host = NULL;
364	} else if (argv[0] && argv[1]) {
365		host = argv[0];
366		uport = argv[1];
367	} else
368		usage(1);
369
370	/* Detect if hostname has the telnet source-routing syntax (see sourceroute()) */
371	srcroute_hosts = host;
372	if (srcroute_hosts && (srcroute_hosts[0] == '@' || srcroute_hosts[0] == '!')) {
373		if (
374#ifdef INET6
375			family == AF_INET6 ||
376#endif
377			(host = strrchr(srcroute_hosts, ':')) == NULL)
378		{
379			host = strrchr(srcroute_hosts, '@');
380		}
381		if (host == NULL) {
382			host = srcroute_hosts;
383		} else {
384			host++;
385			srcroute = 1;
386		}
387	}
388
389	if (Mflag && Oflag)
390		errx(1, "cannot use -M and -O");
391	if (srcroute && Mflag)
392		errx(1, "source routing isn't compatible with -M");
393	if (srcroute && !Oflag)
394		errx(1, "must use -O for source routing");
395	if (lflag && sflag)
396		errx(1, "cannot use -s and -l");
397	if (lflag && pflag)
398		errx(1, "cannot use -p and -l");
399	if (lflag && zflag)
400		errx(1, "cannot use -z and -l");
401	if (!lflag && kflag)
402		errx(1, "must use -l with -k");
403
404	/* Initialize addrinfo structure. */
405	if (family != AF_UNIX) {
406		memset(&hints, 0, sizeof(struct addrinfo));
407		hints.ai_family = family;
408		hints.ai_socktype = uflag ? SOCK_DGRAM : SOCK_STREAM;
409		hints.ai_protocol = uflag ? IPPROTO_UDP : IPPROTO_TCP;
410		if (nflag)
411			hints.ai_flags |= AI_NUMERICHOST;
412	}
413
414	if (xflag) {
415		if (uflag)
416			errx(1, "no proxy support for UDP mode");
417
418		if (lflag)
419			errx(1, "no proxy support for listen");
420
421		if (family == AF_UNIX)
422			errx(1, "no proxy support for unix sockets");
423
424		/* XXX IPv6 transport to proxy would probably work */
425		if (family == AF_INET6)
426			errx(1, "no proxy support for IPv6");
427
428		if (sflag)
429			errx(1, "no proxy support for local source address");
430
431		proxyhost = strsep(&proxy, ":");
432		proxyport = proxy;
433
434		memset(&proxyhints, 0, sizeof(struct addrinfo));
435		proxyhints.ai_family = family;
436		proxyhints.ai_socktype = SOCK_STREAM;
437		proxyhints.ai_protocol = IPPROTO_TCP;
438		if (nflag)
439			proxyhints.ai_flags |= AI_NUMERICHOST;
440	}
441
442	if (lflag) {
443		int connfd;
444		ret = 0;
445
446		if (family == AF_UNIX)
447			s = unix_listen(host);
448
449		/* Allow only one connection at a time, but stay alive. */
450		for (;;) {
451			if (family != AF_UNIX)
452				s = local_listen(host, uport, hints);
453			if (s < 0)
454				err(1, NULL);
455			/*
456			 * For UDP, we will use recvfrom() initially
457			 * to wait for a caller, then use the regular
458			 * functions to talk to the caller.
459			 */
460			if (uflag) {
461				int rv, plen;
462				char buf[8192];
463				struct sockaddr_storage z;
464
465				len = sizeof(z);
466#ifndef __APPLE__
467				plen = jflag ? 8192 : 1024;
468#else /* __APPLE__ */
469				plen = 1024;
470#endif /* !__APPLE__ */
471				rv = recvfrom(s, buf, plen, MSG_PEEK,
472				    (struct sockaddr *)&z, &len);
473				if (rv < 0)
474					err(1, "recvfrom");
475
476				rv = connect(s, (struct sockaddr *)&z, len);
477				if (rv < 0)
478					err(1, "connect");
479
480				connfd = s;
481			} else {
482				len = sizeof(cliaddr);
483				connfd = accept(s, (struct sockaddr *)&cliaddr,
484				    &len);
485			}
486
487			readwrite(connfd);
488			close(connfd);
489			if (family != AF_UNIX)
490				close(s);
491
492			if (!kflag)
493				break;
494		}
495	} else if (family == AF_UNIX) {
496		ret = 0;
497
498		if ((s = unix_connect(host)) > 0 && !zflag) {
499			readwrite(s);
500			close(s);
501		} else
502			ret = 1;
503
504		exit(ret);
505
506	} else {
507		int i = 0;
508
509		/* Construct the portlist[] array. */
510		build_ports(uport);
511
512		/* Cycle through portlist, connecting to each port. */
513		for (i = 0; portlist[i] != NULL; i++) {
514			if (s)
515				close(s);
516
517			if (xflag)
518				s = socks_connect(host, portlist[i], hints,
519				    proxyhost, proxyport, proxyhints, socksv);
520			else if (!Oflag)
521				s = remote_connectx(host, portlist[i], hints);
522			else
523				s = remote_connect(host, portlist[i], hints);
524
525			if (s < 0)
526				continue;
527
528			ret = 0;
529			if (vflag || zflag) {
530				/* For UDP, make sure we are connected. */
531				if (uflag) {
532					if (udptest(s) == -1) {
533						ret = 1;
534						continue;
535					}
536				}
537
538				/* Don't look up port if -n. */
539				if (nflag)
540					sv = NULL;
541				else {
542					sv = getservbyport(
543					    ntohs(atoi(portlist[i])),
544					    uflag ? "udp" : "tcp");
545				}
546
547				printf("Connection to %s port %s [%s/%s] succeeded!\n",
548				    host, portlist[i], uflag ? "udp" : "tcp",
549				    sv ? sv->s_name : "*");
550			}
551			if (!zflag)
552				readwrite(s);
553		}
554	}
555
556	if (s)
557		close(s);
558
559	exit(ret);
560}
561
562/*
563 * unix_connect()
564 * Returns a socket connected to a local unix socket. Returns -1 on failure.
565 */
566int
567unix_connect(char *path)
568{
569	struct sockaddr_un sun;
570	int s;
571
572	if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
573		return (-1);
574	(void)fcntl(s, F_SETFD, 1);
575
576	memset(&sun, 0, sizeof(struct sockaddr_un));
577	sun.sun_family = AF_UNIX;
578
579	if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
580	    sizeof(sun.sun_path)) {
581		close(s);
582		errno = ENAMETOOLONG;
583		return (-1);
584	}
585	if (connect(s, (struct sockaddr *)&sun, SUN_LEN(&sun)) < 0) {
586		close(s);
587		return (-1);
588	}
589	return (s);
590
591}
592
593/*
594 * unix_listen()
595 * Create a unix domain socket, and listen on it.
596 */
597int
598unix_listen(char *path)
599{
600	struct sockaddr_un sun;
601	int s;
602
603	/* Create unix domain socket. */
604	if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
605		return (-1);
606
607	memset(&sun, 0, sizeof(struct sockaddr_un));
608	sun.sun_family = AF_UNIX;
609
610	if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
611	    sizeof(sun.sun_path)) {
612		close(s);
613		errno = ENAMETOOLONG;
614		return (-1);
615	}
616
617	if (bind(s, (struct sockaddr *)&sun, SUN_LEN(&sun)) < 0) {
618		close(s);
619		return (-1);
620	}
621
622	if (listen(s, 5) < 0) {
623		close(s);
624		return (-1);
625	}
626	return (s);
627}
628
629/*
630 * remote_connect()
631 * Returns a socket connected to a remote host. Properly binds to a local
632 * port or source address if needed. Returns -1 on failure.
633 */
634int
635remote_connect(const char *host, const char *port, struct addrinfo hints)
636{
637	struct addrinfo *res, *res0;
638	int s, error;
639
640	if ((error = getaddrinfo(host, port, &hints, &res)))
641		errx(1, "getaddrinfo: %s", gai_strerror(error));
642
643	res0 = res;
644	do {
645		if ((s = socket(res0->ai_family, res0->ai_socktype,
646		    res0->ai_protocol)) < 0)
647			continue;
648
649		/* Bind to a local port or source address if specified. */
650		if (sflag || pflag) {
651			struct addrinfo ahints, *ares;
652
653			if (!(sflag && pflag)) {
654				if (!sflag)
655					sflag = NULL;
656				else
657					pflag = NULL;
658			}
659
660			memset(&ahints, 0, sizeof(struct addrinfo));
661			ahints.ai_family = res0->ai_family;
662			ahints.ai_socktype = uflag ? SOCK_DGRAM : SOCK_STREAM;
663			ahints.ai_protocol = uflag ? IPPROTO_UDP : IPPROTO_TCP;
664			ahints.ai_flags = AI_PASSIVE;
665			if ((error = getaddrinfo(sflag, pflag, &ahints, &ares)))
666				errx(1, "getaddrinfo: %s", gai_strerror(error));
667
668			if (bind(s, (struct sockaddr *)ares->ai_addr,
669			    ares->ai_addrlen) < 0)
670				errx(1, "bind failed: %s", strerror(errno));
671			freeaddrinfo(ares);
672		}
673
674		/* Set source-routing option */
675		if (srcroute != 0) {
676			int result, proto, opt, srlen = 0;
677			char *srp = NULL;
678
679			result = sourceroute(res, srcroute_hosts, &srp, &srlen, &proto, &opt);
680			if (result == 1 && srp != NULL) {
681				if (setsockopt(s, proto, opt, srp, srlen) < 0)
682					perror("setsockopt (source route)");
683			} else {
684				warn("bad source route option: %s\n", srcroute_hosts);
685			}
686		}
687
688		if (!oflag)
689			set_common_sockopts(s);
690
691		if (connect(s, res0->ai_addr, res0->ai_addrlen) == 0) {
692			if (oflag)
693				set_common_sockopts(s);
694			break;
695		} else if (vflag) {
696			warn("connect to %s port %s (%s) failed", host, port,
697			    uflag ? "udp" : "tcp");
698		}
699		close(s);
700		s = -1;
701	} while ((res0 = res0->ai_next) != NULL);
702
703	freeaddrinfo(res);
704
705	return (s);
706}
707
708/*
709 * remote_connectx()
710 * Returns a socket connected to a remote host. Properly binds to a local
711 * port or source address if needed, using connectx(2). Returns -1 on failure.
712 */
713int
714remote_connectx(const char *host, const char *port, struct addrinfo hints)
715{
716	struct addrinfo *res, *res0, *ares = NULL;
717	connid_t cid;
718	int s, error;
719
720	if ((error = getaddrinfo(host, port, &hints, &res)))
721		errx(1, "getaddrinfo: %s", gai_strerror(error));
722
723	res0 = res;
724	do {
725		if ((s = socket(Mflag ? PF_MULTIPATH : res0->ai_family,
726		     res0->ai_socktype, res0->ai_protocol)) < 0) {
727			warn("socket(%d,%d,%d) failed",
728			    (Mflag ? PF_MULTIPATH : res0->ai_family),
729			    res0->ai_socktype, res0->ai_protocol);
730			continue;
731		}
732
733		/* Bind to a local port or source address if specified. */
734		if (sflag || pflag) {
735			struct addrinfo ahints;
736
737			if (!(sflag && pflag)) {
738				if (!sflag)
739					sflag = NULL;
740				else
741					pflag = NULL;
742			}
743			if (ares != NULL) {
744				freeaddrinfo(ares);
745				ares = NULL;
746			}
747
748			memset(&ahints, 0, sizeof(struct addrinfo));
749			ahints.ai_family = res0->ai_family;
750			ahints.ai_socktype = uflag ? SOCK_DGRAM : SOCK_STREAM;
751			ahints.ai_protocol = uflag ? IPPROTO_UDP : IPPROTO_TCP;
752			ahints.ai_flags = AI_PASSIVE;
753			if ((error = getaddrinfo(sflag, pflag, &ahints, &ares)))
754				errx(1, "getaddrinfo: %s", gai_strerror(error));
755		}
756
757		if (!oflag)
758			set_common_sockopts(s);
759
760		cid = CONNID_ANY;
761		if (ares == NULL) {
762			error = connectx(s, NULL, 0, res0->ai_addr,
763			     res0->ai_addrlen, ifscope, ASSOCID_ANY, &cid);
764		} else {
765			error = connectx(s, ares->ai_addr, ares->ai_addrlen,
766			    res0->ai_addr, res0->ai_addrlen, ifscope,
767			    ASSOCID_ANY, &cid);
768		}
769
770		if (error == 0) {
771			if (oflag)
772				set_common_sockopts(s);
773			if (vflag)
774				showmpinfo(s);
775			break;
776		} else if (errno == EPROTO) {	/* PF_MULTIPATH specific */
777			int ps;
778			warn("connectx to %s port %s (%s) succeded without "
779			    "multipath association (connid %d)",
780			    host, port, uflag ? "udp" : "tcp", cid);
781			if (vflag)
782				showmpinfo(s);
783			ps = peeloff(s, ASSOCID_ANY);
784			if (ps != -1) {
785				close(s);
786				s = ps;
787				if (oflag)
788					set_common_sockopts(s);
789				break;
790			}
791			warn("peeloff failed for connid %d", cid);
792		} else if (vflag) {
793			warn("connectx to %s port %s (%s) failed", host, port,
794			    (uflag ? "udp" : (Mflag ? "mptcp" : "tcp")));
795		}
796		close(s);
797		s = -1;
798	} while ((res0 = res0->ai_next) != NULL);
799
800	freeaddrinfo(res);
801	if (ares != NULL)
802		freeaddrinfo(ares);
803
804	return (s);
805}
806
807/*
808 * local_listen()
809 * Returns a socket listening on a local port, binds to specified source
810 * address. Returns -1 on failure.
811 */
812int
813local_listen(char *host, char *port, struct addrinfo hints)
814{
815	struct addrinfo *res, *res0;
816	int s, ret, x = 1;
817	int error;
818
819	/* Allow nodename to be null. */
820	hints.ai_flags |= AI_PASSIVE;
821
822	/*
823	 * In the case of binding to a wildcard address
824	 * default to binding to an ipv4 address.
825	 */
826	if (host == NULL && hints.ai_family == AF_UNSPEC)
827		hints.ai_family = AF_INET;
828
829	if ((error = getaddrinfo(host, port, &hints, &res)))
830		errx(1, "getaddrinfo: %s", gai_strerror(error));
831
832	res0 = res;
833	do {
834		if ((s = socket(res0->ai_family, res0->ai_socktype,
835		    res0->ai_protocol)) < 0)
836			continue;
837
838		ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof(x));
839		if (ret == -1)
840			err(1, NULL);
841
842		if (!oflag)
843			set_common_sockopts(s);
844
845		if (bind(s, (struct sockaddr *)res0->ai_addr,
846		    res0->ai_addrlen) == 0) {
847			if (oflag)
848				set_common_sockopts(s);
849			break;
850		}
851
852		close(s);
853		s = -1;
854	} while ((res0 = res0->ai_next) != NULL);
855
856	if (!uflag && s != -1) {
857		if (listen(s, 1) < 0)
858			err(1, "listen");
859	}
860
861	freeaddrinfo(res);
862
863	return (s);
864}
865
866/*
867 * readwrite()
868 * Loop that polls on the network file descriptor and stdin.
869 */
870void
871readwrite(int nfd)
872{
873#ifdef USE_SELECT
874	fd_set readfds;
875	struct timeval tv;
876	int nfd_open = 1, wfd_open = 1;
877#else
878	struct pollfd pfd[2];
879#endif
880	unsigned char buf[8192];
881	int n, wfd = fileno(stdin);
882	int lfd = fileno(stdout);
883	int plen;
884
885#ifndef __APPLE__
886	plen = jflag ? 8192 : 1024;
887#else /* __APPLE__ */
888	plen = 1024;
889#endif /* !__APPLE__ */
890
891#ifndef USE_SELECT
892	/* Setup Network FD */
893	pfd[0].fd = nfd;
894	pfd[0].events = POLLIN;
895
896	/* Set up STDIN FD. */
897	pfd[1].fd = wfd;
898	pfd[1].events = POLLIN;
899#endif
900
901#ifdef USE_SELECT
902	while (nfd_open) {
903#else
904	while (pfd[0].fd != -1) {
905#endif
906		if (iflag)
907			sleep(iflag);
908
909#ifdef USE_SELECT
910		FD_ZERO(&readfds);
911		if (nfd_open)
912			FD_SET(nfd, &readfds);
913		if (wfd_open)
914			FD_SET(wfd, &readfds);
915
916		tv.tv_sec = timeout;
917		tv.tv_usec = 0;
918
919		if ((n = select(nfd + 1, &readfds, NULL, NULL, (timeout == -1) ? NULL : &tv)) < 0) {
920#else
921		if ((n = poll(pfd, 2 - dflag, timeout)) < 0) {
922#endif
923			close(nfd);
924			err(1, "Polling Error");
925		}
926
927		if (n == 0)
928			return;
929
930#ifdef USE_SELECT
931		if (FD_ISSET(nfd, &readfds)) {
932#else
933		if (pfd[0].revents & POLLIN) {
934#endif
935			if ((n = read(nfd, buf, plen)) < 0)
936				return;
937			else if (n == 0) {
938				shutdown(nfd, SHUT_RD);
939#ifdef USE_SELECT
940				nfd_open = 0;
941#else
942				pfd[0].fd = -1;
943				pfd[0].events = 0;
944#endif
945			} else {
946				if (tflag)
947					atelnet(nfd, buf, n);
948				if (atomicio(vwrite, lfd, buf, n) != n)
949					return;
950			}
951		}
952
953#ifdef USE_SELECT
954		if (!dflag && FD_ISSET(wfd, &readfds)) {
955#else
956		if (!dflag && pfd[1].revents & POLLIN) {
957#endif
958			if ((n = read(wfd, buf, plen)) < 0)
959				return;
960			else if (n == 0) {
961				shutdown(nfd, SHUT_WR);
962#ifdef USE_SELECT
963				wfd_open = 0;
964#else
965				pfd[1].fd = -1;
966				pfd[1].events = 0;
967#endif
968			} else {
969				if ((cflag) && (buf[n - 1] == '\n')) {
970					if (atomicio(vwrite, nfd, buf, n - 1) != (n - 1))
971						return;
972					if (atomicio(vwrite, nfd, "\r\n", 2) != 2)
973						return;
974				} else {
975					if (atomicio(vwrite, nfd, buf, n) != n)
976						return;
977				}
978			}
979		}
980	}
981}
982
983/* Deal with RFC 854 WILL/WONT DO/DONT negotiation. */
984void
985atelnet(int nfd, unsigned char *buf, unsigned int size)
986{
987	unsigned char *p, *end;
988	unsigned char obuf[4];
989
990	end = buf + size;
991	obuf[0] = '\0';
992
993	for (p = buf; p < end; p++) {
994		if (*p != IAC)
995			break;
996
997		obuf[0] = IAC;
998		p++;
999		if ((*p == WILL) || (*p == WONT))
1000			obuf[1] = DONT;
1001		if ((*p == DO) || (*p == DONT))
1002			obuf[1] = WONT;
1003		if (obuf) {
1004			p++;
1005			obuf[2] = *p;
1006			obuf[3] = '\0';
1007			if (atomicio(vwrite, nfd, obuf, 3) != 3)
1008				warn("Write Error!");
1009			obuf[0] = '\0';
1010		}
1011	}
1012}
1013
1014/*
1015 * build_ports()
1016 * Build an array or ports in portlist[], listing each port
1017 * that we should try to connect to.
1018 */
1019void
1020build_ports(char *p)
1021{
1022	char *n, *endp;
1023	int hi, lo, cp;
1024	int x = 0;
1025
1026	if ((n = strchr(p, '-')) != NULL) {
1027		if (lflag)
1028			errx(1, "Cannot use -l with multiple ports!");
1029
1030		*n = '\0';
1031		n++;
1032
1033		/* Make sure the ports are in order: lowest->highest. */
1034		hi = (int)strtoul(n, &endp, 10);
1035		if (hi <= 0 || hi > PORT_MAX || *endp != '\0')
1036			errx(1, "port range not valid");
1037		lo = (int)strtoul(p, &endp, 10);
1038		if (lo <= 0 || lo > PORT_MAX || *endp != '\0')
1039			errx(1, "port range not valid");
1040
1041		if (lo > hi) {
1042			cp = hi;
1043			hi = lo;
1044			lo = cp;
1045		}
1046
1047		/* Load ports sequentially. */
1048		for (cp = lo; cp <= hi; cp++) {
1049			portlist[x] = calloc(1, PORT_MAX_LEN);
1050			if (portlist[x] == NULL)
1051				err(1, NULL);
1052			snprintf(portlist[x], PORT_MAX_LEN, "%d", cp);
1053			x++;
1054		}
1055
1056		/* Randomly swap ports. */
1057		if (rflag) {
1058			int y;
1059			char *c;
1060
1061			for (x = 0; x <= (hi - lo); x++) {
1062				y = (arc4random() & 0xFFFF) % (hi - lo);
1063				c = portlist[x];
1064				portlist[x] = portlist[y];
1065				portlist[y] = c;
1066			}
1067		}
1068	} else {
1069		hi = (int)strtoul(p, &endp, 10);
1070		if (hi <= 0 || hi > PORT_MAX || *endp != '\0')
1071			errx(1, "port range not valid");
1072		portlist[0] = calloc(1, PORT_MAX_LEN);
1073		if (portlist[0] == NULL)
1074			err(1, NULL);
1075		strlcpy(portlist[0], p, PORT_MAX_LEN);
1076	}
1077}
1078
1079/*
1080 * udptest()
1081 * Do a few writes to see if the UDP port is there.
1082 * XXX - Better way of doing this? Doesn't work for IPv6.
1083 * Also fails after around 100 ports checked.
1084 */
1085int
1086udptest(int s)
1087{
1088	int i, ret;
1089
1090	for (i = 0; i <= 3; i++) {
1091		if (write(s, "X", 1) == 1)
1092			ret = 1;
1093		else
1094			ret = -1;
1095	}
1096	return (ret);
1097}
1098
1099void
1100set_common_sockopts(int s)
1101{
1102	int x = 1;
1103
1104#ifndef __APPLE__
1105	if (Sflag) {
1106		if (setsockopt(s, IPPROTO_TCP, TCP_MD5SIG,
1107			&x, sizeof(x)) == -1)
1108			err(1, NULL);
1109	}
1110#endif /* !__APPLE__ */
1111	if (Dflag) {
1112		if (setsockopt(s, SOL_SOCKET, SO_DEBUG,
1113			&x, sizeof(x)) == -1)
1114			err(1, "SO_DEBUG");
1115	}
1116#ifndef __APPLE__
1117	if (jflag) {
1118		if (setsockopt(s, SOL_SOCKET, SO_JUMBO,
1119			&x, sizeof(x)) == -1)
1120			err(1, NULL);
1121	}
1122#endif /* !__APPLE__ */
1123#ifdef __APPLE__
1124	if (Aflag) {
1125		if (setsockopt(s, SOL_SOCKET, SO_RECV_ANYIF,
1126			&x, sizeof(x)) == -1)
1127			err(1, "SO_RECV_ANYIF");
1128	}
1129
1130	if (aflag) {
1131		if (setsockopt(s, SOL_SOCKET, SO_AWDL_UNRESTRICTED,
1132			&x, sizeof(x)) == -1)
1133			err(1, "SO_AWDL_UNRESTRICTED");
1134	}
1135
1136	if (boundif && (lflag || Oflag)) {
1137		/* Socket family could be AF_UNSPEC, so try both */
1138		if (setsockopt(s, IPPROTO_IP, IP_BOUND_IF, &ifscope,
1139		    sizeof (ifscope)) == -1 &&
1140		    setsockopt(s, IPPROTO_IPV6, IPV6_BOUND_IF, &ifscope,
1141		    sizeof (ifscope)) == -1)
1142			err(1, "{IP,IPV6}_BOUND_IF");
1143	}
1144
1145	if (Cflag) {
1146		uint32_t restrictions = SO_RESTRICT_DENY_CELLULAR;
1147		if (setsockopt(s, SOL_SOCKET, SO_RESTRICTIONS, &restrictions,
1148		     sizeof (restrictions)) == -1)
1149			err(1, "SO_RESTRICTIONS: SO_RESTRICT_DENY_CELLULAR");
1150	}
1151
1152	if (Eflag) {
1153		uint32_t restrictions = SO_RESTRICT_DENY_EXPENSIVE;
1154		if (setsockopt(s, SOL_SOCKET, SO_RESTRICTIONS, &restrictions,
1155		     sizeof (restrictions)) == -1)
1156			err(1, "SO_RESTRICTIONS: SO_RESTRICT_DENY_EXPENSIVE");
1157	}
1158
1159	if (Kflag) {
1160		if (setsockopt(s, SOL_SOCKET, SO_TRAFFIC_CLASS,
1161		    &tclass, sizeof (tclass)) == -1)
1162			err(1, "SO_TRAFFIC_CLASS");
1163	}
1164
1165	if (Gflag) {
1166		if (setsockopt(s, IPPROTO_TCP, TCP_CONNECTIONTIMEOUT, &tcp_conn_timeout,
1167			sizeof(tcp_conn_timeout)) == -1)
1168			err(1, "TCP_CONNECTIONTIMEOUT");
1169	}
1170
1171	if (Hflag || Iflag || Jflag) {
1172		/* enable keep alives on this socket */
1173		int on = 1;
1174		if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
1175			&on, sizeof(on)) == -1)
1176			err(1, "SO_KEEPALIVE");
1177	}
1178
1179	if (Hflag) {
1180		if (setsockopt(s, IPPROTO_TCP, TCP_KEEPALIVE,
1181			&tcp_conn_keepidle, sizeof(tcp_conn_keepidle)) == -1)
1182			err(1, "TCP_KEEPALIVE");
1183	}
1184
1185	if (Iflag) {
1186		if (setsockopt(s, IPPROTO_TCP, TCP_KEEPINTVL,
1187			&tcp_conn_keepintvl, sizeof(tcp_conn_keepintvl)) == -1)
1188			err(1, "TCP_KEEPINTVL");
1189	}
1190
1191	if (Jflag) {
1192		if (setsockopt(s, IPPROTO_TCP, TCP_KEEPCNT,
1193			&tcp_conn_keepcnt, sizeof(tcp_conn_keepcnt)) == -1)
1194			err(1, "TCP_KEEPCNT");
1195	}
1196
1197	if (Lflag) {
1198		if (setsockopt(s, IPPROTO_TCP, TCP_ADAPTIVE_READ_TIMEOUT,
1199			&tcp_conn_adaptive_rtimo,
1200			sizeof(tcp_conn_adaptive_rtimo)) == -1)
1201			err(1, "TCP_ADAPTIVE_READ_TIMEOUT");
1202	}
1203
1204	if (Nflag) {
1205		if (setsockopt(s, IPPROTO_TCP,TCP_ADAPTIVE_WRITE_TIMEOUT,
1206			&tcp_conn_adaptive_wtimo,
1207			sizeof(tcp_conn_adaptive_wtimo)) == -1)
1208			err(1, "TCP_ADAPTIVE_WRITE_TIMEOUT");
1209	}
1210#endif /* __APPLE__ */
1211}
1212
1213void
1214help(void)
1215{
1216	usage(0);
1217	fprintf(stderr, "\tCommand Summary:\n\
1218	\t-4		Use IPv4\n\
1219	\t-6		Use IPv6\n\
1220%s\
1221%s\
1222%s\
1223%s\
1224%s\
1225	\t-c		Send CRLF as line-ending\n\
1226	\t-D		Enable the debug socket option\n\
1227	\t-d		Detach from stdin\n\
1228	\t-h		This help text\n\
1229	\t-i secs\t	Delay interval for lines sent, ports scanned\n\
1230	\t-k		Keep inbound sockets open for multiple connects\n\
1231	\t-l		Listen mode, for inbound connects\n\
1232	\t-n		Suppress name/port resolutions\n\
1233	\t-p port\t	Specify local port for remote connects\n\
1234	\t-r		Randomize remote ports\n\
1235%s\
1236%s\
1237%s\
1238%s\
1239%s\
1240%s\
1241%s\
1242	\t-s addr\t	Local source address\n\
1243	\t-t		Answer TELNET negotiation\n\
1244	\t-U		Use UNIX domain socket\n\
1245	\t-u		UDP mode\n\
1246	\t-v		Verbose\n\
1247	\t-w secs\t	Timeout for connects and final net reads\n\
1248	\t-X proto	Proxy protocol: \"4\", \"5\" (SOCKS) or \"connect\"\n\
1249	\t-x addr[:port]\tSpecify proxy address and port\n\
1250	\t-z		Zero-I/O mode [used for scanning]\n\
1251	Port numbers can be individual or ranges: lo-hi [inclusive]\n",
1252#ifndef __APPLE__
1253	"",
1254	"",
1255	"",
1256	"	\t-S		Enable the TCP MD5 signature option\n",
1257	"",
1258	"",
1259	"",
1260	"",
1261	""
1262#else /* __APPLE__ */
1263	"	\t-A		Set SO_RECV_ANYIF on socket\n",
1264	"	\t-a		Set SO_AWDL_UNRESTRICTED on socket\n",
1265	"	\t-C		Don't use cellular connection\n",
1266	"	\t-E		Don't use expensive interfaces\n",
1267	"	\t-b ifbound	Bind socket to interface\n",
1268	"	\t-F		Do not use flow advisory (flow adv enabled by default)\n",
1269	"	\t-O		Use old-style connect instead of connectx\n",
1270	"	\t-o		Issue socket options after connect/bind\n",
1271	"	\t-K tclass	Specify traffic class\n",
1272	"	\t-G conntimo	Connection timeout in seconds\n",
1273	"	\t-H keepidle	Initial idle timeout in seconds\n",
1274	"	\t-I keepintvl	Interval for repeating idle timeouts in seconds\n",
1275	"	\t-J keepcnt	Number of times to repeat idle timeout\n",
1276	"	\t-L num_probes Number of probes to send before generating a read timeout event\n",
1277	"	\t-M		Use MULTIPATH domain socket\n",
1278	"	\t-N num_probes Number of probes to send before generating a write timeout event\n"
1279#endif /* !__APPLE__ */
1280	);
1281	exit(1);
1282}
1283
1284void
1285usage(int ret)
1286{
1287#ifndef __APPLE__
1288	fprintf(stderr, "usage: nc [-46cDdhklnrStUuvz] [-i interval] [-p source_port]\n");
1289#else /* __APPLE__ */
1290	fprintf(stderr, "usage: nc [-46AacCDdEFhklMnOortUuvz] [-K tc] [-b boundif] [-i interval] [-p source_port]\n");
1291#endif /* !__APPLE__ */
1292	fprintf(stderr, "\t  [-s source_ip_address] [-w timeout] [-X proxy_version]\n");
1293	fprintf(stderr, "\t  [-x proxy_address[:port]] [hostname] [port[s]]\n");
1294	if (ret)
1295		exit(1);
1296}
1297
1298int
1299wait_for_flowadv(int fd)
1300{
1301	static int kq = -1;
1302	int rc;
1303	struct kevent kev[2];
1304	bzero(kev, sizeof(kev));
1305
1306	/* Got ENOBUFS.
1307	 * Now wait for flow advisory only if UDP is set and flow adv is enabled. */
1308	if (!uflag || !use_flowadv) {
1309		return (1);
1310	}
1311
1312	if (kq < 0) {
1313		kq = kqueue();
1314		if (kq < 0) {
1315			errx(1, "failed to create kqueue for flow advisory");
1316			return (1);
1317		}
1318	}
1319
1320	EV_SET(&kev[0], fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, 0);
1321	rc = kevent(kq, &kev[0], 1, NULL, 0, NULL);
1322	if (rc < 0) {
1323		return (1);
1324	}
1325
1326	rc = kevent(kq, NULL, 0, &kev[1], 1, NULL);
1327	if (rc < 0) {
1328		return (1);
1329	}
1330
1331	if (kev[1].flags & EV_EOF) {
1332		return (2);
1333	}
1334
1335	return (0);
1336}
1337
1338/*
1339 * Print a value a la the %b format of the kernel's printf
1340 */
1341void
1342printb(const char *s, unsigned v, const char *bits)
1343{
1344	int i, any = 0;
1345	char c;
1346
1347	if (bits && *bits == 8)
1348		printf("%s=%o", s, v);
1349	else
1350		printf("%s=%x", s, v);
1351	bits++;
1352	if (bits) {
1353		putchar('<');
1354		while ((i = *bits++) != '\0') {
1355			if (v & (1 << (i-1))) {
1356				if (any)
1357					putchar(',');
1358				any = 1;
1359				for (; (c = *bits) > 32; bits++)
1360					putchar(c);
1361			} else
1362				for (; *bits > 32; bits++)
1363					;
1364		}
1365		putchar('>');
1366	}
1367}
1368
1369#define	CIF_BITS	\
1370	"\020\1CONNECTING\2CONNECTED\3DISCONNECTING\4DISCONNECTED\5BOUND_IF" \
1371	"\6BOUND_IP\7BOUND_PORT\10PREFERRED\11MP_CAPABLE\12MP_READY" \
1372	"\13MP_DEGRADED"
1373
1374int
1375showconninfo(int s, connid_t cid)
1376{
1377	struct so_cordreq scor;
1378	char buf[INET6_ADDRSTRLEN];
1379	conninfo_t *cfo = NULL;
1380	int err;
1381
1382	err = copyconninfo(s, cid, &cfo);
1383	if (err != 0) {
1384		warn("copyconninfo failed for cid %d\n", cid);
1385		goto out;
1386	}
1387
1388	printf("%6d:\t", cid);
1389	printb("flags", cfo->ci_flags, CIF_BITS);
1390	printf("\n");
1391	printf("\toutif %s\n", if_indextoname(cfo->ci_ifindex, buf));
1392	if (cfo->ci_src != NULL) {
1393		printf("\tsrc %s port %d\n", inet_ntop(cfo->ci_src->sa_family,
1394		    (cfo->ci_src->sa_family == AF_INET) ?
1395		    (void *)&((struct sockaddr_in *)cfo->ci_src)->
1396		    sin_addr.s_addr :
1397		    (void *)&((struct sockaddr_in6 *)cfo->ci_src)->sin6_addr,
1398		    buf, sizeof (buf)),
1399		    (cfo->ci_src->sa_family == AF_INET) ?
1400		    ntohs(((struct sockaddr_in *)cfo->ci_src)->sin_port) :
1401		    ntohs(((struct sockaddr_in6 *)cfo->ci_src)->sin6_port));
1402	}
1403	if (cfo->ci_dst != NULL) {
1404		printf("\tdst %s port %d\n", inet_ntop(cfo->ci_dst->sa_family,
1405		    (cfo->ci_dst->sa_family == AF_INET) ?
1406		    (void *)&((struct sockaddr_in *)cfo->ci_dst)->
1407		    sin_addr.s_addr :
1408		    (void *)&((struct sockaddr_in6 *)cfo->ci_dst)->sin6_addr,
1409		    buf, sizeof (buf)),
1410		    (cfo->ci_dst->sa_family == AF_INET) ?
1411		    ntohs(((struct sockaddr_in *)cfo->ci_dst)->sin_port) :
1412		    ntohs(((struct sockaddr_in6 *)cfo->ci_dst)->sin6_port));
1413	}
1414
1415	bzero(&scor, sizeof (scor));
1416	scor.sco_cid = cid;
1417	err = ioctl(s, SIOCGCONNORDER, &scor);
1418	if (err == 0) {
1419		printf("\trank %d\n", scor.sco_rank);
1420	} else {
1421		printf("\trank info not available\n");
1422	}
1423
1424	if (cfo->ci_aux_data != NULL) {
1425		switch (cfo->ci_aux_type) {
1426		case CIAUX_TCP:
1427			printf("\tTCP aux info available\n");
1428			break;
1429		default:
1430			printf("\tUnknown aux type %d\n", cfo->ci_aux_type);
1431			break;
1432		}
1433	}
1434out:
1435	if (cfo != NULL)
1436		freeconninfo(cfo);
1437
1438	return (err);
1439}
1440
1441void
1442showmpinfo(int s)
1443{
1444	uint32_t aid_cnt, cid_cnt;
1445	associd_t *aid = NULL;
1446	connid_t *cid = NULL;
1447	int i, err;
1448
1449	err = copyassocids(s, &aid, &aid_cnt);
1450	if (err != 0) {
1451		warn("copyassocids failed\n");
1452		goto done;
1453	} else {
1454		printf("found %d associations", aid_cnt);
1455		if (aid_cnt > 0) {
1456			printf(" with IDs:");
1457			for (i = 0; i < aid_cnt; i++)
1458				printf(" %d\n", aid[i]);
1459		}
1460		printf("\n");
1461	}
1462
1463	/* just do an association for now */
1464	err = copyconnids(s, ASSOCID_ANY, &cid, &cid_cnt);
1465	if (err != 0) {
1466		warn("copyconnids failed\n");
1467		goto done;
1468	} else {
1469		printf("found %d connections", cid_cnt);
1470		if (cid_cnt > 0) {
1471			printf(":\n");
1472			for (i = 0; i < cid_cnt; i++) {
1473				if (showconninfo(s, cid[i]) != 0)
1474					break;
1475			}
1476		}
1477		printf("\n");
1478	}
1479
1480done:
1481	if (aid != NULL)
1482		freeassocids(aid);
1483	if (cid != NULL)
1484		freeconnids(cid);
1485}
1486