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)
131 * Because TCP4 is the default assume IP or host is given without
136 sinp->sin_family = AF_INET;
137 sinp->sin_len = sizeof(*sinp);
138 /* Extract optional port. */
139 pp = strrchr(addr, ':');
141 /* Port not given, use the default. */
142 sinp->sin_port = htons(HASTD_PORT);
146 if (numfromstr(pp + 1, 1, 65535, &port) < 0)
148 sinp->sin_port = htons(port);
150 /* Extract host name or IP address. */
152 size = sizeof(iporhost);
153 if (strlcpy(iporhost, addr, size) >= size)
154 return (ENAMETOOLONG);
156 size = (size_t)(pp - addr + 1);
157 if (size > sizeof(iporhost))
158 return (ENAMETOOLONG);
159 (void)strlcpy(iporhost, addr, size);
161 /* Convert string (IP address or host name) to in_addr_t. */
162 ip = str2ip(iporhost);
163 if (ip == INADDR_NONE)
165 sinp->sin_addr.s_addr = ip;
171 tcp4_common_setup(const char *addr, void **ctxp, int side)
173 struct tcp4_ctx *tctx;
176 tctx = malloc(sizeof(*tctx));
180 /* Parse given address. */
181 if ((ret = tcp4_addr(addr, &tctx->tc_sin)) != 0) {
186 tctx->tc_fd = socket(AF_INET, SOCK_STREAM, 0);
187 if (tctx->tc_fd == -1) {
193 /* Socket settings. */
195 if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &val,
196 sizeof(val)) == -1) {
197 pjdlog_warning("Unable to set TCP_NOELAY on %s", addr);
200 if (setsockopt(tctx->tc_fd, SOL_SOCKET, SO_SNDBUF, &val,
201 sizeof(val)) == -1) {
202 pjdlog_warning("Unable to set send buffer size on %s", addr);
205 if (setsockopt(tctx->tc_fd, SOL_SOCKET, SO_RCVBUF, &val,
206 sizeof(val)) == -1) {
207 pjdlog_warning("Unable to set receive buffer size on %s", addr);
210 tctx->tc_side = side;
211 tctx->tc_magic = TCP4_CTX_MAGIC;
218 tcp4_client(const char *addr, void **ctxp)
221 return (tcp4_common_setup(addr, ctxp, TCP4_SIDE_CLIENT));
225 tcp4_connect(void *ctx)
227 struct tcp4_ctx *tctx = ctx;
231 int error, flags, ret;
233 assert(tctx != NULL);
234 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
235 assert(tctx->tc_side == TCP4_SIDE_CLIENT);
236 assert(tctx->tc_fd >= 0);
238 flags = fcntl(tctx->tc_fd, F_GETFL);
240 KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno,
241 "fcntl(F_GETFL) failed"));
245 * We make socket non-blocking so we can handle connection timeout
249 if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
250 KEEP_ERRNO(pjdlog_common(LOG_DEBUG, 1, errno,
251 "fcntl(F_SETFL, O_NONBLOCK) failed"));
255 if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
256 sizeof(tctx->tc_sin)) == 0) {
260 if (errno != EINPROGRESS) {
262 pjdlog_common(LOG_DEBUG, 1, errno, "connect() failed");
266 * Connection can't be established immediately, let's wait
267 * for HAST_TIMEOUT seconds.
269 tv.tv_sec = HAST_TIMEOUT;
273 FD_SET(tctx->tc_fd, &fdset);
274 ret = select(tctx->tc_fd + 1, NULL, &fdset, NULL, &tv);
278 } else if (ret == -1) {
282 pjdlog_common(LOG_DEBUG, 1, errno, "select() failed");
286 assert(FD_ISSET(tctx->tc_fd, &fdset));
287 esize = sizeof(error);
288 if (getsockopt(tctx->tc_fd, SOL_SOCKET, SO_ERROR, &error,
291 pjdlog_common(LOG_DEBUG, 1, errno,
292 "getsockopt(SO_ERROR) failed");
296 pjdlog_common(LOG_DEBUG, 1, error,
297 "getsockopt(SO_ERROR) returned error");
302 flags &= ~O_NONBLOCK;
303 if (fcntl(tctx->tc_fd, F_SETFL, flags) == -1) {
306 pjdlog_common(LOG_DEBUG, 1, errno,
307 "fcntl(F_SETFL, ~O_NONBLOCK) failed");
313 tcp4_server(const char *addr, void **ctxp)
315 struct tcp4_ctx *tctx;
318 ret = tcp4_common_setup(addr, ctxp, TCP4_SIDE_SERVER_LISTEN);
325 /* Ignore failure. */
326 (void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val,
329 if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
330 sizeof(tctx->tc_sin)) < 0) {
335 if (listen(tctx->tc_fd, 8) < 0) {
345 tcp4_accept(void *ctx, void **newctxp)
347 struct tcp4_ctx *tctx = ctx;
348 struct tcp4_ctx *newtctx;
352 assert(tctx != NULL);
353 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
354 assert(tctx->tc_side == TCP4_SIDE_SERVER_LISTEN);
355 assert(tctx->tc_fd >= 0);
357 newtctx = malloc(sizeof(*newtctx));
361 fromlen = sizeof(tctx->tc_sin);
362 newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin,
364 if (newtctx->tc_fd < 0) {
370 newtctx->tc_side = TCP4_SIDE_SERVER_WORK;
371 newtctx->tc_magic = TCP4_CTX_MAGIC;
378 tcp4_send(void *ctx, const unsigned char *data, size_t size)
380 struct tcp4_ctx *tctx = ctx;
382 assert(tctx != NULL);
383 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
384 assert(tctx->tc_fd >= 0);
386 return (proto_common_send(tctx->tc_fd, data, size));
390 tcp4_recv(void *ctx, unsigned char *data, size_t size)
392 struct tcp4_ctx *tctx = ctx;
394 assert(tctx != NULL);
395 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
396 assert(tctx->tc_fd >= 0);
398 return (proto_common_recv(tctx->tc_fd, data, size));
402 tcp4_descriptor(const void *ctx)
404 const struct tcp4_ctx *tctx = ctx;
406 assert(tctx != NULL);
407 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
409 return (tctx->tc_fd);
413 sin2str(struct sockaddr_in *sinp, char *addr, size_t size)
418 assert(addr != NULL);
419 assert(sinp->sin_family == AF_INET);
421 ip = ntohl(sinp->sin_addr.s_addr);
422 port = ntohs(sinp->sin_port);
423 PJDLOG_VERIFY(snprintf(addr, size, "tcp4://%u.%u.%u.%u:%u",
424 ((ip >> 24) & 0xff), ((ip >> 16) & 0xff), ((ip >> 8) & 0xff),
425 (ip & 0xff), port) < (ssize_t)size);
429 tcp4_address_match(const void *ctx, const char *addr)
431 const struct tcp4_ctx *tctx = ctx;
432 struct sockaddr_in sin;
436 assert(tctx != NULL);
437 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
439 if (tcp4_addr(addr, &sin) != 0)
441 ip1 = sin.sin_addr.s_addr;
443 sinlen = sizeof(sin);
444 if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0)
446 ip2 = sin.sin_addr.s_addr;
452 tcp4_local_address(const void *ctx, char *addr, size_t size)
454 const struct tcp4_ctx *tctx = ctx;
455 struct sockaddr_in sin;
458 assert(tctx != NULL);
459 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
461 sinlen = sizeof(sin);
462 if (getsockname(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
463 PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
466 sin2str(&sin, addr, size);
470 tcp4_remote_address(const void *ctx, char *addr, size_t size)
472 const struct tcp4_ctx *tctx = ctx;
473 struct sockaddr_in sin;
476 assert(tctx != NULL);
477 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
479 sinlen = sizeof(sin);
480 if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) {
481 PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size);
484 sin2str(&sin, addr, size);
488 tcp4_close(void *ctx)
490 struct tcp4_ctx *tctx = ctx;
492 assert(tctx != NULL);
493 assert(tctx->tc_magic == TCP4_CTX_MAGIC);
495 if (tctx->tc_fd >= 0)
501 static struct hast_proto tcp4_proto = {
503 .hp_client = tcp4_client,
504 .hp_connect = tcp4_connect,
505 .hp_server = tcp4_server,
506 .hp_accept = tcp4_accept,
507 .hp_send = tcp4_send,
508 .hp_recv = tcp4_recv,
509 .hp_descriptor = tcp4_descriptor,
510 .hp_address_match = tcp4_address_match,
511 .hp_local_address = tcp4_local_address,
512 .hp_remote_address = tcp4_remote_address,
513 .hp_close = tcp4_close
516 static __constructor void
520 proto_register(&tcp4_proto, true);