summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2009-08-12 11:03:23 +0000
committerBenny Prijono <bennylp@teluu.com>2009-08-12 11:03:23 +0000
commit8df6724ddca5de98cfdc21316fb5d7e5de95d726 (patch)
treee48368d28208f4454071f196f49f7cddf3b0e9ba
parentc4851558e0c60e8a8929473631b825e1945f239b (diff)
Ticket #866: Allow application to specify more than one STUN servers for more robustness, and continue application startup if STUN resolution fails
PJSUA-LIB: - New fields in pjsua_config to specify more than one STUN servers (the stun_srv_cnt and stun_srv array) - The existing stun_host and stun_domain fields are deprecated, but backward compatibility is maintained. If stun_srv_cnt is zero, the library will import the entries from stun_host and stun_domain - The library will now resolve the STUN server entries one by one and test it before using it - New auxiliary API pjsua_resolve_stun_servers() to perform resolution and test against array of STUN servers pjsua application: - The "stun-srv" command line options can now be specified more than once git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2864 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjsip-apps/src/pjsua/pjsua_app.c29
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h133
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h28
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c436
-rw-r--r--pjsip/src/pjsua-lib/pjsua_media.c4
5 files changed, 483 insertions, 147 deletions
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c
index 6e7c8b81..883d6704 100644
--- a/pjsip-apps/src/pjsua/pjsua_app.c
+++ b/pjsip-apps/src/pjsua/pjsua_app.c
@@ -190,7 +190,7 @@ static void usage(void)
puts (" --password=string Set authentication password");
puts (" --publish Send presence PUBLISH for this account");
puts (" --use-100rel Require reliable provisional response (100rel)");
- puts (" --use-timer Require session timers");
+ puts (" --use-timer Require SIP session timers");
puts (" --timer-se Session timers expiration period, in secs (def:1800)");
puts (" --timer-min-se Session timers minimum expiration period, in secs (def:90)");
puts (" --auto-update-nat=N Where N is 0 or 1 to enable/disable SIP traversal behind");
@@ -216,7 +216,8 @@ static void usage(void)
puts (" This option can be specified multiple times.");
puts (" --outbound=url Set the URL of global outbound proxy server");
puts (" May be specified multiple times");
- puts (" --stun-srv=name Set STUN server host or domain");
+ puts (" --stun-srv=FORMAT Set STUN server host or domain. This option may be");
+ puts (" specified more than once. FORMAT is hostdom[:PORT]");
puts ("");
puts ("TLS Options:");
puts (" --use-tls Enable TLS transport (default=no)");
@@ -477,7 +478,7 @@ static pj_status_t parse_args(int argc, char *argv[],
OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
OPT_BOUND_ADDR, OPT_CONTACT_PARAMS, OPT_CONTACT_URI_PARAMS,
OPT_100REL, OPT_USE_IMS, OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
- OPT_NAMESERVER, OPT_STUN_DOMAIN, OPT_STUN_SRV,
+ OPT_NAMESERVER, OPT_STUN_SRV,
OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
OPT_AUTO_ANSWER, OPT_AUTO_PLAY, OPT_AUTO_PLAY_HANGUP, OPT_AUTO_LOOP,
OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO,
@@ -543,7 +544,6 @@ static pj_status_t parse_args(int argc, char *argv[],
{ "username", 1, 0, OPT_USERNAME},
{ "password", 1, 0, OPT_PASSWORD},
{ "nameserver", 1, 0, OPT_NAMESERVER},
- { "stun-domain",1, 0, OPT_STUN_DOMAIN},
{ "stun-srv", 1, 0, OPT_STUN_SRV},
{ "add-buddy", 1, 0, OPT_ADD_BUDDY},
{ "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
@@ -959,12 +959,13 @@ static pj_status_t parse_args(int argc, char *argv[],
}
break;
- case OPT_STUN_DOMAIN: /* STUN domain */
- cfg->cfg.stun_domain = pj_str(pj_optarg);
- break;
-
case OPT_STUN_SRV: /* STUN server */
cfg->cfg.stun_host = pj_str(pj_optarg);
+ if (cfg->cfg.stun_srv_cnt==PJ_ARRAY_SIZE(cfg->cfg.stun_srv)) {
+ PJ_LOG(1,(THIS_FILE, "Error: too many STUN servers"));
+ return PJ_ETOOMANY;
+ }
+ cfg->cfg.stun_srv[cfg->cfg.stun_srv_cnt++] = pj_str(pj_optarg);
break;
case OPT_ADD_BUDDY: /* Add to buddy list. */
@@ -1615,16 +1616,10 @@ static int write_settings(const struct app_config *config,
}
/* STUN */
- if (config->cfg.stun_domain.slen) {
- pj_ansi_sprintf(line, "--stun-domain %.*s\n",
- (int)config->cfg.stun_domain.slen,
- config->cfg.stun_domain.ptr);
- pj_strcat2(&cfg, line);
- }
- if (config->cfg.stun_host.slen) {
+ for (i=0; i<config->cfg.stun_srv_cnt; ++i) {
pj_ansi_sprintf(line, "--stun-srv %.*s\n",
- (int)config->cfg.stun_host.slen,
- config->cfg.stun_host.ptr);
+ (int)config->cfg.stun_srv[i].slen,
+ config->cfg.stun_srv[i].ptr);
pj_strcat2(&cfg, line);
}
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index 43db66ea..cd86a524 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -891,6 +891,10 @@ typedef struct pjsua_config
pj_str_t outbound_proxy[4];
/**
+ * Warning: deprecated, please use \a stun_srv field instead. To maintain
+ * backward compatibility, if \a stun_srv_cnt is zero then the value of
+ * this field will be copied to \a stun_srv field, if present.
+ *
* Specify domain name to be resolved with DNS SRV resolution to get the
* address of the STUN server. Alternatively application may specify
* \a stun_host instead.
@@ -901,12 +905,52 @@ typedef struct pjsua_config
pj_str_t stun_domain;
/**
+ * Warning: deprecated, please use \a stun_srv field instead. To maintain
+ * backward compatibility, if \a stun_srv_cnt is zero then the value of
+ * this field will be copied to \a stun_srv field, if present.
+ *
* Specify STUN server to be used, in "HOST[:PORT]" format. If port is
* not specified, default port 3478 will be used.
*/
pj_str_t stun_host;
/**
+ * Number of STUN server entries in \a stun_srv array.
+ */
+ unsigned stun_srv_cnt;
+
+ /**
+ * Array of STUN servers to try. The library will try to resolve and
+ * contact each of the STUN server entry until it finds one that is
+ * usable. Each entry may be a domain name, host name, IP address, and
+ * it may contain an optional port number. For example:
+ * - "pjsip.org" (domain name)
+ * - "sip.pjsip.org" (host name)
+ * - "pjsip.org:33478" (domain name and a non-standard port number)
+ * - "10.0.0.1:3478" (IP address and port number)
+ *
+ * When nameserver is configured in the \a pjsua_config.nameserver field,
+ * if entry is not an IP address, it will be resolved with DNS SRV
+ * resolution first, and it will fallback to use DNS A resolution if this
+ * fails. Port number may be specified even if the entry is a domain name,
+ * in case the DNS SRV resolution should fallback to a non-standard port.
+ *
+ * When nameserver is not configured, entries will be resolved with
+ * #pj_gethostbyname() if it's not an IP address. Port number may be
+ * specified if the server is not listening in standard STUN port.
+ */
+ pj_str_t stun_srv[8];
+
+ /**
+ * 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
+ * start if it fails to resolve or contact any of the STUN servers.
+ *
+ * Default: PJ_TRUE
+ */
+ pj_bool_t stun_ignore_failure;
+
+ /**
* Support for adding and parsing NAT type in the SDP to assist
* troubleshooting. The valid values are:
* - 0: no information will be added in SDP, and parsing is disabled.
@@ -1215,6 +1259,46 @@ PJ_DECL(pj_pool_factory*) pjsua_get_pool_factory(void);
*/
/**
+ * This structure is used to represent the result of the STUN server
+ * resolution and testing, the #pjsua_resolve_stun_servers() function.
+ * This structure will be passed in #pj_stun_resolve_cb callback.
+ */
+typedef struct pj_stun_resolve_result
+{
+ /**
+ * Arbitrary data that was passed to #pjsua_resolve_stun_servers()
+ * function.
+ */
+ void *token;
+
+ /**
+ * This will contain PJ_SUCCESS if at least one usable STUN server
+ * is found, otherwise it will contain the last error code during
+ * the operation.
+ */
+ pj_status_t status;
+
+ /**
+ * The server name that yields successful result. This will only
+ * contain value if status is successful.
+ */
+ pj_str_t name;
+
+ /**
+ * The server IP address. This will only contain value if status
+ * is successful.
+ */
+ pj_sockaddr addr;
+
+} pj_stun_resolve_result;
+
+
+/**
+ * Typedef of callback to be registered to #pjsua_resolve_stun_servers().
+ */
+typedef void (*pj_stun_resolve_cb)(const pj_stun_resolve_result *result);
+
+/**
* This is a utility function to detect NAT type in front of this
* endpoint. Once invoked successfully, this function will complete
* asynchronously and report the result in \a on_nat_detect() callback
@@ -1253,6 +1337,55 @@ PJ_DECL(pj_status_t) pjsua_get_nat_type(pj_stun_nat_type *type);
/**
+ * Auxiliary function to resolve and contact each of the STUN server
+ * entries (sequentially) to find which is usable. The #pjsua_init() must
+ * have been called before calling this function.
+ *
+ * @param count Number of STUN server entries to try.
+ * @param srv Array of STUN server entries to try. Please see
+ * the \a stun_srv field in the #pjsua_config
+ * documentation about the format of this entry.
+ * @param wait Specify non-zero to make the function block until
+ * it gets the result. In this case, the function
+ * will block while the resolution is being done,
+ * and the callback will be called before this function
+ * returns.
+ * @param token Arbitrary token to be passed back to application
+ * in the callback.
+ * @param cb Callback to be called to notify the result of
+ * the function.
+ *
+ * @return If \a wait parameter is non-zero, this will return
+ * PJ_SUCCESS if one usable STUN server is found.
+ * Otherwise it will always return PJ_SUCCESS, and
+ * application will be notified about the result in
+ * the callback.
+ */
+PJ_DECL(pj_status_t) pjsua_resolve_stun_servers(unsigned count,
+ pj_str_t srv[],
+ pj_bool_t wait,
+ void *token,
+ pj_stun_resolve_cb cb);
+
+/**
+ * Cancel pending STUN resolution which match the specified token.
+ *
+ * @param token The token to match. This token was given to
+ * #pjsua_resolve_stun_servers()
+ * @param notify_cb Boolean to control whether the callback should
+ * be called for cancelled resolutions. When the
+ * callback is called, the status in the result
+ * will be set as PJ_ECANCELLED.
+ *
+ * @return PJ_SUCCESS if there is at least one pending STUN
+ * resolution cancelled, or PJ_ENOTFOUND if there is
+ * no matching one, or other error.
+ */
+PJ_DECL(pj_status_t) pjsua_cancel_stun_resolution(void *token,
+ pj_bool_t notify_cb);
+
+
+/**
* This is a utility function to verify that valid SIP url is given. If the
* URL is valid, PJ_SUCCESS will be returned.
*
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index d80f5d1d..03c6627c 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -212,6 +212,22 @@ typedef struct pjsua_conf_setting
unsigned bits_per_sample;
} pjsua_conf_setting;
+typedef struct pjsua_stun_resolve
+{
+ PJ_DECL_LIST_MEMBER(struct pjsua_stun_resolve);
+
+ pj_pool_t *pool; /**< Pool */
+ unsigned count; /**< # of entries */
+ pj_str_t *srv; /**< Array of entries */
+ unsigned idx; /**< Current index */
+ void *token; /**< App token */
+ pj_stun_resolve_cb cb; /**< App callback */
+ pj_bool_t blocking; /**< Blocking? */
+ pj_status_t status; /**< Session status */
+ pj_sockaddr addr; /**< Result */
+ pj_stun_sock *stun_sock; /**< Testing STUN sock */
+} pjsua_stun_resolve;
+
/**
* Global pjsua application data.
@@ -241,6 +257,7 @@ struct pjsua_data
pj_stun_config stun_cfg; /**< Global STUN settings. */
pj_sockaddr stun_srv; /**< Resolved STUN server address */
pj_status_t stun_status; /**< STUN server status. */
+ pjsua_stun_resolve stun_res; /**< List of pending STUN resolution*/
pj_dns_resolver *resolver; /**< DNS resolver. */
/* Detected NAT type */
@@ -350,6 +367,12 @@ PJ_INLINE(pjsua_im_data*) pjsua_im_data_dup(pj_pool_t *pool,
#define PJSUA_UNLOCK()
#endif
+/******
+ * STUN resolution
+ */
+/* Resolve the STUN server */
+pj_status_t resolve_stun_server(pj_bool_t wait);
+
/**
* Normalize route URI (check for ";lr" and append one if it doesn't
* exist and pjsua_config.force_lr is set.
@@ -357,11 +380,6 @@ PJ_INLINE(pjsua_im_data*) pjsua_im_data_dup(pj_pool_t *pool,
pj_status_t normalize_route_uri(pj_pool_t *pool, pj_str_t *uri);
/**
- * Resolve STUN server.
- */
-pj_status_t pjsua_resolve_stun_server(pj_bool_t wait);
-
-/**
* Handle incoming invite request.
*/
pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata);
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index c714be77..c4f38d9b 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -24,6 +24,10 @@
#define THIS_FILE "pjsua_core.c"
+/* Internal prototypes */
+static void resolve_stun_entry(pjsua_stun_resolve *sess);
+
+
/* PJSUA application instance. */
struct pjsua_data pjsua_var;
@@ -59,6 +63,7 @@ static void init_data()
pjsua_var.stun_status = PJ_EUNKNOWN;
pjsua_var.nat_status = PJ_EPENDING;
+ pj_list_init(&pjsua_var.stun_res);
}
@@ -92,6 +97,7 @@ PJ_DEF(void) pjsua_config_default(pjsua_config *cfg)
cfg->max_calls = ((PJSUA_MAX_CALLS) < 4) ? (PJSUA_MAX_CALLS) : 4;
cfg->thread_cnt = 1;
cfg->nat_type_in_sdp = 1;
+ cfg->stun_ignore_failure = PJ_TRUE;
cfg->force_lr = PJ_TRUE;
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
cfg->use_srtp = PJSUA_DEFAULT_USE_SRTP;
@@ -122,6 +128,10 @@ PJ_DEF(void) pjsua_config_dup(pj_pool_t *pool,
pj_strdup_with_null(pool, &dst->user_agent, &src->user_agent);
pj_strdup_with_null(pool, &dst->stun_domain, &src->stun_domain);
pj_strdup_with_null(pool, &dst->stun_host, &src->stun_host);
+
+ for (i=0; i<src->stun_srv_cnt; ++i) {
+ pj_strdup_with_null(pool, &dst->stun_srv[i], &src->stun_srv[i]);
+ }
}
PJ_DEF(void) pjsua_msg_data_init(pjsua_msg_data *msg_data)
@@ -761,10 +771,20 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
if (status != PJ_SUCCESS)
goto on_error;
+ /* Convert deprecated STUN settings */
+ if (pjsua_var.ua_cfg.stun_srv_cnt==0) {
+ if (pjsua_var.ua_cfg.stun_domain.slen) {
+ pjsua_var.ua_cfg.stun_srv[pjsua_var.ua_cfg.stun_srv_cnt++] =
+ pjsua_var.ua_cfg.stun_domain;
+ }
+ if (pjsua_var.ua_cfg.stun_host.slen) {
+ pjsua_var.ua_cfg.stun_srv[pjsua_var.ua_cfg.stun_srv_cnt++] =
+ pjsua_var.ua_cfg.stun_host;
+ }
+ }
/* Start resolving STUN server */
-
- status = pjsua_resolve_stun_server(PJ_FALSE);
+ status = resolve_stun_server(PJ_FALSE);
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
return status;
@@ -859,154 +879,308 @@ static void busy_sleep(unsigned msec)
} while (PJ_TIME_VAL_LT(now, timeout));
}
+/* Internal function to destroy STUN resolution session
+ * (pj_stun_resolve).
+ */
+static void destroy_stun_resolve(pjsua_stun_resolve *sess)
+{
+ PJSUA_LOCK();
+ pj_list_erase(sess);
+ PJSUA_UNLOCK();
+
+ pj_assert(sess->stun_sock==NULL);
+ pj_pool_release(sess->pool);
+}
-/*
- * Callback function to receive notification from the resolver
- * when the resolution process completes.
+/* This is the internal function to be called when STUN resolution
+ * session (pj_stun_resolve) has completed.
*/
-static void stun_dns_srv_resolver_cb(void *user_data,
- pj_status_t status,
- const pj_dns_srv_record *rec)
+static void stun_resolve_complete(pjsua_stun_resolve *sess)
{
- unsigned i;
+ pj_stun_resolve_result result;
+
+ pj_bzero(&result, sizeof(result));
+ result.token = sess->token;
+ result.status = sess->status;
+ result.name = sess->srv[sess->idx];
+ pj_memcpy(&result.addr, &sess->addr, sizeof(result.addr));
+
+ if (result.status == PJ_SUCCESS) {
+ char addr[PJ_INET6_ADDRSTRLEN+10];
+ pj_sockaddr_print(&result.addr, addr, sizeof(addr), 3);
+ PJ_LOG(4,(THIS_FILE,
+ "STUN resolution success, using %.*s, address is %s",
+ (int)sess->srv[sess->idx].slen,
+ sess->srv[sess->idx].ptr,
+ addr));
+ } else {
+ char errmsg[PJ_ERR_MSG_SIZE];
+ pj_strerror(result.status, errmsg, sizeof(errmsg));
+ PJ_LOG(1,(THIS_FILE, "STUN resolution failed: %s", errmsg));
+ }
- PJ_UNUSED_ARG(user_data);
+ sess->cb(&result);
+
+ if (!sess->blocking) {
+ destroy_stun_resolve(sess);
+ }
+}
+
+/* This is the callback called by the STUN socket (pj_stun_sock)
+ * to report it's state. We use this as part of testing the
+ * STUN server.
+ */
+static pj_bool_t test_stun_on_status(pj_stun_sock *stun_sock,
+ pj_stun_sock_op op,
+ pj_status_t status)
+{
+ pjsua_stun_resolve *sess;
+
+ sess = pj_stun_sock_get_user_data(stun_sock);
+ pj_assert(stun_sock == sess->stun_sock);
- pjsua_var.stun_status = status;
-
if (status != PJ_SUCCESS) {
- /* DNS SRV resolution failed. If stun_host is specified, resolve
- * it with gethostbyname()
- */
- if (pjsua_var.ua_cfg.stun_host.slen) {
- pjsua_var.stun_status =
- pj_sockaddr_parse(pj_AF_INET(), 0,
- &pjsua_var.ua_cfg.stun_host,
- &pjsua_var.stun_srv);
- if (pjsua_var.stun_status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Invalid STUN server",
- pjsua_var.stun_status);
- } else {
- if (pj_sockaddr_get_port(&pjsua_var.stun_srv)==0)
- pj_sockaddr_set_port(&pjsua_var.stun_srv, 3478);
-
- PJ_LOG(3,(THIS_FILE,
- "STUN server %.*s resolved, address is %s:%d",
- (int)pjsua_var.ua_cfg.stun_host.slen,
- pjsua_var.ua_cfg.stun_host.ptr,
- pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
- (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
- }
- } else {
+ char errmsg[PJ_ERR_MSG_SIZE];
+ pj_strerror(status, errmsg, sizeof(errmsg));
+
+ PJ_LOG(4,(THIS_FILE, "STUN resolution for %.*s failed: %s",
+ (int)sess->srv[sess->idx].slen,
+ sess->srv[sess->idx].ptr, errmsg));
+
+ sess->status = status;
+
+ pj_stun_sock_destroy(stun_sock);
+ sess->stun_sock = NULL;
+
+ ++sess->idx;
+ resolve_stun_entry(sess);
+
+ return PJ_FALSE;
+
+ } else if (op == PJ_STUN_SOCK_BINDING_OP) {
+ pj_stun_sock_info ssi;
+
+ pj_stun_sock_get_info(stun_sock, &ssi);
+ pj_memcpy(&sess->addr, &ssi.srv_addr, sizeof(sess->addr));
+
+ sess->status = PJ_SUCCESS;
+ pj_stun_sock_destroy(stun_sock);
+ sess->stun_sock = NULL;
+
+ stun_resolve_complete(sess);
+
+ return PJ_FALSE;
+
+ } else
+ return PJ_TRUE;
+
+}
+
+/* This is an internal function to resolve and test current
+ * server entry in pj_stun_resolve session. It is called by
+ * pjsua_resolve_stun_servers() and test_stun_on_status() above
+ */
+static void resolve_stun_entry(pjsua_stun_resolve *sess)
+{
+ /* Loop while we have entry to try */
+ for (; sess->idx < sess->count; ++sess->idx) {
+ const int af = pj_AF_INET();
+ pj_str_t hostpart;
+ pj_uint16_t port;
+ pj_stun_sock_cb stun_sock_cb;
+
+ pj_assert(sess->idx < sess->count);
+
+ /* Parse the server entry into host:port */
+ sess->status = pj_sockaddr_parse2(af, 0, &sess->srv[sess->idx],
+ &hostpart, &port, NULL);
+ if (sess->status != PJ_SUCCESS) {
+ PJ_LOG(2,(THIS_FILE, "Invalid STUN server entry %.*s",
+ (int)sess->srv[sess->idx].slen,
+ sess->srv[sess->idx].ptr));
+ continue;
+ }
+
+ /* Use default port if not specified */
+ if (port == 0)
+ port = PJ_STUN_PORT;
+
+ pj_assert(sess->stun_sock == NULL);
+
+ PJ_LOG(4,(THIS_FILE, "Trying STUN server %.*s (%d of %d)..",
+ (int)sess->srv[sess->idx].slen,
+ sess->srv[sess->idx].ptr,
+ 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->status = pj_stun_sock_create(&pjsua_var.stun_cfg, "stunresolve",
+ pj_AF_INET(), &stun_sock_cb,
+ NULL, sess, &sess->stun_sock);
+ if (sess->status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
+ pj_strerror(sess->status, errmsg, sizeof(errmsg));
+ PJ_LOG(4,(THIS_FILE,
+ "Error creating STUN socket for %.*s: %s",
+ (int)sess->srv[sess->idx].slen,
+ sess->srv[sess->idx].ptr, errmsg));
+
+ continue;
+ }
- pj_strerror(status, errmsg, sizeof(errmsg));
- PJ_LOG(1,(THIS_FILE,
- "DNS SRV resolution failed for STUN server %.*s: %s",
- (int)pjsua_var.ua_cfg.stun_domain.slen,
- pjsua_var.ua_cfg.stun_domain.ptr,
- errmsg));
+ sess->status = pj_stun_sock_start(sess->stun_sock, &hostpart,
+ port, pjsua_var.resolver);
+ if (sess->status != PJ_SUCCESS) {
+ char errmsg[PJ_ERR_MSG_SIZE];
+ pj_strerror(sess->status, errmsg, sizeof(errmsg));
+ PJ_LOG(4,(THIS_FILE,
+ "Error starting STUN socket for %.*s: %s",
+ (int)sess->srv[sess->idx].slen,
+ sess->srv[sess->idx].ptr, errmsg));
+
+ pj_stun_sock_destroy(sess->stun_sock);
+ sess->stun_sock = NULL;
+ continue;
}
+
+ /* Done for now, testing will resume/complete asynchronously in
+ * stun_sock_cb()
+ */
return;
}
- pj_assert(rec->count != 0 && rec->entry[0].server.addr_count != 0);
- pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL,
- rec->entry[0].port);
- pjsua_var.stun_srv.ipv4.sin_addr.s_addr =
- rec->entry[0].server.addr[0].s_addr;
+ if (sess->idx >= sess->count) {
+ /* No more entries to try */
+ PJ_ASSERT_ON_FAIL(sess->status != PJ_SUCCESS,
+ sess->status = PJ_EUNKNOWN);
+ stun_resolve_complete(sess);
+ }
+}
+
+
+/*
+ * Resolve STUN server.
+ */
+PJ_DEF(pj_status_t) pjsua_resolve_stun_servers( unsigned count,
+ pj_str_t srv[],
+ pj_bool_t wait,
+ void *token,
+ pj_stun_resolve_cb cb)
+{
+ pj_pool_t *pool;
+ pjsua_stun_resolve *sess;
+ pj_status_t status;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(count && srv && cb, PJ_EINVAL);
+
+ pool = pjsua_pool_create("stunres", 256, 256);
+ if (!pool)
+ return PJ_ENOMEM;
+
+ sess = PJ_POOL_ZALLOC_T(pool, pjsua_stun_resolve);
+ sess->pool = pool;
+ sess->token = token;
+ sess->cb = cb;
+ sess->count = count;
+ sess->blocking = wait;
+ sess->status = PJ_EPENDING;
+ 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]);
+ }
+
+ PJSUA_LOCK();
+ pj_list_push_back(&pjsua_var.stun_res, sess);
+ PJSUA_UNLOCK();
- PJ_LOG(3,(THIS_FILE, "_stun._udp.%.*s resolved, found %d entry(s):",
- (int)pjsua_var.ua_cfg.stun_domain.slen,
- pjsua_var.ua_cfg.stun_domain.ptr,
- rec->count));
+ resolve_stun_entry(sess);
+
+ if (!wait)
+ return PJ_SUCCESS;
- for (i=0; i<rec->count; ++i) {
- PJ_LOG(3,(THIS_FILE,
- " %d: prio=%d, weight=%d %s:%d",
- i, rec->entry[i].priority, rec->entry[i].weight,
- pj_inet_ntoa(rec->entry[i].server.addr[0]),
- (int)rec->entry[i].port));
+ while (sess->status == PJ_EPENDING) {
+ pjsua_handle_events(50);
}
+ status = sess->status;
+ destroy_stun_resolve(sess);
+
+ return status;
+}
+
+/*
+ * Cancel pending STUN resolution.
+ */
+PJ_DEF(pj_status_t) pjsua_cancel_stun_resolution( void *token,
+ pj_bool_t notify_cb)
+{
+ pjsua_stun_resolve *sess;
+ unsigned cancelled_count = 0;
+
+ PJSUA_LOCK();
+ sess = pjsua_var.stun_res.next;
+ while (sess != &pjsua_var.stun_res) {
+ pjsua_stun_resolve *next = sess->next;
+
+ if (sess->token == token) {
+ if (notify_cb) {
+ pj_stun_resolve_result result;
+
+ pj_bzero(&result, sizeof(result));
+ result.token = token;
+ result.status = PJ_ECANCELLED;
+
+ sess->cb(&result);
+ }
+
+ destroy_stun_resolve(sess);
+ ++cancelled_count;
+ }
+
+ sess = next;
+ }
+ PJSUA_UNLOCK();
+
+ return cancelled_count ? PJ_SUCCESS : PJ_ENOTFOUND;
+}
+
+static void internal_stun_resolve_cb(const pj_stun_resolve_result *result)
+{
+ pjsua_var.stun_status = result->status;
+ if (result->status == PJ_SUCCESS) {
+ pj_memcpy(&pjsua_var.stun_srv, &result->addr, sizeof(result->addr));
+ }
}
/*
* Resolve STUN server.
*/
-pj_status_t pjsua_resolve_stun_server(pj_bool_t wait)
+pj_status_t resolve_stun_server(pj_bool_t wait)
{
if (pjsua_var.stun_status == PJ_EUNKNOWN) {
+ pj_status_t status;
+
/* Initialize STUN configuration */
pj_stun_config_init(&pjsua_var.stun_cfg, &pjsua_var.cp.factory, 0,
pjsip_endpt_get_ioqueue(pjsua_var.endpt),
pjsip_endpt_get_timer_heap(pjsua_var.endpt));
/* Start STUN server resolution */
-
- pjsua_var.stun_status = PJ_EPENDING;
-
- /* If stun_domain is specified, resolve STUN servers with DNS
- * SRV resolution.
- */
- if (pjsua_var.ua_cfg.stun_domain.slen) {
- pj_str_t res_type;
- pj_status_t status;
-
- /* Fail if resolver is not configured */
- if (pjsua_var.resolver == NULL) {
- PJ_LOG(1,(THIS_FILE, "Nameserver must be configured when "
- "stun_domain is specified"));
- pjsua_var.stun_status = PJLIB_UTIL_EDNSNONS;
- return PJLIB_UTIL_EDNSNONS;
- }
- res_type = pj_str("_stun._udp");
- status =
- pj_dns_srv_resolve(&pjsua_var.ua_cfg.stun_domain, &res_type,
- 3478, pjsua_var.pool, pjsua_var.resolver,
- 0, NULL, &stun_dns_srv_resolver_cb, NULL);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Error starting DNS SRV resolution",
- pjsua_var.stun_status);
+ if (pjsua_var.ua_cfg.stun_srv_cnt) {
+ pjsua_var.stun_status = PJ_EPENDING;
+ status = pjsua_resolve_stun_servers(pjsua_var.ua_cfg.stun_srv_cnt,
+ pjsua_var.ua_cfg.stun_srv,
+ wait, NULL,
+ &internal_stun_resolve_cb);
+ if (wait || status != PJ_SUCCESS) {
pjsua_var.stun_status = status;
- return pjsua_var.stun_status;
- } else {
- pjsua_var.stun_status = PJ_EPENDING;
}
- }
- /* Otherwise if stun_host is specified, resolve STUN server with
- * gethostbyname().
- */
- else if (pjsua_var.ua_cfg.stun_host.slen) {
- pjsua_var.stun_status =
- pj_sockaddr_parse(pj_AF_INET(), 0,
- &pjsua_var.ua_cfg.stun_host,
- &pjsua_var.stun_srv);
- if (pjsua_var.stun_status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Invalid STUN server",
- pjsua_var.stun_status);
- return pjsua_var.stun_status;
- }
-
- if (pj_sockaddr_get_port(&pjsua_var.stun_srv)==0)
- pj_sockaddr_set_port(&pjsua_var.stun_srv, 3478);
-
- PJ_LOG(3,(THIS_FILE,
- "STUN server %.*s resolved, address is %s:%d",
- (int)pjsua_var.ua_cfg.stun_host.slen,
- pjsua_var.ua_cfg.stun_host.ptr,
- pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
- (int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
-
- }
- /* Otherwise disable STUN. */
- else {
+ } else {
pjsua_var.stun_status = PJ_SUCCESS;
}
-
- return pjsua_var.stun_status;
-
} else if (pjsua_var.stun_status == PJ_EPENDING) {
/* STUN server resolution has been started, wait for the
* result.
@@ -1015,13 +1189,18 @@ pj_status_t pjsua_resolve_stun_server(pj_bool_t wait)
while (pjsua_var.stun_status == PJ_EPENDING)
pjsua_handle_events(10);
}
+ }
- return pjsua_var.stun_status;
-
- } else {
- /* STUN server has been resolved, return the status */
- return pjsua_var.stun_status;
+ if (pjsua_var.stun_status != PJ_EPENDING &&
+ pjsua_var.stun_status != PJ_SUCCESS &&
+ pjsua_var.ua_cfg.stun_ignore_failure)
+ {
+ PJ_LOG(2,(THIS_FILE,
+ "Ignoring STUN resolution failure (by setting)"));
+ pjsua_var.stun_status = PJ_SUCCESS;
}
+
+ return pjsua_var.stun_status;
}
/*
@@ -1074,6 +1253,17 @@ PJ_DEF(pj_status_t) pjsua_destroy(void)
/* Destroy endpoint. */
if (pjsua_var.endpt) {
+
+ /* Terminate any pending STUN resolution */
+ if (!pj_list_empty(&pjsua_var.stun_res)) {
+ pjsua_stun_resolve *sess = pjsua_var.stun_res.next;
+ while (sess != &pjsua_var.stun_res) {
+ pjsua_stun_resolve *next = sess->next;
+ destroy_stun_resolve(sess);
+ sess = next;
+ }
+ }
+
/* Wait for some time to allow unregistration and ICE/TURN
* transports shutdown to complete:
*/
@@ -1270,7 +1460,7 @@ static pj_status_t create_sip_udp_sock(int af,
pj_status_t status;
/* Make sure STUN server resolution has completed */
- status = pjsua_resolve_stun_server(PJ_TRUE);
+ status = resolve_stun_server(PJ_TRUE);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
return status;
@@ -1982,7 +2172,7 @@ PJ_DEF(pj_status_t) pjsua_detect_nat_type()
return PJ_SUCCESS;
/* Make sure STUN server resolution has completed */
- status = pjsua_resolve_stun_server(PJ_TRUE);
+ status = resolve_stun_server(PJ_TRUE);
if (status != PJ_SUCCESS) {
pjsua_var.nat_status = status;
pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index b4d39711..7b95a137 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -359,7 +359,7 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
pj_sock_t sock[2];
/* Make sure STUN server resolution has completed */
- status = pjsua_resolve_stun_server(PJ_TRUE);
+ status = resolve_stun_server(PJ_TRUE);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
return status;
@@ -860,7 +860,7 @@ static pj_status_t create_ice_media_transports(void)
pj_status_t status;
/* Make sure STUN server resolution has completed */
- status = pjsua_resolve_stun_server(PJ_TRUE);
+ status = resolve_stun_server(PJ_TRUE);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
return status;