proto_tcp.c revision 225781
1193323Sed/*-
2193323Sed * Copyright (c) 2009-2010 The FreeBSD Foundation
3193323Sed * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
4193323Sed * All rights reserved.
5193323Sed *
6193323Sed * This software was developed by Pawel Jakub Dawidek under sponsorship from
7193323Sed * the FreeBSD Foundation.
8193323Sed *
9193323Sed * Redistribution and use in source and binary forms, with or without
10193323Sed * modification, are permitted provided that the following conditions
11193323Sed * are met:
12193323Sed * 1. Redistributions of source code must retain the above copyright
13193323Sed *    notice, this list of conditions and the following disclaimer.
14193323Sed * 2. Redistributions in binary form must reproduce the above copyright
15263509Sdim *    notice, this list of conditions and the following disclaimer in the
16263509Sdim *    documentation and/or other materials provided with the distribution.
17201360Srdivacky *
18201360Srdivacky * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21193323Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22201360Srdivacky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24201360Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25201360Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26201360Srdivacky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27201360Srdivacky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28201360Srdivacky * SUCH DAMAGE.
29201360Srdivacky */
30201360Srdivacky
31201360Srdivacky#include <sys/cdefs.h>
32201360Srdivacky__FBSDID("$FreeBSD: head/sbin/hastd/proto_tcp.c 225781 2011-09-27 07:52:39Z pjd $");
33201360Srdivacky
34201360Srdivacky#include <sys/param.h>	/* MAXHOSTNAMELEN */
35201360Srdivacky#include <sys/socket.h>
36201360Srdivacky
37201360Srdivacky#include <arpa/inet.h>
38201360Srdivacky
39201360Srdivacky#include <netinet/in.h>
40201360Srdivacky#include <netinet/tcp.h>
41201360Srdivacky
42201360Srdivacky#include <errno.h>
43201360Srdivacky#include <fcntl.h>
44201360Srdivacky#include <netdb.h>
45201360Srdivacky#include <stdbool.h>
46201360Srdivacky#include <stdint.h>
47201360Srdivacky#include <stdio.h>
48201360Srdivacky#include <string.h>
49201360Srdivacky#include <unistd.h>
50201360Srdivacky
51201360Srdivacky#include "pjdlog.h"
52201360Srdivacky#include "proto_impl.h"
53201360Srdivacky#include "subr.h"
54201360Srdivacky
55201360Srdivacky#define	TCP_CTX_MAGIC	0x7c41c
56201360Srdivackystruct tcp_ctx {
57201360Srdivacky	int			tc_magic;
58201360Srdivacky	struct sockaddr_storage	tc_sa;
59201360Srdivacky	int			tc_fd;
60201360Srdivacky	int			tc_side;
61201360Srdivacky#define	TCP_SIDE_CLIENT		0
62201360Srdivacky#define	TCP_SIDE_SERVER_LISTEN	1
63201360Srdivacky#define	TCP_SIDE_SERVER_WORK	2
64263509Sdim};
65263509Sdim
66201360Srdivackystatic int tcp_connect_wait(void *ctx, int timeout);
67201360Srdivackystatic void tcp_close(void *ctx);
68201360Srdivacky
69201360Srdivacky/*
70201360Srdivacky * Function converts the given string to unsigned number.
71201360Srdivacky */
72201360Srdivackystatic int
73201360Srdivackynumfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump)
74201360Srdivacky{
75201360Srdivacky	intmax_t digit, num;
76201360Srdivacky
77201360Srdivacky	if (str[0] == '\0')
78201360Srdivacky		goto invalid;	/* Empty string. */
79201360Srdivacky	num = 0;
80201360Srdivacky	for (; *str != '\0'; str++) {
81201360Srdivacky		if (*str < '0' || *str > '9')
82201360Srdivacky			goto invalid;	/* Non-digit character. */
83218893Sdim		digit = *str - '0';
84218893Sdim		if (num > num * 10 + digit)
85226890Sdim			goto invalid;	/* Overflow. */
86226890Sdim		num = num * 10 + digit;
87226890Sdim		if (num > maxnum)
88226890Sdim			goto invalid;	/* Too big. */
89226890Sdim	}
90226890Sdim	if (num < minnum)
91226890Sdim		goto invalid;	/* Too small. */
92226890Sdim	*nump = num;
93226890Sdim	return (0);
94226890Sdiminvalid:
95226890Sdim	errno = EINVAL;
96226890Sdim	return (-1);
97226890Sdim}
98235633Sdim
99193323Sedstatic int
100201360Srdivackytcp_addr(const char *addr, int defport, struct sockaddr_storage *sap)
101193323Sed{
102193323Sed	char iporhost[MAXHOSTNAMELEN], portstr[6];
103193323Sed	struct addrinfo hints;
104193323Sed	struct addrinfo *res;
105201360Srdivacky	const char *pp;
106193323Sed	intmax_t port;
107210299Sed	size_t size;
108210299Sed	int error;
109193323Sed
110201360Srdivacky	if (addr == NULL)
111193323Sed		return (-1);
112193323Sed
113193323Sed	bzero(&hints, sizeof(hints));
114193323Sed	hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
115201360Srdivacky	hints.ai_family = PF_UNSPEC;
116193323Sed	hints.ai_socktype = SOCK_STREAM;
117201360Srdivacky	hints.ai_protocol = IPPROTO_TCP;
118201360Srdivacky
119201360Srdivacky	if (strncasecmp(addr, "tcp4://", 7) == 0) {
120201360Srdivacky		addr += 7;
121201360Srdivacky		hints.ai_family = PF_INET;
122201360Srdivacky	} else if (strncasecmp(addr, "tcp6://", 7) == 0) {
123201360Srdivacky		addr += 7;
124201360Srdivacky		hints.ai_family = PF_INET6;
125201360Srdivacky	} else if (strncasecmp(addr, "tcp://", 6) == 0) {
126201360Srdivacky		addr += 6;
127201360Srdivacky	} else {
128201360Srdivacky		/*
129201360Srdivacky		 * Because TCP is the default assume IP or host is given without
130201360Srdivacky		 * prefix.
131201360Srdivacky		 */
132201360Srdivacky	}
133201360Srdivacky
134201360Srdivacky	/*
135201360Srdivacky	 * Extract optional port.
136201360Srdivacky	 * There are three cases to consider.
137201360Srdivacky	 * 1. hostname with port, eg. freefall.freebsd.org:8457
138201360Srdivacky	 * 2. IPv4 address with port, eg. 192.168.0.101:8457
139201360Srdivacky	 * 3. IPv6 address with port, eg. [fe80::1]:8457
140201360Srdivacky	 * We discover IPv6 address by checking for two colons and if port is
141201360Srdivacky	 * given, the address has to start with [.
142201360Srdivacky	 */
143201360Srdivacky	pp = NULL;
144201360Srdivacky	if (strchr(addr, ':') != strrchr(addr, ':')) {
145201360Srdivacky		if (addr[0] == '[')
146201360Srdivacky			pp = strrchr(addr, ':');
147201360Srdivacky	} else {
148201360Srdivacky		pp = strrchr(addr, ':');
149201360Srdivacky	}
150201360Srdivacky	if (pp == NULL) {
151201360Srdivacky		/* Port not given, use the default. */
152201360Srdivacky		port = defport;
153201360Srdivacky	} else {
154201360Srdivacky		if (numfromstr(pp + 1, 1, 65535, &port) < 0)
155201360Srdivacky			return (errno);
156201360Srdivacky	}
157201360Srdivacky	(void)snprintf(portstr, sizeof(portstr), "%jd", (intmax_t)port);
158201360Srdivacky	/* Extract host name or IP address. */
159201360Srdivacky	if (pp == NULL) {
160201360Srdivacky		size = sizeof(iporhost);
161201360Srdivacky		if (strlcpy(iporhost, addr, size) >= size)
162201360Srdivacky			return (ENAMETOOLONG);
163201360Srdivacky	} else if (addr[0] == '[' && pp[-1] == ']') {
164201360Srdivacky		size = (size_t)(pp - addr - 2 + 1);
165201360Srdivacky		if (size > sizeof(iporhost))
166201360Srdivacky			return (ENAMETOOLONG);
167201360Srdivacky		(void)strlcpy(iporhost, addr + 1, size);
168201360Srdivacky	} else {
169201360Srdivacky		size = (size_t)(pp - addr + 1);
170201360Srdivacky		if (size > sizeof(iporhost))
171201360Srdivacky			return (ENAMETOOLONG);
172201360Srdivacky		(void)strlcpy(iporhost, addr, size);
173201360Srdivacky	}
174201360Srdivacky
175201360Srdivacky	error = getaddrinfo(iporhost, portstr, &hints, &res);
176201360Srdivacky	if (error != 0) {
177201360Srdivacky		pjdlog_debug(1, "getaddrinfo(%s, %s) failed: %s.", iporhost,
178201360Srdivacky		    portstr, gai_strerror(error));
179201360Srdivacky		return (EINVAL);
180201360Srdivacky	}
181201360Srdivacky	if (res == NULL)
182201360Srdivacky		return (ENOENT);
183201360Srdivacky
184201360Srdivacky	memcpy(sap, res->ai_addr, res->ai_addrlen);
185201360Srdivacky
186201360Srdivacky	freeaddrinfo(res);
187201360Srdivacky
188201360Srdivacky	return (0);
189201360Srdivacky}
190201360Srdivacky
191201360Srdivackystatic int
192201360Srdivackytcp_setup_new(const char *addr, int side, void **ctxp)
193201360Srdivacky{
194201360Srdivacky	struct tcp_ctx *tctx;
195201360Srdivacky	int ret, nodelay;
196201360Srdivacky
197201360Srdivacky	PJDLOG_ASSERT(addr != NULL);
198201360Srdivacky	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
199201360Srdivacky	    side == TCP_SIDE_SERVER_LISTEN);
200201360Srdivacky	PJDLOG_ASSERT(ctxp != NULL);
201201360Srdivacky
202201360Srdivacky	tctx = malloc(sizeof(*tctx));
203226890Sdim	if (tctx == NULL)
204226890Sdim		return (errno);
205226890Sdim
206226890Sdim	/* Parse given address. */
207226890Sdim	if ((ret = tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &tctx->tc_sa)) != 0) {
208226890Sdim		free(tctx);
209226890Sdim		return (ret);
210226890Sdim	}
211226890Sdim
212226890Sdim	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
213226890Sdim
214226890Sdim	tctx->tc_fd = socket(tctx->tc_sa.ss_family, SOCK_STREAM, 0);
215201360Srdivacky	if (tctx->tc_fd == -1) {
216226890Sdim		ret = errno;
217226890Sdim		free(tctx);
218226890Sdim		return (ret);
219226890Sdim	}
220226890Sdim
221226890Sdim	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
222226890Sdim
223226890Sdim	/* Socket settings. */
224226890Sdim	nodelay = 1;
225226890Sdim	if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay,
226226890Sdim	    sizeof(nodelay)) == -1) {
227201360Srdivacky		pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY");
228201360Srdivacky	}
229201360Srdivacky
230201360Srdivacky	tctx->tc_side = side;
231201360Srdivacky	tctx->tc_magic = TCP_CTX_MAGIC;
232201360Srdivacky	*ctxp = tctx;
233201360Srdivacky
234226890Sdim	return (0);
235263509Sdim}
236226890Sdim
237201360Srdivackystatic int
238201360Srdivackytcp_setup_wrap(int fd, int side, void **ctxp)
239201360Srdivacky{
240201360Srdivacky	struct tcp_ctx *tctx;
241201360Srdivacky
242201360Srdivacky	PJDLOG_ASSERT(fd >= 0);
243207618Srdivacky	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
244201360Srdivacky	    side == TCP_SIDE_SERVER_WORK);
245207618Srdivacky	PJDLOG_ASSERT(ctxp != NULL);
246221345Sdim
247221345Sdim	tctx = malloc(sizeof(*tctx));
248221345Sdim	if (tctx == NULL)
249221345Sdim		return (errno);
250235633Sdim
251223017Sdim	tctx->tc_fd = fd;
252252723Sdim	tctx->tc_sa.ss_family = AF_UNSPEC;
253252723Sdim	tctx->tc_side = side;
254252723Sdim	tctx->tc_magic = TCP_CTX_MAGIC;
255252723Sdim	*ctxp = tctx;
256252723Sdim
257252723Sdim	return (0);
258252723Sdim}
259252723Sdim
260193323Sedstatic int
261201360Srdivackytcp_client(const char *srcaddr, const char *dstaddr, void **ctxp)
262193323Sed{
263193323Sed	struct tcp_ctx *tctx;
264193323Sed	struct sockaddr_storage sa;
265193323Sed	int ret;
266201360Srdivacky
267193323Sed	ret = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, ctxp);
268210299Sed	if (ret != 0)
269210299Sed		return (ret);
270210299Sed	tctx = *ctxp;
271210299Sed	if (srcaddr == NULL)
272210299Sed		return (0);
273210299Sed	ret = tcp_addr(srcaddr, 0, &sa);
274210299Sed	if (ret != 0) {
275210299Sed		tcp_close(tctx);
276210299Sed		return (ret);
277210299Sed	}
278210299Sed	if (bind(tctx->tc_fd, (struct sockaddr *)&sa, sa.ss_len) < 0) {
279210299Sed		ret = errno;
280210299Sed		tcp_close(tctx);
281210299Sed		return (ret);
282210299Sed	}
283210299Sed	return (0);
284210299Sed}
285210299Sed
286210299Sedstatic int
287210299Sedtcp_connect(void *ctx, int timeout)
288210299Sed{
289226890Sdim	struct tcp_ctx *tctx = ctx;
290226890Sdim	int error, flags;
291226890Sdim
292226890Sdim	PJDLOG_ASSERT(tctx != NULL);
293252723Sdim	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
294252723Sdim	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
295252723Sdim	PJDLOG_ASSERT(tctx->tc_fd >= 0);
296252723Sdim	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
297193323Sed	PJDLOG_ASSERT(timeout >= -1);
298201360Srdivacky
299193323Sed	flags = fcntl(tctx->tc_fd, F_GETFL);
300193323Sed	if (flags == -1) {
301193323Sed		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
302193323Sed		return (errno);
303201360Srdivacky	}
304193323Sed	/*
305210299Sed	 * We make socket non-blocking so we can handle connection timeout
306210299Sed	 * manually.
307210299Sed	 */
308210299Sed	flags |= O_NONBLOCK;
309210299Sed	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
310210299Sed		pjdlog_common(LOG_DEBUG, 1, errno,
311210299Sed		    "fcntl(F_SETFL, O_NONBLOCK) failed");
312210299Sed		return (errno);
313210299Sed	}
314210299Sed
315210299Sed	if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
316210299Sed	    tctx->tc_sa.ss_len) == 0) {
317210299Sed		if (timeout == -1)
318210299Sed			return (0);
319210299Sed		error = 0;
320210299Sed		goto done;
321210299Sed	}
322210299Sed	if (errno != EINPROGRESS) {
323210299Sed		error = errno;
324210299Sed		pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
325210299Sed		goto done;
326210299Sed	}
327210299Sed	if (timeout == -1)
328210299Sed		return (0);
329210299Sed	return (tcp_connect_wait(ctx, timeout));
330210299Seddone:
331210299Sed	flags &= ~O_NONBLOCK;
332210299Sed	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
333210299Sed		if (error == 0)
334210299Sed			error = errno;
335210299Sed		pjdlog_common(LOG_DEBUG, 1, errno,
336210299Sed		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
337210299Sed	}
338210299Sed	return (error);
339210299Sed}
340210299Sed
341210299Sedstatic int
342210299Sedtcp_connect_wait(void *ctx, int timeout)
343210299Sed{
344210299Sed	struct tcp_ctx *tctx = ctx;
345210299Sed	struct timeval tv;
346210299Sed	fd_set fdset;
347210299Sed	socklen_t esize;
348210299Sed	int error, flags, ret;
349210299Sed
350210299Sed	PJDLOG_ASSERT(tctx != NULL);
351210299Sed	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
352210299Sed	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
353210299Sed	PJDLOG_ASSERT(tctx->tc_fd >= 0);
354210299Sed	PJDLOG_ASSERT(timeout >= 0);
355210299Sed
356210299Sed	tv.tv_sec = timeout;
357210299Sed	tv.tv_usec = 0;
358210299Sedagain:
359210299Sed	FD_ZERO(&fdset);
360210299Sed	FD_SET(tctx->tc_fd, &fdset);
361210299Sed	ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
362210299Sed	if (ret == 0) {
363210299Sed		error = ETIMEDOUT;
364210299Sed		goto done;
365210299Sed	} else if (ret == -1) {
366210299Sed		if (errno == EINTR)
367210299Sed			goto again;
368210299Sed		error = errno;
369210299Sed		pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
370210299Sed		goto done;
371210299Sed	}
372210299Sed	PJDLOG_ASSERT(ret > 0);
373210299Sed	PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset));
374210299Sed	esize = sizeof(error);
375210299Sed	if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
376210299Sed	    &esize) == -1) {
377210299Sed		error = errno;
378210299Sed		pjdlog_common(LOG_DEBUG, 1, errno,
379210299Sed		    "getsockopt(SO_ERROR) failed");
380210299Sed		goto done;
381210299Sed	}
382210299Sed	if (error != 0) {
383210299Sed		pjdlog_common(LOG_DEBUG, 1, error,
384210299Sed		    "getsockopt(SO_ERROR) returned error");
385210299Sed		goto done;
386210299Sed	}
387210299Sed	error = 0;
388210299Seddone:
389210299Sed	flags = fcntl(tctx->tc_fd, F_GETFL);
390210299Sed	if (flags == -1) {
391210299Sed		if (error == 0)
392210299Sed			error = errno;
393210299Sed		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
394210299Sed		return (error);
395210299Sed	}
396210299Sed	flags &= ~O_NONBLOCK;
397210299Sed	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
398210299Sed		if (error == 0)
399210299Sed			error = errno;
400210299Sed		pjdlog_common(LOG_DEBUG, 1, errno,
401210299Sed		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
402210299Sed	}
403210299Sed	return (error);
404210299Sed}
405210299Sed
406210299Sedstatic int
407210299Sedtcp_server(const char *addr, void **ctxp)
408210299Sed{
409210299Sed	struct tcp_ctx *tctx;
410210299Sed	int ret, val;
411210299Sed
412210299Sed	ret = tcp_setup_new(addr, TCP_SIDE_SERVER_LISTEN, ctxp);
413210299Sed	if (ret != 0)
414210299Sed		return (ret);
415210299Sed
416210299Sed	tctx = *ctxp;
417210299Sed
418210299Sed	val = 1;
419210299Sed	/* Ignore failure. */
420210299Sed	(void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
421210299Sed	   sizeof(val));
422210299Sed
423210299Sed	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
424210299Sed
425210299Sed	if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
426210299Sed	    tctx->tc_sa.ss_len) < 0) {
427210299Sed		ret = errno;
428210299Sed		tcp_close(tctx);
429210299Sed		return (ret);
430210299Sed	}
431210299Sed	if (listen(tctx->tc_fd, 8) < 0) {
432210299Sed		ret = errno;
433210299Sed		tcp_close(tctx);
434210299Sed		return (ret);
435210299Sed	}
436210299Sed
437210299Sed	return (0);
438210299Sed}
439210299Sed
440210299Sedstatic int
441210299Sedtcp_accept(void *ctx, void **newctxp)
442210299Sed{
443210299Sed	struct tcp_ctx *tctx = ctx;
444210299Sed	struct tcp_ctx *newtctx;
445210299Sed	socklen_t fromlen;
446210299Sed	int ret;
447210299Sed
448210299Sed	PJDLOG_ASSERT(tctx != NULL);
449210299Sed	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
450210299Sed	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN);
451210299Sed	PJDLOG_ASSERT(tctx->tc_fd >= 0);
452210299Sed	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
453210299Sed
454210299Sed	newtctx = malloc(sizeof(*newtctx));
455210299Sed	if (newtctx == NULL)
456221345Sdim		return (errno);
457226890Sdim
458226890Sdim	fromlen = tctx->tc_sa.ss_len;
459252723Sdim	newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
460263509Sdim	    &fromlen);
461263509Sdim	if (newtctx->tc_fd < 0) {
462263509Sdim		ret = errno;
463263509Sdim		free(newtctx);
464252723Sdim		return (ret);
465252723Sdim	}
466193323Sed
467201360Srdivacky	newtctx->tc_side = TCP_SIDE_SERVER_WORK;
468193323Sed	newtctx->tc_magic = TCP_CTX_MAGIC;
469193323Sed	*newctxp = newtctx;
470193323Sed
471193323Sed	return (0);
472201360Srdivacky}
473193323Sed
474210299Sedstatic int
475210299Sedtcp_wrap(int fd, bool client, void **ctxp)
476210299Sed{
477210299Sed
478210299Sed	return (tcp_setup_wrap(fd,
479210299Sed	    client ? TCP_SIDE_CLIENT : TCP_SIDE_SERVER_WORK, ctxp));
480210299Sed}
481210299Sed
482210299Sedstatic int
483226890Sdimtcp_send(void *ctx, const unsigned char *data, size_t size, int fd)
484210299Sed{
485210299Sed	struct tcp_ctx *tctx = ctx;
486210299Sed
487210299Sed	PJDLOG_ASSERT(tctx != NULL);
488210299Sed	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
489210299Sed	PJDLOG_ASSERT(tctx->tc_fd >= 0);
490210299Sed	PJDLOG_ASSERT(fd == -1);
491210299Sed
492193323Sed	return (proto_common_send(tctx->tc_fd, data, size, -1));
493201360Srdivacky}
494193323Sed
495193323Sedstatic int
496193323Sedtcp_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
497193323Sed{
498201360Srdivacky	struct tcp_ctx *tctx = ctx;
499193323Sed
500210299Sed	PJDLOG_ASSERT(tctx != NULL);
501210299Sed	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
502210299Sed	PJDLOG_ASSERT(tctx->tc_fd >= 0);
503210299Sed	PJDLOG_ASSERT(fdp == NULL);
504210299Sed
505193323Sed	return (proto_common_recv(tctx->tc_fd, data, size, NULL));
506201360Srdivacky}
507193323Sed
508193323Sedstatic int
509193323Sedtcp_descriptor(const void *ctx)
510193323Sed{
511201360Srdivacky	const struct tcp_ctx *tctx = ctx;
512193323Sed
513210299Sed	PJDLOG_ASSERT(tctx != NULL);
514210299Sed	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
515210299Sed
516210299Sed	return (tctx->tc_fd);
517210299Sed}
518193323Sed
519201360Srdivackystatic bool
520193323Sedtcp_address_match(const void *ctx, const char *addr)
521193323Sed{
522193323Sed	const struct tcp_ctx *tctx = ctx;
523193323Sed	struct sockaddr_storage sa1, sa2;
524201360Srdivacky	socklen_t salen;
525193323Sed
526201360Srdivacky	PJDLOG_ASSERT(tctx != NULL);
527210299Sed	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
528210299Sed
529210299Sed	if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sa1) != 0)
530193323Sed		return (false);
531201360Srdivacky
532193323Sed	salen = sizeof(sa2);
533193323Sed	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) < 0)
534193323Sed		return (false);
535193323Sed
536201360Srdivacky	if (sa1.ss_family != sa2.ss_family || sa1.ss_len != sa2.ss_len)
537193323Sed		return (false);
538210299Sed
539210299Sed	switch (sa1.ss_family) {
540210299Sed	case AF_INET:
541193323Sed	    {
542201360Srdivacky		struct sockaddr_in *sin1, *sin2;
543193323Sed
544193323Sed		sin1 = (struct sockaddr_in *)&sa1;
545193323Sed		sin2 = (struct sockaddr_in *)&sa2;
546193323Sed
547201360Srdivacky		return (memcmp(&sin1->sin_addr, &sin2->sin_addr,
548193323Sed		    sizeof(sin1->sin_addr)) == 0);
549210299Sed	    }
550210299Sed	case AF_INET6:
551210299Sed	    {
552193323Sed		struct sockaddr_in6 *sin1, *sin2;
553201360Srdivacky
554193323Sed		sin1 = (struct sockaddr_in6 *)&sa1;
555193323Sed		sin2 = (struct sockaddr_in6 *)&sa2;
556193323Sed
557193323Sed		return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
558201360Srdivacky		    sizeof(sin1->sin6_addr)) == 0);
559193323Sed	    }
560210299Sed	default:
561210299Sed		return (false);
562210299Sed	}
563210299Sed}
564210299Sed
565210299Sedstatic void
566210299Sedtcp_local_address(const void *ctx, char *addr, size_t size)
567210299Sed{
568210299Sed	const struct tcp_ctx *tctx = ctx;
569210299Sed	struct sockaddr_storage sa;
570210299Sed	socklen_t salen;
571210299Sed
572210299Sed	PJDLOG_ASSERT(tctx != NULL);
573210299Sed	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
574210299Sed
575210299Sed	salen = sizeof(sa);
576210299Sed	if (getsockname(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) {
577210299Sed		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
578210299Sed		return;
579210299Sed	}
580210299Sed	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
581193323Sed}
582201360Srdivacky
583193323Sedstatic void
584193323Sedtcp_remote_address(const void *ctx, char *addr, size_t size)
585193323Sed{
586193323Sed	const struct tcp_ctx *tctx = ctx;
587201360Srdivacky	struct sockaddr_storage sa;
588201360Srdivacky	socklen_t salen;
589210299Sed
590210299Sed	PJDLOG_ASSERT(tctx != NULL);
591210299Sed	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
592210299Sed
593193323Sed	salen = sizeof(sa);
594201360Srdivacky	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) {
595193323Sed		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
596193323Sed		return;
597193323Sed	}
598193323Sed	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
599201360Srdivacky}
600193323Sed
601210299Sedstatic void
602210299Sedtcp_close(void *ctx)
603210299Sed{
604210299Sed	struct tcp_ctx *tctx = ctx;
605210299Sed
606193323Sed	PJDLOG_ASSERT(tctx != NULL);
607201360Srdivacky	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
608193323Sed
609193323Sed	if (tctx->tc_fd >= 0)
610193323Sed		close(tctx->tc_fd);
611193323Sed	tctx->tc_magic = 0;
612201360Srdivacky	free(tctx);
613201360Srdivacky}
614210299Sed
615210299Sedstatic struct proto tcp_proto = {
616210299Sed	.prt_name = "tcp",
617210299Sed	.prt_client = tcp_client,
618193323Sed	.prt_connect = tcp_connect,
619201360Srdivacky	.prt_connect_wait = tcp_connect_wait,
620193323Sed	.prt_server = tcp_server,
621193323Sed	.prt_accept = tcp_accept,
622193323Sed	.prt_wrap = tcp_wrap,
623193323Sed	.prt_send = tcp_send,
624201360Srdivacky	.prt_recv = tcp_recv,
625201360Srdivacky	.prt_descriptor = tcp_descriptor,
626210299Sed	.prt_address_match = tcp_address_match,
627210299Sed	.prt_local_address = tcp_local_address,
628193323Sed	.prt_remote_address = tcp_remote_address,
629201360Srdivacky	.prt_close = tcp_close
630193323Sed};
631193323Sed
632193323Sedstatic __constructor void
633193323Sedtcp_ctor(void)
634201360Srdivacky{
635201360Srdivacky
636210299Sed	proto_register(&tcp_proto, true);
637210299Sed}
638193323Sed