1204076Spjd/*-
2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation
3222118Spjd * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
4204076Spjd * All rights reserved.
5204076Spjd *
6204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from
7204076Spjd * the FreeBSD Foundation.
8204076Spjd *
9204076Spjd * Redistribution and use in source and binary forms, with or without
10204076Spjd * modification, are permitted provided that the following conditions
11204076Spjd * are met:
12204076Spjd * 1. Redistributions of source code must retain the above copyright
13204076Spjd *    notice, this list of conditions and the following disclaimer.
14204076Spjd * 2. Redistributions in binary form must reproduce the above copyright
15204076Spjd *    notice, this list of conditions and the following disclaimer in the
16204076Spjd *    documentation and/or other materials provided with the distribution.
17204076Spjd *
18204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21204076Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28204076Spjd * SUCH DAMAGE.
29204076Spjd */
30204076Spjd
31204076Spjd#include <sys/cdefs.h>
32204076Spjd__FBSDID("$FreeBSD$");
33204076Spjd
34204076Spjd#include <sys/param.h>	/* MAXHOSTNAMELEN */
35219873Spjd#include <sys/socket.h>
36204076Spjd
37219873Spjd#include <arpa/inet.h>
38219873Spjd
39204076Spjd#include <netinet/in.h>
40204076Spjd#include <netinet/tcp.h>
41204076Spjd
42204076Spjd#include <errno.h>
43207390Spjd#include <fcntl.h>
44204076Spjd#include <netdb.h>
45204076Spjd#include <stdbool.h>
46204076Spjd#include <stdint.h>
47204076Spjd#include <stdio.h>
48204076Spjd#include <string.h>
49204076Spjd#include <unistd.h>
50204076Spjd
51204076Spjd#include "pjdlog.h"
52204076Spjd#include "proto_impl.h"
53207390Spjd#include "subr.h"
54204076Spjd
55222118Spjd#define	TCP_CTX_MAGIC	0x7c41c
56222116Spjdstruct tcp_ctx {
57204076Spjd	int			tc_magic;
58222118Spjd	struct sockaddr_storage	tc_sa;
59204076Spjd	int			tc_fd;
60204076Spjd	int			tc_side;
61222116Spjd#define	TCP_SIDE_CLIENT		0
62222116Spjd#define	TCP_SIDE_SERVER_LISTEN	1
63222116Spjd#define	TCP_SIDE_SERVER_WORK	2
64204076Spjd};
65204076Spjd
66222116Spjdstatic int tcp_connect_wait(void *ctx, int timeout);
67222116Spjdstatic void tcp_close(void *ctx);
68204076Spjd
69204076Spjd/*
70204076Spjd * Function converts the given string to unsigned number.
71204076Spjd */
72204076Spjdstatic int
73204076Spjdnumfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump)
74204076Spjd{
75204076Spjd	intmax_t digit, num;
76204076Spjd
77204076Spjd	if (str[0] == '\0')
78204076Spjd		goto invalid;	/* Empty string. */
79204076Spjd	num = 0;
80204076Spjd	for (; *str != '\0'; str++) {
81204076Spjd		if (*str < '0' || *str > '9')
82204076Spjd			goto invalid;	/* Non-digit character. */
83204076Spjd		digit = *str - '0';
84204076Spjd		if (num > num * 10 + digit)
85204076Spjd			goto invalid;	/* Overflow. */
86204076Spjd		num = num * 10 + digit;
87204076Spjd		if (num > maxnum)
88204076Spjd			goto invalid;	/* Too big. */
89204076Spjd	}
90204076Spjd	if (num < minnum)
91204076Spjd		goto invalid;	/* Too small. */
92204076Spjd	*nump = num;
93204076Spjd	return (0);
94204076Spjdinvalid:
95204076Spjd	errno = EINVAL;
96204076Spjd	return (-1);
97204076Spjd}
98204076Spjd
99204076Spjdstatic int
100222118Spjdtcp_addr(const char *addr, int defport, struct sockaddr_storage *sap)
101204076Spjd{
102222118Spjd	char iporhost[MAXHOSTNAMELEN], portstr[6];
103222118Spjd	struct addrinfo hints;
104222118Spjd	struct addrinfo *res;
105204076Spjd	const char *pp;
106222118Spjd	intmax_t port;
107204076Spjd	size_t size;
108222118Spjd	int error;
109204076Spjd
110204076Spjd	if (addr == NULL)
111204076Spjd		return (-1);
112204076Spjd
113222118Spjd	bzero(&hints, sizeof(hints));
114222118Spjd	hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
115222118Spjd	hints.ai_family = PF_UNSPEC;
116222118Spjd	hints.ai_socktype = SOCK_STREAM;
117222118Spjd	hints.ai_protocol = IPPROTO_TCP;
118222118Spjd
119222118Spjd	if (strncasecmp(addr, "tcp4://", 7) == 0) {
120204076Spjd		addr += 7;
121222118Spjd		hints.ai_family = PF_INET;
122222118Spjd	} else if (strncasecmp(addr, "tcp6://", 7) == 0) {
123222118Spjd		addr += 7;
124222118Spjd		hints.ai_family = PF_INET6;
125222118Spjd	} else if (strncasecmp(addr, "tcp://", 6) == 0) {
126204076Spjd		addr += 6;
127222118Spjd	} else {
128210870Spjd		/*
129222116Spjd		 * Because TCP is the default assume IP or host is given without
130210870Spjd		 * prefix.
131210870Spjd		 */
132210870Spjd	}
133204076Spjd
134222118Spjd	/*
135222118Spjd	 * Extract optional port.
136222118Spjd	 * There are three cases to consider.
137222118Spjd	 * 1. hostname with port, eg. freefall.freebsd.org:8457
138222118Spjd	 * 2. IPv4 address with port, eg. 192.168.0.101:8457
139222118Spjd	 * 3. IPv6 address with port, eg. [fe80::1]:8457
140222118Spjd	 * We discover IPv6 address by checking for two colons and if port is
141222118Spjd	 * given, the address has to start with [.
142222118Spjd	 */
143222118Spjd	pp = NULL;
144222118Spjd	if (strchr(addr, ':') != strrchr(addr, ':')) {
145222118Spjd		if (addr[0] == '[')
146222118Spjd			pp = strrchr(addr, ':');
147222118Spjd	} else {
148222118Spjd		pp = strrchr(addr, ':');
149222118Spjd	}
150204076Spjd	if (pp == NULL) {
151204076Spjd		/* Port not given, use the default. */
152222118Spjd		port = defport;
153204076Spjd	} else {
154231017Strociny		if (numfromstr(pp + 1, 1, 65535, &port) == -1)
155204076Spjd			return (errno);
156204076Spjd	}
157222118Spjd	(void)snprintf(portstr, sizeof(portstr), "%jd", (intmax_t)port);
158204076Spjd	/* Extract host name or IP address. */
159204076Spjd	if (pp == NULL) {
160204076Spjd		size = sizeof(iporhost);
161204076Spjd		if (strlcpy(iporhost, addr, size) >= size)
162204076Spjd			return (ENAMETOOLONG);
163222118Spjd	} else if (addr[0] == '[' && pp[-1] == ']') {
164222118Spjd		size = (size_t)(pp - addr - 2 + 1);
165222118Spjd		if (size > sizeof(iporhost))
166222118Spjd			return (ENAMETOOLONG);
167222118Spjd		(void)strlcpy(iporhost, addr + 1, size);
168204076Spjd	} else {
169204076Spjd		size = (size_t)(pp - addr + 1);
170204076Spjd		if (size > sizeof(iporhost))
171204076Spjd			return (ENAMETOOLONG);
172211407Spjd		(void)strlcpy(iporhost, addr, size);
173204076Spjd	}
174222118Spjd
175222118Spjd	error = getaddrinfo(iporhost, portstr, &hints, &res);
176222118Spjd	if (error != 0) {
177222118Spjd		pjdlog_debug(1, "getaddrinfo(%s, %s) failed: %s.", iporhost,
178222118Spjd		    portstr, gai_strerror(error));
179204076Spjd		return (EINVAL);
180222118Spjd	}
181222118Spjd	if (res == NULL)
182222118Spjd		return (ENOENT);
183204076Spjd
184222118Spjd	memcpy(sap, res->ai_addr, res->ai_addrlen);
185222118Spjd
186222118Spjd	freeaddrinfo(res);
187222118Spjd
188204076Spjd	return (0);
189204076Spjd}
190204076Spjd
191204076Spjdstatic int
192222116Spjdtcp_setup_new(const char *addr, int side, void **ctxp)
193204076Spjd{
194222116Spjd	struct tcp_ctx *tctx;
195218158Spjd	int ret, nodelay;
196204076Spjd
197218194Spjd	PJDLOG_ASSERT(addr != NULL);
198222116Spjd	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
199222116Spjd	    side == TCP_SIDE_SERVER_LISTEN);
200218194Spjd	PJDLOG_ASSERT(ctxp != NULL);
201218194Spjd
202204076Spjd	tctx = malloc(sizeof(*tctx));
203204076Spjd	if (tctx == NULL)
204204076Spjd		return (errno);
205204076Spjd
206204076Spjd	/* Parse given address. */
207222118Spjd	if ((ret = tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &tctx->tc_sa)) != 0) {
208204076Spjd		free(tctx);
209204076Spjd		return (ret);
210204076Spjd	}
211204076Spjd
212222118Spjd	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
213218194Spjd
214222118Spjd	tctx->tc_fd = socket(tctx->tc_sa.ss_family, SOCK_STREAM, 0);
215204076Spjd	if (tctx->tc_fd == -1) {
216204076Spjd		ret = errno;
217204076Spjd		free(tctx);
218204076Spjd		return (ret);
219204076Spjd	}
220204076Spjd
221222118Spjd	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
222219818Spjd
223204076Spjd	/* Socket settings. */
224218158Spjd	nodelay = 1;
225218158Spjd	if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay,
226218158Spjd	    sizeof(nodelay)) == -1) {
227218194Spjd		pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY");
228204076Spjd	}
229204076Spjd
230204076Spjd	tctx->tc_side = side;
231222116Spjd	tctx->tc_magic = TCP_CTX_MAGIC;
232204076Spjd	*ctxp = tctx;
233204076Spjd
234204076Spjd	return (0);
235204076Spjd}
236204076Spjd
237204076Spjdstatic int
238222116Spjdtcp_setup_wrap(int fd, int side, void **ctxp)
239218194Spjd{
240222116Spjd	struct tcp_ctx *tctx;
241218194Spjd
242218194Spjd	PJDLOG_ASSERT(fd >= 0);
243222116Spjd	PJDLOG_ASSERT(side == TCP_SIDE_CLIENT ||
244222116Spjd	    side == TCP_SIDE_SERVER_WORK);
245218194Spjd	PJDLOG_ASSERT(ctxp != NULL);
246218194Spjd
247218194Spjd	tctx = malloc(sizeof(*tctx));
248218194Spjd	if (tctx == NULL)
249218194Spjd		return (errno);
250218194Spjd
251218194Spjd	tctx->tc_fd = fd;
252222118Spjd	tctx->tc_sa.ss_family = AF_UNSPEC;
253218194Spjd	tctx->tc_side = side;
254222116Spjd	tctx->tc_magic = TCP_CTX_MAGIC;
255218194Spjd	*ctxp = tctx;
256218194Spjd
257218194Spjd	return (0);
258218194Spjd}
259218194Spjd
260218194Spjdstatic int
261222116Spjdtcp_client(const char *srcaddr, const char *dstaddr, void **ctxp)
262204076Spjd{
263222116Spjd	struct tcp_ctx *tctx;
264222118Spjd	struct sockaddr_storage sa;
265219818Spjd	int ret;
266204076Spjd
267222116Spjd	ret = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, ctxp);
268219818Spjd	if (ret != 0)
269219818Spjd		return (ret);
270219818Spjd	tctx = *ctxp;
271219818Spjd	if (srcaddr == NULL)
272219818Spjd		return (0);
273222118Spjd	ret = tcp_addr(srcaddr, 0, &sa);
274219818Spjd	if (ret != 0) {
275222116Spjd		tcp_close(tctx);
276219818Spjd		return (ret);
277219818Spjd	}
278231017Strociny	if (bind(tctx->tc_fd, (struct sockaddr *)&sa, sa.ss_len) == -1) {
279219818Spjd		ret = errno;
280222116Spjd		tcp_close(tctx);
281219818Spjd		return (ret);
282219818Spjd	}
283219818Spjd	return (0);
284204076Spjd}
285204076Spjd
286204076Spjdstatic int
287222116Spjdtcp_connect(void *ctx, int timeout)
288204076Spjd{
289222116Spjd	struct tcp_ctx *tctx = ctx;
290218193Spjd	int error, flags;
291204076Spjd
292218138Spjd	PJDLOG_ASSERT(tctx != NULL);
293222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
294222116Spjd	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
295218138Spjd	PJDLOG_ASSERT(tctx->tc_fd >= 0);
296222118Spjd	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
297218193Spjd	PJDLOG_ASSERT(timeout >= -1);
298204076Spjd
299207390Spjd	flags = fcntl(tctx->tc_fd, F_GETFL);
300207390Spjd	if (flags == -1) {
301229509Strociny		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
302204076Spjd		return (errno);
303204076Spjd	}
304207390Spjd	/*
305211875Spjd	 * We make socket non-blocking so we can handle connection timeout
306211875Spjd	 * manually.
307207390Spjd	 */
308207390Spjd	flags |= O_NONBLOCK;
309207390Spjd	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
310229509Strociny		pjdlog_common(LOG_DEBUG, 1, errno,
311229509Strociny		    "fcntl(F_SETFL, O_NONBLOCK) failed");
312207390Spjd		return (errno);
313207390Spjd	}
314204076Spjd
315222118Spjd	if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
316222118Spjd	    tctx->tc_sa.ss_len) == 0) {
317218193Spjd		if (timeout == -1)
318218193Spjd			return (0);
319207390Spjd		error = 0;
320207390Spjd		goto done;
321207390Spjd	}
322207390Spjd	if (errno != EINPROGRESS) {
323207390Spjd		error = errno;
324207390Spjd		pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
325207390Spjd		goto done;
326207390Spjd	}
327218193Spjd	if (timeout == -1)
328218193Spjd		return (0);
329222116Spjd	return (tcp_connect_wait(ctx, timeout));
330218193Spjddone:
331218193Spjd	flags &= ~O_NONBLOCK;
332218193Spjd	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
333218193Spjd		if (error == 0)
334218193Spjd			error = errno;
335218193Spjd		pjdlog_common(LOG_DEBUG, 1, errno,
336218193Spjd		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
337218193Spjd	}
338218193Spjd	return (error);
339218193Spjd}
340218193Spjd
341218193Spjdstatic int
342222116Spjdtcp_connect_wait(void *ctx, int timeout)
343218193Spjd{
344222116Spjd	struct tcp_ctx *tctx = ctx;
345218193Spjd	struct timeval tv;
346218193Spjd	fd_set fdset;
347218193Spjd	socklen_t esize;
348218193Spjd	int error, flags, ret;
349218193Spjd
350218193Spjd	PJDLOG_ASSERT(tctx != NULL);
351222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
352222116Spjd	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT);
353218193Spjd	PJDLOG_ASSERT(tctx->tc_fd >= 0);
354218193Spjd	PJDLOG_ASSERT(timeout >= 0);
355218193Spjd
356218192Spjd	tv.tv_sec = timeout;
357207390Spjd	tv.tv_usec = 0;
358207390Spjdagain:
359207390Spjd	FD_ZERO(&fdset);
360219864Spjd	FD_SET(tctx->tc_fd, &fdset);
361207390Spjd	ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
362207390Spjd	if (ret == 0) {
363207390Spjd		error = ETIMEDOUT;
364207390Spjd		goto done;
365207390Spjd	} else if (ret == -1) {
366207390Spjd		if (errno == EINTR)
367207390Spjd			goto again;
368207390Spjd		error = errno;
369207390Spjd		pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
370207390Spjd		goto done;
371207390Spjd	}
372218138Spjd	PJDLOG_ASSERT(ret > 0);
373218138Spjd	PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset));
374207390Spjd	esize = sizeof(error);
375207390Spjd	if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
376207390Spjd	    &esize) == -1) {
377207390Spjd		error = errno;
378207390Spjd		pjdlog_common(LOG_DEBUG, 1, errno,
379207390Spjd		    "getsockopt(SO_ERROR) failed");
380207390Spjd		goto done;
381207390Spjd	}
382207390Spjd	if (error != 0) {
383207390Spjd		pjdlog_common(LOG_DEBUG, 1, error,
384207390Spjd		    "getsockopt(SO_ERROR) returned error");
385207390Spjd		goto done;
386207390Spjd	}
387207390Spjd	error = 0;
388207390Spjddone:
389218193Spjd	flags = fcntl(tctx->tc_fd, F_GETFL);
390218193Spjd	if (flags == -1) {
391218193Spjd		if (error == 0)
392218193Spjd			error = errno;
393218193Spjd		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
394218193Spjd		return (error);
395218193Spjd	}
396207390Spjd	flags &= ~O_NONBLOCK;
397207390Spjd	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
398207390Spjd		if (error == 0)
399207390Spjd			error = errno;
400207390Spjd		pjdlog_common(LOG_DEBUG, 1, errno,
401207390Spjd		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
402207390Spjd	}
403207390Spjd	return (error);
404204076Spjd}
405204076Spjd
406204076Spjdstatic int
407222116Spjdtcp_server(const char *addr, void **ctxp)
408204076Spjd{
409222116Spjd	struct tcp_ctx *tctx;
410204076Spjd	int ret, val;
411204076Spjd
412222116Spjd	ret = tcp_setup_new(addr, TCP_SIDE_SERVER_LISTEN, ctxp);
413204076Spjd	if (ret != 0)
414204076Spjd		return (ret);
415204076Spjd
416204076Spjd	tctx = *ctxp;
417204076Spjd
418204076Spjd	val = 1;
419204076Spjd	/* Ignore failure. */
420204076Spjd	(void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
421204076Spjd	   sizeof(val));
422204076Spjd
423222118Spjd	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
424218194Spjd
425222118Spjd	if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
426231017Strociny	    tctx->tc_sa.ss_len) == -1) {
427204076Spjd		ret = errno;
428222116Spjd		tcp_close(tctx);
429204076Spjd		return (ret);
430204076Spjd	}
431231017Strociny	if (listen(tctx->tc_fd, 8) == -1) {
432204076Spjd		ret = errno;
433222116Spjd		tcp_close(tctx);
434204076Spjd		return (ret);
435204076Spjd	}
436204076Spjd
437204076Spjd	return (0);
438204076Spjd}
439204076Spjd
440204076Spjdstatic int
441222116Spjdtcp_accept(void *ctx, void **newctxp)
442204076Spjd{
443222116Spjd	struct tcp_ctx *tctx = ctx;
444222116Spjd	struct tcp_ctx *newtctx;
445204076Spjd	socklen_t fromlen;
446204076Spjd	int ret;
447204076Spjd
448218138Spjd	PJDLOG_ASSERT(tctx != NULL);
449222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
450222116Spjd	PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN);
451218138Spjd	PJDLOG_ASSERT(tctx->tc_fd >= 0);
452222118Spjd	PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC);
453204076Spjd
454204076Spjd	newtctx = malloc(sizeof(*newtctx));
455204076Spjd	if (newtctx == NULL)
456204076Spjd		return (errno);
457204076Spjd
458222118Spjd	fromlen = tctx->tc_sa.ss_len;
459222118Spjd	newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa,
460204076Spjd	    &fromlen);
461231017Strociny	if (newtctx->tc_fd == -1) {
462204076Spjd		ret = errno;
463204076Spjd		free(newtctx);
464204076Spjd		return (ret);
465204076Spjd	}
466204076Spjd
467222116Spjd	newtctx->tc_side = TCP_SIDE_SERVER_WORK;
468222116Spjd	newtctx->tc_magic = TCP_CTX_MAGIC;
469204076Spjd	*newctxp = newtctx;
470204076Spjd
471204076Spjd	return (0);
472204076Spjd}
473204076Spjd
474204076Spjdstatic int
475222116Spjdtcp_wrap(int fd, bool client, void **ctxp)
476204076Spjd{
477218194Spjd
478222116Spjd	return (tcp_setup_wrap(fd,
479222116Spjd	    client ? TCP_SIDE_CLIENT : TCP_SIDE_SERVER_WORK, ctxp));
480218194Spjd}
481218194Spjd
482218194Spjdstatic int
483222116Spjdtcp_send(void *ctx, const unsigned char *data, size_t size, int fd)
484218194Spjd{
485222116Spjd	struct tcp_ctx *tctx = ctx;
486204076Spjd
487218138Spjd	PJDLOG_ASSERT(tctx != NULL);
488222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
489218138Spjd	PJDLOG_ASSERT(tctx->tc_fd >= 0);
490218194Spjd	PJDLOG_ASSERT(fd == -1);
491204076Spjd
492218194Spjd	return (proto_common_send(tctx->tc_fd, data, size, -1));
493204076Spjd}
494204076Spjd
495204076Spjdstatic int
496222116Spjdtcp_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
497204076Spjd{
498222116Spjd	struct tcp_ctx *tctx = ctx;
499204076Spjd
500218138Spjd	PJDLOG_ASSERT(tctx != NULL);
501222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
502218138Spjd	PJDLOG_ASSERT(tctx->tc_fd >= 0);
503218194Spjd	PJDLOG_ASSERT(fdp == NULL);
504204076Spjd
505218194Spjd	return (proto_common_recv(tctx->tc_fd, data, size, NULL));
506204076Spjd}
507204076Spjd
508204076Spjdstatic int
509222116Spjdtcp_descriptor(const void *ctx)
510204076Spjd{
511222116Spjd	const struct tcp_ctx *tctx = ctx;
512204076Spjd
513218138Spjd	PJDLOG_ASSERT(tctx != NULL);
514222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
515204076Spjd
516204076Spjd	return (tctx->tc_fd);
517204076Spjd}
518204076Spjd
519204076Spjdstatic bool
520222116Spjdtcp_address_match(const void *ctx, const char *addr)
521204076Spjd{
522222116Spjd	const struct tcp_ctx *tctx = ctx;
523222118Spjd	struct sockaddr_storage sa1, sa2;
524222118Spjd	socklen_t salen;
525204076Spjd
526218138Spjd	PJDLOG_ASSERT(tctx != NULL);
527222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
528204076Spjd
529222118Spjd	if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sa1) != 0)
530204076Spjd		return (false);
531204076Spjd
532222118Spjd	salen = sizeof(sa2);
533231017Strociny	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) == -1)
534204076Spjd		return (false);
535204076Spjd
536222118Spjd	if (sa1.ss_family != sa2.ss_family || sa1.ss_len != sa2.ss_len)
537222118Spjd		return (false);
538222118Spjd
539222118Spjd	switch (sa1.ss_family) {
540222118Spjd	case AF_INET:
541222118Spjd	    {
542222118Spjd		struct sockaddr_in *sin1, *sin2;
543222118Spjd
544222118Spjd		sin1 = (struct sockaddr_in *)&sa1;
545222118Spjd		sin2 = (struct sockaddr_in *)&sa2;
546222118Spjd
547222118Spjd		return (memcmp(&sin1->sin_addr, &sin2->sin_addr,
548222118Spjd		    sizeof(sin1->sin_addr)) == 0);
549222118Spjd	    }
550222118Spjd	case AF_INET6:
551222118Spjd	    {
552222118Spjd		struct sockaddr_in6 *sin1, *sin2;
553222118Spjd
554222118Spjd		sin1 = (struct sockaddr_in6 *)&sa1;
555222118Spjd		sin2 = (struct sockaddr_in6 *)&sa2;
556222118Spjd
557222118Spjd		return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
558222118Spjd		    sizeof(sin1->sin6_addr)) == 0);
559222118Spjd	    }
560222118Spjd	default:
561222118Spjd		return (false);
562222118Spjd	}
563204076Spjd}
564204076Spjd
565204076Spjdstatic void
566222116Spjdtcp_local_address(const void *ctx, char *addr, size_t size)
567204076Spjd{
568222116Spjd	const struct tcp_ctx *tctx = ctx;
569222118Spjd	struct sockaddr_storage sa;
570222118Spjd	socklen_t salen;
571204076Spjd
572218138Spjd	PJDLOG_ASSERT(tctx != NULL);
573222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
574204076Spjd
575222118Spjd	salen = sizeof(sa);
576231017Strociny	if (getsockname(tctx->tc_fd, (struct sockaddr *)&sa, &salen) == -1) {
577210876Spjd		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
578204076Spjd		return;
579204076Spjd	}
580222118Spjd	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
581204076Spjd}
582204076Spjd
583204076Spjdstatic void
584222116Spjdtcp_remote_address(const void *ctx, char *addr, size_t size)
585204076Spjd{
586222116Spjd	const struct tcp_ctx *tctx = ctx;
587222118Spjd	struct sockaddr_storage sa;
588222118Spjd	socklen_t salen;
589204076Spjd
590218138Spjd	PJDLOG_ASSERT(tctx != NULL);
591222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
592204076Spjd
593222118Spjd	salen = sizeof(sa);
594231017Strociny	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) == -1) {
595210876Spjd		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
596204076Spjd		return;
597204076Spjd	}
598222118Spjd	PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size);
599204076Spjd}
600204076Spjd
601204076Spjdstatic void
602222116Spjdtcp_close(void *ctx)
603204076Spjd{
604222116Spjd	struct tcp_ctx *tctx = ctx;
605204076Spjd
606218138Spjd	PJDLOG_ASSERT(tctx != NULL);
607222116Spjd	PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC);
608204076Spjd
609204076Spjd	if (tctx->tc_fd >= 0)
610204076Spjd		close(tctx->tc_fd);
611204076Spjd	tctx->tc_magic = 0;
612204076Spjd	free(tctx);
613204076Spjd}
614204076Spjd
615222116Spjdstatic struct proto tcp_proto = {
616222116Spjd	.prt_name = "tcp",
617222116Spjd	.prt_client = tcp_client,
618222116Spjd	.prt_connect = tcp_connect,
619222116Spjd	.prt_connect_wait = tcp_connect_wait,
620222116Spjd	.prt_server = tcp_server,
621222116Spjd	.prt_accept = tcp_accept,
622222116Spjd	.prt_wrap = tcp_wrap,
623222116Spjd	.prt_send = tcp_send,
624222116Spjd	.prt_recv = tcp_recv,
625222116Spjd	.prt_descriptor = tcp_descriptor,
626222116Spjd	.prt_address_match = tcp_address_match,
627222116Spjd	.prt_local_address = tcp_local_address,
628222116Spjd	.prt_remote_address = tcp_remote_address,
629222116Spjd	.prt_close = tcp_close
630204076Spjd};
631204076Spjd
632204076Spjdstatic __constructor void
633222116Spjdtcp_ctor(void)
634204076Spjd{
635204076Spjd
636222116Spjd	proto_register(&tcp_proto, true);
637204076Spjd}
638