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