Fixed #2032: NAT64 support for IPv4 interoperability
git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@5636 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
parent
494f58b07d
commit
e34fa7ed5e
|
@ -210,6 +210,11 @@ typedef struct pjstun_setting
|
|||
* insert magic cookie (specified in RFC 5389) in the transaction ID.
|
||||
*/
|
||||
pj_bool_t use_stun2;
|
||||
|
||||
/**
|
||||
* Address family of the STUN servers.
|
||||
*/
|
||||
int af;
|
||||
|
||||
/**
|
||||
* Host name or IP address string of the first STUN server.
|
||||
|
|
|
@ -63,7 +63,7 @@ PJ_DEF(pj_status_t) pjstun_get_mapped_addr2(pj_pool_factory *pf,
|
|||
unsigned srv_cnt;
|
||||
const pj_str_t *srv1, *srv2;
|
||||
int port1, port2;
|
||||
pj_sockaddr_in srv_addr[2];
|
||||
pj_sockaddr srv_addr[2];
|
||||
int i, send_cnt = 0, nfds;
|
||||
pj_pool_t *pool;
|
||||
struct query_rec {
|
||||
|
@ -116,20 +116,19 @@ PJ_DEF(pj_status_t) pjstun_get_mapped_addr2(pj_pool_factory *pf,
|
|||
TRACE_((THIS_FILE, " Binding request created."));
|
||||
|
||||
/* Resolve servers. */
|
||||
status = pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1);
|
||||
status = pj_sockaddr_init(opt->af, &srv_addr[0], srv1, (pj_uint16_t)port1);
|
||||
if (status != PJ_SUCCESS)
|
||||
goto on_error;
|
||||
|
||||
srv_cnt = 1;
|
||||
|
||||
if (srv2 && port2) {
|
||||
status = pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2);
|
||||
status = pj_sockaddr_init(opt->af, &srv_addr[1], srv2,
|
||||
(pj_uint16_t)port2);
|
||||
if (status != PJ_SUCCESS)
|
||||
goto on_error;
|
||||
|
||||
if (srv_addr[1].sin_addr.s_addr != srv_addr[0].sin_addr.s_addr &&
|
||||
srv_addr[1].sin_port != srv_addr[0].sin_port)
|
||||
{
|
||||
if (pj_sockaddr_cmp(&srv_addr[1], &srv_addr[0]) != 0) {
|
||||
srv_cnt++;
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +180,7 @@ PJ_DEF(pj_status_t) pjstun_get_mapped_addr2(pj_pool_factory *pf,
|
|||
sent_len = out_msg_len;
|
||||
status = pj_sock_sendto(sock[i], out_msg, &sent_len, 0,
|
||||
(pj_sockaddr_t*)&srv_addr[j],
|
||||
sizeof(pj_sockaddr_in));
|
||||
pj_sockaddr_get_len(&srv_addr[j]));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,7 +220,7 @@ PJ_DEF(pj_status_t) pjstun_get_mapped_addr2(pj_pool_factory *pf,
|
|||
int sock_idx, srv_idx;
|
||||
pj_ssize_t len;
|
||||
pjstun_msg msg;
|
||||
pj_sockaddr_in addr;
|
||||
pj_sockaddr addr;
|
||||
int addrlen = sizeof(addr);
|
||||
pjstun_mapped_addr_attr *attr;
|
||||
char recv_buf[128];
|
||||
|
|
|
@ -933,6 +933,24 @@ PJ_DECL(void) pj_sockaddr_copy_addr(pj_sockaddr *dst,
|
|||
*/
|
||||
PJ_DECL(void) pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src);
|
||||
|
||||
/*
|
||||
* If the source's and desired address family matches, copy the address,
|
||||
* otherwise synthesize a new address with the desired address family,
|
||||
* from the source address. This can be useful to generate an IPv4-mapped
|
||||
* IPv6 address.
|
||||
*
|
||||
* @param dst_af Desired address family.
|
||||
* @param dst Destination socket address, invalid if synthesis is
|
||||
* required and failed.
|
||||
* @param src Source socket address.
|
||||
*
|
||||
* @return PJ_SUCCESS on success, or the error status
|
||||
* if synthesis is required and failed.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pj_sockaddr_synthesize(int dst_af,
|
||||
pj_sockaddr_t *dst,
|
||||
const pj_sockaddr_t *src);
|
||||
|
||||
/**
|
||||
* Get the IP address of an IPv4 socket address.
|
||||
* The address is returned as 32bit value in host byte order.
|
||||
|
|
|
@ -415,6 +415,40 @@ PJ_DEF(void) pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src)
|
|||
pj_memcpy(dst, src, pj_sockaddr_get_len(src));
|
||||
}
|
||||
|
||||
/*
|
||||
* Synthesize address.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pj_sockaddr_synthesize(int dst_af,
|
||||
pj_sockaddr_t *dst,
|
||||
const pj_sockaddr_t *src)
|
||||
{
|
||||
char ip_addr_buf[PJ_INET6_ADDRSTRLEN];
|
||||
unsigned int count = 1;
|
||||
pj_addrinfo ai[1];
|
||||
pj_str_t ip_addr;
|
||||
pj_status_t status;
|
||||
|
||||
/* Validate arguments */
|
||||
PJ_ASSERT_RETURN(src && dst, PJ_EINVAL);
|
||||
|
||||
if (dst_af == ((const pj_sockaddr *)src)->addr.sa_family) {
|
||||
pj_sockaddr_cp(dst, src);
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
pj_sockaddr_print(src, ip_addr_buf, sizeof(ip_addr_buf), 0);
|
||||
ip_addr = pj_str(ip_addr_buf);
|
||||
|
||||
/* Try to synthesize address using pj_getaddrinfo(). */
|
||||
status = pj_getaddrinfo(dst_af, &ip_addr, &count, ai);
|
||||
if (status == PJ_SUCCESS && count > 0) {
|
||||
pj_sockaddr_cp(dst, &ai[0].ai_addr);
|
||||
pj_sockaddr_set_port(dst, pj_sockaddr_get_port(src));
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set port number of pj_sockaddr_in
|
||||
*/
|
||||
|
|
|
@ -652,6 +652,9 @@ static pj_status_t transport_attach( pjmedia_transport *tp,
|
|||
{
|
||||
struct transport_udp *udp = (struct transport_udp*) tp;
|
||||
const pj_sockaddr *rtcp_addr;
|
||||
pj_sockaddr sock_addr, remote_addr, remote_rtcp;
|
||||
int rem_addr_len;
|
||||
pj_status_t status;
|
||||
|
||||
/* Validate arguments */
|
||||
PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL);
|
||||
|
@ -667,19 +670,39 @@ static pj_status_t transport_attach( pjmedia_transport *tp,
|
|||
|
||||
/* "Attach" the application: */
|
||||
|
||||
rem_addr_len = sizeof(pj_sockaddr);
|
||||
pj_sock_getsockname(udp->rtp_sock, &sock_addr, &rem_addr_len);
|
||||
|
||||
/* Synthesize address, if necessary. */
|
||||
status = pj_sockaddr_synthesize(sock_addr.addr.sa_family,
|
||||
&remote_addr, rem_addr);
|
||||
if (status != PJ_SUCCESS) {
|
||||
pj_perror(3, tp->name, status, "Failed to synthesize the correct"
|
||||
"IP address for RTP");
|
||||
}
|
||||
rem_addr_len = pj_sockaddr_get_len(&remote_addr);
|
||||
|
||||
/* Copy remote RTP address */
|
||||
pj_memcpy(&udp->rem_rtp_addr, rem_addr, addr_len);
|
||||
pj_memcpy(&udp->rem_rtp_addr, &remote_addr, rem_addr_len);
|
||||
|
||||
/* Copy remote RTP address, if one is specified. */
|
||||
rtcp_addr = (const pj_sockaddr*) rem_rtcp;
|
||||
if (rtcp_addr && pj_sockaddr_has_addr(rtcp_addr)) {
|
||||
pj_memcpy(&udp->rem_rtcp_addr, rem_rtcp, addr_len);
|
||||
pj_status_t status;
|
||||
|
||||
status = pj_sockaddr_synthesize(sock_addr.addr.sa_family,
|
||||
&remote_rtcp, rem_rtcp);
|
||||
if (status != PJ_SUCCESS) {
|
||||
pj_perror(3, tp->name, status, "Failed to synthesize the correct"
|
||||
"IP address for RTCP");
|
||||
}
|
||||
pj_memcpy(&udp->rem_rtcp_addr, &remote_rtcp, rem_addr_len);
|
||||
|
||||
} else {
|
||||
unsigned rtcp_port;
|
||||
|
||||
/* Otherwise guess the RTCP address from the RTP address */
|
||||
pj_memcpy(&udp->rem_rtcp_addr, rem_addr, addr_len);
|
||||
pj_memcpy(&udp->rem_rtcp_addr, &udp->rem_rtp_addr, rem_addr_len);
|
||||
rtcp_port = pj_sockaddr_get_port(&udp->rem_rtp_addr) + 1;
|
||||
pj_sockaddr_set_port(&udp->rem_rtcp_addr, (pj_uint16_t)rtcp_port);
|
||||
}
|
||||
|
@ -690,7 +713,7 @@ static pj_status_t transport_attach( pjmedia_transport *tp,
|
|||
udp->user_data = user_data;
|
||||
|
||||
/* Save address length */
|
||||
udp->addr_len = addr_len;
|
||||
udp->addr_len = rem_addr_len;
|
||||
|
||||
/* Last, mark transport as attached */
|
||||
//udp->attached = PJ_TRUE;
|
||||
|
|
|
@ -2185,6 +2185,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
|
|||
pj_ice_sess_cand *lcand;
|
||||
pj_ice_sess_checklist *clist;
|
||||
pj_stun_xor_mapped_addr_attr *xaddr;
|
||||
const pj_sockaddr_t *source_addr = src_addr;
|
||||
unsigned i;
|
||||
|
||||
PJ_UNUSED_ARG(stun_sess);
|
||||
|
@ -2286,8 +2287,26 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
|
|||
* the response match the source IP address and port that the Binding
|
||||
* Request was sent from.
|
||||
*/
|
||||
if (pj_sockaddr_cmp(&check->rcand->addr, (const pj_sockaddr*)src_addr)!=0)
|
||||
if (check->rcand->addr.addr.sa_family == pj_AF_INET() &&
|
||||
((pj_sockaddr *)src_addr)->addr.sa_family == pj_AF_INET6())
|
||||
{
|
||||
/* If the address family is different, we need to check
|
||||
* whether the two addresses are equivalent (i.e. the IPv6
|
||||
* is synthesized from IPv4).
|
||||
*/
|
||||
pj_sockaddr synth_addr;
|
||||
pj_status_t status;
|
||||
|
||||
status = pj_sockaddr_synthesize(pj_AF_INET6(), &synth_addr,
|
||||
&check->rcand->addr);
|
||||
if (status == PJ_SUCCESS &&
|
||||
pj_sockaddr_cmp(&synth_addr, src_addr) == 0)
|
||||
{
|
||||
source_addr = &check->rcand->addr;
|
||||
}
|
||||
}
|
||||
|
||||
if (pj_sockaddr_cmp(&check->rcand->addr, source_addr) != 0) {
|
||||
status = PJNATH_EICEINSRCADDR;
|
||||
LOG4((ice->obj_name,
|
||||
"Check %s%s: connectivity check FAILED: source address mismatch",
|
||||
|
@ -2474,6 +2493,8 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
|
|||
pj_stun_uint64_attr *role_attr;
|
||||
pj_stun_tx_data *tdata;
|
||||
pj_ice_rx_check *rcheck, tmp_rcheck;
|
||||
const pj_sockaddr_t *source_addr = src_addr;
|
||||
unsigned source_addr_len = src_addr_len;
|
||||
pj_status_t status;
|
||||
|
||||
PJ_UNUSED_ARG(pkt);
|
||||
|
@ -2587,10 +2608,54 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
|
|||
return status;
|
||||
}
|
||||
|
||||
if (((pj_sockaddr *)src_addr)->addr.sa_family == pj_AF_INET6()) {
|
||||
unsigned i;
|
||||
unsigned transport_id = ((pj_ice_msg_data*)token)->transport_id;
|
||||
pj_ice_sess_cand *lcand = NULL;
|
||||
|
||||
for (i = 0; i < ice->clist.count; ++i) {
|
||||
pj_ice_sess_check *c = &ice->clist.checks[i];
|
||||
if (c->lcand->comp_id == sd->comp_id &&
|
||||
c->lcand->transport_id == transport_id)
|
||||
{
|
||||
lcand = c->lcand;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lcand != NULL && lcand->addr.addr.sa_family == pj_AF_INET()) {
|
||||
/* We are behind NAT64, so src_addr is a synthesized IPv6
|
||||
* address. Instead of putting this synth IPv6 address as
|
||||
* the XOR-MAPPED-ADDRESS, we need to find its original
|
||||
* IPv4 address.
|
||||
*/
|
||||
for (i = 0; i < ice->rcand_cnt; ++i) {
|
||||
pj_status_t status;
|
||||
pj_sockaddr synth_addr;
|
||||
|
||||
if (ice->rcand[i].addr.addr.sa_family != pj_AF_INET())
|
||||
continue;
|
||||
|
||||
status = pj_sockaddr_synthesize(pj_AF_INET6(), &synth_addr,
|
||||
&ice->rcand[i].addr);
|
||||
if (status == PJ_SUCCESS &&
|
||||
pj_sockaddr_cmp(src_addr, &synth_addr) == 0)
|
||||
{
|
||||
/* We find the original IPv4 address. */
|
||||
source_addr = &ice->rcand[i].addr;
|
||||
source_addr_len = pj_sockaddr_get_len(source_addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Add XOR-MAPPED-ADDRESS attribute */
|
||||
status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
|
||||
PJ_STUN_ATTR_XOR_MAPPED_ADDR,
|
||||
PJ_TRUE, src_addr, src_addr_len);
|
||||
PJ_TRUE, source_addr,
|
||||
source_addr_len);
|
||||
|
||||
/* Create a msg_data to be associated with this response */
|
||||
msg_data = PJ_POOL_ZALLOC_T(tdata->pool, pj_ice_msg_data);
|
||||
|
@ -2619,8 +2684,8 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
|
|||
/* Init rcheck */
|
||||
rcheck->comp_id = sd->comp_id;
|
||||
rcheck->transport_id = ((pj_ice_msg_data*)token)->transport_id;
|
||||
rcheck->src_addr_len = src_addr_len;
|
||||
pj_sockaddr_cp(&rcheck->src_addr, src_addr);
|
||||
rcheck->src_addr_len = source_addr_len;
|
||||
pj_sockaddr_cp(&rcheck->src_addr, source_addr);
|
||||
rcheck->use_candidate = (uc_attr != NULL);
|
||||
rcheck->priority = prio_attr->value;
|
||||
rcheck->role_attr = role_attr;
|
||||
|
|
|
@ -168,6 +168,11 @@ typedef struct pj_ice_strans_comp
|
|||
unsigned cand_cnt; /**< # of candidates/aliaes. */
|
||||
pj_ice_sess_cand cand_list[PJ_ICE_ST_MAX_CAND]; /**< Cand array */
|
||||
|
||||
pj_bool_t ipv4_mapped; /**< Is IPv6 addr mapped to IPv4?*/
|
||||
pj_sockaddr dst_addr; /**< Destination address */
|
||||
pj_sockaddr synth_addr; /**< Synthesized dest address */
|
||||
unsigned synth_addr_len;/**< Synthesized dest addr len */
|
||||
|
||||
unsigned default_cand; /**< Default candidate. */
|
||||
|
||||
} pj_ice_strans_comp;
|
||||
|
@ -654,7 +659,8 @@ static pj_status_t add_stun_and_host(pj_ice_strans *ice_st,
|
|||
* address family
|
||||
*/
|
||||
if (comp->ice_st->cfg.af != pj_AF_UNSPEC() &&
|
||||
comp->cand_list[comp->default_cand].addr.addr.sa_family !=
|
||||
addr->addr.sa_family == comp->ice_st->cfg.af &&
|
||||
comp->cand_list[comp->default_cand].base_addr.addr.sa_family !=
|
||||
ice_st->cfg.af)
|
||||
{
|
||||
comp->default_cand = (unsigned)(cand - comp->cand_list);
|
||||
|
@ -1134,6 +1140,15 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st,
|
|||
/* Must have address */
|
||||
pj_assert(pj_sockaddr_has_addr(&cand->addr));
|
||||
|
||||
/* Skip if we are mapped to IPv4 address and this candidate
|
||||
* is not IPv4.
|
||||
*/
|
||||
if (comp->ipv4_mapped &&
|
||||
cand->addr.addr.sa_family != pj_AF_INET())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Add the candidate */
|
||||
status = pj_ice_sess_add_cand(ice_st->ice, comp->comp_id,
|
||||
cand->transport_id, cand->type,
|
||||
|
@ -1500,9 +1515,33 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st,
|
|||
return (status==PJ_SUCCESS||status==PJ_EPENDING) ?
|
||||
PJ_SUCCESS : status;
|
||||
} else {
|
||||
const pj_sockaddr_t *dest_addr;
|
||||
unsigned dest_addr_len;
|
||||
|
||||
if (comp->ipv4_mapped) {
|
||||
if (comp->synth_addr_len == 0 ||
|
||||
pj_sockaddr_cmp(&comp->dst_addr, dst_addr) != 0)
|
||||
{
|
||||
status = pj_sockaddr_synthesize(pj_AF_INET6(),
|
||||
&comp->synth_addr,
|
||||
dst_addr);
|
||||
if (status != PJ_SUCCESS)
|
||||
return status;
|
||||
|
||||
pj_sockaddr_cp(&comp->dst_addr, dst_addr);
|
||||
comp->synth_addr_len = pj_sockaddr_get_len(
|
||||
&comp->synth_addr);
|
||||
}
|
||||
dest_addr = &comp->synth_addr;
|
||||
dest_addr_len = comp->synth_addr_len;
|
||||
} else {
|
||||
dest_addr = dst_addr;
|
||||
dest_addr_len = dst_addr_len;
|
||||
}
|
||||
|
||||
status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, data,
|
||||
(unsigned)data_len, 0, dst_addr,
|
||||
dst_addr_len);
|
||||
(unsigned)data_len, 0, dest_addr,
|
||||
dest_addr_len);
|
||||
return (status==PJ_SUCCESS||status==PJ_EPENDING) ?
|
||||
PJ_SUCCESS : status;
|
||||
}
|
||||
|
@ -1649,9 +1688,31 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice,
|
|||
status = PJ_EINVALIDOP;
|
||||
}
|
||||
} else if (tp_typ == TP_STUN) {
|
||||
const pj_sockaddr_t *dest_addr;
|
||||
unsigned dest_addr_len;
|
||||
|
||||
if (comp->ipv4_mapped) {
|
||||
if (comp->synth_addr_len == 0 ||
|
||||
pj_sockaddr_cmp(&comp->dst_addr, dst_addr) != 0)
|
||||
{
|
||||
status = pj_sockaddr_synthesize(pj_AF_INET6(),
|
||||
&comp->synth_addr, dst_addr);
|
||||
if (status != PJ_SUCCESS)
|
||||
return status;
|
||||
|
||||
pj_sockaddr_cp(&comp->dst_addr, dst_addr);
|
||||
comp->synth_addr_len = pj_sockaddr_get_len(&comp->synth_addr);
|
||||
}
|
||||
dest_addr = &comp->synth_addr;
|
||||
dest_addr_len = comp->synth_addr_len;
|
||||
} else {
|
||||
dest_addr = dst_addr;
|
||||
dest_addr_len = dst_addr_len;
|
||||
}
|
||||
|
||||
status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL,
|
||||
pkt, (unsigned)size, 0,
|
||||
dst_addr, dst_addr_len);
|
||||
dest_addr, dest_addr_len);
|
||||
} else {
|
||||
pj_assert(!"Invalid transport ID");
|
||||
status = PJ_EINVALIDOP;
|
||||
|
@ -1819,6 +1880,41 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock,
|
|||
"srflx address changed";
|
||||
pj_bool_t dup = PJ_FALSE;
|
||||
|
||||
if (info.mapped_addr.addr.sa_family == pj_AF_INET() &&
|
||||
cand->base_addr.addr.sa_family == pj_AF_INET6())
|
||||
{
|
||||
/* We get an IPv4 mapped address for our IPv6
|
||||
* host address.
|
||||
*/
|
||||
comp->ipv4_mapped = PJ_TRUE;
|
||||
|
||||
/* Find other host candidates with the same (IPv6)
|
||||
* address, and replace it with the new (IPv4)
|
||||
* mapped address.
|
||||
*/
|
||||
for (i = 0; i < comp->cand_cnt; ++i) {
|
||||
pj_sockaddr *a1, *a2;
|
||||
|
||||
if (comp->cand_list[i].type != PJ_ICE_CAND_TYPE_HOST)
|
||||
continue;
|
||||
|
||||
a1 = &comp->cand_list[i].addr;
|
||||
a2 = &cand->base_addr;
|
||||
if (pj_memcmp(pj_sockaddr_get_addr(a1),
|
||||
pj_sockaddr_get_addr(a2),
|
||||
pj_sockaddr_get_addr_len(a1)) == 0)
|
||||
{
|
||||
pj_uint16_t port = pj_sockaddr_get_port(a1);
|
||||
pj_sockaddr_cp(a1, &info.mapped_addr);
|
||||
if (port != pj_sockaddr_get_port(a2))
|
||||
pj_sockaddr_set_port(a1, port);
|
||||
pj_sockaddr_cp(&comp->cand_list[i].base_addr, a1);
|
||||
}
|
||||
}
|
||||
pj_sockaddr_cp(&cand->base_addr, &info.mapped_addr);
|
||||
pj_sockaddr_cp(&cand->rel_addr, &info.mapped_addr);
|
||||
}
|
||||
|
||||
/* Eliminate the srflx candidate if the address is
|
||||
* equal to other (host) candidates.
|
||||
*/
|
||||
|
|
|
@ -88,6 +88,7 @@ typedef struct nat_detect_session
|
|||
pj_ioqueue_key_t *key;
|
||||
pj_sockaddr server;
|
||||
pj_sockaddr *cur_server;
|
||||
pj_sockaddr cur_addr;
|
||||
pj_stun_session *stun_sess;
|
||||
|
||||
pj_ioqueue_op_key_t read_op, write_op;
|
||||
|
@ -854,11 +855,17 @@ static pj_status_t send_test(nat_detect_session *sess,
|
|||
if (status != PJ_SUCCESS)
|
||||
goto on_error;
|
||||
|
||||
/* Configure alternate address */
|
||||
if (alt_addr)
|
||||
sess->cur_server = (pj_sockaddr*) alt_addr;
|
||||
else
|
||||
/* Configure alternate address, synthesize it if necessary */
|
||||
if (alt_addr) {
|
||||
status = pj_sockaddr_synthesize(sess->server.addr.sa_family,
|
||||
&sess->cur_addr, alt_addr);
|
||||
if (status != PJ_SUCCESS)
|
||||
goto on_error;
|
||||
|
||||
sess->cur_server = &sess->cur_addr;
|
||||
} else {
|
||||
sess->cur_server = &sess->server;
|
||||
}
|
||||
|
||||
PJ_LOG(5,(sess->pool->obj_name,
|
||||
"Performing %s to %s:%d",
|
||||
|
|
|
@ -677,7 +677,7 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess,
|
|||
pj_sockaddr *addr = &sess->srv_addr_list[i];
|
||||
pj_memcpy(addr, &ai[i].ai_addr, sizeof(pj_sockaddr));
|
||||
addr->addr.sa_family = sess->af;
|
||||
addr->ipv4.sin_port = pj_htons(sess->default_port);
|
||||
pj_sockaddr_set_port(addr, sess->default_port);
|
||||
}
|
||||
|
||||
sess->srv_addr = &sess->srv_addr_list[0];
|
||||
|
|
|
@ -153,6 +153,8 @@ typedef enum pjsua_sip_timer_use {PJSUA_SIP_TIMER_INACTIVE, PJSUA_SIP_TIMER_OPTI
|
|||
|
||||
typedef enum pjsua_ipv6_use {PJSUA_IPV6_DISABLED, PJSUA_IPV6_ENABLED} pjsua_ipv6_use;
|
||||
|
||||
typedef enum pjsua_nat64_opt {PJSUA_NAT64_DISABLED, PJSUA_NAT64_ENABLED} pjsua_nat64_opt;
|
||||
|
||||
typedef enum pjsua_buddy_status {PJSUA_BUDDY_STATUS_UNKNOWN, PJSUA_BUDDY_STATUS_ONLINE, PJSUA_BUDDY_STATUS_OFFLINE} pjsua_buddy_status;
|
||||
|
||||
typedef enum pjsua_call_media_status {PJSUA_CALL_MEDIA_NONE, PJSUA_CALL_MEDIA_ACTIVE, PJSUA_CALL_MEDIA_LOCAL_HOLD, PJSUA_CALL_MEDIA_REMOTE_HOLD, PJSUA_CALL_MEDIA_ERROR} pjsua_call_media_status;
|
||||
|
|
|
@ -33,4 +33,4 @@ pjsip-simple/evsub.h pjsip_evsub_state
|
|||
|
||||
pjsip-ua/sip_inv.h pjsip_inv_state
|
||||
|
||||
pjsua-lib/pjsua.h pjsua_invalid_id_const_ pjsua_state pjsua_stun_use pjsua_call_hold_type pjsua_acc_id pjsua_destroy_flag pjsua_100rel_use pjsua_sip_timer_use pjsua_ipv6_use pjsua_buddy_status pjsua_call_media_status pjsua_vid_win_id pjsua_call_id pjsua_med_tp_st pjsua_call_vid_strm_op pjsua_vid_req_keyframe_method pjsua_call_flag pjsua_create_media_transport_flag pjsua_snd_dev_mode
|
||||
pjsua-lib/pjsua.h pjsua_invalid_id_const_ pjsua_state pjsua_stun_use pjsua_call_hold_type pjsua_acc_id pjsua_destroy_flag pjsua_100rel_use pjsua_sip_timer_use pjsua_ipv6_use pjsua_nat64_opt pjsua_buddy_status pjsua_call_media_status pjsua_vid_win_id pjsua_call_id pjsua_med_tp_st pjsua_call_vid_strm_op pjsua_vid_req_keyframe_method pjsua_call_flag pjsua_create_media_transport_flag pjsua_snd_dev_mode
|
||||
|
|
|
@ -1594,6 +1594,15 @@ typedef struct pjsua_config
|
|||
*/
|
||||
pj_str_t stun_srv[8];
|
||||
|
||||
/**
|
||||
* This specifies if the library should try to do an IPv6 resolution of
|
||||
* the STUN servers if the IPv4 resolution fails. It can be useful
|
||||
* in an IPv6-only environment, including on NAT64.
|
||||
*
|
||||
* Default: PJ_FALSE
|
||||
*/
|
||||
pj_bool_t stun_try_ipv6;
|
||||
|
||||
/**
|
||||
* This specifies if the library should ignore failure with the
|
||||
* STUN servers. If this is set to PJ_FALSE, the library will refuse to
|
||||
|
@ -2909,6 +2918,23 @@ typedef enum pjsua_ipv6_use
|
|||
|
||||
} pjsua_ipv6_use;
|
||||
|
||||
/**
|
||||
* Specify NAT64 options to be used in account config.
|
||||
*/
|
||||
typedef enum pjsua_nat64_opt
|
||||
{
|
||||
/**
|
||||
* NAT64 is not used.
|
||||
*/
|
||||
PJSUA_NAT64_DISABLED,
|
||||
|
||||
/**
|
||||
* NAT64 is enabled.
|
||||
*/
|
||||
PJSUA_NAT64_ENABLED
|
||||
|
||||
} pjsua_nat64_opt;
|
||||
|
||||
/**
|
||||
* This structure describes account configuration to be specified when
|
||||
* adding a new account with #pjsua_acc_add(). Application MUST initialize
|
||||
|
@ -3346,6 +3372,13 @@ typedef struct pjsua_acc_config
|
|||
*/
|
||||
pjsua_transport_config rtp_cfg;
|
||||
|
||||
/**
|
||||
* Specify NAT64 options.
|
||||
*
|
||||
* Default: PJSUA_NAT64_DISABLED
|
||||
*/
|
||||
pjsua_nat64_opt nat64_opt;
|
||||
|
||||
/**
|
||||
* Specify whether IPv6 should be used on media.
|
||||
*/
|
||||
|
|
|
@ -374,6 +374,7 @@ typedef struct pjsua_stun_resolve
|
|||
pj_status_t status; /**< Session status */
|
||||
pj_sockaddr addr; /**< Result */
|
||||
pj_stun_sock *stun_sock; /**< Testing STUN sock */
|
||||
int af; /**< Address family */
|
||||
pj_bool_t async_wait;/**< Async resolution
|
||||
of STUN entry */
|
||||
} pjsua_stun_resolve;
|
||||
|
|
|
@ -468,6 +468,13 @@ struct AccountNatConfig : public PersistentObject
|
|||
*/
|
||||
pjsua_stun_use mediaStunUse;
|
||||
|
||||
/**
|
||||
* Specify NAT64 options.
|
||||
*
|
||||
* Default: PJSUA_NAT64_DISABLED
|
||||
*/
|
||||
pjsua_nat64_opt nat64Opt;
|
||||
|
||||
/**
|
||||
* Enable ICE for the media transport.
|
||||
*
|
||||
|
|
|
@ -390,6 +390,15 @@ struct UaConfig : public PersistentObject
|
|||
*/
|
||||
StringVector stunServer;
|
||||
|
||||
/**
|
||||
* This specifies if the library should try to do an IPv6 resolution of
|
||||
* the STUN servers if the IPv4 resolution fails. It can be useful
|
||||
* in an IPv6-only environment, including on NAT64.
|
||||
*
|
||||
* Default: FALSE
|
||||
*/
|
||||
|
||||
bool stunTryIpv6;
|
||||
/**
|
||||
* This specifies if the library startup should ignore failure with the
|
||||
* STUN servers. If this is set to PJ_FALSE, the library will refuse to
|
||||
|
|
|
@ -700,7 +700,7 @@ static pj_status_t transport_attach( pjsip_endpoint *endpt,
|
|||
{
|
||||
pj_pool_t *pool;
|
||||
struct udp_transport *tp;
|
||||
const char *format, *ipv6_quoteb, *ipv6_quotee;
|
||||
const char *format, *ipv6_quoteb = "", *ipv6_quotee = "";
|
||||
unsigned i;
|
||||
pj_status_t status;
|
||||
|
||||
|
@ -709,12 +709,18 @@ static pj_status_t transport_attach( pjsip_endpoint *endpt,
|
|||
|
||||
/* Object name. */
|
||||
if (type & PJSIP_TRANSPORT_IPV6) {
|
||||
pj_in6_addr dummy6;
|
||||
|
||||
format = "udpv6%p";
|
||||
ipv6_quoteb = "[";
|
||||
ipv6_quotee = "]";
|
||||
/* We don't need to add quote if the transport type is IPv6, but
|
||||
* actually translated to IPv4.
|
||||
*/
|
||||
if (pj_inet_pton(pj_AF_INET6(), &a_name->host, &dummy6)==PJ_SUCCESS) {
|
||||
ipv6_quoteb = "[";
|
||||
ipv6_quotee = "]";
|
||||
}
|
||||
} else {
|
||||
format = "udp%p";
|
||||
ipv6_quoteb = ipv6_quotee = "";
|
||||
}
|
||||
|
||||
/* Create pool. */
|
||||
|
|
|
@ -1219,7 +1219,8 @@ static void stateless_send_transport_cb( void *token,
|
|||
* request.
|
||||
*/
|
||||
if (tdata->via_addr.host.slen > 0 &&
|
||||
(tdata->via_tp == (void *)stateless_data->cur_transport ||
|
||||
(!tdata->via_tp ||
|
||||
tdata->via_tp == (void *)stateless_data->cur_transport ||
|
||||
tdata->msg->line.req.method.id == PJSIP_CANCEL_METHOD))
|
||||
{
|
||||
via->sent_by = tdata->via_addr;
|
||||
|
|
|
@ -32,6 +32,7 @@ enum
|
|||
};
|
||||
|
||||
|
||||
static int get_ip_addr_ver(const pj_str_t *host);
|
||||
static void schedule_reregistration(pjsua_acc *acc);
|
||||
static void keep_alive_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te);
|
||||
|
||||
|
@ -547,7 +548,7 @@ PJ_DEF(pj_status_t) pjsua_acc_add_local( pjsua_transport_id tid,
|
|||
--cfg.priority;
|
||||
|
||||
/* Enclose IPv6 address in square brackets */
|
||||
if (t->type & PJSIP_TRANSPORT_IPV6) {
|
||||
if (get_ip_addr_ver(&t->local_name.host) == 6) {
|
||||
beginquote = "[";
|
||||
endquote = "]";
|
||||
} else {
|
||||
|
@ -1325,6 +1326,7 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id,
|
|||
acc->cfg.rtp_cfg.bound_addr = b_addr;
|
||||
}
|
||||
|
||||
acc->cfg.nat64_opt = cfg->nat64_opt;
|
||||
acc->cfg.ipv6_media_use = cfg->ipv6_media_use;
|
||||
|
||||
/* STUN and Media customization */
|
||||
|
@ -1696,9 +1698,12 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
|
|||
if (status == PJ_SUCCESS) {
|
||||
/* Compare the addresses as sockaddr according to the ticket above,
|
||||
* but only if they have the same family (ipv4 vs ipv4, or
|
||||
* ipv6 vs ipv6)
|
||||
* ipv6 vs ipv6).
|
||||
* Checking for the same address family is currently disabled,
|
||||
* since it can be useful in cases such as when on NAT64,
|
||||
* in order to get the IPv4-mapped address from IPv6.
|
||||
*/
|
||||
matched = (contact_addr.addr.sa_family != recv_addr.addr.sa_family) ||
|
||||
matched = //(contact_addr.addr.sa_family != recv_addr.addr.sa_family)||
|
||||
(uri->port == rport &&
|
||||
pj_sockaddr_cmp(&contact_addr, &recv_addr)==0);
|
||||
} else {
|
||||
|
@ -3114,6 +3119,7 @@ pj_status_t pjsua_acc_get_uac_addr(pjsua_acc_id acc_id,
|
|||
pjsip_tpselector tp_sel;
|
||||
pjsip_tpmgr *tpmgr;
|
||||
pjsip_tpmgr_fla2_param tfla2_prm;
|
||||
pj_bool_t update_addr = PJ_TRUE;
|
||||
|
||||
PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
|
||||
acc = &pjsua_var.acc[acc_id];
|
||||
|
@ -3183,6 +3189,25 @@ pj_status_t pjsua_acc_get_uac_addr(pjsua_acc_id acc_id,
|
|||
addr->host = tfla2_prm.ret_addr;
|
||||
addr->port = tfla2_prm.ret_port;
|
||||
|
||||
/* If we are behind NAT64, use the Contact and Via address from
|
||||
* the UDP6 transport, which should be obtained from STUN.
|
||||
*/
|
||||
if (acc->cfg.nat64_opt != PJSUA_NAT64_DISABLED) {
|
||||
pjsip_tpmgr_fla2_param tfla2_prm2 = tfla2_prm;
|
||||
|
||||
tfla2_prm2.tp_type = PJSIP_TRANSPORT_UDP6;
|
||||
tfla2_prm2.tp_sel = NULL;
|
||||
tfla2_prm2.local_if = (!pjsua_sip_acc_is_using_stun(acc_id));
|
||||
status = pjsip_tpmgr_find_local_addr2(tpmgr, pool, &tfla2_prm2);
|
||||
if (status == PJ_SUCCESS) {
|
||||
update_addr = PJ_FALSE;
|
||||
addr->host = tfla2_prm2.ret_addr;
|
||||
pj_strdup(acc->pool, &acc->via_addr.host, &addr->host);
|
||||
acc->via_addr.port = addr->port;
|
||||
acc->via_tp = (pjsip_transport *)tfla2_prm.ret_tp;
|
||||
}
|
||||
}
|
||||
|
||||
/* For TCP/TLS, acc may request to specify source port */
|
||||
if (acc->cfg.contact_use_src_port) {
|
||||
pjsip_host_info dinfo;
|
||||
|
@ -3276,8 +3301,12 @@ pj_status_t pjsua_acc_get_uac_addr(pjsua_acc_id acc_id,
|
|||
}
|
||||
|
||||
if (status == PJ_SUCCESS) {
|
||||
/* Got the local transport address */
|
||||
pj_strdup(pool, &addr->host, &tp->local_name.host);
|
||||
/* Got the local transport address, don't update if
|
||||
* we are on NAT64 and already obtained the address
|
||||
* from STUN above.
|
||||
*/
|
||||
if (update_addr)
|
||||
pj_strdup(pool, &addr->host, &tp->local_name.host);
|
||||
addr->port = tp->local_name.port;
|
||||
}
|
||||
|
||||
|
|
|
@ -1297,9 +1297,14 @@ static pj_bool_t test_stun_on_status(pj_stun_sock *stun_sock,
|
|||
|
||||
stun_resolve_add_ref(sess);
|
||||
|
||||
++sess->idx;
|
||||
if (sess->idx >= sess->count)
|
||||
sess->status = status;
|
||||
if (pjsua_var.ua_cfg.stun_try_ipv6 && sess->af == pj_AF_INET()) {
|
||||
sess->af = pj_AF_INET6();
|
||||
} else {
|
||||
++sess->idx;
|
||||
sess->af = pj_AF_INET();
|
||||
if (sess->idx >= sess->count)
|
||||
sess->status = status;
|
||||
}
|
||||
|
||||
resolve_stun_entry(sess);
|
||||
|
||||
|
@ -1339,7 +1344,10 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess)
|
|||
pj_status_t status = PJ_EUNKNOWN;
|
||||
|
||||
/* Loop while we have entry to try */
|
||||
for (; sess->idx < sess->count; ++sess->idx) {
|
||||
for (; sess->idx < sess->count;
|
||||
(pjsua_var.ua_cfg.stun_try_ipv6 && sess->af == pj_AF_INET())?
|
||||
sess->af = pj_AF_INET6(): (++sess->idx, sess->af = pj_AF_INET()))
|
||||
{
|
||||
int af;
|
||||
char target[64];
|
||||
pj_str_t hostpart;
|
||||
|
@ -1358,14 +1366,6 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess)
|
|||
if (status != PJ_SUCCESS) {
|
||||
PJ_LOG(2,(THIS_FILE, "Invalid STUN server entry %s", target));
|
||||
continue;
|
||||
} else if (af != pj_AF_INET()) {
|
||||
/* Ignore IPv6 STUN server for now */
|
||||
status = PJ_EAFNOTSUP;
|
||||
PJ_LOG(3,(THIS_FILE, "Ignored STUN server entry %s, currently "
|
||||
"only IPv4 STUN server is supported (does "
|
||||
"IPv6 still need a mapped address?)",
|
||||
target));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Use default port if not specified */
|
||||
|
@ -1374,15 +1374,16 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess)
|
|||
|
||||
pj_assert(sess->stun_sock == NULL);
|
||||
|
||||
PJ_LOG(4,(THIS_FILE, "Trying STUN server %s (%d of %d)..",
|
||||
target, sess->idx+1, sess->count));
|
||||
PJ_LOG(4,(THIS_FILE, "Trying STUN server %s %s (%d of %d)..",
|
||||
target, (sess->af == pj_AF_INET()? "IPv4": "IPv6"),
|
||||
sess->idx+1, sess->count));
|
||||
|
||||
/* Use STUN_sock to test this entry */
|
||||
pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb));
|
||||
stun_sock_cb.on_status = &test_stun_on_status;
|
||||
sess->async_wait = PJ_FALSE;
|
||||
status = pj_stun_sock_create(&pjsua_var.stun_cfg, "stunresolve",
|
||||
pj_AF_INET(), &stun_sock_cb,
|
||||
sess->af, &stun_sock_cb,
|
||||
NULL, sess, &sess->stun_sock);
|
||||
if (status != PJ_SUCCESS) {
|
||||
char errmsg[PJ_ERR_MSG_SIZE];
|
||||
|
@ -1488,6 +1489,7 @@ PJ_DEF(pj_status_t) pjsua_resolve_stun_servers( unsigned count,
|
|||
sess->blocking = wait;
|
||||
sess->waiter = pj_thread_this();
|
||||
sess->status = PJ_EPENDING;
|
||||
sess->af = pj_AF_INET();
|
||||
sess->srv = (pj_str_t*) pj_pool_calloc(pool, count, sizeof(pj_str_t));
|
||||
for (i=0; i<count; ++i) {
|
||||
pj_strdup(pool, &sess->srv[i], &srv[i]);
|
||||
|
@ -2157,18 +2159,22 @@ static pj_status_t create_sip_udp_sock(int af,
|
|||
if (pj_sockaddr_get_port(p_pub_addr) == 0)
|
||||
pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
|
||||
|
||||
} else if (stun_srv.slen && af == pj_AF_INET()) {
|
||||
} else if (stun_srv.slen &&
|
||||
(af == pj_AF_INET() || pjsua_var.ua_cfg.stun_try_ipv6))
|
||||
{
|
||||
pjstun_setting stun_opt;
|
||||
|
||||
/*
|
||||
* STUN is specified, resolve the address with STUN.
|
||||
* Currently, this is available for IPv4 address only.
|
||||
* Currently, this is only to get IPv4 mapped address
|
||||
* (does IPv6 still need a mapped address?).
|
||||
*/
|
||||
pj_bzero(&stun_opt, sizeof(stun_opt));
|
||||
stun_opt.use_stun2 = pjsua_var.ua_cfg.stun_map_use_stun2;
|
||||
stun_opt.af = pjsua_var.stun_srv.addr.sa_family;
|
||||
stun_opt.srv1 = stun_opt.srv2 = stun_srv;
|
||||
stun_opt.port1 = stun_opt.port2 =
|
||||
pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port);
|
||||
pj_sockaddr_get_port(&pjsua_var.stun_srv);
|
||||
status = pjstun_get_mapped_addr2(&pjsua_var.cp.factory, &stun_opt,
|
||||
1, &sock, &p_pub_addr->ipv4);
|
||||
if (status != PJ_SUCCESS) {
|
||||
|
@ -2894,14 +2900,14 @@ PJ_DEF(pj_status_t) pjsua_detect_nat_type()
|
|||
}
|
||||
|
||||
/* Make sure we have STUN */
|
||||
if (pjsua_var.stun_srv.ipv4.sin_family == 0) {
|
||||
if (pjsua_var.stun_srv.addr.sa_family == 0) {
|
||||
pjsua_var.nat_status = PJNATH_ESTUNINSERVER;
|
||||
return PJNATH_ESTUNINSERVER;
|
||||
}
|
||||
|
||||
status = pj_stun_detect_nat_type(&pjsua_var.stun_srv.ipv4,
|
||||
&pjsua_var.stun_cfg,
|
||||
NULL, &nat_detect_cb);
|
||||
status = pj_stun_detect_nat_type2(&pjsua_var.stun_srv,
|
||||
&pjsua_var.stun_cfg,
|
||||
NULL, &nat_detect_cb);
|
||||
|
||||
if (status != PJ_SUCCESS) {
|
||||
pjsua_var.nat_status = status;
|
||||
|
|
|
@ -240,7 +240,7 @@ static pj_status_t create_rtp_rtcp_sock(pjsua_call_media *call_med,
|
|||
RTP_RETRY = 100
|
||||
};
|
||||
int i;
|
||||
pj_bool_t use_ipv6;
|
||||
pj_bool_t use_ipv6, use_nat64;
|
||||
int af;
|
||||
pj_sockaddr bound_addr;
|
||||
pj_sockaddr mapped_addr[2];
|
||||
|
@ -250,10 +250,13 @@ static pj_status_t create_rtp_rtcp_sock(pjsua_call_media *call_med,
|
|||
pj_sock_t sock[2];
|
||||
|
||||
use_ipv6 = (acc->cfg.ipv6_media_use != PJSUA_IPV6_DISABLED);
|
||||
af = use_ipv6 ? pj_AF_INET6() : pj_AF_INET();
|
||||
use_nat64 = (acc->cfg.nat64_opt != PJSUA_NAT64_DISABLED);
|
||||
af = (use_ipv6 || use_nat64) ? pj_AF_INET6() : pj_AF_INET();
|
||||
|
||||
/* Make sure STUN server resolution has completed */
|
||||
if (!use_ipv6 && pjsua_media_acc_is_using_stun(call_med->call->acc_id)) {
|
||||
if ((!use_ipv6 || use_nat64) &&
|
||||
pjsua_media_acc_is_using_stun(call_med->call->acc_id))
|
||||
{
|
||||
pj_bool_t retry_stun = (acc->cfg.media_stun_use &
|
||||
PJSUA_STUN_RETRY_ON_FAILURE) ==
|
||||
PJSUA_STUN_RETRY_ON_FAILURE;
|
||||
|
@ -353,11 +356,11 @@ static pj_status_t create_rtp_rtcp_sock(pjsua_call_media *call_med,
|
|||
* If we're configured to use STUN, then find out the mapped address,
|
||||
* and make sure that the mapped RTCP port is adjacent with the RTP.
|
||||
*/
|
||||
if (!use_ipv6 &&
|
||||
if ((!use_ipv6 || use_nat64) &&
|
||||
pjsua_media_acc_is_using_stun(call_med->call->acc_id) &&
|
||||
pjsua_var.stun_srv.addr.sa_family != 0)
|
||||
{
|
||||
char ip_addr[32];
|
||||
char ip_addr[PJ_INET6_ADDRSTRLEN];
|
||||
pj_str_t stun_srv;
|
||||
pj_sockaddr_in resolved_addr[2];
|
||||
pjstun_setting stun_opt;
|
||||
|
@ -367,9 +370,10 @@ static pj_status_t create_rtp_rtcp_sock(pjsua_call_media *call_med,
|
|||
|
||||
pj_bzero(&stun_opt, sizeof(stun_opt));
|
||||
stun_opt.use_stun2 = pjsua_var.ua_cfg.stun_map_use_stun2;
|
||||
stun_opt.af = pjsua_var.stun_srv.addr.sa_family;
|
||||
stun_opt.srv1 = stun_opt.srv2 = stun_srv;
|
||||
stun_opt.port1 = stun_opt.port2 =
|
||||
pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port);
|
||||
pj_sockaddr_get_port(&pjsua_var.stun_srv);
|
||||
status=pjstun_get_mapped_addr2(&pjsua_var.cp.factory, &stun_opt,
|
||||
2, sock, resolved_addr);
|
||||
#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
|
||||
|
@ -432,9 +436,10 @@ static pj_status_t create_rtp_rtcp_sock(pjsua_call_media *call_med,
|
|||
stun_srv.slen = 0;
|
||||
}
|
||||
|
||||
stun_opt.af = pjsua_var.stun_srv.addr.sa_family;
|
||||
stun_opt.srv1 = stun_opt.srv2 = stun_srv;
|
||||
stun_opt.port1 = stun_opt.port2 =
|
||||
pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port);
|
||||
pj_sockaddr_get_port(&pjsua_var.stun_srv);
|
||||
status = pjstun_get_mapped_addr2(&pjsua_var.cp.factory,
|
||||
&stun_opt, 2, sock,
|
||||
resolved_addr);
|
||||
|
@ -823,10 +828,11 @@ static pj_status_t create_ice_media_transport(
|
|||
char name[32];
|
||||
unsigned comp_cnt;
|
||||
pj_status_t status;
|
||||
pj_bool_t use_ipv6;
|
||||
pj_bool_t use_ipv6, use_nat64;
|
||||
|
||||
acc_cfg = &pjsua_var.acc[call_med->call->acc_id].cfg;
|
||||
use_ipv6 = (acc_cfg->ipv6_media_use != PJSUA_IPV6_DISABLED);
|
||||
use_nat64 = (acc_cfg->nat64_opt != PJSUA_NAT64_DISABLED);
|
||||
|
||||
/* Make sure STUN server resolution has completed */
|
||||
if (pjsua_media_acc_is_using_stun(call_med->call->acc_id)) {
|
||||
|
@ -863,7 +869,7 @@ static pj_status_t create_ice_media_transport(
|
|||
|
||||
if (pj_stricmp(&c->addr_type, &ID_IP6) == 0)
|
||||
ice_cfg.af = pj_AF_INET6();
|
||||
} else if (use_ipv6) {
|
||||
} else if (use_ipv6 || use_nat64) {
|
||||
ice_cfg.af = pj_AF_INET6();
|
||||
}
|
||||
|
||||
|
@ -874,7 +880,9 @@ static pj_status_t create_ice_media_transport(
|
|||
{
|
||||
ice_cfg.stun_tp_cnt = 1;
|
||||
pj_ice_strans_stun_cfg_default(&ice_cfg.stun_tp[0]);
|
||||
if (use_ipv6 && PJ_ICE_MAX_STUN >= 2) {
|
||||
if (use_nat64) {
|
||||
ice_cfg.stun_tp[0].af = pj_AF_INET6();
|
||||
} else if (use_ipv6 && PJ_ICE_MAX_STUN >= 2) {
|
||||
ice_cfg.stun_tp_cnt = 2;
|
||||
pj_ice_strans_stun_cfg_default(&ice_cfg.stun_tp[1]);
|
||||
ice_cfg.stun_tp[1].af = pj_AF_INET6();
|
||||
|
@ -885,72 +893,69 @@ static pj_status_t create_ice_media_transport(
|
|||
if (ice_cfg.stun_tp_cnt) {
|
||||
unsigned i;
|
||||
|
||||
/* Configure STUN server (currently only for IPv4) */
|
||||
if (pj_sockaddr_has_addr(&pjsua_var.stun_srv) &&
|
||||
pjsua_media_acc_is_using_stun(call_med->call->acc_id))
|
||||
{
|
||||
pj_sockaddr_print(&pjsua_var.stun_srv, stunip, sizeof(stunip), 0);
|
||||
ice_cfg.stun_tp[0].server = pj_str(stunip);
|
||||
ice_cfg.stun_tp[0].port =
|
||||
pj_sockaddr_get_port(&pjsua_var.stun_srv);
|
||||
}
|
||||
pj_sockaddr_print(&pjsua_var.stun_srv, stunip, sizeof(stunip), 0);
|
||||
|
||||
/* Configure max host candidates */
|
||||
if (acc_cfg->ice_cfg.ice_max_host_cands >= 0) {
|
||||
for (i = 0; i < ice_cfg.stun_tp_cnt; ++i)
|
||||
for (i = 0; i < ice_cfg.stun_tp_cnt; ++i) {
|
||||
pj_str_t IN6_ADDR_ANY = {"0", 1};
|
||||
|
||||
/* Configure STUN server */
|
||||
if (pj_sockaddr_has_addr(&pjsua_var.stun_srv) &&
|
||||
pjsua_media_acc_is_using_stun(call_med->call->acc_id))
|
||||
{
|
||||
ice_cfg.stun_tp[i].server = pj_str(stunip);
|
||||
ice_cfg.stun_tp[i].port = pj_sockaddr_get_port(
|
||||
&pjsua_var.stun_srv);
|
||||
}
|
||||
|
||||
/* Configure max host candidates */
|
||||
if (acc_cfg->ice_cfg.ice_max_host_cands >= 0) {
|
||||
ice_cfg.stun_tp[i].max_host_cands =
|
||||
acc_cfg->ice_cfg.ice_max_host_cands;
|
||||
}
|
||||
}
|
||||
|
||||
/* Configure binding address */
|
||||
pj_sockaddr_init(ice_cfg.stun_tp[0].af,
|
||||
&ice_cfg.stun_tp[0].cfg.bound_addr,
|
||||
&cfg->bound_addr, (pj_uint16_t)cfg->port);
|
||||
ice_cfg.stun_tp[0].cfg.port_range = (pj_uint16_t)cfg->port_range;
|
||||
if (cfg->port != 0 && ice_cfg.stun_tp[0].cfg.port_range == 0) {
|
||||
ice_cfg.stun_tp[0].cfg.port_range =
|
||||
/* Configure binding address */
|
||||
pj_sockaddr_init(ice_cfg.stun_tp[i].af,
|
||||
&ice_cfg.stun_tp[i].cfg.bound_addr,
|
||||
(ice_cfg.stun_tp[i].af == pj_AF_INET()?
|
||||
&cfg->bound_addr: &IN6_ADDR_ANY),
|
||||
(pj_uint16_t)cfg->port);
|
||||
ice_cfg.stun_tp[i].cfg.port_range = (pj_uint16_t)cfg->port_range;
|
||||
if (cfg->port != 0 && ice_cfg.stun_tp[i].cfg.port_range == 0) {
|
||||
ice_cfg.stun_tp[i].cfg.port_range =
|
||||
(pj_uint16_t)(pjsua_var.ua_cfg.max_calls * 10);
|
||||
}
|
||||
if (use_ipv6 && ice_cfg.stun_tp_cnt > 1) {
|
||||
pj_str_t IN6_ADDR_ANY = {"0", 1};
|
||||
pj_sockaddr_init(pj_AF_INET6(),
|
||||
&ice_cfg.stun_tp[1].cfg.bound_addr,
|
||||
&IN6_ADDR_ANY, (pj_uint16_t)cfg->port);
|
||||
ice_cfg.stun_tp[1].cfg.port_range =
|
||||
ice_cfg.stun_tp[0].cfg.port_range;
|
||||
}
|
||||
}
|
||||
|
||||
/* Configure QoS setting */
|
||||
ice_cfg.stun_tp[0].cfg.qos_type = cfg->qos_type;
|
||||
pj_memcpy(&ice_cfg.stun_tp[0].cfg.qos_params, &cfg->qos_params,
|
||||
sizeof(cfg->qos_params));
|
||||
if (use_ipv6 && ice_cfg.stun_tp_cnt > 1) {
|
||||
ice_cfg.stun_tp[1].cfg.qos_type = cfg->qos_type;
|
||||
pj_memcpy(&ice_cfg.stun_tp[1].cfg.qos_params, &cfg->qos_params,
|
||||
/* Configure QoS setting */
|
||||
ice_cfg.stun_tp[i].cfg.qos_type = cfg->qos_type;
|
||||
pj_memcpy(&ice_cfg.stun_tp[i].cfg.qos_params, &cfg->qos_params,
|
||||
sizeof(cfg->qos_params));
|
||||
|
||||
/* Configure max packet size */
|
||||
ice_cfg.stun_tp[i].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
|
||||
}
|
||||
|
||||
/* Configure max packet size */
|
||||
ice_cfg.stun_tp[0].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
|
||||
if (use_ipv6 && ice_cfg.stun_tp_cnt > 1)
|
||||
ice_cfg.stun_tp[1].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
|
||||
|
||||
}
|
||||
|
||||
/* Configure TURN settings */
|
||||
if (acc_cfg->turn_cfg.enable_turn) {
|
||||
ice_cfg.turn_tp_cnt = 1;
|
||||
pj_ice_strans_turn_cfg_default(&ice_cfg.turn_tp[0]);
|
||||
if (use_ipv6 && PJ_ICE_MAX_TURN >= 3) {
|
||||
ice_cfg.turn_tp_cnt = 3;
|
||||
unsigned i, idx = 0;
|
||||
|
||||
if (use_ipv6 && !use_nat64 && PJ_ICE_MAX_TURN >= 3) {
|
||||
ice_cfg.turn_tp_cnt = 3;
|
||||
idx = 1;
|
||||
} else {
|
||||
ice_cfg.turn_tp_cnt = 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < ice_cfg.turn_tp_cnt; i++)
|
||||
pj_ice_strans_turn_cfg_default(&ice_cfg.turn_tp[i]);
|
||||
|
||||
pj_ice_strans_turn_cfg_default(&ice_cfg.turn_tp[1]);
|
||||
ice_cfg.turn_tp[1].af = pj_AF_INET6();
|
||||
if (use_ipv6 || use_nat64) {
|
||||
if (!use_nat64)
|
||||
ice_cfg.turn_tp[idx++].af = pj_AF_INET6();
|
||||
|
||||
/* Additional candidate: IPv4 relay via IPv6 TURN server */
|
||||
pj_ice_strans_turn_cfg_default(&ice_cfg.turn_tp[2]);
|
||||
ice_cfg.turn_tp[2].af = pj_AF_INET6();
|
||||
ice_cfg.turn_tp[2].alloc_param.af = pj_AF_INET();
|
||||
ice_cfg.turn_tp[idx].af = pj_AF_INET6();
|
||||
ice_cfg.turn_tp[idx].alloc_param.af = pj_AF_INET();
|
||||
}
|
||||
|
||||
/* Configure TURN server */
|
||||
|
@ -962,72 +967,38 @@ static pj_status_t create_ice_media_transport(
|
|||
return PJ_EINVAL;
|
||||
}
|
||||
|
||||
/* Configure TURN connection settings and credential */
|
||||
if (ice_cfg.turn_tp[0].port == 0)
|
||||
ice_cfg.turn_tp[0].port = 3479;
|
||||
ice_cfg.turn_tp[0].conn_type = acc_cfg->turn_cfg.turn_conn_type;
|
||||
pj_memcpy(&ice_cfg.turn_tp[0].auth_cred,
|
||||
&acc_cfg->turn_cfg.turn_auth_cred,
|
||||
sizeof(ice_cfg.turn_tp[0].auth_cred));
|
||||
|
||||
if (use_ipv6 && ice_cfg.turn_tp_cnt > 1) {
|
||||
ice_cfg.turn_tp[1].server = ice_cfg.turn_tp[0].server;
|
||||
ice_cfg.turn_tp[1].port = ice_cfg.turn_tp[0].port;
|
||||
ice_cfg.turn_tp[1].conn_type = ice_cfg.turn_tp[0].conn_type;
|
||||
pj_memcpy(&ice_cfg.turn_tp[1].auth_cred,
|
||||
for (i = 0; i < ice_cfg.turn_tp_cnt; i++) {
|
||||
pj_str_t IN6_ADDR_ANY = {"0", 1};
|
||||
|
||||
/* Configure TURN connection settings and credential */
|
||||
ice_cfg.turn_tp[i].server = ice_cfg.turn_tp[0].server;
|
||||
ice_cfg.turn_tp[i].port = ice_cfg.turn_tp[0].port;
|
||||
ice_cfg.turn_tp[i].conn_type = acc_cfg->turn_cfg.turn_conn_type;
|
||||
pj_memcpy(&ice_cfg.turn_tp[i].auth_cred,
|
||||
&acc_cfg->turn_cfg.turn_auth_cred,
|
||||
sizeof(ice_cfg.turn_tp[1].auth_cred));
|
||||
sizeof(ice_cfg.turn_tp[i].auth_cred));
|
||||
|
||||
ice_cfg.turn_tp[2].server = ice_cfg.turn_tp[0].server;
|
||||
ice_cfg.turn_tp[2].port = ice_cfg.turn_tp[0].port;
|
||||
ice_cfg.turn_tp[2].conn_type = ice_cfg.turn_tp[0].conn_type;
|
||||
pj_memcpy(&ice_cfg.turn_tp[2].auth_cred,
|
||||
&acc_cfg->turn_cfg.turn_auth_cred,
|
||||
sizeof(ice_cfg.turn_tp[2].auth_cred));
|
||||
}
|
||||
|
||||
/* Configure QoS setting */
|
||||
ice_cfg.turn_tp[0].cfg.qos_type = cfg->qos_type;
|
||||
pj_memcpy(&ice_cfg.turn_tp[0].cfg.qos_params, &cfg->qos_params,
|
||||
sizeof(cfg->qos_params));
|
||||
if (use_ipv6 && ice_cfg.turn_tp_cnt > 1) {
|
||||
ice_cfg.turn_tp[1].cfg.qos_type = cfg->qos_type;
|
||||
pj_memcpy(&ice_cfg.turn_tp[1].cfg.qos_params, &cfg->qos_params,
|
||||
/* Configure QoS setting */
|
||||
ice_cfg.turn_tp[i].cfg.qos_type = cfg->qos_type;
|
||||
pj_memcpy(&ice_cfg.turn_tp[i].cfg.qos_params, &cfg->qos_params,
|
||||
sizeof(cfg->qos_params));
|
||||
|
||||
ice_cfg.turn_tp[2].cfg.qos_type = cfg->qos_type;
|
||||
pj_memcpy(&ice_cfg.turn_tp[2].cfg.qos_params, &cfg->qos_params,
|
||||
sizeof(cfg->qos_params));
|
||||
}
|
||||
|
||||
/* Configure binding address */
|
||||
pj_sockaddr_init(ice_cfg.turn_tp[0].af, &ice_cfg.turn_tp[0].cfg.bound_addr,
|
||||
&cfg->bound_addr, (pj_uint16_t)cfg->port);
|
||||
ice_cfg.turn_tp[0].cfg.port_range = (pj_uint16_t)cfg->port_range;
|
||||
if (cfg->port != 0 && ice_cfg.turn_tp[0].cfg.port_range == 0)
|
||||
ice_cfg.turn_tp[0].cfg.port_range =
|
||||
/* Configure binding address */
|
||||
pj_sockaddr_init(ice_cfg.turn_tp[i].af,
|
||||
&ice_cfg.turn_tp[i].cfg.bound_addr,
|
||||
(ice_cfg.turn_tp[i].af == pj_AF_INET()?
|
||||
&cfg->bound_addr: &IN6_ADDR_ANY),
|
||||
(pj_uint16_t)cfg->port);
|
||||
ice_cfg.turn_tp[i].cfg.port_range = (pj_uint16_t)cfg->port_range;
|
||||
if (cfg->port != 0 && ice_cfg.turn_tp[i].cfg.port_range == 0)
|
||||
ice_cfg.turn_tp[i].cfg.port_range =
|
||||
(pj_uint16_t)(pjsua_var.ua_cfg.max_calls * 10);
|
||||
|
||||
if (use_ipv6 && ice_cfg.turn_tp_cnt > 1) {
|
||||
pj_str_t IN6_ADDR_ANY = {"0", 1};
|
||||
pj_sockaddr_init(pj_AF_INET6(),
|
||||
&ice_cfg.turn_tp[1].cfg.bound_addr,
|
||||
&IN6_ADDR_ANY, (pj_uint16_t)cfg->port);
|
||||
ice_cfg.turn_tp[1].cfg.port_range =
|
||||
ice_cfg.turn_tp[0].cfg.port_range;
|
||||
|
||||
pj_sockaddr_init(pj_AF_INET6(),
|
||||
&ice_cfg.turn_tp[2].cfg.bound_addr,
|
||||
&IN6_ADDR_ANY, (pj_uint16_t)cfg->port);
|
||||
ice_cfg.turn_tp[2].cfg.port_range =
|
||||
ice_cfg.turn_tp[0].cfg.port_range;
|
||||
}
|
||||
|
||||
/* Configure max packet size */
|
||||
ice_cfg.turn_tp[0].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
|
||||
if (use_ipv6 && ice_cfg.turn_tp_cnt > 1) {
|
||||
ice_cfg.turn_tp[1].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
|
||||
ice_cfg.turn_tp[2].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
|
||||
/* Configure max packet size */
|
||||
ice_cfg.turn_tp[i].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2395,13 +2366,16 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
|
|||
/* Add connection line, if none */
|
||||
if (m->conn == NULL && sdp->conn == NULL) {
|
||||
pj_bool_t use_ipv6;
|
||||
pj_bool_t use_nat64;
|
||||
|
||||
use_ipv6 = (pjsua_var.acc[call->acc_id].cfg.ipv6_media_use !=
|
||||
PJSUA_IPV6_DISABLED);
|
||||
use_nat64 = (pjsua_var.acc[call->acc_id].cfg.nat64_opt !=
|
||||
PJSUA_NAT64_DISABLED);
|
||||
|
||||
m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
|
||||
m->conn->net_type = pj_str("IN");
|
||||
if (use_ipv6) {
|
||||
if (use_ipv6 && !use_nat64) {
|
||||
m->conn->addr_type = pj_str("IP6");
|
||||
m->conn->addr = pj_str("::1");
|
||||
} else {
|
||||
|
|
|
@ -184,6 +184,7 @@ void AccountNatConfig::readObject(const ContainerNode &node) throw(Error)
|
|||
|
||||
NODE_READ_NUM_T ( this_node, pjsua_stun_use, sipStunUse);
|
||||
NODE_READ_NUM_T ( this_node, pjsua_stun_use, mediaStunUse);
|
||||
NODE_READ_NUM_T ( this_node, pjsua_nat64_opt, nat64Opt);
|
||||
NODE_READ_BOOL ( this_node, iceEnabled);
|
||||
NODE_READ_INT ( this_node, iceMaxHostCands);
|
||||
NODE_READ_BOOL ( this_node, iceAggressiveNomination);
|
||||
|
@ -215,6 +216,7 @@ void AccountNatConfig::writeObject(ContainerNode &node) const throw(Error)
|
|||
|
||||
NODE_WRITE_NUM_T ( this_node, pjsua_stun_use, sipStunUse);
|
||||
NODE_WRITE_NUM_T ( this_node, pjsua_stun_use, mediaStunUse);
|
||||
NODE_WRITE_NUM_T ( this_node, pjsua_nat64_opt, nat64Opt);
|
||||
NODE_WRITE_BOOL ( this_node, iceEnabled);
|
||||
NODE_WRITE_INT ( this_node, iceMaxHostCands);
|
||||
NODE_WRITE_BOOL ( this_node, iceAggressiveNomination);
|
||||
|
@ -391,6 +393,7 @@ void AccountConfig::toPj(pjsua_acc_config &ret) const
|
|||
// AccountNatConfig
|
||||
ret.sip_stun_use = natConfig.sipStunUse;
|
||||
ret.media_stun_use = natConfig.mediaStunUse;
|
||||
ret.nat64_opt = natConfig.nat64Opt;
|
||||
ret.ice_cfg_use = PJSUA_ICE_CONFIG_USE_CUSTOM;
|
||||
ret.ice_cfg.enable_ice = natConfig.iceEnabled;
|
||||
ret.ice_cfg.ice_max_host_cands = natConfig.iceMaxHostCands;
|
||||
|
@ -534,6 +537,7 @@ void AccountConfig::fromPj(const pjsua_acc_config &prm,
|
|||
// AccountNatConfig
|
||||
natConfig.sipStunUse = prm.sip_stun_use;
|
||||
natConfig.mediaStunUse = prm.media_stun_use;
|
||||
natConfig.nat64Opt = prm.nat64_opt;
|
||||
if (prm.ice_cfg_use == PJSUA_ICE_CONFIG_USE_CUSTOM) {
|
||||
natConfig.iceEnabled = PJ2BOOL(prm.ice_cfg.enable_ice);
|
||||
natConfig.iceMaxHostCands = prm.ice_cfg.ice_max_host_cands;
|
||||
|
|
|
@ -148,6 +148,7 @@ void UaConfig::fromPj(const pjsua_config &ua_cfg)
|
|||
this->stunServer.push_back(pj2Str(ua_cfg.stun_srv[i]));
|
||||
}
|
||||
|
||||
this->stunTryIpv6 = PJ2BOOL(ua_cfg.stun_try_ipv6);
|
||||
this->stunIgnoreFailure = PJ2BOOL(ua_cfg.stun_ignore_failure);
|
||||
this->natTypeInSdp = ua_cfg.nat_type_in_sdp;
|
||||
this->mwiUnsolicitedEnabled = PJ2BOOL(ua_cfg.enable_unsolicited_mwi);
|
||||
|
@ -194,6 +195,7 @@ void UaConfig::readObject(const ContainerNode &node) throw(Error)
|
|||
NODE_READ_STRINGV ( this_node, nameserver);
|
||||
NODE_READ_STRING ( this_node, userAgent);
|
||||
NODE_READ_STRINGV ( this_node, stunServer);
|
||||
NODE_READ_BOOL ( this_node, stunTryIpv6);
|
||||
NODE_READ_BOOL ( this_node, stunIgnoreFailure);
|
||||
NODE_READ_INT ( this_node, natTypeInSdp);
|
||||
NODE_READ_BOOL ( this_node, mwiUnsolicitedEnabled);
|
||||
|
@ -209,6 +211,7 @@ void UaConfig::writeObject(ContainerNode &node) const throw(Error)
|
|||
NODE_WRITE_STRINGV ( this_node, nameserver);
|
||||
NODE_WRITE_STRING ( this_node, userAgent);
|
||||
NODE_WRITE_STRINGV ( this_node, stunServer);
|
||||
NODE_WRITE_BOOL ( this_node, stunTryIpv6);
|
||||
NODE_WRITE_BOOL ( this_node, stunIgnoreFailure);
|
||||
NODE_WRITE_INT ( this_node, natTypeInSdp);
|
||||
NODE_WRITE_BOOL ( this_node, mwiUnsolicitedEnabled);
|
||||
|
|
Loading…
Reference in New Issue