1204076Spjd/*- 2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation 3222118Spjd * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net> 4204076Spjd * All rights reserved. 5204076Spjd * 6204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from 7204076Spjd * the FreeBSD Foundation. 8204076Spjd * 9204076Spjd * Redistribution and use in source and binary forms, with or without 10204076Spjd * modification, are permitted provided that the following conditions 11204076Spjd * are met: 12204076Spjd * 1. Redistributions of source code must retain the above copyright 13204076Spjd * notice, this list of conditions and the following disclaimer. 14204076Spjd * 2. Redistributions in binary form must reproduce the above copyright 15204076Spjd * notice, this list of conditions and the following disclaimer in the 16204076Spjd * documentation and/or other materials provided with the distribution. 17204076Spjd * 18204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21204076Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28204076Spjd * SUCH DAMAGE. 29204076Spjd */ 30204076Spjd 31204076Spjd#include <sys/cdefs.h> 32204076Spjd__FBSDID("$FreeBSD$"); 33204076Spjd 34204076Spjd#include <sys/param.h> /* MAXHOSTNAMELEN */ 35219873Spjd#include <sys/socket.h> 36204076Spjd 37219873Spjd#include <arpa/inet.h> 38219873Spjd 39204076Spjd#include <netinet/in.h> 40204076Spjd#include <netinet/tcp.h> 41204076Spjd 42204076Spjd#include <errno.h> 43207390Spjd#include <fcntl.h> 44204076Spjd#include <netdb.h> 45204076Spjd#include <stdbool.h> 46204076Spjd#include <stdint.h> 47204076Spjd#include <stdio.h> 48204076Spjd#include <string.h> 49204076Spjd#include <unistd.h> 50204076Spjd 51204076Spjd#include "pjdlog.h" 52204076Spjd#include "proto_impl.h" 53207390Spjd#include "subr.h" 54204076Spjd 55222118Spjd#define TCP_CTX_MAGIC 0x7c41c 56222116Spjdstruct tcp_ctx { 57204076Spjd int tc_magic; 58222118Spjd struct sockaddr_storage tc_sa; 59204076Spjd int tc_fd; 60204076Spjd int tc_side; 61222116Spjd#define TCP_SIDE_CLIENT 0 62222116Spjd#define TCP_SIDE_SERVER_LISTEN 1 63222116Spjd#define TCP_SIDE_SERVER_WORK 2 64204076Spjd}; 65204076Spjd 66222116Spjdstatic int tcp_connect_wait(void *ctx, int timeout); 67222116Spjdstatic void tcp_close(void *ctx); 68204076Spjd 69204076Spjd/* 70204076Spjd * Function converts the given string to unsigned number. 71204076Spjd */ 72204076Spjdstatic int 73204076Spjdnumfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump) 74204076Spjd{ 75204076Spjd intmax_t digit, num; 76204076Spjd 77204076Spjd if (str[0] == '\0') 78204076Spjd goto invalid; /* Empty string. */ 79204076Spjd num = 0; 80204076Spjd for (; *str != '\0'; str++) { 81204076Spjd if (*str < '0' || *str > '9') 82204076Spjd goto invalid; /* Non-digit character. */ 83204076Spjd digit = *str - '0'; 84204076Spjd if (num > num * 10 + digit) 85204076Spjd goto invalid; /* Overflow. */ 86204076Spjd num = num * 10 + digit; 87204076Spjd if (num > maxnum) 88204076Spjd goto invalid; /* Too big. */ 89204076Spjd } 90204076Spjd if (num < minnum) 91204076Spjd goto invalid; /* Too small. */ 92204076Spjd *nump = num; 93204076Spjd return (0); 94204076Spjdinvalid: 95204076Spjd errno = EINVAL; 96204076Spjd return (-1); 97204076Spjd} 98204076Spjd 99204076Spjdstatic int 100222118Spjdtcp_addr(const char *addr, int defport, struct sockaddr_storage *sap) 101204076Spjd{ 102222118Spjd char iporhost[MAXHOSTNAMELEN], portstr[6]; 103222118Spjd struct addrinfo hints; 104222118Spjd struct addrinfo *res; 105204076Spjd const char *pp; 106222118Spjd intmax_t port; 107204076Spjd size_t size; 108222118Spjd int error; 109204076Spjd 110204076Spjd if (addr == NULL) 111204076Spjd return (-1); 112204076Spjd 113222118Spjd bzero(&hints, sizeof(hints)); 114222118Spjd hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; 115222118Spjd hints.ai_family = PF_UNSPEC; 116222118Spjd hints.ai_socktype = SOCK_STREAM; 117222118Spjd hints.ai_protocol = IPPROTO_TCP; 118222118Spjd 119222118Spjd if (strncasecmp(addr, "tcp4://", 7) == 0) { 120204076Spjd addr += 7; 121222118Spjd hints.ai_family = PF_INET; 122222118Spjd } else if (strncasecmp(addr, "tcp6://", 7) == 0) { 123222118Spjd addr += 7; 124222118Spjd hints.ai_family = PF_INET6; 125222118Spjd } else if (strncasecmp(addr, "tcp://", 6) == 0) { 126204076Spjd addr += 6; 127222118Spjd } else { 128210870Spjd /* 129222116Spjd * Because TCP is the default assume IP or host is given without 130210870Spjd * prefix. 131210870Spjd */ 132210870Spjd } 133204076Spjd 134222118Spjd /* 135222118Spjd * Extract optional port. 136222118Spjd * There are three cases to consider. 137222118Spjd * 1. hostname with port, eg. freefall.freebsd.org:8457 138222118Spjd * 2. IPv4 address with port, eg. 192.168.0.101:8457 139222118Spjd * 3. IPv6 address with port, eg. [fe80::1]:8457 140222118Spjd * We discover IPv6 address by checking for two colons and if port is 141222118Spjd * given, the address has to start with [. 142222118Spjd */ 143222118Spjd pp = NULL; 144222118Spjd if (strchr(addr, ':') != strrchr(addr, ':')) { 145222118Spjd if (addr[0] == '[') 146222118Spjd pp = strrchr(addr, ':'); 147222118Spjd } else { 148222118Spjd pp = strrchr(addr, ':'); 149222118Spjd } 150204076Spjd if (pp == NULL) { 151204076Spjd /* Port not given, use the default. */ 152222118Spjd port = defport; 153204076Spjd } else { 154231017Strociny if (numfromstr(pp + 1, 1, 65535, &port) == -1) 155204076Spjd return (errno); 156204076Spjd } 157222118Spjd (void)snprintf(portstr, sizeof(portstr), "%jd", (intmax_t)port); 158204076Spjd /* Extract host name or IP address. */ 159204076Spjd if (pp == NULL) { 160204076Spjd size = sizeof(iporhost); 161204076Spjd if (strlcpy(iporhost, addr, size) >= size) 162204076Spjd return (ENAMETOOLONG); 163222118Spjd } else if (addr[0] == '[' && pp[-1] == ']') { 164222118Spjd size = (size_t)(pp - addr - 2 + 1); 165222118Spjd if (size > sizeof(iporhost)) 166222118Spjd return (ENAMETOOLONG); 167222118Spjd (void)strlcpy(iporhost, addr + 1, size); 168204076Spjd } else { 169204076Spjd size = (size_t)(pp - addr + 1); 170204076Spjd if (size > sizeof(iporhost)) 171204076Spjd return (ENAMETOOLONG); 172211407Spjd (void)strlcpy(iporhost, addr, size); 173204076Spjd } 174222118Spjd 175222118Spjd error = getaddrinfo(iporhost, portstr, &hints, &res); 176222118Spjd if (error != 0) { 177222118Spjd pjdlog_debug(1, "getaddrinfo(%s, %s) failed: %s.", iporhost, 178222118Spjd portstr, gai_strerror(error)); 179204076Spjd return (EINVAL); 180222118Spjd } 181222118Spjd if (res == NULL) 182222118Spjd return (ENOENT); 183204076Spjd 184222118Spjd memcpy(sap, res->ai_addr, res->ai_addrlen); 185222118Spjd 186222118Spjd freeaddrinfo(res); 187222118Spjd 188204076Spjd return (0); 189204076Spjd} 190204076Spjd 191204076Spjdstatic int 192222116Spjdtcp_setup_new(const char *addr, int side, void **ctxp) 193204076Spjd{ 194222116Spjd struct tcp_ctx *tctx; 195218158Spjd int ret, nodelay; 196204076Spjd 197218194Spjd PJDLOG_ASSERT(addr != NULL); 198222116Spjd PJDLOG_ASSERT(side == TCP_SIDE_CLIENT || 199222116Spjd side == TCP_SIDE_SERVER_LISTEN); 200218194Spjd PJDLOG_ASSERT(ctxp != NULL); 201218194Spjd 202204076Spjd tctx = malloc(sizeof(*tctx)); 203204076Spjd if (tctx == NULL) 204204076Spjd return (errno); 205204076Spjd 206204076Spjd /* Parse given address. */ 207222118Spjd if ((ret = tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &tctx->tc_sa)) != 0) { 208204076Spjd free(tctx); 209204076Spjd return (ret); 210204076Spjd } 211204076Spjd 212222118Spjd PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 213218194Spjd 214222118Spjd tctx->tc_fd = socket(tctx->tc_sa.ss_family, SOCK_STREAM, 0); 215204076Spjd if (tctx->tc_fd == -1) { 216204076Spjd ret = errno; 217204076Spjd free(tctx); 218204076Spjd return (ret); 219204076Spjd } 220204076Spjd 221222118Spjd PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 222219818Spjd 223204076Spjd /* Socket settings. */ 224218158Spjd nodelay = 1; 225218158Spjd if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, 226218158Spjd sizeof(nodelay)) == -1) { 227218194Spjd pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY"); 228204076Spjd } 229204076Spjd 230204076Spjd tctx->tc_side = side; 231222116Spjd tctx->tc_magic = TCP_CTX_MAGIC; 232204076Spjd *ctxp = tctx; 233204076Spjd 234204076Spjd return (0); 235204076Spjd} 236204076Spjd 237204076Spjdstatic int 238222116Spjdtcp_setup_wrap(int fd, int side, void **ctxp) 239218194Spjd{ 240222116Spjd struct tcp_ctx *tctx; 241218194Spjd 242218194Spjd PJDLOG_ASSERT(fd >= 0); 243222116Spjd PJDLOG_ASSERT(side == TCP_SIDE_CLIENT || 244222116Spjd side == TCP_SIDE_SERVER_WORK); 245218194Spjd PJDLOG_ASSERT(ctxp != NULL); 246218194Spjd 247218194Spjd tctx = malloc(sizeof(*tctx)); 248218194Spjd if (tctx == NULL) 249218194Spjd return (errno); 250218194Spjd 251218194Spjd tctx->tc_fd = fd; 252222118Spjd tctx->tc_sa.ss_family = AF_UNSPEC; 253218194Spjd tctx->tc_side = side; 254222116Spjd tctx->tc_magic = TCP_CTX_MAGIC; 255218194Spjd *ctxp = tctx; 256218194Spjd 257218194Spjd return (0); 258218194Spjd} 259218194Spjd 260218194Spjdstatic int 261222116Spjdtcp_client(const char *srcaddr, const char *dstaddr, void **ctxp) 262204076Spjd{ 263222116Spjd struct tcp_ctx *tctx; 264222118Spjd struct sockaddr_storage sa; 265219818Spjd int ret; 266204076Spjd 267222116Spjd ret = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, ctxp); 268219818Spjd if (ret != 0) 269219818Spjd return (ret); 270219818Spjd tctx = *ctxp; 271219818Spjd if (srcaddr == NULL) 272219818Spjd return (0); 273222118Spjd ret = tcp_addr(srcaddr, 0, &sa); 274219818Spjd if (ret != 0) { 275222116Spjd tcp_close(tctx); 276219818Spjd return (ret); 277219818Spjd } 278231017Strociny if (bind(tctx->tc_fd, (struct sockaddr *)&sa, sa.ss_len) == -1) { 279219818Spjd ret = errno; 280222116Spjd tcp_close(tctx); 281219818Spjd return (ret); 282219818Spjd } 283219818Spjd return (0); 284204076Spjd} 285204076Spjd 286204076Spjdstatic int 287222116Spjdtcp_connect(void *ctx, int timeout) 288204076Spjd{ 289222116Spjd struct tcp_ctx *tctx = ctx; 290218193Spjd int error, flags; 291204076Spjd 292218138Spjd PJDLOG_ASSERT(tctx != NULL); 293222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 294222116Spjd PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT); 295218138Spjd PJDLOG_ASSERT(tctx->tc_fd >= 0); 296222118Spjd PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 297218193Spjd PJDLOG_ASSERT(timeout >= -1); 298204076Spjd 299207390Spjd flags = fcntl(tctx->tc_fd, F_GETFL); 300207390Spjd if (flags == -1) { 301229509Strociny pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed"); 302204076Spjd return (errno); 303204076Spjd } 304207390Spjd /* 305211875Spjd * We make socket non-blocking so we can handle connection timeout 306211875Spjd * manually. 307207390Spjd */ 308207390Spjd flags |= O_NONBLOCK; 309207390Spjd if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 310229509Strociny pjdlog_common(LOG_DEBUG, 1, errno, 311229509Strociny "fcntl(F_SETFL, O_NONBLOCK) failed"); 312207390Spjd return (errno); 313207390Spjd } 314204076Spjd 315222118Spjd if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa, 316222118Spjd tctx->tc_sa.ss_len) == 0) { 317218193Spjd if (timeout == -1) 318218193Spjd return (0); 319207390Spjd error = 0; 320207390Spjd goto done; 321207390Spjd } 322207390Spjd if (errno != EINPROGRESS) { 323207390Spjd error = errno; 324207390Spjd pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed"); 325207390Spjd goto done; 326207390Spjd } 327218193Spjd if (timeout == -1) 328218193Spjd return (0); 329222116Spjd return (tcp_connect_wait(ctx, timeout)); 330218193Spjddone: 331218193Spjd flags &= ~O_NONBLOCK; 332218193Spjd if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 333218193Spjd if (error == 0) 334218193Spjd error = errno; 335218193Spjd pjdlog_common(LOG_DEBUG, 1, errno, 336218193Spjd "fcntl(F_SETFL, ~O_NONBLOCK) failed"); 337218193Spjd } 338218193Spjd return (error); 339218193Spjd} 340218193Spjd 341218193Spjdstatic int 342222116Spjdtcp_connect_wait(void *ctx, int timeout) 343218193Spjd{ 344222116Spjd struct tcp_ctx *tctx = ctx; 345218193Spjd struct timeval tv; 346218193Spjd fd_set fdset; 347218193Spjd socklen_t esize; 348218193Spjd int error, flags, ret; 349218193Spjd 350218193Spjd PJDLOG_ASSERT(tctx != NULL); 351222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 352222116Spjd PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT); 353218193Spjd PJDLOG_ASSERT(tctx->tc_fd >= 0); 354218193Spjd PJDLOG_ASSERT(timeout >= 0); 355218193Spjd 356218192Spjd tv.tv_sec = timeout; 357207390Spjd tv.tv_usec = 0; 358207390Spjdagain: 359207390Spjd FD_ZERO(&fdset); 360219864Spjd FD_SET(tctx->tc_fd, &fdset); 361207390Spjd ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv); 362207390Spjd if (ret == 0) { 363207390Spjd error = ETIMEDOUT; 364207390Spjd goto done; 365207390Spjd } else if (ret == -1) { 366207390Spjd if (errno == EINTR) 367207390Spjd goto again; 368207390Spjd error = errno; 369207390Spjd pjdlog_common(LOG_DEBUG, 1, errno, "select() failed"); 370207390Spjd goto done; 371207390Spjd } 372218138Spjd PJDLOG_ASSERT(ret > 0); 373218138Spjd PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset)); 374207390Spjd esize = sizeof(error); 375207390Spjd if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error, 376207390Spjd &esize) == -1) { 377207390Spjd error = errno; 378207390Spjd pjdlog_common(LOG_DEBUG, 1, errno, 379207390Spjd "getsockopt(SO_ERROR) failed"); 380207390Spjd goto done; 381207390Spjd } 382207390Spjd if (error != 0) { 383207390Spjd pjdlog_common(LOG_DEBUG, 1, error, 384207390Spjd "getsockopt(SO_ERROR) returned error"); 385207390Spjd goto done; 386207390Spjd } 387207390Spjd error = 0; 388207390Spjddone: 389218193Spjd flags = fcntl(tctx->tc_fd, F_GETFL); 390218193Spjd if (flags == -1) { 391218193Spjd if (error == 0) 392218193Spjd error = errno; 393218193Spjd pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed"); 394218193Spjd return (error); 395218193Spjd } 396207390Spjd flags &= ~O_NONBLOCK; 397207390Spjd if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 398207390Spjd if (error == 0) 399207390Spjd error = errno; 400207390Spjd pjdlog_common(LOG_DEBUG, 1, errno, 401207390Spjd "fcntl(F_SETFL, ~O_NONBLOCK) failed"); 402207390Spjd } 403207390Spjd return (error); 404204076Spjd} 405204076Spjd 406204076Spjdstatic int 407222116Spjdtcp_server(const char *addr, void **ctxp) 408204076Spjd{ 409222116Spjd struct tcp_ctx *tctx; 410204076Spjd int ret, val; 411204076Spjd 412222116Spjd ret = tcp_setup_new(addr, TCP_SIDE_SERVER_LISTEN, ctxp); 413204076Spjd if (ret != 0) 414204076Spjd return (ret); 415204076Spjd 416204076Spjd tctx = *ctxp; 417204076Spjd 418204076Spjd val = 1; 419204076Spjd /* Ignore failure. */ 420204076Spjd (void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val, 421204076Spjd sizeof(val)); 422204076Spjd 423222118Spjd PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 424218194Spjd 425222118Spjd if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa, 426231017Strociny tctx->tc_sa.ss_len) == -1) { 427204076Spjd ret = errno; 428222116Spjd tcp_close(tctx); 429204076Spjd return (ret); 430204076Spjd } 431231017Strociny if (listen(tctx->tc_fd, 8) == -1) { 432204076Spjd ret = errno; 433222116Spjd tcp_close(tctx); 434204076Spjd return (ret); 435204076Spjd } 436204076Spjd 437204076Spjd return (0); 438204076Spjd} 439204076Spjd 440204076Spjdstatic int 441222116Spjdtcp_accept(void *ctx, void **newctxp) 442204076Spjd{ 443222116Spjd struct tcp_ctx *tctx = ctx; 444222116Spjd struct tcp_ctx *newtctx; 445204076Spjd socklen_t fromlen; 446204076Spjd int ret; 447204076Spjd 448218138Spjd PJDLOG_ASSERT(tctx != NULL); 449222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 450222116Spjd PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN); 451218138Spjd PJDLOG_ASSERT(tctx->tc_fd >= 0); 452222118Spjd PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 453204076Spjd 454204076Spjd newtctx = malloc(sizeof(*newtctx)); 455204076Spjd if (newtctx == NULL) 456204076Spjd return (errno); 457204076Spjd 458222118Spjd fromlen = tctx->tc_sa.ss_len; 459222118Spjd newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa, 460204076Spjd &fromlen); 461231017Strociny if (newtctx->tc_fd == -1) { 462204076Spjd ret = errno; 463204076Spjd free(newtctx); 464204076Spjd return (ret); 465204076Spjd } 466204076Spjd 467222116Spjd newtctx->tc_side = TCP_SIDE_SERVER_WORK; 468222116Spjd newtctx->tc_magic = TCP_CTX_MAGIC; 469204076Spjd *newctxp = newtctx; 470204076Spjd 471204076Spjd return (0); 472204076Spjd} 473204076Spjd 474204076Spjdstatic int 475222116Spjdtcp_wrap(int fd, bool client, void **ctxp) 476204076Spjd{ 477218194Spjd 478222116Spjd return (tcp_setup_wrap(fd, 479222116Spjd client ? TCP_SIDE_CLIENT : TCP_SIDE_SERVER_WORK, ctxp)); 480218194Spjd} 481218194Spjd 482218194Spjdstatic int 483222116Spjdtcp_send(void *ctx, const unsigned char *data, size_t size, int fd) 484218194Spjd{ 485222116Spjd struct tcp_ctx *tctx = ctx; 486204076Spjd 487218138Spjd PJDLOG_ASSERT(tctx != NULL); 488222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 489218138Spjd PJDLOG_ASSERT(tctx->tc_fd >= 0); 490218194Spjd PJDLOG_ASSERT(fd == -1); 491204076Spjd 492218194Spjd return (proto_common_send(tctx->tc_fd, data, size, -1)); 493204076Spjd} 494204076Spjd 495204076Spjdstatic int 496222116Spjdtcp_recv(void *ctx, unsigned char *data, size_t size, int *fdp) 497204076Spjd{ 498222116Spjd struct tcp_ctx *tctx = ctx; 499204076Spjd 500218138Spjd PJDLOG_ASSERT(tctx != NULL); 501222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 502218138Spjd PJDLOG_ASSERT(tctx->tc_fd >= 0); 503218194Spjd PJDLOG_ASSERT(fdp == NULL); 504204076Spjd 505218194Spjd return (proto_common_recv(tctx->tc_fd, data, size, NULL)); 506204076Spjd} 507204076Spjd 508204076Spjdstatic int 509222116Spjdtcp_descriptor(const void *ctx) 510204076Spjd{ 511222116Spjd const struct tcp_ctx *tctx = ctx; 512204076Spjd 513218138Spjd PJDLOG_ASSERT(tctx != NULL); 514222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 515204076Spjd 516204076Spjd return (tctx->tc_fd); 517204076Spjd} 518204076Spjd 519204076Spjdstatic bool 520222116Spjdtcp_address_match(const void *ctx, const char *addr) 521204076Spjd{ 522222116Spjd const struct tcp_ctx *tctx = ctx; 523222118Spjd struct sockaddr_storage sa1, sa2; 524222118Spjd socklen_t salen; 525204076Spjd 526218138Spjd PJDLOG_ASSERT(tctx != NULL); 527222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 528204076Spjd 529222118Spjd if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sa1) != 0) 530204076Spjd return (false); 531204076Spjd 532222118Spjd salen = sizeof(sa2); 533231017Strociny if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) == -1) 534204076Spjd return (false); 535204076Spjd 536222118Spjd if (sa1.ss_family != sa2.ss_family || sa1.ss_len != sa2.ss_len) 537222118Spjd return (false); 538222118Spjd 539222118Spjd switch (sa1.ss_family) { 540222118Spjd case AF_INET: 541222118Spjd { 542222118Spjd struct sockaddr_in *sin1, *sin2; 543222118Spjd 544222118Spjd sin1 = (struct sockaddr_in *)&sa1; 545222118Spjd sin2 = (struct sockaddr_in *)&sa2; 546222118Spjd 547222118Spjd return (memcmp(&sin1->sin_addr, &sin2->sin_addr, 548222118Spjd sizeof(sin1->sin_addr)) == 0); 549222118Spjd } 550222118Spjd case AF_INET6: 551222118Spjd { 552222118Spjd struct sockaddr_in6 *sin1, *sin2; 553222118Spjd 554222118Spjd sin1 = (struct sockaddr_in6 *)&sa1; 555222118Spjd sin2 = (struct sockaddr_in6 *)&sa2; 556222118Spjd 557222118Spjd return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr, 558222118Spjd sizeof(sin1->sin6_addr)) == 0); 559222118Spjd } 560222118Spjd default: 561222118Spjd return (false); 562222118Spjd } 563204076Spjd} 564204076Spjd 565204076Spjdstatic void 566222116Spjdtcp_local_address(const void *ctx, char *addr, size_t size) 567204076Spjd{ 568222116Spjd const struct tcp_ctx *tctx = ctx; 569222118Spjd struct sockaddr_storage sa; 570222118Spjd socklen_t salen; 571204076Spjd 572218138Spjd PJDLOG_ASSERT(tctx != NULL); 573222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 574204076Spjd 575222118Spjd salen = sizeof(sa); 576231017Strociny if (getsockname(tctx->tc_fd, (struct sockaddr *)&sa, &salen) == -1) { 577210876Spjd PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); 578204076Spjd return; 579204076Spjd } 580222118Spjd PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size); 581204076Spjd} 582204076Spjd 583204076Spjdstatic void 584222116Spjdtcp_remote_address(const void *ctx, char *addr, size_t size) 585204076Spjd{ 586222116Spjd const struct tcp_ctx *tctx = ctx; 587222118Spjd struct sockaddr_storage sa; 588222118Spjd socklen_t salen; 589204076Spjd 590218138Spjd PJDLOG_ASSERT(tctx != NULL); 591222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 592204076Spjd 593222118Spjd salen = sizeof(sa); 594231017Strociny if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) == -1) { 595210876Spjd PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); 596204076Spjd return; 597204076Spjd } 598222118Spjd PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size); 599204076Spjd} 600204076Spjd 601204076Spjdstatic void 602222116Spjdtcp_close(void *ctx) 603204076Spjd{ 604222116Spjd struct tcp_ctx *tctx = ctx; 605204076Spjd 606218138Spjd PJDLOG_ASSERT(tctx != NULL); 607222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 608204076Spjd 609204076Spjd if (tctx->tc_fd >= 0) 610204076Spjd close(tctx->tc_fd); 611204076Spjd tctx->tc_magic = 0; 612204076Spjd free(tctx); 613204076Spjd} 614204076Spjd 615222116Spjdstatic struct proto tcp_proto = { 616222116Spjd .prt_name = "tcp", 617222116Spjd .prt_client = tcp_client, 618222116Spjd .prt_connect = tcp_connect, 619222116Spjd .prt_connect_wait = tcp_connect_wait, 620222116Spjd .prt_server = tcp_server, 621222116Spjd .prt_accept = tcp_accept, 622222116Spjd .prt_wrap = tcp_wrap, 623222116Spjd .prt_send = tcp_send, 624222116Spjd .prt_recv = tcp_recv, 625222116Spjd .prt_descriptor = tcp_descriptor, 626222116Spjd .prt_address_match = tcp_address_match, 627222116Spjd .prt_local_address = tcp_local_address, 628222116Spjd .prt_remote_address = tcp_remote_address, 629222116Spjd .prt_close = tcp_close 630204076Spjd}; 631204076Spjd 632204076Spjdstatic __constructor void 633222116Spjdtcp_ctor(void) 634204076Spjd{ 635204076Spjd 636222116Spjd proto_register(&tcp_proto, true); 637204076Spjd} 638