summaryrefslogtreecommitdiff
path: root/pjsip
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-01-18 23:34:15 +0000
committerBenny Prijono <bennylp@teluu.com>2006-01-18 23:34:15 +0000
commit3a125b4c3b0d5aedb30d5ca8088f6f5b4b33caf5 (patch)
treedd519ac8366416811627ad3ddb9d0ab4e9004594 /pjsip
parentd192c694c8bf65798b042853a23932d08dc806a1 (diff)
Complete tsx layer selftest, implemented authentication framework
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@123 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip')
-rw-r--r--pjsip/build/pjsip_core.dsp6
-rw-r--r--pjsip/build/pjsip_ua.dsp14
-rw-r--r--pjsip/include/pjsip-ua/sip_ua.h58
-rw-r--r--pjsip/include/pjsip/sip_auth.h272
-rw-r--r--pjsip/include/pjsip/sip_errno.h133
-rw-r--r--pjsip/include/pjsip/sip_transport.h2
-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
19 files changed, 1258 insertions, 629 deletions
diff --git a/pjsip/build/pjsip_core.dsp b/pjsip/build/pjsip_core.dsp
index 2fddad05..9a283221 100644
--- a/pjsip/build/pjsip_core.dsp
+++ b/pjsip/build/pjsip_core.dsp
@@ -87,7 +87,7 @@ LIB32=link.exe -lib
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
-SOURCE=..\src\pjsip\sip_auth.c
+SOURCE=..\src\pjsip\sip_auth_client.c
# End Source File
# Begin Source File
@@ -99,6 +99,10 @@ SOURCE=..\src\pjsip\sip_auth_parser.c
# End Source File
# Begin Source File
+SOURCE=..\src\pjsip\sip_auth_server.c
+# End Source File
+# Begin Source File
+
SOURCE=..\src\pjsip\sip_endpoint.c
# End Source File
# Begin Source File
diff --git a/pjsip/build/pjsip_ua.dsp b/pjsip/build/pjsip_ua.dsp
index cd45b10c..4d7ae9e5 100644
--- a/pjsip/build/pjsip_ua.dsp
+++ b/pjsip/build/pjsip_ua.dsp
@@ -107,23 +107,15 @@ SOURCE="..\src\pjsip-ua\sip_ua_private.h"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
-SOURCE=..\src\pjsip_ua.h
+SOURCE="..\include\pjsip-ua\sip_dialog.h"
# End Source File
# Begin Source File
-SOURCE=..\src\pjsip_mod_ua\sip_dialog.h
+SOURCE="..\include\pjsip-ua\sip_regc.h"
# End Source File
# Begin Source File
-SOURCE=..\src\pjsip_mod_ua\sip_reg.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjsip_mod_ua\sip_ua.h
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjsip_mod_ua\sip_ua_private.h
+SOURCE="..\include\pjsip-ua\sip_ua.h"
# End Source File
# End Group
# End Target
diff --git a/pjsip/include/pjsip-ua/sip_ua.h b/pjsip/include/pjsip-ua/sip_ua.h
index 92bf0db1..a1d7c423 100644
--- a/pjsip/include/pjsip-ua/sip_ua.h
+++ b/pjsip/include/pjsip-ua/sip_ua.h
@@ -20,11 +20,10 @@
#define __PJSIP_SIP_UA_H__
/**
- * @file ua.h
- * @brief SIP User Agent Library
+ * @file sip_ua.h
+ * @brief SIP User Agent Module
*/
-#include <pjsip_mod_ua/sip_dialog.h>
PJ_BEGIN_DECL
@@ -40,58 +39,41 @@ PJ_BEGIN_DECL
* User Agent manages the interactions between application and SIP dialogs.
*/
-typedef struct pjsip_dlg_callback pjsip_dlg_callback;
+/** User agent type. */
+typedef pjsip_module pjsip_user_agent;
-/**
- * \brief This structure describes a User Agent instance.
- */
-struct pjsip_user_agent
-{
- pjsip_endpoint *endpt;
- pj_pool_t *pool;
- pj_mutex_t *mutex;
- pj_uint32_t mod_id;
- pj_hash_table_t *dlg_table;
- pjsip_dlg_callback *dlg_cb;
- pj_list dlg_list;
-};
/**
- * Create a new dialog.
+ * Initialize user agent layer and register it to the specified endpoint.
+ *
+ * @param endpt The endpoint where the user agent will be
+ * registered.
+ *
+ * @return PJ_SUCCESS on success.
*/
-PJ_DECL(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua,
- pjsip_role_e role );
-
+PJ_DECL(pj_status_t) pjsip_ua_init(pjsip_endpoint *endpt);
/**
- * Destroy dialog.
- */
-PJ_DECL(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg );
-
-
-/**
- * Register callback to receive dialog notifications.
+ * Get the instance of the user agent.
+ *
+ * @return The user agent module instance.
*/
-PJ_DECL(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua,
- pjsip_dlg_callback *cb );
-
+PJ_DECL(pjsip_user_agent*) pjsip_ua_instance(void);
/**
- * Get the module interface for the UA module.
+ * Destroy the user agent layer.
+ *
+ * @return PJ_SUCCESS on success.
*/
-PJ_DECL(pjsip_module*) pjsip_ua_get_module(void);
+PJ_DECL(pj_status_t) pjsip_ua_destroy(void);
/**
- * Dump user agent state to log file.
- */
-PJ_DECL(void) pjsip_ua_dump( pjsip_user_agent *ua );
-
-/**
* @}
*/
PJ_END_DECL
+
#endif /* __PJSIP_UA_H__ */
diff --git a/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h
index 7d8b9c62..e5f80655 100644
--- a/pjsip/include/pjsip/sip_auth.h
+++ b/pjsip/include/pjsip/sip_auth.h
@@ -29,13 +29,18 @@
PJ_BEGIN_DECL
+
/**
* @defgroup PJSIP_AUTH_API Authorization API's
* @ingroup PJSIP_AUTH
* @{
*/
- /** Type of data in the credential information. */
+/* Length of digest string. */
+#define PJSIP_MD5STRLEN 32
+
+
+/** Type of data in the credential information. */
typedef enum pjsip_cred_data_type
{
PJSIP_CRED_DATA_PLAIN_PASSWD, /**< Plain text password. */
@@ -77,8 +82,8 @@ typedef struct pjsip_cached_auth_hdr
{
PJ_DECL_LIST_MEMBER(struct pjsip_cached_auth_hdr);
- pjsip_method method;
- pjsip_authorization_hdr *hdr;
+ pjsip_method method; /**< To quickly see the method. */
+ pjsip_authorization_hdr *hdr; /**< The cached header. */
} pjsip_cached_auth_hdr;
@@ -93,89 +98,109 @@ typedef struct pjsip_cached_auth_hdr
* Other than that, this structure also keeps the last authorization headers
* that have been sent in the cache list.
*/
-typedef struct pjsip_auth_session
+typedef struct pjsip_cached_auth
{
- PJ_DECL_LIST_MEMBER(struct pjsip_auth_session);
+ PJ_DECL_LIST_MEMBER(struct pjsip_cached_auth);
- pj_str_t realm;
- pj_bool_t is_proxy;
- pjsip_auth_qop_type qop_value;
+ pj_str_t realm; /**< Realm. */
+ pj_bool_t is_proxy; /**< Server type (401/407) */
+ pjsip_auth_qop_type qop_value; /**< qop required by server. */
#if PJSIP_AUTH_QOP_SUPPORT
- pj_uint32_t nc;
- pj_str_t cnonce;
+ pj_uint32_t nc; /**< Nonce count. */
+ pj_str_t cnonce; /**< Cnonce value. */
#endif
#if PJSIP_AUTH_AUTO_SEND_NEXT
- pjsip_www_authenticate_hdr *last_chal;
+ pjsip_www_authenticate_hdr *last_chal; /**< Last challenge seen. */
#endif
#if PJSIP_AUTH_HEADER_CACHING
- pjsip_cached_auth_hdr cached_hdr;
+ pjsip_cached_auth_hdr cached_hdr;/**< List of cached header for
+ each method. */
#endif
-} pjsip_auth_session;
+} pjsip_cached_auth;
/**
- * Create authorization header for the specified credential.
- * Application calls this function to create Authorization or Proxy-Authorization
- * header after receiving WWW-Authenticate or Proxy-Authenticate challenge
- * (normally in 401/407 response).
- * If authorization session argument is specified, this function will update
- * the session with the updated information if required (e.g. to update
- * nonce-count when qop is "auth" or "auth-int"). This function will also
- * save the authorization header in the session's cached header list.
- *
- * @param req_pool Pool to allocate new header for the request.
- * @param hdr The WWW-Authenticate or Proxy-Authenticate found in
- * the response.
- * @param uri The URI for which authorization is targeted to.
- * @param cred_info The credential to be used for authentication.
- * @param method The method.
- * @param sess_pool Session pool to update session or to allocate message
- * in the cache. May be NULL if auth_sess is NULL.
- * @param auth_sess If not NULL, this specifies the specific authentication
- * session to be used or updated.
- *
- * @return The Authorization header, which can be typecasted to
- * Proxy-Authorization.
- */
-PJ_DECL(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);
+ * This structure describes client authentication sessions. It keeps
+ * all the information needed to authorize the client against all downstream
+ * servers.
+ */
+typedef struct pjsip_auth_clt_sess
+{
+ pj_pool_t *pool; /**< Pool to use. */
+ pjsip_endpoint *endpt; /**< Endpoint where this belongs. */
+ unsigned cred_cnt; /**< Number of credentials. */
+ pjsip_cred_info *cred_info; /**< Array of credential information*/
+ pjsip_cached_auth cached_auth; /**< Cached authorization info. */
+
+} pjsip_auth_clt_sess;
+
/**
- * Verify digest in the authorization request.
+ * Type of function to lookup credential for the specified name.
*
- * @param hdr The incoming Authorization/Proxy-Authorization header.
- * @param method The method.
- * @param password The plaintext password to verify.
+ * @param pool Pool to initialize the credential info.
+ * @param realm Realm to find the account.
+ * @param acc_name Account name to look for.
+ * @param cred_info The structure to put the credential when it's found.
*
- * @return Non-zero if authorization succeed.
+ * @return The function MUST return PJ_SUCCESS when it found
+ * a correct credential for the specified account and
+ * realm. Otherwise it may return PJSIP_EAUTHACCNOTFOUND
+ * or PJSIP_EAUTHACCDISABLED.
*/
-PJ_DECL(pj_bool_t) pjsip_auth_verify( const pjsip_authorization_hdr *hdr,
- const pj_str_t *method,
- const pjsip_cred_info *cred_info );
+typedef pj_status_t pjsip_auth_lookup_cred( pj_pool_t *pool,
+ const pj_str_t *realm,
+ const pj_str_t *acc_name,
+ pjsip_cred_info *cred_info );
+/** Flag to specify that server is a proxy. */
+#define PJSIP_AUTH_SRV_IS_PROXY 1
/**
- * This function can be used to find credential information which matches
- * the specified realm.
+ * This structure describes server authentication information.
+ */
+typedef struct pjsip_auth_srv
+{
+ pj_str_t realm; /**< Realm to serve. */
+ pj_bool_t is_proxy; /**< Will issue 407 instead of 401 */
+ pjsip_auth_lookup_cred *lookup; /**< Lookup function. */
+
+} pjsip_auth_srv;
+
+
+/**
+ * Initialize client authentication session data structure, and set the
+ * session to use pool for its subsequent memory allocation. The argument
+ * options should be set to zero for this PJSIP version.
+ *
+ * @param sess The client authentication session.
+ * @param endpt Endpoint where this session belongs.
+ * @param pool Pool to use.
+ * @param options Must be zero.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_auth_clt_init( pjsip_auth_clt_sess *sess,
+ pjsip_endpoint *endpt,
+ pj_pool_t *pool,
+ unsigned options);
+
+
+
+/**
+ * Set the credentials to be used during the session. This will duplicate
+ * the specified credentials using client authentication's pool.
*
- * @param count Number of credentials in the parameter.
- * @param cred The array of credentials.
- * @param realm Realm to search.
- * @param scheme Authentication scheme.
+ * @param sess The client authentication session.
+ * @param cred_cnt Number of credentials.
+ * @param c Array of credentials.
*
- * @return The credential which matches the specified realm.
+ * @return PJ_SUCCESS on success.
*/
-PJ_DECL(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 );
+PJ_DECL(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess,
+ int cred_cnt,
+ const pjsip_cred_info *c);
/**
@@ -194,20 +219,14 @@ PJ_DECL(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count,
* are not set, this function will do nothing. The stack then will only send
* Authorization/Proxy-Authorization to respond 401/407 response.
*
- * @param sess_pool Session level pool, where memory will be allocated from
- * for data that persists across requests (e.g. caching).
+ * @param sess The client authentication session.
* @param tdata The request message to be initialized.
- * @param sess_list List of authorization sessions that have been recorded.
- * @param cred_count Number of credentials.
- * @param cred_info Array of credentials.
*
- * @return Zero if successfull.
+ * @return PJ_SUCCESS if successfull.
*/
-PJ_DECL(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[]);
+PJ_DECL(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess,
+ pjsip_tx_data *tdata );
+
/**
* Call this function when a transaction failed with 401 or 407 response.
@@ -215,33 +234,104 @@ PJ_DECL(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool,
* authentication challenge found in the response message, and add the
* new authorization header in the authorization cache.
*
- * Note that upon return the reference counter of the transmit data
- * will be incremented.
+ * Note that upon return the reference counter of the new transmit data
+ * will be set to 1.
*
- * @param endpt Endpoint.
- * @param pool The pool to allocate memory for new cred_info.
- * @param cached_list Cached authorization headers.
- * @param cred_count Number of credentials.
- * @param cred_info Array of credentials to use.
- * @param tdata The original request message, which normally can be
- * retrieved from tsx->last_tx.
+ * @param sess The client authentication session.
* @param rdata The response message containing 401/407 status.
+ * @param old_request The original request message, which will be re-
+ * created with authorization info.
+ * @param new_request Pointer to receive new request message which
+ * will contain all required authorization headers.
*
- * @return New transmit data buffer, or NULL if the dialog
- * can not respond to the authorization challenge.
+ * @return PJ_SUCCESS if new request can be successfully
+ * created to respond all the authentication
+ * challenges.
*/
-PJ_DECL(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_DECL(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 );
+
+/**
+ * Initialize server authorization session data structure to serve the
+ * specified realm and to use lookup_func function to look for the credential
+ * info.
+ *
+ * @param pool Pool used to initialize the authentication server.
+ * @param auth_srv The authentication server structure.
+ * @param realm Realm to be served by the server.
+ * @param lookup Account lookup function.
+ * @param options Options, bitmask of:
+ * - PJSIP_AUTH_SRV_IS_PROXY: to specify that the server
+ * will authorize clients as a proxy server (instead of
+ * as UAS), which means that Proxy-Authenticate will
+ * be used instead of WWW-Authenticate.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(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 );
+
+
+/**
+ * Request the authorization server framework to verify the authorization
+ * information in the specified request in rdata.
+ *
+ * @param auth_srv The server authentication structure.
+ * @param rdata Incoming request to be authenticated.
+ * @param status_code When not null, it will be filled with suitable
+ * status code to be sent to the client.
+ *
+ * @return PJ_SUCCESS if request is successfully authenticated.
+ * Otherwise the function may return one of the
+ * following error codes:
+ * - PJSIP_EAUTHNOAUTH
+ * - PJSIP_EINVALIDAUTHSCHEME
+ * - PJSIP_EAUTHACCNOTFOUND
+ * - PJSIP_EAUTHACCDISABLED
+ * - PJSIP_EAUTHINVALIDREALM
+ * - PJSIP_EAUTHINVALIDDIGEST
+ */
+PJ_DECL(pj_status_t) pjsip_auth_srv_verify( pjsip_auth_srv *auth_srv,
+ pjsip_rx_data *rdata,
+ int *status_code );
+
+
+/**
+ * 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.
+ *
+ * @param auth_srv The server authentication structure.
+ * @param qop Optional qop value.
+ * @param nonce Optional nonce value.
+ * @param opaque Optional opaque value.
+ * @param stale Stale indication.
+ * @param tdata The outgoing response message. The response must have
+ * 401 or 407 response code.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(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);
+
/**
* @}
*/
+
PJ_END_DECL
+
#endif /* __PJSIP_AUTH_SIP_AUTH_H__ */
diff --git a/pjsip/include/pjsip/sip_errno.h b/pjsip/include/pjsip/sip_errno.h
index 7c3dbbd2..9c04c755 100644
--- a/pjsip/include/pjsip/sip_errno.h
+++ b/pjsip/include/pjsip/sip_errno.h
@@ -79,7 +79,7 @@ PJ_DECL(pj_str_t) pjsip_strerror( pj_status_t status, char *buffer,
#define PJSIP_ERRNO_START_PJSIP (PJSIP_ERRNO_START + 1000)
/************************************************************
- * GENERIC SIP ERRORS
+ * GENERIC/GENERAL SIP ERRORS
***********************************************************/
/**
* @hideinitializer
@@ -96,6 +96,11 @@ PJ_DECL(pj_str_t) pjsip_strerror( pj_status_t status, char *buffer,
* SIP stack is shutting down.
*/
#define PJSIP_ESHUTDOWN (PJSIP_ERRNO_START_PJSIP + 3) /* 171003 */
+/**
+ * @hideinitializer
+ * SIP object is not initialized.
+ */
+#define PJSIP_ENOTINITIALIZED (PJSIP_ERRNO_START_PJSIP + 4) /* 171004 */
/************************************************************
@@ -108,64 +113,77 @@ PJ_DECL(pj_str_t) pjsip_strerror( pj_status_t status, char *buffer,
#define PJSIP_EINVALIDMSG (PJSIP_ERRNO_START_PJSIP + 20) /* 171020 */
/**
* @hideinitializer
- * Unsupported URL scheme.
+ * Expecting request message.
+ */
+#define PJSIP_ENOTREQUESTMSG (PJSIP_ERRNO_START_PJSIP + 21) /* 171021 */
+/**
+ * @hideinitializer
+ * Expecting response message.
*/
-#define PJSIP_EINVALIDSCHEME (PJSIP_ERRNO_START_PJSIP + 21) /* 171021 */
+#define PJSIP_ENOTRESPONSEMSG (PJSIP_ERRNO_START_PJSIP + 22) /* 171022 */
/**
* @hideinitializer
* Message too long. See also PJSIP_ERXOVERFLOW.
*/
-#define PJSIP_EMSGTOOLONG (PJSIP_ERRNO_START_PJSIP + 22) /* 171022 */
+#define PJSIP_EMSGTOOLONG (PJSIP_ERRNO_START_PJSIP + 23) /* 171023 */
/**
* @hideinitializer
* Message not completely received.
*/
-#define PJSIP_EPARTIALMSG (PJSIP_ERRNO_START_PJSIP + 23) /* 171023 */
+#define PJSIP_EPARTIALMSG (PJSIP_ERRNO_START_PJSIP + 24) /* 171024 */
+
/**
* @hideinitializer
- * Missing Request-URI.
+ * Status code is invalid.
*/
-#define PJSIP_EMISSINGREQURI (PJSIP_ERRNO_START_PJSIP + 24) /* 171024 */
+#define PJSIP_EINVALIDSTATUS (PJSIP_ERRNO_START_PJSIP + 30) /* 171030 */
+
/**
* @hideinitializer
- * Missing required header(s).
+ * Unsupported URL scheme.
*/
-#define PJSIP_EMISSINGHDR (PJSIP_ERRNO_START_PJSIP + 25) /* 171025 */
+#define PJSIP_EINVALIDSCHEME (PJSIP_ERRNO_START_PJSIP + 40) /* 171040 */
/**
* @hideinitializer
- * Missing message body.
+ * Missing Request-URI.
*/
-#define PJSIP_EMISSINGBODY (PJSIP_ERRNO_START_PJSIP + 26) /* 171026 */
+#define PJSIP_EMISSINGREQURI (PJSIP_ERRNO_START_PJSIP + 41) /* 171041 */
/**
* @hideinitializer
- * Invalid Via header in response (sent-by, etc).
+ * Invalid request URI.
*/
-#define PJSIP_EINVALIDVIA (PJSIP_ERRNO_START_PJSIP + 27) /* 171027 */
+#define PJSIP_EINVALIDREQURI (PJSIP_ERRNO_START_PJSIP + 42) /* 171042 */
/**
* @hideinitializer
- * Multiple Via headers in response.
+ * URI is too long.
*/
-#define PJSIP_EMULTIPLEVIA (PJSIP_ERRNO_START_PJSIP + 28) /* 171028 */
+#define PJSIP_EURITOOLONG (PJSIP_ERRNO_START_PJSIP + 43) /* 171043 */
+
/**
* @hideinitializer
- * Invalid request URI.
+ * Missing required header(s).
*/
-#define PJSIP_EINVALIDREQURI (PJSIP_ERRNO_START_PJSIP + 29) /* 171029 */
+#define PJSIP_EMISSINGHDR (PJSIP_ERRNO_START_PJSIP + 50) /* 171050 */
/**
* @hideinitializer
- * Expecting request message.
+ * Invalid header field.
*/
-#define PJSIP_ENOTREQUESTMSG (PJSIP_ERRNO_START_PJSIP + 30) /* 171030 */
+#define PJSIP_EINVALIDHDR (PJSIP_ERRNO_START_PJSIP + 51) /* 171051 */
/**
* @hideinitializer
- * Expecting response message.
+ * Invalid Via header in response (sent-by, etc).
*/
-#define PJSIP_ENOTRESPONSEMSG (PJSIP_ERRNO_START_PJSIP + 31) /* 171031 */
+#define PJSIP_EINVALIDVIA (PJSIP_ERRNO_START_PJSIP + 52) /* 171052 */
/**
* @hideinitializer
- * Invalid header field.
+ * Multiple Via headers in response.
+ */
+#define PJSIP_EMULTIPLEVIA (PJSIP_ERRNO_START_PJSIP + 53) /* 171053 */
+/**
+ * @hideinitializer
+ * Missing message body.
*/
-#define PJSIP_EINVALIDHDR (PJSIP_ERRNO_START_PJSIP + 32) /* 171032 */
+#define PJSIP_EMISSINGBODY (PJSIP_ERRNO_START_PJSIP + 54) /* 171054 */
/************************************************************
@@ -175,23 +193,23 @@ PJ_DECL(pj_str_t) pjsip_strerror( pj_status_t status, char *buffer,
* @hideinitializer
* Unsupported transport type.
*/
-#define PJSIP_EUNSUPTRANSPORT (PJSIP_ERRNO_START_PJSIP + 40) /* 171040 */
+#define PJSIP_EUNSUPTRANSPORT (PJSIP_ERRNO_START_PJSIP + 60) /* 171060 */
/**
* @hideinitializer
* Buffer is being sent, operation still pending.
*/
-#define PJSIP_EPENDINGTX (PJSIP_ERRNO_START_PJSIP + 41) /* 171041 */
+#define PJSIP_EPENDINGTX (PJSIP_ERRNO_START_PJSIP + 61) /* 171061 */
/**
* @hideinitializer
* Rx buffer overflow. See also PJSIP_EMSGTOOLONG.
*/
-#define PJSIP_ERXOVERFLOW (PJSIP_ERRNO_START_PJSIP + 42) /* 171042 */
+#define PJSIP_ERXOVERFLOW (PJSIP_ERRNO_START_PJSIP + 62) /* 171062 */
/**
* @hideinitializer
* This is not really an error, it just informs application that
* transmit data has been deleted on return of pjsip_tx_data_dec_ref().
*/
-#define PJSIP_EBUFDESTROYED (PJSIP_ERRNO_START_PJSIP + 43) /* 171043 */
+#define PJSIP_EBUFDESTROYED (PJSIP_ERRNO_START_PJSIP + 63) /* 171063 */
/************************************************************
@@ -201,7 +219,7 @@ PJ_DECL(pj_str_t) pjsip_strerror( pj_status_t status, char *buffer,
* @hideinitializer
* Transaction has just been destroyed.
*/
-#define PJSIP_ETSXDESTROYED (PJSIP_ERRNO_START_PJSIP + 60) /* 171060 */
+#define PJSIP_ETSXDESTROYED (PJSIP_ERRNO_START_PJSIP + 70) /* 171070 */
/************************************************************
@@ -269,6 +287,65 @@ PJ_DECL(pj_str_t) pjsip_strerror( pj_status_t status, char *buffer,
#define PJSIP_ECMPHEADERPARAM (PJSIP_ERRNO_START_PJSIP + 91) /* 171091 */
+/************************************************************
+ * AUTHENTICATION FRAMEWORK
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * Credential failed to authenticate.
+ */
+#define PJSIP_EFAILEDCREDENTIAL (PJSIP_ERRNO_START_PJSIP + 100) /* 171100 */
+/**
+ * @hideinitializer
+ * No suitable credential.
+ */
+#define PJSIP_ENOCREDENTIAL (PJSIP_ERRNO_START_PJSIP + 101) /* 171101 */
+/**
+ * @hideinitializer
+ * Invalid/unsupported algorithm.
+ */
+#define PJSIP_EINVALIDALGORITHM (PJSIP_ERRNO_START_PJSIP + 102) /* 171102 */
+/**
+ * @hideinitializer
+ * Invalid/unsupported qop.
+ */
+#define PJSIP_EINVALIDQOP (PJSIP_ERRNO_START_PJSIP + 103) /* 171103 */
+/**
+ * @hideinitializer
+ * Invalid/unsupported authentication scheme.
+ */
+#define PJSIP_EINVALIDAUTHSCHEME (PJSIP_ERRNO_START_PJSIP + 104)/* 171104 */
+/**
+ * @hideinitializer
+ * No previous challenge.
+ */
+#define PJSIP_EAUTHNOPREVCHAL (PJSIP_ERRNO_START_PJSIP + 105) /* 171105 */
+/**
+ * @hideinitializer
+ * No authorization is found.
+ */
+#define PJSIP_EAUTHNOAUTH (PJSIP_ERRNO_START_PJSIP + 106) /* 171106 */
+/**
+ * @hideinitializer
+ * Account not found.
+ */
+#define PJSIP_EAUTHACCNOTFOUND (PJSIP_ERRNO_START_PJSIP + 107) /* 171107 */
+/**
+ * @hideinitializer
+ * Account is disabled.
+ */
+#define PJSIP_EAUTHACCDISABLED (PJSIP_ERRNO_START_PJSIP + 108) /* 171108 */
+/**
+ * @hideinitializer
+ * Invalid realm.
+ */
+#define PJSIP_EAUTHINVALIDREALM (PJSIP_ERRNO_START_PJSIP + 109) /* 171109 */
+/**
+ * @hideinitializer
+ * Invalid digest.
+ */
+#define PJSIP_EAUTHINVALIDDIGEST (PJSIP_ERRNO_START_PJSIP+110) /* 171110 */
+
PJ_END_DECL
diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h
index f2cce0f6..f8968f90 100644
--- a/pjsip/include/pjsip/sip_transport.h
+++ b/pjsip/include/pjsip/sip_transport.h
@@ -218,7 +218,7 @@ struct pjsip_rx_data
char *info;
/** The Call-ID header as found in the message. */
- pj_str_t call_id;
+ pjsip_cid_hdr *cid;
/** The From header as found in the message. */
pjsip_from_hdr *from;
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;