proto_tcp.c revision 218193
1161748Scperciva/*-
2161748Scperciva * Copyright (c) 2009-2010 The FreeBSD Foundation
3161748Scperciva * All rights reserved.
4173441Scperciva *
5161748Scperciva * This software was developed by Pawel Jakub Dawidek under sponsorship from
6161748Scperciva * the FreeBSD Foundation.
7161748Scperciva *
8161748Scperciva * Redistribution and use in source and binary forms, with or without
9161748Scperciva * modification, are permitted provided that the following conditions
10161748Scperciva * are met:
11161748Scperciva * 1. Redistributions of source code must retain the above copyright
12161748Scperciva *    notice, this list of conditions and the following disclaimer.
13161748Scperciva * 2. Redistributions in binary form must reproduce the above copyright
14161748Scperciva *    notice, this list of conditions and the following disclaimer in the
15161748Scperciva *    documentation and/or other materials provided with the distribution.
16161748Scperciva *
17161748Scperciva * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18161748Scperciva * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19161748Scperciva * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20161748Scperciva * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21161748Scperciva * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22161748Scperciva * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23161748Scperciva * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24161748Scperciva * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25161748Scperciva * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26161748Scperciva * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27161748Scperciva * SUCH DAMAGE.
28161748Scperciva */
29161748Scperciva
30161748Scperciva#include <sys/cdefs.h>
31161748Scperciva__FBSDID("$FreeBSD: head/sbin/hastd/proto_tcp4.c 218193 2011-02-02 15:46:28Z pjd $");
32161748Scperciva
33161748Scperciva#include <sys/param.h>	/* MAXHOSTNAMELEN */
34161748Scperciva
35161748Scperciva#include <netinet/in.h>
36161748Scperciva#include <netinet/tcp.h>
37161748Scperciva
38161748Scperciva#include <errno.h>
39161748Scperciva#include <fcntl.h>
40161748Scperciva#include <netdb.h>
41161748Scperciva#include <stdbool.h>
42161748Scperciva#include <stdint.h>
43161748Scperciva#include <stdio.h>
44161748Scperciva#include <string.h>
45161748Scperciva#include <unistd.h>
46161748Scperciva
47173564Scperciva#include "hast.h"
48161748Scperciva#include "pjdlog.h"
49161748Scperciva#include "proto_impl.h"
50161748Scperciva#include "subr.h"
51161748Scperciva
52161748Scperciva#define	TCP4_CTX_MAGIC	0x7c441c
53161748Scpercivastruct tcp4_ctx {
54161748Scperciva	int			tc_magic;
55161748Scperciva	struct sockaddr_in	tc_sin;
56173564Scperciva	int			tc_fd;
57173564Scperciva	int			tc_side;
58161748Scperciva#define	TCP4_SIDE_CLIENT	0
59181142Scperciva#define	TCP4_SIDE_SERVER_LISTEN	1
60161748Scperciva#define	TCP4_SIDE_SERVER_WORK	2
61161748Scperciva};
62161748Scperciva
63161748Scpercivastatic int tcp4_connect_wait(void *ctx, int timeout);
64161748Scpercivastatic void tcp4_close(void *ctx);
65161748Scperciva
66161748Scpercivastatic in_addr_t
67161748Scpercivastr2ip(const char *str)
68161748Scperciva{
69161748Scperciva	struct hostent *hp;
70161748Scperciva	in_addr_t ip;
71161748Scperciva
72161748Scperciva	ip = inet_addr(str);
73161748Scperciva	if (ip != INADDR_NONE) {
74161748Scperciva		/* It is a valid IP address. */
75161748Scperciva		return (ip);
76161748Scperciva	}
77161748Scperciva	/* Check if it is a valid host name. */
78161748Scperciva	hp = gethostbyname(str);
79161748Scperciva	if (hp == NULL)
80161748Scperciva		return (INADDR_NONE);
81161748Scperciva	return (((struct in_addr *)(void *)hp->h_addr)->s_addr);
82161748Scperciva}
83161748Scperciva
84161748Scperciva/*
85161748Scperciva * Function converts the given string to unsigned number.
86161748Scperciva */
87161748Scpercivastatic int
88161748Scpercivanumfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump)
89161748Scperciva{
90181142Scperciva	intmax_t digit, num;
91196392Ssimon
92161748Scperciva	if (str[0] == '\0')
93161748Scperciva		goto invalid;	/* Empty string. */
94161748Scperciva	num = 0;
95161748Scperciva	for (; *str != '\0'; str++) {
96161748Scperciva		if (*str < '0' || *str > '9')
97161748Scperciva			goto invalid;	/* Non-digit character. */
98161748Scperciva		digit = *str - '0';
99161748Scperciva		if (num > num * 10 + digit)
100161748Scperciva			goto invalid;	/* Overflow. */
101161748Scperciva		num = num * 10 + digit;
102161748Scperciva		if (num > maxnum)
103161748Scperciva			goto invalid;	/* Too big. */
104161748Scperciva	}
105161748Scperciva	if (num < minnum)
106161748Scperciva		goto invalid;	/* Too small. */
107161748Scperciva	*nump = num;
108161748Scperciva	return (0);
109161748Scpercivainvalid:
110161748Scperciva	errno = EINVAL;
111161748Scperciva	return (-1);
112161748Scperciva}
113161748Scperciva
114161748Scpercivastatic int
115161748Scpercivatcp4_addr(const char *addr, struct sockaddr_in *sinp)
116161748Scperciva{
117161748Scperciva	char iporhost[MAXHOSTNAMELEN];
118161748Scperciva	const char *pp;
119161748Scperciva	size_t size;
120161748Scperciva	in_addr_t ip;
121161748Scperciva
122161748Scperciva	if (addr == NULL)
123161748Scperciva		return (-1);
124161748Scperciva
125161748Scperciva	if (strncasecmp(addr, "tcp4://", 7) == 0)
126161748Scperciva		addr += 7;
127161748Scperciva	else if (strncasecmp(addr, "tcp://", 6) == 0)
128161748Scperciva		addr += 6;
129161748Scperciva	else {
130161748Scperciva		/*
131161748Scperciva		 * Because TCP4 is the default assume IP or host is given without
132161748Scperciva		 * prefix.
133161748Scperciva		 */
134161748Scperciva	}
135161748Scperciva
136161748Scperciva	sinp->sin_family = AF_INET;
137161748Scperciva	sinp->sin_len = sizeof(*sinp);
138161748Scperciva	/* Extract optional port. */
139161748Scperciva	pp = strrchr(addr, ':');
140161748Scperciva	if (pp == NULL) {
141161748Scperciva		/* Port not given, use the default. */
142161748Scperciva		sinp->sin_port = htons(HASTD_PORT);
143161748Scperciva	} else {
144161748Scperciva		intmax_t port;
145161748Scperciva
146161748Scperciva		if (numfromstr(pp + 1, 1, 65535, &port) < 0)
147161748Scperciva			return (errno);
148161748Scperciva		sinp->sin_port = htons(port);
149161748Scperciva	}
150161748Scperciva	/* Extract host name or IP address. */
151161748Scperciva	if (pp == NULL) {
152161748Scperciva		size = sizeof(iporhost);
153161748Scperciva		if (strlcpy(iporhost, addr, size) >= size)
154161748Scperciva			return (ENAMETOOLONG);
155161748Scperciva	} else {
156161748Scperciva		size = (size_t)(pp - addr + 1);
157161748Scperciva		if (size > sizeof(iporhost))
158161748Scperciva			return (ENAMETOOLONG);
159161748Scperciva		(void)strlcpy(iporhost, addr, size);
160161748Scperciva	}
161161748Scperciva	/* Convert string (IP address or host name) to in_addr_t. */
162161748Scperciva	ip = str2ip(iporhost);
163161748Scperciva	if (ip == INADDR_NONE)
164161748Scperciva		return (EINVAL);
165161748Scperciva	sinp->sin_addr.s_addr = ip;
166161748Scperciva
167161748Scperciva	return (0);
168161748Scperciva}
169161748Scperciva
170161748Scpercivastatic int
171161748Scpercivatcp4_common_setup(const char *addr, void **ctxp, int side)
172161748Scperciva{
173161748Scperciva	struct tcp4_ctx *tctx;
174161748Scperciva	int ret, nodelay;
175161748Scperciva
176161748Scperciva	tctx = malloc(sizeof(*tctx));
177161748Scperciva	if (tctx == NULL)
178161748Scperciva		return (errno);
179161748Scperciva
180161748Scperciva	/* Parse given address. */
181161748Scperciva	if ((ret = tcp4_addr(addr, &tctx->tc_sin)) != 0) {
182161748Scperciva		free(tctx);
183161748Scperciva		return (ret);
184161748Scperciva	}
185161748Scperciva
186161748Scperciva	tctx->tc_fd = socket(AF_INET, SOCK_STREAM, 0);
187161748Scperciva	if (tctx->tc_fd == -1) {
188161748Scperciva		ret = errno;
189161748Scperciva		free(tctx);
190161748Scperciva		return (ret);
191161748Scperciva	}
192161748Scperciva
193161748Scperciva	/* Socket settings. */
194161748Scperciva	nodelay = 1;
195161748Scperciva	if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay,
196161748Scperciva	    sizeof(nodelay)) == -1) {
197161748Scperciva		pjdlog_warning("Unable to set TCP_NOELAY on %s", addr);
198161748Scperciva	}
199161748Scperciva
200161748Scperciva	tctx->tc_side = side;
201161748Scperciva	tctx->tc_magic = TCP4_CTX_MAGIC;
202161748Scperciva	*ctxp = tctx;
203161748Scperciva
204161748Scperciva	return (0);
205161748Scperciva}
206161748Scperciva
207161748Scpercivastatic int
208161748Scpercivatcp4_client(const char *addr, void **ctxp)
209161748Scperciva{
210161748Scperciva
211161748Scperciva	return (tcp4_common_setup(addr, ctxp, TCP4_SIDE_CLIENT));
212161748Scperciva}
213161748Scperciva
214161748Scpercivastatic int
215161748Scpercivatcp4_connect(void *ctx, int timeout)
216161748Scperciva{
217161748Scperciva	struct tcp4_ctx *tctx = ctx;
218161748Scperciva	int error, flags;
219161748Scperciva
220161748Scperciva	PJDLOG_ASSERT(tctx != NULL);
221161748Scperciva	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
222161748Scperciva	PJDLOG_ASSERT(tctx->tc_side == TCP4_SIDE_CLIENT);
223161748Scperciva	PJDLOG_ASSERT(tctx->tc_fd >= 0);
224161748Scperciva	PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC);
225161748Scperciva	PJDLOG_ASSERT(timeout >= -1);
226161748Scperciva
227181142Scperciva	flags = fcntl(tctx->tc_fd, F_GETFL);
228181142Scperciva	if (flags == -1) {
229181142Scperciva		KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno,
230181142Scperciva		    "fcntl(F_GETFL) failed"));
231181142Scperciva		return (errno);
232181142Scperciva	}
233181142Scperciva	/*
234161748Scperciva	 * We make socket non-blocking so we can handle connection timeout
235161748Scperciva	 * manually.
236161748Scperciva	 */
237161748Scperciva	flags |= O_NONBLOCK;
238161748Scperciva	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
239161748Scperciva		KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno,
240161748Scperciva		    "fcntl(F_SETFL, O_NONBLOCK) failed"));
241161748Scperciva		return (errno);
242173564Scperciva	}
243173564Scperciva
244173564Scperciva	if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
245173564Scperciva	    sizeof(tctx->tc_sin)) == 0) {
246173564Scperciva		if (timeout == -1)
247173564Scperciva			return (0);
248173564Scperciva		error = 0;
249173564Scperciva		goto done;
250161748Scperciva	}
251161748Scperciva	if (errno != EINPROGRESS) {
252161748Scperciva		error = errno;
253161748Scperciva		pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
254161748Scperciva		goto done;
255161748Scperciva	}
256161748Scperciva	if (timeout == -1)
257161748Scperciva		return (0);
258161748Scperciva	return (tcp4_connect_wait(ctx, timeout));
259173564Scpercivadone:
260173564Scperciva	flags &= ~O_NONBLOCK;
261173564Scperciva	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
262173564Scperciva		if (error == 0)
263173564Scperciva			error = errno;
264173564Scperciva		pjdlog_common(LOG_DEBUG, 1, errno,
265173564Scperciva		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
266173564Scperciva	}
267173564Scperciva	return (error);
268173564Scperciva}
269173564Scperciva
270173564Scpercivastatic int
271173564Scpercivatcp4_connect_wait(void *ctx, int timeout)
272173564Scperciva{
273173564Scperciva	struct tcp4_ctx *tctx = ctx;
274173564Scperciva	struct timeval tv;
275173564Scperciva	fd_set fdset;
276173564Scperciva	socklen_t esize;
277173564Scperciva	int error, flags, ret;
278173564Scperciva
279173564Scperciva	PJDLOG_ASSERT(tctx != NULL);
280173564Scperciva	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
281173564Scperciva	PJDLOG_ASSERT(tctx->tc_side == TCP4_SIDE_CLIENT);
282173564Scperciva	PJDLOG_ASSERT(tctx->tc_fd >= 0);
283173564Scperciva	PJDLOG_ASSERT(timeout >= 0);
284173564Scperciva
285173564Scperciva	tv.tv_sec = timeout;
286173564Scperciva	tv.tv_usec = 0;
287197618Scpercivaagain:
288197618Scperciva	FD_ZERO(&fdset);
289197618Scperciva	FD_SET(tctx->tc_fd, &fdset);
290173564Scperciva	ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
291173564Scperciva	if (ret == 0) {
292161748Scperciva		error = ETIMEDOUT;
293161748Scperciva		goto done;
294161748Scperciva	} else if (ret == -1) {
295161748Scperciva		if (errno == EINTR)
296161748Scperciva			goto again;
297161748Scperciva		error = errno;
298161748Scperciva		pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
299161748Scperciva		goto done;
300161748Scperciva	}
301161748Scperciva	PJDLOG_ASSERT(ret > 0);
302161748Scperciva	PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset));
303161748Scperciva	esize = sizeof(error);
304161748Scperciva	if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
305161748Scperciva	    &esize) == -1) {
306161748Scperciva		error = errno;
307161748Scperciva		pjdlog_common(LOG_DEBUG, 1, errno,
308161748Scperciva		    "getsockopt(SO_ERROR) failed");
309161748Scperciva		goto done;
310161748Scperciva	}
311161748Scperciva	if (error != 0) {
312161748Scperciva		pjdlog_common(LOG_DEBUG, 1, error,
313161748Scperciva		    "getsockopt(SO_ERROR) returned error");
314196392Ssimon		goto done;
315196392Ssimon	}
316196392Ssimon	error = 0;
317196392Ssimondone:
318196392Ssimon	flags = fcntl(tctx->tc_fd, F_GETFL);
319196392Ssimon	if (flags == -1) {
320196392Ssimon		if (error == 0)
321196392Ssimon			error = errno;
322196392Ssimon		pjdlog_common(LOG_DEBUG, 1, errno, "fcntl(F_GETFL) failed");
323196392Ssimon		return (error);
324196392Ssimon	}
325196392Ssimon	flags &= ~O_NONBLOCK;
326196392Ssimon	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
327196392Ssimon		if (error == 0)
328196392Ssimon			error = errno;
329196392Ssimon		pjdlog_common(LOG_DEBUG, 1, errno,
330196392Ssimon		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
331196392Ssimon	}
332196392Ssimon	return (error);
333196392Ssimon}
334196392Ssimon
335196392Ssimonstatic int
336196392Ssimontcp4_server(const char *addr, void **ctxp)
337196392Ssimon{
338196392Ssimon	struct tcp4_ctx *tctx;
339196392Ssimon	int ret, val;
340196392Ssimon
341196392Ssimon	ret = tcp4_common_setup(addr, ctxp, TCP4_SIDE_SERVER_LISTEN);
342196392Ssimon	if (ret != 0)
343196392Ssimon		return (ret);
344196392Ssimon
345196392Ssimon	tctx = *ctxp;
346196392Ssimon
347196392Ssimon	val = 1;
348196392Ssimon	/* Ignore failure. */
349196392Ssimon	(void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
350196392Ssimon	   sizeof(val));
351196392Ssimon
352196392Ssimon	if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
353196392Ssimon	    sizeof(tctx->tc_sin)) < 0) {
354196392Ssimon		ret = errno;
355196392Ssimon		tcp4_close(tctx);
356196392Ssimon		return (ret);
357196392Ssimon	}
358196392Ssimon	if (listen(tctx->tc_fd, 8) < 0) {
359196392Ssimon		ret = errno;
360196392Ssimon		tcp4_close(tctx);
361196392Ssimon		return (ret);
362196392Ssimon	}
363196392Ssimon
364196392Ssimon	return (0);
365196392Ssimon}
366196392Ssimon
367196392Ssimonstatic int
368196392Ssimontcp4_accept(void *ctx, void **newctxp)
369196392Ssimon{
370196392Ssimon	struct tcp4_ctx *tctx = ctx;
371196392Ssimon	struct tcp4_ctx *newtctx;
372196392Ssimon	socklen_t fromlen;
373196392Ssimon	int ret;
374196392Ssimon
375196392Ssimon	PJDLOG_ASSERT(tctx != NULL);
376196392Ssimon	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
377196392Ssimon	PJDLOG_ASSERT(tctx->tc_side == TCP4_SIDE_SERVER_LISTEN);
378161748Scperciva	PJDLOG_ASSERT(tctx->tc_fd >= 0);
379161748Scperciva
380161748Scperciva	newtctx = malloc(sizeof(*newtctx));
381161748Scperciva	if (newtctx == NULL)
382161748Scperciva		return (errno);
383161748Scperciva
384161748Scperciva	fromlen = sizeof(tctx->tc_sin);
385161748Scperciva	newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
386161748Scperciva	    &fromlen);
387161748Scperciva	if (newtctx->tc_fd < 0) {
388161748Scperciva		ret = errno;
389161748Scperciva		free(newtctx);
390161748Scperciva		return (ret);
391161748Scperciva	}
392161748Scperciva
393161748Scperciva	newtctx->tc_side = TCP4_SIDE_SERVER_WORK;
394161748Scperciva	newtctx->tc_magic = TCP4_CTX_MAGIC;
395161748Scperciva	*newctxp = newtctx;
396161748Scperciva
397161748Scperciva	return (0);
398161748Scperciva}
399161748Scperciva
400161748Scpercivastatic int
401161748Scpercivatcp4_send(void *ctx, const unsigned char *data, size_t size)
402161748Scperciva{
403161748Scperciva	struct tcp4_ctx *tctx = ctx;
404161748Scperciva
405161748Scperciva	PJDLOG_ASSERT(tctx != NULL);
406161748Scperciva	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
407161748Scperciva	PJDLOG_ASSERT(tctx->tc_fd >= 0);
408161748Scperciva
409161748Scperciva	return (proto_common_send(tctx->tc_fd, data, size));
410161748Scperciva}
411161748Scperciva
412161748Scpercivastatic int
413161748Scpercivatcp4_recv(void *ctx, unsigned char *data, size_t size)
414161748Scperciva{
415161748Scperciva	struct tcp4_ctx *tctx = ctx;
416161748Scperciva
417161748Scperciva	PJDLOG_ASSERT(tctx != NULL);
418161748Scperciva	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
419161748Scperciva	PJDLOG_ASSERT(tctx->tc_fd >= 0);
420161748Scperciva
421161748Scperciva	return (proto_common_recv(tctx->tc_fd, data, size));
422161748Scperciva}
423161748Scperciva
424161748Scpercivastatic int
425161748Scpercivatcp4_descriptor(const void *ctx)
426161748Scperciva{
427161748Scperciva	const struct tcp4_ctx *tctx = ctx;
428161748Scperciva
429161748Scperciva	PJDLOG_ASSERT(tctx != NULL);
430161748Scperciva	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
431161748Scperciva
432173564Scperciva	return (tctx->tc_fd);
433173564Scperciva}
434173564Scperciva
435173564Scpercivastatic void
436161748Scpercivasin2str(struct sockaddr_in *sinp, char *addr, size_t size)
437161748Scperciva{
438161748Scperciva	in_addr_t ip;
439161748Scperciva	unsigned int port;
440161748Scperciva
441161748Scperciva	PJDLOG_ASSERT(addr != NULL);
442161748Scperciva	PJDLOG_ASSERT(sinp->sin_family == AF_INET);
443161748Scperciva
444161748Scperciva	ip = ntohl(sinp->sin_addr.s_addr);
445161748Scperciva	port = ntohs(sinp->sin_port);
446161748Scperciva	PJDLOG_VERIFY(snprintf(addr, size, "tcp4://%u.%u.%u.%u:%u",
447161748Scperciva	    ((ip >> 24) & 0xff), ((ip >> 16) & 0xff), ((ip >> 8) & 0xff),
448161748Scperciva	    (ip & 0xff), port) < (ssize_t)size);
449161748Scperciva}
450161748Scperciva
451161748Scpercivastatic bool
452161748Scpercivatcp4_address_match(const void *ctx, const char *addr)
453161748Scperciva{
454181142Scperciva	const struct tcp4_ctx *tctx = ctx;
455161748Scperciva	struct sockaddr_in sin;
456161748Scperciva	socklen_t sinlen;
457161748Scperciva	in_addr_t ip1, ip2;
458161748Scperciva
459161748Scperciva	PJDLOG_ASSERT(tctx != NULL);
460161748Scperciva	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
461161748Scperciva
462161748Scperciva	if (tcp4_addr(addr, &sin) != 0)
463161748Scperciva		return (false);
464161748Scperciva	ip1 = sin.sin_addr.s_addr;
465161748Scperciva
466161748Scperciva	sinlen = sizeof(sin);
467161748Scperciva	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0)
468161748Scperciva		return (false);
469161748Scperciva	ip2 = sin.sin_addr.s_addr;
470161748Scperciva
471161748Scperciva	return (ip1 == ip2);
472161748Scperciva}
473161748Scperciva
474161748Scpercivastatic void
475161748Scpercivatcp4_local_address(const void *ctx, char *addr, size_t size)
476161748Scperciva{
477161748Scperciva	const struct tcp4_ctx *tctx = ctx;
478161748Scperciva	struct sockaddr_in sin;
479161748Scperciva	socklen_t sinlen;
480161748Scperciva
481161748Scperciva	PJDLOG_ASSERT(tctx != NULL);
482161748Scperciva	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
483161748Scperciva
484161748Scperciva	sinlen = sizeof(sin);
485161748Scperciva	if (getsockname(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
486161748Scperciva		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
487161748Scperciva		return;
488161748Scperciva	}
489161748Scperciva	sin2str(&sin, addr, size);
490161748Scperciva}
491161748Scperciva
492161748Scpercivastatic void
493161748Scpercivatcp4_remote_address(const void *ctx, char *addr, size_t size)
494161748Scperciva{
495161748Scperciva	const struct tcp4_ctx *tctx = ctx;
496161748Scperciva	struct sockaddr_in sin;
497161748Scperciva	socklen_t sinlen;
498161748Scperciva
499161748Scperciva	PJDLOG_ASSERT(tctx != NULL);
500161748Scperciva	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
501161748Scperciva
502161748Scperciva	sinlen = sizeof(sin);
503161748Scperciva	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
504161748Scperciva		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
505161748Scperciva		return;
506161748Scperciva	}
507161748Scperciva	sin2str(&sin, addr, size);
508161748Scperciva}
509161748Scperciva
510161748Scpercivastatic void
511161748Scpercivatcp4_close(void *ctx)
512161748Scperciva{
513161748Scperciva	struct tcp4_ctx *tctx = ctx;
514161748Scperciva
515161748Scperciva	PJDLOG_ASSERT(tctx != NULL);
516161748Scperciva	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
517161748Scperciva
518161748Scperciva	if (tctx->tc_fd >= 0)
519161748Scperciva		close(tctx->tc_fd);
520161748Scperciva	tctx->tc_magic = 0;
521161748Scperciva	free(tctx);
522161748Scperciva}
523161748Scperciva
524161748Scpercivastatic struct hast_proto tcp4_proto = {
525161748Scperciva	.hp_name = "tcp4",
526161748Scperciva	.hp_client = tcp4_client,
527161748Scperciva	.hp_connect = tcp4_connect,
528161748Scperciva	.hp_connect_wait = tcp4_connect_wait,
529161748Scperciva	.hp_server = tcp4_server,
530173564Scperciva	.hp_accept = tcp4_accept,
531196392Ssimon	.hp_send = tcp4_send,
532196392Ssimon	.hp_recv = tcp4_recv,
533196392Ssimon	.hp_descriptor = tcp4_descriptor,
534161748Scperciva	.hp_address_match = tcp4_address_match,
535161748Scperciva	.hp_local_address = tcp4_local_address,
536161748Scperciva	.hp_remote_address = tcp4_remote_address,
537161748Scperciva	.hp_close = tcp4_close
538161748Scperciva};
539161748Scperciva
540161748Scpercivastatic __constructor void
541161748Scpercivatcp4_ctor(void)
542161748Scperciva{
543161748Scperciva
544161748Scperciva	proto_register(&tcp4_proto, true);
545161748Scperciva}
546161748Scperciva