xref: /freebsd/sbin/hastd/proto_tcp.c (revision 4d846d260e2b9a3d4d0a701462568268cbfe7a5b)
1933728eeSPawel Jakub Dawidek /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
4933728eeSPawel Jakub Dawidek  * Copyright (c) 2009-2010 The FreeBSD Foundation
5dc18c8aeSPawel Jakub Dawidek  * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
6933728eeSPawel Jakub Dawidek  * All rights reserved.
7933728eeSPawel Jakub Dawidek  *
8933728eeSPawel Jakub Dawidek  * This software was developed by Pawel Jakub Dawidek under sponsorship from
9933728eeSPawel Jakub Dawidek  * the FreeBSD Foundation.
10933728eeSPawel Jakub Dawidek  *
11933728eeSPawel Jakub Dawidek  * Redistribution and use in source and binary forms, with or without
12933728eeSPawel Jakub Dawidek  * modification, are permitted provided that the following conditions
13933728eeSPawel Jakub Dawidek  * are met:
14933728eeSPawel Jakub Dawidek  * 1. Redistributions of source code must retain the above copyright
15933728eeSPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer.
16933728eeSPawel Jakub Dawidek  * 2. Redistributions in binary form must reproduce the above copyright
17933728eeSPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer in the
18933728eeSPawel Jakub Dawidek  *    documentation and/or other materials provided with the distribution.
19933728eeSPawel Jakub Dawidek  *
20933728eeSPawel Jakub Dawidek  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21933728eeSPawel Jakub Dawidek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22933728eeSPawel Jakub Dawidek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23933728eeSPawel Jakub Dawidek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24933728eeSPawel Jakub Dawidek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25933728eeSPawel Jakub Dawidek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26933728eeSPawel Jakub Dawidek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27933728eeSPawel Jakub Dawidek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28933728eeSPawel Jakub Dawidek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29933728eeSPawel Jakub Dawidek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30933728eeSPawel Jakub Dawidek  * SUCH DAMAGE.
31933728eeSPawel Jakub Dawidek  */
32933728eeSPawel Jakub Dawidek 
33933728eeSPawel Jakub Dawidek #include <sys/cdefs.h>
34933728eeSPawel Jakub Dawidek __FBSDID("$FreeBSD$");
35933728eeSPawel Jakub Dawidek 
36933728eeSPawel Jakub Dawidek #include <sys/param.h>	/* MAXHOSTNAMELEN */
37933728eeSPawel Jakub Dawidek #include <sys/socket.h>
38933728eeSPawel Jakub Dawidek 
39933728eeSPawel Jakub Dawidek #include <arpa/inet.h>
40933728eeSPawel Jakub Dawidek 
41933728eeSPawel Jakub Dawidek #include <netinet/in.h>
42933728eeSPawel Jakub Dawidek #include <netinet/tcp.h>
43933728eeSPawel Jakub Dawidek 
44933728eeSPawel Jakub Dawidek #include <errno.h>
45933728eeSPawel Jakub Dawidek #include <fcntl.h>
46933728eeSPawel Jakub Dawidek #include <netdb.h>
47933728eeSPawel Jakub Dawidek #include <stdbool.h>
48933728eeSPawel Jakub Dawidek #include <stdint.h>
49933728eeSPawel Jakub Dawidek #include <stdio.h>
50933728eeSPawel Jakub Dawidek #include <string.h>
51933728eeSPawel Jakub Dawidek #include <unistd.h>
52933728eeSPawel Jakub Dawidek 
53933728eeSPawel Jakub Dawidek #include "pjdlog.h"
54933728eeSPawel Jakub Dawidek #include "proto_impl.h"
55933728eeSPawel Jakub Dawidek #include "subr.h"
56933728eeSPawel Jakub Dawidek 
57dc18c8aeSPawel Jakub Dawidek #define	TCP_CTX_MAGIC	0x7c41c
58bdbd046bSPawel Jakub Dawidek struct tcp_ctx {
59933728eeSPawel Jakub Dawidek 	int			tc_magic;
60dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage	tc_sa;
61933728eeSPawel Jakub Dawidek 	int			tc_fd;
62933728eeSPawel Jakub Dawidek 	int			tc_side;
63bdbd046bSPawel Jakub Dawidek #define	TCP_SIDE_CLIENT		0
64bdbd046bSPawel Jakub Dawidek #define	TCP_SIDE_SERVER_LISTEN	1
65bdbd046bSPawel Jakub Dawidek #define	TCP_SIDE_SERVER_WORK	2
66933728eeSPawel Jakub Dawidek };
67933728eeSPawel Jakub Dawidek 
68bdbd046bSPawel Jakub Dawidek static int tcp_connect_wait(void *ctx, int timeout);
69bdbd046bSPawel Jakub Dawidek static void tcp_close(void *ctx);
70933728eeSPawel Jakub Dawidek 
71933728eeSPawel Jakub Dawidek /*
72933728eeSPawel Jakub Dawidek  * Function converts the given string to unsigned number.
73933728eeSPawel Jakub Dawidek  */
74933728eeSPawel Jakub Dawidek static int
75933728eeSPawel Jakub Dawidek numfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump)
76933728eeSPawel Jakub Dawidek {
77933728eeSPawel Jakub Dawidek 	intmax_t digit, num;
78933728eeSPawel Jakub Dawidek 
79933728eeSPawel Jakub Dawidek 	if (str[0] == '\0')
80933728eeSPawel Jakub Dawidek 		goto invalid;	/* Empty string. */
81933728eeSPawel Jakub Dawidek 	num = 0;
82933728eeSPawel Jakub Dawidek 	for (; *str != '\0'; str++) {
83933728eeSPawel Jakub Dawidek 		if (*str < '0' || *str > '9')
84933728eeSPawel Jakub Dawidek 			goto invalid;	/* Non-digit character. */
85933728eeSPawel Jakub Dawidek 		digit = *str - '0';
86933728eeSPawel Jakub Dawidek 		if (num > num * 10 + digit)
87933728eeSPawel Jakub Dawidek 			goto invalid;	/* Overflow. */
88933728eeSPawel Jakub Dawidek 		num = num * 10 + digit;
89933728eeSPawel Jakub Dawidek 		if (num > maxnum)
90933728eeSPawel Jakub Dawidek 			goto invalid;	/* Too big. */
91933728eeSPawel Jakub Dawidek 	}
92933728eeSPawel Jakub Dawidek 	if (num < minnum)
93933728eeSPawel Jakub Dawidek 		goto invalid;	/* Too small. */
94933728eeSPawel Jakub Dawidek 	*nump = num;
95933728eeSPawel Jakub Dawidek 	return (0);
96933728eeSPawel Jakub Dawidek invalid:
97933728eeSPawel Jakub Dawidek 	errno = EINVAL;
98933728eeSPawel Jakub Dawidek 	return (-1);
99933728eeSPawel Jakub Dawidek }
100933728eeSPawel Jakub Dawidek 
101933728eeSPawel Jakub Dawidek static int
102dc18c8aeSPawel Jakub Dawidek tcp_addr(const char *addr, int defport, struct sockaddr_storage *sap)
103933728eeSPawel Jakub Dawidek {
104dc18c8aeSPawel Jakub Dawidek 	char iporhost[MAXHOSTNAMELEN], portstr[6];
105dc18c8aeSPawel Jakub Dawidek 	struct addrinfo hints;
106dc18c8aeSPawel Jakub Dawidek 	struct addrinfo *res;
107933728eeSPawel Jakub Dawidek 	const char *pp;
108dc18c8aeSPawel Jakub Dawidek 	intmax_t port;
109933728eeSPawel Jakub Dawidek 	size_t size;
110dc18c8aeSPawel Jakub Dawidek 	int error;
111933728eeSPawel Jakub Dawidek 
112933728eeSPawel Jakub Dawidek 	if (addr == NULL)
113933728eeSPawel Jakub Dawidek 		return (-1);
114933728eeSPawel Jakub Dawidek 
115dc18c8aeSPawel Jakub Dawidek 	bzero(&hints, sizeof(hints));
116dc18c8aeSPawel Jakub Dawidek 	hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
117dc18c8aeSPawel Jakub Dawidek 	hints.ai_family = PF_UNSPEC;
118dc18c8aeSPawel Jakub Dawidek 	hints.ai_socktype = SOCK_STREAM;
119dc18c8aeSPawel Jakub Dawidek 	hints.ai_protocol = IPPROTO_TCP;
120dc18c8aeSPawel Jakub Dawidek 
121dc18c8aeSPawel Jakub Dawidek 	if (strncasecmp(addr, "tcp4://", 7) == 0) {
122933728eeSPawel Jakub Dawidek 		addr += 7;
123dc18c8aeSPawel Jakub Dawidek 		hints.ai_family = PF_INET;
124dc18c8aeSPawel Jakub Dawidek 	} else if (strncasecmp(addr, "tcp6://", 7) == 0) {
125dc18c8aeSPawel Jakub Dawidek 		addr += 7;
126dc18c8aeSPawel Jakub Dawidek 		hints.ai_family = PF_INET6;
127dc18c8aeSPawel Jakub Dawidek 	} else if (strncasecmp(addr, "tcp://", 6) == 0) {
128933728eeSPawel Jakub Dawidek 		addr += 6;
129dc18c8aeSPawel Jakub Dawidek 	} else {
130933728eeSPawel Jakub Dawidek 		/*
131bdbd046bSPawel Jakub Dawidek 		 * Because TCP is the default assume IP or host is given without
132933728eeSPawel Jakub Dawidek 		 * prefix.
133933728eeSPawel Jakub Dawidek 		 */
134933728eeSPawel Jakub Dawidek 	}
135933728eeSPawel Jakub Dawidek 
136dc18c8aeSPawel Jakub Dawidek 	/*
137dc18c8aeSPawel Jakub Dawidek 	 * Extract optional port.
138dc18c8aeSPawel Jakub Dawidek 	 * There are three cases to consider.
139dc18c8aeSPawel Jakub Dawidek 	 * 1. hostname with port, eg. freefall.freebsd.org:8457
140dc18c8aeSPawel Jakub Dawidek 	 * 2. IPv4 address with port, eg. 192.168.0.101:8457
141dc18c8aeSPawel Jakub Dawidek 	 * 3. IPv6 address with port, eg. [fe80::1]:8457
142dc18c8aeSPawel Jakub Dawidek 	 * We discover IPv6 address by checking for two colons and if port is
143dc18c8aeSPawel Jakub Dawidek 	 * given, the address has to start with [.
144dc18c8aeSPawel Jakub Dawidek 	 */
145dc18c8aeSPawel Jakub Dawidek 	pp = NULL;
146dc18c8aeSPawel Jakub Dawidek 	if (strchr(addr, ':') != strrchr(addr, ':')) {
147dc18c8aeSPawel Jakub Dawidek 		if (addr[0] == '[')
148933728eeSPawel Jakub Dawidek 			pp = strrchr(addr, ':');
149dc18c8aeSPawel Jakub Dawidek 	} else {
150dc18c8aeSPawel Jakub Dawidek 		pp = strrchr(addr, ':');
151dc18c8aeSPawel Jakub Dawidek 	}
152933728eeSPawel Jakub Dawidek 	if (pp == NULL) {
153933728eeSPawel Jakub Dawidek 		/* Port not given, use the default. */
154dc18c8aeSPawel Jakub Dawidek 		port = defport;
155933728eeSPawel Jakub Dawidek 	} else {
1562b1b224dSPawel Jakub Dawidek 		if (numfromstr(pp + 1, 1, 65535, &port) == -1)
157933728eeSPawel Jakub Dawidek 			return (errno);
158933728eeSPawel Jakub Dawidek 	}
159dc18c8aeSPawel Jakub Dawidek 	(void)snprintf(portstr, sizeof(portstr), "%jd", (intmax_t)port);
160933728eeSPawel Jakub Dawidek 	/* Extract host name or IP address. */
161933728eeSPawel Jakub Dawidek 	if (pp == NULL) {
162933728eeSPawel Jakub Dawidek 		size = sizeof(iporhost);
163933728eeSPawel Jakub Dawidek 		if (strlcpy(iporhost, addr, size) >= size)
164933728eeSPawel Jakub Dawidek 			return (ENAMETOOLONG);
165dc18c8aeSPawel Jakub Dawidek 	} else if (addr[0] == '[' && pp[-1] == ']') {
166dc18c8aeSPawel Jakub Dawidek 		size = (size_t)(pp - addr - 2 + 1);
167dc18c8aeSPawel Jakub Dawidek 		if (size > sizeof(iporhost))
168dc18c8aeSPawel Jakub Dawidek 			return (ENAMETOOLONG);
169dc18c8aeSPawel Jakub Dawidek 		(void)strlcpy(iporhost, addr + 1, size);
170933728eeSPawel Jakub Dawidek 	} else {
171933728eeSPawel Jakub Dawidek 		size = (size_t)(pp - addr + 1);
172933728eeSPawel Jakub Dawidek 		if (size > sizeof(iporhost))
173933728eeSPawel Jakub Dawidek 			return (ENAMETOOLONG);
174933728eeSPawel Jakub Dawidek 		(void)strlcpy(iporhost, addr, size);
175933728eeSPawel Jakub Dawidek 	}
176dc18c8aeSPawel Jakub Dawidek 
177dc18c8aeSPawel Jakub Dawidek 	error = getaddrinfo(iporhost, portstr, &hints, &res);
178dc18c8aeSPawel Jakub Dawidek 	if (error != 0) {
179dc18c8aeSPawel Jakub Dawidek 		pjdlog_debug(1, "getaddrinfo(%s, %s) failed: %s.", iporhost,
180dc18c8aeSPawel Jakub Dawidek 		    portstr, gai_strerror(error));
181933728eeSPawel Jakub Dawidek 		return (EINVAL);
182dc18c8aeSPawel Jakub Dawidek 	}
183dc18c8aeSPawel Jakub Dawidek 	if (res == NULL)
184dc18c8aeSPawel Jakub Dawidek 		return (ENOENT);
185dc18c8aeSPawel Jakub Dawidek 
186dc18c8aeSPawel Jakub Dawidek 	memcpy(sap, res->ai_addr, res->ai_addrlen);
187dc18c8aeSPawel Jakub Dawidek 
188dc18c8aeSPawel Jakub Dawidek 	freeaddrinfo(res);
189933728eeSPawel Jakub Dawidek 
190933728eeSPawel Jakub Dawidek 	return (0);
191933728eeSPawel Jakub Dawidek }
192933728eeSPawel Jakub Dawidek 
193933728eeSPawel Jakub Dawidek static int
194bdbd046bSPawel Jakub Dawidek tcp_setup_new(const char *addr, int side, void **ctxp)
195933728eeSPawel Jakub Dawidek {
196bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx;
197933728eeSPawel Jakub Dawidek 	int ret, nodelay;
198933728eeSPawel Jakub Dawidek 
199933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(addr != NULL);
200bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
201bdbd046bSPawel Jakub Dawidek 	    side == TCP_SIDE_SERVER_LISTEN);
202933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(ctxp != NULL);
203933728eeSPawel Jakub Dawidek 
204933728eeSPawel Jakub Dawidek 	tctx = malloc(sizeof(*tctx));
205933728eeSPawel Jakub Dawidek 	if (tctx == NULL)
206933728eeSPawel Jakub Dawidek 		return (errno);
207933728eeSPawel Jakub Dawidek 
208933728eeSPawel Jakub Dawidek 	/* Parse given address. */
209dc18c8aeSPawel Jakub Dawidek 	if ((ret = tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &tctx->tc_sa)) != 0) {
210933728eeSPawel Jakub Dawidek 		free(tctx);
211933728eeSPawel Jakub Dawidek 		return (ret);
212933728eeSPawel Jakub Dawidek 	}
213933728eeSPawel Jakub Dawidek 
214dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
215933728eeSPawel Jakub Dawidek 
216dc18c8aeSPawel Jakub Dawidek 	tctx->tc_fd = socket(tctx->tc_sa.ss_family, SOCK_STREAM, 0);
217933728eeSPawel Jakub Dawidek 	if (tctx->tc_fd == -1) {
218933728eeSPawel Jakub Dawidek 		ret = errno;
219933728eeSPawel Jakub Dawidek 		free(tctx);
220933728eeSPawel Jakub Dawidek 		return (ret);
221933728eeSPawel Jakub Dawidek 	}
222933728eeSPawel Jakub Dawidek 
223dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
224933728eeSPawel Jakub Dawidek 
225933728eeSPawel Jakub Dawidek 	/* Socket settings. */
226933728eeSPawel Jakub Dawidek 	nodelay = 1;
227933728eeSPawel Jakub Dawidek 	if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay,
228933728eeSPawel Jakub Dawidek 	    sizeof(nodelay)) == -1) {
229933728eeSPawel Jakub Dawidek 		pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY");
230933728eeSPawel Jakub Dawidek 	}
231933728eeSPawel Jakub Dawidek 
232933728eeSPawel Jakub Dawidek 	tctx->tc_side = side;
233bdbd046bSPawel Jakub Dawidek 	tctx->tc_magic = TCP_CTX_MAGIC;
234933728eeSPawel Jakub Dawidek 	*ctxp = tctx;
235933728eeSPawel Jakub Dawidek 
236933728eeSPawel Jakub Dawidek 	return (0);
237933728eeSPawel Jakub Dawidek }
238933728eeSPawel Jakub Dawidek 
239933728eeSPawel Jakub Dawidek static int
240bdbd046bSPawel Jakub Dawidek tcp_setup_wrap(int fd, int side, void **ctxp)
241933728eeSPawel Jakub Dawidek {
242bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx;
243933728eeSPawel Jakub Dawidek 
244933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(fd >= 0);
245bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
246bdbd046bSPawel Jakub Dawidek 	    side == TCP_SIDE_SERVER_WORK);
247933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(ctxp != NULL);
248933728eeSPawel Jakub Dawidek 
249933728eeSPawel Jakub Dawidek 	tctx = malloc(sizeof(*tctx));
250933728eeSPawel Jakub Dawidek 	if (tctx == NULL)
251933728eeSPawel Jakub Dawidek 		return (errno);
252933728eeSPawel Jakub Dawidek 
253933728eeSPawel Jakub Dawidek 	tctx->tc_fd = fd;
254dc18c8aeSPawel Jakub Dawidek 	tctx->tc_sa.ss_family = AF_UNSPEC;
255933728eeSPawel Jakub Dawidek 	tctx->tc_side = side;
256bdbd046bSPawel Jakub Dawidek 	tctx->tc_magic = TCP_CTX_MAGIC;
257933728eeSPawel Jakub Dawidek 	*ctxp = tctx;
258933728eeSPawel Jakub Dawidek 
259933728eeSPawel Jakub Dawidek 	return (0);
260933728eeSPawel Jakub Dawidek }
261933728eeSPawel Jakub Dawidek 
262933728eeSPawel Jakub Dawidek static int
263bdbd046bSPawel Jakub Dawidek tcp_client(const char *srcaddr, const char *dstaddr, void **ctxp)
264933728eeSPawel Jakub Dawidek {
265bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx;
266dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage sa;
267933728eeSPawel Jakub Dawidek 	int ret;
268933728eeSPawel Jakub Dawidek 
269bdbd046bSPawel Jakub Dawidek 	ret = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, ctxp);
270933728eeSPawel Jakub Dawidek 	if (ret != 0)
271933728eeSPawel Jakub Dawidek 		return (ret);
272933728eeSPawel Jakub Dawidek 	tctx = *ctxp;
273933728eeSPawel Jakub Dawidek 	if (srcaddr == NULL)
274933728eeSPawel Jakub Dawidek 		return (0);
275dc18c8aeSPawel Jakub Dawidek 	ret = tcp_addr(srcaddr, 0, &sa);
276933728eeSPawel Jakub Dawidek 	if (ret != 0) {
277bdbd046bSPawel Jakub Dawidek 		tcp_close(tctx);
278933728eeSPawel Jakub Dawidek 		return (ret);
279933728eeSPawel Jakub Dawidek 	}
2802b1b224dSPawel Jakub Dawidek 	if (bind(tctx->tc_fd, (struct sockaddr *)&sa, sa.ss_len) == -1) {
281933728eeSPawel Jakub Dawidek 		ret = errno;
282bdbd046bSPawel Jakub Dawidek 		tcp_close(tctx);
283933728eeSPawel Jakub Dawidek 		return (ret);
284933728eeSPawel Jakub Dawidek 	}
285933728eeSPawel Jakub Dawidek 	return (0);
286933728eeSPawel Jakub Dawidek }
287933728eeSPawel Jakub Dawidek 
288933728eeSPawel Jakub Dawidek static int
289bdbd046bSPawel Jakub Dawidek tcp_connect(void *ctx, int timeout)
290933728eeSPawel Jakub Dawidek {
291bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
292933728eeSPawel Jakub Dawidek 	int error, flags;
293933728eeSPawel Jakub Dawidek 
294933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
295bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
296bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
297933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
298dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
299933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(timeout >= -1);
300933728eeSPawel Jakub Dawidek 
301933728eeSPawel Jakub Dawidek 	flags = fcntl(tctx->tc_fd, F_GETFL);
302933728eeSPawel Jakub Dawidek 	if (flags == -1) {
3031ebc0407SPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
304933728eeSPawel Jakub Dawidek 		return (errno);
305933728eeSPawel Jakub Dawidek 	}
306933728eeSPawel Jakub Dawidek 	/*
307933728eeSPawel Jakub Dawidek 	 * We make socket non-blocking so we can handle connection timeout
308933728eeSPawel Jakub Dawidek 	 * manually.
309933728eeSPawel Jakub Dawidek 	 */
310933728eeSPawel Jakub Dawidek 	flags |= O_NONBLOCK;
311933728eeSPawel Jakub Dawidek 	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
3121ebc0407SPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno,
3131ebc0407SPawel Jakub Dawidek 		    "fcntl(F_SETFL, O_NONBLOCK) failed");
314933728eeSPawel Jakub Dawidek 		return (errno);
315933728eeSPawel Jakub Dawidek 	}
316933728eeSPawel Jakub Dawidek 
317dc18c8aeSPawel Jakub Dawidek 	if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
318dc18c8aeSPawel Jakub Dawidek 	    tctx->tc_sa.ss_len) == 0) {
319933728eeSPawel Jakub Dawidek 		if (timeout == -1)
320933728eeSPawel Jakub Dawidek 			return (0);
321933728eeSPawel Jakub Dawidek 		error = 0;
322933728eeSPawel Jakub Dawidek 		goto done;
323933728eeSPawel Jakub Dawidek 	}
324933728eeSPawel Jakub Dawidek 	if (errno != EINPROGRESS) {
325933728eeSPawel Jakub Dawidek 		error = errno;
326933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
327933728eeSPawel Jakub Dawidek 		goto done;
328933728eeSPawel Jakub Dawidek 	}
329933728eeSPawel Jakub Dawidek 	if (timeout == -1)
330933728eeSPawel Jakub Dawidek 		return (0);
331bdbd046bSPawel Jakub Dawidek 	return (tcp_connect_wait(ctx, timeout));
332933728eeSPawel Jakub Dawidek done:
333933728eeSPawel Jakub Dawidek 	flags &= ~O_NONBLOCK;
334933728eeSPawel Jakub Dawidek 	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
335933728eeSPawel Jakub Dawidek 		if (error == 0)
336933728eeSPawel Jakub Dawidek 			error = errno;
337933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno,
338933728eeSPawel Jakub Dawidek 		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
339933728eeSPawel Jakub Dawidek 	}
340933728eeSPawel Jakub Dawidek 	return (error);
341933728eeSPawel Jakub Dawidek }
342933728eeSPawel Jakub Dawidek 
343933728eeSPawel Jakub Dawidek static int
344bdbd046bSPawel Jakub Dawidek tcp_connect_wait(void *ctx, int timeout)
345933728eeSPawel Jakub Dawidek {
346bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
347933728eeSPawel Jakub Dawidek 	struct timeval tv;
348933728eeSPawel Jakub Dawidek 	fd_set fdset;
349933728eeSPawel Jakub Dawidek 	socklen_t esize;
350933728eeSPawel Jakub Dawidek 	int error, flags, ret;
351933728eeSPawel Jakub Dawidek 
352933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
353bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
354bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
355933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
356933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(timeout >= 0);
357933728eeSPawel Jakub Dawidek 
358933728eeSPawel Jakub Dawidek 	tv.tv_sec = timeout;
359933728eeSPawel Jakub Dawidek 	tv.tv_usec = 0;
360933728eeSPawel Jakub Dawidek again:
361933728eeSPawel Jakub Dawidek 	FD_ZERO(&fdset);
362933728eeSPawel Jakub Dawidek 	FD_SET(tctx->tc_fd, &fdset);
363933728eeSPawel Jakub Dawidek 	ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
364933728eeSPawel Jakub Dawidek 	if (ret == 0) {
365933728eeSPawel Jakub Dawidek 		error = ETIMEDOUT;
366933728eeSPawel Jakub Dawidek 		goto done;
367933728eeSPawel Jakub Dawidek 	} else if (ret == -1) {
368933728eeSPawel Jakub Dawidek 		if (errno == EINTR)
369933728eeSPawel Jakub Dawidek 			goto again;
370933728eeSPawel Jakub Dawidek 		error = errno;
371933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
372933728eeSPawel Jakub Dawidek 		goto done;
373933728eeSPawel Jakub Dawidek 	}
374933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(ret > 0);
375933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset));
376933728eeSPawel Jakub Dawidek 	esize = sizeof(error);
377933728eeSPawel Jakub Dawidek 	if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
378933728eeSPawel Jakub Dawidek 	    &esize) == -1) {
379933728eeSPawel Jakub Dawidek 		error = errno;
380933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno,
381933728eeSPawel Jakub Dawidek 		    "getsockopt(SO_ERROR) failed");
382933728eeSPawel Jakub Dawidek 		goto done;
383933728eeSPawel Jakub Dawidek 	}
384933728eeSPawel Jakub Dawidek 	if (error != 0) {
385933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, error,
386933728eeSPawel Jakub Dawidek 		    "getsockopt(SO_ERROR) returned error");
387933728eeSPawel Jakub Dawidek 		goto done;
388933728eeSPawel Jakub Dawidek 	}
389933728eeSPawel Jakub Dawidek 	error = 0;
390933728eeSPawel Jakub Dawidek done:
391933728eeSPawel Jakub Dawidek 	flags = fcntl(tctx->tc_fd, F_GETFL);
392933728eeSPawel Jakub Dawidek 	if (flags == -1) {
393933728eeSPawel Jakub Dawidek 		if (error == 0)
394933728eeSPawel Jakub Dawidek 			error = errno;
395933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
396933728eeSPawel Jakub Dawidek 		return (error);
397933728eeSPawel Jakub Dawidek 	}
398933728eeSPawel Jakub Dawidek 	flags &= ~O_NONBLOCK;
399933728eeSPawel Jakub Dawidek 	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
400933728eeSPawel Jakub Dawidek 		if (error == 0)
401933728eeSPawel Jakub Dawidek 			error = errno;
402933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno,
403933728eeSPawel Jakub Dawidek 		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
404933728eeSPawel Jakub Dawidek 	}
405933728eeSPawel Jakub Dawidek 	return (error);
406933728eeSPawel Jakub Dawidek }
407933728eeSPawel Jakub Dawidek 
408933728eeSPawel Jakub Dawidek static int
409bdbd046bSPawel Jakub Dawidek tcp_server(const char *addr, void **ctxp)
410933728eeSPawel Jakub Dawidek {
411bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx;
412933728eeSPawel Jakub Dawidek 	int ret, val;
413933728eeSPawel Jakub Dawidek 
414bdbd046bSPawel Jakub Dawidek 	ret = tcp_setup_new(addr, TCP_SIDE_SERVER_LISTEN, ctxp);
415933728eeSPawel Jakub Dawidek 	if (ret != 0)
416933728eeSPawel Jakub Dawidek 		return (ret);
417933728eeSPawel Jakub Dawidek 
418933728eeSPawel Jakub Dawidek 	tctx = *ctxp;
419933728eeSPawel Jakub Dawidek 
420933728eeSPawel Jakub Dawidek 	val = 1;
421933728eeSPawel Jakub Dawidek 	/* Ignore failure. */
422933728eeSPawel Jakub Dawidek 	(void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
423933728eeSPawel Jakub Dawidek 	   sizeof(val));
424933728eeSPawel Jakub Dawidek 
425dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
426933728eeSPawel Jakub Dawidek 
427dc18c8aeSPawel Jakub Dawidek 	if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
4282b1b224dSPawel Jakub Dawidek 	    tctx->tc_sa.ss_len) == -1) {
429933728eeSPawel Jakub Dawidek 		ret = errno;
430bdbd046bSPawel Jakub Dawidek 		tcp_close(tctx);
431933728eeSPawel Jakub Dawidek 		return (ret);
432933728eeSPawel Jakub Dawidek 	}
4332b1b224dSPawel Jakub Dawidek 	if (listen(tctx->tc_fd, 8) == -1) {
434933728eeSPawel Jakub Dawidek 		ret = errno;
435bdbd046bSPawel Jakub Dawidek 		tcp_close(tctx);
436933728eeSPawel Jakub Dawidek 		return (ret);
437933728eeSPawel Jakub Dawidek 	}
438933728eeSPawel Jakub Dawidek 
439933728eeSPawel Jakub Dawidek 	return (0);
440933728eeSPawel Jakub Dawidek }
441933728eeSPawel Jakub Dawidek 
442933728eeSPawel Jakub Dawidek static int
443bdbd046bSPawel Jakub Dawidek tcp_accept(void *ctx, void **newctxp)
444933728eeSPawel Jakub Dawidek {
445bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
446bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *newtctx;
447933728eeSPawel Jakub Dawidek 	socklen_t fromlen;
448933728eeSPawel Jakub Dawidek 	int ret;
449933728eeSPawel Jakub Dawidek 
450933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
451bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
452bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN);
453933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
454dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
455933728eeSPawel Jakub Dawidek 
456933728eeSPawel Jakub Dawidek 	newtctx = malloc(sizeof(*newtctx));
457933728eeSPawel Jakub Dawidek 	if (newtctx == NULL)
458933728eeSPawel Jakub Dawidek 		return (errno);
459933728eeSPawel Jakub Dawidek 
460dc18c8aeSPawel Jakub Dawidek 	fromlen = tctx->tc_sa.ss_len;
461dc18c8aeSPawel Jakub Dawidek 	newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
462933728eeSPawel Jakub Dawidek 	    &fromlen);
4632b1b224dSPawel Jakub Dawidek 	if (newtctx->tc_fd == -1) {
464933728eeSPawel Jakub Dawidek 		ret = errno;
465933728eeSPawel Jakub Dawidek 		free(newtctx);
466933728eeSPawel Jakub Dawidek 		return (ret);
467933728eeSPawel Jakub Dawidek 	}
468933728eeSPawel Jakub Dawidek 
469bdbd046bSPawel Jakub Dawidek 	newtctx->tc_side = TCP_SIDE_SERVER_WORK;
470bdbd046bSPawel Jakub Dawidek 	newtctx->tc_magic = TCP_CTX_MAGIC;
471933728eeSPawel Jakub Dawidek 	*newctxp = newtctx;
472933728eeSPawel Jakub Dawidek 
473933728eeSPawel Jakub Dawidek 	return (0);
474933728eeSPawel Jakub Dawidek }
475933728eeSPawel Jakub Dawidek 
476933728eeSPawel Jakub Dawidek static int
477bdbd046bSPawel Jakub Dawidek tcp_wrap(int fd, bool client, void **ctxp)
478933728eeSPawel Jakub Dawidek {
479933728eeSPawel Jakub Dawidek 
480bdbd046bSPawel Jakub Dawidek 	return (tcp_setup_wrap(fd,
481bdbd046bSPawel Jakub Dawidek 	    client ? TCP_SIDE_CLIENT : TCP_SIDE_SERVER_WORK, ctxp));
482933728eeSPawel Jakub Dawidek }
483933728eeSPawel Jakub Dawidek 
484933728eeSPawel Jakub Dawidek static int
485bdbd046bSPawel Jakub Dawidek tcp_send(void *ctx, const unsigned char *data, size_t size, int fd)
486933728eeSPawel Jakub Dawidek {
487bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
488933728eeSPawel Jakub Dawidek 
489933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
490bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
491933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
492933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(fd == -1);
493933728eeSPawel Jakub Dawidek 
494933728eeSPawel Jakub Dawidek 	return (proto_common_send(tctx->tc_fd, data, size, -1));
495933728eeSPawel Jakub Dawidek }
496933728eeSPawel Jakub Dawidek 
497933728eeSPawel Jakub Dawidek static int
498bdbd046bSPawel Jakub Dawidek tcp_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
499933728eeSPawel Jakub Dawidek {
500bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
501933728eeSPawel Jakub Dawidek 
502933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
503bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
504933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
505933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(fdp == NULL);
506933728eeSPawel Jakub Dawidek 
507933728eeSPawel Jakub Dawidek 	return (proto_common_recv(tctx->tc_fd, data, size, NULL));
508933728eeSPawel Jakub Dawidek }
509933728eeSPawel Jakub Dawidek 
510933728eeSPawel Jakub Dawidek static int
511bdbd046bSPawel Jakub Dawidek tcp_descriptor(const void *ctx)
512933728eeSPawel Jakub Dawidek {
513bdbd046bSPawel Jakub Dawidek 	const struct tcp_ctx *tctx = ctx;
514933728eeSPawel Jakub Dawidek 
515933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
516bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
517933728eeSPawel Jakub Dawidek 
518933728eeSPawel Jakub Dawidek 	return (tctx->tc_fd);
519933728eeSPawel Jakub Dawidek }
520933728eeSPawel Jakub Dawidek 
521933728eeSPawel Jakub Dawidek static bool
522bdbd046bSPawel Jakub Dawidek tcp_address_match(const void *ctx, const char *addr)
523933728eeSPawel Jakub Dawidek {
524bdbd046bSPawel Jakub Dawidek 	const struct tcp_ctx *tctx = ctx;
525dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage sa1, sa2;
526dc18c8aeSPawel Jakub Dawidek 	socklen_t salen;
527933728eeSPawel Jakub Dawidek 
528933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
529bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
530933728eeSPawel Jakub Dawidek 
531dc18c8aeSPawel Jakub Dawidek 	if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sa1) != 0)
532933728eeSPawel Jakub Dawidek 		return (false);
533933728eeSPawel Jakub Dawidek 
534dc18c8aeSPawel Jakub Dawidek 	salen = sizeof(sa2);
5352b1b224dSPawel Jakub Dawidek 	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) == -1)
536933728eeSPawel Jakub Dawidek 		return (false);
537933728eeSPawel Jakub Dawidek 
538dc18c8aeSPawel Jakub Dawidek 	if (sa1.ss_family != sa2.ss_family || sa1.ss_len != sa2.ss_len)
539dc18c8aeSPawel Jakub Dawidek 		return (false);
540dc18c8aeSPawel Jakub Dawidek 
541dc18c8aeSPawel Jakub Dawidek 	switch (sa1.ss_family) {
542dc18c8aeSPawel Jakub Dawidek 	case AF_INET:
543dc18c8aeSPawel Jakub Dawidek 	    {
544dc18c8aeSPawel Jakub Dawidek 		struct sockaddr_in *sin1, *sin2;
545dc18c8aeSPawel Jakub Dawidek 
546dc18c8aeSPawel Jakub Dawidek 		sin1 = (struct sockaddr_in *)&sa1;
547dc18c8aeSPawel Jakub Dawidek 		sin2 = (struct sockaddr_in *)&sa2;
548dc18c8aeSPawel Jakub Dawidek 
549dc18c8aeSPawel Jakub Dawidek 		return (memcmp(&sin1->sin_addr, &sin2->sin_addr,
550dc18c8aeSPawel Jakub Dawidek 		    sizeof(sin1->sin_addr)) == 0);
551dc18c8aeSPawel Jakub Dawidek 	    }
552dc18c8aeSPawel Jakub Dawidek 	case AF_INET6:
553dc18c8aeSPawel Jakub Dawidek 	    {
554dc18c8aeSPawel Jakub Dawidek 		struct sockaddr_in6 *sin1, *sin2;
555dc18c8aeSPawel Jakub Dawidek 
556dc18c8aeSPawel Jakub Dawidek 		sin1 = (struct sockaddr_in6 *)&sa1;
557dc18c8aeSPawel Jakub Dawidek 		sin2 = (struct sockaddr_in6 *)&sa2;
558dc18c8aeSPawel Jakub Dawidek 
559dc18c8aeSPawel Jakub Dawidek 		return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
560dc18c8aeSPawel Jakub Dawidek 		    sizeof(sin1->sin6_addr)) == 0);
561dc18c8aeSPawel Jakub Dawidek 	    }
562dc18c8aeSPawel Jakub Dawidek 	default:
563dc18c8aeSPawel Jakub Dawidek 		return (false);
564dc18c8aeSPawel Jakub Dawidek 	}
565933728eeSPawel Jakub Dawidek }
566933728eeSPawel Jakub Dawidek 
567933728eeSPawel Jakub Dawidek static void
568bdbd046bSPawel Jakub Dawidek tcp_local_address(const void *ctx, char *addr, size_t size)
569933728eeSPawel Jakub Dawidek {
570bdbd046bSPawel Jakub Dawidek 	const struct tcp_ctx *tctx = ctx;
571dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage sa;
572dc18c8aeSPawel Jakub Dawidek 	socklen_t salen;
573933728eeSPawel Jakub Dawidek 
574933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
575bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
576933728eeSPawel Jakub Dawidek 
577dc18c8aeSPawel Jakub Dawidek 	salen = sizeof(sa);
5782b1b224dSPawel Jakub Dawidek 	if (getsockname(tctx->tc_fd, (struct sockaddr *)&sa, &salen) == -1) {
579933728eeSPawel Jakub Dawidek 		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
580933728eeSPawel Jakub Dawidek 		return;
581933728eeSPawel Jakub Dawidek 	}
582dc18c8aeSPawel Jakub Dawidek 	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
583933728eeSPawel Jakub Dawidek }
584933728eeSPawel Jakub Dawidek 
585933728eeSPawel Jakub Dawidek static void
586bdbd046bSPawel Jakub Dawidek tcp_remote_address(const void *ctx, char *addr, size_t size)
587933728eeSPawel Jakub Dawidek {
588bdbd046bSPawel Jakub Dawidek 	const struct tcp_ctx *tctx = ctx;
589dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage sa;
590dc18c8aeSPawel Jakub Dawidek 	socklen_t salen;
591933728eeSPawel Jakub Dawidek 
592933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
593bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
594933728eeSPawel Jakub Dawidek 
595dc18c8aeSPawel Jakub Dawidek 	salen = sizeof(sa);
5962b1b224dSPawel Jakub Dawidek 	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) == -1) {
597933728eeSPawel Jakub Dawidek 		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
598933728eeSPawel Jakub Dawidek 		return;
599933728eeSPawel Jakub Dawidek 	}
600dc18c8aeSPawel Jakub Dawidek 	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
601933728eeSPawel Jakub Dawidek }
602933728eeSPawel Jakub Dawidek 
603933728eeSPawel Jakub Dawidek static void
604bdbd046bSPawel Jakub Dawidek tcp_close(void *ctx)
605933728eeSPawel Jakub Dawidek {
606bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
607933728eeSPawel Jakub Dawidek 
608933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
609bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
610933728eeSPawel Jakub Dawidek 
611933728eeSPawel Jakub Dawidek 	if (tctx->tc_fd >= 0)
612933728eeSPawel Jakub Dawidek 		close(tctx->tc_fd);
613933728eeSPawel Jakub Dawidek 	tctx->tc_magic = 0;
614933728eeSPawel Jakub Dawidek 	free(tctx);
615933728eeSPawel Jakub Dawidek }
616933728eeSPawel Jakub Dawidek 
617bdbd046bSPawel Jakub Dawidek static struct proto tcp_proto = {
618bdbd046bSPawel Jakub Dawidek 	.prt_name = "tcp",
619bdbd046bSPawel Jakub Dawidek 	.prt_client = tcp_client,
620bdbd046bSPawel Jakub Dawidek 	.prt_connect = tcp_connect,
621bdbd046bSPawel Jakub Dawidek 	.prt_connect_wait = tcp_connect_wait,
622bdbd046bSPawel Jakub Dawidek 	.prt_server = tcp_server,
623bdbd046bSPawel Jakub Dawidek 	.prt_accept = tcp_accept,
624bdbd046bSPawel Jakub Dawidek 	.prt_wrap = tcp_wrap,
625bdbd046bSPawel Jakub Dawidek 	.prt_send = tcp_send,
626bdbd046bSPawel Jakub Dawidek 	.prt_recv = tcp_recv,
627bdbd046bSPawel Jakub Dawidek 	.prt_descriptor = tcp_descriptor,
628bdbd046bSPawel Jakub Dawidek 	.prt_address_match = tcp_address_match,
629bdbd046bSPawel Jakub Dawidek 	.prt_local_address = tcp_local_address,
630bdbd046bSPawel Jakub Dawidek 	.prt_remote_address = tcp_remote_address,
631bdbd046bSPawel Jakub Dawidek 	.prt_close = tcp_close
632933728eeSPawel Jakub Dawidek };
633933728eeSPawel Jakub Dawidek 
634933728eeSPawel Jakub Dawidek static __constructor void
635bdbd046bSPawel Jakub Dawidek tcp_ctor(void)
636933728eeSPawel Jakub Dawidek {
637933728eeSPawel Jakub Dawidek 
638bdbd046bSPawel Jakub Dawidek 	proto_register(&tcp_proto, true);
639933728eeSPawel Jakub Dawidek }
640