proto_tcp.c revision 222116
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_tcp.c 222116 2011-05-20 11:09:02Z pjd $");
32204076Spjd
33204076Spjd#include <sys/param.h>	/* MAXHOSTNAMELEN */
34219873Spjd#include <sys/socket.h>
35204076Spjd
36219873Spjd#include <arpa/inet.h>
37219873Spjd
38204076Spjd#include <netinet/in.h>
39204076Spjd#include <netinet/tcp.h>
40204076Spjd
41204076Spjd#include <errno.h>
42207390Spjd#include <fcntl.h>
43204076Spjd#include <netdb.h>
44204076Spjd#include <stdbool.h>
45204076Spjd#include <stdint.h>
46204076Spjd#include <stdio.h>
47204076Spjd#include <string.h>
48204076Spjd#include <unistd.h>
49204076Spjd
50204076Spjd#include "pjdlog.h"
51204076Spjd#include "proto_impl.h"
52207390Spjd#include "subr.h"
53204076Spjd
54222116Spjd#define	TCP_CTX_MAGIC	0x7c441c
55222116Spjdstruct tcp_ctx {
56204076Spjd	int			tc_magic;
57204076Spjd	struct sockaddr_in	tc_sin;
58204076Spjd	int			tc_fd;
59204076Spjd	int			tc_side;
60222116Spjd#define	TCP_SIDE_CLIENT		0
61222116Spjd#define	TCP_SIDE_SERVER_LISTEN	1
62222116Spjd#define	TCP_SIDE_SERVER_WORK	2
63204076Spjd};
64204076Spjd
65222116Spjdstatic int tcp_connect_wait(void *ctx, int timeout);
66222116Spjdstatic void tcp_close(void *ctx);
67204076Spjd
68204076Spjdstatic in_addr_t
69204076Spjdstr2ip(const char *str)
70204076Spjd{
71204076Spjd	struct hostent *hp;
72204076Spjd	in_addr_t ip;
73204076Spjd
74204076Spjd	ip = inet_addr(str);
75204076Spjd	if (ip != INADDR_NONE) {
76204076Spjd		/* It is a valid IP address. */
77204076Spjd		return (ip);
78204076Spjd	}
79204076Spjd	/* Check if it is a valid host name. */
80204076Spjd	hp = gethostbyname(str);
81204076Spjd	if (hp == NULL)
82204076Spjd		return (INADDR_NONE);
83204076Spjd	return (((struct in_addr *)(void *)hp->h_addr)->s_addr);
84204076Spjd}
85204076Spjd
86204076Spjd/*
87204076Spjd * Function converts the given string to unsigned number.
88204076Spjd */
89204076Spjdstatic int
90204076Spjdnumfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump)
91204076Spjd{
92204076Spjd	intmax_t digit, num;
93204076Spjd
94204076Spjd	if (str[0] == '\0')
95204076Spjd		goto invalid;	/* Empty string. */
96204076Spjd	num = 0;
97204076Spjd	for (; *str != '\0'; str++) {
98204076Spjd		if (*str < '0' || *str > '9')
99204076Spjd			goto invalid;	/* Non-digit character. */
100204076Spjd		digit = *str - '0';
101204076Spjd		if (num > num * 10 + digit)
102204076Spjd			goto invalid;	/* Overflow. */
103204076Spjd		num = num * 10 + digit;
104204076Spjd		if (num > maxnum)
105204076Spjd			goto invalid;	/* Too big. */
106204076Spjd	}
107204076Spjd	if (num < minnum)
108204076Spjd		goto invalid;	/* Too small. */
109204076Spjd	*nump = num;
110204076Spjd	return (0);
111204076Spjdinvalid:
112204076Spjd	errno = EINVAL;
113204076Spjd	return (-1);
114204076Spjd}
115204076Spjd
116204076Spjdstatic int
117222116Spjdtcp_addr(const char *addr, int defport, struct sockaddr_in *sinp)
118204076Spjd{
119204076Spjd	char iporhost[MAXHOSTNAMELEN];
120204076Spjd	const char *pp;
121204076Spjd	size_t size;
122204076Spjd	in_addr_t ip;
123204076Spjd
124204076Spjd	if (addr == NULL)
125204076Spjd		return (-1);
126204076Spjd
127222116Spjd	if (strncasecmp(addr, "tcp://", 7) == 0)
128204076Spjd		addr += 7;
129204076Spjd	else if (strncasecmp(addr, "tcp://", 6) == 0)
130204076Spjd		addr += 6;
131210870Spjd	else {
132210870Spjd		/*
133222116Spjd		 * Because TCP is the default assume IP or host is given without
134210870Spjd		 * prefix.
135210870Spjd		 */
136210870Spjd	}
137204076Spjd
138204076Spjd	sinp->sin_family = AF_INET;
139204076Spjd	sinp->sin_len = sizeof(*sinp);
140204076Spjd	/* Extract optional port. */
141204076Spjd	pp = strrchr(addr, ':');
142204076Spjd	if (pp == NULL) {
143204076Spjd		/* Port not given, use the default. */
144219818Spjd		sinp->sin_port = htons(defport);
145204076Spjd	} else {
146204076Spjd		intmax_t port;
147204076Spjd
148204076Spjd		if (numfromstr(pp + 1, 1, 65535, &port) < 0)
149204076Spjd			return (errno);
150204076Spjd		sinp->sin_port = htons(port);
151204076Spjd	}
152204076Spjd	/* Extract host name or IP address. */
153204076Spjd	if (pp == NULL) {
154204076Spjd		size = sizeof(iporhost);
155204076Spjd		if (strlcpy(iporhost, addr, size) >= size)
156204076Spjd			return (ENAMETOOLONG);
157204076Spjd	} else {
158204076Spjd		size = (size_t)(pp - addr + 1);
159204076Spjd		if (size > sizeof(iporhost))
160204076Spjd			return (ENAMETOOLONG);
161211407Spjd		(void)strlcpy(iporhost, addr, size);
162204076Spjd	}
163204076Spjd	/* Convert string (IP address or host name) to in_addr_t. */
164204076Spjd	ip = str2ip(iporhost);
165204076Spjd	if (ip == INADDR_NONE)
166204076Spjd		return (EINVAL);
167204076Spjd	sinp->sin_addr.s_addr = ip;
168204076Spjd
169204076Spjd	return (0);
170204076Spjd}
171204076Spjd
172204076Spjdstatic int
173222116Spjdtcp_setup_new(const char *addr, int side, void **ctxp)
174204076Spjd{
175222116Spjd	struct tcp_ctx *tctx;
176218158Spjd	int ret, nodelay;
177204076Spjd
178218194Spjd	PJDLOG_ASSERT(addr != NULL);
179222116Spjd	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
180222116Spjd	    side == TCP_SIDE_SERVER_LISTEN);
181218194Spjd	PJDLOG_ASSERT(ctxp != NULL);
182218194Spjd
183204076Spjd	tctx = malloc(sizeof(*tctx));
184204076Spjd	if (tctx == NULL)
185204076Spjd		return (errno);
186204076Spjd
187204076Spjd	/* Parse given address. */
188222116Spjd	if ((ret = tcp_addr(addr, PROTO_TCP_DEFAULT_PORT,
189219873Spjd	    &tctx->tc_sin)) != 0) {
190204076Spjd		free(tctx);
191204076Spjd		return (ret);
192204076Spjd	}
193204076Spjd
194218194Spjd	PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC);
195218194Spjd
196204076Spjd	tctx->tc_fd = socket(AF_INET, SOCK_STREAM, 0);
197204076Spjd	if (tctx->tc_fd == -1) {
198204076Spjd		ret = errno;
199204076Spjd		free(tctx);
200204076Spjd		return (ret);
201204076Spjd	}
202204076Spjd
203219818Spjd	PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC);
204219818Spjd
205204076Spjd	/* Socket settings. */
206218158Spjd	nodelay = 1;
207218158Spjd	if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay,
208218158Spjd	    sizeof(nodelay)) == -1) {
209218194Spjd		pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY");
210204076Spjd	}
211204076Spjd
212204076Spjd	tctx->tc_side = side;
213222116Spjd	tctx->tc_magic = TCP_CTX_MAGIC;
214204076Spjd	*ctxp = tctx;
215204076Spjd
216204076Spjd	return (0);
217204076Spjd}
218204076Spjd
219204076Spjdstatic int
220222116Spjdtcp_setup_wrap(int fd, int side, void **ctxp)
221218194Spjd{
222222116Spjd	struct tcp_ctx *tctx;
223218194Spjd
224218194Spjd	PJDLOG_ASSERT(fd >= 0);
225222116Spjd	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
226222116Spjd	    side == TCP_SIDE_SERVER_WORK);
227218194Spjd	PJDLOG_ASSERT(ctxp != NULL);
228218194Spjd
229218194Spjd	tctx = malloc(sizeof(*tctx));
230218194Spjd	if (tctx == NULL)
231218194Spjd		return (errno);
232218194Spjd
233218194Spjd	tctx->tc_fd = fd;
234218194Spjd	tctx->tc_sin.sin_family = AF_UNSPEC;
235218194Spjd	tctx->tc_side = side;
236222116Spjd	tctx->tc_magic = TCP_CTX_MAGIC;
237218194Spjd	*ctxp = tctx;
238218194Spjd
239218194Spjd	return (0);
240218194Spjd}
241218194Spjd
242218194Spjdstatic int
243222116Spjdtcp_client(const char *srcaddr, const char *dstaddr, void **ctxp)
244204076Spjd{
245222116Spjd	struct tcp_ctx *tctx;
246219818Spjd	struct sockaddr_in sin;
247219818Spjd	int ret;
248204076Spjd
249222116Spjd	ret = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, ctxp);
250219818Spjd	if (ret != 0)
251219818Spjd		return (ret);
252219818Spjd	tctx = *ctxp;
253219818Spjd	if (srcaddr == NULL)
254219818Spjd		return (0);
255222116Spjd	ret = tcp_addr(srcaddr, 0, &sin);
256219818Spjd	if (ret != 0) {
257222116Spjd		tcp_close(tctx);
258219818Spjd		return (ret);
259219818Spjd	}
260219818Spjd	if (bind(tctx->tc_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
261219818Spjd		ret = errno;
262222116Spjd		tcp_close(tctx);
263219818Spjd		return (ret);
264219818Spjd	}
265219818Spjd	return (0);
266204076Spjd}
267204076Spjd
268204076Spjdstatic int
269222116Spjdtcp_connect(void *ctx, int timeout)
270204076Spjd{
271222116Spjd	struct tcp_ctx *tctx = ctx;
272218193Spjd	int error, flags;
273204076Spjd
274218138Spjd	PJDLOG_ASSERT(tctx != NULL);
275222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
276222116Spjd	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
277218138Spjd	PJDLOG_ASSERT(tctx->tc_fd >= 0);
278218193Spjd	PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC);
279218193Spjd	PJDLOG_ASSERT(timeout >= -1);
280204076Spjd
281207390Spjd	flags = fcntl(tctx->tc_fd, F_GETFL);
282207390Spjd	if (flags == -1) {
283207390Spjd		KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno,
284207390Spjd		    "fcntl(F_GETFL) failed"));
285204076Spjd		return (errno);
286204076Spjd	}
287207390Spjd	/*
288211875Spjd	 * We make socket non-blocking so we can handle connection timeout
289211875Spjd	 * manually.
290207390Spjd	 */
291207390Spjd	flags |= O_NONBLOCK;
292207390Spjd	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
293207390Spjd		KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno,
294207390Spjd		    "fcntl(F_SETFL, O_NONBLOCK) failed"));
295207390Spjd		return (errno);
296207390Spjd	}
297204076Spjd
298207390Spjd	if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
299207390Spjd	    sizeof(tctx->tc_sin)) == 0) {
300218193Spjd		if (timeout == -1)
301218193Spjd			return (0);
302207390Spjd		error = 0;
303207390Spjd		goto done;
304207390Spjd	}
305207390Spjd	if (errno != EINPROGRESS) {
306207390Spjd		error = errno;
307207390Spjd		pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
308207390Spjd		goto done;
309207390Spjd	}
310218193Spjd	if (timeout == -1)
311218193Spjd		return (0);
312222116Spjd	return (tcp_connect_wait(ctx, timeout));
313218193Spjddone:
314218193Spjd	flags &= ~O_NONBLOCK;
315218193Spjd	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
316218193Spjd		if (error == 0)
317218193Spjd			error = errno;
318218193Spjd		pjdlog_common(LOG_DEBUG, 1, errno,
319218193Spjd		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
320218193Spjd	}
321218193Spjd	return (error);
322218193Spjd}
323218193Spjd
324218193Spjdstatic int
325222116Spjdtcp_connect_wait(void *ctx, int timeout)
326218193Spjd{
327222116Spjd	struct tcp_ctx *tctx = ctx;
328218193Spjd	struct timeval tv;
329218193Spjd	fd_set fdset;
330218193Spjd	socklen_t esize;
331218193Spjd	int error, flags, ret;
332218193Spjd
333218193Spjd	PJDLOG_ASSERT(tctx != NULL);
334222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
335222116Spjd	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
336218193Spjd	PJDLOG_ASSERT(tctx->tc_fd >= 0);
337218193Spjd	PJDLOG_ASSERT(timeout >= 0);
338218193Spjd
339218192Spjd	tv.tv_sec = timeout;
340207390Spjd	tv.tv_usec = 0;
341207390Spjdagain:
342207390Spjd	FD_ZERO(&fdset);
343219864Spjd	FD_SET(tctx->tc_fd, &fdset);
344207390Spjd	ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
345207390Spjd	if (ret == 0) {
346207390Spjd		error = ETIMEDOUT;
347207390Spjd		goto done;
348207390Spjd	} else if (ret == -1) {
349207390Spjd		if (errno == EINTR)
350207390Spjd			goto again;
351207390Spjd		error = errno;
352207390Spjd		pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
353207390Spjd		goto done;
354207390Spjd	}
355218138Spjd	PJDLOG_ASSERT(ret > 0);
356218138Spjd	PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset));
357207390Spjd	esize = sizeof(error);
358207390Spjd	if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
359207390Spjd	    &esize) == -1) {
360207390Spjd		error = errno;
361207390Spjd		pjdlog_common(LOG_DEBUG, 1, errno,
362207390Spjd		    "getsockopt(SO_ERROR) failed");
363207390Spjd		goto done;
364207390Spjd	}
365207390Spjd	if (error != 0) {
366207390Spjd		pjdlog_common(LOG_DEBUG, 1, error,
367207390Spjd		    "getsockopt(SO_ERROR) returned error");
368207390Spjd		goto done;
369207390Spjd	}
370207390Spjd	error = 0;
371207390Spjddone:
372218193Spjd	flags = fcntl(tctx->tc_fd, F_GETFL);
373218193Spjd	if (flags == -1) {
374218193Spjd		if (error == 0)
375218193Spjd			error = errno;
376218193Spjd		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
377218193Spjd		return (error);
378218193Spjd	}
379207390Spjd	flags &= ~O_NONBLOCK;
380207390Spjd	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
381207390Spjd		if (error == 0)
382207390Spjd			error = errno;
383207390Spjd		pjdlog_common(LOG_DEBUG, 1, errno,
384207390Spjd		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
385207390Spjd	}
386207390Spjd	return (error);
387204076Spjd}
388204076Spjd
389204076Spjdstatic int
390222116Spjdtcp_server(const char *addr, void **ctxp)
391204076Spjd{
392222116Spjd	struct tcp_ctx *tctx;
393204076Spjd	int ret, val;
394204076Spjd
395222116Spjd	ret = tcp_setup_new(addr, TCP_SIDE_SERVER_LISTEN, ctxp);
396204076Spjd	if (ret != 0)
397204076Spjd		return (ret);
398204076Spjd
399204076Spjd	tctx = *ctxp;
400204076Spjd
401204076Spjd	val = 1;
402204076Spjd	/* Ignore failure. */
403204076Spjd	(void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
404204076Spjd	   sizeof(val));
405204076Spjd
406218194Spjd	PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC);
407218194Spjd
408204076Spjd	if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
409204076Spjd	    sizeof(tctx->tc_sin)) < 0) {
410204076Spjd		ret = errno;
411222116Spjd		tcp_close(tctx);
412204076Spjd		return (ret);
413204076Spjd	}
414204076Spjd	if (listen(tctx->tc_fd, 8) < 0) {
415204076Spjd		ret = errno;
416222116Spjd		tcp_close(tctx);
417204076Spjd		return (ret);
418204076Spjd	}
419204076Spjd
420204076Spjd	return (0);
421204076Spjd}
422204076Spjd
423204076Spjdstatic int
424222116Spjdtcp_accept(void *ctx, void **newctxp)
425204076Spjd{
426222116Spjd	struct tcp_ctx *tctx = ctx;
427222116Spjd	struct tcp_ctx *newtctx;
428204076Spjd	socklen_t fromlen;
429204076Spjd	int ret;
430204076Spjd
431218138Spjd	PJDLOG_ASSERT(tctx != NULL);
432222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
433222116Spjd	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN);
434218138Spjd	PJDLOG_ASSERT(tctx->tc_fd >= 0);
435218194Spjd	PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC);
436204076Spjd
437204076Spjd	newtctx = malloc(sizeof(*newtctx));
438204076Spjd	if (newtctx == NULL)
439204076Spjd		return (errno);
440204076Spjd
441204076Spjd	fromlen = sizeof(tctx->tc_sin);
442204076Spjd	newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
443204076Spjd	    &fromlen);
444204076Spjd	if (newtctx->tc_fd < 0) {
445204076Spjd		ret = errno;
446204076Spjd		free(newtctx);
447204076Spjd		return (ret);
448204076Spjd	}
449204076Spjd
450222116Spjd	newtctx->tc_side = TCP_SIDE_SERVER_WORK;
451222116Spjd	newtctx->tc_magic = TCP_CTX_MAGIC;
452204076Spjd	*newctxp = newtctx;
453204076Spjd
454204076Spjd	return (0);
455204076Spjd}
456204076Spjd
457204076Spjdstatic int
458222116Spjdtcp_wrap(int fd, bool client, void **ctxp)
459204076Spjd{
460218194Spjd
461222116Spjd	return (tcp_setup_wrap(fd,
462222116Spjd	    client ? TCP_SIDE_CLIENT : TCP_SIDE_SERVER_WORK, ctxp));
463218194Spjd}
464218194Spjd
465218194Spjdstatic int
466222116Spjdtcp_send(void *ctx, const unsigned char *data, size_t size, int fd)
467218194Spjd{
468222116Spjd	struct tcp_ctx *tctx = ctx;
469204076Spjd
470218138Spjd	PJDLOG_ASSERT(tctx != NULL);
471222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
472218138Spjd	PJDLOG_ASSERT(tctx->tc_fd >= 0);
473218194Spjd	PJDLOG_ASSERT(fd == -1);
474204076Spjd
475218194Spjd	return (proto_common_send(tctx->tc_fd, data, size, -1));
476204076Spjd}
477204076Spjd
478204076Spjdstatic int
479222116Spjdtcp_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
480204076Spjd{
481222116Spjd	struct tcp_ctx *tctx = ctx;
482204076Spjd
483218138Spjd	PJDLOG_ASSERT(tctx != NULL);
484222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
485218138Spjd	PJDLOG_ASSERT(tctx->tc_fd >= 0);
486218194Spjd	PJDLOG_ASSERT(fdp == NULL);
487204076Spjd
488218194Spjd	return (proto_common_recv(tctx->tc_fd, data, size, NULL));
489204076Spjd}
490204076Spjd
491204076Spjdstatic int
492222116Spjdtcp_descriptor(const void *ctx)
493204076Spjd{
494222116Spjd	const struct tcp_ctx *tctx = ctx;
495204076Spjd
496218138Spjd	PJDLOG_ASSERT(tctx != NULL);
497222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
498204076Spjd
499204076Spjd	return (tctx->tc_fd);
500204076Spjd}
501204076Spjd
502204076Spjdstatic bool
503222116Spjdtcp_address_match(const void *ctx, const char *addr)
504204076Spjd{
505222116Spjd	const struct tcp_ctx *tctx = ctx;
506204076Spjd	struct sockaddr_in sin;
507204076Spjd	socklen_t sinlen;
508204076Spjd	in_addr_t ip1, ip2;
509204076Spjd
510218138Spjd	PJDLOG_ASSERT(tctx != NULL);
511222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
512204076Spjd
513222116Spjd	if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sin) != 0)
514204076Spjd		return (false);
515204076Spjd	ip1 = sin.sin_addr.s_addr;
516204076Spjd
517204076Spjd	sinlen = sizeof(sin);
518204076Spjd	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0)
519204076Spjd		return (false);
520204076Spjd	ip2 = sin.sin_addr.s_addr;
521204076Spjd
522204076Spjd	return (ip1 == ip2);
523204076Spjd}
524204076Spjd
525204076Spjdstatic void
526222116Spjdtcp_local_address(const void *ctx, char *addr, size_t size)
527204076Spjd{
528222116Spjd	const struct tcp_ctx *tctx = ctx;
529204076Spjd	struct sockaddr_in sin;
530204076Spjd	socklen_t sinlen;
531204076Spjd
532218138Spjd	PJDLOG_ASSERT(tctx != NULL);
533222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
534204076Spjd
535204076Spjd	sinlen = sizeof(sin);
536204076Spjd	if (getsockname(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
537210876Spjd		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
538204076Spjd		return;
539204076Spjd	}
540222116Spjd	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sin) < (ssize_t)size);
541204076Spjd}
542204076Spjd
543204076Spjdstatic void
544222116Spjdtcp_remote_address(const void *ctx, char *addr, size_t size)
545204076Spjd{
546222116Spjd	const struct tcp_ctx *tctx = ctx;
547204076Spjd	struct sockaddr_in sin;
548204076Spjd	socklen_t sinlen;
549204076Spjd
550218138Spjd	PJDLOG_ASSERT(tctx != NULL);
551222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
552204076Spjd
553204076Spjd	sinlen = sizeof(sin);
554204076Spjd	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
555210876Spjd		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
556204076Spjd		return;
557204076Spjd	}
558222116Spjd	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sin) < (ssize_t)size);
559204076Spjd}
560204076Spjd
561204076Spjdstatic void
562222116Spjdtcp_close(void *ctx)
563204076Spjd{
564222116Spjd	struct tcp_ctx *tctx = ctx;
565204076Spjd
566218138Spjd	PJDLOG_ASSERT(tctx != NULL);
567222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
568204076Spjd
569204076Spjd	if (tctx->tc_fd >= 0)
570204076Spjd		close(tctx->tc_fd);
571204076Spjd	tctx->tc_magic = 0;
572204076Spjd	free(tctx);
573204076Spjd}
574204076Spjd
575222116Spjdstatic struct proto tcp_proto = {
576222116Spjd	.prt_name = "tcp",
577222116Spjd	.prt_client = tcp_client,
578222116Spjd	.prt_connect = tcp_connect,
579222116Spjd	.prt_connect_wait = tcp_connect_wait,
580222116Spjd	.prt_server = tcp_server,
581222116Spjd	.prt_accept = tcp_accept,
582222116Spjd	.prt_wrap = tcp_wrap,
583222116Spjd	.prt_send = tcp_send,
584222116Spjd	.prt_recv = tcp_recv,
585222116Spjd	.prt_descriptor = tcp_descriptor,
586222116Spjd	.prt_address_match = tcp_address_match,
587222116Spjd	.prt_local_address = tcp_local_address,
588222116Spjd	.prt_remote_address = tcp_remote_address,
589222116Spjd	.prt_close = tcp_close
590204076Spjd};
591204076Spjd
592204076Spjdstatic __constructor void
593222116Spjdtcp_ctor(void)
594204076Spjd{
595204076Spjd
596222116Spjd	proto_register(&tcp_proto, true);
597204076Spjd}
598