proto_tcp.c revision 222116
1204076Spjd/*- 2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation 3204076Spjd * All rights reserved. 4204076Spjd * 5204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from 6204076Spjd * the FreeBSD Foundation. 7204076Spjd * 8204076Spjd * Redistribution and use in source and binary forms, with or without 9204076Spjd * modification, are permitted provided that the following conditions 10204076Spjd * are met: 11204076Spjd * 1. Redistributions of source code must retain the above copyright 12204076Spjd * notice, this list of conditions and the following disclaimer. 13204076Spjd * 2. Redistributions in binary form must reproduce the above copyright 14204076Spjd * notice, this list of conditions and the following disclaimer in the 15204076Spjd * documentation and/or other materials provided with the distribution. 16204076Spjd * 17204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20204076Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27204076Spjd * SUCH DAMAGE. 28204076Spjd */ 29204076Spjd 30204076Spjd#include <sys/cdefs.h> 31204076Spjd__FBSDID("$FreeBSD: head/sbin/hastd/proto_tcp.c 222116 2011-05-20 11:09:02Z pjd $"); 32204076Spjd 33204076Spjd#include <sys/param.h> /* MAXHOSTNAMELEN */ 34219873Spjd#include <sys/socket.h> 35204076Spjd 36219873Spjd#include <arpa/inet.h> 37219873Spjd 38204076Spjd#include <netinet/in.h> 39204076Spjd#include <netinet/tcp.h> 40204076Spjd 41204076Spjd#include <errno.h> 42207390Spjd#include <fcntl.h> 43204076Spjd#include <netdb.h> 44204076Spjd#include <stdbool.h> 45204076Spjd#include <stdint.h> 46204076Spjd#include <stdio.h> 47204076Spjd#include <string.h> 48204076Spjd#include <unistd.h> 49204076Spjd 50204076Spjd#include "pjdlog.h" 51204076Spjd#include "proto_impl.h" 52207390Spjd#include "subr.h" 53204076Spjd 54222116Spjd#define TCP_CTX_MAGIC 0x7c441c 55222116Spjdstruct tcp_ctx { 56204076Spjd int tc_magic; 57204076Spjd struct sockaddr_in tc_sin; 58204076Spjd int tc_fd; 59204076Spjd int tc_side; 60222116Spjd#define TCP_SIDE_CLIENT 0 61222116Spjd#define TCP_SIDE_SERVER_LISTEN 1 62222116Spjd#define TCP_SIDE_SERVER_WORK 2 63204076Spjd}; 64204076Spjd 65222116Spjdstatic int tcp_connect_wait(void *ctx, int timeout); 66222116Spjdstatic void tcp_close(void *ctx); 67204076Spjd 68204076Spjdstatic in_addr_t 69204076Spjdstr2ip(const char *str) 70204076Spjd{ 71204076Spjd struct hostent *hp; 72204076Spjd in_addr_t ip; 73204076Spjd 74204076Spjd ip = inet_addr(str); 75204076Spjd if (ip != INADDR_NONE) { 76204076Spjd /* It is a valid IP address. */ 77204076Spjd return (ip); 78204076Spjd } 79204076Spjd /* Check if it is a valid host name. */ 80204076Spjd hp = gethostbyname(str); 81204076Spjd if (hp == NULL) 82204076Spjd return (INADDR_NONE); 83204076Spjd return (((struct in_addr *)(void *)hp->h_addr)->s_addr); 84204076Spjd} 85204076Spjd 86204076Spjd/* 87204076Spjd * Function converts the given string to unsigned number. 88204076Spjd */ 89204076Spjdstatic int 90204076Spjdnumfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump) 91204076Spjd{ 92204076Spjd intmax_t digit, num; 93204076Spjd 94204076Spjd if (str[0] == '\0') 95204076Spjd goto invalid; /* Empty string. */ 96204076Spjd num = 0; 97204076Spjd for (; *str != '\0'; str++) { 98204076Spjd if (*str < '0' || *str > '9') 99204076Spjd goto invalid; /* Non-digit character. */ 100204076Spjd digit = *str - '0'; 101204076Spjd if (num > num * 10 + digit) 102204076Spjd goto invalid; /* Overflow. */ 103204076Spjd num = num * 10 + digit; 104204076Spjd if (num > maxnum) 105204076Spjd goto invalid; /* Too big. */ 106204076Spjd } 107204076Spjd if (num < minnum) 108204076Spjd goto invalid; /* Too small. */ 109204076Spjd *nump = num; 110204076Spjd return (0); 111204076Spjdinvalid: 112204076Spjd errno = EINVAL; 113204076Spjd return (-1); 114204076Spjd} 115204076Spjd 116204076Spjdstatic int 117222116Spjdtcp_addr(const char *addr, int defport, struct sockaddr_in *sinp) 118204076Spjd{ 119204076Spjd char iporhost[MAXHOSTNAMELEN]; 120204076Spjd const char *pp; 121204076Spjd size_t size; 122204076Spjd in_addr_t ip; 123204076Spjd 124204076Spjd if (addr == NULL) 125204076Spjd return (-1); 126204076Spjd 127222116Spjd if (strncasecmp(addr, "tcp://", 7) == 0) 128204076Spjd addr += 7; 129204076Spjd else if (strncasecmp(addr, "tcp://", 6) == 0) 130204076Spjd addr += 6; 131210870Spjd else { 132210870Spjd /* 133222116Spjd * Because TCP is the default assume IP or host is given without 134210870Spjd * prefix. 135210870Spjd */ 136210870Spjd } 137204076Spjd 138204076Spjd sinp->sin_family = AF_INET; 139204076Spjd sinp->sin_len = sizeof(*sinp); 140204076Spjd /* Extract optional port. */ 141204076Spjd pp = strrchr(addr, ':'); 142204076Spjd if (pp == NULL) { 143204076Spjd /* Port not given, use the default. */ 144219818Spjd sinp->sin_port = htons(defport); 145204076Spjd } else { 146204076Spjd intmax_t port; 147204076Spjd 148204076Spjd if (numfromstr(pp + 1, 1, 65535, &port) < 0) 149204076Spjd return (errno); 150204076Spjd sinp->sin_port = htons(port); 151204076Spjd } 152204076Spjd /* Extract host name or IP address. */ 153204076Spjd if (pp == NULL) { 154204076Spjd size = sizeof(iporhost); 155204076Spjd if (strlcpy(iporhost, addr, size) >= size) 156204076Spjd return (ENAMETOOLONG); 157204076Spjd } else { 158204076Spjd size = (size_t)(pp - addr + 1); 159204076Spjd if (size > sizeof(iporhost)) 160204076Spjd return (ENAMETOOLONG); 161211407Spjd (void)strlcpy(iporhost, addr, size); 162204076Spjd } 163204076Spjd /* Convert string (IP address or host name) to in_addr_t. */ 164204076Spjd ip = str2ip(iporhost); 165204076Spjd if (ip == INADDR_NONE) 166204076Spjd return (EINVAL); 167204076Spjd sinp->sin_addr.s_addr = ip; 168204076Spjd 169204076Spjd return (0); 170204076Spjd} 171204076Spjd 172204076Spjdstatic int 173222116Spjdtcp_setup_new(const char *addr, int side, void **ctxp) 174204076Spjd{ 175222116Spjd struct tcp_ctx *tctx; 176218158Spjd int ret, nodelay; 177204076Spjd 178218194Spjd PJDLOG_ASSERT(addr != NULL); 179222116Spjd PJDLOG_ASSERT(side == TCP_SIDE_CLIENT || 180222116Spjd side == TCP_SIDE_SERVER_LISTEN); 181218194Spjd PJDLOG_ASSERT(ctxp != NULL); 182218194Spjd 183204076Spjd tctx = malloc(sizeof(*tctx)); 184204076Spjd if (tctx == NULL) 185204076Spjd return (errno); 186204076Spjd 187204076Spjd /* Parse given address. */ 188222116Spjd if ((ret = tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, 189219873Spjd &tctx->tc_sin)) != 0) { 190204076Spjd free(tctx); 191204076Spjd return (ret); 192204076Spjd } 193204076Spjd 194218194Spjd PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); 195218194Spjd 196204076Spjd tctx->tc_fd = socket(AF_INET, SOCK_STREAM, 0); 197204076Spjd if (tctx->tc_fd == -1) { 198204076Spjd ret = errno; 199204076Spjd free(tctx); 200204076Spjd return (ret); 201204076Spjd } 202204076Spjd 203219818Spjd PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); 204219818Spjd 205204076Spjd /* Socket settings. */ 206218158Spjd nodelay = 1; 207218158Spjd if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, 208218158Spjd sizeof(nodelay)) == -1) { 209218194Spjd pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY"); 210204076Spjd } 211204076Spjd 212204076Spjd tctx->tc_side = side; 213222116Spjd tctx->tc_magic = TCP_CTX_MAGIC; 214204076Spjd *ctxp = tctx; 215204076Spjd 216204076Spjd return (0); 217204076Spjd} 218204076Spjd 219204076Spjdstatic int 220222116Spjdtcp_setup_wrap(int fd, int side, void **ctxp) 221218194Spjd{ 222222116Spjd struct tcp_ctx *tctx; 223218194Spjd 224218194Spjd PJDLOG_ASSERT(fd >= 0); 225222116Spjd PJDLOG_ASSERT(side == TCP_SIDE_CLIENT || 226222116Spjd side == TCP_SIDE_SERVER_WORK); 227218194Spjd PJDLOG_ASSERT(ctxp != NULL); 228218194Spjd 229218194Spjd tctx = malloc(sizeof(*tctx)); 230218194Spjd if (tctx == NULL) 231218194Spjd return (errno); 232218194Spjd 233218194Spjd tctx->tc_fd = fd; 234218194Spjd tctx->tc_sin.sin_family = AF_UNSPEC; 235218194Spjd tctx->tc_side = side; 236222116Spjd tctx->tc_magic = TCP_CTX_MAGIC; 237218194Spjd *ctxp = tctx; 238218194Spjd 239218194Spjd return (0); 240218194Spjd} 241218194Spjd 242218194Spjdstatic int 243222116Spjdtcp_client(const char *srcaddr, const char *dstaddr, void **ctxp) 244204076Spjd{ 245222116Spjd struct tcp_ctx *tctx; 246219818Spjd struct sockaddr_in sin; 247219818Spjd int ret; 248204076Spjd 249222116Spjd ret = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, ctxp); 250219818Spjd if (ret != 0) 251219818Spjd return (ret); 252219818Spjd tctx = *ctxp; 253219818Spjd if (srcaddr == NULL) 254219818Spjd return (0); 255222116Spjd ret = tcp_addr(srcaddr, 0, &sin); 256219818Spjd if (ret != 0) { 257222116Spjd tcp_close(tctx); 258219818Spjd return (ret); 259219818Spjd } 260219818Spjd if (bind(tctx->tc_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 261219818Spjd ret = errno; 262222116Spjd tcp_close(tctx); 263219818Spjd return (ret); 264219818Spjd } 265219818Spjd return (0); 266204076Spjd} 267204076Spjd 268204076Spjdstatic int 269222116Spjdtcp_connect(void *ctx, int timeout) 270204076Spjd{ 271222116Spjd struct tcp_ctx *tctx = ctx; 272218193Spjd int error, flags; 273204076Spjd 274218138Spjd PJDLOG_ASSERT(tctx != NULL); 275222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 276222116Spjd PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT); 277218138Spjd PJDLOG_ASSERT(tctx->tc_fd >= 0); 278218193Spjd PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); 279218193Spjd PJDLOG_ASSERT(timeout >= -1); 280204076Spjd 281207390Spjd flags = fcntl(tctx->tc_fd, F_GETFL); 282207390Spjd if (flags == -1) { 283207390Spjd KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno, 284207390Spjd "fcntl(F_GETFL) failed")); 285204076Spjd return (errno); 286204076Spjd } 287207390Spjd /* 288211875Spjd * We make socket non-blocking so we can handle connection timeout 289211875Spjd * manually. 290207390Spjd */ 291207390Spjd flags |= O_NONBLOCK; 292207390Spjd if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 293207390Spjd KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno, 294207390Spjd "fcntl(F_SETFL, O_NONBLOCK) failed")); 295207390Spjd return (errno); 296207390Spjd } 297204076Spjd 298207390Spjd if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, 299207390Spjd sizeof(tctx->tc_sin)) == 0) { 300218193Spjd if (timeout == -1) 301218193Spjd return (0); 302207390Spjd error = 0; 303207390Spjd goto done; 304207390Spjd } 305207390Spjd if (errno != EINPROGRESS) { 306207390Spjd error = errno; 307207390Spjd pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed"); 308207390Spjd goto done; 309207390Spjd } 310218193Spjd if (timeout == -1) 311218193Spjd return (0); 312222116Spjd return (tcp_connect_wait(ctx, timeout)); 313218193Spjddone: 314218193Spjd flags &= ~O_NONBLOCK; 315218193Spjd if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 316218193Spjd if (error == 0) 317218193Spjd error = errno; 318218193Spjd pjdlog_common(LOG_DEBUG, 1, errno, 319218193Spjd "fcntl(F_SETFL, ~O_NONBLOCK) failed"); 320218193Spjd } 321218193Spjd return (error); 322218193Spjd} 323218193Spjd 324218193Spjdstatic int 325222116Spjdtcp_connect_wait(void *ctx, int timeout) 326218193Spjd{ 327222116Spjd struct tcp_ctx *tctx = ctx; 328218193Spjd struct timeval tv; 329218193Spjd fd_set fdset; 330218193Spjd socklen_t esize; 331218193Spjd int error, flags, ret; 332218193Spjd 333218193Spjd PJDLOG_ASSERT(tctx != NULL); 334222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 335222116Spjd PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT); 336218193Spjd PJDLOG_ASSERT(tctx->tc_fd >= 0); 337218193Spjd PJDLOG_ASSERT(timeout >= 0); 338218193Spjd 339218192Spjd tv.tv_sec = timeout; 340207390Spjd tv.tv_usec = 0; 341207390Spjdagain: 342207390Spjd FD_ZERO(&fdset); 343219864Spjd FD_SET(tctx->tc_fd, &fdset); 344207390Spjd ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv); 345207390Spjd if (ret == 0) { 346207390Spjd error = ETIMEDOUT; 347207390Spjd goto done; 348207390Spjd } else if (ret == -1) { 349207390Spjd if (errno == EINTR) 350207390Spjd goto again; 351207390Spjd error = errno; 352207390Spjd pjdlog_common(LOG_DEBUG, 1, errno, "select() failed"); 353207390Spjd goto done; 354207390Spjd } 355218138Spjd PJDLOG_ASSERT(ret > 0); 356218138Spjd PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset)); 357207390Spjd esize = sizeof(error); 358207390Spjd if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error, 359207390Spjd &esize) == -1) { 360207390Spjd error = errno; 361207390Spjd pjdlog_common(LOG_DEBUG, 1, errno, 362207390Spjd "getsockopt(SO_ERROR) failed"); 363207390Spjd goto done; 364207390Spjd } 365207390Spjd if (error != 0) { 366207390Spjd pjdlog_common(LOG_DEBUG, 1, error, 367207390Spjd "getsockopt(SO_ERROR) returned error"); 368207390Spjd goto done; 369207390Spjd } 370207390Spjd error = 0; 371207390Spjddone: 372218193Spjd flags = fcntl(tctx->tc_fd, F_GETFL); 373218193Spjd if (flags == -1) { 374218193Spjd if (error == 0) 375218193Spjd error = errno; 376218193Spjd pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed"); 377218193Spjd return (error); 378218193Spjd } 379207390Spjd flags &= ~O_NONBLOCK; 380207390Spjd if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 381207390Spjd if (error == 0) 382207390Spjd error = errno; 383207390Spjd pjdlog_common(LOG_DEBUG, 1, errno, 384207390Spjd "fcntl(F_SETFL, ~O_NONBLOCK) failed"); 385207390Spjd } 386207390Spjd return (error); 387204076Spjd} 388204076Spjd 389204076Spjdstatic int 390222116Spjdtcp_server(const char *addr, void **ctxp) 391204076Spjd{ 392222116Spjd struct tcp_ctx *tctx; 393204076Spjd int ret, val; 394204076Spjd 395222116Spjd ret = tcp_setup_new(addr, TCP_SIDE_SERVER_LISTEN, ctxp); 396204076Spjd if (ret != 0) 397204076Spjd return (ret); 398204076Spjd 399204076Spjd tctx = *ctxp; 400204076Spjd 401204076Spjd val = 1; 402204076Spjd /* Ignore failure. */ 403204076Spjd (void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val, 404204076Spjd sizeof(val)); 405204076Spjd 406218194Spjd PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); 407218194Spjd 408204076Spjd if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, 409204076Spjd sizeof(tctx->tc_sin)) < 0) { 410204076Spjd ret = errno; 411222116Spjd tcp_close(tctx); 412204076Spjd return (ret); 413204076Spjd } 414204076Spjd if (listen(tctx->tc_fd, 8) < 0) { 415204076Spjd ret = errno; 416222116Spjd tcp_close(tctx); 417204076Spjd return (ret); 418204076Spjd } 419204076Spjd 420204076Spjd return (0); 421204076Spjd} 422204076Spjd 423204076Spjdstatic int 424222116Spjdtcp_accept(void *ctx, void **newctxp) 425204076Spjd{ 426222116Spjd struct tcp_ctx *tctx = ctx; 427222116Spjd struct tcp_ctx *newtctx; 428204076Spjd socklen_t fromlen; 429204076Spjd int ret; 430204076Spjd 431218138Spjd PJDLOG_ASSERT(tctx != NULL); 432222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 433222116Spjd PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN); 434218138Spjd PJDLOG_ASSERT(tctx->tc_fd >= 0); 435218194Spjd PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); 436204076Spjd 437204076Spjd newtctx = malloc(sizeof(*newtctx)); 438204076Spjd if (newtctx == NULL) 439204076Spjd return (errno); 440204076Spjd 441204076Spjd fromlen = sizeof(tctx->tc_sin); 442204076Spjd newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, 443204076Spjd &fromlen); 444204076Spjd if (newtctx->tc_fd < 0) { 445204076Spjd ret = errno; 446204076Spjd free(newtctx); 447204076Spjd return (ret); 448204076Spjd } 449204076Spjd 450222116Spjd newtctx->tc_side = TCP_SIDE_SERVER_WORK; 451222116Spjd newtctx->tc_magic = TCP_CTX_MAGIC; 452204076Spjd *newctxp = newtctx; 453204076Spjd 454204076Spjd return (0); 455204076Spjd} 456204076Spjd 457204076Spjdstatic int 458222116Spjdtcp_wrap(int fd, bool client, void **ctxp) 459204076Spjd{ 460218194Spjd 461222116Spjd return (tcp_setup_wrap(fd, 462222116Spjd client ? TCP_SIDE_CLIENT : TCP_SIDE_SERVER_WORK, ctxp)); 463218194Spjd} 464218194Spjd 465218194Spjdstatic int 466222116Spjdtcp_send(void *ctx, const unsigned char *data, size_t size, int fd) 467218194Spjd{ 468222116Spjd struct tcp_ctx *tctx = ctx; 469204076Spjd 470218138Spjd PJDLOG_ASSERT(tctx != NULL); 471222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 472218138Spjd PJDLOG_ASSERT(tctx->tc_fd >= 0); 473218194Spjd PJDLOG_ASSERT(fd == -1); 474204076Spjd 475218194Spjd return (proto_common_send(tctx->tc_fd, data, size, -1)); 476204076Spjd} 477204076Spjd 478204076Spjdstatic int 479222116Spjdtcp_recv(void *ctx, unsigned char *data, size_t size, int *fdp) 480204076Spjd{ 481222116Spjd struct tcp_ctx *tctx = ctx; 482204076Spjd 483218138Spjd PJDLOG_ASSERT(tctx != NULL); 484222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 485218138Spjd PJDLOG_ASSERT(tctx->tc_fd >= 0); 486218194Spjd PJDLOG_ASSERT(fdp == NULL); 487204076Spjd 488218194Spjd return (proto_common_recv(tctx->tc_fd, data, size, NULL)); 489204076Spjd} 490204076Spjd 491204076Spjdstatic int 492222116Spjdtcp_descriptor(const void *ctx) 493204076Spjd{ 494222116Spjd const struct tcp_ctx *tctx = ctx; 495204076Spjd 496218138Spjd PJDLOG_ASSERT(tctx != NULL); 497222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 498204076Spjd 499204076Spjd return (tctx->tc_fd); 500204076Spjd} 501204076Spjd 502204076Spjdstatic bool 503222116Spjdtcp_address_match(const void *ctx, const char *addr) 504204076Spjd{ 505222116Spjd const struct tcp_ctx *tctx = ctx; 506204076Spjd struct sockaddr_in sin; 507204076Spjd socklen_t sinlen; 508204076Spjd in_addr_t ip1, ip2; 509204076Spjd 510218138Spjd PJDLOG_ASSERT(tctx != NULL); 511222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 512204076Spjd 513222116Spjd if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sin) != 0) 514204076Spjd return (false); 515204076Spjd ip1 = sin.sin_addr.s_addr; 516204076Spjd 517204076Spjd sinlen = sizeof(sin); 518204076Spjd if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) 519204076Spjd return (false); 520204076Spjd ip2 = sin.sin_addr.s_addr; 521204076Spjd 522204076Spjd return (ip1 == ip2); 523204076Spjd} 524204076Spjd 525204076Spjdstatic void 526222116Spjdtcp_local_address(const void *ctx, char *addr, size_t size) 527204076Spjd{ 528222116Spjd const struct tcp_ctx *tctx = ctx; 529204076Spjd struct sockaddr_in sin; 530204076Spjd socklen_t sinlen; 531204076Spjd 532218138Spjd PJDLOG_ASSERT(tctx != NULL); 533222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 534204076Spjd 535204076Spjd sinlen = sizeof(sin); 536204076Spjd if (getsockname(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) { 537210876Spjd PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); 538204076Spjd return; 539204076Spjd } 540222116Spjd PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sin) < (ssize_t)size); 541204076Spjd} 542204076Spjd 543204076Spjdstatic void 544222116Spjdtcp_remote_address(const void *ctx, char *addr, size_t size) 545204076Spjd{ 546222116Spjd const struct tcp_ctx *tctx = ctx; 547204076Spjd struct sockaddr_in sin; 548204076Spjd socklen_t sinlen; 549204076Spjd 550218138Spjd PJDLOG_ASSERT(tctx != NULL); 551222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 552204076Spjd 553204076Spjd sinlen = sizeof(sin); 554204076Spjd if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) { 555210876Spjd PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); 556204076Spjd return; 557204076Spjd } 558222116Spjd PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sin) < (ssize_t)size); 559204076Spjd} 560204076Spjd 561204076Spjdstatic void 562222116Spjdtcp_close(void *ctx) 563204076Spjd{ 564222116Spjd struct tcp_ctx *tctx = ctx; 565204076Spjd 566218138Spjd PJDLOG_ASSERT(tctx != NULL); 567222116Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 568204076Spjd 569204076Spjd if (tctx->tc_fd >= 0) 570204076Spjd close(tctx->tc_fd); 571204076Spjd tctx->tc_magic = 0; 572204076Spjd free(tctx); 573204076Spjd} 574204076Spjd 575222116Spjdstatic struct proto tcp_proto = { 576222116Spjd .prt_name = "tcp", 577222116Spjd .prt_client = tcp_client, 578222116Spjd .prt_connect = tcp_connect, 579222116Spjd .prt_connect_wait = tcp_connect_wait, 580222116Spjd .prt_server = tcp_server, 581222116Spjd .prt_accept = tcp_accept, 582222116Spjd .prt_wrap = tcp_wrap, 583222116Spjd .prt_send = tcp_send, 584222116Spjd .prt_recv = tcp_recv, 585222116Spjd .prt_descriptor = tcp_descriptor, 586222116Spjd .prt_address_match = tcp_address_match, 587222116Spjd .prt_local_address = tcp_local_address, 588222116Spjd .prt_remote_address = tcp_remote_address, 589222116Spjd .prt_close = tcp_close 590204076Spjd}; 591204076Spjd 592204076Spjdstatic __constructor void 593222116Spjdtcp_ctor(void) 594204076Spjd{ 595204076Spjd 596222116Spjd proto_register(&tcp_proto, true); 597204076Spjd} 598