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