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>
50 #include "proto_impl.h"
53 #define TCP4_CTX_MAGIC 0x7c441c
56 struct sockaddr_in tc_sin;
59 #define TCP4_SIDE_CLIENT 0
60 #define TCP4_SIDE_SERVER_LISTEN 1
61 #define TCP4_SIDE_SERVER_WORK 2
64 static void tcp4_close(void *ctx);
67 str2ip(const char *str)
73 if (ip != INADDR_NONE) {
74 /* It is a valid IP address. */
77 /* Check if it is a valid host name. */
78 hp = gethostbyname(str);
81 return (((struct in_addr *)(void *)hp->h_addr)->s_addr);
85 * Function converts the given string to unsigned number.
88 numfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump)
93 goto invalid; /* Empty string. */
95 for (; *str != '\0'; str++) {
96 if (*str < '0' || *str > '9')
97 goto invalid; /* Non-digit character. */
99 if (num > num * 10 + digit)
100 goto invalid; /* Overflow. */
101 num = num * 10 + digit;
103 goto invalid; /* Too big. */
106 goto invalid; /* Too small. */
115 tcp4_addr(const char *addr, struct sockaddr_in *sinp)
117 char iporhost[MAXHOSTNAMELEN];
125 if (strncasecmp(addr, "tcp4://", 7) == 0)
127 else if (strncasecmp(addr, "tcp://", 6) == 0)
129 else if (addr[0] != '/' && /* If this is not path... */
130 strstr(addr, "://") == NULL)/* ...and has no prefix... */
131 ; /* ...tcp4 is the default. */
135 sinp->sin_family = AF_INET;
136 sinp->sin_len = sizeof(*sinp);
137 /* Extract optional port. */
138 pp = strrchr(addr, ':');
140 /* Port not given, use the default. */
141 sinp->sin_port = htons(HASTD_PORT);
145 if (numfromstr(pp + 1, 1, 65535, &port) < 0)
147 sinp->sin_port = htons(port);
149 /* Extract host name or IP address. */
151 size = sizeof(iporhost);
152 if (strlcpy(iporhost, addr, size) >= size)
153 return (ENAMETOOLONG);
155 size = (size_t)(pp - addr + 1);
156 if (size > sizeof(iporhost))
157 return (ENAMETOOLONG);
158 strlcpy(iporhost, addr, size);
160 /* Convert string (IP address or host name) to in_addr_t. */
161 ip = str2ip(iporhost);
162 if (ip == INADDR_NONE)
164 sinp->sin_addr.s_addr = ip;
170 tcp4_common_setup(const char *addr, void **ctxp, int side)
172 struct tcp4_ctx *tctx;
175 tctx = malloc(sizeof(*tctx));
179 /* Parse given address. */
180 if ((ret = tcp4_addr(addr, &tctx->tc_sin)) != 0) {
185 tctx->tc_fd = socket(AF_INET, SOCK_STREAM, 0);
186 if (tctx->tc_fd == -1) {
192 /* Socket settings. */
194 if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &val,
195 sizeof(val)) == -1) {
196 pjdlog_warning("Unable to set TCP_NOELAY on %s", addr);
199 if (setsockopt(tctx->tc_fd, SOL_SOCKET, SO_SNDBUF, &val,
200 sizeof(val)) == -1) {
201 pjdlog_warning("Unable to set send buffer size on %s", addr);
204 if (setsockopt(tctx->tc_fd, SOL_SOCKET, SO_RCVBUF, &val,
205 sizeof(val)) == -1) {
206 pjdlog_warning("Unable to set receive buffer size on %s", addr);
209 tctx->tc_side = side;
210 tctx->tc_magic = TCP4_CTX_MAGIC;
217 tcp4_client(const char *addr, void **ctxp)
220 return (tcp4_common_setup(addr, ctxp, TCP4_SIDE_CLIENT));
224 tcp4_connect(void *ctx)
226 struct tcp4_ctx *tctx = ctx;
230 int error, flags, ret;
232 assert(tctx != NULL);
233 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
234 assert(tctx->tc_side == TCP4_SIDE_CLIENT);
235 assert(tctx->tc_fd >= 0);
237 flags = fcntl(tctx->tc_fd, F_GETFL);
239 KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno,
240 "fcntl(F_GETFL) failed"));
244 * We make socket non-blocking so we have decided about connection
248 if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
249 KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno,
250 "fcntl(F_SETFL, O_NONBLOCK) failed"));
254 if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
255 sizeof(tctx->tc_sin)) == 0) {
259 if (errno != EINPROGRESS) {
261 pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
265 * Connection can't be established immediately, let's wait
266 * for HAST_TIMEOUT seconds.
268 tv.tv_sec = HAST_TIMEOUT;
272 FD_SET(tctx->tc_fd, &fdset);
273 ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
277 } else if (ret == -1) {
281 pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
285 assert(FD_ISSET(tctx->tc_fd, &fdset));
286 esize = sizeof(error);
287 if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
290 pjdlog_common(LOG_DEBUG, 1, errno,
291 "getsockopt(SO_ERROR) failed");
295 pjdlog_common(LOG_DEBUG, 1, error,
296 "getsockopt(SO_ERROR) returned error");
301 flags &= ~O_NONBLOCK;
302 if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
305 pjdlog_common(LOG_DEBUG, 1, errno,
306 "fcntl(F_SETFL, ~O_NONBLOCK) failed");
312 tcp4_server(const char *addr, void **ctxp)
314 struct tcp4_ctx *tctx;
317 ret = tcp4_common_setup(addr, ctxp, TCP4_SIDE_SERVER_LISTEN);
324 /* Ignore failure. */
325 (void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
328 if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
329 sizeof(tctx->tc_sin)) < 0) {
334 if (listen(tctx->tc_fd, 8) < 0) {
344 tcp4_accept(void *ctx, void **newctxp)
346 struct tcp4_ctx *tctx = ctx;
347 struct tcp4_ctx *newtctx;
351 assert(tctx != NULL);
352 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
353 assert(tctx->tc_side == TCP4_SIDE_SERVER_LISTEN);
354 assert(tctx->tc_fd >= 0);
356 newtctx = malloc(sizeof(*newtctx));
360 fromlen = sizeof(tctx->tc_sin);
361 newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
363 if (newtctx->tc_fd < 0) {
369 newtctx->tc_side = TCP4_SIDE_SERVER_WORK;
370 newtctx->tc_magic = TCP4_CTX_MAGIC;
377 tcp4_send(void *ctx, const unsigned char *data, size_t size)
379 struct tcp4_ctx *tctx = ctx;
381 assert(tctx != NULL);
382 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
383 assert(tctx->tc_fd >= 0);
385 return (proto_common_send(tctx->tc_fd, data, size));
389 tcp4_recv(void *ctx, unsigned char *data, size_t size)
391 struct tcp4_ctx *tctx = ctx;
393 assert(tctx != NULL);
394 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
395 assert(tctx->tc_fd >= 0);
397 return (proto_common_recv(tctx->tc_fd, data, size));
401 tcp4_descriptor(const void *ctx)
403 const struct tcp4_ctx *tctx = ctx;
405 assert(tctx != NULL);
406 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
408 return (tctx->tc_fd);
412 sin2str(struct sockaddr_in *sinp, char *addr, size_t size)
417 assert(addr != NULL);
418 assert(sinp->sin_family == AF_INET);
420 ip = ntohl(sinp->sin_addr.s_addr);
421 port = ntohs(sinp->sin_port);
422 snprintf(addr, size, "tcp4://%u.%u.%u.%u:%u", ((ip >> 24) & 0xff),
423 ((ip >> 16) & 0xff), ((ip >> 8) & 0xff), (ip & 0xff), port);
427 tcp4_address_match(const void *ctx, const char *addr)
429 const struct tcp4_ctx *tctx = ctx;
430 struct sockaddr_in sin;
434 assert(tctx != NULL);
435 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
437 if (tcp4_addr(addr, &sin) != 0)
439 ip1 = sin.sin_addr.s_addr;
441 sinlen = sizeof(sin);
442 if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0)
444 ip2 = sin.sin_addr.s_addr;
450 tcp4_local_address(const void *ctx, char *addr, size_t size)
452 const struct tcp4_ctx *tctx = ctx;
453 struct sockaddr_in sin;
456 assert(tctx != NULL);
457 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
459 sinlen = sizeof(sin);
460 if (getsockname(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
461 strlcpy(addr, "N/A", size);
464 sin2str(&sin, addr, size);
468 tcp4_remote_address(const void *ctx, char *addr, size_t size)
470 const struct tcp4_ctx *tctx = ctx;
471 struct sockaddr_in sin;
474 assert(tctx != NULL);
475 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
477 sinlen = sizeof(sin);
478 if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
479 strlcpy(addr, "N/A", size);
482 sin2str(&sin, addr, size);
486 tcp4_close(void *ctx)
488 struct tcp4_ctx *tctx = ctx;
490 assert(tctx != NULL);
491 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
493 if (tctx->tc_fd >= 0)
499 static struct hast_proto tcp4_proto = {
501 .hp_client = tcp4_client,
502 .hp_connect = tcp4_connect,
503 .hp_server = tcp4_server,
504 .hp_accept = tcp4_accept,
505 .hp_send = tcp4_send,
506 .hp_recv = tcp4_recv,
507 .hp_descriptor = tcp4_descriptor,
508 .hp_address_match = tcp4_address_match,
509 .hp_local_address = tcp4_local_address,
510 .hp_remote_address = tcp4_remote_address,
511 .hp_close = tcp4_close
514 static __constructor void
518 proto_register(&tcp4_proto);