proto_tcp.c revision 219873
11590Srgrimes/*- 21590Srgrimes * Copyright (c) 2009-2010 The FreeBSD Foundation 31590Srgrimes * All rights reserved. 41590Srgrimes * 51590Srgrimes * This software was developed by Pawel Jakub Dawidek under sponsorship from 61590Srgrimes * the FreeBSD Foundation. 71590Srgrimes * 81590Srgrimes * Redistribution and use in source and binary forms, with or without 91590Srgrimes * modification, are permitted provided that the following conditions 101590Srgrimes * are met: 111590Srgrimes * 1. Redistributions of source code must retain the above copyright 121590Srgrimes * notice, this list of conditions and the following disclaimer. 131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141590Srgrimes * notice, this list of conditions and the following disclaimer in the 151590Srgrimes * documentation and/or other materials provided with the distribution. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 301590Srgrimes#include <sys/cdefs.h> 311590Srgrimes__FBSDID("$FreeBSD: head/sbin/hastd/proto_tcp4.c 219873 2011-03-22 16:21:11Z pjd $"); 321590Srgrimes 331590Srgrimes#include <sys/param.h> /* MAXHOSTNAMELEN */ 341590Srgrimes#include <sys/socket.h> 3527919Scharnier 361590Srgrimes#include <arpa/inet.h> 371590Srgrimes 381590Srgrimes#include <netinet/in.h> 391590Srgrimes#include <netinet/tcp.h> 401590Srgrimes 4129922Smarkm#include <errno.h> 4227919Scharnier#include <fcntl.h> 4350477Speter#include <netdb.h> 441590Srgrimes#include <stdbool.h> 451590Srgrimes#include <stdint.h> 461590Srgrimes#include <stdio.h> 471590Srgrimes#include <string.h> 481590Srgrimes#include <unistd.h> 491590Srgrimes 501590Srgrimes#include "pjdlog.h" 511590Srgrimes#include "proto_impl.h" 521590Srgrimes#include "subr.h" 531590Srgrimes 541590Srgrimes#define TCP4_CTX_MAGIC 0x7c441c 551590Srgrimesstruct tcp4_ctx { 561590Srgrimes int tc_magic; 571590Srgrimes struct sockaddr_in tc_sin; 588232Sdg int tc_fd; 591590Srgrimes int tc_side; 6027919Scharnier#define TCP4_SIDE_CLIENT 0 611590Srgrimes#define TCP4_SIDE_SERVER_LISTEN 1 621590Srgrimes#define TCP4_SIDE_SERVER_WORK 2 6340103Smarkm}; 641590Srgrimes 651590Srgrimesstatic int tcp4_connect_wait(void *ctx, int timeout); 661590Srgrimesstatic void tcp4_close(void *ctx); 671590Srgrimes 681590Srgrimesstatic in_addr_t 691590Srgrimesstr2ip(const char *str) 701590Srgrimes{ 711590Srgrimes struct hostent *hp; 721590Srgrimes in_addr_t ip; 7329922Smarkm 741590Srgrimes ip = inet_addr(str); 751590Srgrimes if (ip != INADDR_NONE) { 7614024Smarkm /* It is a valid IP address. */ 7729922Smarkm return (ip); 781590Srgrimes } 7934897Smarkm /* Check if it is a valid host name. */ 801590Srgrimes hp = gethostbyname(str); 811590Srgrimes if (hp == NULL) 821590Srgrimes return (INADDR_NONE); 831590Srgrimes return (((struct in_addr *)(void *)hp->h_addr)->s_addr); 841590Srgrimes} 851590Srgrimes 861590Srgrimes/* 871590Srgrimes * Function converts the given string to unsigned number. 881590Srgrimes */ 891590Srgrimesstatic int 901590Srgrimesnumfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump) 911590Srgrimes{ 921590Srgrimes intmax_t digit, num; 931590Srgrimes 941590Srgrimes if (str[0] == '\0') 951590Srgrimes goto invalid; /* Empty string. */ 961590Srgrimes num = 0; 971590Srgrimes for (; *str != '\0'; str++) { 9857232Sshin if (*str < '0' || *str > '9') 991590Srgrimes goto invalid; /* Non-digit character. */ 1001590Srgrimes digit = *str - '0'; 1011590Srgrimes if (num > num * 10 + digit) 1021590Srgrimes goto invalid; /* Overflow. */ 1031590Srgrimes num = num * 10 + digit; 1041590Srgrimes if (num > maxnum) 1059881Sache goto invalid; /* Too big. */ 10617284Spst } 1071590Srgrimes if (num < minnum) 1081590Srgrimes goto invalid; /* Too small. */ 1091590Srgrimes *nump = num; 1101590Srgrimes return (0); 1111590Srgrimesinvalid: 1121590Srgrimes errno = EINVAL; 1131590Srgrimes return (-1); 1141590Srgrimes} 1151590Srgrimes 1161590Srgrimesstatic int 1171590Srgrimestcp4_addr(const char *addr, int defport, struct sockaddr_in *sinp) 1181590Srgrimes{ 1191590Srgrimes char iporhost[MAXHOSTNAMELEN]; 1201590Srgrimes const char *pp; 12118286Sbde size_t size; 12218286Sbde in_addr_t ip; 1231590Srgrimes 1241590Srgrimes if (addr == NULL) 1251590Srgrimes return (-1); 1261590Srgrimes 1271590Srgrimes if (strncasecmp(addr, "tcp4://", 7) == 0) 1281590Srgrimes addr += 7; 1291590Srgrimes else if (strncasecmp(addr, "tcp://", 6) == 0) 1301590Srgrimes addr += 6; 1311590Srgrimes else { 1321590Srgrimes /* 1331590Srgrimes * Because TCP4 is the default assume IP or host is given without 13418286Sbde * prefix. 1351590Srgrimes */ 1361590Srgrimes } 1371590Srgrimes 1381590Srgrimes sinp->sin_family = AF_INET; 1391590Srgrimes sinp->sin_len = sizeof(*sinp); 1401590Srgrimes /* Extract optional port. */ 1411590Srgrimes pp = strrchr(addr, ':'); 1421590Srgrimes if (pp == NULL) { 1431590Srgrimes /* Port not given, use the default. */ 1441590Srgrimes sinp->sin_port = htons(defport); 1451590Srgrimes } else { 1461590Srgrimes intmax_t port; 1471590Srgrimes 1481590Srgrimes if (numfromstr(pp + 1, 1, 65535, &port) < 0) 1491590Srgrimes return (errno); 1501590Srgrimes sinp->sin_port = htons(port); 1511590Srgrimes } 1521590Srgrimes /* Extract host name or IP address. */ 1538232Sdg if (pp == NULL) { 15447549Sbde size = sizeof(iporhost); 15540103Smarkm if (strlcpy(iporhost, addr, size) >= size) 15640103Smarkm return (ENAMETOOLONG); 15740103Smarkm } else { 15856590Sshin size = (size_t)(pp - addr + 1); 15956590Sshin if (size > sizeof(iporhost)) 1601590Srgrimes return (ENAMETOOLONG); 1618232Sdg (void)strlcpy(iporhost, addr, size); 1621590Srgrimes } 16347549Sbde /* Convert string (IP address or host name) to in_addr_t. */ 1641590Srgrimes ip = str2ip(iporhost); 16529922Smarkm if (ip == INADDR_NONE) 1661590Srgrimes return (EINVAL); 1671590Srgrimes sinp->sin_addr.s_addr = ip; 1681590Srgrimes 1691590Srgrimes return (0); 1701590Srgrimes} 1711590Srgrimes 1721590Srgrimesstatic int 1731590Srgrimestcp4_setup_new(const char *addr, int side, void **ctxp) 1741590Srgrimes{ 1751590Srgrimes struct tcp4_ctx *tctx; 1761590Srgrimes int ret, nodelay; 1771590Srgrimes 1781590Srgrimes PJDLOG_ASSERT(addr != NULL); 1791590Srgrimes PJDLOG_ASSERT(side == TCP4_SIDE_CLIENT || 18057232Sshin side == TCP4_SIDE_SERVER_LISTEN); 1811590Srgrimes PJDLOG_ASSERT(ctxp != NULL); 18257232Sshin 1831590Srgrimes tctx = malloc(sizeof(*tctx)); 18424360Simp if (tctx == NULL) 1851590Srgrimes return (errno); 18657232Sshin 18757232Sshin /* Parse given address. */ 18857232Sshin if ((ret = tcp4_addr(addr, PROTO_TCP4_DEFAULT_PORT, 18957232Sshin &tctx->tc_sin)) != 0) { 19057232Sshin free(tctx); 19157232Sshin return (ret); 19257232Sshin } 19357232Sshin 1941590Srgrimes PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); 1951590Srgrimes 1961590Srgrimes tctx->tc_fd = socket(AF_INET, SOCK_STREAM, 0); 1978232Sdg if (tctx->tc_fd == -1) { 1988232Sdg ret = errno; 1998232Sdg free(tctx); 2001590Srgrimes return (ret); 2011590Srgrimes } 2021590Srgrimes 2031590Srgrimes PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); 2041590Srgrimes 2051590Srgrimes /* Socket settings. */ 2061590Srgrimes nodelay = 1; 2071590Srgrimes if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, 2081590Srgrimes sizeof(nodelay)) == -1) { 2091590Srgrimes pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY"); 2101590Srgrimes } 2111590Srgrimes 2121590Srgrimes tctx->tc_side = side; 2131590Srgrimes tctx->tc_magic = TCP4_CTX_MAGIC; 2141590Srgrimes *ctxp = tctx; 2151590Srgrimes 2161590Srgrimes return (0); 2171590Srgrimes} 21847488Speter 21947549Sbdestatic int 22047549Sbdetcp4_setup_wrap(int fd, int side, void **ctxp) 22147488Speter{ 22247488Speter struct tcp4_ctx *tctx; 2231590Srgrimes 2241590Srgrimes PJDLOG_ASSERT(fd >= 0); 2251590Srgrimes PJDLOG_ASSERT(side == TCP4_SIDE_CLIENT || 2261590Srgrimes side == TCP4_SIDE_SERVER_WORK); 2271590Srgrimes PJDLOG_ASSERT(ctxp != NULL); 2281590Srgrimes 2291590Srgrimes tctx = malloc(sizeof(*tctx)); 2301590Srgrimes if (tctx == NULL) 2311590Srgrimes return (errno); 2321590Srgrimes 2331590Srgrimes tctx->tc_fd = fd; 2341590Srgrimes tctx->tc_sin.sin_family = AF_UNSPEC; 2351590Srgrimes tctx->tc_side = side; 2361590Srgrimes tctx->tc_magic = TCP4_CTX_MAGIC; 2371590Srgrimes *ctxp = tctx; 2381590Srgrimes 2391590Srgrimes return (0); 2401590Srgrimes} 2411590Srgrimes 2421590Srgrimesstatic int 2431590Srgrimestcp4_client(const char *srcaddr, const char *dstaddr, void **ctxp) 2441590Srgrimes{ 2451590Srgrimes struct tcp4_ctx *tctx; 24634897Smarkm struct sockaddr_in sin; 2471590Srgrimes int ret; 2481590Srgrimes 24934897Smarkm ret = tcp4_setup_new(dstaddr, TCP4_SIDE_CLIENT, ctxp); 2501590Srgrimes if (ret != 0) 2511590Srgrimes return (ret); 25227919Scharnier tctx = *ctxp; 25327919Scharnier if (srcaddr == NULL) 2541590Srgrimes return (0); 2551590Srgrimes ret = tcp4_addr(srcaddr, 0, &sin); 25647488Speter if (ret != 0) { 25747488Speter tcp4_close(tctx); 2581590Srgrimes return (ret); 2591590Srgrimes } 2601590Srgrimes if (bind(tctx->tc_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 26140103Smarkm ret = errno; 26240103Smarkm tcp4_close(tctx); 26340103Smarkm return (ret); 2641590Srgrimes } 2651590Srgrimes return (0); 2661590Srgrimes} 2671590Srgrimes 26829922Smarkmstatic int 2691590Srgrimestcp4_connect(void *ctx, int timeout) 2701590Srgrimes{ 2711590Srgrimes struct tcp4_ctx *tctx = ctx; 2721590Srgrimes int error, flags; 2731590Srgrimes 2741590Srgrimes PJDLOG_ASSERT(tctx != NULL); 27527919Scharnier PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 27627919Scharnier PJDLOG_ASSERT(tctx->tc_side == TCP4_SIDE_CLIENT); 2771590Srgrimes PJDLOG_ASSERT(tctx->tc_fd >= 0); 27817284Spst PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); 27917284Spst PJDLOG_ASSERT(timeout >= -1); 28017284Spst 28117284Spst flags = fcntl(tctx->tc_fd, F_GETFL); 28217284Spst if (flags == -1) { 2831590Srgrimes KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno, 2841590Srgrimes "fcntl(F_GETFL) failed")); 2851590Srgrimes return (errno); 2861590Srgrimes } 2871590Srgrimes /* 2881590Srgrimes * We make socket non-blocking so we can handle connection timeout 2891590Srgrimes * manually. 2901590Srgrimes */ 2911590Srgrimes flags |= O_NONBLOCK; 2921590Srgrimes if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 2931590Srgrimes KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno, 2941590Srgrimes "fcntl(F_SETFL, O_NONBLOCK) failed")); 2951590Srgrimes return (errno); 2961590Srgrimes } 2971590Srgrimes 2981590Srgrimes if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, 2991590Srgrimes sizeof(tctx->tc_sin)) == 0) { 3001590Srgrimes if (timeout == -1) 3011590Srgrimes return (0); 3021590Srgrimes error = 0; 3031590Srgrimes goto done; 30434897Smarkm } 3051590Srgrimes if (errno != EINPROGRESS) { 3061590Srgrimes error = errno; 3071590Srgrimes pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed"); 3081590Srgrimes goto done; 3091590Srgrimes } 3101590Srgrimes if (timeout == -1) 3113113Sdfr return (0); 3121590Srgrimes return (tcp4_connect_wait(ctx, timeout)); 3131590Srgrimesdone: 31429922Smarkm flags &= ~O_NONBLOCK; 3153113Sdfr if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 3161590Srgrimes if (error == 0) 3171590Srgrimes error = errno; 3181590Srgrimes pjdlog_common(LOG_DEBUG, 1, errno, 3191590Srgrimes "fcntl(F_SETFL, ~O_NONBLOCK) failed"); 32034897Smarkm } 32134897Smarkm return (error); 32234897Smarkm} 3231590Srgrimes 32427919Scharnierstatic int 32527919Scharniertcp4_connect_wait(void *ctx, int timeout) 3261590Srgrimes{ 32729922Smarkm struct tcp4_ctx *tctx = ctx; 3281590Srgrimes struct timeval tv; 32929922Smarkm fd_set fdset; 33034897Smarkm socklen_t esize; 33134897Smarkm int error, flags, ret; 33234897Smarkm 33334897Smarkm PJDLOG_ASSERT(tctx != NULL); 33434897Smarkm PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 33534897Smarkm PJDLOG_ASSERT(tctx->tc_side == TCP4_SIDE_CLIENT); 33634897Smarkm PJDLOG_ASSERT(tctx->tc_fd >= 0); 33734897Smarkm PJDLOG_ASSERT(timeout >= 0); 33834897Smarkm 3391590Srgrimes tv.tv_sec = timeout; 3401590Srgrimes tv.tv_usec = 0; 3411590Srgrimesagain: 34227919Scharnier FD_ZERO(&fdset); 34327919Scharnier FD_SET(tctx->tc_fd, &fdset); 3441590Srgrimes ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv); 34556590Sshin if (ret == 0) { 34657232Sshin error = ETIMEDOUT; 3471590Srgrimes goto done; 3481590Srgrimes } else if (ret == -1) { 34957232Sshin if (errno == EINTR) 3501590Srgrimes goto again; 3511590Srgrimes error = errno; 3521590Srgrimes pjdlog_common(LOG_DEBUG, 1, errno, "select() failed"); 3531590Srgrimes goto done; 3541590Srgrimes } 3551590Srgrimes PJDLOG_ASSERT(ret > 0); 3561590Srgrimes PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset)); 35727919Scharnier esize = sizeof(error); 3588232Sdg if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error, 3598232Sdg &esize) == -1) { 36027919Scharnier error = errno; 3618232Sdg pjdlog_common(LOG_DEBUG, 1, errno, 36256590Sshin "getsockopt(SO_ERROR) failed"); 3631590Srgrimes goto done; 36456590Sshin } 36556590Sshin if (error != 0) { 36656590Sshin pjdlog_common(LOG_DEBUG, 1, error, 36756590Sshin "getsockopt(SO_ERROR) returned error"); 36856590Sshin goto done; 36956590Sshin } 37056590Sshin error = 0; 37156590Sshindone: 3721590Srgrimes flags = fcntl(tctx->tc_fd, F_GETFL); 3731590Srgrimes if (flags == -1) { 3741590Srgrimes if (error == 0) 3751590Srgrimes error = errno; 3761590Srgrimes pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed"); 3771590Srgrimes return (error); 3781590Srgrimes } 3791590Srgrimes flags &= ~O_NONBLOCK; 3801590Srgrimes if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) { 3811590Srgrimes if (error == 0) 3821590Srgrimes error = errno; 3831590Srgrimes pjdlog_common(LOG_DEBUG, 1, errno, 3841590Srgrimes "fcntl(F_SETFL, ~O_NONBLOCK) failed"); 3851590Srgrimes } 3861590Srgrimes return (error); 3871590Srgrimes} 3881590Srgrimes 3891590Srgrimesstatic int 3901590Srgrimestcp4_server(const char *addr, void **ctxp) 3911590Srgrimes{ 3921590Srgrimes struct tcp4_ctx *tctx; 3931590Srgrimes int ret, val; 3941590Srgrimes 3951590Srgrimes ret = tcp4_setup_new(addr, TCP4_SIDE_SERVER_LISTEN, ctxp); 3961590Srgrimes if (ret != 0) 3971590Srgrimes return (ret); 3981590Srgrimes 3991590Srgrimes tctx = *ctxp; 4001590Srgrimes 4011590Srgrimes val = 1; 4021590Srgrimes /* Ignore failure. */ 4031590Srgrimes (void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val, 4041590Srgrimes sizeof(val)); 4051590Srgrimes 4061590Srgrimes PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); 40727919Scharnier 4081590Srgrimes if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, 4091590Srgrimes sizeof(tctx->tc_sin)) < 0) { 4101590Srgrimes ret = errno; 4111590Srgrimes tcp4_close(tctx); 4121590Srgrimes return (ret); 4131590Srgrimes } 4141590Srgrimes if (listen(tctx->tc_fd, 8) < 0) { 4151590Srgrimes ret = errno; 4161590Srgrimes tcp4_close(tctx); 4171590Srgrimes return (ret); 4181590Srgrimes } 4191590Srgrimes 4201590Srgrimes return (0); 4211590Srgrimes} 4221590Srgrimes 4231590Srgrimesstatic int 4241590Srgrimestcp4_accept(void *ctx, void **newctxp) 4251590Srgrimes{ 4261590Srgrimes struct tcp4_ctx *tctx = ctx; 4271590Srgrimes struct tcp4_ctx *newtctx; 4281590Srgrimes socklen_t fromlen; 4291590Srgrimes int ret; 4301590Srgrimes 4311590Srgrimes PJDLOG_ASSERT(tctx != NULL); 4321590Srgrimes PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 4331590Srgrimes PJDLOG_ASSERT(tctx->tc_side == TCP4_SIDE_SERVER_LISTEN); 4341590Srgrimes PJDLOG_ASSERT(tctx->tc_fd >= 0); 4351590Srgrimes PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); 4361590Srgrimes 4371590Srgrimes newtctx = malloc(sizeof(*newtctx)); 4381590Srgrimes if (newtctx == NULL) 4391590Srgrimes return (errno); 4401590Srgrimes 4411590Srgrimes fromlen = sizeof(tctx->tc_sin); 4421590Srgrimes newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, 4431590Srgrimes &fromlen); 4441590Srgrimes if (newtctx->tc_fd < 0) { 4451590Srgrimes ret = errno; 4461590Srgrimes free(newtctx); 44718286Sbde return (ret); 4481590Srgrimes } 4491590Srgrimes 4501590Srgrimes newtctx->tc_side = TCP4_SIDE_SERVER_WORK; 4511590Srgrimes newtctx->tc_magic = TCP4_CTX_MAGIC; 4521590Srgrimes *newctxp = newtctx; 4531590Srgrimes 4541590Srgrimes return (0); 4551590Srgrimes} 4561590Srgrimes 4571590Srgrimesstatic int 4581590Srgrimestcp4_wrap(int fd, bool client, void **ctxp) 4591590Srgrimes{ 4601590Srgrimes 4611590Srgrimes return (tcp4_setup_wrap(fd, 4621590Srgrimes client ? TCP4_SIDE_CLIENT : TCP4_SIDE_SERVER_WORK, ctxp)); 4631590Srgrimes} 4641590Srgrimes 4651590Srgrimesstatic int 4661590Srgrimestcp4_send(void *ctx, const unsigned char *data, size_t size, int fd) 4671590Srgrimes{ 4681590Srgrimes struct tcp4_ctx *tctx = ctx; 4691590Srgrimes 4701590Srgrimes PJDLOG_ASSERT(tctx != NULL); 4711590Srgrimes PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 4721590Srgrimes PJDLOG_ASSERT(tctx->tc_fd >= 0); 4731590Srgrimes PJDLOG_ASSERT(fd == -1); 4741590Srgrimes 4751590Srgrimes return (proto_common_send(tctx->tc_fd, data, size, -1)); 4761590Srgrimes} 4771590Srgrimes 4781590Srgrimesstatic int 4791590Srgrimestcp4_recv(void *ctx, unsigned char *data, size_t size, int *fdp) 4801590Srgrimes{ 4811590Srgrimes struct tcp4_ctx *tctx = ctx; 4821590Srgrimes 4831590Srgrimes PJDLOG_ASSERT(tctx != NULL); 4841590Srgrimes PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 4851590Srgrimes PJDLOG_ASSERT(tctx->tc_fd >= 0); 4861590Srgrimes PJDLOG_ASSERT(fdp == NULL); 4871590Srgrimes 4881590Srgrimes return (proto_common_recv(tctx->tc_fd, data, size, NULL)); 4891590Srgrimes} 4901590Srgrimes 4911590Srgrimesstatic int 4921590Srgrimestcp4_descriptor(const void *ctx) 4931590Srgrimes{ 4941590Srgrimes const struct tcp4_ctx *tctx = ctx; 4951590Srgrimes 4961590Srgrimes PJDLOG_ASSERT(tctx != NULL); 4971590Srgrimes PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 4981590Srgrimes 4991590Srgrimes return (tctx->tc_fd); 5001590Srgrimes} 5011590Srgrimes 5021590Srgrimesstatic bool 5031590Srgrimestcp4_address_match(const void *ctx, const char *addr) 5041590Srgrimes{ 5051590Srgrimes const struct tcp4_ctx *tctx = ctx; 5061590Srgrimes struct sockaddr_in sin; 5071590Srgrimes socklen_t sinlen; 5081590Srgrimes in_addr_t ip1, ip2; 5091590Srgrimes 5101590Srgrimes PJDLOG_ASSERT(tctx != NULL); 5111590Srgrimes PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 5121590Srgrimes 5131590Srgrimes if (tcp4_addr(addr, PROTO_TCP4_DEFAULT_PORT, &sin) != 0) 5141590Srgrimes return (false); 5151590Srgrimes ip1 = sin.sin_addr.s_addr; 5161590Srgrimes 5171590Srgrimes sinlen = sizeof(sin); 5181590Srgrimes if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) 5191590Srgrimes return (false); 5201590Srgrimes ip2 = sin.sin_addr.s_addr; 5211590Srgrimes 5221590Srgrimes return (ip1 == ip2); 5231590Srgrimes} 5241590Srgrimes 5251590Srgrimesstatic void 5261590Srgrimestcp4_local_address(const void *ctx, char *addr, size_t size) 5271590Srgrimes{ 5281590Srgrimes const struct tcp4_ctx *tctx = ctx; 5291590Srgrimes struct sockaddr_in sin; 5301590Srgrimes socklen_t sinlen; 5311590Srgrimes 5321590Srgrimes PJDLOG_ASSERT(tctx != NULL); 5331590Srgrimes PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 5341590Srgrimes 5351590Srgrimes sinlen = sizeof(sin); 5361590Srgrimes if (getsockname(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) { 5371590Srgrimes PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); 5381590Srgrimes return; 5391590Srgrimes } 5401590Srgrimes PJDLOG_VERIFY(snprintf(addr, size, "tcp4://%S", &sin) < (ssize_t)size); 5411590Srgrimes} 5421590Srgrimes 5431590Srgrimesstatic void 5441590Srgrimestcp4_remote_address(const void *ctx, char *addr, size_t size) 5451590Srgrimes{ 5461590Srgrimes const struct tcp4_ctx *tctx = ctx; 5471590Srgrimes struct sockaddr_in sin; 54829922Smarkm socklen_t sinlen; 54929922Smarkm 55029922Smarkm PJDLOG_ASSERT(tctx != NULL); 5511590Srgrimes PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 5521590Srgrimes 5531590Srgrimes sinlen = sizeof(sin); 5541590Srgrimes if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) { 5551590Srgrimes PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); 5561590Srgrimes return; 5571590Srgrimes } 5581590Srgrimes PJDLOG_VERIFY(snprintf(addr, size, "tcp4://%S", &sin) < (ssize_t)size); 5591590Srgrimes} 56029922Smarkm 5611590Srgrimesstatic void 5621590Srgrimestcp4_close(void *ctx) 5631590Srgrimes{ 5641590Srgrimes struct tcp4_ctx *tctx = ctx; 5651590Srgrimes 5661590Srgrimes PJDLOG_ASSERT(tctx != NULL); 5671590Srgrimes PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC); 5681590Srgrimes 5691590Srgrimes if (tctx->tc_fd >= 0) 5701590Srgrimes close(tctx->tc_fd); 5711590Srgrimes tctx->tc_magic = 0; 5721590Srgrimes free(tctx); 5731590Srgrimes} 5741590Srgrimes 5751590Srgrimesstatic struct proto tcp4_proto = { 5761590Srgrimes .prt_name = "tcp4", 5771590Srgrimes .prt_client = tcp4_client, 5781590Srgrimes .prt_connect = tcp4_connect, 5791590Srgrimes .prt_connect_wait = tcp4_connect_wait, 5801590Srgrimes .prt_server = tcp4_server, 5811590Srgrimes .prt_accept = tcp4_accept, 5821590Srgrimes .prt_wrap = tcp4_wrap, 5831590Srgrimes .prt_send = tcp4_send, 5841590Srgrimes .prt_recv = tcp4_recv, 5851590Srgrimes .prt_descriptor = tcp4_descriptor, 5861590Srgrimes .prt_address_match = tcp4_address_match, 5871590Srgrimes .prt_local_address = tcp4_local_address, 5881590Srgrimes .prt_remote_address = tcp4_remote_address, 5891590Srgrimes .prt_close = tcp4_close 5901590Srgrimes}; 5911590Srgrimes 5921590Srgrimesstatic __constructor void 5931590Srgrimestcp4_ctor(void) 5941590Srgrimes{ 5951590Srgrimes 5961590Srgrimes proto_register(&tcp4_proto, true); 5971590Srgrimes} 5981590Srgrimes