Thu Feb 12 15:14:07 CST 2009 Pekka Pessi <first.last@nokia.com>
* soa: make better effort in keeping addresses (c= and o= lines) in user SDP Ignore-this: a6da9ed249dba3309e3dbbbdb4262082 The address selection logic now prefers (unicast) addresses in already present in SDP, if they get returned by su_getlocalinfo(). The process currently tries to avoid link-local addresses. git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@11962 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
parent
2f0b4f5f84
commit
b8f52353f3
|
@ -1 +1 @@
|
|||
Thu Feb 12 15:25:06 CST 2009
|
||||
Thu Feb 12 15:28:20 CST 2009
|
||||
|
|
|
@ -14,7 +14,8 @@ INCLUDES = -I$(srcdir)/../sdp -I../sdp \
|
|||
-I$(srcdir)/../url -I../url \
|
||||
-I$(srcdir)/../ipt -I../ipt \
|
||||
-I$(srcdir)/../bnf -I../bnf \
|
||||
-I$(srcdir)/../su -I../su
|
||||
-I$(srcdir)/../su -I../su \
|
||||
-I$(top_srcdir)/s2check
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Build targets
|
||||
|
@ -39,7 +40,8 @@ libsoa_la_SOURCES = soa.c soa_static.c \
|
|||
|
||||
COVERAGE_INPUT = $(libsoa_la_SOURCES) $(include_sofia_HEADERS)
|
||||
|
||||
LDADD = libsoa.la \
|
||||
test_soa_LDADD = $(top_builddir)/s2check/libs2.a \
|
||||
libsoa.la \
|
||||
../sip/libsip.la \
|
||||
../msg/libmsg.la \
|
||||
../url/liburl.la \
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "sofia-sip/soa_add.h"
|
||||
|
||||
#include <sofia-sip/hostdomain.h>
|
||||
#include <sofia-sip/bnf.h>
|
||||
#include <sofia-sip/su_tagarg.h>
|
||||
#include <sofia-sip/su_localinfo.h>
|
||||
#include <sofia-sip/su_uniqueid.h>
|
||||
|
@ -2166,15 +2167,36 @@ void soa_description_free(soa_session_t *ss,
|
|||
su_free(ss->ss_home, tbf4);
|
||||
}
|
||||
|
||||
|
||||
/** Initialize SDP o= line */
|
||||
int
|
||||
soa_init_sdp_origin(soa_session_t *ss, sdp_origin_t *o, char buffer[64])
|
||||
{
|
||||
sdp_connection_t *c;
|
||||
return soa_init_sdp_origin_with_session(ss, o, buffer, NULL);
|
||||
}
|
||||
|
||||
if (ss == NULL || o == NULL)
|
||||
return su_seterrno(EFAULT), -1;
|
||||
|
||||
/** Check if c= has valid address
|
||||
*/
|
||||
int
|
||||
soa_check_sdp_connection(sdp_connection_t const *c)
|
||||
{
|
||||
return c != NULL &&
|
||||
c->c_nettype &&c->c_address &&
|
||||
strcmp(c->c_address, "") &&
|
||||
strcmp(c->c_address, "0.0.0.0") &&
|
||||
strcmp(c->c_address, "::");
|
||||
}
|
||||
|
||||
|
||||
/** Initialize SDP o= line with values from @a sdp session. */
|
||||
int
|
||||
soa_init_sdp_origin_with_session(soa_session_t *ss,
|
||||
sdp_origin_t *o,
|
||||
char buffer[64],
|
||||
sdp_session_t const *sdp)
|
||||
{
|
||||
if (ss == NULL || o == NULL || buffer == NULL)
|
||||
return su_seterrno(EFAULT);
|
||||
|
||||
assert(o->o_address);
|
||||
|
||||
|
@ -2189,49 +2211,13 @@ soa_init_sdp_origin(soa_session_t *ss, sdp_origin_t *o, char buffer[64])
|
|||
su_randmem(&o->o_version, sizeof o->o_version);
|
||||
o->o_version &= ((unsigned longlong)1 << 63) - 1;
|
||||
|
||||
c = o->o_address;
|
||||
|
||||
if (!c->c_nettype ||
|
||||
!c->c_address ||
|
||||
strcmp(c->c_address, "") == 0 ||
|
||||
strcmp(c->c_address, "0.0.0.0") == 0 ||
|
||||
strcmp(c->c_address, "::") == 0 ||
|
||||
!host_is_local(c->c_address)) {
|
||||
return soa_init_sdp_connection(ss, c, buffer);
|
||||
}
|
||||
if (!soa_check_sdp_connection(o->o_address) ||
|
||||
host_is_local(o->o_address->c_address))
|
||||
return soa_init_sdp_connection_with_session(ss, o->o_address, buffer, sdp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Search for an local address item from string provided by user */
|
||||
static
|
||||
su_localinfo_t *li_in_list(su_localinfo_t *li0, char const **llist)
|
||||
{
|
||||
char const *list = *llist;
|
||||
size_t n;
|
||||
|
||||
if (!list)
|
||||
return NULL;
|
||||
|
||||
while ((n = strcspn(list, ", "))) {
|
||||
su_localinfo_t *li;
|
||||
|
||||
for (li = li0; li; li = li->li_next) {
|
||||
if (su_casenmatch(li->li_canonname, list, n) &&
|
||||
li->li_canonname[n] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
list += n; while (list[0] == ' ' || list[0] == ',') list++;
|
||||
*llist = list;
|
||||
|
||||
if (li)
|
||||
return li;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/** Obtain a local address for SDP connection structure */
|
||||
int
|
||||
|
@ -2239,12 +2225,35 @@ soa_init_sdp_connection(soa_session_t *ss,
|
|||
sdp_connection_t *c,
|
||||
char buffer[64])
|
||||
{
|
||||
su_localinfo_t *res, hints[1] = {{ LI_CANONNAME | LI_NUMERIC }};
|
||||
su_localinfo_t *li, *li4, *li6;
|
||||
char const *address;
|
||||
int ip4, ip6, error;
|
||||
return soa_init_sdp_connection_with_session(ss, c, buffer, NULL);
|
||||
}
|
||||
|
||||
if (ss == NULL || c == NULL)
|
||||
static su_localinfo_t const *best_listed_address_in_localinfo(
|
||||
su_localinfo_t const *res, char const *address, int ip4, int ip6);
|
||||
static sdp_connection_t const *best_listed_address_in_session(
|
||||
sdp_session_t const *sdp, char const *address0, int ip4, int ip6);
|
||||
static su_localinfo_t const *best_listed_address(
|
||||
su_localinfo_t *li0, char const *address, int ip4, int ip6);
|
||||
|
||||
|
||||
/** Obtain a local address for SDP connection structure.
|
||||
*
|
||||
* Prefer an address already found in @a sdp.
|
||||
*/
|
||||
int
|
||||
soa_init_sdp_connection_with_session(soa_session_t *ss,
|
||||
sdp_connection_t *c,
|
||||
char buffer[64],
|
||||
sdp_session_t const *sdp)
|
||||
{
|
||||
su_localinfo_t *res, hints[1] = {{ LI_CANONNAME | LI_NUMERIC }}, li0[1];
|
||||
su_localinfo_t const *li, *li4, *li6;
|
||||
char const *address;
|
||||
char const *source = NULL;
|
||||
int ip4, ip6, error;
|
||||
char abuffer[64]; /* getting value from ss_address */
|
||||
|
||||
if (ss == NULL || c == NULL || buffer == NULL)
|
||||
return su_seterrno(EFAULT), -1;
|
||||
|
||||
address = ss->ss_address;
|
||||
|
@ -2268,32 +2277,15 @@ soa_init_sdp_connection(soa_session_t *ss,
|
|||
c->c_address = memcpy(buffer, address + 1, len - 1);
|
||||
buffer[len - 1] = '\0';
|
||||
}
|
||||
|
||||
SU_DEBUG_5(("%s: using SOATAG_ADDRESS(\"%s\")\n", __func__, c->c_address));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX - using LI_SCOPE_LINK requires some tweaking */
|
||||
hints->li_scope = LI_SCOPE_GLOBAL | LI_SCOPE_SITE /* | LI_SCOPE_LINK */;
|
||||
|
||||
switch (ss->ss_af) {
|
||||
case SOA_AF_IP4_ONLY:
|
||||
hints->li_family = AF_INET, ip4 = 1, ip6 = 0;
|
||||
break;
|
||||
|
||||
#if HAVE_SIN6
|
||||
case SOA_AF_IP6_ONLY:
|
||||
hints->li_family = AF_INET6, ip6 = 1, ip4 = 0;
|
||||
break;
|
||||
case SOA_AF_IP4_IP6:
|
||||
ip4 = 2, ip6 = 1;
|
||||
break;
|
||||
case SOA_AF_IP6_IP4:
|
||||
ip4 = 1, ip6 = 2;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ip4 = ip6 = 1;
|
||||
}
|
||||
|
||||
for (res = NULL; res == NULL;) {
|
||||
if ((error = su_getlocalinfo(hints, &res)) < 0
|
||||
&& error != ELI_NOADDRESS) {
|
||||
|
@ -2306,62 +2298,270 @@ soa_init_sdp_connection(soa_session_t *ss,
|
|||
hints->li_scope |= LI_SCOPE_HOST;
|
||||
}
|
||||
|
||||
if (!(ip4 & ip6 && c->c_nettype == sdp_net_in))
|
||||
/* Use ss_af preference */;
|
||||
else if (c->c_addrtype == sdp_addr_ip4)
|
||||
ip4 = 2, ip6 = 1;
|
||||
else if (c->c_addrtype == sdp_addr_ip6)
|
||||
ip6 = 2, ip4 = 1;
|
||||
|
||||
if (address)
|
||||
SU_DEBUG_3(("%s: searching for %s from list \"%s\"\n",
|
||||
__func__, ip6 && !ip4 ? "IP6 " : !ip6 && ip4 ? "IP4 " : "",
|
||||
address));
|
||||
|
||||
li = res, li4 = NULL, li6 = NULL;
|
||||
|
||||
for (;;) {
|
||||
if (address)
|
||||
li = li_in_list(li, &address);
|
||||
|
||||
if (!li)
|
||||
break;
|
||||
#if HAVE_SIN6
|
||||
else if (li->li_family == AF_INET6) {
|
||||
if (ip6 >= ip4)
|
||||
break;
|
||||
else if (!li6)
|
||||
li6 = li; /* Best IP6 address */
|
||||
}
|
||||
#endif
|
||||
else if (li->li_family == AF_INET) {
|
||||
if (ip4 > ip6)
|
||||
break;
|
||||
else if (!li4)
|
||||
li4 = li; /* Best IP4 address */
|
||||
}
|
||||
|
||||
if (!address)
|
||||
li = li->li_next;
|
||||
if (c->c_nettype != sdp_net_in ||
|
||||
(c->c_addrtype != sdp_addr_ip4 && c->c_addrtype != sdp_addr_ip6)) {
|
||||
c->c_nettype = sdp_net_in, c->c_addrtype = 0;
|
||||
c->c_address = strcpy(buffer, "");
|
||||
}
|
||||
|
||||
if (li == NULL)
|
||||
li = li4;
|
||||
if (li == NULL)
|
||||
li = li6;
|
||||
switch (ss->ss_af) {
|
||||
case SOA_AF_IP4_ONLY:
|
||||
ip4 = 1, ip6 = 0;
|
||||
break;
|
||||
case SOA_AF_IP6_ONLY:
|
||||
ip6 = 1, ip4 = 0;
|
||||
break;
|
||||
case SOA_AF_IP4_IP6:
|
||||
ip4 = 2, ip6 = 1;
|
||||
break;
|
||||
case SOA_AF_IP6_IP4:
|
||||
ip4 = 1, ip6 = 2;
|
||||
break;
|
||||
default:
|
||||
ip4 = ip6 = 1;
|
||||
}
|
||||
|
||||
if (li == NULL)
|
||||
;
|
||||
else if (li->li_family == AF_INET)
|
||||
c->c_nettype = sdp_net_in, c->c_addrtype = sdp_addr_ip4;
|
||||
#if HAVE_SIN6
|
||||
else if (li->li_family == AF_INET6)
|
||||
c->c_nettype = sdp_net_in, c->c_addrtype = sdp_addr_ip6;
|
||||
if (ip4 && ip6) {
|
||||
/* Prefer address family already used in session, if any */
|
||||
sdp_addrtype_e addrtype = 0;
|
||||
char const *because = "error";
|
||||
|
||||
if (sdp && sdp->sdp_connection &&
|
||||
sdp->sdp_connection->c_nettype == sdp_net_in) {
|
||||
addrtype = sdp->sdp_connection->c_addrtype;
|
||||
because = "an existing c= line";
|
||||
}
|
||||
else if (sdp) {
|
||||
int mip4 = 0, mip6 = 0;
|
||||
sdp_media_t const *m;
|
||||
|
||||
for (m = sdp->sdp_media; m; m = m->m_next) {
|
||||
sdp_connection_t const *mc;
|
||||
|
||||
if (m->m_rejected)
|
||||
continue;
|
||||
|
||||
for (mc = m->m_connections; mc; mc = mc->c_next) {
|
||||
if (mc->c_nettype == sdp_net_in) {
|
||||
if (mc->c_addrtype == sdp_addr_ip4)
|
||||
mip4++;
|
||||
else if (mc->c_addrtype == sdp_addr_ip6)
|
||||
mip6++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mip4 && mip6)
|
||||
/* Mixed. */;
|
||||
else if (mip4)
|
||||
addrtype = sdp_addr_ip4, because = "an existing c= line under m=";
|
||||
else if (mip6)
|
||||
addrtype = sdp_addr_ip6, because = "an existing c= line under m=";
|
||||
}
|
||||
|
||||
if (addrtype == 0)
|
||||
addrtype = c->c_addrtype, because = "the user sdp";
|
||||
|
||||
if (addrtype == sdp_addr_ip4) {
|
||||
if (ip6 >= ip4)
|
||||
SU_DEBUG_5(("%s: prefer %s because of %s\n", __func__, "IP4", because));
|
||||
ip4 = 2, ip6 = 1;
|
||||
}
|
||||
else if (addrtype == sdp_addr_ip6) {
|
||||
if (ip4 >= ip6)
|
||||
SU_DEBUG_5(("%s: prefer %s because of %s\n", __func__, "IP4", because));
|
||||
ip6 = 2, ip4 = 1;
|
||||
}
|
||||
}
|
||||
|
||||
li = NULL, li4 = NULL, li6 = NULL;
|
||||
|
||||
if (li == NULL && ss->ss_address) {
|
||||
li = best_listed_address_in_localinfo(res, ss->ss_address, ip4, ip6);
|
||||
if (li)
|
||||
source = "local address from SOATAG_ADDRESS() list";
|
||||
}
|
||||
|
||||
if (li == NULL && ss->ss_address && sdp) {
|
||||
sdp_connection_t const *c;
|
||||
c = best_listed_address_in_session(sdp, ss->ss_address, ip4, ip6);
|
||||
if (c) {
|
||||
li = memset(li0, 0, sizeof li0);
|
||||
if (c->c_addrtype == sdp_addr_ip4)
|
||||
li0->li_family = AF_INET;
|
||||
#if SU_HAVE_IN6
|
||||
else
|
||||
li0->li_family = AF_INET6;
|
||||
#endif
|
||||
li0->li_canonname = (char *)c->c_address;
|
||||
source = "address from SOATAG_ADDRESS() list already in session";
|
||||
}
|
||||
}
|
||||
|
||||
if (li == NULL && ss->ss_address) {
|
||||
memset(li0, 0, sizeof li0);
|
||||
li0->li_canonname = abuffer;
|
||||
li = best_listed_address(li0, ss->ss_address, ip4, ip6);
|
||||
if (li)
|
||||
source = "address from SOATAG_ADDRESS() list";
|
||||
}
|
||||
|
||||
if (li == NULL) {
|
||||
for (li = res; li; li = li->li_next) {
|
||||
if (su_casematch(li->li_canonname, c->c_address))
|
||||
break;
|
||||
}
|
||||
if (li)
|
||||
source = "the proposed local address";
|
||||
}
|
||||
|
||||
/* Check if session-level c= address is local */
|
||||
if (li == NULL && sdp && sdp->sdp_connection) {
|
||||
for (li = res; li; li = li->li_next) {
|
||||
if (!su_casematch(li->li_canonname, sdp->sdp_connection->c_address))
|
||||
continue;
|
||||
#if HAVE_SIN6
|
||||
if (li->li_family == AF_INET6) {
|
||||
if (ip6 >= ip4)
|
||||
break;
|
||||
else if (!li6)
|
||||
li6 = li; /* Best IP6 address */
|
||||
}
|
||||
#endif
|
||||
else if (li->li_family == AF_INET) {
|
||||
if (ip4 >= ip6)
|
||||
break;
|
||||
else if (!li4)
|
||||
li4 = li; /* Best IP4 address */
|
||||
}
|
||||
}
|
||||
|
||||
if (li == NULL && ip4)
|
||||
li = li4;
|
||||
if (li == NULL && ip6)
|
||||
li = li6;
|
||||
if (li)
|
||||
source = "an existing session-level c= line";
|
||||
}
|
||||
|
||||
/* Check for best local media-level c= address */
|
||||
if (li == NULL && sdp) {
|
||||
sdp_media_t const *m;
|
||||
|
||||
for (m = sdp->sdp_media; m; m = m->m_next) {
|
||||
sdp_connection_t const *mc;
|
||||
|
||||
if (m->m_rejected)
|
||||
continue;
|
||||
|
||||
for (mc = m->m_connections; mc; mc = mc->c_next) {
|
||||
for (li = res; li; li = li->li_next) {
|
||||
if (!su_casematch(li->li_canonname, sdp->sdp_connection->c_address))
|
||||
continue;
|
||||
#if HAVE_SIN6
|
||||
if (li->li_family == AF_INET6) {
|
||||
if (ip6 > ip4)
|
||||
break;
|
||||
else if (!li6)
|
||||
li6 = li; /* Best IP6 address */
|
||||
}
|
||||
#endif
|
||||
else if (li->li_family == AF_INET) {
|
||||
if (ip4 > ip6)
|
||||
break;
|
||||
else if (!li4)
|
||||
li4 = li; /* Best IP4 address */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (li)
|
||||
break;
|
||||
}
|
||||
|
||||
if (li == NULL && ip4)
|
||||
li = li4;
|
||||
if (li == NULL && ip6)
|
||||
li = li6;
|
||||
if (li)
|
||||
source = "an existing c= address from media descriptions";
|
||||
}
|
||||
|
||||
/* Check if o= address is local */
|
||||
if (li == NULL && sdp && sdp->sdp_origin) {
|
||||
char const *address = sdp->sdp_origin->o_address->c_address;
|
||||
|
||||
for (li = res; li; li = li->li_next) {
|
||||
if (!su_casematch(li->li_canonname, address))
|
||||
continue;
|
||||
#if HAVE_SIN6
|
||||
if (li->li_family == AF_INET6) {
|
||||
if (ip6 >= ip4)
|
||||
break;
|
||||
else if (!li6)
|
||||
li6 = li; /* Best IP6 address */
|
||||
}
|
||||
#endif
|
||||
else if (li->li_family == AF_INET) {
|
||||
if (ip4 >= ip6)
|
||||
break;
|
||||
else if (!li4)
|
||||
li4 = li; /* Best IP4 address */
|
||||
}
|
||||
}
|
||||
|
||||
if (li == NULL && ip4)
|
||||
li = li4;
|
||||
if (li == NULL && ip6)
|
||||
li = li6;
|
||||
if (li)
|
||||
source = "an existing address from o= line";
|
||||
}
|
||||
|
||||
if (li == NULL) {
|
||||
for (li = res; li; li = li->li_next) {
|
||||
#if HAVE_SIN6
|
||||
if (li->li_family == AF_INET6) {
|
||||
if (ip6 >= ip4)
|
||||
break;
|
||||
else if (!li6)
|
||||
li6 = li; /* Best IP6 address */
|
||||
}
|
||||
#endif
|
||||
else if (li->li_family == AF_INET) {
|
||||
if (ip4 >= ip6)
|
||||
break;
|
||||
else if (!li4)
|
||||
li4 = li; /* Best IP4 address */
|
||||
}
|
||||
}
|
||||
|
||||
if (li == NULL && ip4)
|
||||
li = li4;
|
||||
if (li == NULL && ip6)
|
||||
li = li6;
|
||||
|
||||
if (li)
|
||||
source = "a local address";
|
||||
}
|
||||
|
||||
if (li) {
|
||||
char const *typename;
|
||||
|
||||
if (li->li_family == AF_INET)
|
||||
c->c_nettype = sdp_net_in, c->c_addrtype = sdp_addr_ip4, typename = "IP4";
|
||||
#if HAVE_SIN6
|
||||
else if (li->li_family == AF_INET6)
|
||||
c->c_nettype = sdp_net_in, c->c_addrtype = sdp_addr_ip6, typename = "IP6";
|
||||
#endif
|
||||
else
|
||||
typename = "???";
|
||||
|
||||
assert(strlen(li->li_canonname) < 64);
|
||||
c->c_address = strcpy(buffer, li->li_canonname);
|
||||
|
||||
SU_DEBUG_5(("%s: selected IN %s %s (%s)\n", __func__,
|
||||
typename, li->li_canonname, source));
|
||||
}
|
||||
|
||||
su_freelocalinfo(res);
|
||||
|
@ -2371,3 +2571,174 @@ soa_init_sdp_connection(soa_session_t *ss,
|
|||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Search for first entry from SOATAG_ADDRESS() list on localinfo list */
|
||||
static su_localinfo_t const *
|
||||
best_listed_address_in_localinfo(su_localinfo_t const *res,
|
||||
char const *address,
|
||||
int ip4,
|
||||
int ip6)
|
||||
{
|
||||
su_localinfo_t const *li = NULL, *best = NULL;
|
||||
size_t n;
|
||||
|
||||
SU_DEBUG_3(("%s: searching for %s from list \"%s\"\n",
|
||||
__func__, ip6 && !ip4 ? "IP6 " : !ip6 && ip4 ? "IP4 " : "",
|
||||
address));
|
||||
|
||||
for (; address[0]; address += n + strspn(address, ", ")) {
|
||||
n = strcspn(address, ", ");
|
||||
if (n == 0)
|
||||
continue;
|
||||
|
||||
for (li = res; li; li = li->li_next) {
|
||||
if (su_casenmatch(li->li_canonname, address, n) &&
|
||||
li->li_canonname[n] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
if (li == NULL)
|
||||
continue;
|
||||
#if HAVE_SIN6
|
||||
else if (li->li_family == AF_INET6) {
|
||||
if (ip6 >= ip4)
|
||||
return li;
|
||||
else if (ip6 && !best)
|
||||
best = li; /* Best IP6 address */
|
||||
}
|
||||
#endif
|
||||
else if (li->li_family == AF_INET) {
|
||||
if (ip4 >= ip6)
|
||||
return li;
|
||||
else if (ip4 && !best)
|
||||
best = li; /* Best IP4 address */
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
/* Search for first entry from SOATAG_ADDRESS() list in session */
|
||||
static sdp_connection_t const *
|
||||
best_listed_address_in_session(sdp_session_t const *sdp,
|
||||
char const *address0,
|
||||
int ip4,
|
||||
int ip6)
|
||||
{
|
||||
sdp_connection_t *c = NULL, *best = NULL;
|
||||
sdp_media_t *m;
|
||||
char const *address;
|
||||
size_t n;
|
||||
|
||||
for (address = address0; address[0]; address += n + strspn(address, ", ")) {
|
||||
n = strcspn(address, ", ");
|
||||
if (n == 0)
|
||||
continue;
|
||||
|
||||
c = sdp->sdp_connection;
|
||||
|
||||
if (c && su_casenmatch(c->c_address, address, n) && c->c_address[n] == 0)
|
||||
;
|
||||
else
|
||||
for (m = sdp->sdp_media; m; m = m->m_next) {
|
||||
if (m->m_connections && m->m_connections != sdp->sdp_connection) {
|
||||
c = sdp->sdp_connection;
|
||||
if (su_casenmatch(c->c_address, address, n) && c->c_address[n] == 0)
|
||||
break;
|
||||
c = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == NULL || c->c_nettype != sdp_net_in)
|
||||
continue;
|
||||
#if HAVE_SIN6
|
||||
else if (c->c_addrtype == sdp_addr_ip6) {
|
||||
if (ip6 >= ip4)
|
||||
return c;
|
||||
else if (ip6 && !best)
|
||||
best = c; /* Best IP6 address */
|
||||
}
|
||||
#endif
|
||||
else if (c->c_addrtype == sdp_addr_ip4) {
|
||||
if (ip4 >= ip6)
|
||||
return c;
|
||||
else if (ip4 && !best)
|
||||
best = c; /* Best IP4 address */
|
||||
}
|
||||
}
|
||||
|
||||
if (best || sdp->sdp_origin == NULL)
|
||||
return best;
|
||||
|
||||
/* Check if address on list is already been used on o= line */
|
||||
for (address = address0; address[0]; address += n + strspn(address, ", ")) {
|
||||
n = strcspn(address, ", ");
|
||||
if (n == 0)
|
||||
continue;
|
||||
c = sdp->sdp_origin->o_address;
|
||||
|
||||
if (su_casenmatch(c->c_address, address, n) && c->c_address[n] != 0)
|
||||
continue;
|
||||
#if HAVE_SIN6
|
||||
else if (c->c_addrtype == sdp_addr_ip6) {
|
||||
if (ip6 >= ip4)
|
||||
return c;
|
||||
else if (ip6 && !best)
|
||||
best = c; /* Best IP6 address */
|
||||
}
|
||||
#endif
|
||||
else if (c->c_addrtype == sdp_addr_ip4) {
|
||||
if (ip4 >= ip6)
|
||||
return c;
|
||||
else if (ip4 && !best)
|
||||
best = c; /* Best IP4 address */
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
static su_localinfo_t const *
|
||||
best_listed_address(su_localinfo_t *li0,
|
||||
char const *address,
|
||||
int ip4,
|
||||
int ip6)
|
||||
{
|
||||
size_t n, best = 0;
|
||||
char *buffer = (char *)li0->li_canonname;
|
||||
|
||||
for (; address[0]; address += n + strspn(address + n, " ,")) {
|
||||
if ((n = span_ip6_address(address))) {
|
||||
#if SU_HAVE_IN6
|
||||
if (ip6 > ip4) {
|
||||
li0->li_family = AF_INET6;
|
||||
strncpy(buffer, address, n)[n] = '\0';
|
||||
return li0;
|
||||
}
|
||||
else if (ip6 && !best) {
|
||||
li0->li_family = AF_INET6;
|
||||
strncpy(buffer, address, best = n)[n] = '\0';
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if ((n = span_ip4_address(address))) {
|
||||
if (ip4 > ip6) {
|
||||
li0->li_family = AF_INET;
|
||||
strncpy(buffer, address, n)[n] = '\0';
|
||||
return li0;
|
||||
}
|
||||
else if (ip4 && !best) {
|
||||
li0->li_family = AF_INET;
|
||||
strncpy(buffer, address, best = n)[n] = '\0';
|
||||
}
|
||||
}
|
||||
else {
|
||||
n = strcspn(address, " ,");
|
||||
}
|
||||
}
|
||||
|
||||
if (best)
|
||||
return li0;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -1092,7 +1092,6 @@ static int offer_answer_step(soa_session_t *ss,
|
|||
{
|
||||
soa_static_session_t *sss = (soa_static_session_t *)ss;
|
||||
|
||||
char c_address[64];
|
||||
sdp_session_t *local = ss->ss_local->ssd_sdp;
|
||||
sdp_session_t local0[1];
|
||||
|
||||
|
@ -1102,8 +1101,12 @@ static int offer_answer_step(soa_session_t *ss,
|
|||
sdp_session_t *remote = ss->ss_remote->ssd_sdp;
|
||||
unsigned remote_version = ss->ss_remote_version;
|
||||
|
||||
int fresh = 0;
|
||||
|
||||
sdp_origin_t o[1] = {{ sizeof(o) }};
|
||||
sdp_connection_t *c, c0[1] = {{ sizeof(c0) }};
|
||||
char c0_buffer[64];
|
||||
|
||||
sdp_time_t t[1] = {{ sizeof(t) }};
|
||||
|
||||
int *u2s = NULL, *s2u = NULL, *tbf;
|
||||
|
@ -1150,22 +1153,16 @@ static int offer_answer_step(soa_session_t *ss,
|
|||
SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
|
||||
"generating local description"));
|
||||
|
||||
fresh = 1;
|
||||
local = local0;
|
||||
*local = *user, local->sdp_media = NULL;
|
||||
|
||||
if (local->sdp_origin) {
|
||||
o->o_username = local->sdp_origin->o_username;
|
||||
/* o->o_address = local->sdp_origin->o_address; */
|
||||
}
|
||||
if (!o->o_address)
|
||||
o->o_address = c0;
|
||||
local->sdp_origin = o;
|
||||
|
||||
if (soa_init_sdp_origin(ss, o, c_address) < 0) {
|
||||
phrase = "Cannot Get IP Address for Media";
|
||||
goto internal_error;
|
||||
}
|
||||
o->o_username = "-";
|
||||
o->o_address = c0;
|
||||
c0->c_address = c0_buffer;
|
||||
|
||||
if (!local->sdp_origin)
|
||||
local->sdp_origin = o;
|
||||
break;
|
||||
|
||||
case process_answer:
|
||||
|
@ -1283,28 +1280,46 @@ static int offer_answer_step(soa_session_t *ss,
|
|||
break;
|
||||
}
|
||||
|
||||
/* Step F: Update c= line */
|
||||
/* Step F0: Initialize o= line */
|
||||
if (fresh) {
|
||||
if (user->sdp_origin)
|
||||
o->o_username = user->sdp_origin->o_username;
|
||||
|
||||
if (soa_init_sdp_origin_with_session(ss, o, c0_buffer, local) < 0) {
|
||||
phrase = "Cannot Get IP Address for Session Description";
|
||||
goto internal_error;
|
||||
}
|
||||
|
||||
local->sdp_origin = o;
|
||||
}
|
||||
|
||||
/* Step F: Update c= line(s) */
|
||||
switch (action) {
|
||||
sdp_connection_t *user_c, *local_c;
|
||||
|
||||
case generate_offer:
|
||||
case generate_answer:
|
||||
/* Upgrade local SDP based of user SDP */
|
||||
if (ss->ss_local_user_version == user_version &&
|
||||
local->sdp_connection)
|
||||
break;
|
||||
user_c = user->sdp_connection;
|
||||
if (!soa_check_sdp_connection(user_c))
|
||||
user_c = NULL;
|
||||
|
||||
if (local->sdp_connection == NULL ||
|
||||
(user->sdp_connection != NULL &&
|
||||
sdp_connection_cmp(local->sdp_connection, user->sdp_connection))) {
|
||||
local_c = local->sdp_connection;
|
||||
if (!soa_check_sdp_connection(local_c))
|
||||
local_c = NULL;
|
||||
|
||||
if (ss->ss_local_user_version != user_version ||
|
||||
local_c == NULL ||
|
||||
(user_c != NULL && sdp_connection_cmp(local_c, user_c))) {
|
||||
sdp_media_t *m;
|
||||
|
||||
if (user_c)
|
||||
c = user_c;
|
||||
else
|
||||
c = local->sdp_origin->o_address;
|
||||
|
||||
/* Every m= line (even rejected one) must have a c= line
|
||||
* or there must be a c= line at session level
|
||||
*/
|
||||
if (user->sdp_connection)
|
||||
c = user->sdp_connection;
|
||||
else
|
||||
c = local->sdp_origin->o_address;
|
||||
|
||||
for (m = local->sdp_media; m; m = m->m_next)
|
||||
if (m->m_connections == NULL)
|
||||
break;
|
||||
|
|
|
@ -238,7 +238,11 @@ SOFIAPUBFUN int soa_has_received_sdp(soa_session_t const *ss);
|
|||
SOFIAPUBFUN int soa_set_status(soa_session_t *ss,
|
||||
int status, char const *phrase);
|
||||
|
||||
enum soa_activity { soa_activity_local, soa_activity_remote, soa_activity_session };
|
||||
enum soa_activity {
|
||||
soa_activity_local,
|
||||
soa_activity_remote,
|
||||
soa_activity_session
|
||||
};
|
||||
|
||||
SOFIAPUBFUN void soa_set_activity(soa_session_t *ss,
|
||||
sdp_media_t const *m,
|
||||
|
@ -259,8 +263,18 @@ SOFIAPUBFUN int soa_description_dup(su_home_t *,
|
|||
|
||||
SOFIAPUBFUN int soa_init_sdp_origin(soa_session_t *ss,
|
||||
sdp_origin_t *o, char buf[64]);
|
||||
SOFIAPUBFUN int soa_init_sdp_origin_with_session(soa_session_t *ss,
|
||||
sdp_origin_t *o,
|
||||
char buffer[64],
|
||||
sdp_session_t const *sdp);
|
||||
SOFIAPUBFUN int soa_check_sdp_connection(sdp_connection_t const *c);
|
||||
SOFIAPUBFUN int soa_init_sdp_connection(soa_session_t *,
|
||||
sdp_connection_t *, char buf[64]);
|
||||
SOFIAPUBFUN int soa_init_sdp_connection_with_session(soa_session_t *,
|
||||
sdp_connection_t *, char buf[64],
|
||||
sdp_session_t const *sdp);
|
||||
|
||||
SOFIAPUBFUN sdp_connection_t *soa_find_local_sdp_connection(sdp_session_t const*);
|
||||
|
||||
/* ====================================================================== */
|
||||
/* Debug log settings */
|
||||
|
|
|
@ -55,6 +55,10 @@ struct context;
|
|||
#include <sofia-sip/su_log.h>
|
||||
#include <sofia-sip/sip_tag.h>
|
||||
|
||||
#include <s2_localinfo.h>
|
||||
|
||||
S2_LOCALINFO_STUBS();
|
||||
|
||||
extern su_log_t soa_log[];
|
||||
|
||||
char const name[] = "test_soa";
|
||||
|
@ -72,6 +76,98 @@ int tstflags = 0;
|
|||
|
||||
#define NONE ((void*)-1)
|
||||
|
||||
static char const *test_ifaces1[] = {
|
||||
"eth0\0" "11.12.13.14\0" "2001:1508:1003::21a:a0ff:fe71:813\0" "fe80::21a:a0ff:fe71:813\0",
|
||||
"eth1\0" "12.13.14.15\0" "2001:1508:1004::21a:a0ff:fe71:814\0" "fe80::21a:a0ff:fe71:814\0",
|
||||
"eth2\0" "192.168.2.15\0" "fec0::21a:a0ff:fe71:815\0" "fe80::21a:a0ff:fe71:815\0",
|
||||
"lo0\0" "127.0.0.1\0" "::1\0",
|
||||
NULL
|
||||
};
|
||||
|
||||
int test_localinfo_replacement(void)
|
||||
{
|
||||
BEGIN();
|
||||
su_localinfo_t *res, *li, hints[1];
|
||||
int error, n;
|
||||
struct results {
|
||||
struct afresult { unsigned global, site, link, host; } ip6[1], ip4[1];
|
||||
} results[1];
|
||||
|
||||
s2_localinfo_ifaces(test_ifaces1);
|
||||
|
||||
error = su_getlocalinfo(NULL, &res);
|
||||
TEST(error, ELI_NOERROR);
|
||||
TEST_1(res != NULL);
|
||||
memset(results, 0, sizeof results);
|
||||
for (li = res, n = 0; li; li = li->li_next) {
|
||||
struct afresult *afr;
|
||||
TEST_1(li->li_family == AF_INET || li->li_family == AF_INET6);
|
||||
if (li->li_family == AF_INET)
|
||||
afr = results->ip4;
|
||||
else
|
||||
afr = results->ip6;
|
||||
|
||||
if (li->li_scope == LI_SCOPE_GLOBAL)
|
||||
afr->global++;
|
||||
else if (li->li_scope == LI_SCOPE_SITE)
|
||||
afr->site++;
|
||||
else if (li->li_scope == LI_SCOPE_LINK)
|
||||
afr->link++;
|
||||
else if (li->li_scope == LI_SCOPE_HOST)
|
||||
afr->host++;
|
||||
n++;
|
||||
}
|
||||
TEST(n, 11);
|
||||
TEST(results->ip4->global, 2);
|
||||
TEST(results->ip4->site, 1);
|
||||
TEST(results->ip4->link, 0);
|
||||
TEST(results->ip4->host, 1);
|
||||
#if SU_HAVE_IN6
|
||||
TEST(results->ip6->global, 2);
|
||||
TEST(results->ip6->site, 1);
|
||||
TEST(results->ip6->link, 3);
|
||||
TEST(results->ip6->host, 1);
|
||||
#endif
|
||||
su_freelocalinfo(res);
|
||||
|
||||
error = su_getlocalinfo(memset(hints, 0, sizeof hints), &res);
|
||||
TEST(error, ELI_NOERROR);
|
||||
TEST_1(res != NULL);
|
||||
for (li = res, n = 0; li; li = li->li_next)
|
||||
n++;
|
||||
TEST(n, 11);
|
||||
su_freelocalinfo(res);
|
||||
|
||||
hints->li_flags = LI_CANONNAME;
|
||||
|
||||
error = su_getlocalinfo(hints, &res);
|
||||
TEST(error, ELI_NOERROR);
|
||||
TEST_1(res != NULL);
|
||||
for (li = res, n = 0; li; li = li->li_next) {
|
||||
TEST_1(li->li_canonname != NULL);
|
||||
n++;
|
||||
}
|
||||
TEST(n, 11);
|
||||
su_freelocalinfo(res);
|
||||
|
||||
hints->li_flags = LI_IFNAME | LI_CANONNAME;
|
||||
hints->li_ifname = "eth1";
|
||||
|
||||
error = su_getlocalinfo(hints, &res);
|
||||
TEST(error, ELI_NOERROR);
|
||||
TEST_1(res != NULL);
|
||||
for (li = res, n = 0; li; li = li->li_next) {
|
||||
TEST_1(li->li_canonname != NULL);
|
||||
TEST_S(li->li_ifname, "eth1");
|
||||
n++;
|
||||
}
|
||||
TEST(n, 3);
|
||||
su_freelocalinfo(res);
|
||||
|
||||
END();
|
||||
}
|
||||
/* ========================================================================= */
|
||||
|
||||
struct context
|
||||
{
|
||||
su_home_t home[1];
|
||||
|
@ -1871,6 +1967,463 @@ int test_asynch_offer_answer(struct context *ctx)
|
|||
END();
|
||||
}
|
||||
|
||||
#define TEST_OC_ADDRESS(s, address, ip) \
|
||||
TEST(test_address_in_offer(s, address, sdp_addr_ ## ip, address, sdp_addr_ ## ip), 0);
|
||||
|
||||
static int
|
||||
test_address_in_offer(soa_session_t *ss,
|
||||
char const *o_address,
|
||||
int o_addrtype,
|
||||
char const *c_address,
|
||||
int c_addrtype)
|
||||
{
|
||||
sdp_session_t const *sdp = NULL;
|
||||
sdp_connection_t const *c;
|
||||
|
||||
TEST(soa_get_local_sdp(ss, &sdp, NULL, NULL), 1);
|
||||
TEST_1(sdp != NULL);
|
||||
TEST_1(c = sdp->sdp_connection);
|
||||
TEST(c->c_nettype, sdp_net_in);
|
||||
if (c_addrtype) TEST(c->c_addrtype, c_addrtype);
|
||||
if (c_address) TEST_S(c->c_address, c_address);
|
||||
|
||||
TEST_1(c = sdp->sdp_origin->o_address);
|
||||
TEST(c->c_nettype, sdp_net_in);
|
||||
if (o_addrtype) TEST(c->c_addrtype, o_addrtype);
|
||||
if (o_address) TEST_S(c->c_address, o_address);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** This tests the IP address selection logic.
|
||||
*
|
||||
* The IP address is selected based on the SOATAG_AF() preference,
|
||||
* SOATAG_ADDRESS(), and locally obtained address list.
|
||||
*/
|
||||
int test_address_selection(struct context *ctx)
|
||||
{
|
||||
BEGIN();
|
||||
int n;
|
||||
|
||||
static char const *ifaces1[] = {
|
||||
"eth2\0" "192.168.2.15\0" "fec0::21a:a0ff:fe71:815\0" "fe80::21a:a0ff:fe71:815\0",
|
||||
"eth0\0" "11.12.13.14\0" "2001:1508:1003::21a:a0ff:fe71:813\0" "fe80::21a:a0ff:fe71:813\0",
|
||||
"eth1\0" "12.13.14.15\0" "2001:1508:1004::21a:a0ff:fe71:814\0" "fe80::21a:a0ff:fe71:814\0",
|
||||
"lo0\0" "127.0.0.1\0" "::1\0",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char const *ifaces_ip6only[] = {
|
||||
"eth2\0" "fec0::21a:a0ff:fe71:815\0" "fe80::21a:a0ff:fe71:815\0",
|
||||
"eth0\0" "2001:1508:1003::21a:a0ff:fe71:813\0" "fe80::21a:a0ff:fe71:813\0",
|
||||
"eth1\0" "2001:1508:1004::21a:a0ff:fe71:814\0" "fe80::21a:a0ff:fe71:814\0",
|
||||
"lo0\0" "127.0.0.1\0" "::1\0",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char const *ifaces_ip4only[] = {
|
||||
"eth2\0" "192.168.2.15\0" "fe80::21a:a0ff:fe71:815\0",
|
||||
"eth0\0" "11.12.13.14\0" "fe80::21a:a0ff:fe71:813\0",
|
||||
"eth1\0" "12.13.14.15\0" "fe80::21a:a0ff:fe71:814\0",
|
||||
"lo0\0" "127.0.0.1\0" "::1\0",
|
||||
NULL
|
||||
};
|
||||
|
||||
soa_session_t *a, *b;
|
||||
sdp_origin_t *o;
|
||||
|
||||
su_home_t home[1] = { SU_HOME_INIT(home) };
|
||||
|
||||
s2_localinfo_ifaces(ifaces1);
|
||||
|
||||
TEST_1(a = soa_clone(ctx->a, ctx->root, ctx));
|
||||
|
||||
/* SOATAG_AF(SOA_AF_IP4_ONLY) => select IP4 address */
|
||||
n = soa_set_params(a, SOATAG_AF(SOA_AF_IP4_ONLY), TAG_END());
|
||||
n = soa_set_user_sdp(a, 0, "m=audio 5008 RTP/AVP 0 8", -1); TEST(n, 1);
|
||||
n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
|
||||
TEST_OC_ADDRESS(a, "11.12.13.14", ip4);
|
||||
/* Should flush the session */
|
||||
TEST_VOID(soa_process_reject(a, NULL));
|
||||
|
||||
/* SOATAG_AF(SOA_AF_IP6_ONLY) => select IP6 address */
|
||||
n = soa_set_params(a, SOATAG_AF(SOA_AF_IP6_ONLY), TAG_END());
|
||||
n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
|
||||
TEST_OC_ADDRESS(a, "2001:1508:1003::21a:a0ff:fe71:813", ip6);
|
||||
TEST_VOID(soa_terminate(a, NULL));
|
||||
|
||||
/* SOATAG_AF(SOA_AF_IP4_IP6) => select IP4 address */
|
||||
n = soa_set_params(a, SOATAG_AF(SOA_AF_IP4_IP6), TAG_END());
|
||||
n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
|
||||
TEST_OC_ADDRESS(a, "11.12.13.14", ip4);
|
||||
TEST_VOID(soa_process_reject(a, NULL));
|
||||
|
||||
/* SOATAG_AF(SOA_AF_IP6_IP4) => select IP6 address */
|
||||
n = soa_set_params(a, SOATAG_AF(SOA_AF_IP6_IP4), TAG_END());
|
||||
n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
|
||||
TEST_OC_ADDRESS(a, "2001:1508:1003::21a:a0ff:fe71:813", ip6);
|
||||
TEST_VOID(soa_terminate(a, NULL));
|
||||
|
||||
/* SOATAG_AF(SOA_AF_IP4_IP6) but session mentions IP6 => select IP6 */
|
||||
n = soa_set_params(a, SOATAG_AF(SOA_AF_IP4_IP6), TAG_END());
|
||||
n = soa_set_user_sdp(a, 0, "c=IN IP6 ::\r\nm=audio 5008 RTP/AVP 0 8", -1); TEST(n, 1);
|
||||
n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
|
||||
TEST_OC_ADDRESS(a, "2001:1508:1003::21a:a0ff:fe71:813", ip6);
|
||||
TEST_VOID(soa_terminate(a, NULL));
|
||||
|
||||
/* SOATAG_AF(SOA_AF_IP4_IP6), o= mentions IP6 => select IP4 */
|
||||
n = soa_set_user_sdp(a, 0, "o=- 1 1 IN IP6 ::\r\n"
|
||||
"m=audio 5008 RTP/AVP 0 8", -1); TEST(n, 1);
|
||||
n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
|
||||
TEST_OC_ADDRESS(a, "11.12.13.14", ip4);
|
||||
TEST_VOID(soa_process_reject(a, NULL));
|
||||
|
||||
/* SOATAG_AF(SOA_AF_IP4_IP6), c= uses non-local IP6
|
||||
=> select local IP6 on o= */
|
||||
n = soa_set_user_sdp(a, 0,
|
||||
"c=IN IP6 2001:1508:1004::21a:a0ff:fe71:819\r\n"
|
||||
"m=audio 5008 RTP/AVP 0 8", -1);
|
||||
TEST(n, 1);
|
||||
n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
|
||||
TEST(test_address_in_offer(a,
|
||||
/* o= has local address */
|
||||
"2001:1508:1003::21a:a0ff:fe71:813", sdp_addr_ip6,
|
||||
/* c= has sdp-provided address */
|
||||
"2001:1508:1004::21a:a0ff:fe71:819", sdp_addr_ip6), 0);
|
||||
TEST_VOID(soa_terminate(a, NULL));
|
||||
|
||||
/* SOATAG_AF(SOA_AF_IP4_ONLY), no IP4 addresses */
|
||||
s2_localinfo_ifaces(ifaces_ip6only);
|
||||
n = soa_set_params(a, SOATAG_AF(SOA_AF_IP4_ONLY), TAG_END());
|
||||
n = soa_set_user_sdp(a, 0, "m=audio 5008 RTP/AVP 0 8", -1);
|
||||
TEST(soa_generate_offer(a, 1, test_completed), -1);
|
||||
|
||||
/* Retry with IP6 enabled */
|
||||
n = soa_set_params(a, SOATAG_AF(SOA_AF_IP4_IP6), TAG_END());
|
||||
TEST(soa_generate_offer(a, 1, test_completed), 0);
|
||||
TEST_OC_ADDRESS(a, "2001:1508:1003::21a:a0ff:fe71:813", ip6);
|
||||
TEST_VOID(soa_terminate(a, NULL));
|
||||
|
||||
/* SOATAG_AF(SOA_AF_IP6_ONLY), no IP6 addresses */
|
||||
s2_localinfo_ifaces(ifaces_ip4only);
|
||||
n = soa_set_params(a, SOATAG_AF(SOA_AF_IP6_ONLY), TAG_END());
|
||||
TEST(soa_generate_offer(a, 1, test_completed), -1); /* should fail */
|
||||
TEST_VOID(soa_terminate(a, NULL));
|
||||
|
||||
/* SOATAG_AF(SOA_AF_IP4_ONLY), no IP4 addresses */
|
||||
s2_localinfo_ifaces(ifaces_ip6only);
|
||||
n = soa_set_params(a, SOATAG_AF(SOA_AF_IP4_ONLY), TAG_END());
|
||||
TEST(soa_generate_offer(a, 1, test_completed), -1); /* should fail */
|
||||
TEST_VOID(soa_terminate(a, NULL));
|
||||
|
||||
/* Select locally available address from the SOATAG_ADDRESS() list */
|
||||
s2_localinfo_ifaces(ifaces1);
|
||||
n = soa_set_params(a, SOATAG_AF(SOA_AF_IP4_IP6),
|
||||
SOATAG_ADDRESS("test.com 17.18.19.20 12.13.14.15"),
|
||||
TAG_END());
|
||||
n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
|
||||
TEST_OC_ADDRESS(a, "12.13.14.15", ip4);
|
||||
TEST_VOID(soa_process_reject(a, NULL));
|
||||
|
||||
/* Select locally available IP6 address from the SOATAG_ADDRESS() list */
|
||||
n = soa_set_params(a, SOATAG_AF(SOA_AF_IP6_IP4),
|
||||
SOATAG_ADDRESS("test.com 12.13.14.15 fec0::21a:a0ff:fe71:815"),
|
||||
TAG_END());
|
||||
n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
|
||||
TEST_OC_ADDRESS(a, "fec0::21a:a0ff:fe71:815", ip6);
|
||||
TEST_VOID(soa_process_reject(a, NULL));
|
||||
|
||||
/* Select first available address from the SOATAG_ADDRESS() list */
|
||||
n = soa_set_params(a, SOATAG_AF(SOA_AF_ANY),
|
||||
SOATAG_ADDRESS("test.com 12.13.14.15 fec0::21a:a0ff:fe71:815"),
|
||||
TAG_END());
|
||||
n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
|
||||
TEST_OC_ADDRESS(a, "12.13.14.15", ip4);
|
||||
TEST_VOID(soa_process_reject(a, NULL));
|
||||
|
||||
/* Select preferred address from the SOATAG_ADDRESS() list */
|
||||
s2_localinfo_ifaces(ifaces1);
|
||||
n = soa_set_params(a, SOATAG_AF(SOA_AF_IP4_IP6),
|
||||
SOATAG_ADDRESS("test.com fec0::22a:a0ff:fe71:815 19.18.19.20"),
|
||||
TAG_END());
|
||||
n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
|
||||
TEST_OC_ADDRESS(a, "19.18.19.20", ip4);
|
||||
TEST_VOID(soa_process_reject(a, NULL));
|
||||
|
||||
/* Select preferred address from the SOATAG_ADDRESS() list */
|
||||
s2_localinfo_ifaces(ifaces1);
|
||||
n = soa_set_params(a, SOATAG_AF(SOA_AF_IP6_IP4),
|
||||
SOATAG_ADDRESS("test.com 19.18.19.20 fec0::22a:a0ff:fe71:815 fec0::22a:a0ff:fe71:819"),
|
||||
TAG_END());
|
||||
n = soa_generate_offer(a, 1, test_completed); TEST(n, 0);
|
||||
TEST_OC_ADDRESS(a, "fec0::22a:a0ff:fe71:815", ip6);
|
||||
TEST_VOID(soa_process_reject(a, NULL));
|
||||
|
||||
TEST_VOID(soa_destroy(a));
|
||||
|
||||
(void)b; (void)o;
|
||||
#if 0
|
||||
TEST_1(b = soa_clone(ctx->b, ctx->root, ctx));
|
||||
|
||||
n = soa_set_remote_sdp(b, 0, offer, offerlen); TEST(n, 1);
|
||||
|
||||
n = soa_get_local_sdp(b, NULL, &answer, &answerlen); TEST(n, 0);
|
||||
|
||||
n = soa_set_params(b,
|
||||
SOATAG_LOCAL_SDP_STR("m=audio 5004 RTP/AVP 8"),
|
||||
SOATAG_AF(SOA_AF_IP4_ONLY),
|
||||
SOATAG_ADDRESS("1.2.3.4"),
|
||||
TAG_END());
|
||||
|
||||
n = soa_generate_answer(b, test_completed); TEST(n, 0);
|
||||
|
||||
TEST_1(soa_is_complete(b));
|
||||
TEST(soa_activate(b, NULL), 0);
|
||||
|
||||
n = soa_get_local_sdp(b, NULL, &answer, &answerlen); TEST(n, 1);
|
||||
TEST_1(answer != NULL && answer != NONE);
|
||||
TEST_1(strstr(answer, "c=IN IP4 1.2.3.4"));
|
||||
|
||||
n = soa_set_remote_sdp(a, 0, answer, -1); TEST(n, 1);
|
||||
|
||||
n = soa_process_answer(a, test_completed); TEST(n, 0);
|
||||
|
||||
TEST_1(soa_is_complete(a));
|
||||
TEST(soa_activate(a, NULL), 0);
|
||||
|
||||
TEST(soa_is_audio_active(a), SOA_ACTIVE_SENDRECV);
|
||||
TEST(soa_is_video_active(a), SOA_ACTIVE_DISABLED);
|
||||
TEST(soa_is_image_active(a), SOA_ACTIVE_DISABLED);
|
||||
TEST(soa_is_chat_active(a), SOA_ACTIVE_DISABLED);
|
||||
|
||||
TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDRECV);
|
||||
TEST(soa_is_remote_video_active(a), SOA_ACTIVE_DISABLED);
|
||||
TEST(soa_is_remote_image_active(a), SOA_ACTIVE_DISABLED);
|
||||
TEST(soa_is_remote_chat_active(a), SOA_ACTIVE_DISABLED);
|
||||
|
||||
/* 'A' will put call on hold */
|
||||
offer = NONE;
|
||||
TEST(soa_set_params(a, SOATAG_HOLD("*"), TAG_END()), 1);
|
||||
|
||||
TEST(soa_generate_offer(a, 1, test_completed), 0);
|
||||
TEST(soa_get_local_sdp(a, NULL, &offer, &offerlen), 1);
|
||||
TEST_1(offer != NULL && offer != NONE);
|
||||
TEST_1(strstr(offer, "a=sendonly"));
|
||||
TEST(soa_set_remote_sdp(b, 0, offer, offerlen), 1);
|
||||
TEST(soa_generate_answer(b, test_completed), 0);
|
||||
TEST_1(soa_is_complete(b));
|
||||
TEST(soa_activate(b, NULL), 0);
|
||||
TEST(soa_get_local_sdp(b, NULL, &answer, &answerlen), 1);
|
||||
TEST_1(answer != NULL && answer != NONE);
|
||||
TEST_1(strstr(answer, "a=recvonly"));
|
||||
TEST(soa_set_remote_sdp(a, 0, answer, -1), 1);
|
||||
TEST(soa_process_answer(a, test_completed), 0);
|
||||
TEST(soa_activate(a, NULL), 0);
|
||||
|
||||
TEST(soa_is_audio_active(a), SOA_ACTIVE_SENDONLY);
|
||||
TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDONLY);
|
||||
|
||||
/* 'A' will put call inactive */
|
||||
offer = NONE;
|
||||
TEST(soa_set_params(a, SOATAG_HOLD("#"), TAG_END()), 1);
|
||||
|
||||
TEST(soa_generate_offer(a, 1, test_completed), 0);
|
||||
TEST(soa_get_local_sdp(a, NULL, &offer, &offerlen), 1);
|
||||
TEST_1(offer != NULL && offer != NONE);
|
||||
TEST_1(strstr(offer, "a=inactive"));
|
||||
TEST(soa_set_remote_sdp(b, 0, offer, offerlen), 1);
|
||||
TEST(soa_generate_answer(b, test_completed), 0);
|
||||
TEST_1(soa_is_complete(b));
|
||||
TEST(soa_activate(b, NULL), 0);
|
||||
TEST(soa_get_local_sdp(b, NULL, &answer, &answerlen), 1);
|
||||
TEST_1(answer != NULL && answer != NONE);
|
||||
TEST_1(strstr(answer, "a=inactive"));
|
||||
TEST(soa_set_remote_sdp(a, 0, answer, -1), 1);
|
||||
TEST(soa_process_answer(a, test_completed), 0);
|
||||
TEST(soa_activate(a, NULL), 0);
|
||||
|
||||
TEST(soa_is_audio_active(a), SOA_ACTIVE_INACTIVE);
|
||||
TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_INACTIVE);
|
||||
|
||||
/* B will send an offer to A, but there is no change in O/A status */
|
||||
TEST(soa_generate_offer(b, 1, test_completed), 0);
|
||||
TEST(soa_get_local_sdp(b, NULL, &offer, &offerlen), 1);
|
||||
TEST_1(offer != NULL && offer != NONE);
|
||||
TEST_1(!strstr(offer, "a=inactive"));
|
||||
/* printf("offer:\n%s", offer); */
|
||||
TEST(soa_set_remote_sdp(a, 0, offer, offerlen), 1);
|
||||
TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDRECV);
|
||||
TEST(soa_generate_answer(a, test_completed), 0);
|
||||
TEST(soa_is_audio_active(a), SOA_ACTIVE_INACTIVE);
|
||||
TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_INACTIVE);
|
||||
TEST_1(soa_is_complete(a));
|
||||
TEST(soa_activate(a, NULL), 0);
|
||||
TEST(soa_get_local_sdp(a, NULL, &answer, &answerlen), 1);
|
||||
TEST_1(answer != NULL && answer != NONE);
|
||||
TEST_1(strstr(answer, "a=inactive"));
|
||||
/* printf("answer:\n%s", answer); */
|
||||
TEST(soa_set_remote_sdp(b, 0, answer, -1), 1);
|
||||
TEST(soa_process_answer(b, test_completed), 0);
|
||||
TEST(soa_activate(b, NULL), 0);
|
||||
|
||||
|
||||
TEST(soa_is_audio_active(b), SOA_ACTIVE_INACTIVE);
|
||||
TEST(soa_is_remote_audio_active(b), SOA_ACTIVE_INACTIVE);
|
||||
|
||||
/* 'A' will release hold. */
|
||||
TEST(soa_set_params(a, SOATAG_HOLD(NULL), TAG_END()), 1);
|
||||
|
||||
TEST(soa_generate_offer(a, 1, test_completed), 0);
|
||||
TEST(soa_get_local_sdp(a, NULL, &offer, &offerlen), 1);
|
||||
TEST_1(offer != NULL && offer != NONE);
|
||||
TEST_1(!strstr(offer, "a=sendonly") && !strstr(offer, "a=inactive"));
|
||||
TEST(soa_set_remote_sdp(b, 0, offer, offerlen), 1);
|
||||
TEST(soa_generate_answer(b, test_completed), 0);
|
||||
TEST_1(soa_is_complete(b));
|
||||
TEST(soa_activate(b, NULL), 0);
|
||||
TEST(soa_get_local_sdp(b, NULL, &answer, &answerlen), 1);
|
||||
TEST_1(answer != NULL && answer != NONE);
|
||||
TEST_1(!strstr(answer, "a=recvonly") && !strstr(answer, "a=inactive"));
|
||||
TEST(soa_set_remote_sdp(a, 0, answer, -1), 1);
|
||||
TEST(soa_process_answer(a, test_completed), 0);
|
||||
TEST(soa_activate(a, NULL), 0);
|
||||
|
||||
TEST(soa_is_audio_active(a), SOA_ACTIVE_SENDRECV);
|
||||
TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDRECV);
|
||||
|
||||
/* 'A' will put B on hold but this time with c=IN IP4 0.0.0.0 */
|
||||
TEST(soa_set_params(a, SOATAG_HOLD("*"), TAG_END()), 1);
|
||||
TEST(soa_generate_offer(a, 1, test_completed), 0);
|
||||
|
||||
{
|
||||
sdp_session_t const *o_sdp;
|
||||
sdp_session_t *sdp;
|
||||
sdp_printer_t *p;
|
||||
sdp_connection_t *c;
|
||||
|
||||
TEST(soa_get_local_sdp(a, &o_sdp, NULL, NULL), 1);
|
||||
TEST_1(o_sdp != NULL && o_sdp != NONE);
|
||||
TEST_1(sdp = sdp_session_dup(home, o_sdp));
|
||||
|
||||
/* Remove mode, change c=, encode offer */
|
||||
if (sdp->sdp_media->m_connections)
|
||||
c = sdp->sdp_media->m_connections;
|
||||
else
|
||||
c = sdp->sdp_connection;
|
||||
TEST_1(c);
|
||||
c->c_address = "0.0.0.0";
|
||||
|
||||
TEST_1(p = sdp_print(home, sdp, NULL, 0, sdp_f_realloc));
|
||||
TEST_1(sdp_message(p));
|
||||
offer = sdp_message(p); offerlen = strlen(offer);
|
||||
}
|
||||
|
||||
TEST(soa_set_remote_sdp(b, 0, offer, -1), 1);
|
||||
TEST(soa_generate_answer(b, test_completed), 0);
|
||||
TEST_1(soa_is_complete(b));
|
||||
TEST(soa_activate(b, NULL), 0);
|
||||
TEST(soa_get_local_sdp(b, NULL, &answer, &answerlen), 1);
|
||||
TEST_1(answer != NULL && answer != NONE);
|
||||
TEST_1(strstr(answer, "a=recvonly"));
|
||||
TEST(soa_set_remote_sdp(a, 0, answer, -1), 1);
|
||||
TEST(soa_process_answer(a, test_completed), 0);
|
||||
TEST(soa_activate(a, NULL), 0);
|
||||
|
||||
TEST(soa_is_audio_active(a), SOA_ACTIVE_SENDONLY);
|
||||
TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDONLY);
|
||||
TEST(soa_is_audio_active(b), SOA_ACTIVE_RECVONLY);
|
||||
TEST(soa_is_remote_audio_active(b), SOA_ACTIVE_RECVONLY);
|
||||
|
||||
/* 'A' will propose adding video. */
|
||||
/* 'B' will reject. */
|
||||
TEST(soa_set_params(a,
|
||||
SOATAG_HOLD(NULL), /* 'A' will release hold. */
|
||||
SOATAG_USER_SDP_STR("m=audio 5008 RTP/AVP 0 8\r\ni=x\r\n"
|
||||
"m=video 5006 RTP/AVP 34\r\n"),
|
||||
TAG_END()), 2);
|
||||
|
||||
TEST(soa_generate_offer(a, 1, test_completed), 0);
|
||||
TEST(soa_get_local_sdp(a, NULL, &offer, &offerlen), 1);
|
||||
TEST_1(offer != NULL && offer != NONE);
|
||||
TEST_1(!strstr(offer, "a=sendonly"));
|
||||
TEST_1(strstr(offer, "m=video"));
|
||||
TEST(soa_set_remote_sdp(b, 0, offer, offerlen), 1);
|
||||
TEST(soa_generate_answer(b, test_completed), 0);
|
||||
TEST_1(soa_is_complete(b));
|
||||
TEST(soa_activate(b, NULL), 0);
|
||||
TEST(soa_get_local_sdp(b, NULL, &answer, &answerlen), 1);
|
||||
TEST_1(answer != NULL && answer != NONE);
|
||||
TEST_1(!strstr(answer, "a=recvonly"));
|
||||
TEST_1(strstr(answer, "m=video"));
|
||||
TEST(soa_set_remote_sdp(a, 0, answer, -1), 1);
|
||||
TEST(soa_process_answer(a, test_completed), 0);
|
||||
TEST(soa_activate(a, NULL), 0);
|
||||
|
||||
TEST(soa_is_audio_active(a), SOA_ACTIVE_SENDRECV);
|
||||
TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDRECV);
|
||||
TEST(soa_is_video_active(a), SOA_ACTIVE_REJECTED);
|
||||
|
||||
{
|
||||
/* Test tags */
|
||||
sdp_session_t const *l = NULL, *u = NULL, *r = NULL;
|
||||
sdp_media_t const *m;
|
||||
|
||||
TEST(soa_get_params(b,
|
||||
SOATAG_LOCAL_SDP_REF(l),
|
||||
SOATAG_USER_SDP_REF(u),
|
||||
SOATAG_REMOTE_SDP_REF(r),
|
||||
TAG_END()), 3);
|
||||
|
||||
TEST_1(l); TEST_1(u); TEST_1(r);
|
||||
TEST_1(m = l->sdp_media); TEST(m->m_type, sdp_media_audio);
|
||||
TEST_1(!m->m_rejected);
|
||||
TEST_1(m = m->m_next); TEST(m->m_type, sdp_media_video);
|
||||
TEST_1(m->m_rejected);
|
||||
}
|
||||
|
||||
/* 'B' will now propose adding video. */
|
||||
/* 'A' will accept. */
|
||||
TEST(soa_set_params(b,
|
||||
SOATAG_USER_SDP_STR("m=audio 5004 RTP/AVP 0 8\r\n"
|
||||
"m=video 5006 RTP/AVP 34\r\n"),
|
||||
TAG_END()), 1);
|
||||
|
||||
TEST(soa_generate_offer(b, 1, test_completed), 0);
|
||||
TEST(soa_get_local_sdp(b, NULL, &offer, &offerlen), 1);
|
||||
TEST_1(offer != NULL && offer != NONE);
|
||||
TEST_1(!strstr(offer, "b=sendonly"));
|
||||
TEST_1(strstr(offer, "m=video"));
|
||||
TEST(soa_set_remote_sdp(a, 0, offer, offerlen), 1);
|
||||
TEST(soa_generate_answer(a, test_completed), 0);
|
||||
TEST_1(soa_is_complete(a));
|
||||
TEST(soa_activate(a, NULL), 0);
|
||||
TEST(soa_get_local_sdp(a, NULL, &answer, &answerlen), 1);
|
||||
TEST_1(answer != NULL && answer != NONE);
|
||||
TEST_1(!strstr(answer, "b=recvonly"));
|
||||
TEST_1(strstr(answer, "m=video"));
|
||||
TEST(soa_set_remote_sdp(b, 0, answer, -1), 1);
|
||||
TEST(soa_process_answer(b, test_completed), 0);
|
||||
TEST(soa_activate(b, NULL), 0);
|
||||
|
||||
TEST(soa_is_audio_active(a), SOA_ACTIVE_SENDRECV);
|
||||
TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_SENDRECV);
|
||||
TEST(soa_is_video_active(a), SOA_ACTIVE_SENDRECV);
|
||||
|
||||
TEST_VOID(soa_terminate(a, NULL));
|
||||
|
||||
TEST(soa_is_audio_active(a), SOA_ACTIVE_DISABLED);
|
||||
TEST(soa_is_remote_audio_active(a), SOA_ACTIVE_DISABLED);
|
||||
|
||||
TEST_VOID(soa_terminate(b, NULL));
|
||||
|
||||
TEST_VOID(soa_destroy(a));
|
||||
TEST_VOID(soa_destroy(b));
|
||||
#endif
|
||||
su_home_deinit(home);
|
||||
|
||||
END();
|
||||
}
|
||||
|
||||
int test_deinit(struct context *ctx)
|
||||
{
|
||||
BEGIN();
|
||||
|
@ -1974,10 +2527,14 @@ int main(int argc, char *argv[])
|
|||
if (retval && quit_on_single_failure) { su_deinit(); return retval; } \
|
||||
} while(0)
|
||||
|
||||
retval |= test_localinfo_replacement(); SINGLE_FAILURE_CHECK();
|
||||
|
||||
retval |= test_api_errors(ctx); SINGLE_FAILURE_CHECK();
|
||||
retval |= test_soa_tags(ctx); SINGLE_FAILURE_CHECK();
|
||||
retval |= test_init(ctx, argv + i); SINGLE_FAILURE_CHECK();
|
||||
|
||||
if (retval == 0) {
|
||||
retval |= test_address_selection(ctx); SINGLE_FAILURE_CHECK();
|
||||
|
||||
retval |= test_params(ctx); SINGLE_FAILURE_CHECK();
|
||||
retval |= test_static_offer_answer(ctx); SINGLE_FAILURE_CHECK();
|
||||
|
|
Loading…
Reference in New Issue