1243730Srwatson/*-
2243730Srwatson * Copyright (c) 2009-2010 The FreeBSD Foundation
3243730Srwatson * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
4243730Srwatson * All rights reserved.
5243730Srwatson *
6243730Srwatson * This software was developed by Pawel Jakub Dawidek under sponsorship from
7243730Srwatson * the FreeBSD Foundation.
8243730Srwatson *
9243730Srwatson * Redistribution and use in source and binary forms, with or without
10243730Srwatson * modification, are permitted provided that the following conditions
11243730Srwatson * are met:
12243730Srwatson * 1. Redistributions of source code must retain the above copyright
13243730Srwatson *    notice, this list of conditions and the following disclaimer.
14243730Srwatson * 2. Redistributions in binary form must reproduce the above copyright
15243730Srwatson *    notice, this list of conditions and the following disclaimer in the
16243730Srwatson *    documentation and/or other materials provided with the distribution.
17243730Srwatson *
18243730Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19243730Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20243730Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21243730Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22243730Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23243730Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24243730Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25243730Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26243730Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27243730Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28243730Srwatson * SUCH DAMAGE.
29243730Srwatson *
30243734Srwatson * $P4: //depot/projects/trustedbsd/openbsm/bin/auditdistd/proto_tcp.c#2 $
31243730Srwatson */
32243730Srwatson
33243734Srwatson#include <config/config.h>
34243730Srwatson
35243730Srwatson#include <sys/param.h>	/* MAXHOSTNAMELEN */
36243730Srwatson#include <sys/socket.h>
37243730Srwatson
38243730Srwatson#include <arpa/inet.h>
39243730Srwatson
40243730Srwatson#include <netinet/in.h>
41243730Srwatson#include <netinet/tcp.h>
42243730Srwatson
43243730Srwatson#include <errno.h>
44243730Srwatson#include <fcntl.h>
45243730Srwatson#include <netdb.h>
46243730Srwatson#include <stdbool.h>
47243730Srwatson#include <stdint.h>
48243730Srwatson#include <stdio.h>
49243730Srwatson#include <stdlib.h>
50243730Srwatson#include <string.h>
51243730Srwatson#include <unistd.h>
52243730Srwatson
53243730Srwatson#ifndef HAVE_STRLCPY
54243730Srwatson#include <compat/strlcpy.h>
55243730Srwatson#endif
56243730Srwatson
57243730Srwatson#include "pjdlog.h"
58243730Srwatson#include "proto_impl.h"
59243730Srwatson#include "subr.h"
60243730Srwatson
61243730Srwatson#define	TCP_CTX_MAGIC	0x7c41c
62243730Srwatsonstruct tcp_ctx {
63243730Srwatson	int			tc_magic;
64243730Srwatson	struct sockaddr_storage	tc_sa;
65243730Srwatson	int			tc_fd;
66243730Srwatson	int			tc_side;
67243730Srwatson#define	TCP_SIDE_CLIENT		0
68243730Srwatson#define	TCP_SIDE_SERVER_LISTEN	1
69243730Srwatson#define	TCP_SIDE_SERVER_WORK	2
70243730Srwatson	bool			tc_wait_called;
71243730Srwatson};
72243730Srwatson
73243730Srwatsonstatic int tcp_connect_wait(void *ctx, int timeout);
74243730Srwatsonstatic void tcp_close(void *ctx);
75243730Srwatson
76243730Srwatson/*
77243730Srwatson * Function converts the given string to unsigned number.
78243730Srwatson */
79243730Srwatsonstatic int
80243730Srwatsonnumfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump)
81243730Srwatson{
82243730Srwatson	intmax_t digit, num;
83243730Srwatson
84243730Srwatson	if (str[0] == '\0')
85243730Srwatson		goto invalid;	/* Empty string. */
86243730Srwatson	num = 0;
87243730Srwatson	for (; *str != '\0'; str++) {
88243730Srwatson		if (*str < '0' || *str > '9')
89243730Srwatson			goto invalid;	/* Non-digit character. */
90243730Srwatson		digit = *str - '0';
91243730Srwatson		if (num > num * 10 + digit)
92243730Srwatson			goto invalid;	/* Overflow. */
93243730Srwatson		num = num * 10 + digit;
94243730Srwatson		if (num > maxnum)
95243730Srwatson			goto invalid;	/* Too big. */
96243730Srwatson	}
97243730Srwatson	if (num < minnum)
98243730Srwatson		goto invalid;	/* Too small. */
99243730Srwatson	*nump = num;
100243730Srwatson	return (0);
101243730Srwatsoninvalid:
102243730Srwatson	errno = EINVAL;
103243730Srwatson	return (-1);
104243730Srwatson}
105243730Srwatson
106243730Srwatsonstatic int
107243730Srwatsontcp_addr(const char *addr, int defport, struct sockaddr_storage *sap)
108243730Srwatson{
109243730Srwatson	char iporhost[MAXHOSTNAMELEN], portstr[6];
110243730Srwatson	struct addrinfo hints;
111243730Srwatson	struct addrinfo *res;
112243730Srwatson	const char *pp;
113243730Srwatson	intmax_t port;
114243730Srwatson	size_t size;
115243730Srwatson	int error;
116243730Srwatson
117243730Srwatson	if (addr == NULL)
118243730Srwatson		return (-1);
119243730Srwatson
120243730Srwatson	bzero(&hints, sizeof(hints));
121243730Srwatson	hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
122243730Srwatson	hints.ai_family = PF_UNSPEC;
123243730Srwatson	hints.ai_socktype = SOCK_STREAM;
124243730Srwatson	hints.ai_protocol = IPPROTO_TCP;
125243730Srwatson
126243730Srwatson	if (strncasecmp(addr, "tcp4://", 7) == 0) {
127243730Srwatson		addr += 7;
128243730Srwatson		hints.ai_family = PF_INET;
129243730Srwatson	} else if (strncasecmp(addr, "tcp6://", 7) == 0) {
130243730Srwatson		addr += 7;
131243730Srwatson		hints.ai_family = PF_INET6;
132243730Srwatson	} else if (strncasecmp(addr, "tcp://", 6) == 0) {
133243730Srwatson		addr += 6;
134243730Srwatson	} else {
135243730Srwatson		/*
136243730Srwatson		 * Because TCP is the default assume IP or host is given without
137243730Srwatson		 * prefix.
138243730Srwatson		 */
139243730Srwatson	}
140243730Srwatson
141243730Srwatson	/*
142243730Srwatson	 * Extract optional port.
143243730Srwatson	 * There are three cases to consider.
144243730Srwatson	 * 1. hostname with port, eg. freefall.freebsd.org:8457
145243730Srwatson	 * 2. IPv4 address with port, eg. 192.168.0.101:8457
146243730Srwatson	 * 3. IPv6 address with port, eg. [fe80::1]:8457
147243730Srwatson	 * We discover IPv6 address by checking for two colons and if port is
148243730Srwatson	 * given, the address has to start with [.
149243730Srwatson	 */
150243730Srwatson	pp = NULL;
151243730Srwatson	if (strchr(addr, ':') != strrchr(addr, ':')) {
152243730Srwatson		if (addr[0] == '[')
153243730Srwatson			pp = strrchr(addr, ':');
154243730Srwatson	} else {
155243730Srwatson		pp = strrchr(addr, ':');
156243730Srwatson	}
157243730Srwatson	if (pp == NULL) {
158243730Srwatson		/* Port not given, use the default. */
159243730Srwatson		port = defport;
160243730Srwatson	} else {
161243730Srwatson		if (numfromstr(pp + 1, 1, 65535, &port) < 0)
162243730Srwatson			return (errno);
163243730Srwatson	}
164243730Srwatson	(void)snprintf(portstr, sizeof(portstr), "%jd", (intmax_t)port);
165243730Srwatson	/* Extract host name or IP address. */
166243730Srwatson	if (pp == NULL) {
167243730Srwatson		size = sizeof(iporhost);
168243730Srwatson		if (strlcpy(iporhost, addr, size) >= size)
169243730Srwatson			return (ENAMETOOLONG);
170243730Srwatson	} else if (addr[0] == '[' && pp[-1] == ']') {
171243730Srwatson		size = (size_t)(pp - addr - 2 + 1);
172243730Srwatson		if (size > sizeof(iporhost))
173243730Srwatson			return (ENAMETOOLONG);
174243730Srwatson		(void)strlcpy(iporhost, addr + 1, size);
175243730Srwatson	} else {
176243730Srwatson		size = (size_t)(pp - addr + 1);
177243730Srwatson		if (size > sizeof(iporhost))
178243730Srwatson			return (ENAMETOOLONG);
179243730Srwatson		(void)strlcpy(iporhost, addr, size);
180243730Srwatson	}
181243730Srwatson
182243730Srwatson	error = getaddrinfo(iporhost, portstr, &hints, &res);
183243730Srwatson	if (error != 0) {
184243730Srwatson		pjdlog_debug(1, "getaddrinfo(%s, %s) failed: %s.", iporhost,
185243730Srwatson		    portstr, gai_strerror(error));
186243730Srwatson		return (EINVAL);
187243730Srwatson	}
188243730Srwatson	if (res == NULL)
189243730Srwatson		return (ENOENT);
190243730Srwatson
191243730Srwatson	memcpy(sap, res->ai_addr, res->ai_addrlen);
192243730Srwatson
193243730Srwatson	freeaddrinfo(res);
194243730Srwatson
195243730Srwatson	return (0);
196243730Srwatson}
197243730Srwatson
198243730Srwatsonstatic int
199243730Srwatsontcp_setup_new(const char *addr, int side, struct tcp_ctx **tctxp)
200243730Srwatson{
201243730Srwatson	struct tcp_ctx *tctx;
202243730Srwatson	int error, nodelay;
203243730Srwatson
204243730Srwatson	PJDLOG_ASSERT(addr != NULL);
205243730Srwatson	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
206243730Srwatson	    side == TCP_SIDE_SERVER_LISTEN);
207243730Srwatson	PJDLOG_ASSERT(tctxp != NULL);
208243730Srwatson
209243730Srwatson	tctx = malloc(sizeof(*tctx));
210243730Srwatson	if (tctx == NULL)
211243730Srwatson		return (errno);
212243730Srwatson
213243730Srwatson	/* Parse given address. */
214243730Srwatson	error = tcp_addr(addr, atoi(proto_get("tcp:port")), &tctx->tc_sa);
215243730Srwatson	if (error != 0) {
216243730Srwatson		free(tctx);
217243730Srwatson		return (error);
218243730Srwatson	}
219243730Srwatson
220243730Srwatson	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
221243730Srwatson
222243730Srwatson	tctx->tc_fd = socket(tctx->tc_sa.ss_family, SOCK_STREAM, 0);
223243730Srwatson	if (tctx->tc_fd == -1) {
224243730Srwatson		error = errno;
225243730Srwatson		free(tctx);
226243730Srwatson		return (error);
227243730Srwatson	}
228243730Srwatson
229243730Srwatson	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
230243730Srwatson
231243730Srwatson	/* Socket settings. */
232243730Srwatson	nodelay = 1;
233243730Srwatson	if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay,
234243730Srwatson	    sizeof(nodelay)) == -1) {
235243730Srwatson		pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY");
236243730Srwatson	}
237243730Srwatson
238243730Srwatson	tctx->tc_wait_called = (side == TCP_SIDE_CLIENT ? false : true);
239243730Srwatson	tctx->tc_side = side;
240243730Srwatson	tctx->tc_magic = TCP_CTX_MAGIC;
241243730Srwatson	*tctxp = tctx;
242243730Srwatson
243243730Srwatson	return (0);
244243730Srwatson}
245243730Srwatson
246243730Srwatsonstatic socklen_t
247243730Srwatsonsockaddr_len(const struct sockaddr_storage *ss)
248243730Srwatson{
249243730Srwatson
250243730Srwatson#ifdef HAVE_SOCKADDR_STORAGE_SS_LEN
251243730Srwatson	return (ss->ss_len);
252243730Srwatson#else
253243730Srwatson	switch (ss->ss_family) {
254243730Srwatson	case AF_INET:
255243730Srwatson		return (sizeof(struct sockaddr_in));
256243730Srwatson	case AF_INET6:
257243730Srwatson		return (sizeof(struct sockaddr_in6));
258243730Srwatson	default:
259243730Srwatson		PJDLOG_ABORT("Unexpected family %hhu.", ss->ss_family);
260243730Srwatson	}
261243730Srwatson#endif
262243730Srwatson}
263243730Srwatson
264243730Srwatsonstatic int
265243730Srwatsontcp_connect(const char *srcaddr, const char *dstaddr, int timeout, void **ctxp)
266243730Srwatson{
267243730Srwatson	struct tcp_ctx *tctx;
268243730Srwatson	struct sockaddr_storage sa;
269243730Srwatson	int error, flags, ret;
270243730Srwatson
271243730Srwatson	PJDLOG_ASSERT(srcaddr == NULL || srcaddr[0] != '\0');
272243730Srwatson	PJDLOG_ASSERT(dstaddr != NULL);
273243730Srwatson	PJDLOG_ASSERT(timeout >= -1);
274243730Srwatson
275243730Srwatson	error = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, &tctx);
276243730Srwatson	if (error != 0)
277243730Srwatson		return (error);
278243730Srwatson	if (srcaddr != NULL) {
279243730Srwatson		error = tcp_addr(srcaddr, 0, &sa);
280243730Srwatson		if (error != 0)
281243730Srwatson			goto fail;
282243730Srwatson		if (bind(tctx->tc_fd, (struct sockaddr *)&sa,
283243730Srwatson		    sockaddr_len(&sa)) == -1) {
284243730Srwatson			error = errno;
285243730Srwatson			goto fail;
286243730Srwatson		}
287243730Srwatson	}
288243730Srwatson
289243730Srwatson	flags = fcntl(tctx->tc_fd, F_GETFL);
290243730Srwatson	if (flags == -1) {
291243730Srwatson		error = errno;
292243730Srwatson		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
293243730Srwatson		goto fail;
294243730Srwatson	}
295243730Srwatson	/*
296243730Srwatson	 * We make socket non-blocking so we can handle connection timeout
297243730Srwatson	 * manually.
298243730Srwatson	 */
299243730Srwatson	flags |= O_NONBLOCK;
300243730Srwatson	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
301243730Srwatson		error = errno;
302243730Srwatson		pjdlog_common(LOG_DEBUG, 1, errno,
303243730Srwatson		    "fcntl(F_SETFL, O_NONBLOCK) failed");
304243730Srwatson		goto fail;
305243730Srwatson	}
306243730Srwatson
307243730Srwatson	ret = connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
308243730Srwatson	    sockaddr_len(&tctx->tc_sa));
309243730Srwatson	if (ret == -1 && errno != EINPROGRESS) {
310243730Srwatson		error = errno;
311243730Srwatson		pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
312243730Srwatson		goto fail;
313243730Srwatson	}
314243730Srwatson
315243730Srwatson	if (timeout >= 0) {
316243730Srwatson		if (ret == -1) {
317243730Srwatson			/* Connection still in progress. Wait for it. */
318243730Srwatson			error = tcp_connect_wait(tctx, timeout);
319243730Srwatson			if (error != 0)
320243730Srwatson				goto fail;
321243730Srwatson		} else {
322243730Srwatson			/* Connection already complete. */
323243730Srwatson			flags &= ~O_NONBLOCK;
324243730Srwatson			if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
325243730Srwatson				error = errno;
326243730Srwatson				pjdlog_common(LOG_DEBUG, 1, errno,
327243730Srwatson				    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
328243730Srwatson				goto fail;
329243730Srwatson			}
330243730Srwatson		}
331243730Srwatson	}
332243730Srwatson
333243730Srwatson	*ctxp = tctx;
334243730Srwatson	return (0);
335243730Srwatsonfail:
336243730Srwatson	tcp_close(tctx);
337243730Srwatson	return (error);
338243730Srwatson}
339243730Srwatson
340243730Srwatsonstatic int
341243730Srwatsontcp_connect_wait(void *ctx, int timeout)
342243730Srwatson{
343243730Srwatson	struct tcp_ctx *tctx = ctx;
344243730Srwatson	struct timeval tv;
345243730Srwatson	fd_set fdset;
346243730Srwatson	socklen_t esize;
347243730Srwatson	int error, flags, ret;
348243730Srwatson
349243730Srwatson	PJDLOG_ASSERT(tctx != NULL);
350243730Srwatson	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
351243730Srwatson	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
352243730Srwatson	PJDLOG_ASSERT(!tctx->tc_wait_called);
353243730Srwatson	PJDLOG_ASSERT(tctx->tc_fd >= 0);
354243730Srwatson	PJDLOG_ASSERT(timeout >= 0);
355243730Srwatson
356243730Srwatson	tv.tv_sec = timeout;
357243730Srwatson	tv.tv_usec = 0;
358243730Srwatsonagain:
359243730Srwatson	FD_ZERO(&fdset);
360243730Srwatson	FD_SET(tctx->tc_fd, &fdset);
361243730Srwatson	ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
362243730Srwatson	if (ret == 0) {
363243730Srwatson		error = ETIMEDOUT;
364243730Srwatson		goto done;
365243730Srwatson	} else if (ret == -1) {
366243730Srwatson		if (errno == EINTR)
367243730Srwatson			goto again;
368243730Srwatson		error = errno;
369243730Srwatson		pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
370243730Srwatson		goto done;
371243730Srwatson	}
372243730Srwatson	PJDLOG_ASSERT(ret > 0);
373243730Srwatson	PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset));
374243730Srwatson	esize = sizeof(error);
375243730Srwatson	if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
376243730Srwatson	    &esize) == -1) {
377243730Srwatson		error = errno;
378243730Srwatson		pjdlog_common(LOG_DEBUG, 1, errno,
379243730Srwatson		    "getsockopt(SO_ERROR) failed");
380243730Srwatson		goto done;
381243730Srwatson	}
382243730Srwatson	if (error != 0) {
383243730Srwatson		pjdlog_common(LOG_DEBUG, 1, error,
384243730Srwatson		    "getsockopt(SO_ERROR) returned error");
385243730Srwatson		goto done;
386243730Srwatson	}
387243730Srwatson	error = 0;
388243730Srwatson	tctx->tc_wait_called = true;
389243730Srwatsondone:
390243730Srwatson	flags = fcntl(tctx->tc_fd, F_GETFL);
391243730Srwatson	if (flags == -1) {
392243730Srwatson		if (error == 0)
393243730Srwatson			error = errno;
394243730Srwatson		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
395243730Srwatson		return (error);
396243730Srwatson	}
397243730Srwatson	flags &= ~O_NONBLOCK;
398243730Srwatson	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
399243730Srwatson		if (error == 0)
400243730Srwatson			error = errno;
401243730Srwatson		pjdlog_common(LOG_DEBUG, 1, errno,
402243730Srwatson		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
403243730Srwatson	}
404243730Srwatson	return (error);
405243730Srwatson}
406243730Srwatson
407243730Srwatsonstatic int
408243730Srwatsontcp_server(const char *addr, void **ctxp)
409243730Srwatson{
410243730Srwatson	struct tcp_ctx *tctx;
411243730Srwatson	int error, val;
412243730Srwatson
413243730Srwatson	error = tcp_setup_new(addr, TCP_SIDE_SERVER_LISTEN, &tctx);
414243730Srwatson	if (error != 0)
415243730Srwatson		return (error);
416243730Srwatson
417243730Srwatson	val = 1;
418243730Srwatson	/* Ignore failure. */
419243730Srwatson	(void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
420243730Srwatson	   sizeof(val));
421243730Srwatson
422243730Srwatson	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
423243730Srwatson
424243730Srwatson	if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
425243730Srwatson	    sockaddr_len(&tctx->tc_sa)) == -1) {
426243730Srwatson		error = errno;
427243730Srwatson		tcp_close(tctx);
428243730Srwatson		return (error);
429243730Srwatson	}
430243730Srwatson	if (listen(tctx->tc_fd, 8) == -1) {
431243730Srwatson		error = errno;
432243730Srwatson		tcp_close(tctx);
433243730Srwatson		return (error);
434243730Srwatson	}
435243730Srwatson
436243730Srwatson	*ctxp = tctx;
437243730Srwatson
438243730Srwatson	return (0);
439243730Srwatson}
440243730Srwatson
441243730Srwatsonstatic int
442243730Srwatsontcp_accept(void *ctx, void **newctxp)
443243730Srwatson{
444243730Srwatson	struct tcp_ctx *tctx = ctx;
445243730Srwatson	struct tcp_ctx *newtctx;
446243730Srwatson	socklen_t fromlen;
447243730Srwatson	int ret;
448243730Srwatson
449243730Srwatson	PJDLOG_ASSERT(tctx != NULL);
450243730Srwatson	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
451243730Srwatson	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN);
452243730Srwatson	PJDLOG_ASSERT(tctx->tc_fd >= 0);
453243730Srwatson	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
454243730Srwatson
455243730Srwatson	newtctx = malloc(sizeof(*newtctx));
456243730Srwatson	if (newtctx == NULL)
457243730Srwatson		return (errno);
458243730Srwatson
459243730Srwatson	fromlen = sockaddr_len(&tctx->tc_sa);
460243730Srwatson	newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
461243730Srwatson	    &fromlen);
462243730Srwatson	if (newtctx->tc_fd < 0) {
463243730Srwatson		ret = errno;
464243730Srwatson		free(newtctx);
465243730Srwatson		return (ret);
466243730Srwatson	}
467243730Srwatson
468243730Srwatson	newtctx->tc_wait_called = true;
469243730Srwatson	newtctx->tc_side = TCP_SIDE_SERVER_WORK;
470243730Srwatson	newtctx->tc_magic = TCP_CTX_MAGIC;
471243730Srwatson	*newctxp = newtctx;
472243730Srwatson
473243730Srwatson	return (0);
474243730Srwatson}
475243730Srwatson
476243730Srwatsonstatic int
477243730Srwatsontcp_wrap(int fd, bool client, void **ctxp)
478243730Srwatson{
479243730Srwatson	struct tcp_ctx *tctx;
480243730Srwatson
481243730Srwatson	PJDLOG_ASSERT(fd >= 0);
482243730Srwatson	PJDLOG_ASSERT(ctxp != NULL);
483243730Srwatson
484243730Srwatson	tctx = malloc(sizeof(*tctx));
485243730Srwatson	if (tctx == NULL)
486243730Srwatson		return (errno);
487243730Srwatson
488243730Srwatson	tctx->tc_fd = fd;
489243730Srwatson	tctx->tc_sa.ss_family = AF_UNSPEC;
490243730Srwatson	tctx->tc_wait_called = (client ? false : true);
491243730Srwatson	tctx->tc_side = (client ? TCP_SIDE_CLIENT : TCP_SIDE_SERVER_WORK);
492243730Srwatson	tctx->tc_magic = TCP_CTX_MAGIC;
493243730Srwatson	*ctxp = tctx;
494243730Srwatson
495243730Srwatson	return (0);
496243730Srwatson}
497243730Srwatson
498243730Srwatsonstatic int
499243730Srwatsontcp_send(void *ctx, const unsigned char *data, size_t size, int fd)
500243730Srwatson{
501243730Srwatson	struct tcp_ctx *tctx = ctx;
502243730Srwatson
503243730Srwatson	PJDLOG_ASSERT(tctx != NULL);
504243730Srwatson	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
505243730Srwatson	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT ||
506243730Srwatson	    tctx->tc_side == TCP_SIDE_SERVER_WORK);
507243730Srwatson	PJDLOG_ASSERT(tctx->tc_wait_called);
508243730Srwatson	PJDLOG_ASSERT(tctx->tc_fd >= 0);
509243730Srwatson	PJDLOG_ASSERT(fd == -1);
510243730Srwatson
511243730Srwatson	return (proto_common_send(tctx->tc_fd, data, size, -1));
512243730Srwatson}
513243730Srwatson
514243730Srwatsonstatic int
515243730Srwatsontcp_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
516243730Srwatson{
517243730Srwatson	struct tcp_ctx *tctx = ctx;
518243730Srwatson
519243730Srwatson	PJDLOG_ASSERT(tctx != NULL);
520243730Srwatson	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
521243730Srwatson	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT ||
522243730Srwatson	    tctx->tc_side == TCP_SIDE_SERVER_WORK);
523243730Srwatson	PJDLOG_ASSERT(tctx->tc_wait_called);
524243730Srwatson	PJDLOG_ASSERT(tctx->tc_fd >= 0);
525243730Srwatson	PJDLOG_ASSERT(fdp == NULL);
526243730Srwatson
527243730Srwatson	return (proto_common_recv(tctx->tc_fd, data, size, NULL));
528243730Srwatson}
529243730Srwatson
530243730Srwatsonstatic int
531243730Srwatsontcp_descriptor(const void *ctx)
532243730Srwatson{
533243730Srwatson	const struct tcp_ctx *tctx = ctx;
534243730Srwatson
535243730Srwatson	PJDLOG_ASSERT(tctx != NULL);
536243730Srwatson	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
537243730Srwatson
538243730Srwatson	return (tctx->tc_fd);
539243730Srwatson}
540243730Srwatson
541243730Srwatsonstatic bool
542243730Srwatsontcp_address_match(const void *ctx, const char *addr)
543243730Srwatson{
544243730Srwatson	const struct tcp_ctx *tctx = ctx;
545243730Srwatson	struct sockaddr_storage sa1, sa2;
546243730Srwatson	socklen_t salen;
547243730Srwatson
548243730Srwatson	PJDLOG_ASSERT(tctx != NULL);
549243730Srwatson	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
550243730Srwatson
551243730Srwatson	if (tcp_addr(addr, atoi(proto_get("tcp:port")), &sa1) != 0)
552243730Srwatson		return (false);
553243730Srwatson
554243730Srwatson	salen = sizeof(sa2);
555243730Srwatson	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) < 0)
556243730Srwatson		return (false);
557243730Srwatson
558243730Srwatson	if (sa1.ss_family != sa2.ss_family)
559243730Srwatson		return (false);
560243730Srwatson
561243730Srwatson#ifdef HAVE_SOCKADDR_STORAGE_SS_LEN
562243730Srwatson	if (sa1.ss_len != sa2.ss_len)
563243730Srwatson		return (false);
564243730Srwatson#endif
565243730Srwatson
566243730Srwatson	switch (sa1.ss_family) {
567243730Srwatson	case AF_INET:
568243730Srwatson	    {
569243730Srwatson		struct sockaddr_in *sin1, *sin2;
570243730Srwatson
571243730Srwatson		sin1 = (struct sockaddr_in *)&sa1;
572243730Srwatson		sin2 = (struct sockaddr_in *)&sa2;
573243730Srwatson
574243730Srwatson		return (memcmp(&sin1->sin_addr, &sin2->sin_addr,
575243730Srwatson		    sizeof(sin1->sin_addr)) == 0);
576243730Srwatson	    }
577243730Srwatson	case AF_INET6:
578243730Srwatson	    {
579243730Srwatson		struct sockaddr_in6 *sin1, *sin2;
580243730Srwatson
581243730Srwatson		sin1 = (struct sockaddr_in6 *)&sa1;
582243730Srwatson		sin2 = (struct sockaddr_in6 *)&sa2;
583243730Srwatson
584243730Srwatson		return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
585243730Srwatson		    sizeof(sin1->sin6_addr)) == 0);
586243730Srwatson	    }
587243730Srwatson	default:
588243730Srwatson		return (false);
589243730Srwatson	}
590243730Srwatson}
591243730Srwatson
592243730Srwatson#ifndef __FreeBSD__
593243730Srwatsonstatic void
594243730Srwatsonsockaddr_to_string(const void *sa, char *buf, size_t size)
595243730Srwatson{
596243730Srwatson	const struct sockaddr_storage *ss;
597243730Srwatson
598243730Srwatson	ss = (const struct sockaddr_storage * const *)sa;
599243730Srwatson	switch (ss->ss_family) {
600243730Srwatson	case AF_INET:
601243730Srwatson	    {
602243730Srwatson		char addr[INET_ADDRSTRLEN];
603243730Srwatson		const struct sockaddr_in *sin;
604243730Srwatson		unsigned int port;
605243730Srwatson
606243730Srwatson		sin = (const struct sockaddr_in *)ss;
607243730Srwatson		port = ntohs(sin->sin_port);
608243730Srwatson		if (inet_ntop(ss->ss_family, &sin->sin_addr, addr,
609243730Srwatson		    sizeof(addr)) == NULL) {
610243730Srwatson			PJDLOG_ABORT("inet_ntop(AF_INET) failed: %s.",
611243730Srwatson			    strerror(errno));
612243730Srwatson		}
613243730Srwatson		snprintf(buf, size, "%s:%u", addr, port);
614243730Srwatson		break;
615243730Srwatson	    }
616243730Srwatson	case AF_INET6:
617243730Srwatson	    {
618243730Srwatson		char addr[INET6_ADDRSTRLEN];
619243730Srwatson		const struct sockaddr_in6 *sin;
620243730Srwatson		unsigned int port;
621243730Srwatson
622243730Srwatson		sin = (const struct sockaddr_in6 *)ss;
623243730Srwatson		port = ntohs(sin->sin6_port);
624243730Srwatson		if (inet_ntop(ss->ss_family, &sin->sin6_addr, addr,
625243730Srwatson		    sizeof(addr)) == NULL) {
626243730Srwatson			PJDLOG_ABORT("inet_ntop(AF_INET6) failed: %s.",
627243730Srwatson			    strerror(errno));
628243730Srwatson		}
629243730Srwatson		snprintf(buf, size, "[%s]:%u", addr, port);
630243730Srwatson		break;
631243730Srwatson	    }
632243730Srwatson	default:
633243730Srwatson		snprintf(buf, size, "[unsupported family %hhu]",
634243730Srwatson		    ss->ss_family);
635243730Srwatson		break;
636243730Srwatson	}
637243730Srwatson}
638243730Srwatson#endif	/* !__FreeBSD__ */
639243730Srwatson
640243730Srwatsonstatic void
641243730Srwatsontcp_local_address(const void *ctx, char *addr, size_t size)
642243730Srwatson{
643243730Srwatson	const struct tcp_ctx *tctx = ctx;
644243730Srwatson	struct sockaddr_storage sa;
645243730Srwatson	socklen_t salen;
646243730Srwatson
647243730Srwatson	PJDLOG_ASSERT(tctx != NULL);
648243730Srwatson	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
649243730Srwatson
650243730Srwatson	salen = sizeof(sa);
651243730Srwatson	if (getsockname(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) {
652243730Srwatson		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
653243730Srwatson		return;
654243730Srwatson	}
655243730Srwatson#ifdef __FreeBSD__
656243730Srwatson	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
657243730Srwatson#else
658243730Srwatson	strlcpy(addr, "tcp://", size);
659243730Srwatson	if (size > 6)
660243730Srwatson		sockaddr_to_string(&sa, addr + 6, size - 6);
661243730Srwatson#endif
662243730Srwatson}
663243730Srwatson
664243730Srwatsonstatic void
665243730Srwatsontcp_remote_address(const void *ctx, char *addr, size_t size)
666243730Srwatson{
667243730Srwatson	const struct tcp_ctx *tctx = ctx;
668243730Srwatson	struct sockaddr_storage sa;
669243730Srwatson	socklen_t salen;
670243730Srwatson
671243730Srwatson	PJDLOG_ASSERT(tctx != NULL);
672243730Srwatson	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
673243730Srwatson
674243730Srwatson	salen = sizeof(sa);
675243730Srwatson	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) {
676243730Srwatson		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
677243730Srwatson		return;
678243730Srwatson	}
679243730Srwatson#ifdef __FreeBSD__
680243730Srwatson	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
681243730Srwatson#else
682243730Srwatson	strlcpy(addr, "tcp://", size);
683243730Srwatson	if (size > 6)
684243730Srwatson		sockaddr_to_string(&sa, addr + 6, size - 6);
685243730Srwatson#endif
686243730Srwatson}
687243730Srwatson
688243730Srwatsonstatic void
689243730Srwatsontcp_close(void *ctx)
690243730Srwatson{
691243730Srwatson	struct tcp_ctx *tctx = ctx;
692243730Srwatson
693243730Srwatson	PJDLOG_ASSERT(tctx != NULL);
694243730Srwatson	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
695243730Srwatson
696243730Srwatson	if (tctx->tc_fd >= 0)
697243730Srwatson		close(tctx->tc_fd);
698243730Srwatson	tctx->tc_magic = 0;
699243730Srwatson	free(tctx);
700243730Srwatson}
701243730Srwatson
702243730Srwatsonstatic struct proto tcp_proto = {
703243730Srwatson	.prt_name = "tcp",
704243730Srwatson	.prt_connect = tcp_connect,
705243730Srwatson	.prt_connect_wait = tcp_connect_wait,
706243730Srwatson	.prt_server = tcp_server,
707243730Srwatson	.prt_accept = tcp_accept,
708243730Srwatson	.prt_wrap = tcp_wrap,
709243730Srwatson	.prt_send = tcp_send,
710243730Srwatson	.prt_recv = tcp_recv,
711243730Srwatson	.prt_descriptor = tcp_descriptor,
712243730Srwatson	.prt_address_match = tcp_address_match,
713243730Srwatson	.prt_local_address = tcp_local_address,
714243730Srwatson	.prt_remote_address = tcp_remote_address,
715243730Srwatson	.prt_close = tcp_close
716243730Srwatson};
717243730Srwatson
718243730Srwatsonstatic __constructor void
719243730Srwatsontcp_ctor(void)
720243730Srwatson{
721243730Srwatson
722243730Srwatson	proto_register(&tcp_proto, true);
723243730Srwatson}
724