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