proto_tcp.c revision 210876
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 210876 2010-08-05 18:27:41Z pjd $");
32204076Spjd
33204076Spjd#include <sys/param.h>	/* MAXHOSTNAMELEN */
34204076Spjd
35204076Spjd#include <netinet/in.h>
36204076Spjd#include <netinet/tcp.h>
37204076Spjd
38204076Spjd#include <assert.h>
39204076Spjd#include <errno.h>
40207390Spjd#include <fcntl.h>
41204076Spjd#include <netdb.h>
42204076Spjd#include <stdbool.h>
43204076Spjd#include <stdint.h>
44204076Spjd#include <stdio.h>
45204076Spjd#include <string.h>
46204076Spjd#include <unistd.h>
47204076Spjd
48204076Spjd#include "hast.h"
49204076Spjd#include "pjdlog.h"
50204076Spjd#include "proto_impl.h"
51207390Spjd#include "subr.h"
52204076Spjd
53204076Spjd#define	TCP4_CTX_MAGIC	0x7c441c
54204076Spjdstruct tcp4_ctx {
55204076Spjd	int			tc_magic;
56204076Spjd	struct sockaddr_in	tc_sin;
57204076Spjd	int			tc_fd;
58204076Spjd	int			tc_side;
59204076Spjd#define	TCP4_SIDE_CLIENT	0
60204076Spjd#define	TCP4_SIDE_SERVER_LISTEN	1
61204076Spjd#define	TCP4_SIDE_SERVER_WORK	2
62204076Spjd};
63204076Spjd
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
115204076Spjdtcp4_addr(const char *addr, 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. */
142204076Spjd		sinp->sin_port = htons(HASTD_PORT);
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);
159210876Spjd		if (strlcpy(iporhost, addr, size) >= size)
160210876Spjd			return (ENAMETOOLONG);
161204076Spjd	}
162204076Spjd	/* Convert string (IP address or host name) to in_addr_t. */
163204076Spjd	ip = str2ip(iporhost);
164204076Spjd	if (ip == INADDR_NONE)
165204076Spjd		return (EINVAL);
166204076Spjd	sinp->sin_addr.s_addr = ip;
167204076Spjd
168204076Spjd	return (0);
169204076Spjd}
170204076Spjd
171204076Spjdstatic int
172204076Spjdtcp4_common_setup(const char *addr, void **ctxp, int side)
173204076Spjd{
174204076Spjd	struct tcp4_ctx *tctx;
175204076Spjd	int ret, val;
176204076Spjd
177204076Spjd	tctx = malloc(sizeof(*tctx));
178204076Spjd	if (tctx == NULL)
179204076Spjd		return (errno);
180204076Spjd
181204076Spjd	/* Parse given address. */
182204076Spjd	if ((ret = tcp4_addr(addr, &tctx->tc_sin)) != 0) {
183204076Spjd		free(tctx);
184204076Spjd		return (ret);
185204076Spjd	}
186204076Spjd
187204076Spjd	tctx->tc_fd = socket(AF_INET, SOCK_STREAM, 0);
188204076Spjd	if (tctx->tc_fd == -1) {
189204076Spjd		ret = errno;
190204076Spjd		free(tctx);
191204076Spjd		return (ret);
192204076Spjd	}
193204076Spjd
194204076Spjd	/* Socket settings. */
195204076Spjd	val = 1;
196204076Spjd	if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &val,
197204076Spjd	    sizeof(val)) == -1) {
198204076Spjd		pjdlog_warning("Unable to set TCP_NOELAY on %s", addr);
199204076Spjd	}
200204076Spjd	val = 131072;
201204076Spjd	if (setsockopt(tctx->tc_fd, SOL_SOCKET, SO_SNDBUF, &val,
202204076Spjd	    sizeof(val)) == -1) {
203204076Spjd		pjdlog_warning("Unable to set send buffer size on %s", addr);
204204076Spjd	}
205204076Spjd	val = 131072;
206204076Spjd	if (setsockopt(tctx->tc_fd, SOL_SOCKET, SO_RCVBUF, &val,
207204076Spjd	    sizeof(val)) == -1) {
208204076Spjd		pjdlog_warning("Unable to set receive buffer size on %s", addr);
209204076Spjd	}
210204076Spjd
211204076Spjd	tctx->tc_side = side;
212204076Spjd	tctx->tc_magic = TCP4_CTX_MAGIC;
213204076Spjd	*ctxp = tctx;
214204076Spjd
215204076Spjd	return (0);
216204076Spjd}
217204076Spjd
218204076Spjdstatic int
219204076Spjdtcp4_client(const char *addr, void **ctxp)
220204076Spjd{
221204076Spjd
222204076Spjd	return (tcp4_common_setup(addr, ctxp, TCP4_SIDE_CLIENT));
223204076Spjd}
224204076Spjd
225204076Spjdstatic int
226204076Spjdtcp4_connect(void *ctx)
227204076Spjd{
228204076Spjd	struct tcp4_ctx *tctx = ctx;
229207390Spjd	struct timeval tv;
230207390Spjd	fd_set fdset;
231207390Spjd	socklen_t esize;
232207390Spjd	int error, flags, ret;
233204076Spjd
234204076Spjd	assert(tctx != NULL);
235204076Spjd	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
236204076Spjd	assert(tctx->tc_side == TCP4_SIDE_CLIENT);
237204076Spjd	assert(tctx->tc_fd >= 0);
238204076Spjd
239207390Spjd	flags = fcntl(tctx->tc_fd, F_GETFL);
240207390Spjd	if (flags == -1) {
241207390Spjd		KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno,
242207390Spjd		    "fcntl(F_GETFL) failed"));
243204076Spjd		return (errno);
244204076Spjd	}
245207390Spjd	/*
246207390Spjd	 * We make socket non-blocking so we have decided about connection
247207390Spjd	 * timeout.
248207390Spjd	 */
249207390Spjd	flags |= O_NONBLOCK;
250207390Spjd	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
251207390Spjd		KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno,
252207390Spjd		    "fcntl(F_SETFL, O_NONBLOCK) failed"));
253207390Spjd		return (errno);
254207390Spjd	}
255204076Spjd
256207390Spjd	if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
257207390Spjd	    sizeof(tctx->tc_sin)) == 0) {
258207390Spjd		error = 0;
259207390Spjd		goto done;
260207390Spjd	}
261207390Spjd	if (errno != EINPROGRESS) {
262207390Spjd		error = errno;
263207390Spjd		pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
264207390Spjd		goto done;
265207390Spjd	}
266207390Spjd	/*
267207390Spjd	 * Connection can't be established immediately, let's wait
268207390Spjd	 * for HAST_TIMEOUT seconds.
269207390Spjd	 */
270207390Spjd	tv.tv_sec = HAST_TIMEOUT;
271207390Spjd	tv.tv_usec = 0;
272207390Spjdagain:
273207390Spjd	FD_ZERO(&fdset);
274207390Spjd	FD_SET(tctx->tc_fd, &fdset);
275207390Spjd	ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
276207390Spjd	if (ret == 0) {
277207390Spjd		error = ETIMEDOUT;
278207390Spjd		goto done;
279207390Spjd	} else if (ret == -1) {
280207390Spjd		if (errno == EINTR)
281207390Spjd			goto again;
282207390Spjd		error = errno;
283207390Spjd		pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
284207390Spjd		goto done;
285207390Spjd	}
286207390Spjd	assert(ret > 0);
287207390Spjd	assert(FD_ISSET(tctx->tc_fd, &fdset));
288207390Spjd	esize = sizeof(error);
289207390Spjd	if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
290207390Spjd	    &esize) == -1) {
291207390Spjd		error = errno;
292207390Spjd		pjdlog_common(LOG_DEBUG, 1, errno,
293207390Spjd		    "getsockopt(SO_ERROR) failed");
294207390Spjd		goto done;
295207390Spjd	}
296207390Spjd	if (error != 0) {
297207390Spjd		pjdlog_common(LOG_DEBUG, 1, error,
298207390Spjd		    "getsockopt(SO_ERROR) returned error");
299207390Spjd		goto done;
300207390Spjd	}
301207390Spjd	error = 0;
302207390Spjddone:
303207390Spjd	flags &= ~O_NONBLOCK;
304207390Spjd	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
305207390Spjd		if (error == 0)
306207390Spjd			error = errno;
307207390Spjd		pjdlog_common(LOG_DEBUG, 1, errno,
308207390Spjd		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
309207390Spjd	}
310207390Spjd	return (error);
311204076Spjd}
312204076Spjd
313204076Spjdstatic int
314204076Spjdtcp4_server(const char *addr, void **ctxp)
315204076Spjd{
316204076Spjd	struct tcp4_ctx *tctx;
317204076Spjd	int ret, val;
318204076Spjd
319204076Spjd	ret = tcp4_common_setup(addr, ctxp, TCP4_SIDE_SERVER_LISTEN);
320204076Spjd	if (ret != 0)
321204076Spjd		return (ret);
322204076Spjd
323204076Spjd	tctx = *ctxp;
324204076Spjd
325204076Spjd	val = 1;
326204076Spjd	/* Ignore failure. */
327204076Spjd	(void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
328204076Spjd	   sizeof(val));
329204076Spjd
330204076Spjd	if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
331204076Spjd	    sizeof(tctx->tc_sin)) < 0) {
332204076Spjd		ret = errno;
333204076Spjd		tcp4_close(tctx);
334204076Spjd		return (ret);
335204076Spjd	}
336204076Spjd	if (listen(tctx->tc_fd, 8) < 0) {
337204076Spjd		ret = errno;
338204076Spjd		tcp4_close(tctx);
339204076Spjd		return (ret);
340204076Spjd	}
341204076Spjd
342204076Spjd	return (0);
343204076Spjd}
344204076Spjd
345204076Spjdstatic int
346204076Spjdtcp4_accept(void *ctx, void **newctxp)
347204076Spjd{
348204076Spjd	struct tcp4_ctx *tctx = ctx;
349204076Spjd	struct tcp4_ctx *newtctx;
350204076Spjd	socklen_t fromlen;
351204076Spjd	int ret;
352204076Spjd
353204076Spjd	assert(tctx != NULL);
354204076Spjd	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
355204076Spjd	assert(tctx->tc_side == TCP4_SIDE_SERVER_LISTEN);
356204076Spjd	assert(tctx->tc_fd >= 0);
357204076Spjd
358204076Spjd	newtctx = malloc(sizeof(*newtctx));
359204076Spjd	if (newtctx == NULL)
360204076Spjd		return (errno);
361204076Spjd
362204076Spjd	fromlen = sizeof(tctx->tc_sin);
363204076Spjd	newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
364204076Spjd	    &fromlen);
365204076Spjd	if (newtctx->tc_fd < 0) {
366204076Spjd		ret = errno;
367204076Spjd		free(newtctx);
368204076Spjd		return (ret);
369204076Spjd	}
370204076Spjd
371204076Spjd	newtctx->tc_side = TCP4_SIDE_SERVER_WORK;
372204076Spjd	newtctx->tc_magic = TCP4_CTX_MAGIC;
373204076Spjd	*newctxp = newtctx;
374204076Spjd
375204076Spjd	return (0);
376204076Spjd}
377204076Spjd
378204076Spjdstatic int
379204076Spjdtcp4_send(void *ctx, const unsigned char *data, size_t size)
380204076Spjd{
381204076Spjd	struct tcp4_ctx *tctx = ctx;
382204076Spjd
383204076Spjd	assert(tctx != NULL);
384204076Spjd	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
385204076Spjd	assert(tctx->tc_fd >= 0);
386204076Spjd
387204076Spjd	return (proto_common_send(tctx->tc_fd, data, size));
388204076Spjd}
389204076Spjd
390204076Spjdstatic int
391204076Spjdtcp4_recv(void *ctx, unsigned char *data, size_t size)
392204076Spjd{
393204076Spjd	struct tcp4_ctx *tctx = ctx;
394204076Spjd
395204076Spjd	assert(tctx != NULL);
396204076Spjd	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
397204076Spjd	assert(tctx->tc_fd >= 0);
398204076Spjd
399204076Spjd	return (proto_common_recv(tctx->tc_fd, data, size));
400204076Spjd}
401204076Spjd
402204076Spjdstatic int
403204076Spjdtcp4_descriptor(const void *ctx)
404204076Spjd{
405204076Spjd	const struct tcp4_ctx *tctx = ctx;
406204076Spjd
407204076Spjd	assert(tctx != NULL);
408204076Spjd	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
409204076Spjd
410204076Spjd	return (tctx->tc_fd);
411204076Spjd}
412204076Spjd
413204076Spjdstatic void
414204076Spjdsin2str(struct sockaddr_in *sinp, char *addr, size_t size)
415204076Spjd{
416204076Spjd	in_addr_t ip;
417204076Spjd	unsigned int port;
418204076Spjd
419204076Spjd	assert(addr != NULL);
420204076Spjd	assert(sinp->sin_family == AF_INET);
421204076Spjd
422204076Spjd	ip = ntohl(sinp->sin_addr.s_addr);
423204076Spjd	port = ntohs(sinp->sin_port);
424210876Spjd	PJDLOG_VERIFY(snprintf(addr, size, "tcp4://%u.%u.%u.%u:%u",
425210876Spjd	    ((ip >> 24) & 0xff), ((ip >> 16) & 0xff), ((ip >> 8) & 0xff),
426210876Spjd	    (ip & 0xff), port) < (ssize_t)size);
427204076Spjd}
428204076Spjd
429204076Spjdstatic bool
430204076Spjdtcp4_address_match(const void *ctx, const char *addr)
431204076Spjd{
432204076Spjd	const struct tcp4_ctx *tctx = ctx;
433204076Spjd	struct sockaddr_in sin;
434204076Spjd	socklen_t sinlen;
435204076Spjd	in_addr_t ip1, ip2;
436204076Spjd
437204076Spjd	assert(tctx != NULL);
438204076Spjd	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
439204076Spjd
440204076Spjd	if (tcp4_addr(addr, &sin) != 0)
441204076Spjd		return (false);
442204076Spjd	ip1 = sin.sin_addr.s_addr;
443204076Spjd
444204076Spjd	sinlen = sizeof(sin);
445204076Spjd	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0)
446204076Spjd		return (false);
447204076Spjd	ip2 = sin.sin_addr.s_addr;
448204076Spjd
449204076Spjd	return (ip1 == ip2);
450204076Spjd}
451204076Spjd
452204076Spjdstatic void
453204076Spjdtcp4_local_address(const void *ctx, char *addr, size_t size)
454204076Spjd{
455204076Spjd	const struct tcp4_ctx *tctx = ctx;
456204076Spjd	struct sockaddr_in sin;
457204076Spjd	socklen_t sinlen;
458204076Spjd
459204076Spjd	assert(tctx != NULL);
460204076Spjd	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
461204076Spjd
462204076Spjd	sinlen = sizeof(sin);
463204076Spjd	if (getsockname(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
464210876Spjd		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
465204076Spjd		return;
466204076Spjd	}
467204076Spjd	sin2str(&sin, addr, size);
468204076Spjd}
469204076Spjd
470204076Spjdstatic void
471204076Spjdtcp4_remote_address(const void *ctx, char *addr, size_t size)
472204076Spjd{
473204076Spjd	const struct tcp4_ctx *tctx = ctx;
474204076Spjd	struct sockaddr_in sin;
475204076Spjd	socklen_t sinlen;
476204076Spjd
477204076Spjd	assert(tctx != NULL);
478204076Spjd	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
479204076Spjd
480204076Spjd	sinlen = sizeof(sin);
481204076Spjd	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
482210876Spjd		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
483204076Spjd		return;
484204076Spjd	}
485204076Spjd	sin2str(&sin, addr, size);
486204076Spjd}
487204076Spjd
488204076Spjdstatic void
489204076Spjdtcp4_close(void *ctx)
490204076Spjd{
491204076Spjd	struct tcp4_ctx *tctx = ctx;
492204076Spjd
493204076Spjd	assert(tctx != NULL);
494204076Spjd	assert(tctx->tc_magic == TCP4_CTX_MAGIC);
495204076Spjd
496204076Spjd	if (tctx->tc_fd >= 0)
497204076Spjd		close(tctx->tc_fd);
498204076Spjd	tctx->tc_magic = 0;
499204076Spjd	free(tctx);
500204076Spjd}
501204076Spjd
502204076Spjdstatic struct hast_proto tcp4_proto = {
503204076Spjd	.hp_name = "tcp4",
504204076Spjd	.hp_client = tcp4_client,
505204076Spjd	.hp_connect = tcp4_connect,
506204076Spjd	.hp_server = tcp4_server,
507204076Spjd	.hp_accept = tcp4_accept,
508204076Spjd	.hp_send = tcp4_send,
509204076Spjd	.hp_recv = tcp4_recv,
510204076Spjd	.hp_descriptor = tcp4_descriptor,
511204076Spjd	.hp_address_match = tcp4_address_match,
512204076Spjd	.hp_local_address = tcp4_local_address,
513204076Spjd	.hp_remote_address = tcp4_remote_address,
514204076Spjd	.hp_close = tcp4_close
515204076Spjd};
516204076Spjd
517204076Spjdstatic __constructor void
518204076Spjdtcp4_ctor(void)
519204076Spjd{
520204076Spjd
521210869Spjd	proto_register(&tcp4_proto, true);
522204076Spjd}
523