xref: /freebsd/sbin/hastd/proto_tcp.c (revision dc18c8ae6cd3cc98b1195ef3d9331663d361c001)
1933728eeSPawel Jakub Dawidek /*-
2933728eeSPawel Jakub Dawidek  * Copyright (c) 2009-2010 The FreeBSD Foundation
3*dc18c8aeSPawel Jakub Dawidek  * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
4933728eeSPawel Jakub Dawidek  * All rights reserved.
5933728eeSPawel Jakub Dawidek  *
6933728eeSPawel Jakub Dawidek  * This software was developed by Pawel Jakub Dawidek under sponsorship from
7933728eeSPawel Jakub Dawidek  * the FreeBSD Foundation.
8933728eeSPawel Jakub Dawidek  *
9933728eeSPawel Jakub Dawidek  * Redistribution and use in source and binary forms, with or without
10933728eeSPawel Jakub Dawidek  * modification, are permitted provided that the following conditions
11933728eeSPawel Jakub Dawidek  * are met:
12933728eeSPawel Jakub Dawidek  * 1. Redistributions of source code must retain the above copyright
13933728eeSPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer.
14933728eeSPawel Jakub Dawidek  * 2. Redistributions in binary form must reproduce the above copyright
15933728eeSPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer in the
16933728eeSPawel Jakub Dawidek  *    documentation and/or other materials provided with the distribution.
17933728eeSPawel Jakub Dawidek  *
18933728eeSPawel Jakub Dawidek  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19933728eeSPawel Jakub Dawidek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20933728eeSPawel Jakub Dawidek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21933728eeSPawel Jakub Dawidek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22933728eeSPawel Jakub Dawidek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23933728eeSPawel Jakub Dawidek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24933728eeSPawel Jakub Dawidek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25933728eeSPawel Jakub Dawidek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26933728eeSPawel Jakub Dawidek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27933728eeSPawel Jakub Dawidek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28933728eeSPawel Jakub Dawidek  * SUCH DAMAGE.
29933728eeSPawel Jakub Dawidek  */
30933728eeSPawel Jakub Dawidek 
31933728eeSPawel Jakub Dawidek #include <sys/cdefs.h>
32933728eeSPawel Jakub Dawidek __FBSDID("$FreeBSD$");
33933728eeSPawel Jakub Dawidek 
34933728eeSPawel Jakub Dawidek #include <sys/param.h>	/* MAXHOSTNAMELEN */
35933728eeSPawel Jakub Dawidek #include <sys/socket.h>
36933728eeSPawel Jakub Dawidek 
37933728eeSPawel Jakub Dawidek #include <arpa/inet.h>
38933728eeSPawel Jakub Dawidek 
39933728eeSPawel Jakub Dawidek #include <netinet/in.h>
40933728eeSPawel Jakub Dawidek #include <netinet/tcp.h>
41933728eeSPawel Jakub Dawidek 
42933728eeSPawel Jakub Dawidek #include <errno.h>
43933728eeSPawel Jakub Dawidek #include <fcntl.h>
44933728eeSPawel Jakub Dawidek #include <netdb.h>
45933728eeSPawel Jakub Dawidek #include <stdbool.h>
46933728eeSPawel Jakub Dawidek #include <stdint.h>
47933728eeSPawel Jakub Dawidek #include <stdio.h>
48933728eeSPawel Jakub Dawidek #include <string.h>
49933728eeSPawel Jakub Dawidek #include <unistd.h>
50933728eeSPawel Jakub Dawidek 
51933728eeSPawel Jakub Dawidek #include "pjdlog.h"
52933728eeSPawel Jakub Dawidek #include "proto_impl.h"
53933728eeSPawel Jakub Dawidek #include "subr.h"
54933728eeSPawel Jakub Dawidek 
55*dc18c8aeSPawel Jakub Dawidek #define	TCP_CTX_MAGIC	0x7c41c
56bdbd046bSPawel Jakub Dawidek struct tcp_ctx {
57933728eeSPawel Jakub Dawidek 	int			tc_magic;
58*dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage	tc_sa;
59933728eeSPawel Jakub Dawidek 	int			tc_fd;
60933728eeSPawel Jakub Dawidek 	int			tc_side;
61bdbd046bSPawel Jakub Dawidek #define	TCP_SIDE_CLIENT		0
62bdbd046bSPawel Jakub Dawidek #define	TCP_SIDE_SERVER_LISTEN	1
63bdbd046bSPawel Jakub Dawidek #define	TCP_SIDE_SERVER_WORK	2
64933728eeSPawel Jakub Dawidek };
65933728eeSPawel Jakub Dawidek 
66bdbd046bSPawel Jakub Dawidek static int tcp_connect_wait(void *ctx, int timeout);
67bdbd046bSPawel Jakub Dawidek static void tcp_close(void *ctx);
68933728eeSPawel Jakub Dawidek 
69933728eeSPawel Jakub Dawidek /*
70933728eeSPawel Jakub Dawidek  * Function converts the given string to unsigned number.
71933728eeSPawel Jakub Dawidek  */
72933728eeSPawel Jakub Dawidek static int
73933728eeSPawel Jakub Dawidek numfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump)
74933728eeSPawel Jakub Dawidek {
75933728eeSPawel Jakub Dawidek 	intmax_t digit, num;
76933728eeSPawel Jakub Dawidek 
77933728eeSPawel Jakub Dawidek 	if (str[0] == '\0')
78933728eeSPawel Jakub Dawidek 		goto invalid;	/* Empty string. */
79933728eeSPawel Jakub Dawidek 	num = 0;
80933728eeSPawel Jakub Dawidek 	for (; *str != '\0'; str++) {
81933728eeSPawel Jakub Dawidek 		if (*str < '0' || *str > '9')
82933728eeSPawel Jakub Dawidek 			goto invalid;	/* Non-digit character. */
83933728eeSPawel Jakub Dawidek 		digit = *str - '0';
84933728eeSPawel Jakub Dawidek 		if (num > num * 10 + digit)
85933728eeSPawel Jakub Dawidek 			goto invalid;	/* Overflow. */
86933728eeSPawel Jakub Dawidek 		num = num * 10 + digit;
87933728eeSPawel Jakub Dawidek 		if (num > maxnum)
88933728eeSPawel Jakub Dawidek 			goto invalid;	/* Too big. */
89933728eeSPawel Jakub Dawidek 	}
90933728eeSPawel Jakub Dawidek 	if (num < minnum)
91933728eeSPawel Jakub Dawidek 		goto invalid;	/* Too small. */
92933728eeSPawel Jakub Dawidek 	*nump = num;
93933728eeSPawel Jakub Dawidek 	return (0);
94933728eeSPawel Jakub Dawidek invalid:
95933728eeSPawel Jakub Dawidek 	errno = EINVAL;
96933728eeSPawel Jakub Dawidek 	return (-1);
97933728eeSPawel Jakub Dawidek }
98933728eeSPawel Jakub Dawidek 
99933728eeSPawel Jakub Dawidek static int
100*dc18c8aeSPawel Jakub Dawidek tcp_addr(const char *addr, int defport, struct sockaddr_storage *sap)
101933728eeSPawel Jakub Dawidek {
102*dc18c8aeSPawel Jakub Dawidek 	char iporhost[MAXHOSTNAMELEN], portstr[6];
103*dc18c8aeSPawel Jakub Dawidek 	struct addrinfo hints;
104*dc18c8aeSPawel Jakub Dawidek 	struct addrinfo *res;
105933728eeSPawel Jakub Dawidek 	const char *pp;
106*dc18c8aeSPawel Jakub Dawidek 	intmax_t port;
107933728eeSPawel Jakub Dawidek 	size_t size;
108*dc18c8aeSPawel Jakub Dawidek 	int error;
109933728eeSPawel Jakub Dawidek 
110933728eeSPawel Jakub Dawidek 	if (addr == NULL)
111933728eeSPawel Jakub Dawidek 		return (-1);
112933728eeSPawel Jakub Dawidek 
113*dc18c8aeSPawel Jakub Dawidek 	bzero(&hints, sizeof(hints));
114*dc18c8aeSPawel Jakub Dawidek 	hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
115*dc18c8aeSPawel Jakub Dawidek 	hints.ai_family = PF_UNSPEC;
116*dc18c8aeSPawel Jakub Dawidek 	hints.ai_socktype = SOCK_STREAM;
117*dc18c8aeSPawel Jakub Dawidek 	hints.ai_protocol = IPPROTO_TCP;
118*dc18c8aeSPawel Jakub Dawidek 
119*dc18c8aeSPawel Jakub Dawidek 	if (strncasecmp(addr, "tcp4://", 7) == 0) {
120933728eeSPawel Jakub Dawidek 		addr += 7;
121*dc18c8aeSPawel Jakub Dawidek 		hints.ai_family = PF_INET;
122*dc18c8aeSPawel Jakub Dawidek 	} else if (strncasecmp(addr, "tcp6://", 7) == 0) {
123*dc18c8aeSPawel Jakub Dawidek 		addr += 7;
124*dc18c8aeSPawel Jakub Dawidek 		hints.ai_family = PF_INET6;
125*dc18c8aeSPawel Jakub Dawidek 	} else if (strncasecmp(addr, "tcp://", 6) == 0) {
126933728eeSPawel Jakub Dawidek 		addr += 6;
127*dc18c8aeSPawel Jakub Dawidek 	} else {
128933728eeSPawel Jakub Dawidek 		/*
129bdbd046bSPawel Jakub Dawidek 		 * Because TCP is the default assume IP or host is given without
130933728eeSPawel Jakub Dawidek 		 * prefix.
131933728eeSPawel Jakub Dawidek 		 */
132933728eeSPawel Jakub Dawidek 	}
133933728eeSPawel Jakub Dawidek 
134*dc18c8aeSPawel Jakub Dawidek 	/*
135*dc18c8aeSPawel Jakub Dawidek 	 * Extract optional port.
136*dc18c8aeSPawel Jakub Dawidek 	 * There are three cases to consider.
137*dc18c8aeSPawel Jakub Dawidek 	 * 1. hostname with port, eg. freefall.freebsd.org:8457
138*dc18c8aeSPawel Jakub Dawidek 	 * 2. IPv4 address with port, eg. 192.168.0.101:8457
139*dc18c8aeSPawel Jakub Dawidek 	 * 3. IPv6 address with port, eg. [fe80::1]:8457
140*dc18c8aeSPawel Jakub Dawidek 	 * We discover IPv6 address by checking for two colons and if port is
141*dc18c8aeSPawel Jakub Dawidek 	 * given, the address has to start with [.
142*dc18c8aeSPawel Jakub Dawidek 	 */
143*dc18c8aeSPawel Jakub Dawidek 	pp = NULL;
144*dc18c8aeSPawel Jakub Dawidek 	if (strchr(addr, ':') != strrchr(addr, ':')) {
145*dc18c8aeSPawel Jakub Dawidek 		if (addr[0] == '[')
146933728eeSPawel Jakub Dawidek 			pp = strrchr(addr, ':');
147*dc18c8aeSPawel Jakub Dawidek 	} else {
148*dc18c8aeSPawel Jakub Dawidek 		pp = strrchr(addr, ':');
149*dc18c8aeSPawel Jakub Dawidek 	}
150933728eeSPawel Jakub Dawidek 	if (pp == NULL) {
151933728eeSPawel Jakub Dawidek 		/* Port not given, use the default. */
152*dc18c8aeSPawel Jakub Dawidek 		port = defport;
153933728eeSPawel Jakub Dawidek 	} else {
154933728eeSPawel Jakub Dawidek 		if (numfromstr(pp + 1, 1, 65535, &port) < 0)
155933728eeSPawel Jakub Dawidek 			return (errno);
156933728eeSPawel Jakub Dawidek 	}
157*dc18c8aeSPawel Jakub Dawidek 	(void)snprintf(portstr, sizeof(portstr), "%jd", (intmax_t)port);
158933728eeSPawel Jakub Dawidek 	/* Extract host name or IP address. */
159933728eeSPawel Jakub Dawidek 	if (pp == NULL) {
160933728eeSPawel Jakub Dawidek 		size = sizeof(iporhost);
161933728eeSPawel Jakub Dawidek 		if (strlcpy(iporhost, addr, size) >= size)
162933728eeSPawel Jakub Dawidek 			return (ENAMETOOLONG);
163*dc18c8aeSPawel Jakub Dawidek 	} else if (addr[0] == '[' && pp[-1] == ']') {
164*dc18c8aeSPawel Jakub Dawidek 		size = (size_t)(pp - addr - 2 + 1);
165*dc18c8aeSPawel Jakub Dawidek 		if (size > sizeof(iporhost))
166*dc18c8aeSPawel Jakub Dawidek 			return (ENAMETOOLONG);
167*dc18c8aeSPawel Jakub Dawidek 		(void)strlcpy(iporhost, addr + 1, size);
168933728eeSPawel Jakub Dawidek 	} else {
169933728eeSPawel Jakub Dawidek 		size = (size_t)(pp - addr + 1);
170933728eeSPawel Jakub Dawidek 		if (size > sizeof(iporhost))
171933728eeSPawel Jakub Dawidek 			return (ENAMETOOLONG);
172933728eeSPawel Jakub Dawidek 		(void)strlcpy(iporhost, addr, size);
173933728eeSPawel Jakub Dawidek 	}
174*dc18c8aeSPawel Jakub Dawidek 
175*dc18c8aeSPawel Jakub Dawidek 	error = getaddrinfo(iporhost, portstr, &hints, &res);
176*dc18c8aeSPawel Jakub Dawidek 	if (error != 0) {
177*dc18c8aeSPawel Jakub Dawidek 		pjdlog_debug(1, "getaddrinfo(%s, %s) failed: %s.", iporhost,
178*dc18c8aeSPawel Jakub Dawidek 		    portstr, gai_strerror(error));
179933728eeSPawel Jakub Dawidek 		return (EINVAL);
180*dc18c8aeSPawel Jakub Dawidek 	}
181*dc18c8aeSPawel Jakub Dawidek 	if (res == NULL)
182*dc18c8aeSPawel Jakub Dawidek 		return (ENOENT);
183*dc18c8aeSPawel Jakub Dawidek 
184*dc18c8aeSPawel Jakub Dawidek 	memcpy(sap, res->ai_addr, res->ai_addrlen);
185*dc18c8aeSPawel Jakub Dawidek 
186*dc18c8aeSPawel Jakub Dawidek 	freeaddrinfo(res);
187933728eeSPawel Jakub Dawidek 
188933728eeSPawel Jakub Dawidek 	return (0);
189933728eeSPawel Jakub Dawidek }
190933728eeSPawel Jakub Dawidek 
191933728eeSPawel Jakub Dawidek static int
192bdbd046bSPawel Jakub Dawidek tcp_setup_new(const char *addr, int side, void **ctxp)
193933728eeSPawel Jakub Dawidek {
194bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx;
195933728eeSPawel Jakub Dawidek 	int ret, nodelay;
196933728eeSPawel Jakub Dawidek 
197933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(addr != NULL);
198bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
199bdbd046bSPawel Jakub Dawidek 	    side == TCP_SIDE_SERVER_LISTEN);
200933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(ctxp != NULL);
201933728eeSPawel Jakub Dawidek 
202933728eeSPawel Jakub Dawidek 	tctx = malloc(sizeof(*tctx));
203933728eeSPawel Jakub Dawidek 	if (tctx == NULL)
204933728eeSPawel Jakub Dawidek 		return (errno);
205933728eeSPawel Jakub Dawidek 
206933728eeSPawel Jakub Dawidek 	/* Parse given address. */
207*dc18c8aeSPawel Jakub Dawidek 	if ((ret = tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &tctx->tc_sa)) != 0) {
208933728eeSPawel Jakub Dawidek 		free(tctx);
209933728eeSPawel Jakub Dawidek 		return (ret);
210933728eeSPawel Jakub Dawidek 	}
211933728eeSPawel Jakub Dawidek 
212*dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
213933728eeSPawel Jakub Dawidek 
214*dc18c8aeSPawel Jakub Dawidek 	tctx->tc_fd = socket(tctx->tc_sa.ss_family, SOCK_STREAM, 0);
215933728eeSPawel Jakub Dawidek 	if (tctx->tc_fd == -1) {
216933728eeSPawel Jakub Dawidek 		ret = errno;
217933728eeSPawel Jakub Dawidek 		free(tctx);
218933728eeSPawel Jakub Dawidek 		return (ret);
219933728eeSPawel Jakub Dawidek 	}
220933728eeSPawel Jakub Dawidek 
221*dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
222933728eeSPawel Jakub Dawidek 
223933728eeSPawel Jakub Dawidek 	/* Socket settings. */
224933728eeSPawel Jakub Dawidek 	nodelay = 1;
225933728eeSPawel Jakub Dawidek 	if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay,
226933728eeSPawel Jakub Dawidek 	    sizeof(nodelay)) == -1) {
227933728eeSPawel Jakub Dawidek 		pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY");
228933728eeSPawel Jakub Dawidek 	}
229933728eeSPawel Jakub Dawidek 
230933728eeSPawel Jakub Dawidek 	tctx->tc_side = side;
231bdbd046bSPawel Jakub Dawidek 	tctx->tc_magic = TCP_CTX_MAGIC;
232933728eeSPawel Jakub Dawidek 	*ctxp = tctx;
233933728eeSPawel Jakub Dawidek 
234933728eeSPawel Jakub Dawidek 	return (0);
235933728eeSPawel Jakub Dawidek }
236933728eeSPawel Jakub Dawidek 
237933728eeSPawel Jakub Dawidek static int
238bdbd046bSPawel Jakub Dawidek tcp_setup_wrap(int fd, int side, void **ctxp)
239933728eeSPawel Jakub Dawidek {
240bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx;
241933728eeSPawel Jakub Dawidek 
242933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(fd >= 0);
243bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
244bdbd046bSPawel Jakub Dawidek 	    side == TCP_SIDE_SERVER_WORK);
245933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(ctxp != NULL);
246933728eeSPawel Jakub Dawidek 
247933728eeSPawel Jakub Dawidek 	tctx = malloc(sizeof(*tctx));
248933728eeSPawel Jakub Dawidek 	if (tctx == NULL)
249933728eeSPawel Jakub Dawidek 		return (errno);
250933728eeSPawel Jakub Dawidek 
251933728eeSPawel Jakub Dawidek 	tctx->tc_fd = fd;
252*dc18c8aeSPawel Jakub Dawidek 	tctx->tc_sa.ss_family = AF_UNSPEC;
253933728eeSPawel Jakub Dawidek 	tctx->tc_side = side;
254bdbd046bSPawel Jakub Dawidek 	tctx->tc_magic = TCP_CTX_MAGIC;
255933728eeSPawel Jakub Dawidek 	*ctxp = tctx;
256933728eeSPawel Jakub Dawidek 
257933728eeSPawel Jakub Dawidek 	return (0);
258933728eeSPawel Jakub Dawidek }
259933728eeSPawel Jakub Dawidek 
260933728eeSPawel Jakub Dawidek static int
261bdbd046bSPawel Jakub Dawidek tcp_client(const char *srcaddr, const char *dstaddr, void **ctxp)
262933728eeSPawel Jakub Dawidek {
263bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx;
264*dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage sa;
265933728eeSPawel Jakub Dawidek 	int ret;
266933728eeSPawel Jakub Dawidek 
267bdbd046bSPawel Jakub Dawidek 	ret = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, ctxp);
268933728eeSPawel Jakub Dawidek 	if (ret != 0)
269933728eeSPawel Jakub Dawidek 		return (ret);
270933728eeSPawel Jakub Dawidek 	tctx = *ctxp;
271933728eeSPawel Jakub Dawidek 	if (srcaddr == NULL)
272933728eeSPawel Jakub Dawidek 		return (0);
273*dc18c8aeSPawel Jakub Dawidek 	ret = tcp_addr(srcaddr, 0, &sa);
274933728eeSPawel Jakub Dawidek 	if (ret != 0) {
275bdbd046bSPawel Jakub Dawidek 		tcp_close(tctx);
276933728eeSPawel Jakub Dawidek 		return (ret);
277933728eeSPawel Jakub Dawidek 	}
278*dc18c8aeSPawel Jakub Dawidek 	if (bind(tctx->tc_fd, (struct sockaddr *)&sa, sa.ss_len) < 0) {
279933728eeSPawel Jakub Dawidek 		ret = errno;
280bdbd046bSPawel Jakub Dawidek 		tcp_close(tctx);
281933728eeSPawel Jakub Dawidek 		return (ret);
282933728eeSPawel Jakub Dawidek 	}
283933728eeSPawel Jakub Dawidek 	return (0);
284933728eeSPawel Jakub Dawidek }
285933728eeSPawel Jakub Dawidek 
286933728eeSPawel Jakub Dawidek static int
287bdbd046bSPawel Jakub Dawidek tcp_connect(void *ctx, int timeout)
288933728eeSPawel Jakub Dawidek {
289bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
290933728eeSPawel Jakub Dawidek 	int error, flags;
291933728eeSPawel Jakub Dawidek 
292933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
293bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
294bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
295933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
296*dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
297933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(timeout >= -1);
298933728eeSPawel Jakub Dawidek 
299933728eeSPawel Jakub Dawidek 	flags = fcntl(tctx->tc_fd, F_GETFL);
300933728eeSPawel Jakub Dawidek 	if (flags == -1) {
301933728eeSPawel Jakub Dawidek 		KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno,
302933728eeSPawel Jakub Dawidek 		    "fcntl(F_GETFL) failed"));
303933728eeSPawel Jakub Dawidek 		return (errno);
304933728eeSPawel Jakub Dawidek 	}
305933728eeSPawel Jakub Dawidek 	/*
306933728eeSPawel Jakub Dawidek 	 * We make socket non-blocking so we can handle connection timeout
307933728eeSPawel Jakub Dawidek 	 * manually.
308933728eeSPawel Jakub Dawidek 	 */
309933728eeSPawel Jakub Dawidek 	flags |= O_NONBLOCK;
310933728eeSPawel Jakub Dawidek 	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
311933728eeSPawel Jakub Dawidek 		KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno,
312933728eeSPawel Jakub Dawidek 		    "fcntl(F_SETFL, O_NONBLOCK) failed"));
313933728eeSPawel Jakub Dawidek 		return (errno);
314933728eeSPawel Jakub Dawidek 	}
315933728eeSPawel Jakub Dawidek 
316*dc18c8aeSPawel Jakub Dawidek 	if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
317*dc18c8aeSPawel Jakub Dawidek 	    tctx->tc_sa.ss_len) == 0) {
318933728eeSPawel Jakub Dawidek 		if (timeout == -1)
319933728eeSPawel Jakub Dawidek 			return (0);
320933728eeSPawel Jakub Dawidek 		error = 0;
321933728eeSPawel Jakub Dawidek 		goto done;
322933728eeSPawel Jakub Dawidek 	}
323933728eeSPawel Jakub Dawidek 	if (errno != EINPROGRESS) {
324933728eeSPawel Jakub Dawidek 		error = errno;
325933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
326933728eeSPawel Jakub Dawidek 		goto done;
327933728eeSPawel Jakub Dawidek 	}
328933728eeSPawel Jakub Dawidek 	if (timeout == -1)
329933728eeSPawel Jakub Dawidek 		return (0);
330bdbd046bSPawel Jakub Dawidek 	return (tcp_connect_wait(ctx, timeout));
331933728eeSPawel Jakub Dawidek done:
332933728eeSPawel Jakub Dawidek 	flags &= ~O_NONBLOCK;
333933728eeSPawel Jakub Dawidek 	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
334933728eeSPawel Jakub Dawidek 		if (error == 0)
335933728eeSPawel Jakub Dawidek 			error = errno;
336933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno,
337933728eeSPawel Jakub Dawidek 		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
338933728eeSPawel Jakub Dawidek 	}
339933728eeSPawel Jakub Dawidek 	return (error);
340933728eeSPawel Jakub Dawidek }
341933728eeSPawel Jakub Dawidek 
342933728eeSPawel Jakub Dawidek static int
343bdbd046bSPawel Jakub Dawidek tcp_connect_wait(void *ctx, int timeout)
344933728eeSPawel Jakub Dawidek {
345bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
346933728eeSPawel Jakub Dawidek 	struct timeval tv;
347933728eeSPawel Jakub Dawidek 	fd_set fdset;
348933728eeSPawel Jakub Dawidek 	socklen_t esize;
349933728eeSPawel Jakub Dawidek 	int error, flags, ret;
350933728eeSPawel Jakub Dawidek 
351933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
352bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
353bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
354933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
355933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(timeout >= 0);
356933728eeSPawel Jakub Dawidek 
357933728eeSPawel Jakub Dawidek 	tv.tv_sec = timeout;
358933728eeSPawel Jakub Dawidek 	tv.tv_usec = 0;
359933728eeSPawel Jakub Dawidek again:
360933728eeSPawel Jakub Dawidek 	FD_ZERO(&fdset);
361933728eeSPawel Jakub Dawidek 	FD_SET(tctx->tc_fd, &fdset);
362933728eeSPawel Jakub Dawidek 	ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
363933728eeSPawel Jakub Dawidek 	if (ret == 0) {
364933728eeSPawel Jakub Dawidek 		error = ETIMEDOUT;
365933728eeSPawel Jakub Dawidek 		goto done;
366933728eeSPawel Jakub Dawidek 	} else if (ret == -1) {
367933728eeSPawel Jakub Dawidek 		if (errno == EINTR)
368933728eeSPawel Jakub Dawidek 			goto again;
369933728eeSPawel Jakub Dawidek 		error = errno;
370933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
371933728eeSPawel Jakub Dawidek 		goto done;
372933728eeSPawel Jakub Dawidek 	}
373933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(ret > 0);
374933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset));
375933728eeSPawel Jakub Dawidek 	esize = sizeof(error);
376933728eeSPawel Jakub Dawidek 	if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
377933728eeSPawel Jakub Dawidek 	    &esize) == -1) {
378933728eeSPawel Jakub Dawidek 		error = errno;
379933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno,
380933728eeSPawel Jakub Dawidek 		    "getsockopt(SO_ERROR) failed");
381933728eeSPawel Jakub Dawidek 		goto done;
382933728eeSPawel Jakub Dawidek 	}
383933728eeSPawel Jakub Dawidek 	if (error != 0) {
384933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, error,
385933728eeSPawel Jakub Dawidek 		    "getsockopt(SO_ERROR) returned error");
386933728eeSPawel Jakub Dawidek 		goto done;
387933728eeSPawel Jakub Dawidek 	}
388933728eeSPawel Jakub Dawidek 	error = 0;
389933728eeSPawel Jakub Dawidek done:
390933728eeSPawel Jakub Dawidek 	flags = fcntl(tctx->tc_fd, F_GETFL);
391933728eeSPawel Jakub Dawidek 	if (flags == -1) {
392933728eeSPawel Jakub Dawidek 		if (error == 0)
393933728eeSPawel Jakub Dawidek 			error = errno;
394933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
395933728eeSPawel Jakub Dawidek 		return (error);
396933728eeSPawel Jakub Dawidek 	}
397933728eeSPawel Jakub Dawidek 	flags &= ~O_NONBLOCK;
398933728eeSPawel Jakub Dawidek 	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
399933728eeSPawel Jakub Dawidek 		if (error == 0)
400933728eeSPawel Jakub Dawidek 			error = errno;
401933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno,
402933728eeSPawel Jakub Dawidek 		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
403933728eeSPawel Jakub Dawidek 	}
404933728eeSPawel Jakub Dawidek 	return (error);
405933728eeSPawel Jakub Dawidek }
406933728eeSPawel Jakub Dawidek 
407933728eeSPawel Jakub Dawidek static int
408bdbd046bSPawel Jakub Dawidek tcp_server(const char *addr, void **ctxp)
409933728eeSPawel Jakub Dawidek {
410bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx;
411933728eeSPawel Jakub Dawidek 	int ret, val;
412933728eeSPawel Jakub Dawidek 
413bdbd046bSPawel Jakub Dawidek 	ret = tcp_setup_new(addr, TCP_SIDE_SERVER_LISTEN, ctxp);
414933728eeSPawel Jakub Dawidek 	if (ret != 0)
415933728eeSPawel Jakub Dawidek 		return (ret);
416933728eeSPawel Jakub Dawidek 
417933728eeSPawel Jakub Dawidek 	tctx = *ctxp;
418933728eeSPawel Jakub Dawidek 
419933728eeSPawel Jakub Dawidek 	val = 1;
420933728eeSPawel Jakub Dawidek 	/* Ignore failure. */
421933728eeSPawel Jakub Dawidek 	(void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
422933728eeSPawel Jakub Dawidek 	   sizeof(val));
423933728eeSPawel Jakub Dawidek 
424*dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
425933728eeSPawel Jakub Dawidek 
426*dc18c8aeSPawel Jakub Dawidek 	if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
427*dc18c8aeSPawel Jakub Dawidek 	    tctx->tc_sa.ss_len) < 0) {
428933728eeSPawel Jakub Dawidek 		ret = errno;
429bdbd046bSPawel Jakub Dawidek 		tcp_close(tctx);
430933728eeSPawel Jakub Dawidek 		return (ret);
431933728eeSPawel Jakub Dawidek 	}
432933728eeSPawel Jakub Dawidek 	if (listen(tctx->tc_fd, 8) < 0) {
433933728eeSPawel Jakub Dawidek 		ret = errno;
434bdbd046bSPawel Jakub Dawidek 		tcp_close(tctx);
435933728eeSPawel Jakub Dawidek 		return (ret);
436933728eeSPawel Jakub Dawidek 	}
437933728eeSPawel Jakub Dawidek 
438933728eeSPawel Jakub Dawidek 	return (0);
439933728eeSPawel Jakub Dawidek }
440933728eeSPawel Jakub Dawidek 
441933728eeSPawel Jakub Dawidek static int
442bdbd046bSPawel Jakub Dawidek tcp_accept(void *ctx, void **newctxp)
443933728eeSPawel Jakub Dawidek {
444bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
445bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *newtctx;
446933728eeSPawel Jakub Dawidek 	socklen_t fromlen;
447933728eeSPawel Jakub Dawidek 	int ret;
448933728eeSPawel Jakub Dawidek 
449933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
450bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
451bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN);
452933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
453*dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
454933728eeSPawel Jakub Dawidek 
455933728eeSPawel Jakub Dawidek 	newtctx = malloc(sizeof(*newtctx));
456933728eeSPawel Jakub Dawidek 	if (newtctx == NULL)
457933728eeSPawel Jakub Dawidek 		return (errno);
458933728eeSPawel Jakub Dawidek 
459*dc18c8aeSPawel Jakub Dawidek 	fromlen = tctx->tc_sa.ss_len;
460*dc18c8aeSPawel Jakub Dawidek 	newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
461933728eeSPawel Jakub Dawidek 	    &fromlen);
462933728eeSPawel Jakub Dawidek 	if (newtctx->tc_fd < 0) {
463933728eeSPawel Jakub Dawidek 		ret = errno;
464933728eeSPawel Jakub Dawidek 		free(newtctx);
465933728eeSPawel Jakub Dawidek 		return (ret);
466933728eeSPawel Jakub Dawidek 	}
467933728eeSPawel Jakub Dawidek 
468bdbd046bSPawel Jakub Dawidek 	newtctx->tc_side = TCP_SIDE_SERVER_WORK;
469bdbd046bSPawel Jakub Dawidek 	newtctx->tc_magic = TCP_CTX_MAGIC;
470933728eeSPawel Jakub Dawidek 	*newctxp = newtctx;
471933728eeSPawel Jakub Dawidek 
472933728eeSPawel Jakub Dawidek 	return (0);
473933728eeSPawel Jakub Dawidek }
474933728eeSPawel Jakub Dawidek 
475933728eeSPawel Jakub Dawidek static int
476bdbd046bSPawel Jakub Dawidek tcp_wrap(int fd, bool client, void **ctxp)
477933728eeSPawel Jakub Dawidek {
478933728eeSPawel Jakub Dawidek 
479bdbd046bSPawel Jakub Dawidek 	return (tcp_setup_wrap(fd,
480bdbd046bSPawel Jakub Dawidek 	    client ? TCP_SIDE_CLIENT : TCP_SIDE_SERVER_WORK, ctxp));
481933728eeSPawel Jakub Dawidek }
482933728eeSPawel Jakub Dawidek 
483933728eeSPawel Jakub Dawidek static int
484bdbd046bSPawel Jakub Dawidek tcp_send(void *ctx, const unsigned char *data, size_t size, int fd)
485933728eeSPawel Jakub Dawidek {
486bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
487933728eeSPawel Jakub Dawidek 
488933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
489bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
490933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
491933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(fd == -1);
492933728eeSPawel Jakub Dawidek 
493933728eeSPawel Jakub Dawidek 	return (proto_common_send(tctx->tc_fd, data, size, -1));
494933728eeSPawel Jakub Dawidek }
495933728eeSPawel Jakub Dawidek 
496933728eeSPawel Jakub Dawidek static int
497bdbd046bSPawel Jakub Dawidek tcp_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
498933728eeSPawel Jakub Dawidek {
499bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
500933728eeSPawel Jakub Dawidek 
501933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
502bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
503933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
504933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(fdp == NULL);
505933728eeSPawel Jakub Dawidek 
506933728eeSPawel Jakub Dawidek 	return (proto_common_recv(tctx->tc_fd, data, size, NULL));
507933728eeSPawel Jakub Dawidek }
508933728eeSPawel Jakub Dawidek 
509933728eeSPawel Jakub Dawidek static int
510bdbd046bSPawel Jakub Dawidek tcp_descriptor(const void *ctx)
511933728eeSPawel Jakub Dawidek {
512bdbd046bSPawel Jakub Dawidek 	const struct tcp_ctx *tctx = ctx;
513933728eeSPawel Jakub Dawidek 
514933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
515bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
516933728eeSPawel Jakub Dawidek 
517933728eeSPawel Jakub Dawidek 	return (tctx->tc_fd);
518933728eeSPawel Jakub Dawidek }
519933728eeSPawel Jakub Dawidek 
520933728eeSPawel Jakub Dawidek static bool
521bdbd046bSPawel Jakub Dawidek tcp_address_match(const void *ctx, const char *addr)
522933728eeSPawel Jakub Dawidek {
523bdbd046bSPawel Jakub Dawidek 	const struct tcp_ctx *tctx = ctx;
524*dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage sa1, sa2;
525*dc18c8aeSPawel Jakub Dawidek 	socklen_t salen;
526933728eeSPawel Jakub Dawidek 
527933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
528bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
529933728eeSPawel Jakub Dawidek 
530*dc18c8aeSPawel Jakub Dawidek 	if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sa1) != 0)
531933728eeSPawel Jakub Dawidek 		return (false);
532933728eeSPawel Jakub Dawidek 
533*dc18c8aeSPawel Jakub Dawidek 	salen = sizeof(sa2);
534*dc18c8aeSPawel Jakub Dawidek 	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) < 0)
535933728eeSPawel Jakub Dawidek 		return (false);
536933728eeSPawel Jakub Dawidek 
537*dc18c8aeSPawel Jakub Dawidek 	if (sa1.ss_family != sa2.ss_family || sa1.ss_len != sa2.ss_len)
538*dc18c8aeSPawel Jakub Dawidek 		return (false);
539*dc18c8aeSPawel Jakub Dawidek 
540*dc18c8aeSPawel Jakub Dawidek 	switch (sa1.ss_family) {
541*dc18c8aeSPawel Jakub Dawidek 	case AF_INET:
542*dc18c8aeSPawel Jakub Dawidek 	    {
543*dc18c8aeSPawel Jakub Dawidek 		struct sockaddr_in *sin1, *sin2;
544*dc18c8aeSPawel Jakub Dawidek 
545*dc18c8aeSPawel Jakub Dawidek 		sin1 = (struct sockaddr_in *)&sa1;
546*dc18c8aeSPawel Jakub Dawidek 		sin2 = (struct sockaddr_in *)&sa2;
547*dc18c8aeSPawel Jakub Dawidek 
548*dc18c8aeSPawel Jakub Dawidek 		return (memcmp(&sin1->sin_addr, &sin2->sin_addr,
549*dc18c8aeSPawel Jakub Dawidek 		    sizeof(sin1->sin_addr)) == 0);
550*dc18c8aeSPawel Jakub Dawidek 	    }
551*dc18c8aeSPawel Jakub Dawidek 	case AF_INET6:
552*dc18c8aeSPawel Jakub Dawidek 	    {
553*dc18c8aeSPawel Jakub Dawidek 		struct sockaddr_in6 *sin1, *sin2;
554*dc18c8aeSPawel Jakub Dawidek 
555*dc18c8aeSPawel Jakub Dawidek 		sin1 = (struct sockaddr_in6 *)&sa1;
556*dc18c8aeSPawel Jakub Dawidek 		sin2 = (struct sockaddr_in6 *)&sa2;
557*dc18c8aeSPawel Jakub Dawidek 
558*dc18c8aeSPawel Jakub Dawidek 		return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
559*dc18c8aeSPawel Jakub Dawidek 		    sizeof(sin1->sin6_addr)) == 0);
560*dc18c8aeSPawel Jakub Dawidek 	    }
561*dc18c8aeSPawel Jakub Dawidek 	default:
562*dc18c8aeSPawel Jakub Dawidek 		return (false);
563*dc18c8aeSPawel Jakub Dawidek 	}
564933728eeSPawel Jakub Dawidek }
565933728eeSPawel Jakub Dawidek 
566933728eeSPawel Jakub Dawidek static void
567bdbd046bSPawel Jakub Dawidek tcp_local_address(const void *ctx, char *addr, size_t size)
568933728eeSPawel Jakub Dawidek {
569bdbd046bSPawel Jakub Dawidek 	const struct tcp_ctx *tctx = ctx;
570*dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage sa;
571*dc18c8aeSPawel Jakub Dawidek 	socklen_t salen;
572933728eeSPawel Jakub Dawidek 
573933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
574bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
575933728eeSPawel Jakub Dawidek 
576*dc18c8aeSPawel Jakub Dawidek 	salen = sizeof(sa);
577*dc18c8aeSPawel Jakub Dawidek 	if (getsockname(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) {
578933728eeSPawel Jakub Dawidek 		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
579933728eeSPawel Jakub Dawidek 		return;
580933728eeSPawel Jakub Dawidek 	}
581*dc18c8aeSPawel Jakub Dawidek 	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
582933728eeSPawel Jakub Dawidek }
583933728eeSPawel Jakub Dawidek 
584933728eeSPawel Jakub Dawidek static void
585bdbd046bSPawel Jakub Dawidek tcp_remote_address(const void *ctx, char *addr, size_t size)
586933728eeSPawel Jakub Dawidek {
587bdbd046bSPawel Jakub Dawidek 	const struct tcp_ctx *tctx = ctx;
588*dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage sa;
589*dc18c8aeSPawel Jakub Dawidek 	socklen_t salen;
590933728eeSPawel Jakub Dawidek 
591933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
592bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
593933728eeSPawel Jakub Dawidek 
594*dc18c8aeSPawel Jakub Dawidek 	salen = sizeof(sa);
595*dc18c8aeSPawel Jakub Dawidek 	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) {
596933728eeSPawel Jakub Dawidek 		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
597933728eeSPawel Jakub Dawidek 		return;
598933728eeSPawel Jakub Dawidek 	}
599*dc18c8aeSPawel Jakub Dawidek 	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
600933728eeSPawel Jakub Dawidek }
601933728eeSPawel Jakub Dawidek 
602933728eeSPawel Jakub Dawidek static void
603bdbd046bSPawel Jakub Dawidek tcp_close(void *ctx)
604933728eeSPawel Jakub Dawidek {
605bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
606933728eeSPawel Jakub Dawidek 
607933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
608bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
609933728eeSPawel Jakub Dawidek 
610933728eeSPawel Jakub Dawidek 	if (tctx->tc_fd >= 0)
611933728eeSPawel Jakub Dawidek 		close(tctx->tc_fd);
612933728eeSPawel Jakub Dawidek 	tctx->tc_magic = 0;
613933728eeSPawel Jakub Dawidek 	free(tctx);
614933728eeSPawel Jakub Dawidek }
615933728eeSPawel Jakub Dawidek 
616bdbd046bSPawel Jakub Dawidek static struct proto tcp_proto = {
617bdbd046bSPawel Jakub Dawidek 	.prt_name = "tcp",
618bdbd046bSPawel Jakub Dawidek 	.prt_client = tcp_client,
619bdbd046bSPawel Jakub Dawidek 	.prt_connect = tcp_connect,
620bdbd046bSPawel Jakub Dawidek 	.prt_connect_wait = tcp_connect_wait,
621bdbd046bSPawel Jakub Dawidek 	.prt_server = tcp_server,
622bdbd046bSPawel Jakub Dawidek 	.prt_accept = tcp_accept,
623bdbd046bSPawel Jakub Dawidek 	.prt_wrap = tcp_wrap,
624bdbd046bSPawel Jakub Dawidek 	.prt_send = tcp_send,
625bdbd046bSPawel Jakub Dawidek 	.prt_recv = tcp_recv,
626bdbd046bSPawel Jakub Dawidek 	.prt_descriptor = tcp_descriptor,
627bdbd046bSPawel Jakub Dawidek 	.prt_address_match = tcp_address_match,
628bdbd046bSPawel Jakub Dawidek 	.prt_local_address = tcp_local_address,
629bdbd046bSPawel Jakub Dawidek 	.prt_remote_address = tcp_remote_address,
630bdbd046bSPawel Jakub Dawidek 	.prt_close = tcp_close
631933728eeSPawel Jakub Dawidek };
632933728eeSPawel Jakub Dawidek 
633933728eeSPawel Jakub Dawidek static __constructor void
634bdbd046bSPawel Jakub Dawidek tcp_ctor(void)
635933728eeSPawel Jakub Dawidek {
636933728eeSPawel Jakub Dawidek 
637bdbd046bSPawel Jakub Dawidek 	proto_register(&tcp_proto, true);
638933728eeSPawel Jakub Dawidek }
639