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