- Added option to send empty Authorization header in outgoing requests

- When UAS has sent answer in reliable 1xx, do not put SDP in 2xx
- Handle the case when UPDATE is challenged with 401/407
- Obsolete --service-route option in pjsua



git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@1561 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Benny Prijono 2007-11-08 09:24:30 +00:00
parent 3f302ff3f0
commit 48ab2b70e3
11 changed files with 280 additions and 22 deletions

View File

@ -117,6 +117,7 @@ static void usage(void)
puts (" --app-log-level=N Set log max level for stdout display (default=4)");
puts ("");
puts ("SIP Account options:");
puts (" --use-ims Enable 3GPP/IMS related settings on this account");
puts (" --registrar=url Set the URL of registrar server");
puts (" --id=url Set the URL of local ID (used in From header)");
puts (" --contact=url Optionally override the Contact information");
@ -128,7 +129,6 @@ static void usage(void)
puts (" --password=string Set authentication password");
puts (" --publish Send presence PUBLISH for this account");
puts (" --use-100rel Require reliable provisional response (100rel)");
puts (" --service-route Enable Service-Route processing");
puts (" --next-cred Add another credentials");
puts ("");
puts ("SIP Account Control:");
@ -372,7 +372,7 @@ static pj_status_t parse_args(int argc, char *argv[],
OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO,
OPT_LOCAL_PORT, OPT_IP_ADDR, OPT_PROXY, OPT_OUTBOUND_PROXY,
OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
OPT_100REL, OPT_SERVICE_ROUTE, OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
OPT_100REL, OPT_USE_IMS, OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
OPT_NAMESERVER, OPT_STUN_DOMAIN, OPT_STUN_SRV,
OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP,
@ -409,7 +409,7 @@ static pj_status_t parse_args(int argc, char *argv[],
{ "reg-timeout",1, 0, OPT_REG_TIMEOUT},
{ "publish", 0, 0, OPT_PUBLISH},
{ "use-100rel", 0, 0, OPT_100REL},
{ "service-route", 0, 0, OPT_SERVICE_ROUTE},
{ "use-ims", 0, 0, OPT_USE_IMS},
{ "id", 1, 0, OPT_ID},
{ "contact", 1, 0, OPT_CONTACT},
{ "realm", 1, 0, OPT_REALM},
@ -636,8 +636,8 @@ static pj_status_t parse_args(int argc, char *argv[],
cfg->cfg.require_100rel = PJ_TRUE;
break;
case OPT_SERVICE_ROUTE: /* Service-Route processing */
cur_acc->enable_service_route = PJ_TRUE;
case OPT_USE_IMS: /* Activate IMS settings */
cur_acc->auth_pref.initial_auth = PJ_TRUE;
break;
case OPT_ID: /* id */
@ -667,7 +667,7 @@ static pj_status_t parse_args(int argc, char *argv[],
case OPT_USERNAME: /* Default authentication user */
cur_acc->cred_info[cur_acc->cred_count].username = pj_str(pj_optarg);
cur_acc->cred_info[cur_acc->cred_count].scheme = pj_str("digest");
cur_acc->cred_info[cur_acc->cred_count].scheme = pj_str("Digest");
break;
case OPT_REALM: /* Default authentication realm. */
@ -992,9 +992,34 @@ static pj_status_t parse_args(int argc, char *argv[],
cfg->acc_cnt++;
for (i=0; i<cfg->acc_cnt; ++i) {
if (cfg->acc_cfg[i].cred_info[cfg->acc_cfg[i].cred_count].username.slen)
pjsua_acc_config *acfg = &cfg->acc_cfg[i];
if (acfg->cred_info[acfg->cred_count].username.slen)
{
cfg->acc_cfg[i].cred_count++;
acfg->cred_count++;
}
/* When IMS mode is enabled for the account, verify that settings
* are okay.
*/
/* For now we check if IMS mode is activated by looking if
* initial_auth is set.
*/
if (acfg->auth_pref.initial_auth && acfg->cred_count) {
/* Realm must point to the real domain */
if (*acfg->cred_info[0].realm.ptr=='*') {
PJ_LOG(1,(THIS_FILE,
"Error: cannot use '*' as realm with IMS"));
return PJ_EINVAL;
}
/* Username for authentication must be in a@b format */
if (strchr(acfg->cred_info[0].username.ptr, '@')==0) {
PJ_LOG(1,(THIS_FILE,
"Error: Username for authentication must "
"be in user@domain format with IMS"));
return PJ_EINVAL;
}
}
}
@ -2685,7 +2710,7 @@ void console_app_main(const pj_str_t *uri_to_call)
acc_cfg.id = pj_str(id);
acc_cfg.reg_uri = pj_str(registrar);
acc_cfg.cred_count = 1;
acc_cfg.cred_info[0].scheme = pj_str("digest");
acc_cfg.cred_info[0].scheme = pj_str("Digest");
acc_cfg.cred_info[0].realm = pj_str(realm);
acc_cfg.cred_info[0].username = pj_str(uname);
acc_cfg.cred_info[0].data_type = 0;

View File

@ -188,6 +188,17 @@ PJ_DECL(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
int count,
const pjsip_cred_info cred[] );
/**
* Set authentication preference.
*
* @param regc The registration structure.
* @param pref Authentication preference.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjsip_regc_set_prefs( pjsip_regc *regc,
const pjsip_auth_clt_pref *pref);
/**
* Set route set to be used for outgoing requests.
*

View File

@ -182,6 +182,28 @@ typedef struct pjsip_cached_auth
} pjsip_cached_auth;
/**
* This structure describes client authentication session preference.
* The preference can be set by calling #pjsip_auth_clt_set_prefs().
*/
typedef struct pjsip_auth_clt_pref
{
/**
* If this flag is set, the authentication client framework will
* send an empty Authorization header in each initial request.
* Default is no.
*/
pj_bool_t initial_auth;
/**
* Specify the algorithm to use when empty Authorization header
* is to be sent for each initial request (see above)
*/
pj_str_t algorithm;
} pjsip_auth_clt_pref;
/**
* This structure describes client authentication sessions. It keeps
* all the information needed to authorize the client against all downstream
@ -191,6 +213,7 @@ typedef struct pjsip_auth_clt_sess
{
pj_pool_t *pool; /**< Pool to use. */
pjsip_endpoint *endpt; /**< Endpoint where this belongs. */
pjsip_auth_clt_pref pref; /**< Preference/options. */
unsigned cred_cnt; /**< Number of credentials. */
pjsip_cred_info *cred_info; /**< Array of credential information*/
pjsip_cached_auth cached_auth; /**< Cached authorization info. */
@ -288,6 +311,29 @@ PJ_DECL(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess,
const pjsip_cred_info *c);
/**
* Set the preference for the client authentication session.
*
* @param sess The client authentication session.
* @param p Preference.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjsip_auth_clt_set_prefs(pjsip_auth_clt_sess *sess,
const pjsip_auth_clt_pref *p);
/**
* Get the preference for the client authentication session.
*
* @param sess The client authentication session.
* @param p Pointer to receive the preference.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjsip_auth_clt_get_prefs(pjsip_auth_clt_sess *sess,
pjsip_auth_clt_pref *p);
/**
* Initialize new request message with authorization headers.
* This function will put Authorization/Proxy-Authorization headers to the

View File

@ -1840,9 +1840,16 @@ typedef struct pjsua_acc_config
/**
* If this flag is set, the presence information of this account will
* be PUBLISH-ed to the server where the account belongs.
*
* Default: PJ_FALSE
*/
pj_bool_t publish_enabled;
/**
* Authentication preference.
*/
pjsip_auth_clt_pref auth_pref;
/**
* Optional PIDF tuple ID for outgoing PUBLISH and NOTIFY. If this value
* is not specified, a random string will be used.
@ -1890,11 +1897,6 @@ typedef struct pjsua_acc_config
*/
pj_str_t proxy[PJSUA_ACC_MAX_PROXIES];
/**
* Enable Service-Route processing for this account.
*/
pj_bool_t enable_service_route;
/**
* Optional interval for registration, in seconds. If the value is zero,
* default interval will be used (PJSUA_REG_INTERVAL, 300 seconds).

View File

@ -1563,9 +1563,19 @@ static pj_status_t process_answer( pjsip_inv_session *inv,
/* Include SDP when it's available for 2xx and 18x (but not 180) response.
* Subsequent response will include this SDP.
*
* Note note:
* - When offer/answer has been completed in reliable 183, we MUST NOT
* send SDP in 2xx response. So if we don't have SDP to send, clear
* the SDP in the message body ONLY if 100rel is active in this
* session.
*/
if (sdp) {
tdata->msg->body = create_sdp_body(tdata->pool, sdp);
} else {
if (inv->options & PJSIP_INV_REQUIRE_100REL) {
tdata->msg->body = NULL;
}
}
@ -2226,8 +2236,32 @@ static void inv_handle_update_response( pjsip_inv_session *inv,
struct tsx_inv_data *tsx_inv_data = NULL;
pj_status_t status = -1;
/* Process 2xx response */
/* Handle 401/407 challenge. */
if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
(tsx->status_code == 401 || tsx->status_code == 407)) {
pjsip_tx_data *tdata;
status = pjsip_auth_clt_reinit_req( &inv->dlg->auth_sess,
e->body.tsx_state.src.rdata,
tsx->last_tx,
&tdata);
if (status != PJ_SUCCESS) {
/* Does not have proper credentials.
* End the session anyway.
*/
inv_set_cause(inv, PJSIP_SC_OK, NULL);
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
} else {
/* Re-send BYE. */
status = pjsip_inv_send_msg(inv, tdata);
}
/* Process 2xx response */
} else if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
tsx->status_code/100 == 2 &&
e->body.tsx_state.src.rdata->msg_info.msg->body)
{

View File

@ -307,6 +307,13 @@ PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
return pjsip_auth_clt_set_credentials(&regc->auth_sess, count, cred);
}
PJ_DEF(pj_status_t) pjsip_regc_set_prefs( pjsip_regc *regc,
const pjsip_auth_clt_pref *pref)
{
PJ_ASSERT_RETURN(regc && pref, PJ_EINVAL);
return pjsip_auth_clt_set_prefs(&regc->auth_sess, pref);
}
PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
const pjsip_route_hdr *route_set)
{

View File

@ -532,6 +532,36 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess,
}
/*
* Set the preference for the client authentication session.
*/
PJ_DEF(pj_status_t) pjsip_auth_clt_set_prefs(pjsip_auth_clt_sess *sess,
const pjsip_auth_clt_pref *p)
{
PJ_ASSERT_RETURN(sess && p, PJ_EINVAL);
pj_memcpy(&sess->pref, p, sizeof(*p));
pj_strdup(sess->pool, &sess->pref.algorithm, &p->algorithm);
//if (sess->pref.algorithm.slen == 0)
// sess->pref.algorithm = pj_str("md5");
return PJ_SUCCESS;
}
/*
* Get the preference for the client authentication session.
*/
PJ_DEF(pj_status_t) pjsip_auth_clt_get_prefs(pjsip_auth_clt_sess *sess,
pjsip_auth_clt_pref *p)
{
PJ_ASSERT_RETURN(sess && p, PJ_EINVAL);
pj_memcpy(p, &sess->pref, sizeof(pjsip_auth_clt_pref));
return PJ_SUCCESS;
}
/*
* Create Authorization/Proxy-Authorization response header based on the challege
* in WWW-Authenticate/Proxy-Authenticate header.
@ -698,6 +728,22 @@ static pj_status_t new_auth_for_req( pjsip_tx_data *tdata,
#endif
/* Find credential in list of (Proxy-)Authorization headers */
static pjsip_authorization_hdr* get_header_for_realm(const pjsip_hdr *hdr_list,
const pj_str_t *realm)
{
pjsip_authorization_hdr *h;
h = (pjsip_authorization_hdr*)hdr_list->next;
while (h != (pjsip_authorization_hdr*)hdr_list) {
if (pj_stricmp(&h->credential.digest.realm, realm)==0)
return h;
h = h->next;
}
return NULL;
}
/* Initialize outgoing request. */
PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess,
@ -705,12 +751,16 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess,
{
const pjsip_method *method;
pjsip_cached_auth *auth;
pjsip_hdr added;
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);
/* Init list */
pj_list_init(&added);
/* Get the method. */
method = &tdata->msg->line.req.method;
@ -728,7 +778,8 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess,
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);
//pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
pj_list_push_back(&added, hauth);
break;
}
entry = entry->next;
@ -776,13 +827,80 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess,
if (status != PJ_SUCCESS)
return status;
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
//pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
pj_list_push_back(&added, hauth);
}
# endif /* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */
auth = auth->next;
}
if (sess->pref.initial_auth == PJ_FALSE) {
pjsip_hdr *h;
/* Don't want to send initial empty Authorization header, so
* just send whatever available in the list (maybe empty).
*/
h = added.next;
while (h != &added) {
pjsip_hdr *next = h->next;
pjsip_msg_add_hdr(tdata->msg, h);
h = next;
}
} else {
/* For each realm, add either the cached authorization header
* or add an empty authorization header.
*/
unsigned i;
char *uri_str;
int len;
uri_str = pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE);
len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, tdata->msg->line.req.uri,
uri_str, PJSIP_MAX_URL_SIZE);
if (len < 1 || len >= PJSIP_MAX_URL_SIZE)
return PJSIP_EURITOOLONG;
for (i=0; i<sess->cred_cnt; ++i) {
pjsip_cred_info *c = &sess->cred_info[i];
pjsip_authorization_hdr *h;
h = get_header_for_realm(&added, &c->realm);
if (h) {
pj_list_erase(h);
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)h);
} else {
enum { HDRLEN = 256 };
const pj_str_t hname = pj_str("Authorization");
pj_str_t hval;
pjsip_generic_string_hdr *hs;
char *hdr;
hdr = pj_pool_alloc(tdata->pool, HDRLEN);
len = pj_ansi_snprintf(
hdr, HDRLEN,
"%.*s username=\"%.*s\", realm=\"%.*s\","
" nonce=\"\", uri=\"%s\",%s%.*s%s response=\"\"",
(int)c->scheme.slen, c->scheme.ptr,
(int)c->username.slen, c->username.ptr,
(int)c->realm.slen, c->realm.ptr,
uri_str,
(sess->pref.algorithm.slen ? " algorithm=" : ""),
(int)sess->pref.algorithm.slen, sess->pref.algorithm.ptr,
(sess->pref.algorithm.slen ? "," : ""));
PJ_ASSERT_RETURN(len>0 && len<HDRLEN, PJ_ETOOBIG);
hval.ptr = hdr;
hval.slen = len;
hs = pjsip_generic_string_hdr_create(tdata->pool, &hname,
&hval);
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hs);
}
}
}
return PJ_SUCCESS;
}

View File

@ -544,10 +544,6 @@ void update_service_route(pjsua_acc *acc, pjsip_rx_data *rdata)
pjsip_uri *uri[PJSUA_ACC_MAX_PROXIES];
unsigned i, uri_cnt = 0, rcnt;
/* Skip processing is enable_service_route is not set */
if (!acc->cfg.enable_service_route)
return;
/* Find and parse Service-Route headers */
for (;;) {
char saved;
@ -916,6 +912,9 @@ static pj_status_t pjsua_regc_init(int acc_id)
pjsip_regc_set_credentials( acc->regc, acc->cred_cnt, acc->cred);
}
/* Set authentication preference */
pjsip_regc_set_prefs(acc->regc, &acc->cfg.auth_pref);
/* Set route-set
*/
if (!pj_list_empty(&acc->route_set)) {

View File

@ -383,6 +383,8 @@ PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
acc->cred_cnt, acc->cred);
}
/* Set authentication preference */
pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
/* Create initial INVITE: */
@ -666,6 +668,10 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
pjsua_var.acc[acc_id].cred);
}
/* Set preference */
pjsip_auth_clt_set_prefs(&dlg->auth_sess,
&pjsua_var.acc[acc_id].cfg.auth_pref);
/* Create invite session: */
status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
if (status != PJ_SUCCESS) {

View File

@ -321,6 +321,9 @@ static void im_callback(void *token, pjsip_event *e)
pjsua_var.acc[im_data->acc_id].cred_cnt,
pjsua_var.acc[im_data->acc_id].cred);
pjsip_auth_clt_set_prefs(&auth,
&pjsua_var.acc[im_data->acc_id].cfg.auth_pref);
status = pjsip_auth_clt_reinit_req(&auth, rdata, tsx->last_tx,
&tdata);
if (status == PJ_SUCCESS) {
@ -417,6 +420,9 @@ static void typing_callback(void *token, pjsip_event *e)
pjsua_var.acc[im_data->acc_id].cred_cnt,
pjsua_var.acc[im_data->acc_id].cred);
pjsip_auth_clt_set_prefs(&auth,
&pjsua_var.acc[im_data->acc_id].cfg.auth_pref);
status = pjsip_auth_clt_reinit_req(&auth, rdata, tsx->last_tx,
&tdata);
if (status == PJ_SUCCESS) {

View File

@ -571,8 +571,9 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
return PJ_TRUE;
}
/* Set credentials. */
/* Set credentials and preference. */
pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred);
pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
/* Init callback: */
pj_bzero(&pres_cb, sizeof(pres_cb));
@ -1173,6 +1174,9 @@ static void subscribe_buddy_presence(unsigned index)
acc->cred_cnt, acc->cred);
}
/* Set authentication preference */
pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref);
pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
status = pjsip_pres_initiate(buddy->sub, -1, &tdata);