diff options
Diffstat (limited to 'lib/src/util')
-rw-r--r-- | lib/src/util/ip_address.c | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/lib/src/util/ip_address.c b/lib/src/util/ip_address.c new file mode 100644 index 000000000..ea238167f --- /dev/null +++ b/lib/src/util/ip_address.c @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file ip_address.c + * \brief Implementation of IP address type + */ + +#include <hicn/util/ip_address.h> + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#ifdef __ANDROID__ +#define SWAP(x) bswap_32(x) +#else +#define SWAP(x) __bswap_constant_32(x) +#endif +#else +#define SWAP(x) x +#endif + + +/* No htonl() with const */ +const ip_address_t IPV4_LOOPBACK = (ip_address_t) { + .v4.as_inaddr.s_addr = SWAP(INADDR_LOOPBACK), +}; + +const ip_address_t IPV6_LOOPBACK = (ip_address_t) { + .v6.as_in6addr = IN6ADDR_LOOPBACK_INIT, +}; + +const ip_address_t IPV4_ANY = (ip_address_t) { + .v4.as_inaddr.s_addr = INADDR_ANY, +}; + +const ip_address_t IPV6_ANY = (ip_address_t) { + .v6.as_in6addr = IN6ADDR_ANY_INIT, +}; + +const ip_address_t IP_ADDRESS_EMPTY = { + .as_u64 = { 0 }, +}; + + +/* IP address */ + +int +ip_address_get_family (const char * ip_address) +{ + struct addrinfo hint, *res = NULL; + int rc; + + memset (&hint, '\0', sizeof hint); + + hint.ai_family = PF_UNSPEC; + hint.ai_flags = AI_NUMERICHOST; + + rc = getaddrinfo (ip_address, NULL, &hint, &res); + if (rc) + { + return -1; + } + rc = res->ai_family; + freeaddrinfo (res); + return rc; +} + +int +ip_address_len (const ip_address_t * ip_address, int family) +{ + return (family == AF_INET6) ? IPV6_ADDR_LEN : + (family == AF_INET) ? IPV4_ADDR_LEN : 0; +} + +int +ip_address_ntop (const ip_address_t * ip_address, char *dst, const size_t len, + int family) +{ + const char * s = inet_ntop (family, ip_address->buffer, dst, len); + return (s ? 1 : -1); +} + +/* + * Parse ip addresses in presentation format + */ +int +ip_address_pton (const char *ip_address_str, ip_address_t * ip_address) +{ + int pton_fd; + char *addr = strdup (ip_address_str); + int family; + + + family = ip_address_get_family (addr); + + switch (family) + { + case AF_INET6: + pton_fd = inet_pton (AF_INET6, addr, &ip_address->buffer); + break; + case AF_INET: + pton_fd = inet_pton (AF_INET, addr, &ip_address->buffer); + break; + default: + goto ERR; + } + + // 0 = not in presentation format + // < 0 = other error (use perror) + if (pton_fd <= 0) + { + goto ERR; + } + + return 1; +ERR: + free (addr); + return -1; +} + +int +ip_address_snprintf(char * s, size_t size, const ip_address_t * ip_address, int family) +{ + size_t len = family == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN; + const char * rc = inet_ntop (family, ip_address->buffer, s, len); + return rc ? strlen(rc) : -1; +} + +int +ip_address_to_sockaddr(const ip_address_t * ip_address, + struct sockaddr *sa, int family) +{ + struct sockaddr_in6 *tmp6 = (struct sockaddr_in6 *) sa; + struct sockaddr_in *tmp4 = (struct sockaddr_in *) sa; + + switch (family) + { + case AF_INET6: + tmp6->sin6_family = AF_INET6; + tmp6->sin6_port = DUMMY_PORT; + tmp6->sin6_scope_id = 0; + memcpy (&tmp6->sin6_addr, ip_address->buffer, IPV6_ADDR_LEN); + break; + case AF_INET: + tmp4->sin_family = AF_INET; + tmp4->sin_port = DUMMY_PORT; + memcpy (&tmp4->sin_addr, ip_address->buffer, IPV4_ADDR_LEN); + break; + default: + return -1; + } + + return 1; +} + +int +ip_address_cmp(const ip_address_t * ip1, const ip_address_t * ip2, int family) +{ + return memcmp(ip1, ip2, ip_address_len(ip1, family)); +} + +int +ip_address_empty(const ip_address_t * ip) +{ + return (memcmp(ip, &IP_ADDRESS_EMPTY, sizeof(IP_ADDRESS_EMPTY)) == 0); +} + + + +/* Prefix */ + +/* Parse IP Prefixes in presentation format (in bits, separated by a slash) */ +int +ip_prefix_pton (const char *ip_address_str, ip_prefix_t * ip_prefix) +{ + int pton_fd; + char *p; + char *eptr; + char *addr = strdup (ip_address_str); + + p = strchr (addr, '/'); + if (!p) + ip_prefix->len = 0; // until we get the ip address family + else { + ip_prefix->len = strtoul (p + 1, &eptr, 10); + *p = 0; + } + + ip_prefix->family = ip_address_get_family (addr); + + switch (ip_prefix->family) + { + case AF_INET6: + if (ip_prefix->len > IPV6_ADDR_LEN_BITS) + goto ERR; + pton_fd = inet_pton (AF_INET6, addr, &ip_prefix->address.buffer); + break; + case AF_INET: + if (ip_prefix->len > IPV4_ADDR_LEN_BITS) + goto ERR; + pton_fd = inet_pton (AF_INET, addr, &ip_prefix->address.buffer); + break; + default: + goto ERR; + } + + // 0 = not in presentation format + // < 0 = other error (use perror) + if (pton_fd <= 0) + goto ERR; + + return 1; +ERR: + free (addr); + return -1; +} + +int +ip_prefix_ntop (const ip_prefix_t * ip_prefix, char *dst, size_t size) +{ + char ip_s[MAXSZ_IP_ADDRESS]; + const char * s = inet_ntop (ip_prefix->family, ip_prefix->address.buffer, ip_s, MAXSZ_IP_ADDRESS); + if (!s) + return -1; + size_t n = snprintf(dst, size, "%s/%d", ip_s, ip_prefix->len); + + return (n > 0 ? 1 : -1); +} + +int +ip_prefix_len (const ip_prefix_t * prefix) +{ + return prefix->len; // ip_address_len(&prefix->address, prefix->family); +} + +bool +ip_prefix_empty (const ip_prefix_t * prefix) +{ + return prefix->len == 0; +} + +int ip_prefix_to_sockaddr(const ip_prefix_t * prefix, + struct sockaddr *sa) +{ + // XXX assert len == ip_address_len + return ip_address_to_sockaddr(&prefix->address, sa, prefix->family); +} + + +/* URL */ + +#define MAXSZ_PROTO_ 8 /* inetX:// */ +#define MAXSZ_PROTO MAXSZ_PROTO_ + NULLTERM + +#define MAXSZ_URL4_ MAXSZ_PROTO_ + MAXSZ_IP4_ADDRESS_ + MAXSZ_PORT_ +#define MAXSZ_URL6_ MAXSZ_PROTO_ + MAXSZ_IP6_ADDRESS_ + MAXSZ_PORT_ +#define MAXSZ_URL_ MAXSZ_URL6_ +#define MAXSZ_URL4 MAXSZ_URL4_ + NULLTERM +#define MAXSZ_URL6 MAXSZ_URL6_ + NULLTERM +#define MAXSZ_URL MAXSZ_URL_ + NULLTERM + +int +url_snprintf(char * s, size_t size, int family, + const ip_address_t * ip_address, u16 port) +{ + char * cur = s; + int rc; + + /* Other address are currently not supported */ + if (!IS_VALID_FAMILY(family)) { + return -1; + } + + rc = snprintf(cur, s + size - cur, "inet%c://", + (family == AF_INET) ? '4' : '6'); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + + rc = ip_address_snprintf(cur, s + size - cur, ip_address, family); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + + rc = snprintf(cur, s + size - cur, ":"); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + + rc = snprintf(cur, s + size - cur, "%d", port); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + + return cur - s; +} |