diff --git a/sflphone-common/src/sdp.cpp b/sflphone-common/src/sdp.cpp index 9256b238b8ca8db8dd1cf6341e296940d5b1c4da..2bd7420dbf658332d875cce60a04872fc4836b83 100644 --- a/sflphone-common/src/sdp.cpp +++ b/sflphone-common/src/sdp.cpp @@ -180,7 +180,6 @@ int Sdp::receiving_initial_offer (pjmedia_sdp_session* remote) // pjmedia_sdp_neg_create_w_remote_offer with the remote offer, and by providing the local offer ( optional ) pj_status_t status; - pjmedia_sdp_neg_state state; _debug ("Receiving initial offer\n"); @@ -200,13 +199,61 @@ int Sdp::receiving_initial_offer (pjmedia_sdp_session* remote) status = pjmedia_sdp_neg_create_w_remote_offer (_pool, get_local_sdp_session(), remote, &_negociator); - state = pjmedia_sdp_neg_get_state (_negociator); - PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1); return PJ_SUCCESS; } +pj_status_t Sdp::check_incoming_sdp(pjsip_inv_session *inv, pjsip_rx_data *rdata) +{ + static const pj_str_t str_application = { "application", 11 }; + static const pj_str_t str_sdp = { "sdp", 3 }; + pj_status_t status; + pjsip_msg * message; + pjmedia_sdp_session * remote_sdp; + + message = rdata->msg_info.msg; + if (message->body == NULL) { + _debug("Empty message body\n"); + return -1; + } + + if (pj_stricmp(&message->body->content_type.type, &str_application) || pj_stricmp(&message->body->content_type.subtype, &str_sdp)) { + _debug("Incoming Message does not contain SDP\n"); + return PJMEDIA_SDP_EINSDP; + } + + // Parse the SDP body. + status = pjmedia_sdp_parse(rdata->tp_info.pool, (char*)message->body->data, message->body->len, &remote_sdp); + if (status == PJ_SUCCESS) { + status = pjmedia_sdp_validate(remote_sdp); + } + + if (status != PJ_SUCCESS) { + _debug("SDP cannot be validated\n"); + return PJMEDIA_SDP_EINSDP; + } + + if (pjmedia_sdp_neg_get_state(inv->neg) == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) { + // This is an answer + _debug("Got SDP answer %s\n", pjsip_rx_data_get_info(rdata)); + status = pjmedia_sdp_neg_set_remote_answer(inv->pool, inv->neg, remote_sdp); + + if (status != PJ_SUCCESS) { + _debug("An error occured while processing remote answer %s\n", pjsip_rx_data_get_info(rdata)); + return PJMEDIA_SDP_EINSDP; + } + + // Prefer our codecs to remote when possible + pjmedia_sdp_neg_set_prefer_remote_codec_order(inv->neg, 0); + + status = pjmedia_sdp_neg_negotiate(inv->pool, inv->neg, 0); + _debug("Negotiation returned with status %d PJ_SUCCESS being %d\n", status, PJ_SUCCESS); + } + + return status; +} + void Sdp::sdp_add_protocol (void) { this->_local_offer->origin.version = 0; @@ -298,7 +345,7 @@ void Sdp::clean_session_media() _session_media.clear(); } -void Sdp::set_negociated_offer (const pjmedia_sdp_session *sdp) +void Sdp::set_negotiated_sdp (const pjmedia_sdp_session *sdp) { int nb_media, nb_codecs; diff --git a/sflphone-common/src/sdp.h b/sflphone-common/src/sdp.h index ec3178955b57c5370872d43ada2c92302f9f18af..b32f9886f681749b46dcd36100e3866df96248bd 100644 --- a/sflphone-common/src/sdp.h +++ b/sflphone-common/src/sdp.h @@ -25,6 +25,8 @@ #include <pjmedia/sdp_neg.h> #include <pjsip/sip_transport.h> #include <pjlib.h> +#include <pjsip_ua.h> +#include <pjmedia/errno.h> #include <pj/pool.h> #include <pj/assert.h> @@ -97,6 +99,17 @@ class Sdp { */ int receiving_initial_offer( pjmedia_sdp_session* remote ); + /* + * On receiving a message, check if it contains SDP and negotiate. Should be used for + * SDP answer and offer but currently is only used for answer. + * SDP negociator instance with the remote offer. + * + * @param inv The the invitation + * @param rdata The remote data + */ + + pj_status_t check_incoming_sdp(pjsip_inv_session *inv, pjsip_rx_data *rdata); + /* * Remove all media in the session media vector. */ @@ -138,7 +151,7 @@ class Sdp { * * @param sdp the negociated offer */ - void set_negociated_offer( const pjmedia_sdp_session *sdp ); + void set_negotiated_sdp ( const pjmedia_sdp_session *sdp ); /* * Attribute the specified port to every medias provided diff --git a/sflphone-common/src/sipvoiplink.cpp b/sflphone-common/src/sipvoiplink.cpp index af41b6d268c760870fcca500204e488df9331432..13f7fa47ebfed7e8dc598ebad4532cb0ffcacb56 100644 --- a/sflphone-common/src/sipvoiplink.cpp +++ b/sflphone-common/src/sipvoiplink.cpp @@ -503,7 +503,6 @@ SIPVoIPLink::newOutgoingCall (const CallID& id, const std::string& toUrl) SIPCall* call = new SIPCall (id, Call::Outgoing, _pool); - if (call) { account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount (Manager::instance().getAccountFromCall (id))); @@ -520,7 +519,6 @@ SIPVoIPLink::newOutgoingCall (const CallID& id, const std::string& toUrl) setCallAudioLocal (call, getLocalIPAddress(), useStun(), getStunServer()); - try { _debug ("CREATE NEW RTP SESSION FROM NEWOUTGOINGCALL\n"); _audiortp->createNewSession (call); @@ -528,9 +526,6 @@ SIPVoIPLink::newOutgoingCall (const CallID& id, const std::string& toUrl) _debug ("Failed to create rtp thread from newOutGoingCall\n"); } - - - call->initRecFileName(); _debug ("Try to make a call to: %s with call ID: %s\n", toUrl.data(), id.data()); @@ -560,8 +555,6 @@ SIPVoIPLink::newOutgoingCall (const CallID& id, const std::string& toUrl) bool SIPVoIPLink::answer (const CallID& id) { - - int i; SIPCall *call; pj_status_t status; pjsip_tx_data *tdata; @@ -1415,7 +1408,6 @@ bool get_dns_server_addresses (std::vector<std::string> *servers) nameservers.push_back (inet_ntoa (address)); } - //nameservers.push_back ("192.168.50.3"); *servers = nameservers; return true; @@ -1427,8 +1419,6 @@ pj_status_t SIPVoIPLink::enable_dns_srv_resolver (pjsip_endpoint *endpt, pj_dns_ pj_status_t status; pj_dns_resolver *resv; std::vector <std::string> dns_servers; - pj_uint16_t port = 5353; - pjsip_resolver_t *res; int scount, i; // Create the DNS resolver instance @@ -1706,13 +1696,10 @@ int SIPVoIPLink::createUDPServer (void) pj_sockaddr_in bound_addr; pjsip_host_port a_name; char tmpIP[32]; - pj_sock_t sock; - // Init bound address to ANY pj_memset (&bound_addr, 0, sizeof (bound_addr)); - bound_addr.sin_addr.s_addr = pj_htonl (PJ_INADDR_ANY); bound_addr.sin_port = pj_htons ( (pj_uint16_t) _localPort); bound_addr.sin_family = PJ_AF_INET; @@ -1875,22 +1862,17 @@ void SIPVoIPLink::handle_reinvite (SIPCall *call) } } - -/*******************************/ -/* CALLBACKS IMPLEMENTATION */ -/*******************************/ - +// This callback is called when the invite session state has changed void call_on_state_changed (pjsip_inv_session *inv, pjsip_event *e) { - _debug ("--------------------- call_on_state_changed --------------------- %i\n", inv->state); SIPCall *call; AccountID accId; SIPVoIPLink *link; pjsip_rx_data *rdata; - - + pj_status_t status; + /* Retrieve the call information */ call = reinterpret_cast<SIPCall*> (inv->mod_data[_mod_ua.id]); @@ -1900,7 +1882,7 @@ void call_on_state_changed (pjsip_inv_session *inv, pjsip_event *e) //Retrieve the body message rdata = e->body.tsx_state.src.rdata; - + /* If this is an outgoing INVITE that was created because of * REFER/transfer, send NOTIFY to transferer. */ @@ -1961,84 +1943,104 @@ void call_on_state_changed (pjsip_inv_session *inv, pjsip_event *e) } } } - } else { + + return; + } + + // The call is ringing - We need to handle this case only on outgoing call + if (inv->state == PJSIP_INV_STATE_EARLY && e->body.tsx_state.tsx->role == PJSIP_ROLE_UAC) { + call->setConnectionState (Call::Ringing); + Manager::instance().peerRingingCall (call->getCallId()); + } + // After 2xx is sent/received. + else if (inv->state == PJSIP_INV_STATE_CONNECTING) { + status = call->getLocalSDP()->check_incoming_sdp (inv, rdata); + if (status != PJ_SUCCESS) { + _debug("Failed to check_incoming_sdp in call_on_state_changed\n"); + return; + } + } + // After we sent or received a ACK - The connection is established + else if (inv->state == PJSIP_INV_STATE_CONFIRMED) { - // The call is ringing - We need to handle this case only on outgoing call - if (inv->state == PJSIP_INV_STATE_EARLY && e->body.tsx_state.tsx->role == PJSIP_ROLE_UAC) { - call->setConnectionState (Call::Ringing); - Manager::instance().peerRingingCall (call->getCallId()); + /* If the call is a direct IP-to-IP call */ + if (call->getCallConfiguration () == Call::IPtoIP) { + link = SIPVoIPLink::instance (""); + } else { + accId = Manager::instance().getAccountFromCall (call->getCallId()); + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink (accId)); } - // We receive a ACK - The connection is established - else if (inv->state == PJSIP_INV_STATE_CONFIRMED) { + if (link) + link->SIPCallAnswered (call, rdata); + } + else if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { + _debug ("------------------- Call disconnected ---------------------\n"); + _debug ("State: %i, Disconnection cause: %i\n", inv->state, inv->cause); - /* If the call is a direct IP-to-IP call */ - if (call->getCallConfiguration () == Call::IPtoIP) { - link = SIPVoIPLink::instance (""); - } else { + switch (inv->cause) { + /* The call terminates normally - BYE / CANCEL */ + case PJSIP_SC_OK: + case PJSIP_SC_DECLINE: + case PJSIP_SC_REQUEST_TERMINATED: accId = Manager::instance().getAccountFromCall (call->getCallId()); link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink (accId)); - } - - if (link) - link->SIPCallAnswered (call, rdata); - } - - else if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { - int count = 0; - _debug ("------------------- Call disconnected ---------------------\n"); - _debug ("State: %i, Disconnection cause: %i\n", inv->state, inv->cause); - - switch (inv->cause) { - /* The call terminates normally - BYE / CANCEL */ - - case PJSIP_SC_OK: - - case PJSIP_SC_DECLINE: - - case PJSIP_SC_REQUEST_TERMINATED: - accId = Manager::instance().getAccountFromCall (call->getCallId()); - link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink (accId)); - - if (link) { - link->SIPCallClosed (call); - } - - break; + if (link) { + link->SIPCallClosed (call); + } - /* The call connection failed */ + break; + /* The call connection failed */ + case PJSIP_SC_NOT_FOUND: /* peer not found */ + case PJSIP_SC_REQUEST_TIMEOUT: /* request timeout */ + case PJSIP_SC_NOT_ACCEPTABLE_HERE: /* no compatible codecs */ + case PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE: + case PJSIP_SC_UNSUPPORTED_MEDIA_TYPE: + case PJSIP_SC_UNAUTHORIZED: + case PJSIP_SC_REQUEST_PENDING: + accId = Manager::instance().getAccountFromCall (call->getCallId()); + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink (accId)); - case PJSIP_SC_NOT_FOUND: /* peer not found */ + if (link) { + link->SIPCallServerFailure (call); + } - case PJSIP_SC_REQUEST_TIMEOUT: /* request timeout */ + break; + default: + _debug ("sipvoiplink.cpp - line %d : Unhandled call state. This is probably a bug.\n", __LINE__); + break; + } + } + +} - case PJSIP_SC_NOT_ACCEPTABLE_HERE: /* no compatible codecs */ +int terminate_call_bad_sdp_answer(SIPCall * call, SIPVoIPLink * link) +{ + pj_status_t status; + pjsip_tx_data *tdata = NULL; + + status = pjsip_inv_end_session(call->getInvSession(), 488, NULL, &tdata); - case PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE: + if (status != PJ_SUCCESS) + return false; - case PJSIP_SC_UNSUPPORTED_MEDIA_TYPE: + if (tdata == NULL) + return true; - case PJSIP_SC_UNAUTHORIZED: + status = pjsip_inv_send_msg (call->getInvSession(), tdata); - case PJSIP_SC_REQUEST_PENDING: - accId = Manager::instance().getAccountFromCall (call->getCallId()); - link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink (accId)); + if (status != PJ_SUCCESS) + return false; - if (link) { - link->SIPCallServerFailure (call); - } + call->getInvSession()->mod_data[getModId() ] = NULL; - break; + link->terminateOneCall (call->getCallId()); - default: - _debug ("sipvoiplink.cpp - line %d : Unhandled call state. This is probably a bug.\n", __LINE__); - break; - } - } - } + link->removeCall (call->getCallId()); } +// This callback is called after SDP offer/answer session has completed. void call_on_media_update (pjsip_inv_session *inv, pj_status_t status) { _debug ("--------------------- call_on_media_update --------------------- \n"); @@ -2048,38 +2050,42 @@ void call_on_media_update (pjsip_inv_session *inv, pj_status_t status) SIPVoIPLink * link = NULL; SIPCall * call; - - if (status != PJ_SUCCESS) { - _debug ("Error while negociating the offer\n"); - return; - } - - // Get the new sdp, result of the negociation - pjmedia_sdp_neg_get_active_local (inv->neg, &local_sdp); - pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp); - + call = reinterpret_cast<SIPCall *> (inv->mod_data[getModId() ]); if (!call) { _debug ("Call declined by peer, SDP negociation stopped\n"); return; } - - // Clean the resulting sdp offer to create a new one (in case of a reinvite) - call->getLocalSDP()->clean_session_media(); - - // Set the fresh negociated one - call->getLocalSDP()->set_negociated_offer (local_sdp); - // Set remote ip / port - call->getLocalSDP()->set_media_transport_info_from_remote_sdp (remote_sdp); - link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(AccountNULL)); if(link == NULL) { _debug ("Failed to get sip link\n"); return; } + if (status != PJ_SUCCESS) { + _debug ("Error while negotiating the offer\n"); + //terminate_call_bad_sdp_answer(call, link); + link->hangup(call->getCallId()); + return; + } + + // Get the new sdp, result of the negotiation + pjmedia_sdp_neg_get_active_local (inv->neg, &local_sdp); + pjmedia_sdp_neg_get_active_remote (inv->neg, &remote_sdp); + + // Clean the resulting sdp offer to create a new one (in case of a reinvite) + call->getLocalSDP()->clean_session_media(); + + // Set the fresh negotiated one, no matter if that was an offer or answer. + // The local sdp is updated in case of an answer, even if the remote sdp + // is kept internally. + call->getLocalSDP()->set_negotiated_sdp (local_sdp); + + // Set remote ip / port + call->getLocalSDP()->set_media_transport_info_from_remote_sdp (remote_sdp); + try { call->setAudioStart (true); link->getAudioRtp()->start(); @@ -2107,11 +2113,8 @@ void call_on_tsx_changed (pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_ void regc_cb (struct pjsip_regc_cbparam *param) { - - //AccountID *id = static_cast<AccountID *> (param->token); SIPAccount *account; - //_debug("UserAgent: Account ID is %s, Register result: %d, Status: %d\n", id->data(), param->status, param->code); account = static_cast<SIPAccount *> (param->token); if (!account) @@ -2167,11 +2170,10 @@ void regc_cb (struct pjsip_regc_cbparam *param) } +// Optional function to be called to process incoming request message. pj_bool_t mod_on_rx_request (pjsip_rx_data *rdata) { - - pj_status_t status; pj_str_t reason; unsigned options = 0; @@ -2325,7 +2327,6 @@ mod_on_rx_request (pjsip_rx_data *rdata) get_remote_sdp_from_offer (rdata, &r_sdp); -// _debug("r_sdp = %s\n", r_sdp); status = call->getLocalSDP()->receiving_initial_offer (r_sdp); if (status!=PJ_SUCCESS) { @@ -2334,7 +2335,6 @@ mod_on_rx_request (pjsip_rx_data *rdata) return false; } - call->setConnectionState (Call::Progressing); call->setPeerNumber (peerNumber); @@ -2364,7 +2364,6 @@ mod_on_rx_request (pjsip_rx_data *rdata) return true; } - // Specify media capability during invite session creation status = pjsip_inv_create_uas (dialog, rdata, call->getLocalSDP()->get_local_sdp_session(), 0, &inv);