From 3754af14367e447f67e946ed1a0d21860762ae2f Mon Sep 17 00:00:00 2001 From: Mohamed Chibani <mohamed.chibani@savoirfairelinux.com> Date: Thu, 3 Sep 2020 15:07:38 -0400 Subject: [PATCH] user-agent: add user-agent header Add user-agent header to most relevant SIP messages (INVITE, RINGING, OK, ...). The user-agent string is generated from the build version. The user-agent header is useful to identify build version used by the peer. Change-Id: Ic99263ca147d3f96c3c092be2ff3d723bdb6fe6f Gitlab: #287 --- src/account.cpp | 44 ++++++++++++++++++++++---------- src/account.h | 17 ++++++++++--- src/buildinfo.cpp | 21 +++++++++++++++ src/dring/dring.h | 5 ++++ src/jamidht/jamiaccount.cpp | 15 +++++------ src/sip/sip_utils.cpp | 43 +++++++++++++++++++++++++++++++ src/sip/sip_utils.h | 4 ++- src/sip/sipaccount.cpp | 27 +++++--------------- src/sip/sipaccount.h | 1 - src/sip/sipcall.cpp | 11 ++++++++ src/sip/sipvoiplink.cpp | 51 ++++++++++++++++++++++++++++++++++--- 11 files changed, 189 insertions(+), 50 deletions(-) diff --git a/src/account.cpp b/src/account.cpp index 12ea361e1c..981fb81b95 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -81,12 +81,12 @@ const char* const Account::ACCOUNT_AUTOANSWER_KEY = "autoAnswer"; const char* const Account::ACCOUNT_ISRENDEZVOUS_KEY = "rendezVous"; const char* const Account::ACCOUNT_ACTIVE_CALL_LIMIT_KEY = "activeCallLimit"; const char* const Account::MAILBOX_KEY = "mailbox"; -const char* const Account::DEFAULT_USER_AGENT = PACKAGE_NAME; const char* const Account::USER_AGENT_KEY = "useragent"; const char* const Account::HAS_CUSTOM_USER_AGENT_KEY = "hasCustomUserAgent"; const char* const Account::PRESENCE_MODULE_ENABLED_KEY = "presenceModuleEnabled"; const char* const Account::UPNP_ENABLED_KEY = "upnpEnabled"; const char* const Account::ACTIVE_CODEC_KEY = "activeCodecs"; +const std::string Account::DEFAULT_USER_AGENT = Account::setDefaultUserAgent(); #ifdef __ANDROID__ constexpr const char* const DEFAULT_RINGTONE_PATH @@ -107,16 +107,15 @@ Account::Account(const std::string& accountID) , registrationState_(RegistrationState::UNREGISTERED) , systemCodecContainer_(getSystemCodecContainer()) , accountCodecInfoList_() - , ringtonePath_("") + , ringtonePath_(DEFAULT_RINGTONE_PATH) , ringtoneEnabled_(true) , displayName_("") - , userAgent_(DEFAULT_USER_AGENT) + , customUserAgent_("") , hasCustomUserAgent_(false) , mailBox_() { // Initialize the codec order, used when creating a new account loadDefaultCodecs(); - ringtonePath_ = DEFAULT_RINGTONE_PATH; } Account::~Account() {} @@ -233,7 +232,7 @@ Account::serialize(YAML::Emitter& out) const out << YAML::Key << RINGTONE_ENABLED_KEY << YAML::Value << ringtoneEnabled_; out << YAML::Key << RINGTONE_PATH_KEY << YAML::Value << ringtonePath_; out << YAML::Key << HAS_CUSTOM_USER_AGENT_KEY << YAML::Value << hasCustomUserAgent_; - out << YAML::Key << USER_AGENT_KEY << YAML::Value << userAgent_; + out << YAML::Key << USER_AGENT_KEY << YAML::Value << customUserAgent_; out << YAML::Key << DISPLAY_NAME_KEY << YAML::Value << displayName_; out << YAML::Key << HOSTNAME_KEY << YAML::Value << hostname_; out << YAML::Key << UPNP_ENABLED_KEY << YAML::Value << bool(upnp_); @@ -278,7 +277,7 @@ Account::unserialize(const YAML::Node& node) parseValue(node, HOSTNAME_KEY, hostname_); parseValue(node, HAS_CUSTOM_USER_AGENT_KEY, hasCustomUserAgent_); - parseValue(node, USER_AGENT_KEY, userAgent_); + parseValue(node, USER_AGENT_KEY, customUserAgent_); parseValue(node, RINGTONE_PATH_KEY, ringtonePath_); parseValue(node, RINGTONE_ENABLED_KEY, ringtoneEnabled_); if (ringtonePath_.empty()) { @@ -299,7 +298,6 @@ Account::setAccountDetails(const std::map<std::string, std::string>& details) parseBool(details, Conf::CONFIG_ACCOUNT_ENABLE, enabled_); parseString(details, Conf::CONFIG_ACCOUNT_HOSTNAME, hostname_); parseString(details, Conf::CONFIG_ACCOUNT_MAILBOX, mailBox_); - parseString(details, Conf::CONFIG_ACCOUNT_USERAGENT, userAgent_); parseBool(details, Conf::CONFIG_ACCOUNT_AUTOANSWER, autoAnswerEnabled_); parseBool(details, Conf::CONFIG_ACCOUNT_ISRENDEZVOUS, isRendezVous_); parseInt(details, DRing::Account::ConfProperties::ACTIVE_CALL_LIMIT, activeCallLimit_); @@ -310,9 +308,8 @@ Account::setAccountDetails(const std::map<std::string, std::string>& details) } parseBool(details, Conf::CONFIG_ACCOUNT_HAS_CUSTOM_USERAGENT, hasCustomUserAgent_); if (hasCustomUserAgent_) - parseString(details, Conf::CONFIG_ACCOUNT_USERAGENT, userAgent_); - else - userAgent_ = DEFAULT_USER_AGENT; + parseString(details, Conf::CONFIG_ACCOUNT_USERAGENT, customUserAgent_); + bool enabled; parseBool(details, Conf::CONFIG_UPNP_ENABLED, enabled); enableUpnp(enabled && isEnabled()); @@ -328,9 +325,8 @@ Account::getAccountDetails() const {Conf::CONFIG_ACCOUNT_HOSTNAME, hostname_}, {Conf::CONFIG_ACCOUNT_USERNAME, username_}, {Conf::CONFIG_ACCOUNT_MAILBOX, mailBox_}, - {Conf::CONFIG_ACCOUNT_USERAGENT, hasCustomUserAgent_ ? userAgent_ : DEFAULT_USER_AGENT}, - {Conf::CONFIG_ACCOUNT_HAS_CUSTOM_USERAGENT, - hasCustomUserAgent_ ? userAgent_ : DEFAULT_USER_AGENT}, + {Conf::CONFIG_ACCOUNT_USERAGENT, customUserAgent_}, + {Conf::CONFIG_ACCOUNT_HAS_CUSTOM_USERAGENT, hasCustomUserAgent_ ? TRUE_STR : FALSE_STR}, {Conf::CONFIG_ACCOUNT_AUTOANSWER, autoAnswerEnabled_ ? TRUE_STR : FALSE_STR}, {Conf::CONFIG_ACCOUNT_ISRENDEZVOUS, isRendezVous_ ? TRUE_STR : FALSE_STR}, {DRing::Account::ConfProperties::ACTIVE_CALL_LIMIT, std::to_string(activeCallLimit_)}, @@ -661,4 +657,26 @@ Account::getIceOptions() const noexcept return opts; } +const std::string& +Account::getUserAgentName() +{ + if (hasCustomUserAgent_ and not customUserAgent_.empty()) + return customUserAgent_; + return DEFAULT_USER_AGENT; +} + +std::string +Account::setDefaultUserAgent() +{ + // Build the default user-agent string + std::string defaultUA; + defaultUA.append(PACKAGE_NAME); + defaultUA.append(" "); + defaultUA.append(DRing::version()); + defaultUA.append(" ("); + defaultUA.append(DRing::platform()); + defaultUA.append(")"); + + return defaultUA; +} } // namespace jami diff --git a/src/account.h b/src/account.h index a4c0d60826..40d91bc73b 100644 --- a/src/account.h +++ b/src/account.h @@ -309,6 +309,11 @@ public: std::vector<unsigned> convertIdToAVId(const std::vector<unsigned>& list); + /** + * Get the user-agent + */ + const std::string& getUserAgentName(); + public: // virtual methods that has to be implemented by concrete classes /** * This method is called to request removal of possible account traces on the system, @@ -374,7 +379,6 @@ protected: static const char* const MAILBOX_KEY; static const char* const USER_AGENT_KEY; static const char* const HAS_CUSTOM_USER_AGENT_KEY; - static const char* const DEFAULT_USER_AGENT; static const char* const PRESENCE_MODULE_ENABLED_KEY; static const char* const UPNP_ENABLED_KEY; static const char* const PROXY_ENABLED_KEY; @@ -382,8 +386,15 @@ protected: static const char* const PROXY_PUSH_TOKEN_KEY; static const char* const ACTIVE_CODEC_KEY; + static const std::string DEFAULT_USER_AGENT; + static std::string mapStateNumberToString(RegistrationState state); + /** + * Build the user-agent string + */ + static std::string setDefaultUserAgent(); + /** * Account ID are assign in constructor and shall not changed */ @@ -472,9 +483,9 @@ protected: std::string displayName_; /** - * Useragent used for registration + * User-agent used for registration */ - std::string userAgent_; + std::string customUserAgent_; // true if user has overridden default bool hasCustomUserAgent_; diff --git a/src/buildinfo.cpp b/src/buildinfo.cpp index 79646499be..a501b8b225 100644 --- a/src/buildinfo.cpp +++ b/src/buildinfo.cpp @@ -49,4 +49,25 @@ version() noexcept : (RING_REVISION[0] ? PACKAGE_VERSION "-" RING_REVISION : PACKAGE_VERSION); } +const char* +platform() noexcept +{ +#ifdef __linux__ + #if defined(__ANDROID__) + return "android"; + #else + return "linux"; + #endif +#elif defined(_WIN32) + return "win32"; +#elif defined(__APPLE__) + #ifdef TARGET_OS_IOS + return "iOS"; + #else + return "macOS"; + #endif +#else + #error "Unknown OS" +#endif +} } // namespace DRing diff --git a/src/dring/dring.h b/src/dring/dring.h index 24b132fc1e..0c21e56a17 100644 --- a/src/dring/dring.h +++ b/src/dring/dring.h @@ -44,6 +44,11 @@ enum InitFlag { */ DRING_PUBLIC const char* version() noexcept; +/** + * Return the target platform (OS) as a string. + */ +DRING_PUBLIC const char* platform() noexcept; + /** * Initialize globals, create underlaying daemon. * diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp index 5111c390d6..e5d95fff96 100644 --- a/src/jamidht/jamiaccount.cpp +++ b/src/jamidht/jamiaccount.cpp @@ -872,6 +872,10 @@ JamiAccount::SIPStartCall(SIPCall& call, IpAddr target) } JAMI_DBG("[call:%s] Sending SIP invite", call.getCallId().c_str()); + + // Add user-agent header + sip_utils::addUserAgenttHeader(getUserAgentName(), tdata); + if (pjsip_inv_send_msg(call.inv.get(), tdata) != PJ_SUCCESS) { JAMI_ERR("Unable to send invite message for this call"); return false; @@ -3681,15 +3685,8 @@ JamiAccount::sendSIPMessage(SipConnection& conn, pjsip_generic_string_hdr_create(tdata->pool, &STR_MESSAGE_ID, &pjMessageId)); pjsip_msg_add_hdr(tdata->msg, hdr); - // Add user agent header. - pjsip_hdr* hdr_list; - constexpr auto pJuseragent = sip_utils::CONST_PJ_STR("Jami"); - constexpr pj_str_t STR_USER_AGENT = jami::sip_utils::CONST_PJ_STR("User-Agent"); - - // Add Header - hdr_list = reinterpret_cast<pjsip_hdr*>( - pjsip_user_agent_hdr_create(tdata->pool, &STR_USER_AGENT, &pJuseragent)); - pjsip_msg_add_hdr(tdata->msg, hdr_list); + // Add user-agent header + sip_utils::addUserAgenttHeader(getUserAgentName(), tdata); // Init tdata const pjsip_tpselector tp_sel = SIPVoIPLink::getTransportSelector(transport->get()); diff --git a/src/sip/sip_utils.cpp b/src/sip/sip_utils.cpp index 93c5d3c973..16029b907c 100644 --- a/src/sip/sip_utils.cpp +++ b/src/sip/sip_utils.cpp @@ -184,6 +184,49 @@ addContactHeader(const pj_str_t* contact_str, pjsip_tx_data* tdata) pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) contact); } +void +addUserAgenttHeader(const std::string& userAgent, pjsip_tx_data* tdata) +{ + if (tdata == nullptr) + return; + + pj_str_t pjUserAgent = pj_str((char*)userAgent.c_str()); + constexpr pj_str_t STR_USER_AGENT = jami::sip_utils::CONST_PJ_STR("User-Agent"); + + // Do nothing if user-agent header is present. + pjsip_hdr* hdr = static_cast<pjsip_hdr*>(pjsip_msg_find_hdr_by_name(tdata->msg, &STR_USER_AGENT, nullptr)); + if (hdr != nullptr) { + return; + } + + // Add Header + hdr = reinterpret_cast<pjsip_hdr*>( + pjsip_user_agent_hdr_create(tdata->pool, &STR_USER_AGENT, &pjUserAgent)); + + if (hdr != nullptr) { + JAMI_DBG("Add header to SIP message: \"%.*s: %.*s\"", (int) hdr->name.slen, hdr->name.ptr, + (int) pjUserAgent.slen, pjUserAgent.ptr); + pjsip_msg_add_hdr(tdata->msg, hdr); + } +} + +void logMessageHeaders(const pjsip_hdr* hdr_list) +{ + const pjsip_hdr* hdr = hdr_list->next; + const pjsip_hdr* end = hdr_list; + std::string msgHdrStr("Message headers:\n"); + for (; hdr != end; hdr = hdr->next) { + char buf[1024]; + int size = pjsip_hdr_print_on((void*)hdr, buf, sizeof(buf)); + if (size > 0) { + msgHdrStr.append(buf, size); + msgHdrStr.push_back('\n'); + } + } + + JAMI_INFO("%.*s", (int) msgHdrStr.size(), msgHdrStr.c_str()); +} + std::string sip_strerror(pj_status_t code) { diff --git a/src/sip/sip_utils.h b/src/sip/sip_utils.h index cfa9bdfe9f..9425c1233d 100644 --- a/src/sip/sip_utils.h +++ b/src/sip/sip_utils.h @@ -99,6 +99,8 @@ std::string parseDisplayName(const pjsip_contact_hdr* header); std::string getHostFromUri(const std::string& sipUri); void addContactHeader(const pj_str_t* contactStr, pjsip_tx_data* tdata); +void addUserAgenttHeader(const std::string& userAgent, pjsip_tx_data* tdata); +void logMessageHeaders(const pjsip_hdr* hdr_list); std::string sip_strerror(pj_status_t code); @@ -109,7 +111,7 @@ std::string sip_strerror(pj_status_t code); void register_thread(); // Helper function that return a constant pj_str_t from an array of any types -// that may be staticaly casted into char pointer. +// that may be statically casted into char pointer. // Per convention, the input array is supposed to be null terminated. template<typename T, std::size_t N> constexpr const pj_str_t CONST_PJ_STR(T (&a)[N]) noexcept diff --git a/src/sip/sipaccount.cpp b/src/sip/sipaccount.cpp index fbae6850da..4c5f00dbde 100644 --- a/src/sip/sipaccount.cpp +++ b/src/sip/sipaccount.cpp @@ -409,6 +409,9 @@ SIPAccount::SIPStartCall(std::shared_ptr<SIPCall>& call) return false; } + // Add user-agent header + sip_utils::addUserAgenttHeader(getUserAgentName(), tdata); + if (pjsip_inv_send_msg(call->inv.get(), tdata) != PJ_SUCCESS) { JAMI_ERR("Unable to send invite message for this call"); return false; @@ -1052,13 +1055,12 @@ SIPAccount::sendRegister() pjsip_hdr hdr_list; pj_list_init(&hdr_list); - std::string useragent(getUserAgentName()); - auto pJuseragent = CONST_PJ_STR(useragent); + auto pjUserAgent = CONST_PJ_STR(getUserAgentName()); constexpr pj_str_t STR_USER_AGENT = CONST_PJ_STR("User-Agent"); pjsip_generic_string_hdr* h = pjsip_generic_string_hdr_create(link_.getPool(), &STR_USER_AGENT, - &pJuseragent); + &pjUserAgent); pj_list_push_back(&hdr_list, (pjsip_hdr*) h); pjsip_regc_add_headers(regc, &hdr_list); @@ -1724,14 +1726,6 @@ SIPAccount::setRegistrationState(RegistrationState state, SIPAccountBase::setRegistrationState(state, details_code, details_str); } -std::string -SIPAccount::getUserAgentName() const -{ - if (not hasCustomUserAgent_ or userAgent_.empty()) - return DEFAULT_USER_AGENT; - return userAgent_; -} - std::map<std::string, std::string> SIPAccount::getTlsSettings() const { @@ -2168,15 +2162,8 @@ SIPAccount::sendTextMessage(const std::string& to, pjsip_date_hdr_create(tdata->pool, &key, pj_cstr(&date_str, date))); pjsip_msg_add_hdr(tdata->msg, hdr); - /* Add user agent header. */ - pjsip_hdr* hdr_list; - auto pJuseragent = CONST_PJ_STR(getUserAgentName()); - constexpr pj_str_t STR_USER_AGENT = CONST_PJ_STR("User-Agent"); - - // Add Header - hdr_list = reinterpret_cast<pjsip_hdr*>( - pjsip_user_agent_hdr_create(tdata->pool, &STR_USER_AGENT, &pJuseragent)); - pjsip_msg_add_hdr(tdata->msg, hdr_list); + // Add user-agent header + sip_utils::addUserAgenttHeader(getUserAgentName(), tdata); // Set input token into callback std::unique_ptr<ctx> t {new ctx(new pjsip_auth_clt_sess)}; diff --git a/src/sip/sipaccount.h b/src/sip/sipaccount.h index 27978f3a03..1c5ae07e11 100644 --- a/src/sip/sipaccount.h +++ b/src/sip/sipaccount.h @@ -99,7 +99,6 @@ public: pjsip_host_port getHostPortFromSTUN(pj_pool_t* pool); - std::string getUserAgentName() const; void setRegistrationStateDetailed(const std::pair<int, std::string>& details) { registrationStateDetailed_ = details; diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp index 222aa60a83..3e04140e9d 100644 --- a/src/sip/sipcall.cpp +++ b/src/sip/sipcall.cpp @@ -286,6 +286,10 @@ SIPCall::SIPSessionReinvite() if (result == PJ_SUCCESS) { if (!tdata) return PJ_SUCCESS; + + // Add user-agent header + sip_utils::addUserAgenttHeader(getAccount().getUserAgentName(), tdata); + result = pjsip_inv_send_msg(inv.get(), tdata); if (result == PJ_SUCCESS) return PJ_SUCCESS; @@ -394,6 +398,10 @@ SIPCall::terminateSipSession(int status) auto contact = getSIPAccount().getContactHeader(transport_ ? transport_->get() : nullptr); sip_utils::addContactHeader(&contact, tdata); + + // Add user-agent header + sip_utils::addUserAgenttHeader(getAccount().getUserAgentName(), tdata); + ret = pjsip_inv_send_msg(inv.get(), tdata); if (ret != PJ_SUCCESS) JAMI_ERR("[call:%s] failed to send terminate msg, SIP error (%s)", @@ -455,6 +463,9 @@ SIPCall::answer() sip_utils::addContactHeader(&contactHeader_, tdata); } + // Add user-agent header + sip_utils::addUserAgenttHeader(account.getUserAgentName(), tdata); + if (pjsip_inv_send_msg(inv.get(), tdata) != PJ_SUCCESS) { inv.reset(); throw std::runtime_error("Could not send invite request answer (200 OK)"); diff --git a/src/sip/sipvoiplink.cpp b/src/sip/sipvoiplink.cpp index 8c0dc4ed17..892d655628 100644 --- a/src/sip/sipvoiplink.cpp +++ b/src/sip/sipvoiplink.cpp @@ -83,6 +83,7 @@ static void transaction_state_changed_cb(pjsip_inv_session* inv, pjsip_transaction* tsx, pjsip_event* e); static std::shared_ptr<SIPCall> getCallFromInvite(pjsip_inv_session* inv); +static void processInviteResponseHelper(pjsip_inv_session* inv, pjsip_event* e); static void handleIncomingOptions(pjsip_rx_data* rdata) @@ -112,7 +113,7 @@ handleIncomingOptions(pjsip_rx_data* rdata) pjsip_tx_data_dec_ref(tdata); } -// return PJ_FALSE so that eventuall other modules will handle these requests +// return PJ_FALSE so that eventually other modules will handle these requests // TODO: move Voicemail to separate module static pj_bool_t transaction_response_cb(pjsip_rx_data* rdata) @@ -295,6 +296,12 @@ transaction_request_cb(pjsip_rx_data* rdata) return PJ_FALSE; } + if (method->id == PJSIP_INVITE_METHOD) { + // Log headers of received INVITE + JAMI_INFO("Received a SIP INVITE request"); + sip_utils::logMessageHeaders(&rdata->msg_info.msg->hdr); + } + pjmedia_sdp_session* r_sdp; if (!body @@ -465,7 +472,7 @@ transaction_request_cb(pjsip_rx_data* rdata) } // Check if call has been transferred - pjsip_tx_data* tdata = 0; + pjsip_tx_data* tdata = nullptr; if (pjsip_inv_initial_answer(call->inv.get(), rdata, PJSIP_SC_TRYING, NULL, NULL, &tdata) != PJ_SUCCESS) { @@ -473,6 +480,9 @@ transaction_request_cb(pjsip_rx_data* rdata) return PJ_FALSE; } + // Add user-agent header + sip_utils::addUserAgenttHeader(account->getUserAgentName(), tdata); + if (pjsip_inv_send_msg(call->inv.get(), tdata) != PJ_SUCCESS) { JAMI_ERR("Could not send msg TRYING"); return PJ_FALSE; @@ -825,7 +835,7 @@ invite_session_state_changed_cb(pjsip_inv_session* inv, pjsip_event* ev) return; if (ev->type != PJSIP_EVENT_TSX_STATE and ev->type != PJSIP_EVENT_TX_MSG) { - JAMI_WARN("[call:%s] INVITE@%p state changed to %d (%s): unwaited event type %d", + JAMI_WARN("[call:%s] INVITE@%p state changed to %d (%s): unexpected event type %d", call->getCallId().c_str(), inv, inv->state, @@ -1181,6 +1191,8 @@ transaction_state_changed_cb(pjsip_inv_session* inv, pjsip_transaction* tsx, pjs if (not call) return; + processInviteResponseHelper(inv, event); + // We process here only incoming request message if (tsx->role != PJSIP_ROLE_UAS or tsx->state != PJSIP_TSX_STATE_TRYING or event->body.tsx_state.type != PJSIP_EVENT_RX_MSG) { @@ -1224,6 +1236,39 @@ transaction_state_changed_cb(pjsip_inv_session* inv, pjsip_transaction* tsx, pjs } } + +static void processInviteResponseHelper(pjsip_inv_session* inv, pjsip_event* event) +{ + + if (event->body.tsx_state.type != PJSIP_EVENT_RX_MSG) + return; + + const auto rdata = event->body.tsx_state.src.rdata; + if (rdata == nullptr or rdata->msg_info.msg == nullptr) + return; + + const auto msg = rdata->msg_info.msg; + if (msg->type != PJSIP_RESPONSE_MSG) + return; + + // Only handle the following responses + switch(msg->line.status.code) { + case PJSIP_SC_TRYING : + case PJSIP_SC_RINGING : + case PJSIP_SC_OK : + break; + default : + return; + } + + JAMI_INFO("[INVITE:%p] SIP RX response: reason %s, status code %i", + inv, + std::string(msg->line.status.reason.ptr, msg->line.status.reason.slen).c_str(), + msg->line.status.code); + + sip_utils::logMessageHeaders(&msg->hdr); +} + int SIPVoIPLink::getModId() { -- GitLab