proto_tcp.c revision 225781
1193323Sed/*- 2193323Sed * Copyright (c) 2009-2010 The FreeBSD Foundation 3193323Sed * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net> 4193323Sed * All rights reserved. 5193323Sed * 6193323Sed * This software was developed by Pawel Jakub Dawidek under sponsorship from 7193323Sed * the FreeBSD Foundation. 8193323Sed * 9193323Sed * Redistribution and use in source and binary forms, with or without 10193323Sed * modification, are permitted provided that the following conditions 11193323Sed * are met: 12193323Sed * 1. Redistributions of source code must retain the above copyright 13193323Sed * notice, this list of conditions and the following disclaimer. 14193323Sed * 2. Redistributions in binary form must reproduce the above copyright 15263509Sdim * notice, this list of conditions and the following disclaimer in the 16263509Sdim * documentation and/or other materials provided with the distribution. 17201360Srdivacky * 18201360Srdivacky * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21193323Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22201360Srdivacky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24201360Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25201360Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26201360Srdivacky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27201360Srdivacky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28201360Srdivacky * SUCH DAMAGE. 29201360Srdivacky */ 30201360Srdivacky 31201360Srdivacky#include <sys/cdefs.h> 32201360Srdivacky__FBSDID("$FreeBSD: head/sbin/hastd/proto_tcp.c 225781 2011-09-27 07:52:39Z pjd $"); 33201360Srdivacky 34201360Srdivacky#include <sys/param.h> /* MAXHOSTNAMELEN */ 35201360Srdivacky#include <sys/socket.h> 36201360Srdivacky 37201360Srdivacky#include <arpa/inet.h> 38201360Srdivacky 39201360Srdivacky#include <netinet/in.h> 40201360Srdivacky#include <netinet/tcp.h> 41201360Srdivacky 42201360Srdivacky#include <errno.h> 43201360Srdivacky#include <fcntl.h> 44201360Srdivacky#include <netdb.h> 45201360Srdivacky#include <stdbool.h> 46201360Srdivacky#include <stdint.h> 47201360Srdivacky#include <stdio.h> 48201360Srdivacky#include <string.h> 49201360Srdivacky#include <unistd.h> 50201360Srdivacky 51201360Srdivacky#include "pjdlog.h" 52201360Srdivacky#include "proto_impl.h" 53201360Srdivacky#include "subr.h" 54201360Srdivacky 55201360Srdivacky#define TCP_CTX_MAGIC 0x7c41c 56201360Srdivackystruct tcp_ctx { 57201360Srdivacky int tc_magic; 58201360Srdivacky struct sockaddr_storage tc_sa; 59201360Srdivacky int tc_fd; 60201360Srdivacky int tc_side; 61201360Srdivacky#define TCP_SIDE_CLIENT 0 62201360Srdivacky#define TCP_SIDE_SERVER_LISTEN 1 63201360Srdivacky#define TCP_SIDE_SERVER_WORK 2 64263509Sdim}; 65263509Sdim 66201360Srdivackystatic int tcp_connect_wait(void *ctx, int timeout); 67201360Srdivackystatic void tcp_close(void *ctx); 68201360Srdivacky 69201360Srdivacky/* 70201360Srdivacky * Function converts the given string to unsigned number. 71201360Srdivacky */ 72201360Srdivackystatic int 73201360Srdivackynumfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump) 74201360Srdivacky{ 75201360Srdivacky intmax_t digit, num; 76201360Srdivacky 77201360Srdivacky if (str[0] == '\0') 78201360Srdivacky goto invalid; /* Empty string. */ 79201360Srdivacky num = 0; 80201360Srdivacky for (; *str != '\0'; str++) { 81201360Srdivacky if (*str < '0' || *str > '9') 82201360Srdivacky goto invalid; /* Non-digit character. */ 83218893Sdim digit = *str - '0'; 84218893Sdim if (num > num * 10 + digit) 85226890Sdim goto invalid; /* Overflow. */ 86226890Sdim num = num * 10 + digit; 87226890Sdim if (num > maxnum) 88226890Sdim goto invalid; /* Too big. */ 89226890Sdim } 90226890Sdim if (num < minnum) 91226890Sdim goto invalid; /* Too small. */ 92226890Sdim *nump = num; 93226890Sdim return (0); 94226890Sdiminvalid: 95226890Sdim errno = EINVAL; 96226890Sdim return (-1); 97226890Sdim} 98235633Sdim 99193323Sedstatic int 100201360Srdivackytcp_addr(const char *addr, int defport, struct sockaddr_storage *sap) 101193323Sed{ 102193323Sed char iporhost[MAXHOSTNAMELEN], portstr[6]; 103193323Sed struct addrinfo hints; 104193323Sed struct addrinfo *res; 105201360Srdivacky const char *pp; 106193323Sed intmax_t port; 107210299Sed size_t size; 108210299Sed int error; 109193323Sed 110201360Srdivacky if (addr == NULL) 111193323Sed return (-1); 112193323Sed 113193323Sed bzero(&hints, sizeof(hints)); 114193323Sed hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; 115201360Srdivacky hints.ai_family = PF_UNSPEC; 116193323Sed hints.ai_socktype = SOCK_STREAM; 117201360Srdivacky hints.ai_protocol = IPPROTO_TCP; 118201360Srdivacky 119201360Srdivacky if (strncasecmp(addr, "tcp4://", 7) == 0) { 120201360Srdivacky addr += 7; 121201360Srdivacky hints.ai_family = PF_INET; 122201360Srdivacky } else if (strncasecmp(addr, "tcp6://", 7) == 0) { 123201360Srdivacky addr += 7; 124201360Srdivacky hints.ai_family = PF_INET6; 125201360Srdivacky } else if (strncasecmp(addr, "tcp://", 6) == 0) { 126201360Srdivacky addr += 6; 127201360Srdivacky } else { 128201360Srdivacky /* 129201360Srdivacky * Because TCP is the default assume IP or host is given without 130201360Srdivacky * prefix. 131201360Srdivacky */ 132201360Srdivacky } 133201360Srdivacky 134201360Srdivacky /* 135201360Srdivacky * Extract optional port. 136201360Srdivacky * There are three cases to consider. 137201360Srdivacky * 1. hostname with port, eg. freefall.freebsd.org:8457 138201360Srdivacky * 2. IPv4 address with port, eg. 192.168.0.101:8457 139201360Srdivacky * 3. IPv6 address with port, eg. [fe80::1]:8457 140201360Srdivacky * We discover IPv6 address by checking for two colons and if port is 141201360Srdivacky * given, the address has to start with [. 142201360Srdivacky */ 143201360Srdivacky pp = NULL; 144201360Srdivacky if (strchr(addr, ':') != strrchr(addr, ':')) { 145201360Srdivacky if (addr[0] == '[') 146201360Srdivacky pp = strrchr(addr, ':'); 147201360Srdivacky } else { 148201360Srdivacky pp = strrchr(addr, ':'); 149201360Srdivacky } 150201360Srdivacky if (pp == NULL) { 151201360Srdivacky /* Port not given, use the default. */ 152201360Srdivacky port = defport; 153201360Srdivacky } else { 154201360Srdivacky if (numfromstr(pp + 1, 1, 65535, &port) < 0) 155201360Srdivacky return (errno); 156201360Srdivacky } 157201360Srdivacky (void)snprintf(portstr, sizeof(portstr), "%jd", (intmax_t)port); 158201360Srdivacky /* Extract host name or IP address. */ 159201360Srdivacky if (pp == NULL) { 160201360Srdivacky size = sizeof(iporhost); 161201360Srdivacky if (strlcpy(iporhost, addr, size) >= size) 162201360Srdivacky return (ENAMETOOLONG); 163201360Srdivacky } else if (addr[0] == '[' && pp[-1] == ']') { 164201360Srdivacky size = (size_t)(pp - addr - 2 + 1); 165201360Srdivacky if (size > sizeof(iporhost)) 166201360Srdivacky return (ENAMETOOLONG); 167201360Srdivacky (void)strlcpy(iporhost, addr + 1, size); 168201360Srdivacky } else { 169201360Srdivacky size = (size_t)(pp - addr + 1); 170201360Srdivacky if (size > sizeof(iporhost)) 171201360Srdivacky return (ENAMETOOLONG); 172201360Srdivacky (void)strlcpy(iporhost, addr, size); 173201360Srdivacky } 174201360Srdivacky 175201360Srdivacky error = getaddrinfo(iporhost, portstr, &hints, &res); 176201360Srdivacky if (error != 0) { 177201360Srdivacky pjdlog_debug(1, "getaddrinfo(%s, %s) failed: %s.", iporhost, 178201360Srdivacky portstr, gai_strerror(error)); 179201360Srdivacky return (EINVAL); 180201360Srdivacky } 181201360Srdivacky if (res == NULL) 182201360Srdivacky return (ENOENT); 183201360Srdivacky 184201360Srdivacky memcpy(sap, res->ai_addr, res->ai_addrlen); 185201360Srdivacky 186201360Srdivacky freeaddrinfo(res); 187201360Srdivacky 188201360Srdivacky return (0); 189201360Srdivacky} 190201360Srdivacky 191201360Srdivackystatic int 192201360Srdivackytcp_setup_new(const char *addr, int side, void **ctxp) 193201360Srdivacky{ 194201360Srdivacky struct tcp_ctx *tctx; 195201360Srdivacky int ret, nodelay; 196201360Srdivacky 197201360Srdivacky PJDLOG_ASSERT(addr != NULL); 198201360Srdivacky PJDLOG_ASSERT(side == TCP_SIDE_CLIENT || 199201360Srdivacky side == TCP_SIDE_SERVER_LISTEN); 200201360Srdivacky PJDLOG_ASSERT(ctxp != NULL); 201201360Srdivacky 202201360Srdivacky tctx = malloc(sizeof(*tctx)); 203226890Sdim if (tctx == NULL) 204226890Sdim return (errno); 205226890Sdim 206226890Sdim /* Parse given address. */ 207226890Sdim if ((ret = tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &tctx->tc_sa)) != 0) { 208226890Sdim free(tctx); 209226890Sdim return (ret); 210226890Sdim } 211226890Sdim 212226890Sdim PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 213226890Sdim 214226890Sdim tctx->tc_fd = socket(tctx->tc_sa.ss_family, SOCK_STREAM, 0); 215201360Srdivacky if (tctx->tc_fd == -1) { 216226890Sdim ret = errno; 217226890Sdim free(tctx); 218226890Sdim return (ret); 219226890Sdim } 220226890Sdim 221226890Sdim PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 222226890Sdim 223226890Sdim /* Socket settings. */ 224226890Sdim nodelay = 1; 225226890Sdim if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, 226226890Sdim sizeof(nodelay)) == -1) { 227201360Srdivacky pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY"); 228201360Srdivacky } 229201360Srdivacky 230201360Srdivacky tctx->tc_side = side; 231201360Srdivacky tctx->tc_magic = TCP_CTX_MAGIC; 232201360Srdivacky *ctxp = tctx; 233201360Srdivacky 234226890Sdim return (0); 235263509Sdim} 236226890Sdim 237201360Srdivackystatic int 238201360Srdivackytcp_setup_wrap(int fd, int side, void **ctxp) 239201360Srdivacky{ 240201360Srdivacky struct tcp_ctx *tctx; 241201360Srdivacky 242201360Srdivacky PJDLOG_ASSERT(fd >= 0); 243207618Srdivacky PJDLOG_ASSERT(side == TCP_SIDE_CLIENT || 244201360Srdivacky side == TCP_SIDE_SERVER_WORK); 245207618Srdivacky PJDLOG_ASSERT(ctxp != NULL); 246221345Sdim 247221345Sdim tctx = malloc(sizeof(*tctx)); 248221345Sdim if (tctx == NULL) 249221345Sdim return (errno); 250235633Sdim 251223017Sdim tctx->tc_fd = fd; 252252723Sdim tctx->tc_sa.ss_family = AF_UNSPEC; 253252723Sdim tctx->tc_side = side; 254252723Sdim tctx->tc_magic = TCP_CTX_MAGIC; 255252723Sdim *ctxp = tctx; 256252723Sdim 257252723Sdim return (0); 258252723Sdim} 259252723Sdim 260193323Sedstatic int 261201360Srdivackytcp_client(const char *srcaddr, const char *dstaddr, void **ctxp) 262193323Sed{ 263193323Sed struct tcp_ctx *tctx; 264193323Sed struct sockaddr_storage sa; 265193323Sed int ret; 266201360Srdivacky 267193323Sed ret = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, ctxp); 268210299Sed if (ret != 0) 269210299Sed return (ret); 270210299Sed tctx = *ctxp; 271210299Sed if (srcaddr == NULL) 272210299Sed return (0); 273210299Sed ret = tcp_addr(srcaddr, 0, &sa); 274210299Sed if (ret != 0) { 275210299Sed tcp_close(tctx); 276210299Sed return (ret); 277210299Sed } 278210299Sed if (bind(tctx->tc_fd, (struct sockaddr *)&sa, sa.ss_len) < 0) { 279210299Sed ret = errno; 280210299Sed tcp_close(tctx); 281210299Sed return (ret); 282210299Sed } 283210299Sed return (0); 284210299Sed} 285210299Sed 286210299Sedstatic int 287210299Sedtcp_connect(void *ctx, int timeout) 288210299Sed{ 289226890Sdim struct tcp_ctx *tctx = ctx; 290226890Sdim int error, flags; 291226890Sdim 292226890Sdim PJDLOG_ASSERT(tctx != NULL); 293252723Sdim PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 294252723Sdim PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT); 295252723Sdim PJDLOG_ASSERT(tctx->tc_fd >= 0); 296252723Sdim PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 297193323Sed PJDLOG_ASSERT(timeout >= -1); 298201360Srdivacky 299193323Sed flags = fcntl(tctx->tc_fd, F_GETFL); 300193323Sed if (flags == -1) { 301193323Sed pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed"); 302193323Sed return (errno); 303201360Srdivacky } 304193323Sed /* 305210299Sed * We make socket non-blocking so we can handle connection timeout 306210299Sed * manually. 307210299Sed */ 308210299Sed flags |= O_NONBLOCK; 309210299Sed if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 310210299Sed pjdlog_common(LOG_DEBUG, 1, errno, 311210299Sed "fcntl(F_SETFL, O_NONBLOCK) failed"); 312210299Sed return (errno); 313210299Sed } 314210299Sed 315210299Sed if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa, 316210299Sed tctx->tc_sa.ss_len) == 0) { 317210299Sed if (timeout == -1) 318210299Sed return (0); 319210299Sed error = 0; 320210299Sed goto done; 321210299Sed } 322210299Sed if (errno != EINPROGRESS) { 323210299Sed error = errno; 324210299Sed pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed"); 325210299Sed goto done; 326210299Sed } 327210299Sed if (timeout == -1) 328210299Sed return (0); 329210299Sed return (tcp_connect_wait(ctx, timeout)); 330210299Seddone: 331210299Sed flags &= ~O_NONBLOCK; 332210299Sed if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 333210299Sed if (error == 0) 334210299Sed error = errno; 335210299Sed pjdlog_common(LOG_DEBUG, 1, errno, 336210299Sed "fcntl(F_SETFL, ~O_NONBLOCK) failed"); 337210299Sed } 338210299Sed return (error); 339210299Sed} 340210299Sed 341210299Sedstatic int 342210299Sedtcp_connect_wait(void *ctx, int timeout) 343210299Sed{ 344210299Sed struct tcp_ctx *tctx = ctx; 345210299Sed struct timeval tv; 346210299Sed fd_set fdset; 347210299Sed socklen_t esize; 348210299Sed int error, flags, ret; 349210299Sed 350210299Sed PJDLOG_ASSERT(tctx != NULL); 351210299Sed PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 352210299Sed PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT); 353210299Sed PJDLOG_ASSERT(tctx->tc_fd >= 0); 354210299Sed PJDLOG_ASSERT(timeout >= 0); 355210299Sed 356210299Sed tv.tv_sec = timeout; 357210299Sed tv.tv_usec = 0; 358210299Sedagain: 359210299Sed FD_ZERO(&fdset); 360210299Sed FD_SET(tctx->tc_fd, &fdset); 361210299Sed ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv); 362210299Sed if (ret == 0) { 363210299Sed error = ETIMEDOUT; 364210299Sed goto done; 365210299Sed } else if (ret == -1) { 366210299Sed if (errno == EINTR) 367210299Sed goto again; 368210299Sed error = errno; 369210299Sed pjdlog_common(LOG_DEBUG, 1, errno, "select() failed"); 370210299Sed goto done; 371210299Sed } 372210299Sed PJDLOG_ASSERT(ret > 0); 373210299Sed PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset)); 374210299Sed esize = sizeof(error); 375210299Sed if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error, 376210299Sed &esize) == -1) { 377210299Sed error = errno; 378210299Sed pjdlog_common(LOG_DEBUG, 1, errno, 379210299Sed "getsockopt(SO_ERROR) failed"); 380210299Sed goto done; 381210299Sed } 382210299Sed if (error != 0) { 383210299Sed pjdlog_common(LOG_DEBUG, 1, error, 384210299Sed "getsockopt(SO_ERROR) returned error"); 385210299Sed goto done; 386210299Sed } 387210299Sed error = 0; 388210299Seddone: 389210299Sed flags = fcntl(tctx->tc_fd, F_GETFL); 390210299Sed if (flags == -1) { 391210299Sed if (error == 0) 392210299Sed error = errno; 393210299Sed pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed"); 394210299Sed return (error); 395210299Sed } 396210299Sed flags &= ~O_NONBLOCK; 397210299Sed if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 398210299Sed if (error == 0) 399210299Sed error = errno; 400210299Sed pjdlog_common(LOG_DEBUG, 1, errno, 401210299Sed "fcntl(F_SETFL, ~O_NONBLOCK) failed"); 402210299Sed } 403210299Sed return (error); 404210299Sed} 405210299Sed 406210299Sedstatic int 407210299Sedtcp_server(const char *addr, void **ctxp) 408210299Sed{ 409210299Sed struct tcp_ctx *tctx; 410210299Sed int ret, val; 411210299Sed 412210299Sed ret = tcp_setup_new(addr, TCP_SIDE_SERVER_LISTEN, ctxp); 413210299Sed if (ret != 0) 414210299Sed return (ret); 415210299Sed 416210299Sed tctx = *ctxp; 417210299Sed 418210299Sed val = 1; 419210299Sed /* Ignore failure. */ 420210299Sed (void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val, 421210299Sed sizeof(val)); 422210299Sed 423210299Sed PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 424210299Sed 425210299Sed if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa, 426210299Sed tctx->tc_sa.ss_len) < 0) { 427210299Sed ret = errno; 428210299Sed tcp_close(tctx); 429210299Sed return (ret); 430210299Sed } 431210299Sed if (listen(tctx->tc_fd, 8) < 0) { 432210299Sed ret = errno; 433210299Sed tcp_close(tctx); 434210299Sed return (ret); 435210299Sed } 436210299Sed 437210299Sed return (0); 438210299Sed} 439210299Sed 440210299Sedstatic int 441210299Sedtcp_accept(void *ctx, void **newctxp) 442210299Sed{ 443210299Sed struct tcp_ctx *tctx = ctx; 444210299Sed struct tcp_ctx *newtctx; 445210299Sed socklen_t fromlen; 446210299Sed int ret; 447210299Sed 448210299Sed PJDLOG_ASSERT(tctx != NULL); 449210299Sed PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 450210299Sed PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN); 451210299Sed PJDLOG_ASSERT(tctx->tc_fd >= 0); 452210299Sed PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 453210299Sed 454210299Sed newtctx = malloc(sizeof(*newtctx)); 455210299Sed if (newtctx == NULL) 456221345Sdim return (errno); 457226890Sdim 458226890Sdim fromlen = tctx->tc_sa.ss_len; 459252723Sdim newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa, 460263509Sdim &fromlen); 461263509Sdim if (newtctx->tc_fd < 0) { 462263509Sdim ret = errno; 463263509Sdim free(newtctx); 464252723Sdim return (ret); 465252723Sdim } 466193323Sed 467201360Srdivacky newtctx->tc_side = TCP_SIDE_SERVER_WORK; 468193323Sed newtctx->tc_magic = TCP_CTX_MAGIC; 469193323Sed *newctxp = newtctx; 470193323Sed 471193323Sed return (0); 472201360Srdivacky} 473193323Sed 474210299Sedstatic int 475210299Sedtcp_wrap(int fd, bool client, void **ctxp) 476210299Sed{ 477210299Sed 478210299Sed return (tcp_setup_wrap(fd, 479210299Sed client ? TCP_SIDE_CLIENT : TCP_SIDE_SERVER_WORK, ctxp)); 480210299Sed} 481210299Sed 482210299Sedstatic int 483226890Sdimtcp_send(void *ctx, const unsigned char *data, size_t size, int fd) 484210299Sed{ 485210299Sed struct tcp_ctx *tctx = ctx; 486210299Sed 487210299Sed PJDLOG_ASSERT(tctx != NULL); 488210299Sed PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 489210299Sed PJDLOG_ASSERT(tctx->tc_fd >= 0); 490210299Sed PJDLOG_ASSERT(fd == -1); 491210299Sed 492193323Sed return (proto_common_send(tctx->tc_fd, data, size, -1)); 493201360Srdivacky} 494193323Sed 495193323Sedstatic int 496193323Sedtcp_recv(void *ctx, unsigned char *data, size_t size, int *fdp) 497193323Sed{ 498201360Srdivacky struct tcp_ctx *tctx = ctx; 499193323Sed 500210299Sed PJDLOG_ASSERT(tctx != NULL); 501210299Sed PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 502210299Sed PJDLOG_ASSERT(tctx->tc_fd >= 0); 503210299Sed PJDLOG_ASSERT(fdp == NULL); 504210299Sed 505193323Sed return (proto_common_recv(tctx->tc_fd, data, size, NULL)); 506201360Srdivacky} 507193323Sed 508193323Sedstatic int 509193323Sedtcp_descriptor(const void *ctx) 510193323Sed{ 511201360Srdivacky const struct tcp_ctx *tctx = ctx; 512193323Sed 513210299Sed PJDLOG_ASSERT(tctx != NULL); 514210299Sed PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 515210299Sed 516210299Sed return (tctx->tc_fd); 517210299Sed} 518193323Sed 519201360Srdivackystatic bool 520193323Sedtcp_address_match(const void *ctx, const char *addr) 521193323Sed{ 522193323Sed const struct tcp_ctx *tctx = ctx; 523193323Sed struct sockaddr_storage sa1, sa2; 524201360Srdivacky socklen_t salen; 525193323Sed 526201360Srdivacky PJDLOG_ASSERT(tctx != NULL); 527210299Sed PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 528210299Sed 529210299Sed if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sa1) != 0) 530193323Sed return (false); 531201360Srdivacky 532193323Sed salen = sizeof(sa2); 533193323Sed if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) < 0) 534193323Sed return (false); 535193323Sed 536201360Srdivacky if (sa1.ss_family != sa2.ss_family || sa1.ss_len != sa2.ss_len) 537193323Sed return (false); 538210299Sed 539210299Sed switch (sa1.ss_family) { 540210299Sed case AF_INET: 541193323Sed { 542201360Srdivacky struct sockaddr_in *sin1, *sin2; 543193323Sed 544193323Sed sin1 = (struct sockaddr_in *)&sa1; 545193323Sed sin2 = (struct sockaddr_in *)&sa2; 546193323Sed 547201360Srdivacky return (memcmp(&sin1->sin_addr, &sin2->sin_addr, 548193323Sed sizeof(sin1->sin_addr)) == 0); 549210299Sed } 550210299Sed case AF_INET6: 551210299Sed { 552193323Sed struct sockaddr_in6 *sin1, *sin2; 553201360Srdivacky 554193323Sed sin1 = (struct sockaddr_in6 *)&sa1; 555193323Sed sin2 = (struct sockaddr_in6 *)&sa2; 556193323Sed 557193323Sed return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr, 558201360Srdivacky sizeof(sin1->sin6_addr)) == 0); 559193323Sed } 560210299Sed default: 561210299Sed return (false); 562210299Sed } 563210299Sed} 564210299Sed 565210299Sedstatic void 566210299Sedtcp_local_address(const void *ctx, char *addr, size_t size) 567210299Sed{ 568210299Sed const struct tcp_ctx *tctx = ctx; 569210299Sed struct sockaddr_storage sa; 570210299Sed socklen_t salen; 571210299Sed 572210299Sed PJDLOG_ASSERT(tctx != NULL); 573210299Sed PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 574210299Sed 575210299Sed salen = sizeof(sa); 576210299Sed if (getsockname(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) { 577210299Sed PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); 578210299Sed return; 579210299Sed } 580210299Sed PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size); 581193323Sed} 582201360Srdivacky 583193323Sedstatic void 584193323Sedtcp_remote_address(const void *ctx, char *addr, size_t size) 585193323Sed{ 586193323Sed const struct tcp_ctx *tctx = ctx; 587201360Srdivacky struct sockaddr_storage sa; 588201360Srdivacky socklen_t salen; 589210299Sed 590210299Sed PJDLOG_ASSERT(tctx != NULL); 591210299Sed PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 592210299Sed 593193323Sed salen = sizeof(sa); 594201360Srdivacky if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) { 595193323Sed PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); 596193323Sed return; 597193323Sed } 598193323Sed PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size); 599201360Srdivacky} 600193323Sed 601210299Sedstatic void 602210299Sedtcp_close(void *ctx) 603210299Sed{ 604210299Sed struct tcp_ctx *tctx = ctx; 605210299Sed 606193323Sed PJDLOG_ASSERT(tctx != NULL); 607201360Srdivacky PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 608193323Sed 609193323Sed if (tctx->tc_fd >= 0) 610193323Sed close(tctx->tc_fd); 611193323Sed tctx->tc_magic = 0; 612201360Srdivacky free(tctx); 613201360Srdivacky} 614210299Sed 615210299Sedstatic struct proto tcp_proto = { 616210299Sed .prt_name = "tcp", 617210299Sed .prt_client = tcp_client, 618193323Sed .prt_connect = tcp_connect, 619201360Srdivacky .prt_connect_wait = tcp_connect_wait, 620193323Sed .prt_server = tcp_server, 621193323Sed .prt_accept = tcp_accept, 622193323Sed .prt_wrap = tcp_wrap, 623193323Sed .prt_send = tcp_send, 624201360Srdivacky .prt_recv = tcp_recv, 625201360Srdivacky .prt_descriptor = tcp_descriptor, 626210299Sed .prt_address_match = tcp_address_match, 627210299Sed .prt_local_address = tcp_local_address, 628193323Sed .prt_remote_address = tcp_remote_address, 629201360Srdivacky .prt_close = tcp_close 630193323Sed}; 631193323Sed 632193323Sedstatic __constructor void 633193323Sedtcp_ctor(void) 634201360Srdivacky{ 635201360Srdivacky 636210299Sed proto_register(&tcp_proto, true); 637210299Sed} 638193323Sed