xref: /freebsd/contrib/openbsm/bin/auditdistd/proto_tcp.c (revision b626f5a73a48f44a31a200291b141e1da408a2ff)
1*aa772005SRobert Watson /*-
2*aa772005SRobert Watson  * Copyright (c) 2009-2010 The FreeBSD Foundation
3*aa772005SRobert Watson  * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
4*aa772005SRobert Watson  * All rights reserved.
5*aa772005SRobert Watson  *
6*aa772005SRobert Watson  * This software was developed by Pawel Jakub Dawidek under sponsorship from
7*aa772005SRobert Watson  * the FreeBSD Foundation.
8*aa772005SRobert Watson  *
9*aa772005SRobert Watson  * Redistribution and use in source and binary forms, with or without
10*aa772005SRobert Watson  * modification, are permitted provided that the following conditions
11*aa772005SRobert Watson  * are met:
12*aa772005SRobert Watson  * 1. Redistributions of source code must retain the above copyright
13*aa772005SRobert Watson  *    notice, this list of conditions and the following disclaimer.
14*aa772005SRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
15*aa772005SRobert Watson  *    notice, this list of conditions and the following disclaimer in the
16*aa772005SRobert Watson  *    documentation and/or other materials provided with the distribution.
17*aa772005SRobert Watson  *
18*aa772005SRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19*aa772005SRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*aa772005SRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*aa772005SRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22*aa772005SRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23*aa772005SRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24*aa772005SRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25*aa772005SRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26*aa772005SRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27*aa772005SRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28*aa772005SRobert Watson  * SUCH DAMAGE.
29*aa772005SRobert Watson  */
30*aa772005SRobert Watson 
31*aa772005SRobert Watson #include <config/config.h>
32*aa772005SRobert Watson 
33*aa772005SRobert Watson #include <sys/param.h>	/* MAXHOSTNAMELEN */
34*aa772005SRobert Watson #include <sys/socket.h>
35*aa772005SRobert Watson 
36*aa772005SRobert Watson #include <arpa/inet.h>
37*aa772005SRobert Watson 
38*aa772005SRobert Watson #include <netinet/in.h>
39*aa772005SRobert Watson #include <netinet/tcp.h>
40*aa772005SRobert Watson 
41*aa772005SRobert Watson #include <errno.h>
42*aa772005SRobert Watson #include <fcntl.h>
43*aa772005SRobert Watson #include <netdb.h>
44*aa772005SRobert Watson #include <stdbool.h>
45*aa772005SRobert Watson #include <stdint.h>
46*aa772005SRobert Watson #include <stdio.h>
47*aa772005SRobert Watson #include <stdlib.h>
48*aa772005SRobert Watson #include <string.h>
49*aa772005SRobert Watson #include <unistd.h>
50*aa772005SRobert Watson 
51*aa772005SRobert Watson #ifndef HAVE_STRLCPY
52*aa772005SRobert Watson #include <compat/strlcpy.h>
53*aa772005SRobert Watson #endif
54*aa772005SRobert Watson 
55*aa772005SRobert Watson #include "pjdlog.h"
56*aa772005SRobert Watson #include "proto_impl.h"
57*aa772005SRobert Watson #include "subr.h"
58*aa772005SRobert Watson 
59*aa772005SRobert Watson #define	TCP_CTX_MAGIC	0x7c41c
60*aa772005SRobert Watson struct tcp_ctx {
61*aa772005SRobert Watson 	int			tc_magic;
62*aa772005SRobert Watson 	struct sockaddr_storage	tc_sa;
63*aa772005SRobert Watson 	int			tc_fd;
64*aa772005SRobert Watson 	int			tc_side;
65*aa772005SRobert Watson #define	TCP_SIDE_CLIENT		0
66*aa772005SRobert Watson #define	TCP_SIDE_SERVER_LISTEN	1
67*aa772005SRobert Watson #define	TCP_SIDE_SERVER_WORK	2
68*aa772005SRobert Watson 	bool			tc_wait_called;
69*aa772005SRobert Watson };
70*aa772005SRobert Watson 
71*aa772005SRobert Watson static int tcp_connect_wait(void *ctx, int timeout);
72*aa772005SRobert Watson static void tcp_close(void *ctx);
73*aa772005SRobert Watson 
74*aa772005SRobert Watson /*
75*aa772005SRobert Watson  * Function converts the given string to unsigned number.
76*aa772005SRobert Watson  */
77*aa772005SRobert Watson static int
numfromstr(const char * str,intmax_t minnum,intmax_t maxnum,intmax_t * nump)78*aa772005SRobert Watson numfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump)
79*aa772005SRobert Watson {
80*aa772005SRobert Watson 	intmax_t digit, num;
81*aa772005SRobert Watson 
82*aa772005SRobert Watson 	if (str[0] == '\0')
83*aa772005SRobert Watson 		goto invalid;	/* Empty string. */
84*aa772005SRobert Watson 	num = 0;
85*aa772005SRobert Watson 	for (; *str != '\0'; str++) {
86*aa772005SRobert Watson 		if (*str < '0' || *str > '9')
87*aa772005SRobert Watson 			goto invalid;	/* Non-digit character. */
88*aa772005SRobert Watson 		digit = *str - '0';
89*aa772005SRobert Watson 		if (num > num * 10 + digit)
90*aa772005SRobert Watson 			goto invalid;	/* Overflow. */
91*aa772005SRobert Watson 		num = num * 10 + digit;
92*aa772005SRobert Watson 		if (num > maxnum)
93*aa772005SRobert Watson 			goto invalid;	/* Too big. */
94*aa772005SRobert Watson 	}
95*aa772005SRobert Watson 	if (num < minnum)
96*aa772005SRobert Watson 		goto invalid;	/* Too small. */
97*aa772005SRobert Watson 	*nump = num;
98*aa772005SRobert Watson 	return (0);
99*aa772005SRobert Watson invalid:
100*aa772005SRobert Watson 	errno = EINVAL;
101*aa772005SRobert Watson 	return (-1);
102*aa772005SRobert Watson }
103*aa772005SRobert Watson 
104*aa772005SRobert Watson static int
tcp_addr(const char * addr,int defport,struct sockaddr_storage * sap)105*aa772005SRobert Watson tcp_addr(const char *addr, int defport, struct sockaddr_storage *sap)
106*aa772005SRobert Watson {
107*aa772005SRobert Watson 	char iporhost[MAXHOSTNAMELEN], portstr[6];
108*aa772005SRobert Watson 	struct addrinfo hints;
109*aa772005SRobert Watson 	struct addrinfo *res;
110*aa772005SRobert Watson 	const char *pp;
111*aa772005SRobert Watson 	intmax_t port;
112*aa772005SRobert Watson 	size_t size;
113*aa772005SRobert Watson 	int error;
114*aa772005SRobert Watson 
115*aa772005SRobert Watson 	if (addr == NULL)
116*aa772005SRobert Watson 		return (-1);
117*aa772005SRobert Watson 
118*aa772005SRobert Watson 	bzero(&hints, sizeof(hints));
119*aa772005SRobert Watson 	hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
120*aa772005SRobert Watson 	hints.ai_family = PF_UNSPEC;
121*aa772005SRobert Watson 	hints.ai_socktype = SOCK_STREAM;
122*aa772005SRobert Watson 	hints.ai_protocol = IPPROTO_TCP;
123*aa772005SRobert Watson 
124*aa772005SRobert Watson 	if (strncasecmp(addr, "tcp4://", 7) == 0) {
125*aa772005SRobert Watson 		addr += 7;
126*aa772005SRobert Watson 		hints.ai_family = PF_INET;
127*aa772005SRobert Watson 	} else if (strncasecmp(addr, "tcp6://", 7) == 0) {
128*aa772005SRobert Watson 		addr += 7;
129*aa772005SRobert Watson 		hints.ai_family = PF_INET6;
130*aa772005SRobert Watson 	} else if (strncasecmp(addr, "tcp://", 6) == 0) {
131*aa772005SRobert Watson 		addr += 6;
132*aa772005SRobert Watson 	} else {
133*aa772005SRobert Watson 		/*
134*aa772005SRobert Watson 		 * Because TCP is the default assume IP or host is given without
135*aa772005SRobert Watson 		 * prefix.
136*aa772005SRobert Watson 		 */
137*aa772005SRobert Watson 	}
138*aa772005SRobert Watson 
139*aa772005SRobert Watson 	/*
140*aa772005SRobert Watson 	 * Extract optional port.
141*aa772005SRobert Watson 	 * There are three cases to consider.
142*aa772005SRobert Watson 	 * 1. hostname with port, eg. freefall.freebsd.org:8457
143*aa772005SRobert Watson 	 * 2. IPv4 address with port, eg. 192.168.0.101:8457
144*aa772005SRobert Watson 	 * 3. IPv6 address with port, eg. [fe80::1]:8457
145*aa772005SRobert Watson 	 * We discover IPv6 address by checking for two colons and if port is
146*aa772005SRobert Watson 	 * given, the address has to start with [.
147*aa772005SRobert Watson 	 */
148*aa772005SRobert Watson 	pp = NULL;
149*aa772005SRobert Watson 	if (strchr(addr, ':') != strrchr(addr, ':')) {
150*aa772005SRobert Watson 		if (addr[0] == '[')
151*aa772005SRobert Watson 			pp = strrchr(addr, ':');
152*aa772005SRobert Watson 	} else {
153*aa772005SRobert Watson 		pp = strrchr(addr, ':');
154*aa772005SRobert Watson 	}
155*aa772005SRobert Watson 	if (pp == NULL) {
156*aa772005SRobert Watson 		/* Port not given, use the default. */
157*aa772005SRobert Watson 		port = defport;
158*aa772005SRobert Watson 	} else {
159*aa772005SRobert Watson 		if (numfromstr(pp + 1, 1, 65535, &port) < 0)
160*aa772005SRobert Watson 			return (errno);
161*aa772005SRobert Watson 	}
162*aa772005SRobert Watson 	(void)snprintf(portstr, sizeof(portstr), "%jd", (intmax_t)port);
163*aa772005SRobert Watson 	/* Extract host name or IP address. */
164*aa772005SRobert Watson 	if (pp == NULL) {
165*aa772005SRobert Watson 		size = sizeof(iporhost);
166*aa772005SRobert Watson 		if (strlcpy(iporhost, addr, size) >= size)
167*aa772005SRobert Watson 			return (ENAMETOOLONG);
168*aa772005SRobert Watson 	} else if (addr[0] == '[' && pp[-1] == ']') {
169*aa772005SRobert Watson 		size = (size_t)(pp - addr - 2 + 1);
170*aa772005SRobert Watson 		if (size > sizeof(iporhost))
171*aa772005SRobert Watson 			return (ENAMETOOLONG);
172*aa772005SRobert Watson 		(void)strlcpy(iporhost, addr + 1, size);
173*aa772005SRobert Watson 	} else {
174*aa772005SRobert Watson 		size = (size_t)(pp - addr + 1);
175*aa772005SRobert Watson 		if (size > sizeof(iporhost))
176*aa772005SRobert Watson 			return (ENAMETOOLONG);
177*aa772005SRobert Watson 		(void)strlcpy(iporhost, addr, size);
178*aa772005SRobert Watson 	}
179*aa772005SRobert Watson 
180*aa772005SRobert Watson 	error = getaddrinfo(iporhost, portstr, &hints, &res);
181*aa772005SRobert Watson 	if (error != 0) {
182*aa772005SRobert Watson 		pjdlog_debug(1, "getaddrinfo(%s, %s) failed: %s.", iporhost,
183*aa772005SRobert Watson 		    portstr, gai_strerror(error));
184*aa772005SRobert Watson 		return (EINVAL);
185*aa772005SRobert Watson 	}
186*aa772005SRobert Watson 	if (res == NULL)
187*aa772005SRobert Watson 		return (ENOENT);
188*aa772005SRobert Watson 
189*aa772005SRobert Watson 	memcpy(sap, res->ai_addr, res->ai_addrlen);
190*aa772005SRobert Watson 
191*aa772005SRobert Watson 	freeaddrinfo(res);
192*aa772005SRobert Watson 
193*aa772005SRobert Watson 	return (0);
194*aa772005SRobert Watson }
195*aa772005SRobert Watson 
196*aa772005SRobert Watson static int
tcp_setup_new(const char * addr,int side,struct tcp_ctx ** tctxp)197*aa772005SRobert Watson tcp_setup_new(const char *addr, int side, struct tcp_ctx **tctxp)
198*aa772005SRobert Watson {
199*aa772005SRobert Watson 	struct tcp_ctx *tctx;
200*aa772005SRobert Watson 	int error, nodelay;
201*aa772005SRobert Watson 
202*aa772005SRobert Watson 	PJDLOG_ASSERT(addr != NULL);
203*aa772005SRobert Watson 	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
204*aa772005SRobert Watson 	    side == TCP_SIDE_SERVER_LISTEN);
205*aa772005SRobert Watson 	PJDLOG_ASSERT(tctxp != NULL);
206*aa772005SRobert Watson 
207*aa772005SRobert Watson 	tctx = malloc(sizeof(*tctx));
208*aa772005SRobert Watson 	if (tctx == NULL)
209*aa772005SRobert Watson 		return (errno);
210*aa772005SRobert Watson 
211*aa772005SRobert Watson 	/* Parse given address. */
212*aa772005SRobert Watson 	error = tcp_addr(addr, atoi(proto_get("tcp:port")), &tctx->tc_sa);
213*aa772005SRobert Watson 	if (error != 0) {
214*aa772005SRobert Watson 		free(tctx);
215*aa772005SRobert Watson 		return (error);
216*aa772005SRobert Watson 	}
217*aa772005SRobert Watson 
218*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
219*aa772005SRobert Watson 
220*aa772005SRobert Watson 	tctx->tc_fd = socket(tctx->tc_sa.ss_family, SOCK_STREAM, 0);
221*aa772005SRobert Watson 	if (tctx->tc_fd == -1) {
222*aa772005SRobert Watson 		error = errno;
223*aa772005SRobert Watson 		free(tctx);
224*aa772005SRobert Watson 		return (error);
225*aa772005SRobert Watson 	}
226*aa772005SRobert Watson 
227*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
228*aa772005SRobert Watson 
229*aa772005SRobert Watson 	/* Socket settings. */
230*aa772005SRobert Watson 	nodelay = 1;
231*aa772005SRobert Watson 	if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay,
232*aa772005SRobert Watson 	    sizeof(nodelay)) == -1) {
233*aa772005SRobert Watson 		pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY");
234*aa772005SRobert Watson 	}
235*aa772005SRobert Watson 
236*aa772005SRobert Watson 	tctx->tc_wait_called = (side == TCP_SIDE_CLIENT ? false : true);
237*aa772005SRobert Watson 	tctx->tc_side = side;
238*aa772005SRobert Watson 	tctx->tc_magic = TCP_CTX_MAGIC;
239*aa772005SRobert Watson 	*tctxp = tctx;
240*aa772005SRobert Watson 
241*aa772005SRobert Watson 	return (0);
242*aa772005SRobert Watson }
243*aa772005SRobert Watson 
244*aa772005SRobert Watson static socklen_t
sockaddr_len(const struct sockaddr_storage * ss)245*aa772005SRobert Watson sockaddr_len(const struct sockaddr_storage *ss)
246*aa772005SRobert Watson {
247*aa772005SRobert Watson 
248*aa772005SRobert Watson #ifdef HAVE_SOCKADDR_STORAGE_SS_LEN
249*aa772005SRobert Watson 	return (ss->ss_len);
250*aa772005SRobert Watson #else
251*aa772005SRobert Watson 	switch (ss->ss_family) {
252*aa772005SRobert Watson 	case AF_INET:
253*aa772005SRobert Watson 		return (sizeof(struct sockaddr_in));
254*aa772005SRobert Watson 	case AF_INET6:
255*aa772005SRobert Watson 		return (sizeof(struct sockaddr_in6));
256*aa772005SRobert Watson 	default:
257*aa772005SRobert Watson 		PJDLOG_ABORT("Unexpected family %hhu.", ss->ss_family);
258*aa772005SRobert Watson 	}
259*aa772005SRobert Watson #endif
260*aa772005SRobert Watson }
261*aa772005SRobert Watson 
262*aa772005SRobert Watson static int
tcp_connect(const char * srcaddr,const char * dstaddr,int timeout,void ** ctxp)263*aa772005SRobert Watson tcp_connect(const char *srcaddr, const char *dstaddr, int timeout, void **ctxp)
264*aa772005SRobert Watson {
265*aa772005SRobert Watson 	struct tcp_ctx *tctx;
266*aa772005SRobert Watson 	struct sockaddr_storage sa;
267*aa772005SRobert Watson 	int error, flags, ret;
268*aa772005SRobert Watson 
269*aa772005SRobert Watson 	PJDLOG_ASSERT(srcaddr == NULL || srcaddr[0] != '\0');
270*aa772005SRobert Watson 	PJDLOG_ASSERT(dstaddr != NULL);
271*aa772005SRobert Watson 	PJDLOG_ASSERT(timeout >= -1);
272*aa772005SRobert Watson 
273*aa772005SRobert Watson 	error = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, &tctx);
274*aa772005SRobert Watson 	if (error != 0)
275*aa772005SRobert Watson 		return (error);
276*aa772005SRobert Watson 	if (srcaddr != NULL) {
277*aa772005SRobert Watson 		error = tcp_addr(srcaddr, 0, &sa);
278*aa772005SRobert Watson 		if (error != 0)
279*aa772005SRobert Watson 			goto fail;
280*aa772005SRobert Watson 		if (bind(tctx->tc_fd, (struct sockaddr *)&sa,
281*aa772005SRobert Watson 		    sockaddr_len(&sa)) == -1) {
282*aa772005SRobert Watson 			error = errno;
283*aa772005SRobert Watson 			goto fail;
284*aa772005SRobert Watson 		}
285*aa772005SRobert Watson 	}
286*aa772005SRobert Watson 
287*aa772005SRobert Watson 	flags = fcntl(tctx->tc_fd, F_GETFL);
288*aa772005SRobert Watson 	if (flags == -1) {
289*aa772005SRobert Watson 		error = errno;
290*aa772005SRobert Watson 		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
291*aa772005SRobert Watson 		goto fail;
292*aa772005SRobert Watson 	}
293*aa772005SRobert Watson 	/*
294*aa772005SRobert Watson 	 * We make socket non-blocking so we can handle connection timeout
295*aa772005SRobert Watson 	 * manually.
296*aa772005SRobert Watson 	 */
297*aa772005SRobert Watson 	flags |= O_NONBLOCK;
298*aa772005SRobert Watson 	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
299*aa772005SRobert Watson 		error = errno;
300*aa772005SRobert Watson 		pjdlog_common(LOG_DEBUG, 1, errno,
301*aa772005SRobert Watson 		    "fcntl(F_SETFL, O_NONBLOCK) failed");
302*aa772005SRobert Watson 		goto fail;
303*aa772005SRobert Watson 	}
304*aa772005SRobert Watson 
305*aa772005SRobert Watson 	ret = connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
306*aa772005SRobert Watson 	    sockaddr_len(&tctx->tc_sa));
307*aa772005SRobert Watson 	if (ret == -1 && errno != EINPROGRESS) {
308*aa772005SRobert Watson 		error = errno;
309*aa772005SRobert Watson 		pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
310*aa772005SRobert Watson 		goto fail;
311*aa772005SRobert Watson 	}
312*aa772005SRobert Watson 
313*aa772005SRobert Watson 	if (timeout >= 0) {
314*aa772005SRobert Watson 		if (ret == -1) {
315*aa772005SRobert Watson 			/* Connection still in progress. Wait for it. */
316*aa772005SRobert Watson 			error = tcp_connect_wait(tctx, timeout);
317*aa772005SRobert Watson 			if (error != 0)
318*aa772005SRobert Watson 				goto fail;
319*aa772005SRobert Watson 		} else {
320*aa772005SRobert Watson 			/* Connection already complete. */
321*aa772005SRobert Watson 			flags &= ~O_NONBLOCK;
322*aa772005SRobert Watson 			if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
323*aa772005SRobert Watson 				error = errno;
324*aa772005SRobert Watson 				pjdlog_common(LOG_DEBUG, 1, errno,
325*aa772005SRobert Watson 				    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
326*aa772005SRobert Watson 				goto fail;
327*aa772005SRobert Watson 			}
328*aa772005SRobert Watson 		}
329*aa772005SRobert Watson 	}
330*aa772005SRobert Watson 
331*aa772005SRobert Watson 	*ctxp = tctx;
332*aa772005SRobert Watson 	return (0);
333*aa772005SRobert Watson fail:
334*aa772005SRobert Watson 	tcp_close(tctx);
335*aa772005SRobert Watson 	return (error);
336*aa772005SRobert Watson }
337*aa772005SRobert Watson 
338*aa772005SRobert Watson static int
tcp_connect_wait(void * ctx,int timeout)339*aa772005SRobert Watson tcp_connect_wait(void *ctx, int timeout)
340*aa772005SRobert Watson {
341*aa772005SRobert Watson 	struct tcp_ctx *tctx = ctx;
342*aa772005SRobert Watson 	struct timeval tv;
343*aa772005SRobert Watson 	fd_set fdset;
344*aa772005SRobert Watson 	socklen_t esize;
345*aa772005SRobert Watson 	int error, flags, ret;
346*aa772005SRobert Watson 
347*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx != NULL);
348*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
349*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
350*aa772005SRobert Watson 	PJDLOG_ASSERT(!tctx->tc_wait_called);
351*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
352*aa772005SRobert Watson 	PJDLOG_ASSERT(timeout >= 0);
353*aa772005SRobert Watson 
354*aa772005SRobert Watson 	tv.tv_sec = timeout;
355*aa772005SRobert Watson 	tv.tv_usec = 0;
356*aa772005SRobert Watson again:
357*aa772005SRobert Watson 	FD_ZERO(&fdset);
358*aa772005SRobert Watson 	FD_SET(tctx->tc_fd, &fdset);
359*aa772005SRobert Watson 	ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
360*aa772005SRobert Watson 	if (ret == 0) {
361*aa772005SRobert Watson 		error = ETIMEDOUT;
362*aa772005SRobert Watson 		goto done;
363*aa772005SRobert Watson 	} else if (ret == -1) {
364*aa772005SRobert Watson 		if (errno == EINTR)
365*aa772005SRobert Watson 			goto again;
366*aa772005SRobert Watson 		error = errno;
367*aa772005SRobert Watson 		pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
368*aa772005SRobert Watson 		goto done;
369*aa772005SRobert Watson 	}
370*aa772005SRobert Watson 	PJDLOG_ASSERT(ret > 0);
371*aa772005SRobert Watson 	PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset));
372*aa772005SRobert Watson 	esize = sizeof(error);
373*aa772005SRobert Watson 	if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
374*aa772005SRobert Watson 	    &esize) == -1) {
375*aa772005SRobert Watson 		error = errno;
376*aa772005SRobert Watson 		pjdlog_common(LOG_DEBUG, 1, errno,
377*aa772005SRobert Watson 		    "getsockopt(SO_ERROR) failed");
378*aa772005SRobert Watson 		goto done;
379*aa772005SRobert Watson 	}
380*aa772005SRobert Watson 	if (error != 0) {
381*aa772005SRobert Watson 		pjdlog_common(LOG_DEBUG, 1, error,
382*aa772005SRobert Watson 		    "getsockopt(SO_ERROR) returned error");
383*aa772005SRobert Watson 		goto done;
384*aa772005SRobert Watson 	}
385*aa772005SRobert Watson 	error = 0;
386*aa772005SRobert Watson 	tctx->tc_wait_called = true;
387*aa772005SRobert Watson done:
388*aa772005SRobert Watson 	flags = fcntl(tctx->tc_fd, F_GETFL);
389*aa772005SRobert Watson 	if (flags == -1) {
390*aa772005SRobert Watson 		if (error == 0)
391*aa772005SRobert Watson 			error = errno;
392*aa772005SRobert Watson 		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
393*aa772005SRobert Watson 		return (error);
394*aa772005SRobert Watson 	}
395*aa772005SRobert Watson 	flags &= ~O_NONBLOCK;
396*aa772005SRobert Watson 	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
397*aa772005SRobert Watson 		if (error == 0)
398*aa772005SRobert Watson 			error = errno;
399*aa772005SRobert Watson 		pjdlog_common(LOG_DEBUG, 1, errno,
400*aa772005SRobert Watson 		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
401*aa772005SRobert Watson 	}
402*aa772005SRobert Watson 	return (error);
403*aa772005SRobert Watson }
404*aa772005SRobert Watson 
405*aa772005SRobert Watson static int
tcp_server(const char * addr,void ** ctxp)406*aa772005SRobert Watson tcp_server(const char *addr, void **ctxp)
407*aa772005SRobert Watson {
408*aa772005SRobert Watson 	struct tcp_ctx *tctx;
409*aa772005SRobert Watson 	int error, val;
410*aa772005SRobert Watson 
411*aa772005SRobert Watson 	error = tcp_setup_new(addr, TCP_SIDE_SERVER_LISTEN, &tctx);
412*aa772005SRobert Watson 	if (error != 0)
413*aa772005SRobert Watson 		return (error);
414*aa772005SRobert Watson 
415*aa772005SRobert Watson 	val = 1;
416*aa772005SRobert Watson 	/* Ignore failure. */
417*aa772005SRobert Watson 	(void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
418*aa772005SRobert Watson 	   sizeof(val));
419*aa772005SRobert Watson 
420*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
421*aa772005SRobert Watson 
422*aa772005SRobert Watson 	if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
423*aa772005SRobert Watson 	    sockaddr_len(&tctx->tc_sa)) == -1) {
424*aa772005SRobert Watson 		error = errno;
425*aa772005SRobert Watson 		tcp_close(tctx);
426*aa772005SRobert Watson 		return (error);
427*aa772005SRobert Watson 	}
428*aa772005SRobert Watson 	if (listen(tctx->tc_fd, 8) == -1) {
429*aa772005SRobert Watson 		error = errno;
430*aa772005SRobert Watson 		tcp_close(tctx);
431*aa772005SRobert Watson 		return (error);
432*aa772005SRobert Watson 	}
433*aa772005SRobert Watson 
434*aa772005SRobert Watson 	*ctxp = tctx;
435*aa772005SRobert Watson 
436*aa772005SRobert Watson 	return (0);
437*aa772005SRobert Watson }
438*aa772005SRobert Watson 
439*aa772005SRobert Watson static int
tcp_accept(void * ctx,void ** newctxp)440*aa772005SRobert Watson tcp_accept(void *ctx, void **newctxp)
441*aa772005SRobert Watson {
442*aa772005SRobert Watson 	struct tcp_ctx *tctx = ctx;
443*aa772005SRobert Watson 	struct tcp_ctx *newtctx;
444*aa772005SRobert Watson 	socklen_t fromlen;
445*aa772005SRobert Watson 	int ret;
446*aa772005SRobert Watson 
447*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx != NULL);
448*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
449*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN);
450*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
451*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
452*aa772005SRobert Watson 
453*aa772005SRobert Watson 	newtctx = malloc(sizeof(*newtctx));
454*aa772005SRobert Watson 	if (newtctx == NULL)
455*aa772005SRobert Watson 		return (errno);
456*aa772005SRobert Watson 
457*aa772005SRobert Watson 	fromlen = sockaddr_len(&tctx->tc_sa);
458*aa772005SRobert Watson 	newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
459*aa772005SRobert Watson 	    &fromlen);
460*aa772005SRobert Watson 	if (newtctx->tc_fd < 0) {
461*aa772005SRobert Watson 		ret = errno;
462*aa772005SRobert Watson 		free(newtctx);
463*aa772005SRobert Watson 		return (ret);
464*aa772005SRobert Watson 	}
465*aa772005SRobert Watson 
466*aa772005SRobert Watson 	newtctx->tc_wait_called = true;
467*aa772005SRobert Watson 	newtctx->tc_side = TCP_SIDE_SERVER_WORK;
468*aa772005SRobert Watson 	newtctx->tc_magic = TCP_CTX_MAGIC;
469*aa772005SRobert Watson 	*newctxp = newtctx;
470*aa772005SRobert Watson 
471*aa772005SRobert Watson 	return (0);
472*aa772005SRobert Watson }
473*aa772005SRobert Watson 
474*aa772005SRobert Watson static int
tcp_wrap(int fd,bool client,void ** ctxp)475*aa772005SRobert Watson tcp_wrap(int fd, bool client, void **ctxp)
476*aa772005SRobert Watson {
477*aa772005SRobert Watson 	struct tcp_ctx *tctx;
478*aa772005SRobert Watson 
479*aa772005SRobert Watson 	PJDLOG_ASSERT(fd >= 0);
480*aa772005SRobert Watson 	PJDLOG_ASSERT(ctxp != NULL);
481*aa772005SRobert Watson 
482*aa772005SRobert Watson 	tctx = malloc(sizeof(*tctx));
483*aa772005SRobert Watson 	if (tctx == NULL)
484*aa772005SRobert Watson 		return (errno);
485*aa772005SRobert Watson 
486*aa772005SRobert Watson 	tctx->tc_fd = fd;
487*aa772005SRobert Watson 	tctx->tc_sa.ss_family = AF_UNSPEC;
488*aa772005SRobert Watson 	tctx->tc_wait_called = (client ? false : true);
489*aa772005SRobert Watson 	tctx->tc_side = (client ? TCP_SIDE_CLIENT : TCP_SIDE_SERVER_WORK);
490*aa772005SRobert Watson 	tctx->tc_magic = TCP_CTX_MAGIC;
491*aa772005SRobert Watson 	*ctxp = tctx;
492*aa772005SRobert Watson 
493*aa772005SRobert Watson 	return (0);
494*aa772005SRobert Watson }
495*aa772005SRobert Watson 
496*aa772005SRobert Watson static int
tcp_send(void * ctx,const unsigned char * data,size_t size,int fd)497*aa772005SRobert Watson tcp_send(void *ctx, const unsigned char *data, size_t size, int fd)
498*aa772005SRobert Watson {
499*aa772005SRobert Watson 	struct tcp_ctx *tctx = ctx;
500*aa772005SRobert Watson 
501*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx != NULL);
502*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
503*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT ||
504*aa772005SRobert Watson 	    tctx->tc_side == TCP_SIDE_SERVER_WORK);
505*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_wait_called);
506*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
507*aa772005SRobert Watson 	PJDLOG_ASSERT(fd == -1);
508*aa772005SRobert Watson 
509*aa772005SRobert Watson 	return (proto_common_send(tctx->tc_fd, data, size, -1));
510*aa772005SRobert Watson }
511*aa772005SRobert Watson 
512*aa772005SRobert Watson static int
tcp_recv(void * ctx,unsigned char * data,size_t size,int * fdp)513*aa772005SRobert Watson tcp_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
514*aa772005SRobert Watson {
515*aa772005SRobert Watson 	struct tcp_ctx *tctx = ctx;
516*aa772005SRobert Watson 
517*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx != NULL);
518*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
519*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT ||
520*aa772005SRobert Watson 	    tctx->tc_side == TCP_SIDE_SERVER_WORK);
521*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_wait_called);
522*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
523*aa772005SRobert Watson 	PJDLOG_ASSERT(fdp == NULL);
524*aa772005SRobert Watson 
525*aa772005SRobert Watson 	return (proto_common_recv(tctx->tc_fd, data, size, NULL));
526*aa772005SRobert Watson }
527*aa772005SRobert Watson 
528*aa772005SRobert Watson static int
tcp_descriptor(const void * ctx)529*aa772005SRobert Watson tcp_descriptor(const void *ctx)
530*aa772005SRobert Watson {
531*aa772005SRobert Watson 	const struct tcp_ctx *tctx = ctx;
532*aa772005SRobert Watson 
533*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx != NULL);
534*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
535*aa772005SRobert Watson 
536*aa772005SRobert Watson 	return (tctx->tc_fd);
537*aa772005SRobert Watson }
538*aa772005SRobert Watson 
539*aa772005SRobert Watson static bool
tcp_address_match(const void * ctx,const char * addr)540*aa772005SRobert Watson tcp_address_match(const void *ctx, const char *addr)
541*aa772005SRobert Watson {
542*aa772005SRobert Watson 	const struct tcp_ctx *tctx = ctx;
543*aa772005SRobert Watson 	struct sockaddr_storage sa1, sa2;
544*aa772005SRobert Watson 	socklen_t salen;
545*aa772005SRobert Watson 
546*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx != NULL);
547*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
548*aa772005SRobert Watson 
549*aa772005SRobert Watson 	if (tcp_addr(addr, atoi(proto_get("tcp:port")), &sa1) != 0)
550*aa772005SRobert Watson 		return (false);
551*aa772005SRobert Watson 
552*aa772005SRobert Watson 	salen = sizeof(sa2);
553*aa772005SRobert Watson 	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) < 0)
554*aa772005SRobert Watson 		return (false);
555*aa772005SRobert Watson 
556*aa772005SRobert Watson 	if (sa1.ss_family != sa2.ss_family)
557*aa772005SRobert Watson 		return (false);
558*aa772005SRobert Watson 
559*aa772005SRobert Watson #ifdef HAVE_SOCKADDR_STORAGE_SS_LEN
560*aa772005SRobert Watson 	if (sa1.ss_len != sa2.ss_len)
561*aa772005SRobert Watson 		return (false);
562*aa772005SRobert Watson #endif
563*aa772005SRobert Watson 
564*aa772005SRobert Watson 	switch (sa1.ss_family) {
565*aa772005SRobert Watson 	case AF_INET:
566*aa772005SRobert Watson 	    {
567*aa772005SRobert Watson 		struct sockaddr_in *sin1, *sin2;
568*aa772005SRobert Watson 
569*aa772005SRobert Watson 		sin1 = (struct sockaddr_in *)&sa1;
570*aa772005SRobert Watson 		sin2 = (struct sockaddr_in *)&sa2;
571*aa772005SRobert Watson 
572*aa772005SRobert Watson 		return (memcmp(&sin1->sin_addr, &sin2->sin_addr,
573*aa772005SRobert Watson 		    sizeof(sin1->sin_addr)) == 0);
574*aa772005SRobert Watson 	    }
575*aa772005SRobert Watson 	case AF_INET6:
576*aa772005SRobert Watson 	    {
577*aa772005SRobert Watson 		struct sockaddr_in6 *sin1, *sin2;
578*aa772005SRobert Watson 
579*aa772005SRobert Watson 		sin1 = (struct sockaddr_in6 *)&sa1;
580*aa772005SRobert Watson 		sin2 = (struct sockaddr_in6 *)&sa2;
581*aa772005SRobert Watson 
582*aa772005SRobert Watson 		return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
583*aa772005SRobert Watson 		    sizeof(sin1->sin6_addr)) == 0);
584*aa772005SRobert Watson 	    }
585*aa772005SRobert Watson 	default:
586*aa772005SRobert Watson 		return (false);
587*aa772005SRobert Watson 	}
588*aa772005SRobert Watson }
589*aa772005SRobert Watson 
590*aa772005SRobert Watson #ifndef __FreeBSD__
591*aa772005SRobert Watson static void
sockaddr_to_string(const void * sa,char * buf,size_t size)592*aa772005SRobert Watson sockaddr_to_string(const void *sa, char *buf, size_t size)
593*aa772005SRobert Watson {
594*aa772005SRobert Watson 	const struct sockaddr_storage *ss;
595*aa772005SRobert Watson 
596*aa772005SRobert Watson 	ss = (const struct sockaddr_storage * const *)sa;
597*aa772005SRobert Watson 	switch (ss->ss_family) {
598*aa772005SRobert Watson 	case AF_INET:
599*aa772005SRobert Watson 	    {
600*aa772005SRobert Watson 		char addr[INET_ADDRSTRLEN];
601*aa772005SRobert Watson 		const struct sockaddr_in *sin;
602*aa772005SRobert Watson 		unsigned int port;
603*aa772005SRobert Watson 
604*aa772005SRobert Watson 		sin = (const struct sockaddr_in *)ss;
605*aa772005SRobert Watson 		port = ntohs(sin->sin_port);
606*aa772005SRobert Watson 		if (inet_ntop(ss->ss_family, &sin->sin_addr, addr,
607*aa772005SRobert Watson 		    sizeof(addr)) == NULL) {
608*aa772005SRobert Watson 			PJDLOG_ABORT("inet_ntop(AF_INET) failed: %s.",
609*aa772005SRobert Watson 			    strerror(errno));
610*aa772005SRobert Watson 		}
611*aa772005SRobert Watson 		snprintf(buf, size, "%s:%u", addr, port);
612*aa772005SRobert Watson 		break;
613*aa772005SRobert Watson 	    }
614*aa772005SRobert Watson 	case AF_INET6:
615*aa772005SRobert Watson 	    {
616*aa772005SRobert Watson 		char addr[INET6_ADDRSTRLEN];
617*aa772005SRobert Watson 		const struct sockaddr_in6 *sin;
618*aa772005SRobert Watson 		unsigned int port;
619*aa772005SRobert Watson 
620*aa772005SRobert Watson 		sin = (const struct sockaddr_in6 *)ss;
621*aa772005SRobert Watson 		port = ntohs(sin->sin6_port);
622*aa772005SRobert Watson 		if (inet_ntop(ss->ss_family, &sin->sin6_addr, addr,
623*aa772005SRobert Watson 		    sizeof(addr)) == NULL) {
624*aa772005SRobert Watson 			PJDLOG_ABORT("inet_ntop(AF_INET6) failed: %s.",
625*aa772005SRobert Watson 			    strerror(errno));
626*aa772005SRobert Watson 		}
627*aa772005SRobert Watson 		snprintf(buf, size, "[%s]:%u", addr, port);
628*aa772005SRobert Watson 		break;
629*aa772005SRobert Watson 	    }
630*aa772005SRobert Watson 	default:
631*aa772005SRobert Watson 		snprintf(buf, size, "[unsupported family %hhu]",
632*aa772005SRobert Watson 		    ss->ss_family);
633*aa772005SRobert Watson 		break;
634*aa772005SRobert Watson 	}
635*aa772005SRobert Watson }
636*aa772005SRobert Watson #endif	/* !__FreeBSD__ */
637*aa772005SRobert Watson 
638*aa772005SRobert Watson static void
tcp_local_address(const void * ctx,char * addr,size_t size)639*aa772005SRobert Watson tcp_local_address(const void *ctx, char *addr, size_t size)
640*aa772005SRobert Watson {
641*aa772005SRobert Watson 	const struct tcp_ctx *tctx = ctx;
642*aa772005SRobert Watson 	struct sockaddr_storage sa;
643*aa772005SRobert Watson 	socklen_t salen;
644*aa772005SRobert Watson 
645*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx != NULL);
646*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
647*aa772005SRobert Watson 
648*aa772005SRobert Watson 	salen = sizeof(sa);
649*aa772005SRobert Watson 	if (getsockname(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) {
650*aa772005SRobert Watson 		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
651*aa772005SRobert Watson 		return;
652*aa772005SRobert Watson 	}
653*aa772005SRobert Watson #ifdef __FreeBSD__
654*aa772005SRobert Watson 	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
655*aa772005SRobert Watson #else
656*aa772005SRobert Watson 	strlcpy(addr, "tcp://", size);
657*aa772005SRobert Watson 	if (size > 6)
658*aa772005SRobert Watson 		sockaddr_to_string(&sa, addr + 6, size - 6);
659*aa772005SRobert Watson #endif
660*aa772005SRobert Watson }
661*aa772005SRobert Watson 
662*aa772005SRobert Watson static void
tcp_remote_address(const void * ctx,char * addr,size_t size)663*aa772005SRobert Watson tcp_remote_address(const void *ctx, char *addr, size_t size)
664*aa772005SRobert Watson {
665*aa772005SRobert Watson 	const struct tcp_ctx *tctx = ctx;
666*aa772005SRobert Watson 	struct sockaddr_storage sa;
667*aa772005SRobert Watson 	socklen_t salen;
668*aa772005SRobert Watson 
669*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx != NULL);
670*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
671*aa772005SRobert Watson 
672*aa772005SRobert Watson 	salen = sizeof(sa);
673*aa772005SRobert Watson 	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) {
674*aa772005SRobert Watson 		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
675*aa772005SRobert Watson 		return;
676*aa772005SRobert Watson 	}
677*aa772005SRobert Watson #ifdef __FreeBSD__
678*aa772005SRobert Watson 	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
679*aa772005SRobert Watson #else
680*aa772005SRobert Watson 	strlcpy(addr, "tcp://", size);
681*aa772005SRobert Watson 	if (size > 6)
682*aa772005SRobert Watson 		sockaddr_to_string(&sa, addr + 6, size - 6);
683*aa772005SRobert Watson #endif
684*aa772005SRobert Watson }
685*aa772005SRobert Watson 
686*aa772005SRobert Watson static void
tcp_close(void * ctx)687*aa772005SRobert Watson tcp_close(void *ctx)
688*aa772005SRobert Watson {
689*aa772005SRobert Watson 	struct tcp_ctx *tctx = ctx;
690*aa772005SRobert Watson 
691*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx != NULL);
692*aa772005SRobert Watson 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
693*aa772005SRobert Watson 
694*aa772005SRobert Watson 	if (tctx->tc_fd >= 0)
695*aa772005SRobert Watson 		close(tctx->tc_fd);
696*aa772005SRobert Watson 	tctx->tc_magic = 0;
697*aa772005SRobert Watson 	free(tctx);
698*aa772005SRobert Watson }
699*aa772005SRobert Watson 
700*aa772005SRobert Watson static struct proto tcp_proto = {
701*aa772005SRobert Watson 	.prt_name = "tcp",
702*aa772005SRobert Watson 	.prt_connect = tcp_connect,
703*aa772005SRobert Watson 	.prt_connect_wait = tcp_connect_wait,
704*aa772005SRobert Watson 	.prt_server = tcp_server,
705*aa772005SRobert Watson 	.prt_accept = tcp_accept,
706*aa772005SRobert Watson 	.prt_wrap = tcp_wrap,
707*aa772005SRobert Watson 	.prt_send = tcp_send,
708*aa772005SRobert Watson 	.prt_recv = tcp_recv,
709*aa772005SRobert Watson 	.prt_descriptor = tcp_descriptor,
710*aa772005SRobert Watson 	.prt_address_match = tcp_address_match,
711*aa772005SRobert Watson 	.prt_local_address = tcp_local_address,
712*aa772005SRobert Watson 	.prt_remote_address = tcp_remote_address,
713*aa772005SRobert Watson 	.prt_close = tcp_close
714*aa772005SRobert Watson };
715*aa772005SRobert Watson 
716*aa772005SRobert Watson static __constructor void
tcp_ctor(void)717*aa772005SRobert Watson tcp_ctor(void)
718*aa772005SRobert Watson {
719*aa772005SRobert Watson 
720*aa772005SRobert Watson 	proto_register(&tcp_proto, true);
721*aa772005SRobert Watson }
722