| /* |
| * Copyright 2012 The WebRTC Project Authors. All rights reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #ifndef P2P_BASE_TURN_PORT_H_ |
| #define P2P_BASE_TURN_PORT_H_ |
| |
| #include <stdio.h> |
| |
| #include <list> |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "absl/memory/memory.h" |
| #include "p2p/base/port.h" |
| #include "p2p/client/basic_port_allocator.h" |
| #include "rtc_base/async_packet_socket.h" |
| #include "rtc_base/async_resolver_interface.h" |
| #include "rtc_base/ssl_certificate.h" |
| #include "rtc_base/task_utils/pending_task_safety_flag.h" |
| |
| namespace webrtc { |
| class TurnCustomizer; |
| } |
| |
| namespace cricket { |
| |
| const int kMaxTurnUsernameLength = 509; // RFC 8489 section 14.3 |
| |
| extern const int STUN_ATTR_TURN_LOGGING_ID; |
| extern const char TURN_PORT_TYPE[]; |
| class TurnAllocateRequest; |
| class TurnEntry; |
| |
| class TurnPort : public Port { |
| public: |
| enum PortState { |
| STATE_CONNECTING, // Initial state, cannot send any packets. |
| STATE_CONNECTED, // Socket connected, ready to send stun requests. |
| STATE_READY, // Received allocate success, can send any packets. |
| STATE_RECEIVEONLY, // Had REFRESH_REQUEST error, cannot send any packets. |
| STATE_DISCONNECTED, // TCP connection died, cannot send/receive any |
| // packets. |
| }; |
| // Create a TURN port using the shared UDP socket, `socket`. |
| static std::unique_ptr<TurnPort> Create( |
| rtc::Thread* thread, |
| rtc::PacketSocketFactory* factory, |
| rtc::Network* network, |
| rtc::AsyncPacketSocket* socket, |
| const std::string& username, // ice username. |
| const std::string& password, // ice password. |
| const ProtocolAddress& server_address, |
| const RelayCredentials& credentials, |
| int server_priority, |
| const std::string& origin, |
| webrtc::TurnCustomizer* customizer) { |
| // Do basic parameter validation. |
| if (credentials.username.size() > kMaxTurnUsernameLength) { |
| RTC_LOG(LS_ERROR) << "Attempt to use TURN with a too long username " |
| << "of length " << credentials.username.size(); |
| return nullptr; |
| } |
| // Do not connect to low-numbered ports. The default STUN port is 3478. |
| if (!AllowedTurnPort(server_address.address.port())) { |
| RTC_LOG(LS_ERROR) << "Attempt to use TURN to connect to port " |
| << server_address.address.port(); |
| return nullptr; |
| } |
| // Using `new` to access a non-public constructor. |
| return absl::WrapUnique(new TurnPort( |
| thread, factory, network, socket, username, password, server_address, |
| credentials, server_priority, origin, customizer)); |
| } |
| // TODO(steveanton): Remove once downstream clients have moved to `Create`. |
| static std::unique_ptr<TurnPort> CreateUnique( |
| rtc::Thread* thread, |
| rtc::PacketSocketFactory* factory, |
| rtc::Network* network, |
| rtc::AsyncPacketSocket* socket, |
| const std::string& username, // ice username. |
| const std::string& password, // ice password. |
| const ProtocolAddress& server_address, |
| const RelayCredentials& credentials, |
| int server_priority, |
| const std::string& origin, |
| webrtc::TurnCustomizer* customizer) { |
| return Create(thread, factory, network, socket, username, password, |
| server_address, credentials, server_priority, origin, |
| customizer); |
| } |
| |
| // Create a TURN port that will use a new socket, bound to `network` and |
| // using a port in the range between `min_port` and `max_port`. |
| static std::unique_ptr<TurnPort> Create( |
| rtc::Thread* thread, |
| rtc::PacketSocketFactory* factory, |
| rtc::Network* network, |
| uint16_t min_port, |
| uint16_t max_port, |
| const std::string& username, // ice username. |
| const std::string& password, // ice password. |
| const ProtocolAddress& server_address, |
| const RelayCredentials& credentials, |
| int server_priority, |
| const std::string& origin, |
| const std::vector<std::string>& tls_alpn_protocols, |
| const std::vector<std::string>& tls_elliptic_curves, |
| webrtc::TurnCustomizer* customizer, |
| rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr) { |
| // Do basic parameter validation. |
| if (credentials.username.size() > kMaxTurnUsernameLength) { |
| RTC_LOG(LS_ERROR) << "Attempt to use TURN with a too long username " |
| << "of length " << credentials.username.size(); |
| return nullptr; |
| } |
| // Do not connect to low-numbered ports. The default STUN port is 3478. |
| if (!AllowedTurnPort(server_address.address.port())) { |
| RTC_LOG(LS_ERROR) << "Attempt to use TURN to connect to port " |
| << server_address.address.port(); |
| return nullptr; |
| } |
| // Using `new` to access a non-public constructor. |
| return absl::WrapUnique( |
| new TurnPort(thread, factory, network, min_port, max_port, username, |
| password, server_address, credentials, server_priority, |
| origin, tls_alpn_protocols, tls_elliptic_curves, |
| customizer, tls_cert_verifier)); |
| } |
| // TODO(steveanton): Remove once downstream clients have moved to `Create`. |
| static std::unique_ptr<TurnPort> CreateUnique( |
| rtc::Thread* thread, |
| rtc::PacketSocketFactory* factory, |
| rtc::Network* network, |
| uint16_t min_port, |
| uint16_t max_port, |
| const std::string& username, // ice username. |
| const std::string& password, // ice password. |
| const ProtocolAddress& server_address, |
| const RelayCredentials& credentials, |
| int server_priority, |
| const std::string& origin, |
| const std::vector<std::string>& tls_alpn_protocols, |
| const std::vector<std::string>& tls_elliptic_curves, |
| webrtc::TurnCustomizer* customizer, |
| rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr) { |
| return Create(thread, factory, network, min_port, max_port, username, |
| password, server_address, credentials, server_priority, |
| origin, tls_alpn_protocols, tls_elliptic_curves, customizer, |
| tls_cert_verifier); |
| } |
| |
| ~TurnPort() override; |
| |
| const ProtocolAddress& server_address() const { return server_address_; } |
| // Returns an empty address if the local address has not been assigned. |
| rtc::SocketAddress GetLocalAddress() const; |
| |
| bool ready() const { return state_ == STATE_READY; } |
| bool connected() const { |
| return state_ == STATE_READY || state_ == STATE_CONNECTED; |
| } |
| const RelayCredentials& credentials() const { return credentials_; } |
| |
| ProtocolType GetProtocol() const override; |
| |
| virtual TlsCertPolicy GetTlsCertPolicy() const; |
| virtual void SetTlsCertPolicy(TlsCertPolicy tls_cert_policy); |
| |
| void SetTurnLoggingId(const std::string& turn_logging_id); |
| |
| virtual std::vector<std::string> GetTlsAlpnProtocols() const; |
| virtual std::vector<std::string> GetTlsEllipticCurves() const; |
| |
| // Release a TURN allocation by sending a refresh with lifetime 0. |
| // Sets state to STATE_RECEIVEONLY. |
| void Release(); |
| |
| void PrepareAddress() override; |
| Connection* CreateConnection(const Candidate& c, |
| PortInterface::CandidateOrigin origin) override; |
| int SendTo(const void* data, |
| size_t size, |
| const rtc::SocketAddress& addr, |
| const rtc::PacketOptions& options, |
| bool payload) override; |
| int SetOption(rtc::Socket::Option opt, int value) override; |
| int GetOption(rtc::Socket::Option opt, int* value) override; |
| int GetError() override; |
| |
| bool HandleIncomingPacket(rtc::AsyncPacketSocket* socket, |
| const char* data, |
| size_t size, |
| const rtc::SocketAddress& remote_addr, |
| int64_t packet_time_us) override; |
| bool CanHandleIncomingPacketsFrom( |
| const rtc::SocketAddress& addr) const override; |
| virtual void OnReadPacket(rtc::AsyncPacketSocket* socket, |
| const char* data, |
| size_t size, |
| const rtc::SocketAddress& remote_addr, |
| const int64_t& packet_time_us); |
| |
| void OnSentPacket(rtc::AsyncPacketSocket* socket, |
| const rtc::SentPacket& sent_packet) override; |
| virtual void OnReadyToSend(rtc::AsyncPacketSocket* socket); |
| bool SupportsProtocol(const std::string& protocol) const override; |
| |
| void OnSocketConnect(rtc::AsyncPacketSocket* socket); |
| void OnSocketClose(rtc::AsyncPacketSocket* socket, int error); |
| |
| const std::string& hash() const { return hash_; } |
| const std::string& nonce() const { return nonce_; } |
| |
| int error() const { return error_; } |
| |
| void OnAllocateMismatch(); |
| |
| rtc::AsyncPacketSocket* socket() const { return socket_; } |
| |
| // Signal with resolved server address. |
| // Parameters are port, server address and resolved server address. |
| // This signal will be sent only if server address is resolved successfully. |
| sigslot:: |
| signal3<TurnPort*, const rtc::SocketAddress&, const rtc::SocketAddress&> |
| SignalResolvedServerAddress; |
| |
| // Signal when TurnPort is closed, |
| // e.g remote socket closed (TCP) |
| // or receiveing a REFRESH response with lifetime 0. |
| sigslot::signal1<TurnPort*> SignalTurnPortClosed; |
| |
| // All public methods/signals below are for testing only. |
| sigslot::signal2<TurnPort*, int> SignalTurnRefreshResult; |
| sigslot::signal3<TurnPort*, const rtc::SocketAddress&, int> |
| SignalCreatePermissionResult; |
| void FlushRequests(int msg_type) { request_manager_.Flush(msg_type); } |
| bool HasRequests() { return !request_manager_.empty(); } |
| void set_credentials(const RelayCredentials& credentials) { |
| credentials_ = credentials; |
| } |
| // Finds the turn entry with `address` and sets its channel id. |
| // Returns true if the entry is found. |
| bool SetEntryChannelId(const rtc::SocketAddress& address, int channel_id); |
| // Visible for testing. |
| // Shuts down the turn port, usually because of some fatal errors. |
| void Close(); |
| |
| void HandleConnectionDestroyed(Connection* conn) override; |
| |
| protected: |
| TurnPort(rtc::Thread* thread, |
| rtc::PacketSocketFactory* factory, |
| rtc::Network* network, |
| rtc::AsyncPacketSocket* socket, |
| const std::string& username, |
| const std::string& password, |
| const ProtocolAddress& server_address, |
| const RelayCredentials& credentials, |
| int server_priority, |
| const std::string& origin, |
| webrtc::TurnCustomizer* customizer); |
| |
| TurnPort(rtc::Thread* thread, |
| rtc::PacketSocketFactory* factory, |
| rtc::Network* network, |
| uint16_t min_port, |
| uint16_t max_port, |
| const std::string& username, |
| const std::string& password, |
| const ProtocolAddress& server_address, |
| const RelayCredentials& credentials, |
| int server_priority, |
| const std::string& origin, |
| const std::vector<std::string>& tls_alpn_protocols, |
| const std::vector<std::string>& tls_elliptic_curves, |
| webrtc::TurnCustomizer* customizer, |
| rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr); |
| |
| // NOTE: This method needs to be accessible for StacPort |
| // return true if entry was created (i.e channel_number consumed). |
| bool CreateOrRefreshEntry(const rtc::SocketAddress& addr, int channel_number); |
| |
| bool CreateOrRefreshEntry(const rtc::SocketAddress& addr, |
| int channel_number, |
| const std::string& remote_ufrag); |
| |
| rtc::DiffServCodePoint StunDscpValue() const override; |
| |
| private: |
| enum { |
| MSG_ALLOCATE_ERROR = MSG_FIRST_AVAILABLE, |
| MSG_ALLOCATE_MISMATCH, |
| MSG_TRY_ALTERNATE_SERVER, |
| MSG_REFRESH_ERROR, |
| MSG_ALLOCATION_RELEASED |
| }; |
| |
| typedef std::list<TurnEntry*> EntryList; |
| typedef std::map<rtc::Socket::Option, int> SocketOptionsMap; |
| typedef std::set<rtc::SocketAddress> AttemptedServerSet; |
| |
| static bool AllowedTurnPort(int port); |
| void OnMessage(rtc::Message* pmsg) override; |
| |
| bool CreateTurnClientSocket(); |
| |
| void set_nonce(const std::string& nonce) { nonce_ = nonce; } |
| void set_realm(const std::string& realm) { |
| if (realm != realm_) { |
| realm_ = realm; |
| UpdateHash(); |
| } |
| } |
| |
| void OnRefreshError(); |
| void HandleRefreshError(); |
| bool SetAlternateServer(const rtc::SocketAddress& address); |
| void ResolveTurnAddress(const rtc::SocketAddress& address); |
| void OnResolveResult(rtc::AsyncResolverInterface* resolver); |
| |
| void AddRequestAuthInfo(StunMessage* msg); |
| void OnSendStunPacket(const void* data, size_t size, StunRequest* request); |
| // Stun address from allocate success response. |
| // Currently used only for testing. |
| void OnStunAddress(const rtc::SocketAddress& address); |
| void OnAllocateSuccess(const rtc::SocketAddress& address, |
| const rtc::SocketAddress& stun_address); |
| void OnAllocateError(int error_code, const std::string& reason); |
| void OnAllocateRequestTimeout(); |
| |
| void HandleDataIndication(const char* data, |
| size_t size, |
| int64_t packet_time_us); |
| void HandleChannelData(int channel_id, |
| const char* data, |
| size_t size, |
| int64_t packet_time_us); |
| void DispatchPacket(const char* data, |
| size_t size, |
| const rtc::SocketAddress& remote_addr, |
| ProtocolType proto, |
| int64_t packet_time_us); |
| |
| bool ScheduleRefresh(uint32_t lifetime); |
| void SendRequest(StunRequest* request, int delay); |
| int Send(const void* data, size_t size, const rtc::PacketOptions& options); |
| void UpdateHash(); |
| bool UpdateNonce(StunMessage* response); |
| void ResetNonce(); |
| |
| bool HasPermission(const rtc::IPAddress& ipaddr) const; |
| TurnEntry* FindEntry(const rtc::SocketAddress& address) const; |
| TurnEntry* FindEntry(int channel_id) const; |
| bool EntryExists(TurnEntry* e); |
| void DestroyEntry(TurnEntry* entry); |
| // Destroys the entry only if `timestamp` matches the destruction timestamp |
| // in `entry`. |
| void DestroyEntryIfNotCancelled(TurnEntry* entry, int64_t timestamp); |
| void ScheduleEntryDestruction(TurnEntry* entry); |
| |
| // Marks the connection with remote address `address` failed and |
| // pruned (a.k.a. write-timed-out). Returns true if a connection is found. |
| bool FailAndPruneConnection(const rtc::SocketAddress& address); |
| |
| // Reconstruct the URL of the server which the candidate is gathered from. |
| std::string ReconstructedServerUrl(bool use_hostname); |
| |
| void MaybeAddTurnLoggingId(StunMessage* message); |
| |
| void TurnCustomizerMaybeModifyOutgoingStunMessage(StunMessage* message); |
| bool TurnCustomizerAllowChannelData(const void* data, |
| size_t size, |
| bool payload); |
| |
| ProtocolAddress server_address_; |
| TlsCertPolicy tls_cert_policy_ = TlsCertPolicy::TLS_CERT_POLICY_SECURE; |
| std::vector<std::string> tls_alpn_protocols_; |
| std::vector<std::string> tls_elliptic_curves_; |
| rtc::SSLCertificateVerifier* tls_cert_verifier_; |
| RelayCredentials credentials_; |
| AttemptedServerSet attempted_server_addresses_; |
| |
| rtc::AsyncPacketSocket* socket_; |
| SocketOptionsMap socket_options_; |
| rtc::AsyncResolverInterface* resolver_; |
| int error_; |
| rtc::DiffServCodePoint stun_dscp_value_; |
| |
| StunRequestManager request_manager_; |
| std::string realm_; // From 401/438 response message. |
| std::string nonce_; // From 401/438 response message. |
| std::string hash_; // Digest of username:realm:password |
| |
| int next_channel_number_; |
| EntryList entries_; |
| |
| PortState state_; |
| // By default the value will be set to 0. This value will be used in |
| // calculating the candidate priority. |
| int server_priority_; |
| |
| // The number of retries made due to allocate mismatch error. |
| size_t allocate_mismatch_retries_; |
| |
| // Optional TurnCustomizer that can modify outgoing messages. Once set, this |
| // must outlive the TurnPort's lifetime. |
| webrtc::TurnCustomizer* turn_customizer_ = nullptr; |
| |
| // Optional TurnLoggingId. |
| // An identifier set by application that is added to TURN_ALLOCATE_REQUEST |
| // and can be used to match client/backend logs. |
| // TODO(jonaso): This should really be initialized in constructor, |
| // but that is currently so terrible. Fix once constructor is changed |
| // to be more easy to work with. |
| std::string turn_logging_id_; |
| |
| webrtc::ScopedTaskSafety task_safety_; |
| |
| friend class TurnEntry; |
| friend class TurnAllocateRequest; |
| friend class TurnRefreshRequest; |
| friend class TurnCreatePermissionRequest; |
| friend class TurnChannelBindRequest; |
| }; |
| |
| } // namespace cricket |
| |
| #endif // P2P_BASE_TURN_PORT_H_ |