/*
* Asterisk -- An open source telephony toolkit.
*
- * Copyright (C) 1999 - 2006, Digium, Inc.
+ * Copyright (C) 1999 - 2012, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* \author Mark Spencer <markster@digium.com>
*/
-#include "asterisk.h"
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include "asterisk.h"
#include "asterisk/network.h"
#include "asterisk/srv.h"
#if (!defined(SOLARIS) && !defined(HAVE_GETIFADDRS))
-static int get_local_address(struct in_addr *ourip)
+static int get_local_address(struct ast_sockaddr *ourip)
{
return -1;
}
/* 172.20.0.0 - 172.29.255.255, but not 172.200.0.0 - 172.255.255.255 nor 172.2.0.0 - 172.2.255.255 */
} else if (address[4] == '2' && address[6] == '.') {
score = -5;
- /* 172.30.0.0 - 172.31.255.255 */
- } else if (address[4] == '3' && address[5] <= '1') {
+ /* 172.30.0.0 - 172.31.255.255, but not 172.3.0.0 - 172.3.255.255 */
+ } else if (address[4] == '3' && (address[5] == '0' || address[5] == '1')) {
score = -5;
/* All other 172 addresses are public */
} else {
}
}
-static int get_local_address(struct in_addr *ourip)
+static int get_local_address(struct ast_sockaddr *ourip)
{
int s, res = -1;
#ifdef SOLARIS
char *buf = NULL;
int bufsz, x;
#endif /* SOLARIS */
-#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__linux__) || defined(__Darwin__)
+#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__linux__) || defined(__Darwin__) || defined(__GLIBC__)
struct ifaddrs *ifap, *ifaphead;
int rtnerr;
const struct sockaddr_in *sin;
int best_score = -100;
memset(&best_addr, 0, sizeof(best_addr));
-#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__linux__) || defined(__Darwin__)
+#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__linux__) || defined(__Darwin__) || defined(__GLIBC__)
rtnerr = getifaddrs(&ifaphead);
if (rtnerr) {
perror(NULL);
s = socket(AF_INET, SOCK_STREAM, 0);
if (s > 0) {
-#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__linux__) || defined(__Darwin__)
+#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__linux__) || defined(__Darwin__) || defined(__GLIBC__)
for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) {
if (ifap->ifa_addr && ifap->ifa_addr->sa_family == AF_INET) {
}
bufsz = ifn.lifn_count * sizeof(struct lifreq);
- if (!(buf = malloc(bufsz))) {
+ if (!(buf = ast_malloc(bufsz))) {
close(s);
return -1;
}
ifc.lifc_flags = 0;
if (ioctl(s, SIOCGLIFCONF, &ifc) < 0) {
close(s);
- free(buf);
+ ast_free(buf);
return -1;
}
}
}
- free(buf);
+ ast_free(buf);
#endif /* SOLARIS */
close(s);
#endif /* BSD_OR_LINUX */
if (res == 0 && ourip) {
- memcpy(ourip, &best_addr, sizeof(*ourip));
+ ast_sockaddr_setnull(ourip);
+ ourip->ss.ss_family = AF_INET;
+ ((struct sockaddr_in *)&ourip->ss)->sin_addr = best_addr;
}
return res;
}
}
}
+/* Free ACL list structure */
+struct ast_acl_list *ast_free_acl_list(struct ast_acl_list *acl_list)
+{
+ struct ast_acl *current;
+
+ if (!acl_list) {
+ return NULL;
+ }
+
+ AST_LIST_LOCK(acl_list);
+ while ((current = AST_LIST_REMOVE_HEAD(acl_list, list))) {
+ ast_free_ha(current->acl);
+ ast_free(current);
+ }
+ AST_LIST_UNLOCK(acl_list);
+
+ AST_LIST_HEAD_DESTROY(acl_list);
+ ast_free(acl_list);
+
+ return NULL;
+}
+
/* Copy HA structure */
void ast_copy_ha(const struct ast_ha *from, struct ast_ha *to)
{
- memcpy(&to->netaddr, &from->netaddr, sizeof(from->netaddr));
- memcpy(&to->netmask, &from->netmask, sizeof(from->netmask));
+ ast_sockaddr_copy(&to->addr, &from->addr);
+ ast_sockaddr_copy(&to->netmask, &from->netmask);
to->sense = from->sense;
}
{
struct ast_ha *new_ha;
- if ((new_ha = ast_malloc(sizeof(*new_ha)))) {
+ if ((new_ha = ast_calloc(1, sizeof(*new_ha)))) {
/* Copy from original to new object */
ast_copy_ha(original, new_ha);
}
while (start) {
current = ast_duplicate_ha(start); /* Create copy of this object */
+ if (!current) {
+ ast_free_ha(ret);
+
+ return NULL;
+ }
+
if (prev) {
prev->next = current; /* Link previous to this object */
}
return ret; /* Return start of list */
}
+static int acl_new(struct ast_acl **pointer, const char *name) {
+ struct ast_acl *acl;
+ if (!(acl = ast_calloc(1, sizeof(*acl)))) {
+ return 1;
+ }
+
+ *pointer = acl;
+ ast_copy_string(acl->name, name, ACL_NAME_LENGTH);
+ return 0;
+}
+
+struct ast_acl_list *ast_duplicate_acl_list(struct ast_acl_list *original)
+{
+ struct ast_acl_list *clone;
+ struct ast_acl *current_cursor;
+ struct ast_acl *current_clone;
+
+ /* Early return if we receive a duplication request for a NULL original. */
+ if (!original) {
+ return NULL;
+ }
+
+ if (!(clone = ast_calloc(1, sizeof(*clone)))) {
+ ast_log(LOG_ERROR, "Failed to allocate ast_acl_list struct while cloning an ACL\n");
+ return NULL;
+ }
+ AST_LIST_HEAD_INIT(clone);
+
+ AST_LIST_LOCK(original);
+
+ AST_LIST_TRAVERSE(original, current_cursor, list) {
+ if ((acl_new(¤t_clone, current_cursor->name))) {
+ ast_log(LOG_ERROR, "Failed to allocate ast_acl struct while cloning an ACL.\n");
+ ast_free_acl_list(clone);
+ clone = NULL;
+ break;
+ }
+
+ /* Copy data from original ACL to clone ACL */
+ current_clone->acl = ast_duplicate_ha_list(current_cursor->acl);
+
+ current_clone->is_invalid = current_cursor->is_invalid;
+ current_clone->is_realtime = current_cursor->is_realtime;
+
+ AST_LIST_INSERT_TAIL(clone, current_clone, list);
+
+ if (current_cursor->acl && !current_clone->acl) {
+ /* Deal with failure after adding to clone so we don't have to free
+ * current_clone separately. */
+ ast_log(LOG_ERROR, "Failed to duplicate HA list while cloning ACL.\n");
+ ast_free_acl_list(clone);
+ clone = NULL;
+ break;
+ }
+ }
+
+ AST_LIST_UNLOCK(original);
+
+ return clone;
+}
+
+/*!
+ * \brief
+ * Parse a netmask in CIDR notation
+ *
+ * \details
+ * For a mask of an IPv4 address, this should be a number between 0 and 32. For
+ * a mask of an IPv6 address, this should be a number between 0 and 128. This
+ * function creates an IPv6 ast_sockaddr from the given netmask. For masks of
+ * IPv4 addresses, this is accomplished by adding 96 to the original netmask.
+ *
+ * \param[out] addr The ast_sockaddr produced from the CIDR netmask
+ * \param is_v4 Tells if the address we are masking is IPv4.
+ * \param mask_str The CIDR mask to convert
+ * \retval -1 Failure
+ * \retval 0 Success
+ */
+static int parse_cidr_mask(struct ast_sockaddr *addr, int is_v4, const char *mask_str)
+{
+ int mask;
+
+ if (sscanf(mask_str, "%30d", &mask) != 1) {
+ return -1;
+ }
+
+ if (is_v4) {
+ struct sockaddr_in sin;
+ if (mask < 0 || mask > 32) {
+ return -1;
+ }
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ /* If mask is 0, then we already have the
+ * appropriate all 0s address in sin from
+ * the above memset.
+ */
+ if (mask != 0) {
+ sin.sin_addr.s_addr = htonl(0xFFFFFFFF << (32 - mask));
+ }
+ ast_sockaddr_from_sin(addr, &sin);
+ } else {
+ struct sockaddr_in6 sin6;
+ int i;
+ if (mask < 0 || mask > 128) {
+ return -1;
+ }
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ for (i = 0; i < 4; ++i) {
+ /* Once mask reaches 0, we don't have
+ * to explicitly set anything anymore
+ * since sin6 was zeroed out already
+ */
+ if (mask > 0) {
+ V6_WORD(&sin6, i) = htonl(0xFFFFFFFF << (mask < 32 ? (32 - mask) : 0));
+ mask -= mask < 32 ? mask : 32;
+ }
+ }
+ memcpy(&addr->ss, &sin6, sizeof(sin6));
+ addr->len = sizeof(sin6);
+ }
+
+ return 0;
+}
+
+void ast_append_acl(const char *sense, const char *stuff, struct ast_acl_list **path, int *error, int *named_acl_flag)
+{
+ struct ast_acl *acl = NULL;
+ struct ast_acl *current;
+ struct ast_acl_list *working_list;
+
+ char *tmp, *list;
+
+ /* If the ACL list is currently uninitialized, it must be initialized. */
+ if (*path == NULL) {
+ struct ast_acl_list *list;
+ list = ast_calloc(1, sizeof(*list));
+ if (!list) {
+ /* Allocation Error */
+ if (error) {
+ *error = 1;
+ }
+ return;
+ }
+
+ AST_LIST_HEAD_INIT(list);
+ *path = list;
+ }
+
+ working_list = *path;
+
+ AST_LIST_LOCK(working_list);
+
+ /* First we need to determine if we will need to add a new ACL node or if we can use an existing one. */
+ if (strncasecmp(sense, "a", 1)) {
+ /* The first element in the path should be the unnamed, base ACL. If that's the case, we use it. If not,
+ * we have to make one and link it up appropriately. */
+ current = AST_LIST_FIRST(working_list);
+
+ if (!current || !ast_strlen_zero(current->name)) {
+ if (acl_new(&acl, "")) {
+ if (error) {
+ *error = 1;
+ }
+ AST_LIST_UNLOCK(working_list);
+ return;
+ }
+ // Need to INSERT the ACL at the head here.
+ AST_LIST_INSERT_HEAD(working_list, acl, list);
+ } else {
+ /* If the first element was already the unnamed base ACL, we just use that one. */
+ acl = current;
+ }
+
+ /* With the proper ACL set for modification, we can just pass this off to the ast_ha append function. */
+ acl->acl = ast_append_ha(sense, stuff, acl->acl, error);
+
+ AST_LIST_UNLOCK(working_list);
+ return;
+ }
+
+ /* We are in ACL append mode, so we know we'll be adding one or more named ACLs. */
+ list = ast_strdupa(stuff);
+
+ while ((tmp = strsep(&list, ","))) {
+ struct ast_ha *named_ha;
+ int already_included = 0;
+
+ /* Remove leading whitespace from the string in case the user put spaces between items */
+ tmp = ast_skip_blanks(tmp);
+
+ /* The first step is to check for a duplicate */
+ AST_LIST_TRAVERSE(working_list, current, list) {
+ if (!strcasecmp(current->name, tmp)) { /* ACL= */
+ /* Inclusion of the same ACL multiple times isn't a catastrophic error, but it will raise the error flag and skip the entry. */
+ ast_log(LOG_ERROR, "Named ACL '%s' occurs multiple times in ACL definition. "
+ "Please update your ACL configuration.\n", tmp);
+ if (error) {
+ *error = 1;
+ }
+ already_included = 1;
+ break;
+ }
+ }
+
+ if (already_included) {
+ continue;
+ }
+
+ if (acl_new(&acl, tmp)) {
+ /* This is a catastrophic allocation error and we'll return immediately if this happens. */
+ if (error) {
+ *error = 1;
+ }
+ AST_LIST_UNLOCK(working_list);
+ return;
+ }
+
+ /* Attempt to grab the Named ACL we are looking for. */
+ named_ha = ast_named_acl_find(tmp, &acl->is_realtime, &acl->is_invalid);
+
+ /* Set the ACL's ast_ha to the duplicated named ACL retrieved above. */
+ acl->acl = named_ha;
+
+ /* Raise the named_acl_flag since we are adding a named ACL to the ACL container. */
+ if (named_acl_flag) {
+ *named_acl_flag = 1;
+ }
+
+ /* Now insert the new ACL at the end of the list. */
+ AST_LIST_INSERT_TAIL(working_list, acl, list);
+ }
+
+ AST_LIST_UNLOCK(working_list);
+}
+
+int ast_acl_list_is_empty(struct ast_acl_list *acl_list)
+{
+ struct ast_acl *head;
+
+ if (!acl_list) {
+ return 1;
+ }
+
+ AST_LIST_LOCK(acl_list);
+ head = AST_LIST_FIRST(acl_list);
+ AST_LIST_UNLOCK(acl_list);
+
+ if (head) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/*!
+ * \internal
+ * \brief Used by ast_append_ha to avoid ast_strdupa in a loop.
+ *
+ * \note This function is only called at debug level 3 and higher.
+ */
+static void debug_ha_sense_appended(struct ast_ha *ha)
+{
+ const char *parsed_mask = ast_strdupa(ast_sockaddr_stringify(&ha->netmask));
+
+ ast_log(LOG_DEBUG, "%s/%s sense %u appended to ACL\n",
+ ast_sockaddr_stringify(&ha->addr),
+ parsed_mask,
+ ha->sense);
+}
+
struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha *path, int *error)
{
struct ast_ha *ha;
- char *nm;
struct ast_ha *prev = NULL;
struct ast_ha *ret;
- int x;
- char *tmp = ast_strdupa(stuff);
+ char *tmp, *list = ast_strdupa(stuff);
+ char *address = NULL, *mask = NULL;
+ int addr_is_v4;
+ int allowing = strncasecmp(sense, "p", 1) ? AST_SENSE_DENY : AST_SENSE_ALLOW;
ret = path;
while (path) {
path = path->next;
}
- if (!(ha = ast_malloc(sizeof(*ha)))) {
- return ret;
- }
+ while ((tmp = strsep(&list, ","))) {
+ if (!(ha = ast_calloc(1, sizeof(*ha)))) {
+ if (error) {
+ *error = 1;
+ }
+ return ret;
+ }
- if (!(nm = strchr(tmp, '/'))) {
- /* assume /32. Yes, htonl does not do anything for this particular mask
- but we better use it to show we remember about byte order */
- ha->netmask.s_addr = htonl(0xFFFFFFFF);
- } else {
- *nm = '\0';
- nm++;
+ address = strsep(&tmp, "/");
+ if (!address) {
+ address = tmp;
+ } else {
+ mask = tmp;
+ }
- if (!strchr(nm, '.')) {
- if ((sscanf(nm, "%30d", &x) == 1) && (x >= 0) && (x <= 32)) {
- ha->netmask.s_addr = htonl(0xFFFFFFFF << (32 - x));
- } else {
- ast_log(LOG_WARNING, "Invalid CIDR in %s\n", stuff);
- ast_free(ha);
+ if (*address == '!') {
+ ha->sense = (allowing == AST_SENSE_DENY) ? AST_SENSE_ALLOW : AST_SENSE_DENY;
+ address++;
+ } else {
+ ha->sense = allowing;
+ }
+
+ if (!ast_sockaddr_parse(&ha->addr, address, PARSE_PORT_FORBID)) {
+ ast_log(LOG_WARNING, "Invalid IP address: %s\n", address);
+ ast_free_ha(ha);
+ if (error) {
+ *error = 1;
+ }
+ return ret;
+ }
+
+ /* If someone specifies an IPv4-mapped IPv6 address,
+ * we just convert this to an IPv4 ACL
+ */
+ if (ast_sockaddr_ipv4_mapped(&ha->addr, &ha->addr)) {
+ ast_log(LOG_NOTICE, "IPv4-mapped ACL network address specified. "
+ "Converting to an IPv4 ACL network address.\n");
+ }
+
+ addr_is_v4 = ast_sockaddr_is_ipv4(&ha->addr);
+
+ if (!mask) {
+ parse_cidr_mask(&ha->netmask, addr_is_v4, addr_is_v4 ? "32" : "128");
+ } else if (strchr(mask, ':') || strchr(mask, '.')) {
+ int mask_is_v4;
+ /* Mask is of x.x.x.x or x:x:x:x:x:x:x:x variety */
+ if (!ast_sockaddr_parse(&ha->netmask, mask, PARSE_PORT_FORBID)) {
+ ast_log(LOG_WARNING, "Invalid netmask: %s\n", mask);
+ ast_free_ha(ha);
if (error) {
*error = 1;
}
return ret;
}
- } else if (!inet_aton(nm, &ha->netmask)) {
- ast_log(LOG_WARNING, "Invalid mask in %s\n", stuff);
- ast_free(ha);
+ /* If someone specifies an IPv4-mapped IPv6 netmask,
+ * we just convert this to an IPv4 ACL
+ */
+ if (ast_sockaddr_ipv4_mapped(&ha->netmask, &ha->netmask)) {
+ ast_log(LOG_NOTICE, "IPv4-mapped ACL netmask specified. "
+ "Converting to an IPv4 ACL netmask.\n");
+ }
+ mask_is_v4 = ast_sockaddr_is_ipv4(&ha->netmask);
+ if (addr_is_v4 ^ mask_is_v4) {
+ ast_log(LOG_WARNING, "Address and mask are not using same address scheme.\n");
+ ast_free_ha(ha);
+ if (error) {
+ *error = 1;
+ }
+ return ret;
+ }
+ } else if (parse_cidr_mask(&ha->netmask, addr_is_v4, mask)) {
+ ast_log(LOG_WARNING, "Invalid CIDR netmask: %s\n", mask);
+ ast_free_ha(ha);
+ if (error) {
+ *error = 1;
+ }
+ return ret;
+ }
+
+ if (ast_sockaddr_apply_netmask(&ha->addr, &ha->netmask, &ha->addr)) {
+ /* This shouldn't happen because ast_sockaddr_parse would
+ * have failed much earlier on an unsupported address scheme
+ */
+ char *failmask = ast_strdupa(ast_sockaddr_stringify(&ha->netmask));
+ char *failaddr = ast_strdupa(ast_sockaddr_stringify(&ha->addr));
+ ast_log(LOG_WARNING, "Unable to apply netmask %s to address %s\n", failmask, failaddr);
+ ast_free_ha(ha);
if (error) {
*error = 1;
}
return ret;
}
+
+ if (prev) {
+ prev->next = ha;
+ } else {
+ ret = ha;
+ }
+ prev = ha;
+
+ if (DEBUG_ATLEAST(3)) {
+ debug_ha_sense_appended(ha);
+ }
}
- if (!inet_aton(tmp, &ha->netaddr)) {
- ast_log(LOG_WARNING, "Invalid IP address in %s\n", stuff);
- ast_free(ha);
- if (error) {
- *error = 1;
+ return ret;
+}
+
+void ast_ha_join(const struct ast_ha *ha, struct ast_str **buf)
+{
+ for (; ha; ha = ha->next) {
+ ast_str_append(buf, 0, "%s%s/",
+ ha->sense == AST_SENSE_ALLOW ? "!" : "",
+ ast_sockaddr_stringify_addr(&ha->addr));
+ /* Separated to avoid duplicating stringified addresses. */
+ ast_str_append(buf, 0, "%s", ast_sockaddr_stringify_addr(&ha->netmask));
+ if (ha->next) {
+ ast_str_append(buf, 0, ",");
}
- return ret;
}
+}
- ha->netaddr.s_addr &= ha->netmask.s_addr;
+void ast_ha_join_cidr(const struct ast_ha *ha, struct ast_str **buf)
+{
+ for (; ha; ha = ha->next) {
+ const char *addr = ast_sockaddr_stringify_addr(&ha->addr);
+ ast_str_append(buf, 0, "%s%s/%d",
+ ha->sense == AST_SENSE_ALLOW ? "!" : "",
+ addr, ast_sockaddr_cidr_bits(&ha->netmask));
+ if (ha->next) {
+ ast_str_append(buf, 0, ",");
+ }
+ }
+}
- ha->sense = strncasecmp(sense, "p", 1) ? AST_SENSE_DENY : AST_SENSE_ALLOW;
+enum ast_acl_sense ast_apply_acl(struct ast_acl_list *acl_list, const struct ast_sockaddr *addr, const char *purpose)
+{
+ struct ast_acl *acl;
- ha->next = NULL;
- if (prev) {
- prev->next = ha;
- } else {
- ret = ha;
+ /* If the list is NULL, there are no rules, so we'll allow automatically. */
+ if (!acl_list) {
+ return AST_SENSE_ALLOW;
}
- ast_debug(1, "%s/%s sense %d appended to acl for peer\n", ast_strdupa(ast_inet_ntoa(ha->netaddr)), ast_strdupa(ast_inet_ntoa(ha->netmask)), ha->sense);
+ AST_LIST_LOCK(acl_list);
- return ret;
+ AST_LIST_TRAVERSE(acl_list, acl, list) {
+ if (acl->is_invalid) {
+ /* In this case, the baseline ACL shouldn't ever trigger this, but if that somehow happens, it'll still be shown. */
+ ast_log(LOG_WARNING, "%sRejecting '%s' due to use of an invalid ACL '%s'.\n", purpose ? purpose : "", ast_sockaddr_stringify_addr(addr),
+ ast_strlen_zero(acl->name) ? "(BASELINE)" : acl->name);
+ AST_LIST_UNLOCK(acl_list);
+ return AST_SENSE_DENY;
+ }
+
+ if (acl->acl) {
+ if (ast_apply_ha(acl->acl, addr) == AST_SENSE_DENY) {
+ ast_log(LOG_NOTICE, "%sRejecting '%s' due to a failure to pass ACL '%s'\n", purpose ? purpose : "", ast_sockaddr_stringify_addr(addr),
+ ast_strlen_zero(acl->name) ? "(BASELINE)" : acl->name);
+ AST_LIST_UNLOCK(acl_list);
+ return AST_SENSE_DENY;
+ }
+ }
+ }
+
+ AST_LIST_UNLOCK(acl_list);
+
+ return AST_SENSE_ALLOW;
}
-int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin)
+enum ast_acl_sense ast_apply_ha(const struct ast_ha *ha, const struct ast_sockaddr *addr)
{
/* Start optimistic */
- int res = AST_SENSE_ALLOW;
- while (ha) {
+ enum ast_acl_sense res = AST_SENSE_ALLOW;
+ const struct ast_ha *current_ha;
+
+ for (current_ha = ha; current_ha; current_ha = current_ha->next) {
+ struct ast_sockaddr result;
+ struct ast_sockaddr mapped_addr;
+ const struct ast_sockaddr *addr_to_use;
#if 0 /* debugging code */
char iabuf[INET_ADDRSTRLEN];
char iabuf2[INET_ADDRSTRLEN];
/* DEBUG */
- ast_copy_string(iabuf, ast_inet_ntoa(sin->sin_addr), sizeof(iabuf));
- ast_copy_string(iabuf2, ast_inet_ntoa(ha->netaddr), sizeof(iabuf2));
+ ast_copy_string(iabuf, ast_sockaddr_stringify(addr), sizeof(iabuf));
+ ast_copy_string(iabuf2, ast_sockaddr_stringify(¤t_ha->addr), sizeof(iabuf2));
ast_debug(1, "##### Testing %s with %s\n", iabuf, iabuf2);
#endif
+ if (ast_sockaddr_is_ipv4(¤t_ha->addr)) {
+ if (ast_sockaddr_is_ipv6(addr)) {
+ if (ast_sockaddr_is_ipv4_mapped(addr)) {
+ /* IPv4 ACLs apply to IPv4-mapped addresses */
+ if (!ast_sockaddr_ipv4_mapped(addr, &mapped_addr)) {
+ ast_log(LOG_ERROR, "%s provided to ast_sockaddr_ipv4_mapped could not be converted. That shouldn't be possible.\n",
+ ast_sockaddr_stringify(addr));
+ continue;
+ }
+ addr_to_use = &mapped_addr;
+ } else {
+ /* An IPv4 ACL does not apply to an IPv6 address */
+ continue;
+ }
+ } else {
+ /* Address is IPv4 and ACL is IPv4. No biggie */
+ addr_to_use = addr;
+ }
+ } else {
+ if (ast_sockaddr_is_ipv6(addr) && !ast_sockaddr_is_ipv4_mapped(addr)) {
+ addr_to_use = addr;
+ } else {
+ /* Address is IPv4 or IPv4 mapped but ACL is IPv6. Skip */
+ continue;
+ }
+ }
+
/* For each rule, if this address and the netmask = the net address
apply the current rule */
- if ((sin->sin_addr.s_addr & ha->netmask.s_addr) == ha->netaddr.s_addr) {
- res = ha->sense;
+ if (ast_sockaddr_apply_netmask(addr_to_use, ¤t_ha->netmask, &result)) {
+ /* Unlikely to happen since we know the address to be IPv4 or IPv6 */
+ continue;
+ }
+ if (!ast_sockaddr_cmp_addr(&result, ¤t_ha->addr)) {
+ res = current_ha->sense;
}
- ha = ha->next;
}
return res;
}
-int ast_get_ip_or_srv(struct sockaddr_in *sin, const char *value, const char *service)
+static int resolve_first(struct ast_sockaddr *addr, const char *name, int flag,
+ int family)
+{
+ struct ast_sockaddr *addrs;
+ int addrs_cnt;
+
+ addrs_cnt = ast_sockaddr_resolve(&addrs, name, flag, family);
+ if (addrs_cnt > 0) {
+ if (addrs_cnt > 1) {
+ ast_debug(1, "Multiple addresses. Using the first only\n");
+ }
+ ast_sockaddr_copy(addr, &addrs[0]);
+ ast_free(addrs);
+ } else {
+ ast_log(LOG_WARNING, "Unable to lookup '%s'\n", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+int ast_get_ip_or_srv(struct ast_sockaddr *addr, const char *hostname, const char *service)
{
- struct hostent *hp;
- struct ast_hostent ahp;
char srv[256];
char host[256];
- int tportno = ntohs(sin->sin_port);
+ int srv_ret = 0;
+ int tportno;
+
if (service) {
- snprintf(srv, sizeof(srv), "%s.%s", service, value);
- if (ast_get_srv(NULL, host, sizeof(host), &tportno, srv) > 0) {
- sin->sin_port = htons(tportno);
- value = host;
+ snprintf(srv, sizeof(srv), "%s.%s", service, hostname);
+ if ((srv_ret = ast_get_srv(NULL, host, sizeof(host), &tportno, srv)) > 0) {
+ hostname = host;
}
}
- if ((hp = ast_gethostbyname(value, &ahp))) {
- memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
- } else {
- ast_log(LOG_WARNING, "Unable to lookup '%s'\n", value);
+
+ if (resolve_first(addr, hostname, PARSE_PORT_FORBID, addr->ss.ss_family) != 0) {
return -1;
}
+
+ if (srv_ret > 0) {
+ ast_sockaddr_set_port(addr, tportno);
+ }
+
return 0;
}
return "unknown";
}
-int ast_get_ip(struct sockaddr_in *sin, const char *value)
+int ast_get_ip(struct ast_sockaddr *addr, const char *hostname)
{
- return ast_get_ip_or_srv(sin, value, NULL);
+ return ast_get_ip_or_srv(addr, hostname, NULL);
}
-int ast_ouraddrfor(struct in_addr *them, struct in_addr *us)
+int ast_ouraddrfor(const struct ast_sockaddr *them, struct ast_sockaddr *us)
{
+ /*
+ * We must create the errno string before creating the address
+ * string because it could wipe out errno on the error return
+ * paths.
+ */
+ const char *sock_err;
+ int port;
int s;
- struct sockaddr_in sin;
- socklen_t slen;
- if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
- ast_log(LOG_ERROR, "Cannot create socket\n");
+ /* Preserve our original address port */
+ port = ast_sockaddr_port(us);
+
+ s = socket(ast_sockaddr_is_ipv6(them) ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ sock_err = ast_strdupa(strerror(errno));
+ ast_log(LOG_ERROR, "Cannot create socket to %s: %s\n",
+ ast_sockaddr_stringify_addr(them), sock_err);
return -1;
}
- sin.sin_family = AF_INET;
- sin.sin_port = 5060;
- sin.sin_addr = *them;
- if (connect(s, (struct sockaddr *)&sin, sizeof(sin))) {
- ast_log(LOG_WARNING, "Cannot connect\n");
+
+ if (ast_connect(s, them)) {
+ sock_err = ast_strdupa(strerror(errno));
+ ast_log(LOG_WARNING, "Cannot connect to %s: %s\n",
+ ast_sockaddr_stringify_addr(them), sock_err);
close(s);
return -1;
}
- slen = sizeof(sin);
- if (getsockname(s, (struct sockaddr *)&sin, &slen)) {
- ast_log(LOG_WARNING, "Cannot get socket name\n");
+ if (ast_getsockname(s, us)) {
+ sock_err = ast_strdupa(strerror(errno));
+ ast_log(LOG_WARNING, "Cannot get socket name for connection to %s: %s\n",
+ ast_sockaddr_stringify_addr(them), sock_err);
close(s);
return -1;
}
close(s);
- ast_debug(3, "Found IP address for this socket\n");
- *us = sin.sin_addr;
+
+ ast_sockaddr_set_port(us, port);
+
+ ast_debug(3, "For destination '%s', our source address is '%s'.\n",
+ ast_strdupa(ast_sockaddr_stringify_addr(them)),
+ ast_strdupa(ast_sockaddr_stringify_addr(us)));
+
return 0;
}
-int ast_find_ourip(struct in_addr *ourip, struct sockaddr_in bindaddr)
+int ast_find_ourip(struct ast_sockaddr *ourip, const struct ast_sockaddr *bindaddr, int family)
{
char ourhost[MAXHOSTNAMELEN] = "";
- struct ast_hostent ahp;
- struct hostent *hp;
- struct in_addr saddr;
+ struct ast_sockaddr root;
+ int res, port = ast_sockaddr_port(ourip);
/* just use the bind address if it is nonzero */
- if (ntohl(bindaddr.sin_addr.s_addr)) {
- memcpy(ourip, &bindaddr.sin_addr, sizeof(*ourip));
+ if (!ast_sockaddr_is_any(bindaddr)) {
+ ast_sockaddr_copy(ourip, bindaddr);
ast_debug(3, "Attached to given IP address\n");
return 0;
}
if (gethostname(ourhost, sizeof(ourhost) - 1)) {
ast_log(LOG_WARNING, "Unable to get hostname\n");
} else {
- if ((hp = ast_gethostbyname(ourhost, &ahp))) {
- memcpy(ourip, hp->h_addr, sizeof(*ourip));
- ast_debug(3, "Found one IP address based on local hostname %s.\n", ourhost);
+ if (resolve_first(ourip, ourhost, PARSE_PORT_FORBID, family) == 0) {
+ /* reset port since resolve_first wipes this out */
+ ast_sockaddr_set_port(ourip, port);
return 0;
}
}
ast_debug(3, "Trying to check A.ROOT-SERVERS.NET and get our IP address for that connection\n");
/* A.ROOT-SERVERS.NET. */
- if (inet_aton("198.41.0.4", &saddr) && !ast_ouraddrfor(&saddr, ourip)) {
+ if (!resolve_first(&root, "A.ROOT-SERVERS.NET", PARSE_PORT_FORBID, 0) &&
+ !ast_ouraddrfor(&root, ourip)) {
+ /* reset port since resolve_first wipes this out */
+ ast_sockaddr_set_port(ourip, port);
return 0;
}
- return get_local_address(ourip);
+ res = get_local_address(ourip);
+ ast_sockaddr_set_port(ourip, port);
+ return res;
}
-