1933728eeSPawel Jakub Dawidek /*- 2933728eeSPawel Jakub Dawidek * Copyright (c) 2009-2010 The FreeBSD Foundation 3*dc18c8aeSPawel Jakub Dawidek * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net> 4933728eeSPawel Jakub Dawidek * All rights reserved. 5933728eeSPawel Jakub Dawidek * 6933728eeSPawel Jakub Dawidek * This software was developed by Pawel Jakub Dawidek under sponsorship from 7933728eeSPawel Jakub Dawidek * the FreeBSD Foundation. 8933728eeSPawel Jakub Dawidek * 9933728eeSPawel Jakub Dawidek * Redistribution and use in source and binary forms, with or without 10933728eeSPawel Jakub Dawidek * modification, are permitted provided that the following conditions 11933728eeSPawel Jakub Dawidek * are met: 12933728eeSPawel Jakub Dawidek * 1. Redistributions of source code must retain the above copyright 13933728eeSPawel Jakub Dawidek * notice, this list of conditions and the following disclaimer. 14933728eeSPawel Jakub Dawidek * 2. Redistributions in binary form must reproduce the above copyright 15933728eeSPawel Jakub Dawidek * notice, this list of conditions and the following disclaimer in the 16933728eeSPawel Jakub Dawidek * documentation and/or other materials provided with the distribution. 17933728eeSPawel Jakub Dawidek * 18933728eeSPawel Jakub Dawidek * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19933728eeSPawel Jakub Dawidek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20933728eeSPawel Jakub Dawidek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21933728eeSPawel Jakub Dawidek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22933728eeSPawel Jakub Dawidek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23933728eeSPawel Jakub Dawidek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24933728eeSPawel Jakub Dawidek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25933728eeSPawel Jakub Dawidek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26933728eeSPawel Jakub Dawidek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27933728eeSPawel Jakub Dawidek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28933728eeSPawel Jakub Dawidek * SUCH DAMAGE. 29933728eeSPawel Jakub Dawidek */ 30933728eeSPawel Jakub Dawidek 31933728eeSPawel Jakub Dawidek #include <sys/cdefs.h> 32933728eeSPawel Jakub Dawidek __FBSDID("$FreeBSD$"); 33933728eeSPawel Jakub Dawidek 34933728eeSPawel Jakub Dawidek #include <sys/param.h> /* MAXHOSTNAMELEN */ 35933728eeSPawel Jakub Dawidek #include <sys/socket.h> 36933728eeSPawel Jakub Dawidek 37933728eeSPawel Jakub Dawidek #include <arpa/inet.h> 38933728eeSPawel Jakub Dawidek 39933728eeSPawel Jakub Dawidek #include <netinet/in.h> 40933728eeSPawel Jakub Dawidek #include <netinet/tcp.h> 41933728eeSPawel Jakub Dawidek 42933728eeSPawel Jakub Dawidek #include <errno.h> 43933728eeSPawel Jakub Dawidek #include <fcntl.h> 44933728eeSPawel Jakub Dawidek #include <netdb.h> 45933728eeSPawel Jakub Dawidek #include <stdbool.h> 46933728eeSPawel Jakub Dawidek #include <stdint.h> 47933728eeSPawel Jakub Dawidek #include <stdio.h> 48933728eeSPawel Jakub Dawidek #include <string.h> 49933728eeSPawel Jakub Dawidek #include <unistd.h> 50933728eeSPawel Jakub Dawidek 51933728eeSPawel Jakub Dawidek #include "pjdlog.h" 52933728eeSPawel Jakub Dawidek #include "proto_impl.h" 53933728eeSPawel Jakub Dawidek #include "subr.h" 54933728eeSPawel Jakub Dawidek 55*dc18c8aeSPawel Jakub Dawidek #define TCP_CTX_MAGIC 0x7c41c 56bdbd046bSPawel Jakub Dawidek struct tcp_ctx { 57933728eeSPawel Jakub Dawidek int tc_magic; 58*dc18c8aeSPawel Jakub Dawidek struct sockaddr_storage tc_sa; 59933728eeSPawel Jakub Dawidek int tc_fd; 60933728eeSPawel Jakub Dawidek int tc_side; 61bdbd046bSPawel Jakub Dawidek #define TCP_SIDE_CLIENT 0 62bdbd046bSPawel Jakub Dawidek #define TCP_SIDE_SERVER_LISTEN 1 63bdbd046bSPawel Jakub Dawidek #define TCP_SIDE_SERVER_WORK 2 64933728eeSPawel Jakub Dawidek }; 65933728eeSPawel Jakub Dawidek 66bdbd046bSPawel Jakub Dawidek static int tcp_connect_wait(void *ctx, int timeout); 67bdbd046bSPawel Jakub Dawidek static void tcp_close(void *ctx); 68933728eeSPawel Jakub Dawidek 69933728eeSPawel Jakub Dawidek /* 70933728eeSPawel Jakub Dawidek * Function converts the given string to unsigned number. 71933728eeSPawel Jakub Dawidek */ 72933728eeSPawel Jakub Dawidek static int 73933728eeSPawel Jakub Dawidek numfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump) 74933728eeSPawel Jakub Dawidek { 75933728eeSPawel Jakub Dawidek intmax_t digit, num; 76933728eeSPawel Jakub Dawidek 77933728eeSPawel Jakub Dawidek if (str[0] == '\0') 78933728eeSPawel Jakub Dawidek goto invalid; /* Empty string. */ 79933728eeSPawel Jakub Dawidek num = 0; 80933728eeSPawel Jakub Dawidek for (; *str != '\0'; str++) { 81933728eeSPawel Jakub Dawidek if (*str < '0' || *str > '9') 82933728eeSPawel Jakub Dawidek goto invalid; /* Non-digit character. */ 83933728eeSPawel Jakub Dawidek digit = *str - '0'; 84933728eeSPawel Jakub Dawidek if (num > num * 10 + digit) 85933728eeSPawel Jakub Dawidek goto invalid; /* Overflow. */ 86933728eeSPawel Jakub Dawidek num = num * 10 + digit; 87933728eeSPawel Jakub Dawidek if (num > maxnum) 88933728eeSPawel Jakub Dawidek goto invalid; /* Too big. */ 89933728eeSPawel Jakub Dawidek } 90933728eeSPawel Jakub Dawidek if (num < minnum) 91933728eeSPawel Jakub Dawidek goto invalid; /* Too small. */ 92933728eeSPawel Jakub Dawidek *nump = num; 93933728eeSPawel Jakub Dawidek return (0); 94933728eeSPawel Jakub Dawidek invalid: 95933728eeSPawel Jakub Dawidek errno = EINVAL; 96933728eeSPawel Jakub Dawidek return (-1); 97933728eeSPawel Jakub Dawidek } 98933728eeSPawel Jakub Dawidek 99933728eeSPawel Jakub Dawidek static int 100*dc18c8aeSPawel Jakub Dawidek tcp_addr(const char *addr, int defport, struct sockaddr_storage *sap) 101933728eeSPawel Jakub Dawidek { 102*dc18c8aeSPawel Jakub Dawidek char iporhost[MAXHOSTNAMELEN], portstr[6]; 103*dc18c8aeSPawel Jakub Dawidek struct addrinfo hints; 104*dc18c8aeSPawel Jakub Dawidek struct addrinfo *res; 105933728eeSPawel Jakub Dawidek const char *pp; 106*dc18c8aeSPawel Jakub Dawidek intmax_t port; 107933728eeSPawel Jakub Dawidek size_t size; 108*dc18c8aeSPawel Jakub Dawidek int error; 109933728eeSPawel Jakub Dawidek 110933728eeSPawel Jakub Dawidek if (addr == NULL) 111933728eeSPawel Jakub Dawidek return (-1); 112933728eeSPawel Jakub Dawidek 113*dc18c8aeSPawel Jakub Dawidek bzero(&hints, sizeof(hints)); 114*dc18c8aeSPawel Jakub Dawidek hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; 115*dc18c8aeSPawel Jakub Dawidek hints.ai_family = PF_UNSPEC; 116*dc18c8aeSPawel Jakub Dawidek hints.ai_socktype = SOCK_STREAM; 117*dc18c8aeSPawel Jakub Dawidek hints.ai_protocol = IPPROTO_TCP; 118*dc18c8aeSPawel Jakub Dawidek 119*dc18c8aeSPawel Jakub Dawidek if (strncasecmp(addr, "tcp4://", 7) == 0) { 120933728eeSPawel Jakub Dawidek addr += 7; 121*dc18c8aeSPawel Jakub Dawidek hints.ai_family = PF_INET; 122*dc18c8aeSPawel Jakub Dawidek } else if (strncasecmp(addr, "tcp6://", 7) == 0) { 123*dc18c8aeSPawel Jakub Dawidek addr += 7; 124*dc18c8aeSPawel Jakub Dawidek hints.ai_family = PF_INET6; 125*dc18c8aeSPawel Jakub Dawidek } else if (strncasecmp(addr, "tcp://", 6) == 0) { 126933728eeSPawel Jakub Dawidek addr += 6; 127*dc18c8aeSPawel Jakub Dawidek } else { 128933728eeSPawel Jakub Dawidek /* 129bdbd046bSPawel Jakub Dawidek * Because TCP is the default assume IP or host is given without 130933728eeSPawel Jakub Dawidek * prefix. 131933728eeSPawel Jakub Dawidek */ 132933728eeSPawel Jakub Dawidek } 133933728eeSPawel Jakub Dawidek 134*dc18c8aeSPawel Jakub Dawidek /* 135*dc18c8aeSPawel Jakub Dawidek * Extract optional port. 136*dc18c8aeSPawel Jakub Dawidek * There are three cases to consider. 137*dc18c8aeSPawel Jakub Dawidek * 1. hostname with port, eg. freefall.freebsd.org:8457 138*dc18c8aeSPawel Jakub Dawidek * 2. IPv4 address with port, eg. 192.168.0.101:8457 139*dc18c8aeSPawel Jakub Dawidek * 3. IPv6 address with port, eg. [fe80::1]:8457 140*dc18c8aeSPawel Jakub Dawidek * We discover IPv6 address by checking for two colons and if port is 141*dc18c8aeSPawel Jakub Dawidek * given, the address has to start with [. 142*dc18c8aeSPawel Jakub Dawidek */ 143*dc18c8aeSPawel Jakub Dawidek pp = NULL; 144*dc18c8aeSPawel Jakub Dawidek if (strchr(addr, ':') != strrchr(addr, ':')) { 145*dc18c8aeSPawel Jakub Dawidek if (addr[0] == '[') 146933728eeSPawel Jakub Dawidek pp = strrchr(addr, ':'); 147*dc18c8aeSPawel Jakub Dawidek } else { 148*dc18c8aeSPawel Jakub Dawidek pp = strrchr(addr, ':'); 149*dc18c8aeSPawel Jakub Dawidek } 150933728eeSPawel Jakub Dawidek if (pp == NULL) { 151933728eeSPawel Jakub Dawidek /* Port not given, use the default. */ 152*dc18c8aeSPawel Jakub Dawidek port = defport; 153933728eeSPawel Jakub Dawidek } else { 154933728eeSPawel Jakub Dawidek if (numfromstr(pp + 1, 1, 65535, &port) < 0) 155933728eeSPawel Jakub Dawidek return (errno); 156933728eeSPawel Jakub Dawidek } 157*dc18c8aeSPawel Jakub Dawidek (void)snprintf(portstr, sizeof(portstr), "%jd", (intmax_t)port); 158933728eeSPawel Jakub Dawidek /* Extract host name or IP address. */ 159933728eeSPawel Jakub Dawidek if (pp == NULL) { 160933728eeSPawel Jakub Dawidek size = sizeof(iporhost); 161933728eeSPawel Jakub Dawidek if (strlcpy(iporhost, addr, size) >= size) 162933728eeSPawel Jakub Dawidek return (ENAMETOOLONG); 163*dc18c8aeSPawel Jakub Dawidek } else if (addr[0] == '[' && pp[-1] == ']') { 164*dc18c8aeSPawel Jakub Dawidek size = (size_t)(pp - addr - 2 + 1); 165*dc18c8aeSPawel Jakub Dawidek if (size > sizeof(iporhost)) 166*dc18c8aeSPawel Jakub Dawidek return (ENAMETOOLONG); 167*dc18c8aeSPawel Jakub Dawidek (void)strlcpy(iporhost, addr + 1, size); 168933728eeSPawel Jakub Dawidek } else { 169933728eeSPawel Jakub Dawidek size = (size_t)(pp - addr + 1); 170933728eeSPawel Jakub Dawidek if (size > sizeof(iporhost)) 171933728eeSPawel Jakub Dawidek return (ENAMETOOLONG); 172933728eeSPawel Jakub Dawidek (void)strlcpy(iporhost, addr, size); 173933728eeSPawel Jakub Dawidek } 174*dc18c8aeSPawel Jakub Dawidek 175*dc18c8aeSPawel Jakub Dawidek error = getaddrinfo(iporhost, portstr, &hints, &res); 176*dc18c8aeSPawel Jakub Dawidek if (error != 0) { 177*dc18c8aeSPawel Jakub Dawidek pjdlog_debug(1, "getaddrinfo(%s, %s) failed: %s.", iporhost, 178*dc18c8aeSPawel Jakub Dawidek portstr, gai_strerror(error)); 179933728eeSPawel Jakub Dawidek return (EINVAL); 180*dc18c8aeSPawel Jakub Dawidek } 181*dc18c8aeSPawel Jakub Dawidek if (res == NULL) 182*dc18c8aeSPawel Jakub Dawidek return (ENOENT); 183*dc18c8aeSPawel Jakub Dawidek 184*dc18c8aeSPawel Jakub Dawidek memcpy(sap, res->ai_addr, res->ai_addrlen); 185*dc18c8aeSPawel Jakub Dawidek 186*dc18c8aeSPawel Jakub Dawidek freeaddrinfo(res); 187933728eeSPawel Jakub Dawidek 188933728eeSPawel Jakub Dawidek return (0); 189933728eeSPawel Jakub Dawidek } 190933728eeSPawel Jakub Dawidek 191933728eeSPawel Jakub Dawidek static int 192bdbd046bSPawel Jakub Dawidek tcp_setup_new(const char *addr, int side, void **ctxp) 193933728eeSPawel Jakub Dawidek { 194bdbd046bSPawel Jakub Dawidek struct tcp_ctx *tctx; 195933728eeSPawel Jakub Dawidek int ret, nodelay; 196933728eeSPawel Jakub Dawidek 197933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(addr != NULL); 198bdbd046bSPawel Jakub Dawidek PJDLOG_ASSERT(side == TCP_SIDE_CLIENT || 199bdbd046bSPawel Jakub Dawidek side == TCP_SIDE_SERVER_LISTEN); 200933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(ctxp != NULL); 201933728eeSPawel Jakub Dawidek 202933728eeSPawel Jakub Dawidek tctx = malloc(sizeof(*tctx)); 203933728eeSPawel Jakub Dawidek if (tctx == NULL) 204933728eeSPawel Jakub Dawidek return (errno); 205933728eeSPawel Jakub Dawidek 206933728eeSPawel Jakub Dawidek /* Parse given address. */ 207*dc18c8aeSPawel Jakub Dawidek if ((ret = tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &tctx->tc_sa)) != 0) { 208933728eeSPawel Jakub Dawidek free(tctx); 209933728eeSPawel Jakub Dawidek return (ret); 210933728eeSPawel Jakub Dawidek } 211933728eeSPawel Jakub Dawidek 212*dc18c8aeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 213933728eeSPawel Jakub Dawidek 214*dc18c8aeSPawel Jakub Dawidek tctx->tc_fd = socket(tctx->tc_sa.ss_family, SOCK_STREAM, 0); 215933728eeSPawel Jakub Dawidek if (tctx->tc_fd == -1) { 216933728eeSPawel Jakub Dawidek ret = errno; 217933728eeSPawel Jakub Dawidek free(tctx); 218933728eeSPawel Jakub Dawidek return (ret); 219933728eeSPawel Jakub Dawidek } 220933728eeSPawel Jakub Dawidek 221*dc18c8aeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 222933728eeSPawel Jakub Dawidek 223933728eeSPawel Jakub Dawidek /* Socket settings. */ 224933728eeSPawel Jakub Dawidek nodelay = 1; 225933728eeSPawel Jakub Dawidek if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, 226933728eeSPawel Jakub Dawidek sizeof(nodelay)) == -1) { 227933728eeSPawel Jakub Dawidek pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY"); 228933728eeSPawel Jakub Dawidek } 229933728eeSPawel Jakub Dawidek 230933728eeSPawel Jakub Dawidek tctx->tc_side = side; 231bdbd046bSPawel Jakub Dawidek tctx->tc_magic = TCP_CTX_MAGIC; 232933728eeSPawel Jakub Dawidek *ctxp = tctx; 233933728eeSPawel Jakub Dawidek 234933728eeSPawel Jakub Dawidek return (0); 235933728eeSPawel Jakub Dawidek } 236933728eeSPawel Jakub Dawidek 237933728eeSPawel Jakub Dawidek static int 238bdbd046bSPawel Jakub Dawidek tcp_setup_wrap(int fd, int side, void **ctxp) 239933728eeSPawel Jakub Dawidek { 240bdbd046bSPawel Jakub Dawidek struct tcp_ctx *tctx; 241933728eeSPawel Jakub Dawidek 242933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(fd >= 0); 243bdbd046bSPawel Jakub Dawidek PJDLOG_ASSERT(side == TCP_SIDE_CLIENT || 244bdbd046bSPawel Jakub Dawidek side == TCP_SIDE_SERVER_WORK); 245933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(ctxp != NULL); 246933728eeSPawel Jakub Dawidek 247933728eeSPawel Jakub Dawidek tctx = malloc(sizeof(*tctx)); 248933728eeSPawel Jakub Dawidek if (tctx == NULL) 249933728eeSPawel Jakub Dawidek return (errno); 250933728eeSPawel Jakub Dawidek 251933728eeSPawel Jakub Dawidek tctx->tc_fd = fd; 252*dc18c8aeSPawel Jakub Dawidek tctx->tc_sa.ss_family = AF_UNSPEC; 253933728eeSPawel Jakub Dawidek tctx->tc_side = side; 254bdbd046bSPawel Jakub Dawidek tctx->tc_magic = TCP_CTX_MAGIC; 255933728eeSPawel Jakub Dawidek *ctxp = tctx; 256933728eeSPawel Jakub Dawidek 257933728eeSPawel Jakub Dawidek return (0); 258933728eeSPawel Jakub Dawidek } 259933728eeSPawel Jakub Dawidek 260933728eeSPawel Jakub Dawidek static int 261bdbd046bSPawel Jakub Dawidek tcp_client(const char *srcaddr, const char *dstaddr, void **ctxp) 262933728eeSPawel Jakub Dawidek { 263bdbd046bSPawel Jakub Dawidek struct tcp_ctx *tctx; 264*dc18c8aeSPawel Jakub Dawidek struct sockaddr_storage sa; 265933728eeSPawel Jakub Dawidek int ret; 266933728eeSPawel Jakub Dawidek 267bdbd046bSPawel Jakub Dawidek ret = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, ctxp); 268933728eeSPawel Jakub Dawidek if (ret != 0) 269933728eeSPawel Jakub Dawidek return (ret); 270933728eeSPawel Jakub Dawidek tctx = *ctxp; 271933728eeSPawel Jakub Dawidek if (srcaddr == NULL) 272933728eeSPawel Jakub Dawidek return (0); 273*dc18c8aeSPawel Jakub Dawidek ret = tcp_addr(srcaddr, 0, &sa); 274933728eeSPawel Jakub Dawidek if (ret != 0) { 275bdbd046bSPawel Jakub Dawidek tcp_close(tctx); 276933728eeSPawel Jakub Dawidek return (ret); 277933728eeSPawel Jakub Dawidek } 278*dc18c8aeSPawel Jakub Dawidek if (bind(tctx->tc_fd, (struct sockaddr *)&sa, sa.ss_len) < 0) { 279933728eeSPawel Jakub Dawidek ret = errno; 280bdbd046bSPawel Jakub Dawidek tcp_close(tctx); 281933728eeSPawel Jakub Dawidek return (ret); 282933728eeSPawel Jakub Dawidek } 283933728eeSPawel Jakub Dawidek return (0); 284933728eeSPawel Jakub Dawidek } 285933728eeSPawel Jakub Dawidek 286933728eeSPawel Jakub Dawidek static int 287bdbd046bSPawel Jakub Dawidek tcp_connect(void *ctx, int timeout) 288933728eeSPawel Jakub Dawidek { 289bdbd046bSPawel Jakub Dawidek struct tcp_ctx *tctx = ctx; 290933728eeSPawel Jakub Dawidek int error, flags; 291933728eeSPawel Jakub Dawidek 292933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx != NULL); 293bdbd046bSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 294bdbd046bSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT); 295933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_fd >= 0); 296*dc18c8aeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 297933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(timeout >= -1); 298933728eeSPawel Jakub Dawidek 299933728eeSPawel Jakub Dawidek flags = fcntl(tctx->tc_fd, F_GETFL); 300933728eeSPawel Jakub Dawidek if (flags == -1) { 301933728eeSPawel Jakub Dawidek KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno, 302933728eeSPawel Jakub Dawidek "fcntl(F_GETFL) failed")); 303933728eeSPawel Jakub Dawidek return (errno); 304933728eeSPawel Jakub Dawidek } 305933728eeSPawel Jakub Dawidek /* 306933728eeSPawel Jakub Dawidek * We make socket non-blocking so we can handle connection timeout 307933728eeSPawel Jakub Dawidek * manually. 308933728eeSPawel Jakub Dawidek */ 309933728eeSPawel Jakub Dawidek flags |= O_NONBLOCK; 310933728eeSPawel Jakub Dawidek if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 311933728eeSPawel Jakub Dawidek KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno, 312933728eeSPawel Jakub Dawidek "fcntl(F_SETFL, O_NONBLOCK) failed")); 313933728eeSPawel Jakub Dawidek return (errno); 314933728eeSPawel Jakub Dawidek } 315933728eeSPawel Jakub Dawidek 316*dc18c8aeSPawel Jakub Dawidek if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa, 317*dc18c8aeSPawel Jakub Dawidek tctx->tc_sa.ss_len) == 0) { 318933728eeSPawel Jakub Dawidek if (timeout == -1) 319933728eeSPawel Jakub Dawidek return (0); 320933728eeSPawel Jakub Dawidek error = 0; 321933728eeSPawel Jakub Dawidek goto done; 322933728eeSPawel Jakub Dawidek } 323933728eeSPawel Jakub Dawidek if (errno != EINPROGRESS) { 324933728eeSPawel Jakub Dawidek error = errno; 325933728eeSPawel Jakub Dawidek pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed"); 326933728eeSPawel Jakub Dawidek goto done; 327933728eeSPawel Jakub Dawidek } 328933728eeSPawel Jakub Dawidek if (timeout == -1) 329933728eeSPawel Jakub Dawidek return (0); 330bdbd046bSPawel Jakub Dawidek return (tcp_connect_wait(ctx, timeout)); 331933728eeSPawel Jakub Dawidek done: 332933728eeSPawel Jakub Dawidek flags &= ~O_NONBLOCK; 333933728eeSPawel Jakub Dawidek if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 334933728eeSPawel Jakub Dawidek if (error == 0) 335933728eeSPawel Jakub Dawidek error = errno; 336933728eeSPawel Jakub Dawidek pjdlog_common(LOG_DEBUG, 1, errno, 337933728eeSPawel Jakub Dawidek "fcntl(F_SETFL, ~O_NONBLOCK) failed"); 338933728eeSPawel Jakub Dawidek } 339933728eeSPawel Jakub Dawidek return (error); 340933728eeSPawel Jakub Dawidek } 341933728eeSPawel Jakub Dawidek 342933728eeSPawel Jakub Dawidek static int 343bdbd046bSPawel Jakub Dawidek tcp_connect_wait(void *ctx, int timeout) 344933728eeSPawel Jakub Dawidek { 345bdbd046bSPawel Jakub Dawidek struct tcp_ctx *tctx = ctx; 346933728eeSPawel Jakub Dawidek struct timeval tv; 347933728eeSPawel Jakub Dawidek fd_set fdset; 348933728eeSPawel Jakub Dawidek socklen_t esize; 349933728eeSPawel Jakub Dawidek int error, flags, ret; 350933728eeSPawel Jakub Dawidek 351933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx != NULL); 352bdbd046bSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 353bdbd046bSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT); 354933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_fd >= 0); 355933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(timeout >= 0); 356933728eeSPawel Jakub Dawidek 357933728eeSPawel Jakub Dawidek tv.tv_sec = timeout; 358933728eeSPawel Jakub Dawidek tv.tv_usec = 0; 359933728eeSPawel Jakub Dawidek again: 360933728eeSPawel Jakub Dawidek FD_ZERO(&fdset); 361933728eeSPawel Jakub Dawidek FD_SET(tctx->tc_fd, &fdset); 362933728eeSPawel Jakub Dawidek ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv); 363933728eeSPawel Jakub Dawidek if (ret == 0) { 364933728eeSPawel Jakub Dawidek error = ETIMEDOUT; 365933728eeSPawel Jakub Dawidek goto done; 366933728eeSPawel Jakub Dawidek } else if (ret == -1) { 367933728eeSPawel Jakub Dawidek if (errno == EINTR) 368933728eeSPawel Jakub Dawidek goto again; 369933728eeSPawel Jakub Dawidek error = errno; 370933728eeSPawel Jakub Dawidek pjdlog_common(LOG_DEBUG, 1, errno, "select() failed"); 371933728eeSPawel Jakub Dawidek goto done; 372933728eeSPawel Jakub Dawidek } 373933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(ret > 0); 374933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset)); 375933728eeSPawel Jakub Dawidek esize = sizeof(error); 376933728eeSPawel Jakub Dawidek if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error, 377933728eeSPawel Jakub Dawidek &esize) == -1) { 378933728eeSPawel Jakub Dawidek error = errno; 379933728eeSPawel Jakub Dawidek pjdlog_common(LOG_DEBUG, 1, errno, 380933728eeSPawel Jakub Dawidek "getsockopt(SO_ERROR) failed"); 381933728eeSPawel Jakub Dawidek goto done; 382933728eeSPawel Jakub Dawidek } 383933728eeSPawel Jakub Dawidek if (error != 0) { 384933728eeSPawel Jakub Dawidek pjdlog_common(LOG_DEBUG, 1, error, 385933728eeSPawel Jakub Dawidek "getsockopt(SO_ERROR) returned error"); 386933728eeSPawel Jakub Dawidek goto done; 387933728eeSPawel Jakub Dawidek } 388933728eeSPawel Jakub Dawidek error = 0; 389933728eeSPawel Jakub Dawidek done: 390933728eeSPawel Jakub Dawidek flags = fcntl(tctx->tc_fd, F_GETFL); 391933728eeSPawel Jakub Dawidek if (flags == -1) { 392933728eeSPawel Jakub Dawidek if (error == 0) 393933728eeSPawel Jakub Dawidek error = errno; 394933728eeSPawel Jakub Dawidek pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed"); 395933728eeSPawel Jakub Dawidek return (error); 396933728eeSPawel Jakub Dawidek } 397933728eeSPawel Jakub Dawidek flags &= ~O_NONBLOCK; 398933728eeSPawel Jakub Dawidek if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 399933728eeSPawel Jakub Dawidek if (error == 0) 400933728eeSPawel Jakub Dawidek error = errno; 401933728eeSPawel Jakub Dawidek pjdlog_common(LOG_DEBUG, 1, errno, 402933728eeSPawel Jakub Dawidek "fcntl(F_SETFL, ~O_NONBLOCK) failed"); 403933728eeSPawel Jakub Dawidek } 404933728eeSPawel Jakub Dawidek return (error); 405933728eeSPawel Jakub Dawidek } 406933728eeSPawel Jakub Dawidek 407933728eeSPawel Jakub Dawidek static int 408bdbd046bSPawel Jakub Dawidek tcp_server(const char *addr, void **ctxp) 409933728eeSPawel Jakub Dawidek { 410bdbd046bSPawel Jakub Dawidek struct tcp_ctx *tctx; 411933728eeSPawel Jakub Dawidek int ret, val; 412933728eeSPawel Jakub Dawidek 413bdbd046bSPawel Jakub Dawidek ret = tcp_setup_new(addr, TCP_SIDE_SERVER_LISTEN, ctxp); 414933728eeSPawel Jakub Dawidek if (ret != 0) 415933728eeSPawel Jakub Dawidek return (ret); 416933728eeSPawel Jakub Dawidek 417933728eeSPawel Jakub Dawidek tctx = *ctxp; 418933728eeSPawel Jakub Dawidek 419933728eeSPawel Jakub Dawidek val = 1; 420933728eeSPawel Jakub Dawidek /* Ignore failure. */ 421933728eeSPawel Jakub Dawidek (void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val, 422933728eeSPawel Jakub Dawidek sizeof(val)); 423933728eeSPawel Jakub Dawidek 424*dc18c8aeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 425933728eeSPawel Jakub Dawidek 426*dc18c8aeSPawel Jakub Dawidek if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa, 427*dc18c8aeSPawel Jakub Dawidek tctx->tc_sa.ss_len) < 0) { 428933728eeSPawel Jakub Dawidek ret = errno; 429bdbd046bSPawel Jakub Dawidek tcp_close(tctx); 430933728eeSPawel Jakub Dawidek return (ret); 431933728eeSPawel Jakub Dawidek } 432933728eeSPawel Jakub Dawidek if (listen(tctx->tc_fd, 8) < 0) { 433933728eeSPawel Jakub Dawidek ret = errno; 434bdbd046bSPawel Jakub Dawidek tcp_close(tctx); 435933728eeSPawel Jakub Dawidek return (ret); 436933728eeSPawel Jakub Dawidek } 437933728eeSPawel Jakub Dawidek 438933728eeSPawel Jakub Dawidek return (0); 439933728eeSPawel Jakub Dawidek } 440933728eeSPawel Jakub Dawidek 441933728eeSPawel Jakub Dawidek static int 442bdbd046bSPawel Jakub Dawidek tcp_accept(void *ctx, void **newctxp) 443933728eeSPawel Jakub Dawidek { 444bdbd046bSPawel Jakub Dawidek struct tcp_ctx *tctx = ctx; 445bdbd046bSPawel Jakub Dawidek struct tcp_ctx *newtctx; 446933728eeSPawel Jakub Dawidek socklen_t fromlen; 447933728eeSPawel Jakub Dawidek int ret; 448933728eeSPawel Jakub Dawidek 449933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx != NULL); 450bdbd046bSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 451bdbd046bSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN); 452933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_fd >= 0); 453*dc18c8aeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); 454933728eeSPawel Jakub Dawidek 455933728eeSPawel Jakub Dawidek newtctx = malloc(sizeof(*newtctx)); 456933728eeSPawel Jakub Dawidek if (newtctx == NULL) 457933728eeSPawel Jakub Dawidek return (errno); 458933728eeSPawel Jakub Dawidek 459*dc18c8aeSPawel Jakub Dawidek fromlen = tctx->tc_sa.ss_len; 460*dc18c8aeSPawel Jakub Dawidek newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa, 461933728eeSPawel Jakub Dawidek &fromlen); 462933728eeSPawel Jakub Dawidek if (newtctx->tc_fd < 0) { 463933728eeSPawel Jakub Dawidek ret = errno; 464933728eeSPawel Jakub Dawidek free(newtctx); 465933728eeSPawel Jakub Dawidek return (ret); 466933728eeSPawel Jakub Dawidek } 467933728eeSPawel Jakub Dawidek 468bdbd046bSPawel Jakub Dawidek newtctx->tc_side = TCP_SIDE_SERVER_WORK; 469bdbd046bSPawel Jakub Dawidek newtctx->tc_magic = TCP_CTX_MAGIC; 470933728eeSPawel Jakub Dawidek *newctxp = newtctx; 471933728eeSPawel Jakub Dawidek 472933728eeSPawel Jakub Dawidek return (0); 473933728eeSPawel Jakub Dawidek } 474933728eeSPawel Jakub Dawidek 475933728eeSPawel Jakub Dawidek static int 476bdbd046bSPawel Jakub Dawidek tcp_wrap(int fd, bool client, void **ctxp) 477933728eeSPawel Jakub Dawidek { 478933728eeSPawel Jakub Dawidek 479bdbd046bSPawel Jakub Dawidek return (tcp_setup_wrap(fd, 480bdbd046bSPawel Jakub Dawidek client ? TCP_SIDE_CLIENT : TCP_SIDE_SERVER_WORK, ctxp)); 481933728eeSPawel Jakub Dawidek } 482933728eeSPawel Jakub Dawidek 483933728eeSPawel Jakub Dawidek static int 484bdbd046bSPawel Jakub Dawidek tcp_send(void *ctx, const unsigned char *data, size_t size, int fd) 485933728eeSPawel Jakub Dawidek { 486bdbd046bSPawel Jakub Dawidek struct tcp_ctx *tctx = ctx; 487933728eeSPawel Jakub Dawidek 488933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx != NULL); 489bdbd046bSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 490933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_fd >= 0); 491933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(fd == -1); 492933728eeSPawel Jakub Dawidek 493933728eeSPawel Jakub Dawidek return (proto_common_send(tctx->tc_fd, data, size, -1)); 494933728eeSPawel Jakub Dawidek } 495933728eeSPawel Jakub Dawidek 496933728eeSPawel Jakub Dawidek static int 497bdbd046bSPawel Jakub Dawidek tcp_recv(void *ctx, unsigned char *data, size_t size, int *fdp) 498933728eeSPawel Jakub Dawidek { 499bdbd046bSPawel Jakub Dawidek struct tcp_ctx *tctx = ctx; 500933728eeSPawel Jakub Dawidek 501933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx != NULL); 502bdbd046bSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 503933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_fd >= 0); 504933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(fdp == NULL); 505933728eeSPawel Jakub Dawidek 506933728eeSPawel Jakub Dawidek return (proto_common_recv(tctx->tc_fd, data, size, NULL)); 507933728eeSPawel Jakub Dawidek } 508933728eeSPawel Jakub Dawidek 509933728eeSPawel Jakub Dawidek static int 510bdbd046bSPawel Jakub Dawidek tcp_descriptor(const void *ctx) 511933728eeSPawel Jakub Dawidek { 512bdbd046bSPawel Jakub Dawidek const struct tcp_ctx *tctx = ctx; 513933728eeSPawel Jakub Dawidek 514933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx != NULL); 515bdbd046bSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 516933728eeSPawel Jakub Dawidek 517933728eeSPawel Jakub Dawidek return (tctx->tc_fd); 518933728eeSPawel Jakub Dawidek } 519933728eeSPawel Jakub Dawidek 520933728eeSPawel Jakub Dawidek static bool 521bdbd046bSPawel Jakub Dawidek tcp_address_match(const void *ctx, const char *addr) 522933728eeSPawel Jakub Dawidek { 523bdbd046bSPawel Jakub Dawidek const struct tcp_ctx *tctx = ctx; 524*dc18c8aeSPawel Jakub Dawidek struct sockaddr_storage sa1, sa2; 525*dc18c8aeSPawel Jakub Dawidek socklen_t salen; 526933728eeSPawel Jakub Dawidek 527933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx != NULL); 528bdbd046bSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 529933728eeSPawel Jakub Dawidek 530*dc18c8aeSPawel Jakub Dawidek if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sa1) != 0) 531933728eeSPawel Jakub Dawidek return (false); 532933728eeSPawel Jakub Dawidek 533*dc18c8aeSPawel Jakub Dawidek salen = sizeof(sa2); 534*dc18c8aeSPawel Jakub Dawidek if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) < 0) 535933728eeSPawel Jakub Dawidek return (false); 536933728eeSPawel Jakub Dawidek 537*dc18c8aeSPawel Jakub Dawidek if (sa1.ss_family != sa2.ss_family || sa1.ss_len != sa2.ss_len) 538*dc18c8aeSPawel Jakub Dawidek return (false); 539*dc18c8aeSPawel Jakub Dawidek 540*dc18c8aeSPawel Jakub Dawidek switch (sa1.ss_family) { 541*dc18c8aeSPawel Jakub Dawidek case AF_INET: 542*dc18c8aeSPawel Jakub Dawidek { 543*dc18c8aeSPawel Jakub Dawidek struct sockaddr_in *sin1, *sin2; 544*dc18c8aeSPawel Jakub Dawidek 545*dc18c8aeSPawel Jakub Dawidek sin1 = (struct sockaddr_in *)&sa1; 546*dc18c8aeSPawel Jakub Dawidek sin2 = (struct sockaddr_in *)&sa2; 547*dc18c8aeSPawel Jakub Dawidek 548*dc18c8aeSPawel Jakub Dawidek return (memcmp(&sin1->sin_addr, &sin2->sin_addr, 549*dc18c8aeSPawel Jakub Dawidek sizeof(sin1->sin_addr)) == 0); 550*dc18c8aeSPawel Jakub Dawidek } 551*dc18c8aeSPawel Jakub Dawidek case AF_INET6: 552*dc18c8aeSPawel Jakub Dawidek { 553*dc18c8aeSPawel Jakub Dawidek struct sockaddr_in6 *sin1, *sin2; 554*dc18c8aeSPawel Jakub Dawidek 555*dc18c8aeSPawel Jakub Dawidek sin1 = (struct sockaddr_in6 *)&sa1; 556*dc18c8aeSPawel Jakub Dawidek sin2 = (struct sockaddr_in6 *)&sa2; 557*dc18c8aeSPawel Jakub Dawidek 558*dc18c8aeSPawel Jakub Dawidek return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr, 559*dc18c8aeSPawel Jakub Dawidek sizeof(sin1->sin6_addr)) == 0); 560*dc18c8aeSPawel Jakub Dawidek } 561*dc18c8aeSPawel Jakub Dawidek default: 562*dc18c8aeSPawel Jakub Dawidek return (false); 563*dc18c8aeSPawel Jakub Dawidek } 564933728eeSPawel Jakub Dawidek } 565933728eeSPawel Jakub Dawidek 566933728eeSPawel Jakub Dawidek static void 567bdbd046bSPawel Jakub Dawidek tcp_local_address(const void *ctx, char *addr, size_t size) 568933728eeSPawel Jakub Dawidek { 569bdbd046bSPawel Jakub Dawidek const struct tcp_ctx *tctx = ctx; 570*dc18c8aeSPawel Jakub Dawidek struct sockaddr_storage sa; 571*dc18c8aeSPawel Jakub Dawidek socklen_t salen; 572933728eeSPawel Jakub Dawidek 573933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx != NULL); 574bdbd046bSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 575933728eeSPawel Jakub Dawidek 576*dc18c8aeSPawel Jakub Dawidek salen = sizeof(sa); 577*dc18c8aeSPawel Jakub Dawidek if (getsockname(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) { 578933728eeSPawel Jakub Dawidek PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); 579933728eeSPawel Jakub Dawidek return; 580933728eeSPawel Jakub Dawidek } 581*dc18c8aeSPawel Jakub Dawidek PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size); 582933728eeSPawel Jakub Dawidek } 583933728eeSPawel Jakub Dawidek 584933728eeSPawel Jakub Dawidek static void 585bdbd046bSPawel Jakub Dawidek tcp_remote_address(const void *ctx, char *addr, size_t size) 586933728eeSPawel Jakub Dawidek { 587bdbd046bSPawel Jakub Dawidek const struct tcp_ctx *tctx = ctx; 588*dc18c8aeSPawel Jakub Dawidek struct sockaddr_storage sa; 589*dc18c8aeSPawel Jakub Dawidek socklen_t salen; 590933728eeSPawel Jakub Dawidek 591933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx != NULL); 592bdbd046bSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 593933728eeSPawel Jakub Dawidek 594*dc18c8aeSPawel Jakub Dawidek salen = sizeof(sa); 595*dc18c8aeSPawel Jakub Dawidek if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) { 596933728eeSPawel Jakub Dawidek PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); 597933728eeSPawel Jakub Dawidek return; 598933728eeSPawel Jakub Dawidek } 599*dc18c8aeSPawel Jakub Dawidek PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size); 600933728eeSPawel Jakub Dawidek } 601933728eeSPawel Jakub Dawidek 602933728eeSPawel Jakub Dawidek static void 603bdbd046bSPawel Jakub Dawidek tcp_close(void *ctx) 604933728eeSPawel Jakub Dawidek { 605bdbd046bSPawel Jakub Dawidek struct tcp_ctx *tctx = ctx; 606933728eeSPawel Jakub Dawidek 607933728eeSPawel Jakub Dawidek PJDLOG_ASSERT(tctx != NULL); 608bdbd046bSPawel Jakub Dawidek PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); 609933728eeSPawel Jakub Dawidek 610933728eeSPawel Jakub Dawidek if (tctx->tc_fd >= 0) 611933728eeSPawel Jakub Dawidek close(tctx->tc_fd); 612933728eeSPawel Jakub Dawidek tctx->tc_magic = 0; 613933728eeSPawel Jakub Dawidek free(tctx); 614933728eeSPawel Jakub Dawidek } 615933728eeSPawel Jakub Dawidek 616bdbd046bSPawel Jakub Dawidek static struct proto tcp_proto = { 617bdbd046bSPawel Jakub Dawidek .prt_name = "tcp", 618bdbd046bSPawel Jakub Dawidek .prt_client = tcp_client, 619bdbd046bSPawel Jakub Dawidek .prt_connect = tcp_connect, 620bdbd046bSPawel Jakub Dawidek .prt_connect_wait = tcp_connect_wait, 621bdbd046bSPawel Jakub Dawidek .prt_server = tcp_server, 622bdbd046bSPawel Jakub Dawidek .prt_accept = tcp_accept, 623bdbd046bSPawel Jakub Dawidek .prt_wrap = tcp_wrap, 624bdbd046bSPawel Jakub Dawidek .prt_send = tcp_send, 625bdbd046bSPawel Jakub Dawidek .prt_recv = tcp_recv, 626bdbd046bSPawel Jakub Dawidek .prt_descriptor = tcp_descriptor, 627bdbd046bSPawel Jakub Dawidek .prt_address_match = tcp_address_match, 628bdbd046bSPawel Jakub Dawidek .prt_local_address = tcp_local_address, 629bdbd046bSPawel Jakub Dawidek .prt_remote_address = tcp_remote_address, 630bdbd046bSPawel Jakub Dawidek .prt_close = tcp_close 631933728eeSPawel Jakub Dawidek }; 632933728eeSPawel Jakub Dawidek 633933728eeSPawel Jakub Dawidek static __constructor void 634bdbd046bSPawel Jakub Dawidek tcp_ctor(void) 635933728eeSPawel Jakub Dawidek { 636933728eeSPawel Jakub Dawidek 637bdbd046bSPawel Jakub Dawidek proto_register(&tcp_proto, true); 638933728eeSPawel Jakub Dawidek } 639