/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ /* * Copyright (C) 2019 by Sukchan Lee * * This file is part of Open5GS. * * 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. */ #include "core-config-private.h" #if HAVE_ARPA_INET_H #include #endif #if HAVE_CTYPE_H #include #endif #if HAVE_IFADDRS_H #include #endif #if HAVE_NETDB_H #include #endif #include "ogs-core.h" #undef OGS_LOG_DOMAIN #define OGS_LOG_DOMAIN __ogs_sock_domain /* If you want to use getnameinfo, * you need to consider DNS query delay (about 10 seconds) */ #if 0 int ogs_getnameinfo( char *hostname, socklen_t hostname_len, ogs_sockaddr_t *addr, int flags) { ogs_assert(hostname); ogs_assert(addr); return getnameinfo(&addr->sa, ogs_sockaddr_len(addr), hostname, hostname_len, NULL, 0, flags != 0 ? flags : NI_NAMEREQD); } #endif int ogs_getaddrinfo(ogs_sockaddr_t **sa_list, int family, const char *hostname, uint16_t port, int flags) { *sa_list = NULL; return ogs_addaddrinfo(sa_list, family, hostname, port, flags); } int ogs_freeaddrinfo(ogs_sockaddr_t *sa_list) { ogs_sockaddr_t *next = NULL, *addr = NULL; addr = sa_list; while (addr) { next = addr->next; if (addr->hostname) ogs_free(addr->hostname); ogs_free(addr); addr = next; } return OGS_OK; } int ogs_addaddrinfo(ogs_sockaddr_t **sa_list, int family, const char *hostname, uint16_t port, int flags) { int rc; char service[NI_MAXSERV]; struct addrinfo hints, *ai, *ai_list; ogs_sockaddr_t *prev; char buf[OGS_ADDRSTRLEN]; ogs_assert(sa_list); memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = flags; ogs_snprintf(service, sizeof(service), "%u", port); rc = getaddrinfo(hostname, service, &hints, &ai_list); if (rc != 0) { ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, "getaddrinfo(%d:%s:%d:0x%x) failed", family, hostname, port, flags); return OGS_ERROR; } prev = NULL; if (*sa_list) { prev = *sa_list; while(prev->next) prev = prev->next; } for (ai = ai_list; ai; ai = ai->ai_next) { ogs_sockaddr_t *new, tmp; if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; new = ogs_calloc(1, sizeof(ogs_sockaddr_t)); ogs_expect_or_return_val(new, OGS_ERROR); memcpy(&new->sa, ai->ai_addr, ai->ai_addrlen); new->ogs_sin_port = htobe16(port); if (hostname) { if (ogs_inet_pton(ai->ai_family, hostname, &tmp) == OGS_OK) { /* It's a valid IP address */ ogs_debug("addr:%s, port:%d", OGS_ADDR(new, buf), port); } else { /* INVALID IP address! We assume it is a hostname */ new->hostname = ogs_strdup(hostname); ogs_assert(new->hostname); ogs_debug("name:%s, port:%d", new->hostname, port); } } if (!prev) *sa_list = new; else prev->next = new; prev = new; } freeaddrinfo(ai_list); if (prev == NULL) { ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, "ogs_getaddrinfo(%d:%s:%d:%d) failed", family, hostname, port, flags); return OGS_ERROR; } return OGS_OK; } int ogs_filteraddrinfo(ogs_sockaddr_t **sa_list, int family) { ogs_sockaddr_t *addr = NULL, *prev = NULL, *next = NULL; ogs_assert(sa_list); prev = NULL; addr = *sa_list; while (addr) { next = addr->next; if (addr->ogs_sa_family != family) { if (prev) prev->next = addr->next; else *sa_list = addr->next; if (addr->hostname) ogs_free(addr->hostname); ogs_free(addr); } else { prev = addr; } addr = next; } return OGS_OK; } int ogs_copyaddrinfo(ogs_sockaddr_t **dst, const ogs_sockaddr_t *src) { ogs_sockaddr_t *d; const ogs_sockaddr_t *s; for (*dst = d = NULL, s = src; s; s = s->next) { if (!d) { *dst = d = ogs_memdup(s, sizeof *s); ogs_expect_or_return_val(*dst, OGS_ERROR); } else { d = d->next = ogs_memdup(s, sizeof *s); ogs_expect_or_return_val(d, OGS_ERROR); } if (s->hostname) { if (s == src || s->hostname != src->hostname) { d->hostname = ogs_strdup(s->hostname); ogs_expect_or_return_val(d->hostname, OGS_ERROR); } else { d->hostname = (*dst)->hostname; } } } return OGS_OK; } int ogs_sortaddrinfo(ogs_sockaddr_t **sa_list, int family) { ogs_sockaddr_t *head = NULL, *addr = NULL, *new = NULL, *old = NULL; ogs_assert(sa_list); old = *sa_list; while (old) { addr = old; old = old->next; if (head == NULL || addr->ogs_sa_family == family) { addr->next = head; head = addr; } else { new = head; while(new->next != NULL && new->next->ogs_sa_family != family) { new = new->next; } addr->next = new->next; new->next = addr; } } *sa_list = head; return OGS_OK; } ogs_sockaddr_t *ogs_link_local_addr(const char *dev, const ogs_sockaddr_t *sa) { #if defined(HAVE_GETIFADDRS) struct ifaddrs *iflist, *cur; int rc; rc = getifaddrs(&iflist); if (rc != 0) { ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, "getifaddrs failed"); return NULL; } for (cur = iflist; cur != NULL; cur = cur->ifa_next) { ogs_sockaddr_t *ifa_addr = NULL; ogs_sockaddr_t *addr = NULL; ifa_addr = (ogs_sockaddr_t *)cur->ifa_addr; if (ifa_addr == NULL) /* may happen with ppp interfaces */ continue; if (ifa_addr->ogs_sa_family == AF_INET) continue; if (!IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6.sin6_addr)) continue; if (dev && strcmp(dev, cur->ifa_name) != 0) continue; if (sa && memcmp(&sa->sin6.sin6_addr, &ifa_addr->sin6.sin6_addr, sizeof(struct in6_addr)) != 0) continue; addr = ogs_calloc(1, sizeof(ogs_sockaddr_t)); ogs_expect_or_return_val(addr, NULL); ogs_assert(addr); memcpy(&addr->sa, cur->ifa_addr, ogs_sockaddr_len(cur->ifa_addr)); freeifaddrs(iflist); return addr; } freeifaddrs(iflist); #endif return NULL; } ogs_sockaddr_t *ogs_link_local_addr_by_dev(const char *dev) { ogs_assert(dev); return ogs_link_local_addr(dev, NULL); } ogs_sockaddr_t *ogs_link_local_addr_by_sa(const ogs_sockaddr_t *sa) { ogs_assert(sa); return ogs_link_local_addr(NULL, sa); } int ogs_filter_ip_version(ogs_sockaddr_t **addr, int no_ipv4, int no_ipv6, int prefer_ipv4) { int rv; if (no_ipv4 == 1) { rv = ogs_filteraddrinfo(addr, AF_INET6); ogs_assert(rv == OGS_OK); } if (no_ipv6 == 1) { rv = ogs_filteraddrinfo(addr, AF_INET); ogs_assert(rv == OGS_OK); } if (prefer_ipv4 == 1) { rv = ogs_sortaddrinfo(addr, AF_INET); ogs_assert(rv == OGS_OK); } else { rv = ogs_sortaddrinfo(addr, AF_INET6); ogs_assert(rv == OGS_OK); } return OGS_OK; } const char *ogs_inet_ntop(void *sa, char *buf, int buflen) { int family; ogs_sockaddr_t *sockaddr = NULL; sockaddr = sa; ogs_assert(sockaddr); ogs_assert(buf); ogs_assert(buflen >= OGS_ADDRSTRLEN); family = sockaddr->ogs_sa_family; switch (family) { case AF_INET: return inet_ntop(family, &sockaddr->sin.sin_addr, buf, INET_ADDRSTRLEN); case AF_INET6: return inet_ntop(family, &sockaddr->sin6.sin6_addr, buf, INET6_ADDRSTRLEN); default: ogs_fatal("Unknown family(%d)", family); ogs_abort(); return NULL; } } int ogs_inet_pton(int family, const char *src, void *sa) { ogs_sockaddr_t *dst = NULL; ogs_assert(src); dst = sa; ogs_assert(dst); dst->ogs_sa_family = family; switch(family) { case AF_INET: return inet_pton(family, src, &dst->sin.sin_addr) == 1 ? OGS_OK : OGS_ERROR; case AF_INET6: return inet_pton(family, src, &dst->sin6.sin6_addr) == 1 ? OGS_OK : OGS_ERROR; default: ogs_fatal("Unknown family(%d)", family); ogs_abort(); return OGS_ERROR; } } socklen_t ogs_sockaddr_len(const void *sa) { const ogs_sockaddr_t *sockaddr = sa; ogs_assert(sa); switch(sockaddr->ogs_sa_family) { case AF_INET: return sizeof(struct sockaddr_in); case AF_INET6: return sizeof(struct sockaddr_in6); default: ogs_fatal("Unknown family(%d)", sockaddr->ogs_sa_family); ogs_abort(); return OGS_ERROR; } } bool ogs_sockaddr_is_equal(void *p, void *q) { ogs_sockaddr_t *a, *b; a = p; ogs_assert(a); b = q; ogs_assert(b); if (a->ogs_sa_family != b->ogs_sa_family) return false; switch (a->ogs_sa_family) { case AF_INET: if (a->sin.sin_port != b->sin.sin_port) return false; if (memcmp(&a->sin.sin_addr, &b->sin.sin_addr, sizeof(struct in_addr)) != 0) return false; return true; case AF_INET6: if (a->sin6.sin6_port != b->sin6.sin6_port) return false; if (memcmp(&a->sin6.sin6_addr, &b->sin6.sin6_addr, sizeof(struct in6_addr)) != 0) return false; return true; default: ogs_error("Unexpected address faimily %u", a->ogs_sa_family); ogs_abort(); } } static int parse_network(ogs_ipsubnet_t *ipsub, const char *network) { /* legacy syntax for ip addrs: a.b.c. ==> a.b.c.0/24 for example */ int shift; char *s, *t; int octet; char buf[sizeof "255.255.255.255"]; if (strlen(network) < sizeof buf) strcpy(buf, network); else return OGS_ERROR; /* parse components */ s = buf; ipsub->sub[0] = 0; ipsub->mask[0] = 0; shift = 24; while (*s) { t = s; if (!isdigit(*t)) return OGS_ERROR; while (isdigit(*t)) ++t; if (*t == '.') *t++ = 0; else if (*t) return OGS_ERROR; if (shift < 0) return OGS_ERROR; octet = atoi(s); if (octet < 0 || octet > 255) return OGS_ERROR; ipsub->sub[0] |= octet << shift; ipsub->mask[0] |= 0xFFUL << shift; s = t; shift -= 8; } ipsub->sub[0] = be32toh(ipsub->sub[0]); ipsub->mask[0] = be32toh(ipsub->mask[0]); ipsub->family = AF_INET; return OGS_OK; } /* return values: * CORE_EINVAL not an IP address; caller should see * if it is something else * CORE_BADIP IP address portion is is not valid * CORE_BADMASK mask portion is not valid */ static int parse_ip( ogs_ipsubnet_t *ipsub, const char *ipstr, int network_allowed) { /* supported flavors of IP: * * . IPv6 numeric address string (e.g., "fe80::1") * * IMPORTANT: Don't store IPv4-mapped IPv6 address as an IPv6 address. * * . IPv4 numeric address string (e.g., "127.0.0.1") * * . IPv4 network string (e.g., "9.67") * * IMPORTANT: This network form is only allowed if network_allowed is on. */ int rc; rc = inet_pton(AF_INET6, ipstr, ipsub->sub); if (rc == 1) { if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ipsub->sub)) { /* ipsubnet_test() assumes that we don't create IPv4-mapped IPv6 * addresses; this of course forces the user to specify * IPv4 addresses in a.b.c.d style instead of ::ffff:a.b.c.d style. */ ogs_error("Cannot support IPv4-mapped IPv6: " "Use IPv4 address in a.b.c.d style " "instead of ::ffff:a.b.c.d style"); return OGS_ERROR; } ipsub->family = AF_INET6; } else { rc = inet_pton(AF_INET, ipstr, ipsub->sub); if (rc == 1) { ipsub->family = AF_INET; } } if (rc != 1) { if (network_allowed) return parse_network(ipsub, ipstr); else return OGS_ERROR; } return OGS_OK; } static int looks_like_ip(const char *ipstr) { if (strlen(ipstr) == 0) return 0; if (strchr(ipstr, ':')) { /* definitely not a hostname; * assume it is intended to be an IPv6 address */ return 1; } /* simple IPv4 address string check */ while ((*ipstr == '.') || isdigit(*ipstr)) ipstr++; return (*ipstr == '\0'); } static void fix_subnet(ogs_ipsubnet_t *ipsub) { /* in case caller specified more bits in network address than are * valid according to the mask, turn off the extra bits */ int i; for (i = 0; i < sizeof ipsub->mask / sizeof(int32_t); i++) ipsub->sub[i] &= ipsub->mask[i]; } /* be sure not to store any IPv4 address as a v4-mapped IPv6 address */ int ogs_ipsubnet(ogs_ipsubnet_t *ipsub, const char *ipstr, const char *mask_or_numbits) { int rv; char *endptr; long bits, maxbits = 32; ogs_assert(ipsub); ogs_assert(ipstr); /* filter out stuff which doesn't look remotely like an IP address; * this helps callers like mod_access which have a syntax allowing * hostname or IP address; * CORE_EINVAL tells the caller that it was probably not intended * to be an IP address */ if (!looks_like_ip(ipstr)) { ogs_error("looks_like_ip(%s, %s) failed", ipstr, mask_or_numbits); return OGS_ERROR; } /* assume ipstr is an individual IP address, not a subnet */ memset(ipsub->mask, 0xFF, sizeof ipsub->mask); rv = parse_ip(ipsub, ipstr, mask_or_numbits == NULL); if (rv != OGS_OK) { ogs_error("parse_ip(%s, %s) failed", ipstr, mask_or_numbits); return rv; } if (mask_or_numbits) { if (ipsub->family == AF_INET6) { maxbits = 128; } bits = strtol(mask_or_numbits, &endptr, 10); if (*endptr == '\0' && bits > 0 && bits <= maxbits) { /* valid num-bits string; fill in mask appropriately */ int cur_entry = 0; int32_t cur_bit_value; memset(ipsub->mask, 0, sizeof ipsub->mask); while (bits > 32) { ipsub->mask[cur_entry] = 0xFFFFFFFF; /* all 32 bits */ bits -= 32; ++cur_entry; } cur_bit_value = 0x80000000; while (bits) { ipsub->mask[cur_entry] |= cur_bit_value; --bits; cur_bit_value /= 2; } ipsub->mask[cur_entry] = htobe32(ipsub->mask[cur_entry]); } else if (inet_pton(AF_INET, mask_or_numbits, ipsub->mask) == 1 && ipsub->family == AF_INET) { /* valid IPv4 netmask */ } else { ogs_error("Bad netmask %s", mask_or_numbits); return OGS_ERROR; } } fix_subnet(ipsub); return OGS_OK; } char *ogs_gethostname(ogs_sockaddr_t *addr) { return addr->hostname; } char *ogs_ipstrdup(ogs_sockaddr_t *addr) { char buf[OGS_ADDRSTRLEN + 1]; ogs_assert(addr); memset(buf, 0, sizeof(buf)); OGS_ADDR(addr, buf); return ogs_strdup(buf); }