2 * Copyright (c) 2009-2010 The FreeBSD Foundation
5 * This software was developed by Pawel Jakub Dawidek under sponsorship from
6 * the FreeBSD Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h> /* MAXHOSTNAMELEN */
35 #include <netinet/in.h>
36 #include <netinet/tcp.h>
49 #include "proto_impl.h"
51 #define TCP4_CTX_MAGIC 0x7c441c
54 struct sockaddr_in tc_sin;
57 #define TCP4_SIDE_CLIENT 0
58 #define TCP4_SIDE_SERVER_LISTEN 1
59 #define TCP4_SIDE_SERVER_WORK 2
62 static void tcp4_close(void *ctx);
65 str2ip(const char *str)
71 if (ip != INADDR_NONE) {
72 /* It is a valid IP address. */
75 /* Check if it is a valid host name. */
76 hp = gethostbyname(str);
79 return (((struct in_addr *)(void *)hp->h_addr)->s_addr);
83 * Function converts the given string to unsigned number.
86 numfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump)
91 goto invalid; /* Empty string. */
93 for (; *str != '\0'; str++) {
94 if (*str < '0' || *str > '9')
95 goto invalid; /* Non-digit character. */
97 if (num > num * 10 + digit)
98 goto invalid; /* Overflow. */
99 num = num * 10 + digit;
101 goto invalid; /* Too big. */
104 goto invalid; /* Too small. */
113 tcp4_addr(const char *addr, struct sockaddr_in *sinp)
115 char iporhost[MAXHOSTNAMELEN];
123 if (strncasecmp(addr, "tcp4://", 7) == 0)
125 else if (strncasecmp(addr, "tcp://", 6) == 0)
127 else if (addr[0] != '/' && /* If this is not path... */
128 strstr(addr, "://") == NULL)/* ...and has no prefix... */
129 ; /* ...tcp4 is the default. */
133 sinp->sin_family = AF_INET;
134 sinp->sin_len = sizeof(*sinp);
135 /* Extract optional port. */
136 pp = strrchr(addr, ':');
138 /* Port not given, use the default. */
139 sinp->sin_port = htons(HASTD_PORT);
143 if (numfromstr(pp + 1, 1, 65535, &port) < 0)
145 sinp->sin_port = htons(port);
147 /* Extract host name or IP address. */
149 size = sizeof(iporhost);
150 if (strlcpy(iporhost, addr, size) >= size)
151 return (ENAMETOOLONG);
153 size = (size_t)(pp - addr + 1);
154 if (size > sizeof(iporhost))
155 return (ENAMETOOLONG);
156 strlcpy(iporhost, addr, size);
158 /* Convert string (IP address or host name) to in_addr_t. */
159 ip = str2ip(iporhost);
160 if (ip == INADDR_NONE)
162 sinp->sin_addr.s_addr = ip;
168 tcp4_common_setup(const char *addr, void **ctxp, int side)
170 struct tcp4_ctx *tctx;
173 tctx = malloc(sizeof(*tctx));
177 /* Parse given address. */
178 if ((ret = tcp4_addr(addr, &tctx->tc_sin)) != 0) {
183 tctx->tc_fd = socket(AF_INET, SOCK_STREAM, 0);
184 if (tctx->tc_fd == -1) {
190 /* Socket settings. */
192 if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &val,
193 sizeof(val)) == -1) {
194 pjdlog_warning("Unable to set TCP_NOELAY on %s", addr);
197 if (setsockopt(tctx->tc_fd, SOL_SOCKET, SO_SNDBUF, &val,
198 sizeof(val)) == -1) {
199 pjdlog_warning("Unable to set send buffer size on %s", addr);
202 if (setsockopt(tctx->tc_fd, SOL_SOCKET, SO_RCVBUF, &val,
203 sizeof(val)) == -1) {
204 pjdlog_warning("Unable to set receive buffer size on %s", addr);
207 tctx->tc_side = side;
208 tctx->tc_magic = TCP4_CTX_MAGIC;
215 tcp4_client(const char *addr, void **ctxp)
218 return (tcp4_common_setup(addr, ctxp, TCP4_SIDE_CLIENT));
222 tcp4_connect(void *ctx)
224 struct tcp4_ctx *tctx = ctx;
226 assert(tctx != NULL);
227 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
228 assert(tctx->tc_side == TCP4_SIDE_CLIENT);
229 assert(tctx->tc_fd >= 0);
231 if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
232 sizeof(tctx->tc_sin)) < 0) {
240 tcp4_server(const char *addr, void **ctxp)
242 struct tcp4_ctx *tctx;
245 ret = tcp4_common_setup(addr, ctxp, TCP4_SIDE_SERVER_LISTEN);
252 /* Ignore failure. */
253 (void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
256 if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
257 sizeof(tctx->tc_sin)) < 0) {
262 if (listen(tctx->tc_fd, 8) < 0) {
272 tcp4_accept(void *ctx, void **newctxp)
274 struct tcp4_ctx *tctx = ctx;
275 struct tcp4_ctx *newtctx;
279 assert(tctx != NULL);
280 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
281 assert(tctx->tc_side == TCP4_SIDE_SERVER_LISTEN);
282 assert(tctx->tc_fd >= 0);
284 newtctx = malloc(sizeof(*newtctx));
288 fromlen = sizeof(tctx->tc_sin);
289 newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
291 if (newtctx->tc_fd < 0) {
297 newtctx->tc_side = TCP4_SIDE_SERVER_WORK;
298 newtctx->tc_magic = TCP4_CTX_MAGIC;
305 tcp4_send(void *ctx, const unsigned char *data, size_t size)
307 struct tcp4_ctx *tctx = ctx;
309 assert(tctx != NULL);
310 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
311 assert(tctx->tc_fd >= 0);
313 return (proto_common_send(tctx->tc_fd, data, size));
317 tcp4_recv(void *ctx, unsigned char *data, size_t size)
319 struct tcp4_ctx *tctx = ctx;
321 assert(tctx != NULL);
322 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
323 assert(tctx->tc_fd >= 0);
325 return (proto_common_recv(tctx->tc_fd, data, size));
329 tcp4_descriptor(const void *ctx)
331 const struct tcp4_ctx *tctx = ctx;
333 assert(tctx != NULL);
334 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
336 return (tctx->tc_fd);
340 sin2str(struct sockaddr_in *sinp, char *addr, size_t size)
345 assert(addr != NULL);
346 assert(sinp->sin_family == AF_INET);
348 ip = ntohl(sinp->sin_addr.s_addr);
349 port = ntohs(sinp->sin_port);
350 snprintf(addr, size, "tcp4://%u.%u.%u.%u:%u", ((ip >> 24) & 0xff),
351 ((ip >> 16) & 0xff), ((ip >> 8) & 0xff), (ip & 0xff), port);
355 tcp4_address_match(const void *ctx, const char *addr)
357 const struct tcp4_ctx *tctx = ctx;
358 struct sockaddr_in sin;
362 assert(tctx != NULL);
363 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
365 if (tcp4_addr(addr, &sin) != 0)
367 ip1 = sin.sin_addr.s_addr;
369 sinlen = sizeof(sin);
370 if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0)
372 ip2 = sin.sin_addr.s_addr;
378 tcp4_local_address(const void *ctx, char *addr, size_t size)
380 const struct tcp4_ctx *tctx = ctx;
381 struct sockaddr_in sin;
384 assert(tctx != NULL);
385 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
387 sinlen = sizeof(sin);
388 if (getsockname(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
389 strlcpy(addr, "N/A", size);
392 sin2str(&sin, addr, size);
396 tcp4_remote_address(const void *ctx, char *addr, size_t size)
398 const struct tcp4_ctx *tctx = ctx;
399 struct sockaddr_in sin;
402 assert(tctx != NULL);
403 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
405 sinlen = sizeof(sin);
406 if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
407 strlcpy(addr, "N/A", size);
410 sin2str(&sin, addr, size);
414 tcp4_close(void *ctx)
416 struct tcp4_ctx *tctx = ctx;
418 assert(tctx != NULL);
419 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
421 if (tctx->tc_fd >= 0)
427 static struct hast_proto tcp4_proto = {
429 .hp_client = tcp4_client,
430 .hp_connect = tcp4_connect,
431 .hp_server = tcp4_server,
432 .hp_accept = tcp4_accept,
433 .hp_send = tcp4_send,
434 .hp_recv = tcp4_recv,
435 .hp_descriptor = tcp4_descriptor,
436 .hp_address_match = tcp4_address_match,
437 .hp_local_address = tcp4_local_address,
438 .hp_remote_address = tcp4_remote_address,
439 .hp_close = tcp4_close
442 static __constructor void
446 proto_register(&tcp4_proto);