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
numfromstr(const char * str,intmax_t minnum,intmax_t maxnum,intmax_t * nump)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
tcp_addr(const char * addr,int defport,struct sockaddr_storage * sap)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
tcp_setup_new(const char * addr,int side,struct tcp_ctx ** tctxp)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
sockaddr_len(const struct sockaddr_storage * ss)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
tcp_connect(const char * srcaddr,const char * dstaddr,int timeout,void ** ctxp)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
tcp_connect_wait(void * ctx,int timeout)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
tcp_server(const char * addr,void ** ctxp)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
tcp_accept(void * ctx,void ** newctxp)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
tcp_wrap(int fd,bool client,void ** ctxp)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
tcp_send(void * ctx,const unsigned char * data,size_t size,int fd)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
tcp_recv(void * ctx,unsigned char * data,size_t size,int * fdp)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
tcp_descriptor(const void * ctx)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
tcp_address_match(const void * ctx,const char * addr)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
sockaddr_to_string(const void * sa,char * buf,size_t size)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
tcp_local_address(const void * ctx,char * addr,size_t size)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
tcp_remote_address(const void * ctx,char * addr,size_t size)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
tcp_close(void * ctx)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
tcp_ctor(void)717 tcp_ctor(void)
718 {
719
720 proto_register(&tcp_proto, true);
721 }
722