res_pjproject: Add utility functions to convert between socket structures
authorSean Bright <sean.bright@gmail.com>
Tue, 28 Aug 2018 13:42:13 +0000 (09:42 -0400)
committerSean Bright <sean.bright@gmail.com>
Thu, 6 Sep 2018 18:30:12 +0000 (13:30 -0500)
Currently, to convert from a pj_sockaddr to an ast_sockaddr, the address
needs to be rendered to a string and then parsed into the correct
structure. This also involves a call to getaddrinfo(3). The same is true
for the inverse operation.

Instead, because we know the internal structure of both ast_sockaddr and
pj_sockaddr, we can translate directly between the two without the
need for an intermediate string.

Change-Id: If0fc4bba9643f755604c6ffbb0d7cc46020bc761

include/asterisk/res_pjproject.h
res/res_pjproject.c
res/res_pjproject.exports.in

index 17ebdd2..d195c0f 100644 (file)
@@ -22,6 +22,8 @@
 #include <pj/types.h>
 #include <pj/pool.h>
 
+struct ast_sockaddr;
+
 /*!
  * \brief Retrieve a pjproject build option
  *
@@ -97,4 +99,28 @@ void ast_pjproject_caching_pool_init(pj_caching_pool *cp,
  */
 void ast_pjproject_caching_pool_destroy(pj_caching_pool *cp);
 
+/*!
+ * \brief Fill a pj_sockaddr from an ast_sockaddr
+ * \since 13.24.0
+ *
+ * \param addr The source address to copy
+ * \param pjaddr The target address to receive the copied address
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sockaddr_to_pj_sockaddr(const struct ast_sockaddr *addr, pj_sockaddr *pjaddr);
+
+/*!
+ * \brief Fill an ast_sockaddr from a pj_sockaddr
+ * \since 13.24.0
+ *
+ * \param addr The target address to receive the copied address
+ * \param pjaddr The source address to copy
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sockaddr_from_pj_sockaddr(struct ast_sockaddr *addr, const pj_sockaddr *pjaddr);
+
 #endif /* _RES_PJPROJECT_H */
index ebd71b9..5b133ec 100644 (file)
 #include "asterisk/res_pjproject.h"
 #include "asterisk/vector.h"
 #include "asterisk/sorcery.h"
+#include "asterisk/test.h"
+#include "asterisk/netsock2.h"
 
 static struct ast_sorcery *pjproject_sorcery;
 static pj_log_func *log_cb_orig;
@@ -471,6 +473,176 @@ void ast_pjproject_caching_pool_destroy(pj_caching_pool *cp)
        pj_caching_pool_destroy(cp);
 }
 
+int ast_sockaddr_to_pj_sockaddr(const struct ast_sockaddr *addr, pj_sockaddr *pjaddr)
+{
+       if (addr->ss.ss_family == AF_INET) {
+               struct sockaddr_in *sin = (struct sockaddr_in *) &addr->ss;
+               pjaddr->ipv4.sin_family = pj_AF_INET();
+               pjaddr->ipv4.sin_addr   = sin->sin_addr;
+               pjaddr->ipv4.sin_port   = sin->sin_port;
+       } else if (addr->ss.ss_family == AF_INET6) {
+               struct sockaddr_in6 *sin = (struct sockaddr_in6 *) &addr->ss;
+               pjaddr->ipv6.sin6_family   = pj_AF_INET6();
+               pjaddr->ipv6.sin6_port     = sin->sin6_port;
+               pjaddr->ipv6.sin6_flowinfo = sin->sin6_flowinfo;
+               pjaddr->ipv6.sin6_scope_id = sin->sin6_scope_id;
+               memcpy(&pjaddr->ipv6.sin6_addr, &sin->sin6_addr, sizeof(pjaddr->ipv6.sin6_addr));
+       } else {
+               memset(pjaddr, 0, sizeof(*pjaddr));
+               return -1;
+       }
+       return 0;
+}
+
+int ast_sockaddr_from_pj_sockaddr(struct ast_sockaddr *addr, const pj_sockaddr *pjaddr)
+{
+       if (pjaddr->addr.sa_family == pj_AF_INET()) {
+               struct sockaddr_in *sin = (struct sockaddr_in *) &addr->ss;
+               sin->sin_family = AF_INET;
+               sin->sin_addr   = pjaddr->ipv4.sin_addr;
+               sin->sin_port   = pjaddr->ipv4.sin_port;
+               addr->len = sizeof(struct sockaddr_in);
+       } else if (pjaddr->addr.sa_family == pj_AF_INET6()) {
+               struct sockaddr_in6 *sin = (struct sockaddr_in6 *) &addr->ss;
+               sin->sin6_family   = AF_INET6;
+               sin->sin6_port     = pjaddr->ipv6.sin6_port;
+               sin->sin6_flowinfo = pjaddr->ipv6.sin6_flowinfo;
+               sin->sin6_scope_id = pjaddr->ipv6.sin6_scope_id;
+               memcpy(&sin->sin6_addr, &pjaddr->ipv6.sin6_addr, sizeof(sin->sin6_addr));
+               addr->len = sizeof(struct sockaddr_in6);
+       } else {
+               memset(addr, 0, sizeof(*addr));
+               return -1;
+       }
+       return 0;
+}
+
+#ifdef TEST_FRAMEWORK
+static void fill_with_garbage(void *x, ssize_t len)
+{
+       unsigned char *w = x;
+       while (len > 0) {
+               int r = ast_random();
+               memcpy(w, &r, len > sizeof(r) ? sizeof(r) : len);
+               w += sizeof(r);
+               len -= sizeof(r);
+       }
+}
+
+AST_TEST_DEFINE(ast_sockaddr_to_pj_sockaddr_test)
+{
+       char *candidates[] = {
+               "127.0.0.1:5555",
+               "[::]:4444",
+               "192.168.0.100:0",
+               "[fec0::1:80]:0",
+               "[fec0::1]:80",
+               NULL,
+       }, **candidate = candidates;
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "ast_sockaddr_to_pj_sockaddr_test";
+               info->category = "/res/res_pjproject/";
+               info->summary = "Validate conversions from an ast_sockaddr to a pj_sockaddr";
+               info->description = "This test converts an ast_sockaddr to a pj_sockaddr and validates\n"
+                       "that the two evaluate to the same string when formatted.";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       while (*candidate) {
+               struct ast_sockaddr addr = {{0,}};
+               pj_sockaddr pjaddr;
+               char buffer[512];
+
+               fill_with_garbage(&pjaddr, sizeof(pj_sockaddr));
+
+               if (!ast_sockaddr_parse(&addr, *candidate, 0)) {
+                       ast_test_status_update(test, "Failed to parse candidate IP: %s\n", *candidate);
+                       return AST_TEST_FAIL;
+               }
+
+               if (ast_sockaddr_to_pj_sockaddr(&addr, &pjaddr)) {
+                       ast_test_status_update(test, "Failed to convert ast_sockaddr to pj_sockaddr: %s\n", *candidate);
+                       return AST_TEST_FAIL;
+               }
+
+               pj_sockaddr_print(&pjaddr, buffer, sizeof(buffer), 1 | 2);
+
+               if (strcmp(*candidate, buffer)) {
+                       ast_test_status_update(test, "Converted sockaddrs do not match: \"%s\" and \"%s\"\n",
+                               *candidate,
+                               buffer);
+                       return AST_TEST_FAIL;
+               }
+
+               candidate++;
+       }
+
+       return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(ast_sockaddr_from_pj_sockaddr_test)
+{
+       char *candidates[] = {
+               "127.0.0.1:5555",
+               "[::]:4444",
+               "192.168.0.100:0",
+               "[fec0::1:80]:0",
+               "[fec0::1]:80",
+               NULL,
+       }, **candidate = candidates;
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "ast_sockaddr_from_pj_sockaddr_test";
+               info->category = "/res/res_pjproject/";
+               info->summary = "Validate conversions from a pj_sockaddr to an ast_sockaddr";
+               info->description = "This test converts a pj_sockaddr to an ast_sockaddr and validates\n"
+                       "that the two evaluate to the same string when formatted.";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       while (*candidate) {
+               struct ast_sockaddr addr = {{0,}};
+               pj_sockaddr pjaddr;
+               pj_str_t t;
+               char buffer[512];
+
+               fill_with_garbage(&addr, sizeof(addr));
+
+               pj_strset(&t, *candidate, strlen(*candidate));
+
+               if (pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &t, &pjaddr) != PJ_SUCCESS) {
+                       ast_test_status_update(test, "Failed to parse candidate IP: %s\n", *candidate);
+                       return AST_TEST_FAIL;
+               }
+
+               if (ast_sockaddr_from_pj_sockaddr(&addr, &pjaddr)) {
+                       ast_test_status_update(test, "Failed to convert pj_sockaddr to ast_sockaddr: %s\n", *candidate);
+                       return AST_TEST_FAIL;
+               }
+
+               snprintf(buffer, sizeof(buffer), "%s", ast_sockaddr_stringify(&addr));
+
+               if (strcmp(*candidate, buffer)) {
+                       ast_test_status_update(test, "Converted sockaddrs do not match: \"%s\" and \"%s\"\n",
+                               *candidate,
+                               buffer);
+                       return AST_TEST_FAIL;
+               }
+
+               candidate++;
+       }
+
+       return AST_TEST_PASS;
+}
+#endif
+
 static int load_module(void)
 {
        ast_debug(3, "Starting PJPROJECT logging to Asterisk logger\n");
@@ -540,6 +712,9 @@ static int load_module(void)
 
        ast_cli_register_multiple(pjproject_cli, ARRAY_LEN(pjproject_cli));
 
+       AST_TEST_REGISTER(ast_sockaddr_to_pj_sockaddr_test);
+       AST_TEST_REGISTER(ast_sockaddr_from_pj_sockaddr_test);
+
        return AST_MODULE_LOAD_SUCCESS;
 }
 
@@ -563,6 +738,9 @@ static int unload_module(void)
 
        ast_sorcery_unref(pjproject_sorcery);
 
+       AST_TEST_UNREGISTER(ast_sockaddr_to_pj_sockaddr_test);
+       AST_TEST_UNREGISTER(ast_sockaddr_from_pj_sockaddr_test);
+
        return 0;
 }
 
index f1821a6..c823197 100644 (file)
@@ -1,6 +1,6 @@
 {
        global:
-               LINKER_SYMBOL_PREFIXast_pjproject_*;
+               LINKER_SYMBOL_PREFIXast_*;
        local:
                *;
 };