├── rustfmt.toml ├── tox_encryptsave ├── tests │ ├── ciphertext │ └── encryptsave_tests.rs └── Cargo.toml ├── tox_core ├── tests │ ├── data │ │ ├── old-profile-no-friends.tox │ │ └── old-profile-with-contacts.tox │ └── state-format-old.rs ├── src │ ├── relay │ │ ├── mod.rs │ │ ├── client │ │ │ ├── mod.rs │ │ │ └── errors.rs │ │ └── server │ │ │ ├── mod.rs │ │ │ └── client.rs │ ├── utils.rs │ ├── dht │ │ ├── mod.rs │ │ ├── precomputed_cache.rs │ │ ├── server │ │ │ └── errors.rs │ │ └── ip_port.rs │ ├── state_format │ │ └── mod.rs │ ├── lib.rs │ ├── io_tokio.rs │ ├── time.rs │ ├── friend_connection │ │ └── errors.rs │ ├── stats.rs │ └── onion │ │ ├── client │ │ └── errors.rs │ │ └── mod.rs └── Cargo.toml ├── tox_packet ├── src │ ├── lib.rs │ ├── messenger │ │ ├── offline.rs │ │ ├── online.rs │ │ ├── conference │ │ │ ├── query.rs │ │ │ ├── peer_leave.rs │ │ │ ├── ping.rs │ │ │ ├── peer_online.rs │ │ │ ├── invite.rs │ │ │ ├── kill_peer.rs │ │ │ ├── freeze_peer.rs │ │ │ ├── invite_response.rs │ │ │ ├── action.rs │ │ │ ├── message.rs │ │ │ ├── title.rs │ │ │ ├── new_peer.rs │ │ │ ├── change_name.rs │ │ │ └── change_title.rs │ │ ├── typing.rs │ │ ├── user_status.rs │ │ ├── file_transfer │ │ │ ├── file_data.rs │ │ │ ├── file_control.rs │ │ │ └── file_send_request.rs │ │ ├── message.rs │ │ ├── action.rs │ │ ├── nickname.rs │ │ └── status_message.rs │ ├── friend_connection │ │ ├── alive.rs │ │ ├── share_relays.rs │ │ ├── mod.rs │ │ └── friend_requests.rs │ ├── packed_node.rs │ ├── relay │ │ ├── pong_response.rs │ │ ├── route_request.rs │ │ ├── connect_notification.rs │ │ ├── oob_receive.rs │ │ ├── ping_request.rs │ │ ├── route_response.rs │ │ ├── disconnect_notification.rs │ │ ├── onion_response.rs │ │ ├── oob_send.rs │ │ ├── connection_id.rs │ │ ├── data.rs │ │ └── onion_request.rs │ ├── dht │ │ ├── lan_discovery.rs │ │ ├── errors.rs │ │ ├── bootstrap_info.rs │ │ └── macros.rs │ └── onion │ │ ├── inner_onion_response.rs │ │ ├── inner_onion_request.rs │ │ ├── onion_response_1.rs │ │ ├── onion_response_3.rs │ │ ├── onion_response_2.rs │ │ └── friend_request.rs └── Cargo.toml ├── .gitignore ├── tox ├── src │ └── lib.rs └── Cargo.toml ├── Cargo.toml ├── tox_node ├── dpkg │ ├── info │ │ └── postinst │ ├── usr.bin.tox-node │ ├── tox-node.service │ └── config.yml ├── Dockerfile ├── Cargo.toml ├── CHANGELOG.md └── src │ └── motd.rs ├── AUTHORS.md ├── .editorconfig ├── .github └── workflows │ ├── commit-msg.yml │ └── rust.yml ├── tox_crypto ├── Cargo.toml └── src │ └── lib.rs ├── tox_binary_io ├── Cargo.toml └── src │ └── crypto.rs ├── COPYING.iOS ├── examples ├── Cargo.toml ├── tcp_server.rs ├── common │ └── mod.rs └── dht_server.rs ├── scripts └── verify-commit-messages.sh └── README.md /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 120 2 | use_try_shorthand = true 3 | -------------------------------------------------------------------------------- /tox_encryptsave/tests/ciphertext: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tox-rs/tox/HEAD/tox_encryptsave/tests/ciphertext -------------------------------------------------------------------------------- /tox_core/tests/data/old-profile-no-friends.tox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tox-rs/tox/HEAD/tox_core/tests/data/old-profile-no-friends.tox -------------------------------------------------------------------------------- /tox_core/tests/data/old-profile-with-contacts.tox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tox-rs/tox/HEAD/tox_core/tests/data/old-profile-with-contacts.tox -------------------------------------------------------------------------------- /tox_packet/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod dht; 2 | pub mod friend_connection; 3 | pub mod ip_port; 4 | pub mod messenger; 5 | pub mod onion; 6 | pub mod packed_node; 7 | pub mod relay; 8 | pub mod toxid; 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Rust 2 | target/ 3 | .cargo/ 4 | 5 | # vim 6 | *.swp 7 | 8 | # git 9 | *.orig 10 | 11 | # other 12 | *.patch 13 | master.zip 14 | kcov/ 15 | 16 | # some scripts 17 | .*.sh 18 | -------------------------------------------------------------------------------- /tox_core/src/relay/mod.rs: -------------------------------------------------------------------------------- 1 | /*! TCP handshake and Packet handling 2 | 3 | */ 4 | 5 | pub mod client; 6 | pub mod codec; 7 | pub mod handshake; 8 | mod links; 9 | pub mod secure; 10 | pub mod server; 11 | -------------------------------------------------------------------------------- /tox/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate rexports all tox crates in tox-rs. 2 | 3 | pub use tox_core as core; 4 | pub use tox_crypto as crypto; 5 | pub use tox_encryptsave as encryptsave; 6 | pub use tox_packet as packet; 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "tox", 4 | "tox_binary_io", 5 | "tox_crypto", 6 | "tox_packet", 7 | "tox_core", 8 | "tox_encryptsave", 9 | "tox_node", 10 | 11 | "examples" 12 | ] 13 | -------------------------------------------------------------------------------- /tox_node/dpkg/info/postinst: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | useradd --home-dir /var/lib/tox-node --create-home --system --shell /sbin/nologin --comment "Account to run Tox's DHT bootstrap daemon" --user-group tox-node 4 | chmod 700 /var/lib/tox-node 5 | -------------------------------------------------------------------------------- /tox_core/src/relay/client/mod.rs: -------------------------------------------------------------------------------- 1 | /*! The implementation of tcp relay client. 2 | */ 3 | 4 | #[allow(clippy::module_inception)] 5 | mod client; 6 | mod connections; 7 | mod errors; 8 | 9 | pub use self::client::*; 10 | pub use self::connections::*; 11 | pub use self::errors::*; 12 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Developers 2 | 3 | (in alphabetical order, with 10 commits or more excluding merges) 4 | 5 | Anonymous (quininer) 6 | Evgeny Kurnevsky (kurnevsky) 7 | Namsoo CHO (NamsooCho) 8 | Roman Proskuryakov (kpp) 9 | Zetok Zalbavar (zetok) 10 | Сухарик (suhr) 11 | -------------------------------------------------------------------------------- /tox_core/src/relay/server/mod.rs: -------------------------------------------------------------------------------- 1 | /*! The implementation of TCP relay server 2 | */ 3 | 4 | mod client; 5 | #[allow(clippy::module_inception)] 6 | mod server; 7 | mod server_ext; 8 | 9 | pub use self::client::Client; 10 | pub use self::server::Server; 11 | pub use self::server_ext::{tcp_run, tcp_run_connection}; 12 | -------------------------------------------------------------------------------- /tox_core/src/utils.rs: -------------------------------------------------------------------------------- 1 | /*! Common utility functions 2 | */ 3 | 4 | use rand::{CryptoRng, Rng}; 5 | 6 | /// Generate non-zero ping_id 7 | pub fn gen_ping_id(rng: &mut R) -> u64 { 8 | let mut ping_id = 0; 9 | while ping_id == 0 { 10 | ping_id = rng.gen(); 11 | } 12 | ping_id 13 | } 14 | -------------------------------------------------------------------------------- /tox_core/src/dht/mod.rs: -------------------------------------------------------------------------------- 1 | /*! The implementaion of dht module functionalities. 2 | */ 3 | 4 | pub mod codec; 5 | pub mod daemon_state; 6 | pub mod dht_friend; 7 | pub mod dht_node; 8 | pub mod forced_ktree; 9 | pub mod ip_port; 10 | pub mod kbucket; 11 | pub mod ktree; 12 | pub mod lan_discovery; 13 | pub mod precomputed_cache; 14 | pub mod request_queue; 15 | pub mod server; 16 | pub mod server_ext; 17 | -------------------------------------------------------------------------------- /tox_node/dpkg/usr.bin.tox-node: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /usr/bin/tox-node { 4 | #include 5 | #include 6 | 7 | /etc/tox-node/config.yml r, 8 | /sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us r, 9 | /usr/bin/tox-node mr, 10 | /var/lib/tox-node/keys r, 11 | owner /proc/*/cgroup r, 12 | owner /proc/*/mountinfo r, 13 | 14 | } 15 | 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.{toml,yml}] 16 | indent_size = 2 17 | 18 | [*.md] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /tox_node/dpkg/tox-node.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=A server application to run tox node written in pure Rust 3 | After=local-fs.target network.target 4 | 5 | [Service] 6 | ExecStart=/usr/bin/tox-node config /etc/tox-node/config.yml 7 | Type=simple 8 | LimitNOFILE=16384 9 | StandardOutput=null 10 | StandardError=null 11 | Restart=always 12 | RestartSec=10 13 | User=tox-node 14 | Group=tox-node 15 | 16 | [Install] 17 | WantedBy=multi-user.target 18 | -------------------------------------------------------------------------------- /.github/workflows/commit-msg.yml: -------------------------------------------------------------------------------- 1 | name: Commit message 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | commit-msg: 7 | name: Check commit messages 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | with: 12 | fetch-depth: 0 13 | - name: Check PR commit messages 14 | run: | 15 | curl -s ${{ github.event.pull_request.commits_url }} | 16 | jq --raw-output '.[].sha' | 17 | xargs ./scripts/verify-commit-messages.sh 18 | -------------------------------------------------------------------------------- /tox_core/src/state_format/mod.rs: -------------------------------------------------------------------------------- 1 | /*! State format – for saving / loading data across restarts. 2 | 3 | *Currently there's only supported old, custom binary format used by toxcore. At 4 | some point it will be deprecated in favour of something better.* 5 | 6 | *After deprecation of the old format there will be a period where it still will 7 | be supported. After deprecation period code for handling old format will be 8 | moved out of toxcore into a separate library and maintained there.* 9 | 10 | https://zetok.github.io/tox-spec/#state-format 11 | */ 12 | 13 | // FIXME: use new dht code instead of old 14 | pub mod old; 15 | -------------------------------------------------------------------------------- /tox_crypto/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tox_crypto" 3 | version = "0.2.0" 4 | authors = [ 5 | "Zetok Zalbavar ", 6 | "Roman Proskuryakov ", 7 | "Namsoo CHO ", 8 | "Evgeny Kurnevsky ", 9 | "Сухарик <65870+suhr@users.noreply.github.com>" 10 | ] 11 | description = "Cryptography used by tox" 12 | documentation = "https://docs.rs/tox_crypto/" 13 | repository = "https://github.com/tox-rs/tox/" 14 | keywords = ["tox", "toxcore", "crypto"] 15 | categories = ["multimedia"] 16 | license = "GPL-3.0+" 17 | edition = "2021" 18 | 19 | [dependencies] 20 | crypto_box = "0.8" 21 | -------------------------------------------------------------------------------- /tox_binary_io/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tox_binary_io" 3 | version = "0.2.0" 4 | authors = [ 5 | "Zetok Zalbavar ", 6 | "Roman Proskuryakov ", 7 | "Namsoo CHO ", 8 | "Evgeny Kurnevsky ", 9 | "Сухарик <65870+suhr@users.noreply.github.com>" 10 | ] 11 | description = "I/O traits for tox" 12 | documentation = "https://docs.rs/tox_binary_io/" 13 | repository = "https://github.com/tox-rs/tox/" 14 | license = "GPL-3.0+" 15 | edition = "2021" 16 | 17 | [dependencies] 18 | nom = "7.1" 19 | cookie-factory = "0.3" 20 | crypto_box = { version = "0.8", optional = true } 21 | 22 | [features] 23 | default = [] 24 | crypto = ["crypto_box"] 25 | -------------------------------------------------------------------------------- /tox_node/Dockerfile: -------------------------------------------------------------------------------- 1 | # build stage 2 | FROM archlinux/base AS builder 3 | 4 | # install build dependencies 5 | RUN pacman --noconfirm -Sy \ 6 | gcc \ 7 | git \ 8 | rust \ 9 | diffutils \ 10 | file \ 11 | awk \ 12 | make 13 | 14 | # copy local files to container 15 | ADD . /tox-node 16 | WORKDIR /tox-node 17 | 18 | # build 19 | RUN cargo build --release 20 | 21 | # run stage 22 | FROM archlinux/base 23 | 24 | COPY --from=builder /tox-node/target/release/tox-node /user/local/ 25 | 26 | # expose ports that are default for a bootstrap node 27 | EXPOSE 443/tcp 3389/tcp 33445/tcp 33445/udp 28 | 29 | # add user 30 | RUN useradd tox_node 31 | 32 | # change running user 33 | USER tox_node 34 | 35 | ENTRYPOINT ["/user/local/tox-node"] 36 | CMD [] 37 | -------------------------------------------------------------------------------- /tox_core/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Rust implementation of the [Tox protocol](https://zetok.github.io/tox-spec). 3 | 4 | Repo: https://github.com/tox-rs/tox 5 | 6 | */ 7 | 8 | #![type_length_limit = "2097152"] 9 | #![forbid(unsafe_code)] 10 | #![doc(html_logo_url = "https://raw.githubusercontent.com/tox-rs/logo/master/logo.png")] 11 | // Remove it when it will be fixed in nom parser 12 | #![allow(clippy::redundant_closure, clippy::result_unit_err)] 13 | 14 | #[macro_use] 15 | extern crate log; 16 | #[macro_use] 17 | extern crate cookie_factory; 18 | 19 | pub mod dht; 20 | pub mod friend_connection; 21 | pub mod io_tokio; 22 | pub mod net_crypto; 23 | pub mod onion; 24 | pub mod relay; 25 | pub mod state_format; 26 | pub mod stats; 27 | pub mod time; 28 | pub mod udp; 29 | pub mod utils; 30 | -------------------------------------------------------------------------------- /tox_core/src/io_tokio.rs: -------------------------------------------------------------------------------- 1 | //! Defines `IoFuture` and `IoStream` 2 | 3 | use futures::sink::SinkExt; 4 | 5 | /// Sends a message into `Option>` 6 | pub async fn maybe_send_bounded( 7 | chan: Option>, 8 | value: T, 9 | ) -> Result<(), futures::channel::mpsc::SendError> { 10 | match chan { 11 | Some(mut c) => c.send(value).await, 12 | None => Ok(()), 13 | } 14 | } 15 | 16 | /// Sends a message into `Option>` 17 | pub async fn maybe_send_unbounded( 18 | chan: Option>, 19 | value: T, 20 | ) -> Result<(), futures::channel::mpsc::SendError> { 21 | match chan { 22 | Some(mut c) => c.send(value).await, 23 | None => Ok(()), 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /COPYING.iOS: -------------------------------------------------------------------------------- 1 | The tox-rs developers are aware that the terms of service that apply 2 | to apps distributed via Apple's App Store services may conflict with 3 | rights granted under the tox-rs license, the GNU General Public 4 | License, version 3 or (at your option) any later version. The 5 | copyright holders of the tox-rs do not wish this conflict to prevent 6 | the otherwise-compliant distribution of derived apps via the App 7 | Store. Therefore, we have committed not to pursue any license 8 | violation that results solely from the conflict between the GNU GPLv3 9 | or any later version and the Apple App Store terms of service. In 10 | other words, as long as you comply with the GPL in all other respects, 11 | including its requirements to provide users with source code and the 12 | text of the license, we will not object to your distribution of the 13 | tox-rs through the App Store. 14 | -------------------------------------------------------------------------------- /tox_encryptsave/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tox_encryptsave" 3 | version = "0.2.0" 4 | authors = [ 5 | "Zetok Zalbavar ", 6 | "Roman Proskuryakov ", 7 | "Namsoo CHO ", 8 | "Evgeny Kurnevsky ", 9 | "Сухарик <65870+suhr@users.noreply.github.com>" 10 | ] 11 | description = "Encryptsave part of tox" 12 | documentation = "https://docs.rs/tox_encryptsave/" 13 | repository = "https://github.com/tox-rs/tox/" 14 | keywords = ["p2p", "instant-messaging", "tox", "toxcore", "networking"] 15 | categories = ["multimedia"] 16 | license = "GPL-3.0+" 17 | edition = "2021" 18 | 19 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 20 | 21 | [dependencies] 22 | thiserror = "1.0" 23 | rand = "0.8" 24 | sha2 = "0.10" 25 | xsalsa20poly1305 = "0.9" 26 | scrypt = "0.11" 27 | zeroize = "1.6" 28 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/offline.rs: -------------------------------------------------------------------------------- 1 | /*! Offline struct 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::tag; 7 | 8 | /** Offline is a struct that holds nothing. 9 | 10 | This packet is used to notify that a friend is being deleted. 11 | Though the friend is deleted, because of conference, Tox client 12 | may try to connect to the friend, this message prevent this friend to 13 | be shown as Online. 14 | 15 | */ 16 | #[derive(Clone, Debug, Eq, PartialEq)] 17 | pub struct Offline; 18 | 19 | impl FromBytes for Offline { 20 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 21 | let (input, _) = tag("\x19")(input)?; 22 | Ok((input, Offline)) 23 | } 24 | } 25 | 26 | impl ToBytes for Offline { 27 | #[rustfmt::skip] 28 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 29 | do_gen!(buf, 30 | gen_be_u8!(0x19) 31 | ) 32 | } 33 | } 34 | 35 | #[cfg(test)] 36 | mod tests { 37 | use super::*; 38 | 39 | encode_decode_test!(offline_encode_decode, Offline); 40 | } 41 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/online.rs: -------------------------------------------------------------------------------- 1 | /*! Online struct 2 | */ 3 | 4 | use super::*; 5 | use nom::bytes::complete::tag; 6 | 7 | /** Online is a struct that holds nothing. 8 | 9 | This packet is used to notify that a friend is online. 10 | When a Tox client receives this message from a friend then 11 | the Tox client stops sending Friend request packets to the friend. 12 | Tox client shows status of it's friend as ONLINE only after receiving this packet. 13 | 14 | */ 15 | #[derive(Clone, Debug, Eq, PartialEq)] 16 | pub struct Online; 17 | 18 | impl FromBytes for Online { 19 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 20 | let (input, _) = tag("\x18")(input)?; 21 | Ok((input, Online)) 22 | } 23 | } 24 | 25 | impl ToBytes for Online { 26 | #[rustfmt::skip] 27 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 28 | do_gen!(buf, 29 | gen_be_u8!(0x18) 30 | ) 31 | } 32 | } 33 | 34 | #[cfg(test)] 35 | mod tests { 36 | use super::*; 37 | 38 | encode_decode_test!(online_encode_decode, Online); 39 | } 40 | -------------------------------------------------------------------------------- /tox_packet/src/friend_connection/alive.rs: -------------------------------------------------------------------------------- 1 | /*! Alive struct 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::tag; 7 | 8 | /// Id of the ping packet. 9 | pub const PACKET_ID_ALIVE: u8 = 0x10; 10 | 11 | /** Alive is a struct that holds nothing. 12 | 13 | This packet is used to check if the friend is online by sending this packet 14 | every 8 seconds using net_crypto connection. 15 | If one node has not received this packet for 32 seconds, the friend connection is timed out 16 | and destroyed. 17 | 18 | */ 19 | #[derive(Clone, Debug, Eq, PartialEq)] 20 | pub struct Alive; 21 | 22 | impl FromBytes for Alive { 23 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 24 | let (input, _) = tag(&[PACKET_ID_ALIVE][..])(input)?; 25 | Ok((input, Alive)) 26 | } 27 | } 28 | 29 | impl ToBytes for Alive { 30 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 31 | gen_be_u8!(buf, PACKET_ID_ALIVE) 32 | } 33 | } 34 | 35 | #[cfg(test)] 36 | mod tests { 37 | use super::*; 38 | 39 | encode_decode_test!(alive_encode_decode, Alive); 40 | } 41 | -------------------------------------------------------------------------------- /tox_packet/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tox_packet" 3 | version = "0.2.0" 4 | authors = [ 5 | "Zetok Zalbavar ", 6 | "Roman Proskuryakov ", 7 | "Namsoo CHO ", 8 | "Evgeny Kurnevsky ", 9 | "Сухарик <65870+suhr@users.noreply.github.com>" 10 | ] 11 | description = "Encoding/decoding for the tox protocol" 12 | documentation = "https://docs.rs/tox_packet/" 13 | repository = "https://github.com/tox-rs/tox/" 14 | keywords = ["p2p", "instant-messaging", "tox", "toxcore", "networking"] 15 | categories = ["multimedia"] 16 | license = "GPL-3.0+" 17 | edition = "2021" 18 | 19 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 20 | 21 | [dependencies] 22 | tox_binary_io = { version = "0.2.0", path = "../tox_binary_io", features = ["crypto"] } 23 | tox_crypto = { version = "0.2.0", path = "../tox_crypto" } 24 | 25 | nom = "7.1" 26 | cookie-factory = "0.3" 27 | bitflags = "2.2" 28 | thiserror = "1.0" 29 | sha2 = "0.10" 30 | xsalsa20poly1305 = "0.9" 31 | # for enabling rand_core feature 32 | aead = { version = "0.5", features = ["rand_core"] } 33 | rand = "0.8" 34 | crypto_box = "0.8" 35 | -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples" 3 | version = "0.0.0" 4 | publish = false 5 | edition = "2021" 6 | 7 | [dev-dependencies] 8 | tox_binary_io = { version = "0.2.0", path = "../tox_binary_io", features = ["crypto"] } 9 | tox_crypto = { version = "0.2.0", path = "../tox_crypto" } 10 | tox_packet = { version = "0.2.0", path = "../tox_packet" } 11 | tox_core = { version = "0.2.0", path = "../tox_core" } 12 | 13 | log = "0.4" 14 | futures = { version = "0.3", default-features = false, features = ["std", "async-await"] } 15 | env_logger = "0.10" 16 | hex = "0.4" 17 | thiserror = "1.0" 18 | rand = "0.8" 19 | anyhow = "1.0" 20 | 21 | [dev-dependencies.tokio] 22 | version = "1.28" 23 | default-features = false 24 | features = ["macros", "test-util", "net", "rt", "rt-multi-thread", "sync", "time"] 25 | 26 | [dev-dependencies.tokio-util] 27 | version = "0.7" 28 | features = ["codec", "net"] 29 | 30 | [[example]] 31 | name = "dht_server" 32 | path = "dht_server.rs" 33 | 34 | [[example]] 35 | name = "echo" 36 | path = "echo.rs" 37 | 38 | [[example]] 39 | name = "onion_client" 40 | path = "onion_client.rs" 41 | 42 | [[example]] 43 | name = "tcp_client" 44 | path = "tcp_client.rs" 45 | 46 | [[example]] 47 | name = "tcp_server" 48 | path = "tcp_server.rs" 49 | -------------------------------------------------------------------------------- /tox_packet/src/packed_node.rs: -------------------------------------------------------------------------------- 1 | //! Variant of PackedNode to contain both TCP and UDP 2 | 3 | use cookie_factory::{do_gen, gen_call, gen_slice}; 4 | use tox_binary_io::*; 5 | use tox_crypto::*; 6 | 7 | use crate::ip_port::*; 8 | 9 | /// Variant of PackedNode to contain both TCP and UDP 10 | #[derive(Clone, Debug, Eq, PartialEq)] 11 | pub struct TcpUdpPackedNode { 12 | // TODO: unify with dht::packed_node 13 | /// IP address of the node. 14 | pub ip_port: IpPort, 15 | /// Public Key of the node. 16 | pub pk: PublicKey, 17 | } 18 | 19 | impl FromBytes for TcpUdpPackedNode { 20 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 21 | let (input, ip_port) = IpPort::from_bytes(input, IpPortPadding::NoPadding)?; 22 | let (input, pk) = PublicKey::from_bytes(input)?; 23 | Ok((input, TcpUdpPackedNode { ip_port, pk })) 24 | } 25 | } 26 | 27 | impl ToBytes for TcpUdpPackedNode { 28 | #[rustfmt::skip] 29 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 30 | do_gen!(buf, 31 | gen_call!(|buf, data| IpPort::to_bytes(data, buf, IpPortPadding::NoPadding), &self.ip_port) >> 32 | gen_slice!(self.pk.as_ref()) 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/tcp_server.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | 4 | use anyhow::Error; 5 | use tox_core::relay::server::{tcp_run, Server}; 6 | use tox_core::stats::Stats; 7 | use tox_crypto::*; 8 | 9 | use tokio::net::TcpListener; 10 | 11 | const TCP_CONNECTIONS_LIMIT: usize = 1024; 12 | 13 | #[tokio::main] 14 | async fn main() -> Result<(), Error> { 15 | env_logger::init(); 16 | // Server constant PK for examples/tests 17 | let server_pk = PublicKey::from([ 18 | 177, 185, 54, 250, 10, 168, 174, 148, 0, 93, 99, 13, 131, 131, 239, 193, 129, 141, 80, 158, 50, 133, 100, 182, 19 | 179, 183, 234, 116, 142, 102, 53, 38, 20 | ]); 21 | let server_sk = SecretKey::from([ 22 | 74, 163, 57, 111, 32, 145, 19, 40, 44, 145, 233, 210, 173, 67, 88, 217, 140, 147, 14, 176, 106, 255, 54, 249, 23 | 159, 12, 18, 39, 123, 29, 125, 230, 24 | ]); 25 | 26 | let addr: std::net::SocketAddr = "0.0.0.0:12345".parse().unwrap(); 27 | 28 | info!("Listening on addr={}, {:?}", addr, &server_pk); 29 | 30 | let server = Server::new(); 31 | 32 | let stats = Stats::new(); 33 | let listener = TcpListener::bind(&addr).await.unwrap(); 34 | tcp_run(&server, listener, server_sk, stats, TCP_CONNECTIONS_LIMIT) 35 | .await 36 | .map_err(Error::from) 37 | } 38 | -------------------------------------------------------------------------------- /scripts/verify-commit-messages.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Script for verifying conformance to commit message format of commits in commit 4 | # range supplied. 5 | # 6 | # Scrpt fails (non-zero exit status) if commit messages don't conform. 7 | 8 | # usage: 9 | # ./$script $commit_sha_list 10 | 11 | REGEX_NORMAL="(feat|fix|docs|style|refactor|perf|revert|test|chore)(\(.+\))?:.{1,68})" 12 | REGEX_MERGE="(Merge pull request #[[:digit:]]{1,10}( from .*/.*)?" 13 | REGEX="^(${REGEX_NORMAL}|${REGEX_MERGE})$" 14 | 15 | echo # formatting 16 | 17 | fail=$(git show -s --format=format:'%s' "$@" | grep -v -E "${REGEX}") 18 | 19 | echo "$fail" 20 | 21 | # Conform, /OR ELSE/. 22 | if [[ $fail ]] 23 | then 24 | echo "" 25 | echo "Above ↑ commits don't conform to commit message format:" 26 | echo "https://github.com/tox-rs/tox/blob/master/CONTRIBUTING.md#commit-message-format" 27 | echo "" 28 | echo "Pls fix." 29 | echo "" 30 | echo "If you're not sure how to rewrite history, here's a helpful tutorial:" 31 | echo "https://www.atlassian.com/git/tutorials/rewriting-history/git-commit--amend/" 32 | echo "" 33 | echo -n "If you're still not sure what to do, feel free to pop on IRC, or " 34 | echo "ask in PR comments for help :)" 35 | # fail the build 36 | exit 1 37 | fi 38 | -------------------------------------------------------------------------------- /tox/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tox" 3 | version = "0.2.0" 4 | authors = [ 5 | "Zetok Zalbavar ", 6 | "Roman Proskuryakov ", 7 | "Namsoo CHO ", 8 | "Evgeny Kurnevsky ", 9 | "Сухарик <65870+suhr@users.noreply.github.com>" 10 | ] 11 | readme = "../README.md" 12 | description = ''' 13 | Implementation of toxcore in pure Rust - 14 | P2P, distributed, encrypted, easy 15 | to use DHT-based network. 16 | ''' 17 | documentation = "https://docs.rs/tox/" 18 | repository = "https://github.com/tox-rs/tox/" 19 | keywords = ["p2p", "instant-messaging", "tox", "toxcore", "networking"] 20 | categories = ["multimedia"] 21 | license = "GPL-3.0+" 22 | edition = "2021" 23 | 24 | [badges] 25 | travis-ci = { repository = "tox-rs/tox" } 26 | appveyor = { repository = "tox-rs/tox", id = "m47ke5odayd6enbn" } 27 | coveralls = { repository = "tox-rs/tox" } 28 | # Available options are `actively-developed`, `passively-maintained`, 29 | # `as-is`, `none`, `experimental`, `looking-for-maintainer`, `deprecated`. 30 | maintenance = { status = "actively-developed" } 31 | 32 | [dependencies] 33 | tox_packet = { version = "0.2.0", path = "../tox_packet" } 34 | tox_core = { version = "0.2.0", path = "../tox_core" } 35 | tox_encryptsave = { version = "0.2.0", path = "../tox_encryptsave" } 36 | tox_crypto = { version = "0.2.0", path = "../tox_crypto" } 37 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/conference/query.rs: -------------------------------------------------------------------------------- 1 | /*! Peer query message struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::tag; 7 | use nom::number::complete::be_u16; 8 | 9 | /** Query is a struct that holds info to query a peer in a conference. 10 | 11 | Serialized form: 12 | 13 | Length | Content 14 | --------- | ------ 15 | `1` | `0x62` 16 | `2` | `conference id` 17 | `1` | `0x08` 18 | 19 | */ 20 | #[derive(Clone, Debug, Eq, PartialEq)] 21 | pub struct Query(pub u16); 22 | 23 | impl FromBytes for Query { 24 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 25 | let (input, _) = tag("\x62")(input)?; 26 | let (input, conference_id) = be_u16(input)?; 27 | let (input, _) = tag("\x08")(input)?; 28 | Ok((input, Query(conference_id))) 29 | } 30 | } 31 | 32 | impl ToBytes for Query { 33 | #[rustfmt::skip] 34 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 35 | do_gen!(buf, 36 | gen_be_u8!(0x62) >> 37 | gen_be_u16!(self.0) >> 38 | gen_be_u8!(0x08) 39 | ) 40 | } 41 | } 42 | 43 | impl Query { 44 | /// Create new Query object. 45 | pub fn new(conference_id: u16) -> Self { 46 | Query(conference_id) 47 | } 48 | } 49 | 50 | #[cfg(test)] 51 | mod tests { 52 | use super::*; 53 | 54 | encode_decode_test!(peer_query_encode_decode, Query::new(1)); 55 | } 56 | -------------------------------------------------------------------------------- /tox_core/tests/state-format-old.rs: -------------------------------------------------------------------------------- 1 | use tox_binary_io::*; 2 | use tox_core::state_format::old::*; 3 | 4 | /* 5 | Load bytes of a real™ profile, de-serialize it and serialize again. Serialized 6 | again bytes must be identical, except for the zeros that trail after the data 7 | in original implementation – they're ommited. Just check if smaller length of 8 | the resulting bytes are in fact due to original being appended with `0`s. 9 | */ 10 | 11 | #[test] 12 | fn load_old_state_format_with_contacts() { 13 | let bytes = include_bytes!("data/old-profile-with-contacts.tox"); 14 | 15 | let (_rest, profile) = State::from_bytes(bytes).unwrap(); 16 | 17 | let mut buf = [0; 1024 * 1024]; 18 | let (_, size) = profile.to_bytes((&mut buf, 0)).unwrap(); 19 | 20 | assert_eq!(&bytes[..size], &buf[..size]); 21 | 22 | // c-toxcore appends `0`s after EOF because reasons 23 | for b in &bytes[size..] { 24 | assert_eq!(0, *b); 25 | } 26 | } 27 | 28 | #[test] 29 | fn load_old_state_format_no_friends() { 30 | let bytes = include_bytes!("data/old-profile-no-friends.tox"); 31 | 32 | let (_rest, profile) = State::from_bytes(bytes).unwrap(); 33 | 34 | let mut buf = [0; 1024 * 1024]; 35 | let (_, size) = profile.to_bytes((&mut buf, 0)).unwrap(); 36 | 37 | assert_eq!(&bytes[..size], &buf[..size]); 38 | 39 | // c-toxcore appends `0`s after EOF because reasons 40 | for b in &bytes[size..] { 41 | assert_eq!(0, *b); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tox_core/src/dht/precomputed_cache.rs: -------------------------------------------------------------------------------- 1 | //! LRU cache for `SalsaBox`es. 2 | 3 | use std::num::NonZeroUsize; 4 | use std::sync::Arc; 5 | 6 | use crypto_box::SalsaBox; 7 | use lru::LruCache; 8 | 9 | use futures::lock::Mutex; 10 | use tox_crypto::*; 11 | 12 | /// LRU cache for `SalsaBox`es. 13 | /// 14 | /// Calculation of `SalsaBox` from the `PublicKey`-`SecretKey` pair is an 15 | /// expensive operation. `SalsaBox`es should be cached whenever possible 16 | /// and reused later. 17 | #[derive(Clone)] 18 | pub struct PrecomputedCache { 19 | sk: SecretKey, 20 | precomputed_keys: Arc>>, 21 | } 22 | 23 | impl PrecomputedCache { 24 | /// Create new `PrecomputedCache`. 25 | pub fn new(sk: SecretKey, capacity: usize) -> PrecomputedCache { 26 | let capacity = NonZeroUsize::new(capacity).expect("must be non zero"); 27 | PrecomputedCache { 28 | sk, 29 | precomputed_keys: Arc::new(Mutex::new(LruCache::new(capacity))), 30 | } 31 | } 32 | 33 | /// Get `SalsaBox` for the given `PublicKey`. 34 | pub async fn get(&self, pk: PublicKey) -> SalsaBox { 35 | let mut keys = self.precomputed_keys.lock().await; 36 | 37 | if let Some(precomputed_key) = keys.get(&pk) { 38 | return precomputed_key.clone(); 39 | } 40 | 41 | let precomputed_key = SalsaBox::new(&pk, &self.sk); 42 | keys.put(pk, precomputed_key.clone()); 43 | precomputed_key 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/conference/peer_leave.rs: -------------------------------------------------------------------------------- 1 | /*! Peer leave message struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::tag; 7 | use nom::number::complete::be_u16; 8 | 9 | /** PeerLeave is a struct that holds info to notify a peer quit a conference. 10 | 11 | Serialized form: 12 | 13 | Length | Content 14 | --------- | ------ 15 | `1` | `0x62` 16 | `2` | `conference id` 17 | `1` | `0x01` 18 | 19 | */ 20 | #[derive(Clone, Debug, Eq, PartialEq)] 21 | pub struct PeerLeave(pub u16); 22 | 23 | impl FromBytes for PeerLeave { 24 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 25 | let (input, _) = tag("\x62")(input)?; 26 | let (input, conference_id) = be_u16(input)?; 27 | let (input, _) = tag("\x01")(input)?; 28 | Ok((input, PeerLeave(conference_id))) 29 | } 30 | } 31 | 32 | impl ToBytes for PeerLeave { 33 | #[rustfmt::skip] 34 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 35 | do_gen!(buf, 36 | gen_be_u8!(0x62) >> 37 | gen_be_u16!(self.0) >> 38 | gen_be_u8!(0x01) 39 | ) 40 | } 41 | } 42 | 43 | impl PeerLeave { 44 | /// Create new PeerLeave object. 45 | pub fn new(conference_id: u16) -> Self { 46 | PeerLeave(conference_id) 47 | } 48 | } 49 | 50 | #[cfg(test)] 51 | mod tests { 52 | use super::*; 53 | 54 | encode_decode_test!(peer_leave_encode_decode, PeerLeave::new(1)); 55 | } 56 | -------------------------------------------------------------------------------- /tox_packet/src/relay/pong_response.rs: -------------------------------------------------------------------------------- 1 | /*! PongResponse packet 2 | */ 3 | 4 | use super::*; 5 | 6 | use tox_binary_io::*; 7 | 8 | use nom::bytes::complete::tag; 9 | use nom::number::complete::be_u64; 10 | 11 | /** Sent by both client and server, both will respond. 12 | The server should respond to ping packets with pong packets with the same `ping_id` 13 | as was in the ping packet. The server should check that each pong packet contains 14 | the same `ping_id` as was in the ping, if not the pong packet must be ignored. 15 | 16 | Serialized form: 17 | 18 | Length | Content 19 | ------ | ------ 20 | `1` | `0x05` 21 | `8` | ping_id in BigEndian 22 | 23 | */ 24 | #[derive(Debug, PartialEq, Eq, Clone)] 25 | pub struct PongResponse { 26 | /// The id of ping to respond 27 | pub ping_id: u64, 28 | } 29 | 30 | impl FromBytes for PongResponse { 31 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 32 | let (input, _) = tag("\x05")(input)?; 33 | let (input, ping_id) = be_u64(input)?; 34 | Ok((input, PongResponse { ping_id })) 35 | } 36 | } 37 | 38 | impl ToBytes for PongResponse { 39 | #[rustfmt::skip] 40 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 41 | do_gen!(buf, 42 | gen_be_u8!(0x05) >> 43 | gen_be_u64!(self.ping_id) 44 | ) 45 | } 46 | } 47 | 48 | #[cfg(test)] 49 | mod test { 50 | use super::*; 51 | 52 | encode_decode_test!(pong_response_encode_decode, PongResponse { ping_id: 12345 }); 53 | } 54 | -------------------------------------------------------------------------------- /tox_packet/src/relay/route_request.rs: -------------------------------------------------------------------------------- 1 | /*! RouteRequest packet 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::tag; 7 | use tox_binary_io::*; 8 | use tox_crypto::*; 9 | 10 | /** Sent by client to server. 11 | Send a routing request to the server that we want to connect 12 | to peer with public key where the public key is the public the peer 13 | announced themselves as. The server must respond to this with a `RouteResponse`. 14 | 15 | Serialized form: 16 | 17 | Length | Content 18 | ------ | ------ 19 | `1` | `0x00` 20 | `32` | Public Key 21 | 22 | */ 23 | #[derive(Debug, PartialEq, Eq, Clone)] 24 | pub struct RouteRequest { 25 | /// The requested PK 26 | pub pk: PublicKey, 27 | } 28 | 29 | impl FromBytes for RouteRequest { 30 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 31 | let (input, _) = tag("\x00")(input)?; 32 | let (input, pk) = PublicKey::from_bytes(input)?; 33 | Ok((input, RouteRequest { pk })) 34 | } 35 | } 36 | 37 | impl ToBytes for RouteRequest { 38 | #[rustfmt::skip] 39 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 40 | do_gen!(buf, 41 | gen_be_u8!(0x00) >> 42 | gen_slice!(self.pk.as_bytes()) 43 | ) 44 | } 45 | } 46 | 47 | #[cfg(test)] 48 | mod test { 49 | use rand::thread_rng; 50 | 51 | use super::*; 52 | 53 | encode_decode_test!( 54 | route_request_encode_decode, 55 | RouteRequest { 56 | pk: SecretKey::generate(&mut thread_rng()).public_key() 57 | } 58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /tox_core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tox_core" 3 | version = "0.2.0" 4 | authors = [ 5 | "Zetok Zalbavar ", 6 | "Roman Proskuryakov ", 7 | "Namsoo CHO ", 8 | "Evgeny Kurnevsky ", 9 | "Сухарик <65870+suhr@users.noreply.github.com>" 10 | ] 11 | description = "The core of tox" 12 | documentation = "https://docs.rs/tox_core/" 13 | repository = "https://github.com/tox-rs/tox/" 14 | keywords = ["p2p", "instant-messaging", "tox", "toxcore", "networking"] 15 | categories = ["multimedia"] 16 | license = "GPL-3.0+" 17 | edition = "2021" 18 | 19 | [dependencies] 20 | tox_binary_io = { version = "0.2.0", path = "../tox_binary_io" } 21 | tox_crypto = { version = "0.2.0", path = "../tox_crypto" } 22 | tox_packet = { version = "0.2.0", path = "../tox_packet" } 23 | 24 | bytes = "1.4" 25 | futures = { version = "0.3", default-features = false, features = ["std", "async-await"] } 26 | log = "0.4" 27 | nom = "7.1" 28 | cookie-factory = "0.3" 29 | get_if_addrs = "0.5" 30 | thiserror = "1.0" 31 | lru = "0.10" 32 | bitflags = "2.2" 33 | itertools = "0.10" 34 | rand = "0.8" 35 | sha2 = "0.10" 36 | xsalsa20poly1305 = "0.9" 37 | crypto_box = "0.8" 38 | 39 | [dependencies.tokio] 40 | version = "1.28" 41 | default-features = false 42 | features = ["net", "sync", "time"] 43 | 44 | [dependencies.tokio-util] 45 | version = "0.7" 46 | features = ["codec", "net"] 47 | 48 | [dev-dependencies.tokio] 49 | version = "1.28" 50 | default-features = false 51 | features = ["macros", "test-util", "net", "rt", "rt-multi-thread", "sync", "time"] 52 | -------------------------------------------------------------------------------- /tox_core/src/time.rs: -------------------------------------------------------------------------------- 1 | //! Functions to work with time 2 | 3 | use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; 4 | 5 | /// Return number of seconds that have elapsed since Unix epoch. 6 | pub fn unix_time(time: SystemTime) -> u64 { 7 | let since_the_epoch = time 8 | .duration_since(UNIX_EPOCH) 9 | .expect("Current time is earlier than Unix epoch"); 10 | since_the_epoch.as_secs() 11 | } 12 | 13 | /// Returns an `Instant` corresponding to "now". Should be used instead of 14 | /// `tokio_timer::clock::now()` to have zero cost mocked time. 15 | #[cfg(test)] 16 | pub fn clock_now() -> Instant { 17 | tokio::time::Instant::now().into_std() 18 | } 19 | 20 | /// Returns an `Instant` corresponding to "now". Should be used instead of 21 | /// `tokio_timer::clock::now()` to have zero cost mocked time. 22 | #[cfg(not(test))] 23 | pub fn clock_now() -> Instant { 24 | Instant::now() 25 | } 26 | 27 | /// Returns the amount of time elapsed since this instant was created. Should be 28 | /// used instead of `Instant::elapsed` in order to work with mocked 29 | /// `tokio_timer::clock::now()`. 30 | pub fn clock_elapsed(time: Instant) -> Duration { 31 | clock_now() - time 32 | } 33 | 34 | #[cfg(test)] 35 | pub mod tests { 36 | use super::{clock_elapsed, clock_now}; 37 | 38 | #[tokio::test] 39 | async fn const_elapsed() { 40 | tokio::time::pause(); 41 | 42 | let now = clock_now(); 43 | let duration = std::time::Duration::from_secs(42); 44 | 45 | tokio::time::advance(duration).await; 46 | 47 | let elapsed = clock_elapsed(now); 48 | assert_eq!(elapsed, duration); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tox_packet/src/relay/connect_notification.rs: -------------------------------------------------------------------------------- 1 | /*! ConnectNotification packet 2 | */ 3 | 4 | use super::*; 5 | 6 | use crate::relay::connection_id::ConnectionId; 7 | use nom::bytes::complete::tag; 8 | use tox_binary_io::*; 9 | 10 | /** Sent by server to client. 11 | Tell the client that connection_id is now connected meaning the other 12 | is online and data can be sent using this `connection_id`. 13 | 14 | Serialized form: 15 | 16 | Length | Content 17 | ------ | ------ 18 | `1` | `0x02` 19 | `1` | connection_id [ `0x10` .. `0xFF` ] 20 | 21 | */ 22 | #[derive(Debug, PartialEq, Eq, Clone)] 23 | pub struct ConnectNotification { 24 | /// The id of the connected client 25 | pub connection_id: ConnectionId, 26 | } 27 | 28 | impl FromBytes for ConnectNotification { 29 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 30 | let (input, _) = tag("\x02")(input)?; 31 | let (input, connection_id) = ConnectionId::from_bytes(input)?; 32 | Ok((input, ConnectNotification { connection_id })) 33 | } 34 | } 35 | 36 | impl ToBytes for ConnectNotification { 37 | #[rustfmt::skip] 38 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 39 | do_gen!(buf, 40 | gen_be_u8!(0x02) >> 41 | gen_call!(|buf, connection_id| ConnectionId::to_bytes(connection_id, buf), &self.connection_id) 42 | ) 43 | } 44 | } 45 | 46 | #[cfg(test)] 47 | mod test { 48 | use super::*; 49 | 50 | encode_decode_test!( 51 | connect_notification_encode_decode, 52 | ConnectNotification { 53 | connection_id: ConnectionId::from_index(1) 54 | } 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /tox_packet/src/dht/lan_discovery.rs: -------------------------------------------------------------------------------- 1 | /*! LanDiscovery packet 2 | */ 3 | use super::*; 4 | 5 | use nom::bytes::complete::tag; 6 | use tox_binary_io::*; 7 | use tox_crypto::*; 8 | 9 | /** LanDiscovery packet struct. 10 | LanDiscovery packets contain the DHT public key of the sender. When a LanDiscovery packet 11 | is received, a NodesRequest packet will be sent to the sender of the packet. This means that 12 | the DHT instance will bootstrap itself to every peer from which it receives one of these packet. 13 | Through this mechanism, Tox clients will bootstrap themselve 14 | automatically from other Tox clients running on the local network. 15 | 16 | 17 | Serialized form: 18 | 19 | Length | Content 20 | ------ | ------ 21 | `1` | `0x21` 22 | `32` | Public Key 23 | 24 | */ 25 | #[derive(Clone, Debug, Eq, PartialEq)] 26 | pub struct LanDiscovery { 27 | /// DHT public key of the sender 28 | pub pk: PublicKey, 29 | } 30 | 31 | impl ToBytes for LanDiscovery { 32 | #[rustfmt::skip] 33 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 34 | do_gen!(buf, 35 | gen_be_u8!(0x21) >> 36 | gen_slice!(self.pk.as_ref()) 37 | ) 38 | } 39 | } 40 | 41 | impl FromBytes for LanDiscovery { 42 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 43 | let (input, _) = tag("\x21")(input)?; 44 | let (input, pk) = PublicKey::from_bytes(input)?; 45 | Ok((input, LanDiscovery { pk })) 46 | } 47 | } 48 | 49 | #[cfg(test)] 50 | mod tests { 51 | use rand::thread_rng; 52 | 53 | use crate::dht::lan_discovery::*; 54 | 55 | encode_decode_test!( 56 | lan_discovery_encode_decode, 57 | LanDiscovery { 58 | pk: SecretKey::generate(&mut thread_rng()).public_key() 59 | } 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /tox_core/src/friend_connection/errors.rs: -------------------------------------------------------------------------------- 1 | //! Errors for friend connections module. 2 | 3 | use futures::channel::mpsc::SendError; 4 | use thiserror::Error; 5 | use tokio::time::error::Elapsed; 6 | 7 | use crate::{ 8 | net_crypto::errors::{KillConnectionError, SendLosslessPacketError}, 9 | relay::client::ConnectionError, 10 | }; 11 | 12 | /// Error that can happen while removing a friend 13 | #[derive(Clone, Debug, Eq, PartialEq, Error)] 14 | pub enum RemoveFriendError { 15 | /// Failed to kill net_crypto connection. 16 | #[error("Failed to kill net_crypto connection")] 17 | KillConnection(KillConnectionError), 18 | /// There is no such friend. 19 | #[error("There is no such friend")] 20 | NoFriend, 21 | } 22 | 23 | /// Error that can happen while removing a friend 24 | #[derive(Debug, Error)] 25 | pub enum RunError { 26 | /// Wakeup timer error. 27 | #[error("Wakeup timer error")] 28 | Wakeup, 29 | /// Timeout error. 30 | #[error("Timeout error")] 31 | Timeout(Elapsed), 32 | /// Failed to kill net_crypto connection. 33 | #[error("Failed to kill net_crypto connection")] 34 | KillConnection(KillConnectionError), 35 | /// Failed to send packet. 36 | #[error("Failed to send packet")] 37 | SendTo(SendLosslessPacketError), 38 | /// Failed to add TCP connection. 39 | #[error("Failed to TCP connection")] 40 | AddTcpConnection(ConnectionError), 41 | /// Failed to send connection status. 42 | #[error("Failed to send connection status")] 43 | SendToConnectionStatus(SendError), 44 | } 45 | 46 | /// Error that can happen while handling `ShareRelays` packet. 47 | #[derive(Debug, Error)] 48 | pub enum HandleShareRelaysError { 49 | /// Failed to add TCP connection. 50 | #[error("Failed to TCP connection")] 51 | AddTcpConnection(ConnectionError), 52 | } 53 | -------------------------------------------------------------------------------- /tox_binary_io/src/crypto.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryInto; 2 | 3 | use nom::bytes::streaming::take; 4 | use nom::combinator::{map, map_opt}; 5 | use nom::IResult; 6 | 7 | use crypto_box::{PublicKey, SecretKey, KEY_SIZE}; 8 | 9 | use super::FromBytes; 10 | 11 | impl FromBytes for PublicKey { 12 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 13 | map( 14 | map_opt(take(KEY_SIZE), |pk: &[u8]| pk.try_into().ok()), 15 | |pk: [u8; KEY_SIZE]| pk.into(), 16 | )(input) 17 | } 18 | } 19 | 20 | /* TODO 21 | Use the following implementation when https://github.com/TokTok/c-toxcore/issues/1169 is fixed. 22 | And when most of tox network will send valid PK for fake friends. 23 | 24 | impl FromBytes for PublicKey { 25 | named!(from_bytes, verify!( 26 | // TODO: use map_from with new nom version 27 | map!(map_opt!(take!(KEY_SIZE), |pk: &[u8]| pk.try_into().ok()), |pk: [u8; KEY_SIZE]| pk.into()), 28 | |pk| public_key_valid(&pk) 29 | )); 30 | } 31 | */ 32 | 33 | impl FromBytes for SecretKey { 34 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 35 | map( 36 | map_opt(take(KEY_SIZE), |sk: &[u8]| sk.try_into().ok()), 37 | |sk: [u8; KEY_SIZE]| sk.into(), 38 | )(input) 39 | } 40 | } 41 | 42 | #[cfg(test)] 43 | mod tests { 44 | use super::*; 45 | 46 | #[test] 47 | fn public_key_parse_bytes_test() { 48 | let bytes = [42; KEY_SIZE]; 49 | let (_rest, pk) = PublicKey::from_bytes(&bytes).unwrap(); 50 | 51 | assert_eq!(pk.as_bytes(), &bytes as &[u8]); 52 | } 53 | 54 | #[test] 55 | fn secret_key_parse_bytes_test() { 56 | let bytes = [42; KEY_SIZE]; 57 | let (_rest, sk) = SecretKey::from_bytes(&bytes).unwrap(); 58 | 59 | assert_eq!(sk.as_bytes(), &bytes as &[u8]); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tox_packet/src/relay/oob_receive.rs: -------------------------------------------------------------------------------- 1 | /*! OobReceive packet 2 | */ 3 | 4 | use super::*; 5 | 6 | use tox_binary_io::*; 7 | use tox_crypto::*; 8 | 9 | use nom::bytes::complete::tag; 10 | use nom::combinator::rest; 11 | 12 | /** Sent by server to client. 13 | OOB recv are sent with the announced public key of the peer that sent the 14 | OOB send packet and the exact data. 15 | 16 | Serialized form: 17 | 18 | Length | Content 19 | -------- | ------ 20 | `1` | `0x07` 21 | `32` | Public Key 22 | variable | Data 23 | 24 | */ 25 | #[derive(Debug, PartialEq, Eq, Clone)] 26 | pub struct OobReceive { 27 | /// Public Key of the sender 28 | pub sender_pk: PublicKey, 29 | /// OOB data packet 30 | pub data: Vec, 31 | } 32 | 33 | impl FromBytes for OobReceive { 34 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 35 | let (input, _) = tag("\x07")(input)?; 36 | let (input, sender_pk) = PublicKey::from_bytes(input)?; 37 | let (input, data) = rest(input)?; 38 | Ok(( 39 | input, 40 | OobReceive { 41 | sender_pk, 42 | data: data.to_vec(), 43 | }, 44 | )) 45 | } 46 | } 47 | 48 | impl ToBytes for OobReceive { 49 | #[rustfmt::skip] 50 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 51 | do_gen!(buf, 52 | gen_be_u8!(0x07) >> 53 | gen_slice!(self.sender_pk.as_bytes()) >> 54 | gen_slice!(self.data.as_slice()) 55 | ) 56 | } 57 | } 58 | 59 | #[cfg(test)] 60 | mod test { 61 | use rand::thread_rng; 62 | 63 | use super::*; 64 | 65 | encode_decode_test!( 66 | oob_receive_encode_decode, 67 | OobReceive { 68 | sender_pk: SecretKey::generate(&mut thread_rng()).public_key(), 69 | data: vec![42; 123] 70 | } 71 | ); 72 | } 73 | -------------------------------------------------------------------------------- /tox_packet/src/relay/ping_request.rs: -------------------------------------------------------------------------------- 1 | /*! PingRequest packet 2 | */ 3 | 4 | use super::*; 5 | 6 | use tox_binary_io::*; 7 | 8 | use nom::bytes::complete::tag; 9 | use nom::number::complete::be_u64; 10 | 11 | /** Sent by both client and server, both will respond. 12 | Ping packets are used to know if the other side of the connection is still 13 | live. TCP when established doesn't have any sane timeouts (1 week isn't sane) 14 | so we are obliged to have our own way to check if the other side is still live. 15 | Ping ids can be anything except 0, this is because of how toxcore sets the 16 | variable storing the `ping_id` that was sent to 0 when it receives a pong 17 | response which means 0 is invalid. 18 | 19 | The server should send ping packets every X seconds (toxcore `TCP_server` sends 20 | them every 30 seconds and times out the peer if it doesn't get a response in 10). 21 | The server should respond immediately to ping packets with pong packets. 22 | 23 | 24 | Serialized form: 25 | 26 | Length | Content 27 | ------ | ------ 28 | `1` | `0x04` 29 | `8` | ping_id in BigEndian 30 | 31 | */ 32 | #[derive(Debug, PartialEq, Eq, Clone)] 33 | pub struct PingRequest { 34 | /// The id of ping 35 | pub ping_id: u64, 36 | } 37 | 38 | impl FromBytes for PingRequest { 39 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 40 | let (input, _) = tag("\x04")(input)?; 41 | let (input, ping_id) = be_u64(input)?; 42 | Ok((input, PingRequest { ping_id })) 43 | } 44 | } 45 | 46 | impl ToBytes for PingRequest { 47 | #[rustfmt::skip] 48 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 49 | do_gen!(buf, 50 | gen_be_u8!(0x04) >> 51 | gen_be_u64!(self.ping_id) 52 | ) 53 | } 54 | } 55 | 56 | #[cfg(test)] 57 | mod test { 58 | use super::*; 59 | 60 | encode_decode_test!(ping_request_encode_decode, PingRequest { ping_id: 12345 }); 61 | } 62 | -------------------------------------------------------------------------------- /tox_core/src/stats.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Statistics of incoming/outgoing packets 3 | This is used by both Udp codec and Tcp codec. 4 | */ 5 | 6 | use std::sync::atomic::{AtomicU64, Ordering}; 7 | use std::sync::Arc; 8 | 9 | /// Struct for various counters 10 | #[derive(Clone, Default)] 11 | pub struct Stats { 12 | /// incoming/outgoing counters 13 | pub counters: Arc, 14 | } 15 | 16 | impl Stats { 17 | /// New Stats object 18 | pub fn new() -> Self { 19 | Default::default() 20 | } 21 | } 22 | 23 | #[derive(Default)] 24 | /// A struct for counting incoming and outgoing packets. 25 | pub struct Counters { 26 | /// Incoming packets count for Udp/Tcp 27 | incoming: AtomicU64, 28 | /// Outgoing packets count for Udp/Tcp 29 | outgoing: AtomicU64, 30 | } 31 | 32 | impl Counters { 33 | /// Add 1 to incoming counter 34 | pub fn increase_incoming(&self) { 35 | self.incoming.fetch_add(1, Ordering::Relaxed); 36 | } 37 | 38 | /// Add 1 to outgoing counter 39 | pub fn increase_outgoing(&self) { 40 | self.outgoing.fetch_add(1, Ordering::Relaxed); 41 | } 42 | 43 | /// Get incoming counter 44 | pub fn incoming(&self) -> u64 { 45 | self.incoming.load(Ordering::Relaxed) 46 | } 47 | 48 | /// Get outgoing counter 49 | pub fn outgoing(&self) -> u64 { 50 | self.outgoing.load(Ordering::Relaxed) 51 | } 52 | } 53 | 54 | #[cfg(test)] 55 | mod tests { 56 | use super::*; 57 | 58 | #[test] 59 | fn incoming() { 60 | let stats = Stats::new(); 61 | assert_eq!(0, stats.counters.incoming()); 62 | stats.counters.increase_incoming(); 63 | assert_eq!(1, stats.counters.incoming()); 64 | } 65 | 66 | #[test] 67 | fn outgoing() { 68 | let stats = Stats::new(); 69 | assert_eq!(0, stats.counters.outgoing()); 70 | stats.counters.increase_outgoing(); 71 | stats.counters.increase_outgoing(); 72 | assert_eq!(2, stats.counters.outgoing()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /examples/common/mod.rs: -------------------------------------------------------------------------------- 1 | use std::net::SocketAddr; 2 | 3 | use tokio::net::UdpSocket; 4 | 5 | pub const BOOTSTRAP_NODES: [(&str, &str); 9] = [ 6 | // Impyy 7 | ( 8 | "1D5A5F2F5D6233058BF0259B09622FB40B482E4FA0931EB8FD3AB8E7BF7DAF6F", 9 | "198.98.51.198:33445", 10 | ), 11 | // nurupo 12 | ( 13 | "F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67", 14 | "67.215.253.85:33445", 15 | ), 16 | // Manolis 17 | ( 18 | "461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F", 19 | "130.133.110.14:33445", 20 | ), 21 | // Busindre 22 | ( 23 | "A179B09749AC826FF01F37A9613F6B57118AE014D4196A0E1105A98F93A54702", 24 | "205.185.116.116:33445", 25 | ), 26 | // ray65536 27 | ( 28 | "8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832", 29 | "85.172.30.117:33445", 30 | ), 31 | // fluke571 32 | ( 33 | "3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B", 34 | "194.249.212.109:33445", 35 | ), 36 | // MAH69K 37 | ( 38 | "DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43", 39 | "185.25.116.107:33445", 40 | ), 41 | // clearmartin 42 | ( 43 | "CD133B521159541FB1D326DE9850F5E56A6C724B5B8E5EB5CD8D950408E95707", 44 | "46.101.197.175:443", 45 | ), 46 | // tastytea 47 | ( 48 | "2B2137E094F743AC8BD44652C55F41DFACC502F125E99E4FE24D40537489E32F", 49 | "5.189.176.217:5190", 50 | ), 51 | ]; 52 | 53 | /// Bind a UDP listener to the socket address. 54 | pub async fn bind_socket(addr: SocketAddr) -> UdpSocket { 55 | let socket = UdpSocket::bind(&addr).await.expect("Failed to bind UDP socket"); 56 | 57 | socket.set_broadcast(true).expect("set_broadcast call failed"); 58 | if addr.is_ipv6() { 59 | socket 60 | .set_multicast_loop_v6(true) 61 | .expect("set_multicast_loop_v6 call failed"); 62 | } 63 | 64 | socket 65 | } 66 | -------------------------------------------------------------------------------- /tox_node/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tox-node" 3 | version = "0.2.0" 4 | authors = [ 5 | "Roman Proskuryakov ", 6 | "Evgeny Kurnevsky ", 7 | "Namsoo CHO ", 8 | ] 9 | readme = "README.md" 10 | description = "A server application to run tox node" 11 | documentation = "https://docs.rs/crate/tox-node/" 12 | repository = "https://github.com/tox-rs/tox/" 13 | keywords = ["p2p", "instant-messaging", "tox", "toxcore", "networking"] 14 | categories = ["multimedia"] 15 | license = "GPL-3.0+" 16 | edition = "2021" 17 | 18 | 19 | [package.metadata.deb] 20 | maintainer = "Roman Proskuryakov " 21 | license-file = ["LICENSE"] 22 | depends = "$auto, systemd" 23 | extended-description = "A server application to run tox node written in pure Rust" 24 | section = "net" 25 | priority = "optional" 26 | assets = [ 27 | ["target/release/tox-node", "/usr/bin/", "755"], 28 | ["README.md", "/usr/share/doc/tox-node/README", "644"], 29 | ["dpkg/tox-node.service", "/lib/systemd/system/", "644"], 30 | ["dpkg/config.yml", "/etc/tox-node/", "644"], 31 | ["dpkg/usr.bin.tox-node", "/etc/apparmor.d/", "644"], 32 | ] 33 | conf-files = ["/etc/tox-node/config.yml"] 34 | maintainer-scripts = "dpkg/info" 35 | 36 | 37 | [dependencies] 38 | clap = { version = "4.2", features = ["cargo", "env"] } 39 | env_logger = "0.10" 40 | anyhow = "1.0" 41 | futures = { version = "0.3", default-features = false, features = ["std", "async-await"] } 42 | hex = "0.4" 43 | itertools = "0.10" 44 | log = "0.4" 45 | serde = { version = "1.0", features = ["derive"] } 46 | serde_yaml = "0.9" 47 | crypto_box = "0.8" 48 | rand = "0.8" 49 | time = { version = "0.3", features = ["serde-well-known"] } 50 | tox = { version = "0.2.0", path = "../tox" } 51 | 52 | [dependencies.config] 53 | version = "0.13" 54 | default-features = false 55 | features = ["yaml"] 56 | 57 | [dependencies.tokio] 58 | version = "1.28" 59 | default-features = false 60 | features = ["net", "time", "rt", "rt-multi-thread"] 61 | 62 | [target.'cfg(unix)'.dependencies] 63 | syslog = "6.1" 64 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/typing.rs: -------------------------------------------------------------------------------- 1 | /*! Typing struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::tag; 7 | use nom::error::{make_error, ErrorKind}; 8 | use nom::number::complete::le_u8; 9 | 10 | /// Typing status of user 11 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 12 | pub enum TypingStatus { 13 | /// Not typing 14 | NotTyping = 0, 15 | /// Typing 16 | Typing, 17 | } 18 | 19 | impl FromBytes for TypingStatus { 20 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 21 | let (input, b) = le_u8(input)?; 22 | match b { 23 | 0 => Ok((input, TypingStatus::NotTyping)), 24 | 1 => Ok((input, TypingStatus::Typing)), 25 | _ => Err(nom::Err::Error(make_error(input, ErrorKind::Switch))), 26 | } 27 | } 28 | } 29 | 30 | /** Typing is a struct that holds typing status of user. 31 | 32 | This packet is used to transmit sender's typing status to a friend. 33 | 34 | Serialized form: 35 | 36 | Length | Content 37 | --------- | ------ 38 | `1` | `0x33` 39 | `1` | Typing status(0 = not typing, 1 = typing) 40 | 41 | */ 42 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 43 | pub struct Typing(TypingStatus); 44 | 45 | impl FromBytes for Typing { 46 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 47 | let (input, _) = tag("\x33")(input)?; 48 | let (input, status) = TypingStatus::from_bytes(input)?; 49 | Ok((input, Typing(status))) 50 | } 51 | } 52 | 53 | impl ToBytes for Typing { 54 | #[rustfmt::skip] 55 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 56 | do_gen!(buf, 57 | gen_be_u8!(0x33) >> 58 | gen_be_u8!(self.0 as u8) 59 | ) 60 | } 61 | } 62 | 63 | impl Typing { 64 | /// Create new Typing object. 65 | pub fn new(status: TypingStatus) -> Self { 66 | Typing(status) 67 | } 68 | } 69 | 70 | #[cfg(test)] 71 | mod tests { 72 | use super::*; 73 | 74 | encode_decode_test!(typing_encode_decode, Typing::new(TypingStatus::Typing)); 75 | } 76 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/conference/ping.rs: -------------------------------------------------------------------------------- 1 | /*! Ping message struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::tag; 7 | use nom::number::complete::{be_u16, be_u32}; 8 | 9 | /** Ping is a struct that holds info to send ping message to a conference. 10 | 11 | Serialized form: 12 | 13 | Length | Content 14 | --------- | ------ 15 | `1` | `0x63` 16 | `2` | `conference id` 17 | `2` | `peer id` 18 | `4` | `message id` 19 | `1` | `0x00` 20 | 21 | */ 22 | #[derive(Clone, Debug, Eq, PartialEq)] 23 | pub struct Ping { 24 | /// Id of conference 25 | pub conference_id: u16, 26 | /// Target peer id 27 | pub peer_id: u16, 28 | /// Id of this message 29 | pub message_id: u32, 30 | } 31 | 32 | impl FromBytes for Ping { 33 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 34 | let (input, _) = tag("\x63")(input)?; 35 | let (input, conference_id) = be_u16(input)?; 36 | let (input, peer_id) = be_u16(input)?; 37 | let (input, message_id) = be_u32(input)?; 38 | let (input, _) = tag("\x00")(input)?; 39 | Ok(( 40 | input, 41 | Ping { 42 | conference_id, 43 | peer_id, 44 | message_id, 45 | }, 46 | )) 47 | } 48 | } 49 | 50 | impl ToBytes for Ping { 51 | #[rustfmt::skip] 52 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 53 | do_gen!(buf, 54 | gen_be_u8!(0x63) >> 55 | gen_be_u16!(self.conference_id) >> 56 | gen_be_u16!(self.peer_id) >> 57 | gen_be_u32!(self.message_id) >> 58 | gen_be_u8!(0x00) 59 | ) 60 | } 61 | } 62 | 63 | impl Ping { 64 | /// Create new Ping object. 65 | pub fn new(conference_id: u16, peer_id: u16, message_id: u32) -> Self { 66 | Ping { 67 | conference_id, 68 | peer_id, 69 | message_id, 70 | } 71 | } 72 | } 73 | 74 | #[cfg(test)] 75 | mod tests { 76 | use super::*; 77 | 78 | encode_decode_test!(ping_encode_decode, Ping::new(1, 2, 3)); 79 | } 80 | -------------------------------------------------------------------------------- /tox_node/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.1.1 (Dec 27, 2020) 2 | 3 | * Update tox to 0.1.1 (#71) 4 | * Fix bug in lan discovery usage (#70) 5 | 6 | # 0.1.0 (Oct 7, 2020) 7 | 8 | * Update tox to 0.1.0 (#68) 9 | 10 | # 0.0.8 (May 27, 2019) 11 | 12 | * Add Dockerfile (#57, #62, #63) 13 | * We are in nixpkgs (#55) 14 | * Add subcommand derive-pk (#51) 15 | * Build Debian package (#50) 16 | 17 | # 0.0.7 (January 27, 2019) 18 | 19 | * Update dependencies (#47) 20 | * Add unit tests for cli args parsing (#46) 21 | * Do not require tcp-address arg (#45) 22 | * Move to Edition 2018 (#44) 23 | * Add limits for TCP connections count (#43) 24 | * Remove custom deserializer for log type (#42) 25 | 26 | # 0.0.6 (December 27, 2018) 27 | 28 | * Fix clippy warnings (#40) 29 | * Pretty fixes (examples, readme) (#39) 30 | * Add subcommand config (#37) 31 | * Use DHT ServerExt (#38) 32 | * Use bounded streams (#36) 33 | * Migrate to sodiumoxide 0.2 (#35) 34 | * Add counters to motd (#35) 35 | * Ignore bootstrap nodes with invalid address (#34) 36 | * Do not override log level (#32) 37 | 38 | # 0.0.5 (October 27, 2018) 39 | 40 | * Update tox core version (#30) 41 | * Add possibility to specify the secret key via environment variable (#29) 42 | * Add feature for MOTD templates (#28) 43 | * Fix deprecated item 'tokio::executor::thread_pool' (#27) 44 | * Handle onion errors (#26) 45 | * Use tox from github (#24) 46 | * Validate loaded public key (#23) 47 | 48 | # 0.0.4 (September 11, 2018) 49 | 50 | * Use tox from crates.io (#22) 51 | 52 | # 0.0.3 (September 7, 2018) 53 | 54 | * download libsodium v1.0.16 (#20) 55 | * Write logs to syslog (#19) 56 | * Add tcp ping sender (#18) 57 | * Use add_initial_bootstrap (#17) 58 | * Run clippy on travis (#16) 59 | * Enable udp ipv6 (#15) 60 | * Update toxcore; fix nodes adding (#14) 61 | * Use secret from arg or key file (#13) 62 | * Make TCP and UDP servers optional (#12) 63 | * Add CI files (#11) 64 | * Add TCP relay (#9) 65 | * Add options to enable/disable LAN discovery (#8) 66 | * Dehardcode several values (#7) 67 | * Save DHT keys to a binary file (#6) 68 | * Update Cargo.toml to improve the package (#5) 69 | * Add multithreading(#4) 70 | * Typo fix (#3) 71 | * DHT server initial implementation (#2) 72 | * Relicense to MIT (#1) 73 | -------------------------------------------------------------------------------- /tox_packet/src/relay/route_response.rs: -------------------------------------------------------------------------------- 1 | /*! RouteResponse packet 2 | */ 3 | 4 | use super::*; 5 | 6 | use crate::relay::connection_id::ConnectionId; 7 | use nom::bytes::complete::tag; 8 | use tox_binary_io::*; 9 | use tox_crypto::*; 10 | 11 | /** Sent by server to client. 12 | The response to the routing request, tell the client if the 13 | routing request succeeded (valid `connection_id`) and if it did, 14 | tell them the id of the connection (`connection_id`). The public 15 | key sent in the routing request is also sent in the response so 16 | that the client can send many requests at the same time to the 17 | server without having code to track which response belongs to which public key. 18 | 19 | Serialized form: 20 | 21 | Length | Content 22 | ------ | ------ 23 | `1` | `0x01` 24 | `1` | connection_id [ `0x10` .. `0xFF` ] 25 | `32` | Public Key 26 | 27 | */ 28 | #[derive(Debug, PartialEq, Eq, Clone)] 29 | pub struct RouteResponse { 30 | /// The id of the requested PK 31 | pub connection_id: ConnectionId, 32 | /// The requested PK 33 | pub pk: PublicKey, 34 | } 35 | 36 | impl FromBytes for RouteResponse { 37 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 38 | let (input, _) = tag("\x01")(input)?; 39 | let (input, connection_id) = ConnectionId::from_bytes(input)?; 40 | let (input, pk) = PublicKey::from_bytes(input)?; 41 | Ok((input, RouteResponse { connection_id, pk })) 42 | } 43 | } 44 | 45 | impl ToBytes for RouteResponse { 46 | #[rustfmt::skip] 47 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 48 | do_gen!(buf, 49 | gen_be_u8!(0x01) >> 50 | gen_call!(|buf, connection_id| ConnectionId::to_bytes(connection_id, buf), &self.connection_id) >> 51 | gen_slice!(self.pk.as_bytes()) 52 | ) 53 | } 54 | } 55 | 56 | #[cfg(test)] 57 | mod test { 58 | use rand::thread_rng; 59 | 60 | use super::*; 61 | 62 | encode_decode_test!( 63 | route_response_encode_decode, 64 | RouteResponse { 65 | connection_id: ConnectionId::from_index(1), 66 | pk: SecretKey::generate(&mut thread_rng()).public_key() 67 | } 68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /tox_packet/src/relay/disconnect_notification.rs: -------------------------------------------------------------------------------- 1 | /*! DisconnectNotification packet 2 | */ 3 | 4 | use super::*; 5 | 6 | use crate::relay::connection_id::ConnectionId; 7 | use nom::bytes::complete::tag; 8 | use tox_binary_io::*; 9 | 10 | /** Sent by client to server. 11 | Sent when client wants the server to forget about the connection related 12 | to the connection_id in the notification. Server must remove this connection 13 | and must be able to reuse the `connection_id` for another connection. If the 14 | connection was connected the server must send a disconnect notification to the 15 | other client. The other client must think that this client has simply 16 | disconnected from the TCP server. 17 | 18 | Sent by server to client. 19 | Sent by the server to the client to tell them that the connection with 20 | `connection_id` that was connected is now disconnected. It is sent either 21 | when the other client of the connection disconnect or when they tell the 22 | server to kill the connection (see above). 23 | 24 | Serialized form: 25 | 26 | Length | Content 27 | ------ | ------ 28 | `1` | `0x03` 29 | `1` | connection_id [ `0x10` .. `0xFF` ] 30 | 31 | */ 32 | #[derive(Debug, PartialEq, Eq, Clone)] 33 | pub struct DisconnectNotification { 34 | /// The id of the disconnected client 35 | pub connection_id: ConnectionId, 36 | } 37 | 38 | impl FromBytes for DisconnectNotification { 39 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 40 | let (input, _) = tag("\x03")(input)?; 41 | let (input, connection_id) = ConnectionId::from_bytes(input)?; 42 | Ok((input, DisconnectNotification { connection_id })) 43 | } 44 | } 45 | 46 | impl ToBytes for DisconnectNotification { 47 | #[rustfmt::skip] 48 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 49 | do_gen!(buf, 50 | gen_be_u8!(0x03) >> 51 | gen_call!(|buf, connection_id| ConnectionId::to_bytes(connection_id, buf), &self.connection_id) 52 | ) 53 | } 54 | } 55 | 56 | #[cfg(test)] 57 | mod test { 58 | use super::*; 59 | 60 | encode_decode_test!( 61 | disconnect_notification_encode_decode, 62 | DisconnectNotification { 63 | connection_id: ConnectionId::from_index(1) 64 | } 65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/user_status.rs: -------------------------------------------------------------------------------- 1 | /*! UserStatus struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::tag; 7 | use nom::error::{make_error, ErrorKind}; 8 | use nom::number::complete::le_u8; 9 | 10 | /// Status of user 11 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 12 | pub enum PeerStatus { 13 | /// Online 14 | Online = 0, 15 | /// Away 16 | Away, 17 | /// Online but I am busy 18 | Busy, 19 | } 20 | 21 | impl FromBytes for PeerStatus { 22 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 23 | let (input, b) = le_u8(input)?; 24 | match b { 25 | 0 => Ok((input, PeerStatus::Online)), 26 | 1 => Ok((input, PeerStatus::Away)), 27 | 2 => Ok((input, PeerStatus::Busy)), 28 | _ => Err(nom::Err::Error(make_error(input, ErrorKind::Switch))), 29 | } 30 | } 31 | } 32 | 33 | /** UserStatus is a struct that holds status of user. 34 | 35 | This packet is used to transmit sender's status to a friend. 36 | Every time a friend become online or my status is changed, 37 | this packet is sent to the friend or to all friends of mine. 38 | 39 | Serialized form: 40 | 41 | Length | Content 42 | --------- | ------ 43 | `1` | `0x32` 44 | `1` | My status(0 = online, 1 = away, 2 = busy) 45 | 46 | */ 47 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 48 | pub struct UserStatus(PeerStatus); 49 | 50 | impl FromBytes for UserStatus { 51 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 52 | let (input, _) = tag("\x32")(input)?; 53 | let (input, status) = PeerStatus::from_bytes(input)?; 54 | Ok((input, UserStatus(status))) 55 | } 56 | } 57 | 58 | impl ToBytes for UserStatus { 59 | #[rustfmt::skip] 60 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 61 | do_gen!(buf, 62 | gen_be_u8!(0x32) >> 63 | gen_be_u8!(self.0 as u8) 64 | ) 65 | } 66 | } 67 | 68 | impl UserStatus { 69 | /// Create new UserStatus object. 70 | pub fn new(status: PeerStatus) -> Self { 71 | UserStatus(status) 72 | } 73 | } 74 | 75 | #[cfg(test)] 76 | mod tests { 77 | use super::*; 78 | 79 | encode_decode_test!(user_status_encode_decode, UserStatus::new(PeerStatus::Online)); 80 | } 81 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/conference/peer_online.rs: -------------------------------------------------------------------------------- 1 | /*! Peer online message struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::tag; 7 | use nom::number::complete::be_u16; 8 | 9 | /** PeerOnline is a struct that holds info to notify adding new peer to a conference. 10 | 11 | Serialized form: 12 | 13 | Length | Content 14 | --------- | ------ 15 | `1` | `0x60` 16 | `1` | `0x00` 17 | `2` | `conference id` 18 | `1` | `conference type`(0: text, 1: audio) 19 | `32` | `unique id` 20 | 21 | */ 22 | #[derive(Clone, Debug, Eq, PartialEq)] 23 | pub struct PeerOnline { 24 | /// Id of conference 25 | pub conference_id: u16, 26 | /// Type of conference 27 | pub conference_type: ConferenceType, 28 | /// Unique id of conference 29 | pub unique_id: ConferenceUid, 30 | } 31 | 32 | impl FromBytes for PeerOnline { 33 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 34 | let (input, _) = tag("\x61")(input)?; 35 | let (input, conference_id) = be_u16(input)?; 36 | let (input, conference_type) = ConferenceType::from_bytes(input)?; 37 | let (input, unique_id) = ConferenceUid::from_bytes(input)?; 38 | Ok(( 39 | input, 40 | PeerOnline { 41 | conference_id, 42 | conference_type, 43 | unique_id, 44 | }, 45 | )) 46 | } 47 | } 48 | 49 | impl ToBytes for PeerOnline { 50 | #[rustfmt::skip] 51 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 52 | do_gen!(buf, 53 | gen_be_u8!(0x61) >> 54 | gen_be_u16!(self.conference_id) >> 55 | gen_be_u8!(self.conference_type as u8) >> 56 | gen_slice!(self.unique_id.0) 57 | ) 58 | } 59 | } 60 | 61 | impl PeerOnline { 62 | /// Create new PeerOnline object. 63 | pub fn new(conference_id: u16, conference_type: ConferenceType, unique_id: ConferenceUid) -> Self { 64 | PeerOnline { 65 | conference_id, 66 | conference_type, 67 | unique_id, 68 | } 69 | } 70 | } 71 | 72 | #[cfg(test)] 73 | mod tests { 74 | use super::*; 75 | 76 | encode_decode_test!( 77 | peer_noline_encode_decode, 78 | PeerOnline::new(1, ConferenceType::Text, ConferenceUid([42; CONFERENCE_UID_BYTES])) 79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /tox_packet/src/onion/inner_onion_response.rs: -------------------------------------------------------------------------------- 1 | /*! InnerOnionResponse enum 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::branch::alt; 7 | use nom::combinator::map; 8 | use tox_binary_io::*; 9 | 10 | /** Onion responses that can be enclosed in onion packets and sent through onion 11 | path. 12 | 13 | Onion allows only two types of packets to be sent as a response through onion 14 | paths: `OnionAnnounceResponse` and `OnionDataResponse`. 15 | */ 16 | #[derive(Clone, Debug, Eq, PartialEq)] 17 | pub enum InnerOnionResponse { 18 | /// [`OnionAnnounceResponse`](./struct.OnionAnnounceResponse.html) structure. 19 | OnionAnnounceResponse(OnionAnnounceResponse), 20 | /// [`OnionDataResponse`](./struct.OnionDataResponse.html) structure. 21 | OnionDataResponse(OnionDataResponse), 22 | } 23 | 24 | impl ToBytes for InnerOnionResponse { 25 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 26 | match *self { 27 | InnerOnionResponse::OnionAnnounceResponse(ref inner) => inner.to_bytes(buf), 28 | InnerOnionResponse::OnionDataResponse(ref inner) => inner.to_bytes(buf), 29 | } 30 | } 31 | } 32 | 33 | impl FromBytes for InnerOnionResponse { 34 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 35 | alt(( 36 | map( 37 | OnionAnnounceResponse::from_bytes, 38 | InnerOnionResponse::OnionAnnounceResponse, 39 | ), 40 | map(OnionDataResponse::from_bytes, InnerOnionResponse::OnionDataResponse), 41 | ))(input) 42 | } 43 | } 44 | 45 | #[cfg(test)] 46 | mod tests { 47 | use rand::thread_rng; 48 | 49 | use super::*; 50 | 51 | encode_decode_test!( 52 | inner_onion_announce_response_encode_decode, 53 | InnerOnionResponse::OnionAnnounceResponse(OnionAnnounceResponse { 54 | sendback_data: 12345, 55 | nonce: [42; ::NonceSize::USIZE], 56 | payload: vec![42; 123] 57 | }) 58 | ); 59 | 60 | encode_decode_test!( 61 | inner_onion_data_response_encode_decode, 62 | InnerOnionResponse::OnionDataResponse(OnionDataResponse { 63 | nonce: [42; ::NonceSize::USIZE], 64 | temporary_pk: SecretKey::generate(&mut thread_rng()).public_key(), 65 | payload: vec![42; 123] 66 | }) 67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/conference/invite.rs: -------------------------------------------------------------------------------- 1 | /*! Invite message struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::tag; 7 | use nom::number::complete::be_u16; 8 | 9 | /** Invite is a struct that holds info to invite a peer to a conference. 10 | 11 | Serialized form: 12 | 13 | Length | Content 14 | --------- | ------ 15 | `1` | `0x60` 16 | `1` | `0x00` 17 | `2` | `conference id` 18 | `1` | `conference type`(0: text, 1: audio) 19 | `32` | `unique id` 20 | 21 | */ 22 | #[derive(Clone, Debug, Eq, PartialEq)] 23 | pub struct Invite { 24 | /// Id of conference 25 | pub conference_id: u16, 26 | /// Type of conference 27 | pub conference_type: ConferenceType, 28 | /// Unique id of conference 29 | pub unique_id: ConferenceUid, 30 | } 31 | 32 | impl FromBytes for Invite { 33 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 34 | let (input, _) = tag("\x60")(input)?; 35 | let (input, _) = tag("\x00")(input)?; 36 | let (input, conference_id) = be_u16(input)?; 37 | let (input, conference_type) = ConferenceType::from_bytes(input)?; 38 | let (input, unique_id) = ConferenceUid::from_bytes(input)?; 39 | Ok(( 40 | input, 41 | Invite { 42 | conference_id, 43 | conference_type, 44 | unique_id, 45 | }, 46 | )) 47 | } 48 | } 49 | 50 | impl ToBytes for Invite { 51 | #[rustfmt::skip] 52 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 53 | do_gen!(buf, 54 | gen_be_u8!(0x60) >> 55 | gen_be_u8!(0x00) >> 56 | gen_be_u16!(self.conference_id) >> 57 | gen_be_u8!(self.conference_type as u8) >> 58 | gen_slice!(self.unique_id.0) 59 | ) 60 | } 61 | } 62 | 63 | impl Invite { 64 | /// Create new Invite object. 65 | pub fn new(conference_id: u16, conference_type: ConferenceType, unique_id: ConferenceUid) -> Self { 66 | Invite { 67 | conference_id, 68 | conference_type, 69 | unique_id, 70 | } 71 | } 72 | } 73 | 74 | #[cfg(test)] 75 | mod tests { 76 | use super::*; 77 | 78 | encode_decode_test!( 79 | invite_encode_decode, 80 | Invite::new(1, ConferenceType::Text, ConferenceUid([42; CONFERENCE_UID_BYTES])) 81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /tox_packet/src/relay/onion_response.rs: -------------------------------------------------------------------------------- 1 | /*! OnionResponse packet 2 | */ 3 | 4 | use super::*; 5 | 6 | use crate::onion::InnerOnionResponse; 7 | use nom::bytes::complete::tag; 8 | use tox_binary_io::*; 9 | 10 | /** Sent by server to client. 11 | The server just sends payload from `OnionResponse1` packet that it got from a 12 | UDP node to the client. 13 | 14 | Serialized form: 15 | 16 | Length | Content 17 | -------- | ------ 18 | `1` | `0x09` 19 | variable | Payload 20 | 21 | */ 22 | #[derive(Debug, PartialEq, Eq, Clone)] 23 | pub struct OnionResponse { 24 | /// Inner onion response 25 | pub payload: InnerOnionResponse, 26 | } 27 | 28 | impl FromBytes for OnionResponse { 29 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 30 | let (input, _) = tag("\x09")(input)?; 31 | let (input, payload) = InnerOnionResponse::from_bytes(input)?; 32 | Ok((input, OnionResponse { payload })) 33 | } 34 | } 35 | 36 | impl ToBytes for OnionResponse { 37 | #[rustfmt::skip] 38 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 39 | do_gen!(buf, 40 | gen_be_u8!(0x09) >> 41 | gen_call!(|buf, payload| InnerOnionResponse::to_bytes(payload, buf), &self.payload) 42 | ) 43 | } 44 | } 45 | 46 | #[cfg(test)] 47 | mod test { 48 | use super::*; 49 | 50 | use crate::onion::{OnionAnnounceResponse, OnionDataResponse}; 51 | use crypto_box::{ 52 | aead::{generic_array::typenum::marker_traits::Unsigned, AeadCore}, 53 | SalsaBox, SecretKey, 54 | }; 55 | use rand::thread_rng; 56 | 57 | encode_decode_test!( 58 | onion_response_with_announce_encode_decode, 59 | OnionResponse { 60 | payload: InnerOnionResponse::OnionAnnounceResponse(OnionAnnounceResponse { 61 | sendback_data: 12345, 62 | nonce: [42; ::NonceSize::USIZE], 63 | payload: vec![42; 123] 64 | }) 65 | } 66 | ); 67 | 68 | encode_decode_test!( 69 | onion_response_with_data_encode_decode, 70 | OnionResponse { 71 | payload: InnerOnionResponse::OnionDataResponse(OnionDataResponse { 72 | nonce: [42; ::NonceSize::USIZE], 73 | temporary_pk: SecretKey::generate(&mut thread_rng()).public_key(), 74 | payload: vec![42; 123] 75 | }) 76 | } 77 | ); 78 | } 79 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | build: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: [ubuntu-latest, macos-latest, windows-latest] 15 | rust: 16 | - stable 17 | - 1.65 18 | steps: 19 | - uses: actions/checkout@v2 20 | - uses: actions-rs/toolchain@v1 21 | with: 22 | profile: minimal 23 | toolchain: ${{ matrix.rust }} 24 | override: true 25 | - name: Build 26 | run: cargo build --verbose 27 | - name: Run tests 28 | run: cargo test --verbose 29 | - name: Build docs 30 | run: cargo doc 31 | 32 | clippy: 33 | name: Clippy 34 | runs-on: ubuntu-latest 35 | strategy: 36 | matrix: 37 | rust: 38 | - stable 39 | steps: 40 | - uses: actions/checkout@v2 41 | - uses: actions-rs/toolchain@v1 42 | with: 43 | profile: minimal 44 | toolchain: ${{ matrix.rust }} 45 | override: true 46 | - run: rustup component add clippy 47 | - uses: actions-rs/cargo@v1 48 | with: 49 | command: clippy 50 | args: -- -D warnings 51 | 52 | rustfmt: 53 | name: Rustfmt 54 | runs-on: ubuntu-latest 55 | strategy: 56 | matrix: 57 | rust: 58 | - stable 59 | steps: 60 | - uses: actions/checkout@v2 61 | - uses: actions-rs/toolchain@v1 62 | with: 63 | profile: minimal 64 | toolchain: ${{ matrix.rust }} 65 | override: true 66 | - run: rustup component add rustfmt 67 | - uses: actions-rs/cargo@v1 68 | with: 69 | command: fmt 70 | args: --check 71 | 72 | coverage: 73 | name: Code coverage 74 | runs-on: ubuntu-latest 75 | steps: 76 | - name: Checkout repository 77 | uses: actions/checkout@v2 78 | - name: Install stable toolchain 79 | uses: actions-rs/toolchain@v1 80 | with: 81 | toolchain: stable 82 | override: true 83 | - name: Run cargo-tarpaulin 84 | uses: actions-rs/tarpaulin@v0.1 85 | with: 86 | version: '0.18.5' 87 | args: '-o Lcov -- --test-threads 1' 88 | - name: Run Coveralls 89 | uses: coverallsapp/github-action@master 90 | with: 91 | github-token: ${{ secrets.GITHUB_TOKEN }} 92 | path-to-lcov: ./lcov.info 93 | -------------------------------------------------------------------------------- /examples/dht_server.rs: -------------------------------------------------------------------------------- 1 | // an example of DHT node with current code 2 | // 3 | #![type_length_limit = "4194304"] 4 | 5 | #[macro_use] 6 | extern crate log; 7 | 8 | use anyhow::Error; 9 | use futures::channel::mpsc; 10 | use futures::future::FutureExt; 11 | use rand::thread_rng; 12 | 13 | use std::net::SocketAddr; 14 | 15 | use tox_core::dht::lan_discovery::*; 16 | use tox_core::dht::server::Server as DhtServer; 17 | use tox_core::dht::server_ext::dht_run_socket; 18 | use tox_core::stats::Stats; 19 | use tox_core::udp::Server as UdpServer; 20 | use tox_crypto::*; 21 | use tox_packet::dht::packed_node::PackedNode; 22 | 23 | mod common; 24 | 25 | fn as_packed_node(pk: &str, saddr: &str) -> PackedNode { 26 | let pk_bytes: [u8; 32] = hex::FromHex::from_hex(pk).unwrap(); 27 | let pk = PublicKey::from(pk_bytes); 28 | let saddr: SocketAddr = saddr.parse().unwrap(); 29 | 30 | PackedNode::new(saddr, pk) 31 | } 32 | 33 | #[tokio::main] 34 | async fn main() -> Result<(), Error> { 35 | env_logger::init(); 36 | 37 | let mut rng = thread_rng(); 38 | let server_sk = SecretKey::generate(&mut rng); 39 | let server_pk = server_sk.public_key(); 40 | 41 | // Create a channel for server to communicate with network 42 | let (tx, rx) = mpsc::channel(32); 43 | 44 | let local_addr: SocketAddr = "0.0.0.0:33445".parse()?; // 0.0.0.0 for IPv4 45 | 46 | // let local_addr: SocketAddr = "[::]:33445".parse()?; // [::] for IPv6 47 | 48 | let stats = Stats::new(); 49 | 50 | let mut lan_discovery_sender = LanDiscoverySender::new(tx.clone(), server_pk.clone(), local_addr.is_ipv6()); 51 | 52 | let mut dht_server = DhtServer::new(tx, server_pk, server_sk); 53 | dht_server.set_bootstrap_info(3_000_000_000, Box::new(|_| b"This is tox-rs".to_vec())); 54 | dht_server.enable_lan_discovery(true); 55 | dht_server.enable_ipv6_mode(local_addr.is_ipv6()); 56 | 57 | // Bootstrap from nodes 58 | for &(pk, saddr) in &common::BOOTSTRAP_NODES { 59 | let bootstrap_pn = as_packed_node(pk, saddr); 60 | 61 | dht_server.add_initial_bootstrap(bootstrap_pn); 62 | } 63 | 64 | let udp_server = UdpServer::new(dht_server); 65 | 66 | let socket = common::bind_socket(local_addr).await; 67 | 68 | info!("Running DHT server on {}", local_addr); 69 | 70 | futures::select! { 71 | res = dht_run_socket(&udp_server, socket, rx, stats).fuse() => res.map_err(Error::from), 72 | res = lan_discovery_sender.run().fuse() => res.map_err(Error::from), 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/conference/kill_peer.rs: -------------------------------------------------------------------------------- 1 | /*! Kill peer message struct. 2 | */ 3 | 4 | use super::*; 5 | use nom::bytes::complete::tag; 6 | use nom::number::complete::{be_u16, be_u32}; 7 | 8 | /** KillPeer is a struct that holds info to send kill peer message to a conference. 9 | 10 | When a peer quit a conference, right before quit, it send this packet. 11 | 12 | Serialized form: 13 | 14 | Length | Content 15 | --------- | ------ 16 | `1` | `0x63` 17 | `2` | `conference id` 18 | `2` | `peer id` 19 | `4` | `message id` 20 | `1` | `0x11` 21 | `2` | `peer id` 22 | 23 | */ 24 | #[derive(Clone, Debug, Eq, PartialEq)] 25 | pub struct KillPeer { 26 | /// Id of conference 27 | pub conference_id: u16, 28 | /// Target peer id 29 | pub peer_id: u16, 30 | /// Id of this message 31 | pub message_id: u32, 32 | /// Peer id to kill 33 | pub kill_peer_id: u16, 34 | } 35 | 36 | impl FromBytes for KillPeer { 37 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 38 | let (input, _) = tag("\x63")(input)?; 39 | let (input, conference_id) = be_u16(input)?; 40 | let (input, peer_id) = be_u16(input)?; 41 | let (input, message_id) = be_u32(input)?; 42 | let (input, _) = tag("\x11")(input)?; 43 | let (input, kill_peer_id) = be_u16(input)?; 44 | Ok(( 45 | input, 46 | KillPeer { 47 | conference_id, 48 | peer_id, 49 | message_id, 50 | kill_peer_id, 51 | }, 52 | )) 53 | } 54 | } 55 | 56 | impl ToBytes for KillPeer { 57 | #[rustfmt::skip] 58 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 59 | do_gen!(buf, 60 | gen_be_u8!(0x63) >> 61 | gen_be_u16!(self.conference_id) >> 62 | gen_be_u16!(self.peer_id) >> 63 | gen_be_u32!(self.message_id) >> 64 | gen_be_u8!(0x11) >> 65 | gen_be_u16!(self.kill_peer_id) 66 | ) 67 | } 68 | } 69 | 70 | impl KillPeer { 71 | /// Create new KillPeer object. 72 | pub fn new(conference_id: u16, peer_id: u16, message_id: u32, kill_peer_id: u16) -> Self { 73 | KillPeer { 74 | conference_id, 75 | peer_id, 76 | message_id, 77 | kill_peer_id, 78 | } 79 | } 80 | } 81 | 82 | #[cfg(test)] 83 | mod tests { 84 | use super::*; 85 | 86 | encode_decode_test!(kill_peer_encode_decode, KillPeer::new(1, 2, 3, 4)); 87 | } 88 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/file_transfer/file_data.rs: -------------------------------------------------------------------------------- 1 | /*! FileData struct. 2 | It is used to transfer chunk of file data to a friend. 3 | */ 4 | 5 | use nom::{ 6 | bytes::complete::tag, 7 | combinator::{rest, rest_len, verify}, 8 | number::complete::le_u8, 9 | AsBytes, 10 | }; 11 | 12 | use super::*; 13 | 14 | /** FileData is a struct that holds chunk of data of a file to transfer to a friend. 15 | 16 | This packet is used to transfer sender's data file to a friend. 17 | It holds `file_id` which is one byte long, means that a tox client can send maximum 256 files concurrently. 18 | And it means that two friends can send 512 files to each other concurrently. 19 | 20 | Serialized form: 21 | 22 | Length | Content 23 | --------- | ------ 24 | `1` | `0x52` 25 | `1` | `file_id` 26 | `0..1371` | file data piece 27 | 28 | */ 29 | 30 | #[derive(Clone, Debug, Eq, PartialEq)] 31 | pub struct FileData { 32 | file_id: u8, 33 | data: Vec, 34 | } 35 | 36 | impl FromBytes for FileData { 37 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 38 | let (input, _) = tag("\x52")(input)?; 39 | let (input, file_id) = le_u8(input)?; 40 | let (input, _) = verify(rest_len, |len| *len <= MAX_FILE_DATA_SIZE)(input)?; 41 | let (input, data) = rest(input)?; 42 | Ok(( 43 | input, 44 | FileData { 45 | file_id, 46 | data: data.to_vec(), 47 | }, 48 | )) 49 | } 50 | } 51 | 52 | impl ToBytes for FileData { 53 | #[rustfmt::skip] 54 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 55 | do_gen!(buf, 56 | gen_be_u8!(0x52) >> 57 | gen_be_u8!(self.file_id) >> 58 | gen_cond!(self.data.len() > MAX_FILE_DATA_SIZE, |buf| gen_error(buf, 0)) >> 59 | gen_slice!(self.data.as_bytes()) 60 | ) 61 | } 62 | } 63 | 64 | impl FileData { 65 | /// Create new FileData object. 66 | pub fn new(file_id: u8, data: Vec) -> Self { 67 | FileData { file_id, data } 68 | } 69 | } 70 | 71 | #[cfg(test)] 72 | mod tests { 73 | use super::*; 74 | 75 | encode_decode_test!(file_data_encode_decode, FileData::new(1, vec![1, 2, 3, 4])); 76 | 77 | #[test] 78 | fn file_data_from_bytes_too_long() { 79 | let mut data = vec![0x52, 1]; 80 | let long_data = [47; MAX_FILE_DATA_SIZE + 1]; 81 | data.extend_from_slice(&long_data); 82 | assert!(FileData::from_bytes(&data).is_err()); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/message.rs: -------------------------------------------------------------------------------- 1 | /*! Message struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::tag; 7 | use nom::combinator::{map_res, rest, verify}; 8 | use std::str; 9 | 10 | /// Maximum size in bytes of message string of message packet 11 | const MAX_MESSAGE_DATA_SIZE: usize = 1372; 12 | 13 | /** Message is a struct that holds string of my message. 14 | 15 | This packet is used to transmit sender's message to a friend. 16 | 17 | Serialized form: 18 | 19 | Length | Content 20 | --------- | ------ 21 | `1` | `0x40` 22 | `0..1372` | UTF8 byte string 23 | 24 | */ 25 | #[derive(Clone, Debug, Eq, PartialEq)] 26 | pub struct Message { 27 | msg: String, 28 | } 29 | 30 | impl FromBytes for Message { 31 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 32 | let (input, _) = tag("\x40")(input)?; 33 | let (input, msg) = map_res( 34 | verify(rest, |msg: &[u8]| msg.len() <= MAX_MESSAGE_DATA_SIZE), 35 | str::from_utf8, 36 | )(input)?; 37 | Ok((input, Message { msg: msg.to_string() })) 38 | } 39 | } 40 | 41 | impl ToBytes for Message { 42 | #[rustfmt::skip] 43 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 44 | do_gen!(buf, 45 | gen_be_u8!(0x40) >> 46 | gen_cond!(self.msg.len() > MAX_MESSAGE_DATA_SIZE, |buf| gen_error(buf, 0)) >> 47 | gen_slice!(self.msg.as_bytes()) 48 | ) 49 | } 50 | } 51 | 52 | impl Message { 53 | /// Create new Message object. 54 | pub fn new(msg: String) -> Self { 55 | Message { msg } 56 | } 57 | } 58 | 59 | #[cfg(test)] 60 | mod tests { 61 | use super::*; 62 | 63 | encode_decode_test!(message_encode_decode, Message::new("1234".to_string())); 64 | 65 | #[test] 66 | fn message_from_bytes_encoding_error() { 67 | let err_string = vec![0, 159, 146, 150]; // not UTF8 bytes. 68 | assert!(Message::from_bytes(&err_string).is_err()); 69 | } 70 | 71 | #[test] 72 | fn message_from_bytes_overflow() { 73 | let large_string = vec![32; MAX_MESSAGE_DATA_SIZE + 1]; 74 | assert!(Message::from_bytes(&large_string).is_err()); 75 | } 76 | 77 | #[test] 78 | fn message_to_bytes_overflow() { 79 | let large_string = String::from_utf8(vec![32u8; MAX_MESSAGE_DATA_SIZE + 1]).unwrap(); 80 | let large_msg = Message::new(large_string); 81 | let mut buf = [0; MAX_MESSAGE_DATA_SIZE + 1]; // `1` is for packet_id. 82 | assert!(large_msg.to_bytes((&mut buf, 0)).is_err()); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/conference/freeze_peer.rs: -------------------------------------------------------------------------------- 1 | /*! Freeze peer message struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::tag; 7 | use nom::number::complete::{be_u16, be_u32}; 8 | 9 | /** Freeze peer is a struct that holds info to send freeze peer message to a conference. 10 | 11 | When a peer quit running, it need to freeze conference rather than remove it. 12 | 13 | Serialized form: 14 | 15 | Length | Content 16 | --------- | ------ 17 | `1` | `0x63` 18 | `2` | `conference id` 19 | `2` | `peer id` 20 | `4` | `message id` 21 | `1` | `0x12` 22 | `2` | `peer id` 23 | 24 | */ 25 | #[derive(Clone, Debug, Eq, PartialEq)] 26 | pub struct FreezePeer { 27 | /// Id of conference 28 | pub conference_id: u16, 29 | /// Target peer id 30 | pub peer_id: u16, 31 | /// Id of this message 32 | pub message_id: u32, 33 | /// Peer id freezed 34 | pub freeze_peer_id: u16, 35 | } 36 | 37 | impl FromBytes for FreezePeer { 38 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 39 | let (input, _) = tag("\x63")(input)?; 40 | let (input, conference_id) = be_u16(input)?; 41 | let (input, peer_id) = be_u16(input)?; 42 | let (input, message_id) = be_u32(input)?; 43 | let (input, _) = tag("\x12")(input)?; 44 | let (input, freeze_peer_id) = be_u16(input)?; 45 | Ok(( 46 | input, 47 | FreezePeer { 48 | conference_id, 49 | peer_id, 50 | message_id, 51 | freeze_peer_id, 52 | }, 53 | )) 54 | } 55 | } 56 | 57 | impl ToBytes for FreezePeer { 58 | #[rustfmt::skip] 59 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 60 | do_gen!(buf, 61 | gen_be_u8!(0x63) >> 62 | gen_be_u16!(self.conference_id) >> 63 | gen_be_u16!(self.peer_id) >> 64 | gen_be_u32!(self.message_id) >> 65 | gen_be_u8!(0x12) >> 66 | gen_be_u16!(self.freeze_peer_id) 67 | ) 68 | } 69 | } 70 | 71 | impl FreezePeer { 72 | /// Create new FreezePeer object. 73 | pub fn new(conference_id: u16, peer_id: u16, message_id: u32, freeze_peer_id: u16) -> Self { 74 | FreezePeer { 75 | conference_id, 76 | peer_id, 77 | message_id, 78 | freeze_peer_id, 79 | } 80 | } 81 | } 82 | 83 | #[cfg(test)] 84 | mod tests { 85 | use super::*; 86 | 87 | encode_decode_test!(freeze_peer_encode_decode, FreezePeer::new(1, 2, 3, 4)); 88 | } 89 | -------------------------------------------------------------------------------- /tox_packet/src/relay/oob_send.rs: -------------------------------------------------------------------------------- 1 | /*! OobSend packet 2 | */ 3 | 4 | use super::*; 5 | 6 | use tox_binary_io::*; 7 | use tox_crypto::*; 8 | 9 | use nom::bytes::complete::tag; 10 | use nom::combinator::rest; 11 | 12 | /** Sent by client to server. 13 | If a peer with private key equal to the key they announced themselves with is 14 | connected, the data in the OOB send packet will be sent to that peer as an 15 | OOB recv packet. If no such peer is connected, the packet is discarded. The 16 | toxcore `TCP_server` implementation has a hard maximum OOB data length of 1024. 17 | 1024 was picked because it is big enough for the `net_crypto` packets related 18 | to the handshake and is large enough that any changes to the protocol would not 19 | require breaking `TCP server`. It is however not large enough for the bigges 20 | `net_crypto` packets sent with an established `net_crypto` connection to 21 | prevent sending those via OOB packets. 22 | 23 | OOB packets can be used just like normal data packets however the extra size 24 | makes sending data only through them less efficient than data packets. 25 | 26 | Serialized form: 27 | 28 | Length | Content 29 | -------- | ------ 30 | `1` | `0x06` 31 | `32` | Public Key 32 | variable | Data 33 | 34 | */ 35 | #[derive(Debug, PartialEq, Eq, Clone)] 36 | pub struct OobSend { 37 | /// Public Key of the receiver 38 | pub destination_pk: PublicKey, 39 | /// OOB data packet 40 | pub data: Vec, 41 | } 42 | 43 | impl FromBytes for OobSend { 44 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 45 | let (input, _) = tag("\x06")(input)?; 46 | let (input, destination_pk) = PublicKey::from_bytes(input)?; 47 | let (input, data) = rest(input)?; 48 | Ok(( 49 | input, 50 | OobSend { 51 | destination_pk, 52 | data: data.to_vec(), 53 | }, 54 | )) 55 | } 56 | } 57 | 58 | impl ToBytes for OobSend { 59 | #[rustfmt::skip] 60 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 61 | do_gen!(buf, 62 | gen_be_u8!(0x06) >> 63 | gen_slice!(self.destination_pk.as_bytes()) >> 64 | gen_slice!(self.data.as_slice()) 65 | ) 66 | } 67 | } 68 | 69 | #[cfg(test)] 70 | mod test { 71 | use rand::thread_rng; 72 | 73 | use super::*; 74 | 75 | encode_decode_test!( 76 | oob_send_encode_decode, 77 | OobSend { 78 | destination_pk: SecretKey::generate(&mut thread_rng()).public_key(), 79 | data: vec![42; 123] 80 | } 81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /tox_packet/src/onion/inner_onion_request.rs: -------------------------------------------------------------------------------- 1 | /*! InnerOnionRequest enum 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::branch::alt; 7 | use nom::combinator::map; 8 | use tox_binary_io::*; 9 | 10 | /** Onion requests that can be enclosed in onion packets and sent through onion 11 | path. 12 | 13 | Onion allows only two types of packets to be sent as a request through onion 14 | paths: `OnionAnnounceRequest` and `OnionDataRequest`. 15 | */ 16 | #[derive(Clone, Debug, Eq, PartialEq)] 17 | pub enum InnerOnionRequest { 18 | /// [`InnerOnionAnnounceRequest`](./struct.InnerOnionAnnounceRequest.html) structure. 19 | InnerOnionAnnounceRequest(InnerOnionAnnounceRequest), 20 | /// [`InnerOnionDataRequest`](./struct.InnerOnionDataRequest.html) structure. 21 | InnerOnionDataRequest(InnerOnionDataRequest), 22 | } 23 | 24 | impl ToBytes for InnerOnionRequest { 25 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 26 | match *self { 27 | InnerOnionRequest::InnerOnionAnnounceRequest(ref inner) => inner.to_bytes(buf), 28 | InnerOnionRequest::InnerOnionDataRequest(ref inner) => inner.to_bytes(buf), 29 | } 30 | } 31 | } 32 | 33 | impl FromBytes for InnerOnionRequest { 34 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 35 | alt(( 36 | map( 37 | InnerOnionAnnounceRequest::from_bytes, 38 | InnerOnionRequest::InnerOnionAnnounceRequest, 39 | ), 40 | map( 41 | InnerOnionDataRequest::from_bytes, 42 | InnerOnionRequest::InnerOnionDataRequest, 43 | ), 44 | ))(input) 45 | } 46 | } 47 | 48 | #[cfg(test)] 49 | mod tests { 50 | use rand::thread_rng; 51 | 52 | use super::*; 53 | 54 | encode_decode_test!( 55 | inner_onion_announce_request_encode_decode, 56 | InnerOnionRequest::InnerOnionAnnounceRequest(InnerOnionAnnounceRequest { 57 | nonce: [42; ::NonceSize::USIZE], 58 | pk: SecretKey::generate(&mut thread_rng()).public_key(), 59 | payload: vec![42; 123] 60 | }) 61 | ); 62 | 63 | encode_decode_test!( 64 | inner_onion_data_request_encode_decode, 65 | InnerOnionRequest::InnerOnionDataRequest(InnerOnionDataRequest { 66 | destination_pk: SecretKey::generate(&mut thread_rng()).public_key(), 67 | nonce: [42; ::NonceSize::USIZE], 68 | temporary_pk: SecretKey::generate(&mut thread_rng()).public_key(), 69 | payload: vec![42; 123] 70 | }) 71 | ); 72 | } 73 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/file_transfer/file_control.rs: -------------------------------------------------------------------------------- 1 | /*! FileControl struct. 2 | It is used to control transferring file to a friend. 3 | */ 4 | 5 | use super::*; 6 | use nom::bytes::complete::tag; 7 | use nom::number::complete::le_u8; 8 | 9 | /** FileControl is a struct that holds info to handle transferring file to a friend. 10 | 11 | This packet is used to control transferring sender's file to a friend. 12 | If a peer of connection wants to pause, kill, seek or accept transferring file, it would use this packet. 13 | 14 | Serialized form: 15 | 16 | Length | Content 17 | --------- | ------ 18 | `1` | `0x51` 19 | `1` | Whether it is sending or receiving, 0 = sender, 1 = receiver 20 | `1` | `file_id` 21 | `1` | Control type: 0 = accept, 1 = pause, 2 = kill, 3 = seek 22 | `8` | Seek parameter which is only included when `control type` is seek(3) 23 | 24 | */ 25 | 26 | #[derive(Clone, Debug, Eq, PartialEq)] 27 | pub struct FileControl { 28 | transfer_direction: TransferDirection, 29 | file_id: u8, 30 | control_type: ControlType, 31 | } 32 | 33 | impl FromBytes for FileControl { 34 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 35 | let (input, _) = tag("\x51")(input)?; 36 | let (input, transfer_direction) = TransferDirection::from_bytes(input)?; 37 | let (input, file_id) = le_u8(input)?; 38 | let (input, control_type) = ControlType::from_bytes(input)?; 39 | Ok(( 40 | input, 41 | FileControl { 42 | transfer_direction, 43 | file_id, 44 | control_type, 45 | }, 46 | )) 47 | } 48 | } 49 | 50 | impl ToBytes for FileControl { 51 | #[rustfmt::skip] 52 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 53 | do_gen!(buf, 54 | gen_be_u8!(0x51) >> 55 | gen_be_u8!(self.transfer_direction as u8) >> 56 | gen_be_u8!(self.file_id) >> 57 | gen_call!(|buf, control_type| ControlType::to_bytes(control_type, buf), &self.control_type) 58 | )} 59 | } 60 | 61 | impl FileControl { 62 | /// Create new FileControl object. 63 | pub fn new(transfer_direction: TransferDirection, file_id: u8, control_type: ControlType) -> Self { 64 | FileControl { 65 | transfer_direction, 66 | file_id, 67 | control_type, 68 | } 69 | } 70 | } 71 | 72 | #[cfg(test)] 73 | mod tests { 74 | use super::*; 75 | 76 | encode_decode_test!( 77 | file_control_encode_decode, 78 | FileControl::new(TransferDirection::Send, 1, ControlType::Seek(100)) 79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/action.rs: -------------------------------------------------------------------------------- 1 | /*! Action message struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::tag; 7 | use nom::combinator::{map_res, rest, verify}; 8 | use std::str; 9 | 10 | /// Maximum size in bytes of action message string of action packet 11 | const MAX_ACTION_MESSAGE_DATA_SIZE: usize = 1372; 12 | 13 | /** Action is a struct that holds string of my action message. 14 | Here, action message is a something like an IRC action 15 | 16 | This packet is used to transmit sender's action message to a friend. 17 | 18 | Serialized form: 19 | 20 | Length | Content 21 | --------- | ------ 22 | `1` | `0x41` 23 | `0..1372` | UTF8 byte string 24 | 25 | */ 26 | #[derive(Clone, Debug, Eq, PartialEq)] 27 | pub struct Action { 28 | msg: String, 29 | } 30 | 31 | impl FromBytes for Action { 32 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 33 | let (input, _) = tag("\x41")(input)?; 34 | let (input, msg) = map_res( 35 | verify(rest, |msg: &[u8]| msg.len() <= MAX_ACTION_MESSAGE_DATA_SIZE), 36 | str::from_utf8, 37 | )(input)?; 38 | Ok((input, Action { msg: msg.to_string() })) 39 | } 40 | } 41 | 42 | impl ToBytes for Action { 43 | #[rustfmt::skip] 44 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 45 | do_gen!(buf, 46 | gen_be_u8!(0x41) >> 47 | gen_cond!(self.msg.len() > MAX_ACTION_MESSAGE_DATA_SIZE, |buf| gen_error(buf, 0)) >> 48 | gen_slice!(self.msg.as_bytes()) 49 | ) 50 | } 51 | } 52 | 53 | impl Action { 54 | /// Create new Action object. 55 | pub fn new(msg: String) -> Self { 56 | Action { msg } 57 | } 58 | } 59 | 60 | #[cfg(test)] 61 | mod tests { 62 | use super::*; 63 | 64 | encode_decode_test!(action_encode_decode, Action::new("1234".to_string())); 65 | 66 | #[test] 67 | fn action_from_bytes_encoding_error() { 68 | let err_string = vec![0, 159, 146, 150]; // not UTF8 bytes. 69 | assert!(Action::from_bytes(&err_string).is_err()); 70 | } 71 | 72 | #[test] 73 | fn action_from_bytes_overflow() { 74 | let large_string = vec![32; MAX_ACTION_MESSAGE_DATA_SIZE + 1]; 75 | assert!(Action::from_bytes(&large_string).is_err()); 76 | } 77 | 78 | #[test] 79 | fn action_to_bytes_overflow() { 80 | let large_string = String::from_utf8(vec![32u8; MAX_ACTION_MESSAGE_DATA_SIZE + 1]).unwrap(); 81 | let large_msg = Action::new(large_string); 82 | let mut buf = [0; MAX_ACTION_MESSAGE_DATA_SIZE + 1]; // `1` is for packet_id. 83 | assert!(large_msg.to_bytes((&mut buf, 0)).is_err()); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tox_packet/src/onion/onion_response_1.rs: -------------------------------------------------------------------------------- 1 | /*! OnionResponse1 packet 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::{tag, take}; 7 | use nom::combinator::{map_parser, rest_len, verify}; 8 | 9 | use tox_binary_io::*; 10 | 11 | /** First onion response packet. It's sent back from the second to the first node 12 | from onion chain. 13 | 14 | Serialized form: 15 | 16 | Length | Content 17 | -------- | ------ 18 | `1` | `0x8e` 19 | `59` | `OnionReturn` 20 | variable | Payload 21 | 22 | where payload is encrypted [`OnionAnnounceResponse`](./struct.OnionAnnounceResponse.html) 23 | or [`OnionDataResponse`](./struct.OnionDataResponse.html) 24 | 25 | */ 26 | #[derive(Clone, Debug, Eq, PartialEq)] 27 | pub struct OnionResponse1 { 28 | /// Return address encrypted by the first node from onion chain 29 | pub onion_return: OnionReturn, 30 | /// Encrypted payload 31 | pub payload: InnerOnionResponse, 32 | } 33 | 34 | impl FromBytes for OnionResponse1 { 35 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 36 | let (input, _) = verify(rest_len, |len| *len <= ONION_MAX_PACKET_SIZE)(input)?; 37 | let (input, _) = tag(&[0x8e][..])(input)?; 38 | let (input, onion_return) = map_parser(take(ONION_RETURN_1_SIZE), OnionReturn::from_bytes)(input)?; 39 | let (input, payload) = InnerOnionResponse::from_bytes(input)?; 40 | Ok((input, OnionResponse1 { onion_return, payload })) 41 | } 42 | } 43 | 44 | impl ToBytes for OnionResponse1 { 45 | #[rustfmt::skip] 46 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 47 | do_gen!(buf, 48 | gen_be_u8!(0x8e) >> 49 | gen_call!(|buf, onion_return| OnionReturn::to_bytes(onion_return, buf), &self.onion_return) >> 50 | gen_call!(|buf, payload| InnerOnionResponse::to_bytes(payload, buf), &self.payload) >> 51 | gen_len_limit(ONION_MAX_PACKET_SIZE) 52 | ) 53 | } 54 | } 55 | 56 | #[cfg(test)] 57 | mod tests { 58 | use super::*; 59 | 60 | const ONION_RETURN_1_PAYLOAD_SIZE: usize = ONION_RETURN_1_SIZE - xsalsa20poly1305::NONCE_SIZE; 61 | 62 | encode_decode_test!( 63 | onion_response_1_encode_decode, 64 | OnionResponse1 { 65 | onion_return: OnionReturn { 66 | nonce: [42; xsalsa20poly1305::NONCE_SIZE], 67 | payload: vec![42; ONION_RETURN_1_PAYLOAD_SIZE] 68 | }, 69 | payload: InnerOnionResponse::OnionAnnounceResponse(OnionAnnounceResponse { 70 | sendback_data: 12345, 71 | nonce: [42; ::NonceSize::USIZE], 72 | payload: vec![42; 123] 73 | }) 74 | } 75 | ); 76 | } 77 | -------------------------------------------------------------------------------- /tox_packet/src/onion/onion_response_3.rs: -------------------------------------------------------------------------------- 1 | /*! OnionResponse3 packet 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::{tag, take}; 7 | use nom::combinator::{map_parser, rest_len, verify}; 8 | 9 | use tox_binary_io::*; 10 | 11 | /** Third onion response packet. It's sent back from the destination node to the 12 | third node from onion chain. 13 | 14 | Serialized form: 15 | 16 | Length | Content 17 | -------- | ------ 18 | `1` | `0x8c` 19 | `177` | `OnionReturn` 20 | variable | Payload 21 | 22 | where payload is encrypted [`OnionAnnounceResponse`](./struct.OnionAnnounceResponse.html) 23 | or [`OnionDataResponse`](./struct.OnionDataResponse.html) 24 | 25 | */ 26 | #[derive(Clone, Debug, Eq, PartialEq)] 27 | pub struct OnionResponse3 { 28 | /// Return address encrypted by the third node from onion chain 29 | pub onion_return: OnionReturn, 30 | /// Encrypted payload 31 | pub payload: InnerOnionResponse, 32 | } 33 | 34 | impl FromBytes for OnionResponse3 { 35 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 36 | let (input, _) = verify(rest_len, |len| *len <= ONION_MAX_PACKET_SIZE)(input)?; 37 | let (input, _) = tag(&[0x8c][..])(input)?; 38 | let (input, onion_return) = map_parser(take(ONION_RETURN_3_SIZE), OnionReturn::from_bytes)(input)?; 39 | let (input, payload) = InnerOnionResponse::from_bytes(input)?; 40 | Ok((input, OnionResponse3 { onion_return, payload })) 41 | } 42 | } 43 | 44 | impl ToBytes for OnionResponse3 { 45 | #[rustfmt::skip] 46 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 47 | do_gen!(buf, 48 | gen_be_u8!(0x8c) >> 49 | gen_call!(|buf, onion_return| OnionReturn::to_bytes(onion_return, buf), &self.onion_return) >> 50 | gen_call!(|buf, payload| InnerOnionResponse::to_bytes(payload, buf), &self.payload) >> 51 | gen_len_limit(ONION_MAX_PACKET_SIZE) 52 | ) 53 | } 54 | } 55 | 56 | #[cfg(test)] 57 | mod tests { 58 | use super::*; 59 | 60 | const ONION_RETURN_3_PAYLOAD_SIZE: usize = ONION_RETURN_3_SIZE - xsalsa20poly1305::NONCE_SIZE; 61 | 62 | encode_decode_test!( 63 | onion_response_3_encode_decode, 64 | OnionResponse3 { 65 | onion_return: OnionReturn { 66 | nonce: [42; xsalsa20poly1305::NONCE_SIZE], 67 | payload: vec![42; ONION_RETURN_3_PAYLOAD_SIZE] 68 | }, 69 | payload: InnerOnionResponse::OnionAnnounceResponse(OnionAnnounceResponse { 70 | sendback_data: 12345, 71 | nonce: [42; ::NonceSize::USIZE], 72 | payload: vec![42; 123] 73 | }) 74 | } 75 | ); 76 | } 77 | -------------------------------------------------------------------------------- /tox_node/src/motd.rs: -------------------------------------------------------------------------------- 1 | use time::OffsetDateTime; 2 | 3 | use tox::core::stats::Stats; 4 | 5 | /// Packet counters for both tcp and udp. 6 | pub struct Counters { 7 | tcp: Stats, 8 | udp: Stats, 9 | } 10 | 11 | impl Counters { 12 | pub fn new(tcp: Stats, udp: Stats) -> Self { 13 | Counters { tcp, udp } 14 | } 15 | } 16 | 17 | pub struct Motd { 18 | start_date: OffsetDateTime, 19 | counters: Counters, 20 | template: String, 21 | } 22 | 23 | impl Motd { 24 | pub fn new(template: String, counters: Counters) -> Motd { 25 | Motd { 26 | start_date: OffsetDateTime::now_utc(), 27 | counters, 28 | template, 29 | } 30 | } 31 | 32 | fn format_n(source: u64) -> String { 33 | match source { 34 | 0..=999 => format!("{}", source), 35 | 1_000..=999_999 => format!("{0:.1}K", source as f32 / 1_000.0), 36 | 1_000_000..=999_999_999 => format!("{0:.1}M", source as f32 / 1_000_000.0), 37 | 1_000_000_000..=999_999_999_999 => format!("{0:.1}G", source as f32 / 1_000_000_000.0), 38 | 1_000_000_000_000..=u64::MAX => format!("{0:.1}T", source as f32 / 1_000_000_000_000.0), 39 | } 40 | } 41 | 42 | pub fn format(&self) -> String { 43 | let start_date = { 44 | let format = time::format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]").unwrap(); 45 | self.start_date.format(&format).unwrap() 46 | }; 47 | 48 | let uptime = { 49 | let uptime = OffsetDateTime::now_utc() - self.start_date; 50 | let days = uptime.whole_days(); 51 | let hours = uptime.whole_hours() - uptime.whole_days() * 24; 52 | let minutes = uptime.whole_minutes() / 60 - uptime.whole_hours() * 60; 53 | format!("{:0>#2} days {:0>#2} hours {:0>#2} minutes", days, hours, minutes) 54 | }; 55 | 56 | let tcp_packets_in = Self::format_n(self.counters.tcp.counters.incoming()); 57 | let tcp_packets_out = Self::format_n(self.counters.tcp.counters.outgoing()); 58 | let udp_packets_in = Self::format_n(self.counters.udp.counters.incoming()); 59 | let udp_packets_out = Self::format_n(self.counters.udp.counters.outgoing()); 60 | 61 | let result = self.template.clone(); 62 | let result = result.replace("{{start_date}}", &start_date); 63 | let result = result.replace("{{uptime}}", &uptime); 64 | let result = result.replace("{{tcp_packets_in}}", &tcp_packets_in); 65 | let result = result.replace("{{tcp_packets_out}}", &tcp_packets_out); 66 | let result = result.replace("{{udp_packets_in}}", &udp_packets_in); 67 | result.replace("{{udp_packets_out}}", &udp_packets_out) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tox_core/src/onion/client/errors.rs: -------------------------------------------------------------------------------- 1 | use futures::channel::mpsc::SendError; 2 | use thiserror::Error; 3 | use tox_packet::dht::GetPayloadError; 4 | 5 | use crate::{dht::server::errors::PingError, relay::client::ConnectionError}; 6 | 7 | /// Error that can happen when handling `OnionAnnounceResponse` packet. 8 | #[derive(Debug, PartialEq, Error)] 9 | pub enum HandleAnnounceResponseError { 10 | /// Invalid request ID when handling OnionAnnounceResponse. 11 | #[error("Invalid request ID when handling OnionAnnounceResponse")] 12 | InvalidRequestId, 13 | /// Invalid announce status in OnionAnnounceResponse. 14 | #[error("Invalid announce status in OnionAnnounceResponse")] 15 | InvalidAnnounceStatus, 16 | /// No friend with PK specified in OnionAnnounceResponse. 17 | #[error("No friend with PK specified in OnionAnnounceResponse")] 18 | NoFriendWithPk, 19 | /// Invalid payload. 20 | #[error("Invalid payload")] 21 | InvalidPayload(GetPayloadError), 22 | /// Send packet(s) error. 23 | #[error("Send packet(s) error")] 24 | SendTo(SendError), 25 | } 26 | 27 | /// Error that can happen when handling `DhtPkAnnounce` packet. 28 | #[derive(Debug, Error)] 29 | pub enum HandleDhtPkAnnounceError { 30 | /// No friend with PK specified in OnionAnnounceResponse. 31 | #[error("No friend with PK specified in OnionAnnounceResponse")] 32 | NoFriendWithPk, 33 | /// Invalid no_reply. 34 | #[error("Invalid no_reply")] 35 | InvalidNoReply, 36 | /// Failed to ping node. 37 | #[error("Failed to ping node")] 38 | PingNode(PingError), 39 | /// Failed to add TCP relay. 40 | #[error("Failed to add TCP relay")] 41 | AddRelay(ConnectionError), 42 | /// Send packet(s) error. 43 | #[error("Send packet(s) error")] 44 | SendTo(SendError), 45 | } 46 | 47 | /// Error that can happen when handling `OnionDataResponse` packet. 48 | #[derive(Debug, Error)] 49 | pub enum HandleDataResponseError { 50 | /// Invalid payload. 51 | #[error("Invalid payload")] 52 | InvalidPayload(GetPayloadError), 53 | /// Invalid inner payload. 54 | #[error("Invalid inner payload")] 55 | InvalidInnerPayload(GetPayloadError), 56 | /// Failed to handle DHT `PublicKey` announce. 57 | #[error("Failed to handle DHT PublicKey announce")] 58 | DhtPkAnnounce(HandleDhtPkAnnounceError), 59 | /// Failed to send a friend request. 60 | #[error("Failed to send a friend request")] 61 | FriendRequest(SendError), 62 | } 63 | 64 | /// Error that can happen when calling `run_*`. 65 | #[derive(Clone, Debug, Eq, PartialEq, Error)] 66 | pub enum RunError { 67 | /// Timer error. 68 | #[error("Timer error")] 69 | Wakeup, 70 | /// Send packet(s) error. 71 | #[error("Send packet(s) error")] 72 | SendTo(SendError), 73 | } 74 | -------------------------------------------------------------------------------- /tox_packet/src/friend_connection/share_relays.rs: -------------------------------------------------------------------------------- 1 | /*! ShareRalays struct 2 | */ 3 | 4 | use super::*; 5 | use crate::dht::packed_node::*; 6 | use nom::bytes::complete::tag; 7 | use nom::combinator::{success, verify}; 8 | use nom::multi::many0; 9 | 10 | /// Id of the `ShareRelays` packet. 11 | pub const PACKET_ID_SHARE_RELAYS: u8 = 0x11; 12 | 13 | /// Maximum number of TCP relays `ShareRelays` packet can carry. 14 | pub const MAX_SHARED_RELAYS: usize = 3; 15 | 16 | /** ShareRelays is a struct that holds at most 3 TCP relays in a `PackedNode` format. 17 | 18 | This packet is used to share relays between two friends. 19 | 20 | Serialized form: 21 | 22 | Length | Content 23 | ---------- | ------ 24 | `1` | `0x11` 25 | `[0, 153]` | Nodes in packed format 26 | 27 | */ 28 | #[derive(Clone, Debug, Eq, PartialEq)] 29 | pub struct ShareRelays { 30 | /// TCP relays. 31 | pub relays: Vec, 32 | } 33 | 34 | impl FromBytes for ShareRelays { 35 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 36 | let (input, _) = tag(&[PACKET_ID_SHARE_RELAYS][..])(input)?; 37 | let (input, relays) = many0(PackedNode::from_tcp_bytes)(input)?; 38 | let (input, _) = verify(success(relays.len()), |len| *len <= MAX_SHARED_RELAYS)(input)?; 39 | Ok((input, ShareRelays { relays })) 40 | } 41 | } 42 | 43 | impl ToBytes for ShareRelays { 44 | #[rustfmt::skip] 45 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 46 | do_gen!(buf, 47 | gen_be_u8!(PACKET_ID_SHARE_RELAYS) >> 48 | gen_cond!(self.relays.len() > MAX_SHARED_RELAYS, |buf| gen_error(buf, 0)) >> 49 | gen_many_ref!(&self.relays, |buf, relay| PackedNode::to_tcp_bytes(relay, buf)) 50 | ) 51 | } 52 | } 53 | 54 | impl ShareRelays { 55 | /// Create new ShareRelays object 56 | pub fn new(relays: Vec) -> Self { 57 | ShareRelays { relays } 58 | } 59 | } 60 | 61 | #[cfg(test)] 62 | mod tests { 63 | use crypto_box::SecretKey; 64 | use rand::thread_rng; 65 | 66 | use super::*; 67 | 68 | encode_decode_test!( 69 | share_relays_encode_decode, 70 | ShareRelays::new(vec![ 71 | PackedNode { 72 | saddr: "1.1.1.1:33445".parse().unwrap(), 73 | pk: SecretKey::generate(&mut thread_rng()).public_key(), 74 | }, 75 | PackedNode { 76 | saddr: "1.1.1.1:33446".parse().unwrap(), 77 | pk: SecretKey::generate(&mut thread_rng()).public_key(), 78 | }, 79 | PackedNode { 80 | saddr: "1.1.1.1:33447".parse().unwrap(), 81 | pk: SecretKey::generate(&mut thread_rng()).public_key(), 82 | }, 83 | ]) 84 | ); 85 | } 86 | -------------------------------------------------------------------------------- /tox_packet/src/dht/errors.rs: -------------------------------------------------------------------------------- 1 | /*! Errors enum for DHT packets. 2 | */ 3 | 4 | use nom::{error::Error as NomError, Err}; 5 | use thiserror::Error; 6 | 7 | use std::convert::From; 8 | use std::io::Error as IoError; 9 | use std::io::ErrorKind as IoErrorKind; 10 | 11 | /// Error that can happen when calling `get_payload` of packet. 12 | #[derive(Debug, PartialEq, Error)] 13 | pub enum GetPayloadError { 14 | /// Error indicates that received payload of encrypted packet can't be decrypted 15 | #[error("Decrypt payload error")] 16 | Decrypt, 17 | /// Error indicates that decrypted payload of packet can't be parsed 18 | #[error("Deserialize payload error: {:?}, data: {:?}", error, payload)] 19 | Deserialize { 20 | /// Parsing error 21 | error: Err>>, 22 | /// Received payload of packet 23 | payload: Vec, 24 | }, 25 | } 26 | 27 | impl GetPayloadError { 28 | pub(crate) fn decrypt() -> GetPayloadError { 29 | GetPayloadError::Decrypt 30 | } 31 | 32 | pub(crate) fn deserialize(e: Err>, payload: Vec) -> GetPayloadError { 33 | GetPayloadError::Deserialize { 34 | error: e.to_owned(), 35 | payload, 36 | } 37 | } 38 | } 39 | 40 | /// From trait for temporary use during transition from io:Error to custom enum error of failure crate 41 | impl From for IoError { 42 | fn from(item: GetPayloadError) -> Self { 43 | IoError::new( 44 | IoErrorKind::Other, 45 | format!("GetPayloadError occured. error: {:?}", item), 46 | ) 47 | } 48 | } 49 | 50 | #[cfg(test)] 51 | mod tests { 52 | use std::num::NonZeroUsize; 53 | 54 | use super::*; 55 | use nom::{ 56 | error::{Error, ErrorKind}, 57 | Needed, 58 | }; 59 | 60 | #[test] 61 | fn get_payload_error_kind() { 62 | let decrypt = GetPayloadError::Decrypt; 63 | assert_eq!(format!("{}", decrypt), "Decrypt payload error".to_owned()); 64 | 65 | let incomplete = GetPayloadError::Deserialize { 66 | error: Err::Incomplete(Needed::Size(NonZeroUsize::new(5).unwrap())), 67 | payload: vec![1, 2, 3, 4], 68 | }; 69 | assert_eq!( 70 | format!("{}", incomplete), 71 | "Deserialize payload error: Incomplete(Size(5)), data: [1, 2, 3, 4]".to_owned() 72 | ); 73 | 74 | let deserialize = GetPayloadError::Deserialize { 75 | error: Err::Error(Error::new(vec![], ErrorKind::Eof)), 76 | payload: vec![1, 2, 3, 4], 77 | }; 78 | assert_eq!( 79 | format!("{}", deserialize), 80 | "Deserialize payload error: Error(Error { input: [], code: Eof }), data: [1, 2, 3, 4]".to_owned() 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/nickname.rs: -------------------------------------------------------------------------------- 1 | /*! Nickname struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::tag; 7 | use nom::combinator::{map_res, rest, verify}; 8 | use std::str; 9 | 10 | /// Maximum size in bytes of nickname string of nickname packet 11 | const MAX_NICKNAME_DATA_SIZE: usize = 128; 12 | 13 | /** Nickname is a struct that holds string of my nickname. 14 | 15 | This packet is used to transmit sender's nickname to a friend. 16 | Every time a friend become online or my nickname is changed, 17 | this packet is sent to the friend or to all friends of mine. 18 | 19 | Serialized form: 20 | 21 | Length | Content 22 | --------- | ------ 23 | `1` | `0x30` 24 | `0..128` | UTF8 byte string 25 | 26 | */ 27 | #[derive(Clone, Debug, Eq, PartialEq)] 28 | pub struct Nickname { 29 | nickname: String, 30 | } 31 | 32 | impl FromBytes for Nickname { 33 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 34 | let (input, _) = tag("\x30")(input)?; 35 | let (input, nickname) = map_res( 36 | verify(rest, |nickname: &[u8]| nickname.len() <= MAX_NICKNAME_DATA_SIZE), 37 | str::from_utf8, 38 | )(input)?; 39 | Ok(( 40 | input, 41 | Nickname { 42 | nickname: nickname.to_string(), 43 | }, 44 | )) 45 | } 46 | } 47 | 48 | impl ToBytes for Nickname { 49 | #[rustfmt::skip] 50 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 51 | do_gen!(buf, 52 | gen_be_u8!(0x30) >> 53 | gen_cond!(self.nickname.len() > MAX_NICKNAME_DATA_SIZE, |buf| gen_error(buf, 0)) >> 54 | gen_slice!(self.nickname.as_bytes()) 55 | ) 56 | } 57 | } 58 | 59 | impl Nickname { 60 | /// Create new Nickname object. 61 | pub fn new(nickname: String) -> Self { 62 | Nickname { nickname } 63 | } 64 | } 65 | 66 | #[cfg(test)] 67 | mod tests { 68 | use super::*; 69 | 70 | encode_decode_test!(nickname_encode_decode, Nickname::new("1234".to_string())); 71 | 72 | #[test] 73 | fn nickname_from_bytes_encoding_error() { 74 | let err_string = vec![0, 159, 146, 150]; // not UTF8 bytes. 75 | assert!(Nickname::from_bytes(&err_string).is_err()); 76 | } 77 | 78 | #[test] 79 | fn nickname_from_bytes_overflow() { 80 | let large_string = vec![32; 300]; 81 | assert!(Nickname::from_bytes(&large_string).is_err()); 82 | } 83 | 84 | #[test] 85 | fn nickname_to_bytes_overflow() { 86 | let large_string = String::from_utf8(vec![32u8; 300]).unwrap(); 87 | let large_nickname = Nickname::new(large_string); 88 | let mut buf = [0; MAX_NICKNAME_DATA_SIZE + 1]; // `1` is for packet_id. 89 | assert!(large_nickname.to_bytes((&mut buf, 0)).is_err()); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tox_packet/src/friend_connection/mod.rs: -------------------------------------------------------------------------------- 1 | /*! Top-level Friend connection Packets 2 | */ 3 | 4 | use tox_binary_io::*; 5 | 6 | mod alive; 7 | mod friend_requests; 8 | mod share_relays; 9 | 10 | pub use self::alive::*; 11 | pub use self::friend_requests::*; 12 | pub use self::share_relays::*; 13 | 14 | use nom::branch::alt; 15 | use nom::combinator::map; 16 | 17 | use cookie_factory::{do_gen, gen_be_u8, gen_call, gen_cond, gen_many_ref, gen_slice}; 18 | 19 | /** Friend connection packet enum that encapsulates all types of Friend connection packets. 20 | */ 21 | #[derive(Clone, Debug, Eq, PartialEq)] 22 | pub enum Packet { 23 | /// [`Alive`](./struct.Alive.html) structure. 24 | Alive(Alive), 25 | /// [`ShareRelays`](./struct.ShareRelays.html) structure. 26 | ShareRelays(ShareRelays), 27 | /// [`FriendRequests`](./struct.FriendRequests.html) structure. 28 | FriendRequests(FriendRequests), 29 | } 30 | 31 | impl ToBytes for Packet { 32 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 33 | match *self { 34 | Packet::Alive(ref p) => p.to_bytes(buf), 35 | Packet::ShareRelays(ref p) => p.to_bytes(buf), 36 | Packet::FriendRequests(ref p) => p.to_bytes(buf), 37 | } 38 | } 39 | } 40 | 41 | impl FromBytes for Packet { 42 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 43 | alt(( 44 | map(Alive::from_bytes, Packet::Alive), 45 | map(ShareRelays::from_bytes, Packet::ShareRelays), 46 | map(FriendRequests::from_bytes, Packet::FriendRequests), 47 | ))(input) 48 | } 49 | } 50 | 51 | #[cfg(test)] 52 | mod tests { 53 | use crypto_box::SecretKey; 54 | use rand::thread_rng; 55 | 56 | use super::*; 57 | use crate::dht::packed_node::*; 58 | use crate::toxid::{NoSpam, NOSPAMBYTES}; 59 | 60 | encode_decode_test!(packet_alive_encode_decode, Packet::Alive(Alive)); 61 | 62 | encode_decode_test!( 63 | packet_friend_requests_encode_decode, 64 | Packet::FriendRequests(FriendRequests::new(NoSpam([42; NOSPAMBYTES]), vec![1, 2, 3, 4])) 65 | ); 66 | 67 | encode_decode_test!( 68 | packet_share_relays_encode_decode, 69 | Packet::ShareRelays(ShareRelays::new(vec![ 70 | PackedNode { 71 | saddr: "1.1.1.1:33445".parse().unwrap(), 72 | pk: SecretKey::generate(&mut thread_rng()).public_key(), 73 | }, 74 | PackedNode { 75 | saddr: "1.1.1.1:33446".parse().unwrap(), 76 | pk: SecretKey::generate(&mut thread_rng()).public_key(), 77 | }, 78 | PackedNode { 79 | saddr: "1.1.1.1:33447".parse().unwrap(), 80 | pk: SecretKey::generate(&mut thread_rng()).public_key(), 81 | }, 82 | ])) 83 | ); 84 | } 85 | -------------------------------------------------------------------------------- /tox_packet/src/onion/onion_response_2.rs: -------------------------------------------------------------------------------- 1 | /*! OnionResponse2 packet 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::{tag, take}; 7 | use nom::combinator::{map_parser, rest_len, verify}; 8 | 9 | use tox_binary_io::*; 10 | 11 | /** Second onion response packet. It's sent back from the third to the second node 12 | from onion chain. 13 | 14 | Serialized form: 15 | 16 | Length | Content 17 | -------- | ------ 18 | `1` | `0x8d` 19 | `118` | `OnionReturn` 20 | variable | Payload 21 | 22 | where payload is encrypted [`OnionAnnounceResponse`](./struct.OnionAnnounceResponse.html) 23 | or [`OnionDataResponse`](./struct.OnionDataResponse.html) 24 | 25 | */ 26 | #[derive(Clone, Debug, Eq, PartialEq)] 27 | pub struct OnionResponse2 { 28 | /// Return address encrypted by the second node from onion chain 29 | pub onion_return: OnionReturn, 30 | /// Encrypted payload 31 | pub payload: InnerOnionResponse, 32 | } 33 | 34 | impl FromBytes for OnionResponse2 { 35 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 36 | let (input, _) = verify(rest_len, |len| *len <= ONION_MAX_PACKET_SIZE)(input)?; 37 | let (input, _) = tag(&[0x8d][..])(input)?; 38 | let (input, onion_return) = map_parser(take(ONION_RETURN_2_SIZE), OnionReturn::from_bytes)(input)?; 39 | let (input, payload) = InnerOnionResponse::from_bytes(input)?; 40 | Ok((input, OnionResponse2 { onion_return, payload })) 41 | } 42 | } 43 | 44 | impl ToBytes for OnionResponse2 { 45 | #[rustfmt::skip] 46 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 47 | do_gen!(buf, 48 | gen_be_u8!(0x8d) >> 49 | gen_call!(|buf, onion_return| OnionReturn::to_bytes(onion_return, buf), &self.onion_return) >> 50 | gen_call!(|buf, payload| InnerOnionResponse::to_bytes(payload, buf), &self.payload) >> 51 | gen_len_limit(ONION_MAX_PACKET_SIZE) 52 | ) 53 | } 54 | } 55 | 56 | #[cfg(test)] 57 | mod tests { 58 | use super::*; 59 | use crypto_box::aead::AeadCore; 60 | use crypto_box::{aead::generic_array::typenum::marker_traits::Unsigned, SalsaBox}; 61 | 62 | const ONION_RETURN_2_PAYLOAD_SIZE: usize = ONION_RETURN_2_SIZE - xsalsa20poly1305::NONCE_SIZE; 63 | 64 | encode_decode_test!( 65 | onion_response_2_encode_decode, 66 | OnionResponse2 { 67 | onion_return: OnionReturn { 68 | nonce: [42; xsalsa20poly1305::NONCE_SIZE], 69 | payload: vec![42; ONION_RETURN_2_PAYLOAD_SIZE] 70 | }, 71 | payload: InnerOnionResponse::OnionAnnounceResponse(OnionAnnounceResponse { 72 | sendback_data: 12345, 73 | nonce: [42; ::NonceSize::USIZE], 74 | payload: vec![42; 123] 75 | }) 76 | } 77 | ); 78 | } 79 | -------------------------------------------------------------------------------- /tox_packet/src/relay/connection_id.rs: -------------------------------------------------------------------------------- 1 | //! Connection ID definition. 2 | 3 | use super::*; 4 | use std::num::NonZeroU8; 5 | 6 | use tox_binary_io::*; 7 | 8 | use nom::number::complete::be_u8; 9 | 10 | /// Connection ID is either a number between [16, 255] or 0. Zero can be 11 | /// included in a response and means that the previous request was invalid. 12 | /// If a connection id is a number between [16, 255] it can be uniquely mapped 13 | /// to a connection index between [0, 239]. 14 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 15 | pub struct ConnectionId(Option); 16 | 17 | impl ConnectionId { 18 | /// Zero connection id meaning invalid request. 19 | pub fn zero() -> Self { 20 | ConnectionId(None) 21 | } 22 | 23 | /// Get connection id corresponding to the index. 24 | pub fn from_index(index: u8) -> Self { 25 | assert!( 26 | index < MAX_LINKS_N, 27 | "The index {} must be lower than {}", 28 | index, 29 | MAX_LINKS_N 30 | ); 31 | ConnectionId(Some(NonZeroU8::new(index + 16).unwrap())) 32 | } 33 | 34 | /// Get index corresponding to the connection id. None if the connection id 35 | /// is zero. 36 | pub fn index(self) -> Option { 37 | self.0.map(|connection_id| connection_id.get() - 16) 38 | } 39 | } 40 | 41 | impl FromBytes for ConnectionId { 42 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 43 | map(verify(be_u8, |id| *id == 0 || *id >= 0x10), |id| { 44 | ConnectionId(NonZeroU8::new(id)) 45 | })(input) 46 | } 47 | } 48 | 49 | impl ToBytes for ConnectionId { 50 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 51 | gen_be_u8!(buf, self.0.map_or(0, |connection_id| connection_id.get())) 52 | } 53 | } 54 | 55 | #[cfg(test)] 56 | mod test { 57 | use super::*; 58 | 59 | encode_decode_test!(connection_id_encode_decode, ConnectionId::from_index(42)); 60 | 61 | encode_decode_test!(connection_id_0_encode_decode, ConnectionId::zero()); 62 | 63 | #[test] 64 | fn zero() { 65 | let connection_id = ConnectionId::zero(); 66 | assert_eq!(connection_id.0, None); 67 | } 68 | 69 | #[test] 70 | fn from_index() { 71 | let connection_id = ConnectionId::from_index(0); 72 | assert_eq!(connection_id.0.unwrap().get(), 16); 73 | } 74 | 75 | #[test] 76 | #[should_panic] 77 | fn from_index_invalid() { 78 | let _connection_id = ConnectionId::from_index(255); 79 | } 80 | 81 | #[test] 82 | fn index() { 83 | let index = 42; 84 | let connection_id = ConnectionId::from_index(index); 85 | assert_eq!(connection_id.index().unwrap(), index); 86 | } 87 | 88 | #[test] 89 | fn index_zero() { 90 | let connection_id = ConnectionId::zero(); 91 | assert!(connection_id.index().is_none()); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/conference/invite_response.rs: -------------------------------------------------------------------------------- 1 | /*! InviteResponse response message struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::tag; 7 | use nom::number::complete::be_u16; 8 | 9 | /** InviteResponse is a struct that holds info to response to invite message from a peer. 10 | 11 | Serialized form: 12 | 13 | Length | Content 14 | --------- | ------ 15 | `1` | `0x60` 16 | `1` | `0x01` 17 | `2` | `conference id(local)` 18 | `2` | `conference id to join` 19 | `1` | `conference type`(0: text, 1: audio) 20 | `32` | `unique id` 21 | 22 | */ 23 | #[derive(Clone, Debug, Eq, PartialEq)] 24 | pub struct InviteResponse { 25 | /// Local conference id 26 | pub conference_id_local: u16, 27 | /// Conference id to join 28 | pub conference_id_join: u16, 29 | /// Type of conference 30 | pub conference_type: ConferenceType, 31 | /// Unique id of conference 32 | pub unique_id: ConferenceUid, 33 | } 34 | 35 | impl FromBytes for InviteResponse { 36 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 37 | let (input, _) = tag("\x60")(input)?; 38 | let (input, _) = tag("\x01")(input)?; 39 | let (input, conference_id_local) = be_u16(input)?; 40 | let (input, conference_id_join) = be_u16(input)?; 41 | let (input, conference_type) = ConferenceType::from_bytes(input)?; 42 | let (input, unique_id) = ConferenceUid::from_bytes(input)?; 43 | Ok(( 44 | input, 45 | InviteResponse { 46 | conference_id_local, 47 | conference_id_join, 48 | conference_type, 49 | unique_id, 50 | }, 51 | )) 52 | } 53 | } 54 | 55 | impl ToBytes for InviteResponse { 56 | #[rustfmt::skip] 57 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 58 | do_gen!(buf, 59 | gen_be_u8!(0x60) >> 60 | gen_be_u8!(0x01) >> 61 | gen_be_u16!(self.conference_id_local) >> 62 | gen_be_u16!(self.conference_id_join) >> 63 | gen_be_u8!(self.conference_type as u8) >> 64 | gen_slice!(self.unique_id.0) 65 | ) 66 | } 67 | } 68 | 69 | impl InviteResponse { 70 | /// Create new InviteResponse object. 71 | pub fn new( 72 | conference_id_local: u16, 73 | conference_id_join: u16, 74 | conference_type: ConferenceType, 75 | unique_id: ConferenceUid, 76 | ) -> Self { 77 | InviteResponse { 78 | conference_id_local, 79 | conference_id_join, 80 | conference_type, 81 | unique_id, 82 | } 83 | } 84 | } 85 | 86 | #[cfg(test)] 87 | mod tests { 88 | use super::*; 89 | 90 | encode_decode_test!( 91 | invite_response_encode_decode, 92 | InviteResponse::new(1, 2, ConferenceType::Audio, ConferenceUid([42; CONFERENCE_UID_BYTES])) 93 | ); 94 | } 95 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/status_message.rs: -------------------------------------------------------------------------------- 1 | /*! StatusMessage struct. 2 | It is used to send my status message to a friend. 3 | This packet is sent to my friends every time they become online, or whenever my status message is changed. 4 | */ 5 | 6 | use super::*; 7 | 8 | use nom::bytes::complete::tag; 9 | use nom::combinator::{map_res, rest, verify}; 10 | use std::str; 11 | 12 | /// Maximum size in bytes of status message string 13 | const MAX_STATUS_MESSAGE_DATA_SIZE: usize = 1007; 14 | 15 | /** StatusMessage is a struct that holds string of my status message. 16 | 17 | This packet is used to transmit sender's status message to a friend. 18 | Every time a friend become online or my status message is changed, 19 | this packet is sent to the friend or to all friends of mine. 20 | 21 | Serialized form: 22 | 23 | Length | Content 24 | --------- | ------ 25 | `1` | `0x31` 26 | `0..1007` | UTF8 byte string 27 | 28 | */ 29 | #[derive(Clone, Debug, Eq, PartialEq)] 30 | pub struct StatusMessage(String); 31 | 32 | impl FromBytes for StatusMessage { 33 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 34 | let (input, _) = tag("\x31")(input)?; 35 | let (input, message) = map_res( 36 | verify(rest, |message: &[u8]| message.len() <= MAX_STATUS_MESSAGE_DATA_SIZE), 37 | str::from_utf8, 38 | )(input)?; 39 | Ok((input, StatusMessage(message.to_string()))) 40 | } 41 | } 42 | 43 | impl ToBytes for StatusMessage { 44 | #[rustfmt::skip] 45 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 46 | do_gen!(buf, 47 | gen_be_u8!(0x31) >> 48 | gen_cond!(self.0.len() > MAX_STATUS_MESSAGE_DATA_SIZE, |buf| gen_error(buf, 0)) >> 49 | gen_slice!(self.0.as_bytes()) 50 | ) 51 | } 52 | } 53 | 54 | impl StatusMessage { 55 | /// Create new StatusMessage object. 56 | pub fn new(message: String) -> Self { 57 | StatusMessage(message) 58 | } 59 | } 60 | 61 | #[cfg(test)] 62 | mod tests { 63 | use super::*; 64 | 65 | encode_decode_test!(status_message_encode_decode, StatusMessage::new("Happy!".to_string())); 66 | 67 | #[test] 68 | fn status_message_from_bytes_encoding_error() { 69 | let err_string = vec![0, 159, 146, 150]; // not UTF8 bytes. 70 | assert!(StatusMessage::from_bytes(&err_string).is_err()); 71 | } 72 | 73 | #[test] 74 | fn nickname_from_bytes_overflow() { 75 | let large_string = vec![32; MAX_STATUS_MESSAGE_DATA_SIZE + 1]; 76 | assert!(StatusMessage::from_bytes(&large_string).is_err()); 77 | } 78 | 79 | #[test] 80 | fn nickname_to_bytes_overflow() { 81 | let large_string = String::from_utf8(vec![32u8; MAX_STATUS_MESSAGE_DATA_SIZE + 1]).unwrap(); 82 | let large_message = StatusMessage::new(large_string); 83 | let mut buf = [0; MAX_STATUS_MESSAGE_DATA_SIZE + 1]; // `1` is for packet_id. 84 | assert!(large_message.to_bytes((&mut buf, 0)).is_err()); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tox_packet/src/dht/bootstrap_info.rs: -------------------------------------------------------------------------------- 1 | /*! BootstrapInfo packet 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::{combinator::rest, number::complete::be_u32}; 7 | 8 | use nom::bytes::complete::tag; 9 | use nom::combinator::verify; 10 | use tox_binary_io::*; 11 | 12 | /** Sent by both client and server, only server will respond. 13 | When server receives this packet it may respond with the version of the library 14 | plus MoTD (message of the day). The max length of MoTD is 256 bytes so the max packet 15 | lenght of server BootstrapInfo is 261=(1+4+256) bytes. 16 | 17 | Client must send a BootstrapInfo of exactly 78 bytes, the only 1 field is required: `packet type` 18 | which is filled automatically. So version may be filled with any value, so does MoTD, but 19 | it has to be exactly 73=(78-1-4) bytes long. The server should check that the size of the 20 | packet is exactly 78 bytes long (or MoTD=73 bytes filled with any values). Frankly speaking, 21 | there should be neither `version` nor `motd` fields in the request version, the serialized form 22 | should be 1 byte with packet type + (78-1) bytes of trash, but this implementation is simplified. 23 | 24 | Serialized form: 25 | 26 | Length | Contents 27 | ----------- | -------- 28 | `1` | `0xF0` 29 | `4` | Version in BigEndian 30 | variable | MoTD, must not longer than 256 bytes 31 | 32 | */ 33 | #[derive(Clone, Debug, Eq, PartialEq)] 34 | pub struct BootstrapInfo { 35 | /// The version of DHT server 36 | pub version: u32, 37 | /// Message of the day 38 | pub motd: Vec, 39 | } 40 | 41 | /// Length of in bytes of MoTD field of [`BootstrapInfo`](./struct.BootstrapInfo.html) 42 | /// when server responds with info. 43 | pub const BOOSTRAP_SERVER_MAX_MOTD_LENGTH: usize = 256; 44 | /// Length of in bytes of MoTD field of [`BootstrapInfo`](./struct.BootstrapInfo.html) 45 | /// when client requests info. 73 = 78 (max client request len) - 1 (type) - 4 (version) 46 | pub const BOOSTRAP_CLIENT_MAX_MOTD_LENGTH: usize = 73; 47 | 48 | impl ToBytes for BootstrapInfo { 49 | #[rustfmt::skip] 50 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 51 | do_gen!(buf, 52 | gen_be_u8!(0xf0) >> 53 | gen_be_u32!(self.version) >> 54 | gen_slice!(self.motd.as_slice()) 55 | ) 56 | } 57 | } 58 | 59 | impl FromBytes for BootstrapInfo { 60 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 61 | let (input, _) = tag(&[0xf0][..])(input)?; 62 | let (input, version) = be_u32(input)?; 63 | let (input, motd) = verify(rest, |motd: &[u8]| motd.len() <= BOOSTRAP_SERVER_MAX_MOTD_LENGTH)(input)?; 64 | Ok(( 65 | input, 66 | BootstrapInfo { 67 | version, 68 | motd: motd.to_vec(), 69 | }, 70 | )) 71 | } 72 | } 73 | 74 | #[cfg(test)] 75 | mod tests { 76 | use crate::dht::bootstrap_info::*; 77 | 78 | encode_decode_test!( 79 | bootstrap_info_encode_decode, 80 | BootstrapInfo { 81 | version: 1717, 82 | motd: vec![1, 2, 3, 4], 83 | } 84 | ); 85 | } 86 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/conference/action.rs: -------------------------------------------------------------------------------- 1 | /*! Conference action action struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::{ 7 | bytes::complete::tag, 8 | combinator::{map_res, rest}, 9 | number::complete::{be_u16, be_u32}, 10 | }; 11 | use std::str; 12 | 13 | /** Action is the struct that holds info to send action to a conference. 14 | 15 | Sent to notify action to all member of conference. 16 | 17 | Serialized form: 18 | 19 | Length | Content 20 | --------- | ------ 21 | `1` | `0x63` 22 | `2` | `conference id` 23 | `2` | `peer id` 24 | `4` | `action id` 25 | `1` | `0x41` 26 | variable | `action`(UTF-8 C String) 27 | 28 | */ 29 | #[derive(Clone, Debug, Eq, PartialEq)] 30 | pub struct Action { 31 | /// Id of conference 32 | pub conference_id: u16, 33 | /// Target peer id 34 | pub peer_id: u16, 35 | /// Id of this message 36 | pub message_id: u32, 37 | /// Maximum length of action is the limit of NetCrypto packet. 38 | /// Do not check the length here. 39 | pub action: String, 40 | } 41 | 42 | impl FromBytes for Action { 43 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 44 | let (input, _) = tag("\x63")(input)?; 45 | let (input, conference_id) = be_u16(input)?; 46 | let (input, peer_id) = be_u16(input)?; 47 | let (input, message_id) = be_u32(input)?; 48 | let (input, _) = tag("\x41")(input)?; 49 | let (input, action) = map_res(rest, str::from_utf8)(input)?; 50 | Ok(( 51 | input, 52 | Action { 53 | conference_id, 54 | peer_id, 55 | message_id, 56 | action: action.to_string(), 57 | }, 58 | )) 59 | } 60 | } 61 | 62 | impl ToBytes for Action { 63 | #[rustfmt::skip] 64 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 65 | do_gen!(buf, 66 | gen_be_u8!(0x63) >> 67 | gen_be_u16!(self.conference_id) >> 68 | gen_be_u16!(self.peer_id) >> 69 | gen_be_u32!(self.message_id) >> 70 | gen_be_u8!(0x41) >> 71 | gen_slice!(self.action.as_bytes()) 72 | ) 73 | } 74 | } 75 | 76 | impl Action { 77 | /// Create new Action object. 78 | pub fn new(conference_id: u16, peer_id: u16, message_id: u32, action: String) -> Self { 79 | Action { 80 | conference_id, 81 | peer_id, 82 | message_id, 83 | action, 84 | } 85 | } 86 | } 87 | 88 | #[cfg(test)] 89 | mod tests { 90 | use super::*; 91 | 92 | encode_decode_test!(conference_action_encode_decode, Action::new(1, 2, 3, "1234".to_owned())); 93 | 94 | #[test] 95 | fn conference_action_from_bytes_encoding_error() { 96 | let err_string = vec![0, 159, 146, 150]; // not UTF8 bytes. 97 | let mut buf = vec![0x63, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x41]; 98 | buf.extend_from_slice(&err_string); 99 | assert!(Action::from_bytes(&buf).is_err()); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/conference/message.rs: -------------------------------------------------------------------------------- 1 | /*! Conference chat message struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::{ 7 | bytes::complete::tag, 8 | combinator::{map_res, rest}, 9 | number::complete::{be_u16, be_u32}, 10 | }; 11 | use std::str; 12 | 13 | /** Message is the struct that holds info to send chat message to a conference. 14 | 15 | Sent to notify chat message to all member of conference. 16 | 17 | Serialized form: 18 | 19 | Length | Content 20 | --------- | ------ 21 | `1` | `0x63` 22 | `2` | `conference id` 23 | `2` | `peer id` 24 | `4` | `message id` 25 | `1` | `0x40` 26 | variable | `message`(UTF-8 C String) 27 | 28 | */ 29 | #[derive(Clone, Debug, Eq, PartialEq)] 30 | pub struct Message { 31 | /// Id of conference 32 | pub conference_id: u16, 33 | /// Target peer id 34 | pub peer_id: u16, 35 | /// Id of this message 36 | pub message_id: u32, 37 | /// Maximum length of message is the limit of NetCrypto packet. 38 | /// Do not check the length here. 39 | pub message: String, 40 | } 41 | 42 | impl FromBytes for Message { 43 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 44 | let (input, _) = tag("\x63")(input)?; 45 | let (input, conference_id) = be_u16(input)?; 46 | let (input, peer_id) = be_u16(input)?; 47 | let (input, message_id) = be_u32(input)?; 48 | let (input, _) = tag("\x40")(input)?; 49 | let (input, message) = map_res(rest, str::from_utf8)(input)?; 50 | Ok(( 51 | input, 52 | Message { 53 | conference_id, 54 | peer_id, 55 | message_id, 56 | message: message.to_string(), 57 | }, 58 | )) 59 | } 60 | } 61 | 62 | impl ToBytes for Message { 63 | #[rustfmt::skip] 64 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 65 | do_gen!(buf, 66 | gen_be_u8!(0x63) >> 67 | gen_be_u16!(self.conference_id) >> 68 | gen_be_u16!(self.peer_id) >> 69 | gen_be_u32!(self.message_id) >> 70 | gen_be_u8!(0x40) >> 71 | gen_slice!(self.message.as_bytes()) 72 | ) 73 | } 74 | } 75 | 76 | impl Message { 77 | /// Create new Message object. 78 | pub fn new(conference_id: u16, peer_id: u16, message_id: u32, message: String) -> Self { 79 | Message { 80 | conference_id, 81 | peer_id, 82 | message_id, 83 | message, 84 | } 85 | } 86 | } 87 | 88 | #[cfg(test)] 89 | mod tests { 90 | use super::*; 91 | 92 | encode_decode_test!( 93 | conference_message_encode_decode, 94 | Message::new(1, 2, 3, "1234".to_owned()) 95 | ); 96 | 97 | #[test] 98 | fn conference_message_from_bytes_encoding_error() { 99 | let err_string = vec![0, 159, 146, 150]; // not UTF8 bytes. 100 | let mut buf = vec![0x63, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40]; 101 | buf.extend_from_slice(&err_string); 102 | assert!(Message::from_bytes(&buf).is_err()); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /tox_core/src/onion/mod.rs: -------------------------------------------------------------------------------- 1 | /*! Onion module allows nodes to announce their long term public keys and find 2 | friends by their long term public keys. 3 | 4 | There are two basic onion requests - `OnionAnnounceRequest` and 5 | `OnionDataRequest`. They are enclosed to OnionRequest packets and sent though 6 | the onion path to prevent nodes finding out long term public key when they know 7 | only temporary DHT public key. There are three types of OnionRequest packets: 8 | `OnionRequest0`, `OnionRequest1` and `OnionRequest2`. `OnionAnnounceRequest` and 9 | `OnionDataRequest` when created are enclosed to `OnionRequest2`, `OnionRequest2` 10 | is enclosed to `OnionRequest1` and `OnionRequest1` is enclosed to 11 | `OnionRequest0`. When DHT node receives OnionRequest packet it decrypts inner 12 | packet and sends it to the next node. 13 | 14 |
15 | +--------+                       +--------+                       +--------+                       +--------+   +----------------------+   +------------+
16 | |        |   +---------------+   |        |   +---------------+   |        |   +---------------+   |        |   | OnionAnnounceRequest |   |            |
17 | | Sender |---| OnionRequest0 |-->| Node 1 |---| OnionRequest1 |-->| Node 2 |---| OnionRequest2 |-->| Node 3 |---+----------------------+-->| Onion node |
18 | |        |   +---------------+   |        |   +---------------+   |        |   +---------------+   |        |   | OnionDataRequest     |   |            |
19 | +--------+                       +--------+                       +--------+                       +--------+   +----------------------+   +------------+
20 | 
21 | 22 | Similarly to requests there are responses `OnionAnnounceResponse` and 23 | `OnionDataResponse` that enclosed to three kind of OnionRespose packets: 24 | `OnionResponse3`, `OnionResponse2` and `OnionResponse1`. OnionResponse 25 | packets are processed in the same way but with reverse ordering. 26 | 27 |
28 | +------------+                        +--------+                        +--------+                        +--------+   +-----------------------+   +----------+
29 | |            |   +----------------+   |        |   +----------------+   |        |   +----------------+   |        |   | OnionAnnounceResponse |   |          |
30 | | Onion node |---| OnionResponse3 |-->| Node 3 |---| OnionResponse2 |-->| Node 2 |---| OnionResponse1 |-->| Node 1 |---+-----------------------+-->| Receiver |
31 | |            |   +----------------+   |        |   +----------------+   |        |   +----------------+   |        |   | OnionDataResponse     |   |          |
32 | +------------+                        +--------+                        +--------+                        +--------+   +-----------------------+   +----------+
33 | 
34 | 35 | When onion node handles `OnionAnnounceRequest` packet it sends answer to 36 | original sender using the same onion path with the help of received onion return 37 | addresses. But when it handles `OnionDataRequest` packet it should send response 38 | packet to another destination node by its long term public key. That means that 39 | when onion node should store long term public keys of announced node along with 40 | onion return addresses. 41 | 42 | */ 43 | 44 | pub mod client; 45 | pub mod onion_announce; 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tox 2 | 3 | [![Github Build Status][gh-badge]][gh-url] 4 | [![Coverage Status][cov-badge]][cov-url] 5 | [![Docs][doc-badge]][doc-url] 6 | [![Current Crates.io Version][crates-badge]][crates-url] 7 | [![Join Gitter][gitter-badge]][gitter-url] 8 | 9 | [gh-badge]: https://github.com/tox-rs/tox/workflows/Rust/badge.svg?branch=master 10 | [gh-url]: https://github.com/tox-rs/tox/actions?query=branch%3Amaster 11 | [cov-badge]: https://coveralls.io/repos/github/tox-rs/tox/badge.svg?branch=master 12 | [cov-url]: https://coveralls.io/github/tox-rs/tox?branch=master 13 | [doc-badge]: https://docs.rs/tox/badge.svg 14 | [doc-url]: https://docs.rs/tox 15 | [crates-badge]: https://img.shields.io/crates/v/tox.svg 16 | [crates-url]: https://crates.io/crates/tox 17 | [gitter-badge]: https://badges.gitter.im/tox-rs/tox.svg 18 | [gitter-url]: https://gitter.im/tox-rs/tox 19 | 20 | This library is an implementation of [toxcore][toxcore] in [Rust] - P2P, 21 | distributed, encrypted, easy to use DHT-based network. 22 | 23 | ## Reference 24 | 25 | [The Tox Reference](https://zetok.github.io/tox-spec) should be used for 26 | implementing toxcore in Rust. [Reference source repository]. 27 | 28 | If existing documentation appears to not be complete, or is not clear enough, 29 | issue / pull request should be filled on the reference repository. 30 | 31 | ## Contributions 32 | 33 | ...are welcome. :smile: For details, look at 34 | [CONTRIBUTING.md](/CONTRIBUTING.md). 35 | 36 | ## Building 37 | 38 | Fairly simple. First, install [Rust] >= 1.65 and a C compiler ([Build Tools 39 | for Visual Studio][VSBuild] on Windows, GCC or Clang on other platforms). 40 | 41 | Then you can build the debug version with 42 | 43 | ```bash 44 | cargo build 45 | ``` 46 | 47 | To run tests, use: 48 | 49 | ```bash 50 | cargo test 51 | ``` 52 | 53 | To build docs and open them in your browser: 54 | 55 | ```bash 56 | cargo doc --open 57 | ``` 58 | 59 | ### With clippy 60 | 61 | To check for [clippy](https://github.com/rust-lang-nursery/rust-clippy) warnings 62 | (linting), you need nightly Rust with `clippy-preview` component. 63 | 64 | To check: 65 | 66 | ```bash 67 | cargo clippy --all 68 | ``` 69 | 70 | To check with tests: 71 | 72 | ```bash 73 | cargo clippy --all --tests 74 | ``` 75 | 76 | ## Goals 77 | 78 | - improved toxcore implementation in Rust 79 | - Rust API 80 | - documentation 81 | - tests 82 | - more 83 | 84 | ## Progress 85 | 86 | A fully working tox-node written in pure Rust with a DHT server and a TCP relay 87 | can be found [here](https://github.com/tox-rs/tox-node). 88 | 89 | Right now we are working on the client part. 90 | 91 | ## Authors 92 | 93 | [zetox](https://github.com/zetok/tox) was created by [Zetok Zalbavar](https://github.com/zetok/) 94 | (zetok/openmailbox/org) and assimilated by the tox-rs team. 95 | 96 | tox-rs has contributions from many users. See [AUTHORS.md](/AUTHORS.md). Thanks everyone! 97 | 98 | ## License 99 | 100 | Licensed under [GPLv3+](/LICENSE) with [Apple app store exception](/COPYING.iOS). 101 | 102 | [Reference source repository]: https://github.com/zetok/tox-spec 103 | [Rust]: https://www.rust-lang.org/ 104 | [VSBuild]: https://visualstudio.microsoft.com/downloads/ 105 | [toxcore]: https://github.com/TokTok/c-toxcore 106 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/conference/title.rs: -------------------------------------------------------------------------------- 1 | /*! Title message struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::{ 7 | bytes::complete::tag, 8 | combinator::{map_res, rest, verify}, 9 | number::complete::be_u16, 10 | }; 11 | use std::str; 12 | 13 | use super::MAX_NAME_LENGTH_IN_CONFERENCE; 14 | 15 | /** Title is a struct that holds info to send a title packet to a conference. 16 | 17 | Serialized form: 18 | 19 | Length | Content 20 | --------- | ------ 21 | `1` | `0x62` 22 | `2` | `conference id` 23 | `1` | `0x0a` 24 | variable | title(UTF-8 C String) 25 | 26 | */ 27 | #[derive(Clone, Debug, Eq, PartialEq)] 28 | pub struct Title { 29 | /// Id of conference 30 | pub conference_id: u16, 31 | /// Title of conference 32 | pub title: String, 33 | } 34 | 35 | impl FromBytes for Title { 36 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 37 | let (input, _) = tag("\x62")(input)?; 38 | let (input, conference_id) = be_u16(input)?; 39 | let (input, _) = tag("\x0a")(input)?; 40 | let (input, title) = map_res( 41 | verify(rest, |title: &[u8]| title.len() <= MAX_NAME_LENGTH_IN_CONFERENCE), 42 | str::from_utf8, 43 | )(input)?; 44 | Ok(( 45 | input, 46 | Title { 47 | conference_id, 48 | title: title.to_string(), 49 | }, 50 | )) 51 | } 52 | } 53 | 54 | impl ToBytes for Title { 55 | #[rustfmt::skip] 56 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 57 | do_gen!(buf, 58 | gen_be_u8!(0x62) >> 59 | gen_be_u16!(self.conference_id) >> 60 | gen_be_u8!(0x0a) >> 61 | gen_cond!(self.title.len() > MAX_NAME_LENGTH_IN_CONFERENCE, |buf| gen_error(buf, 0)) >> 62 | gen_slice!(self.title.as_bytes()) 63 | ) 64 | } 65 | } 66 | 67 | impl Title { 68 | /// Create new Title object. 69 | pub fn new(conference_id: u16, title: String) -> Self { 70 | Title { conference_id, title } 71 | } 72 | } 73 | 74 | #[cfg(test)] 75 | mod tests { 76 | use super::*; 77 | 78 | encode_decode_test!(title_encode_decode, Title::new(1, "1234".to_owned())); 79 | 80 | #[test] 81 | fn title_from_bytes_encoding_error() { 82 | let err_string = vec![0, 159, 146, 150]; // not UTF8 bytes. 83 | let mut buf = vec![0x62, 0x01, 0x00, 0x0a]; 84 | buf.extend_from_slice(&err_string); 85 | assert!(Title::from_bytes(&buf).is_err()); 86 | } 87 | 88 | #[test] 89 | fn title_from_bytes_overflow() { 90 | let large_string = vec![32; MAX_NAME_LENGTH_IN_CONFERENCE + 1]; 91 | let mut buf = vec![0x62, 0x01, 0x00, 0x0a]; 92 | buf.extend_from_slice(&large_string); 93 | assert!(Title::from_bytes(&buf).is_err()); 94 | } 95 | 96 | #[test] 97 | fn title_to_bytes_overflow() { 98 | let large_string = String::from_utf8(vec![32u8; MAX_NAME_LENGTH_IN_CONFERENCE + 1]).unwrap(); 99 | let large_title = Title::new(1, large_string); 100 | let mut buf = [0; MAX_NAME_LENGTH_IN_CONFERENCE + 1 + 2 + 1]; // packet id + conference id + packet kind. 101 | assert!(large_title.to_bytes((&mut buf, 0)).is_err()); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /tox_packet/src/relay/data.rs: -------------------------------------------------------------------------------- 1 | /*! Data packet 2 | */ 3 | 4 | use super::*; 5 | 6 | use crate::dht::{CookieRequest, CookieResponse, CryptoData, CryptoHandshake}; 7 | use crate::relay::connection_id::ConnectionId; 8 | use nom::{branch::alt, combinator::map}; 9 | use tox_binary_io::*; 10 | 11 | /** Sent by client to server. 12 | The client sends data with `connection_id` and the server 13 | relays it to the given connection 14 | 15 | Serialized form: 16 | 17 | Length | Content 18 | -------- | ------ 19 | `1` | connection_id [ `0x10` .. `0xFF` ] 20 | variable | Data 21 | 22 | */ 23 | #[derive(Debug, PartialEq, Eq, Clone)] 24 | pub struct Data { 25 | /// The id of the connection of the client 26 | pub connection_id: ConnectionId, 27 | /// Data payload 28 | pub data: DataPayload, 29 | } 30 | 31 | impl FromBytes for Data { 32 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 33 | let (input, connection_id) = ConnectionId::from_bytes(input)?; 34 | let (input, data) = DataPayload::from_bytes(input)?; 35 | Ok((input, Data { connection_id, data })) 36 | } 37 | } 38 | 39 | impl ToBytes for Data { 40 | #[rustfmt::skip] 41 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 42 | do_gen!(buf, 43 | gen_call!(|buf, connection_id| ConnectionId::to_bytes(connection_id, buf), &self.connection_id) >> 44 | gen_call!(|buf, packet| DataPayload::to_bytes(packet, buf), &self.data) 45 | ) 46 | } 47 | } 48 | 49 | /// Data payload enum. 50 | #[derive(Debug, PartialEq, Eq, Clone)] 51 | pub enum DataPayload { 52 | /// [`CookieRequest`](../../dht/packet/struct.CookieRequest.html) structure. 53 | CookieRequest(CookieRequest), 54 | /// [`CookieResponse`](../../dht/packet/struct.CookieResponse.html) structure. 55 | CookieResponse(CookieResponse), 56 | /// [`CryptoHandshake`](../../dht/packet/struct.CryptoHandshake.html) structure. 57 | CryptoHandshake(CryptoHandshake), 58 | /// [`CryptoData`](../../dht/packet/struct.CryptoData.html) structure. 59 | CryptoData(CryptoData), 60 | } 61 | 62 | impl ToBytes for DataPayload { 63 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 64 | match *self { 65 | DataPayload::CookieRequest(ref p) => p.to_bytes(buf), 66 | DataPayload::CookieResponse(ref p) => p.to_bytes(buf), 67 | DataPayload::CryptoHandshake(ref p) => p.to_bytes(buf), 68 | DataPayload::CryptoData(ref p) => p.to_bytes(buf), 69 | } 70 | } 71 | } 72 | 73 | impl FromBytes for DataPayload { 74 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 75 | alt(( 76 | map(CookieRequest::from_bytes, DataPayload::CookieRequest), 77 | map(CookieResponse::from_bytes, DataPayload::CookieResponse), 78 | map(CryptoHandshake::from_bytes, DataPayload::CryptoHandshake), 79 | map(CryptoData::from_bytes, DataPayload::CryptoData), 80 | ))(input) 81 | } 82 | } 83 | 84 | #[cfg(test)] 85 | mod test { 86 | use super::*; 87 | 88 | encode_decode_test!( 89 | data_encode_decode, 90 | Data { 91 | connection_id: ConnectionId::from_index(1), 92 | data: DataPayload::CryptoData(CryptoData { 93 | nonce_last_bytes: 42, 94 | payload: vec![42; 123], 95 | }), 96 | } 97 | ); 98 | } 99 | -------------------------------------------------------------------------------- /tox_packet/src/friend_connection/friend_requests.rs: -------------------------------------------------------------------------------- 1 | /*! Friend requests struct 2 | */ 3 | 4 | use super::*; 5 | 6 | use crate::ip_port::SIZE_IPPORT; 7 | use crate::onion::*; 8 | use crate::toxid::NoSpam; 9 | use crypto_box::{ 10 | aead::{generic_array::typenum::marker_traits::Unsigned, AeadCore}, 11 | SalsaBox, 12 | }; 13 | use nom::bytes::complete::tag; 14 | use nom::combinator::{rest, verify}; 15 | 16 | const ONION_SEND_BASE: usize = crypto_box::KEY_SIZE + SIZE_IPPORT + ::TagSize::USIZE; 17 | const ONION_SEND_1: usize = xsalsa20poly1305::NONCE_SIZE + ONION_SEND_BASE * 3; 18 | const MAX_ONION_DATA_SIZE: usize = ONION_MAX_PACKET_SIZE - (ONION_SEND_1 + 1); // 1 is for packet_id 19 | const MIN_ONION_DATA_REQUEST_SIZE: usize = 1 20 | + crypto_box::KEY_SIZE 21 | + xsalsa20poly1305::NONCE_SIZE 22 | + crypto_box::KEY_SIZE 23 | + ::TagSize::USIZE; // 1 is for packet_id 24 | /// Maximum size in butes of Onion Data Request packet 25 | pub const MAX_DATA_REQUEST_SIZE: usize = MAX_ONION_DATA_SIZE - MIN_ONION_DATA_REQUEST_SIZE; 26 | /// Minimum size in bytes of Onion Data Response packet 27 | pub const MIN_ONION_DATA_RESPONSE_SIZE: usize = crypto_box::KEY_SIZE + ::TagSize::USIZE; 28 | /// Maximum size in bytes of Onion Data Response inner payload 29 | pub const MAX_ONION_CLIENT_DATA_SIZE: usize = MAX_DATA_REQUEST_SIZE - MIN_ONION_DATA_RESPONSE_SIZE; 30 | 31 | /** FriendRequests is a struct that holds info of nospam and greeting message. 32 | 33 | This packet is used to transmit sender's long term public key, npspam and a message. 34 | It is sent by onion data packet or net-crypto. 35 | If the friend is already directly connected with me and not in conference, it is sent using net-crypto. 36 | Otherwise it is sent using onion. 37 | Both onion and net-crypto packet itself have real public key of sender. 38 | This is why this packet does not contain long term public key. 39 | 40 | */ 41 | #[derive(Clone, Debug, Eq, PartialEq)] 42 | pub struct FriendRequests { 43 | nospam: NoSpam, 44 | message: Vec, 45 | } 46 | 47 | impl FromBytes for FriendRequests { 48 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 49 | let (input, _) = tag("\x12")(input)?; 50 | let (input, nospam) = NoSpam::from_bytes(input)?; 51 | let (input, message) = verify(rest, |message: &[u8]| message.len() <= MAX_ONION_CLIENT_DATA_SIZE)(input)?; 52 | Ok(( 53 | input, 54 | FriendRequests { 55 | nospam, 56 | message: message.to_vec(), 57 | }, 58 | )) 59 | } 60 | } 61 | 62 | impl ToBytes for FriendRequests { 63 | #[rustfmt::skip] 64 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 65 | do_gen!(buf, 66 | gen_be_u8!(0x12) >> 67 | gen_slice!(self.nospam.0) >> 68 | gen_cond!(self.message.len() > MAX_ONION_CLIENT_DATA_SIZE, |buf| gen_error(buf, 0)) >> 69 | gen_slice!(self.message.as_slice()) 70 | ) 71 | } 72 | } 73 | 74 | impl FriendRequests { 75 | /// Create new FriendRequests object 76 | pub fn new(nospam: NoSpam, message: Vec) -> Self { 77 | FriendRequests { nospam, message } 78 | } 79 | } 80 | 81 | #[cfg(test)] 82 | mod tests { 83 | use super::*; 84 | use crate::toxid::NOSPAMBYTES; 85 | 86 | encode_decode_test!( 87 | friend_requests_encode_decode, 88 | FriendRequests::new(NoSpam([42; NOSPAMBYTES]), vec![1, 2, 3, 4]) 89 | ); 90 | } 91 | -------------------------------------------------------------------------------- /tox_core/src/relay/client/errors.rs: -------------------------------------------------------------------------------- 1 | use futures::channel::mpsc::SendError; 2 | use std::io::Error as IoError; 3 | use thiserror::Error; 4 | 5 | use crate::relay::codec::{DecodeError, EncodeError}; 6 | 7 | /// Error that can happen when handling `Tcp relay` packet. 8 | #[derive(Clone, Debug, Eq, PartialEq, Error)] 9 | pub enum HandlePacketError { 10 | /// Send packet(s) error. 11 | #[error("Send packet(s) error")] 12 | SendTo(SendError), 13 | /// Send packet(s) error. 14 | #[error("Send packet(s) error")] 15 | SendPacket(SendPacketError), 16 | /// Server must not send this packet to client. 17 | #[error("Server must not send this packet to client")] 18 | MustNotSend, 19 | /// Invalid connection ID when handling RouteResponse. 20 | #[error("Invalid connection ID when handling RouteResponse")] 21 | InvalidConnectionId, 22 | /// Connection ID is already linked. 23 | #[error("Connection ID is already linked")] 24 | AlreadyLinked, 25 | /// Unexpected route response packet is received. 26 | #[error("Unexpected route response packet is received")] 27 | UnexpectedRouteResponse, 28 | } 29 | 30 | /// Error that can happen when sending packet. 31 | #[derive(Clone, Debug, Eq, PartialEq, Error)] 32 | pub enum SendPacketError { 33 | /// Send packet(s) error. 34 | #[error("Send packet(s) error")] 35 | SendTo(SendError), 36 | /// Send packet(s) with wrong status. 37 | #[error("Send packet(s) with wrong status")] 38 | WrongStatus, 39 | /// Send packet(s) with destination_pk is not online. 40 | #[error("Send packet(s) with destination_pk is not online")] 41 | NotOnline, 42 | /// Send packet(s) with destination_pk is not linked. 43 | #[error("Send packet(s) with destination_pk is not linked")] 44 | NotLinked, 45 | /// Send packet(s) to a connection but no such connection. 46 | #[error("Send packet(s) to a connection but no such connection")] 47 | NoSuchConnection, 48 | } 49 | 50 | /// Error that can happen when spawning a connection. 51 | #[derive(Debug, Error)] 52 | pub enum SpawnError { 53 | /// Read socket to receive packet error. 54 | #[error("Read socket to receive packet error")] 55 | ReadSocket(DecodeError), 56 | /// Send packet(s) error. 57 | #[error("Send packet(s) error")] 58 | SendTo(SendPacketError), 59 | /// Handle packet(s) error. 60 | #[error("Handle packet(s) error")] 61 | HandlePacket(HandlePacketError), 62 | /// Tcp client io error. 63 | #[error("Tcp client io error")] 64 | Io(IoError), 65 | /// Tcp codec encode error. 66 | #[error("Tcp codec encode error")] 67 | Encode(EncodeError), 68 | } 69 | 70 | /// Error that can happen when handling a connection. 71 | #[derive(Debug, Error)] 72 | pub enum ConnectionError { 73 | /// Spawing after adding global connection error. 74 | #[error("Spawing after adding global connection error")] 75 | Spawn(SpawnError), 76 | /// Search relay by relay's PK, but no such relay. 77 | #[error("Search relay by relay's PK, but no such relay")] 78 | NoSuchRelay, 79 | /// Send packet(s) error. 80 | #[error("Send packet(s) error")] 81 | SendTo(SendPacketError), 82 | /// No connection to the node. 83 | #[error("No connection to the node")] 84 | NoConnection, 85 | /// Relay is not connected. 86 | #[error("Relay is not connected")] 87 | NotConnected, 88 | /// Tcp Connections wakeup timer error. 89 | #[error("Tcp Connections wakeup timer error")] 90 | Wakeup, 91 | /// Add connection to client error. 92 | #[error("Add connection to client error")] 93 | AddConnection, 94 | } 95 | -------------------------------------------------------------------------------- /tox_core/src/dht/server/errors.rs: -------------------------------------------------------------------------------- 1 | /*! Errors enum for DHT server. 2 | */ 3 | 4 | use futures::channel::mpsc::SendError; 5 | use thiserror::Error; 6 | use tokio::time::error::Elapsed; 7 | use tox_packet::dht::GetPayloadError; 8 | 9 | use crate::net_crypto::errors::HandlePacketError as HandleNetCryptoPacketError; 10 | use crate::onion::{ 11 | client::errors::{HandleAnnounceResponseError, HandleDataResponseError}, 12 | onion_announce::HandleDataRequestError, 13 | }; 14 | 15 | /// Error that can happen when calling `handle_*` of packet. 16 | #[derive(Debug, Error)] 17 | pub enum HandlePacketError { 18 | /// Error indicates that getting payload of received packet error. 19 | #[error("Get payload of received packet error")] 20 | GetPayload(GetPayloadError), 21 | /// Error indicates that next_onion_return is none. 22 | #[error("Next onion return is none")] 23 | OnionResponseNext, 24 | /// Error indicates that sending response packet faces redirecting failure. 25 | #[error("Sending response redirecting error")] 26 | OnionResponseRedirect, 27 | /// Error indicates that sending response packet faces redirecting failure. 28 | #[error("Sending response redirecting error")] 29 | OnionResponseRedirectSend(SendError), 30 | /// Error indicates that BootstrapInfo error. 31 | #[error("BootstrapInfo handling error")] 32 | BootstrapInfoLength, 33 | /// Error indicates that sending response packet error. 34 | #[error("Sending response error")] 35 | SendTo(SendError), 36 | /// Error indicates that received packet's ping_id is zero. 37 | #[error("Zero ping id error")] 38 | ZeroPingId, 39 | /// Error indicates that received packet's ping_id does not match. 40 | #[error("Ping id mismatch error")] 41 | PingIdMismatch, 42 | /// Error indicates that there is no friend. 43 | #[error("Friend does not exist error")] 44 | NoFriend, 45 | /// Error indicates that NetCrypto is not initialized. 46 | #[error("NetCrypto is not initialized error")] 47 | NetCrypto, 48 | /// Error indicates that OnionClient is not initialized. 49 | #[error("OnionClient is not initialized error")] 50 | OnionClient, 51 | /// Error indicates that handling NetCrypto packet made an error. 52 | #[error("Handling NetCrypto packet failed")] 53 | HandleNetCrypto(HandleNetCryptoPacketError), 54 | /// Error indicates that handling OnionClient data packet made an error. 55 | #[error("Handling OnionClient data packet failed")] 56 | HandleOnionClientData(HandleDataResponseError), 57 | /// Error indicates that handling OnionClient announce packet made an error. 58 | #[error("Handling OnionClient announce packet failed")] 59 | HandleOnionClientAnnounce(HandleAnnounceResponseError), 60 | /// Error indicates that onion processing fails. 61 | #[error("Onion or NetCrypto related error")] 62 | Onion(HandleDataRequestError), 63 | /// Failed to send friend's IP address to the sink. 64 | #[error("Failed to send friend's IP address to the sink")] 65 | FriendSaddr(SendError), 66 | } 67 | 68 | /// Error that can happen when calling `run_*`. 69 | #[derive(Debug, Eq, PartialEq, Error)] 70 | pub enum RunError { 71 | /// Send packet(s) error. 72 | #[error("Send packet(s) error")] 73 | SendTo(SendError), 74 | /// Send packet(s) time out. 75 | #[error("Send packet(s) time out")] 76 | Timeout(Elapsed), 77 | } 78 | 79 | /// Error that can happen when calling `run_*`. 80 | #[derive(Clone, Debug, Eq, PartialEq, Error)] 81 | pub enum PingError { 82 | /// Send packet(s) error. 83 | #[error("Send packet(s) error")] 84 | SendTo(SendError), 85 | } 86 | -------------------------------------------------------------------------------- /tox_encryptsave/tests/encryptsave_tests.rs: -------------------------------------------------------------------------------- 1 | use tox_encryptsave::*; 2 | 3 | #[test] 4 | fn is_encrypted_test() { 5 | assert!(!is_encrypted(b"Hello world.\n")); 6 | assert!(is_encrypted(MAGIC_NUMBER)); 7 | assert!(is_encrypted(include_bytes!("ciphertext"))); 8 | } 9 | 10 | #[test] 11 | fn pass_encrypt_error_test() { 12 | // empty data 13 | assert_eq!(pass_encrypt(&[], &[0]), Err(EncryptionError::Null)); 14 | 15 | // empty passphrase 16 | assert_eq!(pass_encrypt(&[0], &[]), Err(KeyDerivationError::Null.into())); 17 | } 18 | 19 | #[test] 20 | fn pass_encrypt_test() { 21 | let plaintext = [42; 16]; 22 | let passphrase = [53; 16]; 23 | 24 | let encrypted = pass_encrypt(&plaintext, &passphrase).unwrap(); 25 | assert!(is_encrypted(&encrypted)); 26 | 27 | assert_eq!(plaintext.len() + EXTRA_LENGTH, encrypted.len()); 28 | assert_eq!( 29 | &plaintext as &[u8], 30 | &pass_decrypt(&encrypted, &passphrase).unwrap() as &[u8] 31 | ); 32 | 33 | let encrypted2 = pass_encrypt(&plaintext, &passphrase).unwrap(); 34 | assert_ne!(encrypted, encrypted2); 35 | } 36 | 37 | #[test] 38 | fn pass_decrypt_error_null_test() { 39 | // empty data 40 | assert_eq!(pass_decrypt(&[], &[0]), Err(DecryptionError::Null)); 41 | } 42 | 43 | #[test] 44 | fn pass_decrypt_error_invalid_length_test() { 45 | // not enough data 46 | assert_eq!(pass_decrypt(&[0], &[]), Err(DecryptionError::InvalidLength)); 47 | } 48 | 49 | #[test] 50 | fn pass_decrypt_error_key_derivation_test() { 51 | // empty passphrase 52 | let ciphertext = include_bytes!("ciphertext"); 53 | assert_eq!(pass_decrypt(ciphertext, &[]), Err(KeyDerivationError::Null.into())); 54 | } 55 | 56 | #[test] 57 | fn pass_decrypt_error_bad_format_test() { 58 | // one of `MAGIC_NUMBER` bytes is wrong 59 | let ciphertext = include_bytes!("ciphertext"); 60 | let mut bad_ciphertext = Vec::with_capacity(MAGIC_LENGTH + SALT_LENGTH); 61 | bad_ciphertext.extend_from_slice(&[0; MAGIC_LENGTH]); 62 | bad_ciphertext.extend_from_slice(&ciphertext[MAGIC_LENGTH..]); 63 | assert_eq!(pass_decrypt(&bad_ciphertext, &[]), Err(DecryptionError::BadFormat)); 64 | } 65 | 66 | #[test] 67 | fn pass_decrypt_error_failed_test() { 68 | // a data byte is wrong 69 | let ciphertext = include_bytes!("ciphertext"); 70 | let mut bad_ciphertext = Vec::with_capacity(EXTRA_LENGTH + 123); 71 | bad_ciphertext.extend_from_slice(&ciphertext[..EXTRA_LENGTH]); 72 | bad_ciphertext.extend_from_slice(&[42; 123]); 73 | assert_eq!( 74 | pass_decrypt(&bad_ciphertext, b"encryptsave"), 75 | Err(DecryptionError::Failed) 76 | ); 77 | } 78 | 79 | #[test] 80 | fn pass_decrypt_test() { 81 | let passphrase = b"encryptsave"; 82 | let plaintext = b"Hello world.\n"; 83 | let ciphertext = include_bytes!("ciphertext"); 84 | 85 | assert_eq!(pass_decrypt(ciphertext, passphrase).unwrap(), plaintext); 86 | } 87 | 88 | #[test] 89 | fn get_salt_test() { 90 | let ciphertext = include_bytes!("ciphertext"); 91 | let salt = &ciphertext[MAGIC_LENGTH..MAGIC_LENGTH + SALT_LENGTH]; 92 | 93 | assert_eq!(get_salt(ciphertext).unwrap(), salt); 94 | } 95 | 96 | #[test] 97 | fn get_salt_wrong_magic_test() { 98 | let ciphertext = include_bytes!("ciphertext"); 99 | 100 | let mut bad_ciphertext = Vec::with_capacity(MAGIC_LENGTH + SALT_LENGTH); 101 | bad_ciphertext.extend_from_slice(&[0; MAGIC_LENGTH]); 102 | bad_ciphertext.extend_from_slice(&ciphertext[MAGIC_LENGTH..]); 103 | 104 | assert_eq!(get_salt(&bad_ciphertext), None); 105 | } 106 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/conference/new_peer.rs: -------------------------------------------------------------------------------- 1 | /*! New peer message struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::bytes::complete::tag; 7 | use nom::number::complete::{be_u16, be_u32}; 8 | 9 | use tox_crypto::*; 10 | 11 | /** NewPeer is a struct that holds info to send new peer message to a conference. 12 | 13 | Tell everyone about a new peer in the chat. 14 | The peer who invited joining peer sends this packet to warn everyone that there is a new peer. 15 | 16 | Serialized form: 17 | 18 | Length | Content 19 | --------- | ------ 20 | `1` | `0x63` 21 | `2` | `conference id` 22 | `2` | `peer id` 23 | `4` | `message id` 24 | `1` | `0x10` 25 | `2` | `peer id` 26 | `32` | Long term PK 27 | `32` | DHT PK 28 | 29 | */ 30 | #[derive(Clone, Debug, Eq, PartialEq)] 31 | pub struct NewPeer { 32 | /// Id of conference 33 | pub conference_id: u16, 34 | /// Target peer id 35 | pub peer_id: u16, 36 | /// Id of this message 37 | pub message_id: u32, 38 | /// Peer id to join 39 | pub new_peer_id: u16, 40 | /// Long term PK of new peer 41 | pub long_term_pk: PublicKey, 42 | /// DHT PK of new peer 43 | pub dht_pk: PublicKey, 44 | } 45 | 46 | impl FromBytes for NewPeer { 47 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 48 | let (input, _) = tag("\x63")(input)?; 49 | let (input, conference_id) = be_u16(input)?; 50 | let (input, peer_id) = be_u16(input)?; 51 | let (input, message_id) = be_u32(input)?; 52 | let (input, _) = tag("\x10")(input)?; 53 | let (input, new_peer_id) = be_u16(input)?; 54 | let (input, long_term_pk) = PublicKey::from_bytes(input)?; 55 | let (input, dht_pk) = PublicKey::from_bytes(input)?; 56 | Ok(( 57 | input, 58 | NewPeer { 59 | conference_id, 60 | peer_id, 61 | message_id, 62 | new_peer_id, 63 | long_term_pk, 64 | dht_pk, 65 | }, 66 | )) 67 | } 68 | } 69 | 70 | impl ToBytes for NewPeer { 71 | #[rustfmt::skip] 72 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 73 | do_gen!(buf, 74 | gen_be_u8!(0x63) >> 75 | gen_be_u16!(self.conference_id) >> 76 | gen_be_u16!(self.peer_id) >> 77 | gen_be_u32!(self.message_id) >> 78 | gen_be_u8!(0x10) >> 79 | gen_be_u16!(self.new_peer_id) >> 80 | gen_slice!(self.long_term_pk.as_bytes()) >> 81 | gen_slice!(self.dht_pk.as_bytes()) 82 | ) 83 | } 84 | } 85 | 86 | impl NewPeer { 87 | /// Create new NewPeer object. 88 | pub fn new( 89 | conference_id: u16, 90 | peer_id: u16, 91 | message_id: u32, 92 | new_peer_id: u16, 93 | long_term_pk: PublicKey, 94 | dht_pk: PublicKey, 95 | ) -> Self { 96 | NewPeer { 97 | conference_id, 98 | peer_id, 99 | message_id, 100 | new_peer_id, 101 | long_term_pk, 102 | dht_pk, 103 | } 104 | } 105 | } 106 | 107 | #[cfg(test)] 108 | mod tests { 109 | use rand::thread_rng; 110 | 111 | use super::*; 112 | 113 | encode_decode_test!( 114 | new_peer_encode_decode, 115 | NewPeer::new( 116 | 1, 117 | 2, 118 | 3, 119 | 4, 120 | SecretKey::generate(&mut thread_rng()).public_key(), 121 | SecretKey::generate(&mut thread_rng()).public_key() 122 | ) 123 | ); 124 | } 125 | -------------------------------------------------------------------------------- /tox_crypto/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Functions for the core crypto. 2 | 3 | use crypto_box::aead::AeadCore; 4 | use crypto_box::{aead::generic_array::typenum::marker_traits::Unsigned, SalsaBox}; 5 | pub use crypto_box::{PublicKey, SecretKey}; 6 | 7 | pub type Nonce = [u8; ::NonceSize::USIZE]; 8 | pub const NONCEBYTES: usize = ::NonceSize::USIZE; 9 | 10 | // TODO: check if `#[inline]` is actually useful 11 | 12 | /** Check if Tox public key is valid. Should be used only for input 13 | validation. 14 | 15 | Returns `true` if valid, `false` otherwise. 16 | */ 17 | pub fn public_key_valid(pk: &PublicKey) -> bool { 18 | pk.as_bytes()[crypto_box::KEY_SIZE - 1] <= 127 // Last bit of key is always zero. 19 | } 20 | 21 | /** Inrement given nonce by 1. 22 | 23 | Treats `Nonce` as BE number. 24 | 25 | If nonce can't be incremented (all bits are `1`), nonce is zeroed. 26 | 27 | *Note that behaviour of this function might change to not increment supplied 28 | nonces, but rather, return an increased nonce.* 29 | 30 | Spec: https://zetok.github.io/tox-spec#nonce-2 31 | */ 32 | #[inline] 33 | pub fn increment_nonce(nonce: &mut Nonce) { 34 | increment_nonce_number(nonce, 1) 35 | } 36 | 37 | /// Inrement given nonce by number `num`. 38 | pub fn increment_nonce_number(nonce: &mut Nonce, num: u16) { 39 | let mut c = num as u32; 40 | for i in (0..NONCEBYTES).rev() { 41 | c += nonce[i] as u32; 42 | nonce[i] = c as u8; 43 | c >>= 8; 44 | } 45 | } 46 | 47 | #[cfg(test)] 48 | pub mod tests { 49 | use super::*; 50 | 51 | #[test] 52 | fn increment_nonce_test_zero_plus_one() { 53 | let cmp_nonce = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; 54 | 55 | let mut nonce = [0; NONCEBYTES]; 56 | increment_nonce(&mut nonce); 57 | assert_eq!(nonce, cmp_nonce); 58 | } 59 | 60 | #[test] 61 | fn increment_nonce_test_0xf_plus_one() { 62 | let cmp_nonce = [ 63 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10, 64 | ]; 65 | 66 | let mut nonce = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xf]; 67 | increment_nonce(&mut nonce); 68 | assert_eq!(nonce, cmp_nonce); 69 | } 70 | 71 | #[test] 72 | fn increment_nonce_test_0xff_plus_one() { 73 | let cmp_nonce = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]; 74 | 75 | let mut nonce = [ 76 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 77 | ]; 78 | increment_nonce(&mut nonce); 79 | assert_eq!(nonce, cmp_nonce); 80 | } 81 | 82 | #[test] 83 | fn increment_nonce_test_0xff_max() { 84 | let cmp_nonce = [0; NONCEBYTES]; 85 | let mut nonce = [0xff; NONCEBYTES]; 86 | increment_nonce(&mut nonce); 87 | assert_eq!(cmp_nonce, nonce); 88 | } 89 | 90 | // increment_nonce_number() 91 | 92 | #[test] 93 | fn increment_nonce_number_test_zero_plus_0xff00() { 94 | let cmp_nonce = [ 95 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0, 96 | ]; 97 | let mut nonce = [0; NONCEBYTES]; 98 | 99 | increment_nonce_number(&mut nonce, 0xff00); 100 | assert_eq!(nonce, cmp_nonce); 101 | } 102 | 103 | #[test] 104 | fn increment_nonce_number_test_0xff00_plus_0x0110() { 105 | let cmp_nonce = [ 106 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0x10, 107 | ]; 108 | 109 | let mut nonce = [ 110 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0, 111 | ]; 112 | 113 | increment_nonce_number(&mut nonce, 0x01_10); 114 | assert_eq!(nonce, cmp_nonce); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /tox_core/src/dht/ip_port.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Module for utils of IP and Port. 3 | */ 4 | 5 | use std::net::IpAddr; 6 | 7 | /// TODO: replace with https://doc.rust-lang.org/std/net/struct.Ipv4Addr.html#method.is_global when it is stabilized 8 | pub trait IsGlobal { 9 | /// checkif IP is not a LAN ip 10 | fn is_global(&self) -> bool; 11 | } 12 | 13 | impl IsGlobal for IpAddr { 14 | /// TODO: replace with https://doc.rust-lang.org/std/net/struct.Ipv4Addr.html#method.is_global when it is stabilized 15 | fn is_global(&self) -> bool { 16 | if self.is_loopback() { 17 | return false; 18 | } 19 | 20 | match *self { 21 | IpAddr::V4(ipv4) => { 22 | let addrs = ipv4.octets(); 23 | 24 | /* 10.0.0.0 to 10.255.255.255 range. */ 25 | if addrs[0] == 10 { 26 | return false; 27 | } 28 | 29 | /* 172.16.0.0 to 172.31.255.255 range. */ 30 | if addrs[0] == 172 && addrs[1] >= 16 && addrs[1] <= 31 { 31 | return false; 32 | } 33 | 34 | /* 192.168.0.0 to 192.168.255.255 range. */ 35 | if addrs[0] == 192 && addrs[1] == 168 { 36 | return false; 37 | } 38 | 39 | /* 169.254.1.0 to 169.254.254.255 range. */ 40 | if addrs[0] == 169 && addrs[1] == 254 && addrs[2] != 0 && addrs[2] != 255 { 41 | return false; 42 | } 43 | 44 | /* RFC 6598: 100.64.0.0 to 100.127.255.255 (100.64.0.0/10) 45 | * (shared address space to stack another layer of NAT) */ 46 | if addrs[0] == 100 && (addrs[1] & 0xC0) == 0x40 { 47 | return false; 48 | } 49 | } 50 | IpAddr::V6(ipv6) => { 51 | let addrs = ipv6.octets(); 52 | 53 | /* autogenerated for each interface: FE80::* (up to FEBF::*) 54 | FF02::1 is - according to RFC 4291 - multicast all-nodes link-local */ 55 | if (addrs[0] == 0xFF && addrs[1] < 3 && addrs[15] == 1) 56 | || (addrs[0] == 0xFE && (addrs[1] & 0xC0) == 0x80) 57 | { 58 | return false; 59 | } 60 | 61 | /* embedded IPv4-in-IPv6 */ 62 | if let Some(ipv4) = ipv6.to_ipv4() { 63 | return IsGlobal::is_global(&IpAddr::V4(ipv4)); 64 | } else { 65 | } 66 | } 67 | }; 68 | 69 | true 70 | } 71 | } 72 | 73 | #[cfg(test)] 74 | mod tests { 75 | use super::*; 76 | use std::net::Ipv4Addr; 77 | use std::str::FromStr; 78 | 79 | #[test] 80 | fn is_global_test() { 81 | let ipv4 = "127.0.0.1".parse().unwrap(); 82 | assert!(!IsGlobal::is_global(&IpAddr::V4(ipv4))); 83 | 84 | let ipv6 = Ipv4Addr::from_str("127.0.0.1").unwrap().to_ipv6_mapped(); 85 | assert!(!IsGlobal::is_global(&IpAddr::V6(ipv6))); 86 | 87 | let ipv6 = "::1".parse().unwrap(); 88 | assert!(!IsGlobal::is_global(&IpAddr::V6(ipv6))); 89 | 90 | let ipv4 = "10.0.0.1".parse().unwrap(); 91 | assert!(!IsGlobal::is_global(&IpAddr::V4(ipv4))); 92 | 93 | let ipv4 = "192.168.0.1".parse().unwrap(); 94 | assert!(!IsGlobal::is_global(&IpAddr::V4(ipv4))); 95 | 96 | let ipv4 = "169.254.1.1".parse().unwrap(); 97 | assert!(!IsGlobal::is_global(&IpAddr::V4(ipv4))); 98 | 99 | let ipv4 = "100.64.0.1".parse().unwrap(); 100 | assert!(!IsGlobal::is_global(&IpAddr::V4(ipv4))); 101 | 102 | let ipv6 = "FF02::1".parse().unwrap(); 103 | assert!(!IsGlobal::is_global(&IpAddr::V6(ipv6))); 104 | 105 | let ipv6 = Ipv4Addr::from_str("192.168.0.1").unwrap().to_ipv6_mapped(); 106 | assert!(!IsGlobal::is_global(&IpAddr::V6(ipv6))); 107 | 108 | // global ip 109 | let ipv4 = "128.0.0.1".parse().unwrap(); 110 | assert!(IsGlobal::is_global(&IpAddr::V4(ipv4))); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /tox_packet/src/dht/macros.rs: -------------------------------------------------------------------------------- 1 | /*! Macros for test functions 2 | */ 3 | 4 | macro_rules! dht_packet_encode_decode ( 5 | ($test:ident, $packet:ident) => ( 6 | encode_decode_test!( 7 | $test, 8 | Packet::$packet($packet { 9 | pk: crypto_box::SecretKey::generate(&mut rand::thread_rng()).public_key(), 10 | nonce: SalsaBox::generate_nonce(&mut rand::thread_rng()).into(), 11 | payload: vec![42; 123], 12 | }) 13 | ); 14 | ) 15 | ); 16 | 17 | macro_rules! dht_packet_encrypt_decrypt ( 18 | ($test:ident, $packet:ident, $payload:expr) => ( 19 | #[test] 20 | fn $test() { 21 | use crypto_box::SecretKey; 22 | 23 | let mut rng = rand::thread_rng(); 24 | let alice_sk = SecretKey::generate(&mut rng); 25 | let alice_pk = alice_sk.public_key(); 26 | let bob_pk = SecretKey::generate(&mut rng).public_key(); 27 | let shared_secret = SalsaBox::new(&bob_pk, &alice_sk); 28 | let payload = $payload; 29 | // encode payload with shared secret 30 | let dht_packet = $packet::new(&shared_secret, alice_pk, &payload); 31 | // decode payload with shared secret 32 | let decoded_payload = dht_packet.get_payload(&shared_secret).unwrap(); 33 | // payloads should be equal 34 | assert_eq!(decoded_payload, payload); 35 | } 36 | ) 37 | ); 38 | 39 | macro_rules! dht_packet_encrypt_decrypt_invalid_key ( 40 | ($test:ident, $packet:ident, $payload:expr) => ( 41 | #[test] 42 | fn $test() { 43 | use crypto_box::SecretKey; 44 | 45 | let mut rng = rand::thread_rng(); 46 | let alice_sk = SecretKey::generate(&mut rng); 47 | let alice_pk = alice_sk.public_key(); 48 | let bob_pk = SecretKey::generate(&mut rng).public_key(); 49 | let eve_sk = SecretKey::generate(&mut rng); 50 | let shared_secret = SalsaBox::new(&bob_pk, &alice_sk); 51 | let shared_secret_invalid = SalsaBox::new(&bob_pk, &eve_sk); 52 | let payload = $payload; 53 | // encode payload with shared secret 54 | let dht_packet = $packet::new(&shared_secret, alice_pk, &payload); 55 | // try to decode payload with invalid shared secret 56 | let decoded_payload = dht_packet.get_payload(&shared_secret_invalid); 57 | assert!(decoded_payload.is_err()); 58 | } 59 | ) 60 | ); 61 | 62 | macro_rules! dht_packet_decode_invalid ( 63 | ($test:ident, $packet:ident) => ( 64 | #[test] 65 | fn $test() { 66 | use aead::Aead; 67 | use crypto_box::SecretKey; 68 | 69 | let mut rng = rand::thread_rng(); 70 | let alice_sk = SecretKey::generate(&mut rng); 71 | let alice_pk = alice_sk.public_key(); 72 | let bob_pk = SecretKey::generate(&mut rng).public_key(); 73 | let shared_secret = SalsaBox::new(&bob_pk, &alice_sk); 74 | let nonce = SalsaBox::generate_nonce(&mut rng); 75 | // Try long invalid array 76 | let invalid_payload = [42; 123]; 77 | let invalid_payload_encoded = shared_secret.encrypt(&nonce, &invalid_payload[..]).unwrap(); 78 | let invalid_packet = $packet { 79 | pk: alice_pk.clone(), 80 | nonce: nonce.into(), 81 | payload: invalid_payload_encoded 82 | }; 83 | let decoded_payload = invalid_packet.get_payload(&shared_secret); 84 | assert!(decoded_payload.is_err()); 85 | // Try short incomplete array 86 | let invalid_payload = []; 87 | let invalid_payload_encoded = shared_secret.encrypt(&nonce, &invalid_payload[..]).unwrap(); 88 | let invalid_packet = $packet { 89 | pk: alice_pk, 90 | nonce: nonce.into(), 91 | payload: invalid_payload_encoded 92 | }; 93 | let decoded_payload = invalid_packet.get_payload(&shared_secret); 94 | assert!(decoded_payload.is_err()); 95 | } 96 | ); 97 | ); 98 | -------------------------------------------------------------------------------- /tox_node/dpkg/config.yml: -------------------------------------------------------------------------------- 1 | log-type: Syslog 2 | keys-file: /var/lib/tox-node/keys 3 | udp-address: 0.0.0.0:33445 4 | tcp-addresses: 5 | - 0.0.0.0:33445 6 | tcp-connections-limit: 8192 7 | lan-discovery: True 8 | threads: 1 9 | motd: "Hi from tox-rs! I'm up {{uptime}}. TCP: incoming {{tcp_packets_in}}, outgoing {{tcp_packets_out}}, UDP: incoming {{udp_packets_in}}, outgoing {{udp_packets_out}}" 10 | 11 | 12 | bootstrap-nodes: 13 | - pk: 7E5668E0EE09E19F320AD47902419331FFEE147BB3606769CFBE921A2A2FD34C 14 | addr: 144.217.167.73:33445 15 | - pk: 10C00EB250C3233E343E2AEBA07115A5C28920E9C8D29492F6D00B29049EDC7E 16 | addr: tox.abilinski.com:33445 17 | - pk: BEF0CFB37AF874BD17B9A8F9FE64C75521DB95A37D33C5BDB00E9CF58659C04F 18 | addr: 198.199.98.108:33445 19 | - pk: BEF0CFB37AF874BD17B9A8F9FE64C75521DB95A37D33C5BDB00E9CF58659C04F 20 | addr: 2604:a880:1:20::32f:1001:33445 21 | - pk: 82EF82BA33445A1F91A7DB27189ECFC0C013E06E3DA71F588ED692BED625EC23 22 | addr: tox.kurnevsky.net:33445 23 | - pk: 3091C6BEB2A993F1C6300C16549FABA67098FF3D62C6D253828B531470B53D68 24 | addr: 205.185.115.131:53 25 | - pk: 7A6098B590BDC73F9723FC59F82B3F9085A64D1B213AAF8E610FD351930D052D 26 | addr: tox2.abilinski.com:33445 27 | - pk: CD133B521159541FB1D326DE9850F5E56A6C724B5B8E5EB5CD8D950408E95707 28 | addr: 46.101.197.175:33445 29 | - pk: CD133B521159541FB1D326DE9850F5E56A6C724B5B8E5EB5CD8D950408E95707 30 | addr: 2a03:b0c0:3:d0::ac:5001:33445 31 | - pk: B3E5FA80DC8EBD1149AD2AB35ED8B85BD546DEDE261CA593234C619249419506 32 | addr: tox1.mf-net.eu:33445 33 | - pk: 70EA214FDE161E7432530605213F18F7427DC773E276B3E317A07531F548545F 34 | addr: tox2.mf-net.eu:33445 35 | - pk: B84E865125B4EC4C368CD047C72BCE447644A2DC31EF75BD2CDA345BFD310107 36 | addr: 195.201.7.101:33445 37 | - pk: 836D1DA2BE12FE0E669334E437BE3FB02806F1528C2B2782113E0910C7711409 38 | addr: tox4.plastiras.org:33445 39 | - pk: 1911341A83E02503AB1FD6561BD64AF3A9D6C3F12B5FBB656976B2E678644A67 40 | addr: 188.225.9.167:33445 41 | - pk: 1911341A83E02503AB1FD6561BD64AF3A9D6C3F12B5FBB656976B2E678644A67 42 | addr: 209:dead:ded:4991:49f3:b6c0:9869:3019:33445 43 | - pk: 5716530A10D362867C8E87EE1CD5362A233BAFBBA4CF47FA73B7CAD368BD5E6E 44 | addr: 122.116.39.151:33445 45 | - pk: 5716530A10D362867C8E87EE1CD5362A233BAFBBA4CF47FA73B7CAD368BD5E6E 46 | addr: 2001:b011:8:2f22:1957:7f9d:e31f:96dd:33445 47 | - pk: 4B031C96673B6FF123269FF18F2847E1909A8A04642BBECD0189AC8AEEADAF64 48 | addr: tox3.plastiras.org:33445 49 | - pk: 933BA20B2E258B4C0D475B6DECE90C7E827FE83EFA9655414E7841251B19A72C 50 | addr: 104.225.141.59:43334 51 | - pk: F76A11284547163889DDC89A7738CF271797BF5E5E220643E97AD3C7E7903D55 52 | addr: 139.162.110.188:33445 53 | - pk: F76A11284547163889DDC89A7738CF271797BF5E5E220643E97AD3C7E7903D55 54 | addr: 2400:8902::f03c:93ff:fe69:bf77:33445 55 | - pk: 28DB44A3CEEE69146469855DFFE5F54DA567F5D65E03EFB1D38BBAEFF2553255 56 | addr: 198.98.49.206:33445 57 | - pk: 28DB44A3CEEE69146469855DFFE5F54DA567F5D65E03EFB1D38BBAEFF2553255 58 | addr: 2605:6400:10:caa:1:be:a:7001:33445 59 | - pk: D46E97CF995DC1820B92B7D899E152A217D36ABE22730FEA4B6BF1BFC06C617C 60 | addr: 172.105.109.31:33445 61 | - pk: D46E97CF995DC1820B92B7D899E152A217D36ABE22730FEA4B6BF1BFC06C617C 62 | addr: 2600:3c04::f03c:92ff:fe30:5df:33445 63 | - pk: B5E7DAC610DBDE55F359C7F8690B294C8E4FCEC4385DE9525DBFA5523EAD9D53 64 | addr: 91.146.66.26:33445 65 | - pk: FD04EB03ABC5FC5266A93D37B4D6D6171C9931176DC68736629552D8EF0DE174 66 | addr: tox01.ky0uraku.xyz:33445 67 | - pk: D3D6D7C0C7009FC75406B0A49E475996C8C4F8BCE1E6FC5967DE427F8F600527 68 | addr: tox02.ky0uraku.xyz:33445 69 | - pk: 8E8B63299B3D520FB377FE5100E65E3322F7AE5B20A0ACED2981769FC5B43725 70 | addr: tox.plastiras.org:33445 71 | - pk: BE7ED53CD924813507BA711FD40386062E6DC6F790EFA122C78F7CDEEE4B6D1B 72 | addr: kusoneko.moe:33445 73 | - pk: B6626D386BE7E3ACA107B46F48A5C4D522D29281750D44A0CBA6A2721E79C951 74 | addr: tox2.plastiras.org:33445 75 | - pk: DA2BD927E01CD05EBCC2574EBE5BEBB10FF59AE0B2105A7D1E2B40E49BB20239 76 | addr: 172.104.215.182:33445 77 | - pk: DA2BD927E01CD05EBCC2574EBE5BEBB10FF59AE0B2105A7D1E2B40E49BB20239 78 | addr: 2600:3c03::f03c:93ff:fe7f:6096:33445 79 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/conference/change_name.rs: -------------------------------------------------------------------------------- 1 | /*! Change name message struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::{ 7 | bytes::complete::tag, 8 | combinator::{map_res, rest, verify}, 9 | number::complete::{be_u16, be_u32}, 10 | }; 11 | use std::str; 12 | 13 | use super::MAX_NAME_LENGTH_IN_CONFERENCE; 14 | 15 | /** ChangeName is the struct that holds info to notify changing name of a peer to a conference. 16 | 17 | Sent by a peer who wants to change its name or by a joining peer to notify its name to members of conference. 18 | 19 | Serialized form: 20 | 21 | Length | Content 22 | --------- | ------ 23 | `1` | `0x63` 24 | `2` | `conference id` 25 | `2` | `peer id` 26 | `4` | `message id` 27 | `1` | `0x30` 28 | variable | `name`(UTF-8 C String) 29 | 30 | */ 31 | #[derive(Clone, Debug, Eq, PartialEq)] 32 | pub struct ChangeName { 33 | /// Id of conference 34 | pub conference_id: u16, 35 | /// Target peer id 36 | pub peer_id: u16, 37 | /// Id of this message 38 | pub message_id: u32, 39 | /// Name to change 40 | pub name: String, 41 | } 42 | 43 | impl FromBytes for ChangeName { 44 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 45 | let (input, _) = tag("\x63")(input)?; 46 | let (input, conference_id) = be_u16(input)?; 47 | let (input, peer_id) = be_u16(input)?; 48 | let (input, message_id) = be_u32(input)?; 49 | let (input, _) = tag("\x30")(input)?; 50 | let (input, name) = map_res( 51 | verify(rest, |name: &[u8]| name.len() <= MAX_NAME_LENGTH_IN_CONFERENCE), 52 | str::from_utf8, 53 | )(input)?; 54 | Ok(( 55 | input, 56 | ChangeName { 57 | conference_id, 58 | peer_id, 59 | message_id, 60 | name: name.to_string(), 61 | }, 62 | )) 63 | } 64 | } 65 | 66 | impl ToBytes for ChangeName { 67 | #[rustfmt::skip] 68 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 69 | do_gen!(buf, 70 | gen_be_u8!(0x63) >> 71 | gen_be_u16!(self.conference_id) >> 72 | gen_be_u16!(self.peer_id) >> 73 | gen_be_u32!(self.message_id) >> 74 | gen_be_u8!(0x30) >> 75 | gen_cond!(self.name.len() > MAX_NAME_LENGTH_IN_CONFERENCE, |buf| gen_error(buf, 0)) >> 76 | gen_slice!(self.name.as_bytes()) 77 | ) 78 | } 79 | } 80 | 81 | impl ChangeName { 82 | /// Create new ChangeName object. 83 | pub fn new(conference_id: u16, peer_id: u16, message_id: u32, name: String) -> Self { 84 | ChangeName { 85 | conference_id, 86 | peer_id, 87 | message_id, 88 | name, 89 | } 90 | } 91 | } 92 | 93 | #[cfg(test)] 94 | mod tests { 95 | use super::*; 96 | 97 | encode_decode_test!(change_name_encode_decode, ChangeName::new(1, 2, 3, "1234".to_owned())); 98 | 99 | #[test] 100 | fn change_name_from_bytes_encoding_error() { 101 | let err_string = vec![0, 159, 146, 150]; // not UTF8 bytes. 102 | let mut buf = vec![0x63, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30]; 103 | buf.extend_from_slice(&err_string); 104 | assert!(ChangeName::from_bytes(&buf).is_err()); 105 | } 106 | 107 | #[test] 108 | fn change_name_from_bytes_overflow() { 109 | let large_string = vec![32; MAX_NAME_LENGTH_IN_CONFERENCE + 1]; 110 | let mut buf = vec![0x63, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30]; 111 | buf.extend_from_slice(&large_string); 112 | assert!(ChangeName::from_bytes(&buf).is_err()); 113 | } 114 | 115 | #[test] 116 | fn change_name_to_bytes_overflow() { 117 | let large_string = String::from_utf8(vec![32u8; MAX_NAME_LENGTH_IN_CONFERENCE + 1]).unwrap(); 118 | let large_name = ChangeName::new(1, 2, 3, large_string); 119 | let mut buf = [0; MAX_NAME_LENGTH_IN_CONFERENCE + 1 + 2 + 2 + 4 + 1]; // packet id + conference id + peer id + message id + message kind. 120 | assert!(large_name.to_bytes((&mut buf, 0)).is_err()); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/conference/change_title.rs: -------------------------------------------------------------------------------- 1 | /*! Change title message struct. 2 | */ 3 | 4 | use super::*; 5 | 6 | use nom::{ 7 | bytes::complete::tag, 8 | combinator::{map_res, rest, verify}, 9 | number::complete::{be_u16, be_u32}, 10 | }; 11 | use std::str; 12 | 13 | use super::MAX_NAME_LENGTH_IN_CONFERENCE; 14 | 15 | /** ChangeTitle is the struct that holds info to change title of a conference. 16 | 17 | Sent by anyone who is member of conference. 18 | This packet is used to change the title of conference. 19 | 20 | Serialized form: 21 | 22 | Length | Content 23 | --------- | ------ 24 | `1` | `0x63` 25 | `2` | `conference id` 26 | `2` | `peer id` 27 | `4` | `message id` 28 | `1` | `0x31` 29 | variable | `title`(UTF-8 C String) 30 | 31 | */ 32 | #[derive(Clone, Debug, Eq, PartialEq)] 33 | pub struct ChangeTitle { 34 | /// Id of conference 35 | pub conference_id: u16, 36 | /// Target peer id 37 | pub peer_id: u16, 38 | /// Id of this message 39 | pub message_id: u32, 40 | /// Title to change 41 | pub title: String, 42 | } 43 | 44 | impl FromBytes for ChangeTitle { 45 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 46 | let (input, _) = tag("\x63")(input)?; 47 | let (input, conference_id) = be_u16(input)?; 48 | let (input, peer_id) = be_u16(input)?; 49 | let (input, message_id) = be_u32(input)?; 50 | let (input, _) = tag("\x31")(input)?; 51 | let (input, title) = map_res( 52 | verify(rest, |title: &[u8]| title.len() <= MAX_NAME_LENGTH_IN_CONFERENCE), 53 | str::from_utf8, 54 | )(input)?; 55 | Ok(( 56 | input, 57 | ChangeTitle { 58 | conference_id, 59 | peer_id, 60 | message_id, 61 | title: title.to_string(), 62 | }, 63 | )) 64 | } 65 | } 66 | 67 | impl ToBytes for ChangeTitle { 68 | #[rustfmt::skip] 69 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 70 | do_gen!(buf, 71 | gen_be_u8!(0x63) >> 72 | gen_be_u16!(self.conference_id) >> 73 | gen_be_u16!(self.peer_id) >> 74 | gen_be_u32!(self.message_id) >> 75 | gen_be_u8!(0x31) >> 76 | gen_cond!(self.title.len() > MAX_NAME_LENGTH_IN_CONFERENCE, |buf| gen_error(buf, 0)) >> 77 | gen_slice!(self.title.as_bytes()) 78 | ) 79 | } 80 | } 81 | 82 | impl ChangeTitle { 83 | /// Create new ChangeTitle object. 84 | pub fn new(conference_id: u16, peer_id: u16, message_id: u32, title: String) -> Self { 85 | ChangeTitle { 86 | conference_id, 87 | peer_id, 88 | message_id, 89 | title, 90 | } 91 | } 92 | } 93 | 94 | #[cfg(test)] 95 | mod tests { 96 | use super::*; 97 | 98 | encode_decode_test!(change_title_encode_decode, ChangeTitle::new(1, 2, 3, "1234".to_owned())); 99 | 100 | #[test] 101 | fn change_title_from_bytes_encoding_error() { 102 | let err_string = vec![0, 159, 146, 150]; // not UTF8 bytes. 103 | let mut buf = vec![0x63, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31]; 104 | buf.extend_from_slice(&err_string); 105 | assert!(ChangeTitle::from_bytes(&buf).is_err()); 106 | } 107 | 108 | #[test] 109 | fn change_title_from_bytes_overflow() { 110 | let large_string = vec![32; MAX_NAME_LENGTH_IN_CONFERENCE + 1]; 111 | let mut buf = vec![0x63, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31]; 112 | buf.extend_from_slice(&large_string); 113 | assert!(ChangeTitle::from_bytes(&buf).is_err()); 114 | } 115 | 116 | #[test] 117 | fn change_title_to_bytes_overflow() { 118 | let large_string = String::from_utf8(vec![32u8; MAX_NAME_LENGTH_IN_CONFERENCE + 1]).unwrap(); 119 | let large_title = ChangeTitle::new(1, 2, 3, large_string); 120 | let mut buf = [0; MAX_NAME_LENGTH_IN_CONFERENCE + 1 + 2 + 2 + 4 + 1]; // packet id + conference id + peer id + message id + message kind. 121 | assert!(large_title.to_bytes((&mut buf, 0)).is_err()); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /tox_core/src/relay/server/client.rs: -------------------------------------------------------------------------------- 1 | /*! The inner implementation of client used only by relay server. 2 | */ 3 | 4 | use crate::relay::links::Links; 5 | use crate::time::*; 6 | use crate::utils::*; 7 | use tox_crypto::*; 8 | use tox_packet::relay::*; 9 | 10 | use std::net::IpAddr; 11 | use std::time::{Duration, Instant}; 12 | 13 | use futures::channel::mpsc; 14 | use rand::thread_rng; 15 | 16 | /// Interval of time for sending TCP PingRequest 17 | pub const TCP_PING_FREQUENCY: Duration = Duration::from_secs(30); 18 | /// Interval of time for waiting response of PingRequest sent 19 | pub const TCP_PING_TIMEOUT: Duration = Duration::from_secs(10); 20 | 21 | /** Structure that represents how Server keeps connected clients. A write-only socket with 22 | human interface. A client cannot send a message directly to another client, whereas server can. 23 | */ 24 | pub struct Client { 25 | /// PublicKey of the client. 26 | pk: PublicKey, 27 | /// IpAddr of the client. 28 | ip_addr: IpAddr, 29 | /// Port of the client. 30 | port: u16, 31 | /// The transmission end of a channel which is used to send values. 32 | tx: mpsc::Sender, 33 | /** links - a table of indexing links from this client to another 34 | 35 | A client requests to link him with another client by PK with RouteRequest. 36 | The server inserts that PK into links and gives the index of the link back to client 37 | via RouteResponse. Now the client may use this index to communicate with the connection 38 | using that index, e.g. send Data by index. Our links are 0-based while wire indices are 39 | 16-based. E.g. `::get_connection_id` and `::insert_connection_id` return `Some(x+16)`, 40 | `::get_link` and `::take_link` accept ids in `[0; 240) + 16`. All conversions are done only 41 | inside this module. 42 | */ 43 | links: Links, 44 | /// Used to check whether PongResponse is correct 45 | ping_id: u64, 46 | /// Last time sent PingRequest packet 47 | last_pinged: Instant, 48 | /// Last time received PongResponse 49 | last_pong_resp: Instant, 50 | } 51 | 52 | impl Client { 53 | /** Create new Client 54 | */ 55 | pub fn new(tx: mpsc::Sender, pk: &PublicKey, ip_addr: IpAddr, port: u16) -> Client { 56 | Client { 57 | pk: pk.clone(), 58 | ip_addr, 59 | port, 60 | tx, 61 | links: Links::new(), 62 | ping_id: 0, 63 | last_pinged: clock_now(), 64 | last_pong_resp: clock_now(), 65 | } 66 | } 67 | 68 | /** PK of the `Client` 69 | */ 70 | pub fn pk(&self) -> PublicKey { 71 | self.pk.clone() 72 | } 73 | 74 | /** `std::net::IpAddr` of the `Client` 75 | */ 76 | pub fn ip_addr(&self) -> IpAddr { 77 | self.ip_addr 78 | } 79 | 80 | /** Port of the `Client` 81 | */ 82 | pub fn port(&self) -> u16 { 83 | self.port 84 | } 85 | 86 | /** Last ping_id sent to client. 87 | */ 88 | pub fn ping_id(&self) -> u64 { 89 | self.ping_id 90 | } 91 | 92 | /** Set last_pong_resp 93 | */ 94 | pub fn set_last_pong_resp(&mut self, time: Instant) { 95 | self.last_pong_resp = time; 96 | } 97 | 98 | /** Check if PongResponse timed out 99 | */ 100 | pub fn is_pong_timedout(&self) -> bool { 101 | clock_elapsed(self.last_pong_resp) > TCP_PING_TIMEOUT + TCP_PING_FREQUENCY 102 | } 103 | 104 | /** Check if Ping interval is elapsed 105 | */ 106 | pub fn is_ping_interval_passed(&self) -> bool { 107 | clock_elapsed(self.last_pinged) >= TCP_PING_FREQUENCY 108 | } 109 | 110 | /** Get the Links of the Client 111 | */ 112 | pub fn links(&self) -> &Links { 113 | &self.links 114 | } 115 | 116 | /** Get the Links of the Client 117 | */ 118 | pub fn links_mut(&mut self) -> &mut Links { 119 | &mut self.links 120 | } 121 | 122 | pub fn tx(&self) -> mpsc::Sender { 123 | self.tx.clone() 124 | } 125 | 126 | pub fn new_ping_id(&mut self) -> u64 { 127 | let ping_id = gen_ping_id(&mut thread_rng()); 128 | 129 | self.last_pinged = Instant::now(); 130 | self.ping_id = ping_id; 131 | 132 | ping_id 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /tox_packet/src/relay/onion_request.rs: -------------------------------------------------------------------------------- 1 | /*! OnionRequest packet 2 | */ 3 | 4 | use super::*; 5 | 6 | use crate::ip_port::*; 7 | use crate::onion::{ONION_MAX_PACKET_SIZE, ONION_RETURN_1_SIZE}; 8 | use crypto_box::{ 9 | aead::{generic_array::typenum::marker_traits::Unsigned, AeadCore}, 10 | SalsaBox, 11 | }; 12 | use tox_binary_io::*; 13 | use tox_crypto::*; 14 | 15 | use nom::bytes::complete::tag; 16 | use nom::combinator::{rest, verify}; 17 | 18 | /// Encrypted payload should contain `IpPort`, `PublicKey` and inner encrypted 19 | /// payload that should contain at least `IpPort` struct. 20 | const ONION_MIN_PAYLOAD_SIZE: usize = (SIZE_IPPORT + ::TagSize::USIZE) * 2 + crypto_box::KEY_SIZE; 21 | 22 | /// `OnionRequest1` packet with encrypted payload from `OnionRequest` packet 23 | /// shouldn't be bigger than `ONION_MAX_PACKET_SIZE`. 24 | const ONION_MAX_PAYLOAD_SIZE: usize = 25 | ONION_MAX_PACKET_SIZE - (1 + NONCEBYTES + crypto_box::KEY_SIZE + ONION_RETURN_1_SIZE); 26 | 27 | /** Sent by client to server. 28 | The server will pack payload from this request to `OnionRequest1` packet and send 29 | it to UDP socket. The server can accept both TCP and UDP families as destination 30 | IP address but regardless of this it will always send `OnionRequest1` to UDP 31 | socket. Return address from `OnionRequest1` will contain TCP address so that 32 | when we get `OnionResponse2` we will know that this response should be sent to 33 | TCP client connected to our server. 34 | 35 | Serialized form: 36 | 37 | Length | Content 38 | -------- | ------ 39 | `1` | `0x08` 40 | `24` | Nonce 41 | `1` | IpType 42 | `4` or `16` | IPv4 or IPv6 address 43 | `0` or `12` | Padding for IPv4 44 | `2` | Port 45 | `32` | PublicKey 46 | variable | Payload 47 | 48 | */ 49 | #[derive(Debug, PartialEq, Eq, Clone)] 50 | pub struct OnionRequest { 51 | /// Nonce that was used for payload encryption 52 | pub nonce: Nonce, 53 | /// Address of the next onion node 54 | pub ip_port: IpPort, 55 | /// Temporary `PublicKey` for the current encrypted payload 56 | pub temporary_pk: PublicKey, 57 | /// Encrypted payload 58 | pub payload: Vec, 59 | } 60 | 61 | impl FromBytes for OnionRequest { 62 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 63 | let (input, _) = tag("\x08")(input)?; 64 | let (input, nonce) = Nonce::from_bytes(input)?; 65 | let (input, ip_port) = IpPort::from_bytes(input, IpPortPadding::WithPadding)?; 66 | let (input, temporary_pk) = PublicKey::from_bytes(input)?; 67 | let (input, payload) = verify(rest, |payload: &[u8]| { 68 | payload.len() >= ONION_MIN_PAYLOAD_SIZE && payload.len() <= ONION_MAX_PAYLOAD_SIZE 69 | })(input)?; 70 | Ok(( 71 | input, 72 | OnionRequest { 73 | nonce, 74 | ip_port, 75 | temporary_pk, 76 | payload: payload.to_vec(), 77 | }, 78 | )) 79 | } 80 | } 81 | 82 | impl ToBytes for OnionRequest { 83 | #[rustfmt::skip] 84 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 85 | do_gen!(buf, 86 | gen_cond!( 87 | self.payload.len() < ONION_MIN_PAYLOAD_SIZE || self.payload.len() > ONION_MAX_PAYLOAD_SIZE, 88 | |buf| gen_error(buf, 0) 89 | ) >> 90 | gen_be_u8!(0x08) >> 91 | gen_slice!(self.nonce.as_ref()) >> 92 | gen_call!(|buf, ip_port| IpPort::to_bytes(ip_port, buf, IpPortPadding::WithPadding), &self.ip_port) >> 93 | gen_slice!(self.temporary_pk.as_ref()) >> 94 | gen_slice!(self.payload.as_slice()) 95 | ) 96 | } 97 | } 98 | 99 | #[cfg(test)] 100 | mod test { 101 | use rand::thread_rng; 102 | 103 | use super::*; 104 | 105 | encode_decode_test!( 106 | onion_request_encode_decode, 107 | OnionRequest { 108 | nonce: [42; ::NonceSize::USIZE], 109 | ip_port: IpPort { 110 | protocol: ProtocolType::Tcp, 111 | ip_addr: "5.6.7.8".parse().unwrap(), 112 | port: 12345, 113 | }, 114 | temporary_pk: SecretKey::generate(&mut thread_rng()).public_key(), 115 | payload: vec![42; ONION_MIN_PAYLOAD_SIZE] 116 | } 117 | ); 118 | } 119 | -------------------------------------------------------------------------------- /tox_packet/src/onion/friend_request.rs: -------------------------------------------------------------------------------- 1 | /*! FriendRequest packet 2 | */ 3 | 4 | use super::*; 5 | use crate::toxid::{NoSpam, NOSPAMBYTES}; 6 | use nom::bytes::complete::tag; 7 | use nom::combinator::{map_res, verify}; 8 | use std::str; 9 | 10 | const MAX_FRIEND_REQUEST_MSG_SIZE: usize = MAX_ONION_CLIENT_DATA_SIZE - (1 + NOSPAMBYTES); 11 | 12 | /** Friend request that can be enclosed in onion data packet and sent through onion 13 | path. 14 | 15 | Length | Content 16 | --------- | ------------------------- 17 | `1` | `0x20` 18 | `4` | NoSpam 19 | `1..991` | Message 20 | 21 | */ 22 | #[derive(Clone, Debug, Eq, PartialEq)] 23 | pub struct FriendRequest { 24 | nospam: NoSpam, 25 | msg: String, 26 | } 27 | 28 | impl FriendRequest { 29 | /// Create new object 30 | pub fn new(nospam: NoSpam, msg: String) -> Self { 31 | FriendRequest { nospam, msg } 32 | } 33 | } 34 | 35 | impl ToBytes for FriendRequest { 36 | #[rustfmt::skip] 37 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 38 | do_gen!(buf, 39 | gen_be_u8!(0x20) >> 40 | gen_slice!(self.nospam.0) >> 41 | gen_cond!(self.msg.len() > MAX_FRIEND_REQUEST_MSG_SIZE || self.msg.is_empty(), |buf| gen_error(buf, 0)) >> 42 | gen_slice!(self.msg.as_bytes()) 43 | ) 44 | } 45 | } 46 | 47 | impl FromBytes for FriendRequest { 48 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 49 | let (input, _) = tag(&[0x20][..])(input)?; 50 | let (input, nospam) = NoSpam::from_bytes(input)?; 51 | let (input, msg) = map_res( 52 | verify(rest, |msg: &[u8]| { 53 | msg.len() <= MAX_FRIEND_REQUEST_MSG_SIZE && !msg.is_empty() 54 | }), 55 | str::from_utf8, 56 | )(input)?; 57 | Ok(( 58 | input, 59 | FriendRequest { 60 | nospam, 61 | msg: msg.to_string(), 62 | }, 63 | )) 64 | } 65 | } 66 | 67 | #[cfg(test)] 68 | mod tests { 69 | use super::*; 70 | use crate::toxid::NOSPAMBYTES; 71 | 72 | encode_decode_test!( 73 | friend_req_encode_decode, 74 | FriendRequest { 75 | nospam: NoSpam([42; NOSPAMBYTES]), 76 | msg: "1234".to_owned(), 77 | } 78 | ); 79 | 80 | #[test] 81 | fn friend_request_from_bytes_encoding_error() { 82 | let err_string = vec![0, 159, 146, 150]; // not UTF8 bytes. 83 | let nospam = [42; NOSPAMBYTES]; 84 | 85 | let mut friend_req = vec![0x20]; 86 | friend_req.extend_from_slice(&nospam); 87 | friend_req.extend(err_string); 88 | assert!(FriendRequest::from_bytes(&friend_req).is_err()); 89 | } 90 | 91 | #[test] 92 | fn friend_request_from_bytes_overflow() { 93 | let large_string = vec![32; MAX_FRIEND_REQUEST_MSG_SIZE + 1]; 94 | let nospam = [42; NOSPAMBYTES]; 95 | 96 | let mut friend_req = vec![0x20]; 97 | friend_req.extend_from_slice(&nospam); 98 | friend_req.extend(large_string); 99 | assert!(FriendRequest::from_bytes(&friend_req).is_err()); 100 | } 101 | 102 | #[test] 103 | fn friend_request_to_bytes_overflow() { 104 | let large_string = String::from_utf8(vec![32u8; MAX_FRIEND_REQUEST_MSG_SIZE + 1]).unwrap(); 105 | let friend_req = FriendRequest { 106 | nospam: NoSpam([42; NOSPAMBYTES]), 107 | msg: large_string, 108 | }; 109 | let mut buf = [0; MAX_ONION_CLIENT_DATA_SIZE + 1]; // `1` is to provide enough space for success of serializing 110 | assert!(friend_req.to_bytes((&mut buf, 0)).is_err()); 111 | } 112 | 113 | #[test] 114 | fn friend_request_from_bytes_underflow() { 115 | let nospam = [42; NOSPAMBYTES]; 116 | 117 | let mut friend_req = vec![0x20]; 118 | friend_req.extend_from_slice(&nospam); 119 | assert!(FriendRequest::from_bytes(&friend_req).is_err()); 120 | } 121 | 122 | #[test] 123 | fn friend_request_to_bytes_underflow() { 124 | let friend_req = FriendRequest { 125 | nospam: NoSpam([42; NOSPAMBYTES]), 126 | msg: "".to_string(), 127 | }; 128 | let mut buf = [0; MAX_ONION_CLIENT_DATA_SIZE]; // `1` is to provide enough space for success of serializing 129 | assert!(friend_req.to_bytes((&mut buf, 0)).is_err()); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /tox_packet/src/messenger/file_transfer/file_send_request.rs: -------------------------------------------------------------------------------- 1 | /*! FileSendRequest struct. 2 | It is used to start transferring file to a friend. 3 | */ 4 | 5 | use nom::{ 6 | bytes::complete::tag, 7 | combinator::{map_res, rest, verify}, 8 | number::complete::le_u8, 9 | }; 10 | 11 | use std::str; 12 | 13 | use super::*; 14 | 15 | /** FileSendRequest is a struct that holds info to start transferring file to a friend. 16 | 17 | This packet is used to start transferring sender's file to a friend. 18 | `file_type` and `file_size` are sent in big endian format. 19 | 20 | Serialized form: 21 | 22 | Length | Content 23 | --------- | ------ 24 | `1` | `0x50` 25 | `1` | `file_id` 26 | `4` | `file_type`(0 = normal file, 1 = avatar file) 27 | `8` | `file_size` 28 | `32` | `file_unique_id`(a random bytes) 29 | `0..255` | file name as a UTF-8 C string 30 | 31 | */ 32 | #[derive(Clone, Debug, Eq, PartialEq)] 33 | pub struct FileSendRequest { 34 | file_id: u8, 35 | file_type: FileType, 36 | file_size: u64, 37 | file_unique_id: FileUid, 38 | file_name: String, 39 | } 40 | 41 | impl FromBytes for FileSendRequest { 42 | fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> { 43 | let (input, _) = tag("\x50")(input)?; 44 | let (input, file_id) = le_u8(input)?; 45 | let (input, file_type) = FileType::from_bytes(input)?; 46 | let (input, file_size) = be_u64(input)?; 47 | let (input, file_unique_id) = FileUid::from_bytes(input)?; 48 | let (input, file_name) = map_res( 49 | verify(rest, |file_name: &[u8]| file_name.len() <= MAX_FILESEND_FILENAME_LENGTH), 50 | str::from_utf8, 51 | )(input)?; 52 | Ok(( 53 | input, 54 | FileSendRequest { 55 | file_id, 56 | file_type, 57 | file_size, 58 | file_unique_id, 59 | file_name: file_name.to_string(), 60 | }, 61 | )) 62 | } 63 | } 64 | 65 | impl ToBytes for FileSendRequest { 66 | #[rustfmt::skip] 67 | fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> { 68 | do_gen!(buf, 69 | gen_be_u8!(0x50) >> 70 | gen_be_u8!(self.file_id) >> 71 | gen_be_u32!(self.file_type as u32) >> 72 | gen_be_u64!(self.file_size) >> 73 | gen_slice!(self.file_unique_id.0) >> 74 | gen_cond!(self.file_name.len() > MAX_FILESEND_FILENAME_LENGTH, |buf| gen_error(buf, 0)) >> 75 | gen_slice!(self.file_name.as_bytes()) 76 | )} 77 | } 78 | 79 | impl FileSendRequest { 80 | /// Create new FileControl object. 81 | pub fn new(file_id: u8, file_type: FileType, file_size: u64, file_unique_id: FileUid, file_name: String) -> Self { 82 | FileSendRequest { 83 | file_id, 84 | file_type, 85 | file_size, 86 | file_unique_id, 87 | file_name, 88 | } 89 | } 90 | } 91 | 92 | #[cfg(test)] 93 | mod tests { 94 | use super::*; 95 | 96 | encode_decode_test!( 97 | file_send_request_encode_decode, 98 | FileSendRequest::new(1, FileType::Data, 4, FileUid([42; FILE_UID_BYTES]), "data".to_string()) 99 | ); 100 | 101 | #[test] 102 | fn file_send_request_from_bytes_encoding_error() { 103 | let mut packet = vec![ 104 | 0x50, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 105 | ]; 106 | packet.extend_from_slice(&[42; FILE_UID_BYTES]); 107 | let err_string = vec![0, 159, 146, 150]; // not UTF8 bytes. 108 | packet.extend_from_slice(&err_string); 109 | assert!(FileSendRequest::from_bytes(&packet).is_err()); 110 | } 111 | 112 | #[test] 113 | fn file_send_request_from_bytes_overflow() { 114 | let mut packet = vec![ 115 | 0x50, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 116 | ]; 117 | packet.extend_from_slice(&[42; FILE_UID_BYTES]); 118 | let large_string = vec![32; MAX_FILESEND_FILENAME_LENGTH + 1]; 119 | packet.extend_from_slice(&large_string); 120 | assert!(FileSendRequest::from_bytes(&packet).is_err()); 121 | } 122 | 123 | #[test] 124 | fn file_send_request_to_bytes_overflow() { 125 | let large_string = String::from_utf8(vec![32u8; MAX_FILESEND_FILENAME_LENGTH + 1]).unwrap(); 126 | let large_msg = FileSendRequest::new(1, FileType::Data, 0xff00, FileUid([42; FILE_UID_BYTES]), large_string); 127 | let mut buf = [0; MAX_FILESEND_FILENAME_LENGTH + 1 + 4 + 8 + FILE_UID_BYTES]; // provide needed space for serialize. 128 | assert!(large_msg.to_bytes((&mut buf, 0)).is_err()); 129 | } 130 | } 131 | --------------------------------------------------------------------------------