xref: /freebsd/sbin/hastd/proto_tcp.c (revision 32e86a82f54826f14ea381affa6674db3aa3b5ae)
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/param.h>	/* MAXHOSTNAMELEN */
34933728eeSPawel Jakub Dawidek #include <sys/socket.h>
35933728eeSPawel Jakub Dawidek 
36933728eeSPawel Jakub Dawidek #include <arpa/inet.h>
37933728eeSPawel Jakub Dawidek 
38933728eeSPawel Jakub Dawidek #include <netinet/in.h>
39933728eeSPawel Jakub Dawidek #include <netinet/tcp.h>
40933728eeSPawel Jakub Dawidek 
41933728eeSPawel Jakub Dawidek #include <errno.h>
42933728eeSPawel Jakub Dawidek #include <fcntl.h>
43933728eeSPawel Jakub Dawidek #include <netdb.h>
44933728eeSPawel Jakub Dawidek #include <stdbool.h>
45933728eeSPawel Jakub Dawidek #include <stdint.h>
46933728eeSPawel Jakub Dawidek #include <stdio.h>
47933728eeSPawel Jakub Dawidek #include <string.h>
48933728eeSPawel Jakub Dawidek #include <unistd.h>
49933728eeSPawel Jakub Dawidek 
50933728eeSPawel Jakub Dawidek #include "pjdlog.h"
51933728eeSPawel Jakub Dawidek #include "proto_impl.h"
52933728eeSPawel Jakub Dawidek #include "subr.h"
53933728eeSPawel Jakub Dawidek 
54dc18c8aeSPawel Jakub Dawidek #define	TCP_CTX_MAGIC	0x7c41c
55bdbd046bSPawel Jakub Dawidek struct tcp_ctx {
56933728eeSPawel Jakub Dawidek 	int			tc_magic;
57dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage	tc_sa;
58933728eeSPawel Jakub Dawidek 	int			tc_fd;
59933728eeSPawel Jakub Dawidek 	int			tc_side;
60bdbd046bSPawel Jakub Dawidek #define	TCP_SIDE_CLIENT		0
61bdbd046bSPawel Jakub Dawidek #define	TCP_SIDE_SERVER_LISTEN	1
62bdbd046bSPawel Jakub Dawidek #define	TCP_SIDE_SERVER_WORK	2
63933728eeSPawel Jakub Dawidek };
64933728eeSPawel Jakub Dawidek 
65bdbd046bSPawel Jakub Dawidek static int tcp_connect_wait(void *ctx, int timeout);
66bdbd046bSPawel Jakub Dawidek static void tcp_close(void *ctx);
67933728eeSPawel Jakub Dawidek 
68933728eeSPawel Jakub Dawidek /*
69933728eeSPawel Jakub Dawidek  * Function converts the given string to unsigned number.
70933728eeSPawel Jakub Dawidek  */
71933728eeSPawel Jakub Dawidek static int
numfromstr(const char * str,intmax_t minnum,intmax_t maxnum,intmax_t * nump)72933728eeSPawel Jakub Dawidek numfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump)
73933728eeSPawel Jakub Dawidek {
74933728eeSPawel Jakub Dawidek 	intmax_t digit, num;
75933728eeSPawel Jakub Dawidek 
76933728eeSPawel Jakub Dawidek 	if (str[0] == '\0')
77933728eeSPawel Jakub Dawidek 		goto invalid;	/* Empty string. */
78933728eeSPawel Jakub Dawidek 	num = 0;
79933728eeSPawel Jakub Dawidek 	for (; *str != '\0'; str++) {
80933728eeSPawel Jakub Dawidek 		if (*str < '0' || *str > '9')
81933728eeSPawel Jakub Dawidek 			goto invalid;	/* Non-digit character. */
82933728eeSPawel Jakub Dawidek 		digit = *str - '0';
83933728eeSPawel Jakub Dawidek 		if (num > num * 10 + digit)
84933728eeSPawel Jakub Dawidek 			goto invalid;	/* Overflow. */
85933728eeSPawel Jakub Dawidek 		num = num * 10 + digit;
86933728eeSPawel Jakub Dawidek 		if (num > maxnum)
87933728eeSPawel Jakub Dawidek 			goto invalid;	/* Too big. */
88933728eeSPawel Jakub Dawidek 	}
89933728eeSPawel Jakub Dawidek 	if (num < minnum)
90933728eeSPawel Jakub Dawidek 		goto invalid;	/* Too small. */
91933728eeSPawel Jakub Dawidek 	*nump = num;
92933728eeSPawel Jakub Dawidek 	return (0);
93933728eeSPawel Jakub Dawidek invalid:
94933728eeSPawel Jakub Dawidek 	errno = EINVAL;
95933728eeSPawel Jakub Dawidek 	return (-1);
96933728eeSPawel Jakub Dawidek }
97933728eeSPawel Jakub Dawidek 
98933728eeSPawel Jakub Dawidek static int
tcp_addr(const char * addr,int defport,struct sockaddr_storage * sap)99dc18c8aeSPawel Jakub Dawidek tcp_addr(const char *addr, int defport, struct sockaddr_storage *sap)
100933728eeSPawel Jakub Dawidek {
101dc18c8aeSPawel Jakub Dawidek 	char iporhost[MAXHOSTNAMELEN], portstr[6];
102dc18c8aeSPawel Jakub Dawidek 	struct addrinfo hints;
103dc18c8aeSPawel Jakub Dawidek 	struct addrinfo *res;
104933728eeSPawel Jakub Dawidek 	const char *pp;
105dc18c8aeSPawel Jakub Dawidek 	intmax_t port;
106933728eeSPawel Jakub Dawidek 	size_t size;
107dc18c8aeSPawel Jakub Dawidek 	int error;
108933728eeSPawel Jakub Dawidek 
109933728eeSPawel Jakub Dawidek 	if (addr == NULL)
110933728eeSPawel Jakub Dawidek 		return (-1);
111933728eeSPawel Jakub Dawidek 
112dc18c8aeSPawel Jakub Dawidek 	bzero(&hints, sizeof(hints));
113dc18c8aeSPawel Jakub Dawidek 	hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
114dc18c8aeSPawel Jakub Dawidek 	hints.ai_family = PF_UNSPEC;
115dc18c8aeSPawel Jakub Dawidek 	hints.ai_socktype = SOCK_STREAM;
116dc18c8aeSPawel Jakub Dawidek 	hints.ai_protocol = IPPROTO_TCP;
117dc18c8aeSPawel Jakub Dawidek 
118dc18c8aeSPawel Jakub Dawidek 	if (strncasecmp(addr, "tcp4://", 7) == 0) {
119933728eeSPawel Jakub Dawidek 		addr += 7;
120dc18c8aeSPawel Jakub Dawidek 		hints.ai_family = PF_INET;
121dc18c8aeSPawel Jakub Dawidek 	} else if (strncasecmp(addr, "tcp6://", 7) == 0) {
122dc18c8aeSPawel Jakub Dawidek 		addr += 7;
123dc18c8aeSPawel Jakub Dawidek 		hints.ai_family = PF_INET6;
124dc18c8aeSPawel Jakub Dawidek 	} else if (strncasecmp(addr, "tcp://", 6) == 0) {
125933728eeSPawel Jakub Dawidek 		addr += 6;
126dc18c8aeSPawel Jakub Dawidek 	} else {
127933728eeSPawel Jakub Dawidek 		/*
128bdbd046bSPawel Jakub Dawidek 		 * Because TCP is the default assume IP or host is given without
129933728eeSPawel Jakub Dawidek 		 * prefix.
130933728eeSPawel Jakub Dawidek 		 */
131933728eeSPawel Jakub Dawidek 	}
132933728eeSPawel Jakub Dawidek 
133dc18c8aeSPawel Jakub Dawidek 	/*
134dc18c8aeSPawel Jakub Dawidek 	 * Extract optional port.
135dc18c8aeSPawel Jakub Dawidek 	 * There are three cases to consider.
136dc18c8aeSPawel Jakub Dawidek 	 * 1. hostname with port, eg. freefall.freebsd.org:8457
137dc18c8aeSPawel Jakub Dawidek 	 * 2. IPv4 address with port, eg. 192.168.0.101:8457
138dc18c8aeSPawel Jakub Dawidek 	 * 3. IPv6 address with port, eg. [fe80::1]:8457
139dc18c8aeSPawel Jakub Dawidek 	 * We discover IPv6 address by checking for two colons and if port is
140dc18c8aeSPawel Jakub Dawidek 	 * given, the address has to start with [.
141dc18c8aeSPawel Jakub Dawidek 	 */
142dc18c8aeSPawel Jakub Dawidek 	pp = NULL;
143dc18c8aeSPawel Jakub Dawidek 	if (strchr(addr, ':') != strrchr(addr, ':')) {
144dc18c8aeSPawel Jakub Dawidek 		if (addr[0] == '[')
145933728eeSPawel Jakub Dawidek 			pp = strrchr(addr, ':');
146dc18c8aeSPawel Jakub Dawidek 	} else {
147dc18c8aeSPawel Jakub Dawidek 		pp = strrchr(addr, ':');
148dc18c8aeSPawel Jakub Dawidek 	}
149933728eeSPawel Jakub Dawidek 	if (pp == NULL) {
150933728eeSPawel Jakub Dawidek 		/* Port not given, use the default. */
151dc18c8aeSPawel Jakub Dawidek 		port = defport;
152933728eeSPawel Jakub Dawidek 	} else {
1532b1b224dSPawel Jakub Dawidek 		if (numfromstr(pp + 1, 1, 65535, &port) == -1)
154933728eeSPawel Jakub Dawidek 			return (errno);
155933728eeSPawel Jakub Dawidek 	}
156dc18c8aeSPawel Jakub Dawidek 	(void)snprintf(portstr, sizeof(portstr), "%jd", (intmax_t)port);
157933728eeSPawel Jakub Dawidek 	/* Extract host name or IP address. */
158933728eeSPawel Jakub Dawidek 	if (pp == NULL) {
159933728eeSPawel Jakub Dawidek 		size = sizeof(iporhost);
160933728eeSPawel Jakub Dawidek 		if (strlcpy(iporhost, addr, size) >= size)
161933728eeSPawel Jakub Dawidek 			return (ENAMETOOLONG);
162dc18c8aeSPawel Jakub Dawidek 	} else if (addr[0] == '[' && pp[-1] == ']') {
163dc18c8aeSPawel Jakub Dawidek 		size = (size_t)(pp - addr - 2 + 1);
164dc18c8aeSPawel Jakub Dawidek 		if (size > sizeof(iporhost))
165dc18c8aeSPawel Jakub Dawidek 			return (ENAMETOOLONG);
166dc18c8aeSPawel Jakub Dawidek 		(void)strlcpy(iporhost, addr + 1, size);
167933728eeSPawel Jakub Dawidek 	} else {
168933728eeSPawel Jakub Dawidek 		size = (size_t)(pp - addr + 1);
169933728eeSPawel Jakub Dawidek 		if (size > sizeof(iporhost))
170933728eeSPawel Jakub Dawidek 			return (ENAMETOOLONG);
171933728eeSPawel Jakub Dawidek 		(void)strlcpy(iporhost, addr, size);
172933728eeSPawel Jakub Dawidek 	}
173dc18c8aeSPawel Jakub Dawidek 
174dc18c8aeSPawel Jakub Dawidek 	error = getaddrinfo(iporhost, portstr, &hints, &res);
175dc18c8aeSPawel Jakub Dawidek 	if (error != 0) {
176dc18c8aeSPawel Jakub Dawidek 		pjdlog_debug(1, "getaddrinfo(%s, %s) failed: %s.", iporhost,
177dc18c8aeSPawel Jakub Dawidek 		    portstr, gai_strerror(error));
178933728eeSPawel Jakub Dawidek 		return (EINVAL);
179dc18c8aeSPawel Jakub Dawidek 	}
180dc18c8aeSPawel Jakub Dawidek 	if (res == NULL)
181dc18c8aeSPawel Jakub Dawidek 		return (ENOENT);
182dc18c8aeSPawel Jakub Dawidek 
183dc18c8aeSPawel Jakub Dawidek 	memcpy(sap, res->ai_addr, res->ai_addrlen);
184dc18c8aeSPawel Jakub Dawidek 
185dc18c8aeSPawel Jakub Dawidek 	freeaddrinfo(res);
186933728eeSPawel Jakub Dawidek 
187933728eeSPawel Jakub Dawidek 	return (0);
188933728eeSPawel Jakub Dawidek }
189933728eeSPawel Jakub Dawidek 
190933728eeSPawel Jakub Dawidek static int
tcp_setup_new(const char * addr,int side,void ** ctxp)191bdbd046bSPawel Jakub Dawidek tcp_setup_new(const char *addr, int side, void **ctxp)
192933728eeSPawel Jakub Dawidek {
193bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx;
194933728eeSPawel Jakub Dawidek 	int ret, nodelay;
195933728eeSPawel Jakub Dawidek 
196933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(addr != NULL);
197bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
198bdbd046bSPawel Jakub Dawidek 	    side == TCP_SIDE_SERVER_LISTEN);
199933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(ctxp != NULL);
200933728eeSPawel Jakub Dawidek 
201933728eeSPawel Jakub Dawidek 	tctx = malloc(sizeof(*tctx));
202933728eeSPawel Jakub Dawidek 	if (tctx == NULL)
203933728eeSPawel Jakub Dawidek 		return (errno);
204933728eeSPawel Jakub Dawidek 
205933728eeSPawel Jakub Dawidek 	/* Parse given address. */
206dc18c8aeSPawel Jakub Dawidek 	if ((ret = tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &tctx->tc_sa)) != 0) {
207933728eeSPawel Jakub Dawidek 		free(tctx);
208933728eeSPawel Jakub Dawidek 		return (ret);
209933728eeSPawel Jakub Dawidek 	}
210933728eeSPawel Jakub Dawidek 
211dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
212933728eeSPawel Jakub Dawidek 
213dc18c8aeSPawel Jakub Dawidek 	tctx->tc_fd = socket(tctx->tc_sa.ss_family, SOCK_STREAM, 0);
214933728eeSPawel Jakub Dawidek 	if (tctx->tc_fd == -1) {
215933728eeSPawel Jakub Dawidek 		ret = errno;
216933728eeSPawel Jakub Dawidek 		free(tctx);
217933728eeSPawel Jakub Dawidek 		return (ret);
218933728eeSPawel Jakub Dawidek 	}
219933728eeSPawel Jakub Dawidek 
220dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
221933728eeSPawel Jakub Dawidek 
222933728eeSPawel Jakub Dawidek 	/* Socket settings. */
223933728eeSPawel Jakub Dawidek 	nodelay = 1;
224933728eeSPawel Jakub Dawidek 	if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay,
225933728eeSPawel Jakub Dawidek 	    sizeof(nodelay)) == -1) {
226933728eeSPawel Jakub Dawidek 		pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY");
227933728eeSPawel Jakub Dawidek 	}
228933728eeSPawel Jakub Dawidek 
229933728eeSPawel Jakub Dawidek 	tctx->tc_side = side;
230bdbd046bSPawel Jakub Dawidek 	tctx->tc_magic = TCP_CTX_MAGIC;
231933728eeSPawel Jakub Dawidek 	*ctxp = tctx;
232933728eeSPawel Jakub Dawidek 
233933728eeSPawel Jakub Dawidek 	return (0);
234933728eeSPawel Jakub Dawidek }
235933728eeSPawel Jakub Dawidek 
236933728eeSPawel Jakub Dawidek static int
tcp_setup_wrap(int fd,int side,void ** ctxp)237bdbd046bSPawel Jakub Dawidek tcp_setup_wrap(int fd, int side, void **ctxp)
238933728eeSPawel Jakub Dawidek {
239bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx;
240933728eeSPawel Jakub Dawidek 
241933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(fd >= 0);
242bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
243bdbd046bSPawel Jakub Dawidek 	    side == TCP_SIDE_SERVER_WORK);
244933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(ctxp != NULL);
245933728eeSPawel Jakub Dawidek 
246933728eeSPawel Jakub Dawidek 	tctx = malloc(sizeof(*tctx));
247933728eeSPawel Jakub Dawidek 	if (tctx == NULL)
248933728eeSPawel Jakub Dawidek 		return (errno);
249933728eeSPawel Jakub Dawidek 
250933728eeSPawel Jakub Dawidek 	tctx->tc_fd = fd;
251dc18c8aeSPawel Jakub Dawidek 	tctx->tc_sa.ss_family = AF_UNSPEC;
252933728eeSPawel Jakub Dawidek 	tctx->tc_side = side;
253bdbd046bSPawel Jakub Dawidek 	tctx->tc_magic = TCP_CTX_MAGIC;
254933728eeSPawel Jakub Dawidek 	*ctxp = tctx;
255933728eeSPawel Jakub Dawidek 
256933728eeSPawel Jakub Dawidek 	return (0);
257933728eeSPawel Jakub Dawidek }
258933728eeSPawel Jakub Dawidek 
259933728eeSPawel Jakub Dawidek static int
tcp_client(const char * srcaddr,const char * dstaddr,void ** ctxp)260bdbd046bSPawel Jakub Dawidek tcp_client(const char *srcaddr, const char *dstaddr, void **ctxp)
261933728eeSPawel Jakub Dawidek {
262bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx;
263dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage sa;
264933728eeSPawel Jakub Dawidek 	int ret;
265933728eeSPawel Jakub Dawidek 
266bdbd046bSPawel Jakub Dawidek 	ret = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, ctxp);
267933728eeSPawel Jakub Dawidek 	if (ret != 0)
268933728eeSPawel Jakub Dawidek 		return (ret);
269933728eeSPawel Jakub Dawidek 	tctx = *ctxp;
270933728eeSPawel Jakub Dawidek 	if (srcaddr == NULL)
271933728eeSPawel Jakub Dawidek 		return (0);
272dc18c8aeSPawel Jakub Dawidek 	ret = tcp_addr(srcaddr, 0, &sa);
273933728eeSPawel Jakub Dawidek 	if (ret != 0) {
274bdbd046bSPawel Jakub Dawidek 		tcp_close(tctx);
275933728eeSPawel Jakub Dawidek 		return (ret);
276933728eeSPawel Jakub Dawidek 	}
2772b1b224dSPawel Jakub Dawidek 	if (bind(tctx->tc_fd, (struct sockaddr *)&sa, sa.ss_len) == -1) {
278933728eeSPawel Jakub Dawidek 		ret = errno;
279bdbd046bSPawel Jakub Dawidek 		tcp_close(tctx);
280933728eeSPawel Jakub Dawidek 		return (ret);
281933728eeSPawel Jakub Dawidek 	}
282933728eeSPawel Jakub Dawidek 	return (0);
283933728eeSPawel Jakub Dawidek }
284933728eeSPawel Jakub Dawidek 
285933728eeSPawel Jakub Dawidek static int
tcp_connect(void * ctx,int timeout)286bdbd046bSPawel Jakub Dawidek tcp_connect(void *ctx, int timeout)
287933728eeSPawel Jakub Dawidek {
288bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
289933728eeSPawel Jakub Dawidek 	int error, flags;
290933728eeSPawel Jakub Dawidek 
291933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
292bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
293bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
294933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
295dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
296933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(timeout >= -1);
297933728eeSPawel Jakub Dawidek 
298933728eeSPawel Jakub Dawidek 	flags = fcntl(tctx->tc_fd, F_GETFL);
299933728eeSPawel Jakub Dawidek 	if (flags == -1) {
3001ebc0407SPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
301933728eeSPawel Jakub Dawidek 		return (errno);
302933728eeSPawel Jakub Dawidek 	}
303933728eeSPawel Jakub Dawidek 	/*
304933728eeSPawel Jakub Dawidek 	 * We make socket non-blocking so we can handle connection timeout
305933728eeSPawel Jakub Dawidek 	 * manually.
306933728eeSPawel Jakub Dawidek 	 */
307933728eeSPawel Jakub Dawidek 	flags |= O_NONBLOCK;
308933728eeSPawel Jakub Dawidek 	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
3091ebc0407SPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno,
3101ebc0407SPawel Jakub Dawidek 		    "fcntl(F_SETFL, O_NONBLOCK) failed");
311933728eeSPawel Jakub Dawidek 		return (errno);
312933728eeSPawel Jakub Dawidek 	}
313933728eeSPawel Jakub Dawidek 
314dc18c8aeSPawel Jakub Dawidek 	if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
315dc18c8aeSPawel Jakub Dawidek 	    tctx->tc_sa.ss_len) == 0) {
316933728eeSPawel Jakub Dawidek 		if (timeout == -1)
317933728eeSPawel Jakub Dawidek 			return (0);
318933728eeSPawel Jakub Dawidek 		error = 0;
319933728eeSPawel Jakub Dawidek 		goto done;
320933728eeSPawel Jakub Dawidek 	}
321933728eeSPawel Jakub Dawidek 	if (errno != EINPROGRESS) {
322933728eeSPawel Jakub Dawidek 		error = errno;
323933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
324933728eeSPawel Jakub Dawidek 		goto done;
325933728eeSPawel Jakub Dawidek 	}
326933728eeSPawel Jakub Dawidek 	if (timeout == -1)
327933728eeSPawel Jakub Dawidek 		return (0);
328bdbd046bSPawel Jakub Dawidek 	return (tcp_connect_wait(ctx, timeout));
329933728eeSPawel Jakub Dawidek done:
330933728eeSPawel Jakub Dawidek 	flags &= ~O_NONBLOCK;
331933728eeSPawel Jakub Dawidek 	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
332933728eeSPawel Jakub Dawidek 		if (error == 0)
333933728eeSPawel Jakub Dawidek 			error = errno;
334933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno,
335933728eeSPawel Jakub Dawidek 		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
336933728eeSPawel Jakub Dawidek 	}
337933728eeSPawel Jakub Dawidek 	return (error);
338933728eeSPawel Jakub Dawidek }
339933728eeSPawel Jakub Dawidek 
340933728eeSPawel Jakub Dawidek static int
tcp_connect_wait(void * ctx,int timeout)341bdbd046bSPawel Jakub Dawidek tcp_connect_wait(void *ctx, int timeout)
342933728eeSPawel Jakub Dawidek {
343bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
344933728eeSPawel Jakub Dawidek 	struct timeval tv;
345933728eeSPawel Jakub Dawidek 	fd_set fdset;
346933728eeSPawel Jakub Dawidek 	socklen_t esize;
347933728eeSPawel Jakub Dawidek 	int error, flags, ret;
348933728eeSPawel Jakub Dawidek 
349933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
350bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
351bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
352933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
353933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(timeout >= 0);
354933728eeSPawel Jakub Dawidek 
355933728eeSPawel Jakub Dawidek 	tv.tv_sec = timeout;
356933728eeSPawel Jakub Dawidek 	tv.tv_usec = 0;
357933728eeSPawel Jakub Dawidek again:
358933728eeSPawel Jakub Dawidek 	FD_ZERO(&fdset);
359933728eeSPawel Jakub Dawidek 	FD_SET(tctx->tc_fd, &fdset);
360933728eeSPawel Jakub Dawidek 	ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
361933728eeSPawel Jakub Dawidek 	if (ret == 0) {
362933728eeSPawel Jakub Dawidek 		error = ETIMEDOUT;
363933728eeSPawel Jakub Dawidek 		goto done;
364933728eeSPawel Jakub Dawidek 	} else if (ret == -1) {
365933728eeSPawel Jakub Dawidek 		if (errno == EINTR)
366933728eeSPawel Jakub Dawidek 			goto again;
367933728eeSPawel Jakub Dawidek 		error = errno;
368933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
369933728eeSPawel Jakub Dawidek 		goto done;
370933728eeSPawel Jakub Dawidek 	}
371933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(ret > 0);
372933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset));
373933728eeSPawel Jakub Dawidek 	esize = sizeof(error);
374933728eeSPawel Jakub Dawidek 	if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
375933728eeSPawel Jakub Dawidek 	    &esize) == -1) {
376933728eeSPawel Jakub Dawidek 		error = errno;
377933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno,
378933728eeSPawel Jakub Dawidek 		    "getsockopt(SO_ERROR) failed");
379933728eeSPawel Jakub Dawidek 		goto done;
380933728eeSPawel Jakub Dawidek 	}
381933728eeSPawel Jakub Dawidek 	if (error != 0) {
382933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, error,
383933728eeSPawel Jakub Dawidek 		    "getsockopt(SO_ERROR) returned error");
384933728eeSPawel Jakub Dawidek 		goto done;
385933728eeSPawel Jakub Dawidek 	}
386933728eeSPawel Jakub Dawidek 	error = 0;
387933728eeSPawel Jakub Dawidek done:
388933728eeSPawel Jakub Dawidek 	flags = fcntl(tctx->tc_fd, F_GETFL);
389933728eeSPawel Jakub Dawidek 	if (flags == -1) {
390933728eeSPawel Jakub Dawidek 		if (error == 0)
391933728eeSPawel Jakub Dawidek 			error = errno;
392933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
393933728eeSPawel Jakub Dawidek 		return (error);
394933728eeSPawel Jakub Dawidek 	}
395933728eeSPawel Jakub Dawidek 	flags &= ~O_NONBLOCK;
396933728eeSPawel Jakub Dawidek 	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
397933728eeSPawel Jakub Dawidek 		if (error == 0)
398933728eeSPawel Jakub Dawidek 			error = errno;
399933728eeSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno,
400933728eeSPawel Jakub Dawidek 		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
401933728eeSPawel Jakub Dawidek 	}
402933728eeSPawel Jakub Dawidek 	return (error);
403933728eeSPawel Jakub Dawidek }
404933728eeSPawel Jakub Dawidek 
405933728eeSPawel Jakub Dawidek static int
tcp_server(const char * addr,void ** ctxp)406bdbd046bSPawel Jakub Dawidek tcp_server(const char *addr, void **ctxp)
407933728eeSPawel Jakub Dawidek {
408bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx;
409933728eeSPawel Jakub Dawidek 	int ret, val;
410933728eeSPawel Jakub Dawidek 
411bdbd046bSPawel Jakub Dawidek 	ret = tcp_setup_new(addr, TCP_SIDE_SERVER_LISTEN, ctxp);
412933728eeSPawel Jakub Dawidek 	if (ret != 0)
413933728eeSPawel Jakub Dawidek 		return (ret);
414933728eeSPawel Jakub Dawidek 
415933728eeSPawel Jakub Dawidek 	tctx = *ctxp;
416933728eeSPawel Jakub Dawidek 
417933728eeSPawel Jakub Dawidek 	val = 1;
418933728eeSPawel Jakub Dawidek 	/* Ignore failure. */
419933728eeSPawel Jakub Dawidek 	(void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
420933728eeSPawel Jakub Dawidek 	   sizeof(val));
421933728eeSPawel Jakub Dawidek 
422dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
423933728eeSPawel Jakub Dawidek 
424dc18c8aeSPawel Jakub Dawidek 	if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
4252b1b224dSPawel Jakub Dawidek 	    tctx->tc_sa.ss_len) == -1) {
426933728eeSPawel Jakub Dawidek 		ret = errno;
427bdbd046bSPawel Jakub Dawidek 		tcp_close(tctx);
428933728eeSPawel Jakub Dawidek 		return (ret);
429933728eeSPawel Jakub Dawidek 	}
4302b1b224dSPawel Jakub Dawidek 	if (listen(tctx->tc_fd, 8) == -1) {
431933728eeSPawel Jakub Dawidek 		ret = errno;
432bdbd046bSPawel Jakub Dawidek 		tcp_close(tctx);
433933728eeSPawel Jakub Dawidek 		return (ret);
434933728eeSPawel Jakub Dawidek 	}
435933728eeSPawel Jakub Dawidek 
436933728eeSPawel Jakub Dawidek 	return (0);
437933728eeSPawel Jakub Dawidek }
438933728eeSPawel Jakub Dawidek 
439933728eeSPawel Jakub Dawidek static int
tcp_accept(void * ctx,void ** newctxp)440bdbd046bSPawel Jakub Dawidek tcp_accept(void *ctx, void **newctxp)
441933728eeSPawel Jakub Dawidek {
442bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
443bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *newtctx;
444933728eeSPawel Jakub Dawidek 	socklen_t fromlen;
445933728eeSPawel Jakub Dawidek 	int ret;
446933728eeSPawel Jakub Dawidek 
447933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
448bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
449bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN);
450933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
451dc18c8aeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
452933728eeSPawel Jakub Dawidek 
453933728eeSPawel Jakub Dawidek 	newtctx = malloc(sizeof(*newtctx));
454933728eeSPawel Jakub Dawidek 	if (newtctx == NULL)
455933728eeSPawel Jakub Dawidek 		return (errno);
456933728eeSPawel Jakub Dawidek 
457dc18c8aeSPawel Jakub Dawidek 	fromlen = tctx->tc_sa.ss_len;
458dc18c8aeSPawel Jakub Dawidek 	newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
459933728eeSPawel Jakub Dawidek 	    &fromlen);
4602b1b224dSPawel Jakub Dawidek 	if (newtctx->tc_fd == -1) {
461933728eeSPawel Jakub Dawidek 		ret = errno;
462933728eeSPawel Jakub Dawidek 		free(newtctx);
463933728eeSPawel Jakub Dawidek 		return (ret);
464933728eeSPawel Jakub Dawidek 	}
465933728eeSPawel Jakub Dawidek 
466bdbd046bSPawel Jakub Dawidek 	newtctx->tc_side = TCP_SIDE_SERVER_WORK;
467bdbd046bSPawel Jakub Dawidek 	newtctx->tc_magic = TCP_CTX_MAGIC;
468933728eeSPawel Jakub Dawidek 	*newctxp = newtctx;
469933728eeSPawel Jakub Dawidek 
470933728eeSPawel Jakub Dawidek 	return (0);
471933728eeSPawel Jakub Dawidek }
472933728eeSPawel Jakub Dawidek 
473933728eeSPawel Jakub Dawidek static int
tcp_wrap(int fd,bool client,void ** ctxp)474bdbd046bSPawel Jakub Dawidek tcp_wrap(int fd, bool client, void **ctxp)
475933728eeSPawel Jakub Dawidek {
476933728eeSPawel Jakub Dawidek 
477bdbd046bSPawel Jakub Dawidek 	return (tcp_setup_wrap(fd,
478bdbd046bSPawel Jakub Dawidek 	    client ? TCP_SIDE_CLIENT : TCP_SIDE_SERVER_WORK, ctxp));
479933728eeSPawel Jakub Dawidek }
480933728eeSPawel Jakub Dawidek 
481933728eeSPawel Jakub Dawidek static int
tcp_send(void * ctx,const unsigned char * data,size_t size,int fd)482bdbd046bSPawel Jakub Dawidek tcp_send(void *ctx, const unsigned char *data, size_t size, int fd)
483933728eeSPawel Jakub Dawidek {
484bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
485933728eeSPawel Jakub Dawidek 
486933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
487bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
488933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
489933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(fd == -1);
490933728eeSPawel Jakub Dawidek 
491933728eeSPawel Jakub Dawidek 	return (proto_common_send(tctx->tc_fd, data, size, -1));
492933728eeSPawel Jakub Dawidek }
493933728eeSPawel Jakub Dawidek 
494933728eeSPawel Jakub Dawidek static int
tcp_recv(void * ctx,unsigned char * data,size_t size,int * fdp)495bdbd046bSPawel Jakub Dawidek tcp_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
496933728eeSPawel Jakub Dawidek {
497bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
498933728eeSPawel Jakub Dawidek 
499933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
500bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
501933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
502933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(fdp == NULL);
503933728eeSPawel Jakub Dawidek 
504933728eeSPawel Jakub Dawidek 	return (proto_common_recv(tctx->tc_fd, data, size, NULL));
505933728eeSPawel Jakub Dawidek }
506933728eeSPawel Jakub Dawidek 
507933728eeSPawel Jakub Dawidek static int
tcp_descriptor(const void * ctx)508bdbd046bSPawel Jakub Dawidek tcp_descriptor(const void *ctx)
509933728eeSPawel Jakub Dawidek {
510bdbd046bSPawel Jakub Dawidek 	const struct tcp_ctx *tctx = ctx;
511933728eeSPawel Jakub Dawidek 
512933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
513bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
514933728eeSPawel Jakub Dawidek 
515933728eeSPawel Jakub Dawidek 	return (tctx->tc_fd);
516933728eeSPawel Jakub Dawidek }
517933728eeSPawel Jakub Dawidek 
518933728eeSPawel Jakub Dawidek static bool
tcp_address_match(const void * ctx,const char * addr)519bdbd046bSPawel Jakub Dawidek tcp_address_match(const void *ctx, const char *addr)
520933728eeSPawel Jakub Dawidek {
521bdbd046bSPawel Jakub Dawidek 	const struct tcp_ctx *tctx = ctx;
522dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage sa1, sa2;
523dc18c8aeSPawel Jakub Dawidek 	socklen_t salen;
524933728eeSPawel Jakub Dawidek 
525933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
526bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
527933728eeSPawel Jakub Dawidek 
528dc18c8aeSPawel Jakub Dawidek 	if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sa1) != 0)
529933728eeSPawel Jakub Dawidek 		return (false);
530933728eeSPawel Jakub Dawidek 
531dc18c8aeSPawel Jakub Dawidek 	salen = sizeof(sa2);
5322b1b224dSPawel Jakub Dawidek 	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) == -1)
533933728eeSPawel Jakub Dawidek 		return (false);
534933728eeSPawel Jakub Dawidek 
535dc18c8aeSPawel Jakub Dawidek 	if (sa1.ss_family != sa2.ss_family || sa1.ss_len != sa2.ss_len)
536dc18c8aeSPawel Jakub Dawidek 		return (false);
537dc18c8aeSPawel Jakub Dawidek 
538dc18c8aeSPawel Jakub Dawidek 	switch (sa1.ss_family) {
539dc18c8aeSPawel Jakub Dawidek 	case AF_INET:
540dc18c8aeSPawel Jakub Dawidek 	    {
541dc18c8aeSPawel Jakub Dawidek 		struct sockaddr_in *sin1, *sin2;
542dc18c8aeSPawel Jakub Dawidek 
543dc18c8aeSPawel Jakub Dawidek 		sin1 = (struct sockaddr_in *)&sa1;
544dc18c8aeSPawel Jakub Dawidek 		sin2 = (struct sockaddr_in *)&sa2;
545dc18c8aeSPawel Jakub Dawidek 
546dc18c8aeSPawel Jakub Dawidek 		return (memcmp(&sin1->sin_addr, &sin2->sin_addr,
547dc18c8aeSPawel Jakub Dawidek 		    sizeof(sin1->sin_addr)) == 0);
548dc18c8aeSPawel Jakub Dawidek 	    }
549dc18c8aeSPawel Jakub Dawidek 	case AF_INET6:
550dc18c8aeSPawel Jakub Dawidek 	    {
551dc18c8aeSPawel Jakub Dawidek 		struct sockaddr_in6 *sin1, *sin2;
552dc18c8aeSPawel Jakub Dawidek 
553dc18c8aeSPawel Jakub Dawidek 		sin1 = (struct sockaddr_in6 *)&sa1;
554dc18c8aeSPawel Jakub Dawidek 		sin2 = (struct sockaddr_in6 *)&sa2;
555dc18c8aeSPawel Jakub Dawidek 
556dc18c8aeSPawel Jakub Dawidek 		return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
557dc18c8aeSPawel Jakub Dawidek 		    sizeof(sin1->sin6_addr)) == 0);
558dc18c8aeSPawel Jakub Dawidek 	    }
559dc18c8aeSPawel Jakub Dawidek 	default:
560dc18c8aeSPawel Jakub Dawidek 		return (false);
561dc18c8aeSPawel Jakub Dawidek 	}
562933728eeSPawel Jakub Dawidek }
563933728eeSPawel Jakub Dawidek 
564933728eeSPawel Jakub Dawidek static void
tcp_local_address(const void * ctx,char * addr,size_t size)565bdbd046bSPawel Jakub Dawidek tcp_local_address(const void *ctx, char *addr, size_t size)
566933728eeSPawel Jakub Dawidek {
567bdbd046bSPawel Jakub Dawidek 	const struct tcp_ctx *tctx = ctx;
568dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage sa;
569dc18c8aeSPawel Jakub Dawidek 	socklen_t salen;
570933728eeSPawel Jakub Dawidek 
571933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
572bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
573933728eeSPawel Jakub Dawidek 
574dc18c8aeSPawel Jakub Dawidek 	salen = sizeof(sa);
5752b1b224dSPawel Jakub Dawidek 	if (getsockname(tctx->tc_fd, (struct sockaddr *)&sa, &salen) == -1) {
576933728eeSPawel Jakub Dawidek 		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
577933728eeSPawel Jakub Dawidek 		return;
578933728eeSPawel Jakub Dawidek 	}
579dc18c8aeSPawel Jakub Dawidek 	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
580933728eeSPawel Jakub Dawidek }
581933728eeSPawel Jakub Dawidek 
582933728eeSPawel Jakub Dawidek static void
tcp_remote_address(const void * ctx,char * addr,size_t size)583bdbd046bSPawel Jakub Dawidek tcp_remote_address(const void *ctx, char *addr, size_t size)
584933728eeSPawel Jakub Dawidek {
585bdbd046bSPawel Jakub Dawidek 	const struct tcp_ctx *tctx = ctx;
586dc18c8aeSPawel Jakub Dawidek 	struct sockaddr_storage sa;
587dc18c8aeSPawel Jakub Dawidek 	socklen_t salen;
588933728eeSPawel Jakub Dawidek 
589933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
590bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
591933728eeSPawel Jakub Dawidek 
592dc18c8aeSPawel Jakub Dawidek 	salen = sizeof(sa);
5932b1b224dSPawel Jakub Dawidek 	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) == -1) {
594933728eeSPawel Jakub Dawidek 		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
595933728eeSPawel Jakub Dawidek 		return;
596933728eeSPawel Jakub Dawidek 	}
597dc18c8aeSPawel Jakub Dawidek 	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
598933728eeSPawel Jakub Dawidek }
599933728eeSPawel Jakub Dawidek 
600933728eeSPawel Jakub Dawidek static void
tcp_close(void * ctx)601bdbd046bSPawel Jakub Dawidek tcp_close(void *ctx)
602933728eeSPawel Jakub Dawidek {
603bdbd046bSPawel Jakub Dawidek 	struct tcp_ctx *tctx = ctx;
604933728eeSPawel Jakub Dawidek 
605933728eeSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx != NULL);
606bdbd046bSPawel Jakub Dawidek 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
607933728eeSPawel Jakub Dawidek 
608933728eeSPawel Jakub Dawidek 	if (tctx->tc_fd >= 0)
609933728eeSPawel Jakub Dawidek 		close(tctx->tc_fd);
610933728eeSPawel Jakub Dawidek 	tctx->tc_magic = 0;
611933728eeSPawel Jakub Dawidek 	free(tctx);
612933728eeSPawel Jakub Dawidek }
613933728eeSPawel Jakub Dawidek 
614bdbd046bSPawel Jakub Dawidek static struct proto tcp_proto = {
615bdbd046bSPawel Jakub Dawidek 	.prt_name = "tcp",
616bdbd046bSPawel Jakub Dawidek 	.prt_client = tcp_client,
617bdbd046bSPawel Jakub Dawidek 	.prt_connect = tcp_connect,
618bdbd046bSPawel Jakub Dawidek 	.prt_connect_wait = tcp_connect_wait,
619bdbd046bSPawel Jakub Dawidek 	.prt_server = tcp_server,
620bdbd046bSPawel Jakub Dawidek 	.prt_accept = tcp_accept,
621bdbd046bSPawel Jakub Dawidek 	.prt_wrap = tcp_wrap,
622bdbd046bSPawel Jakub Dawidek 	.prt_send = tcp_send,
623bdbd046bSPawel Jakub Dawidek 	.prt_recv = tcp_recv,
624bdbd046bSPawel Jakub Dawidek 	.prt_descriptor = tcp_descriptor,
625bdbd046bSPawel Jakub Dawidek 	.prt_address_match = tcp_address_match,
626bdbd046bSPawel Jakub Dawidek 	.prt_local_address = tcp_local_address,
627bdbd046bSPawel Jakub Dawidek 	.prt_remote_address = tcp_remote_address,
628bdbd046bSPawel Jakub Dawidek 	.prt_close = tcp_close
629933728eeSPawel Jakub Dawidek };
630933728eeSPawel Jakub Dawidek 
631933728eeSPawel Jakub Dawidek static __constructor void
tcp_ctor(void)632bdbd046bSPawel Jakub Dawidek tcp_ctor(void)
633933728eeSPawel Jakub Dawidek {
634933728eeSPawel Jakub Dawidek 
635bdbd046bSPawel Jakub Dawidek 	proto_register(&tcp_proto, true);
636933728eeSPawel Jakub Dawidek }
637