summaryrefslogtreecommitdiff
path: root/pjsip/src
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip/src')
-rw-r--r--pjsip/src/pjsip-ua/sip_ua.c137
-rw-r--r--pjsip/src/pjsip/sip_auth_client.c (renamed from pjsip/src/pjsip/sip_auth.c)565
-rw-r--r--pjsip/src/pjsip/sip_auth_server.c234
-rw-r--r--pjsip/src/pjsip/sip_errno.c32
-rw-r--r--pjsip/src/pjsip/sip_parser.c2
-rw-r--r--pjsip/src/pjsip/sip_transaction.c20
-rw-r--r--pjsip/src/pjsip/sip_transport.c2
-rw-r--r--pjsip/src/pjsip/sip_transport_loop.c5
-rw-r--r--pjsip/src/pjsip/sip_util.c6
-rw-r--r--pjsip/src/test-pjsip/transport_test.c12
-rw-r--r--pjsip/src/test-pjsip/tsx_uac_test.c16
-rw-r--r--pjsip/src/test-pjsip/tsx_uas_test.c369
-rw-r--r--pjsip/src/test-pjsip/txdata_test.c2
13 files changed, 943 insertions, 459 deletions
diff --git a/pjsip/src/pjsip-ua/sip_ua.c b/pjsip/src/pjsip-ua/sip_ua.c
index 094be07f..ac0980f5 100644
--- a/pjsip/src/pjsip-ua/sip_ua.c
+++ b/pjsip/src/pjsip-ua/sip_ua.c
@@ -41,10 +41,8 @@
/*
* Static prototypes.
*/
-static pj_status_t ua_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id );
-static pj_status_t ua_start( struct pjsip_module *mod );
-static pj_status_t ua_deinit( struct pjsip_module *mod );
+static pj_status_t ua_load(pjsip_endpoint *endpt);
+static pj_status_t ua_unload(void);
static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *evt );
static pjsip_dlg *find_dialog( pjsip_user_agent *ua,
pjsip_rx_data *rdata );
@@ -53,65 +51,63 @@ static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
PJ_DECL(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg );
/*
- * Default UA instance.
- */
-static pjsip_user_agent ua_instance;
-
-/*
* Module interface.
*/
-static struct pjsip_module mod_ua =
+static struct user_agent
{
- { "User-Agent", 10 }, /* Name. */
- 0, /* Flag */
- 128, /* Priority */
- NULL, /* User agent instance, initialized by APP. */
- 0, /* Number of methods supported (will be initialized later). */
- { 0 }, /* Array of methods (will be initialized later) */
- &ua_init, /* init_module() */
- &ua_start, /* start_module() */
- &ua_deinit, /* deinit_module() */
- &ua_tsx_handler, /* tsx_handler() */
+ pjsip_module mod;
+ pj_pool_t *pool;
+ pjsip_endpoint *endpt;
+ pj_mutex_t *mutex;
+ pj_hash_table_t *dlg_table;
+ pjsip_dialog dlg_list;
+
+} mod_ua =
+{
+ {
+ NULL, NULL, /* prev, next. */
+ { "mod-ua", 6 }, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_UA_PROXY_LAYER, /* Priority */
+ NULL, /* User data. */
+ 0, /* Number of methods supported. */
+ { 0 }, /* Array of methods */
+ &ua_load, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ &ua_unload, /* unload() */
+ NULL, /* on_rx_request() */
+ NULL, /* on_rx_response() */
+ NULL, /* on_tx_request. */
+ NULL, /* on_tx_response() */
+ NULL, /* on_tsx_state() */
+ }
};
/*
* Initialize user agent instance.
*/
-static pj_status_t ua_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id )
+static pj_status_t ua_load( pjsip_endpoint *endpt )
{
- static pjsip_method m_invite, m_ack, m_cancel, m_bye;
- pjsip_user_agent *ua = mod->mod_data;
extern int pjsip_dlg_lock_tls_id; /* defined in sip_dialog.c */
+ pj_status_t status;
- pjsip_method_set( &m_invite, PJSIP_INVITE_METHOD );
- pjsip_method_set( &m_ack, PJSIP_ACK_METHOD );
- pjsip_method_set( &m_cancel, PJSIP_CANCEL_METHOD );
- pjsip_method_set( &m_bye, PJSIP_BYE_METHOD );
+ /* Initialize the user agent. */
+ mod_ua.endpt = endpt;
+ status = pjsip_endpt_create_pool( endpt, "pua%p", PJSIP_POOL_LEN_UA,
+ PJSIP_POOL_INC_UA, &mod_ua.pool);
+ if (status != PJ_SUCCESS)
+ return status;
- mod->method_cnt = 4;
- mod->methods[0] = &m_invite;
- mod->methods[1] = &m_ack;
- mod->methods[2] = &m_cancel;
- mod->methods[3] = &m_bye;
+ status = pj_mutex_create_recursive(mod_ua.pool, " ua%p", &mod_ua.mutex);
+ if (status != PJ_SUCCESS)
+ return status;
- /* Initialize the user agent. */
- ua->endpt = endpt;
- ua->pool = pjsip_endpt_create_pool(endpt, "pua%p", PJSIP_POOL_LEN_UA,
- PJSIP_POOL_INC_UA);
- if (!ua->pool) {
- return -1;
- }
- ua->mod_id = id;
- ua->mutex = pj_mutex_create(ua->pool, " ua%p", 0);
- if (!ua->mutex) {
- return -1;
- }
- ua->dlg_table = pj_hash_create(ua->pool, PJSIP_MAX_DIALOG_COUNT);
- if (ua->dlg_table == NULL) {
- return -1;
- }
- pj_list_init(&ua->dlg_list);
+ mod_ua.dlg_table = pj_hash_create(mod_ua.pool, PJSIP_MAX_DIALOG_COUNT);
+ if (ua->dlg_table == NULL)
+ return PJ_ENOMEM;
+
+ pj_list_init(&mod_ua.dlg_list);
/* Initialize dialog lock. */
pjsip_dlg_lock_tls_id = pj_thread_local_alloc();
@@ -120,50 +116,21 @@ static pj_status_t ua_init( pjsip_endpoint *endpt,
}
pj_thread_local_set(pjsip_dlg_lock_tls_id, NULL);
- return 0;
-}
-
-/*
- * Start user agent instance.
- */
-static pj_status_t ua_start( struct pjsip_module *mod )
-{
- PJ_UNUSED_ARG(mod)
- return 0;
+ return PJ_SUCCESS;
}
/*
* Destroy user agent.
*/
-static pj_status_t ua_deinit( struct pjsip_module *mod )
+static pj_status_t ua_unload()
{
- pjsip_user_agent *ua = mod->mod_data;
-
- pj_mutex_unlock(ua->mutex);
+ pj_mutex_unlock(mod_ua.mutex);
/* Release pool */
- if (ua->pool) {
- pjsip_endpt_destroy_pool( ua->endpt, ua->pool );
+ if (mod_ua.pool) {
+ pjsip_endpt_destroy_pool( mod_ua.endpt, mod_ua.pool );
}
- return 0;
-}
-
-/*
- * Get the module interface for the UA module.
- */
-PJ_DEF(pjsip_module*) pjsip_ua_get_module(void)
-{
- mod_ua.mod_data = &ua_instance;
- return &mod_ua;
-}
-
-/*
- * Register callback to receive dialog notifications.
- */
-PJ_DEF(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua,
- pjsip_dlg_callback *cb )
-{
- ua->dlg_cb = cb;
+ return PJ_SUCCESS;
}
/*
diff --git a/pjsip/src/pjsip/sip_auth.c b/pjsip/src/pjsip/sip_auth_client.c
index 9b7c52bb..523b35a9 100644
--- a/pjsip/src/pjsip/sip_auth.c
+++ b/pjsip/src/pjsip/sip_auth_client.c
@@ -16,10 +16,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include <pjsip/sip_auth.h>
#include <pjsip/sip_auth_parser.h> /* just to get pjsip_DIGEST_STR */
#include <pjsip/sip_transport.h>
#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_errno.h>
#include <pjlib-util/md5.h>
#include <pj/log.h>
#include <pj/string.h>
@@ -28,61 +30,53 @@
#include <pj/assert.h>
#include <pj/ctype.h>
-/* Length of digest string. */
-#define MD5STRLEN 32
-/* Maximum stack size we use for storing username+realm+password etc. */
-#define MAX_TEMP 128
/* A macro just to get rid of type mismatch between char and unsigned char */
#define MD5_APPEND(pms,buf,len) pj_md5_update(pms, (const pj_uint8_t*)buf, len)
/* Logging. */
-#define THIS_FILE "sip_auth.c"
+#define THIS_FILE "sip_auth_client.c"
#if 0
# define AUTH_TRACE_(expr) PJ_LOG(3, expr)
#else
# define AUTH_TRACE_(expr)
#endif
-static const char hex[] = "0123456789abcdef";
-
/* Transform digest to string.
- * output must be at least MD5STRLEN+1 bytes.
+ * output must be at least PJSIP_MD5STRLEN+1 bytes.
*
* NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED!
*/
static void digest2str(const unsigned char digest[], char *output)
{
- char *p = output;
int i;
-
for (i = 0; i<16; ++i) {
- int val = digest[i];
- *p++ = hex[val >> 4];
- *p++ = hex[val & 0x0F];
+ pj_val_to_hex_digit(digest[i], output);
+ output += 2;
}
}
+
/*
* Create response digest based on the parameters and store the
* digest ASCII in 'result'.
*/
-static void create_digest( pj_str_t *result,
- const pj_str_t *nonce,
- const pj_str_t *nc,
- const pj_str_t *cnonce,
- const pj_str_t *qop,
- const pj_str_t *uri,
- const pjsip_cred_info *cred_info,
- const pj_str_t *method)
+void pjsip_auth_create_digest( pj_str_t *result,
+ const pj_str_t *nonce,
+ const pj_str_t *nc,
+ const pj_str_t *cnonce,
+ const pj_str_t *qop,
+ const pj_str_t *uri,
+ const pjsip_cred_info *cred_info,
+ const pj_str_t *method)
{
- char ha1[MD5STRLEN];
- char ha2[MD5STRLEN];
+ char ha1[PJSIP_MD5STRLEN];
+ char ha2[PJSIP_MD5STRLEN];
unsigned char digest[16];
pj_md5_context pms;
- pj_assert(result->slen >= MD5STRLEN);
+ pj_assert(result->slen >= PJSIP_MD5STRLEN);
AUTH_TRACE_((THIS_FILE, "Begin creating digest"));
@@ -127,7 +121,7 @@ static void create_digest( pj_str_t *result,
*** response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2)
***/
pj_md5_init(&pms);
- MD5_APPEND( &pms, ha1, MD5STRLEN);
+ MD5_APPEND( &pms, ha1, PJSIP_MD5STRLEN);
MD5_APPEND( &pms, ":", 1);
MD5_APPEND( &pms, nonce->ptr, nonce->slen);
if (qop && qop->slen != 0) {
@@ -139,13 +133,13 @@ static void create_digest( pj_str_t *result,
MD5_APPEND( &pms, qop->ptr, qop->slen);
}
MD5_APPEND( &pms, ":", 1);
- MD5_APPEND( &pms, ha2, MD5STRLEN);
+ MD5_APPEND( &pms, ha2, PJSIP_MD5STRLEN);
/* This is the final response digest. */
pj_md5_final(&pms, digest);
/* Convert digest to string and store in chal->response. */
- result->slen = MD5STRLEN;
+ result->slen = PJSIP_MD5STRLEN;
digest2str(digest, result->ptr);
AUTH_TRACE_((THIS_FILE, " digest=%.32s", result->ptr));
@@ -206,7 +200,7 @@ static pj_status_t respond_digest( pj_pool_t *pool,
{
PJ_LOG(4,(THIS_FILE, "Unsupported digest algorithm \"%.*s\"",
chal->algorithm.slen, chal->algorithm.ptr));
- return -1;
+ return PJSIP_EINVALIDALGORITHM;
}
/* Build digest credential from arguments. */
@@ -218,15 +212,15 @@ static pj_status_t respond_digest( pj_pool_t *pool,
pj_strdup(pool, &cred->opaque, &chal->opaque);
/* Allocate memory. */
- cred->response.ptr = pj_pool_alloc(pool, MD5STRLEN);
- cred->response.slen = MD5STRLEN;
+ cred->response.ptr = pj_pool_alloc(pool, PJSIP_MD5STRLEN);
+ cred->response.slen = PJSIP_MD5STRLEN;
if (chal->qop.slen == 0) {
/* Server doesn't require quality of protection. */
/* Convert digest to string and store in chal->response. */
- create_digest( &cred->response, &cred->nonce, NULL, NULL, NULL,
- uri, cred_info, method);
+ pjsip_auth_create_digest( &cred->response, &cred->nonce, NULL, NULL,
+ NULL, uri, cred_info, method);
} else if (has_auth_qop(pool, &chal->qop)) {
/* Server requires quality of protection.
@@ -243,118 +237,166 @@ static pj_status_t respond_digest( pj_pool_t *pool,
pj_strdup(pool, &cred->cnonce, &dummy_cnonce);
}
- create_digest( &cred->response, &cred->nonce, &cred->nc, cnonce,
- &pjsip_AUTH_STR, uri, cred_info, method );
+ pjsip_auth_create_digest( &cred->response, &cred->nonce, &cred->nc,
+ cnonce, &pjsip_AUTH_STR, uri, cred_info,
+ method );
} else {
/* Server requires quality protection that we don't support. */
PJ_LOG(4,(THIS_FILE, "Unsupported qop offer %.*s",
chal->qop.slen, chal->qop.ptr));
- return -1;
+ return PJSIP_EINVALIDQOP;
}
- return 0;
+ return PJ_SUCCESS;
}
-#if PJSIP_AUTH_QOP_SUPPORT
+#if defined(PJSIP_AUTH_QOP_SUPPORT) && PJSIP_AUTH_QOP_SUPPORT!=0
/*
* Update authentication session with a challenge.
*/
static void update_digest_session( pj_pool_t *ses_pool,
- pjsip_auth_session *auth_sess,
+ pjsip_cached_auth *cached_auth,
const pjsip_www_authenticate_hdr *hdr )
{
if (hdr->challenge.digest.qop.slen == 0)
return;
/* Initialize cnonce and qop if not present. */
- if (auth_sess->cnonce.slen == 0) {
+ if (cached_auth->cnonce.slen == 0) {
/* Save the whole challenge */
- auth_sess->last_chal = pjsip_hdr_clone(ses_pool, hdr);
+ cached_auth->last_chal = pjsip_hdr_clone(ses_pool, hdr);
/* Create cnonce */
- pj_create_unique_string( ses_pool, &auth_sess->cnonce );
+ pj_create_unique_string( ses_pool, &cached_auth->cnonce );
/* Initialize nonce-count */
- auth_sess->nc = 1;
+ cached_auth->nc = 1;
/* Save realm. */
- pj_assert(auth_sess->realm.slen != 0);
- if (auth_sess->realm.slen == 0) {
- pj_strdup(ses_pool, &auth_sess->realm,
+ pj_assert(cached_auth->realm.slen != 0);
+ if (cached_auth->realm.slen == 0) {
+ pj_strdup(ses_pool, &cached_auth->realm,
&hdr->challenge.digest.realm);
}
} else {
/* Update last_nonce and nonce-count */
if (!pj_strcmp(&hdr->challenge.digest.nonce,
- &auth_sess->last_chal->challenge.digest.nonce))
+ &cached_auth->last_chal->challenge.digest.nonce))
{
/* Same nonce, increment nonce-count */
- ++auth_sess->nc;
+ ++cached_auth->nc;
} else {
/* Server gives new nonce. */
- pj_strdup(ses_pool, &auth_sess->last_chal->challenge.digest.nonce,
+ pj_strdup(ses_pool, &cached_auth->last_chal->challenge.digest.nonce,
&hdr->challenge.digest.nonce);
/* Has the opaque changed? */
- if (pj_strcmp(&auth_sess->last_chal->challenge.digest.opaque,
+ if (pj_strcmp(&cached_auth->last_chal->challenge.digest.opaque,
&hdr->challenge.digest.opaque))
{
pj_strdup(ses_pool,
- &auth_sess->last_chal->challenge.digest.opaque,
+ &cached_auth->last_chal->challenge.digest.opaque,
&hdr->challenge.digest.opaque);
}
- auth_sess->nc = 1;
+ cached_auth->nc = 1;
}
}
}
#endif /* PJSIP_AUTH_QOP_SUPPORT */
-/* Find authentication session in the list. */
-static pjsip_auth_session *find_session( pjsip_auth_session *sess_list,
- const pj_str_t *realm )
+/* Find cached authentication in the list for the specified realm. */
+static pjsip_cached_auth *find_cached_auth( pjsip_auth_clt_sess *sess,
+ const pj_str_t *realm )
{
- pjsip_auth_session *sess = sess_list->next;
- while (sess != sess_list) {
- if (pj_stricmp(&sess->realm, realm) == 0)
- return sess;
- sess = sess->next;
+ pjsip_cached_auth *auth = sess->cached_auth.next;
+ while (auth != &sess->cached_auth) {
+ if (pj_stricmp(&auth->realm, realm) == 0)
+ return auth;
+ auth = auth->next;
}
return NULL;
}
+/* Find credential to use for the specified realm and auth scheme. */
+static const pjsip_cred_info* auth_find_cred( const pjsip_auth_clt_sess *sess,
+ const pj_str_t *realm,
+ const pj_str_t *auth_scheme)
+{
+ unsigned i;
+ PJ_UNUSED_ARG(auth_scheme);
+ for (i=0; i<sess->cred_cnt; ++i) {
+ if (pj_stricmp(&sess->cred_info[i].realm, realm) == 0)
+ return &sess->cred_info[i];
+ }
+ return NULL;
+}
+
+
+/* Init client session. */
+PJ_DEF(pj_status_t) pjsip_auth_clt_init( pjsip_auth_clt_sess *sess,
+ pjsip_endpoint *endpt,
+ pj_pool_t *pool,
+ unsigned options)
+{
+ PJ_ASSERT_RETURN(sess && endpt && pool && (options==0), PJ_EINVAL);
+
+ sess->pool = pool;
+ sess->endpt = endpt;
+ sess->cred_cnt = 0;
+ sess->cred_info = NULL;
+ pj_list_init(&sess->cached_auth);
+
+ return PJ_SUCCESS;
+}
+
+
+/* Set client credentials. */
+PJ_DEF(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess,
+ int cred_cnt,
+ const pjsip_cred_info *c)
+{
+ PJ_ASSERT_RETURN(sess && cred_cnt && c, PJ_EINVAL);
+
+ sess->cred_info = pj_pool_alloc(sess->pool, cred_cnt * sizeof(*c));
+ pj_memcpy(sess->cred_info, c, cred_cnt * sizeof(*c));
+ sess->cred_cnt = cred_cnt;
+
+ return PJ_SUCCESS;
+}
+
+
/*
* Create Authorization/Proxy-Authorization response header based on the challege
* in WWW-Authenticate/Proxy-Authenticate header.
*/
-PJ_DEF(pjsip_authorization_hdr*)
-pjsip_auth_respond( pj_pool_t *req_pool,
- const pjsip_www_authenticate_hdr *hdr,
- const pjsip_uri *uri,
- const pjsip_cred_info *cred_info,
- const pjsip_method *method,
- pj_pool_t *sess_pool,
- pjsip_auth_session *auth_sess)
+static pj_status_t auth_respond( pj_pool_t *req_pool,
+ const pjsip_www_authenticate_hdr *hdr,
+ const pjsip_uri *uri,
+ const pjsip_cred_info *cred_info,
+ const pjsip_method *method,
+ pj_pool_t *sess_pool,
+ pjsip_cached_auth *cached_auth,
+ pjsip_authorization_hdr **p_h_auth)
{
- pjsip_authorization_hdr *auth;
+ pjsip_authorization_hdr *hauth;
char tmp[PJSIP_MAX_URL_SIZE];
pj_str_t uri_str;
pj_pool_t *pool;
+ pj_status_t status;
- pj_assert(hdr != NULL);
- pj_assert(uri != NULL);
- pj_assert(cred_info != NULL);
- pj_assert(method != NULL);
+ /* Verify arguments. */
+ PJ_ASSERT_RETURN(req_pool && hdr && uri && cred_info && method &&
+ sess_pool && cached_auth && p_h_auth, PJ_EINVAL);
/* Print URL in the original request. */
uri_str.ptr = tmp;
- uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp, sizeof(tmp));
+ uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp,sizeof(tmp));
if (uri_str.slen < 1) {
pj_assert(!"URL is too long!");
- PJ_LOG(4,(THIS_FILE, "Unable to authorize: URI is too long!"));
- return NULL;
+ return PJSIP_EURITOOLONG;
}
# if (PJSIP_AUTH_HEADER_CACHING)
@@ -370,52 +412,51 @@ pjsip_auth_respond( pj_pool_t *req_pool,
# endif
if (hdr->type == PJSIP_H_WWW_AUTHENTICATE)
- auth = pjsip_authorization_hdr_create(pool);
+ hauth = pjsip_authorization_hdr_create(pool);
else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE)
- auth = pjsip_proxy_authorization_hdr_create(pool);
+ hauth = pjsip_proxy_authorization_hdr_create(pool);
else {
- pj_assert(0);
- return NULL;
+ pj_assert(!"Invalid response header!");
+ return PJSIP_EINVALIDHDR;
}
/* Only support digest scheme at the moment. */
if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
- pj_status_t rc;
pj_str_t *cnonce = NULL;
pj_uint32_t nc = 1;
/* Update the session (nonce-count etc) if required. */
# if PJSIP_AUTH_QOP_SUPPORT
{
- if (auth_sess) {
- update_digest_session( sess_pool, auth_sess, hdr );
+ if (cached_auth) {
+ update_digest_session( sess_pool, cached_auth, hdr );
- cnonce = &auth_sess->cnonce;
- nc = auth_sess->nc;
+ cnonce = &cached_auth->cnonce;
+ nc = cached_auth->nc;
}
}
# endif /* PJSIP_AUTH_QOP_SUPPORT */
- auth->scheme = pjsip_DIGEST_STR;
- rc = respond_digest( pool, &auth->credential.digest,
- &hdr->challenge.digest, &uri_str, cred_info,
- cnonce, nc, &method->name);
- if (rc != 0)
- return NULL;
+ hauth->scheme = pjsip_DIGEST_STR;
+ status = respond_digest( pool, &hauth->credential.digest,
+ &hdr->challenge.digest, &uri_str, cred_info,
+ cnonce, nc, &method->name);
+ if (status != PJ_SUCCESS)
+ return status;
/* Set qop type in auth session the first time only. */
- if (hdr->challenge.digest.qop.slen != 0 && auth_sess) {
- if (auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) {
- pj_str_t *qop_val = &auth->credential.digest.qop;
+ if (hdr->challenge.digest.qop.slen != 0 && cached_auth) {
+ if (cached_auth->qop_value == PJSIP_AUTH_QOP_NONE) {
+ pj_str_t *qop_val = &hauth->credential.digest.qop;
if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) {
- auth_sess->qop_value = PJSIP_AUTH_QOP_AUTH;
+ cached_auth->qop_value = PJSIP_AUTH_QOP_AUTH;
} else {
- auth_sess->qop_value = PJSIP_AUTH_QOP_UNKNOWN;
+ cached_auth->qop_value = PJSIP_AUTH_QOP_UNKNOWN;
}
}
}
} else {
- auth = NULL;
+ return PJSIP_EINVALIDAUTHSCHEME;
}
/* Keep the new authorization header in the cache, only
@@ -423,236 +464,166 @@ pjsip_auth_respond( pj_pool_t *req_pool,
*/
# if PJSIP_AUTH_HEADER_CACHING
{
- if (auth && auth_sess && auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) {
+ if (hauth && cached_auth && cached_auth->qop_value == PJSIP_AUTH_QOP_NONE) {
pjsip_cached_auth_hdr *cached_hdr;
/* Delete old header with the same method. */
- cached_hdr = auth_sess->cached_hdr.next;
- while (cached_hdr != &auth_sess->cached_hdr) {
+ cached_hdr = cached_auth->cached_hdr.next;
+ while (cached_hdr != &cached_auth->cached_hdr) {
if (pjsip_method_cmp(method, &cached_hdr->method)==0)
break;
cached_hdr = cached_hdr->next;
}
/* Save the header to the list. */
- if (cached_hdr != &auth_sess->cached_hdr) {
- cached_hdr->hdr = auth;
+ if (cached_hdr != &cached_auth->cached_hdr) {
+ cached_hdr->hdr = hauth;
} else {
cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr));
pjsip_method_copy( pool, &cached_hdr->method, method);
- cached_hdr->hdr = auth;
- pj_list_insert_before( &auth_sess->cached_hdr, cached_hdr );
+ cached_hdr->hdr = hauth;
+ pj_list_insert_before( &cached_auth->cached_hdr, cached_hdr );
}
}
}
# endif
- return auth;
+ *p_h_auth = hauth;
+ return PJ_SUCCESS;
}
-/* Verify incoming Authorization/Proxy-Authorization header against existing
- * credentials. Will return TRUE if the authorization request matches any of
- * the credential.
- */
-PJ_DEF(pj_bool_t) pjsip_auth_verify(const pjsip_authorization_hdr *hdr,
- const pj_str_t *method,
- const pjsip_cred_info *cred_info )
-{
- if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) {
- char digest_buf[MD5STRLEN];
- pj_str_t digest;
- const pjsip_digest_credential *dig = &hdr->credential.digest;
-
- /* Check that username match. */
- if (pj_strcmp(&dig->username, &cred_info->username) != 0)
- return PJ_FALSE;
-
- /* Check that realm match. */
- if (pj_strcmp(&dig->realm, &cred_info->realm) != 0)
- return PJ_FALSE;
-
- /* Prepare for our digest calculation. */
- digest.ptr = digest_buf;
- digest.slen = MD5STRLEN;
-
- /* Create digest for comparison. */
- create_digest( &digest,
- &hdr->credential.digest.nonce,
- &hdr->credential.digest.nc,
- &hdr->credential.digest.cnonce,
- &hdr->credential.digest.qop,
- &hdr->credential.digest.uri,
- cred_info,
- method );
-
- return pj_stricmp(&digest, &hdr->credential.digest.response) == 0;
- } else {
- pj_assert(0);
- return PJ_FALSE;
- }
-}
-
-/* Find credential to use for the specified realm and scheme. */
-PJ_DEF(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count,
- const pjsip_cred_info cred[],
- const pj_str_t *realm,
- const pj_str_t *scheme)
-{
- unsigned i;
- PJ_UNUSED_ARG(scheme);
- for (i=0; i<count; ++i) {
- if (pj_stricmp(&cred[i].realm, realm) == 0)
- return &cred[i];
- }
- return NULL;
-}
-
-#if PJSIP_AUTH_AUTO_SEND_NEXT
-static void new_auth_for_req( pjsip_tx_data *tdata,
- pj_pool_t *sess_pool,
- pjsip_auth_session *sess,
- int cred_count,
- const pjsip_cred_info cred_info[])
+#if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && PJSIP_AUTH_AUTO_SEND_NEXT!=0
+static pj_status_t new_auth_for_req( pjsip_tx_data *tdata,
+ pjsip_auth_clt_sess *sess,
+ pjsip_cached_auth *auth,
+ pjsip_authorization_hdr **p_h_auth)
{
const pjsip_cred_info *cred;
pjsip_authorization_hdr *hauth;
+ pj_status_t status;
- pj_assert(sess->last_chal != NULL);
+ PJ_ASSERT_RETURN(tdata && sess && auth, PJ_EINVAL);
+ PJ_ASSERT_RETURN(auth->last_chal != NULL, PJSIP_EAUTHNOPREVCHAL);
- cred = pjsip_auth_find_cred( cred_count, cred_info, &sess->realm,
- &sess->last_chal->scheme );
+ cred = auth_find_cred( sess, &auth->realm, &auth->last_chal->scheme );
if (!cred)
- return;
-
+ return PJSIP_ENOCREDENTIAL;
+
+ status = auth_respond( tdata->pool, auth->last_chal,
+ tdata->msg->line.req.uri,
+ cred, &tdata->msg->line.req.method,
+ sess->pool, auth, &hauth);
+ if (status != PJ_SUCCESS)
+ return status;
- hauth = pjsip_auth_respond( tdata->pool, sess->last_chal,
- tdata->msg->line.req.uri,
- cred, &tdata->msg->line.req.method,
- sess_pool, sess);
- if (hauth) {
- pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hauth);
- }
+ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hauth);
+
+ if (p_h_auth)
+ *p_h_auth = hauth;
+
+ return PJ_SUCCESS;
}
#endif
-/*
- * Initialize new request message with authorization headers.
- * This function will put Authorization/Proxy-Authorization headers to the
- * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING)
- * and the session has previously sent Authorization/Proxy-Authorization header
- * with the same method, then the same Authorization/Proxy-Authorization header
- * will be resent from the cache only if qop is not present. If the stack is
- * configured to automatically generate next Authorization/Proxy-Authorization
- * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy-
- * Authorization headers are calculated and generated when they are not present
- * in the case or if authorization session has qop.
- *
- * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag
- * are not set, this function will do nothing. The stack then will only send
- * Authorization/Proxy-Authorization to respond 401/407 response.
- */
-PJ_DEF(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool,
- pjsip_tx_data *tdata,
- pjsip_auth_session *sess_list,
- int cred_count,
- const pjsip_cred_info cred_info[])
-{
- pjsip_auth_session *sess;
- pjsip_method *method = &tdata->msg->line.req.method;
-
- pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
- if (!sess_list)
- return 0;
- sess = sess_list->next;
- while (sess != sess_list) {
- if (sess->qop_value == PJSIP_AUTH_QOP_NONE) {
-# if (PJSIP_AUTH_HEADER_CACHING)
+/* Initialize outgoing request. */
+PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess,
+ pjsip_tx_data *tdata )
+{
+ const pjsip_method *method;
+ pjsip_cached_auth *auth;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL);
+ PJ_ASSERT_RETURN(sess->pool, PJSIP_ENOTINITIALIZED);
+ PJ_ASSERT_RETURN(tdata->msg->type==PJSIP_REQUEST_MSG,
+ PJSIP_ENOTREQUESTMSG);
+
+ /* Get the method. */
+ method = &tdata->msg->line.req.method;
+
+ auth = sess->cached_auth.next;
+ while (auth != &sess->cached_auth) {
+ if (auth->qop_value == PJSIP_AUTH_QOP_NONE) {
+# if defined(PJSIP_AUTH_HEADER_CACHING) && \
+ PJSIP_AUTH_HEADER_CACHING!=0
{
- pjsip_cached_auth_hdr *entry = sess->cached_hdr.next;
- while (entry != &sess->cached_hdr) {
+ pjsip_cached_auth_hdr *entry = auth->cached_hdr.next;
+ while (entry != &auth->cached_hdr) {
if (pjsip_method_cmp(&entry->method, method)==0) {
pjsip_authorization_hdr *hauth;
hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr);
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
} else {
-# if (PJSIP_AUTH_AUTO_SEND_NEXT)
- {
- new_auth_for_req( tdata, sess_pool, sess,
- cred_count, cred_info);
- }
-# else
+# if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \
+ PJSIP_AUTH_AUTO_SEND_NEXT!=0
{
- PJ_UNUSED_ARG(sess_pool);
- PJ_UNUSED_ARG(cred_count);
- PJ_UNUSED_ARG(cred_info);
+ new_auth_for_req( tdata, sess, auth, NULL);
}
-# endif /* PJSIP_AUTH_AUTO_SEND_NEXT */
+# endif
}
entry = entry->next;
}
}
-# elif (PJSIP_AUTH_AUTO_SEND_NEXT)
+# elif defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \
+ PJSIP_AUTH_AUTO_SEND_NEXT!=0
{
- new_auth_for_req( tdata, sess_pool, sess,
- cred_count, cred_info);
+ new_auth_for_req( tdata, sess, auth, NULL);
}
-# else
- {
- PJ_UNUSED_ARG(sess_pool);
- PJ_UNUSED_ARG(cred_count);
- PJ_UNUSED_ARG(cred_info);
- }
-# endif /* PJSIP_AUTH_HEADER_CACHING */
+# endif
}
-# if (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT)
- else if (sess->qop_value == PJSIP_AUTH_QOP_AUTH) {
+# if defined(PJSIP_AUTH_QOP_SUPPORT) && \
+ defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \
+ (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT)
+ else if (auth->qop_value == PJSIP_AUTH_QOP_AUTH) {
/* For qop="auth", we have to re-create the authorization header.
*/
const pjsip_cred_info *cred;
pjsip_authorization_hdr *hauth;
- cred = pjsip_auth_find_cred( cred_count, cred_info,
- &sess->realm,
- &sess->last_chal->scheme);
+ cred = auth_find_cred(sess, &auth->realm,
+ &auth->last_chal->scheme);
if (!cred) {
- sess = sess->next;
+ auth = auth->next;
continue;
}
- hauth = pjsip_auth_respond( tdata->pool, sess->last_chal,
- tdata->msg->line.req.uri,
- cred,
- &tdata->msg->line.req.method,
- sess_pool, sess );
- if (hauth) {
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
- }
+ status = auth_respond( tdata->pool, auth->last_chal,
+ tdata->msg->line.req.uri,
+ cred,
+ &tdata->msg->line.req.method,
+ sess->pool, auth, &hauth);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
}
# endif /* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */
- sess = sess->next;
+ auth = auth->next;
}
- return 0;
+
+ return PJ_SUCCESS;
}
+
/* Process authorization challenge */
-static pjsip_authorization_hdr *process_auth( pj_pool_t *req_pool,
- const pjsip_www_authenticate_hdr *hchal,
- const pjsip_uri *uri,
- pjsip_tx_data *tdata,
- int cred_count,
- const pjsip_cred_info cred_info[],
- pj_pool_t *ses_pool,
- pjsip_auth_session *auth_sess)
+static pj_status_t process_auth( pj_pool_t *req_pool,
+ const pjsip_www_authenticate_hdr *hchal,
+ const pjsip_uri *uri,
+ pjsip_tx_data *tdata,
+ pjsip_auth_clt_sess *sess,
+ pjsip_cached_auth *cached_auth,
+ pjsip_authorization_hdr **h_auth)
{
const pjsip_cred_info *cred;
- pjsip_authorization_hdr *sent_auth = NULL, *hauth;
+ pjsip_authorization_hdr *sent_auth = NULL;
pjsip_hdr *hdr;
+ pj_status_t status;
/* See if we have sent authorization header for this realm */
hdr = tdata->msg->hdr.next;
@@ -685,7 +656,7 @@ static pjsip_authorization_hdr *process_auth( pj_pool_t *req_pool,
sent_auth->credential.digest.username.ptr,
sent_auth->credential.digest.realm.slen,
sent_auth->credential.digest.realm.ptr));
- return NULL;
+ return PJSIP_EFAILEDCREDENTIAL;
}
/* Otherwise remove old, stale authorization header from the mesasge.
@@ -695,8 +666,8 @@ static pjsip_authorization_hdr *process_auth( pj_pool_t *req_pool,
}
/* Find credential to be used for the challenge. */
- cred = pjsip_auth_find_cred( cred_count, cred_info,
- &hchal->challenge.common.realm, &hchal->scheme);
+ cred = auth_find_cred( sess, &hchal->challenge.common.realm,
+ &hchal->scheme);
if (!cred) {
const pj_str_t *realm = &hchal->challenge.common.realm;
PJ_LOG(4,(THIS_FILE,
@@ -704,14 +675,14 @@ static pjsip_authorization_hdr *process_auth( pj_pool_t *req_pool,
tdata->obj_name,
realm->slen, realm->ptr,
hchal->scheme.slen, hchal->scheme.ptr));
- return NULL;
+ return PJSIP_ENOCREDENTIAL;
}
/* Respond to authorization challenge. */
- hauth = pjsip_auth_respond( req_pool, hchal, uri, cred,
- &tdata->msg->line.req.method,
- ses_pool, auth_sess);
- return hauth;
+ status = auth_respond( req_pool, hchal, uri, cred,
+ &tdata->msg->line.req.method,
+ sess->pool, cached_auth, h_auth);
+ return status;
}
@@ -721,29 +692,35 @@ static pjsip_authorization_hdr *process_auth( pj_pool_t *req_pool,
* - to put the newly created Authorization/Proxy-Authorization header
* in cached_list.
*/
-PJ_DEF(pjsip_tx_data*) pjsip_auth_reinit_req( pjsip_endpoint *endpt,
- pj_pool_t *ses_pool,
- pjsip_auth_session *sess_list,
- int cred_count,
- const pjsip_cred_info cred_info[],
- pjsip_tx_data *tdata,
- const pjsip_rx_data *rdata)
+PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess,
+ const pjsip_rx_data *rdata,
+ pjsip_tx_data *old_request,
+ pjsip_tx_data **new_request )
{
+ pjsip_tx_data *tdata;
const pjsip_hdr *hdr;
pjsip_via_hdr *via;
-
- PJ_UNUSED_ARG(endpt);
-
- pj_assert(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG);
- pj_assert(rdata->msg_info.msg->line.status.code == 401 ||
- rdata->msg_info.msg->line.status.code == 407 );
-
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(sess && rdata && old_request && new_request,
+ PJ_EINVAL);
+ PJ_ASSERT_RETURN(sess->pool, PJSIP_ENOTINITIALIZED);
+ PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG,
+ PJSIP_ENOTRESPONSEMSG);
+ PJ_ASSERT_RETURN(old_request->msg->type == PJSIP_REQUEST_MSG,
+ PJSIP_ENOTREQUESTMSG);
+ PJ_ASSERT_RETURN(rdata->msg_info.msg->line.status.code == 401 ||
+ rdata->msg_info.msg->line.status.code == 407,
+ PJSIP_EINVALIDSTATUS);
+
+ tdata = old_request;
+
/*
* Respond to each authentication challenge.
*/
hdr = rdata->msg_info.msg->hdr.next;
while (hdr != &rdata->msg_info.msg->hdr) {
- pjsip_auth_session *sess;
+ pjsip_cached_auth *cached_auth;
const pjsip_www_authenticate_hdr *hchal;
pjsip_authorization_hdr *hauth;
@@ -762,26 +739,26 @@ PJ_DEF(pjsip_tx_data*) pjsip_auth_reinit_req( pjsip_endpoint *endpt,
/* Find authentication session for this realm, create a new one
* if not present.
*/
- sess = find_session(sess_list, &hchal->challenge.common.realm );
- if (!sess) {
- sess = pj_pool_calloc( ses_pool, 1, sizeof(*sess));
- pj_strdup( ses_pool, &sess->realm, &hchal->challenge.common.realm);
- sess->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE);
+ cached_auth = find_cached_auth(sess, &hchal->challenge.common.realm );
+ if (!cached_auth) {
+ cached_auth = pj_pool_zalloc( sess->pool, sizeof(*cached_auth));
+ pj_strdup( sess->pool, &cached_auth->realm, &hchal->challenge.common.realm);
+ cached_auth->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE);
# if (PJSIP_AUTH_HEADER_CACHING)
{
- pj_list_init(&sess->cached_hdr);
+ pj_list_init(&cached_auth->cached_hdr);
}
# endif
- pj_list_insert_before( sess_list, sess );
+ pj_list_insert_before( &sess->cached_auth, cached_auth );
}
/* Create authorization header for this challenge, and update
* authorization session.
*/
- hauth = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri,
- tdata, cred_count, cred_info, ses_pool, sess );
- if (!hauth)
- return NULL;
+ status = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri,
+ tdata, sess, cached_auth, &hauth);
+ if (status != PJ_SUCCESS)
+ return status;
/* Add to the message. */
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
@@ -799,6 +776,8 @@ PJ_DEF(pjsip_tx_data*) pjsip_auth_reinit_req( pjsip_endpoint *endpt,
pjsip_tx_data_add_ref(tdata);
/* Done. */
- return tdata;
+ *new_request = tdata;
+ return PJ_SUCCESS;
+
}
diff --git a/pjsip/src/pjsip/sip_auth_server.c b/pjsip/src/pjsip/sip_auth_server.c
new file mode 100644
index 00000000..7bd347b2
--- /dev/null
+++ b/pjsip/src/pjsip/sip_auth_server.c
@@ -0,0 +1,234 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <pjsip/sip_auth.h>
+#include <pjsip/sip_auth_parser.h> /* just to get pjsip_DIGEST_STR */
+#include <pjsip/sip_auth_msg.h>
+#include <pjsip/sip_errno.h>
+#include <pjsip/sip_transport.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+
+
+/* Defined in sip_auth_client.c */
+void pjsip_auth_create_digest( pj_str_t *result,
+ const pj_str_t *nonce,
+ const pj_str_t *nc,
+ const pj_str_t *cnonce,
+ const pj_str_t *qop,
+ const pj_str_t *uri,
+ const pjsip_cred_info *cred_info,
+ const pj_str_t *method);
+
+
+/*
+ * Initialize server authorization session data structure to serve the
+ * specified realm and to use lookup_func function to look for the credential
+ * info.
+ */
+PJ_DEF(pj_status_t) pjsip_auth_srv_init( pj_pool_t *pool,
+ pjsip_auth_srv *auth_srv,
+ const pj_str_t *realm,
+ pjsip_auth_lookup_cred *lookup,
+ unsigned options )
+{
+ PJ_ASSERT_RETURN(pool && auth_srv && realm && lookup, PJ_EINVAL);
+
+ pj_strdup( pool, &auth_srv->realm, realm);
+ auth_srv->lookup = lookup;
+ auth_srv->is_proxy = (options & PJSIP_AUTH_SRV_IS_PROXY);
+
+ return PJ_SUCCESS;
+}
+
+
+/* Verify incoming Authorization/Proxy-Authorization header against the
+ * specified credential.
+ */
+static pj_status_t pjsip_auth_verify( const pjsip_authorization_hdr *hdr,
+ const pj_str_t *method,
+ const pjsip_cred_info *cred_info )
+{
+ if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) {
+ char digest_buf[PJSIP_MD5STRLEN];
+ pj_str_t digest;
+ const pjsip_digest_credential *dig = &hdr->credential.digest;
+
+ /* Check that username and realm match.
+ * These checks should have been performed before entering this
+ * function.
+ */
+ PJ_ASSERT_RETURN(pj_strcmp(&dig->username, &cred_info->username) == 0,
+ PJ_EINVALIDOP);
+ PJ_ASSERT_RETURN(pj_strcmp(&dig->realm, &cred_info->realm) == 0,
+ PJ_EINVALIDOP);
+
+ /* Prepare for our digest calculation. */
+ digest.ptr = digest_buf;
+ digest.slen = PJSIP_MD5STRLEN;
+
+ /* Create digest for comparison. */
+ pjsip_auth_create_digest(&digest,
+ &hdr->credential.digest.nonce,
+ &hdr->credential.digest.nc,
+ &hdr->credential.digest.cnonce,
+ &hdr->credential.digest.qop,
+ &hdr->credential.digest.uri,
+ cred_info,
+ method );
+
+ /* Compare digest. */
+ return (pj_stricmp(&digest, &hdr->credential.digest.response) == 0) ?
+ PJ_SUCCESS : PJSIP_EAUTHINVALIDDIGEST;
+
+ } else {
+ pj_assert(!"Unsupported authentication scheme");
+ return PJSIP_EINVALIDAUTHSCHEME;
+ }
+}
+
+
+/*
+ * Request the authorization server framework to verify the authorization
+ * information in the specified request in rdata.
+ */
+PJ_DEF(pj_status_t) pjsip_auth_srv_verify( pjsip_auth_srv *auth_srv,
+ pjsip_rx_data *rdata,
+ int *status_code)
+{
+ pjsip_authorization_hdr *h_auth;
+ pjsip_msg *msg = rdata->msg_info.msg;
+ int htype;
+ pj_str_t acc_name;
+ pjsip_cred_info cred_info;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(auth_srv && rdata, PJ_EINVAL);
+ PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG);
+
+ htype = auth_srv->is_proxy ? PJSIP_H_PROXY_AUTHORIZATION :
+ PJSIP_H_AUTHORIZATION;
+
+ /* Initialize status with 200. */
+ *status_code = 200;
+
+ /* Find authorization header for our realm. */
+ h_auth = (pjsip_authorization_hdr*) pjsip_msg_find_hdr(msg, htype, NULL);
+ while (h_auth) {
+ if (!pj_stricmp(&h_auth->credential.common.realm, &auth_srv->realm))
+ break;
+
+ h_auth = h_auth->next;
+ if (h_auth == (void*) &msg->hdr) {
+ h_auth = NULL;
+ break;
+ }
+
+ h_auth=(pjsip_authorization_hdr*)pjsip_msg_find_hdr(msg,htype,h_auth);
+ }
+
+ if (!h_auth) {
+ *status_code = auth_srv->is_proxy ? 407 : 401;
+ return PJSIP_EAUTHNOAUTH;
+ }
+
+ /* Check authorization scheme. */
+ if (pj_stricmp(&h_auth->scheme, &pjsip_DIGEST_STR) == 0)
+ acc_name = h_auth->credential.digest.username;
+ else {
+ *status_code = auth_srv->is_proxy ? 407 : 401;
+ return PJSIP_EINVALIDAUTHSCHEME;
+ }
+
+ /* Find the credential information for the account. */
+ status = (*auth_srv->lookup)(rdata->tp_info.pool, &auth_srv->realm,
+ &acc_name, &cred_info);
+ if (status != PJ_SUCCESS) {
+ *status_code = PJSIP_SC_FORBIDDEN;
+ return status;
+ }
+
+ /* Authenticate with the specified credential. */
+ status = pjsip_auth_verify(h_auth, &msg->line.req.method.name,
+ &cred_info);
+ if (status != PJ_SUCCESS) {
+ *status_code = PJSIP_SC_FORBIDDEN;
+ }
+ return status;
+}
+
+
+/*
+ * Add authentication challenge headers to the outgoing response in tdata.
+ * Application may specify its customized nonce and opaque for the challenge,
+ * or can leave the value to NULL to make the function fills them in with
+ * random characters.
+ */
+PJ_DEF(pj_status_t) pjsip_auth_srv_challenge( pjsip_auth_srv *auth_srv,
+ const pj_str_t *qop,
+ const pj_str_t *nonce,
+ const pj_str_t *opaque,
+ pj_bool_t stale,
+ pjsip_tx_data *tdata)
+{
+ pjsip_www_authenticate_hdr *hdr;
+ char nonce_buf[16];
+ pj_str_t random;
+
+ PJ_ASSERT_RETURN( auth_srv && tdata, PJ_EINVAL );
+
+ random.ptr = nonce_buf;
+ random.slen = sizeof(nonce_buf);
+
+ /* Create the header. */
+ if (auth_srv->is_proxy)
+ hdr = pjsip_proxy_authenticate_hdr_create(tdata->pool);
+ else
+ hdr = pjsip_www_authenticate_hdr_create(tdata->pool);
+
+ /* Initialize header.
+ * Note: only support digest authentication now.
+ */
+ hdr->scheme = pjsip_DIGEST_STR;
+ hdr->challenge.digest.algorithm = pjsip_MD5_STR;
+ if (nonce) {
+ pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, nonce);
+ } else {
+ pj_create_random_string(nonce_buf, sizeof(nonce_buf));
+ pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, &random);
+ }
+ if (opaque) {
+ pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, opaque);
+ } else {
+ pj_create_random_string(nonce_buf, sizeof(nonce_buf));
+ pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, &random);
+ }
+ if (qop) {
+ pj_strdup(tdata->pool, &hdr->challenge.digest.qop, qop);
+ } else {
+ hdr->challenge.digest.qop.slen = 0;
+ }
+ pj_strdup(tdata->pool, &hdr->challenge.digest.realm, &auth_srv->realm);
+ hdr->challenge.digest.stale = stale;
+
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
+
+ return PJ_SUCCESS;
+}
+
diff --git a/pjsip/src/pjsip/sip_errno.c b/pjsip/src/pjsip/sip_errno.c
index 62de365a..a2faf1a2 100644
--- a/pjsip/src/pjsip/sip_errno.c
+++ b/pjsip/src/pjsip/sip_errno.c
@@ -34,21 +34,28 @@ static const struct
{ PJSIP_EBUSY, "Object is busy" },
{ PJSIP_ETYPEEXISTS , "Object with the same type exists" },
{ PJSIP_ESHUTDOWN, "SIP stack shutting down" },
+ { PJSIP_ENOTINITIALIZED, "SIP object is not initialized." },
/* Messaging errors */
{ PJSIP_EINVALIDMSG, "Invalid message/syntax error" },
- { PJSIP_EINVALIDSCHEME, "Invalid URI scheme" },
+ { PJSIP_ENOTREQUESTMSG, "Expecting request message"},
+ { PJSIP_ENOTRESPONSEMSG, "Expecting response message"},
{ PJSIP_EMSGTOOLONG, "Message too long" },
{ PJSIP_EPARTIALMSG, "Partial message" },
+
+ { PJSIP_EINVALIDSTATUS, "Invalid status code"},
+
+ { PJSIP_EINVALIDSCHEME, "Invalid URI scheme" },
{ PJSIP_EMISSINGREQURI, "Missing Request-URI" },
+ { PJSIP_EINVALIDREQURI, "Invalid Request URI" },
+ { PJSIP_EURITOOLONG, "URI is too long" },
+
{ PJSIP_EMISSINGHDR, "Missing required header(s)" },
- { PJSIP_EMISSINGBODY, "Missing message body" },
+ { PJSIP_EINVALIDHDR, "Invalid header field"},
{ PJSIP_EINVALIDVIA, "Invalid Via header" },
{ PJSIP_EMULTIPLEVIA, "Multiple Via headers in response" },
- { PJSIP_EINVALIDREQURI, "Invalid Request URI" },
- { PJSIP_ENOTREQUESTMSG, "Expecting request message"},
- { PJSIP_ENOTRESPONSEMSG, "Expecting response message"},
- { PJSIP_EINVALIDHDR, "Invalid header field"},
+
+ { PJSIP_EMISSINGBODY, "Missing message body" },
/* Transport errors */
{ PJSIP_EUNSUPTRANSPORT, "Unsupported transport"},
@@ -58,6 +65,19 @@ static const struct
/* Transaction errors */
{ PJSIP_ETSXDESTROYED, "Transaction has been destroyed"},
+
+ /* Authentication. */
+ { PJSIP_EFAILEDCREDENTIAL, "Credential failed to authenticate"},
+ { PJSIP_ENOCREDENTIAL, "No suitable credential"},
+ { PJSIP_EINVALIDALGORITHM, "Invalid/unsupported digest algorithm" },
+ { PJSIP_EINVALIDQOP, "Invalid/unsupported digest qop" },
+ { PJSIP_EINVALIDAUTHSCHEME, "Unsupported authentication scheme" },
+ { PJSIP_EAUTHNOPREVCHAL, "No previous challenge" },
+ { PJSIP_EAUTHNOAUTH, "No suitable authorization header" },
+ { PJSIP_EAUTHACCNOTFOUND, "Account or credential not found" },
+ { PJSIP_EAUTHACCDISABLED, "Account or credential is disabled" },
+ { PJSIP_EAUTHINVALIDREALM, "Invalid authorization realm"},
+ { PJSIP_EAUTHINVALIDDIGEST, "Invalid authorization digest" }
};
diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c
index 5ce5433e..f281824b 100644
--- a/pjsip/src/pjsip/sip_parser.c
+++ b/pjsip/src/pjsip/sip_parser.c
@@ -1399,7 +1399,7 @@ static pjsip_hdr* parse_hdr_call_id(pjsip_parse_ctx *ctx)
parse_hdr_end(ctx->scanner);
if (ctx->rdata)
- ctx->rdata->msg_info.call_id = hdr->id;
+ ctx->rdata->msg_info.cid = hdr;
return (pjsip_hdr*)hdr;
}
diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
index ef59652b..61632ed7 100644
--- a/pjsip/src/pjsip/sip_transaction.c
+++ b/pjsip/src/pjsip/sip_transaction.c
@@ -269,7 +269,7 @@ static pj_status_t create_tsx_key_2543( pj_pool_t *pool,
/* Calculate length required. */
len_required = 9 + /* CSeq number */
rdata->msg_info.from->tag.slen + /* From tag. */
- rdata->msg_info.call_id.slen + /* Call-ID */
+ rdata->msg_info.cid->id.slen + /* Call-ID */
host->slen + /* Via host. */
9 + /* Via port. */
16; /* Separator+Allowance. */
@@ -299,8 +299,8 @@ static pj_status_t create_tsx_key_2543( pj_pool_t *pool,
*p++ = SEPARATOR;
/* Add Call-ID. */
- len = rdata->msg_info.call_id.slen;
- pj_memcpy( p, rdata->msg_info.call_id.ptr, len );
+ len = rdata->msg_info.cid->id.slen;
+ pj_memcpy( p, rdata->msg_info.cid->id.ptr, len );
p += len;
*p++ = SEPARATOR;
@@ -1589,12 +1589,11 @@ static pj_status_t tsx_retransmit( pjsip_transaction *tsx, int resched)
++tsx->retransmit_count;
- status = tsx_send_msg( tsx, tsx->last_tx);
- if (status != PJ_SUCCESS)
- return status;
-
- /* Restart timer T1. */
+ /* Restart timer T1 first before sending the message to ensure that
+ * retransmission timer is not engaged when loop transport is used.
+ */
if (resched) {
+ pj_assert(tsx->state != PJSIP_TSX_STATE_CONFIRMED);
if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
} else {
@@ -1602,6 +1601,11 @@ static pj_status_t tsx_retransmit( pjsip_transaction *tsx, int resched)
}
}
+ status = tsx_send_msg( tsx, tsx->last_tx);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
return PJ_SUCCESS;
}
diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
index 6c8da28d..46d98d99 100644
--- a/pjsip/src/pjsip/sip_transport.c
+++ b/pjsip/src/pjsip/sip_transport.c
@@ -878,7 +878,7 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr,
}
/* Perform basic header checking. */
- if (rdata->msg_info.call_id.ptr == NULL ||
+ if (rdata->msg_info.cid->id.ptr == NULL ||
rdata->msg_info.from == NULL ||
rdata->msg_info.to == NULL ||
rdata->msg_info.via == NULL ||
diff --git a/pjsip/src/pjsip/sip_transport_loop.c b/pjsip/src/pjsip/sip_transport_loop.c
index 33b9f229..98011b9f 100644
--- a/pjsip/src/pjsip/sip_transport_loop.c
+++ b/pjsip/src/pjsip/sip_transport_loop.c
@@ -250,7 +250,7 @@ static pj_status_t loop_destroy(pjsip_transport *tp)
}
/* Worker thread for loop transport. */
-static int loop_thread(void *arg)
+static int loop_transport_worker_thread(void *arg)
{
struct loop_transport *loop = arg;
struct recv_list r;
@@ -384,7 +384,8 @@ PJ_DEF(pj_status_t) pjsip_loop_start( pjsip_endpoint *endpt,
pj_list_init(&loop->send_list);
/* Create worker thread. */
- status = pj_thread_create(pool, "loop", &loop_thread, loop, 0,
+ status = pj_thread_create(pool, "loop",
+ &loop_transport_worker_thread, loop, 0,
PJ_THREAD_SUSPENDED, &loop->thread);
if (status != PJ_SUCCESS)
goto on_error;
diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c
index 607b3989..f69ad9b3 100644
--- a/pjsip/src/pjsip/sip_util.c
+++ b/pjsip/src/pjsip/sip_util.c
@@ -351,6 +351,12 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
pjsip_rr_hdr *rr;
pj_status_t status;
+ /* Check arguments. */
+ PJ_ASSERT_RETURN(endpt && rdata && p_tdata, PJ_EINVAL);
+
+ /* Check status code. */
+ PJ_ASSERT_RETURN(st_code >= 100 && st_code <= 699, PJ_EINVAL);
+
/* rdata must be a request message. */
req_msg = rdata->msg_info.msg;
pj_assert(req_msg->type == PJSIP_REQUEST_MSG);
diff --git a/pjsip/src/test-pjsip/transport_test.c b/pjsip/src/test-pjsip/transport_test.c
index b57fedbf..74f6d796 100644
--- a/pjsip/src/test-pjsip/transport_test.c
+++ b/pjsip/src/test-pjsip/transport_test.c
@@ -116,7 +116,7 @@ static pjsip_module my_module =
static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata)
{
/* Check that this is our request. */
- if (pj_strcmp2(&rdata->msg_info.call_id, CALL_ID_HDR) == 0) {
+ if (pj_strcmp2(&rdata->msg_info.cid->id, CALL_ID_HDR) == 0) {
/* It is! */
/* Send response. */
pjsip_tx_data *tdata;
@@ -149,7 +149,7 @@ static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata)
static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata)
{
- if (pj_strcmp2(&rdata->msg_info.call_id, CALL_ID_HDR) == 0) {
+ if (pj_strcmp2(&rdata->msg_info.cid->id, CALL_ID_HDR) == 0) {
pj_get_timestamp(&my_recv_time);
recv_status = PJ_SUCCESS;
return PJ_TRUE;
@@ -334,8 +334,8 @@ static pj_str_t rt_call_id;
static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata)
{
- if (!pj_strncmp(&rdata->msg_info.call_id, &rt_call_id, rt_call_id.slen)) {
- char *pos = pj_strchr(&rdata->msg_info.call_id, '/');
+ if (!pj_strncmp(&rdata->msg_info.cid->id, &rt_call_id, rt_call_id.slen)) {
+ char *pos = pj_strchr(&rdata->msg_info.cid->id, '/');
int thread_id = (*pos - '0');
pjsip_tx_data *tdata;
@@ -422,8 +422,8 @@ static pj_status_t rt_send_request(int thread_id)
static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata)
{
- if (!pj_strncmp(&rdata->msg_info.call_id, &rt_call_id, rt_call_id.slen)) {
- char *pos = pj_strchr(&rdata->msg_info.call_id, '/')+1;
+ if (!pj_strncmp(&rdata->msg_info.cid->id, &rt_call_id, rt_call_id.slen)) {
+ char *pos = pj_strchr(&rdata->msg_info.cid->id, '/')+1;
int thread_id = (*pos - '0');
pj_timestamp recv_time;
diff --git a/pjsip/src/test-pjsip/tsx_uac_test.c b/pjsip/src/test-pjsip/tsx_uac_test.c
index 182269cd..2ec94e5a 100644
--- a/pjsip/src/test-pjsip/tsx_uac_test.c
+++ b/pjsip/src/test-pjsip/tsx_uac_test.c
@@ -398,12 +398,14 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
test_complete = -747;
}
- /* last_tx is ACK in this case. */
+ /* last_tx MUST be the INVITE request
+ * (authorization depends on this behavior)
+ */
if (tsx->last_tx && tsx->last_tx->msg->line.req.method.id !=
- PJSIP_ACK_METHOD)
+ PJSIP_INVITE_METHOD)
{
PJ_LOG(3,(THIS_FILE,
- " error: last_tx is not ACK"));
+ " error: last_tx is not INVITE"));
test_complete = -748;
}
}
@@ -461,12 +463,14 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
test_complete = -763;
}
- /* last_tx is ACK in this case. */
+ /* last_tx MUST be INVITE.
+ * (authorization depends on this behavior)
+ */
if (tsx->last_tx && tsx->last_tx->msg->line.req.method.id !=
- PJSIP_ACK_METHOD)
+ PJSIP_INVITE_METHOD)
{
PJ_LOG(3,(THIS_FILE,
- " error: last_tx is not ACK"));
+ " error: last_tx is not INVITE"));
test_complete = -764;
}
diff --git a/pjsip/src/test-pjsip/tsx_uas_test.c b/pjsip/src/test-pjsip/tsx_uas_test.c
index bed6fd08..ecc174c4 100644
--- a/pjsip/src/test-pjsip/tsx_uas_test.c
+++ b/pjsip/src/test-pjsip/tsx_uas_test.c
@@ -66,10 +66,19 @@
** until it's terminated by user).
** Transaction also MUST terminate in T4 seconds.
**
- ** TEST10_BRANCH_ID
- ** Test where INVITE UAS transaction never receives ACK
- **
** TEST11_BRANCH_ID
+ ** Test scenario where transport fails before response is sent (i.e.
+ ** in TRYING state).
+ **
+ ** TEST12_BRANCH_ID
+ ** As above, after provisional response is sent but before final
+ ** response is sent (i.e. in PROCEEDING state).
+ **
+ ** TEST13_BRANCH_ID
+ ** As above, for INVITE, after final response has been sent but before
+ ** ACK is received (i.e. in CONNECTED state).
+ **
+ ** TEST14_BRANCH_ID
** When UAS failed to deliver the response with the selected transport,
** it should try contacting the client with other transport or begin
** RFC 3263 server resolution procedure.
@@ -79,11 +88,6 @@
** upon receiving request retransmission).
** c. COMPLETED state.
**
- ** TEST12_BRANCH_ID
- ** Variant of previous test, where transaction fails to deliver the
- ** response using any kind of transports. Transaction should report
- ** transport error to its transaction user.
- **
**/
static char *TEST1_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAS-Test1";
@@ -98,6 +102,7 @@ static char *TEST9_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAS-Test9";
static char *TEST10_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAS-Test10";
static char *TEST11_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAS-Test11";
static char *TEST12_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAS-Test12";
+static char *TEST13_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAS-Test13";
#define TEST1_STATUS_CODE 200
#define TEST2_STATUS_CODE 301
@@ -115,6 +120,7 @@ static char *TEST12_BRANCH_ID = PJSIP_RFC3261_BRANCH_ID "-UAS-Test12";
#define TEST6_RESPONSE_COUNT 3
#define TEST7_STATUS_CODE 301
#define TEST8_STATUS_CODE 302
+#define TEST9_STATUS_CODE 301
#define TEST4_TITLE "test4: absorbing request retransmission"
@@ -210,10 +216,14 @@ static void send_response_timer( pj_timer_heap_t *timer_heap,
status = pjsip_tsx_send_msg(tsx, r->tdata);
if (status != PJ_SUCCESS) {
- PJ_LOG(3,(THIS_FILE," error: timer unable to send response"));
+ // Some tests do expect failure!
+ //PJ_LOG(3,(THIS_FILE," error: timer unable to send response"));
+ pj_mutex_unlock(tsx->mutex);
pjsip_tx_data_dec_ref(r->tdata);
return;
}
+
+ pj_mutex_unlock(tsx->mutex);
}
/* Utility to send response. */
@@ -234,9 +244,10 @@ static void send_response( pjsip_rx_data *rdata,
status = pjsip_tsx_send_msg(tsx, tdata);
if (status != PJ_SUCCESS) {
- app_perror(" error: unable to send response", status);
pjsip_tx_data_dec_ref(tdata);
- test_complete = -197;
+ // Some tests do expect failure!
+ //app_perror(" error: unable to send response", status);
+ //test_complete = -197;
return;
}
}
@@ -249,6 +260,7 @@ static void schedule_send_response( pjsip_rx_data *rdata,
{
pj_status_t status;
pjsip_tx_data *tdata;
+ pj_timer_entry *t;
struct response *r;
pj_time_val delay;
@@ -268,14 +280,15 @@ static void schedule_send_response( pjsip_rx_data *rdata,
delay.msec = msec_delay;
pj_time_val_normalize(&delay);
- timer.user_data = r;
- timer.cb = &send_response_timer;
+ t = pj_pool_zalloc(tdata->pool, sizeof(*t));
+ t->user_data = r;
+ t->cb = &send_response_timer;
- status = pjsip_endpt_schedule_timer(endpt, &timer, &delay);
+ status = pjsip_endpt_schedule_timer(endpt, t, &delay);
if (status != PJ_SUCCESS) {
+ pjsip_tx_data_dec_ref(tdata);
app_perror(" error: unable to schedule timer", status);
test_complete = -199;
- pjsip_tx_data_dec_ref(tdata);
return;
}
}
@@ -574,6 +587,83 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
}
+
+ } else
+ if (pj_strcmp2(&tsx->branch, TEST9_BRANCH_ID)==0) {
+ /*
+ * TEST9_BRANCH_ID tests that retransmission of INVITE final response
+ * must cease when ACK is received.
+ */
+
+ if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+
+ if (test_complete == 0)
+ test_complete = 1;
+
+ /* Check status code. */
+ if (tsx->status_code != TEST9_STATUS_CODE) {
+ PJ_LOG(3,(THIS_FILE, " error: incorrect status code"));
+ test_complete = -160;
+ }
+
+ /* Previous state. */
+ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_CONFIRMED) {
+ PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state"));
+ test_complete = -161;
+ }
+
+ } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
+
+ /* Check that status code is status_code. */
+ if (tsx->status_code != TEST9_STATUS_CODE) {
+ PJ_LOG(3,(THIS_FILE, " error: incorrect status code"));
+ test_complete = -162;
+ }
+
+ /* Previous state. */
+ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) {
+ PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state"));
+ test_complete = -163;
+ }
+
+
+ } else if (tsx->state == PJSIP_TSX_STATE_CONFIRMED) {
+
+ /* Check that status code is status_code. */
+ if (tsx->status_code != TEST9_STATUS_CODE) {
+ PJ_LOG(3,(THIS_FILE, " error: incorrect status code"));
+ test_complete = -164;
+ }
+
+ /* Previous state. */
+ if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) {
+ PJ_LOG(3,(THIS_FILE, " error: incorrect prev_state"));
+ test_complete = -165;
+ }
+
+ } else if (tsx->state != PJSIP_TSX_STATE_DESTROYED) {
+
+ PJ_LOG(3,(THIS_FILE, " error: unexpected state"));
+ test_complete = -166;
+
+ }
+
+
+ } else
+ if (pj_strcmp2(&tsx->branch, TEST10_BRANCH_ID)==0 ||
+ pj_strcmp2(&tsx->branch, TEST11_BRANCH_ID)==0 ||
+ pj_strcmp2(&tsx->branch, TEST12_BRANCH_ID)==0)
+ {
+ if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+
+ if (!test_complete)
+ test_complete = 1;
+
+ if (tsx->status_code != PJSIP_SC_TSX_TRANSPORT_ERROR) {
+ PJ_LOG(3,(THIS_FILE," error: incorrect status code"));
+ test_complete = -170;
+ }
+ }
}
}
@@ -612,7 +702,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
TEST1_STATUS_CODE : TEST2_STATUS_CODE;
if (msg->type == PJSIP_REQUEST_MSG) {
- /* On received response, create UAS and respond with final
+ /* On received request, create UAS and respond with final
* response.
*/
pjsip_transaction *tsx;
@@ -652,7 +742,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
/* TEST3_BRANCH_ID tests provisional response. */
if (msg->type == PJSIP_REQUEST_MSG) {
- /* On received response, create UAS and respond with provisional
+ /* On received request, create UAS and respond with provisional
* response, then schedule timer to send final response.
*/
pjsip_transaction *tsx;
@@ -705,7 +795,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
/* TEST6_BRANCH_ID: retransmit last response in COMPLETED state. */
if (msg->type == PJSIP_REQUEST_MSG) {
- /* On received response, create UAS. */
+ /* On received request, create UAS. */
pjsip_transaction *tsx;
status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx);
@@ -786,7 +876,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
*/
if (msg->type == PJSIP_REQUEST_MSG) {
- /* On received response, create UAS. */
+ /* On received request, create UAS. */
pjsip_transaction *tsx;
status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx);
@@ -861,7 +951,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
}
return PJ_TRUE;
- } else if (pj_strcmp2(&branch_param, TEST9_BRANCH_ID)) {
+ } else if (pj_strcmp2(&branch_param, TEST9_BRANCH_ID) == 0) {
/*
* TEST9_BRANCH_ID tests that the retransmission of INVITE final
@@ -870,49 +960,36 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
*/
if (msg->type == PJSIP_REQUEST_MSG) {
- /* On received response, create UAS. */
+ /* On received request, create UAS. */
pjsip_transaction *tsx;
status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx);
if (status != PJ_SUCCESS) {
app_perror(" error: unable to create transaction", status);
- test_complete = -140;
+ test_complete = -150;
return PJ_TRUE;
}
save_key(tsx);
+ send_response(rdata, tsx, TEST9_STATUS_CODE);
- if (pj_strcmp2(&branch_param, TEST7_BRANCH_ID) == 0) {
-
- send_response(rdata, tsx, TEST7_STATUS_CODE);
-
- } else {
-
- send_response(rdata, tsx, TEST8_STATUS_CODE);
-
- }
} else {
- int code;
++recv_count;
- if (pj_strcmp2(&branch_param, TEST7_BRANCH_ID) == 0)
- code = TEST7_STATUS_CODE;
- else
- code = TEST8_STATUS_CODE;
+ if (rdata->msg_info.msg->line.status.code != TEST9_STATUS_CODE) {
+ PJ_LOG(3,(THIS_FILE," error: invalid status code"));
+ test_complete = -151;
+ }
if (recv_count==1) {
-
- if (rdata->msg_info.msg->line.status.code != code) {
- PJ_LOG(3,(THIS_FILE," error: invalid status code"));
- test_complete = -141;
- }
recv_last = rdata->pkt_info.timestamp;
- } else {
+ } else if (recv_count < 5) {
+ /* Let UAS retransmit some messages before we send ACK. */
pj_time_val now;
unsigned msec, msec_expected;
@@ -930,19 +1007,90 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
" error: incorrect retransmission "
"time (%d ms expected, %d ms received",
msec_expected, msec));
- test_complete = -142;
+ test_complete = -152;
}
- if (recv_count > 11) {
- PJ_LOG(3,(THIS_FILE," error: too many responses (%d)",
- recv_count));
- test_complete = -143;
+ recv_last = rdata->pkt_info.timestamp;
+
+ } else if (recv_count == 5) {
+ pjsip_tx_data *tdata;
+ pjsip_sip_uri *uri;
+ pjsip_via_hdr *via;
+
+ status = pjsip_endpt_create_request_from_hdr(
+ endpt, &pjsip_ack_method,
+ rdata->msg_info.to->uri,
+ rdata->msg_info.from,
+ rdata->msg_info.to,
+ NULL,
+ rdata->msg_info.cid,
+ rdata->msg_info.cseq->cseq,
+ NULL,
+ &tdata);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create ACK", status);
+ test_complete = -153;
+ return PJ_TRUE;
}
- recv_last = rdata->pkt_info.timestamp;
+ uri=(pjsip_sip_uri*)pjsip_uri_get_uri(tdata->msg->line.req.uri);
+ uri->transport_param = pj_str("loop-dgram");
+
+ via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
+ via->branch_param = pj_str(TEST9_BRANCH_ID);
+
+ status = pjsip_endpt_send_request_stateless(endpt, tdata,
+ NULL, NULL);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to send ACK", status);
+ test_complete = -154;
+ }
+
+ } else {
+ PJ_LOG(3,(THIS_FILE," error: too many responses (%d)",
+ recv_count));
+ test_complete = -155;
+ }
+
+ }
+ return PJ_TRUE;
+
+ } else if (pj_strcmp2(&branch_param, TEST10_BRANCH_ID) == 0 ||
+ pj_strcmp2(&branch_param, TEST11_BRANCH_ID) == 0 ||
+ pj_strcmp2(&branch_param, TEST12_BRANCH_ID) == 0)
+ {
+ int test_num, code1, code2;
+
+ if (pj_strcmp2(&branch_param, TEST10_BRANCH_ID) == 0)
+ test_num=10, code1 = 100, code2 = 0;
+ else if (pj_strcmp2(&branch_param, TEST11_BRANCH_ID) == 0)
+ test_num=11, code1 = 100, code2 = 200;
+ else
+ test_num=12, code1 = 200, code2 = 0;
+
+ if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
+
+ /* On received response, create UAS. */
+ pjsip_transaction *tsx;
+
+ status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create transaction", status);
+ test_complete = -150;
+ return PJ_TRUE;
}
+ save_key(tsx);
+
+ schedule_send_response(rdata, &tsx_key, code1, 1000);
+
+ if (code2)
+ schedule_send_response(rdata, &tsx_key, code2, 2000);
+
+ } else {
+
}
+
return PJ_TRUE;
}
@@ -1212,6 +1360,113 @@ static int tsx_final_response_retransmission_test(void)
}
+/*****************************************************************************
+ **
+ ** TEST9_BRANCH_ID: retransmission of non-2xx INVITE final response must
+ ** cease when ACK is received
+ **
+ *****************************************************************************
+ */
+static int tsx_ack_test(void)
+{
+ int status;
+
+ PJ_LOG(3,(THIS_FILE,
+ " test9: receiving ACK for non-2xx final response"));
+
+ status = perform_test("sip:129.0.0.1;transport=loop-dgram",
+ "sip:129.0.0.1;transport=loop-dgram",
+ TEST9_BRANCH_ID,
+ 20, /* allow 5 retransmissions */
+ &pjsip_invite_method, 1, 0, 0);
+ if (status != 0)
+ return status;
+
+
+ return 0;
+}
+
+
+
+/*****************************************************************************
+ **
+ ** TEST10_BRANCH_ID: test transport failure in TRYING state.
+ ** TEST11_BRANCH_ID: test transport failure in PROCEEDING state.
+ ** TEST12_BRANCH_ID: test transport failure in CONNECTED state.
+ ** TEST13_BRANCH_ID: test transport failure in CONFIRMED state.
+ **
+ *****************************************************************************
+ */
+static int tsx_transport_failure_test(void)
+{
+ struct test_desc
+ {
+ int transport_delay;
+ int fail_delay;
+ char *branch_id;
+ char *title;
+ } tests[] =
+ {
+ { 0, 10, TEST10_BRANCH_ID, "test10: failed transport in TRYING state (no delay)" },
+ { 50, 10, TEST10_BRANCH_ID, "test10: failed transport in TRYING state (50 ms delay)" },
+ { 0, 1500, TEST11_BRANCH_ID, "test11: failed transport in PROCEEDING state (no delay)" },
+ { 50, 1500, TEST11_BRANCH_ID, "test11: failed transport in PROCEEDING state (50 ms delay)" },
+ { 0, 2500, TEST12_BRANCH_ID, "test12: failed transport in COMPLETED state (no delay)" },
+ { 50, 2500, TEST12_BRANCH_ID, "test12: failed transport in COMPLETED state (50 ms delay)" },
+ };
+ int i, status;
+
+ for (i=0; i<PJ_ARRAY_SIZE(tests); ++i) {
+ pj_time_val fail_time, end_test, now;
+
+ PJ_LOG(3,(THIS_FILE, " %s", tests[i].title));
+ pjsip_loop_set_failure(loop, 0, NULL);
+ pjsip_loop_set_delay(loop, tests[i].transport_delay);
+
+ status = perform_test("sip:129.0.0.1;transport=loop-dgram",
+ "sip:129.0.0.1;transport=loop-dgram",
+ tests[i].branch_id,
+ 0,
+ &pjsip_invite_method, 1, 0, 1);
+ if (status && status != TEST_TIMEOUT_ERROR)
+ return status;
+ if (!status) {
+ PJ_LOG(3,(THIS_FILE, " error: expecting timeout"));
+ return -40;
+ }
+
+ pj_gettimeofday(&fail_time);
+ fail_time.msec += tests[i].fail_delay;
+ pj_time_val_normalize(&fail_time);
+
+ do {
+ pj_time_val interval = { 0, 1 };
+ pj_gettimeofday(&now);
+ pjsip_endpt_handle_events(endpt, &interval);
+ } while (PJ_TIME_VAL_LT(now, fail_time));
+
+ pjsip_loop_set_failure(loop, 1, NULL);
+
+ end_test = now;
+ end_test.sec += 5;
+
+ do {
+ pj_time_val interval = { 0, 1 };
+ pj_gettimeofday(&now);
+ pjsip_endpt_handle_events(endpt, &interval);
+ } while (!test_complete && PJ_TIME_VAL_LT(now, end_test));
+
+ if (test_complete == 0) {
+ PJ_LOG(3,(THIS_FILE, " error: test has timed out"));
+ return -41;
+ }
+
+ if (test_complete != 1)
+ return test_complete;
+ }
+
+ return 0;
+}
/*****************************************************************************
**
@@ -1244,7 +1499,6 @@ int tsx_uas_test(void)
return -4;
}
-#if 0
/* TEST1_BRANCH_ID: Basic 2xx final response.
* TEST2_BRANCH_ID: Basic non-2xx final response.
*/
@@ -1293,7 +1547,22 @@ int tsx_uas_test(void)
if (status != 0)
return status;
-#endif
+ /* TEST9_BRANCH_ID: retransmission of non-2xx INVITE final response must
+ * cease when ACK is received
+ */
+ status = tsx_ack_test();
+ if (status != 0)
+ return status;
+
+ /* TEST10_BRANCH_ID: test transport failure in TRYING state.
+ * TEST11_BRANCH_ID: test transport failure in PROCEEDING state.
+ * TEST12_BRANCH_ID: test transport failure in CONNECTED state.
+ * TEST13_BRANCH_ID: test transport failure in CONFIRMED state.
+ */
+ status = tsx_transport_failure_test();
+ if (status != 0)
+ return status;
+
pjsip_transport_dec_ref(loop);
return 0;
diff --git a/pjsip/src/test-pjsip/txdata_test.c b/pjsip/src/test-pjsip/txdata_test.c
index def46b5a..327ea084 100644
--- a/pjsip/src/test-pjsip/txdata_test.c
+++ b/pjsip/src/test-pjsip/txdata_test.c
@@ -154,7 +154,7 @@ static int core_txdata_test(void)
* We should never do this in real application, as there are many
* many more fields need to be initialized!!
*/
- dummy_rdata.msg_info.call_id = (HFIND(invite->msg, cid, CALL_ID))->id;
+ dummy_rdata.msg_info.cid = HFIND(invite->msg, cid, CALL_ID);
dummy_rdata.msg_info.clen = NULL;
dummy_rdata.msg_info.cseq = HFIND(invite->msg, cseq, CSEQ);
dummy_rdata.msg_info.ctype = NULL;