proto_tcp.c revision 218192
1185089Sraj/*-
2209131Sraj * Copyright (c) 2009-2010 The FreeBSD Foundation
3209131Sraj * All rights reserved.
4240489Sgber *
5185089Sraj * This software was developed by Pawel Jakub Dawidek under sponsorship from
6185089Sraj * the FreeBSD Foundation.
7185089Sraj *
8185089Sraj * Redistribution and use in source and binary forms, with or without
9209131Sraj * modification, are permitted provided that the following conditions
10209131Sraj * are met:
11209131Sraj * 1. Redistributions of source code must retain the above copyright
12185089Sraj *    notice, this list of conditions and the following disclaimer.
13185089Sraj * 2. Redistributions in binary form must reproduce the above copyright
14185089Sraj *    notice, this list of conditions and the following disclaimer in the
15185089Sraj *    documentation and/or other materials provided with the distribution.
16185089Sraj *
17185089Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18185089Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19185089Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20185089Sraj * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21185089Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22185089Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23185089Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24185089Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25185089Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26185089Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27185089Sraj * SUCH DAMAGE.
28185089Sraj */
29185089Sraj
30185089Sraj#include <sys/cdefs.h>
31185089Sraj__FBSDID("$FreeBSD: head/sbin/hastd/proto_tcp4.c 218192 2011-02-02 15:42:00Z pjd $");
32185089Sraj
33185089Sraj#include <sys/param.h>	/* MAXHOSTNAMELEN */
34185089Sraj
35185089Sraj#include <netinet/in.h>
36185089Sraj#include <netinet/tcp.h>
37185089Sraj
38185089Sraj#include <errno.h>
39185089Sraj#include <fcntl.h>
40185089Sraj#include <netdb.h>
41185089Sraj#include <stdbool.h>
42185089Sraj#include <stdint.h>
43185089Sraj#include <stdio.h>
44185089Sraj#include <string.h>
45185089Sraj#include <unistd.h>
46185089Sraj
47185089Sraj#include "hast.h"
48185089Sraj#include "pjdlog.h"
49185089Sraj#include "proto_impl.h"
50185089Sraj#include "subr.h"
51185089Sraj
52185089Sraj#define	TCP4_CTX_MAGIC	0x7c441c
53185089Srajstruct tcp4_ctx {
54185089Sraj	int			tc_magic;
55185089Sraj	struct sockaddr_in	tc_sin;
56260327Snwhitehorn	int			tc_fd;
57240493Sgber	int			tc_side;
58240493Sgber#define	TCP4_SIDE_CLIENT	0
59185089Sraj#define	TCP4_SIDE_SERVER_LISTEN	1
60185089Sraj#define	TCP4_SIDE_SERVER_WORK	2
61185089Sraj};
62209131Sraj
63209131Srajstatic void tcp4_close(void *ctx);
64259484Snwhitehorn
65209131Srajstatic in_addr_t
66185089Srajstr2ip(const char *str)
67185089Sraj{
68185089Sraj	struct hostent *hp;
69185089Sraj	in_addr_t ip;
70209131Sraj
71185089Sraj	ip = inet_addr(str);
72185089Sraj	if (ip != INADDR_NONE) {
73260340Sian		/* It is a valid IP address. */
74185089Sraj		return (ip);
75185089Sraj	}
76185089Sraj	/* Check if it is a valid host name. */
77185089Sraj	hp = gethostbyname(str);
78185089Sraj	if (hp == NULL)
79209131Sraj		return (INADDR_NONE);
80185089Sraj	return (((struct in_addr *)(void *)hp->h_addr)->s_addr);
81240493Sgber}
82240493Sgber
83240493Sgber/*
84240493Sgber * Function converts the given string to unsigned number.
85240493Sgber */
86240493Sgberstatic int
87260340Siannumfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump)
88260340Sian{
89260340Sian	intmax_t digit, num;
90260340Sian
91260340Sian	if (str[0] == '\0')
92260340Sian		goto invalid;	/* Empty string. */
93260340Sian	num = 0;
94260340Sian	for (; *str != '\0'; str++) {
95260340Sian		if (*str < '0' || *str > '9')
96260340Sian			goto invalid;	/* Non-digit character. */
97260340Sian		digit = *str - '0';
98260340Sian		if (num > num * 10 + digit)
99260340Sian			goto invalid;	/* Overflow. */
100260340Sian		num = num * 10 + digit;
101260340Sian		if (num > maxnum)
102260340Sian			goto invalid;	/* Too big. */
103260340Sian	}
104260340Sian	if (num < minnum)
105260340Sian		goto invalid;	/* Too small. */
106260340Sian	*nump = num;
107260340Sian	return (0);
108260340Sianinvalid:
109260340Sian	errno = EINVAL;
110260340Sian	return (-1);
111260340Sian}
112260340Sian
113260340Sianstatic int
114260340Siantcp4_addr(const char *addr, struct sockaddr_in *sinp)
115260340Sian{
116260340Sian	char iporhost[MAXHOSTNAMELEN];
117260340Sian	const char *pp;
118260340Sian	size_t size;
119260340Sian	in_addr_t ip;
120260340Sian
121260340Sian	if (addr == NULL)
122260340Sian		return (-1);
123260340Sian
124260340Sian	if (strncasecmp(addr, "tcp4://", 7) == 0)
125260340Sian		addr += 7;
126260340Sian	else if (strncasecmp(addr, "tcp://", 6) == 0)
127260340Sian		addr += 6;
128260340Sian	else {
129260340Sian		/*
130260340Sian		 * Because TCP4 is the default assume IP or host is given without
131260340Sian		 * prefix.
132260340Sian		 */
133260340Sian	}
134260340Sian
135260340Sian	sinp->sin_family = AF_INET;
136260340Sian	sinp->sin_len = sizeof(*sinp);
137260340Sian	/* Extract optional port. */
138260340Sian	pp = strrchr(addr, ':');
139260340Sian	if (pp == NULL) {
140260340Sian		/* Port not given, use the default. */
141260340Sian		sinp->sin_port = htons(HASTD_PORT);
142260340Sian	} else {
143260340Sian		intmax_t port;
144260340Sian
145260340Sian		if (numfromstr(pp + 1, 1, 65535, &port) < 0)
146260340Sian			return (errno);
147260340Sian		sinp->sin_port = htons(port);
148260340Sian	}
149260340Sian	/* Extract host name or IP address. */
150260340Sian	if (pp == NULL) {
151260340Sian		size = sizeof(iporhost);
152260340Sian		if (strlcpy(iporhost, addr, size) >= size)
153260340Sian			return (ENAMETOOLONG);
154260340Sian	} else {
155260340Sian		size = (size_t)(pp - addr + 1);
156260340Sian		if (size > sizeof(iporhost))
157260340Sian			return (ENAMETOOLONG);
158260340Sian		(void)strlcpy(iporhost, addr, size);
159260340Sian	}
160260340Sian	/* Convert string (IP address or host name) to in_addr_t. */
161260340Sian	ip = str2ip(iporhost);
162260340Sian	if (ip == INADDR_NONE)
163260340Sian		return (EINVAL);
164260340Sian	sinp->sin_addr.s_addr = ip;
165260340Sian
166260340Sian	return (0);
167260340Sian}
168260340Sian
169260340Sianstatic int
170260340Siantcp4_common_setup(const char *addr, void **ctxp, int side)
171260340Sian{
172260340Sian	struct tcp4_ctx *tctx;
173260340Sian	int ret, nodelay;
174260340Sian
175260340Sian	tctx = malloc(sizeof(*tctx));
176260340Sian	if (tctx == NULL)
177260340Sian		return (errno);
178260340Sian
179260340Sian	/* Parse given address. */
180260340Sian	if ((ret = tcp4_addr(addr, &tctx->tc_sin)) != 0) {
181260340Sian		free(tctx);
182260340Sian		return (ret);
183260340Sian	}
184260340Sian
185260340Sian	tctx->tc_fd = socket(AF_INET, SOCK_STREAM, 0);
186260340Sian	if (tctx->tc_fd == -1) {
187260340Sian		ret = errno;
188260340Sian		free(tctx);
189260340Sian		return (ret);
190260340Sian	}
191260340Sian
192260340Sian	/* Socket settings. */
193260340Sian	nodelay = 1;
194260340Sian	if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay,
195260340Sian	    sizeof(nodelay)) == -1) {
196260340Sian		pjdlog_warning("Unable to set TCP_NOELAY on %s", addr);
197260340Sian	}
198260340Sian
199260340Sian	tctx->tc_side = side;
200260340Sian	tctx->tc_magic = TCP4_CTX_MAGIC;
201260340Sian	*ctxp = tctx;
202260340Sian
203260340Sian	return (0);
204260340Sian}
205260340Sian
206260340Sianstatic int
207260340Siantcp4_client(const char *addr, void **ctxp)
208260340Sian{
209260340Sian
210260340Sian	return (tcp4_common_setup(addr, ctxp, TCP4_SIDE_CLIENT));
211260340Sian}
212260340Sian
213260340Sianstatic int
214260340Siantcp4_connect(void *ctx, int timeout)
215260340Sian{
216260340Sian	struct tcp4_ctx *tctx = ctx;
217260340Sian	struct timeval tv;
218260340Sian	fd_set fdset;
219260340Sian	socklen_t esize;
220260340Sian	int error, flags, ret;
221260340Sian
222260340Sian	PJDLOG_ASSERT(tctx != NULL);
223260340Sian	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
224260340Sian	PJDLOG_ASSERT(tctx->tc_side == TCP4_SIDE_CLIENT);
225260340Sian	PJDLOG_ASSERT(tctx->tc_fd >= 0);
226260340Sian	PJDLOG_ASSERT(timeout >= 0);
227260340Sian
228260340Sian	flags = fcntl(tctx->tc_fd, F_GETFL);
229260340Sian	if (flags == -1) {
230260340Sian		KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno,
231260340Sian		    "fcntl(F_GETFL) failed"));
232260340Sian		return (errno);
233260340Sian	}
234260340Sian	/*
235260340Sian	 * We make socket non-blocking so we can handle connection timeout
236260340Sian	 * manually.
237260340Sian	 */
238265852Sian	flags |= O_NONBLOCK;
239260340Sian	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
240260340Sian		KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno,
241260340Sian		    "fcntl(F_SETFL, O_NONBLOCK) failed"));
242260340Sian		return (errno);
243260340Sian	}
244260340Sian
245265852Sian	if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
246260340Sian	    sizeof(tctx->tc_sin)) == 0) {
247260340Sian		error = 0;
248260340Sian		goto done;
249260340Sian	}
250260340Sian	if (errno != EINPROGRESS) {
251260340Sian		error = errno;
252260340Sian		pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
253258780Seadler		goto done;
254185089Sraj	}
255185089Sraj	/*
256185089Sraj	 * Connection can't be established immediately, let's wait
257185089Sraj	 * for HAST_TIMEOUT seconds.
258185089Sraj	 */
259185089Sraj	tv.tv_sec = timeout;
260185089Sraj	tv.tv_usec = 0;
261185089Srajagain:
262185089Sraj	FD_ZERO(&fdset);
263185089Sraj	FD_SET(tctx->tc_fd, &fdset);
264185089Sraj	ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
265185089Sraj	if (ret == 0) {
266185089Sraj		error = ETIMEDOUT;
267185089Sraj		goto done;
268185089Sraj	} else if (ret == -1) {
269240489Sgber		if (errno == EINTR)
270240489Sgber			goto again;
271185089Sraj		error = errno;
272240489Sgber		pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
273185089Sraj		goto done;
274240489Sgber	}
275240489Sgber	PJDLOG_ASSERT(ret > 0);
276185089Sraj	PJDLOG_ASSERT(FD_ISSET(tctx->tc_fd, &fdset));
277240489Sgber	esize = sizeof(error);
278240489Sgber	if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
279240489Sgber	    &esize) == -1) {
280240489Sgber		error = errno;
281240489Sgber		pjdlog_common(LOG_DEBUG, 1, errno,
282240489Sgber		    "getsockopt(SO_ERROR) failed");
283209131Sraj		goto done;
284185089Sraj	}
285185089Sraj	if (error != 0) {
286209131Sraj		pjdlog_common(LOG_DEBUG, 1, error,
287209131Sraj		    "getsockopt(SO_ERROR) returned error");
288209131Sraj		goto done;
289240489Sgber	}
290240489Sgber	error = 0;
291240489Sgberdone:
292209131Sraj	flags &= ~O_NONBLOCK;
293185089Sraj	if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
294209131Sraj		if (error == 0)
295209131Sraj			error = errno;
296209131Sraj		pjdlog_common(LOG_DEBUG, 1, errno,
297240489Sgber		    "fcntl(F_SETFL, ~O_NONBLOCK) failed");
298240489Sgber	}
299209131Sraj	return (error);
300185089Sraj}
301185089Sraj
302185089Srajstatic int
303185089Srajtcp4_server(const char *addr, void **ctxp)
304185089Sraj{
305185089Sraj	struct tcp4_ctx *tctx;
306240493Sgber	int ret, val;
307240493Sgber
308240493Sgber	ret = tcp4_common_setup(addr, ctxp, TCP4_SIDE_SERVER_LISTEN);
309185089Sraj	if (ret != 0)
310185089Sraj		return (ret);
311209131Sraj
312240489Sgber	tctx = *ctxp;
313185089Sraj
314259484Snwhitehorn	val = 1;
315185089Sraj	/* Ignore failure. */
316185089Sraj	(void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
317209131Sraj	   sizeof(val));
318209131Sraj
319209131Sraj	if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
320209131Sraj	    sizeof(tctx->tc_sin)) < 0) {
321209131Sraj		ret = errno;
322209131Sraj		tcp4_close(tctx);
323209131Sraj		return (ret);
324209131Sraj	}
325209131Sraj	if (listen(tctx->tc_fd, 8) < 0) {
326209131Sraj		ret = errno;
327209131Sraj		tcp4_close(tctx);
328240489Sgber		return (ret);
329240489Sgber	}
330185089Sraj
331209131Sraj	return (0);
332209131Sraj}
333209131Sraj
334209131Srajstatic int
335209131Srajtcp4_accept(void *ctx, void **newctxp)
336185089Sraj{
337209131Sraj	struct tcp4_ctx *tctx = ctx;
338185089Sraj	struct tcp4_ctx *newtctx;
339209131Sraj	socklen_t fromlen;
340209131Sraj	int ret;
341185089Sraj
342209131Sraj	PJDLOG_ASSERT(tctx != NULL);
343209131Sraj	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
344209131Sraj	PJDLOG_ASSERT(tctx->tc_side == TCP4_SIDE_SERVER_LISTEN);
345185089Sraj	PJDLOG_ASSERT(tctx->tc_fd >= 0);
346209131Sraj
347240493Sgber	newtctx = malloc(sizeof(*newtctx));
348240493Sgber	if (newtctx == NULL)
349240493Sgber		return (errno);
350240493Sgber
351240493Sgber	fromlen = sizeof(tctx->tc_sin);
352185089Sraj	newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
353185089Sraj	    &fromlen);
354185089Sraj	if (newtctx->tc_fd < 0) {
355185089Sraj		ret = errno;
356209131Sraj		free(newtctx);
357185089Sraj		return (ret);
358209131Sraj	}
359209131Sraj
360185089Sraj	newtctx->tc_side = TCP4_SIDE_SERVER_WORK;
361185089Sraj	newtctx->tc_magic = TCP4_CTX_MAGIC;
362209131Sraj	*newctxp = newtctx;
363209131Sraj
364209131Sraj	return (0);
365209131Sraj}
366185089Sraj
367185089Srajstatic int
368185089Srajtcp4_send(void *ctx, const unsigned char *data, size_t size)
369185089Sraj{
370185089Sraj	struct tcp4_ctx *tctx = ctx;
371185089Sraj
372209131Sraj	PJDLOG_ASSERT(tctx != NULL);
373209131Sraj	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
374209131Sraj	PJDLOG_ASSERT(tctx->tc_fd >= 0);
375209131Sraj
376240493Sgber	return (proto_common_send(tctx->tc_fd, data, size));
377240493Sgber}
378240493Sgber
379240493Sgberstatic int
380240493Sgbertcp4_recv(void *ctx, unsigned char *data, size_t size)
381240493Sgber{
382240493Sgber	struct tcp4_ctx *tctx = ctx;
383209131Sraj
384209131Sraj	PJDLOG_ASSERT(tctx != NULL);
385209131Sraj	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
386209131Sraj	PJDLOG_ASSERT(tctx->tc_fd >= 0);
387209131Sraj
388209131Sraj	return (proto_common_recv(tctx->tc_fd, data, size));
389209131Sraj}
390227843Smarius
391185089Srajstatic int
392185089Srajtcp4_descriptor(const void *ctx)
393209131Sraj{
394185089Sraj	const struct tcp4_ctx *tctx = ctx;
395209131Sraj
396209131Sraj	PJDLOG_ASSERT(tctx != NULL);
397185089Sraj	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
398185089Sraj
399185089Sraj	return (tctx->tc_fd);
400185089Sraj}
401261513Snwhitehorn
402185089Srajstatic void
403185089Srajsin2str(struct sockaddr_in *sinp, char *addr, size_t size)
404185089Sraj{
405185089Sraj	in_addr_t ip;
406209131Sraj	unsigned int port;
407185089Sraj
408218228Smarcel	PJDLOG_ASSERT(addr != NULL);
409185089Sraj	PJDLOG_ASSERT(sinp->sin_family == AF_INET);
410218228Smarcel
411218228Smarcel	ip = ntohl(sinp->sin_addr.s_addr);
412209131Sraj	port = ntohs(sinp->sin_port);
413218228Smarcel	PJDLOG_VERIFY(snprintf(addr, size, "tcp4://%u.%u.%u.%u:%u",
414259484Snwhitehorn	    ((ip >> 24) & 0xff), ((ip >> 16) & 0xff), ((ip >> 8) & 0xff),
415259484Snwhitehorn	    (ip & 0xff), port) < (ssize_t)size);
416209131Sraj}
417185089Sraj
418209131Srajstatic bool
419209131Srajtcp4_address_match(const void *ctx, const char *addr)
420185089Sraj{
421185089Sraj	const struct tcp4_ctx *tctx = ctx;
422185089Sraj	struct sockaddr_in sin;
423209131Sraj	socklen_t sinlen;
424185089Sraj	in_addr_t ip1, ip2;
425209131Sraj
426209131Sraj	PJDLOG_ASSERT(tctx != NULL);
427240489Sgber	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
428209131Sraj
429185089Sraj	if (tcp4_addr(addr, &sin) != 0)
430185089Sraj		return (false);
431209131Sraj	ip1 = sin.sin_addr.s_addr;
432240489Sgber
433185089Sraj	sinlen = sizeof(sin);
434240489Sgber	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0)
435218228Smarcel		return (false);
436218228Smarcel	ip2 = sin.sin_addr.s_addr;
437218228Smarcel
438209131Sraj	return (ip1 == ip2);
439240489Sgber}
440240489Sgber
441240489Sgberstatic void
442218228Smarceltcp4_local_address(const void *ctx, char *addr, size_t size)
443209131Sraj{
444240489Sgber	const struct tcp4_ctx *tctx = ctx;
445209131Sraj	struct sockaddr_in sin;
446209131Sraj	socklen_t sinlen;
447209131Sraj
448185089Sraj	PJDLOG_ASSERT(tctx != NULL);
449185089Sraj	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
450209131Sraj
451209131Sraj	sinlen = sizeof(sin);
452209131Sraj	if (getsockname(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
453185089Sraj		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
454185089Sraj		return;
455185089Sraj	}
456185089Sraj	sin2str(&sin, addr, size);
457209131Sraj}
458185089Sraj
459185089Srajstatic void
460185089Srajtcp4_remote_address(const void *ctx, char *addr, size_t size)
461185089Sraj{
462185089Sraj	const struct tcp4_ctx *tctx = ctx;
463240489Sgber	struct sockaddr_in sin;
464240489Sgber	socklen_t sinlen;
465240489Sgber
466240489Sgber	PJDLOG_ASSERT(tctx != NULL);
467209131Sraj	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
468240489Sgber
469240489Sgber	sinlen = sizeof(sin);
470259484Snwhitehorn	if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
471259484Snwhitehorn		PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
472240489Sgber		return;
473240489Sgber	}
474209131Sraj	sin2str(&sin, addr, size);
475209131Sraj}
476209131Sraj
477209131Srajstatic void
478209131Srajtcp4_close(void *ctx)
479209131Sraj{
480209131Sraj	struct tcp4_ctx *tctx = ctx;
481209131Sraj
482240489Sgber	PJDLOG_ASSERT(tctx != NULL);
483209131Sraj	PJDLOG_ASSERT(tctx->tc_magic == TCP4_CTX_MAGIC);
484240489Sgber
485185089Sraj	if (tctx->tc_fd >= 0)
486240489Sgber		close(tctx->tc_fd);
487240489Sgber	tctx->tc_magic = 0;
488240489Sgber	free(tctx);
489240489Sgber}
490240489Sgber
491240489Sgberstatic struct hast_proto tcp4_proto = {
492185089Sraj	.hp_name = "tcp4",
493240489Sgber	.hp_client = tcp4_client,
494240489Sgber	.hp_connect = tcp4_connect,
495240489Sgber	.hp_server = tcp4_server,
496240489Sgber	.hp_accept = tcp4_accept,
497240489Sgber	.hp_send = tcp4_send,
498240489Sgber	.hp_recv = tcp4_recv,
499240489Sgber	.hp_descriptor = tcp4_descriptor,
500240489Sgber	.hp_address_match = tcp4_address_match,
501240489Sgber	.hp_local_address = tcp4_local_address,
502240489Sgber	.hp_remote_address = tcp4_remote_address,
503240489Sgber	.hp_close = tcp4_close
504240489Sgber};
505240489Sgber
506240489Sgberstatic __constructor void
507240493Sgbertcp4_ctor(void)
508240489Sgber{
509240489Sgber
510240489Sgber	proto_register(&tcp4_proto, true);
511240489Sgber}
512240489Sgber