summaryrefslogtreecommitdiff
path: root/pjsip
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip')
-rw-r--r--pjsip/include/pjsip/sip_transport.h2
-rw-r--r--pjsip/include/pjsip/sip_transport_tcp.h37
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h25
-rw-r--r--pjsip/src/pjsip/sip_transport_tcp.c97
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c102
-rw-r--r--pjsip/src/pjsua-lib/pjsua_media.c29
6 files changed, 250 insertions, 42 deletions
diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h
index f0f915cd..4c9120e9 100644
--- a/pjsip/include/pjsip/sip_transport.h
+++ b/pjsip/include/pjsip/sip_transport.h
@@ -726,6 +726,8 @@ struct pjsip_tpfactory
/** This list is managed by transport manager. */
PJ_DECL_LIST_MEMBER(struct pjsip_tpfactory);
+ char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */
+
pj_pool_t *pool; /**< Owned memory pool. */
pj_lock_t *lock; /**< Lock object. */
diff --git a/pjsip/include/pjsip/sip_transport_tcp.h b/pjsip/include/pjsip/sip_transport_tcp.h
index 4f5664d4..db308d7d 100644
--- a/pjsip/include/pjsip/sip_transport_tcp.h
+++ b/pjsip/include/pjsip/sip_transport_tcp.h
@@ -78,6 +78,43 @@ PJ_DECL(pj_status_t) pjsip_tcp_transport_start(pjsip_endpoint *endpt,
pjsip_tpfactory **p_factory);
+
+/**
+ * A newer variant of #pjsip_tcp_transport_start(), which allows specifying
+ * the published/public address of the TCP transport.
+ *
+ * @param endpt The SIP endpoint.
+ * @param local Optional local address to bind, or specify the
+ * address to bind the server socket to. Both IP
+ * interface address and port fields are optional.
+ * If IP interface address is not specified, socket
+ * will be bound to PJ_INADDR_ANY. If port is not
+ * specified, socket will be bound to any port
+ * selected by the operating system.
+ * @param a_name Optional published address, which is the address to be
+ * advertised as the address of this SIP transport.
+ * If this argument is NULL, then the bound address
+ * will be used as the published address.
+ * @param async_cnt Number of simultaneous asynchronous accept()
+ * operations to be supported. It is recommended that
+ * the number here corresponds to the number of
+ * processors in the system (or the number of SIP
+ * worker threads).
+ * @param p_factory Optional pointer to receive the instance of the
+ * SIP TCP transport factory just created.
+ *
+ * @return PJ_SUCCESS when the transport has been successfully
+ * started and registered to transport manager, or
+ * the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt,
+ const pj_sockaddr_in *local,
+ const pjsip_host_port *a_name,
+ unsigned async_cnt,
+ pjsip_tpfactory **p_factory);
+
+
+
PJ_END_DECL
/**
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index 47641c92..9ded3f15 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -724,7 +724,7 @@ PJ_INLINE(void) pjsua_stun_config_default(pjsua_stun_config *cfg)
/**
- * Transport configuration for creating UDP transports for both SIP
+ * Transport configuration for creating transports for both SIP
* and media.
*/
typedef struct pjsua_transport_config
@@ -738,9 +738,28 @@ typedef struct pjsua_transport_config
unsigned port;
/**
- * Optional address where the socket should be bound.
+ * Optional address to advertise as the address of this transport.
+ * Application can specify any address or hostname for this field,
+ * for example it can point to one of the interface address in the
+ * system, or it can point to the public address of a NAT router
+ * where port mappings have been configured for the application.
+ *
+ * Note: this option can be used for both UDP and TCP as well!
+ */
+ pj_str_t public_addr;
+
+ /**
+ * Optional address where the socket should be bound to. This option
+ * SHOULD only be used to selectively bind the socket to particular
+ * interface (instead of 0.0.0.0), and SHOULD NOT be used to set the
+ * published address of a transport (the public_addr field should be
+ * used for that purpose).
+ *
+ * Note that unlike public_addr field, the address (or hostname) here
+ * MUST correspond to the actual interface address in the host, since
+ * this address will be specified as bind() argument.
*/
- pj_in_addr ip_addr;
+ pj_str_t bound_addr;
/**
* Flag to indicate whether STUN should be used.
diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c
index 4d9132d1..37e21a45 100644
--- a/pjsip/src/pjsip/sip_transport_tcp.c
+++ b/pjsip/src/pjsip/sip_transport_tcp.c
@@ -69,7 +69,6 @@ struct pending_accept
struct tcp_listener
{
pjsip_tpfactory factory;
- char obj_name[PJ_MAX_OBJ_NAME];
pj_bool_t is_registered;
pjsip_endpoint *endpt;
pjsip_tpmgr *tpmgr;
@@ -184,8 +183,9 @@ static void sockaddr_to_host_port( pj_pool_t *pool,
* This is the public API to create, initialize, register, and start the
* TCP listener.
*/
-PJ_DEF(pj_status_t) pjsip_tcp_transport_start( pjsip_endpoint *endpt,
+PJ_DEF(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt,
const pj_sockaddr_in *local,
+ const pjsip_host_port *a_name,
unsigned async_cnt,
pjsip_tpfactory **p_factory)
{
@@ -200,6 +200,19 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start( pjsip_endpoint *endpt,
/* Sanity check */
PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL);
+ /* Verify that address given in a_name (if any) is valid */
+ if (a_name && a_name->host.slen) {
+ pj_sockaddr_in tmp;
+
+ status = pj_sockaddr_in_init(&tmp, &a_name->host,
+ (pj_uint16_t)a_name->port);
+ if (status != PJ_SUCCESS || tmp.sin_addr.s_addr == PJ_INADDR_ANY ||
+ tmp.sin_addr.s_addr == PJ_INADDR_NONE)
+ {
+ /* Invalid address */
+ return PJ_EINVAL;
+ }
+ }
pool = pjsip_endpt_create_pool(endpt, "tcplis", POOL_LIS_INIT,
POOL_LIS_INC);
@@ -214,7 +227,7 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start( pjsip_endpoint *endpt,
pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TCP);
listener->sock = PJ_INVALID_SOCKET;
- pj_ansi_strcpy(listener->obj_name, "tcp");
+ pj_ansi_strcpy(listener->factory.obj_name, "tcplis");
status = pj_lock_create_recursive_mutex(pool, "tcplis",
&listener->factory.lock);
@@ -245,25 +258,46 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start( pjsip_endpoint *endpt,
if (status != PJ_SUCCESS)
goto on_error;
- /* If the address returns 0.0.0.0, use the first interface address
- * as the transport's address.
+ /* If published host/IP is specified, then use that address as the
+ * listener advertised address.
*/
- if (listener_addr->sin_addr.s_addr == 0) {
- pj_in_addr hostip;
+ if (a_name && a_name->host.slen) {
+ /* Copy the address */
+ listener->factory.addr_name = *a_name;
+ pj_strdup(listener->factory.pool, &listener->factory.addr_name.host,
+ &a_name->host);
+ listener->factory.addr_name.port = a_name->port;
- status = pj_gethostip(&hostip);
- if (status != PJ_SUCCESS)
- goto on_error;
+ } else {
+ /* No published address is given, use the bound address */
+
+ /* If the address returns 0.0.0.0, use the default
+ * interface address as the transport's address.
+ */
+ if (listener_addr->sin_addr.s_addr == 0) {
+ pj_in_addr hostip;
+
+ status = pj_gethostip(&hostip);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ listener_addr->sin_addr = hostip;
+ }
+
+ /* Save the address name */
+ sockaddr_to_host_port(listener->factory.pool,
+ &listener->factory.addr_name, listener_addr);
+ }
- listener_addr->sin_addr = hostip;
+ /* If port is zero, get the bound port */
+ if (listener->factory.addr_name.port == 0) {
+ listener->factory.addr_name.port = pj_ntohs(listener_addr->sin_port);
}
- pj_ansi_snprintf(listener->obj_name, sizeof(listener->obj_name),
- "tcp:%d", (int)pj_ntohs(listener_addr->sin_port));
+ pj_ansi_snprintf(listener->factory.obj_name,
+ sizeof(listener->factory.obj_name),
+ "tcplis:%d", listener->factory.addr_name.port);
- /* Save the address name */
- sockaddr_to_host_port(listener->factory.pool,
- &listener->factory.addr_name, listener_addr);
/* Start listening to the address */
status = pj_sock_listen(listener->sock, PJSIP_TCP_TRANSPORT_BACKLOG);
@@ -319,10 +353,11 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start( pjsip_endpoint *endpt,
listener->sock, PJ_EPENDING);
}
- PJ_LOG(4,(listener->obj_name,
- "SIP TCP listener ready for incoming connections at %s:%d",
- pj_inet_ntoa(listener_addr->sin_addr),
- (int)pj_ntohs(listener_addr->sin_port)));
+ PJ_LOG(4,(listener->factory.obj_name,
+ "SIP TCP listener ready for incoming connections at %.*s:%d",
+ (int)listener->factory.addr_name.host.slen,
+ listener->factory.addr_name.host.ptr,
+ listener->factory.addr_name.port));
/* Return the pointer to user */
if (p_factory) *p_factory = &listener->factory;
@@ -335,6 +370,19 @@ on_error:
}
+/*
+ * This is the public API to create, initialize, register, and start the
+ * TCP listener.
+ */
+PJ_DEF(pj_status_t) pjsip_tcp_transport_start( pjsip_endpoint *endpt,
+ const pj_sockaddr_in *local,
+ unsigned async_cnt,
+ pjsip_tpfactory **p_factory)
+{
+ return pjsip_tcp_transport_start2(endpt, local, NULL, async_cnt, p_factory);
+}
+
+
/* This callback is called by transport manager to destroy listener */
static pj_status_t lis_destroy(pjsip_tpfactory *factory)
{
@@ -373,7 +421,7 @@ static pj_status_t lis_destroy(pjsip_tpfactory *factory)
if (listener->factory.pool) {
pj_pool_t *pool = listener->factory.pool;
- PJ_LOG(4,(listener->obj_name, "SIP TCP listener destroyed"));
+ PJ_LOG(4,(listener->factory.obj_name, "SIP TCP listener destroyed"));
listener->factory.pool = NULL;
pj_pool_release(pool);
@@ -855,7 +903,8 @@ static void on_accept_complete( pj_ioqueue_key_t *key,
/*
* Error in accept().
*/
- tcp_perror(listener->obj_name, "Error in accept()", status);
+ tcp_perror(listener->factory.obj_name, "Error in accept()",
+ status);
/*
* Prevent endless accept() error loop by limiting the
@@ -865,7 +914,7 @@ static void on_accept_complete( pj_ioqueue_key_t *key,
*/
++err_cnt;
if (err_cnt >= 10) {
- PJ_LOG(1, (listener->obj_name,
+ PJ_LOG(1, (listener->factory.obj_name,
"Too many errors, listener stopping"));
}
@@ -882,7 +931,7 @@ static void on_accept_complete( pj_ioqueue_key_t *key,
goto next_accept;
}
- PJ_LOG(4,(listener->obj_name,
+ PJ_LOG(4,(listener->factory.obj_name,
"TCP listener %.*s:%d: got incoming TCP connection "
"from %s:%d, sock=%d",
(int)listener->factory.addr_name.host.slen,
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index d26094ab..6b39fdbb 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -798,6 +798,9 @@ static pj_status_t create_sip_udp_sock(pj_in_addr bound_addr,
* the name of local host.
*/
if (stun.stun_srv1.slen) {
+ /*
+ * STUN is specified, resolve the address with STUN.
+ */
status = pj_stun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock,
&stun.stun_srv1,
stun.stun_port1,
@@ -810,6 +813,13 @@ static pj_status_t create_sip_udp_sock(pj_in_addr bound_addr,
return status;
}
+ } else if (p_pub_addr->sin_addr.s_addr != 0) {
+ /*
+ * Public address is already specified, no need to resolve the
+ * address, only set the port.
+ */
+ /* Do nothing */
+
} else {
pj_bzero(p_pub_addr, sizeof(pj_sockaddr_in));
@@ -866,6 +876,7 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
*/
pjsua_transport_config config;
pj_sock_t sock = PJ_INVALID_SOCKET;
+ pj_sockaddr_in bound_addr;
pj_sockaddr_in pub_addr;
pjsip_host_port addr_name;
@@ -875,9 +886,36 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
cfg = &config;
}
- /* Create the socket and possibly resolve the address with STUN */
- status = create_sip_udp_sock(cfg->ip_addr, cfg->port, cfg->use_stun,
- &cfg->stun_config, &sock, &pub_addr);
+ /* Initialize bound address, if any */
+ bound_addr.sin_addr.s_addr = PJ_INADDR_ANY;
+ if (cfg->bound_addr.slen) {
+ status = pj_sockaddr_in_set_str_addr(&bound_addr,&cfg->bound_addr);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE,
+ "Unable to resolve transport bound address",
+ status);
+ goto on_return;
+ }
+ }
+
+ /* Initialize the public address from the config, if any */
+ pj_sockaddr_in_init(&pub_addr, NULL, (pj_uint16_t)cfg->port);
+ if (cfg->public_addr.slen) {
+ status = pj_sockaddr_in_set_str_addr(&pub_addr, &cfg->public_addr);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE,
+ "Unable to resolve transport public address",
+ status);
+ goto on_return;
+ }
+ }
+
+ /* Create the socket and possibly resolve the address with STUN
+ * (only when public address is not specified).
+ */
+ status = create_sip_udp_sock(bound_addr.sin_addr, cfg->port,
+ cfg->use_stun, &cfg->stun_config,
+ &sock, &pub_addr);
if (status != PJ_SUCCESS)
goto on_return;
@@ -906,6 +944,7 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
* Create TCP transport.
*/
pjsua_transport_config config;
+ pjsip_host_port a_name;
pjsip_tpfactory *tcp;
pj_sockaddr_in local_addr;
@@ -921,12 +960,24 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
if (cfg->port)
local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
- if (cfg->ip_addr.s_addr)
- local_addr.sin_addr.s_addr = cfg->ip_addr.s_addr;
+ if (cfg->bound_addr.slen) {
+ status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE,
+ "Unable to resolve transport bound address",
+ status);
+ goto on_return;
+ }
+ }
+
+ /* Init published name */
+ pj_bzero(&a_name, sizeof(pjsip_host_port));
+ if (cfg->public_addr.slen)
+ a_name.host = cfg->public_addr;
/* Create the TCP transport */
- status = pjsip_tcp_transport_start(pjsua_var.endpt, &local_addr, 1,
- &tcp);
+ status = pjsip_tcp_transport_start2(pjsua_var.endpt, &local_addr,
+ &a_name, 1, &tcp);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error creating SIP TCP listener",
@@ -1029,6 +1080,7 @@ PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
pjsua_transport_info *info)
{
struct transport_data *t = &pjsua_var.tpdata[id];
+ pj_status_t status;
pj_bzero(info, sizeof(*info));
@@ -1059,6 +1111,8 @@ PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
info->local_name = tp->local_name;
info->usage_count = pj_atomic_get(tp->ref_cnt);
+ status = PJ_SUCCESS;
+
} else if (pjsua_var.tpdata[id].type == PJSIP_TRANSPORT_TCP) {
pjsip_tpfactory *factory = t->data.factory;
@@ -1078,12 +1132,17 @@ PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
info->local_name = factory->addr_name;
info->usage_count = 0;
+ status = PJ_SUCCESS;
+
+ } else {
+ pj_assert(!"Unsupported transport");
+ status = PJ_EINVALIDOP;
}
PJSUA_UNLOCK();
- return PJ_EINVALIDOP;
+ return status;
}
@@ -1120,12 +1179,29 @@ PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
/* Make sure that transport exists */
PJ_ASSERT_RETURN(pjsua_var.tpdata[id].data.ptr != NULL, PJ_EINVAL);
+ /* Note: destroy() may not work if there are objects still referencing
+ * the transport.
+ */
+ if (force) {
+ switch (pjsua_var.tpdata[id].type) {
+ case PJSIP_TRANSPORT_UDP:
+ return pjsip_transport_destroy(pjsua_var.tpdata[id].data.tp);
+ case PJSIP_TRANSPORT_TCP:
+ break;
+ }
+
+ } else {
+ switch (pjsua_var.tpdata[id].type) {
+ case PJSIP_TRANSPORT_UDP:
+ return pjsip_transport_shutdown(pjsua_var.tpdata[id].data.tp);
+ case PJSIP_TRANSPORT_TCP:
+ return (*pjsua_var.tpdata[id].data.factory->destroy)
+ (pjsua_var.tpdata[id].data.factory);
+ }
+ }
- /* To be done!! */
- PJ_UNUSED_ARG(force);
-
- PJ_TODO(pjsua_transport_close);
-
+ /* Unreachable */
+ pj_assert(!"Unknown transport");
return PJ_EINVALIDOP;
}
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index e37275a2..13ff542c 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -209,6 +209,7 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
};
int i;
static pj_uint16_t rtp_port;
+ pj_sockaddr_in bound_addr;
pj_sockaddr_in mapped_addr[2];
pj_status_t status = PJ_SUCCESS;
pj_sock_t sock[2];
@@ -219,6 +220,15 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
for (i=0; i<2; ++i)
sock[i] = PJ_INVALID_SOCKET;
+ bound_addr.sin_addr.s_addr = PJ_INADDR_ANY;
+ if (cfg->bound_addr.slen) {
+ status = pj_sockaddr_in_set_str_addr(&bound_addr, &cfg->bound_addr);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to resolve transport bind address",
+ status);
+ return status;
+ }
+ }
/* Loop retry to bind RTP and RTCP sockets. */
for (i=0; i<RTP_RETRY; ++i, rtp_port += 2) {
@@ -230,7 +240,8 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
return status;
}
- status = pj_sock_bind_in(sock[0], cfg->ip_addr.s_addr, rtp_port);
+ status = pj_sock_bind_in(sock[0], bound_addr.sin_addr.s_addr,
+ rtp_port);
if (status != PJ_SUCCESS) {
pj_sock_close(sock[0]);
sock[0] = PJ_INVALID_SOCKET;
@@ -245,7 +256,7 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
return status;
}
- status = pj_sock_bind_in(sock[1], cfg->ip_addr.s_addr,
+ status = pj_sock_bind_in(sock[1], bound_addr.sin_addr.s_addr,
(pj_uint16_t)(rtp_port+1));
if (status != PJ_SUCCESS) {
pj_sock_close(sock[0]);
@@ -285,6 +296,20 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
pj_sock_close(sock[1]);
sock[1] = PJ_INVALID_SOCKET;
+ } else if (cfg->public_addr.slen) {
+
+ status = pj_sockaddr_in_init(&mapped_addr[0], &cfg->public_addr,
+ (pj_uint16_t)rtp_port);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ status = pj_sockaddr_in_init(&mapped_addr[1], &cfg->public_addr,
+ (pj_uint16_t)(rtp_port+1));
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ break;
+
} else {
pj_in_addr addr;