]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/openbsm/bin/auditdistd/proto_tcp.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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  * $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
62 struct 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
73 static int tcp_connect_wait(void *ctx, int timeout);
74 static void tcp_close(void *ctx);
75
76 /*
77  * Function converts the given string to unsigned number.
78  */
79 static int
80 numfromstr(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);
101 invalid:
102         errno = EINVAL;
103         return (-1);
104 }
105
106 static int
107 tcp_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
198 static int
199 tcp_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
246 static socklen_t
247 sockaddr_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
264 static int
265 tcp_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);
335 fail:
336         tcp_close(tctx);
337         return (error);
338 }
339
340 static int
341 tcp_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;
358 again:
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;
389 done:
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
407 static int
408 tcp_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
441 static int
442 tcp_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
476 static int
477 tcp_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
498 static int
499 tcp_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
514 static int
515 tcp_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
530 static int
531 tcp_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
541 static bool
542 tcp_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__
593 static void
594 sockaddr_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
640 static void
641 tcp_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
664 static void
665 tcp_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
688 static void
689 tcp_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
702 static 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
718 static __constructor void
719 tcp_ctor(void)
720 {
721
722         proto_register(&tcp_proto, true);
723 }