proto_tcp.c revision 204076
1103856Stjr/*- 2103856Stjr * Copyright (c) 2009-2010 The FreeBSD Foundation 3103856Stjr * All rights reserved. 4103856Stjr * 5227753Stheraven * This software was developed by Pawel Jakub Dawidek under sponsorship from 6227753Stheraven * the FreeBSD Foundation. 7227753Stheraven * 8227753Stheraven * Redistribution and use in source and binary forms, with or without 9227753Stheraven * modification, are permitted provided that the following conditions 10103856Stjr * are met: 11103856Stjr * 1. Redistributions of source code must retain the above copyright 12103856Stjr * notice, this list of conditions and the following disclaimer. 13103856Stjr * 2. Redistributions in binary form must reproduce the above copyright 14103856Stjr * notice, this list of conditions and the following disclaimer in the 15103856Stjr * documentation and/or other materials provided with the distribution. 16103856Stjr * 17103856Stjr * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18103856Stjr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19103856Stjr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20103856Stjr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21103856Stjr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22103856Stjr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23103856Stjr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24103856Stjr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25103856Stjr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26103856Stjr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27103856Stjr * SUCH DAMAGE. 28103856Stjr */ 29103856Stjr 30103856Stjr#include <sys/cdefs.h> 31103856Stjr__FBSDID("$FreeBSD: head/sbin/hastd/proto_tcp4.c 204076 2010-02-18 23:16:19Z pjd $"); 32103856Stjr 33103856Stjr#include <sys/param.h> /* MAXHOSTNAMELEN */ 34103856Stjr 35103856Stjr#include <netinet/in.h> 36103856Stjr#include <netinet/tcp.h> 37103856Stjr 38227753Stheraven#include <assert.h> 39103856Stjr#include <errno.h> 40103856Stjr#include <netdb.h> 41103856Stjr#include <stdbool.h> 42103856Stjr#include <stdint.h> 43103856Stjr#include <stdio.h> 44103856Stjr#include <string.h> 45227753Stheraven#include <unistd.h> 46227753Stheraven 47227753Stheraven#include "hast.h" 48227753Stheraven#include "pjdlog.h" 49227753Stheraven#include "proto_impl.h" 50 51#define TCP4_CTX_MAGIC 0x7c441c 52struct tcp4_ctx { 53 int tc_magic; 54 struct sockaddr_in tc_sin; 55 int tc_fd; 56 int tc_side; 57#define TCP4_SIDE_CLIENT 0 58#define TCP4_SIDE_SERVER_LISTEN 1 59#define TCP4_SIDE_SERVER_WORK 2 60}; 61 62static void tcp4_close(void *ctx); 63 64static in_addr_t 65str2ip(const char *str) 66{ 67 struct hostent *hp; 68 in_addr_t ip; 69 70 ip = inet_addr(str); 71 if (ip != INADDR_NONE) { 72 /* It is a valid IP address. */ 73 return (ip); 74 } 75 /* Check if it is a valid host name. */ 76 hp = gethostbyname(str); 77 if (hp == NULL) 78 return (INADDR_NONE); 79 return (((struct in_addr *)(void *)hp->h_addr)->s_addr); 80} 81 82/* 83 * Function converts the given string to unsigned number. 84 */ 85static int 86numfromstr(const char *str, intmax_t minnum, intmax_t maxnum, intmax_t *nump) 87{ 88 intmax_t digit, num; 89 90 if (str[0] == '\0') 91 goto invalid; /* Empty string. */ 92 num = 0; 93 for (; *str != '\0'; str++) { 94 if (*str < '0' || *str > '9') 95 goto invalid; /* Non-digit character. */ 96 digit = *str - '0'; 97 if (num > num * 10 + digit) 98 goto invalid; /* Overflow. */ 99 num = num * 10 + digit; 100 if (num > maxnum) 101 goto invalid; /* Too big. */ 102 } 103 if (num < minnum) 104 goto invalid; /* Too small. */ 105 *nump = num; 106 return (0); 107invalid: 108 errno = EINVAL; 109 return (-1); 110} 111 112static int 113tcp4_addr(const char *addr, struct sockaddr_in *sinp) 114{ 115 char iporhost[MAXHOSTNAMELEN]; 116 const char *pp; 117 size_t size; 118 in_addr_t ip; 119 120 if (addr == NULL) 121 return (-1); 122 123 if (strncasecmp(addr, "tcp4://", 7) == 0) 124 addr += 7; 125 else if (strncasecmp(addr, "tcp://", 6) == 0) 126 addr += 6; 127 else if (addr[0] != '/' && /* If this is not path... */ 128 strstr(addr, "://") == NULL)/* ...and has no prefix... */ 129 ; /* ...tcp4 is the default. */ 130 else 131 return (-1); 132 133 sinp->sin_family = AF_INET; 134 sinp->sin_len = sizeof(*sinp); 135 /* Extract optional port. */ 136 pp = strrchr(addr, ':'); 137 if (pp == NULL) { 138 /* Port not given, use the default. */ 139 sinp->sin_port = htons(HASTD_PORT); 140 } else { 141 intmax_t port; 142 143 if (numfromstr(pp + 1, 1, 65535, &port) < 0) 144 return (errno); 145 sinp->sin_port = htons(port); 146 } 147 /* Extract host name or IP address. */ 148 if (pp == NULL) { 149 size = sizeof(iporhost); 150 if (strlcpy(iporhost, addr, size) >= size) 151 return (ENAMETOOLONG); 152 } else { 153 size = (size_t)(pp - addr + 1); 154 if (size > sizeof(iporhost)) 155 return (ENAMETOOLONG); 156 strlcpy(iporhost, addr, size); 157 } 158 /* Convert string (IP address or host name) to in_addr_t. */ 159 ip = str2ip(iporhost); 160 if (ip == INADDR_NONE) 161 return (EINVAL); 162 sinp->sin_addr.s_addr = ip; 163 164 return (0); 165} 166 167static int 168tcp4_common_setup(const char *addr, void **ctxp, int side) 169{ 170 struct tcp4_ctx *tctx; 171 int ret, val; 172 173 tctx = malloc(sizeof(*tctx)); 174 if (tctx == NULL) 175 return (errno); 176 177 /* Parse given address. */ 178 if ((ret = tcp4_addr(addr, &tctx->tc_sin)) != 0) { 179 free(tctx); 180 return (ret); 181 } 182 183 tctx->tc_fd = socket(AF_INET, SOCK_STREAM, 0); 184 if (tctx->tc_fd == -1) { 185 ret = errno; 186 free(tctx); 187 return (ret); 188 } 189 190 /* Socket settings. */ 191 val = 1; 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); 195 } 196 val = 131072; 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); 200 } 201 val = 131072; 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); 205 } 206 207 tctx->tc_side = side; 208 tctx->tc_magic = TCP4_CTX_MAGIC; 209 *ctxp = tctx; 210 211 return (0); 212} 213 214static int 215tcp4_client(const char *addr, void **ctxp) 216{ 217 218 return (tcp4_common_setup(addr, ctxp, TCP4_SIDE_CLIENT)); 219} 220 221static int 222tcp4_connect(void *ctx) 223{ 224 struct tcp4_ctx *tctx = ctx; 225 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); 230 231 if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, 232 sizeof(tctx->tc_sin)) < 0) { 233 return (errno); 234 } 235 236 return (0); 237} 238 239static int 240tcp4_server(const char *addr, void **ctxp) 241{ 242 struct tcp4_ctx *tctx; 243 int ret, val; 244 245 ret = tcp4_common_setup(addr, ctxp, TCP4_SIDE_SERVER_LISTEN); 246 if (ret != 0) 247 return (ret); 248 249 tctx = *ctxp; 250 251 val = 1; 252 /* Ignore failure. */ 253 (void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val, 254 sizeof(val)); 255 256 if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, 257 sizeof(tctx->tc_sin)) < 0) { 258 ret = errno; 259 tcp4_close(tctx); 260 return (ret); 261 } 262 if (listen(tctx->tc_fd, 8) < 0) { 263 ret = errno; 264 tcp4_close(tctx); 265 return (ret); 266 } 267 268 return (0); 269} 270 271static int 272tcp4_accept(void *ctx, void **newctxp) 273{ 274 struct tcp4_ctx *tctx = ctx; 275 struct tcp4_ctx *newtctx; 276 socklen_t fromlen; 277 int ret; 278 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); 283 284 newtctx = malloc(sizeof(*newtctx)); 285 if (newtctx == NULL) 286 return (errno); 287 288 fromlen = sizeof(tctx->tc_sin); 289 newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, 290 &fromlen); 291 if (newtctx->tc_fd < 0) { 292 ret = errno; 293 free(newtctx); 294 return (ret); 295 } 296 297 newtctx->tc_side = TCP4_SIDE_SERVER_WORK; 298 newtctx->tc_magic = TCP4_CTX_MAGIC; 299 *newctxp = newtctx; 300 301 return (0); 302} 303 304static int 305tcp4_send(void *ctx, const unsigned char *data, size_t size) 306{ 307 struct tcp4_ctx *tctx = ctx; 308 309 assert(tctx != NULL); 310 assert(tctx->tc_magic == TCP4_CTX_MAGIC); 311 assert(tctx->tc_fd >= 0); 312 313 return (proto_common_send(tctx->tc_fd, data, size)); 314} 315 316static int 317tcp4_recv(void *ctx, unsigned char *data, size_t size) 318{ 319 struct tcp4_ctx *tctx = ctx; 320 321 assert(tctx != NULL); 322 assert(tctx->tc_magic == TCP4_CTX_MAGIC); 323 assert(tctx->tc_fd >= 0); 324 325 return (proto_common_recv(tctx->tc_fd, data, size)); 326} 327 328static int 329tcp4_descriptor(const void *ctx) 330{ 331 const struct tcp4_ctx *tctx = ctx; 332 333 assert(tctx != NULL); 334 assert(tctx->tc_magic == TCP4_CTX_MAGIC); 335 336 return (tctx->tc_fd); 337} 338 339static void 340sin2str(struct sockaddr_in *sinp, char *addr, size_t size) 341{ 342 in_addr_t ip; 343 unsigned int port; 344 345 assert(addr != NULL); 346 assert(sinp->sin_family == AF_INET); 347 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); 352} 353 354static bool 355tcp4_address_match(const void *ctx, const char *addr) 356{ 357 const struct tcp4_ctx *tctx = ctx; 358 struct sockaddr_in sin; 359 socklen_t sinlen; 360 in_addr_t ip1, ip2; 361 362 assert(tctx != NULL); 363 assert(tctx->tc_magic == TCP4_CTX_MAGIC); 364 365 if (tcp4_addr(addr, &sin) != 0) 366 return (false); 367 ip1 = sin.sin_addr.s_addr; 368 369 sinlen = sizeof(sin); 370 if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) 371 return (false); 372 ip2 = sin.sin_addr.s_addr; 373 374 return (ip1 == ip2); 375} 376 377static void 378tcp4_local_address(const void *ctx, char *addr, size_t size) 379{ 380 const struct tcp4_ctx *tctx = ctx; 381 struct sockaddr_in sin; 382 socklen_t sinlen; 383 384 assert(tctx != NULL); 385 assert(tctx->tc_magic == TCP4_CTX_MAGIC); 386 387 sinlen = sizeof(sin); 388 if (getsockname(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) { 389 strlcpy(addr, "N/A", size); 390 return; 391 } 392 sin2str(&sin, addr, size); 393} 394 395static void 396tcp4_remote_address(const void *ctx, char *addr, size_t size) 397{ 398 const struct tcp4_ctx *tctx = ctx; 399 struct sockaddr_in sin; 400 socklen_t sinlen; 401 402 assert(tctx != NULL); 403 assert(tctx->tc_magic == TCP4_CTX_MAGIC); 404 405 sinlen = sizeof(sin); 406 if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) { 407 strlcpy(addr, "N/A", size); 408 return; 409 } 410 sin2str(&sin, addr, size); 411} 412 413static void 414tcp4_close(void *ctx) 415{ 416 struct tcp4_ctx *tctx = ctx; 417 418 assert(tctx != NULL); 419 assert(tctx->tc_magic == TCP4_CTX_MAGIC); 420 421 if (tctx->tc_fd >= 0) 422 close(tctx->tc_fd); 423 tctx->tc_magic = 0; 424 free(tctx); 425} 426 427static struct hast_proto tcp4_proto = { 428 .hp_name = "tcp4", 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 440}; 441 442static __constructor void 443tcp4_ctor(void) 444{ 445 446 proto_register(&tcp4_proto); 447} 448