Index: /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 2863)
+++ /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 2864)
@@ -191,5 +191,5 @@
     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)");
@@ -217,5 +217,6 @@
     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:");
@@ -478,5 +479,5 @@
 	   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,
@@ -544,5 +545,4 @@
 	{ "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},
@@ -960,10 +960,11 @@
 	    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;
 
@@ -1616,14 +1617,8 @@
 
     /* 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);
     }
Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 2863)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 2864)
@@ -892,4 +892,8 @@
 
     /**
+     * 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
@@ -902,8 +906,48 @@
 
     /**
+     * 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;
 
     /**
@@ -1214,4 +1258,44 @@
  *
  */
+
+/**
+ * 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);
 
 /**
@@ -1251,4 +1335,53 @@
  */
 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);
 
 
Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 2863)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 2864)
@@ -213,4 +213,20 @@
 } 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;
+
 
 /**
@@ -242,4 +258,5 @@
     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.			*/
 
@@ -351,4 +368,10 @@
 #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
@@ -356,9 +379,4 @@
  */
 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);
 
 /**
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 2863)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 2864)
@@ -25,4 +25,8 @@
 
 
+/* Internal prototypes */
+static void resolve_stun_entry(pjsua_stun_resolve *sess);
+
+
 /* PJSUA application instance. */
 struct pjsua_data pjsua_var;
@@ -60,4 +64,5 @@
     pjsua_var.stun_status = PJ_EUNKNOWN;
     pjsua_var.nat_status = PJ_EPENDING;
+    pj_list_init(&pjsua_var.stun_res);
 }
 
@@ -93,4 +98,5 @@
     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)
@@ -123,4 +129,8 @@
     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]);
+    }
 }
 
@@ -762,8 +772,18 @@
 	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);
@@ -860,74 +880,277 @@
 }
 
-
-/*
- * Callback function to receive notification from the resolver
- * when the resolution process completes.
- */
-static void stun_dns_srv_resolver_cb(void *user_data,
-				     pj_status_t status,
-				     const pj_dns_srv_record *rec)
-{
+/* 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);
+}
+
+/* This is the internal function to be called when STUN resolution
+ * session (pj_stun_resolve) has completed.
+ */
+static void stun_resolve_complete(pjsua_stun_resolve *sess)
+{
+    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));
+    }
+
+    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);
+
+    if (status != PJ_SUCCESS) {
+	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;
+	}
+
+	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;
+    }
+
+    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_UNUSED_ARG(user_data);
-
-    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)));
+    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();
+
+    resolve_stun_entry(sess);
+
+    if (!wait)
+	return PJ_SUCCESS;
+
+    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);
 	    }
-	} else {
-	    char errmsg[PJ_ERR_MSG_SIZE];
-
-	    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));
-	}
-	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;
-
-    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));
-
-    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));
-    }
-
+
+	    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));
+    }
 }
 
@@ -935,7 +1158,9 @@
  * 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,
@@ -944,67 +1169,16 @@
 
 	/* 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;
+	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;
 	    }
-	    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);
-		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) {
@@ -1016,11 +1190,16 @@
 		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;
 }
 
@@ -1075,4 +1254,15 @@
     /* 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: 
@@ -1271,5 +1461,5 @@
 
     /* 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);
@@ -1983,5 +2173,5 @@
 
     /* 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;
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 2863)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 2864)
@@ -360,5 +360,5 @@
 
     /* 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);
@@ -861,5 +861,5 @@
 
     /* 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);
