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
This commit is contained in:
Sean Bright 2018-08-28 09:42:13 -04:00
parent 16df182155
commit 600c5d79fd
3 changed files with 205 additions and 1 deletions

View 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 */

View File

@ -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;
}

View File

@ -1,6 +1,6 @@
{
global:
LINKER_SYMBOL_PREFIXast_pjproject_*;
LINKER_SYMBOL_PREFIXast_*;
local:
*;
};