diff options
author | Pawel Jakub Dawidek <pjd@FreeBSD.org> | 2011-05-20 11:14:05 +0000 |
---|---|---|
committer | Pawel Jakub Dawidek <pjd@FreeBSD.org> | 2011-05-20 11:14:05 +0000 |
commit | dc18c8ae6cd3cc98b1195ef3d9331663d361c001 (patch) | |
tree | cfebfb44b5ca790d77e7375d1b84aaf8d2b81c4b /sbin/hastd | |
parent | 496a87aa3080dc407e5a0c89766d50e97fec9e31 (diff) | |
download | src-dc18c8ae6cd3cc98b1195ef3d9331663d361c001.tar.gz src-dc18c8ae6cd3cc98b1195ef3d9331663d361c001.zip |
Now that hell is fully frozen it is good time to add IPv6 support to HAST.
MFC after: 3 weeks
Notes
Notes:
svn path=/head/; revision=222118
Diffstat (limited to 'sbin/hastd')
-rw-r--r-- | sbin/hastd/proto_tcp.c | 191 |
1 files changed, 116 insertions, 75 deletions
diff --git a/sbin/hastd/proto_tcp.c b/sbin/hastd/proto_tcp.c index 56c08ecf7582..8f0f157cf020 100644 --- a/sbin/hastd/proto_tcp.c +++ b/sbin/hastd/proto_tcp.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2009-2010 The FreeBSD Foundation + * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net> * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from @@ -51,10 +52,10 @@ __FBSDID("$FreeBSD$"); #include "proto_impl.h" #include "subr.h" -#define TCP_CTX_MAGIC 0x7c441c +#define TCP_CTX_MAGIC 0x7c41c struct tcp_ctx { int tc_magic; - struct sockaddr_in tc_sin; + struct sockaddr_storage tc_sa; int tc_fd; int tc_side; #define TCP_SIDE_CLIENT 0 @@ -65,24 +66,6 @@ struct tcp_ctx { static int tcp_connect_wait(void *ctx, int timeout); static void tcp_close(void *ctx); -static in_addr_t -str2ip(const char *str) -{ - struct hostent *hp; - in_addr_t ip; - - ip = inet_addr(str); - if (ip != INADDR_NONE) { - /* It is a valid IP address. */ - return (ip); - } - /* Check if it is a valid host name. */ - hp = gethostbyname(str); - if (hp == NULL) - return (INADDR_NONE); - return (((struct in_addr *)(void *)hp->h_addr)->s_addr); -} - /* * Function converts the given string to unsigned number. */ @@ -114,57 +97,93 @@ invalid: } static int -tcp_addr(const char *addr, int defport, struct sockaddr_in *sinp) +tcp_addr(const char *addr, int defport, struct sockaddr_storage *sap) { - char iporhost[MAXHOSTNAMELEN]; + char iporhost[MAXHOSTNAMELEN], portstr[6]; + struct addrinfo hints; + struct addrinfo *res; const char *pp; + intmax_t port; size_t size; - in_addr_t ip; + int error; if (addr == NULL) return (-1); - if (strncasecmp(addr, "tcp://", 7) == 0) + bzero(&hints, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if (strncasecmp(addr, "tcp4://", 7) == 0) { + addr += 7; + hints.ai_family = PF_INET; + } else if (strncasecmp(addr, "tcp6://", 7) == 0) { addr += 7; - else if (strncasecmp(addr, "tcp://", 6) == 0) + hints.ai_family = PF_INET6; + } else if (strncasecmp(addr, "tcp://", 6) == 0) { addr += 6; - else { + } else { /* * Because TCP is the default assume IP or host is given without * prefix. */ } - sinp->sin_family = AF_INET; - sinp->sin_len = sizeof(*sinp); - /* Extract optional port. */ - pp = strrchr(addr, ':'); + /* + * Extract optional port. + * There are three cases to consider. + * 1. hostname with port, eg. freefall.freebsd.org:8457 + * 2. IPv4 address with port, eg. 192.168.0.101:8457 + * 3. IPv6 address with port, eg. [fe80::1]:8457 + * We discover IPv6 address by checking for two colons and if port is + * given, the address has to start with [. + */ + pp = NULL; + if (strchr(addr, ':') != strrchr(addr, ':')) { + if (addr[0] == '[') + pp = strrchr(addr, ':'); + } else { + pp = strrchr(addr, ':'); + } if (pp == NULL) { /* Port not given, use the default. */ - sinp->sin_port = htons(defport); + port = defport; } else { - intmax_t port; - if (numfromstr(pp + 1, 1, 65535, &port) < 0) return (errno); - sinp->sin_port = htons(port); } + (void)snprintf(portstr, sizeof(portstr), "%jd", (intmax_t)port); /* Extract host name or IP address. */ if (pp == NULL) { size = sizeof(iporhost); if (strlcpy(iporhost, addr, size) >= size) return (ENAMETOOLONG); + } else if (addr[0] == '[' && pp[-1] == ']') { + size = (size_t)(pp - addr - 2 + 1); + if (size > sizeof(iporhost)) + return (ENAMETOOLONG); + (void)strlcpy(iporhost, addr + 1, size); } else { size = (size_t)(pp - addr + 1); if (size > sizeof(iporhost)) return (ENAMETOOLONG); (void)strlcpy(iporhost, addr, size); } - /* Convert string (IP address or host name) to in_addr_t. */ - ip = str2ip(iporhost); - if (ip == INADDR_NONE) + + error = getaddrinfo(iporhost, portstr, &hints, &res); + if (error != 0) { + pjdlog_debug(1, "getaddrinfo(%s, %s) failed: %s.", iporhost, + portstr, gai_strerror(error)); return (EINVAL); - sinp->sin_addr.s_addr = ip; + } + if (res == NULL) + return (ENOENT); + + memcpy(sap, res->ai_addr, res->ai_addrlen); + + freeaddrinfo(res); return (0); } @@ -185,22 +204,21 @@ tcp_setup_new(const char *addr, int side, void **ctxp) return (errno); /* Parse given address. */ - if ((ret = tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, - &tctx->tc_sin)) != 0) { + if ((ret = tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &tctx->tc_sa)) != 0) { free(tctx); return (ret); } - PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); + PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); - tctx->tc_fd = socket(AF_INET, SOCK_STREAM, 0); + tctx->tc_fd = socket(tctx->tc_sa.ss_family, SOCK_STREAM, 0); if (tctx->tc_fd == -1) { ret = errno; free(tctx); return (ret); } - PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); + PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); /* Socket settings. */ nodelay = 1; @@ -231,7 +249,7 @@ tcp_setup_wrap(int fd, int side, void **ctxp) return (errno); tctx->tc_fd = fd; - tctx->tc_sin.sin_family = AF_UNSPEC; + tctx->tc_sa.ss_family = AF_UNSPEC; tctx->tc_side = side; tctx->tc_magic = TCP_CTX_MAGIC; *ctxp = tctx; @@ -243,7 +261,7 @@ static int tcp_client(const char *srcaddr, const char *dstaddr, void **ctxp) { struct tcp_ctx *tctx; - struct sockaddr_in sin; + struct sockaddr_storage sa; int ret; ret = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, ctxp); @@ -252,12 +270,12 @@ tcp_client(const char *srcaddr, const char *dstaddr, void **ctxp) tctx = *ctxp; if (srcaddr == NULL) return (0); - ret = tcp_addr(srcaddr, 0, &sin); + ret = tcp_addr(srcaddr, 0, &sa); if (ret != 0) { tcp_close(tctx); return (ret); } - if (bind(tctx->tc_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + if (bind(tctx->tc_fd, (struct sockaddr *)&sa, sa.ss_len) < 0) { ret = errno; tcp_close(tctx); return (ret); @@ -275,7 +293,7 @@ tcp_connect(void *ctx, int timeout) PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_CLIENT); PJDLOG_ASSERT(tctx->tc_fd >= 0); - PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); + PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); PJDLOG_ASSERT(timeout >= -1); flags = fcntl(tctx->tc_fd, F_GETFL); @@ -295,8 +313,8 @@ tcp_connect(void *ctx, int timeout) return (errno); } - if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, - sizeof(tctx->tc_sin)) == 0) { + if (connect(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa, + tctx->tc_sa.ss_len) == 0) { if (timeout == -1) return (0); error = 0; @@ -403,10 +421,10 @@ tcp_server(const char *addr, void **ctxp) (void)setsockopt(tctx->tc_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); - PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); + PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); - if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, - sizeof(tctx->tc_sin)) < 0) { + if (bind(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa, + tctx->tc_sa.ss_len) < 0) { ret = errno; tcp_close(tctx); return (ret); @@ -432,14 +450,14 @@ tcp_accept(void *ctx, void **newctxp) PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); PJDLOG_ASSERT(tctx->tc_side == TCP_SIDE_SERVER_LISTEN); PJDLOG_ASSERT(tctx->tc_fd >= 0); - PJDLOG_ASSERT(tctx->tc_sin.sin_family != AF_UNSPEC); + PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); newtctx = malloc(sizeof(*newtctx)); if (newtctx == NULL) return (errno); - fromlen = sizeof(tctx->tc_sin); - newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sin, + fromlen = tctx->tc_sa.ss_len; + newtctx->tc_fd = accept(tctx->tc_fd, (struct sockaddr *)&tctx->tc_sa, &fromlen); if (newtctx->tc_fd < 0) { ret = errno; @@ -503,59 +521,82 @@ static bool tcp_address_match(const void *ctx, const char *addr) { const struct tcp_ctx *tctx = ctx; - struct sockaddr_in sin; - socklen_t sinlen; - in_addr_t ip1, ip2; + struct sockaddr_storage sa1, sa2; + socklen_t salen; PJDLOG_ASSERT(tctx != NULL); PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); - if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sin) != 0) + if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sa1) != 0) + return (false); + + salen = sizeof(sa2); + if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) < 0) return (false); - ip1 = sin.sin_addr.s_addr; - sinlen = sizeof(sin); - if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) + if (sa1.ss_family != sa2.ss_family || sa1.ss_len != sa2.ss_len) return (false); - ip2 = sin.sin_addr.s_addr; - return (ip1 == ip2); + switch (sa1.ss_family) { + case AF_INET: + { + struct sockaddr_in *sin1, *sin2; + + sin1 = (struct sockaddr_in *)&sa1; + sin2 = (struct sockaddr_in *)&sa2; + + return (memcmp(&sin1->sin_addr, &sin2->sin_addr, + sizeof(sin1->sin_addr)) == 0); + } + case AF_INET6: + { + struct sockaddr_in6 *sin1, *sin2; + + sin1 = (struct sockaddr_in6 *)&sa1; + sin2 = (struct sockaddr_in6 *)&sa2; + + return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr, + sizeof(sin1->sin6_addr)) == 0); + } + default: + return (false); + } } static void tcp_local_address(const void *ctx, char *addr, size_t size) { const struct tcp_ctx *tctx = ctx; - struct sockaddr_in sin; - socklen_t sinlen; + struct sockaddr_storage sa; + socklen_t salen; PJDLOG_ASSERT(tctx != NULL); PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); - sinlen = sizeof(sin); - if (getsockname(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) { + salen = sizeof(sa); + if (getsockname(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) { PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); return; } - PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sin) < (ssize_t)size); + PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size); } static void tcp_remote_address(const void *ctx, char *addr, size_t size) { const struct tcp_ctx *tctx = ctx; - struct sockaddr_in sin; - socklen_t sinlen; + struct sockaddr_storage sa; + socklen_t salen; PJDLOG_ASSERT(tctx != NULL); PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); - sinlen = sizeof(sin); - if (getpeername(tctx->tc_fd, (struct sockaddr *)&sin, &sinlen) < 0) { + salen = sizeof(sa); + if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa, &salen) < 0) { PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); return; } - PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sin) < (ssize_t)size); + PJDLOG_VERIFY(snprintf(addr, size, "tcp://%S", &sa) < (ssize_t)size); } static void |