proto_tcp.c revision 229509
1126756Smlaier/*-
2244647Sdelphij * Copyright (c) 2009-2010 The FreeBSD Foundation
3126756Smlaier * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
4126756Smlaier * All rights reserved.
5126756Smlaier *
6126756Smlaier * This software was developed by Pawel Jakub Dawidek under sponsorship from
7126756Smlaier * the FreeBSD Foundation.
8126756Smlaier *
9126756Smlaier * Redistribution and use in source and binary forms, with or without
10126756Smlaier * modification, are permitted provided that the following conditions
11126756Smlaier * are met:
12126756Smlaier * 1. Redistributions of source code must retain the above copyright
13126756Smlaier *    notice, this list of conditions and the following disclaimer.
14126756Smlaier * 2. Redistributions in binary form must reproduce the above copyright
15126756Smlaier *    notice, this list of conditions and the following disclaimer in the
16126756Smlaier *    documentation and/or other materials provided with the distribution.
17126756Smlaier *
18126756Smlaier * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19126756Smlaier * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20126756Smlaier * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21126756Smlaier * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22126756Smlaier * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23126756Smlaier * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24126756Smlaier * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25135183Smlaier * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26135183Smlaier * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27126756Smlaier * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28126756Smlaier * SUCH DAMAGE.
29126756Smlaier */
30126756Smlaier
31126756Smlaier#include <sys/cdefs.h>
32126756Smlaier__FBSDID("$FreeBSD: stable/9/sbin/hastd/proto_tcp.c 229509 2012-01-04 17:22:10Z trociny $");
33126756Smlaier
34126756Smlaier#include <sys/param.h>	/* MAXHOSTNAMELEN */
35126756Smlaier#include <sys/socket.h>
36126756Smlaier
37126756Smlaier#include <arpa/inet.h>
38126756Smlaier
39126756Smlaier#include <netinet/in.h>
40126756Smlaier#include <netinet/tcp.h>
41126756Smlaier
42126756Smlaier#include <errno.h>
43126756Smlaier#include <fcntl.h>
44126756Smlaier#include <netdb.h>
45126756Smlaier#include <stdbool.h>
46126756Smlaier#include <stdint.h>
47126756Smlaier#include <stdio.h>
48126756Smlaier#include <string.h>
49126756Smlaier#include <unistd.h>
50126756Smlaier
51126756Smlaier#include "pjdlog.h"
52126756Smlaier#include "proto_impl.h"
53126756Smlaier#include "subr.h"
54126756Smlaier
55126756Smlaier#define	TCP_CTX_MAGIC	0x7c41c
56126756Smlaierstruct tcp_ctx {
57126756Smlaier	int			tc_magic;
58126756Smlaier	struct sockaddr_storage	tc_sa;
59126756Smlaier	int			tc_fd;
60126756Smlaier	int			tc_side;
61126756Smlaier#define	TCP_SIDE_CLIENT		0
62126756Smlaier#define	TCP_SIDE_SERVER_LISTEN	1
63126756Smlaier#define	TCP_SIDE_SERVER_WORK	2
64126756Smlaier};
65126756Smlaier
66126756Smlaierstatic int tcp_connect_wait(void *ctx, int timeout);
67126756Smlaierstatic void tcp_close(void *ctx);
68126756Smlaier
69126756Smlaier/*
70126756Smlaier * Function converts the given string to unsigned number.
71126756Smlaier */
72126756Smlaierstatic int
73126756Smlaiernumfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump)
74126756Smlaier{
75126756Smlaier	intmax_t digit, num;
76126756Smlaier
77126756Smlaier	if (str[0] == '\0')
78126756Smlaier		goto invalid;	/* Empty string. */
79126756Smlaier	num = 0;
80126756Smlaier	for (; *str != '\0'; str++) {
81126756Smlaier		if (*str < '0' || *str > '9')
82126756Smlaier			goto invalid;	/* Non-digit character. */
83126756Smlaier		digit = *str - '0';
84126756Smlaier		if (num > num * 10 + digit)
85126756Smlaier			goto invalid;	/* Overflow. */
86126756Smlaier		num = num * 10 + digit;
87126756Smlaier		if (num > maxnum)
88126756Smlaier			goto invalid;	/* Too big. */
89126756Smlaier	}
90126756Smlaier	if (num < minnum)
91126756Smlaier		goto invalid;	/* Too small. */
92126756Smlaier	*nump = num;
93126756Smlaier	return (0);
94126756Smlaierinvalid:
95126756Smlaier	errno = EINVAL;
96126756Smlaier	return (-1);
97126756Smlaier}
98126756Smlaier
99126756Smlaierstatic int
100126756Smlaiertcp_addr(const char *addr, int defport, struct sockaddr_storage *sap)
101126756Smlaier{
102126756Smlaier	char iporhost[MAXHOSTNAMELEN], portstr[6];
103126756Smlaier	struct addrinfo hints;
104126756Smlaier	struct addrinfo *res;
105126756Smlaier	const char *pp;
106126756Smlaier	intmax_t port;
107126756Smlaier	size_t size;
108126756Smlaier	int error;
109126756Smlaier
110126756Smlaier	if (addr == NULL)
111126756Smlaier		return (-1);
112126756Smlaier
113126756Smlaier	bzero(&hints, sizeof(hints));
114126756Smlaier	hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
115126756Smlaier	hints.ai_family = PF_UNSPEC;
116126756Smlaier	hints.ai_socktype = SOCK_STREAM;
117126756Smlaier	hints.ai_protocol = IPPROTO_TCP;
118126756Smlaier
119126756Smlaier	if (strncasecmp(addr, "tcp4://", 7) == 0) {
120126756Smlaier		addr += 7;
121126756Smlaier		hints.ai_family = PF_INET;
122126756Smlaier	} else if (strncasecmp(addr, "tcp6://", 7) == 0) {
123126756Smlaier		addr += 7;
124126756Smlaier		hints.ai_family = PF_INET6;
125126756Smlaier	} else if (strncasecmp(addr, "tcp://", 6) == 0) {
126126756Smlaier		addr += 6;
127126756Smlaier	} else {
128126756Smlaier		/*
129126756Smlaier		 * Because TCP is the default assume IP or host is given without
130126756Smlaier		 * prefix.
131126756Smlaier		 */
132126756Smlaier	}
133126756Smlaier
134126756Smlaier	/*
135126756Smlaier	 * Extract optional port.
136126756Smlaier	 * There are three cases to consider.
137126756Smlaier	 * 1. hostname with port, eg. freefall.freebsd.org:8457
138126756Smlaier	 * 2. IPv4 address with port, eg. 192.168.0.101:8457
139126756Smlaier	 * 3. IPv6 address with port, eg. [fe80::1]:8457
140126756Smlaier	 * We discover IPv6 address by checking for two colons and if port is
141126756Smlaier	 * given, the address has to start with [.
142126756Smlaier	 */
143126756Smlaier	pp = NULL;
144126756Smlaier	if (strchr(addr, ':') != strrchr(addr, ':')) {
145135183Smlaier		if (addr[0] == '[')
146135183Smlaier			pp = strrchr(addr, ':');
147126756Smlaier	} else {
148126756Smlaier		pp = strrchr(addr, ':');
149126756Smlaier	}
150126756Smlaier	if (pp == NULL) {
151126756Smlaier		/* Port not given, use the default. */
152126756Smlaier		port = defport;
153126756Smlaier	} else {
154126756Smlaier		if (numfromstr(pp + 1, 1, 65535, &port) < 0)
155135183Smlaier			return (errno);
156135183Smlaier	}
157135183Smlaier	(void)snprintf(portstr, sizeof(portstr), "%jd", (intmax_t)port);
158135183Smlaier	/* Extract host name or IP address. */
159126756Smlaier	if (pp == NULL) {
160126756Smlaier		size = sizeof(iporhost);
161126756Smlaier		if (strlcpy(iporhost, addr, size) >= size)
162126756Smlaier			return (ENAMETOOLONG);
163126756Smlaier	} else if (addr[0] == '[' && pp[-1] == ']') {
164126756Smlaier		size = (size_t)(pp - addr - 2 + 1);
165126756Smlaier		if (size > sizeof(iporhost))
166126756Smlaier			return (ENAMETOOLONG);
167126756Smlaier		(void)strlcpy(iporhost, addr + 1, size);
168126756Smlaier	} else {
169126756Smlaier		size = (size_t)(pp - addr + 1);
170126756Smlaier		if (size > sizeof(iporhost))
171126756Smlaier			return (ENAMETOOLONG);
172126756Smlaier		(void)strlcpy(iporhost, addr, size);
173126756Smlaier	}
174126756Smlaier
175126756Smlaier	error = getaddrinfo(iporhost, portstr, &hints, &res);
176126756Smlaier	if (error != 0) {
177126756Smlaier		pjdlog_debug(1, "getaddrinfo(%s, %s) failed: %s.", iporhost,
178126756Smlaier		    portstr, gai_strerror(error));
179126756Smlaier		return (EINVAL);
180126756Smlaier	}
181126756Smlaier	if (res == NULL)
182126756Smlaier		return (ENOENT);
183126756Smlaier
184126756Smlaier	memcpy(sap, res->ai_addr, res->ai_addrlen);
185126756Smlaier
186126756Smlaier	freeaddrinfo(res);
187126756Smlaier
188126756Smlaier	return (0);
189126756Smlaier}
190126756Smlaier
191126756Smlaierstatic int
192126756Smlaiertcp_setup_new(const char *addr, int side, void **ctxp)
193126756Smlaier{
194126756Smlaier	struct tcp_ctx *tctx;
195126756Smlaier	int ret, nodelay;
196126756Smlaier
197126756Smlaier	PJDLOG_ASSERT(addr != NULL);
198126756Smlaier	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
199126756Smlaier	    side == TCP_SIDE_SERVER_LISTEN);
200126756Smlaier	PJDLOG_ASSERT(ctxp != NULL);
201135183Smlaier
202126756Smlaier	tctx = malloc(sizeof(*tctx));
203126756Smlaier	if (tctx == NULL)
204126756Smlaier		return (errno);
205126756Smlaier
206126756Smlaier	/* Parse given address. */
207126756Smlaier	if ((ret = tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &tctx->tc_sa)) != 0) {
208126756Smlaier		free(tctx);
209126756Smlaier		return (ret);
210126756Smlaier	}
211126756Smlaier
212126756Smlaier	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
213126756Smlaier
214135183Smlaier	tctx->tc_fd = socket(tctx->tc_sa.ss_family, SOCK_STREAM, 0);
215126756Smlaier	if (tctx->tc_fd == -1) {
216126756Smlaier		ret = errno;
217126756Smlaier		free(tctx);
218126756Smlaier		return (ret);
219126756Smlaier	}
220126756Smlaier
221126756Smlaier	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
222126756Smlaier
223126756Smlaier	/* Socket settings. */
224126756Smlaier	nodelay = 1;
225126756Smlaier	if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay,
226163621Sdelphij	    sizeof(nodelay)) == -1) {
227163621Sdelphij		pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY");
228163621Sdelphij	}
229126756Smlaier
230244647Sdelphij	tctx->tc_side = side;
231244647Sdelphij	tctx->tc_magic = TCP_CTX_MAGIC;
232244647Sdelphij	*ctxp = tctx;
233244647Sdelphij
234244647Sdelphij	return (0);
235244647Sdelphij}
236244647Sdelphij
237135183Smlaierstatic int
238126756Smlaiertcp_setup_wrap(int fd, int side, void **ctxp)
239135183Smlaier{
240135183Smlaier	struct tcp_ctx *tctx;
241126756Smlaier
242126756Smlaier	PJDLOG_ASSERT(fd >= 0);
243126756Smlaier	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
244126756Smlaier	    side == TCP_SIDE_SERVER_WORK);
245126756Smlaier	PJDLOG_ASSERT(ctxp != NULL);
246126756Smlaier
247126756Smlaier	tctx = malloc(sizeof(*tctx));
248126756Smlaier	if (tctx == NULL)
249126756Smlaier		return (errno);
250126756Smlaier
251126756Smlaier	tctx->tc_fd = fd;
252126756Smlaier	tctx->tc_sa.ss_family = AF_UNSPEC;
253126756Smlaier	tctx->tc_side = side;
254126756Smlaier	tctx->tc_magic = TCP_CTX_MAGIC;
255126756Smlaier	*ctxp = tctx;
256126756Smlaier
257126756Smlaier	return (0);
258126756Smlaier}
259126756Smlaier
260126756Smlaierstatic int
261126756Smlaiertcp_client(const char *srcaddr, const char *dstaddr, void **ctxp)
262126756Smlaier{
263126756Smlaier	struct tcp_ctx *tctx;
264126756Smlaier	struct sockaddr_storage sa;
265126756Smlaier	int ret;
266126756Smlaier
267126756Smlaier	ret = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, ctxp);
268126756Smlaier	if (ret != 0)
269126756Smlaier		return (ret);
270163621Sdelphij	tctx = *ctxp;
271163621Sdelphij	if (srcaddr == NULL)
272163621Sdelphij		return (0);
273126756Smlaier	ret = tcp_addr(srcaddr, 0, &sa);
274126756Smlaier	if (ret != 0) {
275126756Smlaier		tcp_close(tctx);
276126756Smlaier		return (ret);
277126756Smlaier	}
278163621Sdelphij	if (bind(tctx->tc_fd, (struct sockaddr *)&sa, sa.ss_len) < 0) {
279126756Smlaier		ret = errno;
280163621Sdelphij		tcp_close(tctx);
281126756Smlaier		return (ret);
282163621Sdelphij	}
283163621Sdelphij	return (0);
284163621Sdelphij}
285163621Sdelphij
286126756Smlaierstatic int
287135183Smlaiertcp_connect(void *ctx, int timeout)
288163621Sdelphij{
289163621Sdelphij	struct tcp_ctx *tctx = ctx;
290163621Sdelphij	int error, flags;
291163621Sdelphij
292135183Smlaier	PJDLOG_ASSERT(tctx != NULL);
293126756Smlaier	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
294126756Smlaier	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
295126756Smlaier	PJDLOG_ASSERT(tctx->tc_fd >= 0);
296126756Smlaier	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
297135183Smlaier	PJDLOG_ASSERT(timeout >= -1);
298126756Smlaier
299126756Smlaier	flags = fcntl(tctx->tc_fd, F_GETFL);
300126756Smlaier	if (flags == -1) {
301126756Smlaier		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
302135183Smlaier		return (errno);
303135183Smlaier	}
304126756Smlaier	/*
305126756Smlaier	 * We make socket non-blocking so we can handle connection timeout
306126756Smlaier	 * manually.
307126756Smlaier	 */
308225452Sdelphij	flags |= O_NONBLOCK;
309225452Sdelphij	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
310163621Sdelphij		pjdlog_common(LOG_DEBUG, 1, errno,
311163621Sdelphij		    "fcntl(F_SETFL, O_NONBLOCK) failed");
312126756Smlaier		return (errno);
313163621Sdelphij	}
314126756Smlaier
315225452Sdelphij	if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
316225452Sdelphij	    tctx->tc_sa.ss_len) == 0) {
317225452Sdelphij		if (timeout == -1)
318126756Smlaier			return (0);
319126756Smlaier		error = 0;
320126756Smlaier		goto done;
321126756Smlaier	}
322126756Smlaier	if (errno != EINPROGRESS) {
323126756Smlaier		error = errno;
324126756Smlaier		pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
325126756Smlaier		goto done;
326126756Smlaier	}
327126756Smlaier	if (timeout == -1)
328126756Smlaier		return (0);
329135183Smlaier	return (tcp_connect_wait(ctx, timeout));
330135183Smlaierdone:
331163621Sdelphij	flags &= ~O_NONBLOCK;
332163621Sdelphij	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
333135183Smlaier		if (error == 0)
334126756Smlaier			error = errno;
335126756Smlaier		pjdlog_common(LOG_DEBUG, 1, errno,
336126756Smlaier		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
337126756Smlaier	}
338126756Smlaier	return (error);
339126756Smlaier}
340126756Smlaier
341126756Smlaierstatic int
342126756Smlaiertcp_connect_wait(void *ctx, int timeout)
343126756Smlaier{
344163621Sdelphij	struct tcp_ctx *tctx = ctx;
345163621Sdelphij	struct timeval tv;
346163621Sdelphij	fd_set fdset;
347126756Smlaier	socklen_t esize;
348126756Smlaier	int error, flags, ret;
349135183Smlaier
350126756Smlaier	PJDLOG_ASSERT(tctx != NULL);
351126756Smlaier	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
352126756Smlaier	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
353126756Smlaier	PJDLOG_ASSERT(tctx->tc_fd >= 0);
354126756Smlaier	PJDLOG_ASSERT(timeout >= 0);
355126756Smlaier
356126756Smlaier	tv.tv_sec = timeout;
357126756Smlaier	tv.tv_usec = 0;
358126756Smlaieragain:
359126756Smlaier	FD_ZERO(&fdset);
360135183Smlaier	FD_SET(tctx->tc_fd, &fdset);
361135183Smlaier	ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
362135183Smlaier	if (ret == 0) {
363135183Smlaier		error = ETIMEDOUT;
364126756Smlaier		goto done;
365126756Smlaier	} else if (ret == -1) {
366135183Smlaier		if (errno == EINTR)
367135183Smlaier			goto again;
368135183Smlaier		error = errno;
369126756Smlaier		pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
370135183Smlaier		goto done;
371126756Smlaier	}
372126756Smlaier	PJDLOG_ASSERT(ret > 0);
373135183Smlaier	PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset));
374225452Sdelphij	esize = sizeof(error);
375135183Smlaier	if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
376135183Smlaier	    &esize) == -1) {
377135183Smlaier		error = errno;
378135183Smlaier		pjdlog_common(LOG_DEBUG, 1, errno,
379126756Smlaier		    "getsockopt(SO_ERROR) failed");
380135183Smlaier		goto done;
381135183Smlaier	}
382135183Smlaier	if (error != 0) {
383126756Smlaier		pjdlog_common(LOG_DEBUG, 1, error,
384135183Smlaier		    "getsockopt(SO_ERROR) returned error");
385135183Smlaier		goto done;
386135183Smlaier	}
387135183Smlaier	error = 0;
388126756Smlaierdone:
389135183Smlaier	flags = fcntl(tctx->tc_fd, F_GETFL);
390135183Smlaier	if (flags == -1) {
391135183Smlaier		if (error == 0)
392135183Smlaier			error = errno;
393126756Smlaier		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
394126756Smlaier		return (error);
395126756Smlaier	}
396135183Smlaier	flags &= ~O_NONBLOCK;
397126756Smlaier	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
398126756Smlaier		if (error == 0)
399126756Smlaier			error = errno;
400135183Smlaier		pjdlog_common(LOG_DEBUG, 1, errno,
401126756Smlaier		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
402126756Smlaier	}
403126756Smlaier	return (error);
404126756Smlaier}
405126756Smlaier
406126756Smlaierstatic int
407126756Smlaiertcp_server(const char *addr, void **ctxp)
408126756Smlaier{
409135183Smlaier	struct tcp_ctx *tctx;
410135183Smlaier	int ret, val;
411126756Smlaier
412126756Smlaier	ret = tcp_setup_new(addr, TCP_SIDE_SERVER_LISTEN, ctxp);
413130151Sschweikh	if (ret != 0)
414126756Smlaier		return (ret);
415126756Smlaier
416126756Smlaier	tctx = *ctxp;
417126756Smlaier
418126756Smlaier	val = 1;
419126756Smlaier	/* Ignore failure. */
420135183Smlaier	(void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
421135183Smlaier	   sizeof(val));
422135183Smlaier
423135183Smlaier	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
424135183Smlaier
425135183Smlaier	if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
426135183Smlaier	    tctx->tc_sa.ss_len) < 0) {
427126756Smlaier		ret = errno;
428135183Smlaier		tcp_close(tctx);
429135183Smlaier		return (ret);
430135183Smlaier	}
431135183Smlaier	if (listen(tctx->tc_fd, 8) < 0) {
432135183Smlaier		ret = errno;
433135183Smlaier		tcp_close(tctx);
434135183Smlaier		return (ret);
435135183Smlaier	}
436135183Smlaier
437135183Smlaier	return (0);
438244647Sdelphij}
439244647Sdelphij
440126756Smlaierstatic int
441126756Smlaiertcp_accept(void *ctx, void **newctxp)
442135183Smlaier{
443135183Smlaier	struct tcp_ctx *tctx = ctx;
444135183Smlaier	struct tcp_ctx *newtctx;
445135183Smlaier	socklen_t fromlen;
446135183Smlaier	int ret;
447126756Smlaier
448163621Sdelphij	PJDLOG_ASSERT(tctx != NULL);
449163621Sdelphij	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
450163621Sdelphij	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN);
451163621Sdelphij	PJDLOG_ASSERT(tctx->tc_fd >= 0);
452163621Sdelphij	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
453135183Smlaier
454135183Smlaier	newtctx = malloc(sizeof(*newtctx));
455135183Smlaier	if (newtctx == NULL)
456126756Smlaier		return (errno);
457126756Smlaier
458126756Smlaier	fromlen = tctx->tc_sa.ss_len;
459126756Smlaier	newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
460126756Smlaier	    &fromlen);
461126756Smlaier	if (newtctx->tc_fd < 0) {
462126756Smlaier		ret = errno;
463126756Smlaier		free(newtctx);
464126756Smlaier		return (ret);
465126756Smlaier	}
466126756Smlaier
467126756Smlaier	newtctx->tc_side = TCP_SIDE_SERVER_WORK;
468126756Smlaier	newtctx->tc_magic = TCP_CTX_MAGIC;
469126756Smlaier	*newctxp = newtctx;
470126756Smlaier
471135183Smlaier	return (0);
472126756Smlaier}
473135183Smlaier
474135183Smlaierstatic int
475135183Smlaiertcp_wrap(int fd, bool client, void **ctxp)
476135183Smlaier{
477163621Sdelphij
478126756Smlaier	return (tcp_setup_wrap(fd,
479126756Smlaier	    client ? TCP_SIDE_CLIENT : TCP_SIDE_SERVER_WORK, ctxp));
480126756Smlaier}
481126756Smlaier
482126756Smlaierstatic int
483126756Smlaiertcp_send(void *ctx, const unsigned char *data, size_t size, int fd)
484126756Smlaier{
485126756Smlaier	struct tcp_ctx *tctx = ctx;
486126756Smlaier
487126756Smlaier	PJDLOG_ASSERT(tctx != NULL);
488126756Smlaier	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
489126756Smlaier	PJDLOG_ASSERT(tctx->tc_fd >= 0);
490126756Smlaier	PJDLOG_ASSERT(fd == -1);
491163621Sdelphij
492126756Smlaier	return (proto_common_send(tctx->tc_fd, data, size, -1));
493126756Smlaier}
494126756Smlaier
495126756Smlaierstatic int
496126756Smlaiertcp_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
497126756Smlaier{
498126756Smlaier	struct tcp_ctx *tctx = ctx;
499126756Smlaier
500126756Smlaier	PJDLOG_ASSERT(tctx != NULL);
501126756Smlaier	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
502126756Smlaier	PJDLOG_ASSERT(tctx->tc_fd >= 0);
503126756Smlaier	PJDLOG_ASSERT(fdp == NULL);
504126756Smlaier
505135183Smlaier	return (proto_common_recv(tctx->tc_fd, data, size, NULL));
506135183Smlaier}
507126756Smlaier
508126756Smlaierstatic int
509126756Smlaiertcp_descriptor(const void *ctx)
510126756Smlaier{
511126756Smlaier	const struct tcp_ctx *tctx = ctx;
512126756Smlaier
513126756Smlaier	PJDLOG_ASSERT(tctx != NULL);
514126756Smlaier	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
515126756Smlaier
516126756Smlaier	return (tctx->tc_fd);
517126756Smlaier}
518126756Smlaier
519126756Smlaierstatic bool
520135183Smlaiertcp_address_match(const void *ctx, const char *addr)
521126756Smlaier{
522135183Smlaier	const struct tcp_ctx *tctx = ctx;
523135183Smlaier	struct sockaddr_storage sa1, sa2;
524135183Smlaier	socklen_t salen;
525126756Smlaier
526135183Smlaier	PJDLOG_ASSERT(tctx != NULL);
527163621Sdelphij	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
528135183Smlaier
529126756Smlaier	if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sa1) != 0)
530126756Smlaier		return (false);
531126756Smlaier
532126756Smlaier	salen = sizeof(sa2);
533163621Sdelphij	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) < 0)
534126756Smlaier		return (false);
535135183Smlaier
536135183Smlaier	if (sa1.ss_family != sa2.ss_family || sa1.ss_len != sa2.ss_len)
537135183Smlaier		return (false);
538163621Sdelphij
539135183Smlaier	switch (sa1.ss_family) {
540135183Smlaier	case AF_INET:
541135183Smlaier	    {
542135183Smlaier		struct sockaddr_in *sin1, *sin2;
543135183Smlaier
544135183Smlaier		sin1 = (struct sockaddr_in *)&sa1;
545135183Smlaier		sin2 = (struct sockaddr_in *)&sa2;
546163621Sdelphij
547163621Sdelphij		return (memcmp(&sin1->sin_addr, &sin2->sin_addr,
548163621Sdelphij		    sizeof(sin1->sin_addr)) == 0);
549163621Sdelphij	    }
550135183Smlaier	case AF_INET6:
551135183Smlaier	    {
552135183Smlaier		struct sockaddr_in6 *sin1, *sin2;
553135183Smlaier
554135183Smlaier		sin1 = (struct sockaddr_in6 *)&sa1;
555135183Smlaier		sin2 = (struct sockaddr_in6 *)&sa2;
556135183Smlaier
557135183Smlaier		return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
558135183Smlaier		    sizeof(sin1->sin6_addr)) == 0);
559135183Smlaier	    }
560135183Smlaier	default:
561135183Smlaier		return (false);
562135183Smlaier	}
563126756Smlaier}
564126756Smlaier
565126756Smlaierstatic void
566126756Smlaiertcp_local_address(const void *ctx, char *addr, size_t size)
567126756Smlaier{
568126756Smlaier	const struct tcp_ctx *tctx = ctx;
569126756Smlaier	struct sockaddr_storage sa;
570126756Smlaier	socklen_t salen;
571126756Smlaier
572126756Smlaier	PJDLOG_ASSERT(tctx != NULL);
573135183Smlaier	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
574135183Smlaier
575135183Smlaier	salen = sizeof(sa);
576135183Smlaier	if (getsockname(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) {
577135183Smlaier		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
578135183Smlaier		return;
579135183Smlaier	}
580135183Smlaier	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
581163621Sdelphij}
582135183Smlaier
583163621Sdelphijstatic void
584163621Sdelphijtcp_remote_address(const void *ctx, char *addr, size_t size)
585126756Smlaier{
586126756Smlaier	const struct tcp_ctx *tctx = ctx;
587126756Smlaier	struct sockaddr_storage sa;
588126756Smlaier	socklen_t salen;
589126756Smlaier
590126756Smlaier	PJDLOG_ASSERT(tctx != NULL);
591126756Smlaier	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
592126756Smlaier
593126756Smlaier	salen = sizeof(sa);
594126756Smlaier	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) {
595126756Smlaier		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
596126756Smlaier		return;
597126756Smlaier	}
598126756Smlaier	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
599135183Smlaier}
600126756Smlaier
601126756Smlaierstatic void
602135183Smlaiertcp_close(void *ctx)
603126756Smlaier{
604135183Smlaier	struct tcp_ctx *tctx = ctx;
605135183Smlaier
606135183Smlaier	PJDLOG_ASSERT(tctx != NULL);
607126756Smlaier	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
608126756Smlaier
609126756Smlaier	if (tctx->tc_fd >= 0)
610126756Smlaier		close(tctx->tc_fd);
611126756Smlaier	tctx->tc_magic = 0;
612126756Smlaier	free(tctx);
613126756Smlaier}
614126756Smlaier
615126756Smlaierstatic struct proto tcp_proto = {
616163621Sdelphij	.prt_name = "tcp",
617163621Sdelphij	.prt_client = tcp_client,
618163621Sdelphij	.prt_connect = tcp_connect,
619163621Sdelphij	.prt_connect_wait = tcp_connect_wait,
620126756Smlaier	.prt_server = tcp_server,
621126756Smlaier	.prt_accept = tcp_accept,
622126756Smlaier	.prt_wrap = tcp_wrap,
623126756Smlaier	.prt_send = tcp_send,
624126756Smlaier	.prt_recv = tcp_recv,
625126756Smlaier	.prt_descriptor = tcp_descriptor,
626126756Smlaier	.prt_address_match = tcp_address_match,
627135183Smlaier	.prt_local_address = tcp_local_address,
628163621Sdelphij	.prt_remote_address = tcp_remote_address,
629163621Sdelphij	.prt_close = tcp_close
630126756Smlaier};
631126756Smlaier
632126756Smlaierstatic __constructor void
633163621Sdelphijtcp_ctor(void)
634163621Sdelphij{
635163621Sdelphij
636163621Sdelphij	proto_register(&tcp_proto, true);
637126756Smlaier}
638163621Sdelphij