Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ef453427b | |||
| 256d6e68bc | |||
| df9c8899b2 | |||
| ec23b722f1 |
Generated
+118
-329
@@ -4,9 +4,9 @@ version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.21"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@@ -19,62 +19,44 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.13"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.7"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.4"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.10"
|
||||
version = "3.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
|
||||
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.49"
|
||||
version = "4.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f"
|
||||
checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -82,9 +64,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.49"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730"
|
||||
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -94,9 +76,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.49"
|
||||
version = "4.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
|
||||
checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -106,25 +88,87 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.6"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
|
||||
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "3.0.0"
|
||||
version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e"
|
||||
checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
@@ -133,96 +177,52 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.1"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
name = "rayon"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -234,47 +234,17 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "snaferrous"
|
||||
version = "0.1.0"
|
||||
version = "1.2.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"colored",
|
||||
"tokio",
|
||||
"crossbeam",
|
||||
"rayon",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
@@ -283,48 +253,20 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.19"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
@@ -342,19 +284,13 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -363,24 +299,6 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
@@ -389,132 +307,3 @@ checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm 0.53.1",
|
||||
"windows_aarch64_msvc 0.53.1",
|
||||
"windows_i686_gnu 0.53.1",
|
||||
"windows_i686_gnullvm 0.53.1",
|
||||
"windows_i686_msvc 0.53.1",
|
||||
"windows_x86_64_gnu 0.53.1",
|
||||
"windows_x86_64_gnullvm 0.53.1",
|
||||
"windows_x86_64_msvc 0.53.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
+3
-2
@@ -1,12 +1,13 @@
|
||||
[package]
|
||||
name = "snaferrous"
|
||||
version = "0.1.0"
|
||||
version = "1.2.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.41", features = ["derive"] }
|
||||
colored = "3.0.0"
|
||||
tokio = { version = "1.48.0", features = ["full"] }
|
||||
crossbeam = "0.8.4"
|
||||
rayon = "1.12.0"
|
||||
walkdir = "2.5.0"
|
||||
|
||||
[profile.release]
|
||||
|
||||
@@ -22,6 +22,12 @@ Options:
|
||||
|
||||
-t, --targets <TARGETS> specific targets. should be comma separated.
|
||||
|
||||
-f, --filter_targets <targets> specific targets that should be ingored. Comma separated.
|
||||
|
||||
-l, --local scan the current hot's file system (defaults to false)
|
||||
|
||||
-d --disable_network disable network file discovery
|
||||
|
||||
-v, --verbose echo all found files to the console, regardless of keyword matching. (all files will still be saved to the log file)
|
||||
|
||||
-h, --help Print help (see more with '--help')
|
||||
|
||||
@@ -1,425 +0,0 @@
|
||||
/*
|
||||
Author: Kevin (Kaged Pyro) Gunter
|
||||
Purpose: I got tired of snaffler getting caught, so I rewrote it in rust, which edrs have trouble detecting.
|
||||
|
||||
*/
|
||||
use clap::Parser;
|
||||
use std::fmt::Debug;
|
||||
use std::fs;
|
||||
use std::fs::read_to_string;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::ops::Index;
|
||||
use std::path::PathBuf;
|
||||
use std::process::exit;
|
||||
use std::process::Command;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use tokio;
|
||||
use tokio::sync::mpsc::{channel, Sender, Receiver};
|
||||
use colored::Colorize;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = Some("finds shares, but its written in rust which sometimes gets past EDR!"))]
|
||||
struct Args{
|
||||
#[arg(short, long, help = "path to save output file Defaults to not saving output.")]
|
||||
outfile: Option<PathBuf>,
|
||||
|
||||
#[arg(short, long, help = "number of threads to use, default to 10. \nNote thre thread count will be doubled, one set for share finder tasks, and one set for file and infor finding tasks.")]
|
||||
threads: Option<usize>,
|
||||
|
||||
#[arg(short, long, help = "specific targets. should be comma separated.")]
|
||||
targets: Option<String>,
|
||||
}
|
||||
|
||||
struct ShareFinder{
|
||||
id: usize,
|
||||
tx: Sender<Message>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Message{
|
||||
source: MessageType,
|
||||
destination: MessageType,
|
||||
content: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
enum MessageType{
|
||||
ShareMessage,
|
||||
InfoMessage,
|
||||
ControlMessage,
|
||||
}
|
||||
|
||||
|
||||
async fn find_shares(task: ShareFinder, mut rx: Receiver<Message>){
|
||||
println!("{} share task started!", task.id);
|
||||
let ping_recv = rx.recv().await;
|
||||
if ping_recv.is_some(){
|
||||
let message = Message{source: MessageType::ShareMessage, destination: MessageType::ControlMessage, content: String::from("pong!")};
|
||||
task.tx.send(message).await.unwrap();
|
||||
}
|
||||
loop{
|
||||
if rx.capacity() == 0{
|
||||
println!("rx is full for share finder {}", task.id);
|
||||
}
|
||||
let rx_res = rx.recv().await;
|
||||
if rx_res.is_some(){
|
||||
let computer = rx_res.unwrap().content;
|
||||
if computer == String::from("||DONE||"){
|
||||
let message = Message{source: MessageType::ShareMessage, destination: MessageType::ControlMessage, content: format!("{}:||DONE||", task.id)};
|
||||
task.tx.send(message).await.unwrap();
|
||||
break;
|
||||
}
|
||||
println!("scanning {}", computer);
|
||||
let share_list_res = Command::new("net").arg("view").arg(computer.clone()).arg("/all").output();
|
||||
let mut error_string = String::new();
|
||||
let mut success_string = String::new();
|
||||
if share_list_res.is_ok(){
|
||||
let output = share_list_res.unwrap();
|
||||
if output.stdout.len() > 0{
|
||||
success_string = String::from_utf8_lossy(&output.stdout).to_string();
|
||||
}
|
||||
|
||||
if output.stderr.len() > 0{
|
||||
error_string = String::from_utf8_lossy(&output.stderr).to_string();
|
||||
}
|
||||
}
|
||||
else{
|
||||
error_string = share_list_res.err().unwrap().to_string();
|
||||
}
|
||||
if error_string.len() > 0{
|
||||
eprintln!("{}", "Error listing shares!".red());
|
||||
eprint!("{}", error_string.red());
|
||||
}
|
||||
else if success_string.len() > 0{
|
||||
for line in success_string.lines(){
|
||||
if line.contains("Disk"){
|
||||
let share_name = line.split_whitespace().collect::<Vec<&str>>()[0];
|
||||
let share_path = format!("\\\\{}\\{}", computer, share_name);
|
||||
let message = Message{source: MessageType::ShareMessage, destination: MessageType::InfoMessage, content: format!("{}:{}", task.id, share_path)};
|
||||
task.tx.send(message).await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn find_info(task: ShareFinder, mut rx: Receiver<Message>){
|
||||
println!("{} file task started!", task.id);
|
||||
let ping_recv = rx.recv().await;
|
||||
if ping_recv.is_some(){
|
||||
let message = Message{source: MessageType::ShareMessage, destination: MessageType::ControlMessage, content: String::from("pong!")};
|
||||
task.tx.send(message).await.unwrap();
|
||||
}
|
||||
let files_to_read = vec![
|
||||
".txt",
|
||||
".ini",
|
||||
".xml",
|
||||
".json",
|
||||
".config",
|
||||
".conf",
|
||||
".bat",
|
||||
".cmd",
|
||||
".sql",
|
||||
".ps1",
|
||||
".py",
|
||||
".vbscript"
|
||||
];
|
||||
|
||||
let interesting_info = vec![
|
||||
"password",
|
||||
"pass",
|
||||
"user",
|
||||
"api",
|
||||
"key",
|
||||
"credit card",
|
||||
"cc",
|
||||
"ssn",
|
||||
"social Security",
|
||||
"tax",
|
||||
"i9",
|
||||
"it",
|
||||
"identified",
|
||||
"username",
|
||||
];
|
||||
loop{
|
||||
let rx_res = rx.recv().await;
|
||||
if rx_res.is_some(){
|
||||
let message = rx_res.unwrap();
|
||||
let message_vec: Vec<&str> = message.content.split(":").collect();
|
||||
let path = message_vec[1];
|
||||
if path.contains("||DONE||"){
|
||||
let done_message = Message{source: MessageType::InfoMessage, destination: MessageType::ControlMessage, content: format!("{}:||DONE||", task.id)};
|
||||
task.tx.send(done_message).await.unwrap();
|
||||
}
|
||||
for entry_res in walkdir::WalkDir::new(path){
|
||||
if entry_res.is_ok(){
|
||||
let entry = entry_res.unwrap();
|
||||
let file_path = entry.into_path();
|
||||
let mut file_name = String::new();
|
||||
let mut file_content = String::new();
|
||||
if file_path.file_name().is_some(){
|
||||
file_name = file_path.file_name().unwrap().to_string_lossy().to_string();
|
||||
}
|
||||
for extension in &files_to_read{
|
||||
if file_name.contains(extension){
|
||||
let file_content_res = read_to_string(&file_path);
|
||||
if file_content_res.is_ok(){
|
||||
file_content = file_content_res.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
for thing in &interesting_info{
|
||||
if file_name.contains(thing) || file_content.contains(thing){
|
||||
let message = Message{source: MessageType::InfoMessage, destination: MessageType::ControlMessage, content: format!("{}:Keyword match at {}", task.id, file_path.display())};
|
||||
task.tx.send(message).await.unwrap();
|
||||
}
|
||||
else{
|
||||
let message = Message{source: MessageType::InfoMessage, destination: MessageType::ControlMessage, content: format!("{}:file found at {}", task.id, file_path.display())};
|
||||
task.tx.send(message).await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[tokio::main]
|
||||
async fn main(){
|
||||
let args = Args::parse();
|
||||
let mut outfile = PathBuf::new();
|
||||
let mut file_threads = 1;
|
||||
let mut share_threads = 1;
|
||||
let mut save = false;
|
||||
let mut computers = Vec::new();
|
||||
if args.outfile.is_some(){
|
||||
outfile = args.outfile.unwrap();
|
||||
save = true;
|
||||
}
|
||||
if args.threads.is_some(){
|
||||
let threads = args.threads.unwrap() / 2;
|
||||
file_threads = threads;
|
||||
share_threads = threads;
|
||||
}
|
||||
if args.targets.is_some(){
|
||||
println!("gathering the targets you gave me.");
|
||||
let targets = args.targets.unwrap();
|
||||
if targets.contains(","){
|
||||
let split_targets: Vec<&str> = targets.split(",").collect();
|
||||
for target in split_targets{
|
||||
computers.push(target.to_string());
|
||||
}
|
||||
}
|
||||
else{
|
||||
computers.push(targets);
|
||||
}
|
||||
}
|
||||
else{
|
||||
println!("no targets given, proceeding with domain computer enumeration...");
|
||||
println!("finding computers...");
|
||||
let command_string = String::from("net group \"domain computers\" /domain");
|
||||
let mut temp_file = fs::File::create("./temp.bat").unwrap();
|
||||
write!(temp_file, "{}", command_string).unwrap();
|
||||
let computer_res = Command::new(".\\temp.bat").output();
|
||||
let mut error_string = String::new();
|
||||
let mut success_string = String::new();
|
||||
fs::remove_file("./temp.bat").unwrap();
|
||||
if computer_res.is_ok(){
|
||||
let output = computer_res.unwrap();
|
||||
if output.stdout.len() > 0{
|
||||
success_string = String::from_utf8_lossy(&output.stdout).to_string();
|
||||
}
|
||||
else if output.stderr.len() > 0{
|
||||
error_string = String::from_utf8_lossy(&output.stderr).to_string();
|
||||
}
|
||||
}
|
||||
else{
|
||||
error_string = computer_res.err().unwrap().to_string();
|
||||
}
|
||||
if error_string.len() > 0{
|
||||
eprintln!("{}", "error getting computers!".red());
|
||||
eprintln!("{}", error_string.red());
|
||||
exit(1);
|
||||
}
|
||||
if success_string.len() > 0{
|
||||
for line in success_string.lines(){
|
||||
if line.contains("$"){
|
||||
let words:Vec<&str> = line.split_whitespace().collect();
|
||||
for word in words{
|
||||
let mut computer_name = word.to_string();
|
||||
computer_name.pop();
|
||||
println!("{} {}", "found".green(), computer_name.green());
|
||||
computers.push(computer_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if share_threads > computers.len(){
|
||||
share_threads = computers.len();
|
||||
//file_threads = computers.len();
|
||||
}
|
||||
let mut share_handles = Vec::new();
|
||||
let mut file_handles = Vec::new();
|
||||
println!("computer enumeration finished, starting task finder threads...");
|
||||
let (maintx, mut mainrx) = channel(1024);
|
||||
let mut share_tasks = Vec::new();
|
||||
let mut share_txes = Vec::new();
|
||||
let mut file_tasks = Vec::new();
|
||||
let mut file_txes = Vec::new();
|
||||
for id in 0..share_threads{
|
||||
println!("starting share task {}...", id);
|
||||
let (share_tx,share_rx) = channel(1);
|
||||
let new_share_task = ShareFinder{id, tx: maintx.clone()};
|
||||
share_handles.push(tokio::spawn(find_shares(new_share_task, share_rx)));
|
||||
share_tasks.push(id);
|
||||
share_txes.push(share_tx.clone());
|
||||
let ping_message = Message{source: MessageType::ControlMessage, destination: MessageType::ShareMessage, content: String::from("ping!")};
|
||||
share_tx.send(ping_message).await.unwrap();
|
||||
loop{
|
||||
let rx_recv = mainrx.recv().await;
|
||||
if rx_recv.is_some(){
|
||||
let message = rx_recv.unwrap();
|
||||
if message.content == String::from("pong!"){
|
||||
println!("{} ready!", id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
println!("didn't recieve file pong from {}", id);
|
||||
}
|
||||
}
|
||||
for id in 0..file_threads{
|
||||
println!("starting file task {}...", id);
|
||||
let (file_tx, file_rx) = channel(1);
|
||||
let new_file_task = ShareFinder{id, tx: maintx.clone()};
|
||||
file_handles.push(tokio::spawn(find_info(new_file_task, file_rx)));
|
||||
file_tasks.push(id);
|
||||
file_txes.push(file_tx.clone());
|
||||
let ping_message = Message{source: MessageType::ControlMessage, destination: MessageType::ShareMessage, content: String::from("ping!")};
|
||||
file_tx.send(ping_message).await.unwrap();
|
||||
loop{
|
||||
let rx_recv = mainrx.recv().await;
|
||||
if rx_recv.is_some(){
|
||||
let message = rx_recv.unwrap();
|
||||
if message.content == String::from("pong!"){
|
||||
println!("{} ready!", id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
println!("didn't recieve file pong from {}", id);
|
||||
}
|
||||
}
|
||||
let mut current_computer = 0;
|
||||
let mut shares_finished = false;
|
||||
let mut files_finished = false;
|
||||
let mut file_buffer = Vec::new();
|
||||
let mut finished_counter = 0;
|
||||
let mut empty_counter = 0;
|
||||
let mut handled_lines = Vec::new();
|
||||
loop {
|
||||
if files_finished && shares_finished{
|
||||
exit(0);
|
||||
}
|
||||
if !mainrx.is_empty(){
|
||||
finished_counter = 0;
|
||||
empty_counter = 0;
|
||||
let rx_res = mainrx.recv().await;
|
||||
if rx_res.is_some(){
|
||||
let message = rx_res.unwrap();
|
||||
match message.destination{
|
||||
MessageType::ControlMessage => {
|
||||
let message_vec: Vec<&str> = message.content.split(":").collect();
|
||||
let _id = message_vec[0];
|
||||
let message_content = message_vec[1].to_string();
|
||||
match message_content{
|
||||
_ => {
|
||||
if !handled_lines.contains(&message_content){
|
||||
if save{
|
||||
let open_res = OpenOptions::new().append(true).create(true).open(&outfile);
|
||||
if open_res.is_ok(){
|
||||
let mut file = open_res.unwrap();
|
||||
let write_res = write!(file, "{}\n", message_content);
|
||||
if write_res.is_err(){
|
||||
eprintln!("{}", "error writing to outfile!".red());
|
||||
eprintln!("{}", write_res.err().unwrap().to_string().red());
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("{}", message_content.green());
|
||||
handled_lines.push(message_content);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
MessageType::InfoMessage => {
|
||||
file_buffer.push(message.content);
|
||||
}
|
||||
MessageType::ShareMessage => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut sent = false;
|
||||
if !shares_finished{
|
||||
for tx in &share_txes{
|
||||
if tx.capacity() > 0{
|
||||
let message = Message{source: MessageType::ShareMessage, destination: MessageType::ShareMessage, content: computers[current_computer].clone()};
|
||||
tx.send(message).await.unwrap();
|
||||
sent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if sent{
|
||||
current_computer +=1;
|
||||
if current_computer == computers.len() {
|
||||
shares_finished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if shares_finished{
|
||||
if file_buffer.len() == 0{
|
||||
empty_counter += 1;
|
||||
println!("empty counter: {}", empty_counter);
|
||||
}
|
||||
if empty_counter >= 100{
|
||||
finished_counter +=1;
|
||||
println!("finished counter: {}", finished_counter);
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
}
|
||||
}
|
||||
if file_buffer.len() > 0{
|
||||
let mut sent_index = Vec::new();
|
||||
empty_counter = 0;
|
||||
finished_counter = 0;
|
||||
let mut current_tx = 0;
|
||||
for index in 0 .. file_buffer.len() - 1{
|
||||
let mut sent = false;
|
||||
let message = Message{source: MessageType::ControlMessage, destination: MessageType::InfoMessage, content: file_buffer[index].clone()};
|
||||
if file_txes[current_tx].capacity()> 0{
|
||||
file_txes[current_tx].send(message).await.unwrap();
|
||||
sent = true;
|
||||
}
|
||||
else{
|
||||
current_tx += 1;
|
||||
if current_tx == file_txes.len(){
|
||||
current_tx = 0;
|
||||
}
|
||||
}
|
||||
if sent{
|
||||
sent_index.push(index);
|
||||
}
|
||||
}
|
||||
for index in sent_index{
|
||||
file_buffer.remove(index);
|
||||
}
|
||||
}
|
||||
if finished_counter == 10{
|
||||
files_finished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+448
-291
@@ -1,16 +1,19 @@
|
||||
use clap::Parser;
|
||||
use colored::Colorize;
|
||||
use crossbeam::queue::SegQueue;
|
||||
use std::fmt::Debug;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::fs::read_to_string;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::io::stdin;
|
||||
use std::path::PathBuf;
|
||||
use std::process::exit;
|
||||
use std::process::Command;
|
||||
use colored::Colorize;
|
||||
use tokio;
|
||||
use tokio::sync::mpsc::{channel, Sender};
|
||||
|
||||
use std::process::exit;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
/*
|
||||
Author: Kevin (Kaged Pyro) Gunter
|
||||
Purpose: I got tired of snaffler getting caught, so I rewrote it in rust, which edrs have trouble detecting.
|
||||
@@ -33,7 +36,11 @@ struct Args{
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = Some("finds shares, but its written in rust which sometimes gets past EDR!"))]
|
||||
struct Args {
|
||||
#[arg(short, long, help = "path to save output file Defaults to not saving output.")]
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
help = "path to save output file Defaults to not saving output."
|
||||
)]
|
||||
outfile: Option<PathBuf>,
|
||||
|
||||
#[arg(long, help = "number of threads to use, default to 10.")]
|
||||
@@ -42,43 +49,60 @@ struct Args{
|
||||
#[arg(short, long, help = "specific targets. should be comma separated.")]
|
||||
targets: Option<String>,
|
||||
|
||||
#[arg(short, long, help = "echo all found files to the console, regardless of keyword matching. (all files will still be saved to the log file)")]
|
||||
#[arg(
|
||||
long,
|
||||
help = "specific targets that should be ignored. comma separated."
|
||||
)]
|
||||
filter_targets: Option<String>,
|
||||
|
||||
#[arg(
|
||||
long,
|
||||
help = "specific folder paths or file names that should be ignored. comma separated."
|
||||
)]
|
||||
filter_data: Option<String>,
|
||||
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
help = "echo all found files to the console, regardless of keyword matching. (all files will still be saved to the log file)"
|
||||
)]
|
||||
verbose: bool,
|
||||
|
||||
#[arg(short, long, help = "scan the current host's files")]
|
||||
local: bool,
|
||||
|
||||
#[arg(short, long, help = "disable network discovery")]
|
||||
disable_network: bool,
|
||||
}
|
||||
|
||||
struct AppState {
|
||||
share_que: Arc<SegQueue<FinderTask>>,
|
||||
file_que: Arc<SegQueue<FinderTask>>,
|
||||
info_que: Arc<SegQueue<FinderTask>>,
|
||||
outfile: Option<Arc<Mutex<File>>>,
|
||||
continue_without_saveing: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct FinderTask {
|
||||
id: usize,
|
||||
target: String,
|
||||
tasktype: TaskType,
|
||||
findings: Vec<Finding>,
|
||||
outfile: Option<Arc<Mutex<File>>>,
|
||||
verbose: bool,
|
||||
filter: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Finding{
|
||||
path: String,
|
||||
keyword: Option<bool>
|
||||
}
|
||||
|
||||
struct Message{
|
||||
source: usize,
|
||||
tasktype: TaskType,
|
||||
finding: Option<Finding>,
|
||||
task_finished: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum TaskType{
|
||||
Share,
|
||||
File,
|
||||
Info,
|
||||
}
|
||||
|
||||
|
||||
async fn task_handler(id: usize, current_task: FinderTask, tx: Sender<Message>){
|
||||
match current_task.tasktype{
|
||||
impl FinderTask {
|
||||
fn run(&mut self) {
|
||||
match self.tasktype {
|
||||
TaskType::Share => {
|
||||
println!("scanning {}", current_task.target);
|
||||
let share_list_res = Command::new("net").arg("view").arg(current_task.target.clone()).arg("/all").output();
|
||||
println!("scanning {}", self.target);
|
||||
let share_list_res = Command::new("net")
|
||||
.arg("view")
|
||||
.arg(self.target.clone())
|
||||
.arg("/all")
|
||||
.output();
|
||||
let mut success_string = String::new();
|
||||
if share_list_res.is_ok() {
|
||||
let output = share_list_res.unwrap();
|
||||
@@ -91,36 +115,52 @@ async fn task_handler(id: usize, current_task: FinderTask, tx: Sender<Message>){
|
||||
for line in success_string.lines() {
|
||||
if line.contains("Disk") {
|
||||
let share_name = line.split_whitespace().collect::<Vec<&str>>()[0];
|
||||
let share_path = format!("\\\\{}\\{}", current_task.target, share_name);
|
||||
let share_path = format!("\\\\{}\\{}", self.target, share_name);
|
||||
if !sent_lines.contains(&share_path) {
|
||||
sent_lines.push(share_path.clone());
|
||||
let finding = Finding{path: share_path, keyword: None};
|
||||
let message = Message{source: id, tasktype: TaskType::Share, finding: Some(finding), task_finished: false};
|
||||
tx.send(message).await.unwrap();
|
||||
let finding = Finding { path: share_path };
|
||||
println!("share found: {}", finding.path);
|
||||
if let Some(outfile) = self.outfile.clone() {
|
||||
if let Ok(mut file_lock) = outfile.lock() {
|
||||
if let Err(e) = file_lock.write(
|
||||
format!("share found: {}\r\n", finding.path).as_bytes(),
|
||||
) {
|
||||
eprintln!("Error writing outfile! {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
self.findings.push(finding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TaskType::File => {
|
||||
let mut sent_lines = Vec::new();
|
||||
for entry_res in walkdir::WalkDir::new(current_task.target.clone()){
|
||||
if entry_res.is_ok(){
|
||||
let entry = entry_res.unwrap();
|
||||
walkdir::WalkDir::new(self.target.clone())
|
||||
.into_iter()
|
||||
.for_each(|e| {
|
||||
if let Ok(entry) = e {
|
||||
if self
|
||||
.filter
|
||||
.iter()
|
||||
.any(|f| !entry.path().to_string_lossy().contains(f))
|
||||
{
|
||||
let file_path = entry.into_path();
|
||||
if file_path.file_name().is_some(){
|
||||
let file_path_string = file_path.display().to_string();
|
||||
if !sent_lines.contains(&file_path_string){
|
||||
sent_lines.push(file_path_string.clone());
|
||||
let finding = Finding{path: file_path_string, keyword: None};
|
||||
let message = Message{source: id, tasktype: TaskType::File, finding: Some(finding), task_finished: false};
|
||||
tx.send(message).await.unwrap();
|
||||
}
|
||||
let new_finding = Finding {
|
||||
path: file_path.display().to_string(),
|
||||
};
|
||||
if !self.findings.iter().any(|f| f.path == new_finding.path) {
|
||||
if self.verbose {
|
||||
println!("file found: {}", new_finding.path);
|
||||
}
|
||||
self.findings.push(new_finding);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
TaskType::Info => {
|
||||
if !self.filter.iter().any(|f| self.target.contains(f)) {
|
||||
let files_to_read = vec![
|
||||
".txt",
|
||||
".ini",
|
||||
@@ -133,7 +173,7 @@ async fn task_handler(id: usize, current_task: FinderTask, tx: Sender<Message>){
|
||||
".sql",
|
||||
".ps1",
|
||||
".py",
|
||||
".vbscript"
|
||||
".vbscript",
|
||||
];
|
||||
|
||||
let interesting_info = vec![
|
||||
@@ -144,59 +184,108 @@ async fn task_handler(id: usize, current_task: FinderTask, tx: Sender<Message>){
|
||||
"key",
|
||||
"credit card",
|
||||
" cc ",
|
||||
"_cc_",
|
||||
"-cc-",
|
||||
"ssn",
|
||||
" ssn ",
|
||||
"_ssn_",
|
||||
"-ssn-",
|
||||
"social Security",
|
||||
"tax",
|
||||
"i9",
|
||||
" it ",
|
||||
"_it_",
|
||||
"-it-",
|
||||
"identified",
|
||||
"username",
|
||||
"admin",
|
||||
"administrator",
|
||||
];
|
||||
let mut file_content = String::new();
|
||||
for extension in &files_to_read{
|
||||
if current_task.target.contains(extension){
|
||||
let file_content_res = read_to_string(¤t_task.target);
|
||||
if file_content_res.is_ok(){
|
||||
file_content = file_content_res.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut sent_lines = Vec::new();
|
||||
for thing in &interesting_info{
|
||||
let file_name = current_task.target.clone();
|
||||
if file_name.contains(thing) || file_content.contains(thing){
|
||||
let sent_line = format!("keyword {}", file_name);
|
||||
if !sent_lines.contains(&sent_line){
|
||||
sent_lines.push(sent_line);
|
||||
let finding = Finding{path: file_name, keyword: Some(true)};
|
||||
let message = Message{source: id, tasktype: TaskType::Info, finding: Some(finding), task_finished: false};
|
||||
tx.send(message).await.unwrap();
|
||||
}
|
||||
}
|
||||
else{
|
||||
let sent_line = format!("file {}", file_name);
|
||||
if !sent_lines.contains(&sent_line){
|
||||
sent_lines.push(sent_line);
|
||||
let finding = Finding{path: file_name, keyword: Some(false)};
|
||||
let message = Message{source:id, tasktype: TaskType::Info, finding: Some(finding),task_finished: false};
|
||||
tx.send(message).await.unwrap();
|
||||
let file_path = PathBuf::from(self.target.clone());
|
||||
if file_path.is_file() {
|
||||
if file_path.exists() {
|
||||
let mut read = false;
|
||||
if let Some(filename) = file_path.file_name() {
|
||||
if files_to_read
|
||||
.iter()
|
||||
.any(|r| filename.to_string_lossy().contains(r))
|
||||
{
|
||||
if let Ok(file_content) = read_to_string(&file_path) {
|
||||
read = true;
|
||||
if interesting_info.iter().any(|i| {
|
||||
filename.to_string_lossy().contains(i)
|
||||
|| file_content.contains(i)
|
||||
}) && !self
|
||||
.filter
|
||||
.iter()
|
||||
.any(|f| file_path.to_string_lossy().contains(f))
|
||||
{
|
||||
if !self.findings.iter().any(|f| f.path == self.target)
|
||||
{
|
||||
self.findings.push(Finding {
|
||||
path: self.target.clone(),
|
||||
});
|
||||
println!("keyword match: {}", self.target);
|
||||
if let Some(outfile) = self.outfile.clone() {
|
||||
if let Ok(mut file_lock) = outfile.lock() {
|
||||
if let Err(e) = file_lock.write(
|
||||
format!(
|
||||
"keyword match: {}\n",
|
||||
self.target.clone()
|
||||
)
|
||||
.as_bytes(),
|
||||
) {
|
||||
println!("error writing to log {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !read {
|
||||
if interesting_info
|
||||
.iter()
|
||||
.any(|i| filename.to_string_lossy().contains(i))
|
||||
{
|
||||
println!("keyword match: {}", self.target);
|
||||
if let Some(outfile) = self.outfile.clone() {
|
||||
if let Ok(mut file_lock) = outfile.lock() {
|
||||
if let Err(e) = file_lock.write(
|
||||
format!(
|
||||
"keyword match: {}\n",
|
||||
self.target.clone()
|
||||
)
|
||||
.as_bytes(),
|
||||
) {
|
||||
println!("error writing to log {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let message = Message{source: id, tasktype: TaskType::Share, finding: None, task_finished: true};
|
||||
let send_res = tx.send(message).await;
|
||||
if send_res.is_ok(){
|
||||
send_res.unwrap();
|
||||
}
|
||||
else{
|
||||
println!("{}", send_res.err().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main(){
|
||||
#[derive(Clone)]
|
||||
struct Finding {
|
||||
path: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum TaskType {
|
||||
Share,
|
||||
File,
|
||||
Info,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
print! {
|
||||
"
|
||||
██▒▒
|
||||
@@ -269,16 +358,11 @@ print!{
|
||||
|
||||
}
|
||||
let args = Args::parse();
|
||||
let mut outfile = PathBuf::new();
|
||||
let mut threads = 10;
|
||||
let mut save = false;
|
||||
let mut computers = Vec::new();
|
||||
if args.outfile.is_some(){
|
||||
outfile = args.outfile.unwrap();
|
||||
save = true;
|
||||
}
|
||||
if args.threads.is_some(){
|
||||
threads = args.threads.unwrap();
|
||||
let mut filter_computers = Vec::new();
|
||||
let mut network = true;
|
||||
if args.disable_network {
|
||||
network = false;
|
||||
}
|
||||
if args.targets.is_some() {
|
||||
println!("gathering the targets you gave me.");
|
||||
@@ -286,15 +370,34 @@ print!{
|
||||
if targets.contains(",") {
|
||||
let split_targets: Vec<&str> = targets.split(",").collect();
|
||||
for target in split_targets {
|
||||
computers.push(target.to_string());
|
||||
computers.push(target.trim().to_lowercase());
|
||||
}
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
computers.push(targets);
|
||||
}
|
||||
} else {
|
||||
println!("no targets specified, proceeding with computer enumeration...");
|
||||
}
|
||||
else{
|
||||
println!("no targets given, proceeding with domain computer enumeration...");
|
||||
if args.filter_targets.is_some() {
|
||||
println!("gathering the filter you specified!");
|
||||
let given_filter = args.filter_targets.unwrap();
|
||||
let filters: Vec<&str> = given_filter.split(",").collect();
|
||||
for filter in filters {
|
||||
filter_computers.push(filter.trim().to_lowercase());
|
||||
}
|
||||
}
|
||||
let hostname_res = Command::new("hostname").output();
|
||||
if hostname_res.is_ok() {
|
||||
let hostname_output = hostname_res.unwrap();
|
||||
if hostname_output.stdout.len() > 0 {
|
||||
let hostname_string = String::from_utf8_lossy(&hostname_output.stdout).to_string();
|
||||
if args.local {
|
||||
computers.push(hostname_string.trim().to_lowercase());
|
||||
} else {
|
||||
filter_computers.push(hostname_string.trim().to_lowercase());
|
||||
}
|
||||
}
|
||||
if network {
|
||||
println!("finding computers...");
|
||||
let command_string = String::from("net group \"domain computers\" /domain");
|
||||
let mut temp_file = fs::File::create("./temp.bat").unwrap();
|
||||
@@ -307,12 +410,10 @@ print!{
|
||||
let output = computer_res.unwrap();
|
||||
if output.stdout.len() > 0 {
|
||||
success_string = String::from_utf8_lossy(&output.stdout).to_string();
|
||||
}
|
||||
else if output.stderr.len() > 0{
|
||||
} else if output.stderr.len() > 0 {
|
||||
error_string = String::from_utf8_lossy(&output.stderr).to_string();
|
||||
}
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
error_string = computer_res.err().unwrap().to_string();
|
||||
}
|
||||
if error_string.len() > 0 {
|
||||
@@ -327,207 +428,263 @@ print!{
|
||||
for word in words {
|
||||
let mut computer_name = word.to_string();
|
||||
computer_name.pop();
|
||||
computers.push(computer_name);
|
||||
computers.push(computer_name.trim().to_lowercase());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut tasks = Vec::new();
|
||||
let mut id_counter = 0;
|
||||
}
|
||||
if filter_computers.len() > 0 {
|
||||
computers.retain(|x| !filter_computers.iter().any(|y| y == x));
|
||||
}
|
||||
let mut state = AppState {
|
||||
share_que: Arc::new(SegQueue::new()),
|
||||
file_que: Arc::new(SegQueue::new()),
|
||||
info_que: Arc::new(SegQueue::new()),
|
||||
continue_without_saveing: false,
|
||||
outfile: None,
|
||||
};
|
||||
if let Some(outpath) = args.outfile {
|
||||
if let Ok(outfile) = File::create(outpath) {
|
||||
state.outfile = Some(Arc::new(Mutex::new(outfile)));
|
||||
} else {
|
||||
let mut continue_res = String::new();
|
||||
println!("error creating outfile!");
|
||||
println!("continue without saving? (Y/n");
|
||||
stdin().read_line(&mut continue_res).unwrap();
|
||||
if continue_res.to_lowercase().contains("y") {
|
||||
state.continue_without_saveing = true;
|
||||
} else {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut filter = vec![
|
||||
"C:\\Windows".to_string(),
|
||||
"C$\\Windows".to_string(),
|
||||
"C:\\Windows.old".to_string(),
|
||||
"C$\\Windows.old".to_string(),
|
||||
"ADMIN$\\".to_string(),
|
||||
"ProgramData\\Microsoft".to_string(),
|
||||
"AppData\\Local\\Microsoft\\Internet Explorer\\brndlog.txt".to_string(),
|
||||
"desktop.ini".to_string(),
|
||||
"AppData\\Local\\Packages\\Microsoft.Windows.Search".to_string(),
|
||||
"AppData\\Local\\Microsoft\\Windows\\Shell\\DefaultLayouts.xml".to_string(),
|
||||
"AppData\\Local\\Microsoft\\Internet Explorer\\IECompatData\\iecompatdata.xml".to_string(),
|
||||
"Program Files\\WindowsPowerShell\\Modules\\Pester".to_string(),
|
||||
"vk_swiftshader_icd.json".to_string(),
|
||||
"AppxBlockMap.xml".to_string(),
|
||||
"AppxManifest.xml".to_string(),
|
||||
"MediaReceiverRegistrar.xml".to_string(),
|
||||
"ContentDirectory.xml".to_string(),
|
||||
"avtransport.xml".to_string(),
|
||||
"ThirdPartyNotices.txt".to_string(),
|
||||
"Program Files\\Common Files\\microsoft shared".to_string(),
|
||||
"Diagnostics\\Simple\\Simple.Tests.ps1".to_string(),
|
||||
"Microsoft.PowerShell.Operation.Validation.Format.ps1xml".to_string(),
|
||||
"Test\\Microsoft.PowerShell.Operation.Validation.Tests.ps1".to_string(),
|
||||
"PackageManagement.format.ps1xml".to_string(),
|
||||
"PSGet.Format.ps1xml".to_string(),
|
||||
"PSReadLine.format.ps1xml".to_string(),
|
||||
"ResiliencyLinks\\WidevineCdm\\manifest.json.DATA".to_string(),
|
||||
"Program Files (x86)\\Microsoft\\Edge\\Application".to_string(),
|
||||
"Program Files (x86)\\Microsoft\\EdgeCore".to_string(),
|
||||
"Program Files (x86)\\Microsoft.NET\\RedistList".to_string(),
|
||||
"Program Files (x86)\\Microsoft.NET\\RedistList".to_string(),
|
||||
"Program Files (x86)\\WindowsPowerShell\\Modules\\Microsoft.PowerShell.Operation.Validation".to_string(),
|
||||
"Program Files (x86)\\WindowsPowerShell\\Modules\\PackageManagement".to_string(),
|
||||
"Program Files (x86)\\WindowsPowerShell\\Modules\\PowerShellGet".to_string(),
|
||||
"Program Files (x86)\\WindowsPowerShell\\Modules\\Pester".to_string(),
|
||||
"Program Files\\Internet Explorer".to_string(),
|
||||
"Program Files\\Windows Defender".to_string(),
|
||||
"Program Files\\WindowsApps".to_string(),
|
||||
"Program Files\\WindowsPowerShell\\Modules\\PackageManagement".to_string(),
|
||||
"Program Files (x86)\\Internet Explorer".to_string(),
|
||||
"Program Files (x86)\\Microsoft".to_string(),
|
||||
"ProgramData\\ntuser.pol".to_string(),
|
||||
"ProgramData\\USOShared".to_string(),
|
||||
"AppData\\Local\\ConnectedDevicesPlatform".to_string(),
|
||||
"AppData\\Local\\Microsoft\\Internet Explorer".to_string(),
|
||||
"ntuser.dat".to_string(),
|
||||
"ntuser.ini".to_string(),
|
||||
"Program Files (x86)\\Windows Defender".to_string(),
|
||||
"AppData\\Local\\Microsoft\\Windows\\WinX".to_string(),
|
||||
"AppData\\Local\\Microsoft_Corporation".to_string(),
|
||||
"AppData\\Roaming\\Microsoft\\Internet Explorer".to_string(),
|
||||
|
||||
];
|
||||
if let Some(user_filter) = args.filter_data {
|
||||
user_filter.split(",").into_iter().for_each(|p| {
|
||||
filter.push(p.to_string());
|
||||
});
|
||||
}
|
||||
for computer in &computers {
|
||||
println!("found {}", computer);
|
||||
let new_task = FinderTask{id: id_counter, target: computer.clone(), tasktype: TaskType::Share};
|
||||
tasks.push(new_task);
|
||||
id_counter += 1;
|
||||
let new_task = FinderTask {
|
||||
target: computer.clone(),
|
||||
tasktype: TaskType::Share,
|
||||
findings: Vec::new(),
|
||||
outfile: state.outfile.clone(),
|
||||
verbose: args.verbose.clone(),
|
||||
filter: filter.clone(),
|
||||
};
|
||||
state.share_que.push(new_task);
|
||||
}
|
||||
println!("computer enumeration finished, starting task finder threads...");
|
||||
let (tx, mut rx) = channel(1024);
|
||||
let mut running = Vec::new();
|
||||
let mut continue_wihtout_save = false;
|
||||
println!("Computers found!");
|
||||
let mut thread_count = rayon::current_num_threads();
|
||||
if let Some(count) = args.threads {
|
||||
thread_count = count;
|
||||
}
|
||||
println!("starting finder ques...");
|
||||
let mut outputs = run_tasks(
|
||||
state.share_que.clone(),
|
||||
state.file_que.clone(),
|
||||
state.info_que.clone(),
|
||||
state.outfile.clone(),
|
||||
thread_count,
|
||||
args.verbose.clone(),
|
||||
filter.clone(),
|
||||
);
|
||||
let mut final_findings = Vec::new();
|
||||
loop {
|
||||
if running.len() < threads{
|
||||
for _i in 0 .. threads{
|
||||
if tasks.len() > 0{
|
||||
let task = tasks[0].clone();
|
||||
tasks.remove(0);
|
||||
running.push(task.id.clone());
|
||||
tokio::spawn(task_handler(task.id, task, tx.clone()));
|
||||
if running.len() >= threads{
|
||||
if outputs.0.is_empty()
|
||||
&& outputs.1.is_empty()
|
||||
&& outputs.2.is_empty()
|
||||
&& outputs.3.is_empty()
|
||||
{
|
||||
break;
|
||||
} else {
|
||||
final_findings.extend(outputs.3.clone());
|
||||
outputs = run_tasks(
|
||||
outputs.0,
|
||||
outputs.1,
|
||||
outputs.2,
|
||||
state.outfile.clone(),
|
||||
thread_count,
|
||||
args.verbose.clone(),
|
||||
filter.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
else{
|
||||
break;
|
||||
println!("file enumeration complete, happy hunting!");
|
||||
}
|
||||
|
||||
fn run_tasks(
|
||||
share_que: Arc<SegQueue<FinderTask>>,
|
||||
file_que: Arc<SegQueue<FinderTask>>,
|
||||
info_que: Arc<SegQueue<FinderTask>>,
|
||||
outfile: Option<Arc<Mutex<File>>>,
|
||||
total_threads: usize,
|
||||
verbose: bool,
|
||||
filter: Vec<String>,
|
||||
) -> (
|
||||
Arc<SegQueue<FinderTask>>,
|
||||
Arc<SegQueue<FinderTask>>,
|
||||
Arc<SegQueue<FinderTask>>,
|
||||
Vec<Finding>,
|
||||
) {
|
||||
let findings = Arc::new(Mutex::new(Vec::new()));
|
||||
let ques = rayon::scope(
|
||||
|scope| -> (
|
||||
Arc<SegQueue<FinderTask>>,
|
||||
Arc<SegQueue<FinderTask>>,
|
||||
Arc<SegQueue<FinderTask>>,
|
||||
) {
|
||||
for _ in 0..total_threads {
|
||||
let findings_clone = findings.clone();
|
||||
let share_que = share_que.clone();
|
||||
let file_que = file_que.clone();
|
||||
let info_que = info_que.clone();
|
||||
let outfile = outfile.clone();
|
||||
let verbose = verbose.clone();
|
||||
let filter = filter.clone();
|
||||
let mut last_printed = Instant::now();
|
||||
scope.spawn(move |_| {
|
||||
let mut local_findings = Vec::new();
|
||||
let mut work_remaining = true;
|
||||
while work_remaining {
|
||||
work_remaining = false;
|
||||
if let Some(mut finder_task) = info_que.pop() {
|
||||
work_remaining = true;
|
||||
finder_task.run();
|
||||
local_findings.extend(finder_task.findings);
|
||||
if Instant::now().duration_since(last_printed)
|
||||
>= Duration::from_secs(30)
|
||||
{
|
||||
println!("{} share tasks remaining", share_que.len());
|
||||
println!("{} file tasks remaining", file_que.len());
|
||||
println!("{} info tasks remaining", info_que.len());
|
||||
println!("and we aint done yet!");
|
||||
last_printed = Instant::now();
|
||||
}
|
||||
} else if let Some(mut finder_task) = file_que.pop() {
|
||||
work_remaining = true;
|
||||
finder_task.run();
|
||||
finder_task.findings.iter().for_each(|f| {
|
||||
info_que.push(FinderTask {
|
||||
target: f.path.clone(),
|
||||
tasktype: TaskType::Info,
|
||||
findings: Vec::new(),
|
||||
outfile: outfile.clone(),
|
||||
verbose,
|
||||
filter: filter.clone(),
|
||||
});
|
||||
});
|
||||
local_findings.extend(finder_task.findings);
|
||||
if Instant::now().duration_since(last_printed)
|
||||
>= Duration::from_secs(30)
|
||||
{
|
||||
println!("{} share tasks remaining", share_que.len());
|
||||
println!("{} file tasks remaining", file_que.len());
|
||||
println!("{} info tasks remaining", info_que.len());
|
||||
println!("and we aint done yet!");
|
||||
last_printed = Instant::now();
|
||||
}
|
||||
} else if let Some(mut finder_task) = share_que.pop() {
|
||||
work_remaining = true;
|
||||
finder_task.run();
|
||||
finder_task.findings.iter().for_each(|f| {
|
||||
file_que.push(FinderTask {
|
||||
target: f.path.clone(),
|
||||
tasktype: TaskType::File,
|
||||
findings: Vec::new(),
|
||||
outfile: outfile.clone(),
|
||||
verbose,
|
||||
filter: filter.clone(),
|
||||
});
|
||||
});
|
||||
local_findings.extend(finder_task.findings);
|
||||
if Instant::now().duration_since(last_printed)
|
||||
>= Duration::from_secs(30)
|
||||
{
|
||||
println!("{} share tasks remaining", share_que.len());
|
||||
println!("{} file tasks remaining", file_que.len());
|
||||
println!("{} info tasks remaining", info_que.len());
|
||||
println!("and we aint done yet!");
|
||||
last_printed = Instant::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
if running.len() > 0{
|
||||
let rxres = rx.try_recv();
|
||||
if rxres.is_ok(){
|
||||
let mesage = rxres.unwrap();
|
||||
if mesage.task_finished{
|
||||
for index in 0 .. running.len(){
|
||||
if index == running.len(){
|
||||
break;
|
||||
}
|
||||
else{
|
||||
if running[index] == mesage.source{
|
||||
running.remove(index);
|
||||
if Instant::now().duration_since(last_printed) >= Duration::from_secs(30) {
|
||||
println!("{} share tasks remaining", share_que.len());
|
||||
println!("{} file tasks remaining", file_que.len());
|
||||
println!("{} info tasks remaining", info_que.len());
|
||||
println!("and we aint done yet!");
|
||||
last_printed = Instant::now();
|
||||
}
|
||||
}
|
||||
*findings_clone.lock().unwrap() = local_findings;
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
let finding = mesage.finding.unwrap();
|
||||
match mesage.tasktype{
|
||||
TaskType::Share => {
|
||||
println!("{} {}", "share found!".green(), finding.path);
|
||||
if save{
|
||||
let open_res = OpenOptions::new().create(true).append(true).open(&outfile);
|
||||
if open_res.is_err(){
|
||||
if !continue_wihtout_save{
|
||||
eprintln!("{}", "error opening save file!".red());
|
||||
eprintln!("{}", open_res.err().unwrap().to_string().red());
|
||||
let mut proceed = String::new();
|
||||
println!("continue anyway?");
|
||||
std::io::stdin().read_line(&mut proceed).unwrap();
|
||||
if proceed.to_lowercase().contains("y"){
|
||||
continue_wihtout_save = true;
|
||||
}
|
||||
else{
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
let mut save_file = open_res.unwrap();
|
||||
let write_res = write!(save_file,"share found! {}\\n", finding.path);
|
||||
if write_res.is_err(){
|
||||
if !continue_wihtout_save{
|
||||
eprintln!("{}", "error writing to save file!".red());
|
||||
eprintln!("{}", write_res.err().unwrap().to_string().red());
|
||||
let mut proceed = String::new();
|
||||
println!("proceed without saving?");
|
||||
std::io::stdin().read_line(&mut proceed).unwrap();
|
||||
if proceed.to_lowercase().contains("y"){
|
||||
continue_wihtout_save = true;
|
||||
}
|
||||
else{
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
write_res.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
let new_task = FinderTask{id: id_counter, tasktype: TaskType::File, target: finding.path};
|
||||
tasks.push(new_task);
|
||||
id_counter += 1;
|
||||
}
|
||||
TaskType::File => {
|
||||
let new_task = FinderTask{id: id_counter, tasktype: TaskType::Info, target: finding.path};
|
||||
tasks.push(new_task);
|
||||
id_counter += 1;
|
||||
}
|
||||
TaskType::Info => {
|
||||
if finding.keyword.unwrap(){
|
||||
println!("{} {}", "keyword match at".green(), finding.path.green());
|
||||
if save{
|
||||
let open_res = OpenOptions::new().create(true).append(true).open(&outfile);
|
||||
if open_res.is_err(){
|
||||
if !continue_wihtout_save{
|
||||
eprintln!("{}", "error opening save file!".red());
|
||||
eprintln!("{}", open_res.err().unwrap().to_string().red());
|
||||
let mut proceed = String::new();
|
||||
println!("continue anyway?");
|
||||
std::io::stdin().read_line(&mut proceed).unwrap();
|
||||
if proceed.to_lowercase().contains("y"){
|
||||
continue_wihtout_save = true;
|
||||
}
|
||||
else{
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
let mut save_file = open_res.unwrap();
|
||||
let write_res = write!(save_file,"keyword match at {}\\n", finding.path);
|
||||
if write_res.is_err(){
|
||||
if !continue_wihtout_save{
|
||||
eprintln!("{}", "error writing to save file!".red());
|
||||
eprintln!("{}", write_res.err().unwrap().to_string().red());
|
||||
let mut proceed = String::new();
|
||||
println!("proceed without saving?");
|
||||
std::io::stdin().read_line(&mut proceed).unwrap();
|
||||
if proceed.to_lowercase().contains("y"){
|
||||
continue_wihtout_save = true;
|
||||
}
|
||||
else{
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
write_res.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
if args.verbose{
|
||||
println!("{} {}", "file found at".green(), finding.path.green());
|
||||
}
|
||||
if save{
|
||||
let open_res = OpenOptions::new().create(true).append(true).open(&outfile);
|
||||
if open_res.is_err(){
|
||||
if !continue_wihtout_save{
|
||||
eprintln!("{}", "error opening save file!".red());
|
||||
eprintln!("{}", open_res.err().unwrap().to_string().red());
|
||||
let mut proceed = String::new();
|
||||
println!("continue anyway?");
|
||||
std::io::stdin().read_line(&mut proceed).unwrap();
|
||||
if proceed.to_lowercase().contains("y"){
|
||||
continue_wihtout_save = true;
|
||||
}
|
||||
else{
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
let mut save_file = open_res.unwrap();
|
||||
let write_res = write!(save_file,"file found! {}\\n", finding.path);
|
||||
if write_res.is_err(){
|
||||
if !continue_wihtout_save{
|
||||
eprintln!("{}", "error writing to save file!".red());
|
||||
eprintln!("{}", write_res.err().unwrap().to_string().red());
|
||||
let mut proceed = String::new();
|
||||
println!("proceed without saving?");
|
||||
std::io::stdin().read_line(&mut proceed).unwrap();
|
||||
if proceed.to_lowercase().contains("y"){
|
||||
continue_wihtout_save = true;
|
||||
}
|
||||
else{
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
write_res.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if running.len() == 0 && tasks.len() == 0 && rx.is_empty(){
|
||||
break;
|
||||
}
|
||||
}
|
||||
println!("{} share tasks remaining", share_que.len());
|
||||
println!("{} file tasks remaining", file_que.len());
|
||||
println!("{} info tasks remaining", info_que.len());
|
||||
return (share_que, file_que, info_que);
|
||||
},
|
||||
);
|
||||
let mut new_findings = Vec::new();
|
||||
findings.lock().unwrap().iter().for_each(|f| {
|
||||
new_findings.push(f.clone());
|
||||
});
|
||||
return (ques.0, ques.1, ques.2, new_findings);
|
||||
}
|
||||
Reference in New Issue
Block a user