proto_tcp.c revision 219864
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_tcp4.c 219864 2011-03-22 10:39:34Z pjd $"); 32204076Spjd 33204076Spjd#include <sys/param.h> /* MAXHOSTNAMELEN */ 34204076Spjd 35204076Spjd#include <netinet/in.h> 36204076Spjd#include <netinet/tcp.h> 37204076Spjd 38204076Spjd#include <errno.h> 39207390Spjd#include <fcntl.h> 40204076Spjd#include <netdb.h> 41204076Spjd#include <stdbool.h> 42204076Spjd#include <stdint.h> 43204076Spjd#include <stdio.h> 44204076Spjd#include <string.h> 45204076Spjd#include <unistd.h> 46204076Spjd 47204076Spjd#include "hast.h" 48204076Spjd#include "pjdlog.h" 49204076Spjd#include "proto_impl.h" 50207390Spjd#include "subr.h" 51204076Spjd 52204076Spjd#define TCP4_CTX_MAGIC 0x7c441c 53204076Spjdstruct tcp4_ctx { 54204076Spjd int tc_magic; 55204076Spjd struct sockaddr_in tc_sin; 56204076Spjd int tc_fd; 57204076Spjd int tc_side; 58204076Spjd#define TCP4_SIDE_CLIENT 0 59204076Spjd#define TCP4_SIDE_SERVER_LISTEN 1 60204076Spjd#define TCP4_SIDE_SERVER_WORK 2 61204076Spjd}; 62204076Spjd 63218193Spjdstatic int tcp4_connect_wait(void *ctx, int timeout); 64204076Spjdstatic void tcp4_close(void *ctx); 65204076Spjd 66204076Spjdstatic in_addr_t 67204076Spjdstr2ip(const char *str) 68204076Spjd{ 69204076Spjd struct hostent *hp; 70204076Spjd in_addr_t ip; 71204076Spjd 72204076Spjd ip = inet_addr(str); 73204076Spjd if (ip != INADDR_NONE) { 74204076Spjd /* It is a valid IP address. */ 75204076Spjd return (ip); 76204076Spjd } 77204076Spjd /* Check if it is a valid host name. */ 78204076Spjd hp = gethostbyname(str); 79204076Spjd if (hp == NULL) 80204076Spjd return (INADDR_NONE); 81204076Spjd return (((struct in_addr *)(void *)hp->h_addr)->s_addr); 82204076Spjd} 83204076Spjd 84204076Spjd/* 85204076Spjd * Function converts the given string to unsigned number. 86204076Spjd */ 87204076Spjdstatic int 88204076Spjdnumfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump) 89204076Spjd{ 90204076Spjd intmax_t digit, num; 91204076Spjd 92204076Spjd if (str[0] == '\0') 93204076Spjd goto invalid; /* Empty string. */ 94204076Spjd num = 0; 95204076Spjd for (; *str != '\0'; str++) { 96204076Spjd if (*str < '0' || *str > '9') 97204076Spjd goto invalid; /* Non-digit character. */ 98204076Spjd digit = *str - '0'; 99204076Spjd if (num > num * 10 + digit) 100204076Spjd goto invalid; /* Overflow. */ 101204076Spjd num = num * 10 + digit; 102204076Spjd if (num > maxnum) 103204076Spjd goto invalid; /* Too big. */ 104204076Spjd } 105204076Spjd if (num < minnum) 106204076Spjd goto invalid; /* Too small. */ 107204076Spjd *nump = num; 108204076Spjd return (0); 109204076Spjdinvalid: 110204076Spjd errno = EINVAL; 111204076Spjd return (-1); 112204076Spjd} 113204076Spjd 114204076Spjdstatic int 115219818Spjdtcp4_addr(const char *addr, int defport, struct sockaddr_in *sinp) 116204076Spjd{ 117204076Spjd char iporhost[MAXHOSTNAMELEN]; 118204076Spjd const char *pp; 119204076Spjd size_t size; 120204076Spjd in_addr_t ip; 121204076Spjd 122204076Spjd if (addr == NULL) 123204076Spjd return (-1); 124204076Spjd 125204076Spjd if (strncasecmp(addr, "tcp4://", 7) == 0) 126204076Spjd addr += 7; 127204076Spjd else if (strncasecmp(addr, "tcp://", 6) == 0) 128204076Spjd addr += 6; 129210870Spjd else { 130210870Spjd /* 131210870Spjd * Because TCP4 is the default assume IP or host is given without 132210870Spjd * prefix. 133210870Spjd */ 134210870Spjd } 135204076Spjd 136204076Spjd sinp->sin_family = AF_INET; 137204076Spjd sinp->sin_len = sizeof(*sinp); 138204076Spjd /* Extract optional port. */ 139204076Spjd pp = strrchr(addr, ':'); 140204076Spjd if (pp == NULL) { 141204076Spjd /* Port not given, use the default. */ 142219818Spjd sinp->sin_port = htons(defport); 143204076Spjd } else { 144204076Spjd intmax_t port; 145204076Spjd 146204076Spjd if (numfromstr(pp + 1, 1, 65535, &port) < 0) 147204076Spjd return (errno); 148204076Spjd sinp->sin_port = htons(port); 149204076Spjd } 150204076Spjd /* Extract host name or IP address. */ 151204076Spjd if (pp == NULL) { 152204076Spjd size = sizeof(iporhost); 153204076Spjd if (strlcpy(iporhost, addr, size) >= size) 154204076Spjd return (ENAMETOOLONG); 155204076Spjd } else { 156204076Spjd size = (size_t)(pp - addr + 1); 157204076Spjd if (size > sizeof(iporhost)) 158204076Spjd return (ENAMETOOLONG); 159211407Spjd (void)strlcpy(iporhost, addr, size); 160204076Spjd } 161204076Spjd /* Convert string (IP address or host name) to in_addr_t. */ 162204076Spjd ip = str2ip(iporhost); 163204076Spjd if (ip == INADDR_NONE) 164204076Spjd return (EINVAL); 165204076Spjd sinp->sin_addr.s_addr = ip; 166204076Spjd 167204076Spjd return (0); 168204076Spjd} 169204076Spjd 170204076Spjdstatic int 171218194Spjdtcp4_setup_new(const char *addr, int side, void **ctxp) 172204076Spjd{ 173204076Spjd struct tcp4_ctx *tctx; 174218158Spjd int ret, nodelay; 175204076Spjd 176218194Spjd PJDLOG_ASSERT(addr != NULL); 177218194Spjd PJDLOG_ASSERT(side == TCP4_SIDE_CLIENT || 178218194Spjd side == TCP4_SIDE_SERVER_LISTEN); 179218194Spjd PJDLOG_ASSERT(ctxp != NULL); 180218194Spjd 181204076Spjd tctx = malloc(sizeof(*tctx)); 182204076Spjd if (tctx == NULL) 183204076Spjd return (errno); 184204076Spjd 185204076Spjd /* Parse given address. */ 186219818Spjd if ((ret = tcp4_addr(addr, HASTD_PORT, &tctx->tc_sin)) != 0) { 187204076Spjd free(tctx); 188204076Spjd return (ret); 189204076Spjd } 190204076Spjd 191218194Spjd PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); 192218194Spjd 193204076Spjd tctx->tc_fd = socket(AF_INET, SOCK_STREAM, 0); 194204076Spjd if (tctx->tc_fd == -1) { 195204076Spjd ret = errno; 196204076Spjd free(tctx); 197204076Spjd return (ret); 198204076Spjd } 199204076Spjd 200219818Spjd PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); 201219818Spjd 202204076Spjd /* Socket settings. */ 203218158Spjd nodelay = 1; 204218158Spjd if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, 205218158Spjd sizeof(nodelay)) == -1) { 206218194Spjd pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY"); 207204076Spjd } 208204076Spjd 209204076Spjd tctx->tc_side = side; 210204076Spjd tctx->tc_magic = TCP4_CTX_MAGIC; 211204076Spjd *ctxp = tctx; 212204076Spjd 213204076Spjd return (0); 214204076Spjd} 215204076Spjd 216204076Spjdstatic int 217218194Spjdtcp4_setup_wrap(int fd, int side, void **ctxp) 218218194Spjd{ 219218194Spjd struct tcp4_ctx *tctx; 220218194Spjd 221218194Spjd PJDLOG_ASSERT(fd >= 0); 222218194Spjd PJDLOG_ASSERT(side == TCP4_SIDE_CLIENT || 223218194Spjd side == TCP4_SIDE_SERVER_WORK); 224218194Spjd PJDLOG_ASSERT(ctxp != NULL); 225218194Spjd 226218194Spjd tctx = malloc(sizeof(*tctx)); 227218194Spjd if (tctx == NULL) 228218194Spjd return (errno); 229218194Spjd 230218194Spjd tctx->tc_fd = fd; 231218194Spjd tctx->tc_sin.sin_family = AF_UNSPEC; 232218194Spjd tctx->tc_side = side; 233218194Spjd tctx->tc_magic = TCP4_CTX_MAGIC; 234218194Spjd *ctxp = tctx; 235218194Spjd 236218194Spjd return (0); 237218194Spjd} 238218194Spjd 239218194Spjdstatic int 240219818Spjdtcp4_client(const char *srcaddr, const char *dstaddr, void **ctxp) 241204076Spjd{ 242219818Spjd struct tcp4_ctx *tctx; 243219818Spjd struct sockaddr_in sin; 244219818Spjd int ret; 245204076Spjd 246219818Spjd ret = tcp4_setup_new(dstaddr, TCP4_SIDE_CLIENT, ctxp); 247219818Spjd if (ret != 0) 248219818Spjd return (ret); 249219818Spjd tctx = *ctxp; 250219818Spjd if (srcaddr == NULL) 251219818Spjd return (0); 252219818Spjd ret = tcp4_addr(srcaddr, 0, &sin); 253219818Spjd if (ret != 0) { 254219818Spjd tcp4_close(tctx); 255219818Spjd return (ret); 256219818Spjd } 257219818Spjd if (bind(tctx->tc_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 258219818Spjd ret = errno; 259219818Spjd tcp4_close(tctx); 260219818Spjd return (ret); 261219818Spjd } 262219818Spjd return (0); 263204076Spjd} 264204076Spjd 265204076Spjdstatic int 266218192Spjdtcp4_connect(void *ctx, int timeout) 267204076Spjd{ 268204076Spjd struct tcp4_ctx *tctx = ctx; 269218193Spjd int error, flags; 270204076Spjd 271218138Spjd PJDLOG_ASSERT(tctx != NULL); 272218138Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 273218138Spjd PJDLOG_ASSERT(tctx->tc_side == TCP4_SIDE_CLIENT); 274218138Spjd PJDLOG_ASSERT(tctx->tc_fd >= 0); 275218193Spjd PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); 276218193Spjd PJDLOG_ASSERT(timeout >= -1); 277204076Spjd 278207390Spjd flags = fcntl(tctx->tc_fd, F_GETFL); 279207390Spjd if (flags == -1) { 280207390Spjd KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno, 281207390Spjd "fcntl(F_GETFL) failed")); 282204076Spjd return (errno); 283204076Spjd } 284207390Spjd /* 285211875Spjd * We make socket non-blocking so we can handle connection timeout 286211875Spjd * manually. 287207390Spjd */ 288207390Spjd flags |= O_NONBLOCK; 289207390Spjd if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 290207390Spjd KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno, 291207390Spjd "fcntl(F_SETFL, O_NONBLOCK) failed")); 292207390Spjd return (errno); 293207390Spjd } 294204076Spjd 295207390Spjd if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, 296207390Spjd sizeof(tctx->tc_sin)) == 0) { 297218193Spjd if (timeout == -1) 298218193Spjd return (0); 299207390Spjd error = 0; 300207390Spjd goto done; 301207390Spjd } 302207390Spjd if (errno != EINPROGRESS) { 303207390Spjd error = errno; 304207390Spjd pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed"); 305207390Spjd goto done; 306207390Spjd } 307218193Spjd if (timeout == -1) 308218193Spjd return (0); 309218193Spjd return (tcp4_connect_wait(ctx, timeout)); 310218193Spjddone: 311218193Spjd flags &= ~O_NONBLOCK; 312218193Spjd if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 313218193Spjd if (error == 0) 314218193Spjd error = errno; 315218193Spjd pjdlog_common(LOG_DEBUG, 1, errno, 316218193Spjd "fcntl(F_SETFL, ~O_NONBLOCK) failed"); 317218193Spjd } 318218193Spjd return (error); 319218193Spjd} 320218193Spjd 321218193Spjdstatic int 322218193Spjdtcp4_connect_wait(void *ctx, int timeout) 323218193Spjd{ 324218193Spjd struct tcp4_ctx *tctx = ctx; 325218193Spjd struct timeval tv; 326218193Spjd fd_set fdset; 327218193Spjd socklen_t esize; 328218193Spjd int error, flags, ret; 329218193Spjd 330218193Spjd PJDLOG_ASSERT(tctx != NULL); 331218193Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 332218193Spjd PJDLOG_ASSERT(tctx->tc_side == TCP4_SIDE_CLIENT); 333218193Spjd PJDLOG_ASSERT(tctx->tc_fd >= 0); 334218193Spjd PJDLOG_ASSERT(timeout >= 0); 335218193Spjd 336218192Spjd tv.tv_sec = timeout; 337207390Spjd tv.tv_usec = 0; 338207390Spjdagain: 339207390Spjd FD_ZERO(&fdset); 340219864Spjd FD_SET(tctx->tc_fd, &fdset); 341207390Spjd ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv); 342207390Spjd if (ret == 0) { 343207390Spjd error = ETIMEDOUT; 344207390Spjd goto done; 345207390Spjd } else if (ret == -1) { 346207390Spjd if (errno == EINTR) 347207390Spjd goto again; 348207390Spjd error = errno; 349207390Spjd pjdlog_common(LOG_DEBUG, 1, errno, "select() failed"); 350207390Spjd goto done; 351207390Spjd } 352218138Spjd PJDLOG_ASSERT(ret > 0); 353218138Spjd PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset)); 354207390Spjd esize = sizeof(error); 355207390Spjd if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error, 356207390Spjd &esize) == -1) { 357207390Spjd error = errno; 358207390Spjd pjdlog_common(LOG_DEBUG, 1, errno, 359207390Spjd "getsockopt(SO_ERROR) failed"); 360207390Spjd goto done; 361207390Spjd } 362207390Spjd if (error != 0) { 363207390Spjd pjdlog_common(LOG_DEBUG, 1, error, 364207390Spjd "getsockopt(SO_ERROR) returned error"); 365207390Spjd goto done; 366207390Spjd } 367207390Spjd error = 0; 368207390Spjddone: 369218193Spjd flags = fcntl(tctx->tc_fd, F_GETFL); 370218193Spjd if (flags == -1) { 371218193Spjd if (error == 0) 372218193Spjd error = errno; 373218193Spjd pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed"); 374218193Spjd return (error); 375218193Spjd } 376207390Spjd flags &= ~O_NONBLOCK; 377207390Spjd if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 378207390Spjd if (error == 0) 379207390Spjd error = errno; 380207390Spjd pjdlog_common(LOG_DEBUG, 1, errno, 381207390Spjd "fcntl(F_SETFL, ~O_NONBLOCK) failed"); 382207390Spjd } 383207390Spjd return (error); 384204076Spjd} 385204076Spjd 386204076Spjdstatic int 387204076Spjdtcp4_server(const char *addr, void **ctxp) 388204076Spjd{ 389204076Spjd struct tcp4_ctx *tctx; 390204076Spjd int ret, val; 391204076Spjd 392218194Spjd ret = tcp4_setup_new(addr, TCP4_SIDE_SERVER_LISTEN, ctxp); 393204076Spjd if (ret != 0) 394204076Spjd return (ret); 395204076Spjd 396204076Spjd tctx = *ctxp; 397204076Spjd 398204076Spjd val = 1; 399204076Spjd /* Ignore failure. */ 400204076Spjd (void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val, 401204076Spjd sizeof(val)); 402204076Spjd 403218194Spjd PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); 404218194Spjd 405204076Spjd if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, 406204076Spjd sizeof(tctx->tc_sin)) < 0) { 407204076Spjd ret = errno; 408204076Spjd tcp4_close(tctx); 409204076Spjd return (ret); 410204076Spjd } 411204076Spjd if (listen(tctx->tc_fd, 8) < 0) { 412204076Spjd ret = errno; 413204076Spjd tcp4_close(tctx); 414204076Spjd return (ret); 415204076Spjd } 416204076Spjd 417204076Spjd return (0); 418204076Spjd} 419204076Spjd 420204076Spjdstatic int 421204076Spjdtcp4_accept(void *ctx, void **newctxp) 422204076Spjd{ 423204076Spjd struct tcp4_ctx *tctx = ctx; 424204076Spjd struct tcp4_ctx *newtctx; 425204076Spjd socklen_t fromlen; 426204076Spjd int ret; 427204076Spjd 428218138Spjd PJDLOG_ASSERT(tctx != NULL); 429218138Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 430218138Spjd PJDLOG_ASSERT(tctx->tc_side == TCP4_SIDE_SERVER_LISTEN); 431218138Spjd PJDLOG_ASSERT(tctx->tc_fd >= 0); 432218194Spjd PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); 433204076Spjd 434204076Spjd newtctx = malloc(sizeof(*newtctx)); 435204076Spjd if (newtctx == NULL) 436204076Spjd return (errno); 437204076Spjd 438204076Spjd fromlen = sizeof(tctx->tc_sin); 439204076Spjd newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, 440204076Spjd &fromlen); 441204076Spjd if (newtctx->tc_fd < 0) { 442204076Spjd ret = errno; 443204076Spjd free(newtctx); 444204076Spjd return (ret); 445204076Spjd } 446204076Spjd 447204076Spjd newtctx->tc_side = TCP4_SIDE_SERVER_WORK; 448204076Spjd newtctx->tc_magic = TCP4_CTX_MAGIC; 449204076Spjd *newctxp = newtctx; 450204076Spjd 451204076Spjd return (0); 452204076Spjd} 453204076Spjd 454204076Spjdstatic int 455218194Spjdtcp4_wrap(int fd, bool client, void **ctxp) 456204076Spjd{ 457218194Spjd 458218194Spjd return (tcp4_setup_wrap(fd, 459218194Spjd client ? TCP4_SIDE_CLIENT : TCP4_SIDE_SERVER_WORK, ctxp)); 460218194Spjd} 461218194Spjd 462218194Spjdstatic int 463218194Spjdtcp4_send(void *ctx, const unsigned char *data, size_t size, int fd) 464218194Spjd{ 465204076Spjd struct tcp4_ctx *tctx = ctx; 466204076Spjd 467218138Spjd PJDLOG_ASSERT(tctx != NULL); 468218138Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 469218138Spjd PJDLOG_ASSERT(tctx->tc_fd >= 0); 470218194Spjd PJDLOG_ASSERT(fd == -1); 471204076Spjd 472218194Spjd return (proto_common_send(tctx->tc_fd, data, size, -1)); 473204076Spjd} 474204076Spjd 475204076Spjdstatic int 476218194Spjdtcp4_recv(void *ctx, unsigned char *data, size_t size, int *fdp) 477204076Spjd{ 478204076Spjd struct tcp4_ctx *tctx = ctx; 479204076Spjd 480218138Spjd PJDLOG_ASSERT(tctx != NULL); 481218138Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 482218138Spjd PJDLOG_ASSERT(tctx->tc_fd >= 0); 483218194Spjd PJDLOG_ASSERT(fdp == NULL); 484204076Spjd 485218194Spjd return (proto_common_recv(tctx->tc_fd, data, size, NULL)); 486204076Spjd} 487204076Spjd 488204076Spjdstatic int 489204076Spjdtcp4_descriptor(const void *ctx) 490204076Spjd{ 491204076Spjd const struct tcp4_ctx *tctx = ctx; 492204076Spjd 493218138Spjd PJDLOG_ASSERT(tctx != NULL); 494218138Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 495204076Spjd 496204076Spjd return (tctx->tc_fd); 497204076Spjd} 498204076Spjd 499204076Spjdstatic bool 500204076Spjdtcp4_address_match(const void *ctx, const char *addr) 501204076Spjd{ 502204076Spjd const struct tcp4_ctx *tctx = ctx; 503204076Spjd struct sockaddr_in sin; 504204076Spjd socklen_t sinlen; 505204076Spjd in_addr_t ip1, ip2; 506204076Spjd 507218138Spjd PJDLOG_ASSERT(tctx != NULL); 508218138Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 509204076Spjd 510219818Spjd if (tcp4_addr(addr, HASTD_PORT, &sin) != 0) 511204076Spjd return (false); 512204076Spjd ip1 = sin.sin_addr.s_addr; 513204076Spjd 514204076Spjd sinlen = sizeof(sin); 515204076Spjd if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) 516204076Spjd return (false); 517204076Spjd ip2 = sin.sin_addr.s_addr; 518204076Spjd 519204076Spjd return (ip1 == ip2); 520204076Spjd} 521204076Spjd 522204076Spjdstatic void 523204076Spjdtcp4_local_address(const void *ctx, char *addr, size_t size) 524204076Spjd{ 525204076Spjd const struct tcp4_ctx *tctx = ctx; 526204076Spjd struct sockaddr_in sin; 527204076Spjd socklen_t sinlen; 528204076Spjd 529218138Spjd PJDLOG_ASSERT(tctx != NULL); 530218138Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 531204076Spjd 532204076Spjd sinlen = sizeof(sin); 533204076Spjd if (getsockname(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) { 534210876Spjd PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); 535204076Spjd return; 536204076Spjd } 537219371Spjd PJDLOG_VERIFY(snprintf(addr, size, "tcp4://%S", &sin) < (ssize_t)size); 538204076Spjd} 539204076Spjd 540204076Spjdstatic void 541204076Spjdtcp4_remote_address(const void *ctx, char *addr, size_t size) 542204076Spjd{ 543204076Spjd const struct tcp4_ctx *tctx = ctx; 544204076Spjd struct sockaddr_in sin; 545204076Spjd socklen_t sinlen; 546204076Spjd 547218138Spjd PJDLOG_ASSERT(tctx != NULL); 548218138Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 549204076Spjd 550204076Spjd sinlen = sizeof(sin); 551204076Spjd if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) { 552210876Spjd PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); 553204076Spjd return; 554204076Spjd } 555219371Spjd PJDLOG_VERIFY(snprintf(addr, size, "tcp4://%S", &sin) < (ssize_t)size); 556204076Spjd} 557204076Spjd 558204076Spjdstatic void 559204076Spjdtcp4_close(void *ctx) 560204076Spjd{ 561204076Spjd struct tcp4_ctx *tctx = ctx; 562204076Spjd 563218138Spjd PJDLOG_ASSERT(tctx != NULL); 564218138Spjd PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 565204076Spjd 566204076Spjd if (tctx->tc_fd >= 0) 567204076Spjd close(tctx->tc_fd); 568204076Spjd tctx->tc_magic = 0; 569204076Spjd free(tctx); 570204076Spjd} 571204076Spjd 572204076Spjdstatic struct hast_proto tcp4_proto = { 573204076Spjd .hp_name = "tcp4", 574204076Spjd .hp_client = tcp4_client, 575204076Spjd .hp_connect = tcp4_connect, 576218193Spjd .hp_connect_wait = tcp4_connect_wait, 577204076Spjd .hp_server = tcp4_server, 578204076Spjd .hp_accept = tcp4_accept, 579218194Spjd .hp_wrap = tcp4_wrap, 580204076Spjd .hp_send = tcp4_send, 581204076Spjd .hp_recv = tcp4_recv, 582204076Spjd .hp_descriptor = tcp4_descriptor, 583204076Spjd .hp_address_match = tcp4_address_match, 584204076Spjd .hp_local_address = tcp4_local_address, 585204076Spjd .hp_remote_address = tcp4_remote_address, 586204076Spjd .hp_close = tcp4_close 587204076Spjd}; 588204076Spjd 589204076Spjdstatic __constructor void 590204076Spjdtcp4_ctor(void) 591204076Spjd{ 592204076Spjd 593210869Spjd proto_register(&tcp4_proto, true); 594204076Spjd} 595