From 600c5d79fd433f8eae3ca20c600ee3ed33894531 Mon Sep 17 00:00:00 2001 From: Sean Bright Date: Tue, 28 Aug 2018 09:42:13 -0400 Subject: [PATCH] res_pjproject: Add utility functions to convert between socket structures 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 | 26 +++++ res/res_pjproject.c | 178 +++++++++++++++++++++++++++++++ res/res_pjproject.exports.in | 2 +- 3 files changed, 205 insertions(+), 1 deletion(-) diff --git a/include/asterisk/res_pjproject.h b/include/asterisk/res_pjproject.h index 17ebdd2016..d195c0fecc 100644 --- a/include/asterisk/res_pjproject.h +++ b/include/asterisk/res_pjproject.h @@ -22,6 +22,8 @@ #include #include +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 */ diff --git a/res/res_pjproject.c b/res/res_pjproject.c index ebd71b99bc..5b133ec39b 100644 --- a/res/res_pjproject.c +++ b/res/res_pjproject.c @@ -110,6 +110,8 @@ #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; } diff --git a/res/res_pjproject.exports.in b/res/res_pjproject.exports.in index f1821a6a28..c8231974b3 100644 --- a/res/res_pjproject.exports.in +++ b/res/res_pjproject.exports.in @@ -1,6 +1,6 @@ { global: - LINKER_SYMBOL_PREFIXast_pjproject_*; + LINKER_SYMBOL_PREFIXast_*; local: *; };