Merge "res_calendar: Specialized calendars depend on symbols of general calendar."
[asterisk/asterisk.git] / main / netsock2.c
index 99f7eac..ef74ab9 100644 (file)
  * \author ViagĂ©nie <asteriskv6@viagenie.ca>
  */
 
-#include "asterisk.h"
+/*** MODULEINFO
+       <support_level>core</support_level>
+ ***/
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include "asterisk.h"
 
 #include "asterisk/config.h"
 #include "asterisk/netsock2.h"
@@ -85,7 +87,7 @@ char *ast_sockaddr_stringify_fmt(const struct ast_sockaddr *sa, int format)
                sa_tmp = sa;
        }
 
-       if ((e = getnameinfo((struct sockaddr *)&sa_tmp->ss, sa->len,
+       if ((e = getnameinfo((struct sockaddr *)&sa_tmp->ss, sa_tmp->len,
                             format & AST_SOCKADDR_STR_ADDR ? host : NULL,
                             format & AST_SOCKADDR_STR_ADDR ? sizeof(host) : 0,
                             format & AST_SOCKADDR_STR_PORT ? port : 0,
@@ -95,7 +97,14 @@ char *ast_sockaddr_stringify_fmt(const struct ast_sockaddr *sa, int format)
                return "";
        }
 
-       switch (format)  {
+       if ((format & AST_SOCKADDR_STR_REMOTE) == AST_SOCKADDR_STR_REMOTE) {
+               char *p;
+               if (ast_sockaddr_is_ipv6_link_local(sa) && (p = strchr(host, '%'))) {
+                       *p = '\0';
+               }
+       }
+
+       switch ((format & AST_SOCKADDR_STR_FORMAT_MASK))  {
        case AST_SOCKADDR_STR_DEFAULT:
                ast_str_set(&str, 0, sa_tmp->ss.ss_family == AF_INET6 ?
                                "[%s]:%s" : "%s:%s", host, port);
@@ -118,11 +127,47 @@ char *ast_sockaddr_stringify_fmt(const struct ast_sockaddr *sa, int format)
        return ast_str_buffer(str);
 }
 
+int ast_sockaddr_cidr_bits(const struct ast_sockaddr *sa)
+{
+       struct ast_sockaddr sa_ipv4;
+       const struct ast_sockaddr *sa_tmp;
+       int bits = 0;
+       int bytes;
+       int i;
+       int j;
+       char *addr;
+
+       if (ast_sockaddr_isnull(sa)) {
+               return 0;
+       }
+
+       if (ast_sockaddr_ipv4_mapped(sa, &sa_ipv4)) {
+               sa_tmp = &sa_ipv4;
+       } else {
+               sa_tmp = sa;
+       }
+
+       bytes = sa_tmp->len;
+       addr = ((struct sockaddr *)&sa_tmp->ss)->sa_data;
+
+       for (i = 0; i < bytes ; ++i) {
+               for (j = 0; j < 8; ++j) {
+                       if ((addr[i] >> j) & 1) {
+                               bits++;
+                       }
+               }
+       }
+
+       return bits;
+}
+
 int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags)
 {
        char *s = str;
+       char *orig_str = str;/* Original string in case the port presence is incorrect. */
+       char *host_end = NULL;/* Delay terminating the host in case the port presence is incorrect. */
 
-       ast_debug(5, "Splitting '%s' gives...\n", str);
+       ast_debug(5, "Splitting '%s' into...\n", str);
        *host = NULL;
        *port = NULL;
        if (*s == '[') {
@@ -130,7 +175,8 @@ int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags)
                for (; *s && *s != ']'; ++s) {
                }
                if (*s == ']') {
-                       *s++ = '\0';
+                       host_end = s;
+                       ++s;
                }
                if (*s == ':') {
                        *port = s + 1;
@@ -148,11 +194,10 @@ int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags)
                        }
                }
                if (*port) {
-                       **port = '\0';
+                       host_end = *port;
                        ++*port;
                }
        }
-       ast_debug(5, "...host '%s' and port '%s'.\n", *host, *port);
 
        switch (flags & PARSE_PORT_MASK) {
        case PARSE_PORT_IGNORE:
@@ -160,18 +205,23 @@ int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags)
                break;
        case PARSE_PORT_REQUIRE:
                if (*port == NULL) {
-                       ast_log(LOG_WARNING, "missing port\n");
+                       ast_log(LOG_WARNING, "Port missing in %s\n", orig_str);
                        return 0;
                }
                break;
        case PARSE_PORT_FORBID:
                if (*port != NULL) {
-                       ast_log(LOG_WARNING, "port disallowed\n");
+                       ast_log(LOG_WARNING, "Port disallowed in %s\n", orig_str);
                        return 0;
                }
                break;
        }
 
+       /* Can terminate the host string now if needed. */
+       if (host_end) {
+               *host_end = '\0';
+       }
+       ast_debug(5, "...host '%s' and port '%s'.\n", *host, *port ? *port : "");
        return 1;
 }
 
@@ -217,8 +267,10 @@ int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
                        "addresses. Ignoring all but the first.\n");
        }
 
-       addr->len = res->ai_addrlen;
-       memcpy(&addr->ss, res->ai_addr, addr->len);
+       if (addr) {
+               addr->len = res->ai_addrlen;
+               memcpy(&addr->ss, res->ai_addr, addr->len);
+       }
 
        freeaddrinfo(res);
 
@@ -232,8 +284,14 @@ int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str,
        char *s, *host, *port;
        int     e, i, res_cnt;
 
+       if (!str) {
+               *addrs = NULL;
+               return 0;
+       }
+
        s = ast_strdupa(str);
        if (!ast_sockaddr_split_hostport(s, &host, &port, flags)) {
+               *addrs = NULL;
                return 0;
        }
 
@@ -244,6 +302,7 @@ int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str,
        if ((e = getaddrinfo(host, port, &hints, &res))) {
                ast_log(LOG_ERROR, "getaddrinfo(\"%s\", \"%s\", ...): %s\n",
                        host, S_OR(port, "(null)"), gai_strerror(e));
+               *addrs = NULL;
                return 0;
        }
 
@@ -252,6 +311,11 @@ int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str,
                res_cnt++;
        }
 
+       if (res_cnt == 0) {
+               *addrs = NULL;
+               goto cleanup;
+       }
+
        if ((*addrs = ast_malloc(res_cnt * sizeof(struct ast_sockaddr))) == NULL) {
                res_cnt = 0;
                goto cleanup;
@@ -269,6 +333,37 @@ cleanup:
        return res_cnt;
 }
 
+int ast_sockaddr_apply_netmask(const struct ast_sockaddr *addr, const struct ast_sockaddr *netmask,
+               struct ast_sockaddr *result)
+{
+       int res = 0;
+
+       if (ast_sockaddr_is_ipv4(addr)) {
+               struct sockaddr_in result4 = { 0, };
+               struct sockaddr_in *addr4 = (struct sockaddr_in *) &addr->ss;
+               struct sockaddr_in *mask4 = (struct sockaddr_in *) &netmask->ss;
+               result4.sin_family = AF_INET;
+               result4.sin_addr.s_addr = addr4->sin_addr.s_addr & mask4->sin_addr.s_addr;
+               ast_sockaddr_from_sin(result, &result4);
+       } else if (ast_sockaddr_is_ipv6(addr)) {
+               struct sockaddr_in6 result6 = { 0, };
+               struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &addr->ss;
+               struct sockaddr_in6 *mask6 = (struct sockaddr_in6 *) &netmask->ss;
+               int i;
+               result6.sin6_family = AF_INET6;
+               for (i = 0; i < 4; ++i) {
+                       V6_WORD(&result6, i) = V6_WORD(addr6, i) & V6_WORD(mask6, i);
+               }
+               memcpy(&result->ss, &result6, sizeof(result6));
+               result->len = sizeof(result6);
+       } else {
+               /* Unsupported address scheme */
+               res = -1;
+       }
+
+       return res;
+}
+
 int ast_sockaddr_cmp(const struct ast_sockaddr *a, const struct ast_sockaddr *b)
 {
        const struct ast_sockaddr *a_tmp, *b_tmp;
@@ -336,11 +431,16 @@ int ast_sockaddr_cmp_addr(const struct ast_sockaddr *a, const struct ast_sockadd
 
 uint16_t _ast_sockaddr_port(const struct ast_sockaddr *addr, const char *file, int line, const char *func)
 {
-       if (addr->ss.ss_family == AF_INET &&
-           addr->len == sizeof(struct sockaddr_in)) {
+       /*
+        * Test addr->len first to be tolerant of an ast_sockaddr_setnull()
+        * addr.  In that case addr->len might be the only value initialized.
+        */
+       if (addr->len == sizeof(struct sockaddr_in)
+               && addr->ss.ss_family == AF_INET) {
                return ntohs(((struct sockaddr_in *)&addr->ss)->sin_port);
-       } else if (addr->ss.ss_family == AF_INET6 &&
-                addr->len == sizeof(struct sockaddr_in6)) {
+       }
+       if (addr->len == sizeof(struct sockaddr_in6)
+               && addr->ss.ss_family == AF_INET6) {
                return ntohs(((struct sockaddr_in6 *)&addr->ss)->sin6_port);
        }
        if (option_debug >= 1) {
@@ -351,11 +451,15 @@ uint16_t _ast_sockaddr_port(const struct ast_sockaddr *addr, const char *file, i
 
 void _ast_sockaddr_set_port(struct ast_sockaddr *addr, uint16_t port, const char *file, int line, const char *func)
 {
-       if (addr->ss.ss_family == AF_INET &&
-           addr->len == sizeof(struct sockaddr_in)) {
+       /*
+        * Test addr->len first to be tolerant of an ast_sockaddr_setnull()
+        * addr.  In that case addr->len might be the only value initialized.
+        */
+       if (addr->len == sizeof(struct sockaddr_in)
+               && addr->ss.ss_family == AF_INET) {
                ((struct sockaddr_in *)&addr->ss)->sin_port = htons(port);
-       } else if (addr->ss.ss_family == AF_INET6 &&
-                addr->len == sizeof(struct sockaddr_in6)) {
+       } else if (addr->len == sizeof(struct sockaddr_in6)
+               && addr->ss.ss_family == AF_INET6) {
                ((struct sockaddr_in6 *)&addr->ss)->sin6_port = htons(port);
        } else if (option_debug >= 1) {
                ast_log(__LOG_DEBUG, file, line, func,
@@ -371,8 +475,12 @@ uint32_t ast_sockaddr_ipv4(const struct ast_sockaddr *addr)
 
 int ast_sockaddr_is_ipv4(const struct ast_sockaddr *addr)
 {
-       return addr->ss.ss_family == AF_INET &&
-           addr->len == sizeof(struct sockaddr_in);
+       /*
+        * Test addr->len first to be tolerant of an ast_sockaddr_setnull()
+        * addr.  In that case addr->len might be the only value initialized.
+        */
+       return addr->len == sizeof(struct sockaddr_in)
+               && addr->ss.ss_family == AF_INET;
 }
 
 int ast_sockaddr_is_ipv4_mapped(const struct ast_sockaddr *addr)
@@ -381,19 +489,39 @@ int ast_sockaddr_is_ipv4_mapped(const struct ast_sockaddr *addr)
        return addr->len && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr);
 }
 
+int ast_sockaddr_is_ipv4_multicast(const struct ast_sockaddr *addr)
+{
+       return ((ast_sockaddr_ipv4(addr) & 0xf0000000) == 0xe0000000);
+}
+
+int ast_sockaddr_is_ipv6_link_local(const struct ast_sockaddr *addr)
+{
+       const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr->ss;
+       return ast_sockaddr_is_ipv6(addr) && IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr);
+}
+
 int ast_sockaddr_is_ipv6(const struct ast_sockaddr *addr)
 {
-       return addr->ss.ss_family == AF_INET6 &&
-           addr->len == sizeof(struct sockaddr_in6);
+       /*
+        * Test addr->len first to be tolerant of an ast_sockaddr_setnull()
+        * addr.  In that case addr->len might be the only value initialized.
+        */
+       return addr->len == sizeof(struct sockaddr_in6)
+               && addr->ss.ss_family == AF_INET6;
 }
 
 int ast_sockaddr_is_any(const struct ast_sockaddr *addr)
 {
-       return (ast_sockaddr_is_ipv4(addr) &&
-               ((const struct sockaddr_in *)&addr->ss)->sin_addr.s_addr ==
-               INADDR_ANY) ||
-           (ast_sockaddr_is_ipv6(addr) &&
-            IN6_IS_ADDR_UNSPECIFIED(&((const struct sockaddr_in6 *)&addr->ss)->sin6_addr));
+       union {
+               struct sockaddr_storage ss;
+               struct sockaddr_in sin;
+               struct sockaddr_in6 sin6;
+       } tmp_addr = {
+               .ss = addr->ss,
+       };
+
+       return (ast_sockaddr_is_ipv4(addr) && (tmp_addr.sin.sin_addr.s_addr == INADDR_ANY)) ||
+           (ast_sockaddr_is_ipv6(addr) && IN6_IS_ADDR_UNSPECIFIED(&tmp_addr.sin6.sin6_addr));
 }
 
 int ast_sockaddr_hash(const struct ast_sockaddr *addr)
@@ -414,6 +542,24 @@ int ast_sockaddr_hash(const struct ast_sockaddr *addr)
        }
 }
 
+const char *ast_transport2str(enum ast_transport transport)
+{
+       switch (transport) {
+       case AST_TRANSPORT_TLS:
+               return "TLS";
+       case AST_TRANSPORT_UDP:
+               return "UDP";
+       case AST_TRANSPORT_TCP:
+               return "TCP";
+       case AST_TRANSPORT_WS:
+               return "WS";
+       case AST_TRANSPORT_WSS:
+               return "WSS";
+       }
+
+       return "Undefined";
+}
+
 int ast_accept(int sockfd, struct ast_sockaddr *addr)
 {
        addr->len = sizeof(addr->ss);
@@ -453,14 +599,38 @@ ssize_t ast_sendto(int sockfd, const void *buf, size_t len, int flags,
 
 int ast_set_qos(int sockfd, int tos, int cos, const char *desc)
 {
-       int res;
+       int res = 0;
+       int set_tos;
+       int set_tclass;
+       struct ast_sockaddr addr;
+
+       /* If the sock address is IPv6, the TCLASS field must be set. */
+       set_tclass = !ast_getsockname(sockfd, &addr) && ast_sockaddr_is_ipv6(&addr) ? 1 : 0;
+
+       /* If the sock address is IPv4 or (IPv6 set to any address [::]) set TOS bits */
+       set_tos = (!set_tclass || (set_tclass && ast_sockaddr_is_any(&addr))) ? 1 : 0;
+
+       if (set_tos) {
+               if ((res = setsockopt(sockfd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)))) {
+                       ast_log(LOG_WARNING, "Unable to set %s DSCP TOS value to %d (may be you have no "
+                               "root privileges): %s\n", desc, tos, strerror(errno));
+               } else if (tos) {
+                       ast_verb(2, "Using %s TOS bits %d\n", desc, tos);
+               }
+       }
 
-       if ((res = setsockopt(sockfd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)))) {
-               ast_log(LOG_WARNING, "Unable to set %s TOS to %d (may be you have no "
-                       "root privileges): %s\n", desc, tos, strerror(errno));
-       } else if (tos) {
-               ast_verb(2, "Using %s TOS bits %d\n", desc, tos);
+#if defined(IPV6_TCLASS) && defined(IPPROTO_IPV6)
+       if (set_tclass) {
+               if (!ast_getsockname(sockfd, &addr) && ast_sockaddr_is_ipv6(&addr)) {
+                       if ((res = setsockopt(sockfd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)))) {
+                               ast_log(LOG_WARNING, "Unable to set %s DSCP TCLASS field to %d (may be you have no "
+                                       "root privileges): %s\n", desc, tos, strerror(errno));
+                       } else if (tos) {
+                               ast_verb(2, "Using %s TOS bits %d in TCLASS field.\n", desc, tos);
+                       }
+               }
        }
+#endif
 
 #ifdef linux
        if (setsockopt(sockfd, SOL_SOCKET, SO_PRIORITY, &cos, sizeof(cos))) {