]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - contrib/openbsm/bin/auditdistd/proto_tcp.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / contrib / openbsm / bin / auditdistd / proto_tcp.c
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 }