├── .github ├── FUNDING.yml └── workflows │ └── cargo.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── reserved ├── rtc-interceptor │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── rtc-mdns │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── rtc-media │ ├── Cargo.toml │ └── src │ └── lib.rs ├── rtc-datachannel ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── codecov.yml └── src │ ├── data_channel │ ├── data_channel_test.rs │ └── mod.rs │ ├── lib.rs │ └── message │ ├── message_channel_ack.rs │ ├── message_channel_open.rs │ ├── message_test.rs │ ├── message_type.rs │ └── mod.rs ├── rtc-dtls ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── codecov.yml ├── examples_wip │ ├── certificates │ │ ├── README.md │ │ ├── client.csr │ │ ├── client.pem │ │ ├── client.pem.private_key.pem │ │ ├── client.pub.pem │ │ ├── extfile.conf │ │ ├── server.csr │ │ ├── server.pem │ │ ├── server.pem.private_key.pem │ │ └── server.pub.pem │ ├── dial │ │ ├── psk │ │ │ └── dial_psk.rs │ │ ├── selfsign │ │ │ └── dial_selfsign.rs │ │ └── verify │ │ │ └── dial_verify.rs │ ├── dtls_chat_server.rs │ ├── dtls_client.rs │ ├── dtls_client_selfsign.rs │ ├── dtls_echo_server.rs │ ├── hub │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ └── utilities.rs │ └── listen │ │ ├── psk │ │ └── listen_psk.rs │ │ ├── selfsign │ │ └── listen_selfsign.rs │ │ └── verify │ │ └── listen_verify.rs └── src │ ├── alert │ ├── alert_test.rs │ └── mod.rs │ ├── application_data.rs │ ├── change_cipher_spec │ ├── change_cipher_spec_test.rs │ └── mod.rs │ ├── cipher_suite │ ├── cipher_suite_aes_128_ccm.rs │ ├── cipher_suite_aes_128_gcm_sha256.rs │ ├── cipher_suite_aes_256_cbc_sha.rs │ ├── cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm.rs │ ├── cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm8.rs │ ├── cipher_suite_tls_psk_with_aes_128_ccm.rs │ ├── cipher_suite_tls_psk_with_aes_128_ccm8.rs │ ├── cipher_suite_tls_psk_with_aes_128_gcm_sha256.rs │ └── mod.rs │ ├── client_certificate_type.rs │ ├── compression_methods.rs │ ├── config.rs │ ├── conn │ ├── conn_test.rs │ └── mod.rs │ ├── content.rs │ ├── crypto │ ├── crypto_cbc.rs │ ├── crypto_ccm.rs │ ├── crypto_gcm.rs │ ├── crypto_test.rs │ ├── mod.rs │ └── padding.rs │ ├── curve │ ├── mod.rs │ └── named_curve.rs │ ├── endpoint.rs │ ├── extension │ ├── extension_server_name.rs │ ├── extension_server_name │ │ └── extension_server_name_test.rs │ ├── extension_supported_elliptic_curves.rs │ ├── extension_supported_elliptic_curves │ │ └── extension_supported_elliptic_curves_test.rs │ ├── extension_supported_point_formats.rs │ ├── extension_supported_point_formats │ │ └── extension_supported_point_formats_test.rs │ ├── extension_supported_signature_algorithms.rs │ ├── extension_supported_signature_algorithms │ │ └── extension_supported_signature_algorithms_test.rs │ ├── extension_use_extended_master_secret.rs │ ├── extension_use_extended_master_secret │ │ └── extension_use_extended_master_secret_test.rs │ ├── extension_use_srtp.rs │ ├── extension_use_srtp │ │ └── extension_use_srtp_test.rs │ ├── mod.rs │ ├── renegotiation_info.rs │ └── renegotiation_info │ │ └── renegotiation_info_test.rs │ ├── flight │ ├── flight0.rs │ ├── flight1.rs │ ├── flight2.rs │ ├── flight3.rs │ ├── flight4.rs │ ├── flight5.rs │ ├── flight6.rs │ └── mod.rs │ ├── fragment_buffer │ ├── fragment_buffer_test.rs │ └── mod.rs │ ├── handshake │ ├── handshake_cache.rs │ ├── handshake_cache │ │ └── handshake_cache_test.rs │ ├── handshake_header.rs │ ├── handshake_message_certificate.rs │ ├── handshake_message_certificate │ │ └── handshake_message_certificate_test.rs │ ├── handshake_message_certificate_request.rs │ ├── handshake_message_certificate_request │ │ └── handshake_message_certificate_request_test.rs │ ├── handshake_message_certificate_verify.rs │ ├── handshake_message_certificate_verify │ │ └── handshake_message_certificate_verify_test.rs │ ├── handshake_message_client_hello.rs │ ├── handshake_message_client_hello │ │ └── handshake_message_client_hello_test.rs │ ├── handshake_message_client_key_exchange.rs │ ├── handshake_message_client_key_exchange │ │ └── handshake_message_client_key_exchange_test.rs │ ├── handshake_message_finished.rs │ ├── handshake_message_finished │ │ └── handshake_message_finished_test.rs │ ├── handshake_message_hello_verify_request.rs │ ├── handshake_message_hello_verify_request │ │ └── handshake_message_hello_verify_request_test.rs │ ├── handshake_message_server_hello.rs │ ├── handshake_message_server_hello │ │ └── handshake_message_server_hello_test.rs │ ├── handshake_message_server_hello_done.rs │ ├── handshake_message_server_hello_done │ │ └── handshake_message_server_hello_done_test.rs │ ├── handshake_message_server_key_exchange.rs │ ├── handshake_message_server_key_exchange │ │ └── handshake_message_server_key_exchange_test.rs │ ├── handshake_random.rs │ ├── handshake_test.rs │ └── mod.rs │ ├── handshaker.rs │ ├── lib.rs │ ├── prf │ ├── mod.rs │ └── prf_test.rs │ ├── record_layer │ ├── mod.rs │ ├── record_layer_header.rs │ └── record_layer_test.rs │ ├── signature_hash_algorithm │ ├── mod.rs │ └── signature_hash_algorithm_test.rs │ └── state.rs ├── rtc-ice ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── codecov.yml ├── examples │ └── ping_pong.rs └── src │ ├── agent │ ├── agent_config.rs │ ├── agent_selector.rs │ ├── agent_stats.rs │ ├── agent_test.rs │ └── mod.rs │ ├── attributes │ ├── control │ │ ├── control_test.rs │ │ └── mod.rs │ ├── mod.rs │ ├── priority │ │ ├── mod.rs │ │ └── priority_test.rs │ └── use_candidate │ │ ├── mod.rs │ │ └── use_candidate_test.rs │ ├── candidate │ ├── candidate_host.rs │ ├── candidate_pair.rs │ ├── candidate_pair_test.rs │ ├── candidate_peer_reflexive.rs │ ├── candidate_relay.rs │ ├── candidate_relay_test.rs │ ├── candidate_server_reflexive.rs │ ├── candidate_server_reflexive_test.rs │ ├── candidate_test.rs │ └── mod.rs │ ├── lib.rs │ ├── network_type │ ├── mod.rs │ └── network_type_test.rs │ ├── rand │ ├── mod.rs │ └── rand_test.rs │ ├── state │ ├── mod.rs │ └── state_test.rs │ ├── stats │ └── mod.rs │ ├── tcp_type │ ├── mod.rs │ └── tcp_type_test.rs │ └── url │ ├── mod.rs │ └── url_test.rs ├── rtc-rtcp ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── codecov.yml └── src │ ├── compound_packet │ ├── compound_packet_test.rs │ └── mod.rs │ ├── extended_report │ ├── dlrr.rs │ ├── extended_report_test.rs │ ├── mod.rs │ ├── prt.rs │ ├── rle.rs │ ├── rrt.rs │ ├── ssr.rs │ ├── unknown.rs │ └── vm.rs │ ├── goodbye │ ├── goodbye_test.rs │ └── mod.rs │ ├── header.rs │ ├── lib.rs │ ├── packet.rs │ ├── payload_feedbacks │ ├── full_intra_request │ │ ├── full_intra_request_test.rs │ │ └── mod.rs │ ├── mod.rs │ ├── picture_loss_indication │ │ ├── mod.rs │ │ └── picture_loss_indication_test.rs │ ├── receiver_estimated_maximum_bitrate │ │ ├── mod.rs │ │ └── receiver_estimated_maximum_bitrate_test.rs │ └── slice_loss_indication │ │ ├── mod.rs │ │ └── slice_loss_indication_test.rs │ ├── raw_packet.rs │ ├── receiver_report │ ├── mod.rs │ └── receiver_report_test.rs │ ├── reception_report.rs │ ├── sender_report │ ├── mod.rs │ └── sender_report_test.rs │ ├── source_description │ ├── mod.rs │ └── source_description_test.rs │ ├── transport_feedbacks │ ├── mod.rs │ ├── rapid_resynchronization_request │ │ ├── mod.rs │ │ └── rapid_resynchronization_request_test.rs │ ├── transport_layer_cc │ │ ├── mod.rs │ │ └── transport_layer_cc_test.rs │ └── transport_layer_nack │ │ ├── mod.rs │ │ └── transport_layer_nack_test.rs │ └── util.rs ├── rtc-rtp ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches │ └── packet_bench.rs ├── codecov.yml └── src │ ├── codecs │ ├── av1 │ │ ├── av1_test.rs │ │ ├── leb128.rs │ │ ├── mod.rs │ │ ├── obu.rs │ │ └── packetizer.rs │ ├── g7xx │ │ ├── g7xx_test.rs │ │ └── mod.rs │ ├── h264 │ │ ├── h264_test.rs │ │ └── mod.rs │ ├── h265 │ │ ├── h265_test.rs │ │ └── mod.rs │ ├── mod.rs │ ├── opus │ │ ├── mod.rs │ │ └── opus_test.rs │ ├── vp8 │ │ ├── mod.rs │ │ └── vp8_test.rs │ └── vp9 │ │ ├── mod.rs │ │ └── vp9_test.rs │ ├── extension │ ├── abs_send_time_extension │ │ ├── abs_send_time_extension_test.rs │ │ └── mod.rs │ ├── audio_level_extension │ │ ├── audio_level_extension_test.rs │ │ └── mod.rs │ ├── mod.rs │ ├── transport_cc_extension │ │ ├── mod.rs │ │ └── transport_cc_extension_test.rs │ └── video_orientation_extension │ │ ├── mod.rs │ │ └── video_orientation_extension_test.rs │ ├── header.rs │ ├── lib.rs │ ├── packet │ ├── mod.rs │ └── packet_test.rs │ ├── packetizer │ ├── mod.rs │ └── packetizer_test.rs │ └── sequence.rs ├── rtc-sctp ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── association │ ├── association_test.rs │ ├── mod.rs │ ├── state.rs │ ├── stats.rs │ ├── stream.rs │ └── timer.rs │ ├── chunk │ ├── chunk_abort.rs │ ├── chunk_cookie_ack.rs │ ├── chunk_cookie_echo.rs │ ├── chunk_error.rs │ ├── chunk_forward_tsn.rs │ ├── chunk_header.rs │ ├── chunk_heartbeat.rs │ ├── chunk_heartbeat_ack.rs │ ├── chunk_init.rs │ ├── chunk_payload_data.rs │ ├── chunk_reconfig.rs │ ├── chunk_selective_ack.rs │ ├── chunk_shutdown.rs │ ├── chunk_shutdown_ack.rs │ ├── chunk_shutdown_complete.rs │ ├── chunk_test.rs │ ├── chunk_type.rs │ └── mod.rs │ ├── config.rs │ ├── endpoint │ ├── endpoint_test.rs │ └── mod.rs │ ├── lib.rs │ ├── packet.rs │ ├── param │ ├── mod.rs │ ├── param_chunk_list.rs │ ├── param_forward_tsn_supported.rs │ ├── param_header.rs │ ├── param_heartbeat_info.rs │ ├── param_outgoing_reset_request.rs │ ├── param_random.rs │ ├── param_reconfig_response.rs │ ├── param_requested_hmac_algorithm.rs │ ├── param_state_cookie.rs │ ├── param_supported_extensions.rs │ ├── param_test.rs │ ├── param_type.rs │ └── param_uknown.rs │ ├── queue │ ├── mod.rs │ ├── payload_queue.rs │ ├── pending_queue.rs │ ├── queue_test.rs │ └── reassembly_queue.rs │ ├── shared.rs │ └── util.rs ├── rtc-sdp ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches │ └── bench.rs ├── codecov.yml ├── fuzz │ ├── .gitignore │ ├── Cargo.toml │ └── fuzz_targets │ │ └── parse_session.rs └── src │ ├── description │ ├── common.rs │ ├── description_test.rs │ ├── media.rs │ ├── mod.rs │ └── session.rs │ ├── direction │ ├── direction_test.rs │ └── mod.rs │ ├── extmap │ ├── extmap_test.rs │ └── mod.rs │ ├── lexer │ └── mod.rs │ ├── lib.rs │ └── util │ ├── mod.rs │ └── util_test.rs ├── rtc-shared ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── crypto │ └── mod.rs │ ├── error.rs │ ├── handler.rs │ ├── lib.rs │ ├── marshal │ └── mod.rs │ ├── replay_detector │ ├── fixed_big_int │ │ ├── fixed_big_int_test.rs │ │ └── mod.rs │ ├── mod.rs │ └── replay_detector_test.rs │ └── util.rs ├── rtc-srtp ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── codecov.yml └── src │ ├── cipher │ ├── cipher_aead_aes_gcm.rs │ ├── cipher_aes_cm_hmac_sha1.rs │ └── mod.rs │ ├── config.rs │ ├── context │ ├── context_test.rs │ ├── mod.rs │ ├── srtcp.rs │ ├── srtcp_test.rs │ ├── srtp.rs │ └── srtp_test.rs │ ├── key_derivation.rs │ ├── lib.rs │ ├── option.rs │ └── protection_profile.rs ├── rtc-stun ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches │ └── bench.rs ├── codecov.yml ├── examples │ ├── stun_client.rs │ └── stun_decode.rs └── src │ ├── addr.rs │ ├── addr │ └── addr_test.rs │ ├── agent.rs │ ├── agent │ └── agent_test.rs │ ├── attributes.rs │ ├── attributes │ └── attributes_test.rs │ ├── checks.rs │ ├── client.rs │ ├── error_code.rs │ ├── fingerprint.rs │ ├── fingerprint │ └── fingerprint_test.rs │ ├── integrity.rs │ ├── integrity │ └── integrity_test.rs │ ├── lib.rs │ ├── message.rs │ ├── message │ └── message_test.rs │ ├── textattrs.rs │ ├── textattrs │ └── textattrs_test.rs │ ├── uattrs.rs │ ├── uattrs │ └── uattrs_test.rs │ ├── uri.rs │ ├── uri │ └── uri_test.rs │ ├── xoraddr.rs │ └── xoraddr │ └── xoraddr_test.rs ├── rtc-turn ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches │ └── bench.rs ├── codecov.yml ├── examples │ └── turn_client_udp.rs └── src │ ├── client │ ├── binding.rs │ ├── binding │ │ └── binding_test.rs │ ├── client_test.rs │ ├── mod.rs │ ├── permission.rs │ ├── relay.rs │ └── transaction.rs │ ├── lib.rs │ └── proto │ ├── addr.rs │ ├── addr │ └── addr_test.rs │ ├── chandata.rs │ ├── chandata │ └── chandata_test.rs │ ├── channum.rs │ ├── channum │ └── channnum_test.rs │ ├── data.rs │ ├── data │ └── data_test.rs │ ├── dontfrag.rs │ ├── dontfrag │ └── dontfrag_test.rs │ ├── evenport.rs │ ├── evenport │ └── evenport_test.rs │ ├── lifetime.rs │ ├── lifetime │ └── lifetime_test.rs │ ├── mod.rs │ ├── peeraddr.rs │ ├── peeraddr │ └── peeraddr_test.rs │ ├── proto_test.rs │ ├── relayaddr.rs │ ├── relayaddr │ └── relayaddr_test.rs │ ├── reqfamily.rs │ ├── reqfamily │ └── reqfamily_test.rs │ ├── reqtrans.rs │ ├── reqtrans │ └── reqtrans_test.rs │ ├── rsrvtoken.rs │ └── rsrvtoken │ └── rsrvtoken_test.rs └── rtc ├── CHANGELOG.md ├── Cargo.toml └── src ├── api ├── api_test.rs ├── interceptor_registry │ ├── interceptor_registry_test.rs │ └── mod.rs ├── media_engine │ ├── media_engine_test.rs │ └── mod.rs ├── mod.rs └── setting_engine │ ├── mod.rs │ └── setting_engine_test.rs ├── constants.rs ├── data_channel ├── data_channel_init.rs ├── data_channel_message.rs ├── data_channel_parameters.rs ├── data_channel_state.rs ├── data_channel_test.rs └── mod.rs ├── handler ├── demuxer.rs ├── dtls.rs ├── ice.rs ├── mod.rs └── sctp.rs ├── lib.rs ├── messages.rs ├── peer_connection ├── certificate.rs ├── configuration.rs ├── mod.rs ├── offer_answer_options.rs ├── operation │ ├── mod.rs │ └── operation_test.rs ├── peer_connection_internal.rs ├── peer_connection_state.rs ├── peer_connection_test.rs ├── policy │ ├── bundle_policy.rs │ ├── ice_transport_policy.rs │ ├── mod.rs │ ├── rtcp_mux_policy.rs │ └── sdp_semantics.rs ├── sdp │ ├── mod.rs │ ├── sdp_test.rs │ ├── sdp_type.rs │ └── session_description.rs └── signaling_state.rs ├── rtp_transceiver ├── fmtp │ ├── generic │ │ ├── generic_test.rs │ │ └── mod.rs │ ├── h264 │ │ ├── h264_test.rs │ │ └── mod.rs │ └── mod.rs ├── mod.rs ├── rtp_codec.rs ├── rtp_receiver │ ├── mod.rs │ └── rtp_receiver_test.rs ├── rtp_sender │ ├── mod.rs │ └── rtp_sender_test.rs ├── rtp_transceiver_direction.rs ├── rtp_transceiver_test.rs └── srtp_writer_future.rs ├── stats ├── mod.rs ├── serialize.rs └── stats_collector.rs ├── track ├── mod.rs ├── track_local │ ├── mod.rs │ ├── track_local_static_rtp.rs │ ├── track_local_static_sample.rs │ └── track_local_static_test.rs └── track_remote │ └── mod.rs └── transport ├── dtls_transport ├── dtls_fingerprint.rs ├── dtls_parameters.rs ├── dtls_role.rs ├── dtls_transport_state.rs ├── dtls_transport_test.rs └── mod.rs ├── ice_transport ├── ice_candidate.rs ├── ice_candidate_pair.rs ├── ice_candidate_type.rs ├── ice_connection_state.rs ├── ice_credential_type.rs ├── ice_gatherer.rs ├── ice_gatherer_state.rs ├── ice_gathering_state.rs ├── ice_parameters.rs ├── ice_protocol.rs ├── ice_role.rs ├── ice_server.rs ├── ice_transport_state.rs ├── ice_transport_test.rs └── mod.rs ├── mod.rs └── sctp_transport ├── mod.rs ├── sctp_transport_capabilities.rs ├── sctp_transport_state.rs └── sctp_transport_test.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: webrtc-rs # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: WebRTCrs # Replace with a single Patreon username 5 | open_collective: webrtc-rs # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/cargo.yml: -------------------------------------------------------------------------------- 1 | name: cargo 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | env: 14 | CARGO_TERM_COLOR: always 15 | 16 | jobs: 17 | build: 18 | name: Build and test 19 | strategy: 20 | matrix: 21 | os: ['ubuntu-latest', 'macos-latest', 'windows-latest'] 22 | runs-on: ${{ matrix.os }} 23 | steps: 24 | - uses: actions/checkout@v2 25 | - name: Build 26 | run: cargo build --verbose 27 | - name: Run tests 28 | run: cargo test --verbose 29 | 30 | rustfmt_and_clippy: 31 | name: Check rustfmt style && run clippy 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v2 35 | - uses: actions-rs/toolchain@v1 36 | with: 37 | toolchain: stable 38 | profile: minimal 39 | components: clippy, rustfmt 40 | override: true 41 | - name: Cache cargo registry 42 | uses: actions/cache@v1 43 | with: 44 | path: ~/.cargo/registry 45 | key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} 46 | - name: Run clippy 47 | uses: actions-rs/cargo@v1 48 | with: 49 | command: clippy 50 | - name: Check formating 51 | uses: actions-rs/cargo@v1 52 | with: 53 | command: fmt 54 | args: --all -- --check 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /.idea/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "rtc", 4 | "rtc-datachannel", 5 | "rtc-dtls", 6 | "rtc-ice", 7 | "rtc-rtcp", 8 | "rtc-rtp", 9 | "rtc-sctp", 10 | "rtc-sdp", 11 | "rtc-shared", 12 | "rtc-srtp", 13 | "rtc-stun", 14 | "rtc-turn", 15 | "reserved/rtc-interceptor", 16 | "reserved/rtc-mdns", 17 | "reserved/rtc-media", 18 | ] 19 | resolver = "2" 20 | 21 | [profile.dev] 22 | opt-level = 0 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 ngRTC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /reserved/rtc-interceptor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtc-interceptor" 3 | version = "0.0.0" 4 | authors = ["Rain Liu "] 5 | edition = "2021" 6 | description = "RTC Interceptor in Rust" 7 | license = "MIT/Apache-2.0" 8 | documentation = "https://docs.rs/rtc-interceptor" 9 | homepage = "https://ortc.rs" 10 | repository = "https://github.com/webrtc-rs/rtc/tree/master/reserved/rtc-interceptor" 11 | 12 | [dependencies] 13 | 14 | [dev-dependencies] 15 | -------------------------------------------------------------------------------- /reserved/rtc-interceptor/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms)] 2 | #![allow(dead_code)] 3 | -------------------------------------------------------------------------------- /reserved/rtc-mdns/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtc-mdns" 3 | version = "0.0.0" 4 | authors = ["Rain Liu "] 5 | edition = "2021" 6 | description = "RTC mDNS in Rust" 7 | license = "MIT/Apache-2.0" 8 | documentation = "https://docs.rs/rtc-mdns" 9 | homepage = "https://ortc.rs" 10 | repository = "https://github.com/webrtc-rs/rtc/tree/master/reserved/rtc-mdns" 11 | 12 | [dependencies] 13 | 14 | [dev-dependencies] 15 | -------------------------------------------------------------------------------- /reserved/rtc-mdns/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms)] 2 | #![allow(dead_code)] 3 | -------------------------------------------------------------------------------- /reserved/rtc-media/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtc-media" 3 | version = "0.0.0" 4 | authors = ["Rain Liu "] 5 | edition = "2021" 6 | description = "RTC Media in Rust" 7 | license = "MIT/Apache-2.0" 8 | documentation = "https://docs.rs/rtc-media" 9 | homepage = "https://ortc.rs" 10 | repository = "https://github.com/webrtc-rs/rtc/tree/master/reserved/rtc-media" 11 | 12 | [dependencies] 13 | 14 | [dev-dependencies] 15 | -------------------------------------------------------------------------------- /reserved/rtc-media/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms)] 2 | #![allow(dead_code)] 3 | -------------------------------------------------------------------------------- /rtc-datachannel/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /.idea/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | -------------------------------------------------------------------------------- /rtc-datachannel/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # rtc-datachannel changelog 2 | 3 | ## Unreleased 4 | -------------------------------------------------------------------------------- /rtc-datachannel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtc-datachannel" 3 | version = "0.2.0" 4 | authors = ["Rain Liu "] 5 | edition = "2021" 6 | description = "RTC DataChannel in Rust" 7 | license = "MIT/Apache-2.0" 8 | documentation = "https://docs.rs/rtc-datachannel" 9 | homepage = "https://ortc.rs" 10 | repository = "https://github.com/webrtc-rs/rtc/tree/master/rtc-datachannel" 11 | 12 | [dependencies] 13 | shared = { version = "0.2.0", path = "../rtc-shared", package = "rtc-shared", default-features = false, features = ["marshal"] } 14 | sctp = { version = "0.2.0", path = "../rtc-sctp", package = "rtc-sctp" } 15 | 16 | bytes = "1.5.0" 17 | log = "0.4.21" 18 | 19 | [dev-dependencies] 20 | env_logger = "0.11.3" 21 | chrono = "0.4.35" 22 | -------------------------------------------------------------------------------- /rtc-datachannel/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 WebRTC.rs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rtc-datachannel/README.md: -------------------------------------------------------------------------------- 1 |

2 | WebRTC.rs 3 |
4 |

5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | License: MIT/Apache 2.0 23 | 24 | 25 | Discord 26 | 27 |

28 |

29 | RTC DataChannel Protocol in Rust With Sans-IO 30 |

31 | -------------------------------------------------------------------------------- /rtc-datachannel/codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | max_report_age: off 4 | token: 00d131c6-1478-4018-b481-be1b44f5f094 5 | 6 | coverage: 7 | precision: 2 8 | round: down 9 | range: 50..90 10 | status: 11 | project: 12 | default: 13 | enabled: no 14 | threshold: 0.2 15 | if_not_found: success 16 | patch: 17 | default: 18 | enabled: no 19 | if_not_found: success 20 | changes: 21 | default: 22 | enabled: no 23 | if_not_found: success 24 | -------------------------------------------------------------------------------- /rtc-datachannel/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms)] 2 | #![allow(dead_code)] 3 | 4 | pub mod data_channel; 5 | pub mod message; 6 | -------------------------------------------------------------------------------- /rtc-dtls/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /examples/hub/target 5 | /.idea/ 6 | 7 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 8 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 9 | Cargo.lock 10 | 11 | # These are backup files generated by rustfmt 12 | **/*.rs.bk 13 | -------------------------------------------------------------------------------- /rtc-dtls/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # rtc-dtls changelog 2 | 3 | ## Unreleased 4 | -------------------------------------------------------------------------------- /rtc-dtls/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 WebRTC.rs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rtc-dtls/README.md: -------------------------------------------------------------------------------- 1 |

2 | WebRTC.rs 3 |
4 |

5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | License: MIT/Apache 2.0 23 | 24 | 25 | Discord 26 | 27 |

28 |

29 | RTC DTLS Protocol in Rust With Sans-IO 30 |

31 | -------------------------------------------------------------------------------- /rtc-dtls/codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | max_report_age: off 4 | token: 14bc9fb7-bdcf-4355-8e0e-ebec14066ae5 5 | 6 | coverage: 7 | precision: 2 8 | round: down 9 | range: 50..90 10 | status: 11 | project: 12 | default: 13 | enabled: no 14 | threshold: 0.2 15 | if_not_found: success 16 | patch: 17 | default: 18 | enabled: no 19 | if_not_found: success 20 | changes: 21 | default: 22 | enabled: no 23 | if_not_found: success 24 | -------------------------------------------------------------------------------- /rtc-dtls/examples_wip/certificates/client.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIHHMG8CAQAwDTELMAkGA1UEBhMCTkwwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC 3 | AAQhwOK0F+5DQy5FG0dRdN0GF20p3MsTaBk73IpNzrlK+WtgxdVxmRm55LWCTgkA 4 | RhnOcmzXW+raCEWQgTadaLd5oAAwCgYIKoZIzj0EAwIDSAAwRQIhANDZpyL2lr50 5 | Xr5DrD19SOa7LXpXz3DcM8RDLcBQvx05AiB7mbtcY6I18diHU0jSxHAGcUn5nAeD 6 | EP4tqFOz7QRzgQ== 7 | -----END CERTIFICATE REQUEST----- 8 | -------------------------------------------------------------------------------- /rtc-dtls/examples_wip/certificates/client.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIO7fb5dmM2P0F71o/Clo0ElO29ud+JbtA3fhDIL15AgioAoGCCqGSM49 3 | AwEHoUQDQgAEIcDitBfuQ0MuRRtHUXTdBhdtKdzLE2gZO9yKTc65SvlrYMXVcZkZ 4 | ueS1gk4JAEYZznJs11vq2ghFkIE2nWi3eQ== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /rtc-dtls/examples_wip/certificates/client.pem.private_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg7t9vl2YzY/QXvWj8 3 | KWjQSU7b2534lu0Dd+EMgvXkCCKhRANCAAQhwOK0F+5DQy5FG0dRdN0GF20p3MsT 4 | aBk73IpNzrlK+WtgxdVxmRm55LWCTgkARhnOcmzXW+raCEWQgTadaLd5 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /rtc-dtls/examples_wip/certificates/client.pub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBITCByaADAgECAgMAq80wCgYIKoZIzj0EAwIwDTELMAkGA1UEBhMCTkwwHhcN 3 | MjEwOTE4MjAzNzE1WhcNMjIwOTE4MjAzNzE1WjANMQswCQYDVQQGEwJOTDBZMBMG 4 | ByqGSM49AgEGCCqGSM49AwEHA0IABCHA4rQX7kNDLkUbR1F03QYXbSncyxNoGTvc 5 | ik3OuUr5a2DF1XGZGbnktYJOCQBGGc5ybNdb6toIRZCBNp1ot3mjGDAWMBQGA1Ud 6 | EQQNMAuCCXdlYnJ0Yy5yczAKBggqhkjOPQQDAgNHADBEAiA8mpJVfaCw+RwALmxN 7 | XD28Ze3DUPomlfXhx+NGuePt5QIgAcRxvuDctyL07f8pQ5n22NOioNHdjwOjxww+ 8 | ZekD+Lg= 9 | -----END CERTIFICATE----- 10 | -------------------------------------------------------------------------------- /rtc-dtls/examples_wip/certificates/extfile.conf: -------------------------------------------------------------------------------- 1 | subjectAltName = DNS:webrtc.rs 2 | -------------------------------------------------------------------------------- /rtc-dtls/examples_wip/certificates/server.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIHHMG8CAQAwDTELMAkGA1UEBhMCTkwwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC 3 | AASRyJbgbcieCbC1/HbiqmADkPxfk5Bmwjei2YXhPE+oYS3F5+df4BKNBgs7py7H 4 | sxc768+6X8HmvYlfvk2kHXAVoAAwCgYIKoZIzj0EAwIDSAAwRQIhAKR9rI22Xk/U 5 | L3xp2dzn7q3nyWqgDvp5uTflP4t0MBpJAiAJDKmcOCXNMhhgg4T2lhdfz/pZVfu5 6 | lxLcZm2ELiYImQ== 7 | -----END CERTIFICATE REQUEST----- 8 | -------------------------------------------------------------------------------- /rtc-dtls/examples_wip/certificates/server.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEID358pSfZXZqwqURqBLvLYcqhOdZVVNR2toCMER39YHboAoGCCqGSM49 3 | AwEHoUQDQgAEkciW4G3Ingmwtfx24qpgA5D8X5OQZsI3otmF4TxPqGEtxefnX+AS 4 | jQYLO6cux7MXO+vPul/B5r2JX75NpB1wFQ== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /rtc-dtls/examples_wip/certificates/server.pem.private_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPfnylJ9ldmrCpRGo 3 | Eu8thyqE51lVU1Ha2gIwRHf1gduhRANCAASRyJbgbcieCbC1/HbiqmADkPxfk5Bm 4 | wjei2YXhPE+oYS3F5+df4BKNBgs7py7Hsxc768+6X8HmvYlfvk2kHXAV 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /rtc-dtls/examples_wip/certificates/server.pub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBMjCB2qADAgECAhQzsfVoH1cRfcxCY/drp/cKdjvBDDAKBggqhkjOPQQDAjAN 3 | MQswCQYDVQQGEwJOTDAeFw0yMTA5MTgyMDM2NTVaFw0yMjA5MTgyMDM2NTVaMA0x 4 | CzAJBgNVBAYTAk5MMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkciW4G3Ingmw 5 | tfx24qpgA5D8X5OQZsI3otmF4TxPqGEtxefnX+ASjQYLO6cux7MXO+vPul/B5r2J 6 | X75NpB1wFaMYMBYwFAYDVR0RBA0wC4IJd2VicnRjLnJzMAoGCCqGSM49BAMCA0cA 7 | MEQCIBZBGmNM3qig7OTMZLL4PYj4JrGMjIj/jZFHEhqeQn6HAiBpRte9WzCjJzZX 8 | vzRkUKfCs1NMa/XR0hfdaa8KJAdKyQ== 9 | -----END CERTIFICATE----- 10 | -------------------------------------------------------------------------------- /rtc-dtls/examples_wip/hub/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hub" 3 | version = "0.0.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | util = { version = "0.7.0", package = "webrtc-util", default-features = false, features = ["conn"] } 8 | shared = { path = "../../../rtc-shared", package = "rtc-shared", default-features = false, features = [] } 9 | dtls = { package = "dtls", path = "../../" } 10 | 11 | tokio = { version = "1", features = ["full"] } 12 | rcgen = { version = "0.10", features = ["pem", "x509-parser"] } 13 | rustls = "0.19" 14 | thiserror = "1" 15 | -------------------------------------------------------------------------------- /rtc-dtls/src/alert/alert_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use shared::error::Error; 3 | 4 | use std::io::{BufReader, BufWriter}; 5 | 6 | #[test] 7 | fn test_alert() -> Result<()> { 8 | let tests = vec![ 9 | ( 10 | "Valid Alert", 11 | vec![0x02, 0x0A], 12 | Alert { 13 | alert_level: AlertLevel::Fatal, 14 | alert_description: AlertDescription::UnexpectedMessage, 15 | }, 16 | None, 17 | ), 18 | ( 19 | "Invalid alert length", 20 | vec![0x00], 21 | Alert { 22 | alert_level: AlertLevel::Invalid, 23 | alert_description: AlertDescription::Invalid, 24 | }, 25 | Some(Error::Other("io".to_owned())), 26 | ), 27 | ]; 28 | 29 | for (name, data, wanted, unmarshal_error) in tests { 30 | let mut reader = BufReader::new(data.as_slice()); 31 | let result = Alert::unmarshal(&mut reader); 32 | 33 | if let Some(err) = unmarshal_error { 34 | assert!(result.is_err(), "{name} expected error: {err}"); 35 | } else if let Ok(alert) = result { 36 | assert_eq!(wanted, alert, "{name} expected {wanted}, but got {alert}"); 37 | 38 | let mut data2: Vec = vec![]; 39 | { 40 | let mut writer = BufWriter::<&mut Vec>::new(data2.as_mut()); 41 | alert.marshal(&mut writer)?; 42 | } 43 | assert_eq!(data, data2, "{name} expected {data:?}, but got {data2:?}"); 44 | } else { 45 | assert!(result.is_ok(), "{name} expected Ok, but has error"); 46 | } 47 | } 48 | 49 | Ok(()) 50 | } 51 | -------------------------------------------------------------------------------- /rtc-dtls/src/application_data.rs: -------------------------------------------------------------------------------- 1 | use bytes::BytesMut; 2 | use std::io::{Read, Write}; 3 | 4 | use super::content::*; 5 | use shared::error::Result; 6 | 7 | // Application data messages are carried by the record layer and are 8 | // fragmented, compressed, and encrypted based on the current connection 9 | // state. The messages are treated as transparent data to the record 10 | // layer. 11 | // https://tools.ietf.org/html/rfc5246#section-10 12 | #[derive(Clone, PartialEq, Eq, Debug)] 13 | pub struct ApplicationData { 14 | pub data: BytesMut, 15 | } 16 | 17 | impl ApplicationData { 18 | pub fn content_type(&self) -> ContentType { 19 | ContentType::ApplicationData 20 | } 21 | 22 | pub fn size(&self) -> usize { 23 | self.data.len() 24 | } 25 | 26 | pub fn marshal(&self, writer: &mut W) -> Result<()> { 27 | writer.write_all(&self.data)?; 28 | 29 | Ok(writer.flush()?) 30 | } 31 | 32 | pub fn unmarshal(reader: &mut R) -> Result { 33 | //TODO: use Bytes and implement trait Unmarshal 34 | let mut data: Vec = vec![]; 35 | reader.read_to_end(&mut data)?; 36 | 37 | Ok(ApplicationData { 38 | data: BytesMut::from(&data[..]), 39 | }) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /rtc-dtls/src/change_cipher_spec/change_cipher_spec_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use std::io::{BufReader, BufWriter}; 4 | 5 | #[test] 6 | fn test_change_cipher_spec_round_trip() -> Result<()> { 7 | let c = ChangeCipherSpec {}; 8 | let mut raw = vec![]; 9 | { 10 | let mut writer = BufWriter::<&mut Vec>::new(raw.as_mut()); 11 | c.marshal(&mut writer)?; 12 | } 13 | 14 | let mut reader = BufReader::new(raw.as_slice()); 15 | let cnew = ChangeCipherSpec::unmarshal(&mut reader)?; 16 | assert_eq!( 17 | c, cnew, 18 | "ChangeCipherSpec round trip: got {cnew:?}, want {c:?}" 19 | ); 20 | 21 | Ok(()) 22 | } 23 | 24 | #[test] 25 | fn test_change_cipher_spec_invalid() -> Result<()> { 26 | let data = vec![0x00]; 27 | 28 | let mut reader = BufReader::new(data.as_slice()); 29 | let result = ChangeCipherSpec::unmarshal(&mut reader); 30 | 31 | match result { 32 | Ok(_) => panic!("must be error"), 33 | Err(err) => assert_eq!(err.to_string(), Error::ErrInvalidCipherSpec.to_string()), 34 | }; 35 | 36 | Ok(()) 37 | } 38 | -------------------------------------------------------------------------------- /rtc-dtls/src/change_cipher_spec/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod change_cipher_spec_test; 3 | 4 | use byteorder::{ReadBytesExt, WriteBytesExt}; 5 | use std::io::{Read, Write}; 6 | 7 | use super::content::*; 8 | use shared::error::*; 9 | 10 | // The change cipher spec protocol exists to signal transitions in 11 | // ciphering strategies. The protocol consists of a single message, 12 | // which is encrypted and compressed under the current (not the pending) 13 | // connection state. The message consists of a single byte of value 1. 14 | // https://tools.ietf.org/html/rfc5246#section-7.1 15 | #[derive(Clone, PartialEq, Eq, Debug)] 16 | pub struct ChangeCipherSpec; 17 | 18 | impl ChangeCipherSpec { 19 | pub fn content_type(&self) -> ContentType { 20 | ContentType::ChangeCipherSpec 21 | } 22 | 23 | pub fn size(&self) -> usize { 24 | 1 25 | } 26 | 27 | pub fn marshal(&self, writer: &mut W) -> Result<()> { 28 | writer.write_u8(0x01)?; 29 | 30 | Ok(writer.flush()?) 31 | } 32 | 33 | pub fn unmarshal(reader: &mut R) -> Result { 34 | let data = reader.read_u8()?; 35 | if data != 0x01 { 36 | return Err(Error::ErrInvalidCipherSpec); 37 | } 38 | 39 | Ok(ChangeCipherSpec {}) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /rtc-dtls/src/cipher_suite/cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::cipher_suite::cipher_suite_aes_128_ccm::CipherSuiteAes128Ccm; 3 | use crate::crypto::crypto_ccm::CryptoCcmTagLen; 4 | 5 | pub fn new_cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm() -> CipherSuiteAes128Ccm { 6 | CipherSuiteAes128Ccm::new( 7 | ClientCertificateType::EcdsaSign, 8 | CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_128_Ccm, 9 | false, 10 | CryptoCcmTagLen::CryptoCcmTagLength, 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /rtc-dtls/src/cipher_suite/cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm8.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::cipher_suite::cipher_suite_aes_128_ccm::CipherSuiteAes128Ccm; 3 | use crate::crypto::crypto_ccm::CryptoCcmTagLen; 4 | 5 | pub fn new_cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm8() -> CipherSuiteAes128Ccm { 6 | CipherSuiteAes128Ccm::new( 7 | ClientCertificateType::EcdsaSign, 8 | CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_128_Ccm_8, 9 | false, 10 | CryptoCcmTagLen::CryptoCcm8TagLength, 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /rtc-dtls/src/cipher_suite/cipher_suite_tls_psk_with_aes_128_ccm.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::cipher_suite::cipher_suite_aes_128_ccm::CipherSuiteAes128Ccm; 3 | use crate::crypto::crypto_ccm::CryptoCcmTagLen; 4 | 5 | pub fn new_cipher_suite_tls_psk_with_aes_128_ccm() -> CipherSuiteAes128Ccm { 6 | CipherSuiteAes128Ccm::new( 7 | ClientCertificateType::Unsupported, 8 | CipherSuiteId::Tls_Psk_With_Aes_128_Ccm, 9 | true, 10 | CryptoCcmTagLen::CryptoCcmTagLength, 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /rtc-dtls/src/cipher_suite/cipher_suite_tls_psk_with_aes_128_ccm8.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::cipher_suite::cipher_suite_aes_128_ccm::CipherSuiteAes128Ccm; 3 | use crate::crypto::crypto_ccm::CryptoCcmTagLen; 4 | 5 | pub fn new_cipher_suite_tls_psk_with_aes_128_ccm8() -> CipherSuiteAes128Ccm { 6 | CipherSuiteAes128Ccm::new( 7 | ClientCertificateType::Unsupported, 8 | CipherSuiteId::Tls_Psk_With_Aes_128_Ccm_8, 9 | true, 10 | CryptoCcmTagLen::CryptoCcm8TagLength, 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /rtc-dtls/src/client_certificate_type.rs: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 2 | pub enum ClientCertificateType { 3 | RsaSign = 1, 4 | EcdsaSign = 64, 5 | Unsupported, 6 | } 7 | 8 | impl From for ClientCertificateType { 9 | fn from(val: u8) -> Self { 10 | match val { 11 | 1 => ClientCertificateType::RsaSign, 12 | 64 => ClientCertificateType::EcdsaSign, 13 | _ => ClientCertificateType::Unsupported, 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /rtc-dtls/src/compression_methods.rs: -------------------------------------------------------------------------------- 1 | use shared::error::Result; 2 | 3 | use byteorder::{ReadBytesExt, WriteBytesExt}; 4 | use std::io::{Read, Write}; 5 | 6 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 7 | pub enum CompressionMethodId { 8 | Null = 0, 9 | Unsupported, 10 | } 11 | 12 | impl From for CompressionMethodId { 13 | fn from(val: u8) -> Self { 14 | match val { 15 | 0 => CompressionMethodId::Null, 16 | _ => CompressionMethodId::Unsupported, 17 | } 18 | } 19 | } 20 | 21 | #[derive(Clone, Debug, PartialEq, Eq)] 22 | pub struct CompressionMethods { 23 | pub ids: Vec, 24 | } 25 | 26 | impl CompressionMethods { 27 | pub fn size(&self) -> usize { 28 | 1 + self.ids.len() 29 | } 30 | 31 | pub fn marshal(&self, writer: &mut W) -> Result<()> { 32 | writer.write_u8(self.ids.len() as u8)?; 33 | 34 | for id in &self.ids { 35 | writer.write_u8(*id as u8)?; 36 | } 37 | 38 | Ok(writer.flush()?) 39 | } 40 | 41 | pub fn unmarshal(reader: &mut R) -> Result { 42 | let compression_methods_count = reader.read_u8()? as usize; 43 | let mut ids = vec![]; 44 | for _ in 0..compression_methods_count { 45 | let id = reader.read_u8()?.into(); 46 | if id != CompressionMethodId::Unsupported { 47 | ids.push(id); 48 | } 49 | } 50 | 51 | Ok(CompressionMethods { ids }) 52 | } 53 | } 54 | 55 | pub fn default_compression_methods() -> CompressionMethods { 56 | CompressionMethods { 57 | ids: vec![CompressionMethodId::Null], 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /rtc-dtls/src/curve/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod named_curve; 2 | 3 | // https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-10 4 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 5 | pub enum EllipticCurveType { 6 | NamedCurve = 0x03, 7 | Unsupported, 8 | } 9 | 10 | impl From for EllipticCurveType { 11 | fn from(val: u8) -> Self { 12 | match val { 13 | 0x03 => EllipticCurveType::NamedCurve, 14 | _ => EllipticCurveType::Unsupported, 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rtc-dtls/src/extension/extension_server_name.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod extension_server_name_test; 3 | 4 | use super::*; 5 | 6 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 7 | use std::io::{Read, Write}; 8 | 9 | const EXTENSION_SERVER_NAME_TYPE_DNSHOST_NAME: u8 = 0; 10 | 11 | #[derive(Clone, Debug, PartialEq, Eq)] 12 | pub struct ExtensionServerName { 13 | pub(crate) server_name: String, 14 | } 15 | 16 | impl ExtensionServerName { 17 | pub fn extension_value(&self) -> ExtensionValue { 18 | ExtensionValue::ServerName 19 | } 20 | 21 | pub fn size(&self) -> usize { 22 | //TODO: check how to do cryptobyte? 23 | 2 + 2 + 1 + 2 + self.server_name.len() 24 | } 25 | 26 | pub fn marshal(&self, writer: &mut W) -> Result<()> { 27 | //TODO: check how to do cryptobyte? 28 | writer.write_u16::(2 + 1 + 2 + self.server_name.len() as u16)?; 29 | writer.write_u16::(1 + 2 + self.server_name.len() as u16)?; 30 | writer.write_u8(EXTENSION_SERVER_NAME_TYPE_DNSHOST_NAME)?; 31 | writer.write_u16::(self.server_name.len() as u16)?; 32 | writer.write_all(self.server_name.as_bytes())?; 33 | 34 | Ok(writer.flush()?) 35 | } 36 | 37 | pub fn unmarshal(reader: &mut R) -> Result { 38 | //TODO: check how to do cryptobyte? 39 | let _ = reader.read_u16::()? as usize; 40 | let _ = reader.read_u16::()? as usize; 41 | 42 | let name_type = reader.read_u8()?; 43 | if name_type != EXTENSION_SERVER_NAME_TYPE_DNSHOST_NAME { 44 | return Err(Error::ErrInvalidSniFormat); 45 | } 46 | 47 | let buf_len = reader.read_u16::()? as usize; 48 | let mut buf: Vec = vec![0u8; buf_len]; 49 | reader.read_exact(&mut buf)?; 50 | 51 | let server_name = String::from_utf8(buf)?; 52 | 53 | Ok(ExtensionServerName { server_name }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /rtc-dtls/src/extension/extension_server_name/extension_server_name_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use std::io::{BufReader, BufWriter}; 4 | 5 | #[test] 6 | fn test_extension_server_name() -> Result<()> { 7 | let extension = ExtensionServerName { 8 | server_name: "test.domain".to_owned(), 9 | }; 10 | 11 | let mut raw = vec![]; 12 | { 13 | let mut writer = BufWriter::<&mut Vec>::new(raw.as_mut()); 14 | extension.marshal(&mut writer)?; 15 | } 16 | 17 | let mut reader = BufReader::new(raw.as_slice()); 18 | let new_extension = ExtensionServerName::unmarshal(&mut reader)?; 19 | 20 | assert_eq!( 21 | new_extension, extension, 22 | "extensionServerName marshal: got {new_extension:?} expected {extension:?}", 23 | ); 24 | 25 | Ok(()) 26 | } 27 | -------------------------------------------------------------------------------- /rtc-dtls/src/extension/extension_supported_elliptic_curves.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod extension_supported_elliptic_curves_test; 3 | 4 | use super::*; 5 | use crate::curve::named_curve::*; 6 | 7 | const EXTENSION_SUPPORTED_GROUPS_HEADER_SIZE: usize = 6; 8 | 9 | // https://tools.ietf.org/html/rfc8422#section-5.1.1 10 | #[derive(Clone, Debug, PartialEq, Eq)] 11 | pub struct ExtensionSupportedEllipticCurves { 12 | pub elliptic_curves: Vec, 13 | } 14 | 15 | impl ExtensionSupportedEllipticCurves { 16 | pub fn extension_value(&self) -> ExtensionValue { 17 | ExtensionValue::SupportedEllipticCurves 18 | } 19 | 20 | pub fn size(&self) -> usize { 21 | 2 + 2 + self.elliptic_curves.len() * 2 22 | } 23 | 24 | pub fn marshal(&self, writer: &mut W) -> Result<()> { 25 | writer.write_u16::(2 + 2 * self.elliptic_curves.len() as u16)?; 26 | writer.write_u16::(2 * self.elliptic_curves.len() as u16)?; 27 | for v in &self.elliptic_curves { 28 | writer.write_u16::(*v as u16)?; 29 | } 30 | 31 | Ok(writer.flush()?) 32 | } 33 | 34 | pub fn unmarshal(reader: &mut R) -> Result { 35 | let _ = reader.read_u16::()?; 36 | 37 | let group_count = reader.read_u16::()? as usize / 2; 38 | let mut elliptic_curves = vec![]; 39 | for _ in 0..group_count { 40 | let elliptic_curve = reader.read_u16::()?.into(); 41 | elliptic_curves.push(elliptic_curve); 42 | } 43 | 44 | Ok(ExtensionSupportedEllipticCurves { elliptic_curves }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /rtc-dtls/src/extension/extension_supported_elliptic_curves/extension_supported_elliptic_curves_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use std::io::{BufReader, BufWriter}; 4 | 5 | #[test] 6 | fn test_extension_supported_groups() -> Result<()> { 7 | let raw_supported_groups = vec![0x0, 0x4, 0x0, 0x2, 0x0, 0x1d]; // 0x0, 0xa, 8 | let parsed_supported_groups = ExtensionSupportedEllipticCurves { 9 | elliptic_curves: vec![NamedCurve::X25519], 10 | }; 11 | 12 | let mut raw = vec![]; 13 | { 14 | let mut writer = BufWriter::<&mut Vec>::new(raw.as_mut()); 15 | parsed_supported_groups.marshal(&mut writer)?; 16 | } 17 | 18 | assert_eq!( 19 | raw, raw_supported_groups, 20 | "extensionSupportedGroups marshal: got {raw:?}, want {raw_supported_groups:?}" 21 | ); 22 | 23 | let mut reader = BufReader::new(raw.as_slice()); 24 | let new_supported_groups = ExtensionSupportedEllipticCurves::unmarshal(&mut reader)?; 25 | 26 | assert_eq!( 27 | new_supported_groups, parsed_supported_groups, 28 | "extensionSupportedGroups unmarshal: got {new_supported_groups:?}, want {parsed_supported_groups:?}" 29 | ); 30 | 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /rtc-dtls/src/extension/extension_supported_point_formats.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod extension_supported_point_formats_test; 3 | 4 | use super::*; 5 | 6 | const EXTENSION_SUPPORTED_POINT_FORMATS_SIZE: usize = 5; 7 | 8 | pub type EllipticCurvePointFormat = u8; 9 | 10 | pub const ELLIPTIC_CURVE_POINT_FORMAT_UNCOMPRESSED: EllipticCurvePointFormat = 0; 11 | 12 | // https://tools.ietf.org/html/rfc4492#section-5.1.2 13 | #[derive(Clone, Debug, PartialEq, Eq)] 14 | pub struct ExtensionSupportedPointFormats { 15 | pub(crate) point_formats: Vec, 16 | } 17 | 18 | impl ExtensionSupportedPointFormats { 19 | pub fn extension_value(&self) -> ExtensionValue { 20 | ExtensionValue::SupportedPointFormats 21 | } 22 | 23 | pub fn size(&self) -> usize { 24 | 2 + 1 + self.point_formats.len() 25 | } 26 | 27 | pub fn marshal(&self, writer: &mut W) -> Result<()> { 28 | writer.write_u16::(1 + self.point_formats.len() as u16)?; 29 | writer.write_u8(self.point_formats.len() as u8)?; 30 | for v in &self.point_formats { 31 | writer.write_u8(*v)?; 32 | } 33 | 34 | Ok(writer.flush()?) 35 | } 36 | 37 | pub fn unmarshal(reader: &mut R) -> Result { 38 | let _ = reader.read_u16::()?; 39 | 40 | let point_format_count = reader.read_u8()? as usize; 41 | let mut point_formats = vec![]; 42 | for _ in 0..point_format_count { 43 | let point_format = reader.read_u8()?; 44 | point_formats.push(point_format); 45 | } 46 | 47 | Ok(ExtensionSupportedPointFormats { point_formats }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /rtc-dtls/src/extension/extension_supported_point_formats/extension_supported_point_formats_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use std::io::{BufReader, BufWriter}; 4 | 5 | #[test] 6 | fn test_extension_supported_point_formats() -> Result<()> { 7 | let raw_extension_supported_point_formats = vec![0x00, 0x02, 0x01, 0x00]; // 0x00, 0x0b, 8 | let parsed_extension_supported_point_formats = ExtensionSupportedPointFormats { 9 | point_formats: vec![ELLIPTIC_CURVE_POINT_FORMAT_UNCOMPRESSED], 10 | }; 11 | 12 | let mut raw = vec![]; 13 | { 14 | let mut writer = BufWriter::<&mut Vec>::new(raw.as_mut()); 15 | parsed_extension_supported_point_formats.marshal(&mut writer)?; 16 | } 17 | 18 | assert_eq!( 19 | raw, raw_extension_supported_point_formats, 20 | "extensionSupportedPointFormats marshal: got {raw:?}, want {raw_extension_supported_point_formats:?}" 21 | ); 22 | 23 | let mut reader = BufReader::new(raw.as_slice()); 24 | let new_extension_supported_point_formats = 25 | ExtensionSupportedPointFormats::unmarshal(&mut reader)?; 26 | 27 | assert_eq!( 28 | new_extension_supported_point_formats, parsed_extension_supported_point_formats, 29 | "extensionSupportedPointFormats unmarshal: got {new_extension_supported_point_formats:?}, want {parsed_extension_supported_point_formats:?}" 30 | ); 31 | 32 | Ok(()) 33 | } 34 | -------------------------------------------------------------------------------- /rtc-dtls/src/extension/extension_supported_signature_algorithms.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod extension_supported_signature_algorithms_test; 3 | 4 | use super::*; 5 | use crate::signature_hash_algorithm::*; 6 | 7 | const EXTENSION_SUPPORTED_SIGNATURE_ALGORITHMS_HEADER_SIZE: usize = 6; 8 | 9 | // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 10 | #[derive(Clone, Debug, PartialEq, Eq)] 11 | pub struct ExtensionSupportedSignatureAlgorithms { 12 | pub(crate) signature_hash_algorithms: Vec, 13 | } 14 | 15 | impl ExtensionSupportedSignatureAlgorithms { 16 | pub fn extension_value(&self) -> ExtensionValue { 17 | ExtensionValue::SupportedSignatureAlgorithms 18 | } 19 | 20 | pub fn size(&self) -> usize { 21 | 2 + 2 + self.signature_hash_algorithms.len() * 2 22 | } 23 | 24 | pub fn marshal(&self, writer: &mut W) -> Result<()> { 25 | writer.write_u16::(2 + 2 * self.signature_hash_algorithms.len() as u16)?; 26 | writer.write_u16::(2 * self.signature_hash_algorithms.len() as u16)?; 27 | for v in &self.signature_hash_algorithms { 28 | writer.write_u8(v.hash as u8)?; 29 | writer.write_u8(v.signature as u8)?; 30 | } 31 | 32 | Ok(writer.flush()?) 33 | } 34 | 35 | pub fn unmarshal(reader: &mut R) -> Result { 36 | let _ = reader.read_u16::()?; 37 | 38 | let algorithm_count = reader.read_u16::()? as usize / 2; 39 | let mut signature_hash_algorithms = vec![]; 40 | for _ in 0..algorithm_count { 41 | let hash = reader.read_u8()?.into(); 42 | let signature = reader.read_u8()?.into(); 43 | signature_hash_algorithms.push(SignatureHashAlgorithm { hash, signature }); 44 | } 45 | 46 | Ok(ExtensionSupportedSignatureAlgorithms { 47 | signature_hash_algorithms, 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /rtc-dtls/src/extension/extension_use_extended_master_secret.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod extension_use_extended_master_secret_test; 3 | 4 | use super::*; 5 | 6 | const EXTENSION_USE_EXTENDED_MASTER_SECRET_HEADER_SIZE: usize = 4; 7 | 8 | // https://tools.ietf.org/html/rfc8422 9 | #[derive(Clone, Debug, PartialEq, Eq)] 10 | pub struct ExtensionUseExtendedMasterSecret { 11 | pub(crate) supported: bool, 12 | } 13 | 14 | impl ExtensionUseExtendedMasterSecret { 15 | pub fn extension_value(&self) -> ExtensionValue { 16 | ExtensionValue::UseExtendedMasterSecret 17 | } 18 | 19 | pub fn size(&self) -> usize { 20 | 2 21 | } 22 | 23 | pub fn marshal(&self, writer: &mut W) -> Result<()> { 24 | // length 25 | writer.write_u16::(0)?; 26 | 27 | Ok(writer.flush()?) 28 | } 29 | 30 | pub fn unmarshal(reader: &mut R) -> Result { 31 | let _ = reader.read_u16::()?; 32 | 33 | Ok(ExtensionUseExtendedMasterSecret { supported: true }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /rtc-dtls/src/extension/extension_use_extended_master_secret/extension_use_extended_master_secret_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use std::io::{BufReader, BufWriter}; 4 | 5 | #[test] 6 | fn test_extension_use_extended_master_secret() -> Result<()> { 7 | let raw_extension_use_extended_master_secret = vec![0x00, 0x00]; 8 | let parsed_extension_use_extended_master_secret = 9 | ExtensionUseExtendedMasterSecret { supported: true }; 10 | 11 | let mut raw = vec![]; 12 | { 13 | let mut writer = BufWriter::<&mut Vec>::new(raw.as_mut()); 14 | parsed_extension_use_extended_master_secret.marshal(&mut writer)?; 15 | } 16 | 17 | assert_eq!( 18 | raw, raw_extension_use_extended_master_secret, 19 | "extension_use_extended_master_secret marshal: got {raw:?}, want {raw_extension_use_extended_master_secret:?}" 20 | ); 21 | 22 | let mut reader = BufReader::new(raw.as_slice()); 23 | let new_extension_use_extended_master_secret = 24 | ExtensionUseExtendedMasterSecret::unmarshal(&mut reader)?; 25 | 26 | assert_eq!( 27 | new_extension_use_extended_master_secret, parsed_extension_use_extended_master_secret, 28 | "extension_use_extended_master_secret unmarshal: got {new_extension_use_extended_master_secret:?}, want {parsed_extension_use_extended_master_secret:?}" 29 | ); 30 | 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /rtc-dtls/src/extension/extension_use_srtp/extension_use_srtp_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use std::io::{BufReader, BufWriter}; 4 | 5 | #[test] 6 | fn test_extension_use_srtp() -> Result<()> { 7 | let raw_use_srtp = vec![0x00, 0x05, 0x00, 0x02, 0x00, 0x01, 0x00]; //0x00, 0x0e, 8 | let parsed_use_srtp = ExtensionUseSrtp { 9 | protection_profiles: vec![SrtpProtectionProfile::Srtp_Aes128_Cm_Hmac_Sha1_80], 10 | }; 11 | 12 | let mut raw = vec![]; 13 | { 14 | let mut writer = BufWriter::<&mut Vec>::new(raw.as_mut()); 15 | parsed_use_srtp.marshal(&mut writer)?; 16 | } 17 | 18 | assert_eq!( 19 | raw, raw_use_srtp, 20 | "extensionUseSRTP marshal: got {raw:?}, want {raw_use_srtp:?}" 21 | ); 22 | 23 | let mut reader = BufReader::new(raw.as_slice()); 24 | let new_use_srtp = ExtensionUseSrtp::unmarshal(&mut reader)?; 25 | 26 | assert_eq!( 27 | new_use_srtp, parsed_use_srtp, 28 | "extensionUseSRTP unmarshal: got {new_use_srtp:?}, want {parsed_use_srtp:?}" 29 | ); 30 | 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /rtc-dtls/src/extension/renegotiation_info.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod renegotiation_info_test; 3 | 4 | use super::*; 5 | use shared::error::Error; 6 | 7 | const RENEGOTIATION_INFO_HEADER_SIZE: usize = 5; 8 | 9 | /// RenegotiationInfo allows a Client/Server to 10 | /// communicate their renegotiation support 11 | /// https://tools.ietf.org/html/rfc5746 12 | #[derive(Clone, Debug, PartialEq, Eq)] 13 | pub struct ExtensionRenegotiationInfo { 14 | pub(crate) renegotiated_connection: u8, 15 | } 16 | 17 | impl ExtensionRenegotiationInfo { 18 | // TypeValue returns the extension TypeValue 19 | pub fn extension_value(&self) -> ExtensionValue { 20 | ExtensionValue::RenegotiationInfo 21 | } 22 | 23 | pub fn size(&self) -> usize { 24 | 3 25 | } 26 | 27 | /// marshal encodes the extension 28 | pub fn marshal(&self, writer: &mut W) -> Result<()> { 29 | writer.write_u16::(1)?; //length 30 | writer.write_u8(self.renegotiated_connection)?; 31 | 32 | Ok(writer.flush()?) 33 | } 34 | 35 | /// Unmarshal populates the extension from encoded data 36 | pub fn unmarshal(reader: &mut R) -> Result { 37 | let l = reader.read_u16::()?; //length 38 | if l != 1 { 39 | return Err(Error::ErrInvalidPacketLength); 40 | } 41 | 42 | let renegotiated_connection = reader.read_u8()?; 43 | 44 | Ok(ExtensionRenegotiationInfo { 45 | renegotiated_connection, 46 | }) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /rtc-dtls/src/extension/renegotiation_info/renegotiation_info_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use std::io::{BufReader, BufWriter}; 4 | 5 | #[test] 6 | fn test_renegotiation_info() -> Result<()> { 7 | let extension = ExtensionRenegotiationInfo { 8 | renegotiated_connection: 0, 9 | }; 10 | 11 | let mut raw = vec![]; 12 | { 13 | let mut writer = BufWriter::<&mut Vec>::new(raw.as_mut()); 14 | extension.marshal(&mut writer)?; 15 | } 16 | 17 | let mut reader = BufReader::new(raw.as_slice()); 18 | let new_extension = ExtensionRenegotiationInfo::unmarshal(&mut reader)?; 19 | 20 | assert_eq!( 21 | new_extension.renegotiated_connection, 22 | extension.renegotiated_connection 23 | ); 24 | 25 | Ok(()) 26 | } 27 | -------------------------------------------------------------------------------- /rtc-dtls/src/handshake/handshake_header.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 4 | use std::io::{Read, Write}; 5 | 6 | // msg_len for Handshake messages assumes an extra 12 bytes for 7 | // sequence, Fragment and version information 8 | pub(crate) const HANDSHAKE_HEADER_LENGTH: usize = 12; 9 | 10 | #[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] 11 | pub struct HandshakeHeader { 12 | pub(crate) handshake_type: HandshakeType, 13 | pub(crate) length: u32, // uint24 in spec 14 | pub(crate) message_sequence: u16, 15 | pub(crate) fragment_offset: u32, // uint24 in spec 16 | pub(crate) fragment_length: u32, // uint24 in spec 17 | } 18 | 19 | impl HandshakeHeader { 20 | pub fn size(&self) -> usize { 21 | 1 + 3 + 2 + 3 + 3 22 | } 23 | 24 | pub fn marshal(&self, writer: &mut W) -> Result<()> { 25 | writer.write_u8(self.handshake_type as u8)?; 26 | writer.write_u24::(self.length)?; 27 | writer.write_u16::(self.message_sequence)?; 28 | writer.write_u24::(self.fragment_offset)?; 29 | writer.write_u24::(self.fragment_length)?; 30 | 31 | Ok(writer.flush()?) 32 | } 33 | 34 | pub fn unmarshal(reader: &mut R) -> Result { 35 | let handshake_type = reader.read_u8()?.into(); 36 | let length = reader.read_u24::()?; 37 | let message_sequence = reader.read_u16::()?; 38 | let fragment_offset = reader.read_u24::()?; 39 | let fragment_length = reader.read_u24::()?; 40 | 41 | Ok(HandshakeHeader { 42 | handshake_type, 43 | length, 44 | message_sequence, 45 | fragment_offset, 46 | fragment_length, 47 | }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /rtc-dtls/src/handshake/handshake_message_certificate_verify.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod handshake_message_certificate_verify_test; 3 | 4 | use super::*; 5 | use crate::signature_hash_algorithm::*; 6 | 7 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 8 | use std::io::{Read, Write}; 9 | 10 | #[derive(Clone, Debug, PartialEq, Eq)] 11 | pub struct HandshakeMessageCertificateVerify { 12 | pub(crate) algorithm: SignatureHashAlgorithm, 13 | pub(crate) signature: Vec, 14 | } 15 | 16 | const HANDSHAKE_MESSAGE_CERTIFICATE_VERIFY_MIN_LENGTH: usize = 4; 17 | 18 | impl HandshakeMessageCertificateVerify { 19 | pub fn handshake_type(&self) -> HandshakeType { 20 | HandshakeType::CertificateVerify 21 | } 22 | 23 | pub fn size(&self) -> usize { 24 | 1 + 1 + 2 + self.signature.len() 25 | } 26 | 27 | pub fn marshal(&self, writer: &mut W) -> Result<()> { 28 | writer.write_u8(self.algorithm.hash as u8)?; 29 | writer.write_u8(self.algorithm.signature as u8)?; 30 | writer.write_u16::(self.signature.len() as u16)?; 31 | writer.write_all(&self.signature)?; 32 | 33 | Ok(writer.flush()?) 34 | } 35 | 36 | pub fn unmarshal(reader: &mut R) -> Result { 37 | let hash_algorithm = reader.read_u8()?.into(); 38 | let signature_algorithm = reader.read_u8()?.into(); 39 | let signature_length = reader.read_u16::()? as usize; 40 | let mut signature = vec![0; signature_length]; 41 | reader.read_exact(&mut signature)?; 42 | 43 | Ok(HandshakeMessageCertificateVerify { 44 | algorithm: SignatureHashAlgorithm { 45 | hash: hash_algorithm, 46 | signature: signature_algorithm, 47 | }, 48 | signature, 49 | }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /rtc-dtls/src/handshake/handshake_message_certificate_verify/handshake_message_certificate_verify_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use std::io::{BufReader, BufWriter}; 4 | 5 | #[test] 6 | fn test_handshake_message_certificate_request() -> Result<()> { 7 | let raw_certificate_verify = vec![ 8 | 0x04, 0x03, 0x00, 0x47, 0x30, 0x45, 0x02, 0x20, 0x6b, 0x63, 0x17, 0xad, 0xbe, 0xb7, 0x7b, 9 | 0x0f, 0x86, 0x73, 0x39, 0x1e, 0xba, 0xb3, 0x50, 0x9c, 0xce, 0x9c, 0xe4, 0x8b, 0xe5, 0x13, 10 | 0x07, 0x59, 0x18, 0x1f, 0xe5, 0xa0, 0x2b, 0xca, 0xa6, 0xad, 0x02, 0x21, 0x00, 0xd3, 0xb5, 11 | 0x01, 0xbe, 0x87, 0x6c, 0x04, 0xa1, 0xdc, 0x28, 0xaa, 0x5f, 0xf7, 0x1e, 0x9c, 0xc0, 0x1e, 12 | 0x00, 0x2c, 0xe5, 0x94, 0xbb, 0x03, 0x0e, 0xf1, 0xcb, 0x28, 0x22, 0x33, 0x23, 0x88, 0xad, 13 | ]; 14 | let parsed_certificate_verify = HandshakeMessageCertificateVerify { 15 | algorithm: SignatureHashAlgorithm { 16 | hash: raw_certificate_verify[0].into(), 17 | signature: raw_certificate_verify[1].into(), 18 | }, 19 | signature: raw_certificate_verify[4..].to_vec(), 20 | }; 21 | 22 | let mut reader = BufReader::new(raw_certificate_verify.as_slice()); 23 | let c = HandshakeMessageCertificateVerify::unmarshal(&mut reader)?; 24 | assert_eq!( 25 | c, parsed_certificate_verify, 26 | "handshakeMessageCertificate unmarshal: got {c:?}, want {parsed_certificate_verify:?}" 27 | ); 28 | 29 | let mut raw = vec![]; 30 | { 31 | let mut writer = BufWriter::<&mut Vec>::new(raw.as_mut()); 32 | c.marshal(&mut writer)?; 33 | } 34 | assert_eq!( 35 | raw, raw_certificate_verify, 36 | "handshakeMessageCertificateVerify marshal: got {raw:?}, want {raw_certificate_verify:?}" 37 | ); 38 | 39 | Ok(()) 40 | } 41 | -------------------------------------------------------------------------------- /rtc-dtls/src/handshake/handshake_message_client_key_exchange/handshake_message_client_key_exchange_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use std::io::{BufReader, BufWriter}; 4 | 5 | #[test] 6 | fn test_handshake_message_client_key_exchange() -> Result<()> { 7 | let raw_client_key_exchange = vec![ 8 | 0x20, 0x26, 0x78, 0x4a, 0x78, 0x70, 0xc1, 0xf9, 0x71, 0xea, 0x50, 0x4a, 0xb5, 0xbb, 0x00, 9 | 0x76, 0x02, 0x05, 0xda, 0xf7, 0xd0, 0x3f, 0xe3, 0xf7, 0x4e, 0x8a, 0x14, 0x6f, 0xb7, 0xe0, 10 | 0xc0, 0xff, 0x54, 11 | ]; 12 | let parsed_client_key_exchange = HandshakeMessageClientKeyExchange { 13 | identity_hint: vec![], 14 | public_key: raw_client_key_exchange[1..].to_vec(), 15 | }; 16 | 17 | let mut reader = BufReader::new(raw_client_key_exchange.as_slice()); 18 | let c = HandshakeMessageClientKeyExchange::unmarshal(&mut reader)?; 19 | assert_eq!( 20 | c, parsed_client_key_exchange, 21 | "parsedCertificateRequest unmarshal: got {c:?}, want {parsed_client_key_exchange:?}" 22 | ); 23 | 24 | let mut raw = vec![]; 25 | { 26 | let mut writer = BufWriter::<&mut Vec>::new(raw.as_mut()); 27 | c.marshal(&mut writer)?; 28 | } 29 | assert_eq!( 30 | raw, raw_client_key_exchange, 31 | "handshakeMessageClientKeyExchange marshal: got {raw:?}, want {raw_client_key_exchange:?}" 32 | ); 33 | 34 | Ok(()) 35 | } 36 | -------------------------------------------------------------------------------- /rtc-dtls/src/handshake/handshake_message_finished.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod handshake_message_finished_test; 3 | 4 | use super::*; 5 | 6 | use std::io::{Read, Write}; 7 | 8 | #[derive(Clone, Debug, PartialEq, Eq)] 9 | pub struct HandshakeMessageFinished { 10 | pub(crate) verify_data: Vec, 11 | } 12 | 13 | impl HandshakeMessageFinished { 14 | pub fn handshake_type(&self) -> HandshakeType { 15 | HandshakeType::Finished 16 | } 17 | 18 | pub fn size(&self) -> usize { 19 | self.verify_data.len() 20 | } 21 | 22 | pub fn marshal(&self, writer: &mut W) -> Result<()> { 23 | writer.write_all(&self.verify_data)?; 24 | 25 | Ok(writer.flush()?) 26 | } 27 | 28 | pub fn unmarshal(reader: &mut R) -> Result { 29 | let mut verify_data: Vec = vec![]; 30 | reader.read_to_end(&mut verify_data)?; 31 | 32 | Ok(HandshakeMessageFinished { verify_data }) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rtc-dtls/src/handshake/handshake_message_finished/handshake_message_finished_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use std::io::{BufReader, BufWriter}; 4 | 5 | #[test] 6 | fn test_handshake_message_finished() -> Result<()> { 7 | let raw_finished = vec![ 8 | 0x01, 0x01, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 9 | ]; 10 | let parsed_finished = HandshakeMessageFinished { 11 | verify_data: raw_finished.clone(), 12 | }; 13 | 14 | let mut reader = BufReader::new(raw_finished.as_slice()); 15 | let c = HandshakeMessageFinished::unmarshal(&mut reader)?; 16 | assert_eq!( 17 | c, parsed_finished, 18 | "handshakeMessageFinished unmarshal: got {c:?}, want {parsed_finished:?}" 19 | ); 20 | 21 | let mut raw = vec![]; 22 | { 23 | let mut writer = BufWriter::<&mut Vec>::new(raw.as_mut()); 24 | c.marshal(&mut writer)?; 25 | } 26 | assert_eq!( 27 | raw, raw_finished, 28 | "handshakeMessageFinished marshal: got {raw:?}, want {raw_finished:?}" 29 | ); 30 | 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /rtc-dtls/src/handshake/handshake_message_hello_verify_request/handshake_message_hello_verify_request_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use std::io::{BufReader, BufWriter}; 4 | 5 | #[test] 6 | fn test_handshake_message_hello_verify_request() -> Result<()> { 7 | let raw_hello_verify_request = vec![ 8 | 0xfe, 0xff, 0x14, 0x25, 0xfb, 0xee, 0xb3, 0x7c, 0x95, 0xcf, 0x00, 0xeb, 0xad, 0xe2, 0xef, 9 | 0xc7, 0xfd, 0xbb, 0xed, 0xf7, 0x1f, 0x6c, 0xcd, 10 | ]; 11 | let parsed_hello_verify_request = HandshakeMessageHelloVerifyRequest { 12 | version: ProtocolVersion { 13 | major: 0xFE, 14 | minor: 0xFF, 15 | }, 16 | cookie: vec![ 17 | 0x25, 0xfb, 0xee, 0xb3, 0x7c, 0x95, 0xcf, 0x00, 0xeb, 0xad, 0xe2, 0xef, 0xc7, 0xfd, 18 | 0xbb, 0xed, 0xf7, 0x1f, 0x6c, 0xcd, 19 | ], 20 | }; 21 | 22 | let mut reader = BufReader::new(raw_hello_verify_request.as_slice()); 23 | let c = HandshakeMessageHelloVerifyRequest::unmarshal(&mut reader)?; 24 | assert_eq!( 25 | c, parsed_hello_verify_request, 26 | "parsed_hello_verify_request unmarshal: got {c:?}, want {parsed_hello_verify_request:?}" 27 | ); 28 | 29 | let mut raw = vec![]; 30 | { 31 | let mut writer = BufWriter::<&mut Vec>::new(raw.as_mut()); 32 | c.marshal(&mut writer)?; 33 | } 34 | assert_eq!( 35 | raw, raw_hello_verify_request, 36 | "parsed_hello_verify_request marshal: got {raw:?}, want {raw_hello_verify_request:?}" 37 | ); 38 | 39 | Ok(()) 40 | } 41 | -------------------------------------------------------------------------------- /rtc-dtls/src/handshake/handshake_message_server_hello_done.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod handshake_message_server_hello_done_test; 3 | 4 | use super::*; 5 | 6 | use std::io::{Read, Write}; 7 | 8 | #[derive(Clone, Debug, PartialEq, Eq)] 9 | pub struct HandshakeMessageServerHelloDone; 10 | 11 | impl HandshakeMessageServerHelloDone { 12 | pub fn handshake_type(&self) -> HandshakeType { 13 | HandshakeType::ServerHelloDone 14 | } 15 | 16 | pub fn size(&self) -> usize { 17 | 0 18 | } 19 | 20 | pub fn marshal(&self, _writer: &mut W) -> Result<()> { 21 | Ok(()) 22 | } 23 | 24 | pub fn unmarshal(_reader: &mut R) -> Result { 25 | Ok(HandshakeMessageServerHelloDone {}) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /rtc-dtls/src/handshake/handshake_message_server_hello_done/handshake_message_server_hello_done_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use std::io::{BufReader, BufWriter}; 4 | 5 | #[test] 6 | fn test_handshake_message_server_hello_done() -> Result<()> { 7 | let raw_server_hello_done = vec![]; 8 | let parsed_server_hello_done = HandshakeMessageServerHelloDone {}; 9 | 10 | let mut reader = BufReader::new(raw_server_hello_done.as_slice()); 11 | let c = HandshakeMessageServerHelloDone::unmarshal(&mut reader)?; 12 | assert_eq!( 13 | c, parsed_server_hello_done, 14 | "handshakeMessageServerHelloDone unmarshal: got {c:?}, want {parsed_server_hello_done:?}" 15 | ); 16 | 17 | let mut raw = vec![]; 18 | { 19 | let mut writer = BufWriter::<&mut Vec>::new(raw.as_mut()); 20 | c.marshal(&mut writer)?; 21 | } 22 | assert_eq!( 23 | raw, raw_server_hello_done, 24 | "handshakeMessageServerHelloDone marshal: got {raw:?}, want {raw_server_hello_done:?}" 25 | ); 26 | 27 | Ok(()) 28 | } 29 | -------------------------------------------------------------------------------- /rtc-dtls/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms)] 2 | #![allow(dead_code)] 3 | 4 | pub mod alert; 5 | pub mod application_data; 6 | pub mod change_cipher_spec; 7 | pub mod cipher_suite; 8 | pub mod client_certificate_type; 9 | pub mod compression_methods; 10 | pub mod config; 11 | pub mod conn; 12 | pub mod content; 13 | pub mod crypto; 14 | pub mod curve; 15 | pub mod endpoint; 16 | pub mod extension; 17 | pub mod flight; 18 | pub mod fragment_buffer; 19 | pub mod handshake; 20 | pub mod handshaker; 21 | pub mod prf; 22 | pub mod record_layer; 23 | pub mod signature_hash_algorithm; 24 | pub mod state; 25 | 26 | use cipher_suite::*; 27 | use extension::extension_use_srtp::SrtpProtectionProfile; 28 | 29 | pub(crate) fn find_matching_srtp_profile( 30 | a: &[SrtpProtectionProfile], 31 | b: &[SrtpProtectionProfile], 32 | ) -> Result { 33 | for a_profile in a { 34 | for b_profile in b { 35 | if a_profile == b_profile { 36 | return Ok(*a_profile); 37 | } 38 | } 39 | } 40 | Err(()) 41 | } 42 | 43 | pub(crate) fn find_matching_cipher_suite( 44 | a: &[CipherSuiteId], 45 | b: &[CipherSuiteId], 46 | ) -> Result { 47 | for a_suite in a { 48 | for b_suite in b { 49 | if a_suite == b_suite { 50 | return Ok(*a_suite); 51 | } 52 | } 53 | } 54 | Err(()) 55 | } 56 | -------------------------------------------------------------------------------- /rtc-ice/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /.idea/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | -------------------------------------------------------------------------------- /rtc-ice/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # rtc-ice changelog 2 | 3 | ## Unreleased 4 | -------------------------------------------------------------------------------- /rtc-ice/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtc-ice" 3 | version = "0.2.0" 4 | authors = ["Rain Liu "] 5 | edition = "2021" 6 | description = "RTC ICE in Rust" 7 | license = "MIT/Apache-2.0" 8 | documentation = "https://docs.rs/rtc-ice" 9 | homepage = "https://ortc.rs" 10 | repository = "https://github.com/webrtc-rs/rtc/tree/master/rtc-ice" 11 | 12 | [dependencies] 13 | shared = { version = "0.2.0", path = "../rtc-shared", package = "rtc-shared", default-features = false, features = [] } 14 | stun = { version = "0.2.0", path = "../rtc-stun", package = "rtc-stun" } 15 | 16 | crc = "3.0.1" 17 | log = "0.4.21" 18 | rand = "0.8.5" 19 | serde = { version = "1.0.197", features = ["derive"] } 20 | url = "2.5.0" 21 | bytes = "1.5.0" 22 | 23 | [dev-dependencies] 24 | regex = "1.10.3" 25 | env_logger = "0.11.3" 26 | chrono = "0.4.35" 27 | ipnet = "2.9.0" 28 | clap = { version = "4.5.2", features = ["derive"] } 29 | lazy_static = "1.4.0" 30 | hyper = { version = "0.14.28", features = ["full"] } 31 | sha1 = "0.10.6" 32 | waitgroup = "0.1.2" 33 | serde_json = "1.0.114" 34 | tokio = { version = "1.36", features = ["full"] } 35 | futures = "0.3.30" 36 | ctrlc = "3.4" 37 | 38 | [[example]] 39 | name = "ping_pong" 40 | path = "examples/ping_pong.rs" 41 | bench = false 42 | -------------------------------------------------------------------------------- /rtc-ice/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 WebRTC.rs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rtc-ice/README.md: -------------------------------------------------------------------------------- 1 |

2 | WebRTC.rs 3 |
4 |

5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | License: MIT/Apache 2.0 23 | 24 | 25 | Discord 26 | 27 |

28 |

29 | RTC ICE Protocol in Rust With Sans-IO 30 |

31 | -------------------------------------------------------------------------------- /rtc-ice/codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | max_report_age: off 4 | token: 4bd5cec1-2807-4cd6-8430-d5f3efe32ce0 5 | 6 | coverage: 7 | precision: 2 8 | round: down 9 | range: 50..90 10 | status: 11 | project: 12 | default: 13 | enabled: no 14 | threshold: 0.2 15 | if_not_found: success 16 | patch: 17 | default: 18 | enabled: no 19 | if_not_found: success 20 | changes: 21 | default: 22 | enabled: no 23 | if_not_found: success 24 | -------------------------------------------------------------------------------- /rtc-ice/src/attributes/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod control; 2 | pub mod priority; 3 | pub mod use_candidate; 4 | -------------------------------------------------------------------------------- /rtc-ice/src/attributes/priority/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod priority_test; 3 | 4 | use shared::error::*; 5 | use stun::attributes::ATTR_PRIORITY; 6 | use stun::checks::*; 7 | use stun::message::*; 8 | 9 | /// Represents PRIORITY attribute. 10 | #[derive(Default, PartialEq, Eq, Debug, Copy, Clone)] 11 | pub struct PriorityAttr(pub u32); 12 | 13 | const PRIORITY_SIZE: usize = 4; // 32 bit 14 | 15 | impl Setter for PriorityAttr { 16 | // add_to adds PRIORITY attribute to message. 17 | fn add_to(&self, m: &mut Message) -> Result<()> { 18 | let mut v = vec![0_u8; PRIORITY_SIZE]; 19 | v.copy_from_slice(&self.0.to_be_bytes()); 20 | m.add(ATTR_PRIORITY, &v); 21 | Ok(()) 22 | } 23 | } 24 | 25 | impl PriorityAttr { 26 | /// Decodes PRIORITY attribute from message. 27 | pub fn get_from(&mut self, m: &Message) -> Result<()> { 28 | let v = m.get(ATTR_PRIORITY)?; 29 | 30 | check_size(ATTR_PRIORITY, v.len(), PRIORITY_SIZE)?; 31 | 32 | let p = u32::from_be_bytes([v[0], v[1], v[2], v[3]]); 33 | self.0 = p; 34 | 35 | Ok(()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /rtc-ice/src/attributes/priority/priority_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use shared::error::{Error, Result}; 3 | 4 | #[test] 5 | fn test_priority_get_from() -> Result<()> { 6 | let mut m = Message::new(); 7 | let mut p = PriorityAttr::default(); 8 | let result = p.get_from(&m); 9 | if let Err(err) = result { 10 | assert_eq!(err, Error::ErrAttributeNotFound, "unexpected error"); 11 | } else { 12 | panic!("expected error, but got ok"); 13 | } 14 | 15 | m.build(&[Box::new(BINDING_REQUEST), Box::new(p)])?; 16 | 17 | let mut m1 = Message::new(); 18 | m1.write(&m.raw)?; 19 | 20 | let mut p1 = PriorityAttr::default(); 21 | p1.get_from(&m1)?; 22 | 23 | assert_eq!(p1, p, "not equal"); 24 | 25 | //"IncorrectSize" 26 | { 27 | let mut m3 = Message::new(); 28 | m3.add(ATTR_PRIORITY, &[0; 100]); 29 | let mut p2 = PriorityAttr::default(); 30 | let result = p2.get_from(&m3); 31 | if let Err(err) = result { 32 | assert!(is_attr_size_invalid(&err), "should error"); 33 | } else { 34 | panic!("expected error, but got ok"); 35 | } 36 | } 37 | 38 | Ok(()) 39 | } 40 | -------------------------------------------------------------------------------- /rtc-ice/src/attributes/use_candidate/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod use_candidate_test; 3 | 4 | use shared::error::*; 5 | use stun::attributes::ATTR_USE_CANDIDATE; 6 | use stun::message::*; 7 | 8 | /// Represents USE-CANDIDATE attribute. 9 | #[derive(Default)] 10 | pub struct UseCandidateAttr; 11 | 12 | impl Setter for UseCandidateAttr { 13 | /// Adds USE-CANDIDATE attribute to message. 14 | fn add_to(&self, m: &mut Message) -> Result<()> { 15 | m.add(ATTR_USE_CANDIDATE, &[]); 16 | Ok(()) 17 | } 18 | } 19 | 20 | impl UseCandidateAttr { 21 | #[must_use] 22 | pub const fn new() -> Self { 23 | Self 24 | } 25 | 26 | /// Returns true if USE-CANDIDATE attribute is set. 27 | #[must_use] 28 | pub fn is_set(m: &Message) -> bool { 29 | let result = m.get(ATTR_USE_CANDIDATE); 30 | result.is_ok() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rtc-ice/src/attributes/use_candidate/use_candidate_test.rs: -------------------------------------------------------------------------------- 1 | use stun::message::BINDING_REQUEST; 2 | 3 | use super::*; 4 | use shared::error::Result; 5 | 6 | #[test] 7 | fn test_use_candidate_attr_add_to() -> Result<()> { 8 | let mut m = Message::new(); 9 | assert!(!UseCandidateAttr::is_set(&m), "should not be set"); 10 | 11 | m.build(&[Box::new(BINDING_REQUEST), Box::new(UseCandidateAttr::new())])?; 12 | 13 | let mut m1 = Message::new(); 14 | m1.write(&m.raw)?; 15 | 16 | assert!(UseCandidateAttr::is_set(&m1), "should be set"); 17 | 18 | Ok(()) 19 | } 20 | -------------------------------------------------------------------------------- /rtc-ice/src/candidate/candidate_host.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::rand::generate_cand_id; 3 | 4 | /// The config required to create a new `CandidateHost`. 5 | #[derive(Default)] 6 | pub struct CandidateHostConfig { 7 | pub base_config: CandidateConfig, 8 | 9 | pub tcp_type: TcpType, 10 | } 11 | 12 | impl CandidateHostConfig { 13 | /// Creates a new host candidate. 14 | pub fn new_candidate_host(self) -> Result { 15 | let mut candidate_id = self.base_config.candidate_id; 16 | if candidate_id.is_empty() { 17 | candidate_id = generate_cand_id(); 18 | } 19 | 20 | let ip: IpAddr = match self.base_config.address.parse() { 21 | Ok(ip) => ip, 22 | Err(_) => return Err(Error::ErrAddressParseFailed), 23 | }; 24 | let network_type = determine_network_type(&self.base_config.network, &ip)?; 25 | 26 | Ok(Candidate { 27 | id: candidate_id, 28 | network_type, 29 | candidate_type: CandidateType::Host, 30 | address: self.base_config.address, 31 | port: self.base_config.port, 32 | resolved_addr: SocketAddr::new(ip, self.base_config.port), 33 | component: self.base_config.component, 34 | foundation_override: self.base_config.foundation, 35 | priority_override: self.base_config.priority, 36 | network: self.base_config.network, 37 | tcp_type: self.tcp_type, 38 | ..Candidate::default() 39 | }) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /rtc-ice/src/candidate/candidate_peer_reflexive.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::network_type::determine_network_type; 3 | use crate::rand::generate_cand_id; 4 | use shared::error::*; 5 | 6 | /// The config required to create a new `CandidatePeerReflexive`. 7 | #[derive(Default)] 8 | pub struct CandidatePeerReflexiveConfig { 9 | pub base_config: CandidateConfig, 10 | 11 | pub rel_addr: String, 12 | pub rel_port: u16, 13 | } 14 | 15 | impl CandidatePeerReflexiveConfig { 16 | /// Creates a new peer reflective candidate. 17 | pub fn new_candidate_peer_reflexive(self) -> Result { 18 | let mut candidate_id = self.base_config.candidate_id; 19 | if candidate_id.is_empty() { 20 | candidate_id = generate_cand_id(); 21 | } 22 | 23 | let ip: IpAddr = match self.base_config.address.parse() { 24 | Ok(ip) => ip, 25 | Err(_) => return Err(Error::ErrAddressParseFailed), 26 | }; 27 | let network_type = determine_network_type(&self.base_config.network, &ip)?; 28 | 29 | Ok(Candidate { 30 | id: candidate_id, 31 | network_type, 32 | candidate_type: CandidateType::PeerReflexive, 33 | address: self.base_config.address, 34 | port: self.base_config.port, 35 | resolved_addr: SocketAddr::new(ip, self.base_config.port), 36 | component: self.base_config.component, 37 | foundation_override: self.base_config.foundation, 38 | priority_override: self.base_config.priority, 39 | related_address: Some(CandidateRelatedAddress { 40 | address: self.rel_addr, 41 | port: self.rel_port, 42 | }), 43 | ..Candidate::default() 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /rtc-ice/src/candidate/candidate_relay.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::network_type::determine_network_type; 3 | use crate::rand::generate_cand_id; 4 | use shared::error::*; 5 | 6 | /// The config required to create a new `CandidateRelay`. 7 | #[derive(Default)] 8 | pub struct CandidateRelayConfig { 9 | pub base_config: CandidateConfig, 10 | 11 | pub rel_addr: String, 12 | pub rel_port: u16, 13 | } 14 | 15 | impl CandidateRelayConfig { 16 | /// Creates a new relay candidate. 17 | pub fn new_candidate_relay(self) -> Result { 18 | let mut candidate_id = self.base_config.candidate_id; 19 | if candidate_id.is_empty() { 20 | candidate_id = generate_cand_id(); 21 | } 22 | 23 | let ip: IpAddr = match self.base_config.address.parse() { 24 | Ok(ip) => ip, 25 | Err(_) => return Err(Error::ErrAddressParseFailed), 26 | }; 27 | let network_type = determine_network_type(&self.base_config.network, &ip)?; 28 | 29 | Ok(Candidate { 30 | id: candidate_id, 31 | network_type, 32 | candidate_type: CandidateType::Relay, 33 | address: self.base_config.address, 34 | port: self.base_config.port, 35 | resolved_addr: SocketAddr::new(ip, self.base_config.port), 36 | component: self.base_config.component, 37 | foundation_override: self.base_config.foundation, 38 | priority_override: self.base_config.priority, 39 | related_address: Some(CandidateRelatedAddress { 40 | address: self.rel_addr, 41 | port: self.rel_port, 42 | }), 43 | ..Candidate::default() 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /rtc-ice/src/candidate/candidate_server_reflexive.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::network_type::determine_network_type; 3 | use crate::rand::generate_cand_id; 4 | use shared::error::*; 5 | 6 | /// The config required to create a new `CandidateServerReflexive`. 7 | #[derive(Default)] 8 | pub struct CandidateServerReflexiveConfig { 9 | pub base_config: CandidateConfig, 10 | 11 | pub rel_addr: String, 12 | pub rel_port: u16, 13 | } 14 | 15 | impl CandidateServerReflexiveConfig { 16 | /// Creates a new server reflective candidate. 17 | pub fn new_candidate_server_reflexive(self) -> Result { 18 | let mut candidate_id = self.base_config.candidate_id; 19 | if candidate_id.is_empty() { 20 | candidate_id = generate_cand_id(); 21 | } 22 | 23 | let ip: IpAddr = match self.base_config.address.parse() { 24 | Ok(ip) => ip, 25 | Err(_) => return Err(Error::ErrAddressParseFailed), 26 | }; 27 | let network_type = determine_network_type(&self.base_config.network, &ip)?; 28 | 29 | Ok(Candidate { 30 | id: candidate_id, 31 | network_type, 32 | candidate_type: CandidateType::ServerReflexive, 33 | address: self.base_config.address, 34 | port: self.base_config.port, 35 | resolved_addr: SocketAddr::new(ip, self.base_config.port), 36 | component: self.base_config.component, 37 | foundation_override: self.base_config.foundation, 38 | priority_override: self.base_config.priority, 39 | related_address: Some(CandidateRelatedAddress { 40 | address: self.rel_addr, 41 | port: self.rel_port, 42 | }), 43 | ..Candidate::default() 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /rtc-ice/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms)] 2 | #![allow(dead_code)] 3 | 4 | pub mod agent; 5 | pub mod attributes; 6 | pub mod candidate; 7 | pub mod network_type; 8 | pub mod rand; 9 | pub mod state; 10 | pub mod stats; 11 | pub mod tcp_type; 12 | pub mod url; 13 | 14 | pub use agent::{ 15 | agent_config::AgentConfig, 16 | agent_stats::{CandidatePairStats, CandidateStats}, 17 | Agent, Credentials, Event, 18 | }; 19 | -------------------------------------------------------------------------------- /rtc-ice/src/rand/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod rand_test; 3 | 4 | use rand::{thread_rng, Rng}; 5 | 6 | const RUNES_ALPHA: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 7 | const RUNES_CANDIDATE_ID_FOUNDATION: &[u8] = 8 | b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/+"; 9 | 10 | const LEN_UFRAG: usize = 16; 11 | const LEN_PWD: usize = 32; 12 | 13 | //TODO: generates a random string for cryptographic usage. 14 | pub fn generate_crypto_random_string(n: usize, runes: &[u8]) -> String { 15 | let mut rng = thread_rng(); 16 | 17 | let rand_string: String = (0..n) 18 | .map(|_| { 19 | let idx = rng.gen_range(0..runes.len()); 20 | runes[idx] as char 21 | }) 22 | .collect(); 23 | 24 | rand_string 25 | } 26 | 27 | /// https://tools.ietf.org/html/rfc5245#section-15.1 28 | /// candidate-id = "candidate" ":" foundation 29 | /// foundation = 1*32ice-char 30 | /// ice-char = ALPHA / DIGIT / "+" / "/" 31 | pub fn generate_cand_id() -> String { 32 | format!( 33 | "candidate:{}", 34 | generate_crypto_random_string(32, RUNES_CANDIDATE_ID_FOUNDATION) 35 | ) 36 | } 37 | 38 | /// Generates ICE pwd. 39 | /// This internally uses `generate_crypto_random_string`. 40 | pub fn generate_pwd() -> String { 41 | generate_crypto_random_string(LEN_PWD, RUNES_ALPHA) 42 | } 43 | 44 | /// ICE user fragment. 45 | /// This internally uses `generate_crypto_random_string`. 46 | pub fn generate_ufrag() -> String { 47 | generate_crypto_random_string(LEN_UFRAG, RUNES_ALPHA) 48 | } 49 | -------------------------------------------------------------------------------- /rtc-ice/src/rand/rand_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use shared::error::Result; 3 | 4 | #[test] 5 | fn test_random_generator_collision() -> Result<()> { 6 | let test_cases = vec![ 7 | ( 8 | "CandidateID", 9 | 0, /*||-> String { 10 | generate_cand_id() 11 | },*/ 12 | ), 13 | ( 14 | "PWD", 1, /*||-> String { 15 | generate_pwd() 16 | },*/ 17 | ), 18 | ( 19 | "Ufrag", 2, /*|| ->String { 20 | generate_ufrag() 21 | },*/ 22 | ), 23 | ]; 24 | 25 | const N: usize = 10; 26 | const ITERATION: usize = 10; 27 | 28 | for (name, test_case) in test_cases { 29 | for _ in 0..ITERATION { 30 | let mut rs = vec![]; 31 | 32 | for _ in 0..N { 33 | let s = if test_case == 0 { 34 | generate_cand_id() 35 | } else if test_case == 1 { 36 | generate_pwd() 37 | } else { 38 | generate_ufrag() 39 | }; 40 | 41 | rs.push(s); 42 | } 43 | 44 | assert_eq!(rs.len(), N, "{name} Failed to generate randoms"); 45 | 46 | for i in 0..N { 47 | for j in i + 1..N { 48 | assert_ne!( 49 | rs[i], rs[j], 50 | "{}: generateRandString caused collision: {} == {}", 51 | name, rs[i], rs[j], 52 | ); 53 | } 54 | } 55 | } 56 | } 57 | 58 | Ok(()) 59 | } 60 | -------------------------------------------------------------------------------- /rtc-ice/src/state/state_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use shared::error::Result; 3 | 4 | #[test] 5 | fn test_connected_state_string() -> Result<()> { 6 | let tests = vec![ 7 | (ConnectionState::Unspecified, "Unspecified"), 8 | (ConnectionState::New, "New"), 9 | (ConnectionState::Checking, "Checking"), 10 | (ConnectionState::Connected, "Connected"), 11 | (ConnectionState::Completed, "Completed"), 12 | (ConnectionState::Failed, "Failed"), 13 | (ConnectionState::Disconnected, "Disconnected"), 14 | (ConnectionState::Closed, "Closed"), 15 | ]; 16 | 17 | for (connection_state, expected_string) in tests { 18 | assert_eq!( 19 | connection_state.to_string(), 20 | expected_string, 21 | "testCase: {expected_string} vs {connection_state}", 22 | ) 23 | } 24 | 25 | Ok(()) 26 | } 27 | 28 | #[test] 29 | fn test_gathering_state_string() -> Result<()> { 30 | let tests = vec![ 31 | (GatheringState::Unspecified, "unspecified"), 32 | (GatheringState::New, "new"), 33 | (GatheringState::Gathering, "gathering"), 34 | (GatheringState::Complete, "complete"), 35 | ]; 36 | 37 | for (gathering_state, expected_string) in tests { 38 | assert_eq!( 39 | gathering_state.to_string(), 40 | expected_string, 41 | "testCase: {expected_string} vs {gathering_state}", 42 | ) 43 | } 44 | 45 | Ok(()) 46 | } 47 | -------------------------------------------------------------------------------- /rtc-ice/src/tcp_type/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tcp_type_test; 3 | 4 | use std::fmt; 5 | 6 | // TCPType is the type of ICE TCP candidate as described in 7 | // ttps://tools.ietf.org/html/rfc6544#section-4.5 8 | #[derive(PartialEq, Eq, Debug, Copy, Clone)] 9 | pub enum TcpType { 10 | /// The default value. For example UDP candidates do not need this field. 11 | Unspecified, 12 | /// Active TCP candidate, which initiates TCP connections. 13 | Active, 14 | /// Passive TCP candidate, only accepts TCP connections. 15 | Passive, 16 | /// Like `Active` and `Passive` at the same time. 17 | SimultaneousOpen, 18 | } 19 | 20 | // from creates a new TCPType from string. 21 | impl From<&str> for TcpType { 22 | fn from(raw: &str) -> Self { 23 | match raw { 24 | "active" => Self::Active, 25 | "passive" => Self::Passive, 26 | "so" => Self::SimultaneousOpen, 27 | _ => Self::Unspecified, 28 | } 29 | } 30 | } 31 | 32 | impl fmt::Display for TcpType { 33 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 34 | let s = match *self { 35 | Self::Active => "active", 36 | Self::Passive => "passive", 37 | Self::SimultaneousOpen => "so", 38 | Self::Unspecified => "unspecified", 39 | }; 40 | write!(f, "{s}") 41 | } 42 | } 43 | 44 | impl Default for TcpType { 45 | fn default() -> Self { 46 | Self::Unspecified 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /rtc-ice/src/tcp_type/tcp_type_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use shared::error::Result; 3 | 4 | #[test] 5 | fn test_tcp_type() -> Result<()> { 6 | //assert_eq!(tcpType, TCPType::Unspecified) 7 | assert_eq!(TcpType::from("active"), TcpType::Active); 8 | assert_eq!(TcpType::from("passive"), TcpType::Passive); 9 | assert_eq!(TcpType::from("so"), TcpType::SimultaneousOpen); 10 | assert_eq!(TcpType::from("something else"), TcpType::Unspecified); 11 | 12 | assert_eq!(TcpType::Unspecified.to_string(), "unspecified"); 13 | assert_eq!(TcpType::Active.to_string(), "active"); 14 | assert_eq!(TcpType::Passive.to_string(), "passive"); 15 | assert_eq!(TcpType::SimultaneousOpen.to_string(), "so"); 16 | 17 | Ok(()) 18 | } 19 | -------------------------------------------------------------------------------- /rtc-rtcp/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /.idea/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | -------------------------------------------------------------------------------- /rtc-rtcp/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # rtc-rtcp changelog 2 | 3 | ## Unreleased 4 | -------------------------------------------------------------------------------- /rtc-rtcp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtc-rtcp" 3 | version = "0.2.0" 4 | authors = ["Rain Liu "] 5 | edition = "2021" 6 | description = "RTC RTCP in Rust" 7 | license = "MIT/Apache-2.0" 8 | documentation = "https://docs.rs/rtc-rtcp" 9 | homepage = "https://ortc.rs" 10 | repository = "https://github.com/webrtc-rs/rtc/tree/master/rtc-rtcp" 11 | 12 | [dependencies] 13 | shared = { version = "0.2.0", path = "../rtc-shared", package = "rtc-shared", default-features = false, features = ["marshal"] } 14 | 15 | bytes = "1.5.0" 16 | -------------------------------------------------------------------------------- /rtc-rtcp/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 WebRTC.rs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rtc-rtcp/README.md: -------------------------------------------------------------------------------- 1 |

2 | WebRTC.rs 3 |
4 |

5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | License: MIT/Apache 2.0 23 | 24 | 25 | Discord 26 | 27 |

28 |

29 | RTC RTCP Protocol in Rust With Sans-IO 30 |

31 | -------------------------------------------------------------------------------- /rtc-rtcp/codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | max_report_age: off 4 | token: 2971c79d-6f37-4e06-924b-e2325e3c8a06 5 | 6 | coverage: 7 | precision: 2 8 | round: down 9 | range: 50..90 10 | status: 11 | project: 12 | default: 13 | enabled: no 14 | threshold: 0.2 15 | if_not_found: success 16 | patch: 17 | default: 18 | enabled: no 19 | if_not_found: success 20 | changes: 21 | default: 22 | enabled: no 23 | if_not_found: success 24 | -------------------------------------------------------------------------------- /rtc-rtcp/src/payload_feedbacks/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod full_intra_request; 2 | pub mod picture_loss_indication; 3 | pub mod receiver_estimated_maximum_bitrate; 4 | pub mod slice_loss_indication; 5 | -------------------------------------------------------------------------------- /rtc-rtcp/src/transport_feedbacks/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod rapid_resynchronization_request; 2 | pub mod transport_layer_cc; 3 | pub mod transport_layer_nack; 4 | -------------------------------------------------------------------------------- /rtc-rtp/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /.idea/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | -------------------------------------------------------------------------------- /rtc-rtp/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # rtc-rtp changelog 2 | 3 | ## Unreleased 4 | -------------------------------------------------------------------------------- /rtc-rtp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtc-rtp" 3 | version = "0.2.0" 4 | authors = ["Rain Liu "] 5 | edition = "2021" 6 | description = "RTC RTP in Rust" 7 | license = "MIT/Apache-2.0" 8 | documentation = "https://docs.rs/rtc-rtp" 9 | homepage = "https://ortc.rs" 10 | repository = "https://github.com/webrtc-rs/rtc/tree/master/rtc-rtp" 11 | 12 | [dependencies] 13 | shared = { version = "0.2.0", path = "../rtc-shared", package = "rtc-shared", default-features = false, features = ["marshal"] } 14 | 15 | bytes = "1.5.0" 16 | rand = "0.8.5" 17 | serde = { version = "1.0.197", features = ["derive"] } 18 | 19 | [dev-dependencies] 20 | chrono = "0.4.35" 21 | criterion = "0.5.1" 22 | 23 | [[bench]] 24 | name = "packet_bench" 25 | harness = false 26 | -------------------------------------------------------------------------------- /rtc-rtp/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 WebRTC.rs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rtc-rtp/README.md: -------------------------------------------------------------------------------- 1 |

2 | WebRTC.rs 3 |
4 |

5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | License: MIT/Apache 2.0 23 | 24 | 25 | Discord 26 | 27 |

28 |

29 | RTC RTP Protocol in Rust With Sans-IO 30 |

31 | -------------------------------------------------------------------------------- /rtc-rtp/codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | max_report_age: off 4 | token: cd48e18f-3916-4a20-ba56-81354d68a5d2 5 | 6 | coverage: 7 | precision: 2 8 | round: down 9 | range: 50..90 10 | status: 11 | project: 12 | default: 13 | enabled: no 14 | threshold: 0.2 15 | if_not_found: success 16 | patch: 17 | default: 18 | enabled: no 19 | if_not_found: success 20 | changes: 21 | default: 22 | enabled: no 23 | if_not_found: success 24 | -------------------------------------------------------------------------------- /rtc-rtp/src/codecs/av1/leb128.rs: -------------------------------------------------------------------------------- 1 | use bytes::{BufMut, Bytes, BytesMut}; 2 | 3 | pub fn encode_leb128(mut val: u32) -> u32 { 4 | let mut b = 0; 5 | loop { 6 | b |= val & 0b_0111_1111; 7 | val >>= 7; 8 | if val != 0 { 9 | b |= 0b_1000_0000; 10 | b <<= 8; 11 | } else { 12 | return b; 13 | } 14 | } 15 | } 16 | 17 | pub fn decode_leb128(mut val: u32) -> u32 { 18 | let mut b = 0; 19 | loop { 20 | b |= val & 0b_0111_1111; 21 | val >>= 8; 22 | if val == 0 { 23 | return b; 24 | } 25 | b <<= 7; 26 | } 27 | } 28 | 29 | pub fn read_leb128(bytes: &Bytes) -> (u32, usize) { 30 | let mut encoded = 0; 31 | for i in 0..bytes.len() { 32 | encoded |= bytes[i] as u32; 33 | if bytes[i] & 0b_1000_0000 == 0 { 34 | return (decode_leb128(encoded), i + 1); 35 | } 36 | encoded <<= 8; 37 | } 38 | (0, 0) 39 | } 40 | 41 | pub fn leb128_size(value: u32) -> usize { 42 | let mut size = 0; 43 | let mut value = value; 44 | while value >= 0b_1000_0000 { 45 | size += 1; 46 | value >>= 7; 47 | } 48 | size + 1 49 | } 50 | 51 | pub trait BytesMutExt { 52 | fn put_leb128(&mut self, n: u32); 53 | } 54 | 55 | impl BytesMutExt for BytesMut { 56 | fn put_leb128(&mut self, n: u32) { 57 | let mut encoded = encode_leb128(n); 58 | while encoded >= 0b_1000_0000 { 59 | self.put_u8(0b_1000_0000 | (encoded & 0b_0111_1111) as u8); 60 | encoded >>= 7; 61 | } 62 | self.put_u8(encoded as u8); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /rtc-rtp/src/codecs/g7xx/g7xx_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn test_g7xx_payload() -> Result<()> { 5 | let mut pck = G711Payloader::default(); 6 | 7 | const TEST_LEN: usize = 10000; 8 | const TEST_MTU: usize = 1500; 9 | 10 | //generate random 8-bit g722 samples 11 | let samples: Vec = (0..TEST_LEN).map(|_| rand::random::()).collect(); 12 | 13 | //make a copy, for payloader input 14 | let mut samples_in = vec![0u8; TEST_LEN]; 15 | samples_in.clone_from_slice(&samples); 16 | let samples_in = Bytes::copy_from_slice(&samples_in); 17 | 18 | //split our samples into payloads 19 | let payloads = pck.payload(TEST_MTU, &samples_in)?; 20 | 21 | let outcnt = ((TEST_LEN as f64) / (TEST_MTU as f64)).ceil() as usize; 22 | assert_eq!( 23 | outcnt, 24 | payloads.len(), 25 | "Generated {} payloads instead of {}", 26 | payloads.len(), 27 | outcnt 28 | ); 29 | assert_eq!(&samples, &samples_in, "Modified input samples"); 30 | 31 | let samples_out = payloads.concat(); 32 | assert_eq!(&samples_out, &samples_in, "Output samples don't match"); 33 | 34 | let empty = Bytes::from_static(&[]); 35 | let payload = Bytes::from_static(&[0x90, 0x90, 0x90]); 36 | 37 | // Positive MTU, empty payload 38 | let result = pck.payload(1, &empty)?; 39 | assert!(result.is_empty(), "Generated payload should be empty"); 40 | 41 | // 0 MTU, small payload 42 | let result = pck.payload(0, &payload)?; 43 | assert_eq!(result.len(), 0, "Generated payload should be empty"); 44 | 45 | // Positive MTU, small payload 46 | let result = pck.payload(10, &payload)?; 47 | assert_eq!(result.len(), 1, "Generated payload should be the 1"); 48 | 49 | Ok(()) 50 | } 51 | -------------------------------------------------------------------------------- /rtc-rtp/src/codecs/g7xx/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod g7xx_test; 3 | 4 | use crate::packetizer::Payloader; 5 | use shared::error::Result; 6 | 7 | use bytes::Bytes; 8 | 9 | /// G711Payloader payloads G711 packets 10 | pub type G711Payloader = G7xxPayloader; 11 | /// G722Payloader payloads G722 packets 12 | pub type G722Payloader = G7xxPayloader; 13 | 14 | #[derive(Default, Debug, Copy, Clone)] 15 | pub struct G7xxPayloader; 16 | 17 | impl Payloader for G7xxPayloader { 18 | /// Payload fragments an G7xx packet across one or more byte arrays 19 | fn payload(&mut self, mtu: usize, payload: &Bytes) -> Result> { 20 | if payload.is_empty() || mtu == 0 { 21 | return Ok(vec![]); 22 | } 23 | 24 | let mut payload_data_remaining = payload.len(); 25 | let mut payload_data_index = 0; 26 | let mut payloads = Vec::with_capacity(payload_data_remaining / mtu); 27 | while payload_data_remaining > 0 { 28 | let current_fragment_size = std::cmp::min(mtu, payload_data_remaining); 29 | payloads.push( 30 | payload.slice(payload_data_index..payload_data_index + current_fragment_size), 31 | ); 32 | 33 | payload_data_remaining -= current_fragment_size; 34 | payload_data_index += current_fragment_size; 35 | } 36 | 37 | Ok(payloads) 38 | } 39 | 40 | fn clone_to(&self) -> Box { 41 | Box::new(*self) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /rtc-rtp/src/codecs/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod av1; 2 | pub mod g7xx; 3 | pub mod h264; 4 | pub mod h265; 5 | pub mod opus; 6 | pub mod vp8; 7 | pub mod vp9; 8 | -------------------------------------------------------------------------------- /rtc-rtp/src/codecs/opus/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod opus_test; 3 | 4 | use crate::packetizer::{Depacketizer, Payloader}; 5 | use shared::error::{Error, Result}; 6 | 7 | use bytes::Bytes; 8 | 9 | #[derive(Default, Debug, Copy, Clone)] 10 | pub struct OpusPayloader; 11 | 12 | impl Payloader for OpusPayloader { 13 | fn payload(&mut self, mtu: usize, payload: &Bytes) -> Result> { 14 | if payload.is_empty() || mtu == 0 { 15 | return Ok(vec![]); 16 | } 17 | 18 | Ok(vec![payload.clone()]) 19 | } 20 | 21 | fn clone_to(&self) -> Box { 22 | Box::new(*self) 23 | } 24 | } 25 | 26 | /// OpusPacket represents the Opus header that is stored in the payload of an RTP Packet 27 | #[derive(PartialEq, Eq, Debug, Default, Clone)] 28 | pub struct OpusPacket; 29 | 30 | impl Depacketizer for OpusPacket { 31 | fn depacketize(&mut self, packet: &Bytes) -> Result { 32 | if packet.is_empty() { 33 | Err(Error::ErrShortPacket) 34 | } else { 35 | Ok(packet.clone()) 36 | } 37 | } 38 | 39 | fn is_partition_head(&self, _payload: &Bytes) -> bool { 40 | true 41 | } 42 | 43 | fn is_partition_tail(&self, _marker: bool, _payload: &Bytes) -> bool { 44 | true 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /rtc-rtp/src/codecs/opus/opus_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn test_opus_unmarshal() -> Result<()> { 5 | let mut pck = OpusPacket::default(); 6 | 7 | // Empty packet 8 | let empty_bytes = Bytes::from_static(&[]); 9 | let result = pck.depacketize(&empty_bytes); 10 | assert!(result.is_err(), "Result should be err in case of error"); 11 | 12 | // Normal packet 13 | let raw_bytes = Bytes::from_static(&[0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x90]); 14 | let payload = pck.depacketize(&raw_bytes)?; 15 | assert_eq!(&raw_bytes, &payload, "Payload must be same"); 16 | 17 | Ok(()) 18 | } 19 | 20 | #[test] 21 | fn test_opus_payload() -> Result<()> { 22 | let mut pck = OpusPayloader::default(); 23 | let empty = Bytes::from_static(&[]); 24 | let payload = Bytes::from_static(&[0x90, 0x90, 0x90]); 25 | 26 | // Positive MTU, empty payload 27 | let result = pck.payload(1, &empty)?; 28 | assert!(result.is_empty(), "Generated payload should be empty"); 29 | 30 | // Positive MTU, small payload 31 | let result = pck.payload(1, &payload)?; 32 | assert_eq!(result.len(), 1, "Generated payload should be the 1"); 33 | 34 | // Positive MTU, small payload 35 | let result = pck.payload(2, &payload)?; 36 | assert_eq!(result.len(), 1, "Generated payload should be the 1"); 37 | 38 | Ok(()) 39 | } 40 | 41 | #[test] 42 | fn test_opus_is_partition_head() -> Result<()> { 43 | let opus = OpusPacket::default(); 44 | //"NormalPacket" 45 | assert!( 46 | opus.is_partition_head(&Bytes::from_static(&[0x00, 0x00])), 47 | "All OPUS RTP packet should be the head of a new partition" 48 | ); 49 | 50 | Ok(()) 51 | } 52 | -------------------------------------------------------------------------------- /rtc-rtp/src/extension/audio_level_extension/audio_level_extension_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use bytes::{Bytes, BytesMut}; 3 | use shared::error::Result; 4 | 5 | #[test] 6 | fn test_audio_level_extension_too_small() -> Result<()> { 7 | let mut buf = &vec![0u8; 0][..]; 8 | let result = AudioLevelExtension::unmarshal(&mut buf); 9 | assert!(result.is_err()); 10 | 11 | Ok(()) 12 | } 13 | 14 | #[test] 15 | fn test_audio_level_extension_voice_true() -> Result<()> { 16 | let raw = Bytes::from_static(&[0x88]); 17 | let buf = &mut raw.clone(); 18 | let a1 = AudioLevelExtension::unmarshal(buf)?; 19 | let a2 = AudioLevelExtension { 20 | level: 8, 21 | voice: true, 22 | }; 23 | assert_eq!(a1, a2); 24 | 25 | let mut dst = BytesMut::with_capacity(a2.marshal_size()); 26 | dst.resize(a2.marshal_size(), 0); 27 | a2.marshal_to(&mut dst)?; 28 | assert_eq!(raw, dst.freeze()); 29 | 30 | Ok(()) 31 | } 32 | 33 | #[test] 34 | fn test_audio_level_extension_voice_false() -> Result<()> { 35 | let raw = Bytes::from_static(&[0x8]); 36 | let buf = &mut raw.clone(); 37 | let a1 = AudioLevelExtension::unmarshal(buf)?; 38 | let a2 = AudioLevelExtension { 39 | level: 8, 40 | voice: false, 41 | }; 42 | assert_eq!(a1, a2); 43 | 44 | let mut dst = BytesMut::with_capacity(a2.marshal_size()); 45 | dst.resize(a2.marshal_size(), 0); 46 | a2.marshal_to(&mut dst)?; 47 | assert_eq!(raw, dst.freeze()); 48 | 49 | Ok(()) 50 | } 51 | 52 | #[test] 53 | fn test_audio_level_extension_level_overflow() -> Result<()> { 54 | let a = AudioLevelExtension { 55 | level: 128, 56 | voice: false, 57 | }; 58 | 59 | let mut dst = BytesMut::with_capacity(a.marshal_size()); 60 | dst.resize(a.marshal_size(), 0); 61 | let result = a.marshal_to(&mut dst); 62 | assert!(result.is_err()); 63 | 64 | Ok(()) 65 | } 66 | -------------------------------------------------------------------------------- /rtc-rtp/src/extension/transport_cc_extension/transport_cc_extension_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use bytes::{Bytes, BytesMut}; 3 | use shared::error::Result; 4 | 5 | #[test] 6 | fn test_transport_cc_extension_too_small() -> Result<()> { 7 | let mut buf = &vec![0u8; 0][..]; 8 | let result = TransportCcExtension::unmarshal(&mut buf); 9 | assert!(result.is_err()); 10 | 11 | Ok(()) 12 | } 13 | 14 | #[test] 15 | fn test_transport_cc_extension() -> Result<()> { 16 | let raw = Bytes::from_static(&[0x00, 0x02]); 17 | let buf = &mut raw.clone(); 18 | let t1 = TransportCcExtension::unmarshal(buf)?; 19 | let t2 = TransportCcExtension { 20 | transport_sequence: 2, 21 | }; 22 | assert_eq!(t1, t2); 23 | 24 | let mut dst = BytesMut::with_capacity(t2.marshal_size()); 25 | dst.resize(t2.marshal_size(), 0); 26 | t2.marshal_to(&mut dst)?; 27 | assert_eq!(raw, dst.freeze()); 28 | 29 | Ok(()) 30 | } 31 | 32 | #[test] 33 | fn test_transport_cc_extension_extra_bytes() -> Result<()> { 34 | let mut raw = Bytes::from_static(&[0x00, 0x02, 0x00, 0xff, 0xff]); 35 | let buf = &mut raw; 36 | let t1 = TransportCcExtension::unmarshal(buf)?; 37 | let t2 = TransportCcExtension { 38 | transport_sequence: 2, 39 | }; 40 | assert_eq!(t1, t2); 41 | 42 | Ok(()) 43 | } 44 | -------------------------------------------------------------------------------- /rtc-rtp/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms)] 2 | #![allow(dead_code)] 3 | 4 | pub mod codecs; 5 | pub mod extension; 6 | pub mod header; 7 | pub mod packet; 8 | pub mod packetizer; 9 | pub mod sequence; 10 | 11 | pub use packet::Packet; 12 | -------------------------------------------------------------------------------- /rtc-sctp/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # rtc-sctp changelog 2 | 3 | ## Unreleased 4 | -------------------------------------------------------------------------------- /rtc-sctp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtc-sctp" 3 | version = "0.2.0" 4 | authors = ["Rain Liu "] 5 | edition = "2021" 6 | description = "RTC SCTP in Rust" 7 | license = "MIT/Apache-2.0" 8 | documentation = "https://docs.rs/rtc-sctp" 9 | homepage = "https://ortc.rs" 10 | repository = "https://github.com/webrtc-rs/rtc/tree/master/rtc-sctp" 11 | 12 | [dependencies] 13 | shared = { version = "0.2.0", path = "../rtc-shared", package = "rtc-shared", default-features = false, features = [] } 14 | 15 | bytes = "1.5.0" 16 | fxhash = "0.2.1" 17 | rand = "0.8.5" 18 | slab = "0.4.9" 19 | thiserror = "1.0.57" 20 | log = "0.4.21" 21 | crc = "3.0.1" 22 | 23 | [dev-dependencies] 24 | assert_matches = "1.5.0" 25 | lazy_static = "1.4.0" 26 | -------------------------------------------------------------------------------- /rtc-sctp/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 WebRTC.rs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rtc-sctp/README.md: -------------------------------------------------------------------------------- 1 |

2 | WebRTC.rs 3 |
4 |

5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | License: MIT/Apache 2.0 23 | 24 | 25 | Discord 26 | 27 |

28 |

29 | RTC SCTP Protocol in Rust With Sans-IO 30 |

31 | -------------------------------------------------------------------------------- /rtc-sctp/src/association/stats.rs: -------------------------------------------------------------------------------- 1 | /// Association statistics 2 | #[derive(Default, Debug, Copy, Clone)] 3 | pub struct AssociationStats { 4 | n_datas: u64, 5 | n_sacks: u64, 6 | n_t3timeouts: u64, 7 | n_ack_timeouts: u64, 8 | n_fast_retrans: u64, 9 | } 10 | 11 | impl AssociationStats { 12 | pub fn inc_datas(&mut self) { 13 | self.n_datas += 1; 14 | } 15 | 16 | pub fn get_num_datas(&mut self) -> u64 { 17 | self.n_datas 18 | } 19 | 20 | pub fn inc_sacks(&mut self) { 21 | self.n_sacks += 1; 22 | } 23 | 24 | pub fn get_num_sacks(&mut self) -> u64 { 25 | self.n_sacks 26 | } 27 | 28 | pub fn inc_t3timeouts(&mut self) { 29 | self.n_t3timeouts += 1; 30 | } 31 | 32 | pub fn get_num_t3timeouts(&mut self) -> u64 { 33 | self.n_t3timeouts 34 | } 35 | 36 | pub fn inc_ack_timeouts(&mut self) { 37 | self.n_ack_timeouts += 1; 38 | } 39 | 40 | pub fn get_num_ack_timeouts(&mut self) -> u64 { 41 | self.n_ack_timeouts 42 | } 43 | 44 | pub fn inc_fast_retrans(&mut self) { 45 | self.n_fast_retrans += 1; 46 | } 47 | 48 | pub fn get_num_fast_retrans(&mut self) -> u64 { 49 | self.n_fast_retrans 50 | } 51 | 52 | pub fn reset(&mut self) { 53 | self.n_datas = 0; 54 | self.n_sacks = 0; 55 | self.n_t3timeouts = 0; 56 | self.n_ack_timeouts = 0; 57 | self.n_fast_retrans = 0; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /rtc-sctp/src/chunk/chunk_cookie_ack.rs: -------------------------------------------------------------------------------- 1 | use super::{chunk_header::*, chunk_type::*, *}; 2 | 3 | use bytes::{Bytes, BytesMut}; 4 | use std::fmt; 5 | 6 | /// chunkCookieAck represents an SCTP Chunk of type chunkCookieAck 7 | /// 8 | /// 0 1 2 3 9 | /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 10 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 11 | /// | Type = 11 |Chunk Flags | Length = 4 | 12 | /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 13 | #[derive(Debug, Clone)] 14 | pub(crate) struct ChunkCookieAck; 15 | 16 | /// makes ChunkCookieAck printable 17 | impl fmt::Display for ChunkCookieAck { 18 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 19 | write!(f, "{}", self.header()) 20 | } 21 | } 22 | 23 | impl Chunk for ChunkCookieAck { 24 | fn header(&self) -> ChunkHeader { 25 | ChunkHeader { 26 | typ: CT_COOKIE_ACK, 27 | flags: 0, 28 | value_length: self.value_length() as u16, 29 | } 30 | } 31 | 32 | fn unmarshal(raw: &Bytes) -> Result { 33 | let header = ChunkHeader::unmarshal(raw)?; 34 | 35 | if header.typ != CT_COOKIE_ACK { 36 | return Err(Error::ErrChunkTypeNotCookieAck); 37 | } 38 | 39 | Ok(ChunkCookieAck {}) 40 | } 41 | 42 | fn marshal_to(&self, buf: &mut BytesMut) -> Result { 43 | self.header().marshal_to(buf)?; 44 | Ok(buf.len()) 45 | } 46 | 47 | fn check(&self) -> Result<()> { 48 | Ok(()) 49 | } 50 | 51 | fn value_length(&self) -> usize { 52 | 0 53 | } 54 | 55 | fn as_any(&self) -> &(dyn Any) { 56 | self 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /rtc-sctp/src/chunk/chunk_shutdown_ack.rs: -------------------------------------------------------------------------------- 1 | use super::{chunk_header::*, chunk_type::*, *}; 2 | 3 | use bytes::{Bytes, BytesMut}; 4 | use std::fmt; 5 | 6 | ///chunkShutdownAck represents an SCTP Chunk of type chunkShutdownAck 7 | /// 8 | ///0 1 2 3 9 | ///0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 10 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 11 | ///| Type = 8 | Chunk Flags | Length = 4 | 12 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 13 | #[derive(Default, Debug, Clone)] 14 | pub(crate) struct ChunkShutdownAck; 15 | 16 | /// makes chunkShutdownAck printable 17 | impl fmt::Display for ChunkShutdownAck { 18 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 19 | write!(f, "{}", self.header()) 20 | } 21 | } 22 | 23 | impl Chunk for ChunkShutdownAck { 24 | fn header(&self) -> ChunkHeader { 25 | ChunkHeader { 26 | typ: CT_SHUTDOWN_ACK, 27 | flags: 0, 28 | value_length: self.value_length() as u16, 29 | } 30 | } 31 | 32 | fn unmarshal(raw: &Bytes) -> Result { 33 | let header = ChunkHeader::unmarshal(raw)?; 34 | 35 | if header.typ != CT_SHUTDOWN_ACK { 36 | return Err(Error::ErrChunkTypeNotShutdownAck); 37 | } 38 | 39 | Ok(ChunkShutdownAck {}) 40 | } 41 | 42 | fn marshal_to(&self, writer: &mut BytesMut) -> Result { 43 | self.header().marshal_to(writer)?; 44 | Ok(writer.len()) 45 | } 46 | 47 | fn check(&self) -> Result<()> { 48 | Ok(()) 49 | } 50 | 51 | fn value_length(&self) -> usize { 52 | 0 53 | } 54 | 55 | fn as_any(&self) -> &(dyn Any) { 56 | self 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /rtc-sctp/src/chunk/chunk_shutdown_complete.rs: -------------------------------------------------------------------------------- 1 | use super::{chunk_header::*, chunk_type::*, *}; 2 | 3 | use bytes::{Bytes, BytesMut}; 4 | use std::fmt; 5 | 6 | ///chunkShutdownComplete represents an SCTP Chunk of type chunkShutdownComplete 7 | /// 8 | ///0 1 2 3 9 | ///0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 10 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 11 | ///| Type = 14 |Reserved |T| Length = 4 | 12 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 13 | #[derive(Default, Debug, Clone)] 14 | pub(crate) struct ChunkShutdownComplete; 15 | 16 | /// makes chunkShutdownComplete printable 17 | impl fmt::Display for ChunkShutdownComplete { 18 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 19 | write!(f, "{}", self.header()) 20 | } 21 | } 22 | 23 | impl Chunk for ChunkShutdownComplete { 24 | fn header(&self) -> ChunkHeader { 25 | ChunkHeader { 26 | typ: CT_SHUTDOWN_COMPLETE, 27 | flags: 0, 28 | value_length: self.value_length() as u16, 29 | } 30 | } 31 | 32 | fn unmarshal(raw: &Bytes) -> Result { 33 | let header = ChunkHeader::unmarshal(raw)?; 34 | 35 | if header.typ != CT_SHUTDOWN_COMPLETE { 36 | return Err(Error::ErrChunkTypeNotShutdownComplete); 37 | } 38 | 39 | Ok(ChunkShutdownComplete {}) 40 | } 41 | 42 | fn marshal_to(&self, writer: &mut BytesMut) -> Result { 43 | self.header().marshal_to(writer)?; 44 | Ok(writer.len()) 45 | } 46 | 47 | fn check(&self) -> Result<()> { 48 | Ok(()) 49 | } 50 | 51 | fn value_length(&self) -> usize { 52 | 0 53 | } 54 | 55 | fn as_any(&self) -> &(dyn Any) { 56 | self 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /rtc-sctp/src/param/param_forward_tsn_supported.rs: -------------------------------------------------------------------------------- 1 | use super::{param_header::*, param_type::*, *}; 2 | 3 | use bytes::{Bytes, BytesMut}; 4 | 5 | /// At the initialization of the association, the sender of the INIT or 6 | /// INIT ACK chunk MAY include this OPTIONAL parameter to inform its peer 7 | /// that it is able to support the Forward TSN chunk 8 | /// 9 | /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 10 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 11 | ///| Parameter Type = 49152 | Parameter Length = 4 | 12 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 13 | #[derive(Default, Debug, Clone, PartialEq)] 14 | pub(crate) struct ParamForwardTsnSupported; 15 | 16 | impl fmt::Display for ParamForwardTsnSupported { 17 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 18 | write!(f, "{}", self.header()) 19 | } 20 | } 21 | 22 | impl Param for ParamForwardTsnSupported { 23 | fn header(&self) -> ParamHeader { 24 | ParamHeader { 25 | typ: ParamType::ForwardTsnSupp, 26 | value_length: self.value_length() as u16, 27 | } 28 | } 29 | 30 | fn unmarshal(raw: &Bytes) -> Result { 31 | let _ = ParamHeader::unmarshal(raw)?; 32 | Ok(ParamForwardTsnSupported {}) 33 | } 34 | 35 | fn marshal_to(&self, buf: &mut BytesMut) -> Result { 36 | self.header().marshal_to(buf)?; 37 | Ok(buf.len()) 38 | } 39 | 40 | fn value_length(&self) -> usize { 41 | 0 42 | } 43 | 44 | fn clone_to(&self) -> Box { 45 | Box::new(self.clone()) 46 | } 47 | 48 | fn as_any(&self) -> &(dyn Any) { 49 | self 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /rtc-sctp/src/param/param_header.rs: -------------------------------------------------------------------------------- 1 | use super::{param_type::*, *}; 2 | 3 | use bytes::{Buf, BufMut, Bytes, BytesMut}; 4 | use std::fmt; 5 | 6 | #[derive(Debug, Clone, PartialEq)] 7 | pub(crate) struct ParamHeader { 8 | pub(crate) typ: ParamType, 9 | pub(crate) value_length: u16, 10 | } 11 | 12 | pub(crate) const PARAM_HEADER_LENGTH: usize = 4; 13 | 14 | /// String makes paramHeader printable 15 | impl fmt::Display for ParamHeader { 16 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 17 | write!(f, "{}", self.typ) 18 | } 19 | } 20 | 21 | impl Param for ParamHeader { 22 | fn header(&self) -> ParamHeader { 23 | self.clone() 24 | } 25 | 26 | fn unmarshal(raw: &Bytes) -> Result { 27 | if raw.len() < PARAM_HEADER_LENGTH { 28 | return Err(Error::ErrParamHeaderTooShort); 29 | } 30 | 31 | let reader = &mut raw.clone(); 32 | 33 | let typ: ParamType = reader.get_u16().into(); 34 | 35 | let len = reader.get_u16() as usize; 36 | if len < PARAM_HEADER_LENGTH || raw.len() < len { 37 | return Err(Error::ErrParamHeaderTooShort); 38 | } 39 | 40 | Ok(ParamHeader { 41 | typ, 42 | value_length: (len - PARAM_HEADER_LENGTH) as u16, 43 | }) 44 | } 45 | 46 | fn marshal_to(&self, writer: &mut BytesMut) -> Result { 47 | writer.put_u16(self.typ.into()); 48 | writer.put_u16(self.value_length + PARAM_HEADER_LENGTH as u16); 49 | Ok(writer.len()) 50 | } 51 | 52 | fn value_length(&self) -> usize { 53 | self.value_length as usize 54 | } 55 | 56 | fn clone_to(&self) -> Box { 57 | Box::new(self.clone()) 58 | } 59 | 60 | fn as_any(&self) -> &(dyn Any) { 61 | self 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /rtc-sctp/src/param/param_heartbeat_info.rs: -------------------------------------------------------------------------------- 1 | use super::{param_header::*, param_type::*, *}; 2 | 3 | use bytes::{Bytes, BytesMut}; 4 | 5 | #[derive(Default, Debug, Clone, PartialEq)] 6 | pub(crate) struct ParamHeartbeatInfo { 7 | pub(crate) heartbeat_information: Bytes, 8 | } 9 | 10 | impl fmt::Display for ParamHeartbeatInfo { 11 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 12 | write!(f, "{} {:?}", self.header(), self.heartbeat_information) 13 | } 14 | } 15 | 16 | impl Param for ParamHeartbeatInfo { 17 | fn header(&self) -> ParamHeader { 18 | ParamHeader { 19 | typ: ParamType::HeartbeatInfo, 20 | value_length: self.value_length() as u16, 21 | } 22 | } 23 | 24 | fn unmarshal(raw: &Bytes) -> Result { 25 | let header = ParamHeader::unmarshal(raw)?; 26 | let heartbeat_information = 27 | raw.slice(PARAM_HEADER_LENGTH..PARAM_HEADER_LENGTH + header.value_length()); 28 | Ok(ParamHeartbeatInfo { 29 | heartbeat_information, 30 | }) 31 | } 32 | 33 | fn marshal_to(&self, buf: &mut BytesMut) -> Result { 34 | self.header().marshal_to(buf)?; 35 | buf.extend(self.heartbeat_information.clone()); 36 | Ok(buf.len()) 37 | } 38 | 39 | fn value_length(&self) -> usize { 40 | self.heartbeat_information.len() 41 | } 42 | 43 | fn clone_to(&self) -> Box { 44 | Box::new(self.clone()) 45 | } 46 | 47 | fn as_any(&self) -> &(dyn Any) { 48 | self 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /rtc-sctp/src/param/param_random.rs: -------------------------------------------------------------------------------- 1 | use super::{param_header::*, param_type::*, *}; 2 | 3 | use bytes::{Bytes, BytesMut}; 4 | 5 | #[derive(Default, Debug, Clone, PartialEq)] 6 | pub(crate) struct ParamRandom { 7 | pub(crate) random_data: Bytes, 8 | } 9 | 10 | impl fmt::Display for ParamRandom { 11 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 12 | write!(f, "{} {:?}", self.header(), self.random_data) 13 | } 14 | } 15 | 16 | impl Param for ParamRandom { 17 | fn header(&self) -> ParamHeader { 18 | ParamHeader { 19 | typ: ParamType::Random, 20 | value_length: self.value_length() as u16, 21 | } 22 | } 23 | 24 | fn unmarshal(raw: &Bytes) -> Result { 25 | let header = ParamHeader::unmarshal(raw)?; 26 | let random_data = 27 | raw.slice(PARAM_HEADER_LENGTH..PARAM_HEADER_LENGTH + header.value_length()); 28 | Ok(ParamRandom { random_data }) 29 | } 30 | 31 | fn marshal_to(&self, buf: &mut BytesMut) -> Result { 32 | self.header().marshal_to(buf)?; 33 | buf.extend(self.random_data.clone()); 34 | Ok(buf.len()) 35 | } 36 | 37 | fn value_length(&self) -> usize { 38 | self.random_data.len() 39 | } 40 | 41 | fn clone_to(&self) -> Box { 42 | Box::new(self.clone()) 43 | } 44 | 45 | fn as_any(&self) -> &(dyn Any) { 46 | self 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /rtc-sctp/src/param/param_state_cookie.rs: -------------------------------------------------------------------------------- 1 | use super::{param_header::*, param_type::*, *}; 2 | 3 | use bytes::{Bytes, BytesMut}; 4 | use rand::Rng; 5 | use std::fmt; 6 | 7 | #[derive(Default, Debug, Clone, PartialEq)] 8 | pub(crate) struct ParamStateCookie { 9 | pub(crate) cookie: Bytes, 10 | } 11 | 12 | /// String makes paramStateCookie printable 13 | impl fmt::Display for ParamStateCookie { 14 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 15 | write!(f, "{}: {:?}", self.header(), self.cookie) 16 | } 17 | } 18 | 19 | impl Param for ParamStateCookie { 20 | fn header(&self) -> ParamHeader { 21 | ParamHeader { 22 | typ: ParamType::StateCookie, 23 | value_length: self.value_length() as u16, 24 | } 25 | } 26 | 27 | fn unmarshal(raw: &Bytes) -> Result { 28 | let header = ParamHeader::unmarshal(raw)?; 29 | let cookie = raw.slice(PARAM_HEADER_LENGTH..PARAM_HEADER_LENGTH + header.value_length()); 30 | Ok(ParamStateCookie { cookie }) 31 | } 32 | 33 | fn marshal_to(&self, buf: &mut BytesMut) -> Result { 34 | self.header().marshal_to(buf)?; 35 | buf.extend(self.cookie.clone()); 36 | Ok(buf.len()) 37 | } 38 | 39 | fn value_length(&self) -> usize { 40 | self.cookie.len() 41 | } 42 | 43 | fn clone_to(&self) -> Box { 44 | Box::new(self.clone()) 45 | } 46 | 47 | fn as_any(&self) -> &(dyn Any) { 48 | self 49 | } 50 | } 51 | 52 | impl ParamStateCookie { 53 | pub(crate) fn new() -> Self { 54 | let mut cookie = BytesMut::new(); 55 | cookie.resize(32, 0); 56 | rand::thread_rng().fill(cookie.as_mut()); 57 | 58 | ParamStateCookie { 59 | cookie: cookie.freeze(), 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /rtc-sctp/src/param/param_supported_extensions.rs: -------------------------------------------------------------------------------- 1 | use super::{param_header::*, param_type::*, *}; 2 | use crate::chunk::chunk_type::*; 3 | 4 | use bytes::{Buf, BufMut, Bytes, BytesMut}; 5 | 6 | #[derive(Default, Debug, Clone, PartialEq)] 7 | pub(crate) struct ParamSupportedExtensions { 8 | pub(crate) chunk_types: Vec, 9 | } 10 | 11 | impl fmt::Display for ParamSupportedExtensions { 12 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 13 | write!( 14 | f, 15 | "{} {}", 16 | self.header(), 17 | self.chunk_types 18 | .iter() 19 | .map(|ct| ct.to_string()) 20 | .collect::>() 21 | .join(" "), 22 | ) 23 | } 24 | } 25 | 26 | impl Param for ParamSupportedExtensions { 27 | fn header(&self) -> ParamHeader { 28 | ParamHeader { 29 | typ: ParamType::SupportedExt, 30 | value_length: self.value_length() as u16, 31 | } 32 | } 33 | 34 | fn unmarshal(raw: &Bytes) -> Result { 35 | let header = ParamHeader::unmarshal(raw)?; 36 | 37 | let reader = 38 | &mut raw.slice(PARAM_HEADER_LENGTH..PARAM_HEADER_LENGTH + header.value_length()); 39 | 40 | let mut chunk_types = vec![]; 41 | while reader.has_remaining() { 42 | chunk_types.push(ChunkType(reader.get_u8())); 43 | } 44 | 45 | Ok(ParamSupportedExtensions { chunk_types }) 46 | } 47 | 48 | fn marshal_to(&self, buf: &mut BytesMut) -> Result { 49 | self.header().marshal_to(buf)?; 50 | for ct in &self.chunk_types { 51 | buf.put_u8(ct.0); 52 | } 53 | Ok(buf.len()) 54 | } 55 | 56 | fn value_length(&self) -> usize { 57 | self.chunk_types.len() 58 | } 59 | 60 | fn clone_to(&self) -> Box { 61 | Box::new(self.clone()) 62 | } 63 | 64 | fn as_any(&self) -> &(dyn Any) { 65 | self 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /rtc-sctp/src/queue/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod queue_test; 3 | 4 | pub(crate) mod payload_queue; 5 | pub(crate) mod pending_queue; 6 | pub(crate) mod reassembly_queue; 7 | -------------------------------------------------------------------------------- /rtc-sdp/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /.idea/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | -------------------------------------------------------------------------------- /rtc-sdp/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # rtc-sdp changelog 2 | 3 | ## Unreleased 4 | -------------------------------------------------------------------------------- /rtc-sdp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtc-sdp" 3 | version = "0.2.0" 4 | authors = ["Rain Liu "] 5 | edition = "2021" 6 | description = "RTC SDP in Rust" 7 | license = "MIT/Apache-2.0" 8 | documentation = "https://docs.rs/rtc-sdp" 9 | homepage = "https://ortc.rs" 10 | repository = "https://github.com/webrtc-rs/rtc/tree/master/rtc-sdp" 11 | 12 | [dependencies] 13 | shared = { version = "0.2.1", path = "../rtc-shared", package = "rtc-shared", default-features = false, features = [] } 14 | 15 | url = "2.5.0" 16 | rand = "0.8.5" 17 | 18 | [dev-dependencies] 19 | criterion = "0.5.1" 20 | 21 | [[bench]] 22 | name = "bench" 23 | harness = false 24 | -------------------------------------------------------------------------------- /rtc-sdp/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 WebRTC.rs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rtc-sdp/README.md: -------------------------------------------------------------------------------- 1 |

2 | WebRTC.rs 3 |
4 |

5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | License: MIT/Apache 2.0 23 | 24 | 25 | Discord 26 | 27 |

28 |

29 | RTC SDP Protocol in Rust With Sans-IO 30 |

31 | -------------------------------------------------------------------------------- /rtc-sdp/benches/bench.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | use sdp::SessionDescription; 3 | use std::io::Cursor; 4 | 5 | const CANONICAL_UNMARSHAL_SDP: &str = "v=0\r\n\ 6 | o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n\ 7 | s=SDP Seminar\r\n\ 8 | i=A Seminar on the session description protocol\r\n\ 9 | u=http://www.example.com/seminars/sdp.pdf\r\n\ 10 | e=j.doe@example.com (Jane Doe)\r\n\ 11 | p=+1 617 555-6011\r\n\ 12 | c=IN IP4 224.2.17.12/127\r\n\ 13 | b=X-YZ:128\r\n\ 14 | b=AS:12345\r\n\ 15 | t=2873397496 2873404696\r\n\ 16 | t=3034423619 3042462419\r\n\ 17 | r=604800 3600 0 90000\r\n\ 18 | z=2882844526 -3600 2898848070 0\r\n\ 19 | k=prompt\r\n\ 20 | a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\r\n\ 21 | a=recvonly\r\n\ 22 | m=audio 49170 RTP/AVP 0\r\n\ 23 | i=Vivamus a posuere nisl\r\n\ 24 | c=IN IP4 203.0.113.1\r\n\ 25 | b=X-YZ:128\r\n\ 26 | k=prompt\r\n\ 27 | a=sendrecv\r\n\ 28 | m=video 51372 RTP/AVP 99\r\n\ 29 | a=rtpmap:99 h263-1998/90000\r\n"; 30 | 31 | fn benchmark_sdp(c: &mut Criterion) { 32 | let mut reader = Cursor::new(CANONICAL_UNMARSHAL_SDP.as_bytes()); 33 | let sdp = SessionDescription::unmarshal(&mut reader).unwrap(); 34 | 35 | /////////////////////////////////////////////////////////////////////////////////////////////// 36 | c.bench_function("Benchmark Marshal", |b| { 37 | b.iter(|| { 38 | let _ = sdp.marshal(); 39 | }) 40 | }); 41 | 42 | c.bench_function("Benchmark Unmarshal ", |b| { 43 | b.iter(|| { 44 | let mut reader = Cursor::new(CANONICAL_UNMARSHAL_SDP.as_bytes()); 45 | let _ = SessionDescription::unmarshal(&mut reader).unwrap(); 46 | }) 47 | }); 48 | } 49 | 50 | criterion_group!(benches, benchmark_sdp); 51 | criterion_main!(benches); 52 | -------------------------------------------------------------------------------- /rtc-sdp/codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | max_report_age: off 4 | token: 40894be8-0942-482a-b7cf-e58721cff2c5 5 | 6 | coverage: 7 | precision: 2 8 | round: down 9 | range: 50..90 10 | status: 11 | project: 12 | default: 13 | enabled: no 14 | threshold: 0.2 15 | if_not_found: success 16 | patch: 17 | default: 18 | enabled: no 19 | if_not_found: success 20 | changes: 21 | default: 22 | enabled: no 23 | if_not_found: success 24 | -------------------------------------------------------------------------------- /rtc-sdp/fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | target 3 | corpus 4 | artifacts 5 | -------------------------------------------------------------------------------- /rtc-sdp/fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | name = "sdp-fuzz" 4 | version = "0.0.0" 5 | authors = ["Automatically generated"] 6 | publish = false 7 | edition = "2021" 8 | 9 | [package.metadata] 10 | cargo-fuzz = true 11 | 12 | [dependencies] 13 | libfuzzer-sys = "0.4" 14 | 15 | [dependencies.sdp] 16 | path = ".." 17 | 18 | # Prevent this from interfering with workspaces 19 | [workspace] 20 | members = ["."] 21 | 22 | [[bin]] 23 | name = "parse_session" 24 | path = "fuzz_targets/parse_session.rs" 25 | test = false 26 | doc = false 27 | -------------------------------------------------------------------------------- /rtc-sdp/fuzz/fuzz_targets/parse_session.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use libfuzzer_sys::fuzz_target; 3 | 4 | fuzz_target!(|data: &[u8]| { 5 | let mut cursor = std::io::Cursor::new(data); 6 | let _session = sdp::SessionDescription::unmarshal(&mut cursor); 7 | }); 8 | -------------------------------------------------------------------------------- /rtc-sdp/src/description/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod description_test; 3 | 4 | pub mod common; 5 | pub mod media; 6 | pub mod session; 7 | -------------------------------------------------------------------------------- /rtc-sdp/src/direction/direction_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::iter::Iterator; 3 | 4 | #[test] 5 | fn test_new_direction() { 6 | let passingtests = vec![ 7 | ("sendrecv", Direction::SendRecv), 8 | ("sendonly", Direction::SendOnly), 9 | ("recvonly", Direction::RecvOnly), 10 | ("inactive", Direction::Inactive), 11 | ]; 12 | 13 | let failingtests = vec!["", "notadirection"]; 14 | 15 | for (i, u) in passingtests.iter().enumerate() { 16 | let dir = Direction::new(u.0); 17 | assert!(u.1 == dir, "{}: {}", i, u.0); 18 | } 19 | for (_, &u) in failingtests.iter().enumerate() { 20 | let dir = Direction::new(u); 21 | assert!(dir == Direction::Unspecified); 22 | } 23 | } 24 | 25 | #[test] 26 | fn test_direction_string() { 27 | let tests = vec![ 28 | (Direction::Unspecified, DIRECTION_UNSPECIFIED_STR), 29 | (Direction::SendRecv, "sendrecv"), 30 | (Direction::SendOnly, "sendonly"), 31 | (Direction::RecvOnly, "recvonly"), 32 | (Direction::Inactive, "inactive"), 33 | ]; 34 | 35 | for (i, u) in tests.iter().enumerate() { 36 | assert!(u.1 == u.0.to_string(), "{}: {}", i, u.1); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /rtc-sdp/src/direction/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[cfg(test)] 4 | mod direction_test; 5 | 6 | /// Direction is a marker for transmission direction of an endpoint 7 | #[derive(Default, Debug, PartialEq, Eq, Clone)] 8 | pub enum Direction { 9 | #[default] 10 | Unspecified = 0, 11 | /// Direction::SendRecv is for bidirectional communication 12 | SendRecv = 1, 13 | /// Direction::SendOnly is for outgoing communication 14 | SendOnly = 2, 15 | /// Direction::RecvOnly is for incoming communication 16 | RecvOnly = 3, 17 | /// Direction::Inactive is for no communication 18 | Inactive = 4, 19 | } 20 | 21 | const DIRECTION_SEND_RECV_STR: &str = "sendrecv"; 22 | const DIRECTION_SEND_ONLY_STR: &str = "sendonly"; 23 | const DIRECTION_RECV_ONLY_STR: &str = "recvonly"; 24 | const DIRECTION_INACTIVE_STR: &str = "inactive"; 25 | const DIRECTION_UNSPECIFIED_STR: &str = "Unspecified"; 26 | 27 | impl fmt::Display for Direction { 28 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 29 | let s = match self { 30 | Direction::SendRecv => DIRECTION_SEND_RECV_STR, 31 | Direction::SendOnly => DIRECTION_SEND_ONLY_STR, 32 | Direction::RecvOnly => DIRECTION_RECV_ONLY_STR, 33 | Direction::Inactive => DIRECTION_INACTIVE_STR, 34 | _ => DIRECTION_UNSPECIFIED_STR, 35 | }; 36 | write!(f, "{s}") 37 | } 38 | } 39 | 40 | impl Direction { 41 | /// new defines a procedure for creating a new direction from a raw string. 42 | pub fn new(raw: &str) -> Self { 43 | match raw { 44 | DIRECTION_SEND_RECV_STR => Direction::SendRecv, 45 | DIRECTION_SEND_ONLY_STR => Direction::SendOnly, 46 | DIRECTION_RECV_ONLY_STR => Direction::RecvOnly, 47 | DIRECTION_INACTIVE_STR => Direction::Inactive, 48 | _ => Direction::Unspecified, 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /rtc-sdp/src/lexer/mod.rs: -------------------------------------------------------------------------------- 1 | use super::description::session::SessionDescription; 2 | use shared::error::{Error, Result}; 3 | 4 | use std::io; 5 | use std::io::SeekFrom; 6 | 7 | pub(crate) const END_LINE: &str = "\r\n"; 8 | 9 | pub struct Lexer<'a, R: io::BufRead + io::Seek> { 10 | pub desc: SessionDescription, 11 | pub reader: &'a mut R, 12 | } 13 | 14 | pub type StateFnType<'a, R> = fn(&mut Lexer<'a, R>) -> Result>>; 15 | 16 | pub struct StateFn<'a, R: io::BufRead + io::Seek> { 17 | pub f: StateFnType<'a, R>, 18 | } 19 | 20 | pub fn read_type(reader: &mut R) -> Result<(Vec, usize)> { 21 | let mut b = [0; 1]; 22 | 23 | loop { 24 | if reader.read_exact(&mut b).is_err() { 25 | return Ok((b"".to_vec(), 0)); 26 | } 27 | 28 | if b[0] == b'\n' || b[0] == b'\r' { 29 | continue; 30 | } 31 | reader.seek(SeekFrom::Current(-1))?; 32 | 33 | let mut buf = Vec::with_capacity(2); 34 | let num_bytes = reader.read_until(b'=', &mut buf)?; 35 | if num_bytes == 0 { 36 | return Ok((b"".to_vec(), num_bytes)); 37 | } 38 | match buf.len() { 39 | 2 => return Ok((buf, num_bytes)), 40 | _ => return Err(Error::SdpInvalidSyntax(String::from_utf8(buf)?)), 41 | } 42 | } 43 | } 44 | 45 | pub fn read_value(reader: &mut R) -> Result<(String, usize)> { 46 | let mut value = String::new(); 47 | let num_bytes = reader.read_line(&mut value)?; 48 | Ok((value.trim().to_string(), num_bytes)) 49 | } 50 | 51 | pub fn index_of(element: &str, data: &[&str]) -> i32 { 52 | for (k, &v) in data.iter().enumerate() { 53 | if element == v { 54 | return k as i32; 55 | } 56 | } 57 | -1 58 | } 59 | 60 | pub fn key_value_build(key: &str, value: Option<&String>) -> String { 61 | if let Some(val) = value { 62 | format!("{key}{val}{END_LINE}") 63 | } else { 64 | "".to_string() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /rtc-sdp/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms)] 2 | #![allow(dead_code)] 3 | 4 | pub mod description; 5 | pub mod direction; 6 | pub mod extmap; 7 | pub mod util; 8 | 9 | pub(crate) mod lexer; 10 | 11 | pub use description::{media::MediaDescription, session::SessionDescription}; 12 | -------------------------------------------------------------------------------- /rtc-shared/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # rtc-shared changelog 2 | 3 | ## Unreleased 4 | -------------------------------------------------------------------------------- /rtc-shared/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtc-shared" 3 | version = "0.2.1" 4 | authors = ["Rain Liu "] 5 | edition = "2021" 6 | description = "RTC Shared in Rust" 7 | license = "MIT/Apache-2.0" 8 | documentation = "https://docs.rs/rtc-shared" 9 | homepage = "https://ortc.rs" 10 | repository = "https://github.com/webrtc-rs/rtc/tree/master/rtc-shared" 11 | 12 | [features] 13 | default = ["crypto", "marshal", "replay"] 14 | crypto = [] 15 | marshal = [] 16 | replay = [] 17 | 18 | [dependencies] 19 | thiserror = "1.0.58" 20 | substring = "1.4.5" 21 | bytes = "1.5.0" 22 | aes-gcm = { version = "0.10.3", features = ["std"] } 23 | url = "2.5.0" 24 | rcgen = "0.12.1" 25 | sec1 = { version = "0.7.3", features = ["std"] } 26 | p256 = { version = "0.13.2", features = ["default", "ecdh", "ecdsa"] } 27 | aes = "0.8.4" 28 | retty = "0.29.0" 29 | -------------------------------------------------------------------------------- /rtc-shared/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 WebRTC.rs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rtc-shared/README.md: -------------------------------------------------------------------------------- 1 |

2 | WebRTC.rs 3 |
4 |

5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | License: MIT/Apache 2.0 23 | 24 | 25 | Discord 26 | 27 |

28 |

29 | RTC Shared Utilities in Rust With Sans-IO 30 |

31 | -------------------------------------------------------------------------------- /rtc-shared/src/crypto/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Result; 2 | 3 | /// KeyingMaterialExporter to extract keying material. 4 | /// 5 | /// This trait sits here to avoid getting a direct dependency between 6 | /// the dtls and srtp crates. 7 | pub trait KeyingMaterialExporter { 8 | fn export_keying_material(&self, label: &str, context: &[u8], length: usize) 9 | -> Result>; 10 | } 11 | -------------------------------------------------------------------------------- /rtc-shared/src/handler.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Result; 2 | use retty::transport::Transmit; 3 | use std::time::Instant; 4 | 5 | pub trait RTCHandler { 6 | /// Associated event input message type 7 | type Ein: 'static; 8 | /// Associated event output message type 9 | type Eout: 'static; 10 | /// Associated read input message type 11 | type Rin: 'static; 12 | /// Associated read output message type 13 | type Rout: 'static; 14 | /// Associated write input message type 15 | type Win: 'static; 16 | /// Associated write output message type for 17 | type Wout: 'static; 18 | 19 | /// Handles Rin and returns Rout for next inbound handler handling 20 | fn handle_read(&mut self, msg: Transmit) -> Result<()>; 21 | 22 | /// Polls Rout from internal queue for next inbound handler handling 23 | fn poll_read(&mut self) -> Option>; 24 | 25 | /// Handles Win and returns Wout for next outbound handler handling 26 | fn handle_write(&mut self, msg: Transmit) -> Result<()>; 27 | 28 | /// Polls Wout from internal queue for next outbound handler handling 29 | fn poll_write(&mut self) -> Option>; 30 | 31 | /// Handles event 32 | fn handle_event(&mut self, _evt: Self::Ein) -> Result<()> { 33 | Ok(()) 34 | } 35 | 36 | /// Polls event 37 | fn poll_event(&mut self) -> Option { 38 | None 39 | } 40 | 41 | /// Handles timeout 42 | fn handle_timeout(&mut self, _now: Instant) -> Result<()> { 43 | Ok(()) 44 | } 45 | 46 | /// Polls timeout 47 | fn poll_timeout(&mut self) -> Option { 48 | None 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /rtc-shared/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms)] 2 | #![allow(dead_code)] 3 | 4 | #[cfg(feature = "crypto")] 5 | pub mod crypto; 6 | 7 | #[cfg(feature = "marshal")] 8 | pub mod marshal; 9 | 10 | #[cfg(feature = "replay")] 11 | pub mod replay_detector; 12 | 13 | pub mod error; 14 | pub mod handler; 15 | pub mod util; 16 | 17 | pub use retty::transport::{ 18 | EcnCodepoint, FiveTuple, FourTuple, Protocol, Transmit, TransportContext, 19 | }; 20 | -------------------------------------------------------------------------------- /rtc-shared/src/marshal/mod.rs: -------------------------------------------------------------------------------- 1 | use bytes::{Buf, BytesMut}; 2 | 3 | use crate::error::{Error, Result}; 4 | 5 | pub trait MarshalSize { 6 | fn marshal_size(&self) -> usize; 7 | } 8 | 9 | pub trait Marshal: MarshalSize { 10 | fn marshal_to(&self, buf: &mut [u8]) -> Result; 11 | 12 | fn marshal(&self) -> Result { 13 | let l = self.marshal_size(); 14 | let mut buf = BytesMut::with_capacity(l); 15 | buf.resize(l, 0); 16 | let n = self.marshal_to(&mut buf)?; 17 | if n != l { 18 | Err(Error::Other(format!( 19 | "marshal_to output size {n}, but expect {l}" 20 | ))) 21 | } else { 22 | Ok(buf) 23 | } 24 | } 25 | } 26 | 27 | pub trait Unmarshal: MarshalSize { 28 | fn unmarshal(buf: &mut B) -> Result 29 | where 30 | Self: Sized, 31 | B: Buf; 32 | } 33 | -------------------------------------------------------------------------------- /rtc-srtp/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /.idea/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | -------------------------------------------------------------------------------- /rtc-srtp/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # rtc-srtp changelog 2 | 3 | ## Unreleased 4 | -------------------------------------------------------------------------------- /rtc-srtp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtc-srtp" 3 | version = "0.2.0" 4 | authors = ["Rain Liu "] 5 | edition = "2021" 6 | description = "RTC SRTP in Rust" 7 | license = "MIT/Apache-2.0" 8 | documentation = "https://docs.rs/rtc-srtp" 9 | homepage = "https://ortc.rs" 10 | repository = "https://github.com/webrtc-rs/rtc/tree/master/rtc-srtp" 11 | 12 | [dependencies] 13 | shared = { version = "0.2.0", path = "../rtc-shared", package = "rtc-shared", default-features = false, features = ["crypto", "marshal", "replay"] } 14 | rtp = { version = "0.2.0", path = "../rtc-rtp", package = "rtc-rtp" } 15 | rtcp = { version = "0.2.0", path = "../rtc-rtcp", package = "rtc-rtcp" } 16 | 17 | byteorder = "1.5.0" 18 | bytes = "1.5.0" 19 | hmac = { version = "0.12.1", features = ["std", "reset"] } 20 | sha1 = "0.10.6" 21 | ctr = "0.9.2" 22 | aes = "0.8.4" 23 | subtle = "2.5.0" 24 | aead = { version = "0.5.2", features = ["std"] } 25 | aes-gcm = { version = "0.10.3", features = ["std"] } 26 | 27 | [dev-dependencies] 28 | lazy_static = "1.4.0" 29 | -------------------------------------------------------------------------------- /rtc-srtp/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 WebRTC.rs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rtc-srtp/README.md: -------------------------------------------------------------------------------- 1 |

2 | WebRTC.rs 3 |
4 |

5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | License: MIT/Apache 2.0 23 | 24 | 25 | Discord 26 | 27 |

28 |

29 | RTC SRTP Protocol in Rust With Sans-IO 30 |

31 | -------------------------------------------------------------------------------- /rtc-srtp/codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | max_report_age: off 4 | token: d65de923-7c3d-4836-8d9a-5183b356be4f 5 | 6 | coverage: 7 | precision: 2 8 | round: down 9 | range: 50..90 10 | status: 11 | project: 12 | default: 13 | enabled: no 14 | threshold: 0.2 15 | if_not_found: success 16 | patch: 17 | default: 18 | enabled: no 19 | if_not_found: success 20 | changes: 21 | default: 22 | enabled: no 23 | if_not_found: success 24 | -------------------------------------------------------------------------------- /rtc-srtp/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms)] 2 | #![allow(dead_code)] 3 | 4 | mod cipher; 5 | pub mod config; 6 | pub mod context; 7 | mod key_derivation; 8 | pub mod option; 9 | pub mod protection_profile; 10 | -------------------------------------------------------------------------------- /rtc-srtp/src/option.rs: -------------------------------------------------------------------------------- 1 | use shared::replay_detector::*; 2 | 3 | pub type ContextOption = Box Box)>; 4 | 5 | pub(crate) const MAX_SEQUENCE_NUMBER: u16 = 65535; 6 | pub(crate) const MAX_SRTCP_INDEX: usize = 0x7FFFFFFF; 7 | 8 | /// srtp_replay_protection sets SRTP replay protection window size. 9 | pub fn srtp_replay_protection(window_size: usize) -> ContextOption { 10 | Box::new(move || -> Box { 11 | Box::new(WrappedSlidingWindowDetector::new( 12 | window_size, 13 | MAX_SEQUENCE_NUMBER as u64, 14 | )) 15 | }) 16 | } 17 | 18 | /// Sets SRTCP replay protection window size. 19 | pub fn srtcp_replay_protection(window_size: usize) -> ContextOption { 20 | Box::new(move || -> Box { 21 | Box::new(WrappedSlidingWindowDetector::new( 22 | window_size, 23 | MAX_SRTCP_INDEX as u64, 24 | )) 25 | }) 26 | } 27 | 28 | /// srtp_no_replay_protection disables SRTP replay protection. 29 | pub fn srtp_no_replay_protection() -> ContextOption { 30 | Box::new(|| -> Box { Box::::default() }) 31 | } 32 | 33 | /// srtcp_no_replay_protection disables SRTCP replay protection. 34 | pub fn srtcp_no_replay_protection() -> ContextOption { 35 | Box::new(|| -> Box { Box::::default() }) 36 | } 37 | -------------------------------------------------------------------------------- /rtc-stun/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /.idea/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | -------------------------------------------------------------------------------- /rtc-stun/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # rtc-stun changelog 2 | 3 | ## Unreleased 4 | -------------------------------------------------------------------------------- /rtc-stun/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtc-stun" 3 | version = "0.2.0" 4 | authors = ["Rain Liu "] 5 | edition = "2021" 6 | description = "RTC STUN in Rust" 7 | license = "MIT/Apache-2.0" 8 | documentation = "https://docs.rs/rtc-stun" 9 | homepage = "https://ortc.rs" 10 | repository = "https://github.com/webrtc-rs/rtc/tree/master/rtc-stun" 11 | 12 | [features] 13 | default = [] 14 | bench = [] 15 | 16 | [dependencies] 17 | shared = { version = "0.2.0", path = "../rtc-shared", package = "rtc-shared", default-features = false, features = [] } 18 | 19 | bytes = "1.5.0" 20 | lazy_static = "1.4.0" 21 | url = "2.5.0" 22 | rand = "0.8.5" 23 | base64 = "0.22.0" 24 | subtle = "2.5.0" 25 | crc = "3.0.1" 26 | ring = "0.17.8" 27 | md5 = "0.7.0" 28 | 29 | [dev-dependencies] 30 | clap = { version = "4.5.2", features = ["derive"] } 31 | criterion = "0.5.1" 32 | 33 | [[bench]] 34 | name = "bench" 35 | harness = false 36 | 37 | [[example]] 38 | name = "stun_client" 39 | path = "examples/stun_client.rs" 40 | bench = false 41 | 42 | [[example]] 43 | name = "stun_decode" 44 | path = "examples/stun_decode.rs" 45 | bench = false 46 | -------------------------------------------------------------------------------- /rtc-stun/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 WebRTC.rs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rtc-stun/README.md: -------------------------------------------------------------------------------- 1 |

2 | WebRTC.rs 3 |
4 |

5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | License: MIT/Apache 2.0 23 | 24 | 25 | Discord 26 | 27 |

28 |

29 | RTC STUN Protocol in Rust With Sans-IO 30 |

31 | -------------------------------------------------------------------------------- /rtc-stun/codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | max_report_age: off 4 | token: 5ed548cd-073b-4748-b584-ca2d637027bf 5 | 6 | coverage: 7 | precision: 2 8 | round: down 9 | range: 50..90 10 | status: 11 | project: 12 | default: 13 | enabled: no 14 | threshold: 0.2 15 | if_not_found: success 16 | patch: 17 | default: 18 | enabled: no 19 | if_not_found: success 20 | changes: 21 | default: 22 | enabled: no 23 | if_not_found: success 24 | -------------------------------------------------------------------------------- /rtc-stun/examples/stun_client.rs: -------------------------------------------------------------------------------- 1 | use rtc_stun::client::*; 2 | use rtc_stun::message::*; 3 | use rtc_stun::xoraddr::*; 4 | 5 | use clap::Parser; 6 | use shared::error::Error; 7 | use shared::Protocol; 8 | use std::net::UdpSocket; 9 | 10 | #[derive(Parser)] 11 | #[command(name = "STUN Client")] 12 | #[command(author = "Rusty Rain ")] 13 | #[command(version = "0.1.0")] 14 | #[command(about = "An example of STUN Client", long_about = None)] 15 | struct Cli { 16 | #[arg(long, default_value_t = format!("stun.l.google.com:19302"))] 17 | server: String, 18 | } 19 | 20 | fn main() -> Result<(), Error> { 21 | let cli = Cli::parse(); 22 | 23 | let server = cli.server; 24 | 25 | let conn = UdpSocket::bind("0:0")?; 26 | println!("Local address: {}", conn.local_addr()?); 27 | 28 | println!("Connecting to: {server}"); 29 | conn.connect(server)?; 30 | 31 | let mut client = 32 | ClientBuilder::new().build(conn.local_addr()?, conn.peer_addr()?, Protocol::UDP)?; 33 | 34 | let mut msg = Message::new(); 35 | msg.build(&[Box::::default(), Box::new(BINDING_REQUEST)])?; 36 | client.handle_write(msg)?; 37 | while let Some(transmit) = client.poll_transmit() { 38 | conn.send(&transmit.message)?; 39 | } 40 | 41 | let mut buf = vec![0u8; 1500]; 42 | let n = conn.recv(&mut buf)?; 43 | client.handle_read(&buf[..n])?; 44 | 45 | if let Some(event) = client.poll_event() { 46 | let msg = event.result?; 47 | let mut xor_addr = XorMappedAddress::default(); 48 | xor_addr.get_from(&msg)?; 49 | println!("Got response: {xor_addr}"); 50 | } 51 | 52 | client.handle_close()?; 53 | 54 | Ok(()) 55 | } 56 | -------------------------------------------------------------------------------- /rtc-stun/examples/stun_decode.rs: -------------------------------------------------------------------------------- 1 | use base64::prelude::*; 2 | use clap::Parser; 3 | 4 | use rtc_stun::message::Message; 5 | 6 | #[derive(Parser)] 7 | #[command(name = "STUN decode")] 8 | #[command(author = "Rusty Rain ")] 9 | #[command(version = "0.1.0")] 10 | #[command(about = "An example of STUN decode", long_about = None)] 11 | struct Cli { 12 | /// base64 encoded message, e.g. 'AAEAHCESpEJML0JTQWsyVXkwcmGALwAWaHR0cDovL2xvY2FsaG9zdDozMDAwLwAA'" 13 | #[arg(long)] 14 | data: String, 15 | } 16 | 17 | fn main() { 18 | let cli = Cli::parse(); 19 | 20 | let encoded_data = cli.data; 21 | let decoded_data = match BASE64_STANDARD.decode(encoded_data) { 22 | Ok(d) => d, 23 | Err(e) => panic!("Unable to decode base64 value: {e}"), 24 | }; 25 | 26 | let mut message = Message::new(); 27 | message.raw = decoded_data; 28 | 29 | match message.decode() { 30 | Ok(_) => println!("{message}"), 31 | Err(e) => panic!("Unable to decode message: {e}"), 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /rtc-stun/src/checks.rs: -------------------------------------------------------------------------------- 1 | use crate::attributes::*; 2 | use shared::error::*; 3 | 4 | use subtle::ConstantTimeEq; 5 | 6 | // check_size returns ErrAttrSizeInvalid if got is not equal to expected. 7 | pub fn check_size(_at: AttrType, got: usize, expected: usize) -> Result<()> { 8 | if got == expected { 9 | Ok(()) 10 | } else { 11 | Err(Error::ErrAttributeSizeInvalid) 12 | } 13 | } 14 | 15 | // is_attr_size_invalid returns true if error means that attribute size is invalid. 16 | pub fn is_attr_size_invalid(err: &Error) -> bool { 17 | Error::ErrAttributeSizeInvalid == *err 18 | } 19 | 20 | pub(crate) fn check_hmac(got: &[u8], expected: &[u8]) -> Result<()> { 21 | if got.ct_eq(expected).unwrap_u8() != 1 { 22 | Err(Error::ErrIntegrityMismatch) 23 | } else { 24 | Ok(()) 25 | } 26 | } 27 | 28 | pub(crate) fn check_fingerprint(got: u32, expected: u32) -> Result<()> { 29 | if got == expected { 30 | Ok(()) 31 | } else { 32 | Err(Error::ErrFingerprintMismatch) 33 | } 34 | } 35 | 36 | // check_overflow returns ErrAttributeSizeOverflow if got is bigger that max. 37 | pub fn check_overflow(_at: AttrType, got: usize, max: usize) -> Result<()> { 38 | if got <= max { 39 | Ok(()) 40 | } else { 41 | Err(Error::ErrAttributeSizeOverflow) 42 | } 43 | } 44 | 45 | // is_attr_size_overflow returns true if error means that attribute size is too big. 46 | pub fn is_attr_size_overflow(err: &Error) -> bool { 47 | Error::ErrAttributeSizeOverflow == *err 48 | } 49 | -------------------------------------------------------------------------------- /rtc-stun/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms)] 2 | #![allow(dead_code)] 3 | 4 | #[macro_use] 5 | extern crate lazy_static; 6 | 7 | pub mod addr; 8 | pub mod agent; 9 | pub mod attributes; 10 | pub mod checks; 11 | pub mod client; 12 | pub mod error_code; 13 | pub mod fingerprint; 14 | pub mod integrity; 15 | pub mod message; 16 | pub mod textattrs; 17 | pub mod uattrs; 18 | pub mod uri; 19 | pub mod xoraddr; 20 | 21 | // IANA assigned ports for "stun" protocol. 22 | pub const DEFAULT_PORT: u16 = 3478; 23 | pub const DEFAULT_TLS_PORT: u16 = 5349; 24 | -------------------------------------------------------------------------------- /rtc-stun/src/uattrs.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod uattrs_test; 3 | 4 | use crate::attributes::*; 5 | use crate::message::*; 6 | use shared::error::*; 7 | 8 | use std::fmt; 9 | 10 | // UnknownAttributes represents UNKNOWN-ATTRIBUTES attribute. 11 | // 12 | // RFC 5389 Section 15.9 13 | pub struct UnknownAttributes(pub Vec); 14 | 15 | impl fmt::Display for UnknownAttributes { 16 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 17 | if self.0.is_empty() { 18 | write!(f, "") 19 | } else { 20 | let mut s = vec![]; 21 | for t in &self.0 { 22 | s.push(t.to_string()); 23 | } 24 | write!(f, "{}", s.join(", ")) 25 | } 26 | } 27 | } 28 | 29 | // type size is 16 bit. 30 | const ATTR_TYPE_SIZE: usize = 2; 31 | 32 | impl Setter for UnknownAttributes { 33 | // add_to adds UNKNOWN-ATTRIBUTES attribute to message. 34 | fn add_to(&self, m: &mut Message) -> Result<()> { 35 | let mut v = Vec::with_capacity(ATTR_TYPE_SIZE * 20); // 20 should be enough 36 | // If len(a.Types) > 20, there will be allocations. 37 | for t in &self.0 { 38 | v.extend_from_slice(&t.value().to_be_bytes()); 39 | } 40 | m.add(ATTR_UNKNOWN_ATTRIBUTES, &v); 41 | Ok(()) 42 | } 43 | } 44 | 45 | impl Getter for UnknownAttributes { 46 | // GetFrom parses UNKNOWN-ATTRIBUTES from message. 47 | fn get_from(&mut self, m: &Message) -> Result<()> { 48 | let v = m.get(ATTR_UNKNOWN_ATTRIBUTES)?; 49 | if v.len() % ATTR_TYPE_SIZE != 0 { 50 | return Err(Error::ErrBadUnknownAttrsSize); 51 | } 52 | self.0.clear(); 53 | let mut first = 0usize; 54 | while first < v.len() { 55 | let last = first + ATTR_TYPE_SIZE; 56 | self.0 57 | .push(AttrType(u16::from_be_bytes([v[first], v[first + 1]]))); 58 | first = last; 59 | } 60 | Ok(()) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /rtc-stun/src/uattrs/uattrs_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn test_unknown_attributes() -> Result<()> { 5 | let mut m = Message::new(); 6 | let a = UnknownAttributes(vec![ATTR_DONT_FRAGMENT, ATTR_CHANNEL_NUMBER]); 7 | assert_eq!( 8 | a.to_string(), 9 | "DONT-FRAGMENT, CHANNEL-NUMBER", 10 | "bad String:{a}" 11 | ); 12 | assert_eq!( 13 | UnknownAttributes(vec![]).to_string(), 14 | "", 15 | "bad blank string" 16 | ); 17 | 18 | a.add_to(&mut m)?; 19 | 20 | //"GetFrom" 21 | { 22 | let mut attrs = UnknownAttributes(Vec::with_capacity(10)); 23 | attrs.get_from(&m)?; 24 | for i in 0..a.0.len() { 25 | assert_eq!(a.0[i], attrs.0[i], "expected {} != {}", a.0[i], attrs.0[i]); 26 | } 27 | let mut m_blank = Message::new(); 28 | let result = attrs.get_from(&m_blank); 29 | assert!(result.is_err(), "should error"); 30 | 31 | m_blank.add(ATTR_UNKNOWN_ATTRIBUTES, &[1, 2, 3]); 32 | let result = attrs.get_from(&m_blank); 33 | assert!(result.is_err(), "should error"); 34 | } 35 | 36 | Ok(()) 37 | } 38 | -------------------------------------------------------------------------------- /rtc-turn/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /.idea/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | -------------------------------------------------------------------------------- /rtc-turn/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # rtc-turn changelog 2 | 3 | ## Unreleased 4 | -------------------------------------------------------------------------------- /rtc-turn/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtc-turn" 3 | version = "0.1.0" 4 | authors = ["Rain Liu "] 5 | edition = "2021" 6 | description = "RTC TURN in Rust" 7 | license = "MIT/Apache-2.0" 8 | documentation = "https://docs.rs/rtc-turn" 9 | homepage = "https://ortc.rs" 10 | repository = "https://github.com/webrtc-rs/rtc/tree/master/rtc-turn" 11 | 12 | [dependencies] 13 | shared = { version = "0.2.0", path = "../rtc-shared", package = "rtc-shared", default-features = false, features = [] } 14 | stun = { version = "0.2.0", path = "../rtc-stun", package = "rtc-stun" } 15 | 16 | bytes = "1.5.0" 17 | log = "0.4.21" 18 | 19 | [dev-dependencies] 20 | env_logger = "0.11.3" 21 | chrono = "0.4.35" 22 | hex = "0.4.3" 23 | clap = { version = "4.5.3", features = ["derive"] } 24 | criterion = "0.5.1" 25 | crossbeam-channel = "0.5" 26 | ctrlc = "3.4" 27 | 28 | [features] 29 | metrics = [] 30 | 31 | [[bench]] 32 | name = "bench" 33 | harness = false 34 | 35 | [[example]] 36 | name = "turn_client_udp" 37 | path = "examples/turn_client_udp.rs" 38 | bench = false 39 | -------------------------------------------------------------------------------- /rtc-turn/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 WebRTC.rs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rtc-turn/README.md: -------------------------------------------------------------------------------- 1 |

2 | WebRTC.rs 3 |
4 |

5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | License: MIT/Apache 2.0 23 | 24 | 25 | Discord 26 | 27 |

28 |

29 | RTC TURN Protocol in Rust With Sans-IO 30 |

31 | -------------------------------------------------------------------------------- /rtc-turn/codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | max_report_age: off 4 | token: 640e45ed-ce83-43e1-9eee-473aa65dc136 5 | 6 | coverage: 7 | precision: 2 8 | round: down 9 | range: 50..90 10 | status: 11 | project: 12 | default: 13 | enabled: no 14 | threshold: 0.2 15 | if_not_found: success 16 | patch: 17 | default: 18 | enabled: no 19 | if_not_found: success 20 | changes: 21 | default: 22 | enabled: no 23 | if_not_found: success 24 | -------------------------------------------------------------------------------- /rtc-turn/src/client/permission.rs: -------------------------------------------------------------------------------- 1 | #[derive(Default, Copy, Clone, PartialEq, Debug)] 2 | pub(crate) enum PermState { 3 | #[default] 4 | Idle = 0, 5 | Permitted = 1, 6 | } 7 | 8 | impl From for PermState { 9 | fn from(v: u8) -> Self { 10 | match v { 11 | 0 => PermState::Idle, 12 | _ => PermState::Permitted, 13 | } 14 | } 15 | } 16 | 17 | #[derive(Default)] 18 | pub(crate) struct Permission { 19 | st: PermState, 20 | } 21 | 22 | impl Permission { 23 | pub(crate) fn set_state(&mut self, state: PermState) { 24 | self.st = state; 25 | } 26 | 27 | pub(crate) fn state(&self) -> PermState { 28 | self.st 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /rtc-turn/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms)] 2 | #![allow(dead_code)] 3 | 4 | pub mod client; 5 | pub mod proto; 6 | -------------------------------------------------------------------------------- /rtc-turn/src/proto/addr.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod addr_test; 3 | 4 | use std::net::{IpAddr, Ipv4Addr, SocketAddr}; 5 | 6 | use super::*; 7 | 8 | // Addr is ip:port. 9 | #[derive(PartialEq, Eq, Debug)] 10 | pub struct Addr { 11 | ip: IpAddr, 12 | port: u16, 13 | } 14 | 15 | impl Default for Addr { 16 | fn default() -> Self { 17 | Addr { 18 | ip: IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 19 | port: 0, 20 | } 21 | } 22 | } 23 | 24 | impl fmt::Display for Addr { 25 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 26 | write!(f, "{}:{}", self.ip, self.port) 27 | } 28 | } 29 | 30 | impl Addr { 31 | // Network implements net.Addr. 32 | pub fn network(&self) -> String { 33 | "turn".to_owned() 34 | } 35 | 36 | // sets addr. 37 | pub fn from_socket_addr(n: &SocketAddr) -> Self { 38 | let ip = n.ip(); 39 | let port = n.port(); 40 | 41 | Addr { ip, port } 42 | } 43 | 44 | // EqualIP returns true if a and b have equal IP addresses. 45 | pub fn equal_ip(&self, other: &Addr) -> bool { 46 | self.ip == other.ip 47 | } 48 | } 49 | 50 | // FiveTuple represents 5-TUPLE value. 51 | #[derive(PartialEq, Eq, Default)] 52 | pub struct FiveTuple { 53 | pub client: Addr, 54 | pub server: Addr, 55 | pub proto: Protocol, 56 | } 57 | 58 | impl fmt::Display for FiveTuple { 59 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 60 | write!(f, "{}->{} ({})", self.client, self.server, self.proto) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /rtc-turn/src/proto/data.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod data_test; 3 | 4 | use stun::attributes::*; 5 | use stun::message::*; 6 | 7 | use shared::error::Result; 8 | 9 | // Data represents DATA attribute. 10 | // 11 | // The DATA attribute is present in all Send and Data indications. The 12 | // value portion of this attribute is variable length and consists of 13 | // the application data (that is, the data that would immediately follow 14 | // the UDP header if the data was been sent directly between the client 15 | // and the peer). 16 | // 17 | // RFC 5766 Section 14.4 18 | #[derive(Default, Debug, PartialEq, Eq)] 19 | pub struct Data(pub Vec); 20 | 21 | impl Setter for Data { 22 | // AddTo adds DATA to message. 23 | fn add_to(&self, m: &mut Message) -> Result<()> { 24 | m.add(ATTR_DATA, &self.0); 25 | Ok(()) 26 | } 27 | } 28 | 29 | impl Getter for Data { 30 | // GetFrom decodes DATA from message. 31 | fn get_from(&mut self, m: &Message) -> Result<()> { 32 | self.0 = m.get(ATTR_DATA)?; 33 | Ok(()) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /rtc-turn/src/proto/data/data_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use shared::error::Error; 3 | 4 | #[test] 5 | fn test_data_add_to() -> Result<()> { 6 | let mut m = Message::new(); 7 | let d = Data(vec![1, 2, 33, 44, 0x13, 0xaf]); 8 | d.add_to(&mut m)?; 9 | m.write_header(); 10 | 11 | //"GetFrom" 12 | { 13 | let mut decoded = Message::new(); 14 | decoded.write(&m.raw)?; 15 | 16 | let mut data_decoded = Data::default(); 17 | data_decoded.get_from(&decoded)?; 18 | assert_eq!(data_decoded, d); 19 | 20 | //"HandleErr" 21 | { 22 | let m = Message::new(); 23 | let mut handle = Data::default(); 24 | if let Err(err) = handle.get_from(&m) { 25 | assert_eq!( 26 | Error::ErrAttributeNotFound, 27 | err, 28 | "{err} should be not found" 29 | ); 30 | } 31 | } 32 | } 33 | Ok(()) 34 | } 35 | -------------------------------------------------------------------------------- /rtc-turn/src/proto/dontfrag.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod dontfrag_test; 3 | 4 | use shared::error::Result; 5 | use stun::attributes::*; 6 | use stun::message::*; 7 | 8 | // DontFragmentAttr represents DONT-FRAGMENT attribute. 9 | #[derive(Debug, Default, PartialEq, Eq)] 10 | pub struct DontFragmentAttr; 11 | 12 | impl Setter for DontFragmentAttr { 13 | // AddTo adds DONT-FRAGMENT attribute to message. 14 | fn add_to(&self, m: &mut Message) -> Result<()> { 15 | m.add(ATTR_DONT_FRAGMENT, &[]); 16 | Ok(()) 17 | } 18 | } 19 | 20 | impl Getter for DontFragmentAttr { 21 | // get_from returns true if DONT-FRAGMENT attribute is set. 22 | fn get_from(&mut self, m: &Message) -> Result<()> { 23 | let _ = m.get(ATTR_DONT_FRAGMENT)?; 24 | Ok(()) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /rtc-turn/src/proto/dontfrag/dontfrag_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn test_dont_fragment_false() -> Result<()> { 5 | let mut dont_fragment = DontFragmentAttr::default(); 6 | 7 | let mut m = Message::new(); 8 | m.write_header(); 9 | assert!(dont_fragment.get_from(&m).is_err(), "should not be set"); 10 | 11 | Ok(()) 12 | } 13 | 14 | #[test] 15 | fn test_dont_fragment_add_to() -> Result<()> { 16 | let mut dont_fragment = DontFragmentAttr::default(); 17 | 18 | let mut m = Message::new(); 19 | dont_fragment.add_to(&mut m)?; 20 | m.write_header(); 21 | 22 | let mut decoded = Message::new(); 23 | decoded.write(&m.raw)?; 24 | assert!(dont_fragment.get_from(&m).is_ok(), "should be set"); 25 | 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /rtc-turn/src/proto/evenport.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod evenport_test; 3 | 4 | use std::fmt; 5 | 6 | use shared::error::Result; 7 | use stun::attributes::*; 8 | use stun::checks::*; 9 | use stun::message::*; 10 | 11 | // EvenPort represents EVEN-PORT attribute. 12 | // 13 | // This attribute allows the client to request that the port in the 14 | // relayed transport address be even, and (optionally) that the server 15 | // reserve the next-higher port number. 16 | // 17 | // RFC 5766 Section 14.6 18 | #[derive(Default, Debug, PartialEq, Eq)] 19 | pub struct EvenPort { 20 | // reserve_port means that the server is requested to reserve 21 | // the next-higher port number (on the same IP address) 22 | // for a subsequent allocation. 23 | reserve_port: bool, 24 | } 25 | 26 | impl fmt::Display for EvenPort { 27 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 28 | if self.reserve_port { 29 | write!(f, "reserve: true") 30 | } else { 31 | write!(f, "reserve: false") 32 | } 33 | } 34 | } 35 | 36 | const EVEN_PORT_SIZE: usize = 1; 37 | const FIRST_BIT_SET: u8 = 0b10000000; 38 | 39 | impl Setter for EvenPort { 40 | // AddTo adds EVEN-PORT to message. 41 | fn add_to(&self, m: &mut Message) -> Result<()> { 42 | let mut v = vec![0; EVEN_PORT_SIZE]; 43 | if self.reserve_port { 44 | // Set first bit to 1. 45 | v[0] = FIRST_BIT_SET; 46 | } 47 | m.add(ATTR_EVEN_PORT, &v); 48 | Ok(()) 49 | } 50 | } 51 | 52 | impl Getter for EvenPort { 53 | // GetFrom decodes EVEN-PORT from message. 54 | fn get_from(&mut self, m: &Message) -> Result<()> { 55 | let v = m.get(ATTR_EVEN_PORT)?; 56 | 57 | check_size(ATTR_EVEN_PORT, v.len(), EVEN_PORT_SIZE)?; 58 | 59 | if v[0] & FIRST_BIT_SET > 0 { 60 | self.reserve_port = true; 61 | } 62 | Ok(()) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /rtc-turn/src/proto/lifetime.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod lifetime_test; 3 | 4 | use std::fmt; 5 | use std::time::Duration; 6 | 7 | use shared::error::Result; 8 | use stun::attributes::*; 9 | use stun::checks::*; 10 | use stun::message::*; 11 | 12 | // DEFAULT_LIFETIME in RFC 5766 is 10 minutes. 13 | // 14 | // RFC 5766 Section 2.2 15 | pub const DEFAULT_LIFETIME: Duration = Duration::from_secs(10 * 60); 16 | 17 | // Lifetime represents LIFETIME attribute. 18 | // 19 | // The LIFETIME attribute represents the duration for which the server 20 | // will maintain an allocation in the absence of a refresh. The value 21 | // portion of this attribute is 4-bytes long and consists of a 32-bit 22 | // unsigned integral value representing the number of seconds remaining 23 | // until expiration. 24 | // 25 | // RFC 5766 Section 14.2 26 | #[derive(Default, Debug, PartialEq, Eq)] 27 | pub struct Lifetime(pub Duration); 28 | 29 | impl fmt::Display for Lifetime { 30 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 31 | write!(f, "{}s", self.0.as_secs()) 32 | } 33 | } 34 | 35 | // uint32 seconds 36 | const LIFETIME_SIZE: usize = 4; // 4 bytes, 32 bits 37 | 38 | impl Setter for Lifetime { 39 | // AddTo adds LIFETIME to message. 40 | fn add_to(&self, m: &mut Message) -> Result<()> { 41 | let mut v = vec![0; LIFETIME_SIZE]; 42 | v.copy_from_slice(&(self.0.as_secs() as u32).to_be_bytes()); 43 | m.add(ATTR_LIFETIME, &v); 44 | Ok(()) 45 | } 46 | } 47 | 48 | impl Getter for Lifetime { 49 | // GetFrom decodes LIFETIME from message. 50 | fn get_from(&mut self, m: &Message) -> Result<()> { 51 | let v = m.get(ATTR_LIFETIME)?; 52 | 53 | check_size(ATTR_LIFETIME, v.len(), LIFETIME_SIZE)?; 54 | 55 | let seconds = u32::from_be_bytes([v[0], v[1], v[2], v[3]]); 56 | self.0 = Duration::from_secs(seconds as u64); 57 | 58 | Ok(()) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /rtc-turn/src/proto/lifetime/lifetime_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use shared::error::Error; 3 | 4 | #[test] 5 | fn test_lifetime_string() -> Result<()> { 6 | let l = Lifetime(Duration::from_secs(10)); 7 | assert_eq!(l.to_string(), "10s", "bad string {l}, expected 10s"); 8 | 9 | Ok(()) 10 | } 11 | 12 | #[test] 13 | fn test_lifetime_add_to() -> Result<()> { 14 | let mut m = Message::new(); 15 | let l = Lifetime(Duration::from_secs(10)); 16 | l.add_to(&mut m)?; 17 | m.write_header(); 18 | 19 | //"GetFrom" 20 | { 21 | let mut decoded = Message::new(); 22 | decoded.write(&m.raw)?; 23 | 24 | let mut life = Lifetime::default(); 25 | life.get_from(&decoded)?; 26 | assert_eq!(life, l, "Decoded {life}, expected {l}"); 27 | 28 | //"HandleErr" 29 | { 30 | let mut m = Message::new(); 31 | let mut n_handle = Lifetime::default(); 32 | if let Err(err) = n_handle.get_from(&m) { 33 | assert_eq!( 34 | Error::ErrAttributeNotFound, 35 | err, 36 | "{err} should be not found" 37 | ); 38 | } else { 39 | panic!("expected error, but got ok"); 40 | } 41 | m.add(ATTR_LIFETIME, &[1, 2, 3]); 42 | 43 | if let Err(err) = n_handle.get_from(&m) { 44 | assert!( 45 | is_attr_size_invalid(&err), 46 | "IsAttrSizeInvalid should be true" 47 | ); 48 | } else { 49 | panic!("expected error, but got ok"); 50 | } 51 | } 52 | } 53 | 54 | Ok(()) 55 | } 56 | -------------------------------------------------------------------------------- /rtc-turn/src/proto/peeraddr/peeraddr_test.rs: -------------------------------------------------------------------------------- 1 | use std::net::Ipv4Addr; 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn test_peer_address() -> Result<()> { 7 | // Simple tests because already tested in stun. 8 | let a = PeerAddress { 9 | ip: IpAddr::V4(Ipv4Addr::new(111, 11, 1, 2)), 10 | port: 333, 11 | }; 12 | 13 | assert_eq!(a.to_string(), "111.11.1.2:333", "invalid string"); 14 | 15 | let mut m = Message::new(); 16 | a.add_to(&mut m)?; 17 | m.write_header(); 18 | 19 | let mut decoded = Message::new(); 20 | decoded.write(&m.raw)?; 21 | 22 | let mut a_got = PeerAddress::default(); 23 | a_got.get_from(&decoded)?; 24 | 25 | Ok(()) 26 | } 27 | -------------------------------------------------------------------------------- /rtc-turn/src/proto/proto_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use shared::error::*; 3 | 4 | const CHROME_ALLOC_REQ_TEST_HEX: [&str; 4] = [ 5 | "000300242112a442626b4a6849664c3630526863802f0016687474703a2f2f6c6f63616c686f73743a333030302f00000019000411000000", 6 | "011300582112a442626b4a6849664c36305268630009001000000401556e617574686f72697a656400150010356130323039623563623830363130360014000b61312e63796465762e7275758022001a436f7475726e2d342e352e302e33202764616e204569646572272300", 7 | "0003006c2112a442324e50695a437a4634535034802f0016687474703a2f2f6c6f63616c686f73743a333030302f000000190004110000000006000665726e61646f00000014000b61312e63796465762e7275000015001035613032303962356362383036313036000800145c8743f3b64bec0880cdd8d476d37b801a6c3d33", 8 | "010300582112a442324e50695a437a4634535034001600080001fb922b1ab211002000080001adb2f49f38ae000d0004000002588022001a436f7475726e2d342e352e302e33202764616e204569646572277475000800145d7e85b767a519ffce91dbf0a96775e370db92e3", 9 | ]; 10 | 11 | #[test] 12 | fn test_chrome_alloc_request() -> Result<()> { 13 | let mut data = vec![]; 14 | let mut messages = vec![]; 15 | 16 | // Decoding hex data into binary. 17 | for h in &CHROME_ALLOC_REQ_TEST_HEX { 18 | let b = match hex::decode(h) { 19 | Ok(b) => b, 20 | Err(_) => return Err(Error::Other("hex decode error".to_owned())), 21 | }; 22 | data.push(b); 23 | } 24 | 25 | // All hex streams decoded to raw binary format and stored in data slice. 26 | // Decoding packets to messages. 27 | for packet in data { 28 | let mut m = Message::new(); 29 | m.write(&packet)?; 30 | messages.push(m); 31 | } 32 | assert_eq!(messages.len(), 4, "unexpected message slice list"); 33 | 34 | Ok(()) 35 | } 36 | -------------------------------------------------------------------------------- /rtc-turn/src/proto/relayaddr/relayaddr_test.rs: -------------------------------------------------------------------------------- 1 | use std::net::Ipv4Addr; 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn test_relayed_address() -> Result<()> { 7 | // Simple tests because already tested in stun. 8 | let a = RelayedAddress { 9 | ip: IpAddr::V4(Ipv4Addr::new(111, 11, 1, 2)), 10 | port: 333, 11 | }; 12 | 13 | assert_eq!(a.to_string(), "111.11.1.2:333", "invalid string"); 14 | 15 | let mut m = Message::new(); 16 | a.add_to(&mut m)?; 17 | m.write_header(); 18 | 19 | let mut decoded = Message::new(); 20 | decoded.write(&m.raw)?; 21 | 22 | let mut a_got = RelayedAddress::default(); 23 | a_got.get_from(&decoded)?; 24 | 25 | Ok(()) 26 | } 27 | -------------------------------------------------------------------------------- /rtc-turn/src/proto/reqtrans.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod reqtrans_test; 3 | 4 | use std::fmt; 5 | 6 | use stun::attributes::*; 7 | use stun::checks::*; 8 | use stun::message::*; 9 | 10 | use super::*; 11 | use shared::error::Result; 12 | 13 | // RequestedTransport represents REQUESTED-TRANSPORT attribute. 14 | // 15 | // This attribute is used by the client to request a specific transport 16 | // protocol for the allocated transport address. RFC 5766 only allows the use of 17 | // codepoint 17 (User Datagram protocol). 18 | // 19 | // RFC 5766 Section 14.7 20 | #[derive(Default, Debug, PartialEq, Eq)] 21 | pub struct RequestedTransport { 22 | pub protocol: Protocol, 23 | } 24 | 25 | impl fmt::Display for RequestedTransport { 26 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 27 | write!(f, "protocol: {}", self.protocol) 28 | } 29 | } 30 | 31 | const REQUESTED_TRANSPORT_SIZE: usize = 4; 32 | 33 | impl Setter for RequestedTransport { 34 | // AddTo adds REQUESTED-TRANSPORT to message. 35 | fn add_to(&self, m: &mut Message) -> Result<()> { 36 | let mut v = vec![0; REQUESTED_TRANSPORT_SIZE]; 37 | v[0] = self.protocol.0; 38 | // b[1:4] is RFFU = 0. 39 | // The RFFU field MUST be set to zero on transmission and MUST be 40 | // ignored on reception. It is reserved for future uses. 41 | m.add(ATTR_REQUESTED_TRANSPORT, &v); 42 | Ok(()) 43 | } 44 | } 45 | 46 | impl Getter for RequestedTransport { 47 | // GetFrom decodes REQUESTED-TRANSPORT from message. 48 | fn get_from(&mut self, m: &Message) -> Result<()> { 49 | let v = m.get(ATTR_REQUESTED_TRANSPORT)?; 50 | 51 | check_size(ATTR_REQUESTED_TRANSPORT, v.len(), REQUESTED_TRANSPORT_SIZE)?; 52 | self.protocol = Protocol(v[0]); 53 | Ok(()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /rtc-turn/src/proto/rsrvtoken.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod rsrvtoken_test; 3 | 4 | use shared::error::Result; 5 | use stun::attributes::*; 6 | use stun::checks::*; 7 | use stun::message::*; 8 | 9 | // ReservationToken represents RESERVATION-TOKEN attribute. 10 | // 11 | // The RESERVATION-TOKEN attribute contains a token that uniquely 12 | // identifies a relayed transport address being held in reserve by the 13 | // server. The server includes this attribute in a success response to 14 | // tell the client about the token, and the client includes this 15 | // attribute in a subsequent Allocate request to request the server use 16 | // that relayed transport address for the allocation. 17 | // 18 | // RFC 5766 Section 14.9 19 | #[derive(Debug, Default, PartialEq, Eq)] 20 | pub struct ReservationToken(pub Vec); 21 | 22 | const RESERVATION_TOKEN_SIZE: usize = 8; // 8 bytes 23 | 24 | impl Setter for ReservationToken { 25 | // AddTo adds RESERVATION-TOKEN to message. 26 | fn add_to(&self, m: &mut Message) -> Result<()> { 27 | check_size(ATTR_RESERVATION_TOKEN, self.0.len(), RESERVATION_TOKEN_SIZE)?; 28 | m.add(ATTR_RESERVATION_TOKEN, &self.0); 29 | Ok(()) 30 | } 31 | } 32 | 33 | impl Getter for ReservationToken { 34 | // GetFrom decodes RESERVATION-TOKEN from message. 35 | fn get_from(&mut self, m: &Message) -> Result<()> { 36 | let v = m.get(ATTR_RESERVATION_TOKEN)?; 37 | check_size(ATTR_RESERVATION_TOKEN, v.len(), RESERVATION_TOKEN_SIZE)?; 38 | self.0 = v; 39 | Ok(()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /rtc-turn/src/proto/rsrvtoken/rsrvtoken_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use shared::error::Error; 3 | 4 | #[test] 5 | fn test_reservation_token() -> Result<()> { 6 | let mut m = Message::new(); 7 | let mut v = vec![0; 8]; 8 | v[2] = 33; 9 | v[7] = 1; 10 | let tk = ReservationToken(v); 11 | tk.add_to(&mut m)?; 12 | m.write_header(); 13 | 14 | //"HandleErr" 15 | { 16 | let bad_tk = ReservationToken(vec![34, 45]); 17 | if let Err(err) = bad_tk.add_to(&mut m) { 18 | assert!( 19 | is_attr_size_invalid(&err), 20 | "IsAttrSizeInvalid should be true" 21 | ); 22 | } else { 23 | panic!("expected error, but got ok"); 24 | } 25 | } 26 | 27 | //"GetFrom" 28 | { 29 | let mut decoded = Message::new(); 30 | decoded.write(&m.raw)?; 31 | let mut tok = ReservationToken::default(); 32 | tok.get_from(&decoded)?; 33 | assert_eq!(tok, tk, "Decoded {tok:?}, expected {tk:?}"); 34 | 35 | //"HandleErr" 36 | { 37 | let mut m = Message::new(); 38 | let mut handle = ReservationToken::default(); 39 | if let Err(err) = handle.get_from(&m) { 40 | assert_eq!( 41 | Error::ErrAttributeNotFound, 42 | err, 43 | "{err} should be not found" 44 | ); 45 | } else { 46 | panic!("expected error, but got ok"); 47 | } 48 | m.add(ATTR_RESERVATION_TOKEN, &[1, 2, 3]); 49 | if let Err(err) = handle.get_from(&m) { 50 | assert!( 51 | is_attr_size_invalid(&err), 52 | "IsAttrSizeInvalid should be true" 53 | ); 54 | } else { 55 | panic!("expected error, got ok"); 56 | } 57 | } 58 | } 59 | 60 | Ok(()) 61 | } 62 | -------------------------------------------------------------------------------- /rtc/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # rtc changelog 2 | 3 | ## Unreleased 4 | -------------------------------------------------------------------------------- /rtc/src/api/api_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn test_new_api() -> Result<()> { 5 | let mut s = SettingEngine::default(); 6 | s.detach_data_channels(); 7 | let mut m = MediaEngine::default(); 8 | m.register_default_codecs()?; 9 | 10 | let api = APIBuilder::new() 11 | .with_setting_engine(s) 12 | .with_media_engine(m) 13 | .build(); 14 | 15 | assert!( 16 | api.setting_engine.detach.data_channels, 17 | "Failed to set settings engine" 18 | ); 19 | assert!( 20 | !api.media_engine.audio_codecs.is_empty(), 21 | "Failed to set media engine" 22 | ); 23 | 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /rtc/src/constants.rs: -------------------------------------------------------------------------------- 1 | pub(crate) const UNSPECIFIED_STR: &str = "Unspecified"; 2 | 3 | /// Equal to UDP MTU 4 | pub(crate) const RECEIVE_MTU: usize = 1460; 5 | 6 | pub(crate) const SDP_ATTRIBUTE_RID: &str = "rid"; 7 | pub(crate) const SDP_ATTRIBUTE_SIMULCAST: &str = "simulcast"; 8 | pub(crate) const GENERATED_CERTIFICATE_ORIGIN: &str = "WebRTC"; 9 | pub(crate) const DEFAULT_SESSION_SRTP_REPLAY_PROTECTION_WINDOW: usize = 64; 10 | pub(crate) const DEFAULT_SESSION_SRTCP_REPLAY_PROTECTION_WINDOW: usize = 64; 11 | pub(crate) const DEFAULT_DTLS_REPLAY_PROTECTION_WINDOW: usize = 64; 12 | -------------------------------------------------------------------------------- /rtc/src/data_channel/data_channel_init.rs: -------------------------------------------------------------------------------- 1 | /// DataChannelConfig can be used to configure properties of the underlying 2 | /// channel such as data reliability. 3 | #[derive(Default, Debug, Clone)] 4 | pub struct RTCDataChannelInit { 5 | /// ordered indicates if data is allowed to be delivered out of order. The 6 | /// default value of true, guarantees that data will be delivered in order. 7 | pub ordered: Option, 8 | 9 | /// max_packet_life_time limits the time (in milliseconds) during which the 10 | /// channel will transmit or retransmit data if not acknowledged. This value 11 | /// may be clamped if it exceeds the maximum value supported. 12 | pub max_packet_life_time: Option, 13 | 14 | /// max_retransmits limits the number of times a channel will retransmit data 15 | /// if not successfully delivered. This value may be clamped if it exceeds 16 | /// the maximum value supported. 17 | pub max_retransmits: Option, 18 | 19 | /// protocol describes the subprotocol name used for this channel. 20 | pub protocol: Option, 21 | 22 | /// negotiated describes if the data channel is created by the local peer or 23 | /// the remote peer. The default value of None tells the user agent to 24 | /// announce the channel in-band and instruct the other peer to dispatch a 25 | /// corresponding DataChannel. If set to Some(id), it is up to the application 26 | /// to negotiate the channel and create an DataChannel with the same id 27 | /// at the other peer. 28 | pub negotiated: Option, 29 | } 30 | -------------------------------------------------------------------------------- /rtc/src/data_channel/data_channel_message.rs: -------------------------------------------------------------------------------- 1 | use bytes::Bytes; 2 | 3 | /// DataChannelMessage represents a message received from the 4 | /// data channel. IsString will be set to true if the incoming 5 | /// message is of the string type. Otherwise the message is of 6 | /// a binary type. 7 | #[derive(Default, Debug, Clone)] 8 | pub struct DataChannelMessage { 9 | pub is_string: bool, 10 | pub data: Bytes, 11 | } 12 | -------------------------------------------------------------------------------- /rtc/src/data_channel/data_channel_parameters.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// DataChannelParameters describes the configuration of the DataChannel. 4 | #[derive(Default, Debug, Clone, Serialize, Deserialize)] 5 | pub struct DataChannelParameters { 6 | pub label: String, 7 | pub protocol: String, 8 | pub ordered: bool, 9 | pub max_packet_life_time: u16, 10 | pub max_retransmits: u16, 11 | pub negotiated: Option, 12 | } 13 | -------------------------------------------------------------------------------- /rtc/src/handler/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod demuxer; 2 | pub mod dtls; 3 | pub mod ice; 4 | pub mod sctp; 5 | -------------------------------------------------------------------------------- /rtc/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms)] 2 | #![allow(dead_code)] 3 | 4 | /* 5 | #[macro_use] 6 | extern crate lazy_static; 7 | */ 8 | pub mod api; 9 | pub mod constants; 10 | pub mod data_channel; 11 | pub mod handler; 12 | pub mod messages; 13 | pub mod peer_connection; 14 | pub mod rtp_transceiver; 15 | pub mod stats; 16 | pub mod transport; 17 | 18 | pub mod track; 19 | 20 | // re-export sub-crates 21 | //pub use {data, dtls, ice, interceptor, mdns, media, rtcp, rtp, sctp, sdp, srtp, stun, turn, util}; 22 | -------------------------------------------------------------------------------- /rtc/src/peer_connection/offer_answer_options.rs: -------------------------------------------------------------------------------- 1 | /// AnswerOptions structure describes the options used to control the answer 2 | /// creation process. 3 | #[derive(Default, Debug, PartialEq, Eq, Copy, Clone)] 4 | pub struct RTCAnswerOptions { 5 | /// voice_activity_detection allows the application to provide information 6 | /// about whether it wishes voice detection feature to be enabled or disabled. 7 | pub voice_activity_detection: bool, 8 | } 9 | 10 | /// OfferOptions structure describes the options used to control the offer 11 | /// creation process 12 | #[derive(Default, Debug, PartialEq, Eq, Copy, Clone)] 13 | pub struct RTCOfferOptions { 14 | /// voice_activity_detection allows the application to provide information 15 | /// about whether it wishes voice detection feature to be enabled or disabled. 16 | pub voice_activity_detection: bool, 17 | 18 | /// ice_restart forces the underlying ice gathering process to be restarted. 19 | /// When this value is true, the generated description will have ICE 20 | /// credentials that are different from the current credentials 21 | pub ice_restart: bool, 22 | } 23 | -------------------------------------------------------------------------------- /rtc/src/peer_connection/operation/operation_test.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::Mutex; 2 | 3 | use super::*; 4 | use crate::error::Result; 5 | 6 | #[tokio::test] 7 | async fn test_operations_enqueue() -> Result<()> { 8 | let ops = Operations::new(); 9 | for _ in 0..100 { 10 | let results = Arc::new(Mutex::new(vec![0; 16])); 11 | for k in 0..16 { 12 | let r = Arc::clone(&results); 13 | ops.enqueue(Operation::new( 14 | move || { 15 | let r2 = Arc::clone(&r); 16 | Box::pin(async move { 17 | let mut r3 = r2.lock().await; 18 | r3[k] += k * k; 19 | r3[k] == 225 20 | }) 21 | }, 22 | "test_operations_enqueue", 23 | )) 24 | .await?; 25 | } 26 | 27 | ops.done().await; 28 | let expected = vec![ 29 | 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 450, 30 | ]; 31 | { 32 | let r = results.lock().await; 33 | assert_eq!(r.len(), expected.len()); 34 | assert_eq!(&*r, &expected); 35 | } 36 | } 37 | 38 | Ok(()) 39 | } 40 | 41 | #[tokio::test] 42 | async fn test_operations_done() -> Result<()> { 43 | let ops = Operations::new(); 44 | ops.done().await; 45 | 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /rtc/src/peer_connection/policy/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bundle_policy; 2 | pub mod ice_transport_policy; 3 | pub mod rtcp_mux_policy; 4 | pub mod sdp_semantics; 5 | -------------------------------------------------------------------------------- /rtc/src/rtp_transceiver/fmtp/generic/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod generic_test; 3 | 4 | use super::*; 5 | 6 | /// fmtp_consist checks that two FMTP parameters are not inconsistent. 7 | fn fmtp_consist(a: &HashMap, b: &HashMap) -> bool { 8 | //TODO: add unicode case-folding equal support 9 | for (k, v) in a { 10 | if let Some(vb) = b.get(k) { 11 | if vb.to_uppercase() != v.to_uppercase() { 12 | return false; 13 | } 14 | } 15 | } 16 | for (k, v) in b { 17 | if let Some(va) = a.get(k) { 18 | if va.to_uppercase() != v.to_uppercase() { 19 | return false; 20 | } 21 | } 22 | } 23 | true 24 | } 25 | 26 | #[derive(Debug, PartialEq)] 27 | pub(crate) struct GenericFmtp { 28 | pub(crate) mime_type: String, 29 | pub(crate) parameters: HashMap, 30 | } 31 | 32 | impl Fmtp for GenericFmtp { 33 | fn mime_type(&self) -> &str { 34 | self.mime_type.as_str() 35 | } 36 | 37 | /// Match returns true if g and b are compatible fmtp descriptions 38 | /// The generic implementation is used for MimeTypes that are not defined 39 | fn match_fmtp(&self, f: &(dyn Fmtp)) -> bool { 40 | if let Some(c) = f.as_any().downcast_ref::() { 41 | if self.mime_type.to_lowercase() != c.mime_type().to_lowercase() { 42 | return false; 43 | } 44 | 45 | fmtp_consist(&self.parameters, &c.parameters) 46 | } else { 47 | false 48 | } 49 | } 50 | 51 | fn parameter(&self, key: &str) -> Option<&String> { 52 | self.parameters.get(key) 53 | } 54 | 55 | fn equal(&self, other: &(dyn Fmtp)) -> bool { 56 | other.as_any().downcast_ref::() == Some(self) 57 | } 58 | 59 | fn as_any(&self) -> &(dyn Any) { 60 | self 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /rtc/src/rtp_transceiver/fmtp/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod generic; 2 | pub(crate) mod h264; 3 | 4 | use std::any::Any; 5 | use std::collections::HashMap; 6 | use std::fmt; 7 | 8 | use crate::rtp_transceiver::fmtp::generic::GenericFmtp; 9 | use crate::rtp_transceiver::fmtp::h264::H264Fmtp; 10 | 11 | /// Fmtp interface for implementing custom 12 | /// Fmtp parsers based on mime_type 13 | pub trait Fmtp: fmt::Debug { 14 | /// mime_type returns the mime_type associated with 15 | /// the fmtp 16 | fn mime_type(&self) -> &str; 17 | 18 | /// match_fmtp compares two fmtp descriptions for 19 | /// compatibility based on the mime_type 20 | fn match_fmtp(&self, f: &(dyn Fmtp)) -> bool; 21 | 22 | /// parameter returns a value for the associated key 23 | /// if contained in the parsed fmtp string 24 | fn parameter(&self, key: &str) -> Option<&String>; 25 | 26 | fn equal(&self, other: &(dyn Fmtp)) -> bool; 27 | fn as_any(&self) -> &(dyn Any); 28 | } 29 | 30 | impl PartialEq for dyn Fmtp { 31 | fn eq(&self, other: &Self) -> bool { 32 | self.equal(other) 33 | } 34 | } 35 | 36 | /// parse parses an fmtp string based on the MimeType 37 | pub fn parse(mime_type: &str, line: &str) -> Box { 38 | let mut parameters = HashMap::new(); 39 | for p in line.split(';').collect::>() { 40 | let pp: Vec<&str> = p.trim().splitn(2, '=').collect(); 41 | let key = pp[0].to_lowercase(); 42 | let value = if pp.len() > 1 { 43 | pp[1].to_owned() 44 | } else { 45 | String::new() 46 | }; 47 | parameters.insert(key, value); 48 | } 49 | 50 | if mime_type.to_uppercase() == "video/h264".to_uppercase() { 51 | Box::new(H264Fmtp { parameters }) 52 | } else { 53 | Box::new(GenericFmtp { 54 | mime_type: mime_type.to_owned(), 55 | parameters, 56 | }) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /rtc/src/stats/serialize.rs: -------------------------------------------------------------------------------- 1 | pub mod instant_to_epoch_seconds { 2 | // Serializes a `tokio::time::Instant` to an approximation of epoch time in the form 3 | // of an `f64` where the integer portion is seconds and the decimal portion is milliseconds. 4 | // For instance, `Monday, May 30, 2022 10:45:26.456 PM UTC` converts to `1653950726.456`. 5 | // 6 | // Note that an `Instant` is not connected to real world time, so this conversion is 7 | // approximate. 8 | use std::time::{SystemTime, UNIX_EPOCH}; 9 | 10 | use serde::{Serialize, Serializer}; 11 | use std::time::Instant; 12 | 13 | pub fn serialize(instant: &Instant, serializer: S) -> Result 14 | where 15 | S: Serializer, 16 | { 17 | let system_now = SystemTime::now(); 18 | let instant_now = Instant::now(); 19 | let approx = system_now - (instant_now - *instant); 20 | let epoch = approx 21 | .duration_since(UNIX_EPOCH) 22 | .expect("Time went backwards"); 23 | 24 | let epoch_ms = epoch.as_millis() as f64 / 1000.0; 25 | 26 | epoch_ms.serialize(serializer) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /rtc/src/stats/stats_collector.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use super::StatsReportType; 4 | 5 | #[derive(Debug, Default)] 6 | pub struct StatsCollector { 7 | pub(crate) reports: HashMap, 8 | } 9 | 10 | impl StatsCollector { 11 | pub(crate) fn new() -> Self { 12 | StatsCollector { 13 | ..Default::default() 14 | } 15 | } 16 | 17 | pub(crate) fn insert(&mut self, id: String, stats: StatsReportType) { 18 | self.reports.insert(id, stats); 19 | } 20 | 21 | pub(crate) fn merge(&mut self, stats: HashMap) { 22 | self.reports.extend(stats) 23 | } 24 | 25 | pub(crate) fn into_reports(self) -> HashMap { 26 | self.reports 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /rtc/src/track/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod track_local; 2 | /*pub mod track_remote; 3 | 4 | use std::sync::Arc; 5 | 6 | use interceptor::stream_info::StreamInfo; 7 | use interceptor::{RTCPReader, RTPReader}; 8 | use track_remote::*; 9 | */ 10 | pub(crate) const RTP_OUTBOUND_MTU: usize = 1200; 11 | pub(crate) const RTP_PAYLOAD_TYPE_BITMASK: u8 = 0x7F; 12 | /* 13 | #[derive(Clone)] 14 | pub(crate) struct TrackStream { 15 | pub(crate) stream_info: Option, 16 | pub(crate) rtp_read_stream: Option>, 17 | pub(crate) rtp_interceptor: Option>, 18 | pub(crate) rtcp_read_stream: Option>, 19 | pub(crate) rtcp_interceptor: Option>, 20 | } 21 | 22 | /// TrackStreams maintains a mapping of RTP/RTCP streams to a specific track 23 | /// a RTPReceiver may contain multiple streams if we are dealing with Simulcast 24 | #[derive(Clone)] 25 | pub(crate) struct TrackStreams { 26 | pub(crate) track: Arc, 27 | pub(crate) stream: TrackStream, 28 | pub(crate) repair_stream: TrackStream, 29 | } 30 | */ 31 | -------------------------------------------------------------------------------- /rtc/src/transport/dtls_transport/dtls_fingerprint.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use shared::error::{Error, Result}; 3 | 4 | /// DTLSFingerprint specifies the hash function algorithm and certificate 5 | /// fingerprint as described in . 6 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 7 | pub struct RTCDtlsFingerprint { 8 | /// Algorithm specifies one of the the hash function algorithms defined in 9 | /// the 'Hash function Textual Names' registry. 10 | pub algorithm: String, 11 | 12 | /// Value specifies the value of the certificate fingerprint in lowercase 13 | /// hex string as expressed utilizing the syntax of 'fingerprint' in 14 | /// . 15 | pub value: String, 16 | } 17 | 18 | impl TryFrom<&str> for RTCDtlsFingerprint { 19 | type Error = Error; 20 | 21 | fn try_from(value: &str) -> Result { 22 | let fields: Vec<&str> = value.split_whitespace().collect(); 23 | if fields.len() == 2 { 24 | Ok(Self { 25 | algorithm: fields[0].to_string(), 26 | value: fields[1].to_string(), 27 | }) 28 | } else { 29 | Err(Error::Other("invalid fingerprint".to_string())) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rtc/src/transport/dtls_transport/dtls_parameters.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use super::dtls_fingerprint::*; 4 | use super::dtls_role::*; 5 | 6 | /// DTLSParameters holds information relating to DTLS configuration. 7 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] 8 | pub struct DTLSParameters { 9 | pub role: DTLSRole, 10 | pub fingerprints: Vec, 11 | } 12 | -------------------------------------------------------------------------------- /rtc/src/transport/ice_transport/ice_candidate_pair.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::transport::ice_transport::ice_candidate::*; 4 | 5 | /// ICECandidatePair represents an ICE Candidate pair 6 | #[derive(Default, Debug, Clone, PartialEq, Eq)] 7 | pub struct RTCIceCandidatePair { 8 | stats_id: String, 9 | local: RTCIceCandidate, 10 | remote: RTCIceCandidate, 11 | } 12 | 13 | impl fmt::Display for RTCIceCandidatePair { 14 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 15 | write!(f, "(local) {} <-> (remote) {}", self.local, self.remote) 16 | } 17 | } 18 | 19 | impl RTCIceCandidatePair { 20 | fn stats_id(local_id: &str, remote_id: &str) -> String { 21 | format!("{local_id}-{remote_id}") 22 | } 23 | 24 | /// returns an initialized ICECandidatePair 25 | /// for the given pair of ICECandidate instances 26 | pub fn new(local: RTCIceCandidate, remote: RTCIceCandidate) -> Self { 27 | let stats_id = Self::stats_id(&local.stats_id, &remote.stats_id); 28 | RTCIceCandidatePair { 29 | stats_id, 30 | local, 31 | remote, 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rtc/src/transport/ice_transport/ice_parameters.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// ICEParameters includes the ICE username fragment 4 | /// and password and other ICE-related parameters. 5 | #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] 6 | pub struct RTCIceParameters { 7 | pub username_fragment: String, 8 | pub password: String, 9 | pub ice_lite: bool, 10 | } 11 | -------------------------------------------------------------------------------- /rtc/src/transport/sctp_transport/sctp_transport_capabilities.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// SCTPTransportCapabilities indicates the capabilities of the SCTPTransport. 4 | #[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] 5 | pub struct SCTPTransportCapabilities { 6 | pub max_message_size: u32, 7 | } 8 | -------------------------------------------------------------------------------- /rtc/src/transport/sctp_transport/sctp_transport_test.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::AtomicU16; 2 | 3 | use super::*; 4 | 5 | #[tokio::test] 6 | async fn test_generate_data_channel_id() -> Result<()> { 7 | let sctp_transport_with_channels = |ids: &[u16]| -> RTCSctpTransport { 8 | let mut data_channels = vec![]; 9 | for id in ids { 10 | data_channels.push(Arc::new(RTCDataChannel { 11 | id: AtomicU16::new(*id), 12 | ..Default::default() 13 | })); 14 | } 15 | 16 | RTCSctpTransport { 17 | data_channels: Arc::new(Mutex::new(data_channels)), 18 | ..Default::default() 19 | } 20 | }; 21 | 22 | let tests = vec![ 23 | (DTLSRole::Client, sctp_transport_with_channels(&[]), 0), 24 | (DTLSRole::Client, sctp_transport_with_channels(&[1]), 0), 25 | (DTLSRole::Client, sctp_transport_with_channels(&[0]), 2), 26 | (DTLSRole::Client, sctp_transport_with_channels(&[0, 2]), 4), 27 | (DTLSRole::Client, sctp_transport_with_channels(&[0, 4]), 2), 28 | (DTLSRole::Server, sctp_transport_with_channels(&[]), 1), 29 | (DTLSRole::Server, sctp_transport_with_channels(&[0]), 1), 30 | (DTLSRole::Server, sctp_transport_with_channels(&[1]), 3), 31 | (DTLSRole::Server, sctp_transport_with_channels(&[1, 3]), 5), 32 | (DTLSRole::Server, sctp_transport_with_channels(&[1, 5]), 3), 33 | ]; 34 | 35 | for (role, s, expected) in tests { 36 | match s.generate_and_set_data_channel_id(role).await { 37 | Ok(actual) => assert_eq!(actual, expected), 38 | Err(err) => panic!("failed to generate id: {err}"), 39 | }; 40 | } 41 | 42 | Ok(()) 43 | } 44 | --------------------------------------------------------------------------------