ringaccount.h 13.6 KB
Newer Older
Adrien Béraud's avatar
Adrien Béraud committed
1
/*
Guillaume Roguez's avatar
Guillaume Roguez committed
2
 *  Copyright (C) 2014-2015 Savoir-faire Linux Inc.
Adrien Béraud's avatar
Adrien Béraud committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 *  Author: Adrien Béraud <adrien.beraud@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.
 */

21
22
#ifndef RINGACCOUNT_H
#define RINGACCOUNT_H
Adrien Béraud's avatar
Adrien Béraud committed
23
24
25
26
27
28

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "sip/sipaccountbase.h"
29

Adrien Béraud's avatar
Adrien Béraud committed
30
31
#include "noncopyable.h"
#include "ip_utils.h"
32
#include "ring_types.h" // enable_if_base_of
33
34

#include <opendht/dhtrunner.h>
35
#include <opendht/default_types.h>
Adrien Béraud's avatar
Adrien Béraud committed
36
37
38
39
40

#include <pjsip/sip_types.h>

#include <vector>
#include <map>
41
#include <chrono>
42
#include <list>
Adrien Béraud's avatar
Adrien Béraud committed
43
#include <future>
Adrien Béraud's avatar
Adrien Béraud committed
44

Guillaume Roguez's avatar
Guillaume Roguez committed
45
46
47
48
49
50
51
52
53
54
55
56
/**
 * @file sipaccount.h
 * @brief A SIP Account specify SIP specific functions and object = SIPCall/SIPVoIPLink)
 */

namespace YAML {
class Node;
class Emitter;
}

namespace ring {

Adrien Béraud's avatar
Adrien Béraud committed
57
namespace Conf {
58
59
60
61
62
const char *const DHT_PORT_KEY = "dhtPort";
const char *const DHT_VALUES_PATH_KEY = "dhtValuesPath";
const char *const DHT_CONTACTS = "dhtContacts";
const char *const DHT_PUBLIC_PROFILE = "dhtPublicProfile";
const char *const DHT_PUBLIC_IN_CALLS = "dhtPublicInCalls";
63
64
const char *const DHT_ALLOW_PEERS_FROM_HISTORY = "allowPeersFromHistory";
const char *const DHT_ALLOW_PEERS_FROM_CONTACT = "allowPeersFromContact";
65
const char *const DHT_ALLOW_PEERS_FROM_TRUSTED = "allowPeersFromTrusted";
Adrien Béraud's avatar
Adrien Béraud committed
66
67
}

68
69
class IceTransport;

70
class RingAccount : public SIPAccountBase {
Adrien Béraud's avatar
Adrien Béraud committed
71
    public:
72
        constexpr static const char * const ACCOUNT_TYPE = "RING";
Adrien Béraud's avatar
Adrien Béraud committed
73
        constexpr static const in_port_t DHT_DEFAULT_PORT = 4222;
74
        constexpr static const char * const DHT_DEFAULT_BOOTSTRAP = "bootstrap.ring.cx";
75
76
        constexpr static const char* const DHT_TYPE_NS = "cx.ring";

77
        /* constexpr */ static const std::pair<uint16_t, uint16_t> DHT_PORT_RANGE;
Adrien Béraud's avatar
Adrien Béraud committed
78
79
80
81
82
83
84
85
86

        const char* getAccountType() const {
            return ACCOUNT_TYPE;
        }

        /**
         * Constructor
         * @param accountID The account identifier
         */
87
        RingAccount(const std::string& accountID, bool presenceEnabled);
Adrien Béraud's avatar
Adrien Béraud committed
88

89
        ~RingAccount();
Adrien Béraud's avatar
Adrien Béraud committed
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109

        /**
         * Serialize internal state of this account for configuration
         * @param YamlEmitter the configuration engine which generate the configuration file
         */
        virtual void serialize(YAML::Emitter &out);

        /**
         * 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);

        /**
         * 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;

110
111
112
113
114
115
        /**
         * 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;

Adrien Béraud's avatar
Adrien Béraud committed
116
117
118
        /**
         * Actually useless, since config loading is done in init()
         */
119
        void loadConfig() {}
Adrien Béraud's avatar
Adrien Béraud committed
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152

        /**
         * Connect to the DHT.
         */
        void doRegister();

        /**
         * Disconnect from the DHT.
         */
        void doUnregister(std::function<void(bool)> cb = std::function<void(bool)>());

        /**
         * @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;

        /**
153
         * In the current version of Ring, "srv" uri is obtained in the preformated
Adrien Béraud's avatar
Adrien Béraud committed
154
155
156
157
158
159
160
161
162
163
164
165
         * 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 { return ""; };

        /**
         * Get the contact header for
         * @return pj_str_t The contact header based on account information
         */
166
        pj_str_t getContactHeader(pjsip_transport* = nullptr);
Adrien Béraud's avatar
Adrien Béraud committed
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206

        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_;
        }

        /* Returns true if the username and/or hostname match this account */
        MatchRank matches(const std::string &username, const std::string &hostname) const;

        /**
         * 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.
         */
207
        std::shared_ptr<Call> newOutgoingCall(const std::string& toUrl);
Adrien Béraud's avatar
Adrien Béraud committed
208
209
210
211
212
213
214
215
216
217

        /**
         * 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).
         */
        template <class T=SIPCall>
        std::shared_ptr<enable_if_base_of<T, SIPCall> >
218
        newOutgoingCall(const std::string& toUrl);
Adrien Béraud's avatar
Adrien Béraud committed
219
220
221

        /**
         * Create incoming SIPCall.
222
         * @param[in] from The origin of the call
Adrien Béraud's avatar
Adrien Béraud committed
223
224
225
226
227
         * @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).
         */
        virtual std::shared_ptr<SIPCall>
228
        newIncomingCall(const std::string& from = {});
Adrien Béraud's avatar
Adrien Béraud committed
229
230

        virtual bool isTlsEnabled() const {
231
            return true;
Adrien Béraud's avatar
Adrien Béraud committed
232
233
        }

234
235
236
237
        virtual bool isSrtpEnabled() const {
            return true;
        }

Guillaume Roguez's avatar
Guillaume Roguez committed
238
239
        virtual sip_utils::KeyExchangeProtocol getSrtpKeyExchange() const {
            return sip_utils::KeyExchangeProtocol::SDES;
Adrien Béraud's avatar
Adrien Béraud committed
240
241
242
243
244
245
        }

        virtual bool getSrtpFallback() const {
            return false;
        }

246
247
248
249
250
251
252
253
254
255
256
        bool setCertificateStatus(const std::string& cert_id, tls::TrustStore::Status status);
        std::vector<std::string> getCertificatesByStatus(tls::TrustStore::Status status);

        bool findCertificate(const std::string& id);
        bool findCertificate(const dht::InfoHash& h, std::function<void(const std::shared_ptr<dht::crypto::Certificate>)> cb = {});

        /* contact requests */
        std::map<std::string, std::string> getTrustRequests() const;
        bool acceptTrustRequest(const std::string& from);
        bool discardTrustRequest(const std::string& from);

257
        void sendTrustRequest(const std::string& to, const std::vector<uint8_t>& payload);
258
        virtual void sendTextMessage(const std::string& /* to */, const std::string& /* message */) override;
259

260
261
        void connectivityChanged();

Adrien Béraud's avatar
Adrien Béraud committed
262
    private:
263

Stepan Salenikovich's avatar
Stepan Salenikovich committed
264
        void doRegister_();
265
        void incomingCall(dht::IceCandidates&& msg);
Stepan Salenikovich's avatar
Stepan Salenikovich committed
266

267
        const dht::ValueType USER_PROFILE_TYPE = {9, "User profile", std::chrono::hours(24 * 7)};
268
        //const dht::ValueType ICE_ANNOUCEMENT_TYPE = {10, "ICE descriptors", std::chrono::minutes(3)};
269
270
271
272

        NON_COPYABLE(RingAccount);

        void handleEvents();
273

274
        void createOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::string& to_id, IpAddr target);
Adrien Béraud's avatar
Adrien Béraud committed
275
276
277
278
279
280
281
282
283
284
285
286

        /**
         * Set the internal state for this account, mainly used to manage account details from the client application.
         * @param The map containing the account information.
         */
        virtual void setAccountDetails(const std::map<std::string, std::string> &details);

        /**
         * Start a SIP Call
         * @param call  The current call
         * @return true if all is correct
         */
287
        bool SIPStartCall(const std::shared_ptr<SIPCall>& call, IpAddr target);
Adrien Béraud's avatar
Adrien Béraud committed
288

289

Stepan Salenikovich's avatar
Stepan Salenikovich committed
290
291
292
293
294
        /**
         * Maps require port via UPnP
         */
        bool mapPortUPnP();

Adrien Béraud's avatar
Adrien Béraud committed
295
296
        dht::DhtRunner dht_ {};

297
298
        dht::InfoHash callKey_;

299
300
        struct PendingCall {
            std::chrono::steady_clock::time_point start;
Guillaume Roguez's avatar
Guillaume Roguez committed
301
            std::shared_ptr<IceTransport> ice;
302
303
304
            std::weak_ptr<SIPCall> call;
            std::future<size_t> listen_key;
            dht::InfoHash call_key;
305
            dht::InfoHash from;
306
        };
307

308
        /**
309
         * DHT calls waiting for ICE negotiation
310
311
         */
        std::list<PendingCall> pendingCalls_ {};
312

313
        /**
314
         * Incoming DHT calls that are not yet actual SIP calls.
315
         */
316
        std::list<PendingCall> pendingSipCalls_ {};
317
        std::set<dht::Value::Id> treatedCalls_ {};
318
        std::set<dht::Value::Id> treatedMessages_ {};
319
        mutable std::mutex callsMutex_ {};
320

321
        std::string idPath_ {};
322
        std::string cachePath_ {};
Adrien Béraud's avatar
Adrien Béraud committed
323
        std::string dataPath_ {};
Adrien Béraud's avatar
Adrien Béraud committed
324

325
326
327
        struct TrustRequest {
            dht::InfoHash from;
            std::chrono::system_clock::time_point received;
328
            std::vector<uint8_t> payload;
329
330
331
332
333
334
        };

        std::vector<TrustRequest> trustRequests_;

        tls::TrustStore trust_;

Adrien Béraud's avatar
Adrien Béraud committed
335
        /**
336
337
         * Validate the values for privkeyPath_ and certPath_.
         * If one of these fields is empty, reset them to the default values.
Adrien Béraud's avatar
Adrien Béraud committed
338
         */
339
340
        void checkIdentityPath();

341
        void saveIdentity(const dht::crypto::Identity id, const std::string& path) const;
Adrien Béraud's avatar
Adrien Béraud committed
342
343
344
        void saveNodes(const std::vector<dht::Dht::NodeExport>&) const;
        void saveValues(const std::vector<dht::Dht::ValuesExport>&) const;

345
346
347
        void loadTreatedCalls();
        void saveTreatedCalls() const;

348
349
350
        void loadTreatedMessages();
        void saveTreatedMessages() const;

351
352
353
354
355
        /**
         * If privkeyPath_ is a valid private key file (PEM or DER),
         * and certPath_ a valid certificate file, load and returns them.
         * Otherwise, generate a new identity and returns it.
         */
356
        dht::crypto::Identity loadIdentity();
Adrien Béraud's avatar
Adrien Béraud committed
357
        std::vector<dht::Dht::NodeExport> loadNodes() const;
Adrien Béraud's avatar
Adrien Béraud committed
358
        std::vector<dht::Dht::ValuesExport> loadValues() const;
Adrien Béraud's avatar
Adrien Béraud committed
359

360
361
        bool dhtPublicInCalls_ {true};

Adrien Béraud's avatar
Adrien Béraud committed
362
        /**
Adrien Béraud's avatar
Adrien Béraud committed
363
         * DHT port preference
Adrien Béraud's avatar
Adrien Béraud committed
364
         */
365
        in_port_t dhtPort_ {};
Adrien Béraud's avatar
Adrien Béraud committed
366

Stepan Salenikovich's avatar
Stepan Salenikovich committed
367
368
369
370
371
372
        /**
         * DHT port actually used,
         * this holds the actual port used for DHT, 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
         */
373
        UsedPort dhtPortUsed_ {};
Stepan Salenikovich's avatar
Stepan Salenikovich committed
374

Adrien Béraud's avatar
Adrien Béraud committed
375
376
377
        /**
         * The TLS settings, used only if tls is chosen as a sip transport.
         */
378
        void generateDhParams();
379
380

        std::shared_future<std::unique_ptr<gnutls_dh_params_int, decltype(gnutls_dh_params_deinit)&>> dhParams_;
381
382
        std::mutex dhParamsMtx_;
        std::condition_variable dhParamsCv_;
383
384
        bool allowPeersFromHistory_;
        bool allowPeersFromContact_;
385
        bool allowPeersFromTrusted_;
Adrien Béraud's avatar
Adrien Béraud committed
386
387
388
389
390
391
392
393
394
395
396
397
398
399

        /**
         * Optional: "received" parameter from VIA header
         */
        std::string receivedParameter_ {};

        /**
         * Optional: "rport" parameter from VIA header
         */
        int rPort_ {-1};

        /**
         * Optional: via_addr construct from received parameters
         */
400
        pjsip_host_port via_addr_;
Adrien Béraud's avatar
Adrien Béraud committed
401
402
403
404

        char contactBuffer_[PJSIP_MAX_URL_SIZE] {};
        pj_str_t contact_ {contactBuffer_, 0};
        pjsip_transport *via_tp_ {nullptr};
405
406
407

        template <class... Args>
        std::shared_ptr<IceTransport> createIceTransport(Args... args);
Adrien Béraud's avatar
Adrien Béraud committed
408
409
};

Guillaume Roguez's avatar
Guillaume Roguez committed
410
411
} // namespace ring

Adrien Béraud's avatar
Adrien Béraud committed
412
#endif