xref: /freebsd/sbin/hastd/proto_tcp.c (revision 2b1b224d2486d7df9f51f12377e5f1e0ac9e3d78)
1933728eeSPawel Jakub Dawidek /*-
2933728eeSPawel Jakub Dawidek  * Copyright (c) 2009-2010 The FreeBSD Foundation
3dc18c8aeSPawel 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 
55dc18c8aeSPawel Jakub Dawidek #define	TCP_CTX_MAGIC	0x7c41c
56bdbd046bSPawel Jakub Dawidek struct tcp_ctx {
57933728eeSPawel Jakub Dawidek 	int			tc_magic;
58dc18c8aeSPawel 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
100dc18c8aeSPawel Jakub Dawidek tcp_addr(const char *addr, int defport, struct sockaddr_storage *sap)
101933728eeSPawel Jakub Dawidek {
102dc18c8aeSPawel Jakub Dawidek 	char iporhost[MAXHOSTNAMELEN], portstr[6];
103dc18c8aeSPawel Jakub Dawidek 	struct addrinfo hints;
104dc18c8aeSPawel Jakub Dawidek 	struct addrinfo *res;
105933728eeSPawel Jakub Dawidek 	const char *pp;
106dc18c8aeSPawel Jakub Dawidek 	intmax_t port;
107933728eeSPawel Jakub Dawidek 	size_t size;
108dc18c8aeSPawel Jakub Dawidek 	int error;
109933728eeSPawel Jakub Dawidek 
110933728eeSPawel Jakub Dawidek 	if (addr == NULL)
111933728eeSPawel Jakub Dawidek 		return (-1);
112933728eeSPawel Jakub Dawidek 
113dc18c8aeSPawel Jakub Dawidek 	bzero(&hints, sizeof(hints));
114dc18c8aeSPawel Jakub Dawidek 	hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
115dc18c8aeSPawel Jakub Dawidek 	hints.ai_family = PF_UNSPEC;
116dc18c8aeSPawel Jakub Dawidek 	hints.ai_socktype = SOCK_STREAM;
117dc18c8aeSPawel Jakub Dawidek 	hints.ai_protocol = IPPROTO_TCP;
118dc18c8aeSPawel Jakub Dawidek 
119dc18c8aeSPawel Jakub Dawidek 	if (strncasecmp(addr, "tcp4://", 7) == 0) {
120933728eeSPawel Jakub Dawidek 		addr += 7;
121dc18c8aeSPawel Jakub Dawidek 		hints.ai_family = PF_INET;
122dc18c8aeSPawel Jakub Dawidek 	} else if (strncasecmp(addr, "tcp6://", 7) == 0) {
123dc18c8aeSPawel Jakub Dawidek 		addr += 7;
124dc18c8aeSPawel Jakub Dawidek 		hints.ai_family = PF_INET6;
125dc18c8aeSPawel Jakub Dawidek 	} else if (strncasecmp(addr, "tcp://", 6) == 0) {
126933728eeSPawel Jakub Dawidek 		addr += 6;
127dc18c8aeSPawel 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 
134dc18c8aeSPawel Jakub Dawidek 	/*
135dc18c8aeSPawel Jakub Dawidek 	 * Extract optional port.
136dc18c8aeSPawel Jakub Dawidek 	 * There are three cases to consider.
137dc18c8aeSPawel Jakub Dawidek 	 * 1. hostname with port, eg. freefall.freebsd.org:8457
138dc18c8aeSPawel Jakub Dawidek 	 * 2. IPv4 address with port, eg. 192.168.0.101:8457
139dc18c8aeSPawel Jakub Dawidek 	 * 3. IPv6 address with port, eg. [fe80::1]:8457
140dc18c8aeSPawel Jakub Dawidek 	 * We discover IPv6 address by checking for two colons and if port is
141dc18c8aeSPawel Jakub Dawidek 	 * given, the address has to start with [.
142dc18c8aeSPawel Jakub Dawidek 	 */
143dc18c8aeSPawel Jakub Dawidek 	pp = NULL;
144dc18c8aeSPawel Jakub Dawidek 	if (strchr(addr, ':') != strrchr(addr, ':')) {
145dc18c8aeSPawel Jakub Dawidek 		if (addr[0] == '[')
146933728eeSPawel Jakub Dawidek 			pp = strrchr(addr, ':');
147dc18c8aeSPawel Jakub Dawidek 	} else {
148dc18c8aeSPawel Jakub Dawidek 		pp = strrchr(addr, ':');
149dc18c8aeSPawel Jakub Dawidek 	}
150933728eeSPawel Jakub Dawidek 	if (pp == NULL) {
151933728eeSPawel Jakub Dawidek 		/* Port not given, use the default. */
152dc18c8aeSPawel Jakub Dawidek 		port = defport;
153933728eeSPawel Jakub Dawidek 	} else {
154*2b1b224dSPawel Jakub Dawidek 		if (numfromstr(pp + 1, 1, 65535, &port) == -1)
155933728eeSPawel Jakub Dawidek 			return (errno);
156933728eeSPawel Jakub Dawidek 	}
157dc18c8aeSPawel 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);
163dc18c8aeSPawel Jakub Dawidek 	} else if (addr[0] == '[' && pp[-1] == ']') {
164dc18c8aeSPawel Jakub Dawidek 		size = (size_t)(pp - addr - 2 + 1);
165dc18c8aeSPawel Jakub Dawidek 		if (size > sizeof(iporhost))
166dc18c8aeSPawel Jakub Dawidek 			return (ENAMETOOLONG);
167dc18c8aeSPawel 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 	}
174dc18c8aeSPawel Jakub Dawidek 
175dc18c8aeSPawel Jakub Dawidek 	error = getaddrinfo(iporhost, portstr, &hints, &res);
176dc18c8aeSPawel Jakub Dawidek 	if (error != 0) {
177dc18c8aeSPawel Jakub Dawidek 		pjdlog_debug(1, "getaddrinfo(%s, %s) failed: %s.", iporhost,
178dc18c8aeSPawel Jakub Dawidek 		    portstr, gai_strerror(error));
179933728eeSPawel Jakub Dawidek 		return (EINVAL);
180dc18c8aeSPawel Jakub Dawidek 	}
181dc18c8aeSPawel Jakub Dawidek 	if (res == NULL)
182dc18c8aeSPawel Jakub Dawidek 		return (ENOENT);
183dc18c8aeSPawel Jakub Dawidek 
184dc18c8aeSPawel Jakub Dawidek 	memcpy(sap, res->ai_addr, res->ai_addrlen);
185dc18c8aeSPawel Jakub Dawidek 
186dc18c8aeSPawel 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. */
207dc18c8aeSPawel 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 
212dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
213933728eeSPawel Jakub Dawidek 
214dc18c8aeSPawel 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 
221dc18c8aeSPawel 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;
252dc18c8aeSPawel 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;
264dc18c8aeSPawel 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);
273dc18c8aeSPawel 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*2b1b224dSPawel Jakub Dawidek 	if (bind(tctx->tc_fd, (struct sockaddr *)&sa, sa.ss_len) == -1) {
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);
296dc18c8aeSPawel 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) {
3011ebc0407SPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
302933728eeSPawel Jakub Dawidek 		return (errno);
303933728eeSPawel Jakub Dawidek 	}
304933728eeSPawel Jakub Dawidek 	/*
305933728eeSPawel Jakub Dawidek 	 * We make socket non-blocking so we can handle connection timeout
306933728eeSPawel Jakub Dawidek 	 * manually.
307933728eeSPawel Jakub Dawidek 	 */
308933728eeSPawel Jakub Dawidek 	flags |= O_NONBLOCK;
309933728eeSPawel Jakub Dawidek 	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
3101ebc0407SPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno,
3111ebc0407SPawel Jakub Dawidek 		    "fcntl(F_SETFL, O_NONBLOCK) failed");
312933728eeSPawel Jakub Dawidek 		return (errno);
313933728eeSPawel Jakub Dawidek 	}
314933728eeSPawel Jakub Dawidek 
315dc18c8aeSPawel Jakub Dawidek 	if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
316dc18c8aeSPawel Jakub Dawidek 	    tctx->tc_sa.ss_len) == 0) {
317933728eeSPawel Jakub Dawidek 		if (timeout == -1)
318933728eeSPawel Jakub Dawidek 			return (0);
319933728eeSPawel Jakub Dawidek 		error = 0;
320933728eeSPawel Jakub Dawidek 		goto done;
321933728eeSPawel Jakub Dawidek 	}
322933728eeSPawel Jakub Dawidek 	if (errno != EINPROGRESS) {
323933728eeSPawel Jakub Dawidek 		error = errno;
324933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
325933728eeSPawel Jakub Dawidek 		goto done;
326933728eeSPawel Jakub Dawidek 	}
327933728eeSPawel Jakub Dawidek 	if (timeout == -1)
328933728eeSPawel Jakub Dawidek 		return (0);
329bdbd046bSPawel Jakub Dawidek 	return (tcp_connect_wait(ctx, timeout));
330933728eeSPawel Jakub Dawidek done:
331933728eeSPawel Jakub Dawidek 	flags &= ~O_NONBLOCK;
332933728eeSPawel Jakub Dawidek 	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
333933728eeSPawel Jakub Dawidek 		if (error == 0)
334933728eeSPawel Jakub Dawidek 			error = errno;
335933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno,
336933728eeSPawel Jakub Dawidek 		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
337933728eeSPawel Jakub Dawidek 	}
338933728eeSPawel Jakub Dawidek 	return (error);
339933728eeSPawel Jakub Dawidek }
340933728eeSPawel Jakub Dawidek 
341933728eeSPawel Jakub Dawidek static int
342bdbd046bSPawel Jakub Dawidek tcp_connect_wait(void *ctx, int timeout)
343933728eeSPawel Jakub Dawidek {
344bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
345933728eeSPawel Jakub Dawidek 	struct timeval tv;
346933728eeSPawel Jakub Dawidek 	fd_set fdset;
347933728eeSPawel Jakub Dawidek 	socklen_t esize;
348933728eeSPawel Jakub Dawidek 	int error, flags, ret;
349933728eeSPawel Jakub Dawidek 
350933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
351bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
352bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
353933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
354933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(timeout >= 0);
355933728eeSPawel Jakub Dawidek 
356933728eeSPawel Jakub Dawidek 	tv.tv_sec = timeout;
357933728eeSPawel Jakub Dawidek 	tv.tv_usec = 0;
358933728eeSPawel Jakub Dawidek again:
359933728eeSPawel Jakub Dawidek 	FD_ZERO(&fdset);
360933728eeSPawel Jakub Dawidek 	FD_SET(tctx->tc_fd, &fdset);
361933728eeSPawel Jakub Dawidek 	ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
362933728eeSPawel Jakub Dawidek 	if (ret == 0) {
363933728eeSPawel Jakub Dawidek 		error = ETIMEDOUT;
364933728eeSPawel Jakub Dawidek 		goto done;
365933728eeSPawel Jakub Dawidek 	} else if (ret == -1) {
366933728eeSPawel Jakub Dawidek 		if (errno == EINTR)
367933728eeSPawel Jakub Dawidek 			goto again;
368933728eeSPawel Jakub Dawidek 		error = errno;
369933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
370933728eeSPawel Jakub Dawidek 		goto done;
371933728eeSPawel Jakub Dawidek 	}
372933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(ret > 0);
373933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset));
374933728eeSPawel Jakub Dawidek 	esize = sizeof(error);
375933728eeSPawel Jakub Dawidek 	if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
376933728eeSPawel Jakub Dawidek 	    &esize) == -1) {
377933728eeSPawel Jakub Dawidek 		error = errno;
378933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno,
379933728eeSPawel Jakub Dawidek 		    "getsockopt(SO_ERROR) failed");
380933728eeSPawel Jakub Dawidek 		goto done;
381933728eeSPawel Jakub Dawidek 	}
382933728eeSPawel Jakub Dawidek 	if (error != 0) {
383933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, error,
384933728eeSPawel Jakub Dawidek 		    "getsockopt(SO_ERROR) returned error");
385933728eeSPawel Jakub Dawidek 		goto done;
386933728eeSPawel Jakub Dawidek 	}
387933728eeSPawel Jakub Dawidek 	error = 0;
388933728eeSPawel Jakub Dawidek done:
389933728eeSPawel Jakub Dawidek 	flags = fcntl(tctx->tc_fd, F_GETFL);
390933728eeSPawel Jakub Dawidek 	if (flags == -1) {
391933728eeSPawel Jakub Dawidek 		if (error == 0)
392933728eeSPawel Jakub Dawidek 			error = errno;
393933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
394933728eeSPawel Jakub Dawidek 		return (error);
395933728eeSPawel Jakub Dawidek 	}
396933728eeSPawel Jakub Dawidek 	flags &= ~O_NONBLOCK;
397933728eeSPawel Jakub Dawidek 	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
398933728eeSPawel Jakub Dawidek 		if (error == 0)
399933728eeSPawel Jakub Dawidek 			error = errno;
400933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno,
401933728eeSPawel Jakub Dawidek 		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
402933728eeSPawel Jakub Dawidek 	}
403933728eeSPawel Jakub Dawidek 	return (error);
404933728eeSPawel Jakub Dawidek }
405933728eeSPawel Jakub Dawidek 
406933728eeSPawel Jakub Dawidek static int
407bdbd046bSPawel Jakub Dawidek tcp_server(const char *addr, void **ctxp)
408933728eeSPawel Jakub Dawidek {
409bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx;
410933728eeSPawel Jakub Dawidek 	int ret, val;
411933728eeSPawel Jakub Dawidek 
412bdbd046bSPawel Jakub Dawidek 	ret = tcp_setup_new(addr, TCP_SIDE_SERVER_LISTEN, ctxp);
413933728eeSPawel Jakub Dawidek 	if (ret != 0)
414933728eeSPawel Jakub Dawidek 		return (ret);
415933728eeSPawel Jakub Dawidek 
416933728eeSPawel Jakub Dawidek 	tctx = *ctxp;
417933728eeSPawel Jakub Dawidek 
418933728eeSPawel Jakub Dawidek 	val = 1;
419933728eeSPawel Jakub Dawidek 	/* Ignore failure. */
420933728eeSPawel Jakub Dawidek 	(void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
421933728eeSPawel Jakub Dawidek 	   sizeof(val));
422933728eeSPawel Jakub Dawidek 
423dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
424933728eeSPawel Jakub Dawidek 
425dc18c8aeSPawel Jakub Dawidek 	if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
426*2b1b224dSPawel Jakub Dawidek 	    tctx->tc_sa.ss_len) == -1) {
427933728eeSPawel Jakub Dawidek 		ret = errno;
428bdbd046bSPawel Jakub Dawidek 		tcp_close(tctx);
429933728eeSPawel Jakub Dawidek 		return (ret);
430933728eeSPawel Jakub Dawidek 	}
431*2b1b224dSPawel Jakub Dawidek 	if (listen(tctx->tc_fd, 8) == -1) {
432933728eeSPawel Jakub Dawidek 		ret = errno;
433bdbd046bSPawel Jakub Dawidek 		tcp_close(tctx);
434933728eeSPawel Jakub Dawidek 		return (ret);
435933728eeSPawel Jakub Dawidek 	}
436933728eeSPawel Jakub Dawidek 
437933728eeSPawel Jakub Dawidek 	return (0);
438933728eeSPawel Jakub Dawidek }
439933728eeSPawel Jakub Dawidek 
440933728eeSPawel Jakub Dawidek static int
441bdbd046bSPawel Jakub Dawidek tcp_accept(void *ctx, void **newctxp)
442933728eeSPawel Jakub Dawidek {
443bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
444bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *newtctx;
445933728eeSPawel Jakub Dawidek 	socklen_t fromlen;
446933728eeSPawel Jakub Dawidek 	int ret;
447933728eeSPawel Jakub Dawidek 
448933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
449bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
450bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN);
451933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
452dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
453933728eeSPawel Jakub Dawidek 
454933728eeSPawel Jakub Dawidek 	newtctx = malloc(sizeof(*newtctx));
455933728eeSPawel Jakub Dawidek 	if (newtctx == NULL)
456933728eeSPawel Jakub Dawidek 		return (errno);
457933728eeSPawel Jakub Dawidek 
458dc18c8aeSPawel Jakub Dawidek 	fromlen = tctx->tc_sa.ss_len;
459dc18c8aeSPawel Jakub Dawidek 	newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
460933728eeSPawel Jakub Dawidek 	    &fromlen);
461*2b1b224dSPawel Jakub Dawidek 	if (newtctx->tc_fd == -1) {
462933728eeSPawel Jakub Dawidek 		ret = errno;
463933728eeSPawel Jakub Dawidek 		free(newtctx);
464933728eeSPawel Jakub Dawidek 		return (ret);
465933728eeSPawel Jakub Dawidek 	}
466933728eeSPawel Jakub Dawidek 
467bdbd046bSPawel Jakub Dawidek 	newtctx->tc_side = TCP_SIDE_SERVER_WORK;
468bdbd046bSPawel Jakub Dawidek 	newtctx->tc_magic = TCP_CTX_MAGIC;
469933728eeSPawel Jakub Dawidek 	*newctxp = newtctx;
470933728eeSPawel Jakub Dawidek 
471933728eeSPawel Jakub Dawidek 	return (0);
472933728eeSPawel Jakub Dawidek }
473933728eeSPawel Jakub Dawidek 
474933728eeSPawel Jakub Dawidek static int
475bdbd046bSPawel Jakub Dawidek tcp_wrap(int fd, bool client, void **ctxp)
476933728eeSPawel Jakub Dawidek {
477933728eeSPawel Jakub Dawidek 
478bdbd046bSPawel Jakub Dawidek 	return (tcp_setup_wrap(fd,
479bdbd046bSPawel Jakub Dawidek 	    client ? TCP_SIDE_CLIENT : TCP_SIDE_SERVER_WORK, ctxp));
480933728eeSPawel Jakub Dawidek }
481933728eeSPawel Jakub Dawidek 
482933728eeSPawel Jakub Dawidek static int
483bdbd046bSPawel Jakub Dawidek tcp_send(void *ctx, const unsigned char *data, size_t size, int fd)
484933728eeSPawel Jakub Dawidek {
485bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
486933728eeSPawel Jakub Dawidek 
487933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
488bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
489933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
490933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(fd == -1);
491933728eeSPawel Jakub Dawidek 
492933728eeSPawel Jakub Dawidek 	return (proto_common_send(tctx->tc_fd, data, size, -1));
493933728eeSPawel Jakub Dawidek }
494933728eeSPawel Jakub Dawidek 
495933728eeSPawel Jakub Dawidek static int
496bdbd046bSPawel Jakub Dawidek tcp_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
497933728eeSPawel Jakub Dawidek {
498bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
499933728eeSPawel Jakub Dawidek 
500933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
501bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
502933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
503933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(fdp == NULL);
504933728eeSPawel Jakub Dawidek 
505933728eeSPawel Jakub Dawidek 	return (proto_common_recv(tctx->tc_fd, data, size, NULL));
506933728eeSPawel Jakub Dawidek }
507933728eeSPawel Jakub Dawidek 
508933728eeSPawel Jakub Dawidek static int
509bdbd046bSPawel Jakub Dawidek tcp_descriptor(const void *ctx)
510933728eeSPawel Jakub Dawidek {
511bdbd046bSPawel Jakub Dawidek 	const struct tcp_ctx *tctx = ctx;
512933728eeSPawel Jakub Dawidek 
513933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
514bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
515933728eeSPawel Jakub Dawidek 
516933728eeSPawel Jakub Dawidek 	return (tctx->tc_fd);
517933728eeSPawel Jakub Dawidek }
518933728eeSPawel Jakub Dawidek 
519933728eeSPawel Jakub Dawidek static bool
520bdbd046bSPawel Jakub Dawidek tcp_address_match(const void *ctx, const char *addr)
521933728eeSPawel Jakub Dawidek {
522bdbd046bSPawel Jakub Dawidek 	const struct tcp_ctx *tctx = ctx;
523dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage sa1, sa2;
524dc18c8aeSPawel Jakub Dawidek 	socklen_t salen;
525933728eeSPawel Jakub Dawidek 
526933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
527bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
528933728eeSPawel Jakub Dawidek 
529dc18c8aeSPawel Jakub Dawidek 	if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sa1) != 0)
530933728eeSPawel Jakub Dawidek 		return (false);
531933728eeSPawel Jakub Dawidek 
532dc18c8aeSPawel Jakub Dawidek 	salen = sizeof(sa2);
533*2b1b224dSPawel Jakub Dawidek 	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) == -1)
534933728eeSPawel Jakub Dawidek 		return (false);
535933728eeSPawel Jakub Dawidek 
536dc18c8aeSPawel Jakub Dawidek 	if (sa1.ss_family != sa2.ss_family || sa1.ss_len != sa2.ss_len)
537dc18c8aeSPawel Jakub Dawidek 		return (false);
538dc18c8aeSPawel Jakub Dawidek 
539dc18c8aeSPawel Jakub Dawidek 	switch (sa1.ss_family) {
540dc18c8aeSPawel Jakub Dawidek 	case AF_INET:
541dc18c8aeSPawel Jakub Dawidek 	    {
542dc18c8aeSPawel Jakub Dawidek 		struct sockaddr_in *sin1, *sin2;
543dc18c8aeSPawel Jakub Dawidek 
544dc18c8aeSPawel Jakub Dawidek 		sin1 = (struct sockaddr_in *)&sa1;
545dc18c8aeSPawel Jakub Dawidek 		sin2 = (struct sockaddr_in *)&sa2;
546dc18c8aeSPawel Jakub Dawidek 
547dc18c8aeSPawel Jakub Dawidek 		return (memcmp(&sin1->sin_addr, &sin2->sin_addr,
548dc18c8aeSPawel Jakub Dawidek 		    sizeof(sin1->sin_addr)) == 0);
549dc18c8aeSPawel Jakub Dawidek 	    }
550dc18c8aeSPawel Jakub Dawidek 	case AF_INET6:
551dc18c8aeSPawel Jakub Dawidek 	    {
552dc18c8aeSPawel Jakub Dawidek 		struct sockaddr_in6 *sin1, *sin2;
553dc18c8aeSPawel Jakub Dawidek 
554dc18c8aeSPawel Jakub Dawidek 		sin1 = (struct sockaddr_in6 *)&sa1;
555dc18c8aeSPawel Jakub Dawidek 		sin2 = (struct sockaddr_in6 *)&sa2;
556dc18c8aeSPawel Jakub Dawidek 
557dc18c8aeSPawel Jakub Dawidek 		return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
558dc18c8aeSPawel Jakub Dawidek 		    sizeof(sin1->sin6_addr)) == 0);
559dc18c8aeSPawel Jakub Dawidek 	    }
560dc18c8aeSPawel Jakub Dawidek 	default:
561dc18c8aeSPawel Jakub Dawidek 		return (false);
562dc18c8aeSPawel Jakub Dawidek 	}
563933728eeSPawel Jakub Dawidek }
564933728eeSPawel Jakub Dawidek 
565933728eeSPawel Jakub Dawidek static void
566bdbd046bSPawel Jakub Dawidek tcp_local_address(const void *ctx, char *addr, size_t size)
567933728eeSPawel Jakub Dawidek {
568bdbd046bSPawel Jakub Dawidek 	const struct tcp_ctx *tctx = ctx;
569dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage sa;
570dc18c8aeSPawel Jakub Dawidek 	socklen_t salen;
571933728eeSPawel Jakub Dawidek 
572933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
573bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
574933728eeSPawel Jakub Dawidek 
575dc18c8aeSPawel Jakub Dawidek 	salen = sizeof(sa);
576*2b1b224dSPawel Jakub Dawidek 	if (getsockname(tctx->tc_fd, (struct sockaddr *)&sa, &salen) == -1) {
577933728eeSPawel Jakub Dawidek 		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
578933728eeSPawel Jakub Dawidek 		return;
579933728eeSPawel Jakub Dawidek 	}
580dc18c8aeSPawel Jakub Dawidek 	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
581933728eeSPawel Jakub Dawidek }
582933728eeSPawel Jakub Dawidek 
583933728eeSPawel Jakub Dawidek static void
584bdbd046bSPawel Jakub Dawidek tcp_remote_address(const void *ctx, char *addr, size_t size)
585933728eeSPawel Jakub Dawidek {
586bdbd046bSPawel Jakub Dawidek 	const struct tcp_ctx *tctx = ctx;
587dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage sa;
588dc18c8aeSPawel Jakub Dawidek 	socklen_t salen;
589933728eeSPawel Jakub Dawidek 
590933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
591bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
592933728eeSPawel Jakub Dawidek 
593dc18c8aeSPawel Jakub Dawidek 	salen = sizeof(sa);
594*2b1b224dSPawel Jakub Dawidek 	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) == -1) {
595933728eeSPawel Jakub Dawidek 		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
596933728eeSPawel Jakub Dawidek 		return;
597933728eeSPawel Jakub Dawidek 	}
598dc18c8aeSPawel Jakub Dawidek 	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
599933728eeSPawel Jakub Dawidek }
600933728eeSPawel Jakub Dawidek 
601933728eeSPawel Jakub Dawidek static void
602bdbd046bSPawel Jakub Dawidek tcp_close(void *ctx)
603933728eeSPawel Jakub Dawidek {
604bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
605933728eeSPawel Jakub Dawidek 
606933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
607bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
608933728eeSPawel Jakub Dawidek 
609933728eeSPawel Jakub Dawidek 	if (tctx->tc_fd >= 0)
610933728eeSPawel Jakub Dawidek 		close(tctx->tc_fd);
611933728eeSPawel Jakub Dawidek 	tctx->tc_magic = 0;
612933728eeSPawel Jakub Dawidek 	free(tctx);
613933728eeSPawel Jakub Dawidek }
614933728eeSPawel Jakub Dawidek 
615bdbd046bSPawel Jakub Dawidek static struct proto tcp_proto = {
616bdbd046bSPawel Jakub Dawidek 	.prt_name = "tcp",
617bdbd046bSPawel Jakub Dawidek 	.prt_client = tcp_client,
618bdbd046bSPawel Jakub Dawidek 	.prt_connect = tcp_connect,
619bdbd046bSPawel Jakub Dawidek 	.prt_connect_wait = tcp_connect_wait,
620bdbd046bSPawel Jakub Dawidek 	.prt_server = tcp_server,
621bdbd046bSPawel Jakub Dawidek 	.prt_accept = tcp_accept,
622bdbd046bSPawel Jakub Dawidek 	.prt_wrap = tcp_wrap,
623bdbd046bSPawel Jakub Dawidek 	.prt_send = tcp_send,
624bdbd046bSPawel Jakub Dawidek 	.prt_recv = tcp_recv,
625bdbd046bSPawel Jakub Dawidek 	.prt_descriptor = tcp_descriptor,
626bdbd046bSPawel Jakub Dawidek 	.prt_address_match = tcp_address_match,
627bdbd046bSPawel Jakub Dawidek 	.prt_local_address = tcp_local_address,
628bdbd046bSPawel Jakub Dawidek 	.prt_remote_address = tcp_remote_address,
629bdbd046bSPawel Jakub Dawidek 	.prt_close = tcp_close
630933728eeSPawel Jakub Dawidek };
631933728eeSPawel Jakub Dawidek 
632933728eeSPawel Jakub Dawidek static __constructor void
633bdbd046bSPawel Jakub Dawidek tcp_ctor(void)
634933728eeSPawel Jakub Dawidek {
635933728eeSPawel Jakub Dawidek 
636bdbd046bSPawel Jakub Dawidek 	proto_register(&tcp_proto, true);
637933728eeSPawel Jakub Dawidek }
638