master
eater 4 years ago
parent ef40af30f4
commit c7cc7ff9ed
Signed by: eater
GPG Key ID: AD2560A0F84F0759

571
Cargo.lock generated

@ -21,7 +21,7 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [ dependencies = [
"winapi", "winapi 0.3.9",
] ]
[[package]] [[package]]
@ -75,7 +75,7 @@ dependencies = [
"socket2", "socket2",
"vec-arena", "vec-arena",
"wepoll-sys-stjepang", "wepoll-sys-stjepang",
"winapi", "winapi 0.3.9",
] ]
[[package]] [[package]]
@ -135,7 +135,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi",
"libc", "libc",
"winapi", "winapi 0.3.9",
] ]
[[package]] [[package]]
@ -274,6 +274,32 @@ dependencies = [
"cache-padded", "cache-padded",
] ]
[[package]]
name = "core-foundation"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation-sys"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
[[package]]
name = "crossbeam-channel"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ee0cc8804d5393478d743b035099520087a5186f3b93fa58cec08fa62407b6"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-epoch" name = "crossbeam-epoch"
version = "0.8.2" version = "0.8.2"
@ -300,6 +326,21 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "dtoa"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
[[package]]
name = "encoding_rs"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a51b8cf747471cb9499b6d59e59b0444f4c90eba8968c4e44874e92b5b64ace2"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "event-listener" name = "event-listener"
version = "2.4.0" version = "2.4.0"
@ -334,6 +375,43 @@ version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bd3bdaaf0a72155260a1c098989b60db1cbb22d6a628e64f16237aa4da93cc7" checksum = "4bd3bdaaf0a72155260a1c098989b60db1cbb22d6a628e64f16237aa4da93cc7"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
dependencies = [
"bitflags",
"fuchsia-zircon-sys",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.3.5" version = "0.3.5"
@ -461,6 +539,25 @@ version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
[[package]]
name = "h2"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "993f9e0baeed60001cf565546b0d3dbe6a6ad23f2bd31644a133c641eccf6d53"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.8.2" version = "0.8.2"
@ -485,6 +582,70 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
[[package]]
name = "http"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http-body"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b"
dependencies = [
"bytes",
"http",
]
[[package]]
name = "httparse"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
[[package]]
name = "hyper"
version = "0.13.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e68a8dd9716185d9e64ea473ea6ef63529252e3e27623295a0378a19665d5eb"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"httparse",
"itoa",
"pin-project",
"socket2",
"time",
"tokio",
"tower-service",
"tracing",
"want",
]
[[package]]
name = "hyper-tls"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed"
dependencies = [
"bytes",
"hyper",
"native-tls",
"tokio",
"tokio-tls",
]
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.2.0" version = "0.2.0"
@ -506,6 +667,21 @@ dependencies = [
"hashbrown", "hashbrown",
] ]
[[package]]
name = "iovec"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
dependencies = [
"libc",
]
[[package]]
name = "ipnet"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135"
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "0.4.6" version = "0.4.6"
@ -521,6 +697,16 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]] [[package]]
name = "kv-log-macro" name = "kv-log-macro"
version = "1.0.7" version = "1.0.7"
@ -584,6 +770,22 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "mime_guess"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
dependencies = [
"mime",
"unicase",
]
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.4.0" version = "0.4.0"
@ -593,6 +795,37 @@ dependencies = [
"adler", "adler",
] ]
[[package]]
name = "mio"
version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
dependencies = [
"cfg-if",
"fuchsia-zircon",
"fuchsia-zircon-sys",
"iovec",
"kernel32-sys",
"libc",
"log",
"miow",
"net2",
"slab",
"winapi 0.2.8",
]
[[package]]
name = "miow"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
dependencies = [
"kernel32-sys",
"net2",
"winapi 0.2.8",
"ws2_32-sys",
]
[[package]] [[package]]
name = "multitask" name = "multitask"
version = "0.2.0" version = "0.2.0"
@ -604,6 +837,35 @@ dependencies = [
"fastrand", "fastrand",
] ]
[[package]]
name = "native-tls"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d"
dependencies = [
"lazy_static",
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]]
name = "net2"
version = "0.2.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7"
dependencies = [
"cfg-if",
"libc",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.43" version = "0.1.43"
@ -645,6 +907,39 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
[[package]]
name = "openssl"
version = "0.10.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4"
dependencies = [
"bitflags",
"cfg-if",
"foreign-types",
"lazy_static",
"libc",
"openssl-sys",
]
[[package]]
name = "openssl-probe"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
[[package]]
name = "openssl-sys"
version = "0.9.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de"
dependencies = [
"autocfg",
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "parking" name = "parking"
version = "1.0.6" version = "1.0.6"
@ -695,6 +990,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33"
[[package]] [[package]]
name = "polling" name = "polling"
version = "0.1.6" version = "0.1.6"
@ -705,7 +1006,7 @@ dependencies = [
"libc", "libc",
"log", "log",
"wepoll-sys-stjepang", "wepoll-sys-stjepang",
"winapi", "winapi 0.3.9",
] ]
[[package]] [[package]]
@ -800,6 +1101,50 @@ dependencies = [
"crossbeam-epoch", "crossbeam-epoch",
] ]
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "reqwest"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9eaa17ac5d7b838b7503d118fa16ad88f440498bf9ffe5424e621f93190d61e"
dependencies = [
"base64",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"http",
"http-body",
"hyper",
"hyper-tls",
"ipnet",
"js-sys",
"lazy_static",
"log",
"mime",
"mime_guess",
"native-tls",
"percent-encoding",
"pin-project-lite",
"serde",
"serde_urlencoded",
"tokio",
"tokio-tls",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winreg",
]
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.16.15" version = "0.16.15"
@ -812,7 +1157,7 @@ dependencies = [
"spin", "spin",
"untrusted", "untrusted",
"web-sys", "web-sys",
"winapi", "winapi 0.3.9",
] ]
[[package]] [[package]]
@ -827,6 +1172,16 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "schannel"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
dependencies = [
"lazy_static",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "scoped-tls" name = "scoped-tls"
version = "1.0.0" version = "1.0.0"
@ -839,6 +1194,29 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "security-framework"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535"
dependencies = [
"bitflags",
"core-foundation",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.115" version = "1.0.115"
@ -871,6 +1249,18 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_urlencoded"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
dependencies = [
"dtoa",
"itoa",
"serde",
"url",
]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.2" version = "0.4.2"
@ -886,7 +1276,7 @@ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"redox_syscall", "redox_syscall",
"winapi", "winapi 0.3.9",
] ]
[[package]] [[package]]
@ -924,6 +1314,20 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "tempfile"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
dependencies = [
"cfg-if",
"libc",
"rand",
"redox_syscall",
"remove_dir_all",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.11.0" version = "0.11.0"
@ -940,7 +1344,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
dependencies = [ dependencies = [
"libc", "libc",
"winapi", "winapi 0.3.9",
] ]
[[package]] [[package]]
@ -949,6 +1353,48 @@ version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117"
[[package]]
name = "tokio"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd"
dependencies = [
"bytes",
"fnv",
"futures-core",
"iovec",
"lazy_static",
"memchr",
"mio",
"num_cpus",
"pin-project-lite",
"slab",
]
[[package]]
name = "tokio-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343"
dependencies = [
"native-tls",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"log",
"pin-project-lite",
"tokio",
]
[[package]] [[package]]
name = "torment-bencode" name = "torment-bencode"
version = "0.1.0" version = "0.1.0"
@ -1030,11 +1476,16 @@ name = "torment-manager"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bytes", "bytes",
"crossbeam-channel",
"lazy_static", "lazy_static",
"polling",
"rand",
"reqwest",
"ring", "ring",
"torment-core", "torment-core",
"torment-peer", "torment-peer",
"torment-storage", "torment-storage",
"torment-tracker",
"url", "url",
] ]
@ -1056,6 +1507,57 @@ dependencies = [
"torment-core", "torment-core",
] ]
[[package]]
name = "torment-tracker"
version = "0.1.0"
dependencies = [
"reqwest",
"torment-bencode",
"torment-core",
"url",
]
[[package]]
name = "tower-service"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860"
[[package]]
name = "tracing"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d79ca061b032d6ce30c660fded31189ca0b9922bf483cd70759f13a2d86786c"
dependencies = [
"cfg-if",
"log",
"tracing-core",
]
[[package]]
name = "tracing-core"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f0e00789804e99b20f12bc7003ca416309d28a6f495d6af58d1e2c2842461b5"
dependencies = [
"lazy_static",
]
[[package]]
name = "try-lock"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.4" version = "0.3.4"
@ -1103,6 +1605,12 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "vcpkg"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
[[package]] [[package]]
name = "vec-arena" name = "vec-arena"
version = "0.5.2" version = "0.5.2"
@ -1115,12 +1623,28 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
[[package]] [[package]]
name = "waker-fn" name = "waker-fn"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9571542c2ce85ce642e6b58b3364da2fb53526360dfb7c211add4f5c23105ff7" checksum = "9571542c2ce85ce642e6b58b3364da2fb53526360dfb7c211add4f5c23105ff7"
[[package]]
name = "want"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
dependencies = [
"log",
"try-lock",
]
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.9.0+wasi-snapshot-preview1" version = "0.9.0+wasi-snapshot-preview1"
@ -1134,6 +1658,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0563a9a4b071746dd5aedbc3a28c6fe9be4586fb3fbadb67c400d4f53c6b16c" checksum = "f0563a9a4b071746dd5aedbc3a28c6fe9be4586fb3fbadb67c400d4f53c6b16c"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"serde",
"serde_json",
"wasm-bindgen-macro", "wasm-bindgen-macro",
] ]
@ -1212,6 +1738,12 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@ -1222,6 +1754,12 @@ dependencies = [
"winapi-x86_64-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu",
] ]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
[[package]] [[package]]
name = "winapi-i686-pc-windows-gnu" name = "winapi-i686-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
@ -1234,6 +1772,25 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "winreg"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]] [[package]]
name = "yaml-rust" name = "yaml-rust"
version = "0.3.5" version = "0.3.5"

@ -8,5 +8,6 @@ members = [
"torment-peer", "torment-peer",
"torment-storage", "torment-storage",
"torment-manager", "torment-manager",
"torment-daemon" "torment-daemon",
"torment-tracker",
] ]

@ -1,4 +1,4 @@
use bytes::{Bytes, BytesMut}; use bytes::{Buf, Bytes, BytesMut};
use rand::random; use rand::random;
use std::cmp::min; use std::cmp::min;
use std::convert::TryInto; use std::convert::TryInto;
@ -9,7 +9,6 @@ use torment_core::infohash::v1::U160;
use torment_core::infohash::InfoHashCapable; use torment_core::infohash::InfoHashCapable;
use torment_core::metainfo::Torrent; use torment_core::metainfo::Torrent;
use torment_core::peer_id; use torment_core::peer_id;
use torment_peer::message::Message::Request;
use torment_peer::message::{Handshake, Message, SelectionMessage}; use torment_peer::message::{Handshake, Message, SelectionMessage};
use torment_peer::{Peer, PeerProtocol}; use torment_peer::{Peer, PeerProtocol};
use torment_storage::ToStorageMap; use torment_storage::ToStorageMap;
@ -26,9 +25,10 @@ pub fn download(torrent: Torrent, peers: Vec<SocketAddr>) {
let id = U160::from(peer_id(random())); let id = U160::from(peer_id(random()));
let our_header = Handshake::new(id, torrent.info_hash()); let our_header = Handshake::new(id, torrent.info_hash());
stream.write(&our_header.to_bytes()); stream.write(&our_header.to_bytes()).unwrap();
let mut buffer = [0u8; 4096]; let mut buffer = [0u8; 4096];
let mut done_header = false; let mut done_header = false;
#[allow(unused_assignments)]
let mut header = Handshake::new(U160::random(), U160::random()); let mut header = Handshake::new(U160::random(), U160::random());
let mut peer = None; let mut peer = None;
let mut message_buffer = BytesMut::with_capacity(4096 * 10); let mut message_buffer = BytesMut::with_capacity(4096 * 10);
@ -71,12 +71,12 @@ pub fn download(torrent: Torrent, peers: Vec<SocketAddr>) {
continue; continue;
} }
let mut peer = peer.as_mut().unwrap(); let peer = peer.as_mut().unwrap();
message_buffer.extend_from_slice(&buffer[..size]); message_buffer.extend_from_slice(&buffer[..size]);
while message_buffer.len() >= 4 { while message_buffer.len() >= 4 {
let length = u32::from_be_bytes(message_buffer[..4].try_into().unwrap()); let length = u32::from_be_bytes(message_buffer[..4].try_into().unwrap());
if length == 0 { if length == 0 {
message_buffer.split_to(4); message_buffer.advance(4);
continue; continue;
} }
@ -89,7 +89,9 @@ pub fn download(torrent: Torrent, peers: Vec<SocketAddr>) {
let msg = Message::from_bytes(message.slice(4..)).expect("Failed parsing message"); let msg = Message::from_bytes(message.slice(4..)).expect("Failed parsing message");
// println!("=> {:?}", msg); // println!("=> {:?}", msg);
if msg == Message::Unchoke { if msg == Message::Unchoke {
stream.write_all(&Message::Interested.to_length_prefixed_bytes()); stream
.write_all(&Message::Interested.to_length_prefixed_bytes())
.unwrap();
} }
peer.process(msg); peer.process(msg);
} }

@ -0,0 +1 @@
pub const REQUEST_SIZE: usize = 16384;

@ -10,11 +10,15 @@ use std::fmt::{Display, Formatter};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::str::FromStr; use std::str::FromStr;
mod consts;
pub mod infohash; pub mod infohash;
pub mod ip; pub mod ip;
pub mod metainfo; pub mod metainfo;
pub mod peer_storage;
pub mod utils; pub mod utils;
pub use consts::*;
pub fn peer_id(input: [u8; 12]) -> [u8; 20] { pub fn peer_id(input: [u8; 12]) -> [u8; 20] {
let peer_id = format!( let peer_id = format!(
"eT{:0>2}{:0>2}{:0>2}AAAAAAAAAAAA", "eT{:0>2}{:0>2}{:0>2}AAAAAAAAAAAA",
@ -121,34 +125,44 @@ impl ContactInfo {
} }
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)] #[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct Bitfield(BytesMut); pub struct Bitfield(BytesMut, usize);
impl Bitfield { impl Bitfield {
pub fn new<T: AsRef<[u8]>>(field: T) -> Bitfield { pub fn size(&self) -> usize {
Bitfield(BytesMut::from(field.as_ref())) self.1
}
pub fn new<T: AsRef<[u8]>>(field: T, size: usize) -> Bitfield {
Bitfield(BytesMut::from(field.as_ref()), size)
} }
pub fn with_size(size: usize) -> Bitfield { pub fn with_size(size: usize) -> Bitfield {
Bitfield({ Bitfield(
let mut field = BytesMut::new(); {
field.resize((size / 8) + if size % 8 > 0 { 1 } else { 0 }, 0); let mut field = BytesMut::new();
field field.resize((size / 8) + if size % 8 > 0 { 1 } else { 0 }, 0);
}) field
},
size,
)
} }
pub fn set(&mut self, index: u32) { pub fn set(&mut self, index: u32) {
debug_assert!(index < self.1 as u32, "index out of bounds");
let byte_index = index / 8; let byte_index = index / 8;
let bitmask = 1 << (7 - index % 8); let bitmask = 1 << (7 - (index % 8));
self.0[byte_index as usize] |= bitmask; self.0[byte_index as usize] |= bitmask;
} }
pub fn unset(&mut self, index: u32) { pub fn unset(&mut self, index: u32) {
debug_assert!(index < self.1 as u32, "index out of bounds");
let byte_index = index / 8; let byte_index = index / 8;
let bitmask = 1 << (8 - index % 8); let bitmask = 1 << (7 - (index % 8));
self.0[byte_index as usize] &= !bitmask; self.0[byte_index as usize] &= !bitmask;
} }
pub fn get(&self, index: u32) -> bool { pub fn get(&self, index: u32) -> bool {
debug_assert!(index < self.1 as u32, "index out of bounds");
let byte_index = index / 8; let byte_index = index / 8;
let bitmask = 1 << (7 - (index % 8)); let bitmask = 1 << (7 - (index % 8));
(self.0[byte_index as usize] & bitmask) > 0 (self.0[byte_index as usize] & bitmask) > 0
@ -161,13 +175,40 @@ impl Bitfield {
self.0[i] |= field[i] self.0[i] |= field[i]
} }
} }
pub fn all(&self) -> bool {
for i in 0..self.0.len() {
let byte = self.0[i];
if i + 1 == self.0.len() {
let left = self.1 % 8;
if left != 0 {
return byte == 255 ^ (255 >> left);
}
}
if byte != u8::MAX {
return false;
}
}
return true;
}
} }
impl Debug for Bitfield { impl Debug for Bitfield {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Bitfield(")?; write!(f, "Bitfield(")?;
for byte in &self.0 { for i in 0..self.0.len() {
write!(f, "{:b}", byte)?; let byte = self.0[i];
if i + 1 == self.0.len() {
write!(
f,
"{}",
&format!("{:0>8b}", byte)[0..(if self.1 % 8 == 0 { 8 } else { self.1 % 8 })]
)?;
} else {
write!(f, "{:b}", byte)?;
}
} }
write!(f, ")") write!(f, ")")
@ -180,9 +221,6 @@ impl AsRef<[u8]> for Bitfield {
} }
} }
#[derive(Debug, Default)]
pub struct PeerStorage {}
#[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone)] #[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone)]
pub enum LookupFilter { pub enum LookupFilter {
IPv6, IPv6,
@ -190,17 +228,6 @@ pub enum LookupFilter {
All, All,
} }
impl PeerStorage {
pub fn new() -> PeerStorage {
PeerStorage {}
}
pub fn add_peers(&mut self, _info_hash: U160, _peers: Vec<SocketAddr>) {}
pub fn get_peers(&self, _info_hash: U160, _filter: LookupFilter) -> Vec<SocketAddr> {
vec![]
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{compact_peer_id, peer_id, Bitfield}; use crate::{compact_peer_id, peer_id, Bitfield};
@ -213,6 +240,7 @@ mod tests {
assert_eq!(&compact_peer_id(), b"eT\x00\x01"); assert_eq!(&compact_peer_id(), b"eT\x00\x01");
} }
#[test]
fn test_bitfield() { fn test_bitfield() {
let mut field = Bitfield::with_size(3); let mut field = Bitfield::with_size(3);
field.set(0); field.set(0);
@ -225,5 +253,12 @@ mod tests {
field.add(other_field); field.add(other_field);
assert_eq!(true, field.get(2)); assert_eq!(true, field.get(2));
assert!(!field.all());
field.set(1);
field.set(0);
println!("{} = {:?}", field.as_ref()[0], field);
assert!(field.all());
} }
} }

@ -79,7 +79,7 @@ impl Torrent {
.or(name) .or(name)
.unwrap_or(info.info_hash.to_string()); .unwrap_or(info.info_hash.to_string());
let announce_list = if let Some(announce) = dict.get(b"announce-list") { let mut announce_list = if let Some(announce) = dict.get(b"announce-list") {
announce announce
.list() .list()
.map(|list| -> Result<Vec<HashSet<Url>>, MetaInfoParsingError> { .map(|list| -> Result<Vec<HashSet<Url>>, MetaInfoParsingError> {
@ -110,6 +110,18 @@ impl Torrent {
vec![] vec![]
}; };
if let Some(item) = dict.get(b"announce") {
if let Some(Ok(url)) = item.string() {
if let Ok(url) = Url::parse(&url) {
if announce_list.is_empty() {
announce_list.push(HashSet::new());
}
announce_list[0].insert(url);
}
}
}
Ok(Torrent { Ok(Torrent {
name, name,
announce_list, announce_list,

@ -0,0 +1,122 @@
use crate::infohash::v1::U160;
use crate::LookupFilter;
use std::collections::{HashMap, HashSet};
use std::net::SocketAddr;
use std::ops::Add;
use std::sync::{Arc, RwLock};
use std::time::{Duration, Instant};
use url::Url;
#[derive(Debug, Default)]
pub struct PeerStorage {
torrents: HashMap<U160, PeerCollection>,
}
#[derive(Debug, Default)]
pub struct PeerCollection {
sets: Vec<PeerSet>,
peers: HashSet<SocketAddr>,
peers6: HashSet<SocketAddr>,
}
#[derive(Debug)]
pub struct PeerSet {
source: PeerStorageSource,
expires: Instant,
peers: Vec<SocketAddr>,
}
impl PeerCollection {
fn add_peers(&mut self, peers: Vec<SocketAddr>, source: PeerStorageSource) {
let set = PeerSet {
source,
peers,
expires: Instant::now().add(Duration::from_secs(60 * 30)),
};
for peer in &set.peers {
if peer.is_ipv4() {
self.peers.insert(*peer);
} else {
self.peers6.insert(*peer);
}
}
self.sets.push(set);
}
}
#[derive(Debug, Clone)]
pub enum PeerStorageSource {
DHT,
Tracker(Url),
}
impl PeerStorage {
pub fn new() -> PeerStorage {
PeerStorage {
torrents: Default::default(),
}
}
pub fn add_peers(
&mut self,
info_hash: U160,
peers: Vec<SocketAddr>,
source: PeerStorageSource,
) {
self.torrents
.entry(info_hash)
.or_default()
.add_peers(peers, source)
}
pub fn get_peers(&self, info_hash: U160, filter: LookupFilter) -> Vec<SocketAddr> {
let collection = if let Some(collection) = self.torrents.get(&info_hash) {
collection
} else {
return vec![];
};
let mut buffer = vec![];
if filter != LookupFilter::IPv6 {
buffer.extend(&collection.peers);
}
if filter != LookupFilter::IPv4 {
buffer.extend(&collection.peers6);
}
buffer
}
}
#[derive(Debug, Clone)]
pub struct PeerStorageHolder {
inner: Arc<RwLock<PeerStorage>>,
}
impl PeerStorageHolder {
pub fn new(peer_storage: PeerStorage) -> PeerStorageHolder {
PeerStorageHolder {
inner: Arc::new(RwLock::new(peer_storage)),
}
}
pub fn get_peers(&self, info_hash: U160, filter: LookupFilter) -> Vec<SocketAddr> {
self.inner.read().unwrap().get_peers(info_hash, filter)
}
pub fn add_peers(
&mut self,
info_hash: U160,
peers: Vec<SocketAddr>,
source: PeerStorageSource,
) {
self.inner
.write()
.unwrap()
.add_peers(info_hash, peers, source)
}
}

@ -259,6 +259,10 @@ impl<K: Debug + Clone + Ord> Debug for EphemeralSet<K> {
} }
impl<K: Clone + Ord + Debug> EphemeralSet<K> { impl<K: Clone + Ord + Debug> EphemeralSet<K> {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn new() -> Self { pub fn new() -> Self {
EphemeralSet(EphemeralMap::new()) EphemeralSet(EphemeralMap::new())
} }

@ -1,151 +1,42 @@
use bytes::BytesMut; use rand::random;
use polling::{Event, Poller};
use std::collections::HashMap;
use std::convert::TryInto;
use std::fs::File; use std::fs::File;
use std::io::{Read, Write}; use std::io::Read;
use std::net::TcpStream; use std::net::IpAddr;
use std::option::Option::Some; use std::str::FromStr;
use std::path::PathBuf;
use std::process::exit;
use std::result::Result::Ok;
use std::time::Duration;
use torment_core::infohash::v1::U160; use torment_core::infohash::v1::U160;
use torment_core::metainfo::Torrent; use torment_core::metainfo::Torrent;
use torment_core::peer_id; use torment_core::peer_id;
use torment_core::peer_storage::{PeerStorage, PeerStorageHolder};
use torment_manager::session_manager::SessionManager; use torment_manager::session_manager::SessionManager;
use torment_manager::torment_instance::TormentInstance;
use torment_manager::torrent_manager::{TorrentManager, TorrentTarget}; use torment_manager::torrent_manager::{TorrentManager, TorrentTarget};
use torment_peer::message::{Handshake, Message}; use torment_manager::tracker_manager::TrackerManager;
use torment_peer::PeerProtocol::TCP;
struct TcpState {
builder: BytesMut,
handshake: Option<Handshake>,
stream: TcpStream,
}
fn main() { fn main() {
let mut session_manager = SessionManager::new(); let ip = IpAddr::from_str("0.0.0.0").unwrap();
let peer_id = U160::from(peer_id(random()));
let mut session_manager = SessionManager::new(ip, 50002, peer_id);
let mut buffer = vec![]; let mut file = vec![];
File::open("/home/eater/Downloads/[Commie] Senyuu. - 23 [150B93D5].mkv.torrent") File::open("test.torrent")
.unwrap() .unwrap()
.read_to_end(&mut buffer) .read_to_end(&mut file)
.unwrap(); .unwrap();
let torrent = Torrent::from_bytes(file, Some("test".to_string())).unwrap();
println!("Downloading {:#?}", torrent);
let torrent_manager = TorrentManager::from_torrent( let torrent_manager = TorrentManager::from_torrent(
Torrent::from_bytes( torrent,
buffer,
Some("[Commie] Senyuu. - 23 [150B93D5].mkv".to_string()),
)
.unwrap(),
TorrentTarget { TorrentTarget {
path: PathBuf::from("/tmp"), path: "/tmp/test".into(),
is_base_path: true, is_base_path: true,
}, },
None, Some(session_manager.tracker_manager_mut()),
); );
let peer_id = U160::from(peer_id(rand::random()));
let info_hash = torrent_manager.info_hash();
session_manager.add_torrent_manager(torrent_manager); session_manager.add_torrent_manager(torrent_manager);
let mut tcp_stream_ktorrent = TcpStream::connect("127.0.0.1:6881").unwrap(); let mut instance = TormentInstance::new(50002, session_manager);
tcp_stream_ktorrent.set_nodelay(true).unwrap(); instance.logic_loop();
tcp_stream_ktorrent
.write_all(Handshake::new(peer_id, info_hash).to_bytes().as_ref())
.unwrap();
let mut tcp_stream_transmission = TcpStream::connect("192.168.188.100:51413").unwrap();
tcp_stream_transmission.set_nodelay(true).unwrap();
tcp_stream_transmission
.write_all(Handshake::new(peer_id, info_hash).to_bytes().as_ref())
.unwrap();
let mut buffer = vec![0u8; 4096 * 10];
let mut poller = Poller::new().unwrap();
poller.insert(&tcp_stream_ktorrent).unwrap();
poller.insert(&tcp_stream_transmission).unwrap();
poller.interest(&tcp_stream_ktorrent, Event::readable(0));
poller.interest(&tcp_stream_transmission, Event::readable(1));
let mut items = vec![
TcpState {
builder: Default::default(),
handshake: None,
stream: tcp_stream_ktorrent,
},
TcpState {
builder: Default::default(),
handshake: None,
stream: tcp_stream_transmission,
},
];
let mut peer_map: HashMap<(U160, U160), usize> = Default::default();
loop {
let mut events: Vec<Event> = vec![];
poller.wait(&mut events, Some(Duration::from_secs(10)));
for event in events {
println!("Event => {:?}", event);
if !event.readable {
continue;
}
let item = &mut items[event.key];
let packet = item.stream.read(&mut buffer).unwrap();
item.builder.extend_from_slice(&buffer[..packet]);
let handshake = if let Some(handshake) = &item.handshake {
handshake
} else {
if item.builder.len() >= 68 {
item.handshake =
Some(Handshake::from_bytes(item.builder.split_to(68).freeze()).unwrap());
let handshake = item.handshake.as_ref().unwrap();
println!("{} => {:?}", item.stream.peer_addr().unwrap(), handshake);
peer_map.insert((handshake.info_hash(), handshake.peer_id()), event.key);
session_manager.handshake(*handshake, item.stream.peer_addr().unwrap(), TCP);
handshake
} else {
continue;
}
};
while item.builder.len() >= 4 {
let len = u32::from_be_bytes(item.builder[..4].try_into().unwrap());
if len + 4 > item.builder.len() as u32 {
break;
}
if len == 0 {
item.builder.split_to(4);
continue;
}
let message_bytes = item.builder.split_to((4 + len) as usize).freeze();
let msg = Message::from_bytes(message_bytes.slice(4..)).unwrap();
println!("{} => {:?}", item.stream.peer_addr().unwrap(), msg);
if !session_manager.process(info_hash, handshake.peer_id(), msg) {
exit(1);
}
}
poller.interest(&item.stream, Event::readable(event.key));
}
while let Some(queued) = session_manager.next() {
if let Some(key) = peer_map.get(&(queued.info_hash, queued.peer_id)) {
let item = &mut items[*key];
println!("{} <= {:?}", queued.addr, queued.message);
item.stream
.write_all(&queued.message.to_length_prefixed_bytes())
.unwrap();
}
}
println!("=> Running house keeping");
session_manager.house_keeping();
}
} }

@ -6,6 +6,7 @@ use std::net::SocketAddr;
use std::pin::Pin; use std::pin::Pin;
use std::str::FromStr; use std::str::FromStr;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use torment_core::peer_storage::{PeerStorage, PeerStorageHolder};
use torment_dht::host_node::HostNode; use torment_dht::host_node::HostNode;
use torment_dht::krpc::{FromBencode, Message, ToBencode}; use torment_dht::krpc::{FromBencode, Message, ToBencode};
@ -22,7 +23,8 @@ async fn main() {
let socket = UdpSocket::bind(SocketAddr::from_str("[::]:50002").unwrap()) let socket = UdpSocket::bind(SocketAddr::from_str("[::]:50002").unwrap())
.await .await
.unwrap(); .unwrap();
let mut node = HostNode::new(Default::default(), Some(50002)); let peer_storage = PeerStorage::new();
let mut node = HostNode::new(PeerStorageHolder::new(peer_storage), Some(50002));
node.add_bootstrap(SocketAddr::from_str("67.215.246.10:6881").unwrap(), None); node.add_bootstrap(SocketAddr::from_str("67.215.246.10:6881").unwrap(), None);
node.add_bootstrap( node.add_bootstrap(
SocketAddr::from_str("[2001:41d0:c:5ac:5::1]:6881").unwrap(), SocketAddr::from_str("[2001:41d0:c:5ac:5::1]:6881").unwrap(),

@ -6,13 +6,12 @@ use std::collections::{HashSet, VecDeque};
use std::convert::TryInto; use std::convert::TryInto;
use std::net::{IpAddr, SocketAddr}; use std::net::{IpAddr, SocketAddr};
use std::ops::Add; use std::ops::Add;
use std::rc::Rc;
use std::sync::RwLock;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use torment_core::infohash::v1::U160; use torment_core::infohash::v1::U160;
use torment_core::ip::IpAddrExt; use torment_core::ip::IpAddrExt;
use torment_core::peer_storage::{PeerStorageHolder, PeerStorageSource};
use torment_core::utils::{EphemeralMap, EphemeralSet}; use torment_core::utils::{EphemeralMap, EphemeralSet};
use torment_core::{ContactInfo, LookupFilter, PeerStorage}; use torment_core::{ContactInfo, LookupFilter};
#[derive(Debug)] #[derive(Debug)]
enum OpenRequest { enum OpenRequest {
@ -34,7 +33,7 @@ pub struct HostNode {
ipv6_table: Table, ipv6_table: Table,
nodes: EphemeralMap<(U160, IpAddr, u16), PeerNode>, nodes: EphemeralMap<(U160, IpAddr, u16), PeerNode>,
queue: VecDeque<(Message, SocketAddr)>, queue: VecDeque<(Message, SocketAddr)>,
peer_storage: Rc<RwLock<PeerStorage>>, peer_storage: PeerStorageHolder,
} }
#[derive(Debug)] #[derive(Debug)]
@ -92,7 +91,7 @@ impl PeerNode {
} }
impl HostNode { impl HostNode {
pub fn new(peer_storage: Rc<RwLock<PeerStorage>>, port: Option<u16>) -> HostNode { pub fn new(peer_storage: PeerStorageHolder, port: Option<u16>) -> HostNode {
let id = U160::random(); let id = U160::random();
HostNode { HostNode {
id, id,
@ -108,7 +107,7 @@ impl HostNode {
} }
pub fn from_persistent_state( pub fn from_persistent_state(
peer_storage: Rc<RwLock<PeerStorage>>, peer_storage: PeerStorageHolder,
port: Option<u16>, port: Option<u16>,
state: PersistentState, state: PersistentState,
) -> HostNode { ) -> HostNode {
@ -351,10 +350,11 @@ impl HostNode {
} }
if get_peers.values.len() > 0 { if get_peers.values.len() > 0 {
self.peer_storage self.peer_storage.add_peers(
.write() info_hash,
.unwrap() get_peers.values.clone(),
.add_peers(info_hash, get_peers.values.clone()); PeerStorageSource::DHT,
);
} }
for node in &get_peers.nodes.nodes { for node in &get_peers.nodes.nodes {
@ -502,7 +502,7 @@ impl HostNode {
get_peers get_peers
.want .want
.unwrap_or_else(|| if from.is_ipv4() { Want::N4 } else { Want::N6 }); .unwrap_or_else(|| if from.is_ipv4() { Want::N4 } else { Want::N6 });
let peers = self.peer_storage.read().unwrap().get_peers( let peers = self.peer_storage.get_peers(
get_peers.info_hash, get_peers.info_hash,
if table == Want::N4 { if table == Want::N4 {
LookupFilter::IPv4 LookupFilter::IPv4
@ -611,7 +611,7 @@ impl HostNode {
self.queue self.queue
.push_back((Message::error(t, 203, "Bad token".to_string()), from)) .push_back((Message::error(t, 203, "Bad token".to_string()), from))
} else { } else {
self.peer_storage.write().unwrap().add_peers( self.peer_storage.add_peers(
announce.info_hash, announce.info_hash,
vec![SocketAddr::new( vec![SocketAddr::new(
from.ip(), from.ip(),
@ -621,6 +621,7 @@ impl HostNode {
announce.port announce.port
}, },
)], )],
PeerStorageSource::DHT,
); );
self.queue self.queue
.push_back((Message::response_empty(self.id, t), from)) .push_back((Message::response_empty(self.id, t), from))
@ -686,13 +687,12 @@ mod test {
use crate::host_node::HostNode; use crate::host_node::HostNode;
use crate::krpc::Message; use crate::krpc::Message;
use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::rc::Rc;
use std::sync::RwLock;
use torment_core::infohash::v1::U160; use torment_core::infohash::v1::U160;
use torment_core::{ContactInfo, PeerStorage}; use torment_core::peer_storage::{PeerStorage, PeerStorageHolder};
use torment_core::ContactInfo;
fn get_host() -> HostNode { fn get_host() -> HostNode {
let peer_storage = Rc::new(RwLock::new(PeerStorage::new())); let peer_storage = PeerStorageHolder::new(PeerStorage::new());
HostNode::new(peer_storage, None) HostNode::new(peer_storage, None)
} }

@ -10,7 +10,12 @@ edition = "2018"
torment-core = { path = "../torment-core" } torment-core = { path = "../torment-core" }
torment-peer = { path = "../torment-peer" } torment-peer = { path = "../torment-peer" }
torment-storage = { path = "../torment-storage" } torment-storage = { path = "../torment-storage" }
torment-tracker = { path = "../torment-tracker" }
reqwest = { version = "0.10.8", features = ["blocking"] }
crossbeam-channel = "0.4.3"
bytes = "0.5.6" bytes = "0.5.6"
url = "2.1.1" url = "2.1.1"
lazy_static = "1.4.0" lazy_static = "1.4.0"
ring = "0.16.15" ring = "0.16.15"
polling = "0.1.6"
rand = "0.7.3"

@ -0,0 +1,2 @@
pub const MAX_OPEN_REQUESTS: usize = 250;
pub const MAX_CONNECTIONS_PER_THREAD: usize = 100;

@ -1,3 +1,6 @@
mod consts;
pub mod session_manager; pub mod session_manager;
pub mod torment_instance;
pub mod torrent_manager; pub mod torrent_manager;
pub mod tracker_manager; pub mod tracker_manager;
pub use consts::*;

@ -1,8 +1,10 @@
use crate::torrent_manager::TorrentManager; use crate::torrent_manager::TorrentManager;
use std::collections::{HashMap, VecDeque}; use crate::tracker_manager::TrackerManager;
use std::net::SocketAddr; use std::collections::{HashMap, HashSet, VecDeque};
use std::net::{IpAddr, SocketAddr};
use std::option::Option::Some; use std::option::Option::Some;
use torment_core::infohash::v1::U160; use torment_core::infohash::v1::U160;
use torment_core::peer_storage::{PeerStorage, PeerStorageHolder};
use torment_peer::message::{Handshake, Message}; use torment_peer::message::{Handshake, Message};
use torment_peer::PeerProtocol; use torment_peer::PeerProtocol;
@ -14,17 +16,60 @@ pub struct QueuedMessage {
} }
pub struct SessionManager { pub struct SessionManager {
id: U160,
torrents: HashMap<U160, TorrentManager>, torrents: HashMap<U160, TorrentManager>,
torrents_ids: HashSet<U160>,
peer_socket: HashMap<(U160, U160), SocketAddr>, peer_socket: HashMap<(U160, U160), SocketAddr>,
message_queue: VecDeque<QueuedMessage>, message_queue: VecDeque<QueuedMessage>,
tracker_manager: TrackerManager,
pub peer_storage: PeerStorageHolder,
} }
impl SessionManager { impl SessionManager {
pub fn peer_count(&self, info_hash: U160) -> usize {
self.torrents[&info_hash].peer_count()
}
pub fn torrents(&self) -> Vec<U160> {
self.torrents_ids.iter().copied().collect()
}
pub fn torrent_manager(&self, info_hash: U160) -> &TorrentManager {
&self.torrents[&info_hash]
}
pub fn id(&self) -> U160 {
self.id
}
pub fn tracker_manager(&self) -> &TrackerManager {
&self.tracker_manager
}
pub fn tracker_manager_mut(&mut self) -> &mut TrackerManager {
&mut self.tracker_manager
}
pub fn is_done(&self, info_hash: U160) -> bool {
if let Some(torrent) = self.torrents.get(&info_hash) {
torrent.is_done()
} else {
false
}
}
pub fn dump_queue(&mut self) -> VecDeque<QueuedMessage> {
std::mem::replace(&mut self.message_queue, VecDeque::new())
}
pub fn add_torrent_manager(&mut self, torrent: TorrentManager) { pub fn add_torrent_manager(&mut self, torrent: TorrentManager) {
self.torrents_ids.insert(torrent.info_hash());
self.torrents.insert(torrent.info_hash(), torrent); self.torrents.insert(torrent.info_hash(), torrent);
} }
#[allow(dead_code)]
fn remove_torrent_manager(&mut self, info_hash: U160) -> Option<TorrentManager> { fn remove_torrent_manager(&mut self, info_hash: U160) -> Option<TorrentManager> {
self.torrents_ids.remove(&info_hash);
self.torrents.remove(&info_hash) self.torrents.remove(&info_hash)
} }
@ -46,7 +91,8 @@ impl SessionManager {
pub fn process(&mut self, torrent: U160, peer_id: U160, message: Message) -> bool { pub fn process(&mut self, torrent: U160, peer_id: U160, message: Message) -> bool {
if let Some(torrent_manager) = self.torrents.get_mut(&torrent) { if let Some(torrent_manager) = self.torrents.get_mut(&torrent) {
if torrent_manager.process(peer_id, message) { if torrent_manager.process(peer_id, message) {
while let Some((peer_id, message)) = torrent_manager.next() { let queue = torrent_manager.dump_queue();
for (peer_id, message) in queue {
if let Some(addr) = self.peer_socket.get(&(peer_id, torrent)).copied() { if let Some(addr) = self.peer_socket.get(&(peer_id, torrent)).copied() {
self.message_queue.push_back(QueuedMessage { self.message_queue.push_back(QueuedMessage {
message, message,
@ -66,9 +112,14 @@ impl SessionManager {
} }
} }
pub fn new() -> SessionManager { pub fn new(ip: IpAddr, port: u16, peer_id: U160) -> SessionManager {
let peer_storage = PeerStorageHolder::new(PeerStorage::new());
SessionManager { SessionManager {
id: peer_id,
torrents: Default::default(), torrents: Default::default(),
tracker_manager: TrackerManager::new(ip, port, peer_id, peer_storage.clone()),
peer_storage,
torrents_ids: Default::default(),
peer_socket: Default::default(), peer_socket: Default::default(),
message_queue: Default::default(), message_queue: Default::default(),
} }
@ -79,8 +130,33 @@ impl SessionManager {
} }
pub fn house_keeping(&mut self) { pub fn house_keeping(&mut self) {
for (_, torrent) in &mut self.torrents { for (info_hash, torrent) in &mut self.torrents {
torrent.house_keeping(); torrent.house_keeping();
let queue = torrent.dump_queue();
for (peer_id, message) in queue {
if let Some(addr) = self.peer_socket.get(&(peer_id, *info_hash)).copied() {
self.message_queue.push_back(QueuedMessage {
message,
addr,
peer_id,
info_hash: *info_hash,
});
}
}
}
}
pub fn announce(&mut self) {
println!("Announcing torrents...");
for info_hash in self.torrents_ids.clone() {
let trackers = self.torrents[&info_hash].trackers().clone();
for tracker_tier in trackers {
for tracker in tracker_tier {
self.tracker_manager
.announce(tracker, &self.torrents[&info_hash]);
}
}
} }
} }
} }

@ -0,0 +1,432 @@
#![allow(dead_code)]
use crate::session_manager::SessionManager;
use bytes::{Buf, Bytes, BytesMut};
use crossbeam_channel::{bounded, unbounded, Receiver, Sender};
use polling::{Event, Poller};
use rand::seq::SliceRandom;
use std::cmp::max;
use std::collections::{HashMap, HashSet, VecDeque};
use std::convert::TryInto;
use std::io::{ErrorKind, Read, Write};
use std::net::{IpAddr, Ipv6Addr, SocketAddr, TcpListener, TcpStream};
use std::ops::Add;
use std::option::Option::Some;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::thread::JoinHandle;
use std::time::{Duration, Instant};
use torment_core::infohash::v1::U160;
use torment_core::LookupFilter;
use torment_peer::message::{Handshake, Message};
use torment_peer::PeerProtocol;
use torment_peer::PeerProtocol::TCP;
pub struct TormentInstance {
id: U160,
port: u16,
dht_enabled: bool,
connections: OffshoreConnections,
tracking: HashMap<SocketAddr, (U160, U160)>,
handshake_sent: HashSet<SocketAddr>,
session: SessionManager,
}
struct OffshoreConnections {
control_receiver: Receiver<ControlMessage>,
thread: JoinHandle<()>,
thread_sender: Sender<ControlMessage>,
}
enum ControlMessage {
Data(SocketAddr, Bytes),
Handshake(Handshake, SocketAddr, PeerProtocol),
Own(TcpStream, SocketAddr),
Disown(SocketAddr),
}
struct Stream {
buffer: BytesMut,
addr: SocketAddr,
stream: TcpStream,
handshake_received: bool,
id: usize,
handshake_sent: bool,
}
fn connection_pool_thread(
port: u16,
control_sender: Sender<ControlMessage>,
thread_receiver: Receiver<ControlMessage>,
) {
let mut streams: HashMap<SocketAddr, Stream> = HashMap::new();
let counter = AtomicUsize::new(0);
let mut index: HashMap<usize, SocketAddr> = HashMap::new();
let listener =
TcpListener::bind(SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port)).unwrap();
let (notify_sender, notify_receiver) = bounded(1);
let (notify_restart_sender, notify_restart_receiver) = bounded(1);
let poller = Arc::new(Poller::new().unwrap());
let thread_receiver_clone = thread_receiver.clone();
let poller_clone = Arc::clone(&poller);
std::thread::Builder::new()
.name(format!("io/notify"))
.spawn(move || {
for message in thread_receiver {
// println!("Received channel message, notifying wait");
poller_clone.notify().unwrap();
notify_sender.send(message).unwrap();
notify_restart_receiver.recv().unwrap();
}
});
poller.insert(&listener).unwrap();
poller.interest(&listener, Event::readable(usize::MAX));
let mut buffer = [0u8; 1024 * 1024];
loop {
let mut events = vec![];
match poller.wait(&mut events, None) {
Ok(_) => {}
Err(err) if err.kind() == ErrorKind::Interrupted => {}
Err(err) => Err(err).unwrap(),
}
// println!("Events => {:?}", events);
for event in events {
if !event.readable {
continue;
}
if event.key == usize::MAX {
while let Ok((stream, addr)) = listener.accept() {
let id = counter.fetch_add(1, Ordering::AcqRel);
stream.set_nodelay(true).unwrap();
poller.insert(&stream).unwrap();
poller.interest(&stream, Event::readable(id)).unwrap();
streams.insert(
addr,
Stream {
addr,
handshake_received: false,
handshake_sent: false,
buffer: BytesMut::new(),
stream,
id,
},
);
index.insert(id, addr);
}
poller
.interest(&listener, Event::readable(usize::MAX))
.unwrap();
continue;
}
let addr = index[&event.key];
let size = streams
.get_mut(&addr)
.unwrap()
.stream
.read(&mut buffer)
.unwrap_or(0);
if size == 0 {
control_sender.send(ControlMessage::Disown(addr)).unwrap();
index.remove(&event.key);
if let Some(stream) = streams.remove(&addr) {
poller.remove(&stream.stream).unwrap();
}
println!("{} == Disconnected", addr);
continue;
}
// println!("{} => {} bytes", addr, size);
let stream = streams.get_mut(&addr).unwrap();
stream.buffer.extend_from_slice(&buffer[0..size]);
if stream.buffer.len() >= 68 && !stream.handshake_received {
let handshake = Handshake::from_bytes(stream.buffer.split_to(68).freeze());
let handshake = match handshake {
Err(_) => {
control_sender.send(ControlMessage::Disown(addr)).unwrap();
index.remove(&event.key);
if let Some(stream) = streams.remove(&addr) {
poller.remove(&stream.stream).unwrap();
}
continue;
}
Ok(hand) => hand,
};
println!("{} => {:?}", stream.addr, handshake);
control_sender
.send(ControlMessage::Handshake(handshake, stream.addr, TCP))
.unwrap();
stream.handshake_received = true;
}
poller
.interest(&stream.stream, Event::readable(event.key))
.unwrap();
if !stream.handshake_received {
continue;
}
while stream.buffer.len() >= 4 {
let len = u32::from_be_bytes(stream.buffer[..4].try_into().unwrap());
if len == 0 {
stream.buffer.advance(4);
continue;
}
if len + 4 > stream.buffer.len() as u32 {
break;
}
let mut message_bytes = stream.buffer.split_to((4 + len) as usize);
message_bytes.advance(4);
control_sender
.send(ControlMessage::Data(stream.addr, message_bytes.freeze()))
.unwrap();
}
}
let mut msgs = vec![];
if let Ok(message) = notify_receiver.try_recv() {
msgs.push(message);
}
while let Ok(message) = thread_receiver_clone.try_recv() {
msgs.push(message);
}
for message in msgs {
match message {
ControlMessage::Data(target, data) => {
let ok = if let Some(stream) = streams.get_mut(&target) {
if stream.stream.write_all(&data).is_err() {
control_sender
.send(ControlMessage::Disown(stream.addr))
.unwrap();
false
} else {
true
}
} else {
true
};
if !ok {
if let Some(stream) = streams.remove(&target) {
poller.remove(&stream.stream).unwrap();
index.remove(&stream.id);
println!("{} == Disconnected", stream.addr);
}
}
}
ControlMessage::Disown(addr) => {
if let Some(stream) = streams.remove(&addr) {
index.remove(&stream.id);
poller.remove(&stream.stream).unwrap();
}
}
ControlMessage::Own(stream, addr) => {
let id = counter.fetch_add(1, Ordering::AcqRel);
poller.insert(&stream).unwrap();
poller.interest(&stream, Event::readable(id)).unwrap();
streams.insert(
addr,
Stream {
addr,
handshake_received: false,
handshake_sent: true,
buffer: BytesMut::new(),
stream,
id,
},
);
index.insert(id, addr);
}
_ => {}
};
}
notify_restart_sender.send(()).unwrap();
}
}
impl TormentInstance {
pub fn new(port: u16, session: SessionManager) -> TormentInstance {
let (control_sender, control_receiver) = unbounded();
let (thread_sender, thread_receiver) = unbounded();
let thread = std::thread::Builder::new()
.name(format!("io/poll"))
.spawn(move || connection_pool_thread(port, control_sender, thread_receiver))
.unwrap();
TormentInstance {
id: session.id(),
port,
dht_enabled: false,
connections: OffshoreConnections {
control_receiver,
thread,
thread_sender,
},
tracking: Default::default(),
handshake_sent: Default::default(),
session,
}
}
pub fn tracker_logic(&mut self) {
self.session.announce();
self.session.tracker_manager_mut().house_keeping();
for torrent in self.session.torrents() {
let peer_count = self.session.peer_count(torrent);
if peer_count > 25 {
continue;
}
let mut peers = self
.session
.peer_storage
.get_peers(torrent, LookupFilter::All);
println!("have {} peers for {}", peers.len(), torrent);
peers.shuffle(&mut rand::thread_rng());
let mut todo = max(0, 25 - peer_count);
while todo > 0 && peers.len() > 0 {
let peer = peers.pop().unwrap();
if self.tracking.contains_key(&peer) {
continue;
}
let thread_sender = self.connections.thread_sender.clone();
let peer_id = self.id;
self.handshake_sent.insert(peer);
std::thread::spawn(move || {
// println!("Trying connection with {}", peer);
if let Ok(mut stream) = TcpStream::connect(peer) {
stream.set_nodelay(true).unwrap();
stream
.write_all(&Handshake::new(peer_id, torrent).to_bytes())
.unwrap();
// println!("{} <= Connecting", peer);
thread_sender
.send(ControlMessage::Own(stream, peer))
.unwrap();
}
});
todo -= 1;
}
}
}
pub fn logic_loop(&mut self) {
let mut next_house_keeping = Instant::now();
loop {
if next_house_keeping < Instant::now() {
self.session.house_keeping();
self.tracker_logic();
next_house_keeping = Instant::now().add(Duration::from_secs(10));
}
while next_house_keeping > Instant::now() {
if let Ok(message) = self
.connections
.control_receiver
.recv_timeout(Duration::from_secs(1))
{
match message {
ControlMessage::Handshake(handshake, addr, protocol) => {
let handshake_sent = self.handshake_sent.remove(&addr);
self.tracking
.insert(addr, (handshake.peer_id(), handshake.info_hash()));
if self.session.handshake(handshake, addr, protocol) {
if !handshake_sent {
self.connections
.thread_sender
.send(ControlMessage::Data(
addr,
Bytes::from(
Handshake::new(self.id, handshake.info_hash())
.to_bytes()
.to_vec(),
),
))
.unwrap();
}
}
}
ControlMessage::Data(sock_addr, data) => {
if let Some((peer_id, info_hash)) =
self.tracking.get(&sock_addr).copied()
{
let message = Message::from_bytes(data);
// println!("{} => {:?}", sock_addr, message);
if message.is_err() {
continue;
}
self.session.process(info_hash, peer_id, message.unwrap());
} else {
// println!("{} => ????", sock_addr);
}
}
_ => {}
}
}
let queue: HashMap<_, VecDeque<_>> =
self.session
.dump_queue()
.into_iter()
.fold(HashMap::new(), |mut map, item| {
map.entry(item.addr).or_default().push_back(item.message);
map
});
for (addr, messages) in queue {
let mut bytes = BytesMut::new();
for msg in messages {
// println!("{} <= {:?}", addr, msg);
bytes.extend_from_slice(&msg.to_length_prefixed_bytes());
}
self.connections
.thread_sender
.send(ControlMessage::Data(addr, bytes.freeze()))
.unwrap()
}
}
}
}
}

@ -1,15 +1,19 @@
use crate::tracker_manager::{TrackerId, TrackerManager}; use crate::tracker_manager::{TrackerId, TrackerManager};
use crate::MAX_OPEN_REQUESTS;
use bytes::Bytes; use bytes::Bytes;
use ring::digest::{digest, SHA1_FOR_LEGACY_USE_ONLY}; use ring::digest::{digest, SHA1_FOR_LEGACY_USE_ONLY};
use std::cmp::max;
use std::collections::{HashMap, HashSet, VecDeque}; use std::collections::{HashMap, HashSet, VecDeque};
use std::net::SocketAddr; use std::net::SocketAddr;
use std::ops::Index; use std::ops::Add;
use std::option::Option::Some; use std::option::Option::Some;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::{Duration, Instant};
use torment_core::infohash::v1::U160; use torment_core::infohash::v1::U160;
use torment_core::metainfo::{MetaInfo, Torrent}; use torment_core::metainfo::{MetaInfo, Torrent};
use torment_core::Bitfield; use torment_core::utils::EphemeralSet;
use torment_peer::message::{Handshake, Message, PieceMessage, SelectionMessage}; use torment_core::{Bitfield, REQUEST_SIZE};
use torment_peer::message::{Handshake, Message, PieceMessage};
use torment_peer::{Peer, PeerProtocol}; use torment_peer::{Peer, PeerProtocol};
use torment_storage::{StorageMap, ToStorageMap}; use torment_storage::{StorageMap, ToStorageMap};
@ -46,15 +50,53 @@ pub struct TorrentManager {
peers: HashMap<U160, Peer>, peers: HashMap<U160, Peer>,
queue: VecDeque<(U160, Message)>, queue: VecDeque<(U160, Message)>,
storage_map: StorageMap, storage_map: StorageMap,
requests_queue: VecDeque<u32>,
open_requests: HashMap<u32, EphemeralSet<u32>>,
uploaded: usize,
downloaded: usize,
} }
pub const REQUEST_SIZE: usize = 16384;
impl TorrentManager { impl TorrentManager {
pub fn peer_count(&self) -> usize {
self.peers.len()
}
pub fn trackers(&self) -> &Vec<Vec<TrackerId>> {
&self.trackers
}
pub fn info_hash(&self) -> U160 { pub fn info_hash(&self) -> U160 {
self.info_hash self.info_hash
} }
pub fn is_done(&self) -> bool {
self.bitfield.all()
}
pub fn uploaded(&self) -> usize {
self.uploaded
}
pub fn downloaded(&self) -> usize {
self.downloaded
}
pub fn bytes_left(&self) -> i64 {
let mut size = self.storage_map.size() as i64;
let piece_length = self.storage_map.get_piece_length(0) as i64;
for i in 0..self.bitfield.size() as u32 {
if self.bitfield.get(i) {
size -= piece_length;
}
}
max(0, size)
}
pub fn dump_queue(&mut self) -> VecDeque<(U160, Message)> {
std::mem::replace(&mut self.queue, VecDeque::new())
}
pub fn from_torrent( pub fn from_torrent(
torrent: Torrent, torrent: Torrent,
target: TorrentTarget, target: TorrentTarget,
@ -84,9 +126,13 @@ impl TorrentManager {
torrent: Some(torrent.clone()), torrent: Some(torrent.clone()),
}), }),
storage_map: torrent.to_storage_map(&target.path, target.is_base_path), storage_map: torrent.to_storage_map(&target.path, target.is_base_path),
requests_queue: Default::default(),
target, target,
peers: HashMap::new(), peers: HashMap::new(),
queue: Default::default(), queue: Default::default(),
open_requests: Default::default(),
uploaded: 0,
downloaded: 0,
} }
} }
@ -107,7 +153,8 @@ impl TorrentManager {
} }
let meta_info = self.meta_info(); let meta_info = self.meta_info();
let peer = Peer::new(addr, protocol, handshake, meta_info); let mut peer = Peer::new(addr, protocol, handshake, meta_info);
peer.send_bitfield(&self.bitfield);
self.peers.insert(peer.id(), peer); self.peers.insert(peer.id(), peer);
true true
@ -127,12 +174,12 @@ impl TorrentManager {
pub fn process(&mut self, peer_id: U160, message: Message) -> bool { pub fn process(&mut self, peer_id: U160, message: Message) -> bool {
let mut queue = vec![]; let mut queue = vec![];
if let Message::Piece(piece) = &message {
self.downloaded += piece.piece.len()
}
let ok = if let Some(peer) = self.peers.get_mut(&peer_id) { let ok = if let Some(peer) = self.peers.get_mut(&peer_id) {
if peer.process(message) { if peer.process(message) {
while let Some(message) = peer.next() {
self.queue.push_back((peer_id, message));
}
while let Some(piece) = peer.next_piece() { while let Some(piece) = peer.next_piece() {
if self.storage_map.has_piece(piece.index() as usize) { if self.storage_map.has_piece(piece.index() as usize) {
continue; continue;
@ -151,6 +198,10 @@ impl TorrentManager {
} }
} }
while let Some(_) = peer.next_have() {
// Something i guess
}
while let Some(piece_request) = peer.next_request() { while let Some(piece_request) = peer.next_request() {
if !self.bitfield.get(piece_request.index()) { if !self.bitfield.get(piece_request.index()) {
continue; continue;
@ -175,6 +226,9 @@ impl TorrentManager {
)) ))
} }
self.queue_requests(peer_id);
let msgs = self.peers.get_mut(&peer_id).unwrap().dump_queue();
self.queue_messages(peer_id, msgs);
true true
} else { } else {
false false
@ -183,6 +237,7 @@ impl TorrentManager {
false false
}; };
// offload to io thread?
for have in queue { for have in queue {
let piece_hash = self.meta_info().hash(have); let piece_hash = self.meta_info().hash(have);
let piece_data = self.storage_map.read_piece(have).unwrap(); let piece_data = self.storage_map.read_piece(have).unwrap();
@ -203,7 +258,6 @@ impl TorrentManager {
if !self.peers[&key].has_piece(have as u32) && self.peers[&key].we_choked() { if !self.peers[&key].has_piece(have as u32) && self.peers[&key].we_choked() {
self.peers.get_mut(&key).unwrap().set_we_choked(false); self.peers.get_mut(&key).unwrap().set_we_choked(false);
self.queue.push_back((key, Message::Unchoke));
} }
} }
} }
@ -211,65 +265,133 @@ impl TorrentManager {
ok ok
} }
fn queue_messages(&mut self, peer_id: U160, messages: VecDeque<Message>) {
for msg in messages {
if let Message::Piece(piece) = &msg {
self.uploaded += piece.piece.len();
}
self.queue.push_back((peer_id, msg));
}
}
pub fn next(&mut self) -> Option<(U160, Message)> { pub fn next(&mut self) -> Option<(U160, Message)> {
self.queue.pop_front() self.queue.pop_front()
} }
pub fn house_keeping(&mut self) { fn get_next_request_piece_for_peer(&mut self, peer: &U160) -> Option<(u32, u32, u32)> {
let mut peers = self let mut queue_index = 0;
.peers while let Some(index) = self.requests_queue.get(queue_index).copied() {
.iter() if !self.peers[peer].has_piece(index) {
.filter_map(|(_, peer)| { queue_index += 1;
if peer.has()1
if peer.is_choked() {
None
} else {
Some(peer.id())
}
})
.collect::<HashSet<_>>();
let pieces = self.meta_info().pieces() as u32;
for i in 0u32..pieces {
if self.bitfield.get(i) {
continue; continue;
} }
let length = self.storage_map.get_piece_length(i as usize); let bits = self.storage_map.get_bits_in_pieces(index as usize);
let mut offset = 0; if self.open_requests.get(&index).map_or(0, |set| set.len()) >= bits {
self.requests_queue.remove(0);
}
for peer in &peers.clone() { let set = self
if !self.peers[peer].has_piece(i) { .open_requests
.entry(index)
.or_insert(EphemeralSet::new());
for i in 0..bits as u32 {
let bit_offset = i * REQUEST_SIZE as u32;
if set.contains(&bit_offset) {
continue; continue;
} }
while offset < length && self.peers[peer].count_open_requests() < 25 { set.insert(bit_offset, Instant::now().add(Duration::from_secs(10)));
if !self.storage_map.has_piece_bit(i as usize, offset) { return Some((
let request_length = if (offset + REQUEST_SIZE) > length { index,
length - offset bit_offset,
} else { self.storage_map
REQUEST_SIZE .get_bit_length(index as usize, bit_offset as usize),
}; ));
}
}
let msg = SelectionMessage::new(i, offset as u32, request_length as u32); None
}
if self.peers.get_mut(peer).unwrap().requested(msg) { pub fn house_keeping(&mut self) {
self.queue.push_back((*peer, Message::Request(msg))); let mut map: HashMap<u32, usize> = HashMap::new();
} let mut interested_peers = HashSet::new();
} if self.peers.len() <= 0 {
return;
}
offset += REQUEST_SIZE; let mut done = 0;
}
if self.peers[peer].count_open_requests() >= 25 { for i in 0..self.meta_info().pieces() as u32 {
peers.remove(peer); if self.bitfield.get(i) {
} done += 1;
// Don't need to queue anything
continue;
}
if offset >= length { let mut entries = 0;
break; for (_, peer) in &mut self.peers {
if peer.has_piece(i) {
entries += 1;
interested_peers.insert(peer.id());
} }
} }
if entries == 0 {
// Can't queue entries without peers
continue;
}
map.insert(i, entries);
}
println!(
"{}%",
(done as f64 / self.meta_info().pieces() as f64) * 100f64
);
let mut pieces: Vec<_> = map.keys().copied().collect();
pieces.sort_by_key(|piece| map[piece]);
self.requests_queue = VecDeque::from(pieces);
for (_, peer) in &mut self.peers {
if !interested_peers.contains(&peer.id()) || self.requests_queue.len() == 0 {
peer.lost_interest();
continue;
}
peer.interested();
if peer.is_choked() {
// Don't send requests if they're choked
continue;
}
}
let peer_ids = self.peers.keys().copied().collect::<Vec<_>>();
for peer_id in peer_ids {
self.queue_requests(peer_id);
let message_queue = self.peers.get_mut(&peer_id).unwrap().dump_queue();
self.queue_messages(peer_id, message_queue);
}
}
fn queue_requests(&mut self, peer_id: U160) {
if self.peers[&peer_id].is_choked() {
return;
}
let amount = MAX_OPEN_REQUESTS - self.peers[&peer_id].count_open_requests();
for _ in 0..amount {
if let Some((index, offset, length)) = self.get_next_request_piece_for_peer(&peer_id) {
self.peers
.get_mut(&peer_id)
.unwrap()
.request(index, offset, length);
} else {
break;
}
} }
} }
} }

@ -1,20 +1,94 @@
use crate::torrent_manager::TorrentManager;
use crossbeam_channel::{unbounded, Receiver, Sender};
use reqwest::blocking::Client;
use reqwest::header::HeaderMap;
use std::collections::HashMap; use std::collections::HashMap;
use std::net::IpAddr;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::RwLock;
use std::thread::JoinHandle;
use torment_core::infohash::v1::U160;
use torment_core::peer_storage::PeerStorageHolder;
use torment_tracker::{
block_do_announce, Tracker, TrackerAnnounceRequest, TrackerAnnounceResponse,
};
use url::Url; use url::Url;
pub struct TrackerManager { pub struct TrackerManager {
id: U160,
ip: IpAddr,
port: u16,
counter: AtomicUsize, counter: AtomicUsize,
trackers: HashMap<usize, Tracker>, trackers: HashMap<usize, RwLock<Tracker>>,
url_index: HashMap<String, usize>, url_index: HashMap<String, usize>,
peer_storage: PeerStorageHolder,
tracker_offshore_state: TrackerOffshoreState,
} }
pub struct Tracker { struct TrackerOffshoreState {
url: Url, threads: Vec<JoinHandle<()>>,
tracker_request_sender: Sender<(usize, TrackerAnnounceRequest)>,
tracker_request_receiver: Receiver<(usize, TrackerAnnounceRequest)>,
tracker_response_sender: Sender<(usize, TrackerAnnounceResponse)>,
tracker_response_receiver: Receiver<(usize, TrackerAnnounceResponse)>,
}
impl TrackerOffshoreState {
fn new() -> TrackerOffshoreState {
let (req_send, req_recv) = unbounded();
let (resp_send, resp_recv) = unbounded();
TrackerOffshoreState {
threads: vec![],
tracker_request_sender: req_send,
tracker_request_receiver: req_recv,
tracker_response_sender: resp_send,
tracker_response_receiver: resp_recv,
}
}
} }
pub type TrackerId = usize; pub type TrackerId = usize;
fn tracker_thread(
input: Receiver<(usize, TrackerAnnounceRequest)>,
output: Sender<(usize, TrackerAnnounceResponse)>,
) {
let mut map = HeaderMap::new();
map.insert(
reqwest::header::USER_AGENT,
format!("eater's Torment/{}", env!("CARGO_PKG_VERSION"))
.parse()
.unwrap(),
);
let client = Client::builder().default_headers(map).build().unwrap();
for (tracker_id, req) in input {
output
.send((tracker_id, block_do_announce(req, &client)))
.unwrap();
}
}
impl TrackerManager { impl TrackerManager {
pub fn new(
ip: IpAddr,
port: u16,
peer_id: U160,
peer_storage: PeerStorageHolder,
) -> TrackerManager {
TrackerManager {
id: peer_id,
ip,
port,
counter: Default::default(),
trackers: Default::default(),
url_index: Default::default(),
peer_storage,
tracker_offshore_state: TrackerOffshoreState::new(),
}
}
pub fn get_tracker_id(&mut self, url: &Url) -> Option<TrackerId> { pub fn get_tracker_id(&mut self, url: &Url) -> Option<TrackerId> {
// dht is not a tracker bye // dht is not a tracker bye
if url.scheme() == "dht" { if url.scheme() == "dht" {
@ -28,7 +102,68 @@ impl TrackerManager {
let new_id = self.counter.fetch_add(1, Ordering::AcqRel); let new_id = self.counter.fetch_add(1, Ordering::AcqRel);
self.url_index.insert(url_str.to_string(), new_id); self.url_index.insert(url_str.to_string(), new_id);
self.trackers.insert(
new_id,
RwLock::new(Tracker::new(url.clone(), self.peer_storage.clone())),
);
Some(new_id) Some(new_id)
} }
pub fn announce(&mut self, tracker_id: usize, torrent: &TorrentManager) {
let id = torrent.info_hash();
{
let tracker = &self.trackers[&tracker_id].read().unwrap();
if !tracker.needs_update(id) {
return;
}
}
let tracker = &mut self.trackers[&tracker_id].write().unwrap();
println!(
"Queueing announce {} on {}",
torrent.info_hash(),
tracker.url()
);
let req = tracker.create_update_request(
id,
self.id,
self.ip,
self.port,
torrent.uploaded(),
torrent.downloaded(),
torrent.bytes_left(),
);
self.tracker_offshore_state
.tracker_request_sender
.send((tracker_id, req))
.unwrap()
}
pub fn house_keeping(&mut self) {
while self.tracker_offshore_state.threads.len() < 5 {
let sender = self.tracker_offshore_state.tracker_response_sender.clone();
let receiver = self.tracker_offshore_state.tracker_request_receiver.clone();
self.tracker_offshore_state.threads.push(
std::thread::Builder::new()
.name(format!(
"tracker/{}",
self.tracker_offshore_state.threads.len()
))
.spawn(|| tracker_thread(receiver, sender))
.unwrap(),
);
}
while let Ok((tracker_id, resp)) = self
.tracker_offshore_state
.tracker_response_receiver
.try_recv()
{
self.trackers[&tracker_id].write().unwrap().process(resp);
}
}
} }

@ -48,6 +48,7 @@ pub struct Peer {
requested_queue: HashSet<SelectionMessage>, requested_queue: HashSet<SelectionMessage>,
received_pieces: VecDeque<PieceMessage>, received_pieces: VecDeque<PieceMessage>,
queue: VecDeque<Message>, queue: VecDeque<Message>,
have_queue: VecDeque<u32>,
} }
impl Peer { impl Peer {
@ -55,6 +56,10 @@ impl Peer {
self.id self.id
} }
pub fn dump_queue(&mut self) -> VecDeque<Message> {
std::mem::replace(&mut self.queue, VecDeque::new())
}
pub fn new( pub fn new(
address: SocketAddr, address: SocketAddr,
protocol: PeerProtocol, protocol: PeerProtocol,
@ -74,6 +79,7 @@ impl Peer {
requested_queue: Default::default(), requested_queue: Default::default(),
received_pieces: Default::default(), received_pieces: Default::default(),
queue: Default::default(), queue: Default::default(),
have_queue: Default::default(),
} }
} }
@ -87,7 +93,10 @@ impl Peer {
Message::Unchoke => self.their_state.chocked = false, Message::Unchoke => self.their_state.chocked = false,
Message::Interested => self.their_state.interested = true, Message::Interested => self.their_state.interested = true,
Message::NotInterested => self.their_state.interested = false, Message::NotInterested => self.their_state.interested = false,
Message::Have(nr) => self.has.set(nr), Message::Have(nr) => {
self.has.set(nr);
self.have_queue.push_back(nr);
}
Message::Bitfield(bitfield) => self.has.add(bitfield), Message::Bitfield(bitfield) => self.has.add(bitfield),
Message::Request(selection) => { Message::Request(selection) => {
self.request_queue.push_back(selection); self.request_queue.push_back(selection);
@ -130,17 +139,68 @@ impl Peer {
} }
pub fn set_we_choked(&mut self, choked: bool) { pub fn set_we_choked(&mut self, choked: bool) {
if self.our_state.chocked != choked {
self.queue.push_back(if choked {
Message::Choke
} else {
Message::Unchoke
})
}
self.our_state.chocked = choked; self.our_state.chocked = choked;
} }
pub fn send_bitfield(&mut self, bitfield: &Bitfield) {
self.queue.push_back(Message::Bitfield(bitfield.clone()));
}
pub fn set_we_interested(&mut self, interested: bool) { pub fn set_we_interested(&mut self, interested: bool) {
self.our_state.interested = interested; self.our_state.interested = interested;
} }
pub fn request(&mut self, index: u32, offset: u32, length: u32) {
let msg = SelectionMessage::new(index, offset, length);
if self.requested(msg) {
self.queue.push_back(Message::Request(msg));
}
}
pub fn requested(&mut self, selection: SelectionMessage) -> bool { pub fn requested(&mut self, selection: SelectionMessage) -> bool {
self.requested_queue.insert(selection) self.requested_queue.insert(selection)
} }
pub fn unchoke(&mut self) {
if !self.our_state.chocked {
return;
}
self.queue.push_back(Message::Unchoke);
}
pub fn choke(&mut self) {
if self.our_state.chocked {
return;
}
self.queue.push_back(Message::Choke);
}
pub fn interested(&mut self) {
if self.our_state.interested {
return;
}
self.our_state.interested = true;
self.queue.push_back(Message::Interested);
}
pub fn lost_interest(&mut self) {
if !self.our_state.interested {
return;
}
self.queue.push_back(Message::NotInterested);
}
pub fn next_piece(&mut self) -> Option<PieceMessage> { pub fn next_piece(&mut self) -> Option<PieceMessage> {
self.received_pieces.pop_front() self.received_pieces.pop_front()
} }
@ -157,6 +217,7 @@ impl Peer {
pub fn addr(&self) -> SocketAddr { pub fn addr(&self) -> SocketAddr {
self.address self.address
} }
pub fn has_piece(&self, index: u32) -> bool { pub fn has_piece(&self, index: u32) -> bool {
self.has.get(index as u32) self.has.get(index as u32)
} }
@ -164,12 +225,8 @@ impl Peer {
pub fn next(&mut self) -> Option<Message> { pub fn next(&mut self) -> Option<Message> {
self.queue.pop_front() self.queue.pop_front()
} }
}
#[cfg(test)] pub fn next_have(&mut self) -> Option<u32> {
mod tests { self.have_queue.pop_front()
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
} }
} }

@ -49,7 +49,7 @@ impl Message {
Message::Have(u32::from_be_bytes(bytes[1..5].try_into().unwrap())) Message::Have(u32::from_be_bytes(bytes[1..5].try_into().unwrap()))
} }
5 => Message::Bitfield(Bitfield::new(bytes.slice(1..))), 5 => Message::Bitfield(Bitfield::new(bytes.slice(1..), bytes.len() - 1)),
6 | 8 => { 6 | 8 => {
if bytes.len() < 13 { if bytes.len() < 13 {

@ -1,20 +1,16 @@
use bytes::{Bytes, BytesMut};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use remem::{ItemGuard, Pool}; use remem::{ItemGuard, Pool};
use std::borrow::Borrow;
use std::cmp::min; use std::cmp::min;
use std::collections::{BTreeMap, HashMap, HashSet}; use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use std::fs::{DirBuilder, File, OpenOptions}; use std::fs::{DirBuilder, File, OpenOptions};
use std::io; use std::io;
use std::io::{Read, Seek, SeekFrom, Write}; use std::io::{Read, Seek, SeekFrom, Write};
use std::ops::{Add, Deref, Range};
use std::path::{Path, PathBuf, MAIN_SEPARATOR}; use std::path::{Path, PathBuf, MAIN_SEPARATOR};
use std::rc::Rc; use std::rc::Rc;
use std::sync::Mutex; use std::sync::Mutex;
use std::time::{Duration, Instant}; use torment_core::metainfo::{MetaInfoObject, Torrent};
use torment_core::metainfo::{MetaInfo, MetaInfoObject, Torrent}; use torment_core::REQUEST_SIZE;
use torment_core::utils::EphemeralMap;
lazy_static! { lazy_static! {
static ref MEMORY_POOL: Pool<Vec<u8>> = Pool::new(|| vec![0u8; 4194304]); static ref MEMORY_POOL: Pool<Vec<u8>> = Pool::new(|| vec![0u8; 4194304]);
@ -24,6 +20,7 @@ lazy_static! {
pub struct StorageMap { pub struct StorageMap {
base_path: PathBuf, base_path: PathBuf,
piece_length: usize, piece_length: usize,
bits_in_piece: usize,
pieces: usize, pieces: usize,
open_files: HashMap<usize, Rc<Mutex<File>>>, open_files: HashMap<usize, Rc<Mutex<File>>>,
buffer: HashMap<usize, StoragePiece>, buffer: HashMap<usize, StoragePiece>,
@ -33,14 +30,14 @@ pub struct StorageMap {
pub struct StoragePiece { pub struct StoragePiece {
index: usize, index: usize,
pieces: usize, bits: usize,
ranges: HashSet<usize>, ranges: HashSet<usize>,
buffer: ItemGuard<'static, Vec<u8>>, buffer: ItemGuard<'static, Vec<u8>>,
} }
impl StoragePiece { impl StoragePiece {
fn is_complete(&self) -> bool { fn is_complete(&self) -> bool {
self.pieces == self.ranges.len() self.bits == self.ranges.len()
} }
} }
@ -55,7 +52,6 @@ impl Debug for StoragePiece {
impl StorageMap { impl StorageMap {
fn get_offsets(&self, offset: usize, length: usize) -> Vec<usize> { fn get_offsets(&self, offset: usize, length: usize) -> Vec<usize> {
let x = 0..1;
let end = offset + length; let end = offset + length;
let mut range = self.mapping.range(offset..); let mut range = self.mapping.range(offset..);
let mut items = vec![]; let mut items = vec![];
@ -142,7 +138,7 @@ impl StorageMap {
pub fn read_piece(&mut self, index: usize) -> io::Result<Vec<u8>> { pub fn read_piece(&mut self, index: usize) -> io::Result<Vec<u8>> {
let mut bytes = vec![0; self.get_piece_length(index)]; let mut bytes = vec![0; self.get_piece_length(index)];
self.read(index, 0, &mut bytes); self.read(index, 0, &mut bytes)?;
Ok(bytes) Ok(bytes)
} }
@ -166,21 +162,14 @@ impl StorageMap {
let item = if let Some(item) = self.buffer.get_mut(&index) { let item = if let Some(item) = self.buffer.get_mut(&index) {
item item
} else { } else {
let request_size = 2usize.pow(14); let bits = self.get_bits_in_pieces(index);
let piece_length = self.get_piece_length(index);
let pieces = (piece_length / request_size)
+ if piece_length % request_size > 0 {
1
} else {
0
};
self.buffer.insert(index, { self.buffer.insert(index, {
StoragePiece { StoragePiece {
index, index,
ranges: Default::default(), ranges: Default::default(),
buffer: MEMORY_POOL.get(), buffer: MEMORY_POOL.get(),
pieces, bits,
} }
}); });
@ -209,16 +198,40 @@ impl StorageMap {
} }
pub fn get_piece_length(&self, index: usize) -> usize { pub fn get_piece_length(&self, index: usize) -> usize {
if index + 1 == self.pieces { if index + 1 < self.pieces {
let len = (self.size % self.piece_length); return self.piece_length;
if len == 0 { }
self.piece_length
let len = self.size % self.piece_length;
if len == 0 {
self.piece_length
} else {
len
}
}
pub fn get_bits_in_pieces(&self, index: usize) -> usize {
if index + 1 < self.pieces {
return self.bits_in_piece;
}
let piece_length = self.get_piece_length(index);
(piece_length / REQUEST_SIZE)
+ if piece_length % REQUEST_SIZE > 0 {
1
} else { } else {
len 0
} }
} else { }
self.piece_length
pub fn get_bit_length(&self, index: usize, offset: usize) -> u32 {
let piece_length = self.get_piece_length(index);
if offset + REQUEST_SIZE <= piece_length {
return REQUEST_SIZE as u32;
} }
(piece_length % REQUEST_SIZE) as u32
} }
pub fn has_piece_bit(&self, index: usize, offset: usize) -> bool { pub fn has_piece_bit(&self, index: usize, offset: usize) -> bool {
@ -330,15 +343,25 @@ impl StorageMapBuilder {
} }
pub fn build(self) -> StorageMap { pub fn build(self) -> StorageMap {
let pieces = (self.offset / self.piece_length)
+ if self.offset % self.piece_length > 0 {
1
} else {
0
};
let bits_in_piece = (self.piece_length / REQUEST_SIZE)
+ if (self.piece_length % REQUEST_SIZE) > 0 {
1
} else {
0
};
StorageMap { StorageMap {
base_path: self.base_path, base_path: self.base_path,
piece_length: self.piece_length, piece_length: self.piece_length,
pieces: (self.offset / self.piece_length) pieces,
+ if self.offset % self.piece_length > 0 { bits_in_piece,
1
} else {
0
},
open_files: Default::default(), open_files: Default::default(),
buffer: Default::default(), buffer: Default::default(),
mapping: self.items, mapping: self.items,

@ -0,0 +1,13 @@
[package]
name = "torment-tracker"
version = "0.1.0"
authors = ["eater <=@eater.me>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
torment-core = { path = "../torment-core" }
torment-bencode = { path = "../torment-bencode" }
reqwest = { version = "0.10", features = ["blocking"] }
url = "2.1.1"

@ -0,0 +1,269 @@
use reqwest::blocking::Client;
use reqwest::{Method, Url};
use std::collections::HashMap;
use std::convert::TryInto;
use std::net::{IpAddr, SocketAddr};
use std::ops::Add;
use std::str::FromStr;
use std::time::{Duration, Instant};
use torment_bencode::BencodeValue;
use torment_core::infohash::v1::U160;
use torment_core::infohash::InfoHashCapable;
use torment_core::peer_storage::{PeerStorageHolder, PeerStorageSource};
use torment_core::CompactContact;
pub struct Tracker {
url: Url,
is_udp: bool,
// last_scrape: Option<Instant>,
last_announce: HashMap<U160, Instant>,
requested_interval: Duration,
failure: HashMap<U160, String>,
peer_storage: PeerStorageHolder,
// supports_scrape: bool,
}
pub enum TrackerAnnounceRequest {
HTTP(Url, U160),
UDP(Url, U160),
}
#[derive(Debug)]
pub struct TrackerAnnounceResponse {
info_hash: U160,
// url: Url,
interval: Option<Duration>,
source: PeerStorageSource,
result: Result<Vec<SocketAddr>, String>,
}
impl Tracker {
pub fn url(&self) -> &Url {
&self.url
}
pub fn new(url: Url, peer_storage: PeerStorageHolder) -> Tracker {
Tracker {
is_udp: url.scheme() == "udp",
url,
last_announce: HashMap::new(),
requested_interval: Duration::from_secs(30 * 60),
failure: HashMap::new(),
peer_storage,
}
}
pub fn needs_update(&self, info_hash: U160) -> bool {
self.last_announce
.get(&info_hash)
.map(|inst| inst.add(self.requested_interval) < Instant::now())
.unwrap_or(true)
}
pub fn process(&mut self, response: TrackerAnnounceResponse) {
match response.result {
Ok(peers) => {
self.peer_storage
.add_peers(response.info_hash, peers, response.source);
}
Err(err) => {
self.failure.insert(response.info_hash, err);
}
}
if let Some(interval) = response.interval {
self.requested_interval = interval;
} else {
self.requested_interval = Duration::from_secs(60 * 30);
}
}
pub fn create_update_request(
&mut self,
info_hash: U160,
peer_id: U160,
ip: IpAddr,
port: u16,
uploaded: usize,
downloaded: usize,
left: i64,
) -> TrackerAnnounceRequest {
if self.is_udp {
return TrackerAnnounceRequest::UDP(self.url.clone(), info_hash);
}
let mut url = self.url.clone();
info_hash.to_byte_array();
{
let mut query = url.query_pairs_mut();
query.append_pair("ip", &format!("{}", ip));
query.append_pair("port", &format!("{}", port));
query.append_pair("uploaded", &format!("{}", uploaded));
query.append_pair("downloaded", &format!("{}", downloaded));
query.append_pair("left", &format!("{}", left));
}
let encoded_info_hash = url::form_urlencoded::byte_serialize(&info_hash.to_bytes()).fold(
String::new(),
|mut coll, curr| {
coll.push_str(curr);
coll
},
);
let encoded_peer_id = url::form_urlencoded::byte_serialize(&peer_id.to_bytes()).fold(
String::new(),
|mut coll, curr| {
coll.push_str(curr);
coll
},
);
let query = url.query().unwrap_or("");
url.set_query(Some(&format!(
"{}&info_hash={}&peer_id={}",
query, encoded_info_hash, encoded_peer_id
)));
self.last_announce.insert(info_hash, Instant::now());
TrackerAnnounceRequest::HTTP(url, info_hash)
}
}
pub fn block_do_announce(
request: TrackerAnnounceRequest,
client: &Client,
) -> TrackerAnnounceResponse {
let (url, info_hash) = match request {
TrackerAnnounceRequest::HTTP(url, info_hash) => (url, info_hash),
TrackerAnnounceRequest::UDP(url, info_hash) => {
return TrackerAnnounceResponse {
info_hash,
interval: None,
source: PeerStorageSource::Tracker(url.clone()),
// url,
result: Err(format!("UDP trackers not supported")),
};
}
};
println!("Announcing {} on {}", info_hash, url);
let res = block_do_http_response_parse(url.clone(), client);
TrackerAnnounceResponse {
info_hash,
source: PeerStorageSource::Tracker(url.clone()),
// url,
interval: res.as_ref().map(|x| x.0).ok(),
result: res.map(|x| x.1),
}
}
pub fn block_do_http_response_parse(
url: Url,
http_client: &Client,
) -> Result<(Duration, Vec<SocketAddr>), String> {
let resp = match http_client.request(Method::GET, url).send() {
Ok(resp) => resp,
Err(err) => {
return Err(format!("{}", err));
}
};
let body = resp.bytes();
if let Err(err) = body {
return Err(format!("{}", err));
}
let body = body.unwrap();
let body = BencodeValue::decode(body);
if let Err(err) = body {
return Err(format!("Bencoding error: {}", err));
}
let body = body.unwrap();
let body = body.dict();
if body.is_none() {
return Err(format!("Tracker gave invalid response"));
}
let body = body.unwrap();
if let Some(failure) = body.get(b"failure reason") {
let failure = failure.string().and_then(|x| x.ok()).unwrap_or(format!(
"Tracker failed to respond with valid failure, but did respond with a failure"
));
return Err(failure);
}
let interval = body
.get(b"interval")
.and_then(|item| item.int())
.unwrap_or(30 * 60);
let interval = Duration::from_secs(interval as u64);
let peers = body.get(b"peers");
if peers.is_none() {
return Ok((interval, vec![]));
}
let peers = peers.unwrap();
let peers: Vec<SocketAddr> = match peers {
BencodeValue::List(old) => {
let mut buffer = vec![];
for item in old.iter() {
let dict = if let Some(dict) = item.dict() {
dict
} else {
continue;
};
let ip = if let Some(BencodeValue::Bytes(ip)) = dict.get(b"ip") {
ip.clone()
} else {
continue;
};
let ip = if let Some(ip) = IpAddr::from_str(&String::from_utf8_lossy(&ip)).ok() {
ip
} else {
continue;
};
let port: u16 = if let Some(BencodeValue::Int(nr)) = dict.get(b"port") {
if *nr < u16::MAX as i64 {
if let Ok(port) = (*nr).try_into() {
port
} else {
continue;
}
} else {
continue;
}
} else {
continue;
};
buffer.push(SocketAddr::new(ip, port));
}
buffer
}
BencodeValue::Bytes(compact) => compact
.chunks(6)
.filter_map(|x| SocketAddr::from_compact_contact(x).ok())
.collect(),
_ => {
return Err(format!("Tracker failed to respond with valid response"));
}
};
Ok((interval, peers))
}
Loading…
Cancel
Save