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
60struct 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
71static int tcp_connect_wait(void *ctx, int timeout);
72static void tcp_close(void *ctx);
73
74/*
75 * Function converts the given string to unsigned number.
76 */
77static int
78numfromstr(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);
99invalid:
100	errno = EINVAL;
101	return (-1);
102}
103
104static int
105tcp_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
196static int
197tcp_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
244static socklen_t
245sockaddr_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
262static int
263tcp_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);
333fail:
334	tcp_close(tctx);
335	return (error);
336}
337
338static int
339tcp_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;
356again:
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;
387done:
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
405static int
406tcp_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
439static int
440tcp_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
474static int
475tcp_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
496static int
497tcp_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
512static int
513tcp_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
528static int
529tcp_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
539static bool
540tcp_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__
591static void
592sockaddr_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
638static void
639tcp_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
662static void
663tcp_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
686static void
687tcp_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
700static 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
716static __constructor void
717tcp_ctor(void)
718{
719
720	proto_register(&tcp_proto, true);
721}
722