diff --git a/src/im/instant_messaging.cpp b/src/im/instant_messaging.cpp index b9057ee1f7f857bc46e156ff79160a4e12bedb03..bbf90bd3bce6efa6e932436862adf32084ce8ba8 100644 --- a/src/im/instant_messaging.cpp +++ b/src/im/instant_messaging.cpp @@ -87,7 +87,7 @@ createMessageBody(pj_pool_t* pool, // split paramPair into arg and value by '=' auto paramSplit = paramPair.find('='); if (std::string::npos == paramSplit) { - JAMI_DBG("bad parameter: '%.*s'", (int)paramPair.size(), paramPair.data()); + JAMI_DBG("bad parameter: '%.*s'", (int) paramPair.size(), paramPair.data()); throw im::InstantMessageException("invalid parameter"); } @@ -149,7 +149,8 @@ im::sendSipMessage(pjsip_inv_session* session, const std::map<std::string, std:: return; } - constexpr pjsip_method msg_method = {PJSIP_OTHER_METHOD, CONST_PJ_STR("MESSAGE")}; + constexpr pjsip_method msg_method = {PJSIP_OTHER_METHOD, + CONST_PJ_STR(sip_utils::SIP_METHODS::MESSAGE)}; { auto dialog = session->dlg; @@ -183,8 +184,8 @@ im::sendSipMessage(pjsip_inv_session* session, const std::map<std::string, std:: static std::pair<std::string, std::string> parseMessageBody(const pjsip_msg_body* body) { - std::string header = sip_utils::as_view(body->content_type.type) - + "/" + sip_utils::as_view(body->content_type.subtype); + std::string header = sip_utils::as_view(body->content_type.type) + "/" + + sip_utils::as_view(body->content_type.subtype); // iterate over parameters auto param = body->content_type.param.next; @@ -194,8 +195,7 @@ parseMessageBody(const pjsip_msg_body* body) } // get the payload, assume we can interpret it as chars - return {std::move(header), - std::string(static_cast<char*>(body->data), (size_t)body->len)}; + return {std::move(header), std::string(static_cast<char*>(body->data), (size_t) body->len)}; } /** diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp index 77c57ec61b9a4ed6dfe7f7f66484d70a40c62d93..8e9fe1da92966a9859049a821b183029865ae138 100644 --- a/src/jamidht/jamiaccount.cpp +++ b/src/jamidht/jamiaccount.cpp @@ -3937,7 +3937,8 @@ JamiAccount::sendSIPMessage(SipConnection& conn, pjsip_tx_data* tdata; // Build SIP message - constexpr pjsip_method msg_method = {PJSIP_OTHER_METHOD, sip_utils::CONST_PJ_STR("MESSAGE")}; + constexpr pjsip_method msg_method = {PJSIP_OTHER_METHOD, + sip_utils::CONST_PJ_STR(sip_utils::SIP_METHODS::MESSAGE)}; pj_str_t pjFrom = sip_utils::CONST_PJ_STR(from); pj_str_t pjTo = sip_utils::CONST_PJ_STR(toURI); diff --git a/src/manager.cpp b/src/manager.cpp index b47ccfb36847b1f1c6d15e97ea376d84c9fa8fe2..870336ca653a45dbe99ce7ff1d5fdbffc8a9cfc4 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -1950,7 +1950,7 @@ Manager::sendCallTextMessage(const std::string& accountId, } } } else { - JAMI_ERR("Failed to send message to %s: inexistant call ID", callID.c_str()); + JAMI_ERR("Failed to send message to %s: inexistent call ID", callID.c_str()); } } diff --git a/src/sip/sip_utils.cpp b/src/sip/sip_utils.cpp index edfed41bed70672a15e4895ad70fdf7276a26396..9dd46906c2f871d4d5d7c6ee23565ee1581304df 100644 --- a/src/sip/sip_utils.cpp +++ b/src/sip/sip_utils.cpp @@ -223,7 +223,7 @@ std::string_view getPeerUserAgent(const pjsip_rx_data* rdata) { if (rdata == nullptr or rdata->msg_info.msg == nullptr) { - JAMI_ERR("Unexpected null poiter!"); + JAMI_ERR("Unexpected null pointer!"); return {}; } @@ -236,6 +236,29 @@ getPeerUserAgent(const pjsip_rx_data* rdata) return {}; } +std::vector<std::string> +getPeerAllowMethods(const pjsip_rx_data* rdata) +{ + if (rdata == nullptr or rdata->msg_info.msg == nullptr) { + JAMI_ERR("Unexpected null pointer!"); + return {}; + } + + std::vector<std::string> methods; + + pjsip_allow_hdr* allow = static_cast<pjsip_allow_hdr*>( + pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ALLOW, nullptr)); + + if (allow != nullptr) { + methods.reserve(allow->count); + for (unsigned i = 0; i < allow->count; i++) { + methods.emplace_back(allow->values[i].ptr, allow->values[i].slen); + } + } + + return methods; +} + void logMessageHeaders(const pjsip_hdr* hdr_list) { diff --git a/src/sip/sip_utils.h b/src/sip/sip_utils.h index 9b72323fac12754fbdd1bf2c941fc40fb2276c96..ffe68f42adb31f65820fa87638cd2541af9d5b7b 100644 --- a/src/sip/sip_utils.h +++ b/src/sip/sip_utils.h @@ -39,6 +39,18 @@ namespace jami { namespace sip_utils { +// SIP methods. Only list methods that need to be explicitly +// handled + +namespace SIP_METHODS { +constexpr std::string_view MESSAGE = "MESSAGE"; +constexpr std::string_view INFO = "INFO"; +constexpr std::string_view OPTIONS = "OPTIONS"; +constexpr std::string_view PUBLISH = "PUBLISH"; +constexpr std::string_view REFER = "REFER"; +constexpr std::string_view NOTIFY = "NOTIFY"; +} // namespace SIP_METHODS + static constexpr int DEFAULT_SIP_PORT {5060}; static constexpr int DEFAULT_SIP_TLS_PORT {5061}; static constexpr int DEFAULT_AUTO_SELECT_PORT {0}; @@ -99,6 +111,7 @@ std::string_view getHostFromUri(std::string_view sipUri); void addContactHeader(const std::string& contact, pjsip_tx_data* tdata); void addUserAgentHeader(const std::string& userAgent, pjsip_tx_data* tdata); std::string_view getPeerUserAgent(const pjsip_rx_data* rdata); +std::vector<std::string> getPeerAllowMethods(const pjsip_rx_data* rdata); void logMessageHeaders(const pjsip_hdr* hdr_list); std::string sip_strerror(pj_status_t code); diff --git a/src/sip/sipaccount.cpp b/src/sip/sipaccount.cpp index ac92eae687e3ead235b0495663fbbed318f5ddfa..0c3193014890edda93da48e8b488ad9c9cb91d60 100644 --- a/src/sip/sipaccount.cpp +++ b/src/sip/sipaccount.cpp @@ -2243,7 +2243,8 @@ SIPAccount::sendTextMessage(const std::string& to, auto toUri = getToUri(to); - constexpr pjsip_method msg_method = {PJSIP_OTHER_METHOD, CONST_PJ_STR("MESSAGE")}; + constexpr pjsip_method msg_method = {PJSIP_OTHER_METHOD, + CONST_PJ_STR(sip_utils::SIP_METHODS::MESSAGE)}; std::string from(getFromUri()); pj_str_t pjFrom = sip_utils::CONST_PJ_STR(from); pj_str_t pjTo = sip_utils::CONST_PJ_STR(toUri); diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp index bf245c80f10fb4112cab5275da0a25d65c7db4b6..02a90e8eae34184fe1f67fcf318b2c22552cb662 100644 --- a/src/sip/sipcall.cpp +++ b/src/sip/sipcall.cpp @@ -1460,8 +1460,29 @@ SIPCall::sendTextMessage(const std::map<std::string, std::string>& messages, con } else { if (inviteSession_) { try { + // Ignore if the peer does not allow "MESSAGE" SIP method + // NOTE: + // The SIP "Allow" header is not mandatory as per RFC-3261. If it's + // not present and since "MESSAGE" method is an extention method, + // we choose to assume that the peer does not support the "MESSAGE" + // method to prevent unexpected behavior when interoperating with + // some SIP implementations. + if (not isSipMethodAllowedByPeer(sip_utils::SIP_METHODS::MESSAGE)) { + JAMI_WARN() << fmt::format("[call:{}] Peer does not allow \"{}\" method]", + getCallId(), + sip_utils::SIP_METHODS::MESSAGE); + + // Print peer's allowed methods + JAMI_INFO() << fmt::format("[call:{}] Peer's allowed methods: {}", + getCallId(), + peerAllowedMethods_); + return; + } + im::sendSipMessage(inviteSession_.get(), messages); + } catch (...) { + JAMI_ERR("[call:%s] Failed to send SIP text message", getCallId().c_str()); } } else { pendingOutMessages_.emplace_back(messages, from); @@ -1650,6 +1671,22 @@ SIPCall::setPeerUaVersion(std::string_view ua) } } +void +SIPCall::setPeerAllowMethods(std::vector<std::string> methods) +{ + std::lock_guard<std::recursive_mutex> lock {callMutex_}; + peerAllowedMethods_ = std::move(methods); +} + +bool +SIPCall::isSipMethodAllowedByPeer(const std::string_view method) const +{ + std::lock_guard<std::recursive_mutex> lock {callMutex_}; + + return std::find(peerAllowedMethods_.begin(), peerAllowedMethods_.end(), method) + != peerAllowedMethods_.end(); +} + void SIPCall::onPeerRinging() { @@ -3182,6 +3219,7 @@ SIPCall::merge(Call& call) localVideoPort_ = subcall.localVideoPort_; peerUserAgent_ = subcall.peerUserAgent_; peerSupportMultiStream_ = subcall.peerSupportMultiStream_; + peerAllowedMethods_ = subcall.peerAllowedMethods_; Call::merge(subcall); if (isIceEnabled()) @@ -3234,8 +3272,8 @@ SIPCall::setupIceResponse() if (not remoteHasValidIceAttributes()) { // If ICE attributes are not present, skip the ICE initialization - // step (most likely ICE is not used). - JAMI_ERR("[call:%s] no ICE data in remote SDP", getCallId().c_str()); + // step (most likely peer does not support/enable ICE). + JAMI_WARN("[call:%s] no ICE data in remote SDP", getCallId().c_str()); return; } diff --git a/src/sip/sipcall.h b/src/sip/sipcall.h index 303bfb1a0691d48e83bd70c15b63a2f8a51995ba..d89f4e03b278c6005872d54511b79acc857cea67 100644 --- a/src/sip/sipcall.h +++ b/src/sip/sipcall.h @@ -178,6 +178,16 @@ public: */ void setPeerUaVersion(std::string_view ua); + /** + * Set peer's allowed methods + */ + void setPeerAllowMethods(std::vector<std::string> methods); + + /** + * Check if a SIP method is allowed by peer + */ + bool isSipMethodAllowedByPeer(const std::string_view method) const; + /** * Return the SDP's manager of this call */ @@ -415,6 +425,9 @@ private: // Flag to indicate the the peer's Daemon version support multi-stream. bool peerSupportMultiStream_ {false}; + // Peer's allowed methods. + std::vector<std::string> peerAllowedMethods_; + // Vector holding the current RTP sessions. std::vector<RtpStream> rtpStreams_; diff --git a/src/sip/sipvoiplink.cpp b/src/sip/sipvoiplink.cpp index 5c2d7975f7eb8f472edbb4f0449370d398d76a4d..aad68002c620a54f6ba06c3ea299dcee406b3bc1 100644 --- a/src/sip/sipvoiplink.cpp +++ b/src/sip/sipvoiplink.cpp @@ -299,7 +299,7 @@ transaction_request_cb(pjsip_rx_data* rdata) if (method->id == PJSIP_OTHER_METHOD) { std::string_view request = sip_utils::as_view(method->name); - if (request.find("NOTIFY") != std::string_view::npos) { + if (request.find(sip_utils::SIP_METHODS::NOTIFY) != std::string_view::npos) { if (body and body->data) { std::string_view body_view(static_cast<char*>(body->data), body->len); auto pos = body_view.find("Voice-Message: "); @@ -323,7 +323,7 @@ transaction_request_cb(pjsip_rx_data* rdata) urgentCount); } } - } else if (request.find("MESSAGE") != std::string_view::npos) { + } else if (request.find(sip_utils::SIP_METHODS::MESSAGE) != std::string_view::npos) { // Reply 200 immediately (RFC 3428, ch. 7) try_respond_stateless(endpt_, rdata, PJSIP_SC_OK, nullptr, nullptr, nullptr); // Process message content in case of multi-part body @@ -452,6 +452,7 @@ transaction_request_cb(pjsip_rx_data* rdata) call->setPeerDisplayName(peerDisplayName); call->setState(Call::ConnectionState::PROGRESSING); call->getSDP().setPublishedIP(addrSdp); + call->setPeerAllowMethods(sip_utils::getPeerAllowMethods(rdata)); // Set the temporary media list. Might change when we receive // the accept from the client. @@ -714,10 +715,10 @@ SIPVoIPLink::SIPVoIPLink() TRY(pjsip_inv_usage_init(endpt_, &inv_cb)); static constexpr pj_str_t allowed[] = { - CONST_PJ_STR("INFO"), - CONST_PJ_STR("OPTIONS"), - CONST_PJ_STR("MESSAGE"), - CONST_PJ_STR("PUBLISH"), + CONST_PJ_STR(sip_utils::SIP_METHODS::INFO), + CONST_PJ_STR(sip_utils::SIP_METHODS::OPTIONS), + CONST_PJ_STR(sip_utils::SIP_METHODS::MESSAGE), + CONST_PJ_STR(sip_utils::SIP_METHODS::PUBLISH), }; pjsip_endpt_add_capability(endpt_, @@ -911,11 +912,15 @@ invite_session_state_changed_cb(pjsip_inv_session* inv, pjsip_event* ev) pjsip_inv_state_name(inv->state), inv->cause); } - + pjsip_rx_data* rdata {nullptr}; if (ev->type == PJSIP_EVENT_RX_MSG) { - call->setPeerUaVersion(sip_utils::getPeerUserAgent(ev->body.rx_msg.rdata)); + rdata = ev->body.rx_msg.rdata; } else if (ev->type == PJSIP_EVENT_TSX_STATE and ev->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { - call->setPeerUaVersion(sip_utils::getPeerUserAgent(ev->body.tsx_state.src.rdata)); + rdata = ev->body.tsx_state.src.rdata; + } + if (rdata != nullptr) { + call->setPeerUaVersion(sip_utils::getPeerUserAgent(rdata)); + call->setPeerAllowMethods(sip_utils::getPeerAllowMethods(rdata)); } switch (inv->state) { @@ -1348,15 +1353,15 @@ transaction_state_changed_cb(pjsip_inv_session* inv, pjsip_transaction* tsx, pjs JAMI_DBG("%s", msgbuf); #endif // DEBUG_SIP_MESSAGE - if (methodName == "REFER") + if (methodName == sip_utils::SIP_METHODS::REFER) onRequestRefer(inv, rdata, msg, *call); - else if (methodName == "INFO") + else if (methodName == sip_utils::SIP_METHODS::INFO) onRequestInfo(inv, rdata, msg, *call); - else if (methodName == "NOTIFY") + else if (methodName == sip_utils::SIP_METHODS::NOTIFY) onRequestNotify(inv, rdata, msg, *call); - else if (methodName == "OPTIONS") + else if (methodName == sip_utils::SIP_METHODS::OPTIONS) handleIncomingOptions(rdata); - else if (methodName == "MESSAGE") { + else if (methodName == sip_utils::SIP_METHODS::MESSAGE) { if (msg->body) runOnMainThread([call, m = im::parseSipMessage(msg)]() mutable { call->onTextMessage(std::move(m));