Refactoring

This commit is contained in:
pradt2
2022-04-10 15:28:37 +01:00
parent 224fcd446f
commit da4303574e
10 changed files with 1264 additions and 194 deletions

701
Cargo.lock generated
View File

@@ -2,12 +2,25 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "always-online-stun"
version = "0.1.0"
dependencies = [
"futures",
"log",
"pretty_env_logger",
"rand",
"reqwest",
"serde_json",
"stunclient",
"tokio",
"tokio-stream",
@@ -19,18 +32,41 @@ version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee10e43ae4a853c0a3591d4e2ada1719e553be18199d9da9d4a83f5927c2f5c7"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "base64"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "build_const"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7"
[[package]]
name = "bumpalo"
version = "3.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
[[package]]
name = "bytecodec"
version = "0.4.15"
@@ -47,6 +83,18 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
name = "cc"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]]
name = "cfg-if"
version = "1.0.0"
@@ -62,6 +110,44 @@ dependencies = [
"build_const",
]
[[package]]
name = "encoding_rs"
version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
dependencies = [
"cfg-if",
]
[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
dependencies = [
"matches",
"percent-encoding",
]
[[package]]
name = "futures"
version = "0.3.17"
@@ -167,6 +253,40 @@ dependencies = [
"wasi",
]
[[package]]
name = "h2"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "hmac-sha1"
version = "0.1.3"
@@ -177,20 +297,154 @@ dependencies = [
]
[[package]]
name = "libc"
version = "0.2.107"
name = "http"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http-body"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6"
dependencies = [
"bytes",
"http",
"pin-project-lite",
]
[[package]]
name = "httparse"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4"
[[package]]
name = "httpdate"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "humantime"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
"quick-error",
]
[[package]]
name = "hyper"
version = "0.14.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"socket2",
"tokio",
"tower-service",
"tracing",
"want",
]
[[package]]
name = "hyper-rustls"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac"
dependencies = [
"http",
"hyper",
"rustls",
"tokio",
"tokio-rustls",
]
[[package]]
name = "idna"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "indexmap"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "ipnet"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c"
[[package]]
name = "itoa"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "js-sys"
version = "0.3.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
[[package]]
name = "log"
version = "0.4.14"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
dependencies = [
"cfg-if",
]
[[package]]
name = "matches"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "md5"
version = "0.7.0"
@@ -203,6 +457,12 @@ version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "mio"
version = "0.7.14"
@@ -234,6 +494,18 @@ dependencies = [
"winapi",
]
[[package]]
name = "once_cell"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pin-project-lite"
version = "0.2.7"
@@ -252,6 +524,16 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
[[package]]
name = "pretty_env_logger"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
dependencies = [
"env_logger",
"log",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
@@ -273,6 +555,12 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.10"
@@ -322,6 +610,142 @@ dependencies = [
"rand_core",
]
[[package]]
name = "regex"
version = "1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "reqwest"
version = "0.11.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb"
dependencies = [
"base64",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"hyper",
"hyper-rustls",
"ipnet",
"js-sys",
"lazy_static",
"log",
"mime",
"percent-encoding",
"pin-project-lite",
"rustls",
"rustls-pemfile",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
"tokio-rustls",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"webpki-roots",
"winreg",
]
[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
"cc",
"libc",
"once_cell",
"spin",
"untrusted",
"web-sys",
"winapi",
]
[[package]]
name = "rustls"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fbfeb8d0ddb84706bc597a5574ab8912817c52a397f819e5b614e2265206921"
dependencies = [
"log",
"ring",
"sct",
"webpki",
]
[[package]]
name = "rustls-pemfile"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360"
dependencies = [
"base64",
]
[[package]]
name = "ryu"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]]
name = "sct"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "serde"
version = "1.0.136"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
[[package]]
name = "serde_json"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "sha1"
version = "0.2.0"
@@ -334,6 +758,22 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
[[package]]
name = "socket2"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "stun_codec"
version = "0.1.13"
@@ -373,6 +813,30 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]]
name = "tinyvec"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.14.0"
@@ -380,7 +844,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144"
dependencies = [
"autocfg",
"bytes",
"libc",
"memchr",
"mio",
"pin-project-lite",
"tokio-macros",
@@ -398,6 +864,17 @@ dependencies = [
"syn",
]
[[package]]
name = "tokio-rustls"
version = "0.23.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4151fda0cf2798550ad0b34bcfc9b9dcc2a9d2471c895c68f3a8818e54f2389e"
dependencies = [
"rustls",
"tokio",
"webpki",
]
[[package]]
name = "tokio-stream"
version = "0.1.8"
@@ -409,6 +886,58 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
"tracing",
]
[[package]]
name = "tower-service"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
[[package]]
name = "tracing"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90442985ee2f57c9e1b548ee72ae842f4a9a20e3f417cc38dbc5dc684d9bb4ee"
dependencies = [
"lazy_static",
]
[[package]]
name = "trackable"
version = "0.2.24"
@@ -438,18 +967,162 @@ dependencies = [
"syn",
]
[[package]]
name = "try-lock"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "unicode-bidi"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
[[package]]
name = "unicode-normalization"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "url"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
dependencies = [
"form_urlencoded",
"idna",
"matches",
"percent-encoding",
]
[[package]]
name = "want"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
dependencies = [
"log",
"try-lock",
]
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasm-bindgen"
version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
[[package]]
name = "web-sys"
version = "0.3.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webpki"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "webpki-roots"
version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552ceb903e957524388c4d3475725ff2c8b7960922063af6ce53c9a43da07449"
dependencies = [
"webpki",
]
[[package]]
name = "winapi"
version = "0.3.9"
@@ -466,8 +1139,26 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi",
]

View File

@@ -7,7 +7,11 @@ edition = "2018"
[dependencies]
futures = { version = "0.3.17" }
log = { version = "0.4.16" }
pretty_env_logger = { version = "0.4.0" }
rand = { version = "0.8.4", default-features = false }
reqwest = { version = "0.11.10", default-features = false, features=["json", "rustls-tls"] }
serde_json = { version = "1.0.70", default-features = false }
stunclient = { version = "0.3.1", default-features = false, features = ["async"] }
tokio = { version = "1.14.0", default-features = false, features = ["fs", "macros", "net", "rt"] }
tokio-stream = { version = "0.1.8", default-features = false }

1
geoip_cache.txt Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,38 +0,0 @@
use std::collections::{HashMap, HashSet};
use std::net::ToSocketAddrs;
use std::str::FromStr;
use tokio::io;
use tokio_stream::StreamExt;
pub type StunCandidate = String;
const FILE_PATH: &str = "candidates.txt";
pub async fn clean_candidates() -> io::Result<()> {
let candidates = get_candidates().await?;
println!("Loaded candidates: {}", candidates.len());
let mut candidates = remove_duplicates(candidates);
candidates.sort();
println!("Unique candidates: {}", candidates.len());
let s = candidates.into_iter()
.map(|candidate| candidate.to_string())
.reduce(|a, b| format!("{}\n{}", a, b))
.unwrap_or(String::from(""));
tokio::fs::write(FILE_PATH, s).await
}
pub async fn get_candidates() -> io::Result<Vec<StunCandidate>> {
Ok(tokio::fs::read_to_string(FILE_PATH).await?
.split('\n')
.map(|s| s.trim())
.filter(|s| !s.starts_with('#'))
.map(String::from)
.collect::<Vec<_>>())
}
fn remove_duplicates(candidates: Vec<StunCandidate>) -> Vec<StunCandidate> {
let mut set = HashSet::with_capacity(candidates.len());
candidates.into_iter().for_each(|candidate| {
set.insert(candidate);
});
set.into_iter().collect()
}

115
src/geoip.rs Normal file
View File

@@ -0,0 +1,115 @@
use std::collections::{HashMap};
use std::io;
use std::io::{ErrorKind};
use std::io::ErrorKind::Other;
use std::net::IpAddr;
use std::time::Duration;
pub(crate) struct IpGeolocationIoClient {
api_key: String,
url: String,
client: reqwest::Client,
}
type GeoIpData = (f32, f32);
impl IpGeolocationIoClient {
pub(crate) fn new(api_key: String) -> IpGeolocationIoClient {
IpGeolocationIoClient {
api_key,
url: String::from("https://api.ipgeolocation.io/ipgeo"),
client: reqwest::Client::default()
}
}
pub(crate) fn default() -> IpGeolocationIoClient {
IpGeolocationIoClient::new(std::env::var("IPGEOLOCATIONIO_API_KEY")
.expect("Env var IPGEOLOCATIONIO_API_KEY required. Get a free API key at https://ipgeolocation.io"))
}
}
impl IpGeolocationIoClient {
pub(crate) async fn get_hostname_geoip_info(&self, hostname: &str) -> io::Result<GeoIpData> {
self.get_geoip_info(hostname).await
}
async fn get_ip_geoip_info(&self, ip: IpAddr) -> io::Result<GeoIpData> {
self.get_geoip_info(ip.to_string().as_str()).await
}
async fn get_geoip_info(&self, hostname_or_ip: &str) -> io::Result<GeoIpData> {
let response = self.client.get(self.url.as_str())
.query(&[("apiKey", self.api_key.as_str())])
.query(&[("ip", hostname_or_ip)])
.query(&[("fields", "latitude,longitude")])
.timeout(Duration::from_secs(1))
.send()
.await
.map_err(|err| io::Error::new(ErrorKind::Other, err))?
.json::<HashMap<String, String>>()
.await
.map_err(|err| io::Error::new(ErrorKind::Other, err))?;
let lat = response.get("latitude")
.cloned()
.map(|lat_str| lat_str.parse().unwrap())
.unwrap_or(0 as f32);
let lon = response.get("longitude")
.cloned()
.map(|lon_str| lon_str.parse().unwrap())
.unwrap_or(0 as f32);
Ok((lat, lon))
}
}
pub(crate) struct CachedIpGeolocationIpClient {
path: String,
client: IpGeolocationIoClient,
map: HashMap<String, GeoIpData>,
}
impl CachedIpGeolocationIpClient {
pub(crate) async fn new(filename: String) -> io::Result<CachedIpGeolocationIpClient> {
let cache_str = tokio::fs::read_to_string(filename.as_str()).await?;
let map: HashMap<String, GeoIpData> = serde_json::de::from_str(cache_str.as_str())
.map_err(|err| io::Error::new(Other, err))?;
let client = CachedIpGeolocationIpClient {
path: filename,
client: IpGeolocationIoClient::default(),
map
};
Ok(client)
}
pub(crate) async fn default() -> io::Result<CachedIpGeolocationIpClient> {
CachedIpGeolocationIpClient::new(String::from("geoip_cache.txt")).await
}
pub(crate) async fn get_hostname_geoip_info(&mut self, hostname: &str) -> io::Result<GeoIpData> {
self.get_geoip_info(hostname).await
}
pub(crate) async fn get_ip_geoip_info(&mut self, ip: IpAddr) -> io::Result<GeoIpData> {
self.get_geoip_info(ip.to_string().as_str()).await
}
pub(crate) async fn save(&self) -> io::Result<()> {
let str = serde_json::ser::to_string(&self.map)
.map_err(|err| io::Error::new(Other, err))?;
tokio::fs::write(self.path.as_str(), str).await
}
async fn get_geoip_info(&mut self, hostname_or_ip: &str) -> io::Result<GeoIpData> {
if let Some(geo_ip_data) = self.map.get(hostname_or_ip).cloned() {
return Ok(geo_ip_data)
}
let geo_ip_data = self.client.get_geoip_info(hostname_or_ip).await?;
self.map.insert(String::from(hostname_or_ip), geo_ip_data.clone());
Ok(geo_ip_data)
}
}

View File

@@ -1,98 +1,119 @@
use std::collections::HashSet;
use std::cell::RefCell;
use std::io;
use std::rc::Rc;
use std::thread;
use rand::seq::SliceRandom;
use rand::thread_rng;
use stunclient::Error;
use tokio::{io};
use tokio::macros::support::thread_rng_n;
use tokio::sync::Semaphore;
use std::time::Duration;
use futures::StreamExt;
use tokio::time::Instant;
use tokio_stream::{iter, StreamExt};
use crate::stun::CheckError;
use crate::utils::join_all_with_semaphore;
use crate::outputs::{ValidHosts, ValidIpV4s, ValidIpV6s};
use crate::servers::StunServer;
use crate::stun::{StunServerTestResult, StunSocketResponse};
mod candidates;
extern crate pretty_env_logger;
#[macro_use] extern crate log;
mod servers;
mod stun;
mod utils;
mod outputs;
mod geoip;
const CONCURRENT_SOCKETS_USED_LIMIT: usize = 64;
#[tokio::main(flavor = "current_thread")]
async fn main() -> io::Result<()> {
let stream = candidates::get_candidates().await?.into_iter();
let semaphore = Rc::new(Semaphore::new(100));
let profiles = stream
pretty_env_logger::init();
let client = Rc::new(RefCell::new(geoip::CachedIpGeolocationIpClient::default().await?));
let stun_servers = servers::get_stun_servers().await?;
let stun_servers_count = stun_servers.len();
info!("Loaded {} stun server hosts", stun_servers.len());
let stun_server_test_results = stun_servers.into_iter()
.map(|candidate| {
let semaphore_local_ref = semaphore.clone();
async move {
let permit = semaphore_local_ref.acquire().await.expect("Semaphore to be operating");
let res = stun::check_candidate(candidate.clone()).await;
drop(permit);
match &res {
Ok(profile) => { println!("Success: {:?}", profile) }
Err(err) => { println!("Failure: {:?}", err) }
}
res
let test_result = stun::test_udp_stun_server(candidate).await;
print_stun_server_status(&test_result);
test_result
}
})
.collect::<Vec<_>>();
let timestamp = Instant::now();
let profiles = futures::future::join_all(profiles).await;
let mut all_ok = 0;
let mut dns_unresolved = 0;
let mut partial_timeout = 0;
let mut complete_timeout = 0;
let mut incorrect_mapping_returned = 0;
profiles.iter().for_each(|res| {
match res {
Ok(_) => { all_ok += 1; }
Err(CheckError::DnsResolutionFailed) => { dns_unresolved += 1; }
Err(CheckError::PartialTimeout) => { partial_timeout += 1; }
Err(CheckError::Timeout) => { complete_timeout += 1; }
Err(CheckError::IncorrectMappingReturned) => { incorrect_mapping_returned += 1; }
}
});
println!(
"OK {} , DNS failure {} , p/Timeout {} , Timeout {} , Incorrect {}",
all_ok, dns_unresolved, partial_timeout, complete_timeout, incorrect_mapping_returned
);
let stun_server_test_results = join_all_with_semaphore(stun_server_test_results.into_iter(), CONCURRENT_SOCKETS_USED_LIMIT).await;
let mut output_hosts = profiles.iter()
.filter_map(|res| res.as_ref().ok())
.map(|profile| profile.candidate.clone())
.collect::<Vec<_>>();
output_hosts.shuffle(&mut thread_rng());
let output_hosts = output_hosts.into_iter()
.map(|candidate| String::from(candidate))
.reduce(|a, b| format!("{}\n{}", a, b))
.unwrap_or(String::from(""));
tokio::fs::write("valid_hosts.txt", output_hosts).await?;
ValidHosts::default(&stun_server_test_results).save().await?;
ValidIpV4s::default(&stun_server_test_results).save().await?;
ValidIpV6s::default(&stun_server_test_results).save().await?;
let output_ip4 = profiles.iter()
.filter_map(|res| res.as_ref().ok())
.flat_map(|profile| profile.addrs.clone().into_iter())
.filter(|addr| addr.is_ipv4())
.map(|addr| addr.to_string())
.collect::<HashSet<_>>();
let mut output_ip4 = output_ip4.into_iter()
.collect::<Vec<_>>();
output_ip4.shuffle(&mut thread_rng());
let output_ip4 = output_ip4.into_iter()
.reduce(|a, b| format!("{}\n{}", a, b))
.unwrap_or(String::from(""));
tokio::fs::write("valid_ipv4s.txt", output_ip4).await?;
write_stun_server_summary(stun_servers_count, &stun_server_test_results,timestamp.elapsed());
let output_ip6 = profiles.iter()
.filter_map(|res| res.as_ref().ok())
.flat_map(|profile| profile.addrs.clone().into_iter())
.filter(|addr| addr.is_ipv6())
.map(|addr| addr.to_string())
.collect::<HashSet<_>>();
let mut output_ip6 = output_ip6.into_iter()
.collect::<Vec<_>>();
output_ip6.shuffle(&mut thread_rng());
let output_ip6 = output_ip6.into_iter()
.reduce(|a, b| format!("{}\n{}", a, b))
.unwrap_or(String::from(""));
tokio::fs::write("valid_ipv6s.txt", output_ip6).await?;
futures::stream::iter(stun_server_test_results.iter())
.filter_map(|test_result| async move { if test_result.is_healthy() { Some(test_result) } else { None } })
.map(|test_result| futures::stream::iter(test_result.socket_tests.iter()))
.flatten()
.map(|test_result| test_result.socket)
.for_each(|socket| {
let client = client.clone();
async move {
client.borrow_mut().get_ip_geoip_info(socket.ip()).await.expect("GeoIP IP info must be available");
}
}).await;
client.borrow_mut().save().await?;
println!("Finished in {:?}", timestamp.elapsed());
Ok(())
}
fn print_stun_server_status(test_result: &StunServerTestResult) {
if test_result.is_healthy() {
info!("{:<25} -> Host is healthy", test_result.server.hostname);
} else if !test_result.is_resolvable() {
info!("{:<25} -> Host is not resolvable", test_result.server.hostname);
} else if test_result.is_partial_timeout() {
info!("{:<25} -> Host times out on some sockets", test_result.server.hostname);
} else if test_result.is_timeout() {
info!("{:<25} -> Host times out on all sockets", test_result.server.hostname);
} else {
info!("{:<25} -> Host behaves in an unexpected way. Run with RUST_LOG=DEBUG for more info", test_result.server.hostname);
for socket_test in &test_result.socket_tests {
match &socket_test.result {
StunSocketResponse::HealthyResponse { .. } => { debug!("{:<25} -> Socket {:<21} returned a healthy response", test_result.server.hostname, socket_test.socket) }
StunSocketResponse::InvalidMappingResponse { expected, actual, rtt } => { debug!("{:<25} -> Socket {:<21} returned an invalid mapping: expected={} actual={}", test_result.server.hostname, socket_test.socket, expected, actual) }
StunSocketResponse::Timeout { deadline } => { debug!("{:<25} -> Socket {:<21} timed out after {:?}", test_result.server.hostname, socket_test.socket, deadline) }
StunSocketResponse::UnexpectedError { err } => { debug!("{:<25} -> Socket {:<21} returned an unexpected error: {}", test_result.server.hostname, socket_test.socket, err) }
}
}
}
}
fn write_stun_server_summary(candidate_hosts_count: usize, results: &Vec<StunServerTestResult>, time_taken: Duration) {
let mut healthy = 0;
let mut dns_unresolved = 0;
let mut partial_timeout = 0;
let mut timeout = 0;
let mut unexpected_err = 0;
results.iter().for_each(|server_test_result| {
if server_test_result.is_healthy() {
healthy += 1;
} else if !server_test_result.is_resolvable() {
dns_unresolved += 1;
} else if server_test_result.is_partial_timeout() {
partial_timeout += 1;
} else if server_test_result.is_timeout() {
timeout += 1;
} else {
unexpected_err += 1;
}
});
info!(
"Statistics -> Tested={}, Healthy={}, DNS failure={}, partial Timeout={}, Timeout={}, Unexpected err={}. Finished in {:?}",
candidate_hosts_count, healthy, dns_unresolved, partial_timeout, timeout, unexpected_err, time_taken
);
if healthy == 0 {
warn!("No healthy hosts found! Are you behind NAT?")
}
}

110
src/outputs.rs Normal file
View File

@@ -0,0 +1,110 @@
use std::io;
use rand::prelude::SliceRandom;
use rand::thread_rng;
use crate::StunServerTestResult;
use crate::utils::ReduceToString;
pub(crate) struct ValidHosts<'a> {
server_test_results: &'a Vec<StunServerTestResult>,
file_path: String,
}
impl ValidHosts<'_> {
pub(crate) fn default(server_test_results: &Vec<StunServerTestResult>) -> ValidHosts {
ValidHosts {
server_test_results,
file_path: String::from("valid_hosts.txt")
}
}
pub(crate) fn get_output(&self) -> String {
let mut output = self.server_test_results.iter()
.filter(|server_test_result| server_test_result.is_healthy())
.map(|server_test_result| {
let _proto = server_test_result.server.protocol;
let host= server_test_result.server.hostname.as_str();
let port = server_test_result.server.port;
format!("{}:{}\n", host, port)
})
.collect::<Vec<_>>();
output.shuffle(&mut thread_rng());
let output = output.iter().reduce_to_string();
output
}
pub(crate) async fn save(&self) -> io::Result<()> {
let output = self.get_output();
tokio::fs::write(self.file_path.as_str(), output).await
}
}
pub(crate) struct ValidIpV4s<'a> {
server_test_results: &'a Vec<StunServerTestResult>,
file_path: String,
}
impl ValidIpV4s<'_> {
pub(crate) fn default(server_test_results: &Vec<StunServerTestResult>) -> ValidIpV4s {
ValidIpV4s {
server_test_results,
file_path: String::from("valid_ipv4s.txt")
}
}
pub(crate) fn get_output(&self) -> String {
let mut output = self.server_test_results.iter()
.filter(|server_test_result| server_test_result.is_healthy())
.flat_map(|server_test_result| {
let ipv4s = server_test_result.socket_tests.iter()
.filter(|socket_test| socket_test.socket.is_ipv4())
.map(|socket_test| format!("{}\n", socket_test.socket));
ipv4s
})
.collect::<Vec<_>>();
output.shuffle(&mut thread_rng());
let output = output.iter().reduce_to_string();
output
}
pub(crate) async fn save(&self) -> io::Result<()> {
let output = self.get_output();
tokio::fs::write(self.file_path.as_str(), output).await
}
}
pub(crate) struct ValidIpV6s<'a> {
server_test_results: &'a Vec<StunServerTestResult>,
file_path: String,
}
impl ValidIpV6s<'_> {
pub(crate) fn default(server_test_results: &Vec<StunServerTestResult>) -> ValidIpV6s {
ValidIpV6s {
server_test_results,
file_path: String::from("valid_ipv6s.txt")
}
}
pub(crate) fn get_output(&self) -> String {
let mut output = self.server_test_results.iter()
.filter(|server_test_result| server_test_result.is_healthy())
.flat_map(|server_test_result| {
let ipv4s = server_test_result.socket_tests.iter()
.filter(|socket_test| socket_test.socket.is_ipv6())
.map(|socket_test| format!("{}\n", socket_test.socket));
ipv4s
})
.collect::<Vec<_>>();
output.shuffle(&mut thread_rng());
let output = output.iter().reduce_to_string();
output
}
pub(crate) async fn save(&self) -> io::Result<()> {
let output = self.get_output();
tokio::fs::write(self.file_path.as_str(), output).await
}
}

33
src/servers.rs Normal file
View File

@@ -0,0 +1,33 @@
use tokio::io;
const FILE_PATH: &str = "candidates.txt";
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum TransportProtocol {
UDP, TCP
}
#[derive(Clone, Debug)]
pub struct StunServer {
pub protocol: TransportProtocol,
pub hostname: String,
pub port: u16,
}
pub async fn get_stun_servers() -> io::Result<Vec<StunServer>> {
let stun_servers = tokio::fs::read_to_string(FILE_PATH).await?
.split('\n')
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.filter(|s| !s.starts_with('#'))
.filter(|s| !s.starts_with("//"))
.map(|stun_server_str| {
let (hostname, port) = stun_server_str.split_once(':').unwrap();
StunServer {
protocol: TransportProtocol::UDP,
hostname: String::from(hostname),
port: port.parse().unwrap()
}
}).collect::<Vec<_>>();
return Ok(stun_servers);
}

View File

@@ -1,88 +1,187 @@
use std::future::Future;
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
use std::net::{SocketAddr};
use std::time::{Duration, Instant};
use stunclient::Error;
use tokio::io;
use tokio::net::{lookup_host};
use crate::candidates::StunCandidate;
use crate::utils::join_all_with_semaphore;
use crate::StunServer;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CheckError {
DnsResolutionFailed,
IncorrectMappingReturned,
PartialTimeout,
Timeout,
#[derive(Clone, Debug)]
pub(crate) struct StunServerTestResult {
pub(crate) server: StunServer,
pub(crate) socket_tests: Vec<StunSocketTestResult>,
}
#[derive(Debug)]
pub struct CandidateProfile {
pub candidate: StunCandidate,
pub addrs: Vec<SocketAddr>,
pub rtt_ms: u32,
impl StunServerTestResult {
pub(crate) fn is_resolvable(&self) -> bool {
return self.socket_tests.len() > 0;
}
pub(crate) fn is_healthy(&self) -> bool {
self.is_resolvable() && self.socket_tests.iter()
.all(StunSocketTestResult::is_ok)
}
pub(crate) fn is_partial_timeout(&self) -> bool {
self.is_resolvable()
&& self.is_any_healthy()
&& self.is_any_timeout()
&&! self.is_any_invalid_mapping()
&&! self.is_any_unexpected_response()
}
pub(crate) fn is_timeout(&self) -> bool {
self.is_resolvable()
&&! self.is_any_healthy()
&& self.is_any_timeout()
&&! self.is_any_invalid_mapping()
&&! self.is_any_unexpected_response()
}
fn is_any_healthy(&self) -> bool {
self.is_resolvable() && self.socket_tests.iter()
.any(|result| match result.result {
StunSocketResponse::HealthyResponse {..} => true,
_ => false,
})
}
fn is_any_timeout(&self) -> bool {
self.is_resolvable() && self.socket_tests.iter()
.any(|result| match result.result {
StunSocketResponse::Timeout {..} => true,
_ => false,
})
}
fn is_any_invalid_mapping(&self) -> bool {
self.is_resolvable() && self.socket_tests.iter()
.any(|result| match result.result {
StunSocketResponse::InvalidMappingResponse {..} => true,
_ => false,
})
}
fn is_any_unexpected_response(&self) -> bool {
self.is_resolvable() && self.socket_tests.iter()
.any(|result| match result.result {
StunSocketResponse::UnexpectedError {..} => true,
_ => false,
})
}
}
pub async fn check_candidate(candidate: StunCandidate) -> Result<CandidateProfile, CheckError> {
let addrs = lookup_host(&candidate).await
.map_err(|err| CheckError::DnsResolutionFailed)?.collect::<Vec<_>>();
if addrs.len() == 0 { return Err(CheckError::DnsResolutionFailed); }
#[derive(Clone, Debug)]
pub(crate) struct StunSocketTestResult {
pub(crate) socket: SocketAddr,
pub(crate) result: StunSocketResponse
}
let responses = addrs.iter().map(|address| async move {
let u = tokio::net::UdpSocket::bind(match address {
impl StunSocketTestResult {
pub(crate) fn is_ok(&self) -> bool {
self.result.is_ok()
}
}
#[derive(Clone, Debug)]
pub(crate) enum StunSocketResponse {
HealthyResponse { rtt: Duration },
InvalidMappingResponse { expected: SocketAddr, actual: SocketAddr, rtt: Duration },
Timeout { deadline: Duration },
UnexpectedError { err: String }
}
impl StunSocketResponse {
fn is_ok(&self) -> bool {
match &self {
StunSocketResponse::HealthyResponse { .. } => true,
_ => false
}
}
}
pub(crate) async fn test_udp_stun_server(
server: StunServer
) -> StunServerTestResult {
let socket_addrs = tokio::net::lookup_host(format!("{}:{}", server.hostname, server.port)).await;
if socket_addrs.is_err() {
if socket_addrs.as_ref().err().unwrap().to_string() != "failed to lookup address information: Name or service not known" {
warn!("{:<21} -> Unexpected DNS failure: {}", server.hostname, socket_addrs.as_ref().err().unwrap().to_string());
}
return StunServerTestResult {
server,
socket_tests: vec![],
}
}
let results = socket_addrs.unwrap()
.map(|addr| addr.ip())
.map(|addr| {
let port = server.port;
let hostname = server.hostname.as_str();
async move {
let stun_socket = SocketAddr::new(addr, port);
let res = test_socket_addr(hostname, stun_socket).await;
res
}
})
.collect::<Vec<_>>();
let results = join_all_with_semaphore(results.into_iter(), 1).await;
StunServerTestResult {
server,
socket_tests: results
}
}
async fn test_socket_addr(
hostname: &str,
socket_addr: SocketAddr
) -> StunSocketTestResult {
let local_socket = tokio::net::UdpSocket::bind(
match socket_addr {
SocketAddr::V4(..) => { "0.0.0.0:0" }
SocketAddr::V6(..) => { "[::]:0" }
}.parse::<std::net::SocketAddr>().unwrap()).await.unwrap();
let local_address = u.local_addr().expect("Local address to be available");
let mut client = stunclient::StunClient::new(*address);
client.set_timeout(Duration::from_secs(1));
let time = Instant::now();
let res = client.query_external_address_async(&u).await;
match res {
Ok(mapped_address) => if mapped_address.port() == local_address.port() {
Ok((mapped_address, time.elapsed()))
} else {
Err(CheckError::IncorrectMappingReturned)
},
Err(_) => { Err(CheckError::Timeout) }
}
}).collect::<Vec<_>>();
).await.unwrap();
let responses = futures::future::join_all(responses).await;
let mut client = stunclient::StunClient::new(socket_addr);
let deadline = Duration::from_secs(1);
client.set_timeout(deadline);
let ok_count = responses.iter()
.filter(|response| { response.is_ok() })
.count();
let start = Instant::now();
if ok_count == responses.len() {
let rtt_ms = responses.iter()
.filter_map(|response| response.as_ref().ok())
.map(|response| response.1)
.map(|duration| duration.as_millis() as u32)
.sum::<u32>() / responses.len() as u32;
let result = client.query_external_address_async(&local_socket).await;
return Ok(CandidateProfile {
candidate,
addrs,
rtt_ms,
});
let request_duration = Instant::now() - start;
return match result {
Ok(return_addr) => if return_addr.port() == local_socket.local_addr().unwrap().port() {
debug!("{:<25} -> Socket {:<21} returned a healthy response", hostname, &socket_addr);
StunSocketTestResult {
socket: socket_addr,
result: StunSocketResponse::HealthyResponse { rtt: request_duration },
}
} else {
debug!("{:<25} -> Socket {:<21} returned an invalid mapping: expected={}, actual={}", hostname, &socket_addr, local_socket.local_addr().unwrap(), return_addr);
StunSocketTestResult {
socket: socket_addr,
result: StunSocketResponse::InvalidMappingResponse { expected: local_socket.local_addr().unwrap(), actual: return_addr, rtt: request_duration },
}
},
Err(err) => {
if err.to_string() == "Timed out waiting for STUN server reply" {
debug!("{:<25} -> Socket {:<21} timed out after {:?}", hostname, &socket_addr, deadline);
StunSocketTestResult {
socket: socket_addr,
result: StunSocketResponse::Timeout { deadline },
}
} else {
debug!("{:<25} -> Socket {:<21} returned an unexpected error: {:?}", hostname, &socket_addr, err.to_string());
StunSocketTestResult {
socket: socket_addr,
result: StunSocketResponse::UnexpectedError { err: err.to_string() },
}
}
},
}
let ip_fails = responses.iter()
.filter_map(|response| response.err())
.filter(|err| err == &CheckError::IncorrectMappingReturned)
.count();
if ip_fails > 0 {
return Err(CheckError::IncorrectMappingReturned);
}
let timeouts = responses.iter()
.filter_map(|response| response.err())
.filter(|err| err == &CheckError::Timeout)
.count();
if timeouts < responses.len() {
return Err(CheckError::PartialTimeout);
}
return Err(CheckError::Timeout);
}

34
src/utils.rs Normal file
View File

@@ -0,0 +1,34 @@
use std::fmt::Display;
use std::future::Future;
use std::rc::Rc;
use tokio::sync::Semaphore;
pub(crate) async fn join_all_with_semaphore<T: Iterator<Item=U>, U: Future<Output = V>, V> (
it: T,
permits: usize,
) -> Vec<V> {
let semaphore = Rc::new(Semaphore::new(permits));
let pending_futures = it.map(|it| {
let semaphore_local = semaphore.clone();
async move {
let permit = semaphore_local.acquire().await.unwrap();
let output = it.await;
drop(permit);
output
}
}).collect::<Vec<_>>();
let resolved_futures = futures::future::join_all(pending_futures).await;
resolved_futures
}
pub(crate) trait ReduceToString<U: Display> : Iterator<Item=U> {
fn reduce_to_string(self) -> String where Self: Sized {
let mut s = String::from("");
for it in self {
s.push_str(it.to_string().as_str());
}
s
}
}
impl <I: Iterator<Item=U>, U: Display> ReduceToString<U> for I {}