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