1243730Srwatson/*- 2243730Srwatson * Copyright (c) 2009-2010 The FreeBSD Foundation 3243730Srwatson * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net> 4243730Srwatson * All rights reserved. 5243730Srwatson * 6243730Srwatson * This software was developed by Pawel Jakub Dawidek under sponsorship from 7243730Srwatson * the FreeBSD Foundation. 8243730Srwatson * 9243730Srwatson * Redistribution and use in source and binary forms, with or without 10243730Srwatson * modification, are permitted provided that the following conditions 11243730Srwatson * are met: 12243730Srwatson * 1. Redistributions of source code must retain the above copyright 13243730Srwatson * notice, this list of conditions and the following disclaimer. 14243730Srwatson * 2. Redistributions in binary form must reproduce the above copyright 15243730Srwatson * notice, this list of conditions and the following disclaimer in the 16243730Srwatson * documentation and/or other materials provided with the distribution. 17243730Srwatson * 18243730Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19243730Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20243730Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21243730Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22243730Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23243730Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24243730Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25243730Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26243730Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27243730Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28243730Srwatson * SUCH DAMAGE. 29243730Srwatson */ 30243730Srwatson 31243734Srwatson#include <config/config.h> 32243730Srwatson 33243730Srwatson#include <sys/param.h> /* MAXHOSTNAMELEN */ 34243730Srwatson#include <sys/socket.h> 35243730Srwatson 36243730Srwatson#include <arpa/inet.h> 37243730Srwatson 38243730Srwatson#include <netinet/in.h> 39243730Srwatson#include <netinet/tcp.h> 40243730Srwatson 41243730Srwatson#include <errno.h> 42243730Srwatson#include <fcntl.h> 43243730Srwatson#include <netdb.h> 44243730Srwatson#include <stdbool.h> 45243730Srwatson#include <stdint.h> 46243730Srwatson#include <stdio.h> 47243730Srwatson#include <stdlib.h> 48243730Srwatson#include <string.h> 49243730Srwatson#include <unistd.h> 50243730Srwatson 51243730Srwatson#ifndef HAVE_STRLCPY 52243730Srwatson#include <compat/strlcpy.h> 53243730Srwatson#endif 54243730Srwatson 55243730Srwatson#include "pjdlog.h" 56243730Srwatson#include "proto_impl.h" 57243730Srwatson#include "subr.h" 58243730Srwatson 59243730Srwatson#define TCP_CTX_MAGIC 0x7c41c 60243730Srwatsonstruct tcp_ctx { 61243730Srwatson int tc_magic; 62243730Srwatson struct sockaddr_storage tc_sa; 63243730Srwatson int tc_fd; 64243730Srwatson int tc_side; 65243730Srwatson#define TCP_SIDE_CLIENT 0 66243730Srwatson#define TCP_SIDE_SERVER_LISTEN 1 67243730Srwatson#define TCP_SIDE_SERVER_WORK 2 68243730Srwatson bool tc_wait_called; 69243730Srwatson}; 70243730Srwatson 71243730Srwatsonstatic int tcp_connect_wait(void *ctx, int timeout); 72243730Srwatsonstatic void tcp_close(void *ctx); 73243730Srwatson 74243730Srwatson/* 75243730Srwatson * Function converts the given string to unsigned number. 76243730Srwatson */ 77243730Srwatsonstatic int 78243730Srwatsonnumfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump) 79243730Srwatson{ 80243730Srwatson intmax_t digit, num; 81243730Srwatson 82243730Srwatson if (str[0] == '\0') 83243730Srwatson goto invalid; /* Empty string. */ 84243730Srwatson num = 0; 85243730Srwatson for (; *str != '\0'; str++) { 86243730Srwatson if (*str < '0' || *str > '9') 87243730Srwatson goto invalid; /* Non-digit character. */ 88243730Srwatson digit = *str - '0'; 89243730Srwatson if (num > num * 10 + digit) 90243730Srwatson goto invalid; /* Overflow. */ 91243730Srwatson num = num * 10 + digit; 92243730Srwatson if (num > maxnum) 93243730Srwatson goto invalid; /* Too big. */ 94243730Srwatson } 95243730Srwatson if (num < minnum) 96243730Srwatson goto invalid; /* Too small. */ 97243730Srwatson *nump = num; 98243730Srwatson return (0); 99243730Srwatsoninvalid: 100243730Srwatson errno = EINVAL; 101243730Srwatson return (-1); 102243730Srwatson} 103243730Srwatson 104243730Srwatsonstatic int 105243730Srwatsontcp_addr(const char *addr, int defport, struct sockaddr_storage *sap) 106243730Srwatson{ 107243730Srwatson char iporhost[MAXHOSTNAMELEN], portstr[6]; 108243730Srwatson struct addrinfo hints; 109243730Srwatson struct addrinfo *res; 110243730Srwatson const char *pp; 111243730Srwatson intmax_t port; 112243730Srwatson size_t size; 113243730Srwatson int error; 114243730Srwatson 115243730Srwatson if (addr == NULL) 116243730Srwatson return (-1); 117243730Srwatson 118243730Srwatson bzero(&hints, sizeof(hints)); 119243730Srwatson hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; 120243730Srwatson hints.ai_family = PF_UNSPEC; 121243730Srwatson hints.ai_socktype = SOCK_STREAM; 122243730Srwatson hints.ai_protocol = IPPROTO_TCP; 123243730Srwatson 124243730Srwatson if (strncasecmp(addr, "tcp4://", 7) == 0) { 125243730Srwatson addr += 7; 126243730Srwatson hints.ai_family = PF_INET; 127243730Srwatson } else if (strncasecmp(addr, "tcp6://", 7) == 0) { 128243730Srwatson addr += 7; 129243730Srwatson hints.ai_family = PF_INET6; 130243730Srwatson } else if (strncasecmp(addr, "tcp://", 6) == 0) { 131243730Srwatson addr += 6; 132243730Srwatson } else { 133243730Srwatson /* 134243730Srwatson * Because TCP is the default assume IP or host is given without 135243730Srwatson * prefix. 136243730Srwatson */ 137243730Srwatson } 138243730Srwatson 139243730Srwatson /* 140243730Srwatson * Extract optional port. 141243730Srwatson * There are three cases to consider. 142243730Srwatson * 1. hostname with port, eg. freefall.freebsd.org:8457 143243730Srwatson * 2. IPv4 address with port, eg. 192.168.0.101:8457 144243730Srwatson * 3. IPv6 address with port, eg. [fe80::1]:8457 145243730Srwatson * We discover IPv6 address by checking for two colons and if port is 146243730Srwatson * given, the address has to start with [. 147243730Srwatson */ 148243730Srwatson pp = NULL; 149243730Srwatson if (strchr(addr, ':') != strrchr(addr, ':')) { 150243730Srwatson if (addr[0] == '[') 151243730Srwatson pp = strrchr(addr, ':'); 152243730Srwatson } else { 153243730Srwatson pp = strrchr(addr, ':'); 154243730Srwatson } 155243730Srwatson if (pp == NULL) { 156243730Srwatson /* Port not given, use the default. */ 157243730Srwatson port = defport; 158243730Srwatson } else { 159243730Srwatson if (numfromstr(pp + 1, 1, 65535, &port) < 0) 160243730Srwatson return (errno); 161243730Srwatson } 162243730Srwatson (void)snprintf(portstr, sizeof(portstr), "%jd", (intmax_t)port); 163243730Srwatson /* Extract host name or IP address. */ 164243730Srwatson if (pp == NULL) { 165243730Srwatson size = sizeof(iporhost); 166243730Srwatson if (strlcpy(iporhost, addr, size) >= size) 167243730Srwatson return (ENAMETOOLONG); 168243730Srwatson } else if (addr[0] == '[' && pp[-1] == ']') { 169243730Srwatson size = (size_t)(pp - addr - 2 + 1); 170243730Srwatson if (size > sizeof(iporhost)) 171243730Srwatson return (ENAMETOOLONG); 172243730Srwatson (void)strlcpy(iporhost, addr + 1, size); 173243730Srwatson } else { 174243730Srwatson size = (size_t)(pp - addr + 1); 175243730Srwatson if (size > sizeof(iporhost)) 176243730Srwatson return (ENAMETOOLONG); 177243730Srwatson (void)strlcpy(iporhost, addr, size); 178243730Srwatson } 179243730Srwatson 180243730Srwatson error = getaddrinfo(iporhost, portstr, &hints, &res); 181243730Srwatson if (error != 0) { 182243730Srwatson pjdlog_debug(1, "getaddrinfo(%s, %s) failed: %s.", iporhost, 183243730Srwatson portstr, gai_strerror(error)); 184243730Srwatson return (EINVAL); 185243730Srwatson } 186243730Srwatson if (res == NULL) 187243730Srwatson return (ENOENT); 188243730Srwatson 189243730Srwatson memcpy(sap, res->ai_addr, res->ai_addrlen); 190243730Srwatson 191243730Srwatson freeaddrinfo(res); 192243730Srwatson 193243730Srwatson return (0); 194243730Srwatson} 195243730Srwatson 196243730Srwatsonstatic int 197243730Srwatsontcp_setup_new(const char *addr, int side, struct tcp_ctx **tctxp) 198243730Srwatson{ 199243730Srwatson struct tcp_ctx *tctx; 200243730Srwatson int error, nodelay; 201243730Srwatson 202243730Srwatson PJDLOG_ASSERT(addr != NULL); 203243730Srwatson PJDLOG_ASSERT(side == TCP_SIDE_CLIENT || 204243730Srwatson side == TCP_SIDE_SERVER_LISTEN); 205243730Srwatson PJDLOG_ASSERT(tctxp != NULL); 206243730Srwatson 207243730Srwatson tctx = malloc(sizeof(*tctx)); 208243730Srwatson if (tctx == NULL) 209243730Srwatson return (errno); 210243730Srwatson 211243730Srwatson /* Parse given address. */ 212243730Srwatson error = tcp_addr(addr, atoi(proto_get("tcp:port")), &tctx->tc_sa); 213243730Srwatson if (error != 0) { 214243730Srwatson free(tctx); 215243730Srwatson return (error); 216243730Srwatson } 217243730Srwatson 218243730Srwatson PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 219243730Srwatson 220243730Srwatson tctx->tc_fd = socket(tctx->tc_sa.ss_family, SOCK_STREAM, 0); 221243730Srwatson if (tctx->tc_fd == -1) { 222243730Srwatson error = errno; 223243730Srwatson free(tctx); 224243730Srwatson return (error); 225243730Srwatson } 226243730Srwatson 227243730Srwatson PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 228243730Srwatson 229243730Srwatson /* Socket settings. */ 230243730Srwatson nodelay = 1; 231243730Srwatson if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, 232243730Srwatson sizeof(nodelay)) == -1) { 233243730Srwatson pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY"); 234243730Srwatson } 235243730Srwatson 236243730Srwatson tctx->tc_wait_called = (side == TCP_SIDE_CLIENT ? false : true); 237243730Srwatson tctx->tc_side = side; 238243730Srwatson tctx->tc_magic = TCP_CTX_MAGIC; 239243730Srwatson *tctxp = tctx; 240243730Srwatson 241243730Srwatson return (0); 242243730Srwatson} 243243730Srwatson 244243730Srwatsonstatic socklen_t 245243730Srwatsonsockaddr_len(const struct sockaddr_storage *ss) 246243730Srwatson{ 247243730Srwatson 248243730Srwatson#ifdef HAVE_SOCKADDR_STORAGE_SS_LEN 249243730Srwatson return (ss->ss_len); 250243730Srwatson#else 251243730Srwatson switch (ss->ss_family) { 252243730Srwatson case AF_INET: 253243730Srwatson return (sizeof(struct sockaddr_in)); 254243730Srwatson case AF_INET6: 255243730Srwatson return (sizeof(struct sockaddr_in6)); 256243730Srwatson default: 257243730Srwatson PJDLOG_ABORT("Unexpected family %hhu.", ss->ss_family); 258243730Srwatson } 259243730Srwatson#endif 260243730Srwatson} 261243730Srwatson 262243730Srwatsonstatic int 263243730Srwatsontcp_connect(const char *srcaddr, const char *dstaddr, int timeout, void **ctxp) 264243730Srwatson{ 265243730Srwatson struct tcp_ctx *tctx; 266243730Srwatson struct sockaddr_storage sa; 267243730Srwatson int error, flags, ret; 268243730Srwatson 269243730Srwatson PJDLOG_ASSERT(srcaddr == NULL || srcaddr[0] != '\0'); 270243730Srwatson PJDLOG_ASSERT(dstaddr != NULL); 271243730Srwatson PJDLOG_ASSERT(timeout >= -1); 272243730Srwatson 273243730Srwatson error = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, &tctx); 274243730Srwatson if (error != 0) 275243730Srwatson return (error); 276243730Srwatson if (srcaddr != NULL) { 277243730Srwatson error = tcp_addr(srcaddr, 0, &sa); 278243730Srwatson if (error != 0) 279243730Srwatson goto fail; 280243730Srwatson if (bind(tctx->tc_fd, (struct sockaddr *)&sa, 281243730Srwatson sockaddr_len(&sa)) == -1) { 282243730Srwatson error = errno; 283243730Srwatson goto fail; 284243730Srwatson } 285243730Srwatson } 286243730Srwatson 287243730Srwatson flags = fcntl(tctx->tc_fd, F_GETFL); 288243730Srwatson if (flags == -1) { 289243730Srwatson error = errno; 290243730Srwatson pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed"); 291243730Srwatson goto fail; 292243730Srwatson } 293243730Srwatson /* 294243730Srwatson * We make socket non-blocking so we can handle connection timeout 295243730Srwatson * manually. 296243730Srwatson */ 297243730Srwatson flags |= O_NONBLOCK; 298243730Srwatson if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 299243730Srwatson error = errno; 300243730Srwatson pjdlog_common(LOG_DEBUG, 1, errno, 301243730Srwatson "fcntl(F_SETFL, O_NONBLOCK) failed"); 302243730Srwatson goto fail; 303243730Srwatson } 304243730Srwatson 305243730Srwatson ret = connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa, 306243730Srwatson sockaddr_len(&tctx->tc_sa)); 307243730Srwatson if (ret == -1 && errno != EINPROGRESS) { 308243730Srwatson error = errno; 309243730Srwatson pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed"); 310243730Srwatson goto fail; 311243730Srwatson } 312243730Srwatson 313243730Srwatson if (timeout >= 0) { 314243730Srwatson if (ret == -1) { 315243730Srwatson /* Connection still in progress. Wait for it. */ 316243730Srwatson error = tcp_connect_wait(tctx, timeout); 317243730Srwatson if (error != 0) 318243730Srwatson goto fail; 319243730Srwatson } else { 320243730Srwatson /* Connection already complete. */ 321243730Srwatson flags &= ~O_NONBLOCK; 322243730Srwatson if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 323243730Srwatson error = errno; 324243730Srwatson pjdlog_common(LOG_DEBUG, 1, errno, 325243730Srwatson "fcntl(F_SETFL, ~O_NONBLOCK) failed"); 326243730Srwatson goto fail; 327243730Srwatson } 328243730Srwatson } 329243730Srwatson } 330243730Srwatson 331243730Srwatson *ctxp = tctx; 332243730Srwatson return (0); 333243730Srwatsonfail: 334243730Srwatson tcp_close(tctx); 335243730Srwatson return (error); 336243730Srwatson} 337243730Srwatson 338243730Srwatsonstatic int 339243730Srwatsontcp_connect_wait(void *ctx, int timeout) 340243730Srwatson{ 341243730Srwatson struct tcp_ctx *tctx = ctx; 342243730Srwatson struct timeval tv; 343243730Srwatson fd_set fdset; 344243730Srwatson socklen_t esize; 345243730Srwatson int error, flags, ret; 346243730Srwatson 347243730Srwatson PJDLOG_ASSERT(tctx != NULL); 348243730Srwatson PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 349243730Srwatson PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT); 350243730Srwatson PJDLOG_ASSERT(!tctx->tc_wait_called); 351243730Srwatson PJDLOG_ASSERT(tctx->tc_fd >= 0); 352243730Srwatson PJDLOG_ASSERT(timeout >= 0); 353243730Srwatson 354243730Srwatson tv.tv_sec = timeout; 355243730Srwatson tv.tv_usec = 0; 356243730Srwatsonagain: 357243730Srwatson FD_ZERO(&fdset); 358243730Srwatson FD_SET(tctx->tc_fd, &fdset); 359243730Srwatson ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv); 360243730Srwatson if (ret == 0) { 361243730Srwatson error = ETIMEDOUT; 362243730Srwatson goto done; 363243730Srwatson } else if (ret == -1) { 364243730Srwatson if (errno == EINTR) 365243730Srwatson goto again; 366243730Srwatson error = errno; 367243730Srwatson pjdlog_common(LOG_DEBUG, 1, errno, "select() failed"); 368243730Srwatson goto done; 369243730Srwatson } 370243730Srwatson PJDLOG_ASSERT(ret > 0); 371243730Srwatson PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset)); 372243730Srwatson esize = sizeof(error); 373243730Srwatson if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error, 374243730Srwatson &esize) == -1) { 375243730Srwatson error = errno; 376243730Srwatson pjdlog_common(LOG_DEBUG, 1, errno, 377243730Srwatson "getsockopt(SO_ERROR) failed"); 378243730Srwatson goto done; 379243730Srwatson } 380243730Srwatson if (error != 0) { 381243730Srwatson pjdlog_common(LOG_DEBUG, 1, error, 382243730Srwatson "getsockopt(SO_ERROR) returned error"); 383243730Srwatson goto done; 384243730Srwatson } 385243730Srwatson error = 0; 386243730Srwatson tctx->tc_wait_called = true; 387243730Srwatsondone: 388243730Srwatson flags = fcntl(tctx->tc_fd, F_GETFL); 389243730Srwatson if (flags == -1) { 390243730Srwatson if (error == 0) 391243730Srwatson error = errno; 392243730Srwatson pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed"); 393243730Srwatson return (error); 394243730Srwatson } 395243730Srwatson flags &= ~O_NONBLOCK; 396243730Srwatson if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 397243730Srwatson if (error == 0) 398243730Srwatson error = errno; 399243730Srwatson pjdlog_common(LOG_DEBUG, 1, errno, 400243730Srwatson "fcntl(F_SETFL, ~O_NONBLOCK) failed"); 401243730Srwatson } 402243730Srwatson return (error); 403243730Srwatson} 404243730Srwatson 405243730Srwatsonstatic int 406243730Srwatsontcp_server(const char *addr, void **ctxp) 407243730Srwatson{ 408243730Srwatson struct tcp_ctx *tctx; 409243730Srwatson int error, val; 410243730Srwatson 411243730Srwatson error = tcp_setup_new(addr, TCP_SIDE_SERVER_LISTEN, &tctx); 412243730Srwatson if (error != 0) 413243730Srwatson return (error); 414243730Srwatson 415243730Srwatson val = 1; 416243730Srwatson /* Ignore failure. */ 417243730Srwatson (void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val, 418243730Srwatson sizeof(val)); 419243730Srwatson 420243730Srwatson PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 421243730Srwatson 422243730Srwatson if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa, 423243730Srwatson sockaddr_len(&tctx->tc_sa)) == -1) { 424243730Srwatson error = errno; 425243730Srwatson tcp_close(tctx); 426243730Srwatson return (error); 427243730Srwatson } 428243730Srwatson if (listen(tctx->tc_fd, 8) == -1) { 429243730Srwatson error = errno; 430243730Srwatson tcp_close(tctx); 431243730Srwatson return (error); 432243730Srwatson } 433243730Srwatson 434243730Srwatson *ctxp = tctx; 435243730Srwatson 436243730Srwatson return (0); 437243730Srwatson} 438243730Srwatson 439243730Srwatsonstatic int 440243730Srwatsontcp_accept(void *ctx, void **newctxp) 441243730Srwatson{ 442243730Srwatson struct tcp_ctx *tctx = ctx; 443243730Srwatson struct tcp_ctx *newtctx; 444243730Srwatson socklen_t fromlen; 445243730Srwatson int ret; 446243730Srwatson 447243730Srwatson PJDLOG_ASSERT(tctx != NULL); 448243730Srwatson PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 449243730Srwatson PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN); 450243730Srwatson PJDLOG_ASSERT(tctx->tc_fd >= 0); 451243730Srwatson PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 452243730Srwatson 453243730Srwatson newtctx = malloc(sizeof(*newtctx)); 454243730Srwatson if (newtctx == NULL) 455243730Srwatson return (errno); 456243730Srwatson 457243730Srwatson fromlen = sockaddr_len(&tctx->tc_sa); 458243730Srwatson newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa, 459243730Srwatson &fromlen); 460243730Srwatson if (newtctx->tc_fd < 0) { 461243730Srwatson ret = errno; 462243730Srwatson free(newtctx); 463243730Srwatson return (ret); 464243730Srwatson } 465243730Srwatson 466243730Srwatson newtctx->tc_wait_called = true; 467243730Srwatson newtctx->tc_side = TCP_SIDE_SERVER_WORK; 468243730Srwatson newtctx->tc_magic = TCP_CTX_MAGIC; 469243730Srwatson *newctxp = newtctx; 470243730Srwatson 471243730Srwatson return (0); 472243730Srwatson} 473243730Srwatson 474243730Srwatsonstatic int 475243730Srwatsontcp_wrap(int fd, bool client, void **ctxp) 476243730Srwatson{ 477243730Srwatson struct tcp_ctx *tctx; 478243730Srwatson 479243730Srwatson PJDLOG_ASSERT(fd >= 0); 480243730Srwatson PJDLOG_ASSERT(ctxp != NULL); 481243730Srwatson 482243730Srwatson tctx = malloc(sizeof(*tctx)); 483243730Srwatson if (tctx == NULL) 484243730Srwatson return (errno); 485243730Srwatson 486243730Srwatson tctx->tc_fd = fd; 487243730Srwatson tctx->tc_sa.ss_family = AF_UNSPEC; 488243730Srwatson tctx->tc_wait_called = (client ? false : true); 489243730Srwatson tctx->tc_side = (client ? TCP_SIDE_CLIENT : TCP_SIDE_SERVER_WORK); 490243730Srwatson tctx->tc_magic = TCP_CTX_MAGIC; 491243730Srwatson *ctxp = tctx; 492243730Srwatson 493243730Srwatson return (0); 494243730Srwatson} 495243730Srwatson 496243730Srwatsonstatic int 497243730Srwatsontcp_send(void *ctx, const unsigned char *data, size_t size, int fd) 498243730Srwatson{ 499243730Srwatson struct tcp_ctx *tctx = ctx; 500243730Srwatson 501243730Srwatson PJDLOG_ASSERT(tctx != NULL); 502243730Srwatson PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 503243730Srwatson PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT || 504243730Srwatson tctx->tc_side == TCP_SIDE_SERVER_WORK); 505243730Srwatson PJDLOG_ASSERT(tctx->tc_wait_called); 506243730Srwatson PJDLOG_ASSERT(tctx->tc_fd >= 0); 507243730Srwatson PJDLOG_ASSERT(fd == -1); 508243730Srwatson 509243730Srwatson return (proto_common_send(tctx->tc_fd, data, size, -1)); 510243730Srwatson} 511243730Srwatson 512243730Srwatsonstatic int 513243730Srwatsontcp_recv(void *ctx, unsigned char *data, size_t size, int *fdp) 514243730Srwatson{ 515243730Srwatson struct tcp_ctx *tctx = ctx; 516243730Srwatson 517243730Srwatson PJDLOG_ASSERT(tctx != NULL); 518243730Srwatson PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 519243730Srwatson PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT || 520243730Srwatson tctx->tc_side == TCP_SIDE_SERVER_WORK); 521243730Srwatson PJDLOG_ASSERT(tctx->tc_wait_called); 522243730Srwatson PJDLOG_ASSERT(tctx->tc_fd >= 0); 523243730Srwatson PJDLOG_ASSERT(fdp == NULL); 524243730Srwatson 525243730Srwatson return (proto_common_recv(tctx->tc_fd, data, size, NULL)); 526243730Srwatson} 527243730Srwatson 528243730Srwatsonstatic int 529243730Srwatsontcp_descriptor(const void *ctx) 530243730Srwatson{ 531243730Srwatson const struct tcp_ctx *tctx = ctx; 532243730Srwatson 533243730Srwatson PJDLOG_ASSERT(tctx != NULL); 534243730Srwatson PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 535243730Srwatson 536243730Srwatson return (tctx->tc_fd); 537243730Srwatson} 538243730Srwatson 539243730Srwatsonstatic bool 540243730Srwatsontcp_address_match(const void *ctx, const char *addr) 541243730Srwatson{ 542243730Srwatson const struct tcp_ctx *tctx = ctx; 543243730Srwatson struct sockaddr_storage sa1, sa2; 544243730Srwatson socklen_t salen; 545243730Srwatson 546243730Srwatson PJDLOG_ASSERT(tctx != NULL); 547243730Srwatson PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 548243730Srwatson 549243730Srwatson if (tcp_addr(addr, atoi(proto_get("tcp:port")), &sa1) != 0) 550243730Srwatson return (false); 551243730Srwatson 552243730Srwatson salen = sizeof(sa2); 553243730Srwatson if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) < 0) 554243730Srwatson return (false); 555243730Srwatson 556243730Srwatson if (sa1.ss_family != sa2.ss_family) 557243730Srwatson return (false); 558243730Srwatson 559243730Srwatson#ifdef HAVE_SOCKADDR_STORAGE_SS_LEN 560243730Srwatson if (sa1.ss_len != sa2.ss_len) 561243730Srwatson return (false); 562243730Srwatson#endif 563243730Srwatson 564243730Srwatson switch (sa1.ss_family) { 565243730Srwatson case AF_INET: 566243730Srwatson { 567243730Srwatson struct sockaddr_in *sin1, *sin2; 568243730Srwatson 569243730Srwatson sin1 = (struct sockaddr_in *)&sa1; 570243730Srwatson sin2 = (struct sockaddr_in *)&sa2; 571243730Srwatson 572243730Srwatson return (memcmp(&sin1->sin_addr, &sin2->sin_addr, 573243730Srwatson sizeof(sin1->sin_addr)) == 0); 574243730Srwatson } 575243730Srwatson case AF_INET6: 576243730Srwatson { 577243730Srwatson struct sockaddr_in6 *sin1, *sin2; 578243730Srwatson 579243730Srwatson sin1 = (struct sockaddr_in6 *)&sa1; 580243730Srwatson sin2 = (struct sockaddr_in6 *)&sa2; 581243730Srwatson 582243730Srwatson return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr, 583243730Srwatson sizeof(sin1->sin6_addr)) == 0); 584243730Srwatson } 585243730Srwatson default: 586243730Srwatson return (false); 587243730Srwatson } 588243730Srwatson} 589243730Srwatson 590243730Srwatson#ifndef __FreeBSD__ 591243730Srwatsonstatic void 592243730Srwatsonsockaddr_to_string(const void *sa, char *buf, size_t size) 593243730Srwatson{ 594243730Srwatson const struct sockaddr_storage *ss; 595243730Srwatson 596243730Srwatson ss = (const struct sockaddr_storage * const *)sa; 597243730Srwatson switch (ss->ss_family) { 598243730Srwatson case AF_INET: 599243730Srwatson { 600243730Srwatson char addr[INET_ADDRSTRLEN]; 601243730Srwatson const struct sockaddr_in *sin; 602243730Srwatson unsigned int port; 603243730Srwatson 604243730Srwatson sin = (const struct sockaddr_in *)ss; 605243730Srwatson port = ntohs(sin->sin_port); 606243730Srwatson if (inet_ntop(ss->ss_family, &sin->sin_addr, addr, 607243730Srwatson sizeof(addr)) == NULL) { 608243730Srwatson PJDLOG_ABORT("inet_ntop(AF_INET) failed: %s.", 609243730Srwatson strerror(errno)); 610243730Srwatson } 611243730Srwatson snprintf(buf, size, "%s:%u", addr, port); 612243730Srwatson break; 613243730Srwatson } 614243730Srwatson case AF_INET6: 615243730Srwatson { 616243730Srwatson char addr[INET6_ADDRSTRLEN]; 617243730Srwatson const struct sockaddr_in6 *sin; 618243730Srwatson unsigned int port; 619243730Srwatson 620243730Srwatson sin = (const struct sockaddr_in6 *)ss; 621243730Srwatson port = ntohs(sin->sin6_port); 622243730Srwatson if (inet_ntop(ss->ss_family, &sin->sin6_addr, addr, 623243730Srwatson sizeof(addr)) == NULL) { 624243730Srwatson PJDLOG_ABORT("inet_ntop(AF_INET6) failed: %s.", 625243730Srwatson strerror(errno)); 626243730Srwatson } 627243730Srwatson snprintf(buf, size, "[%s]:%u", addr, port); 628243730Srwatson break; 629243730Srwatson } 630243730Srwatson default: 631243730Srwatson snprintf(buf, size, "[unsupported family %hhu]", 632243730Srwatson ss->ss_family); 633243730Srwatson break; 634243730Srwatson } 635243730Srwatson} 636243730Srwatson#endif /* !__FreeBSD__ */ 637243730Srwatson 638243730Srwatsonstatic void 639243730Srwatsontcp_local_address(const void *ctx, char *addr, size_t size) 640243730Srwatson{ 641243730Srwatson const struct tcp_ctx *tctx = ctx; 642243730Srwatson struct sockaddr_storage sa; 643243730Srwatson socklen_t salen; 644243730Srwatson 645243730Srwatson PJDLOG_ASSERT(tctx != NULL); 646243730Srwatson PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 647243730Srwatson 648243730Srwatson salen = sizeof(sa); 649243730Srwatson if (getsockname(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) { 650243730Srwatson PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); 651243730Srwatson return; 652243730Srwatson } 653243730Srwatson#ifdef __FreeBSD__ 654243730Srwatson PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size); 655243730Srwatson#else 656243730Srwatson strlcpy(addr, "tcp://", size); 657243730Srwatson if (size > 6) 658243730Srwatson sockaddr_to_string(&sa, addr + 6, size - 6); 659243730Srwatson#endif 660243730Srwatson} 661243730Srwatson 662243730Srwatsonstatic void 663243730Srwatsontcp_remote_address(const void *ctx, char *addr, size_t size) 664243730Srwatson{ 665243730Srwatson const struct tcp_ctx *tctx = ctx; 666243730Srwatson struct sockaddr_storage sa; 667243730Srwatson socklen_t salen; 668243730Srwatson 669243730Srwatson PJDLOG_ASSERT(tctx != NULL); 670243730Srwatson PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 671243730Srwatson 672243730Srwatson salen = sizeof(sa); 673243730Srwatson if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) { 674243730Srwatson PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); 675243730Srwatson return; 676243730Srwatson } 677243730Srwatson#ifdef __FreeBSD__ 678243730Srwatson PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size); 679243730Srwatson#else 680243730Srwatson strlcpy(addr, "tcp://", size); 681243730Srwatson if (size > 6) 682243730Srwatson sockaddr_to_string(&sa, addr + 6, size - 6); 683243730Srwatson#endif 684243730Srwatson} 685243730Srwatson 686243730Srwatsonstatic void 687243730Srwatsontcp_close(void *ctx) 688243730Srwatson{ 689243730Srwatson struct tcp_ctx *tctx = ctx; 690243730Srwatson 691243730Srwatson PJDLOG_ASSERT(tctx != NULL); 692243730Srwatson PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 693243730Srwatson 694243730Srwatson if (tctx->tc_fd >= 0) 695243730Srwatson close(tctx->tc_fd); 696243730Srwatson tctx->tc_magic = 0; 697243730Srwatson free(tctx); 698243730Srwatson} 699243730Srwatson 700243730Srwatsonstatic struct proto tcp_proto = { 701243730Srwatson .prt_name = "tcp", 702243730Srwatson .prt_connect = tcp_connect, 703243730Srwatson .prt_connect_wait = tcp_connect_wait, 704243730Srwatson .prt_server = tcp_server, 705243730Srwatson .prt_accept = tcp_accept, 706243730Srwatson .prt_wrap = tcp_wrap, 707243730Srwatson .prt_send = tcp_send, 708243730Srwatson .prt_recv = tcp_recv, 709243730Srwatson .prt_descriptor = tcp_descriptor, 710243730Srwatson .prt_address_match = tcp_address_match, 711243730Srwatson .prt_local_address = tcp_local_address, 712243730Srwatson .prt_remote_address = tcp_remote_address, 713243730Srwatson .prt_close = tcp_close 714243730Srwatson}; 715243730Srwatson 716243730Srwatsonstatic __constructor void 717243730Srwatsontcp_ctor(void) 718243730Srwatson{ 719243730Srwatson 720243730Srwatson proto_register(&tcp_proto, true); 721243730Srwatson} 722