summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2007-10-16 01:34:14 +0000
committerBenny Prijono <bennylp@teluu.com>2007-10-16 01:34:14 +0000
commit5aea96f9de3908f2fc85c16001adc6c3f82d4705 (patch)
treee838b8a6077af937b2e834822540839c4e2515b2
parent4b289329774686a3e261fac70fbd902942cb9b1f (diff)
More ticket #399: added callback to report NAT detection result, and sends NAT type in SDP
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1501 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjnath/include/pjnath/errno.h5
-rw-r--r--pjnath/include/pjnath/nat_detect.h18
-rw-r--r--pjnath/src/pjnath/errno.c1
-rw-r--r--pjnath/src/pjnath/nat_detect.c28
-rw-r--r--pjsip-apps/src/pjsua/pjsua_app.c46
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h61
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h5
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c55
-rw-r--r--pjsip/src/pjsua-lib/pjsua_media.c27
9 files changed, 189 insertions, 57 deletions
diff --git a/pjnath/include/pjnath/errno.h b/pjnath/include/pjnath/errno.h
index 403f191d..1f763fc1 100644
--- a/pjnath/include/pjnath/errno.h
+++ b/pjnath/include/pjnath/errno.h
@@ -110,6 +110,11 @@
*/
#define PJNATH_ESTUNIPV6NOTSUPP (PJNATH_ERRNO_START+41) /* 370041 */
+/**
+ * @hideinitializer
+ * Invalid STUN server or server not configured.
+ */
+#define PJNATH_ESTUNINSERVER (PJNATH_ERRNO_START+42) /* 370042 */
diff --git a/pjnath/include/pjnath/nat_detect.h b/pjnath/include/pjnath/nat_detect.h
index e79ffb14..a4bc5fec 100644
--- a/pjnath/include/pjnath/nat_detect.h
+++ b/pjnath/include/pjnath/nat_detect.h
@@ -48,11 +48,17 @@ PJ_BEGIN_DECL
typedef enum pj_stun_nat_type
{
/**
- * NAT type is unknown, because the detection has failed.
+ * NAT type is unknown because the detection has not been performed.
*/
PJ_STUN_NAT_TYPE_UNKNOWN,
/**
+ * NAT type is unknown because there is failure in the detection
+ * process, possibly because server does not support RFC 3489.
+ */
+ PJ_STUN_NAT_TYPE_ERR_UNKNOWN,
+
+ /**
* This specifies that the client has open access to Internet (or
* at least, its behind a firewall that behaves like a full-cone NAT,
* but without the translation)
@@ -152,6 +158,16 @@ typedef void pj_stun_nat_detect_cb(void *user_data,
/**
+ * Get the NAT name from the specified NAT type.
+ *
+ * @param type NAT type.
+ *
+ * @return NAT name.
+ */
+PJ_DECL(const char*) pj_stun_get_nat_name(pj_stun_nat_type type);
+
+
+/**
* Perform NAT classification function according to the procedures
* specified in RFC 3489. Once this function returns successfully,
* the procedure will run in the "background" and will complete
diff --git a/pjnath/src/pjnath/errno.c b/pjnath/src/pjnath/errno.c
index 21a42d4e..af636fcd 100644
--- a/pjnath/src/pjnath/errno.c
+++ b/pjnath/src/pjnath/errno.c
@@ -49,6 +49,7 @@ static const struct
PJ_BUILD_ERR( PJNATH_ESTUNNOMAPPEDADDR, "STUN (XOR-)MAPPED-ADDRESS attribute not found"),
PJ_BUILD_ERR( PJNATH_ESTUNIPV6NOTSUPP, "STUN IPv6 attribute not supported"),
+ PJ_BUILD_ERR( PJNATH_ESTUNINSERVER, "Invalid STUN server or server not configured"),
/* ICE related errors */
PJ_BUILD_ERR( PJNATH_ENOICE, "ICE session not available"),
diff --git a/pjnath/src/pjnath/nat_detect.c b/pjnath/src/pjnath/nat_detect.c
index cf3ceed4..368c86fb 100644
--- a/pjnath/src/pjnath/nat_detect.c
+++ b/pjnath/src/pjnath/nat_detect.c
@@ -32,7 +32,8 @@
static const char *nat_type_names[] =
{
"Unknown",
- "Open Internet",
+ "ErrUnknown",
+ "Open",
"Blocked",
"Symmetric UDP",
"Full Cone",
@@ -129,6 +130,17 @@ static void on_sess_timer(pj_timer_heap_t *th,
static void sess_destroy(nat_detect_session *sess);
+/*
+ * Get the NAT name from the specified NAT type.
+ */
+PJ_DEF(const char*) pj_stun_get_nat_name(pj_stun_nat_type type)
+{
+ PJ_ASSERT_RETURN(type >= 0 && type <= PJ_STUN_NAT_TYPE_PORT_RESTRICTED,
+ "*Invalid*");
+
+ return nat_type_names[type];
+}
+
static int test_executed(nat_detect_session *sess)
{
unsigned i, count;
@@ -387,7 +399,7 @@ static void on_read_complete(pj_ioqueue_key_t *key,
-bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET))
{
/* Permanent error */
- end_session(sess, -bytes_read, PJ_STUN_NAT_TYPE_UNKNOWN);
+ end_session(sess, -bytes_read, PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
goto on_return;
}
@@ -406,7 +418,7 @@ static void on_read_complete(pj_ioqueue_key_t *key,
if (status != PJ_EPENDING) {
pj_assert(status != PJ_SUCCESS);
- end_session(sess, status, PJ_STUN_NAT_TYPE_UNKNOWN);
+ end_session(sess, status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
}
on_return:
@@ -627,7 +639,7 @@ static void on_request_complete(pj_stun_session *stun_sess,
* We've got other error with Test 2.
*/
end_session(sess, sess->result[ST_TEST_2].status,
- PJ_STUN_NAT_TYPE_UNKNOWN);
+ PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
break;
}
} else {
@@ -690,7 +702,7 @@ static void on_request_complete(pj_stun_session *stun_sess,
* Got other error with test 3.
*/
end_session(sess, sess->result[ST_TEST_3].status,
- PJ_STUN_NAT_TYPE_UNKNOWN);
+ PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
break;
}
}
@@ -707,7 +719,7 @@ static void on_request_complete(pj_stun_session *stun_sess,
* Got other error with test 1B.
*/
end_session(sess, sess->result[ST_TEST_1B].status,
- PJ_STUN_NAT_TYPE_UNKNOWN);
+ PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
break;
}
break;
@@ -716,7 +728,7 @@ static void on_request_complete(pj_stun_session *stun_sess,
* We've got other error with Test 2.
*/
end_session(sess, sess->result[ST_TEST_2].status,
- PJ_STUN_NAT_TYPE_UNKNOWN);
+ PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
break;
}
}
@@ -726,7 +738,7 @@ static void on_request_complete(pj_stun_session *stun_sess,
* We've got other error with Test 1.
*/
end_session(sess, sess->result[ST_TEST_1].status,
- PJ_STUN_NAT_TYPE_UNKNOWN);
+ PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
break;
}
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c
index 80dac5da..31281afc 100644
--- a/pjsip-apps/src/pjsua/pjsua_app.c
+++ b/pjsip-apps/src/pjsua/pjsua_app.c
@@ -1885,6 +1885,19 @@ static void on_call_replaced(pjsua_call_id old_call_id,
/*
+ * NAT type detection callback.
+ */
+static void on_nat_detect(const pj_stun_nat_detect_result *res)
+{
+ if (res->status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "NAT detection failed", res->status);
+ } else {
+ PJ_LOG(3, (THIS_FILE, "NAT detected as %s", res->nat_type_name));
+ }
+}
+
+
+/*
* Print buddy list.
*/
static void print_buddy_list(void)
@@ -2312,33 +2325,6 @@ static void manage_codec_prio(void)
}
-/* Callback upon NAT detection completion */
-static void nat_detect_cb(void *user_data,
- const pj_stun_nat_detect_result *res)
-{
- PJ_UNUSED_ARG(user_data);
-
- if (res->status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "NAT detection failed", res->status);
- } else {
- PJ_LOG(3, (THIS_FILE, "NAT detected as %s", res->nat_type_name));
- }
-}
-
-
-/*
- * Detect NAT type.
- */
-static void detect_nat_type(void)
-{
- pj_status_t status;
-
- status = pjsua_detect_nat_type(NULL, &nat_detect_cb);
- if (status != PJ_SUCCESS)
- pjsua_perror(THIS_FILE, "Error", status);
-}
-
-
/*
* Main "user interface" loop.
*/
@@ -2453,7 +2439,7 @@ void console_app_main(const pj_str_t *uri_to_call)
break;
case 'n':
- detect_nat_type();
+ pjsua_detect_nat_type();
break;
case 'i':
@@ -3307,6 +3293,7 @@ pj_status_t app_init(int argc, char *argv[])
app_config.cfg.cb.on_typing = &on_typing;
app_config.cfg.cb.on_call_transfer_status = &on_call_transfer_status;
app_config.cfg.cb.on_call_replaced = &on_call_replaced;
+ app_config.cfg.cb.on_nat_detect = &on_nat_detect;
/* Initialize pjsua */
status = pjsua_init(&app_config.cfg, &app_config.log_cfg,
@@ -3509,9 +3496,6 @@ pj_status_t app_main(void)
return status;
}
- if (app_config.cfg.stun_domain.slen || app_config.cfg.stun_host.slen)
- detect_nat_type();
-
console_app_main(&uri_arg);
return PJ_SUCCESS;
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index ea16d6b9..93aaefda 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -565,7 +565,9 @@ typedef struct pjsua_callback
/**
* Notify application when media state in the call has changed.
* Normal application would need to implement this callback, e.g.
- * to connect the call's media to sound device.
+ * to connect the call's media to sound device. When ICE is used,
+ * this callback will also be called to report ICE negotiation
+ * failure.
*
* @param call_id The call index.
*
@@ -891,6 +893,14 @@ typedef struct pjsua_callback
const pj_str_t *to, const pj_str_t *contact,
pj_bool_t is_typing);
+ /**
+ * Callback when the library has finished performing NAT type
+ * detection.
+ *
+ * @param res NAT detection result.
+ */
+ void (*on_nat_detect)(const pj_stun_nat_detect_result *res);
+
} pjsua_callback;
@@ -979,6 +989,17 @@ typedef struct pjsua_config
pj_str_t stun_relay_host;
/**
+ * Include local endpoint's NAT type in the SDP to assist troubleshooting.
+ * The valid values are:
+ * - 0: no information will be added in SDP.
+ * - 1: only the NAT type number is added.
+ * - 2: add both NAT type number and name.
+ *
+ * Default: 2
+ */
+ int nat_type_in_sdp;
+
+ /**
* Specify whether support for reliable provisional response (100rel and
* PRACK) should be required by default. Note that this setting can be
* further customized in account configuration (#pjsua_acc_config).
@@ -1315,19 +1336,37 @@ PJ_DECL(pj_pool_factory*) pjsua_get_pool_factory(void);
/**
* 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 the callback.
+ * asynchronously and report the result in \a on_nat_detect() callback
+ * of pjsua_callback.
+ *
+ * After NAT has been detected and the callback is called, application can
+ * get the detected NAT type by calling #pjsua_get_nat_type(). Application
+ * can also perform NAT detection by calling #pjsua_detect_nat_type()
+ * again at later time.
+ *
+ * Note that STUN must be enabled to run this function successfully.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsua_detect_nat_type(void);
+
+
+/**
+ * Get the NAT type as detected by #pjsua_detect_nat_type() function.
+ * This function will only return useful NAT type after #pjsua_detect_nat_type()
+ * has completed successfully and \a on_nat_detect() callback has been called.
*
- * @param srv_port Optional STUN server and port, in "SERVER[:PORT]"
- * format. If this option is NULL, the function will use
- * the STUN server that has been set in the pjsua
- * configuration.
- * @param user_data User data to be returned back in the callback.
- * @param cb Optional callback to report the detection result.
+ * @param type NAT type.
*
- * @return PJ_SUCCESS if detection is started successfully.
+ * @return When detection is in progress, this function will
+ * return PJ_EPENDING and \a type will be set to
+ * PJ_STUN_NAT_TYPE_UNKNOWN. After NAT type has been
+ * detected successfully, this function will return
+ * PJ_SUCCESS and \a type will be set to the correct
+ * value. Other return values indicate error and
+ * \a type will be set to PJ_STUN_NAT_TYPE_ERR_UNKNOWN.
*/
-PJ_DECL(pj_status_t) pjsua_detect_nat_type(void *user_data,
- pj_stun_nat_detect_cb *cb);
+PJ_DECL(pj_status_t) pjsua_get_nat_type(pj_stun_nat_type *type);
/**
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index a83e506a..53a978f4 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -194,6 +194,11 @@ struct pjsua_data
pj_status_t stun_status; /**< STUN server status. */
pj_dns_resolver *resolver; /**< DNS resolver. */
+ /* Detected NAT type */
+ pj_stun_nat_type nat_type; /**< NAT type. */
+ pj_status_t nat_status; /**< Detection status. */
+ pj_bool_t nat_in_progress; /**< Detection in progress */
+
/* Account: */
unsigned acc_cnt; /**< Number of accounts. */
pjsua_acc_id default_acc; /**< Default account ID */
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index 321d3405..95ad7aa8 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -51,6 +51,7 @@ static void init_data()
pjsua_var.tpdata[i].index = i;
pjsua_var.stun_status = PJ_EUNKNOWN;
+ pjsua_var.nat_status = PJ_EPENDING;
}
@@ -79,6 +80,7 @@ PJ_DEF(void) pjsua_config_default(pjsua_config *cfg)
cfg->max_calls = 4;
cfg->thread_cnt = 1;
+ cfg->nat_type_in_sdp = 2;
}
PJ_DEF(void) pjsua_config_dup(pj_pool_t *pool,
@@ -1858,28 +1860,69 @@ void pjsua_init_tpselector(pjsua_transport_id tp_id,
}
+/* Callback upon NAT detection completion */
+static void nat_detect_cb(void *user_data,
+ const pj_stun_nat_detect_result *res)
+{
+ PJ_UNUSED_ARG(user_data);
+
+ pjsua_var.nat_in_progress = PJ_FALSE;
+ pjsua_var.nat_status = res->status;
+ pjsua_var.nat_type = res->nat_type;
+
+ if (pjsua_var.ua_cfg.cb.on_nat_detect) {
+ (*pjsua_var.ua_cfg.cb.on_nat_detect)(res);
+ }
+}
+
+
/*
* Detect NAT type.
*/
-PJ_DEF(pj_status_t) pjsua_detect_nat_type( void *user_data,
- pj_stun_nat_detect_cb *cb)
+PJ_DEF(pj_status_t) pjsua_detect_nat_type()
{
pj_status_t status;
+ if (pjsua_var.nat_in_progress)
+ return PJ_SUCCESS;
+
/* Make sure STUN server resolution has completed */
status = pjsua_resolve_stun_server(PJ_TRUE);
if (status != PJ_SUCCESS) {
+ pjsua_var.nat_status = status;
+ pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
return status;
}
/* Make sure we have STUN */
if (pjsua_var.stun_srv.ipv4.sin_family == 0) {
- return PJ_EINVALIDOP;
+ 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);
+
+ if (status != PJ_SUCCESS) {
+ pjsua_var.nat_status = status;
+ pjsua_var.nat_type = PJ_STUN_NAT_TYPE_ERR_UNKNOWN;
+ return status;
}
- return pj_stun_detect_nat_type(&pjsua_var.stun_srv.ipv4,
- &pjsua_var.stun_cfg,
- user_data, cb);
+ pjsua_var.nat_in_progress = PJ_TRUE;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Get NAT type.
+ */
+PJ_DEF(pj_status_t) pjsua_get_nat_type(pj_stun_nat_type *type)
+{
+ *type = pjsua_var.nat_type;
+ return pjsua_var.nat_status;
}
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index 0f4547b9..f6424353 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -194,6 +194,9 @@ pj_status_t pjsua_media_subsys_init(const pjsua_media_config *cfg)
&pjsua_var.null_port);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ /* Perform NAT detection */
+ pjsua_detect_nat_type();
+
return PJ_SUCCESS;
}
@@ -759,6 +762,30 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
if (status != PJ_SUCCESS)
goto on_error;
+ /* Add NAT info in the SDP */
+ if (pjsua_var.ua_cfg.nat_type_in_sdp) {
+ pjmedia_sdp_attr *a;
+ pj_str_t value;
+ char nat_info[80];
+
+ value.ptr = nat_info;
+ if (pjsua_var.ua_cfg.nat_type_in_sdp == 1) {
+ value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info),
+ "%d", pjsua_var.nat_type);
+ } else {
+ const char *type_name = pj_stun_get_nat_name(pjsua_var.nat_type);
+ value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info),
+ "%d %s",
+ pjsua_var.nat_type,
+ type_name);
+ }
+
+ a = pjmedia_sdp_attr_create(pool, "X-nat", &value);
+
+ pjmedia_sdp_attr_add(&sdp->attr_count, sdp->attr, a);
+
+ }
+
if (pjsua_var.media_cfg.enable_ice) {
status = pjmedia_ice_modify_sdp(call->med_tp, pool, sdp);
if (status != PJ_SUCCESS)