pax_global_header00006660000000000000000000000064146725371530014527gustar00rootroot0000000000000052 comment=469b0deb9a64554c11e1d2227c78fe7c6c769aa7 rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/000077500000000000000000000000001467253715300215255ustar00rootroot00000000000000rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/.github/000077500000000000000000000000001467253715300230655ustar00rootroot00000000000000rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/.github/codecov.yml000066400000000000000000000000431467253715300252270ustar00rootroot00000000000000coverage: status: patch: off rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/.github/dependabot.yml000066400000000000000000000003161467253715300257150ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "cargo" directory: "/" schedule: interval: "daily" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/.github/workflows/000077500000000000000000000000001467253715300251225ustar00rootroot00000000000000rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/.github/workflows/build.yml000066400000000000000000000034731467253715300267530ustar00rootroot00000000000000name: build on: [push, pull_request] jobs: build: name: Build strategy: fail-fast: false matrix: platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: - name: Checkout Sources uses: actions/checkout@v4 - name: Cache Dependencies & Build Outputs uses: actions/cache@v3 with: path: ~/.cargo key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Install Rust Nightly Toolchain uses: dtolnay/rust-toolchain@nightly - name: Install Rust Stable Toolchain uses: dtolnay/rust-toolchain@stable with: components: rustfmt, clippy - name: Check Code Format run: cargo fmt --all -- --check shell: bash - name: Code Lint run: cargo clippy --all-targets --all-features --workspace -- -D warnings shell: bash - name: Code Lint Without Default Features run: cargo clippy --no-default-features --workspace -- -D warnings shell: bash - name: Test run: cargo test --all-features --workspace shell: bash - name: Nightly Test run: cargo test shell: bash env: RUSTFLAGS: '--cfg nightly -Zcrate-attr=feature(variant_count)' RUSTUP_TOOLCHAIN: nightly coverage: name: Code Coverage runs-on: ubuntu-latest steps: - name: Checkout Sources uses: actions/checkout@v4 - name: Install Rust Toolchain uses: dtolnay/rust-toolchain@stable - name: Generate Code Coverage run: | cargo install --version 0.22.0 cargo-tarpaulin cargo tarpaulin --all-features --out Xml shell: bash - name: Upload Code Coverage uses: codecov/codecov-action@v3 rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/.gitignore000066400000000000000000000000321467253715300235100ustar00rootroot00000000000000target Cargo.lock *.rs.bk rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/CHANGELOG.md000066400000000000000000000121601467253715300233360ustar00rootroot00000000000000# 0.18.2 - Implement missing protocols. See [PR 110]. [PR 110]: https://github.com/multiformats/rust-multiaddr/pull/110. - Re-export `libp2p_identity::PeerId`. See [PR 108]. [PR 108]: https://github.com/multiformats/rust-multiaddr/pull/108. - Avoid allocations in Display and Debug of Multiaddr. See [PR 106]. [PR 106]: https://github.com/multiformats/rust-multiaddr/pull/106 # 0.18.1 - Add `with_p2p` on `Multiaddr`. See [PR 102]. [PR 102]: https://github.com/multiformats/rust-multiaddr/pull/102 # 0.18.0 - Add `WebTransport` instance for `Multiaddr`. See [PR 70]. - Disable all features of `multihash`. See [PR 77]. - Mark `Protocol` as `#[non_exhaustive]`. See [PR 82]. - Rename `Protocol::WebRTC` to `Protocol::WebRTCDirect`. See [multiformats/multiaddr discussion] for context. Remove deprecated support for `/webrtc` in favor of the existing `/webrtc-direct` string representation. **Note that this is a breaking change.** - Make `/p2p` typesafe, i.e. have `Protocol::P2p` contain a `PeerId` instead of a `Multihash`. See [PR 83]. [multiformats/multiaddr discussion]: https://github.com/multiformats/multiaddr/pull/150#issuecomment-1468791586 [PR 70]: https://github.com/multiformats/rust-multiaddr/pull/70 [PR 77]: https://github.com/multiformats/rust-multiaddr/pull/77 [PR 82]: https://github.com/multiformats/rust-multiaddr/pull/82 [PR 83]: https://github.com/multiformats/rust-multiaddr/pull/83 # 0.17.1 - Rename string representation of `WebRTC` protocol from `/webrtc` to `/webrt-direct`. For backwards compatibility `/webrtc` will still be decoded to `Protocol::WebRTC`, but `Protocol::WebRTC` will from now on always be encoded as `/webrtc-direct`. See [multiformats/multiaddr discussion] and [PR 84] for context. ``` rust assert_eq!( Multiaddr::empty().with(Protocol::WebRTC), "/webrtc".parse().unwrap(), ); assert_eq!( Multiaddr::empty().with(Protocol::WebRTC), "/webrtc-direct".parse().unwrap(), ); assert_eq!( "/webrtc-direct", Multiaddr::empty().with(Protocol::WebRTC).to_string(), ); assert_ne!( "/webrtc", Multiaddr::empty().with(Protocol::WebRTC).to_string(), ); ``` [PR 84]: https://github.com/multiformats/rust-multiaddr/pull/84 # 0.17.0 - Update to multihash `v0.17`. See [PR 63]. [PR 63]: https://github.com/multiformats/rust-multiaddr/pull/63 # 0.16.0 [2022-11-04] - Create `protocol_stack` for Multiaddr. See [PR 60]. - Add `QuicV1` instance for `Multiaddr`. See [PR 64]. [PR 60]: https://github.com/multiformats/rust-multiaddr/pull/60 [PR 64]: https://github.com/multiformats/rust-multiaddr/pull/64 # 0.15.0 [2022-10-20] - Add `WebRTC` instance for `Multiaddr`. See [PR 59]. - Add `Certhash` instance for `Multiaddr`. See [PR 59]. - Add support for Noise protocol. See [PR 53]. - Use `multibase` instead of `bs58` for base58 encoding. See [PR 56]. [PR 53]: https://github.com/multiformats/rust-multiaddr/pull/53 [PR 56]: https://github.com/multiformats/rust-multiaddr/pull/56 [PR 59]: https://github.com/multiformats/rust-multiaddr/pull/59 # 0.14.0 [2022-02-02] - Add support for TLS protocol (see [PR 48]). - Update to `multihash` `v0.15` (see [PR 50]). - Update to `multihash` `v0.16` (see [PR 51]). [PR 48]: https://github.com/multiformats/rust-multiaddr/pull/48 [PR 50]: https://github.com/multiformats/rust-multiaddr/pull/50 [PR 50]: https://github.com/multiformats/rust-multiaddr/pull/51 # 0.13.0 [2021-07-08] - Update to multihash v0.14.0 (see [PR 44]). - Update to rand v0.8.4 (see [PR 45]). [PR 44]: https://github.com/multiformats/rust-multiaddr/pull/44 [PR 45]: https://github.com/multiformats/rust-multiaddr/pull/45 # 0.12.0 [2021-05-26] - Merge [multiaddr] and [parity-multiaddr] (see [PR 40]). - Functionality to go from a `u64` to a `multiadddr::Protocol` and back is removed. Please open an issue on [multiaddr] in case this is still needed. - Given that `multiaddr::Protocol` now represents both the protocol identifier as well as the protocol data (e.g. protocol identifier `55` (`dns6`) and protocol data `some-domain.example`) `multiaddr::Protocol` is no longer `Copy`. [multiaddr]: https://github.com/multiformats/rust-multiaddr [parity-multiaddr]: https://github.com/libp2p/rust-libp2p/blob/master/misc/multiaddr/ [PR 40]: https://github.com/multiformats/rust-multiaddr/pull/40 # 0.11.2 [2021-03-17] - Add `Multiaddr::ends_with()`. # 0.11.1 [2021-02-15] - Update dependencies # 0.11.0 [2021-01-12] - Update dependencies # 0.10.1 [2021-01-12] - Fix compilation with serde-1.0.119. [PR 1912](https://github.com/libp2p/rust-libp2p/pull/1912) # 0.10.0 [2020-11-25] - Upgrade multihash to `0.13`. # 0.9.6 [2020-11-17] - Move the `from_url` module and functionality behind the `url` feature, enabled by default. [PR 1843](https://github.com/libp2p/rust-libp2p/pull/1843). # 0.9.5 [2020-11-14] - Limit initial memory allocation in `visit_seq`. [PR 1833](https://github.com/libp2p/rust-libp2p/pull/1833). # 0.9.4 [2020-11-09] - Update dependencies. # 0.9.3 [2020-10-16] - Update dependencies. # 0.9.2 [2020-08-31] - Add `Ord` instance for `Multiaddr`. # 0.9.1 [2020-06-22] - Updated dependencies. rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/Cargo.toml000066400000000000000000000021761467253715300234630ustar00rootroot00000000000000[package] authors = ["dignifiedquire ", "Parity Technologies "] description = "Implementation of the multiaddr format" edition = "2021" rust-version = "1.59.0" repository = "https://github.com/multiformats/rust-multiaddr" keywords = ["multiaddr", "ipfs"] license = "MIT" name = "multiaddr" readme = "README.md" version = "0.18.2" [features] default = ["url"] [dependencies] arrayref = "0.3" byteorder = "1.5.0" data-encoding = "2.6.0" multibase = "0.9.1" multihash = "0.19" percent-encoding = "2.3.1" serde = "1.0.209" static_assertions = "1.1" unsigned-varint = "0.8" url = { version = "2.5.0", optional = true, default-features = false } libp2p-identity = { version = "0.2.9", features = ["peerid"] } [dev-dependencies] bincode = "1" quickcheck = { version = "1.0.3", default-features = false } rand = "0.8.5" serde_json = "1.0" # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling [package.metadata.docs.rs] all-features = true [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(nightly)'] } rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/LICENSE000066400000000000000000000021511467253715300225310ustar00rootroot00000000000000The MIT License Copyright (C) 2015-2016 Friedel Ziegelmayer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Status API Training Shop Blog About Pricing rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/README.md000066400000000000000000000050321467253715300230040ustar00rootroot00000000000000# rust-multiaddr [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) [![](https://img.shields.io/badge/project-multiformats-blue.svg?style=flat-square)](https://github.com/multiformats/multiformats) [![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs) [![Travis CI](https://img.shields.io/travis/multiformats/rust-multiaddr.svg?style=flat-square&branch=master)](https://travis-ci.org/multiformats/rust-multiaddr) [![codecov.io](https://img.shields.io/codecov/c/github/multiformats/rust-multiaddr.svg?style=flat-square&branch=master)](https://codecov.io/github/multiformats/rust-multiaddr?branch=master) [![](https://img.shields.io/badge/rust-docs-blue.svg?style=flat-square)](https://docs.rs/crate/multiaddr) [![crates.io](https://img.shields.io/badge/crates.io-v0.2.0-orange.svg?style=flat-square )](https://crates.io/crates/multiaddr) [![](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) > [multiaddr](https://github.com/multiformats/multiaddr) implementation in Rust. ## Table of Contents - [Install](#install) - [Usage](#usage) - [Maintainers](#maintainers) - [Contribute](#contribute) - [License](#license) ## Install First add this to your `Cargo.toml` ```toml [dependencies] multiaddr = "*" ``` then run `cargo build`. ## Usage ```rust extern crate multiaddr; use multiaddr::{Multiaddr, multiaddr}; let address = "/ip4/127.0.0.1/tcp/1234".parse::().unwrap(); // or with a macro let other = multiaddr!(Ip4([127, 0, 0, 1]), Udp(10500u16), QuicV1); assert_eq!(address.to_string(), "/ip4/127.0.0.1/tcp/1234"); assert_eq!(other.to_string(), "/ip4/127.0.0.1/udp/10500/quic-v1"); ``` ## Maintainers Captain: [@dignifiedquire](https://github.com/dignifiedquire). ## Contribute Contributions welcome. Please check out [the issues](https://github.com/multiformats/rust-multiaddr/issues). Check out our [contributing document](https://github.com/multiformats/multiformats/blob/master/contributing.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to multiformats are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. ## License [MIT](LICENSE) © 2015-2017 Friedel Ziegelmeyer rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/src/000077500000000000000000000000001467253715300223145ustar00rootroot00000000000000rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/src/errors.rs000066400000000000000000000046641467253715300242100ustar00rootroot00000000000000use std::{error, fmt, io, net, num, str, string}; use unsigned_varint::decode; pub type Result = ::std::result::Result; /// Error types #[derive(Debug)] #[non_exhaustive] pub enum Error { DataLessThanLen, InvalidMultiaddr, InvalidProtocolString, InvalidUvar(decode::Error), ParsingError(Box), UnknownProtocolId(u32), UnknownProtocolString(String), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Error::DataLessThanLen => f.write_str("we have less data than indicated by length"), Error::InvalidMultiaddr => f.write_str("invalid multiaddr"), Error::InvalidProtocolString => f.write_str("invalid protocol string"), Error::InvalidUvar(e) => write!(f, "failed to decode unsigned varint: {e}"), Error::ParsingError(e) => write!(f, "failed to parse: {e}"), Error::UnknownProtocolId(id) => write!(f, "unknown protocol id: {id}"), Error::UnknownProtocolString(string) => { write!(f, "unknown protocol string: {string}") } } } } impl error::Error for Error { #[inline] fn source(&self) -> Option<&(dyn error::Error + 'static)> { if let Error::ParsingError(e) = self { Some(&**e) } else { None } } } impl From for Error { fn from(err: io::Error) -> Error { Error::ParsingError(err.into()) } } impl From for Error { fn from(err: multihash::Error) -> Error { Error::ParsingError(err.into()) } } impl From for Error { fn from(err: multibase::Error) -> Error { Error::ParsingError(err.into()) } } impl From for Error { fn from(err: net::AddrParseError) -> Error { Error::ParsingError(err.into()) } } impl From for Error { fn from(err: num::ParseIntError) -> Error { Error::ParsingError(err.into()) } } impl From for Error { fn from(err: string::FromUtf8Error) -> Error { Error::ParsingError(err.into()) } } impl From for Error { fn from(err: str::Utf8Error) -> Error { Error::ParsingError(err.into()) } } impl From for Error { fn from(e: decode::Error) -> Error { Error::InvalidUvar(e) } } rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/src/from_url.rs000066400000000000000000000217141467253715300245140ustar00rootroot00000000000000use crate::{Multiaddr, Protocol}; use std::{error, fmt, iter, net::IpAddr}; /// Attempts to parse an URL into a multiaddress. /// /// This function will return an error if some information in the URL cannot be retained in the /// generated multiaddress. This includes a username, password, path (if not supported by the /// multiaddr), and query string. /// /// This function is only present if the `url` feature is enabled, and it is /// enabled by default. /// /// The supported URL schemes are: /// /// - `ws://example.com/` /// - `wss://example.com/` /// - `http://example.com/` /// - `https://example.com/` /// - `unix:/foo/bar` /// /// # Example /// /// ``` /// let addr = multiaddr::from_url("ws://127.0.0.1:8080/").unwrap(); /// assert_eq!(addr, "/ip4/127.0.0.1/tcp/8080/ws".parse().unwrap()); /// ``` /// pub fn from_url(url: &str) -> std::result::Result { from_url_inner(url, false) } /// Attempts to parse an URL into a multiaddress. Ignores possible loss of information. /// /// This function is similar to [`from_url`], except that we don't return an error if some /// information in the URL cannot be retain in the generated multiaddres. /// /// This function is only present if the `url` feature is enabled, and it is /// enabled by default. /// /// # Example /// /// ``` /// let addr = "ws://user:pass@127.0.0.1:8080/"; /// assert!(multiaddr::from_url(addr).is_err()); /// assert!(multiaddr::from_url_lossy(addr).is_ok()); /// ``` /// pub fn from_url_lossy(url: &str) -> std::result::Result { from_url_inner(url, true) } /// Underlying implementation of `from_url` and `from_url_lossy`. fn from_url_inner(url: &str, lossy: bool) -> std::result::Result { let url = url::Url::parse(url).map_err(|_| FromUrlErr::BadUrl)?; match url.scheme() { // Note: if you add support for a new scheme, please update the documentation as well. "ws" | "wss" | "http" | "https" => from_url_inner_http_ws(url, lossy), "unix" => from_url_inner_path(url, lossy), _ => Err(FromUrlErr::UnsupportedScheme), } } /// Called when `url.scheme()` is an Internet-like URL. fn from_url_inner_http_ws( url: url::Url, lossy: bool, ) -> std::result::Result { let (protocol, lost_path, default_port) = match url.scheme() { "ws" => (Protocol::Ws(url.path().to_owned().into()), false, 80), "wss" => (Protocol::Wss(url.path().to_owned().into()), false, 443), "http" => (Protocol::Http, true, 80), "https" => (Protocol::Https, true, 443), _ => unreachable!("We only call this function for one of the given schemes; qed"), }; let port = Protocol::Tcp(url.port().unwrap_or(default_port)); let ip = if let Some(hostname) = url.host_str() { if let Ok(ip) = hostname.parse::() { Protocol::from(ip) } else { Protocol::Dns(hostname.into()) } } else { return Err(FromUrlErr::BadUrl); }; if !lossy && (!url.username().is_empty() || url.password().is_some() || (lost_path && url.path() != "/" && !url.path().is_empty()) || url.query().is_some() || url.fragment().is_some()) { return Err(FromUrlErr::InformationLoss); } Ok(iter::once(ip) .chain(iter::once(port)) .chain(iter::once(protocol)) .collect()) } /// Called when `url.scheme()` is a path-like URL. fn from_url_inner_path(url: url::Url, lossy: bool) -> std::result::Result { let protocol = match url.scheme() { "unix" => Protocol::Unix(url.path().to_owned().into()), _ => unreachable!("We only call this function for one of the given schemes; qed"), }; if !lossy && (!url.username().is_empty() || url.password().is_some() || url.query().is_some() || url.fragment().is_some()) { return Err(FromUrlErr::InformationLoss); } Ok(Multiaddr::from(protocol)) } /// Error while parsing an URL. #[derive(Debug)] pub enum FromUrlErr { /// Failed to parse the URL. BadUrl, /// The URL scheme was not recognized. UnsupportedScheme, /// Some information in the URL would be lost. Never returned by `from_url_lossy`. InformationLoss, } impl fmt::Display for FromUrlErr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { FromUrlErr::BadUrl => write!(f, "Bad URL"), FromUrlErr::UnsupportedScheme => write!(f, "Unrecognized URL scheme"), FromUrlErr::InformationLoss => write!(f, "Some information in the URL would be lost"), } } } impl error::Error for FromUrlErr {} #[cfg(test)] mod tests { use super::*; #[test] fn parse_garbage_doesnt_panic() { for _ in 0..50 { let url = (0..16).map(|_| rand::random::()).collect::>(); let url = String::from_utf8_lossy(&url); assert!(from_url(&url).is_err()); } } #[test] fn normal_usage_ws() { let addr = from_url("ws://127.0.0.1:8000").unwrap(); assert_eq!(addr, "/ip4/127.0.0.1/tcp/8000/ws".parse().unwrap()); } #[test] fn normal_usage_wss() { let addr = from_url("wss://127.0.0.1:8000").unwrap(); assert_eq!(addr, "/ip4/127.0.0.1/tcp/8000/wss".parse().unwrap()); } #[test] fn default_ws_port() { let addr = from_url("ws://127.0.0.1").unwrap(); assert_eq!(addr, "/ip4/127.0.0.1/tcp/80/ws".parse().unwrap()); } #[test] fn default_http_port() { let addr = from_url("http://127.0.0.1").unwrap(); assert_eq!(addr, "/ip4/127.0.0.1/tcp/80/http".parse().unwrap()); } #[test] fn default_wss_port() { let addr = from_url("wss://127.0.0.1").unwrap(); assert_eq!(addr, "/ip4/127.0.0.1/tcp/443/wss".parse().unwrap()); } #[test] fn default_https_port() { let addr = from_url("https://127.0.0.1").unwrap(); assert_eq!(addr, "/ip4/127.0.0.1/tcp/443/https".parse().unwrap()); } #[test] fn dns_addr_ws() { let addr = from_url("ws://example.com").unwrap(); assert_eq!(addr, "/dns/example.com/tcp/80/ws".parse().unwrap()); } #[test] fn dns_addr_http() { let addr = from_url("http://example.com").unwrap(); assert_eq!(addr, "/dns/example.com/tcp/80/http".parse().unwrap()); } #[test] fn dns_addr_wss() { let addr = from_url("wss://example.com").unwrap(); assert_eq!(addr, "/dns/example.com/tcp/443/wss".parse().unwrap()); } #[test] fn dns_addr_https() { let addr = from_url("https://example.com").unwrap(); assert_eq!(addr, "/dns/example.com/tcp/443/https".parse().unwrap()); } #[test] fn bad_hostname() { let addr = from_url("wss://127.0.0.1x").unwrap(); assert_eq!(addr, "/dns/127.0.0.1x/tcp/443/wss".parse().unwrap()); } #[test] fn wrong_scheme() { match from_url("foo://127.0.0.1") { Err(FromUrlErr::UnsupportedScheme) => {} _ => panic!(), } } #[test] fn dns_and_port() { let addr = from_url("http://example.com:1000").unwrap(); assert_eq!(addr, "/dns/example.com/tcp/1000/http".parse().unwrap()); } #[test] fn username_lossy() { let addr = "http://foo@example.com:1000/"; assert!(from_url(addr).is_err()); assert!(from_url_lossy(addr).is_ok()); assert!(from_url("http://@example.com:1000/").is_ok()); } #[test] fn password_lossy() { let addr = "http://:bar@example.com:1000/"; assert!(from_url(addr).is_err()); assert!(from_url_lossy(addr).is_ok()); } #[test] fn path_lossy() { let addr = "http://example.com:1000/foo"; assert!(from_url(addr).is_err()); assert!(from_url_lossy(addr).is_ok()); } #[test] fn fragment_lossy() { let addr = "http://example.com:1000/#foo"; assert!(from_url(addr).is_err()); assert!(from_url_lossy(addr).is_ok()); } #[test] fn unix() { let addr = from_url("unix:/foo/bar").unwrap(); assert_eq!(addr, Multiaddr::from(Protocol::Unix("/foo/bar".into()))); } #[test] fn ws_path() { let addr = from_url("ws://1.2.3.4:1000/foo/bar").unwrap(); assert_eq!( addr, "/ip4/1.2.3.4/tcp/1000/x-parity-ws/%2ffoo%2fbar" .parse() .unwrap() ); let addr = from_url("ws://1.2.3.4:1000/").unwrap(); assert_eq!(addr, "/ip4/1.2.3.4/tcp/1000/ws".parse().unwrap()); let addr = from_url("wss://1.2.3.4:1000/foo/bar").unwrap(); assert_eq!( addr, "/ip4/1.2.3.4/tcp/1000/x-parity-wss/%2ffoo%2fbar" .parse() .unwrap() ); let addr = from_url("wss://1.2.3.4:1000").unwrap(); assert_eq!(addr, "/ip4/1.2.3.4/tcp/1000/wss".parse().unwrap()); } } rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/src/lib.rs000066400000000000000000000345531467253715300234420ustar00rootroot00000000000000//! Implementation of [multiaddr](https://github.com/multiformats/multiaddr) in Rust. #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub use multihash; mod errors; mod onion_addr; mod protocol; #[cfg(feature = "url")] mod from_url; pub use self::errors::{Error, Result}; pub use self::onion_addr::Onion3Addr; pub use self::protocol::Protocol; use serde::{ de::{self, Error as DeserializerError}, Deserialize, Deserializer, Serialize, Serializer, }; use std::{ convert::TryFrom, fmt, io, iter::FromIterator, net::{IpAddr, Ipv4Addr, Ipv6Addr}, result::Result as StdResult, str::FromStr, sync::Arc, }; pub use libp2p_identity::PeerId; #[cfg(feature = "url")] pub use self::from_url::{from_url, from_url_lossy, FromUrlErr}; static_assertions::const_assert! { // This check is most certainly overkill right now, but done here // anyway to ensure the `as u64` casts in this crate are safe. std::mem::size_of::() <= std::mem::size_of::() } /// Representation of a Multiaddr. #[allow(clippy::rc_buffer)] #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)] pub struct Multiaddr { bytes: Arc>, } impl Multiaddr { /// Create a new, empty multiaddress. pub fn empty() -> Self { Self { bytes: Arc::new(Vec::new()), } } /// Create a new, empty multiaddress with the given capacity. pub fn with_capacity(n: usize) -> Self { Self { bytes: Arc::new(Vec::with_capacity(n)), } } /// Return the length in bytes of this multiaddress. pub fn len(&self) -> usize { self.bytes.len() } /// Returns true if the length of this multiaddress is 0. pub fn is_empty(&self) -> bool { self.bytes.len() == 0 } /// Return a copy of this [`Multiaddr`]'s byte representation. pub fn to_vec(&self) -> Vec { Vec::from(&self.bytes[..]) } /// Adds an already-parsed address component to the end of this multiaddr. /// /// # Examples /// /// ``` /// use multiaddr::{Multiaddr, Protocol}; /// /// let mut address: Multiaddr = "/ip4/127.0.0.1".parse().unwrap(); /// address.push(Protocol::Tcp(10000)); /// assert_eq!(address, "/ip4/127.0.0.1/tcp/10000".parse().unwrap()); /// ``` /// pub fn push(&mut self, p: Protocol<'_>) { let mut w = io::Cursor::<&mut Vec>::new(Arc::make_mut(&mut self.bytes)); w.set_position(w.get_ref().len() as u64); p.write_bytes(&mut w) .expect("Writing to a `io::Cursor<&mut Vec>` never fails.") } /// Pops the last `Protocol` of this multiaddr, or `None` if the multiaddr is empty. /// ``` /// use multiaddr::{Multiaddr, Protocol}; /// /// let mut address: Multiaddr = "/ip4/127.0.0.1/udt/sctp/5678".parse().unwrap(); /// /// assert_eq!(address.pop().unwrap(), Protocol::Sctp(5678)); /// assert_eq!(address.pop().unwrap(), Protocol::Udt); /// ``` /// pub fn pop<'a>(&mut self) -> Option> { let mut slice = &self.bytes[..]; // the remaining multiaddr slice if slice.is_empty() { return None; } let protocol = loop { let (p, s) = Protocol::from_bytes(slice).expect("`slice` is a valid `Protocol`."); if s.is_empty() { break p.acquire(); } slice = s }; let remaining_len = self.bytes.len() - slice.len(); Arc::make_mut(&mut self.bytes).truncate(remaining_len); Some(protocol) } /// Like [`Multiaddr::push`] but consumes `self`. pub fn with(mut self, p: Protocol<'_>) -> Self { let mut w = io::Cursor::<&mut Vec>::new(Arc::make_mut(&mut self.bytes)); w.set_position(w.get_ref().len() as u64); p.write_bytes(&mut w) .expect("Writing to a `io::Cursor<&mut Vec>` never fails."); self } /// Appends the given [`PeerId`] if not yet present at the end of this multiaddress. /// /// Fails if this address ends in a _different_ [`PeerId`]. /// In that case, the original, unmodified address is returned. pub fn with_p2p(self, peer: PeerId) -> std::result::Result { match self.iter().last() { Some(Protocol::P2p(p)) if p == peer => Ok(self), Some(Protocol::P2p(_)) => Err(self), _ => Ok(self.with(Protocol::P2p(peer))), } } /// Returns the components of this multiaddress. /// /// # Example /// /// ```rust /// use std::net::Ipv4Addr; /// use multiaddr::{Multiaddr, Protocol}; /// /// let address: Multiaddr = "/ip4/127.0.0.1/udt/sctp/5678".parse().unwrap(); /// /// let components = address.iter().collect::>(); /// assert_eq!(components[0], Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))); /// assert_eq!(components[1], Protocol::Udt); /// assert_eq!(components[2], Protocol::Sctp(5678)); /// ``` /// pub fn iter(&self) -> Iter<'_> { Iter(&self.bytes) } /// Replace a [`Protocol`] at some position in this `Multiaddr`. /// /// The parameter `at` denotes the index of the protocol at which the function /// `by` will be applied to the current protocol, returning an optional replacement. /// /// If `at` is out of bounds or `by` does not yield a replacement value, /// `None` will be returned. Otherwise a copy of this `Multiaddr` with the /// updated `Protocol` at position `at` will be returned. pub fn replace<'a, F>(&self, at: usize, by: F) -> Option where F: FnOnce(&Protocol<'_>) -> Option>, { let mut address = Multiaddr::with_capacity(self.len()); let mut fun = Some(by); let mut replaced = false; for (i, p) in self.iter().enumerate() { if i == at { let f = fun.take().expect("i == at only happens once"); if let Some(q) = f(&p) { address = address.with(q); replaced = true; continue; } return None; } address = address.with(p) } if replaced { Some(address) } else { None } } /// Checks whether the given `Multiaddr` is a suffix of this `Multiaddr`. pub fn ends_with(&self, other: &Multiaddr) -> bool { let n = self.bytes.len(); let m = other.bytes.len(); if n < m { return false; } self.bytes[(n - m)..] == other.bytes[..] } /// Returns &str identifiers for the protocol names themselves. /// This omits specific info like addresses, ports, peer IDs, and the like. /// Example: `"/ip4/127.0.0.1/tcp/5001"` would return `["ip4", "tcp"]` pub fn protocol_stack(&self) -> ProtoStackIter { ProtoStackIter { parts: self.iter() } } } impl fmt::Debug for Multiaddr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, f) } } impl fmt::Display for Multiaddr { /// Convert a Multiaddr to a string /// /// # Example /// /// ``` /// use multiaddr::Multiaddr; /// /// let address: Multiaddr = "/ip4/127.0.0.1/udt".parse().unwrap(); /// assert_eq!(address.to_string(), "/ip4/127.0.0.1/udt"); /// ``` /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for s in self.iter() { s.fmt(f)?; } Ok(()) } } impl AsRef<[u8]> for Multiaddr { fn as_ref(&self) -> &[u8] { self.bytes.as_ref() } } impl<'a> IntoIterator for &'a Multiaddr { type Item = Protocol<'a>; type IntoIter = Iter<'a>; fn into_iter(self) -> Iter<'a> { Iter(&self.bytes) } } impl<'a> FromIterator> for Multiaddr { fn from_iter(iter: T) -> Self where T: IntoIterator>, { let mut writer = Vec::new(); for cmp in iter { cmp.write_bytes(&mut writer) .expect("Writing to a `Vec` never fails."); } Multiaddr { bytes: Arc::new(writer), } } } impl FromStr for Multiaddr { type Err = Error; fn from_str(input: &str) -> Result { let mut writer = Vec::new(); let mut parts = input.split('/').peekable(); if Some("") != parts.next() { // A multiaddr must start with `/` return Err(Error::InvalidMultiaddr); } while parts.peek().is_some() { let p = Protocol::from_str_parts(&mut parts)?; p.write_bytes(&mut writer) .expect("Writing to a `Vec` never fails."); } Ok(Multiaddr { bytes: Arc::new(writer), }) } } /// Iterator over `Multiaddr` [`Protocol`]s. pub struct Iter<'a>(&'a [u8]); impl<'a> Iterator for Iter<'a> { type Item = Protocol<'a>; fn next(&mut self) -> Option { if self.0.is_empty() { return None; } let (p, next_data) = Protocol::from_bytes(self.0).expect("`Multiaddr` is known to be valid."); self.0 = next_data; Some(p) } } /// Iterator over the string idtenfiers of the protocols (not addrs) in a multiaddr pub struct ProtoStackIter<'a> { parts: Iter<'a>, } impl<'a> Iterator for ProtoStackIter<'a> { type Item = &'static str; fn next(&mut self) -> Option { self.parts.next().as_ref().map(Protocol::tag) } } impl<'a> From> for Multiaddr { fn from(p: Protocol<'a>) -> Multiaddr { let mut w = Vec::new(); p.write_bytes(&mut w) .expect("Writing to a `Vec` never fails."); Multiaddr { bytes: Arc::new(w) } } } impl From for Multiaddr { fn from(v: IpAddr) -> Multiaddr { match v { IpAddr::V4(a) => a.into(), IpAddr::V6(a) => a.into(), } } } impl From for Multiaddr { fn from(v: Ipv4Addr) -> Multiaddr { Protocol::Ip4(v).into() } } impl From for Multiaddr { fn from(v: Ipv6Addr) -> Multiaddr { Protocol::Ip6(v).into() } } impl TryFrom> for Multiaddr { type Error = Error; fn try_from(v: Vec) -> Result { // Check if the argument is a valid `Multiaddr` by reading its protocols. let mut slice = &v[..]; while !slice.is_empty() { let (_, s) = Protocol::from_bytes(slice)?; slice = s } Ok(Multiaddr { bytes: Arc::new(v) }) } } impl TryFrom for Multiaddr { type Error = Error; fn try_from(s: String) -> Result { s.parse() } } impl<'a> TryFrom<&'a str> for Multiaddr { type Error = Error; fn try_from(s: &'a str) -> Result { s.parse() } } impl Serialize for Multiaddr { fn serialize(&self, serializer: S) -> StdResult where S: Serializer, { if serializer.is_human_readable() { serializer.serialize_str(&self.to_string()) } else { serializer.serialize_bytes(self.as_ref()) } } } impl<'de> Deserialize<'de> for Multiaddr { fn deserialize(deserializer: D) -> StdResult where D: Deserializer<'de>, { struct Visitor { is_human_readable: bool, } impl<'de> de::Visitor<'de> for Visitor { type Value = Multiaddr; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("multiaddress") } fn visit_seq>( self, mut seq: A, ) -> StdResult { let mut buf: Vec = Vec::with_capacity(std::cmp::min(seq.size_hint().unwrap_or(0), 4096)); while let Some(e) = seq.next_element()? { buf.push(e); } if self.is_human_readable { let s = String::from_utf8(buf).map_err(DeserializerError::custom)?; s.parse().map_err(DeserializerError::custom) } else { Multiaddr::try_from(buf).map_err(DeserializerError::custom) } } fn visit_str(self, v: &str) -> StdResult { v.parse().map_err(DeserializerError::custom) } fn visit_borrowed_str(self, v: &'de str) -> StdResult { self.visit_str(v) } fn visit_string(self, v: String) -> StdResult { self.visit_str(&v) } fn visit_bytes(self, v: &[u8]) -> StdResult { self.visit_byte_buf(v.into()) } fn visit_borrowed_bytes(self, v: &'de [u8]) -> StdResult { self.visit_byte_buf(v.into()) } fn visit_byte_buf(self, v: Vec) -> StdResult { Multiaddr::try_from(v).map_err(DeserializerError::custom) } } if deserializer.is_human_readable() { deserializer.deserialize_str(Visitor { is_human_readable: true, }) } else { deserializer.deserialize_bytes(Visitor { is_human_readable: false, }) } } } /// Easy way for a user to create a `Multiaddr`. /// /// Example: /// /// ```rust /// # use multiaddr::multiaddr; /// let addr = multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)); /// ``` /// /// Each element passed to `multiaddr!` should be a variant of the `Protocol` enum. The /// optional parameter is turned into the proper type with the `Into` trait. /// /// For example, `Ip4([127, 0, 0, 1])` works because `Ipv4Addr` implements `From<[u8; 4]>`. #[macro_export] macro_rules! multiaddr { ($($comp:ident $(($param:expr))*),+) => { { use std::iter; let elem = iter::empty::<$crate::Protocol>(); $( let elem = { let cmp = $crate::Protocol::$comp $(( $param.into() ))*; elem.chain(iter::once(cmp)) }; )+ elem.collect::<$crate::Multiaddr>() } } } rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/src/onion_addr.rs000066400000000000000000000024231467253715300247770ustar00rootroot00000000000000use std::{borrow::Cow, fmt}; /// Represents an Onion v3 address #[derive(Clone)] pub struct Onion3Addr<'a>(Cow<'a, [u8; 35]>, u16); impl<'a> Onion3Addr<'a> { /// Return the hash of the public key as bytes pub fn hash(&self) -> &[u8; 35] { self.0.as_ref() } /// Return the port pub fn port(&self) -> u16 { self.1 } /// Consume this instance and create an owned version containing the same address pub fn acquire<'b>(self) -> Onion3Addr<'b> { Onion3Addr(Cow::Owned(self.0.into_owned()), self.1) } } impl PartialEq for Onion3Addr<'_> { fn eq(&self, other: &Self) -> bool { self.1 == other.1 && self.0[..] == other.0[..] } } impl Eq for Onion3Addr<'_> {} impl From<([u8; 35], u16)> for Onion3Addr<'_> { fn from(parts: ([u8; 35], u16)) -> Self { Self(Cow::Owned(parts.0), parts.1) } } impl<'a> From<(&'a [u8; 35], u16)> for Onion3Addr<'a> { fn from(parts: (&'a [u8; 35], u16)) -> Self { Self(Cow::Borrowed(parts.0), parts.1) } } impl fmt::Debug for Onion3Addr<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.debug_tuple("Onion3Addr") .field(&format!("{:02x?}", &self.0[..])) .field(&self.1) .finish() } } rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/src/protocol.rs000066400000000000000000001000121467253715300245150ustar00rootroot00000000000000use crate::onion_addr::Onion3Addr; use crate::{Error, PeerId, Result}; use arrayref::array_ref; use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt}; use data_encoding::BASE32; use std::{ borrow::Cow, convert::From, fmt, io::{Cursor, Write}, net::{IpAddr, Ipv4Addr, Ipv6Addr}, str::{self, FromStr}, }; use unsigned_varint::{decode, encode}; // All the values are obtained by converting hexadecimal protocol codes to u32. // Protocols as well as their corresponding codes are defined in // https://github.com/multiformats/multiaddr/blob/master/protocols.csv . const DCCP: u32 = 33; const DNS: u32 = 53; const DNS4: u32 = 54; const DNS6: u32 = 55; const DNSADDR: u32 = 56; const HTTP: u32 = 480; const HTTPS: u32 = 443; // Deprecated - alias for /tls/http const IP4: u32 = 4; const IP6: u32 = 41; const P2P_WEBRTC_DIRECT: u32 = 276; // Deprecated const P2P_WEBRTC_STAR: u32 = 275; // Deprecated const WEBRTC_DIRECT: u32 = 280; const CERTHASH: u32 = 466; const P2P_WEBSOCKET_STAR: u32 = 479; // Deprecated const MEMORY: u32 = 777; const ONION: u32 = 444; const ONION3: u32 = 445; const P2P: u32 = 421; const P2P_CIRCUIT: u32 = 290; const QUIC: u32 = 460; const QUIC_V1: u32 = 461; const SCTP: u32 = 132; const TCP: u32 = 6; const TLS: u32 = 448; const NOISE: u32 = 454; const UDP: u32 = 273; const UDT: u32 = 301; const UNIX: u32 = 400; const UTP: u32 = 302; const WEBTRANSPORT: u32 = 465; const WS: u32 = 477; const WS_WITH_PATH: u32 = 4770; // Note: not standard const WSS: u32 = 478; // Deprecated - alias for /tls/ws const WSS_WITH_PATH: u32 = 4780; // Note: not standard const IP6ZONE: u32 = 42; const IPCIDR: u32 = 43; // const IPFS: u32 = 421; // Deprecated const GARLIC64: u32 = 446; const GARLIC32: u32 = 447; const SNI: u32 = 449; const P2P_STARDUST: u32 = 277; // Deprecated const WEBRTC: u32 = 281; /// Type-alias for how multi-addresses use `Multihash`. /// /// The `64` defines the allocation size for the digest within the `Multihash`. /// This allows us to use hashes such as SHA512. /// In case protocols like `/certhash` ever support hashes larger than that, we will need to update this size here (which will be a breaking change!). type Multihash = multihash::Multihash<64>; const PATH_SEGMENT_ENCODE_SET: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS .add(b'%') .add(b'/') .add(b'`') .add(b'?') .add(b'{') .add(b'}') .add(b' ') .add(b'"') .add(b'#') .add(b'<') .add(b'>'); /// `Protocol` describes all possible multiaddress protocols. /// /// For `Unix`, `Ws` and `Wss` we use `&str` instead of `Path` to allow /// cross-platform usage of `Protocol` since encoding `Paths` to bytes is /// platform-specific. This means that the actual validation of paths needs to /// happen separately. #[derive(PartialEq, Eq, Clone, Debug)] #[non_exhaustive] pub enum Protocol<'a> { Dccp(u16), Dns(Cow<'a, str>), Dns4(Cow<'a, str>), Dns6(Cow<'a, str>), Dnsaddr(Cow<'a, str>), Http, Https, Ip4(Ipv4Addr), Ip6(Ipv6Addr), P2pWebRtcDirect, P2pWebRtcStar, WebRTCDirect, Certhash(Multihash), P2pWebSocketStar, /// Contains the "port" to contact. Similar to TCP or UDP, 0 means "assign me a port". Memory(u64), Onion(Cow<'a, [u8; 10]>, u16), Onion3(Onion3Addr<'a>), P2p(PeerId), P2pCircuit, Quic, QuicV1, Sctp(u16), Tcp(u16), Tls, Noise, Udp(u16), Udt, Unix(Cow<'a, str>), Utp, WebTransport, Ws(Cow<'a, str>), Wss(Cow<'a, str>), Ip6zone(Cow<'a, str>), Ipcidr(u8), Garlic64(Cow<'a, [u8]>), Garlic32(Cow<'a, [u8]>), Sni(Cow<'a, str>), P2pStardust, WebRTC, } impl<'a> Protocol<'a> { /// Parse a protocol value from the given iterator of string slices. /// /// The parsing only consumes the minimum amount of string slices necessary to /// produce a well-formed protocol. The same iterator can thus be used to parse /// a sequence of protocols in succession. It is up to client code to check /// that iteration has finished whenever appropriate. pub fn from_str_parts(mut iter: I) -> Result where I: Iterator, { match iter.next().ok_or(Error::InvalidProtocolString)? { "ip4" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; Ok(Protocol::Ip4(Ipv4Addr::from_str(s)?)) } "tcp" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; Ok(Protocol::Tcp(s.parse()?)) } "tls" => Ok(Protocol::Tls), "noise" => Ok(Protocol::Noise), "udp" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; Ok(Protocol::Udp(s.parse()?)) } "dccp" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; Ok(Protocol::Dccp(s.parse()?)) } "ip6" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; Ok(Protocol::Ip6(Ipv6Addr::from_str(s)?)) } "dns" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; Ok(Protocol::Dns(Cow::Borrowed(s))) } "dns4" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; Ok(Protocol::Dns4(Cow::Borrowed(s))) } "dns6" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; Ok(Protocol::Dns6(Cow::Borrowed(s))) } "dnsaddr" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; Ok(Protocol::Dnsaddr(Cow::Borrowed(s))) } "sctp" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; Ok(Protocol::Sctp(s.parse()?)) } "udt" => Ok(Protocol::Udt), "utp" => Ok(Protocol::Utp), "unix" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; Ok(Protocol::Unix(Cow::Borrowed(s))) } "p2p" | "ipfs" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; let decoded = multibase::Base::Base58Btc.decode(s)?; let peer_id = PeerId::from_bytes(&decoded).map_err(|e| Error::ParsingError(Box::new(e)))?; Ok(Protocol::P2p(peer_id)) } "http" => Ok(Protocol::Http), "https" => Ok(Protocol::Https), "onion" => iter .next() .ok_or(Error::InvalidProtocolString) .and_then(|s| read_onion(&s.to_uppercase())) .map(|(a, p)| Protocol::Onion(Cow::Owned(a), p)), "onion3" => iter .next() .ok_or(Error::InvalidProtocolString) .and_then(|s| read_onion3(&s.to_uppercase())) .map(|(a, p)| Protocol::Onion3((a, p).into())), "quic" => Ok(Protocol::Quic), "quic-v1" => Ok(Protocol::QuicV1), "webtransport" => Ok(Protocol::WebTransport), "ws" => Ok(Protocol::Ws(Cow::Borrowed("/"))), "wss" => Ok(Protocol::Wss(Cow::Borrowed("/"))), "x-parity-ws" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; let decoded = percent_encoding::percent_decode(s.as_bytes()).decode_utf8()?; Ok(Protocol::Ws(decoded)) } "x-parity-wss" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; let decoded = percent_encoding::percent_decode(s.as_bytes()).decode_utf8()?; Ok(Protocol::Wss(decoded)) } "p2p-websocket-star" => Ok(Protocol::P2pWebSocketStar), "p2p-webrtc-star" => Ok(Protocol::P2pWebRtcStar), "webrtc-direct" => Ok(Protocol::WebRTCDirect), "certhash" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; let (_base, decoded) = multibase::decode(s)?; Ok(Protocol::Certhash(Multihash::from_bytes(&decoded)?)) } "p2p-webrtc-direct" => Ok(Protocol::P2pWebRtcDirect), "p2p-circuit" => Ok(Protocol::P2pCircuit), "memory" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; Ok(Protocol::Memory(s.parse()?)) } "ip6zone" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; Ok(Protocol::Ip6zone(Cow::Borrowed(s))) } "ipcidr" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; Ok(Protocol::Ipcidr(s.parse()?)) } "garlic64" => { let s = iter .next() .ok_or(Error::InvalidProtocolString)? .replace('-', "+") .replace('~', "/"); if s.len() < 516 || s.len() > 616 { return Err(Error::InvalidProtocolString); } let decoded = multibase::Base::Base64.decode(s)?; Ok(Protocol::Garlic64(Cow::from(decoded))) } "garlic32" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; if s.len() < 55 && s.len() != 52 { return Err(Error::InvalidProtocolString); } let decoded = multibase::Base::Base32Lower.decode(s)?; Ok(Protocol::Garlic32(Cow::from(decoded))) } "sni" => { let s = iter.next().ok_or(Error::InvalidProtocolString)?; Ok(Protocol::Sni(Cow::Borrowed(s))) } "p2p-stardust" => Ok(Protocol::P2pStardust), "webrtc" => Ok(Protocol::WebRTC), unknown => Err(Error::UnknownProtocolString(unknown.to_string())), } } /// Parse a single `Protocol` value from its byte slice representation, /// returning the protocol as well as the remaining byte slice. pub fn from_bytes(input: &'a [u8]) -> Result<(Self, &'a [u8])> { fn split_at(n: usize, input: &[u8]) -> Result<(&[u8], &[u8])> { if input.len() < n { return Err(Error::DataLessThanLen); } Ok(input.split_at(n)) } let (id, input) = decode::u32(input)?; match id { DCCP => { let (data, rest) = split_at(2, input)?; let mut rdr = Cursor::new(data); let num = rdr.read_u16::()?; Ok((Protocol::Dccp(num), rest)) } DNS => { let (n, input) = decode::usize(input)?; let (data, rest) = split_at(n, input)?; Ok((Protocol::Dns(Cow::Borrowed(str::from_utf8(data)?)), rest)) } DNS4 => { let (n, input) = decode::usize(input)?; let (data, rest) = split_at(n, input)?; Ok((Protocol::Dns4(Cow::Borrowed(str::from_utf8(data)?)), rest)) } DNS6 => { let (n, input) = decode::usize(input)?; let (data, rest) = split_at(n, input)?; Ok((Protocol::Dns6(Cow::Borrowed(str::from_utf8(data)?)), rest)) } DNSADDR => { let (n, input) = decode::usize(input)?; let (data, rest) = split_at(n, input)?; Ok(( Protocol::Dnsaddr(Cow::Borrowed(str::from_utf8(data)?)), rest, )) } HTTP => Ok((Protocol::Http, input)), HTTPS => Ok((Protocol::Https, input)), IP4 => { let (data, rest) = split_at(4, input)?; Ok(( Protocol::Ip4(Ipv4Addr::new(data[0], data[1], data[2], data[3])), rest, )) } IP6 => { let (data, rest) = split_at(16, input)?; let mut rdr = Cursor::new(data); let mut seg = [0_u16; 8]; for x in seg.iter_mut() { *x = rdr.read_u16::()?; } let addr = Ipv6Addr::new( seg[0], seg[1], seg[2], seg[3], seg[4], seg[5], seg[6], seg[7], ); Ok((Protocol::Ip6(addr), rest)) } P2P_WEBRTC_DIRECT => Ok((Protocol::P2pWebRtcDirect, input)), P2P_WEBRTC_STAR => Ok((Protocol::P2pWebRtcStar, input)), WEBRTC_DIRECT => Ok((Protocol::WebRTCDirect, input)), CERTHASH => { let (n, input) = decode::usize(input)?; let (data, rest) = split_at(n, input)?; Ok((Protocol::Certhash(Multihash::from_bytes(data)?), rest)) } P2P_WEBSOCKET_STAR => Ok((Protocol::P2pWebSocketStar, input)), MEMORY => { let (data, rest) = split_at(8, input)?; let mut rdr = Cursor::new(data); let num = rdr.read_u64::()?; Ok((Protocol::Memory(num), rest)) } ONION => { let (data, rest) = split_at(12, input)?; let port = BigEndian::read_u16(&data[10..]); Ok(( Protocol::Onion(Cow::Borrowed(array_ref!(data, 0, 10)), port), rest, )) } ONION3 => { let (data, rest) = split_at(37, input)?; let port = BigEndian::read_u16(&data[35..]); Ok(( Protocol::Onion3((array_ref!(data, 0, 35), port).into()), rest, )) } P2P => { let (n, input) = decode::usize(input)?; let (data, rest) = split_at(n, input)?; Ok(( Protocol::P2p( PeerId::from_bytes(data).map_err(|e| Error::ParsingError(Box::new(e)))?, ), rest, )) } P2P_CIRCUIT => Ok((Protocol::P2pCircuit, input)), QUIC => Ok((Protocol::Quic, input)), QUIC_V1 => Ok((Protocol::QuicV1, input)), SCTP => { let (data, rest) = split_at(2, input)?; let mut rdr = Cursor::new(data); let num = rdr.read_u16::()?; Ok((Protocol::Sctp(num), rest)) } TCP => { let (data, rest) = split_at(2, input)?; let mut rdr = Cursor::new(data); let num = rdr.read_u16::()?; Ok((Protocol::Tcp(num), rest)) } TLS => Ok((Protocol::Tls, input)), NOISE => Ok((Protocol::Noise, input)), UDP => { let (data, rest) = split_at(2, input)?; let mut rdr = Cursor::new(data); let num = rdr.read_u16::()?; Ok((Protocol::Udp(num), rest)) } UDT => Ok((Protocol::Udt, input)), UNIX => { let (n, input) = decode::usize(input)?; let (data, rest) = split_at(n, input)?; Ok((Protocol::Unix(Cow::Borrowed(str::from_utf8(data)?)), rest)) } UTP => Ok((Protocol::Utp, input)), WEBTRANSPORT => Ok((Protocol::WebTransport, input)), WS => Ok((Protocol::Ws(Cow::Borrowed("/")), input)), WS_WITH_PATH => { let (n, input) = decode::usize(input)?; let (data, rest) = split_at(n, input)?; Ok((Protocol::Ws(Cow::Borrowed(str::from_utf8(data)?)), rest)) } WSS => Ok((Protocol::Wss(Cow::Borrowed("/")), input)), WSS_WITH_PATH => { let (n, input) = decode::usize(input)?; let (data, rest) = split_at(n, input)?; Ok((Protocol::Wss(Cow::Borrowed(str::from_utf8(data)?)), rest)) } IP6ZONE => { let (n, input) = decode::usize(input)?; let (data, rest) = split_at(n, input)?; Ok(( Protocol::Ip6zone(Cow::Borrowed(str::from_utf8(data)?)), rest, )) } IPCIDR => { let (data, rest) = split_at(1, input)?; Ok((Protocol::Ipcidr(data[0]), rest)) } GARLIC64 => { let (n, input) = decode::usize(input)?; let (data, rest) = split_at(n, input)?; Ok((Protocol::Garlic64(Cow::Borrowed(data)), rest)) } GARLIC32 => { let (n, input) = decode::usize(input)?; let (data, rest) = split_at(n, input)?; Ok((Protocol::Garlic32(Cow::Borrowed(data)), rest)) } SNI => { let (n, input) = decode::usize(input)?; let (data, rest) = split_at(n, input)?; Ok((Protocol::Sni(Cow::Borrowed(str::from_utf8(data)?)), rest)) } P2P_STARDUST => Ok((Protocol::P2pStardust, input)), WEBRTC => Ok((Protocol::WebRTC, input)), _ => Err(Error::UnknownProtocolId(id)), } } /// Encode this protocol by writing its binary representation into /// the given `Write` impl. pub fn write_bytes(&self, w: &mut W) -> Result<()> { let mut buf = encode::u32_buffer(); match self { Protocol::Ip4(addr) => { w.write_all(encode::u32(IP4, &mut buf))?; w.write_all(&addr.octets())? } Protocol::Ip6(addr) => { w.write_all(encode::u32(IP6, &mut buf))?; for &segment in &addr.segments() { w.write_u16::(segment)? } } Protocol::Tcp(port) => { w.write_all(encode::u32(TCP, &mut buf))?; w.write_u16::(*port)? } Protocol::Tls => w.write_all(encode::u32(TLS, &mut buf))?, Protocol::Noise => w.write_all(encode::u32(NOISE, &mut buf))?, Protocol::Udp(port) => { w.write_all(encode::u32(UDP, &mut buf))?; w.write_u16::(*port)? } Protocol::Dccp(port) => { w.write_all(encode::u32(DCCP, &mut buf))?; w.write_u16::(*port)? } Protocol::Sctp(port) => { w.write_all(encode::u32(SCTP, &mut buf))?; w.write_u16::(*port)? } Protocol::Dns(s) => { w.write_all(encode::u32(DNS, &mut buf))?; let bytes = s.as_bytes(); w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?; w.write_all(bytes)? } Protocol::Dns4(s) => { w.write_all(encode::u32(DNS4, &mut buf))?; let bytes = s.as_bytes(); w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?; w.write_all(bytes)? } Protocol::Dns6(s) => { w.write_all(encode::u32(DNS6, &mut buf))?; let bytes = s.as_bytes(); w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?; w.write_all(bytes)? } Protocol::Dnsaddr(s) => { w.write_all(encode::u32(DNSADDR, &mut buf))?; let bytes = s.as_bytes(); w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?; w.write_all(bytes)? } Protocol::Unix(s) => { w.write_all(encode::u32(UNIX, &mut buf))?; let bytes = s.as_bytes(); w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?; w.write_all(bytes)? } Protocol::P2p(peer_id) => { w.write_all(encode::u32(P2P, &mut buf))?; let bytes = peer_id.to_bytes(); w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?; w.write_all(&bytes)? } Protocol::Onion(addr, port) => { w.write_all(encode::u32(ONION, &mut buf))?; w.write_all(addr.as_ref())?; w.write_u16::(*port)? } Protocol::Onion3(addr) => { w.write_all(encode::u32(ONION3, &mut buf))?; w.write_all(addr.hash().as_ref())?; w.write_u16::(addr.port())? } Protocol::Quic => w.write_all(encode::u32(QUIC, &mut buf))?, Protocol::QuicV1 => w.write_all(encode::u32(QUIC_V1, &mut buf))?, Protocol::Utp => w.write_all(encode::u32(UTP, &mut buf))?, Protocol::Udt => w.write_all(encode::u32(UDT, &mut buf))?, Protocol::Http => w.write_all(encode::u32(HTTP, &mut buf))?, Protocol::Https => w.write_all(encode::u32(HTTPS, &mut buf))?, Protocol::WebTransport => w.write_all(encode::u32(WEBTRANSPORT, &mut buf))?, Protocol::Ws(ref s) if s == "/" => w.write_all(encode::u32(WS, &mut buf))?, Protocol::Ws(s) => { w.write_all(encode::u32(WS_WITH_PATH, &mut buf))?; let bytes = s.as_bytes(); w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?; w.write_all(bytes)? } Protocol::Wss(ref s) if s == "/" => w.write_all(encode::u32(WSS, &mut buf))?, Protocol::Wss(s) => { w.write_all(encode::u32(WSS_WITH_PATH, &mut buf))?; let bytes = s.as_bytes(); w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?; w.write_all(bytes)? } Protocol::P2pWebSocketStar => w.write_all(encode::u32(P2P_WEBSOCKET_STAR, &mut buf))?, Protocol::P2pWebRtcStar => w.write_all(encode::u32(P2P_WEBRTC_STAR, &mut buf))?, Protocol::WebRTCDirect => w.write_all(encode::u32(WEBRTC_DIRECT, &mut buf))?, Protocol::Certhash(hash) => { w.write_all(encode::u32(CERTHASH, &mut buf))?; let bytes = hash.to_bytes(); w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?; w.write_all(&bytes)? } Protocol::P2pWebRtcDirect => w.write_all(encode::u32(P2P_WEBRTC_DIRECT, &mut buf))?, Protocol::P2pCircuit => w.write_all(encode::u32(P2P_CIRCUIT, &mut buf))?, Protocol::Memory(port) => { w.write_all(encode::u32(MEMORY, &mut buf))?; w.write_u64::(*port)? } Protocol::Ip6zone(zone_id) => { w.write_all(encode::u32(IP6ZONE, &mut buf))?; let bytes = zone_id.as_bytes(); w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?; w.write_all(bytes)? } Protocol::Ipcidr(mask) => { w.write_all(encode::u32(IPCIDR, &mut buf))?; w.write_u8(*mask)? } Protocol::Garlic64(addr) => { w.write_all(encode::u32(GARLIC64, &mut buf))?; w.write_all(encode::usize(addr.len(), &mut encode::usize_buffer()))?; w.write_all(addr)? } Protocol::Garlic32(addr) => { w.write_all(encode::u32(GARLIC32, &mut buf))?; w.write_all(encode::usize(addr.len(), &mut encode::usize_buffer()))?; w.write_all(addr)? } Protocol::Sni(s) => { w.write_all(encode::u32(SNI, &mut buf))?; let bytes = s.as_bytes(); w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?; w.write_all(bytes)? } Protocol::P2pStardust => w.write_all(encode::u32(P2P_STARDUST, &mut buf))?, Protocol::WebRTC => w.write_all(encode::u32(WEBRTC, &mut buf))?, } Ok(()) } /// Turn this `Protocol` into one that owns its data, thus being valid for any lifetime. pub fn acquire<'b>(self) -> Protocol<'b> { use self::Protocol::*; match self { Dccp(a) => Dccp(a), Dns(cow) => Dns(Cow::Owned(cow.into_owned())), Dns4(cow) => Dns4(Cow::Owned(cow.into_owned())), Dns6(cow) => Dns6(Cow::Owned(cow.into_owned())), Dnsaddr(cow) => Dnsaddr(Cow::Owned(cow.into_owned())), Http => Http, Https => Https, Ip4(a) => Ip4(a), Ip6(a) => Ip6(a), P2pWebRtcDirect => P2pWebRtcDirect, P2pWebRtcStar => P2pWebRtcStar, WebRTCDirect => WebRTCDirect, Certhash(hash) => Certhash(hash), P2pWebSocketStar => P2pWebSocketStar, Memory(a) => Memory(a), Onion(addr, port) => Onion(Cow::Owned(addr.into_owned()), port), Onion3(addr) => Onion3(addr.acquire()), P2p(a) => P2p(a), P2pCircuit => P2pCircuit, Quic => Quic, QuicV1 => QuicV1, Sctp(a) => Sctp(a), Tcp(a) => Tcp(a), Tls => Tls, Noise => Noise, Udp(a) => Udp(a), Udt => Udt, Unix(cow) => Unix(Cow::Owned(cow.into_owned())), Utp => Utp, WebTransport => WebTransport, Ws(cow) => Ws(Cow::Owned(cow.into_owned())), Wss(cow) => Wss(Cow::Owned(cow.into_owned())), Ip6zone(cow) => Ip6zone(Cow::Owned(cow.into_owned())), Ipcidr(mask) => Ipcidr(mask), Garlic64(addr) => Garlic64(Cow::Owned(addr.into_owned())), Garlic32(addr) => Garlic32(Cow::Owned(addr.into_owned())), Sni(cow) => Sni(Cow::Owned(cow.into_owned())), P2pStardust => P2pStardust, WebRTC => WebRTC, } } pub fn tag(&self) -> &'static str { use self::Protocol::*; match self { Dccp(_) => "dccp", Dns(_) => "dns", Dns4(_) => "dns4", Dns6(_) => "dns6", Dnsaddr(_) => "dnsaddr", Http => "http", Https => "https", Ip4(_) => "ip4", Ip6(_) => "ip6", P2pWebRtcDirect => "p2p-webrtc-direct", P2pWebRtcStar => "p2p-webrtc-star", WebRTCDirect => "webrtc-direct", Certhash(_) => "certhash", P2pWebSocketStar => "p2p-websocket-star", Memory(_) => "memory", Onion(_, _) => "onion", Onion3(_) => "onion3", P2p(_) => "p2p", P2pCircuit => "p2p-circuit", Quic => "quic", QuicV1 => "quic-v1", Sctp(_) => "sctp", Tcp(_) => "tcp", Tls => "tls", Noise => "noise", Udp(_) => "udp", Udt => "udt", Unix(_) => "unix", Utp => "utp", WebTransport => "webtransport", Ws(ref s) if s == "/" => "ws", Ws(_) => "x-parity-ws", Wss(ref s) if s == "/" => "wss", Wss(_) => "x-parity-wss", Ip6zone(_) => "ip6zone", Ipcidr(_) => "ipcidr", Garlic64(_) => "garlic64", Garlic32(_) => "garlic32", Sni(_) => "sni", P2pStardust => "p2p-stardust", WebRTC => "webrtc", } } } impl<'a> fmt::Display for Protocol<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use self::Protocol::*; write!(f, "/{}", self.tag())?; match self { Dccp(port) => write!(f, "/{port}"), Dns(s) => write!(f, "/{s}"), Dns4(s) => write!(f, "/{s}"), Dns6(s) => write!(f, "/{s}"), Dnsaddr(s) => write!(f, "/{s}"), Ip4(addr) => write!(f, "/{addr}"), Ip6(addr) => write!(f, "/{addr}"), Certhash(hash) => write!( f, "/{}", multibase::encode(multibase::Base::Base64Url, hash.to_bytes()) ), Memory(port) => write!(f, "/{port}"), Onion(addr, port) => { let s = BASE32.encode(addr.as_ref()); write!(f, "/{}:{}", s.to_lowercase(), port) } Onion3(addr) => { let s = BASE32.encode(addr.hash()); write!(f, "/{}:{}", s.to_lowercase(), addr.port()) } P2p(c) => write!(f, "/{}", multibase::Base::Base58Btc.encode(c.to_bytes())), Sctp(port) => write!(f, "/{port}"), Tcp(port) => write!(f, "/{port}"), Udp(port) => write!(f, "/{port}"), Unix(s) => write!(f, "/{s}"), Ws(s) if s != "/" => { let encoded = percent_encoding::percent_encode(s.as_bytes(), PATH_SEGMENT_ENCODE_SET); write!(f, "/{encoded}") } Wss(s) if s != "/" => { let encoded = percent_encoding::percent_encode(s.as_bytes(), PATH_SEGMENT_ENCODE_SET); write!(f, "/{encoded}") } Ip6zone(zone) => write!(f, "/{zone}"), Ipcidr(mask) => write!(f, "/{mask}"), Garlic64(addr) => write!( f, "/{}", multibase::Base::Base64 .encode(addr) .replace('+', "-") .replace('/', "~") ), Garlic32(addr) => write!(f, "/{}", multibase::Base::Base32Lower.encode(addr)), Sni(s) => write!(f, "/{s}"), _ => Ok(()), } } } impl<'a> From for Protocol<'a> { #[inline] fn from(addr: IpAddr) -> Self { match addr { IpAddr::V4(addr) => Protocol::Ip4(addr), IpAddr::V6(addr) => Protocol::Ip6(addr), } } } impl<'a> From for Protocol<'a> { #[inline] fn from(addr: Ipv4Addr) -> Self { Protocol::Ip4(addr) } } impl<'a> From for Protocol<'a> { #[inline] fn from(addr: Ipv6Addr) -> Self { Protocol::Ip6(addr) } } macro_rules! read_onion_impl { ($name:ident, $len:expr, $encoded_len:expr) => { fn $name(s: &str) -> Result<([u8; $len], u16)> { let mut parts = s.split(':'); // address part (without ".onion") let b32 = parts.next().ok_or(Error::InvalidMultiaddr)?; if b32.len() != $encoded_len { return Err(Error::InvalidMultiaddr); } // port number let port = parts .next() .ok_or(Error::InvalidMultiaddr) .and_then(|p| str::parse(p).map_err(From::from))?; // port == 0 is not valid for onion if port == 0 { return Err(Error::InvalidMultiaddr); } // nothing else expected if parts.next().is_some() { return Err(Error::InvalidMultiaddr); } if $len != BASE32 .decode_len(b32.len()) .map_err(|_| Error::InvalidMultiaddr)? { return Err(Error::InvalidMultiaddr); } let mut buf = [0u8; $len]; BASE32 .decode_mut(b32.as_bytes(), &mut buf) .map_err(|_| Error::InvalidMultiaddr)?; Ok((buf, port)) } }; } // Parse a version 2 onion address and return its binary representation. // // Format: ":" read_onion_impl!(read_onion, 10, 16); // Parse a version 3 onion address and return its binary representation. // // Format: ":" read_onion_impl!(read_onion3, 35, 56); rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/tests/000077500000000000000000000000001467253715300226675ustar00rootroot00000000000000rust-multiaddr-469b0deb9a64554c11e1d2227c78fe7c6c769aa7/tests/lib.rs000066400000000000000000000733461467253715300240200ustar00rootroot00000000000000use data_encoding::HEXUPPER; use multiaddr::*; use multihash::Multihash; use quickcheck::{Arbitrary, Gen, QuickCheck}; use std::{ borrow::Cow, convert::{TryFrom, TryInto}, iter::{self, FromIterator}, net::{Ipv4Addr, Ipv6Addr}, str::FromStr, }; // Property tests #[test] fn to_from_bytes_identity() { fn prop(a: Ma) -> bool { let b = a.0.to_vec(); Some(a) == Multiaddr::try_from(b).ok().map(Ma) } QuickCheck::new().quickcheck(prop as fn(Ma) -> bool) } #[test] fn to_from_str_identity() { fn prop(a: Ma) -> bool { let b = a.0.to_string(); Some(a) == Multiaddr::from_str(&b).ok().map(Ma) } QuickCheck::new().quickcheck(prop as fn(Ma) -> bool) } #[test] fn byteswriter() { fn prop(a: Ma, b: Ma) -> bool { let mut x = a.0.clone(); for p in b.0.iter() { x = x.with(p) } x.iter() .zip(a.0.iter().chain(b.0.iter())) .all(|(x, y)| x == y) } QuickCheck::new().quickcheck(prop as fn(Ma, Ma) -> bool) } #[test] fn push_pop_identity() { fn prop(a: Ma, p: Proto) -> bool { let mut b = a.clone(); let q = p.clone(); b.0.push(q.0); assert_ne!(a.0, b.0); Some(p.0) == b.0.pop() && a.0 == b.0 } QuickCheck::new().quickcheck(prop as fn(Ma, Proto) -> bool) } #[test] fn ends_with() { fn prop(Ma(m): Ma) { let n = m.iter().count(); for i in 0..n { let suffix = m.iter().skip(i).collect::(); assert!(m.ends_with(&suffix)); } } QuickCheck::new().quickcheck(prop as fn(_)) } // Arbitrary impls #[derive(PartialEq, Eq, Clone, Hash, Debug)] struct Ma(Multiaddr); impl Arbitrary for Ma { fn arbitrary(g: &mut Gen) -> Self { let iter = (0..u8::arbitrary(g) % 128).map(|_| Proto::arbitrary(g).0); Ma(Multiaddr::from_iter(iter)) } } #[derive(PartialEq, Eq, Clone, Debug)] struct Proto(Protocol<'static>); impl Proto { const IMPL_VARIANT_COUNT: u8 = 39; } impl Arbitrary for Proto { fn arbitrary(g: &mut Gen) -> Self { use Protocol::*; match u8::arbitrary(g) % Proto::IMPL_VARIANT_COUNT { 0 => Proto(Dccp(Arbitrary::arbitrary(g))), 1 => Proto(Dns(Cow::Owned(SubString::arbitrary(g).0))), 2 => Proto(Dns4(Cow::Owned(SubString::arbitrary(g).0))), 3 => Proto(Dns6(Cow::Owned(SubString::arbitrary(g).0))), 4 => Proto(Dnsaddr(Cow::Owned(SubString::arbitrary(g).0))), 5 => Proto(Http), 6 => Proto(Https), 7 => Proto(Ip4(Ipv4Addr::arbitrary(g))), 8 => Proto(Ip6(Ipv6Addr::arbitrary(g))), 9 => Proto(P2pWebRtcDirect), 10 => Proto(P2pWebRtcStar), 11 => Proto(WebRTCDirect), 12 => Proto(Certhash(Mh::arbitrary(g).0)), 13 => Proto(P2pWebSocketStar), 14 => Proto(Memory(Arbitrary::arbitrary(g))), 15 => { let a = iter::repeat_with(|| u8::arbitrary(g)) .take(10) .collect::>() .try_into() .unwrap(); Proto(Onion(Cow::Owned(a), std::cmp::max(1, u16::arbitrary(g)))) } 16 => { let a: [u8; 35] = iter::repeat_with(|| u8::arbitrary(g)) .take(35) .collect::>() .try_into() .unwrap(); Proto(Onion3((a, std::cmp::max(1, u16::arbitrary(g))).into())) } 17 => Proto(P2p(PId::arbitrary(g).0)), 18 => Proto(P2pCircuit), 19 => Proto(Quic), 20 => Proto(QuicV1), 21 => Proto(Sctp(Arbitrary::arbitrary(g))), 22 => Proto(Tcp(Arbitrary::arbitrary(g))), 23 => Proto(Tls), 24 => Proto(Noise), 25 => Proto(Udp(Arbitrary::arbitrary(g))), 26 => Proto(Udt), 27 => Proto(Unix(Cow::Owned(SubString::arbitrary(g).0))), 28 => Proto(Utp), 29 => Proto(WebTransport), 30 => Proto(Ws("/".into())), 31 => Proto(Wss("/".into())), 32 => Proto(Ip6zone(Cow::Owned(SubString::arbitrary(g).0))), 33 => Proto(Ipcidr(Arbitrary::arbitrary(g))), 34 => { let len = usize::arbitrary(g) % (462 - 387) + 387; let a = iter::repeat_with(|| u8::arbitrary(g)) .take(len) .collect::>(); Proto(Garlic64(Cow::Owned(a))) } 35 => { let len = if bool::arbitrary(g) { 32 } else { usize::arbitrary(g) % 128 + 35 }; let a = iter::repeat_with(|| u8::arbitrary(g)) .take(len) .collect::>(); Proto(Garlic32(Cow::Owned(a))) } 36 => Proto(Sni(Cow::Owned(SubString::arbitrary(g).0))), 37 => Proto(P2pStardust), 38 => Proto(WebRTC), _ => panic!("outside range"), } } } #[derive(Clone, Debug)] struct Mh(Multihash<64>); impl Arbitrary for Mh { fn arbitrary(g: &mut Gen) -> Self { let mut hash: [u8; 32] = [0; 32]; hash.fill_with(|| u8::arbitrary(g)); Mh(Multihash::wrap(0x0, &hash).expect("The digest size is never too large")) } } #[derive(Clone, Debug)] struct PId(PeerId); impl Arbitrary for PId { fn arbitrary(g: &mut Gen) -> Self { let mh = Mh::arbitrary(g); PId(PeerId::from_multihash(mh.0).expect("identity multihash works if digest size < 64")) } } #[derive(PartialEq, Eq, Clone, Debug)] struct SubString(String); // ASCII string without '/' impl Arbitrary for SubString { fn arbitrary(g: &mut Gen) -> Self { let mut s = String::arbitrary(g); s.retain(|c| c.is_ascii() && c != '/'); SubString(s) } } // other unit tests fn ma_valid(source: &str, target: &str, protocols: Vec>) { let parsed = source.parse::().unwrap(); assert_eq!(HEXUPPER.encode(&parsed.to_vec()[..]), target); assert_eq!(parsed.iter().collect::>(), protocols); assert_eq!(source.parse::().unwrap().to_string(), source); assert_eq!( Multiaddr::try_from(HEXUPPER.decode(target.as_bytes()).unwrap()).unwrap(), parsed ); } fn peer_id(s: &str) -> PeerId { s.parse().unwrap() } #[test] fn multiaddr_eq() { let m1 = "/ip4/127.0.0.1/udp/1234".parse::().unwrap(); let m2 = "/ip4/127.0.0.1/tcp/1234".parse::().unwrap(); let m3 = "/ip4/127.0.0.1/tcp/1234".parse::().unwrap(); assert_ne!(m1, m2); assert_ne!(m2, m1); assert_eq!(m2, m3); assert_eq!(m1, m1); } #[test] fn construct_success() { use Protocol::*; let local: Ipv4Addr = "127.0.0.1".parse().unwrap(); let addr6: Ipv6Addr = "2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095".parse().unwrap(); ma_valid( "/ip4/1.2.3.4", "0401020304", vec![Ip4("1.2.3.4".parse().unwrap())], ); ma_valid( "/ip4/0.0.0.0", "0400000000", vec![Ip4("0.0.0.0".parse().unwrap())], ); ma_valid( "/ip6/::1", "2900000000000000000000000000000001", vec![Ip6("::1".parse().unwrap())], ); ma_valid( "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21", "29260100094F819700803ECA6566E80C21", vec![Ip6("2601:9:4f81:9700:803e:ca65:66e8:c21".parse().unwrap())], ); ma_valid( "/ip6/fe80::9700:803e:ca65:66e8:c21/ip6zone/wlan0", "29FE80000000009700803ECA6566E80C212A05776C616E30", vec![ Ip6("fe80::9700:803e:ca65:66e8:c21".parse().unwrap()), Ip6zone(Cow::Borrowed("wlan0")), ], ); ma_valid("/udp/0", "91020000", vec![Udp(0)]); ma_valid("/tcp/0", "060000", vec![Tcp(0)]); ma_valid("/sctp/0", "84010000", vec![Sctp(0)]); ma_valid("/udp/1234", "910204D2", vec![Udp(1234)]); ma_valid("/tcp/1234", "0604D2", vec![Tcp(1234)]); ma_valid("/sctp/1234", "840104D2", vec![Sctp(1234)]); ma_valid("/udp/65535", "9102FFFF", vec![Udp(65535)]); ma_valid("/tcp/65535", "06FFFF", vec![Tcp(65535)]); ma_valid( "/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", vec![P2p(peer_id( "QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", ))], ); ma_valid( "/udp/1234/sctp/1234", "910204D2840104D2", vec![Udp(1234), Sctp(1234)], ); ma_valid("/udp/1234/udt", "910204D2AD02", vec![Udp(1234), Udt]); ma_valid("/udp/1234/utp", "910204D2AE02", vec![Udp(1234), Utp]); ma_valid("/tcp/1234/http", "0604D2E003", vec![Tcp(1234), Http]); ma_valid( "/tcp/1234/tls/http", "0604D2C003E003", vec![Tcp(1234), Tls, Http], ); ma_valid("/tcp/1234/https", "0604D2BB03", vec![Tcp(1234), Https]); ma_valid( "/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", "A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2", vec![ P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")), Tcp(1234), ], ); ma_valid( "/ip4/127.0.0.1/udp/1234", "047F000001910204D2", vec![Ip4(local), Udp(1234)], ); ma_valid( "/ip4/127.0.0.1/udp/0", "047F00000191020000", vec![Ip4(local), Udp(0)], ); ma_valid( "/ip4/127.0.0.1/tcp/1234", "047F0000010604D2", vec![Ip4(local), Tcp(1234)], ); ma_valid( "/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "047F000001A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", vec![ Ip4(local), P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")), ], ); ma_valid("/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", "047F000001A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2", vec![Ip4(local), P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")), Tcp(1234)]); // /unix/a/b/c/d/e, // /unix/stdio, // /ip4/1.2.3.4/tcp/80/unix/a/b/c/d/e/f, // /ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234/unix/stdio ma_valid("/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/8000/ws/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "29200108A07AC542013AC986FFFE317095061F40DD03A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", vec![Ip6(addr6), Tcp(8000), Ws("/".into()), P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")) ]); ma_valid("/p2p-webrtc-star/ip4/127.0.0.1/tcp/9090/ws/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "9302047F000001062382DD03A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", vec![P2pWebRtcStar, Ip4(local), Tcp(9090), Ws("/".into()), P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")) ]); ma_valid("/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/8000/wss/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "29200108A07AC542013AC986FFFE317095061F40DE03A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", vec![Ip6(addr6), Tcp(8000), Wss("/".into()), P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))]); ma_valid("/ip4/127.0.0.1/tcp/9090/p2p-circuit/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "047F000001062382A202A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", vec![Ip4(local), Tcp(9090), P2pCircuit, P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))]); ma_valid( "/onion/aaimaq4ygg2iegci:80", "BC030010C0439831B48218480050", vec![Onion( Cow::Owned([0, 16, 192, 67, 152, 49, 180, 130, 24, 72]), 80, )], ); ma_valid( "/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd:1234", "BD03ADADEC040BE047F9658668B11A504F3155001F231A37F54C4476C07FB4CC139ED7E30304D2", vec![Onion3( ( [ 173, 173, 236, 4, 11, 224, 71, 249, 101, 134, 104, 177, 26, 80, 79, 49, 85, 0, 31, 35, 26, 55, 245, 76, 68, 118, 192, 127, 180, 204, 19, 158, 215, 227, 3, ], 1234, ) .into(), )], ); ma_valid( "/garlic64/jT~IyXaoauTni6N4517EG8mrFUKpy0IlgZh-EY9csMAk82Odatmzr~YTZy8Hv7u~wvkg75EFNOyqb~nAPg-khyp2TS~ObUz8WlqYAM2VlEzJ7wJB91P-cUlKF\ 18zSzVoJFmsrcQHZCirSbWoOknS6iNmsGRh5KVZsBEfp1Dg3gwTipTRIx7Vl5Vy~1OSKQVjYiGZS9q8RL0MF~7xFiKxZDLbPxk0AK9TzGGqm~wMTI2HS0Gm4Ycy8LYPVmLvG\ onIBYndg2bJC7WLuF6tVjVquiokSVDKFwq70BCUU5AU-EvdOD5KEOAM7mPfw-gJUG4tm1TtvcobrObqoRnmhXPTBTN5H7qDD12AvlwFGnfAlBXjuP4xOUAISL5SRLiulrsMS\ iT4GcugSI80mF6sdB0zWRgL1yyvoVWeTBn1TqjO27alr95DGTluuSqrNAxgpQzCKEWAyzrQkBfo2avGAmmz2NaHaAvYbOg0QSJz1PLjv2jdPW~ofiQmrGWM1cd~1cCqAAAA", "BE0383038D3FC8C976A86AE4E78BA378E75EC41BC9AB1542A9CB422581987E118F5CB0C024F3639D6AD9B3AFF613672F07BFBBBFC2F920EF910534ECAA6FF9C03E\ 0FA4872A764D2FCE6D4CFC5A5A9800CD95944CC9EF0241F753FE71494A175F334B35682459ACADC4076428AB49B5A83A49D2EA2366B06461E4A559B0111FA750E0D\ E0C138A94D1231ED5979572FF53922905636221994BDABC44BD0C17FEF11622B16432DB3F193400AF53CC61AA9BFC0C4C8D874B41A6E18732F0B60F5662EF1A89C8\ 0589DD8366C90BB58BB85EAD56356ABA2A244950CA170ABBD01094539014F84BDD383E4A10E00CEE63DFC3E809506E2D9B54EDBDCA1BACE6EAA119E68573D305337\ 91FBA830F5D80BE5C051A77C09415E3B8FE3139400848BE5244B8AE96BB0C4A24F819CBA0488F34985EAC741D3359180BD72CAFA1559E4C19F54EA8CEDBB6A5AFDE\ 4319396EB92AAB340C60A50CC2284580CB3AD09017E8D9ABC60269B3D8D687680BD86CE834412273D4F2E3BF68DD3D6FE87E2426AC658CD5C77FD5C0AA000000", vec![Garlic64( ( &[ 141, 63, 200, 201, 118, 168, 106, 228, 231, 139, 163, 120, 231, 94, 196, 27, 201, 171, 21, 66, 169, 203, 66, 37, 129, 152, 126, 17, 143, 92, 176, 192, 36, 243, 99, 157, 106, 217, 179, 175, 246, 19, 103, 47, 7, 191, 187, 191, 194, 249, 32, 239, 145, 5, 52, 236, 170, 111, 249, 192, 62, 15, 164, 135, 42, 118, 77, 47, 206, 109, 76, 252, 90, 90, 152, 0, 205, 149, 148, 76, 201, 239, 2, 65, 247, 83, 254, 113, 73, 74, 23, 95, 51, 75, 53, 104, 36, 89, 172, 173, 196, 7, 100, 40, 171, 73, 181, 168, 58, 73, 210, 234, 35, 102, 176, 100, 97, 228, 165, 89, 176, 17, 31, 167, 80, 224, 222, 12, 19, 138, 148, 209, 35, 30, 213, 151, 149, 114, 255, 83, 146, 41, 5, 99, 98, 33, 153, 75, 218, 188, 68, 189, 12, 23, 254, 241, 22, 34, 177, 100, 50, 219, 63, 25, 52, 0, 175, 83, 204, 97, 170, 155, 252, 12, 76, 141, 135, 75, 65, 166, 225, 135, 50, 240, 182, 15, 86, 98, 239, 26, 137, 200, 5, 137, 221, 131, 102, 201, 11, 181, 139, 184, 94, 173, 86, 53, 106, 186, 42, 36, 73, 80, 202, 23, 10, 187, 208, 16, 148, 83, 144, 20, 248, 75, 221, 56, 62, 74, 16, 224, 12, 238, 99, 223, 195, 232, 9, 80, 110, 45, 155, 84, 237, 189, 202, 27, 172, 230, 234, 161, 25, 230, 133, 115, 211, 5, 51, 121, 31, 186, 131, 15, 93, 128, 190, 92, 5, 26, 119, 192, 148, 21, 227, 184, 254, 49, 57, 64, 8, 72, 190, 82, 68, 184, 174, 150, 187, 12, 74, 36, 248, 25, 203, 160, 72, 143, 52, 152, 94, 172, 116, 29, 51, 89, 24, 11, 215, 44, 175, 161, 85, 158, 76, 25, 245, 78, 168, 206, 219, 182, 165, 175, 222, 67, 25, 57, 110, 185, 42, 171, 52, 12, 96, 165, 12, 194, 40, 69, 128, 203, 58, 208, 144, 23, 232, 217, 171, 198, 2, 105, 179, 216, 214, 135, 104, 11, 216, 108, 232, 52, 65, 34, 115, 212, 242, 227, 191, 104, 221, 61, 111, 232, 126, 36, 38, 172, 101, 140, 213, 199, 127, 213, 192, 170, 0, 0, 0, ] ).into() )], ); ma_valid( "/dnsaddr/sjc-1.bootstrap.libp2p.io", "3819736A632D312E626F6F7473747261702E6C69627032702E696F", vec![Dnsaddr(Cow::Borrowed("sjc-1.bootstrap.libp2p.io"))], ); ma_valid( "/dnsaddr/sjc-1.bootstrap.libp2p.io/tcp/1234/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", "3819736A632D312E626F6F7473747261702E6C69627032702E696F0604D2A50322122006B3608AA000274049EB28AD8E793A26FF6FAB281A7D3BD77CD18EB745DFAABB", vec![Dnsaddr(Cow::Borrowed("sjc-1.bootstrap.libp2p.io")), Tcp(1234), P2p(peer_id("QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN"))] ); ma_valid( "/ip4/127.0.0.1/tcp/127/ws", "047F00000106007FDD03", vec![Ip4(local), Tcp(127), Ws("/".into())], ); ma_valid( "/ip4/127.0.0.1/tcp/127/tls", "047F00000106007FC003", vec![Ip4(local), Tcp(127), Tls], ); ma_valid( "/ip4/127.0.0.1/tcp/127/tls/ws", "047F00000106007FC003DD03", vec![Ip4(local), Tcp(127), Tls, Ws("/".into())], ); ma_valid( "/ip4/127.0.0.1/tcp/127/noise", "047F00000106007FC603", vec![Ip4(local), Tcp(127), Noise], ); ma_valid( "/ip4/127.0.0.1/udp/1234/webrtc-direct", "047F000001910204D29802", vec![Ip4(local), Udp(1234), WebRTCDirect], ); let (_base, decoded) = multibase::decode("uEiDDq4_xNyDorZBH3TlGazyJdOWSwvo4PUo5YHFMrvDE8g").unwrap(); ma_valid( "/ip4/127.0.0.1/udp/1234/webrtc-direct/certhash/uEiDDq4_xNyDorZBH3TlGazyJdOWSwvo4PUo5YHFMrvDE8g", "047F000001910204D29802D203221220C3AB8FF13720E8AD9047DD39466B3C8974E592C2FA383D4A3960714CAEF0C4F2", vec![ Ip4(local), Udp(1234), WebRTCDirect, Certhash(Multihash::from_bytes(&decoded).unwrap()), ], ); ma_valid( "/ip4/127.0.0.1/udp/1234/quic/webtransport", "047F000001910204D2CC03D103", vec![Ip4(local), Udp(1234), Quic, WebTransport], ); let (_base, decoded) = multibase::decode("uEiDDq4_xNyDorZBH3TlGazyJdOWSwvo4PUo5YHFMrvDE8g").unwrap(); ma_valid( "/ip4/127.0.0.1/udp/1234/webtransport/certhash/uEiDDq4_xNyDorZBH3TlGazyJdOWSwvo4PUo5YHFMrvDE8g", "047F000001910204D2D103D203221220C3AB8FF13720E8AD9047DD39466B3C8974E592C2FA383D4A3960714CAEF0C4F2", vec![ Ip4(local), Udp(1234), WebTransport, Certhash(Multihash::from_bytes(&decoded).unwrap()), ], ); } #[test] fn construct_fail() { let addresses = [ "/ip4", "/ip4/::1", "/ip4/fdpsofodsajfdoisa", "/ip6", "/ip6/fe80::9700:803e:ca65:66e8:c21/ip6zone", "/udp", "/tcp", "/sctp", "/udp/65536", "/tcp/65536", "/onion/9imaq4ygg2iegci7:80", "/onion/aaimaq4ygg2iegci7:80", "/onion/timaq4ygg2iegci7:0", "/onion/timaq4ygg2iegci7:-1", "/onion/timaq4ygg2iegci7", "/onion/timaq4ygg2iegci@:666", "/onion3/9ww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd:80", "/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd7:80", "/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd:0", "/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd:-1", "/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd", "/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyy@:666", "/garlic64/jT~", "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzu", "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzu77", "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzu:80", "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzuq:-1", "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzu@", "/udp/1234/sctp", "/udp/1234/udt/1234", "/udp/1234/utp/1234", "/ip4/127.0.0.1/udp/jfodsajfidosajfoidsa", "/ip4/127.0.0.1/udp", "/ip4/127.0.0.1/tcp/jfodsajfidosajfoidsa", "/ip4/127.0.0.1/tcp", "/ip4/127.0.0.1/p2p", "/ip4/127.0.0.1/p2p/tcp", "/p2p-circuit/50", "/ip4/127.0.0.1/udp/1234/webrtc-direct/certhash", "/ip4/127.0.0.1/udp/1234/webrtc-direct/certhash/b2uaraocy6yrdblb4sfptaddgimjmmp", // 1 character missing from certhash ]; for address in &addresses { assert!( address.parse::().is_err(), "{}", address.to_string() ); } } #[test] fn to_multiaddr() { assert_eq!( Multiaddr::from(Ipv4Addr::new(127, 0, 0, 1)), "/ip4/127.0.0.1".parse().unwrap() ); assert_eq!( Multiaddr::from(Ipv6Addr::new( 0x2601, 0x9, 0x4f81, 0x9700, 0x803e, 0xca65, 0x66e8, 0xc21 )), "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21".parse().unwrap() ); assert_eq!( Multiaddr::try_from("/ip4/127.0.0.1/tcp/1234".to_string()).unwrap(), "/ip4/127.0.0.1/tcp/1234".parse::().unwrap() ); assert_eq!( Multiaddr::try_from("/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21").unwrap(), "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21" .parse::() .unwrap() ); assert_eq!( Multiaddr::from(Ipv4Addr::new(127, 0, 0, 1)).with(Protocol::Tcp(1234)), "/ip4/127.0.0.1/tcp/1234".parse::().unwrap() ); assert_eq!( Multiaddr::from(Ipv6Addr::new( 0x2601, 0x9, 0x4f81, 0x9700, 0x803e, 0xca65, 0x66e8, 0xc21 )) .with(Protocol::Tcp(1234)), "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21/tcp/1234" .parse::() .unwrap() ); } #[test] fn from_bytes_fail() { let bytes = vec![1, 2, 3, 4]; assert!(Multiaddr::try_from(bytes).is_err()); } #[test] fn ser_and_deser_json() { let addr: Multiaddr = "/ip4/0.0.0.0/tcp/0/tls".parse::().unwrap(); let serialized = serde_json::to_string(&addr).unwrap(); assert_eq!(serialized, "\"/ip4/0.0.0.0/tcp/0/tls\""); let deserialized: Multiaddr = serde_json::from_str(&serialized).unwrap(); assert_eq!(addr, deserialized); } #[test] fn ser_and_deser_bincode() { let addr: Multiaddr = "/ip4/0.0.0.0/tcp/0/tls".parse::().unwrap(); let serialized = bincode::serialize(&addr).unwrap(); // compact addressing assert_eq!( serialized, vec![10, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 6, 0, 0, 192, 3] ); let deserialized: Multiaddr = bincode::deserialize(&serialized).unwrap(); assert_eq!(addr, deserialized); } #[test] fn append() { let mut a: Multiaddr = Protocol::Ip4(Ipv4Addr::new(1, 2, 3, 4)).into(); a.push(Protocol::Tcp(80)); a.push(Protocol::Http); let mut i = a.iter(); assert_eq!(Some(Protocol::Ip4(Ipv4Addr::new(1, 2, 3, 4))), i.next()); assert_eq!(Some(Protocol::Tcp(80)), i.next()); assert_eq!(Some(Protocol::Http), i.next()); assert_eq!(None, i.next()) } fn replace_ip_addr(a: &Multiaddr, p: Protocol<'_>) -> Option { a.replace(0, move |x| match x { Protocol::Ip4(_) | Protocol::Ip6(_) => Some(p), _ => None, }) } #[test] fn replace_ip4_with_ip4() { let server = multiaddr!(Ip4(Ipv4Addr::LOCALHOST), Tcp(10000u16)); let result = replace_ip_addr(&server, Protocol::Ip4([80, 81, 82, 83].into())).unwrap(); assert_eq!(result, multiaddr!(Ip4([80, 81, 82, 83]), Tcp(10000u16))) } #[test] fn replace_ip6_with_ip4() { let server = multiaddr!(Ip6(Ipv6Addr::LOCALHOST), Tcp(10000u16)); let result = replace_ip_addr(&server, Protocol::Ip4([80, 81, 82, 83].into())).unwrap(); assert_eq!(result, multiaddr!(Ip4([80, 81, 82, 83]), Tcp(10000u16))) } #[test] fn replace_ip4_with_ip6() { let server = multiaddr!(Ip4(Ipv4Addr::LOCALHOST), Tcp(10000u16)); let result = replace_ip_addr(&server, "2001:db8::1".parse::().unwrap().into()); assert_eq!( result.unwrap(), "/ip6/2001:db8::1/tcp/10000".parse::().unwrap() ) } #[test] fn unknown_protocol_string() { match "/unknown/1.2.3.4".parse::() { Ok(_) => panic!("The UnknownProtocolString error should be caused"), Err(e) => match e { crate::Error::UnknownProtocolString(protocol) => { assert_eq!(protocol, "unknown") } _ => panic!("The UnknownProtocolString error should be caused"), }, } } #[test] fn protocol_stack() { let addresses = [ "/ip4/0.0.0.0", "/ip6/::1", "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21", "/ip6/fe80::9700:803e:ca65:66e8:c21/ip6zone/wlan0", "/udp/0", "/tcp/0", "/sctp/0", "/udp/1234", "/tcp/1234", "/sctp/1234", "/udp/65535", "/tcp/65535", "/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "/udp/1234/sctp/1234", "/udp/1234/udt", "/udp/1234/utp", "/tcp/1234/http", "/tcp/1234/tls/http", "/tcp/1234/https", "/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", "/ip4/127.0.0.1/udp/1234", "/ip4/127.0.0.1/udp/0", "/ip4/127.0.0.1/tcp/1234", "/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", "/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/8000/ws/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "/p2p-webrtc-star/ip4/127.0.0.1/tcp/9090/ws/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/8000/wss/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "/ip4/127.0.0.1/tcp/9090/p2p-circuit/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "/onion/aaimaq4ygg2iegci:80", "/dnsaddr/sjc-1.bootstrap.libp2p.io", "/dnsaddr/sjc-1.bootstrap.libp2p.io/tcp/1234/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", "/ip4/127.0.0.1/tcp/127/ws", "/ip4/127.0.0.1/tcp/127/tls", "/ip4/127.0.0.1/tcp/127/tls/ws", "/ip4/127.0.0.1/tcp/127/noise", "/ip4/127.0.0.1/udp/1234/webrtc-direct", ]; let argless = std::collections::HashSet::from([ "http", "https", "noise", "p2p-circuit", "p2p-webrtc-direct", "p2p-webrtc-star", "p2p-websocket-star", "quic", "quic-v1", "tls", "udt", "utp", "webrtc-direct", "ws", "wss", ]); for addr_str in addresses { let ma = Multiaddr::from_str(addr_str).expect("These are supposed to be valid multiaddrs"); let ps: Vec<&str> = ma.protocol_stack().collect(); let mut toks: Vec<&str> = addr_str.split('/').collect(); assert_eq!("", toks[0]); toks.remove(0); let mut i = 0; while i < toks.len() { let proto_tag = toks[i]; i += 1; if argless.contains(proto_tag) { //skip } else { toks.remove(i); } } assert_eq!(ps, toks); } } // Assert all `Protocol` variants are covered // in its `Arbitrary` impl. #[cfg(nightly)] #[test] fn arbitrary_impl_for_all_proto_variants() { let variants = core::mem::variant_count::() as u8; assert_eq!(variants, Proto::IMPL_VARIANT_COUNT); } mod multiaddr_with_p2p { use multiaddr::{Multiaddr, PeerId}; fn test_multiaddr_with_p2p( multiaddr: &str, peer: &str, expected: std::result::Result<&str, &str>, ) { let peer = peer.parse::().unwrap(); let expected = expected .map(|a| a.parse::().unwrap()) .map_err(|a| a.parse::().unwrap()); let mut multiaddr = multiaddr.parse::().unwrap(); // Testing multiple time to validate idempotence. for _ in 0..3 { let result = multiaddr.with_p2p(peer); assert_eq!(result, expected); multiaddr = result.unwrap_or_else(|addr| addr); } } #[test] fn empty_multiaddr() { // Multiaddr is empty -> it should push and return Ok. test_multiaddr_with_p2p( "", "QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", Ok("/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN"), ) } #[test] fn non_p2p_terminated() { // Last protocol is not p2p -> it should push and return Ok. test_multiaddr_with_p2p( "/ip4/127.0.0.1", "QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", Ok("/ip4/127.0.0.1/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN"), ) } #[test] fn p2p_terminated_same_peer() { // Last protocol is p2p and the contained peer matches the provided one -> it should do nothing and return Ok. test_multiaddr_with_p2p( "/ip4/127.0.0.1/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", "QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", Ok("/ip4/127.0.0.1/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN"), ) } #[test] fn p2p_terminated_different_peer() { // Last protocol is p2p but the contained peer does not match the provided one -> it should do nothing and return Err. test_multiaddr_with_p2p( "/ip4/127.0.0.1/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", "QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", Err("/ip4/127.0.0.1/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN"), ) } }