diff options
author | Benny Prijono <bennylp@teluu.com> | 2006-02-08 22:44:25 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2006-02-08 22:44:25 +0000 |
commit | e88c16ac16d93a586406bc5503d3110f264c5263 (patch) | |
tree | d56410f7f00fc914ed26c1c0442d72040210f146 | |
parent | 66f9158fa3c12ebd3b2d317cf42e461e0b86a6aa (diff) |
Finished invite session UAS implementation
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@160 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r-- | pjsip/include/pjsip/sip_dialog.h | 11 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_event.h | 8 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_transaction.h | 22 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_types.h | 4 | ||||
-rw-r--r-- | pjsip/src/pjsip-ua/sip_inv.c | 148 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_dialog.c | 229 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_transaction.c | 41 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_ua_layer.c | 34 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_util.c | 3 | ||||
-rw-r--r-- | pjsip/src/pjsua/main.c | 92 | ||||
-rw-r--r-- | pjsip/src/pjsua/pjsua_core.c | 96 | ||||
-rw-r--r-- | pjsip/src/test-pjsip/tsx_uas_test.c | 6 |
12 files changed, 453 insertions, 241 deletions
diff --git a/pjsip/include/pjsip/sip_dialog.h b/pjsip/include/pjsip/sip_dialog.h index e63ba6d1..ede79b26 100644 --- a/pjsip/include/pjsip/sip_dialog.h +++ b/pjsip/include/pjsip/sip_dialog.h @@ -49,6 +49,15 @@ typedef struct pjsip_dlg_party /** + * Dialog state. + */ +enum pjsip_dialog_state +{ + PJSIP_DIALOG_STATE_NULL, + PJSIP_DIALOG_STATE_ESTABLISHED, +}; + +/** * This structure describes the dialog structure. */ struct pjsip_dialog @@ -67,7 +76,7 @@ struct pjsip_dialog void *dlg_set; /* Dialog's session properties. */ - pj_bool_t established;/**< Dialog is established? */ + enum pjsip_dialog_state state; /**< Dialog state. */ pjsip_uri *target; /**< Current target. */ pjsip_dlg_party local; /**< Local party info. */ pjsip_dlg_party remote; /**< Remote party info. */ diff --git a/pjsip/include/pjsip/sip_event.h b/pjsip/include/pjsip/sip_event.h index 268a60e7..caac953f 100644 --- a/pjsip/include/pjsip/sip_event.h +++ b/pjsip/include/pjsip/sip_event.h @@ -124,7 +124,6 @@ struct pjsip_event struct { pjsip_tx_data *tdata; /**< The transmit data buffer. */ - pjsip_transaction *tsx; /**< The transaction. */ } tx_msg; @@ -139,7 +138,6 @@ struct pjsip_event struct { pjsip_rx_data *rdata; /**< The receive data buffer. */ - pjsip_transaction *tsx; /**< The transaction. */ } rx_msg; /** User event. */ @@ -178,20 +176,18 @@ struct pjsip_event /** * Init tx msg event. */ -#define PJSIP_EVENT_INIT_TX_MSG(event,ptsx,ptdata) \ +#define PJSIP_EVENT_INIT_TX_MSG(event,ptdata) \ do { \ (event).type = PJSIP_EVENT_TX_MSG; \ - (event).body.tx_msg.tsx = ptsx; \ (event).body.tx_msg.tdata = ptdata; \ } while (0) /** * Init rx msg event. */ -#define PJSIP_EVENT_INIT_RX_MSG(event,ptsx,prdata) \ +#define PJSIP_EVENT_INIT_RX_MSG(event,prdata) \ do { \ (event).type = PJSIP_EVENT_RX_MSG; \ - (event).body.rx_msg.tsx = ptsx; \ (event).body.rx_msg.rdata = prdata; \ } while (0) diff --git a/pjsip/include/pjsip/sip_transaction.h b/pjsip/include/pjsip/sip_transaction.h index 712fa8ad..eece1be0 100644 --- a/pjsip/include/pjsip/sip_transaction.h +++ b/pjsip/include/pjsip/sip_transaction.h @@ -175,7 +175,9 @@ PJ_DECL(pj_status_t) pjsip_tsx_create_uac( pjsip_module *tsx_user, /** * Create, initialize, and register a new transaction as UAS from the - * specified incoming request in \c rdata. + * specified incoming request in \c rdata. After calling this function, + * application MUST call #pjsip_tsx_recv_msg() so that transaction + * moves from state NULL. * * @param tsx_user Module to be registered as transaction user of the new * transaction, which will receive notification from the @@ -189,6 +191,24 @@ PJ_DECL(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user, pjsip_rx_data *rdata, pjsip_transaction **p_tsx ); + +/** + * Call this function to manually feed a message to the transaction. + * For UAS transaction, application MUST call this function after + * UAS transaction has been created. + * + * This function SHOULD only be called to pass initial request message + * to UAS transaction. Before this function returns, on_tsx_state() + * callback of the transaction user will be called. If response message + * is passed to this function, then on_rx_response() will also be called + * before on_tsx_state(). + * + * @param tsx The transaction. + * @param rdata The message. + */ +PJ_DECL(void) pjsip_tsx_recv_msg( pjsip_transaction *tsx, + pjsip_rx_data *rdata); + /** * Transmit message in tdata with this transaction. It is possible to * pass NULL in tdata for UAC transaction, which in this case the last diff --git a/pjsip/include/pjsip/sip_types.h b/pjsip/include/pjsip/sip_types.h index fc79f95a..a77f2550 100644 --- a/pjsip/include/pjsip/sip_types.h +++ b/pjsip/include/pjsip/sip_types.h @@ -133,6 +133,10 @@ typedef pjsip_module pjsip_user_agent; */ typedef struct pjsip_dialog pjsip_dialog; +/** + * Dialog state (sip_dialog.h). + */ +enum pjsip_dialog_state pjsip_dialog_state; /** * Transaction role. diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c index a05576c3..2a5e14ae 100644 --- a/pjsip/src/pjsip-ua/sip_inv.c +++ b/pjsip/src/pjsip-ua/sip_inv.c @@ -109,6 +109,21 @@ static pj_status_t mod_inv_unload(void) } /* + * Set session state. + */ +void inv_set_state(pjsip_inv_session *inv, pjsip_inv_state state, + pjsip_event *e) +{ + inv->state = state; + if (mod_inv.cb.on_state_changed) + (*mod_inv.cb.on_state_changed)(inv, e); + + if (inv->state == PJSIP_INV_STATE_DISCONNECTED) + pjsip_dlg_dec_session(inv->dlg); +} + + +/* * Send ACK for 2xx response. */ static pj_status_t inv_send_ack(pjsip_inv_session *inv, pjsip_rx_data *rdata) @@ -145,11 +160,16 @@ static pj_status_t inv_send_ack(pjsip_inv_session *inv, pjsip_rx_data *rdata) static pj_bool_t mod_inv_on_rx_request(pjsip_rx_data *rdata) { pjsip_method *method; + pjsip_dialog *dlg; + pjsip_inv_session *inv; /* Only wants to receive request from a dialog. */ - if (pjsip_rdata_get_dlg(rdata) == NULL) + dlg = pjsip_rdata_get_dlg(rdata); + if (dlg == NULL) return PJ_FALSE; + inv = dlg->mod_data[mod_inv.mod.id]; + /* Report to dialog that we handle INVITE, CANCEL, BYE, ACK. * If we need to send response, it will be sent in the state * handlers. @@ -158,12 +178,23 @@ static pj_bool_t mod_inv_on_rx_request(pjsip_rx_data *rdata) if (method->id == PJSIP_INVITE_METHOD || method->id == PJSIP_CANCEL_METHOD || - method->id == PJSIP_ACK_METHOD || method->id == PJSIP_BYE_METHOD) { return PJ_TRUE; } + /* On receipt ACK request, when state is CONNECTING, + * move state to CONFIRMED. + */ + if (method->id == PJSIP_ACK_METHOD && inv && + inv->state == PJSIP_INV_STATE_CONFIRMED) + { + pjsip_event event; + + PJSIP_EVENT_INIT_RX_MSG(event, rdata); + inv_set_state(inv, PJSIP_INV_STATE_CONFIRMED, &event); + } + return PJ_FALSE; } @@ -702,7 +733,7 @@ PJ_DEF(pj_status_t) pjsip_inv_create_uas( pjsip_dialog *dlg, inv->pool = dlg->pool; inv->role = PJSIP_ROLE_UAS; - inv->state = PJSIP_INV_STATE_INCOMING; + inv->state = PJSIP_INV_STATE_NULL; inv->dlg = dlg; inv->options = options; @@ -1066,19 +1097,6 @@ PJ_DEF(pj_status_t) pjsip_inv_send_msg( pjsip_inv_session *inv, } -void inv_set_state(pjsip_inv_session *inv, pjsip_inv_state state, - pjsip_event *e) -{ - inv->state = state; - if (mod_inv.cb.on_state_changed) - (*mod_inv.cb.on_state_changed)(inv, e); - - if (inv->state == PJSIP_INV_STATE_DISCONNECTED) - pjsip_dlg_dec_session(inv->dlg); -} - - - /* * Respond to incoming CANCEL request. */ @@ -1166,6 +1184,52 @@ static void inv_respond_incoming_bye( pjsip_inv_session *inv, } /* + * Respond to BYE request. + */ +static void inv_handle_bye_response( pjsip_inv_session *inv, + pjsip_transaction *tsx, + pjsip_rx_data *rdata, + pjsip_event *e ) +{ + pj_status_t status; + + if (e->body.tsx_state.type != PJSIP_EVENT_RX_MSG) { + inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); + return; + } + + /* Handle 401/407 challenge. */ + if (tsx->status_code == 401 || tsx->status_code == 407) { + + pjsip_tx_data *tdata; + + status = pjsip_auth_clt_reinit_req( &inv->dlg->auth_sess, + rdata, + tsx->last_tx, + &tdata); + + if (status != PJ_SUCCESS) { + + /* Does not have proper credentials. + * End the session anyway. + */ + inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); + + } else { + /* Re-send BYE. */ + status = pjsip_inv_send_msg(inv, tdata, NULL ); + } + + } else { + + /* End the session. */ + + inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); + } + +} + +/* * State NULL is before anything is sent/received. */ static void inv_on_state_null( pjsip_inv_session *inv, pjsip_event *e) @@ -1197,6 +1261,11 @@ static void inv_on_state_null( pjsip_inv_session *inv, pjsip_event *e) case PJSIP_TSX_STATE_TRYING: inv_set_state(inv, PJSIP_INV_STATE_INCOMING, e); break; + case PJSIP_TSX_STATE_PROCEEDING: + inv_set_state(inv, PJSIP_INV_STATE_INCOMING, e); + if (tsx->status_code > 100) + inv_set_state(inv, PJSIP_INV_STATE_EARLY, e); + break; default: pj_assert(!"Unexpected state"); } @@ -1463,6 +1532,8 @@ static void inv_on_state_connecting( pjsip_inv_session *inv, pjsip_event *e) switch (tsx->state) { case PJSIP_TSX_STATE_CONFIRMED: + if (tsx->status_code/100 == 2) + inv_set_state(inv, PJSIP_INV_STATE_CONFIRMED, e); break; case PJSIP_TSX_STATE_TERMINATED: @@ -1495,7 +1566,18 @@ static void inv_on_state_connecting( pjsip_inv_session *inv, pjsip_event *e) inv_respond_incoming_bye( inv, tsx, e->body.tsx_state.src.rdata, e ); + } else if (tsx->method.id == PJSIP_BYE_METHOD && + tsx->role == PJSIP_ROLE_UAC && + tsx->state == PJSIP_TSX_STATE_COMPLETED) + { + + /* + * Outgoing BYE + */ + inv_handle_bye_response( inv, tsx, e->body.tsx_state.src.rdata, e); + } + } /* @@ -1513,39 +1595,15 @@ static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e) tsx->role == PJSIP_ROLE_UAC && tsx->state == PJSIP_TSX_STATE_COMPLETED) { + /* - * Outgoing BYE. + * Outgoing BYE */ - pj_status_t status; - - /* Handle 401/407 challenge. */ - if (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_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); - - } else { - /* Re-send BYE. */ - status = pjsip_inv_send_msg(inv, tdata, NULL ); - } - - } else { - - /* End the session. */ + if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) + inv_handle_bye_response( inv, tsx, e->body.tsx_state.src.rdata, e); + else inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); - } } else if (tsx->method.id == PJSIP_BYE_METHOD && diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c index c6ad91ab..f41b5d53 100644 --- a/pjsip/src/pjsip/sip_dialog.c +++ b/pjsip/src/pjsip/sip_dialog.c @@ -74,6 +74,7 @@ static pj_status_t create_dialog( pjsip_user_agent *ua, pj_sprintf(dlg->obj_name, "dlg%p", dlg); dlg->ua = ua; dlg->endpt = endpt; + dlg->state = PJSIP_DIALOG_STATE_NULL; status = pj_mutex_create_recursive(pool, "dlg%p", &dlg->mutex); if (status != PJ_SUCCESS) @@ -374,6 +375,9 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua, PJ_TODO(DIALOG_APP_TIMER); + /* Feed the first request to the transaction. */ + pjsip_tsx_recv_msg(tsx, rdata); + /* Done. */ *p_dlg = dlg; return PJ_SUCCESS; @@ -407,6 +411,11 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg, PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG, PJSIP_ENOTRESPONSEMSG); + /* Status code MUST be 1xx (but not 100), or 2xx */ + status = rdata->msg_info.msg->line.status.code; + PJ_ASSERT_RETURN( (status/100==1 && status!=100) || + (status/100==2), PJ_EBUG); + /* To tag must present in the response. */ PJ_ASSERT_RETURN(rdata->msg_info.to->tag.slen != 0, PJSIP_EMISSINGTAG); @@ -444,6 +453,15 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg, /* Initial role is UAC. */ dlg->role = PJSIP_ROLE_UAC; + /* Dialog state depends on the response. */ + status = rdata->msg_info.msg->line.status.code/100; + if (status == 1 || status == 2) + dlg->state = PJSIP_DIALOG_STATE_ESTABLISHED; + else { + pj_assert(!"Invalid status code"); + dlg->state = PJSIP_DIALOG_STATE_NULL; + } + /* Secure? */ dlg->secure = PJSIP_URI_SCHEME_IS_SIPS(dlg->target); @@ -462,7 +480,7 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg, r = r->next; } - /* Init client authentication session. */ + /* Clone client authentication session. */ status = pjsip_auth_clt_clone(dlg->pool, &dlg->auth_sess, &first_dlg->auth_sess); if (status != PJ_SUCCESS) @@ -849,99 +867,54 @@ on_error: return status; } - -/* - * Create response. - */ -PJ_DEF(pj_status_t) pjsip_dlg_create_response( pjsip_dialog *dlg, - pjsip_rx_data *rdata, - int st_code, - const pj_str_t *st_text, - pjsip_tx_data **p_tdata) +/* Add standard headers for certain types of response */ +static void dlg_beautify_response(pjsip_dialog *dlg, + int st_code, + pjsip_tx_data *tdata) { - pj_status_t status; pjsip_cseq_hdr *cseq; - pjsip_tx_data *tdata; int st_class; + const pjsip_hdr *c_hdr; + pjsip_hdr *hdr; - /* Create generic response. */ - status = pjsip_endpt_create_response(dlg->endpt, - rdata, st_code, st_text, &tdata); - if (status != PJ_SUCCESS) - return status; - - /* Lock the dialog. */ - pj_mutex_lock(dlg->mutex); - - /* Special treatment for 2xx response to request that establishes - * dialog. - * - * RFC 3261 Section 12.1.1 - * - * When a UAS responds to a request with a response that establishes - * a dialog (such as a 2xx to INVITE): - * - MUST copy all Record-Route header field values from the request - * into the response (including the URIs, URI parameters, and any - * Record-Route header field parameters, whether they are known or - * unknown to the UAS) and MUST maintain the order of those values. - * - The Contact header field contains an address where the UAS would - * like to be contacted for subsequent requests in the dialog. - * - * Also from Table 3, page 119. - */ cseq = PJSIP_MSG_CSEQ_HDR(tdata->msg); pj_assert(cseq != NULL); st_class = st_code / 100; - if (cseq->cseq == dlg->remote.first_cseq && - (st_class==1 || st_class==2) && st_code != 100) - { - pjsip_hdr *rr, *hdr; - - /* Duplicate Record-Route header from the request. */ - rr = (pjsip_hdr*) rdata->msg_info.record_route; - while (rr) { - hdr = pjsip_hdr_clone(tdata->pool, rr); - pjsip_msg_add_hdr(tdata->msg, hdr); - - rr = rr->next; - if (rr == &rdata->msg_info.msg->hdr) - break; - rr = pjsip_msg_find_hdr(rdata->msg_info.msg, - PJSIP_H_RECORD_ROUTE, rr); - } - } - - /* Contact header. */ + /* Contact, Allow, Supported header. */ if (pjsip_method_creates_dialog(&cseq->method)) { /* Add Contact header for 1xx, 2xx, 3xx and 485 response. */ if (st_class==2 || st_class==3 || (st_class==1 && st_code != 100) || st_code==485) { - /* Add contact header. */ - pjsip_hdr *hdr = pjsip_hdr_clone(tdata->pool, dlg->local.contact); - pjsip_msg_add_hdr(tdata->msg, hdr); + /* Add contact header only if one is not present. */ + if (pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL) == 0) { + hdr = pjsip_hdr_clone(tdata->pool, dlg->local.contact); + pjsip_msg_add_hdr(tdata->msg, hdr); + } } /* Add Allow header in 2xx and 405 response. */ - if (st_class==2 || st_code==405) { - const pjsip_hdr *c_hdr; + if ((st_class==2 || st_code==405) && + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ALLOW, NULL)==NULL) + { c_hdr = pjsip_endpt_get_capability(dlg->endpt, PJSIP_H_ALLOW, NULL); if (c_hdr) { - pjsip_hdr *hdr = pjsip_hdr_clone(tdata->pool, c_hdr); + hdr = pjsip_hdr_clone(tdata->pool, c_hdr); pjsip_msg_add_hdr(tdata->msg, hdr); } } /* Add Supported header in 2xx response. */ - if (st_class==2) { - const pjsip_hdr *c_hdr; + if (st_class==2 && + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL)==NULL) + { c_hdr = pjsip_endpt_get_capability(dlg->endpt, PJSIP_H_SUPPORTED, NULL); if (c_hdr) { - pjsip_hdr *hdr = pjsip_hdr_clone(tdata->pool, c_hdr); + hdr = pjsip_hdr_clone(tdata->pool, c_hdr); pjsip_msg_add_hdr(tdata->msg, hdr); } } @@ -949,7 +922,7 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_response( pjsip_dialog *dlg, } /* Add To tag in all responses except 100 */ - if (st_code != 100 && rdata->msg_info.to->tag.slen == 0) { + if (st_code != 100) { pjsip_to_hdr *to; to = PJSIP_MSG_TO_HDR(tdata->msg); @@ -957,6 +930,34 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_response( pjsip_dialog *dlg, to->tag = dlg->local.info->tag; } +} + + +/* + * Create response. + */ +PJ_DEF(pj_status_t) pjsip_dlg_create_response( pjsip_dialog *dlg, + pjsip_rx_data *rdata, + int st_code, + const pj_str_t *st_text, + pjsip_tx_data **p_tdata) +{ + pj_status_t status; + pjsip_tx_data *tdata; + + /* Create generic response. + * This will initialize response's Via, To, From, Call-ID, CSeq + * and Record-Route headers from the request. + */ + status = pjsip_endpt_create_response(dlg->endpt, + rdata, st_code, st_text, &tdata); + if (status != PJ_SUCCESS) + return status; + + /* Lock the dialog. */ + pj_mutex_lock(dlg->mutex); + + dlg_beautify_response(dlg, st_code, tdata); /* Unlock the dialog. */ pj_mutex_unlock(dlg->mutex); @@ -980,6 +981,9 @@ PJ_DEF(pj_status_t) pjsip_dlg_modify_response( pjsip_dialog *dlg, PJSIP_ENOTRESPONSEMSG); PJ_ASSERT_RETURN(st_code >= 100 && st_code <= 699, PJ_EINVAL); + pj_mutex_lock(dlg->mutex); + + /* Replace status code and reason */ tdata->msg->line.status.code = st_code; if (st_text) { pj_strdup(tdata->pool, &tdata->msg->line.status.reason, st_text); @@ -987,6 +991,17 @@ PJ_DEF(pj_status_t) pjsip_dlg_modify_response( pjsip_dialog *dlg, tdata->msg->line.status.reason = *pjsip_get_status_text(st_code); } + dlg_beautify_response(dlg, st_code, tdata); + + + /* Must add reference counter, since tsx_send_msg() will decrement it */ + pjsip_tx_data_add_ref(tdata); + + /* Force to re-print message. */ + pjsip_tx_data_invalidate_msg(tdata); + + pj_mutex_unlock(dlg->mutex); + return PJ_SUCCESS; } @@ -1057,18 +1072,22 @@ PJ_DEF(pj_status_t) pjsip_dlg_respond( pjsip_dialog *dlg, void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata ) { pj_status_t status; - pjsip_transaction *tsx; + pjsip_transaction *tsx = NULL; unsigned i; /* Lock the dialog. */ pj_mutex_lock(dlg->mutex); /* Check CSeq */ - if (rdata->msg_info.cseq->cseq <= dlg->remote.cseq) { + if (rdata->msg_info.cseq->cseq <= dlg->remote.cseq && + rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD && + rdata->msg_info.msg->line.req.method.id != PJSIP_CANCEL_METHOD) + { /* Invalid CSeq. * Respond statelessly with 500 (Internal Server Error) */ pj_mutex_unlock(dlg->mutex); + pj_assert(pjsip_rdata_get_tsx(rdata) == NULL); pjsip_endpt_respond_stateless(dlg->endpt, rdata, 500, NULL, NULL, NULL); return; @@ -1078,14 +1097,16 @@ void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata ) dlg->remote.cseq = rdata->msg_info.cseq->cseq; /* Create UAS transaction for this request. */ - status = pjsip_tsx_create_uas(dlg->ua, rdata, &tsx); - PJ_ASSERT_ON_FAIL(status==PJ_SUCCESS,{goto on_return;}); + if (pjsip_rdata_get_tsx(rdata) == NULL) { + status = pjsip_tsx_create_uas(dlg->ua, rdata, &tsx); + PJ_ASSERT_ON_FAIL(status==PJ_SUCCESS,{goto on_return;}); - /* Put this dialog in the transaction data. */ - tsx->mod_data[dlg->ua->id] = dlg; + /* Put this dialog in the transaction data. */ + tsx->mod_data[dlg->ua->id] = dlg; - /* Add transaction count. */ - ++dlg->tsx_count; + /* Add transaction count. */ + ++dlg->tsx_count; + } /* Report the request to dialog usages. */ for (i=0; i<dlg->usage_cnt; ++i) { @@ -1100,28 +1121,9 @@ void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata ) break; } - if (i==dlg->usage_cnt) { - pjsip_tx_data *tdata; - - PJ_LOG(4,(dlg->obj_name, - "%s is unhandled by dialog usages. " - "Dialog will response with 500 (Internal Server Error)", - pjsip_rx_data_get_info(rdata))); - status = pjsip_endpt_create_response(dlg->endpt, - rdata, - PJSIP_SC_INTERNAL_SERVER_ERROR, - NULL, &tdata); - if (status == PJ_SUCCESS) - status = pjsip_tsx_send_msg(tsx, tdata); - - if (status != PJ_SUCCESS) { - char errmsg[PJSIP_ERR_MSG_SIZE]; - pj_strerror(status, errmsg, sizeof(errmsg)); - PJ_LOG(4,(dlg->obj_name,"Error sending %s: %s", - pjsip_tx_data_get_info(tdata), errmsg)); - pjsip_tsx_terminate(tsx, 500); - } - } + /* Feed the first request to the transaction. */ + if (tsx) + pjsip_tsx_recv_msg(tsx, rdata); on_return: /* Unlock dialog. */ @@ -1142,25 +1144,25 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata ) /* Check that rdata already has dialog in mod_data. */ pj_assert(pjsip_rdata_get_dlg(rdata) == dlg); - /* Update the remote tag if it is different. */ - if (pj_strcmp(&dlg->remote.info->tag, &rdata->msg_info.to->tag) != 0) { - - pj_strdup(dlg->pool, &dlg->remote.info->tag, &rdata->msg_info.to->tag); - - /* No need to update remote's tag_hval since its never used. */ - } - /* Keep the response's status code */ res_code = rdata->msg_info.msg->line.status.code; - /* When we receive response that establishes dialog, update the route - * set and dialog target. + /* When we receive response that establishes dialog, update To tag, + * route set and dialog target. */ - if (!dlg->established && + if (dlg->state == PJSIP_DIALOG_STATE_NULL && pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) && (res_code > 100 && res_code < 300) && rdata->msg_info.to->tag.slen) { + pjsip_hdr *hdr, *end_hdr; + pjsip_contact_hdr *contact; + + /* Update To tag. */ + pj_strdup(dlg->pool, &dlg->remote.info->tag, &rdata->msg_info.to->tag); + /* No need to update remote's tag_hval since its never used. */ + + /* RFC 3271 Section 12.1.2: * The route set MUST be set to the list of URIs in the Record-Route * header field from the response, taken in reverse order and @@ -1169,9 +1171,6 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata ) * empty set. This route set, even if empty, overrides any pre-existing * route set for future requests in this dialog. */ - pjsip_hdr *hdr, *end_hdr; - pjsip_contact_hdr *contact; - pj_list_init(&dlg->route_set); end_hdr = &rdata->msg_info.msg->hdr; @@ -1194,7 +1193,7 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata ) dlg->target = dlg->remote.contact->uri; } - dlg->established = 1; + dlg->state = PJSIP_DIALOG_STATE_ESTABLISHED; } /* Update remote target (again) when receiving 2xx response messages @@ -1251,6 +1250,9 @@ void pjsip_dlg_on_tsx_state( pjsip_dialog *dlg, if (tsx->state == PJSIP_TSX_STATE_TERMINATED) --dlg->tsx_count; + /* Increment session to prevent usages from destroying dialog. */ + ++dlg->sess_count; + /* Pass to dialog usages. */ for (i=0; i<dlg->usage_cnt; ++i) { @@ -1260,6 +1262,9 @@ void pjsip_dlg_on_tsx_state( pjsip_dialog *dlg, (*dlg->usage[i]->on_tsx_state)(tsx, e); } + /* Decrement temporary session. */ + --dlg->sess_count; + if (tsx->state == PJSIP_TSX_STATE_TERMINATED && dlg->tsx_count == 0 && dlg->sess_count == 0) { diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c index a84e096b..6dae0370 100644 --- a/pjsip/src/pjsip/sip_transaction.c +++ b/pjsip/src/pjsip/sip_transaction.c @@ -170,8 +170,6 @@ static void tsx_resched_retransmission( pjsip_transaction *tsx ); static pj_status_t tsx_retransmit( pjsip_transaction *tsx, int resched); static int tsx_send_msg( pjsip_transaction *tsx, pjsip_tx_data *tdata); -static void tsx_on_rx_msg( pjsip_transaction *tsx, - pjsip_rx_data *rdata ); /* State handlers for UAC, indexed by state */ @@ -697,12 +695,12 @@ static pj_bool_t mod_tsx_layer_on_rx_request(pjsip_rx_data *rdata) /* Race condition! * Transaction may gets deleted before we have chance to lock it - * in tsx_on_rx_msg(). + * in pjsip_tsx_recv_msg(). */ PJ_TODO(FIX_RACE_CONDITION_HERE); /* Pass the message to the transaction. */ - tsx_on_rx_msg(tsx, rdata ); + pjsip_tsx_recv_msg(tsx, rdata ); return PJ_TRUE; } @@ -745,12 +743,12 @@ static pj_bool_t mod_tsx_layer_on_rx_response(pjsip_rx_data *rdata) /* Race condition! * Transaction may gets deleted before we have chance to lock it - * in tsx_on_rx_msg(). + * in pjsip_tsx_recv_msg(). */ PJ_TODO(FIX_RACE_CONDITION_HERE); /* Pass the message to the transaction. */ - tsx_on_rx_msg(tsx, rdata ); + pjsip_tsx_recv_msg(tsx, rdata ); return PJ_TRUE; } @@ -965,12 +963,7 @@ static void tsx_set_state( pjsip_transaction *tsx, pj_assert(rdata != NULL); - if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG && - tsx->tsx_user->on_rx_request) - { - (*tsx->tsx_user->on_rx_request)(rdata); - - } else if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG && + if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG && tsx->tsx_user->on_rx_response) { (*tsx->tsx_user->on_rx_response)(rdata); @@ -1239,11 +1232,11 @@ PJ_DEF(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user, tsx->transaction_key.ptr)); - /* Begin with state TRYING. + /* Begin with state NULL. * Manually set-up the state becase we don't want to call the callback. */ - tsx->state = PJSIP_TSX_STATE_TRYING; - tsx->state_handler = &tsx_on_state_trying; + tsx->state = PJSIP_TSX_STATE_NULL; + tsx->state_handler = &tsx_on_state_null; /* Get response address. */ status = pjsip_get_response_addr( tsx->pool, rdata, &tsx->res_addr ); @@ -1327,7 +1320,7 @@ PJ_DEF(pj_status_t) pjsip_tsx_send_msg( pjsip_transaction *tsx, pjsip_tx_data_get_info(tdata), state_str[tsx->state])); - PJSIP_EVENT_INIT_TX_MSG(event, tsx, tdata); + PJSIP_EVENT_INIT_TX_MSG(event, tdata); /* Dispatch to transaction. */ lock_tsx(tsx, &lck); @@ -1349,7 +1342,8 @@ PJ_DEF(pj_status_t) pjsip_tsx_send_msg( pjsip_transaction *tsx, * This function is called by endpoint when incoming message for the * transaction is received. */ -static void tsx_on_rx_msg( pjsip_transaction *tsx, pjsip_rx_data *rdata) +PJ_DEF(void) pjsip_tsx_recv_msg( pjsip_transaction *tsx, + pjsip_rx_data *rdata) { pjsip_event event; struct tsx_lock_data lck; @@ -1362,7 +1356,7 @@ static void tsx_on_rx_msg( pjsip_transaction *tsx, pjsip_rx_data *rdata) rdata->endpt_info.mod_data[mod_tsx_layer.mod.id] = tsx; /* Init event. */ - PJSIP_EVENT_INIT_RX_MSG(event, tsx, rdata); + PJSIP_EVENT_INIT_RX_MSG(event, rdata); /* Dispatch to transaction. */ lock_tsx(tsx, &lck); @@ -1716,11 +1710,12 @@ static pj_status_t tsx_on_state_null( pjsip_transaction *tsx, if (tsx->role == PJSIP_ROLE_UAS) { - /* UAS doesn't have STATE_NULL. - * State has moved from NULL after transaction is initialized. - */ - pj_assert(!"Bug bug bug!!"); - return PJ_EBUG; + /* Set state to Trying. */ + pj_assert(event->type == PJSIP_EVENT_RX_MSG && + event->body.rx_msg.rdata->msg_info.msg->type == + PJSIP_REQUEST_MSG); + tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING, PJSIP_EVENT_RX_MSG, + event->body.rx_msg.rdata); } else { pjsip_tx_data *tdata; diff --git a/pjsip/src/pjsip/sip_ua_layer.c b/pjsip/src/pjsip/sip_ua_layer.c index c9bcf958..ada14ad2 100644 --- a/pjsip/src/pjsip/sip_ua_layer.c +++ b/pjsip/src/pjsip/sip_ua_layer.c @@ -474,9 +474,14 @@ static pj_bool_t mod_ua_on_rx_request(pjsip_rx_data *rdata) pj_str_t *from_tag; pjsip_dialog *dlg; - /* Optimized path: bail out early if request doesn't have To tag */ - if (rdata->msg_info.to->tag.slen == 0) + /* Optimized path: bail out early if request is not CANCEL and it doesn't + * have To tag + */ + if (rdata->msg_info.to->tag.slen == 0 && + rdata->msg_info.msg->line.req.method.id != PJSIP_CANCEL_METHOD) + { return PJ_FALSE; + } /* Lock user agent before looking up the dialog hash table. */ pj_mutex_lock(mod_ua.mutex); @@ -624,17 +629,15 @@ static pj_bool_t mod_ua_on_rx_response(pjsip_rx_data *rdata) // rdata->msg_info.cseq->cseq == dlg_set->dlg_list.next->local.first_cseq) if (rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD) { - //pj_str_t *to_tag = &rdata->msg_info.to->tag; + + int st_code = rdata->msg_info.msg->line.status.code; + pj_str_t *to_tag = &rdata->msg_info.to->tag; /* Must hold UA mutex before accessing dialog set. */ pj_mutex_lock(mod_ua.mutex); dlg = dlg_set->dlg_list.next; - /* Forking handling is temporarily disabled. */ - PJ_TODO(UA_LAYER_HANDLE_FORKING); - -#if 0 while (dlg != (pjsip_dialog*)&dlg_set->dlg_list) { /* If there is dialog with no remote tag (i.e. dialog has not @@ -652,9 +655,12 @@ static pj_bool_t mod_ua_on_rx_response(pjsip_rx_data *rdata) } /* If no dialog with matching remote tag is found, this must be - * a forked response. + * a forked response. Respond to this ONLY when response is non-100 + * provisional response OR a 2xx response. */ - if (dlg == (pjsip_dialog*)&dlg_set->dlg_list) { + if (dlg == (pjsip_dialog*)&dlg_set->dlg_list && + ((st_code/100==1 && st_code!=100) || st_code/100==2)) + { /* Report to application about forked condition. * Application can either create a dialog or ignore the response. */ @@ -677,8 +683,16 @@ static pj_bool_t mod_ua_on_rx_response(pjsip_rx_data *rdata) return PJ_TRUE; } + + } else if (dlg == (pjsip_dialog*)&dlg_set->dlg_list) { + + /* For 100 or non-2xx response which has different To tag, + * pass the response to the first dialog. + */ + + dlg = dlg_set->dlg_list.next; + } -#endif /* Done with the dialog set. */ pj_mutex_unlock(mod_ua.mutex); diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c index 9b48d195..12d140e2 100644 --- a/pjsip/src/pjsip/sip_util.c +++ b/pjsip/src/pjsip/sip_util.c @@ -1303,6 +1303,9 @@ PJ_DEF(pj_status_t) pjsip_endpt_respond( pjsip_endpoint *endpt, return status; } + /* Feed the request to the transaction. */ + pjsip_tsx_recv_msg(tsx, rdata); + /* Send the message. */ status = pjsip_tsx_send_msg(tsx, tdata); if (status != PJ_SUCCESS) { diff --git a/pjsip/src/pjsua/main.c b/pjsip/src/pjsua/main.c index 68140d4a..f802799f 100644 --- a/pjsip/src/pjsua/main.c +++ b/pjsip/src/pjsua/main.c @@ -18,15 +18,9 @@ */ #include "pjsua.h" #include "getopt.h" +#include <stdlib.h> -/* For debugging, disable threading. */ -//#define NO_WORKER_THREAD - -#ifdef NO_WORKER_THREAD -#include <conio.h> -#endif - #define THIS_FILE "main.c" static pjsip_inv_session *inv_session; @@ -68,35 +62,45 @@ static void ui_help(void) puts(""); puts("Console keys:"); puts(" m Make a call"); + puts(" a Answer incoming call"); puts(" h Hangup current call"); puts(" q Quit"); puts(""); fflush(stdout); } +static pj_bool_t input(const char *title, char *buf, pj_size_t len) +{ + char *p; + + printf("%s (empty to cancel): ", title); fflush(stdout); + fgets(buf, len, stdin); + + /* Remove trailing newlines. */ + for (p=buf; ; ++p) { + if (*p=='\r' || *p=='\n') *p='\0'; + else if (!*p) break; + } + + if (!*buf) + return PJ_FALSE; + + return PJ_TRUE; +} + static void ui_console_main(void) { - char keyin[10]; char buf[128]; - char *p; pjsip_inv_session *inv; //ui_help(); for (;;) { -#ifdef NO_WORKER_THREAD - pj_time_val timeout = { 0, 10 }; - pjsip_endpt_handle_events (pjsua.endpt, &timeout); - - if (kbhit()) - fgets(keyin, sizeof(keyin), stdin); -#else ui_help(); - fgets(keyin, sizeof(keyin), stdin); -#endif + fgets(buf, sizeof(buf), stdin); - switch (keyin[0]) { + switch (buf[0]) { case 'm': if (inv_session != NULL) { @@ -106,23 +110,9 @@ static void ui_console_main(void) } #if 1 - printf("Enter URL to call: "); fflush(stdout); - fgets(buf, sizeof(buf), stdin); - - if (buf[0]=='\r' || buf[0]=='\n') { - /* Cancelled. */ - puts("<cancelled>"); - fflush(stdout); - continue; - } - - /* Remove trailing newlines. */ - for (p=buf; ; ++p) { - if (*p=='\r' || *p=='\n') *p='\0'; - else if (!*p) break; - } /* Make call! : */ - + if (!input("Enter URL to call", buf, sizeof(buf))) + continue; pjsua_invite(buf, &inv); #else @@ -132,6 +122,33 @@ static void ui_console_main(void) break; + case 'a': + + if (inv_session == NULL || inv_session->role != PJSIP_ROLE_UAS || + inv_session->state >= PJSIP_INV_STATE_CONNECTING) + { + puts("No pending incoming call"); + fflush(stdout); + continue; + + } else { + pj_status_t status; + pjsip_tx_data *tdata; + + if (!input("Answer with code (100-699)", buf, sizeof(buf))) + continue; + + status = pjsip_inv_answer(inv_session, atoi(buf), NULL, NULL, + &tdata); + if (status == PJ_SUCCESS) + status = pjsip_inv_send_msg(inv_session, tdata, NULL); + + if (status != PJ_SUCCESS) + pjsua_perror("Unable to create/send response", status); + } + + break; + case 'h': if (inv_session == NULL) { @@ -672,11 +689,6 @@ int main(int argc, char *argv[]) pjsua_default(); -#ifdef NO_WORKER_THREAD - pjsua.thread_cnt = 0; -#endif - - /* Initialize pjsua (to create pool etc). */ diff --git a/pjsip/src/pjsua/pjsua_core.c b/pjsip/src/pjsua/pjsua_core.c index dd4c0c57..46f7fde7 100644 --- a/pjsip/src/pjsua/pjsua_core.c +++ b/pjsip/src/pjsua/pjsua_core.c @@ -101,8 +101,99 @@ void pjsua_perror(const char *title, pj_status_t status) */ static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata) { - PJ_UNUSED_ARG(rdata); - PJ_TODO(IMPLEMENT_UAS); + pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata); + pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata); + pjsip_msg *msg = rdata->msg_info.msg; + + /* + * Handle incoming INVITE outside dialog. + */ + if (dlg == NULL && tsx == NULL && + msg->line.req.method.id == PJSIP_INVITE_METHOD) + { + pj_status_t status; + pjsip_tx_data *response = NULL; + unsigned options = 0; + + /* Verify that we can handle the request. */ + status = pjsip_inv_verify_request(rdata, &options, NULL, NULL, + pjsua.endpt, &response); + if (status != PJ_SUCCESS) { + + /* + * No we can't handle the incoming INVITE request. + */ + + if (response) { + pjsip_response_addr res_addr; + + pjsip_get_response_addr(response->pool, rdata, &res_addr); + pjsip_endpt_send_response(pjsua.endpt, &res_addr, response, + NULL, NULL); + + } else { + + /* Respond with 500 (Internal Server Error) */ + pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL, + NULL, NULL); + } + + } else { + /* + * Yes we can handle the incoming INVITE request. + */ + pjsip_inv_session *inv; + pjmedia_sdp_session *answer; + + /* Create dummy SDP answer: */ + + + status = pjmedia_sdp_parse(pjsua.pool, PJSUA_DUMMY_SDP_ANSWER, + pj_native_strlen(PJSUA_DUMMY_SDP_ANSWER), + &answer); + if (status != PJ_SUCCESS) { + + pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL, + NULL, NULL); + return PJ_TRUE; + } + + /* Create dialog: */ + + status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, + &pjsua.contact_uri, &dlg); + if (status != PJ_SUCCESS) + return PJ_TRUE; + + + /* Create invite session: */ + + status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv); + if (status != PJ_SUCCESS) { + + status = pjsip_dlg_create_response( dlg, rdata, 500, NULL, + &response); + if (status == PJ_SUCCESS) + status = pjsip_dlg_send_response(dlg, + pjsip_rdata_get_tsx(rdata), + response); + return PJ_TRUE; + + } + + /* Answer with 100 (using the dialog, not invite): */ + + status = pjsip_dlg_create_response(dlg, rdata, 100, NULL, &response); + if (status == PJ_SUCCESS) + status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), response); + } + + /* This INVITE request has been handled. */ + return PJ_TRUE; + } + + + return PJ_FALSE; } @@ -121,7 +212,6 @@ static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata) static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata) { PJ_UNUSED_ARG(rdata); - PJ_TODO(IMPLEMENT_UAS); return PJ_FALSE; } diff --git a/pjsip/src/test-pjsip/tsx_uas_test.c b/pjsip/src/test-pjsip/tsx_uas_test.c index ed8f8574..326f2256 100644 --- a/pjsip/src/test-pjsip/tsx_uas_test.c +++ b/pjsip/src/test-pjsip/tsx_uas_test.c @@ -709,6 +709,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) test_complete = -110; return PJ_TRUE; } + pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); send_response(rdata, tsx, status_code); @@ -749,6 +750,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) test_complete = -120; return PJ_TRUE; } + pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); @@ -801,6 +803,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) return PJ_TRUE; } + pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); if (pj_strcmp2(&branch_param, TEST4_BRANCH_ID) == 0) { @@ -882,6 +885,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) return PJ_TRUE; } + pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); if (pj_strcmp2(&branch_param, TEST7_BRANCH_ID) == 0) { @@ -966,6 +970,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) return PJ_TRUE; } + pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); send_response(rdata, tsx, TEST9_STATUS_CODE); @@ -1076,6 +1081,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata) return PJ_TRUE; } + pjsip_tsx_recv_msg(tsx, rdata); save_key(tsx); schedule_send_response(rdata, &tsx_key, code1, 1000); |