xref: /freebsd/contrib/openbsm/bin/auditdistd/proto_tcp.c (revision 6829dae12bb055451fa467da4589c43bd03b1e64)
1 /*-
2  * Copyright (c) 2009-2010 The FreeBSD Foundation
3  * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
4  * All rights reserved.
5  *
6  * This software was developed by Pawel Jakub Dawidek under sponsorship from
7  * the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <config/config.h>
32 
33 #include <sys/param.h>	/* MAXHOSTNAMELEN */
34 #include <sys/socket.h>
35 
36 #include <arpa/inet.h>
37 
38 #include <netinet/in.h>
39 #include <netinet/tcp.h>
40 
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <netdb.h>
44 #include <stdbool.h>
45 #include <stdint.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 
51 #ifndef HAVE_STRLCPY
52 #include <compat/strlcpy.h>
53 #endif
54 
55 #include "pjdlog.h"
56 #include "proto_impl.h"
57 #include "subr.h"
58 
59 #define	TCP_CTX_MAGIC	0x7c41c
60 struct tcp_ctx {
61 	int			tc_magic;
62 	struct sockaddr_storage	tc_sa;
63 	int			tc_fd;
64 	int			tc_side;
65 #define	TCP_SIDE_CLIENT		0
66 #define	TCP_SIDE_SERVER_LISTEN	1
67 #define	TCP_SIDE_SERVER_WORK	2
68 	bool			tc_wait_called;
69 };
70 
71 static int tcp_connect_wait(void *ctx, int timeout);
72 static void tcp_close(void *ctx);
73 
74 /*
75  * Function converts the given string to unsigned number.
76  */
77 static int
78 numfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump)
79 {
80 	intmax_t digit, num;
81 
82 	if (str[0] == '\0')
83 		goto invalid;	/* Empty string. */
84 	num = 0;
85 	for (; *str != '\0'; str++) {
86 		if (*str < '0' || *str > '9')
87 			goto invalid;	/* Non-digit character. */
88 		digit = *str - '0';
89 		if (num > num * 10 + digit)
90 			goto invalid;	/* Overflow. */
91 		num = num * 10 + digit;
92 		if (num > maxnum)
93 			goto invalid;	/* Too big. */
94 	}
95 	if (num < minnum)
96 		goto invalid;	/* Too small. */
97 	*nump = num;
98 	return (0);
99 invalid:
100 	errno = EINVAL;
101 	return (-1);
102 }
103 
104 static int
105 tcp_addr(const char *addr, int defport, struct sockaddr_storage *sap)
106 {
107 	char iporhost[MAXHOSTNAMELEN], portstr[6];
108 	struct addrinfo hints;
109 	struct addrinfo *res;
110 	const char *pp;
111 	intmax_t port;
112 	size_t size;
113 	int error;
114 
115 	if (addr == NULL)
116 		return (-1);
117 
118 	bzero(&hints, sizeof(hints));
119 	hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
120 	hints.ai_family = PF_UNSPEC;
121 	hints.ai_socktype = SOCK_STREAM;
122 	hints.ai_protocol = IPPROTO_TCP;
123 
124 	if (strncasecmp(addr, "tcp4://", 7) == 0) {
125 		addr += 7;
126 		hints.ai_family = PF_INET;
127 	} else if (strncasecmp(addr, "tcp6://", 7) == 0) {
128 		addr += 7;
129 		hints.ai_family = PF_INET6;
130 	} else if (strncasecmp(addr, "tcp://", 6) == 0) {
131 		addr += 6;
132 	} else {
133 		/*
134 		 * Because TCP is the default assume IP or host is given without
135 		 * prefix.
136 		 */
137 	}
138 
139 	/*
140 	 * Extract optional port.
141 	 * There are three cases to consider.
142 	 * 1. hostname with port, eg. freefall.freebsd.org:8457
143 	 * 2. IPv4 address with port, eg. 192.168.0.101:8457
144 	 * 3. IPv6 address with port, eg. [fe80::1]:8457
145 	 * We discover IPv6 address by checking for two colons and if port is
146 	 * given, the address has to start with [.
147 	 */
148 	pp = NULL;
149 	if (strchr(addr, ':') != strrchr(addr, ':')) {
150 		if (addr[0] == '[')
151 			pp = strrchr(addr, ':');
152 	} else {
153 		pp = strrchr(addr, ':');
154 	}
155 	if (pp == NULL) {
156 		/* Port not given, use the default. */
157 		port = defport;
158 	} else {
159 		if (numfromstr(pp + 1, 1, 65535, &port) < 0)
160 			return (errno);
161 	}
162 	(void)snprintf(portstr, sizeof(portstr), "%jd", (intmax_t)port);
163 	/* Extract host name or IP address. */
164 	if (pp == NULL) {
165 		size = sizeof(iporhost);
166 		if (strlcpy(iporhost, addr, size) >= size)
167 			return (ENAMETOOLONG);
168 	} else if (addr[0] == '[' && pp[-1] == ']') {
169 		size = (size_t)(pp - addr - 2 + 1);
170 		if (size > sizeof(iporhost))
171 			return (ENAMETOOLONG);
172 		(void)strlcpy(iporhost, addr + 1, size);
173 	} else {
174 		size = (size_t)(pp - addr + 1);
175 		if (size > sizeof(iporhost))
176 			return (ENAMETOOLONG);
177 		(void)strlcpy(iporhost, addr, size);
178 	}
179 
180 	error = getaddrinfo(iporhost, portstr, &hints, &res);
181 	if (error != 0) {
182 		pjdlog_debug(1, "getaddrinfo(%s, %s) failed: %s.", iporhost,
183 		    portstr, gai_strerror(error));
184 		return (EINVAL);
185 	}
186 	if (res == NULL)
187 		return (ENOENT);
188 
189 	memcpy(sap, res->ai_addr, res->ai_addrlen);
190 
191 	freeaddrinfo(res);
192 
193 	return (0);
194 }
195 
196 static int
197 tcp_setup_new(const char *addr, int side, struct tcp_ctx **tctxp)
198 {
199 	struct tcp_ctx *tctx;
200 	int error, nodelay;
201 
202 	PJDLOG_ASSERT(addr != NULL);
203 	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
204 	    side == TCP_SIDE_SERVER_LISTEN);
205 	PJDLOG_ASSERT(tctxp != NULL);
206 
207 	tctx = malloc(sizeof(*tctx));
208 	if (tctx == NULL)
209 		return (errno);
210 
211 	/* Parse given address. */
212 	error = tcp_addr(addr, atoi(proto_get("tcp:port")), &tctx->tc_sa);
213 	if (error != 0) {
214 		free(tctx);
215 		return (error);
216 	}
217 
218 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
219 
220 	tctx->tc_fd = socket(tctx->tc_sa.ss_family, SOCK_STREAM, 0);
221 	if (tctx->tc_fd == -1) {
222 		error = errno;
223 		free(tctx);
224 		return (error);
225 	}
226 
227 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
228 
229 	/* Socket settings. */
230 	nodelay = 1;
231 	if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay,
232 	    sizeof(nodelay)) == -1) {
233 		pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY");
234 	}
235 
236 	tctx->tc_wait_called = (side == TCP_SIDE_CLIENT ? false : true);
237 	tctx->tc_side = side;
238 	tctx->tc_magic = TCP_CTX_MAGIC;
239 	*tctxp = tctx;
240 
241 	return (0);
242 }
243 
244 static socklen_t
245 sockaddr_len(const struct sockaddr_storage *ss)
246 {
247 
248 #ifdef HAVE_SOCKADDR_STORAGE_SS_LEN
249 	return (ss->ss_len);
250 #else
251 	switch (ss->ss_family) {
252 	case AF_INET:
253 		return (sizeof(struct sockaddr_in));
254 	case AF_INET6:
255 		return (sizeof(struct sockaddr_in6));
256 	default:
257 		PJDLOG_ABORT("Unexpected family %hhu.", ss->ss_family);
258 	}
259 #endif
260 }
261 
262 static int
263 tcp_connect(const char *srcaddr, const char *dstaddr, int timeout, void **ctxp)
264 {
265 	struct tcp_ctx *tctx;
266 	struct sockaddr_storage sa;
267 	int error, flags, ret;
268 
269 	PJDLOG_ASSERT(srcaddr == NULL || srcaddr[0] != '\0');
270 	PJDLOG_ASSERT(dstaddr != NULL);
271 	PJDLOG_ASSERT(timeout >= -1);
272 
273 	error = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, &tctx);
274 	if (error != 0)
275 		return (error);
276 	if (srcaddr != NULL) {
277 		error = tcp_addr(srcaddr, 0, &sa);
278 		if (error != 0)
279 			goto fail;
280 		if (bind(tctx->tc_fd, (struct sockaddr *)&sa,
281 		    sockaddr_len(&sa)) == -1) {
282 			error = errno;
283 			goto fail;
284 		}
285 	}
286 
287 	flags = fcntl(tctx->tc_fd, F_GETFL);
288 	if (flags == -1) {
289 		error = errno;
290 		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
291 		goto fail;
292 	}
293 	/*
294 	 * We make socket non-blocking so we can handle connection timeout
295 	 * manually.
296 	 */
297 	flags |= O_NONBLOCK;
298 	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
299 		error = errno;
300 		pjdlog_common(LOG_DEBUG, 1, errno,
301 		    "fcntl(F_SETFL, O_NONBLOCK) failed");
302 		goto fail;
303 	}
304 
305 	ret = connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
306 	    sockaddr_len(&tctx->tc_sa));
307 	if (ret == -1 && errno != EINPROGRESS) {
308 		error = errno;
309 		pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
310 		goto fail;
311 	}
312 
313 	if (timeout >= 0) {
314 		if (ret == -1) {
315 			/* Connection still in progress. Wait for it. */
316 			error = tcp_connect_wait(tctx, timeout);
317 			if (error != 0)
318 				goto fail;
319 		} else {
320 			/* Connection already complete. */
321 			flags &= ~O_NONBLOCK;
322 			if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
323 				error = errno;
324 				pjdlog_common(LOG_DEBUG, 1, errno,
325 				    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
326 				goto fail;
327 			}
328 		}
329 	}
330 
331 	*ctxp = tctx;
332 	return (0);
333 fail:
334 	tcp_close(tctx);
335 	return (error);
336 }
337 
338 static int
339 tcp_connect_wait(void *ctx, int timeout)
340 {
341 	struct tcp_ctx *tctx = ctx;
342 	struct timeval tv;
343 	fd_set fdset;
344 	socklen_t esize;
345 	int error, flags, ret;
346 
347 	PJDLOG_ASSERT(tctx != NULL);
348 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
349 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
350 	PJDLOG_ASSERT(!tctx->tc_wait_called);
351 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
352 	PJDLOG_ASSERT(timeout >= 0);
353 
354 	tv.tv_sec = timeout;
355 	tv.tv_usec = 0;
356 again:
357 	FD_ZERO(&fdset);
358 	FD_SET(tctx->tc_fd, &fdset);
359 	ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
360 	if (ret == 0) {
361 		error = ETIMEDOUT;
362 		goto done;
363 	} else if (ret == -1) {
364 		if (errno == EINTR)
365 			goto again;
366 		error = errno;
367 		pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
368 		goto done;
369 	}
370 	PJDLOG_ASSERT(ret > 0);
371 	PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset));
372 	esize = sizeof(error);
373 	if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
374 	    &esize) == -1) {
375 		error = errno;
376 		pjdlog_common(LOG_DEBUG, 1, errno,
377 		    "getsockopt(SO_ERROR) failed");
378 		goto done;
379 	}
380 	if (error != 0) {
381 		pjdlog_common(LOG_DEBUG, 1, error,
382 		    "getsockopt(SO_ERROR) returned error");
383 		goto done;
384 	}
385 	error = 0;
386 	tctx->tc_wait_called = true;
387 done:
388 	flags = fcntl(tctx->tc_fd, F_GETFL);
389 	if (flags == -1) {
390 		if (error == 0)
391 			error = errno;
392 		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
393 		return (error);
394 	}
395 	flags &= ~O_NONBLOCK;
396 	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
397 		if (error == 0)
398 			error = errno;
399 		pjdlog_common(LOG_DEBUG, 1, errno,
400 		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
401 	}
402 	return (error);
403 }
404 
405 static int
406 tcp_server(const char *addr, void **ctxp)
407 {
408 	struct tcp_ctx *tctx;
409 	int error, val;
410 
411 	error = tcp_setup_new(addr, TCP_SIDE_SERVER_LISTEN, &tctx);
412 	if (error != 0)
413 		return (error);
414 
415 	val = 1;
416 	/* Ignore failure. */
417 	(void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
418 	   sizeof(val));
419 
420 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
421 
422 	if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
423 	    sockaddr_len(&tctx->tc_sa)) == -1) {
424 		error = errno;
425 		tcp_close(tctx);
426 		return (error);
427 	}
428 	if (listen(tctx->tc_fd, 8) == -1) {
429 		error = errno;
430 		tcp_close(tctx);
431 		return (error);
432 	}
433 
434 	*ctxp = tctx;
435 
436 	return (0);
437 }
438 
439 static int
440 tcp_accept(void *ctx, void **newctxp)
441 {
442 	struct tcp_ctx *tctx = ctx;
443 	struct tcp_ctx *newtctx;
444 	socklen_t fromlen;
445 	int ret;
446 
447 	PJDLOG_ASSERT(tctx != NULL);
448 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
449 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN);
450 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
451 	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
452 
453 	newtctx = malloc(sizeof(*newtctx));
454 	if (newtctx == NULL)
455 		return (errno);
456 
457 	fromlen = sockaddr_len(&tctx->tc_sa);
458 	newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
459 	    &fromlen);
460 	if (newtctx->tc_fd < 0) {
461 		ret = errno;
462 		free(newtctx);
463 		return (ret);
464 	}
465 
466 	newtctx->tc_wait_called = true;
467 	newtctx->tc_side = TCP_SIDE_SERVER_WORK;
468 	newtctx->tc_magic = TCP_CTX_MAGIC;
469 	*newctxp = newtctx;
470 
471 	return (0);
472 }
473 
474 static int
475 tcp_wrap(int fd, bool client, void **ctxp)
476 {
477 	struct tcp_ctx *tctx;
478 
479 	PJDLOG_ASSERT(fd >= 0);
480 	PJDLOG_ASSERT(ctxp != NULL);
481 
482 	tctx = malloc(sizeof(*tctx));
483 	if (tctx == NULL)
484 		return (errno);
485 
486 	tctx->tc_fd = fd;
487 	tctx->tc_sa.ss_family = AF_UNSPEC;
488 	tctx->tc_wait_called = (client ? false : true);
489 	tctx->tc_side = (client ? TCP_SIDE_CLIENT : TCP_SIDE_SERVER_WORK);
490 	tctx->tc_magic = TCP_CTX_MAGIC;
491 	*ctxp = tctx;
492 
493 	return (0);
494 }
495 
496 static int
497 tcp_send(void *ctx, const unsigned char *data, size_t size, int fd)
498 {
499 	struct tcp_ctx *tctx = ctx;
500 
501 	PJDLOG_ASSERT(tctx != NULL);
502 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
503 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT ||
504 	    tctx->tc_side == TCP_SIDE_SERVER_WORK);
505 	PJDLOG_ASSERT(tctx->tc_wait_called);
506 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
507 	PJDLOG_ASSERT(fd == -1);
508 
509 	return (proto_common_send(tctx->tc_fd, data, size, -1));
510 }
511 
512 static int
513 tcp_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
514 {
515 	struct tcp_ctx *tctx = ctx;
516 
517 	PJDLOG_ASSERT(tctx != NULL);
518 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
519 	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT ||
520 	    tctx->tc_side == TCP_SIDE_SERVER_WORK);
521 	PJDLOG_ASSERT(tctx->tc_wait_called);
522 	PJDLOG_ASSERT(tctx->tc_fd >= 0);
523 	PJDLOG_ASSERT(fdp == NULL);
524 
525 	return (proto_common_recv(tctx->tc_fd, data, size, NULL));
526 }
527 
528 static int
529 tcp_descriptor(const void *ctx)
530 {
531 	const struct tcp_ctx *tctx = ctx;
532 
533 	PJDLOG_ASSERT(tctx != NULL);
534 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
535 
536 	return (tctx->tc_fd);
537 }
538 
539 static bool
540 tcp_address_match(const void *ctx, const char *addr)
541 {
542 	const struct tcp_ctx *tctx = ctx;
543 	struct sockaddr_storage sa1, sa2;
544 	socklen_t salen;
545 
546 	PJDLOG_ASSERT(tctx != NULL);
547 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
548 
549 	if (tcp_addr(addr, atoi(proto_get("tcp:port")), &sa1) != 0)
550 		return (false);
551 
552 	salen = sizeof(sa2);
553 	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) < 0)
554 		return (false);
555 
556 	if (sa1.ss_family != sa2.ss_family)
557 		return (false);
558 
559 #ifdef HAVE_SOCKADDR_STORAGE_SS_LEN
560 	if (sa1.ss_len != sa2.ss_len)
561 		return (false);
562 #endif
563 
564 	switch (sa1.ss_family) {
565 	case AF_INET:
566 	    {
567 		struct sockaddr_in *sin1, *sin2;
568 
569 		sin1 = (struct sockaddr_in *)&sa1;
570 		sin2 = (struct sockaddr_in *)&sa2;
571 
572 		return (memcmp(&sin1->sin_addr, &sin2->sin_addr,
573 		    sizeof(sin1->sin_addr)) == 0);
574 	    }
575 	case AF_INET6:
576 	    {
577 		struct sockaddr_in6 *sin1, *sin2;
578 
579 		sin1 = (struct sockaddr_in6 *)&sa1;
580 		sin2 = (struct sockaddr_in6 *)&sa2;
581 
582 		return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
583 		    sizeof(sin1->sin6_addr)) == 0);
584 	    }
585 	default:
586 		return (false);
587 	}
588 }
589 
590 #ifndef __FreeBSD__
591 static void
592 sockaddr_to_string(const void *sa, char *buf, size_t size)
593 {
594 	const struct sockaddr_storage *ss;
595 
596 	ss = (const struct sockaddr_storage * const *)sa;
597 	switch (ss->ss_family) {
598 	case AF_INET:
599 	    {
600 		char addr[INET_ADDRSTRLEN];
601 		const struct sockaddr_in *sin;
602 		unsigned int port;
603 
604 		sin = (const struct sockaddr_in *)ss;
605 		port = ntohs(sin->sin_port);
606 		if (inet_ntop(ss->ss_family, &sin->sin_addr, addr,
607 		    sizeof(addr)) == NULL) {
608 			PJDLOG_ABORT("inet_ntop(AF_INET) failed: %s.",
609 			    strerror(errno));
610 		}
611 		snprintf(buf, size, "%s:%u", addr, port);
612 		break;
613 	    }
614 	case AF_INET6:
615 	    {
616 		char addr[INET6_ADDRSTRLEN];
617 		const struct sockaddr_in6 *sin;
618 		unsigned int port;
619 
620 		sin = (const struct sockaddr_in6 *)ss;
621 		port = ntohs(sin->sin6_port);
622 		if (inet_ntop(ss->ss_family, &sin->sin6_addr, addr,
623 		    sizeof(addr)) == NULL) {
624 			PJDLOG_ABORT("inet_ntop(AF_INET6) failed: %s.",
625 			    strerror(errno));
626 		}
627 		snprintf(buf, size, "[%s]:%u", addr, port);
628 		break;
629 	    }
630 	default:
631 		snprintf(buf, size, "[unsupported family %hhu]",
632 		    ss->ss_family);
633 		break;
634 	}
635 }
636 #endif	/* !__FreeBSD__ */
637 
638 static void
639 tcp_local_address(const void *ctx, char *addr, size_t size)
640 {
641 	const struct tcp_ctx *tctx = ctx;
642 	struct sockaddr_storage sa;
643 	socklen_t salen;
644 
645 	PJDLOG_ASSERT(tctx != NULL);
646 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
647 
648 	salen = sizeof(sa);
649 	if (getsockname(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) {
650 		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
651 		return;
652 	}
653 #ifdef __FreeBSD__
654 	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
655 #else
656 	strlcpy(addr, "tcp://", size);
657 	if (size > 6)
658 		sockaddr_to_string(&sa, addr + 6, size - 6);
659 #endif
660 }
661 
662 static void
663 tcp_remote_address(const void *ctx, char *addr, size_t size)
664 {
665 	const struct tcp_ctx *tctx = ctx;
666 	struct sockaddr_storage sa;
667 	socklen_t salen;
668 
669 	PJDLOG_ASSERT(tctx != NULL);
670 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
671 
672 	salen = sizeof(sa);
673 	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) {
674 		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
675 		return;
676 	}
677 #ifdef __FreeBSD__
678 	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
679 #else
680 	strlcpy(addr, "tcp://", size);
681 	if (size > 6)
682 		sockaddr_to_string(&sa, addr + 6, size - 6);
683 #endif
684 }
685 
686 static void
687 tcp_close(void *ctx)
688 {
689 	struct tcp_ctx *tctx = ctx;
690 
691 	PJDLOG_ASSERT(tctx != NULL);
692 	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
693 
694 	if (tctx->tc_fd >= 0)
695 		close(tctx->tc_fd);
696 	tctx->tc_magic = 0;
697 	free(tctx);
698 }
699 
700 static struct proto tcp_proto = {
701 	.prt_name = "tcp",
702 	.prt_connect = tcp_connect,
703 	.prt_connect_wait = tcp_connect_wait,
704 	.prt_server = tcp_server,
705 	.prt_accept = tcp_accept,
706 	.prt_wrap = tcp_wrap,
707 	.prt_send = tcp_send,
708 	.prt_recv = tcp_recv,
709 	.prt_descriptor = tcp_descriptor,
710 	.prt_address_match = tcp_address_match,
711 	.prt_local_address = tcp_local_address,
712 	.prt_remote_address = tcp_remote_address,
713 	.prt_close = tcp_close
714 };
715 
716 static __constructor void
717 tcp_ctor(void)
718 {
719 
720 	proto_register(&tcp_proto, true);
721 }
722