|  | /* | 
|  | *  Copyright (c) 2021 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 NET_DCSCTP_SOCKET_DCSCTP_SOCKET_H_ | 
|  | #define NET_DCSCTP_SOCKET_DCSCTP_SOCKET_H_ | 
|  |  | 
|  | #include <cstdint> | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "absl/strings/string_view.h" | 
|  | #include "api/array_view.h" | 
|  | #include "net/dcsctp/packet/chunk/abort_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/cookie_ack_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/cookie_echo_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/data_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/data_common.h" | 
|  | #include "net/dcsctp/packet/chunk/error_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/forward_tsn_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/forward_tsn_common.h" | 
|  | #include "net/dcsctp/packet/chunk/heartbeat_ack_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/heartbeat_request_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/idata_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/iforward_tsn_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/init_ack_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/init_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/reconfig_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/sack_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/shutdown_ack_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/shutdown_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/shutdown_complete_chunk.h" | 
|  | #include "net/dcsctp/packet/data.h" | 
|  | #include "net/dcsctp/packet/sctp_packet.h" | 
|  | #include "net/dcsctp/public/dcsctp_message.h" | 
|  | #include "net/dcsctp/public/dcsctp_options.h" | 
|  | #include "net/dcsctp/public/dcsctp_socket.h" | 
|  | #include "net/dcsctp/public/packet_observer.h" | 
|  | #include "net/dcsctp/rx/data_tracker.h" | 
|  | #include "net/dcsctp/rx/reassembly_queue.h" | 
|  | #include "net/dcsctp/socket/callback_deferrer.h" | 
|  | #include "net/dcsctp/socket/packet_sender.h" | 
|  | #include "net/dcsctp/socket/state_cookie.h" | 
|  | #include "net/dcsctp/socket/transmission_control_block.h" | 
|  | #include "net/dcsctp/timer/timer.h" | 
|  | #include "net/dcsctp/tx/retransmission_error_counter.h" | 
|  | #include "net/dcsctp/tx/retransmission_queue.h" | 
|  | #include "net/dcsctp/tx/retransmission_timeout.h" | 
|  | #include "net/dcsctp/tx/rr_send_queue.h" | 
|  |  | 
|  | namespace dcsctp { | 
|  |  | 
|  | // DcSctpSocket represents a single SCTP socket, to be used over DTLS. | 
|  | // | 
|  | // Every dcSCTP is completely isolated from any other socket. | 
|  | // | 
|  | // This class manages all packet and chunk dispatching and mainly handles the | 
|  | // connection sequences (connect, close, shutdown, etc) as well as managing | 
|  | // the Transmission Control Block (tcb). | 
|  | // | 
|  | // This class is thread-compatible. | 
|  | class DcSctpSocket : public DcSctpSocketInterface { | 
|  | public: | 
|  | // Instantiates a DcSctpSocket, which interacts with the world through the | 
|  | // `callbacks` interface and is configured using `options`. | 
|  | // | 
|  | // For debugging, `log_prefix` will prefix all debug logs, and a | 
|  | // `packet_observer` can be attached to e.g. dump sent and received packets. | 
|  | DcSctpSocket(absl::string_view log_prefix, | 
|  | DcSctpSocketCallbacks& callbacks, | 
|  | std::unique_ptr<PacketObserver> packet_observer, | 
|  | const DcSctpOptions& options); | 
|  |  | 
|  | DcSctpSocket(const DcSctpSocket&) = delete; | 
|  | DcSctpSocket& operator=(const DcSctpSocket&) = delete; | 
|  |  | 
|  | // Implementation of `DcSctpSocketInterface`. | 
|  | void ReceivePacket(rtc::ArrayView<const uint8_t> data) override; | 
|  | void HandleTimeout(TimeoutID timeout_id) override; | 
|  | void Connect() override; | 
|  | void RestoreFromState(const DcSctpSocketHandoverState& state) override; | 
|  | void Shutdown() override; | 
|  | void Close() override; | 
|  | SendStatus Send(DcSctpMessage message, | 
|  | const SendOptions& send_options) override; | 
|  | std::vector<SendStatus> SendMany(rtc::ArrayView<DcSctpMessage> messages, | 
|  | const SendOptions& send_options) override; | 
|  | ResetStreamsStatus ResetStreams( | 
|  | rtc::ArrayView<const StreamID> outgoing_streams) override; | 
|  | SocketState state() const override; | 
|  | const DcSctpOptions& options() const override { return options_; } | 
|  | void SetMaxMessageSize(size_t max_message_size) override; | 
|  | void SetStreamPriority(StreamID stream_id, StreamPriority priority) override; | 
|  | StreamPriority GetStreamPriority(StreamID stream_id) const override; | 
|  | size_t buffered_amount(StreamID stream_id) const override; | 
|  | size_t buffered_amount_low_threshold(StreamID stream_id) const override; | 
|  | void SetBufferedAmountLowThreshold(StreamID stream_id, size_t bytes) override; | 
|  | absl::optional<Metrics> GetMetrics() const override; | 
|  | HandoverReadinessStatus GetHandoverReadiness() const override; | 
|  | absl::optional<DcSctpSocketHandoverState> GetHandoverStateAndClose() override; | 
|  | SctpImplementation peer_implementation() const override { | 
|  | return metrics_.peer_implementation; | 
|  | } | 
|  | // Returns this socket's verification tag, or zero if not yet connected. | 
|  | VerificationTag verification_tag() const { | 
|  | return tcb_ != nullptr ? tcb_->my_verification_tag() : VerificationTag(0); | 
|  | } | 
|  |  | 
|  | private: | 
|  | // Parameter proposals valid during the connect phase. | 
|  | struct ConnectParameters { | 
|  | TSN initial_tsn = TSN(0); | 
|  | VerificationTag verification_tag = VerificationTag(0); | 
|  | }; | 
|  |  | 
|  | // Detailed state (separate from SocketState, which is the public state). | 
|  | enum class State { | 
|  | kClosed, | 
|  | kCookieWait, | 
|  | // TCB valid in these: | 
|  | kCookieEchoed, | 
|  | kEstablished, | 
|  | kShutdownPending, | 
|  | kShutdownSent, | 
|  | kShutdownReceived, | 
|  | kShutdownAckSent, | 
|  | }; | 
|  |  | 
|  | // Returns the log prefix used for debug logging. | 
|  | std::string log_prefix() const; | 
|  |  | 
|  | bool IsConsistent() const; | 
|  | static constexpr absl::string_view ToString(DcSctpSocket::State state); | 
|  |  | 
|  | void CreateTransmissionControlBlock(const Capabilities& capabilities, | 
|  | VerificationTag my_verification_tag, | 
|  | TSN my_initial_tsn, | 
|  | VerificationTag peer_verification_tag, | 
|  | TSN peer_initial_tsn, | 
|  | size_t a_rwnd, | 
|  | TieTag tie_tag); | 
|  |  | 
|  | // Changes the socket state, given a `reason` (for debugging/logging). | 
|  | void SetState(State state, absl::string_view reason); | 
|  | // Closes the association. Note that the TCB will not be valid past this call. | 
|  | void InternalClose(ErrorKind error, absl::string_view message); | 
|  | // Closes the association, because of too many retransmission errors. | 
|  | void CloseConnectionBecauseOfTooManyTransmissionErrors(); | 
|  | // Timer expiration handlers | 
|  | webrtc::TimeDelta OnInitTimerExpiry(); | 
|  | webrtc::TimeDelta OnCookieTimerExpiry(); | 
|  | webrtc::TimeDelta OnShutdownTimerExpiry(); | 
|  | void OnSentPacket(rtc::ArrayView<const uint8_t> packet, | 
|  | SendPacketStatus status); | 
|  | // Sends SHUTDOWN or SHUTDOWN-ACK if the socket is shutting down and if all | 
|  | // outstanding data has been acknowledged. | 
|  | void MaybeSendShutdownOrAck(); | 
|  | // If the socket is shutting down, responds SHUTDOWN to any incoming DATA. | 
|  | void MaybeSendShutdownOnPacketReceived(const SctpPacket& packet); | 
|  | // If there are streams pending to be reset, send a request to reset them. | 
|  | void MaybeSendResetStreamsRequest(); | 
|  | // Performs internal processing shared between Send and SendMany. | 
|  | SendStatus InternalSend(const DcSctpMessage& message, | 
|  | const SendOptions& send_options); | 
|  | // Sends a INIT chunk. | 
|  | void SendInit(); | 
|  | // Sends a SHUTDOWN chunk. | 
|  | void SendShutdown(); | 
|  | // Sends a SHUTDOWN-ACK chunk. | 
|  | void SendShutdownAck(); | 
|  | // Validates the SCTP packet, as a whole - not the validity of individual | 
|  | // chunks within it, as that's done in the different chunk handlers. | 
|  | bool ValidatePacket(const SctpPacket& packet); | 
|  | // Parses `payload`, which is a serialized packet that is just going to be | 
|  | // sent and prints all chunks. | 
|  | void DebugPrintOutgoing(rtc::ArrayView<const uint8_t> payload); | 
|  | // Called whenever data has been received, or the cumulative acknowledgment | 
|  | // TSN has moved, that may result in delivering messages. | 
|  | void MaybeDeliverMessages(); | 
|  | // Returns true if there is a TCB, and false otherwise (and reports an error). | 
|  | bool ValidateHasTCB(); | 
|  |  | 
|  | // Returns true if the parsing of a chunk of type `T` succeeded. If it didn't, | 
|  | // it reports an error and returns false. | 
|  | template <class T> | 
|  | bool ValidateParseSuccess(const absl::optional<T>& c) { | 
|  | if (c.has_value()) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | ReportFailedToParseChunk(T::kType); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Reports failing to have parsed a chunk with the provided `chunk_type`. | 
|  | void ReportFailedToParseChunk(int chunk_type); | 
|  | // Called when unknown chunks are received. May report an error. | 
|  | bool HandleUnrecognizedChunk(const SctpPacket::ChunkDescriptor& descriptor); | 
|  |  | 
|  | // Will dispatch more specific chunk handlers. | 
|  | bool Dispatch(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Handles incoming DATA chunks. | 
|  | void HandleData(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Handles incoming I-DATA chunks. | 
|  | void HandleIData(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Common handler for DATA and I-DATA chunks. | 
|  | void HandleDataCommon(AnyDataChunk& chunk); | 
|  | // Handles incoming INIT chunks. | 
|  | void HandleInit(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Handles incoming INIT-ACK chunks. | 
|  | void HandleInitAck(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Handles incoming SACK chunks. | 
|  | void HandleSack(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Handles incoming HEARTBEAT chunks. | 
|  | void HandleHeartbeatRequest(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Handles incoming HEARTBEAT-ACK chunks. | 
|  | void HandleHeartbeatAck(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Handles incoming ABORT chunks. | 
|  | void HandleAbort(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Handles incoming ERROR chunks. | 
|  | void HandleError(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Handles incoming COOKIE-ECHO chunks. | 
|  | void HandleCookieEcho(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Handles receiving COOKIE-ECHO when there already is a TCB. The return value | 
|  | // indicates if the processing should continue. | 
|  | bool HandleCookieEchoWithTCB(const CommonHeader& header, | 
|  | const StateCookie& cookie); | 
|  | // Handles incoming COOKIE-ACK chunks. | 
|  | void HandleCookieAck(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Handles incoming SHUTDOWN chunks. | 
|  | void HandleShutdown(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Handles incoming SHUTDOWN-ACK chunks. | 
|  | void HandleShutdownAck(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Handles incoming FORWARD-TSN chunks. | 
|  | void HandleForwardTsn(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Handles incoming I-FORWARD-TSN chunks. | 
|  | void HandleIForwardTsn(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Handles incoming RE-CONFIG chunks. | 
|  | void HandleReconfig(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  | // Common handled for FORWARD-TSN/I-FORWARD-TSN. | 
|  | void HandleForwardTsnCommon(const AnyForwardTsnChunk& chunk); | 
|  | // Handles incoming SHUTDOWN-COMPLETE chunks | 
|  | void HandleShutdownComplete(const CommonHeader& header, | 
|  | const SctpPacket::ChunkDescriptor& descriptor); | 
|  |  | 
|  | const std::string log_prefix_; | 
|  | const std::unique_ptr<PacketObserver> packet_observer_; | 
|  | Metrics metrics_; | 
|  | DcSctpOptions options_; | 
|  |  | 
|  | // Enqueues callbacks and dispatches them just before returning to the caller. | 
|  | CallbackDeferrer callbacks_; | 
|  |  | 
|  | TimerManager timer_manager_; | 
|  | const std::unique_ptr<Timer> t1_init_; | 
|  | const std::unique_ptr<Timer> t1_cookie_; | 
|  | const std::unique_ptr<Timer> t2_shutdown_; | 
|  |  | 
|  | // Packets that failed to be sent, but should be retried. | 
|  | PacketSender packet_sender_; | 
|  |  | 
|  | // The actual SendQueue implementation. As data can be sent on a socket before | 
|  | // the connection is established, this component is not in the TCB. | 
|  | RRSendQueue send_queue_; | 
|  |  | 
|  | // Contains verification tag and initial TSN between having sent the INIT | 
|  | // until the connection is established (there is no TCB at this point). | 
|  | ConnectParameters connect_params_; | 
|  | // The socket state. | 
|  | State state_ = State::kClosed; | 
|  | // If the connection is established, contains a transmission control block. | 
|  | std::unique_ptr<TransmissionControlBlock> tcb_; | 
|  | }; | 
|  | }  // namespace dcsctp | 
|  |  | 
|  | #endif  // NET_DCSCTP_SOCKET_DCSCTP_SOCKET_H_ |