Skip to content
Snippets Groups Projects
Select Git revision
  • 616164fa54c982b10033e89ddd7442aab12e88ac
  • master default protected
  • release/202005
  • release/202001
  • release/201912
  • release/201911
  • release/releaseWindowsTestOne
  • release/windowsReleaseTest
  • release/releaseTest
  • release/releaseWindowsTest
  • release/201910
  • release/qt/201910
  • release/windows-test/201910
  • release/201908
  • release/201906
  • release/201905
  • release/201904
  • release/201903
  • release/201902
  • release/201901
  • release/201812
  • 4.0.0
  • 2.2.0
  • 2.1.0
  • 2.0.1
  • 2.0.0
  • 1.4.1
  • 1.4.0
  • 1.3.0
  • 1.2.0
  • 1.1.0
31 results

sipaccount.h

Blame
  • Ming Rui Zhang's avatar
    Ming Rui Zhang authored and Adrien Béraud committed
    - solve the registration failure issue in android due to the possible
      occupied Default sip port (Address already in use)
    
    Change-Id: I4794a4a1f6d3a0128aea2c21d18868de23cbbcee
    616164fa
    History
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    sipaccount.h 26.08 KiB
    /*
     *  Copyright (C) 2004-2019 Savoir-faire Linux Inc.
     *
     *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
     *  Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com>
     *  Author: Yan Morin <yan.morin@savoirfairelinux.com>
     *  Author: Pierre-Luc Bacon <pierre-luc.bacon@savoirfairelinux.com>
     *
     *  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 3 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.
     */
    
    #ifndef SIPACCOUNT_H
    #define SIPACCOUNT_H
    
    #ifdef HAVE_CONFIG_H
    #include "config.h"
    #endif
    
    #include "sipaccountbase.h"
    #include "siptransport.h"
    #include "noncopyable.h"
    #include "ring_types.h" // enable_if_base_of
    
    #include <pjsip/sip_transport_tls.h>
    #include <pjsip/sip_types.h>
    #include <pjsip-ua/sip_regc.h>
    
    #include <vector>
    #include <map>
    
    namespace YAML {
        class Node;
        class Emitter;
    }
    
    namespace jami {
    
    namespace Conf {
        const char *const KEEP_ALIVE_ENABLED = "keepAlive";
    
        // TODO: write an object to store credential which implement serializable
        const char *const SRTP_KEY = "srtp";
        const char *const SRTP_ENABLE_KEY = "enable";
        const char *const KEY_EXCHANGE_KEY = "keyExchange";
        const char *const RTP_FALLBACK_KEY = "rtpFallback";
    }
    
    typedef std::vector<pj_ssl_cipher> CipherArray;
    
    class SIPPresence;
    class SIPCall;
    
    /**
     * @file sipaccount.h
     * @brief A SIP Account specify SIP specific functions and object = SIPCall/SIPVoIPLink)
     */
    class SIPAccount : public SIPAccountBase {
        public:
            constexpr static const char * const ACCOUNT_TYPE = "SIP";
    
            std::shared_ptr<SIPAccount> shared() {
                return std::static_pointer_cast<SIPAccount>(shared_from_this());
            }
            std::shared_ptr<SIPAccount const> shared() const {
                return std::static_pointer_cast<SIPAccount const>(shared_from_this());
            }
            std::weak_ptr<SIPAccount> weak() {
                return std::static_pointer_cast<SIPAccount>(shared_from_this());
            }
            std::weak_ptr<SIPAccount const> weak() const {
                return std::static_pointer_cast<SIPAccount const>(shared_from_this());
            }
    
            /**
             * Constructor
             * @param accountID The account identifier
             */
            SIPAccount(const std::string& accountID, bool presenceEnabled);
    
            ~SIPAccount();
    
            const char* getAccountType() const override {
                return ACCOUNT_TYPE;
            }
    
            pjsip_host_port getHostPortFromSTUN(pj_pool_t *pool);
    
            std::string getUserAgentName() const;
            void setRegistrationStateDetailed(const std::pair<int, std::string> &details) {
                registrationStateDetailed_ = details;
            }
    
            void updateDialogViaSentBy(pjsip_dialog *dlg);
    
            void resetAutoRegistration();
            bool checkNATAddress(pjsip_regc_cbparam *param, pj_pool_t *pool);
    
            /**
             * Returns true if this is the IP2IP account
             */
            bool isIP2IP() const override;
    
            /**
             * Serialize internal state of this account for configuration
             * @param out Emitter to which state will be saved
             */
            virtual void serialize(YAML::Emitter &out) const override;
    
            /**
             * Populate the internal state for this account based on info stored in the configuration file
             * @param The configuration node for this account
             */
            virtual void unserialize(const YAML::Node &node) override;
    
            /**
             * Return an map containing the internal state of this account. Client application can use this method to manage
             * account info.
             * @return A map containing the account information.
             */
            virtual std::map<std::string, std::string> getAccountDetails() const override;
    
            /**
             * Retrieve volatile details such as recent registration errors
             * @return std::map< std::string, std::string > The account volatile details
             */
            virtual std::map<std::string, std::string> getVolatileAccountDetails() const override;
    
            /**
             * Return the TLS settings, mainly used to return security information to
             * a client application
             */
            std::map<std::string, std::string> getTlsSettings() const;
    
            /**
             * Actually useless, since config loading is done in init()
             */
            void loadConfig() override;
    
            /**
             * Initialize the SIP voip link with the account parameters and send registration
             */
            void doRegister() override;
    
            /**
             * Send unregistration.
             */
            void doUnregister(std::function<void(bool)> cb = std::function<void(bool)>()) override;
    
            /**
             * Start the keep alive function, once started, the account will be registered periodically
             * a new REGISTER request is sent bey the client application. The account must be initially
             * registered for this call to be effective.
             */
            void startKeepAliveTimer();
    
            /**
             * Stop the keep alive timer. Once canceled, no further registration will be scheduled
             */
            void stopKeepAliveTimer();
    
            /**
             * Build and send SIP registration request
             */
            void sendRegister();
    
            /**
             * Build and send SIP unregistration request
             * @param destroy_transport If true, attempt to destroy the transport.
             */
            void sendUnregister();
    
            const pjsip_cred_info* getCredInfo() const {
                return cred_.data();
            }
    
            /**
             * Get the number of credentials defined for
             * this account.
             * @param none
             * @return int The number of credentials set for this account.
             */
            unsigned getCredentialCount() const {
                return credentials_.size();
            }
    
            bool hasCredentials() const {
                return not credentials_.empty();
            }
    
            void setCredentials(const std::vector<std::map<std::string, std::string> >& details);
    
            std::vector<std::map<std::string, std::string>>
            getCredentials() const;
    
            virtual void setRegistrationState(RegistrationState state, unsigned code=0, const std::string& detail_str={}) override;
    
            /**
             * A client sendings a REGISTER request MAY suggest an expiration
             * interval that indicates how long the client would like the
             * registration to be valid.
             *
             * @return the expiration value.
             */
            unsigned getRegistrationExpire() const {
                if (registrationExpire_ == 0)
                    return PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
    
                return registrationExpire_;
            }
    
            /**
             * Set the expiration for this account as found in
             * the "Expire" sip header or the CONTACT's "expire" param.
             */
            void setRegistrationExpire(int expire) {
                if (expire > 0)
                    registrationExpire_ = expire;
            }
    
            /**
             * Doubles the Expiration Interval sepecified for registration.
             */
            void doubleRegistrationExpire() {
                registrationExpire_ *= 2;
    
                if (registrationExpire_ < 0)
                    registrationExpire_ = 0;
            }
    
            /**
             * Registration flag
             */
            bool isRegistered() const {
                return bRegister_;
            }
    
            /**
             * Set registration flag
             */
            void setRegister(bool result) {
                bRegister_ = result;
            }
    
            /**
             * Get the registration structure that is used
             * for PJSIP in the registration process.
             * Settings are loaded from configuration file.
             * @return pjsip_regc* A pointer to the registration structure
             */
            pjsip_regc* getRegistrationInfo() {
                return regc_;
            }
    
            /**
             * Set the registration structure that is used
             * for PJSIP in the registration process;
             * @pram A pointer to the new registration structure
             * @return void
             */
            void setRegistrationInfo(pjsip_regc *regc) {
                if (regc_) destroyRegistrationInfo();
                regc_ = regc;
            }
    
            void destroyRegistrationInfo();
    
            /**
             * Get the port on which the transport/listener should use, or is
             * actually using.
             * @return pj_uint16 The port used for that account
             */
            pj_uint16_t getLocalPort() const {
                return localPort_;
            }
    
            /**
             * Set the new port on which this account is running over.
             * @pram port The port used by this account.
             */
            void setLocalPort(pj_uint16_t port) {
                localPort_ = port;
            }
    
            /**
             * Get the bind ip address on which the account should use, or is
             * actually using.
             * Note: if it is NULL, this address should not be used
             * @return std::string The bind ip address used for that account
             */
            std::string getBindAddress() const {
                return bindAddress_;
            }
    
            /**
             * Set the new bind ip address on which this account is bind on.
             * @pram address The bind ip address used by this account.
             */
            void setBindAddress(const std::string &address) {
                bindAddress_ = address;
            }
    
            /**
             * @return pjsip_tls_setting structure, filled from the configuration
             * file, that can be used directly by PJSIP to initialize
             * TLS transport.
             */
            pjsip_tls_setting * getTlsSetting() {
                return &tlsSetting_;
            }
    
            /**
             * Get the local port for TLS listener.
             * @return pj_uint16 The port used for that account
             */
            pj_uint16_t getTlsListenerPort() const {
                return tlsListenerPort_;
            }
    
            pj_str_t getStunServerName() const override {
                return stunServerName_;
            }
    
            static const std::vector<std::string>& getSupportedTlsCiphers();
            static const std::vector<std::string>& getSupportedTlsProtocols();
    
            /**
             * @return pj_uint8_t structure, filled from the configuration
             * file, that can be used directly by PJSIP to initialize
             * an alternate UDP transport.
             */
            pj_uint16_t getStunPort() const override {
                return stunPort_;
            }
    
            /**
             * @return bool Tells if current transport for that
             * account is set to OTHER.
             */
            bool isStunEnabled() const override {
                return stunEnabled_;
            }
    
            /**
             * @return pj_str_t "From" uri based on account information.
             * From RFC3261: "The To header field first and foremost specifies the desired
             * logical" recipient of the request, or the address-of-record of the
             * user or resource that is the target of this request. [...]  As such, it is
             * very important that the From URI not contain IP addresses or the FQDN
             * of the host on which the UA is running, since these are not logical
             * names."
             */
            std::string getFromUri() const;
    
            /**
             * This method adds the correct scheme, hostname and append
             * the ;transport= parameter at the end of the uri, in accordance with RFC3261.
             * It is expected that "port" is present in the internal hostname_.
             *
             * @return pj_str_t "To" uri based on @param username
             * @param username A string formatted as : "username"
             */
            std::string getToUri(const std::string& username) const override;
    
            /**
             * In the current version, "srv" uri is obtained in the preformatted
             * way: hostname:port. This method adds the correct scheme and append
             * the ;transport= parameter at the end of the uri, in accordance with RFC3261.
             *
             * @return pj_str_t "server" uri based on @param hostPort
             * @param hostPort A string formatted as : "hostname:port"
             */
            std::string getServerUri() const;
    
            /**
             * Get the contact header for
             * @return pj_str_t The contact header based on account information
             */
            pj_str_t getContactHeader(pjsip_transport* = nullptr) override;
    
    
            std::string getServiceRoute() const {
                return serviceRoute_;
            }
    
            bool hasServiceRoute() const { return not serviceRoute_.empty(); }
    
            virtual bool isTlsEnabled() const override {
                return tlsEnable_;
            }
    
            virtual sip_utils::KeyExchangeProtocol getSrtpKeyExchange() const override {
                if (tlsEnable_ && srtpKeyExchange_ == sip_utils::KeyExchangeProtocol::NONE)
                    return sip_utils::KeyExchangeProtocol::SDES;
                return srtpKeyExchange_;
            }
    
            virtual bool getSrtpFallback() const override {
                return srtpFallback_;
            }
    
            void setReceivedParameter(const std::string &received) {
                receivedParameter_ = received;
                via_addr_.host.ptr = (char *) receivedParameter_.c_str();
                via_addr_.host.slen = receivedParameter_.size();
            }
    
            std::string getReceivedParameter() const {
                return receivedParameter_;
            }
    
            pjsip_host_port *
            getViaAddr() {
                return &via_addr_;
            }
    
            int getRPort() const {
                if (rPort_ == -1)
                    return localPort_;
                else
                    return rPort_;
            }
    
            void setRPort(int rPort) {
                rPort_ = rPort;
                via_addr_.port = rPort;
            }
    
            /**
             * Timer used to periodically send re-register request based
             * on the "Expire" sip header (or the "expire" Contact parameter)
             */
            static void keepAliveRegistrationCb(pj_timer_heap_t *th, pj_timer_entry *te);
    
            bool isKeepAliveEnabled() const {
                return keepAliveEnabled_;
            }
    
            void setTransport(const std::shared_ptr<SipTransport>& = nullptr);
    
            virtual inline std::shared_ptr<SipTransport> getTransport() {
                return transport_;
            }
    
            inline pjsip_transport_type_e getTransportType() const {
                return transportType_;
            }
    
            /**
             * Shortcut for SipTransport::getTransportSelector(account.getTransport()).
             */
            pjsip_tpselector getTransportSelector();
    
            /* Returns true if the username and/or hostname match this account */
            MatchRank matches(const std::string &username, const std::string &hostname) const override;
    
            /**
             * Presence management
             */
            SIPPresence * getPresence() const;
    
            /**
             * Activate the module.
             * @param function Publish or subscribe to enable
             * @param enable Flag
             */
            void enablePresence(const bool& enable);
            /**
             * Activate the publish/subscribe.
             * @param enable Flag
             */
            void supportPresence(int function, bool enable);
    
            /**
             * Implementation of Account::newOutgoingCall()
             * Note: keep declaration before newOutgoingCall template.
             */
            std::shared_ptr<Call> newOutgoingCall(const std::string& toUrl,
                                                  const std::map<std::string, std::string>& volatileCallDetails = {}) override;
    
            /**
             * Create outgoing SIPCall.
             * @param[in] toUrl The address to call
             * @return std::shared_ptr<T> A shared pointer on the created call.
             *      The type of this instance is given in template argument.
             *      This type can be any base class of SIPCall class (included).
             */
    #ifndef _MSC_VER
            template <class T=SIPCall>
            std::shared_ptr<enable_if_base_of<T, SIPCall> >
            newOutgoingCall(const std::string& toUrl, const std::map<std::string, std::string>& volatileCallDetails = {});
    #else
            template <class T>
            std::shared_ptr<T>
            newOutgoingCall(const std::string& toUrl, const std::map<std::string, std::string>& volatileCallDetails = {});
    #endif
    
            /**
             * Create incoming SIPCall.
             * @param[in] from The origin uri of the call
             * @param details use to set some specific details
             * @return std::shared_ptr<T> A shared pointer on the created call.
             *      The type of this instance is given in template argument.
             *      This type can be any base class of SIPCall class (included).
             */
            std::shared_ptr<SIPCall>
            newIncomingCall(const std::string& from, const std::map<std::string, std::string>& details = {}) override;
    
            void onRegister(pjsip_regc_cbparam *param);
    
            virtual void sendTextMessage(const std::string& to,
                                         const std::map<std::string, std::string>& payloads,
                                         uint64_t id) override;
    
            void connectivityChanged() override;
    
            std::string getUserUri() const override;
    
             /**
             * Create the Ip address that the transport uses
             * @return IpAddr created
             */
            IpAddr createBindingAddress();
    
        private:
            void doRegister1_();
            void doRegister2_();
    
            /**
             * Set the internal state for this account, mainly used to manage account details from the client application.
             * @param The map containing the account information.
             */
            void setAccountDetails(const std::map<std::string, std::string> &details) override;
    
            NON_COPYABLE(SIPAccount);
    
            std::shared_ptr<Call> newRegisteredAccountCall(const std::string& id,
                                                           const std::string& toUrl);
    
            /**
             * Start a SIP Call
             * @param call  The current call
             * @return true if all is correct
             */
            bool SIPStartCall(std::shared_ptr<SIPCall>& call);
    
            void usePublishedAddressPortInVIA();
            void useUPnPAddressPortInVIA();
            bool fullMatch(const std::string &username, const std::string &hostname) const;
            bool userMatch(const std::string &username) const;
            bool hostnameMatch(const std::string &hostname) const;
            bool proxyMatch(const std::string &hostname) const;
    
            bool isSrtpEnabled() const {
                return srtpKeyExchange_ != sip_utils::KeyExchangeProtocol::NONE;
            }
    
            /**
             * Callback called by the transport layer when the registration
             * transport state changes.
             */
            virtual void onTransportStateChanged(pjsip_transport_state state, const pjsip_transport_state_info *info);
    
            struct {
                pj_bool_t    active;    /**< Flag of reregister status. */
                pj_timer_entry   timer;     /**< Timer for reregistration.  */
                void        *reg_tp;    /**< Transport for registration.    */
                unsigned     attempt_cnt; /**< Attempt counter.     */
            } auto_rereg_;           /**< Reregister/reconnect data. */
    
            std::uniform_int_distribution<int> delay10ZeroDist_ {-10000, 10000};
            std::uniform_int_distribution<unsigned int> delay10PosDist_ {0, 10000};
    
            void scheduleReregistration();
            void autoReregTimerCb();
    
            /**
             * Map of credential for this account
             */
            struct Credentials {
                std::string realm {};
                std::string username {};
                std::string password {};
                std::string password_h {};
                Credentials(const std::string& r, const std::string& u, const std::string& p)
                 : realm(r), username(u), password(p) {}
                void computePasswordHash();
            };
            std::vector<Credentials> credentials_;
    
            std::shared_ptr<SipTransport> transport_ {};
    
            std::shared_ptr<TlsListener> tlsListener_ {};
    
            /**
             * Transport type used for this sip account. Currently supported types:
             *    PJSIP_TRANSPORT_UNSPECIFIED
             *    PJSIP_TRANSPORT_UDP
             *    PJSIP_TRANSPORT_TLS
             */
            pjsip_transport_type_e transportType_ {PJSIP_TRANSPORT_UNSPECIFIED};
    
            /**
             * Maps a string description of the SSL method
             * to the corresponding enum value in pjsip_ssl_method.
             * @param method The string representation
             * @return pjsip_ssl_method The corresponding value in the enum
             */
            static pj_uint32_t tlsProtocolFromString(const std::string& method);
    
            /**
             * Initializes tls settings from configuration file.
             */
            void initTlsConfiguration();
    
            /**
             * PJSIP aborts if the string length of our cipher list is too
             * great, so this function forces our cipher list to fit this constraint.
             */
            void trimCiphers();
    
            /**
             * Initializes STUN config from the config file
             */
            void initStunConfiguration();
    
            /**
             * If username is not provided, as it happens for Direct ip calls,
             * fetch the Real Name field of the user that is currently
             * running this program.
             * @return std::string The login name under which the software is running.
             */
            static std::string getLoginName();
    
            /**
             * Maps require port via UPnP
             */
            bool mapPortUPnP();
    
            /**
             * Resolved IP of hostname_ (for registration)
             */
            IpAddr hostIp_;
    
            /**
             * The pjsip client registration information
             */
            pjsip_regc *regc_;
    
            /**
             * To check if the account is registered
             */
            bool bRegister_;
    
            /**
             * Network settings
             */
            int registrationExpire_;
    
            /**
             * Optional list of SIP service this
             */
            std::string serviceRoute_;
    
            /**
             * Credential information stored for further registration.
             * Points to credentials_ members.
             */
            std::vector<pjsip_cred_info> cred_;
    
            /**
             * The TLS settings, used only if tls is chosen as a sip transport.
             */
            pjsip_tls_setting tlsSetting_;
    
            /**
             * Allocate a vector to be used by pjsip to store the supported ciphers on this system.
             */
            CipherArray ciphers_;
    
            /**
             * The STUN server name (hostname)
             */
            pj_str_t stunServerName_ {nullptr, 0};
    
            /**
             * The STUN server port
             */
            pj_uint16_t stunPort_ {PJ_STUN_PORT};
    
            /**
             * Local port to whih this account is bound
             */
            pj_uint16_t localPort_ {sip_utils::DEFAULT_AUTO_SELECT_PORT};
    
            /**
             * Potential ip addresss on which this account is bound
             */
            std::string bindAddress_;
    
            /**
             * The TLS listener port
             */
            pj_uint16_t tlsListenerPort_ {sip_utils::DEFAULT_AUTO_SELECT_PORT};
    
            /**
             * Send Request Callback
             */
            static void onComplete(void *token, pjsip_event *event);
    
            bool tlsEnable_ {false};
            std::string tlsMethod_;
            std::string tlsCiphers_;
            std::string tlsServerName_;
            bool tlsVerifyServer_;
            bool tlsVerifyClient_;
            bool tlsRequireClientCertificate_;
            std::string tlsNegotiationTimeoutSec_;
    
            /**
             * Specifies the type of key exchange used for SRTP, if any.
             * This only determine if the media channel is secured.
             */
            sip_utils::KeyExchangeProtocol srtpKeyExchange_ {sip_utils::KeyExchangeProtocol::NONE};
    
            /**
             * Determine if the softphone should fallback on non secured media channel if SRTP negotiation fails.
             * Make sure other SIP endpoints share the same behavior since it could result in encrypted data to be
             * played through the audio device.
             */
            bool srtpFallback_ {};
    
            /**
             * Details about the registration state.
             * This is a protocol Code:Description pair.
             */
            std::pair<int, std::string> registrationStateDetailed_;
    
            /**
             * Determine if the keep alive timer will be activated or not
             */
            bool keepAliveEnabled_;
    
            /**
             * Timer used to regularrly send re-register request based
             * on the "Expire" sip header (or the "expire" Contact parameter)
             */
            pj_timer_entry keepAliveTimer_;
            std::uniform_int_distribution<decltype(pj_timer_entry::id)> timerIdDist_ {};
    
            /**
             * Once enabled, this variable tells if the keepalive timer is activated
             * for this accout
             */
            bool keepAliveTimerActive_;
    
            /**
             * Optional: "received" parameter from VIA header
             */
            std::string receivedParameter_;
    
            /**
             * Optional: "rport" parameter from VIA header
             */
            int rPort_;
    
            /**
             * Optional: via_addr construct from received parameters
             */
            pjsip_host_port via_addr_;
    
            /**
             * Temporary storage for getUPnPIpAddress().toString()
             * Used only by useUPnPAddressPortInVIA().
             */
            std::string upnpIpAddr_;
    
            char contactBuffer_[PJSIP_MAX_URL_SIZE];
            pj_str_t contact_;
            int contactRewriteMethod_;
            bool allowViaRewrite_;
            /* Undocumented feature in pjsip, this can == 2 */
            int allowContactRewrite_;
            bool contactOverwritten_;
            pjsip_transport *via_tp_;
    
            /**
             * Presence data structure
             */
            SIPPresence * presence_;
    
            /**
             * SIP port actually used,
             * this holds the actual port used for SIP, which may not be the port
             * selected in the configuration in the case that UPnP is used and the
             * configured port is already used by another client
             */
            pj_uint16_t publishedPortUsed_ {sip_utils::DEFAULT_SIP_PORT};
    };
    
    } // namespace jami
    
    #endif