/* * sal.cpp * Copyright (C) 2010-2018 Belledonne Communications SARL * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "sal/sal.h" #include "sal/call-op.h" #include "sal/presence-op.h" #include "sal/refer-op.h" #include "sal/event-op.h" #include "sal/message-op.h" #include "bellesip_sal/sal_impl.h" #include "tester_utils.h" #include "private.h" #include "c-wrapper/internal/c-tools.h" using namespace std; LINPHONE_BEGIN_NAMESPACE void Sal::processDialogTerminatedCb(void *sal, const belle_sip_dialog_terminated_event_t *event) { belle_sip_dialog_t* dialog = belle_sip_dialog_terminated_event_get_dialog(event); SalOp* op = reinterpret_cast(belle_sip_dialog_get_application_data(dialog)); if (op && op->mCallbacks && op->mCallbacks->process_dialog_terminated) { op->mCallbacks->process_dialog_terminated(op,event); } else { ms_error("sal process_dialog_terminated no op found for this dialog [%p], ignoring",dialog); } } void Sal::processIoErrorCb(void *user_ctx, const belle_sip_io_error_event_t *event){ belle_sip_client_transaction_t*client_transaction; SalOp* op; if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(belle_sip_io_error_event_get_source(event),belle_sip_client_transaction_t)) { client_transaction=BELLE_SIP_CLIENT_TRANSACTION(belle_sip_io_error_event_get_source(event)); op = (SalOp*)belle_sip_transaction_get_application_data(BELLE_SIP_TRANSACTION(client_transaction)); /*also reset auth count on IO error*/ op->mAuthRequests=0; if (op->mCallbacks && op->mCallbacks->process_io_error) { op->mCallbacks->process_io_error(op,event); } } else { /*ms_error("sal process_io_error not implemented yet for non transaction");*/ /*nop, because already handle at transaction layer*/ } } void Sal::processRequestEventCb (void *ud, const belle_sip_request_event_t *event) { Sal *sal = (Sal *)ud; SalOp *op = NULL; belle_sip_request_t *req = belle_sip_request_event_get_request(event); belle_sip_dialog_t *dialog = belle_sip_request_event_get_dialog(event); belle_sip_header_address_t *origin_address; belle_sip_header_address_t *address=NULL; belle_sip_header_from_t *from_header; belle_sip_header_to_t *to; belle_sip_header_diversion_t *diversion; belle_sip_response_t *resp; belle_sip_header_t *evh; const char *method = belle_sip_request_get_method(req); belle_sip_header_contact_t *remote_contact = belle_sip_message_get_header_by_type(req, belle_sip_header_contact_t); belle_sip_header_t *subjectHeader = belle_sip_message_get_header(BELLE_SIP_MESSAGE(req), "Subject"); from_header = belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_from_t); if (dialog) { op = (SalOp *)belle_sip_dialog_get_application_data(dialog); if (op == NULL && strcmp("NOTIFY",method) == 0) { /*special case for Dialog created by notify mathing subscribe*/ belle_sip_transaction_t * sub_trans = belle_sip_dialog_get_last_transaction(dialog); op = (SalOp*)belle_sip_transaction_get_application_data(sub_trans); } if (op == NULL || op->mState == SalOp::State::Terminated){ ms_warning("Receiving request for null or terminated op [%p], ignored",op); return; } } else { /*handle the case where we are receiving a request with to tag but it is not belonging to any dialog*/ belle_sip_header_to_t *to = belle_sip_message_get_header_by_type(req, belle_sip_header_to_t); if ((strcmp("INVITE",method)==0 || strcmp("NOTIFY",method)==0) && (belle_sip_header_to_get_tag(to) != NULL)) { ms_warning("Receiving %s with to-tag but no know dialog here. Rejecting.", method); resp=belle_sip_response_create_from_request(req,481); belle_sip_provider_send_response(sal->mProvider,resp); return; /* by default (eg. when a to-tag is present), out of dialog ACK are automatically handled in lower layers (belle-sip) but in case it misses, it will be forwarded to us */ } else if (strcmp("ACK",method)==0 && (belle_sip_header_to_get_tag(to) == NULL)) { ms_warning("Receiving ACK without to-tag but no know dialog here. Ignoring"); return; } if (strcmp("INVITE",method)==0) { op=new SalCallOp(sal); op->fillCallbacks(); }else if ((strcmp("SUBSCRIBE",method)==0 || strcmp("NOTIFY",method)==0) && (evh=belle_sip_message_get_header(BELLE_SIP_MESSAGE(req),"Event"))!=NULL) { if (strncmp(belle_sip_header_get_unparsed_value(evh),"presence",strlen("presence"))==0){ op=new SalPresenceOp(sal); } else { op=new SalSubscribeOp(sal); } op->fillCallbacks(); }else if (strcmp("MESSAGE",method)==0) { op=new SalMessageOp(sal); op->fillCallbacks(); }else if (strcmp("REFER",method)==0) { op=new SalReferOp(sal); }else if (strcmp("OPTIONS",method)==0) { resp=belle_sip_response_create_from_request(req,200); belle_sip_provider_send_response(sal->mProvider,resp); return; }else if (strcmp("INFO",method)==0) { resp=belle_sip_response_create_from_request(req,481);/*INFO out of call dialogs are not allowed*/ belle_sip_provider_send_response(sal->mProvider,resp); return; }else if (strcmp("BYE",method)==0) { resp=belle_sip_response_create_from_request(req,481);/*out of dialog BYE */ belle_sip_provider_send_response(sal->mProvider,resp); return; }else if (strcmp("CANCEL",method)==0) { resp=belle_sip_response_create_from_request(req,481);/*out of dialog CANCEL */ belle_sip_provider_send_response(sal->mProvider,resp); return; }else if (sal->mEnableTestFeatures && strcmp("PUBLISH",method)==0) { resp=belle_sip_response_create_from_request(req,200);/*out of dialog PUBLISH */ belle_sip_message_add_header((belle_sip_message_t*)resp,belle_sip_header_create("SIP-Etag","4441929FFFZQOA")); belle_sip_provider_send_response(sal->mProvider,resp); return; }else { ms_error("sal process_request_event not implemented yet for method [%s]",belle_sip_request_get_method(req)); resp=belle_sip_response_create_from_request(req,405); belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp) ,BELLE_SIP_HEADER(belle_sip_header_allow_create("INVITE, CANCEL, ACK, BYE, SUBSCRIBE, NOTIFY, MESSAGE, OPTIONS, INFO"))); belle_sip_provider_send_response(sal->mProvider,resp); return; } op->mDir=SalOp::Dir::Incoming; } if (!op->mFromAddress) { if (belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header))) address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header)) ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header))); else if ((belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(from_header)))) address=belle_sip_header_address_create2(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header)) ,belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(from_header))); else ms_error("Cannot not find from uri from request [%p]",req); op->setFromAddress((SalAddress*)address); belle_sip_object_unref(address); } if( remote_contact ){ op->setRemoteContact(belle_sip_header_get_unparsed_value(BELLE_SIP_HEADER(remote_contact))); } if (!op->mToAddress) { to=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_to_t); if (belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(to))) address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(to)) ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(to))); else if ((belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(to)))) address=belle_sip_header_address_create2(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(to)) ,belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(to))); else ms_error("Cannot not find to uri from request [%p]",req); op->setToAddress((SalAddress*)address); belle_sip_object_unref(address); } if (subjectHeader) { const char *subject = belle_sip_header_get_unparsed_value(subjectHeader); op->setSubject(subject); } if(!op->mDiversionAddress){ diversion=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_diversion_t); if (diversion) { if (belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(diversion))) address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(diversion)) ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(diversion))); else if ((belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(diversion)))) address=belle_sip_header_address_create2(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(diversion)) ,belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(diversion))); else ms_warning("Cannot not find diversion header from request [%p]",req); if (address) { op->setDiversionAddress((SalAddress*)address); belle_sip_object_unref(address); } } } if (op->mOrigin.empty()) { /*set origin uri*/ origin_address=belle_sip_header_address_create(NULL,belle_sip_request_extract_origin(req)); op->setNetworkOriginAddress((SalAddress*)origin_address); belle_sip_object_unref(origin_address); } if (op->mRemoteUserAgent.empty()) op->setRemoteUserAgent(BELLE_SIP_MESSAGE(req)); if (op->mCallId.empty()) { op->mCallId = belle_sip_header_call_id_get_call_id( BELLE_SIP_HEADER_CALL_ID(belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req), belle_sip_header_call_id_t)) ); } /*It is worth noting that proxies can (and will) remove this header field*/ op->setPrivacyFromMessage((belle_sip_message_t*)req); if (strcmp("ACK",method) != 0){ /*The ACK custom header is processed specifically later on*/ op->assignRecvHeaders((belle_sip_message_t*)req); } if (op->mCallbacks && op->mCallbacks->process_request_event) { op->mCallbacks->process_request_event(op,event); } else { ms_error("sal process_request_event not implemented yet"); } } void Sal::processResponseEventCb(void *user_ctx, const belle_sip_response_event_t *event) { belle_sip_client_transaction_t* client_transaction = belle_sip_response_event_get_client_transaction(event); belle_sip_response_t* response = belle_sip_response_event_get_response(event); int response_code = belle_sip_response_get_status_code(response); if (!client_transaction) { ms_warning("Discarding stateless response [%i]",response_code); return; } else { SalOp* op = (SalOp*)belle_sip_transaction_get_application_data(BELLE_SIP_TRANSACTION(client_transaction)); belle_sip_request_t* request=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction)); belle_sip_header_contact_t* remote_contact = belle_sip_message_get_header_by_type(response, belle_sip_header_contact_t); if (op->mState == SalOp::State::Terminated) { belle_sip_message("Op [%p] is terminated, nothing to do with this [%i]", op, response_code); return; } /*do it all the time, since we can receive provisional responses from a different instance than the final one*/ op->setRemoteUserAgent(BELLE_SIP_MESSAGE(response)); if(remote_contact) { op->setRemoteContact(belle_sip_header_get_unparsed_value(BELLE_SIP_HEADER(remote_contact))); } if (op->mCallId.empty()) { op->mCallId = belle_sip_header_call_id_get_call_id( BELLE_SIP_HEADER_CALL_ID(belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(response), belle_sip_header_call_id_t)) ); } op->assignRecvHeaders((belle_sip_message_t*)response); if (op->mCallbacks && op->mCallbacks->process_response_event) { /*handle authorization*/ switch (response_code) { case 200: break; case 401: case 407: if (op->mState == SalOp::State::Terminating && strcmp("BYE",belle_sip_request_get_method(request))!=0) { /*only bye are completed*/ belle_sip_message("Op is in state terminating, nothing else to do "); } else { if (op->mPendingAuthTransaction){ belle_sip_object_unref(op->mPendingAuthTransaction); op->mPendingAuthTransaction=NULL; } if (++op->mAuthRequests > 2) { ms_warning("Auth info cannot be found for op [%s/%s] after 2 attempts, giving up",op->getFrom().c_str() ,op->getTo().c_str()); op->mRoot->mCallbacks.auth_failure(op,op->mAuthInfo); op->mRoot->removePendingAuth(op); } else { op->mPendingAuthTransaction=(belle_sip_client_transaction_t*)belle_sip_object_ref(client_transaction); op->processAuthentication(); return; } } break; case 403: if (op->mAuthInfo) op->mRoot->mCallbacks.auth_failure(op,op->mAuthInfo); break; case 302: case 301: if (op->processRedirect() == 0) return; break; } if (response_code >= 180 && response_code !=401 && response_code !=407 && response_code !=403) { /*not an auth request*/ op->mAuthRequests=0; } op->mCallbacks->process_response_event(op,event); } else { ms_error("Unhandled event response [%p]",event); } } } void Sal::processTimeoutCb(void *user_ctx, const belle_sip_timeout_event_t *event) { belle_sip_client_transaction_t* client_transaction = belle_sip_timeout_event_get_client_transaction(event); SalOp* op = (SalOp*)belle_sip_transaction_get_application_data(BELLE_SIP_TRANSACTION(client_transaction)); if (op && op->mCallbacks && op->mCallbacks->process_timeout) { op->mCallbacks->process_timeout(op,event); } else { ms_error("Unhandled event timeout [%p]",event); } } void Sal::processTransactionTerminatedCb(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) { belle_sip_client_transaction_t* client_transaction = belle_sip_transaction_terminated_event_get_client_transaction(event); belle_sip_server_transaction_t* server_transaction = belle_sip_transaction_terminated_event_get_server_transaction(event); belle_sip_transaction_t* trans; SalOp* op; if(client_transaction) trans=BELLE_SIP_TRANSACTION(client_transaction); else trans=BELLE_SIP_TRANSACTION(server_transaction); op = (SalOp*)belle_sip_transaction_get_application_data(trans); if (op && op->mCallbacks && op->mCallbacks->process_transaction_terminated) { op->mCallbacks->process_transaction_terminated(op,event); } else { ms_message("Unhandled transaction terminated [%p]",trans); } if (op) { op->unref(); /*because every transaction ref op*/ belle_sip_transaction_set_application_data(trans,NULL); /*no longuer reference something we do not ref to avoid futur access of a released op*/ } } void Sal::processAuthRequestedCb(void *sal, belle_sip_auth_event_t *event) { SalAuthInfo* auth_info = sal_auth_info_create(event); ((Sal*)sal)->mCallbacks.auth_requested(reinterpret_cast(sal),auth_info); belle_sip_auth_event_set_passwd(event,(const char*)auth_info->password); belle_sip_auth_event_set_ha1(event,(const char*)auth_info->ha1); belle_sip_auth_event_set_userid(event,(const char*)auth_info->userid); belle_sip_auth_event_set_signing_key(event,(belle_sip_signing_key_t *)auth_info->key); belle_sip_auth_event_set_client_certificates_chain(event,(belle_sip_certificates_chain_t* )auth_info->certificates); sal_auth_info_delete(auth_info); } Sal::Sal(MSFactory *factory){ belle_sip_listener_callbacks_t listener_callbacks = {0}; /*belle_sip_object_enable_marshal_check(TRUE);*/ mFactory = factory; /*first create the stack, which initializes the belle-sip object's pool for this thread*/ mStack = belle_sip_stack_new(NULL); mUserAgentHeader=belle_sip_header_user_agent_new(); #if defined(PACKAGE_NAME) && defined(LIBLINPHONE_VERSION) belle_sip_header_user_agent_add_product(user_agent, PACKAGE_NAME "/" LIBLINPHONE_VERSION); #else belle_sip_header_user_agent_add_product(mUserAgentHeader, "Unknown"); #endif appendStackStringToUserAgent(); belle_sip_object_ref(mUserAgentHeader); mProvider = belle_sip_stack_create_provider(mStack,NULL); enableNatHelper(TRUE); listener_callbacks.process_dialog_terminated=processDialogTerminatedCb; listener_callbacks.process_io_error=processIoErrorCb; listener_callbacks.process_request_event=processRequestEventCb; listener_callbacks.process_response_event=processResponseEventCb; listener_callbacks.process_timeout=processTimeoutCb; listener_callbacks.process_transaction_terminated=processTransactionTerminatedCb; listener_callbacks.process_auth_requested=processAuthRequestedCb; mListener=belle_sip_listener_create_from_callbacks(&listener_callbacks, this); belle_sip_provider_add_sip_listener(mProvider, mListener); } Sal::~Sal() { belle_sip_object_unref(mUserAgentHeader); belle_sip_object_unref(mProvider); belle_sip_object_unref(mStack); belle_sip_object_unref(mListener); if (mSupportedHeader) belle_sip_object_unref(mSupportedHeader); } void Sal::setCallbacks(const Callbacks *cbs) { memcpy(&mCallbacks,cbs,sizeof(*cbs)); if (mCallbacks.call_received==NULL) mCallbacks.call_received=(OnCallReceivedCb)unimplementedStub; if (mCallbacks.call_ringing==NULL) mCallbacks.call_ringing=(OnCallRingingCb)unimplementedStub; if (mCallbacks.call_accepted==NULL) mCallbacks.call_accepted=(OnCallAcceptedCb)unimplementedStub; if (mCallbacks.call_failure==NULL) mCallbacks.call_failure=(OnCallFailureCb)unimplementedStub; if (mCallbacks.call_terminated==NULL) mCallbacks.call_terminated=(OnCallTerminatedCb)unimplementedStub; if (mCallbacks.call_released==NULL) mCallbacks.call_released=(OnCallReleasedCb)unimplementedStub; if (mCallbacks.call_updating==NULL) mCallbacks.call_updating=(OnCallUpdatingCb)unimplementedStub; if (mCallbacks.auth_failure==NULL) mCallbacks.auth_failure=(OnAuthFailureCb)unimplementedStub; if (mCallbacks.register_success==NULL) mCallbacks.register_success=(OnRegisterSuccessCb)unimplementedStub; if (mCallbacks.register_failure==NULL) mCallbacks.register_failure=(OnRegisterFailureCb)unimplementedStub; if (mCallbacks.dtmf_received==NULL) mCallbacks.dtmf_received=(OnDtmfReceivedCb)unimplementedStub; if (mCallbacks.notify==NULL) mCallbacks.notify=(OnNotifyCb)unimplementedStub; if (mCallbacks.subscribe_received==NULL) mCallbacks.subscribe_received=(OnSubscribeReceivedCb)unimplementedStub; if (mCallbacks.incoming_subscribe_closed==NULL) mCallbacks.incoming_subscribe_closed=(OnIncomingSubscribeClosedCb)unimplementedStub; if (mCallbacks.parse_presence_requested==NULL) mCallbacks.parse_presence_requested=(OnParsePresenceRequestedCb)unimplementedStub; if (mCallbacks.convert_presence_to_xml_requested==NULL) mCallbacks.convert_presence_to_xml_requested=(OnConvertPresenceToXMLRequestedCb)unimplementedStub; if (mCallbacks.notify_presence==NULL) mCallbacks.notify_presence=(OnNotifyPresenceCb)unimplementedStub; if (mCallbacks.subscribe_presence_received==NULL) mCallbacks.subscribe_presence_received=(OnSubscribePresenceReceivedCb)unimplementedStub; if (mCallbacks.message_received==NULL) mCallbacks.message_received=(OnMessageReceivedCb)unimplementedStub; if (mCallbacks.ping_reply==NULL) mCallbacks.ping_reply=(OnPingReplyCb)unimplementedStub; if (mCallbacks.auth_requested==NULL) mCallbacks.auth_requested=(OnAuthRequestedCb)unimplementedStub; if (mCallbacks.info_received==NULL) mCallbacks.info_received=(OnInfoReceivedCb)unimplementedStub; if (mCallbacks.on_publish_response==NULL) mCallbacks.on_publish_response=(OnPublishResponseCb)unimplementedStub; if (mCallbacks.on_expire==NULL) mCallbacks.on_expire=(OnExpireCb)unimplementedStub; } void Sal::setTlsProperties(){ belle_sip_listening_point_t *lp=belle_sip_provider_get_listening_point(mProvider,"TLS"); if (lp){ belle_sip_tls_listening_point_t *tlp=BELLE_SIP_TLS_LISTENING_POINT(lp); belle_tls_crypto_config_t *crypto_config = belle_tls_crypto_config_new(); int verify_exceptions = BELLE_TLS_VERIFY_NONE; if (!mTlsVerify) verify_exceptions = BELLE_TLS_VERIFY_ANY_REASON; else if (!mTlsVerifyCn) verify_exceptions = BELLE_TLS_VERIFY_CN_MISMATCH; belle_tls_crypto_config_set_verify_exceptions(crypto_config, verify_exceptions); if (!mRootCa.empty()) belle_tls_crypto_config_set_root_ca(crypto_config, mRootCa.c_str()); if (!mRootCaData.empty()) belle_tls_crypto_config_set_root_ca_data(crypto_config, mRootCaData.c_str()); if (mSslConfig != NULL) belle_tls_crypto_config_set_ssl_config(crypto_config, mSslConfig); if (mTlsPostcheckCb) belle_tls_crypto_config_set_postcheck_callback(crypto_config, mTlsPostcheckCb, mTlsPostcheckCbData); belle_sip_tls_listening_point_set_crypto_config(tlp, crypto_config); belle_sip_object_unref(crypto_config); } } int Sal::addListenPort(SalAddress* addr, bool is_tunneled) { int result; belle_sip_listening_point_t* lp; if (is_tunneled){ #ifdef TUNNEL_ENABLED if (sal_address_get_transport(addr)!=SalTransportUDP){ ms_error("Tunneled mode is only available for UDP kind of transports."); return -1; } lp = belle_sip_tunnel_listening_point_new(mStack, mTunnelClient); if (!lp){ ms_error("Could not create tunnel listening point."); return -1; } #else ms_error("No tunnel support in library."); return -1; #endif }else{ lp = belle_sip_stack_create_listening_point(mStack, sal_address_get_domain(addr), sal_address_get_port(addr), sal_transport_to_string(sal_address_get_transport(addr))); } if (lp) { belle_sip_listening_point_set_keep_alive(lp,(int)mKeepAlive); result = belle_sip_provider_add_listening_point(mProvider,lp); if (sal_address_get_transport(addr)==SalTransportTLS) { setTlsProperties(); } } else { return -1; } return result; } int Sal::setListenPort (const string &addr, int port, SalTransport tr, bool isTunneled) { SalAddress *salAddr = sal_address_new(nullptr); sal_address_set_domain(salAddr, L_STRING_TO_C(addr)); sal_address_set_port(salAddr, port); sal_address_set_transport(salAddr, tr); int result = addListenPort(salAddr, isTunneled); sal_address_destroy(salAddr); return result; } int Sal::getListeningPort(SalTransport tr){ const char *tpn=sal_transport_to_string(tr); belle_sip_listening_point_t *lp=belle_sip_provider_get_listening_point(mProvider, tpn); if (lp){ return belle_sip_listening_point_get_port(lp); } return 0; } int Sal::unlistenPorts(){ const belle_sip_list_t * lps = belle_sip_provider_get_listening_points(mProvider); belle_sip_list_t * tmp_list = belle_sip_list_copy(lps); belle_sip_list_for_each2 (tmp_list,(void (*)(void*,void*))removeListeningPoint,mProvider); belle_sip_list_free(tmp_list); ms_message("sal_unlisten_ports done"); return 0; } int Sal::isTransportAvailable(SalTransport t) { switch(t){ case SalTransportUDP: case SalTransportTCP: return TRUE; case SalTransportTLS: return belle_sip_stack_tls_available(mStack); case SalTransportDTLS: return FALSE; } return FALSE; } void Sal::makeSupportedHeader () { if (mSupportedHeader) { belle_sip_object_unref(mSupportedHeader); mSupportedHeader = nullptr; } string tags = Utils::join(mSupportedTags, ", "); if (tags.empty()) return; mSupportedHeader = belle_sip_header_create("Supported", tags.c_str()); if (mSupportedHeader) belle_sip_object_ref(mSupportedHeader); } void Sal::setSupportedTags (const string &tags) { vector splittedTags = Utils::split(tags, ","); mSupportedTags.clear(); for (const auto &tag : splittedTags) mSupportedTags.push_back(Utils::trim(tag)); makeSupportedHeader(); } const string &Sal::getSupportedTags () const { if (mSupportedHeader) mSupported = belle_sip_header_get_unparsed_value(mSupportedHeader); else mSupported.clear(); return mSupported; } void Sal::addSupportedTag (const string &tag) { auto it = find(mSupportedTags.cbegin(), mSupportedTags.cend(), tag); if (it == mSupportedTags.cend()) { mSupportedTags.push_back(tag); makeSupportedHeader(); } } void Sal::removeSupportedTag (const string &tag) { auto it = find(mSupportedTags.begin(), mSupportedTags.end(), tag); if (it != mSupportedTags.end()) { mSupportedTags.erase(it); makeSupportedHeader(); } } int Sal::resetTransports() { ms_message("Reseting transports"); belle_sip_provider_clean_channels(mProvider); return 0; } ortp_socket_t Sal::getSocket() const { ms_warning("sal_get_socket is deprecated"); return -1; } void Sal::setUserAgent (const string &value) { belle_sip_header_user_agent_set_products(mUserAgentHeader, nullptr); belle_sip_header_user_agent_add_product(mUserAgentHeader, L_STRING_TO_C(value)); } const string &Sal::getUserAgent () const { char userAgent[256]; belle_sip_header_user_agent_get_products_as_string(mUserAgentHeader, userAgent, (unsigned int)sizeof(userAgent) - 1); mUserAgent = userAgent; return mUserAgent; } void Sal::appendStackStringToUserAgent () { stringstream ss; ss << "(belle-sip/" << belle_sip_version_to_string() << ")"; string stackStr = ss.str(); belle_sip_header_user_agent_add_product(mUserAgentHeader, stackStr.c_str()); } void Sal::setKeepAlivePeriod(unsigned int value) { const belle_sip_list_t* iterator; belle_sip_listening_point_t* lp; mKeepAlive=value; for (iterator=belle_sip_provider_get_listening_points(mProvider);iterator!=NULL;iterator=iterator->next) { lp=(belle_sip_listening_point_t*)iterator->data; if (mUseTcpTlsKeepAlive || strcasecmp(belle_sip_listening_point_get_transport(lp),"udp")==0) { belle_sip_listening_point_set_keep_alive(lp,(int)mKeepAlive); } } } int Sal::setTunnel(void *tunnelclient) { #ifdef TUNNEL_ENABLED mTunnelClient=tunnelclient; return 0; #else return -1; #endif } void Sal::setHttpProxyHost (const string &value) { belle_sip_stack_set_http_proxy_host(mStack, L_STRING_TO_C(value)); } const string &Sal::getHttpProxyHost () const { mHttpProxyHost = belle_sip_stack_get_http_proxy_host(mStack); return mHttpProxyHost; } bool Sal::isContentEncodingAvailable (const string &contentEncoding) const { return !!belle_sip_stack_content_encoding_available(mStack, L_STRING_TO_C(contentEncoding)); } bool Sal::isContentTypeSupported (const string &contentType) const { auto it = find_if(mSupportedContentTypes.cbegin(), mSupportedContentTypes.cend(), [contentType](string supportedContentType) { return contentType == supportedContentType; }); return it != mSupportedContentTypes.cend(); } void Sal::addContentTypeSupport (const string &contentType) { if (!contentType.empty() && !isContentTypeSupported(contentType)) mSupportedContentTypes.push_back(contentType); } void Sal::removeContentTypeSupport (const string &contentType) { auto it = find(mSupportedContentTypes.begin(), mSupportedContentTypes.end(), contentType); if (it != mSupportedContentTypes.end()) mSupportedContentTypes.erase(it); } void Sal::useRport(bool use_rports) { belle_sip_provider_enable_rport(mProvider,use_rports); ms_message("Sal use rport [%s]", use_rports ? "enabled" : "disabled"); } void Sal::setRootCa (const string &value) { mRootCa = value; setTlsProperties(); } void Sal::setRootCaData (const string &value) { mRootCaData = value; setTlsProperties(); } void Sal::verifyServerCertificates(bool verify) { mTlsVerify=verify; setTlsProperties(); } void Sal::verifyServerCn(bool verify) { mTlsVerifyCn = verify; setTlsProperties(); } void Sal::setSslConfig(void *ssl_config) { mSslConfig = ssl_config; setTlsProperties(); } void Sal::setTlsPostcheckCallback(int (*cb)(void *, const bctbx_x509_certificate_t *), void *data){ mTlsPostcheckCb = cb; mTlsPostcheckCbData = data; } int Sal::createUuid(char *uuid, size_t len) { if (generateUuid(uuid, len) == 0) { setUuid(uuid); return 0; } return -1; } int Sal::generateUuid(char *uuid, size_t len) { SalUuid uuid_struct; int i; int written; if (len==0) return -1; /*create an UUID as described in RFC4122, 4.4 */ belle_sip_random_bytes((unsigned char*)&uuid_struct, sizeof(SalUuid)); uuid_struct.clockSeqHiAndReserved&=(unsigned char)~(1<<6); uuid_struct.clockSeqHiAndReserved|=(unsigned char)1<<7; uuid_struct.timeHiAndVersion&=(unsigned char)~(0xf<<12); uuid_struct.timeHiAndVersion|=(unsigned char)4<<12; written=snprintf(uuid,len,"%8.8x-%4.4x-%4.4x-%2.2x%2.2x-", uuid_struct.timeLow, uuid_struct.timeMid, uuid_struct.timeHiAndVersion, uuid_struct.clockSeqHiAndReserved, uuid_struct.clockSeqLow); if ((written < 0) || ((size_t)written > (len +13))) { ms_error("sal_create_uuid(): buffer is too short !"); return -1; } for (i = 0; i < 6; i++) written+=snprintf(uuid+written,len-(unsigned long)written,"%2.2x", uuid_struct.node[i]); uuid[len-1]='\0'; return 0; } void Sal::addPendingAuth (SalOp *op) { auto it = find(mPendingAuths.cbegin(), mPendingAuths.cend(), op); if (it == mPendingAuths.cend()) { mPendingAuths.push_back(op); op->mHasAuthPending = true; } } void Sal::removePendingAuth (SalOp *op) { if (op->mHasAuthPending) { op->mHasAuthPending = false; mPendingAuths.remove(op); } } void Sal::setDefaultSdpHandling(SalOpSDPHandling sdp_handling_method) { if (sdp_handling_method != SalOpSDPNormal ) ms_message("Enabling special SDP handling for all new SalOp in Sal[%p]!", this); mDefaultSdpHandling = sdp_handling_method; } void Sal::enableNatHelper(bool enable) { mNatHelperEnabled=enable; belle_sip_provider_enable_nat_helper(mProvider,enable); ms_message("Sal nat helper [%s]",enable?"enabled":"disabled"); } void Sal::getDefaultLocalIp(int address_family, char *ip, size_t iplen) { strncpy(ip,address_family==AF_INET6 ? "::1" : "127.0.0.1",iplen); ms_error("sal_get_default_local_ip() is deprecated."); } void Sal::setDnsServers(const bctbx_list_t *servers){ belle_sip_list_t *l = NULL; /*we have to convert the bctbx_list_t into a belle_sip_list_t first*/ for (; servers != NULL; servers = servers->next){ l = belle_sip_list_append(l, servers->data); } belle_sip_stack_set_dns_servers(mStack, l); belle_sip_list_free(l); } void Sal::setDnsUserHostsFile (const string &value) { belle_sip_stack_set_dns_user_hosts_file(mStack, value.c_str()); } const string &Sal::getDnsUserHostsFile () const { mDnsUserHostsFile = belle_sip_stack_get_dns_user_hosts_file(mStack); return mDnsUserHostsFile; } belle_sip_resolver_context_t *Sal::resolveA (const string &name, int port, int family, belle_sip_resolver_callback_t cb, void *data) { return belle_sip_stack_resolve_a(mStack, L_STRING_TO_C(name), port, family, cb, data); } belle_sip_resolver_context_t *Sal::resolve (const string &service, const string &transport, const string &name, int port, int family, belle_sip_resolver_callback_t cb, void *data) { return belle_sip_stack_resolve(mStack, L_STRING_TO_C(service), L_STRING_TO_C(transport), L_STRING_TO_C(name), port, family, cb, data); } belle_sip_source_t *Sal::createTimer (belle_sip_source_func_t func, void *data, unsigned int timeoutValueMs, const string &timerName) { belle_sip_main_loop_t *ml = belle_sip_stack_get_main_loop(mStack); return belle_sip_main_loop_create_timeout(ml, func, data, timeoutValueMs, L_STRING_TO_C(timerName)); } void Sal::cancelTimer(belle_sip_source_t *timer) { belle_sip_main_loop_t *ml = belle_sip_stack_get_main_loop(mStack); belle_sip_main_loop_remove_source(ml, timer); } belle_sip_response_t* Sal::createResponseFromRequest (belle_sip_request_t* req, int code ) { belle_sip_response_t *resp=belle_sip_response_create_from_request(req,code); belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp),BELLE_SIP_HEADER(mUserAgentHeader)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp), mSupportedHeader); return resp; } int Sal::findCryptoIndexFromTag (const SalSrtpCryptoAlgo crypto[], unsigned char tag) { for (int i = 0; i < SAL_CRYPTO_ALGO_MAX; i++) { if (crypto[i].tag == tag) return i; } return -1; } /***********************************/ /* Global functions implementation */ /***********************************/ int to_sip_code(SalReason r) { int ret=500; switch(r){ case SalReasonNone: ret=200; break; case SalReasonIOError: ret=503; break; case SalReasonUnknown: ret=400; break; case SalReasonBusy: ret=486; break; case SalReasonDeclined: ret=603; break; case SalReasonDoNotDisturb: ret=600; break; case SalReasonForbidden: ret=403; break; case SalReasonUnsupportedContent: ret=415; break; case SalReasonNotFound: ret=404; break; case SalReasonRedirect: ret=302; break; case SalReasonTemporarilyUnavailable: ret=480; break; case SalReasonServiceUnavailable: ret=503; break; case SalReasonRequestPending: ret=491; break; case SalReasonUnauthorized: ret=401; break; case SalReasonNotAcceptable: ret=488; /*or maybe 606 Not Acceptable ?*/ break; case SalReasonNoMatch: ret=481; break; case SalReasonRequestTimeout: ret=408; break; case SalReasonMovedPermanently: ret=301; break; case SalReasonGone: ret=410; break; case SalReasonAddressIncomplete: ret=484; break; case SalReasonNotImplemented: ret=501; break; case SalReasonServerTimeout: ret=504; break; case SalReasonBadGateway: ret=502; break; case SalReasonInternalError: ret=500; break; } return ret; } LINPHONE_END_NAMESPACE /*******************************/ /* C++ to C wrapping functions */ /*******************************/ using namespace LinphonePrivate; // NOTE: This is ugly but it's not possible to export simply this set of functions in tester_utils... // Because tester_utils and private files are ill-thought. // A workaround is to use LINPHONE_PUBLIC here. extern "C" { LINPHONE_PUBLIC Sal *linphone_core_get_sal(const LinphoneCore *lc) { return lc->sal; } LINPHONE_PUBLIC SalOp *linphone_proxy_config_get_sal_op(const LinphoneProxyConfig *cfg) { return cfg->op; } LINPHONE_PUBLIC SalOp *linphone_call_get_op_as_sal_op(const LinphoneCall *call) { return linphone_call_get_op(call); } LINPHONE_PUBLIC Sal *sal_init(MSFactory *factory) { return new Sal(factory); } LINPHONE_PUBLIC void sal_uninit(Sal* sal) { delete sal; } LINPHONE_PUBLIC int sal_create_uuid(Sal *ctx, char *uuid, size_t len) { return ctx->createUuid(uuid, len); } LINPHONE_PUBLIC void sal_set_uuid(Sal *ctx, const char *uuid) { ctx->setUuid(L_C_TO_STRING(uuid)); } LINPHONE_PUBLIC void sal_default_set_sdp_handling(Sal* h, SalOpSDPHandling handling_method) { h->setDefaultSdpHandling(handling_method); } LINPHONE_PUBLIC void sal_set_send_error(Sal *sal,int value) { sal->setSendError(value); } LINPHONE_PUBLIC void sal_set_recv_error(Sal *sal,int value) { sal->setRecvError(value); } LINPHONE_PUBLIC void sal_enable_pending_trans_checking(Sal *sal, bool value) { sal->enablePendingTransactionChecking(value); } LINPHONE_PUBLIC void sal_enable_unconditional_answer(Sal *sal,int value) { sal->enableUnconditionalAnswer(value); } LINPHONE_PUBLIC void sal_set_dns_timeout(Sal* sal,int timeout) { sal->setDnsTimeout(timeout); } LINPHONE_PUBLIC void sal_set_dns_user_hosts_file(Sal *sal, const char *hosts_file) { sal->setDnsUserHostsFile(hosts_file); } LINPHONE_PUBLIC void *sal_get_stack_impl(Sal *sal) { return sal->getStackImpl(); } LINPHONE_PUBLIC void sal_set_refresher_retry_after(Sal *sal,int value) { sal->setRefresherRetryAfter(value); } LINPHONE_PUBLIC int sal_get_refresher_retry_after(const Sal *sal) { return sal->getRefresherRetryAfter(); } LINPHONE_PUBLIC void sal_set_transport_timeout(Sal* sal,int timeout) { sal->setTransportTimeout(timeout); } LINPHONE_PUBLIC void sal_enable_test_features(Sal*ctx, bool enabled) { ctx->enableTestFeatures(enabled); } LINPHONE_PUBLIC int sal_transport_available(Sal *ctx, SalTransport t) { return ctx->isTransportAvailable(t); } LINPHONE_PUBLIC const SalErrorInfo *sal_op_get_error_info(const SalOp *op) { return op->getErrorInfo(); } LINPHONE_PUBLIC bool sal_call_dialog_request_pending(const SalOp *op) { auto callOp = dynamic_cast(op); return callOp->dialogRequestPending(); } LINPHONE_PUBLIC void sal_call_set_sdp_handling(SalOp *h, SalOpSDPHandling handling) { auto callOp = dynamic_cast(h); callOp->setSdpHandling(handling); } LINPHONE_PUBLIC SalMediaDescription * sal_call_get_final_media_description(SalOp *h) { auto callOp = dynamic_cast(h); return callOp->getFinalMediaDescription(); } LINPHONE_PUBLIC belle_sip_resolver_context_t *sal_resolve_a(Sal *sal, const char *name, int port, int family, belle_sip_resolver_callback_t cb, void *data) { return sal->resolveA(name, port, family, cb, data); } LINPHONE_PUBLIC Sal *sal_op_get_sal(SalOp *op) { return op->getSal(); } LINPHONE_PUBLIC SalOp *sal_create_refer_op(Sal *sal) { return new SalReferOp(sal); } LINPHONE_PUBLIC void sal_release_op(SalOp *op) { op->release(); } LINPHONE_PUBLIC void sal_op_set_from(SalOp *sal_refer_op, const char* from) { auto referOp = dynamic_cast(sal_refer_op); referOp->setFrom(from); } LINPHONE_PUBLIC void sal_op_set_to(SalOp *sal_refer_op, const char* to) { auto referOp = dynamic_cast(sal_refer_op); referOp->setTo(to); } LINPHONE_PUBLIC void sal_op_send_refer(SalOp *sal_refer_op, SalAddress* refer_to) { auto referOp = dynamic_cast(sal_refer_op); referOp->sendRefer(refer_to); } LINPHONE_PUBLIC void sal_set_user_pointer(Sal *sal, void *user_pointer) { sal->setUserPointer(user_pointer); } LINPHONE_PUBLIC void *sal_get_user_pointer(Sal *sal) { return sal->getUserPointer(); } LINPHONE_PUBLIC void sal_set_call_refer_callback(Sal *sal, void (*OnReferCb)(SalOp *op, const SalAddress *referto)) { struct Sal::Callbacks cbs = {NULL}; cbs.refer_received = OnReferCb; sal->setCallbacks(&cbs); } }