WIP: Add UI to application #1
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
/target
|
||||
*.log
|
||||
7
.vscode/launch.json
vendored
Normal file
7
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": []
|
||||
}
|
||||
442
Cargo.lock
generated
442
Cargo.lock
generated
@ -2,6 +2,17 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "addr"
|
||||
version = "0.15.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a93b8a41dbe230ad5087cc721f8d41611de654542180586b315d9f4cf6b72bef"
|
||||
dependencies = [
|
||||
"psl",
|
||||
"psl-types",
|
||||
"publicsuffix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.21.0"
|
||||
@ -28,6 +39,18 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
@ -37,6 +60,27 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.4"
|
||||
@ -81,6 +125,12 @@ version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
@ -117,6 +167,12 @@ dependencies = [
|
||||
"syn_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.15.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
|
||||
|
||||
[[package]]
|
||||
name = "byte-unit"
|
||||
version = "5.1.4"
|
||||
@ -156,6 +212,21 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
||||
|
||||
[[package]]
|
||||
name = "cassowary"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.90"
|
||||
@ -174,12 +245,70 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
|
||||
dependencies = [
|
||||
"castaway",
|
||||
"cfg-if",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "configparser"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ec6d3da8e550377a85339063af6e3735f4b1d9392108da4e083a1b3b9820288"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"crossterm_winapi",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.5.0"
|
||||
@ -190,9 +319,15 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
|
||||
name = "dns_lookup_project"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"addr",
|
||||
"chrono",
|
||||
"configparser",
|
||||
"crossterm",
|
||||
"hickory-resolver",
|
||||
"ratatui",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"whois-rust",
|
||||
]
|
||||
|
||||
@ -208,6 +343,12 @@ dependencies = [
|
||||
"syn 2.0.53",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
||||
|
||||
[[package]]
|
||||
name = "endian-type"
|
||||
version = "0.1.2"
|
||||
@ -330,7 +471,7 @@ version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"ahash 0.7.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -338,6 +479,10 @@ name = "hashbrown"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@ -407,6 +552,39 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.4.0"
|
||||
@ -437,6 +615,12 @@ dependencies = [
|
||||
"hashbrown 0.14.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
|
||||
|
||||
[[package]]
|
||||
name = "ipconfig"
|
||||
version = "0.3.2"
|
||||
@ -455,12 +639,30 @@ version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
@ -483,6 +685,21 @@ dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru-cache"
|
||||
version = "0.1.2"
|
||||
@ -520,6 +737,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
@ -590,6 +808,12 @@ dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
@ -655,6 +879,21 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psl"
|
||||
version = "2.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe63c1c8bb438205c1245719638a0b14fe6e3b33f4406ac36b4770a706655345"
|
||||
dependencies = [
|
||||
"psl-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psl-types"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
|
||||
|
||||
[[package]]
|
||||
name = "ptr_meta"
|
||||
version = "0.1.4"
|
||||
@ -675,6 +914,16 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "publicsuffix"
|
||||
version = "2.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457"
|
||||
dependencies = [
|
||||
"idna 0.3.0",
|
||||
"psl-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
@ -736,13 +985,33 @@ dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ratatui"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcb12f8fbf6c62614b0d56eb352af54f6a22410c3b079eb53ee93c7b97dd31d8"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"cassowary",
|
||||
"compact_str",
|
||||
"crossterm",
|
||||
"indoc",
|
||||
"itertools",
|
||||
"lru",
|
||||
"paste",
|
||||
"stability",
|
||||
"strum",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -844,6 +1113,12 @@ version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.17"
|
||||
@ -899,6 +1174,36 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-mio"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simdutf8"
|
||||
version = "0.1.4"
|
||||
@ -930,6 +1235,44 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stability"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.53",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
@ -1134,6 +1477,18 @@ dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.0"
|
||||
@ -1197,6 +1552,60 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.53",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.53",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||
|
||||
[[package]]
|
||||
name = "whois-rust"
|
||||
version = "1.6.0"
|
||||
@ -1238,6 +1647,15 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
@ -1397,3 +1815,23 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||
dependencies = [
|
||||
"tap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.53",
|
||||
]
|
||||
|
||||
12
Cargo.toml
12
Cargo.toml
@ -6,7 +6,13 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
configparser = "3.0.4"
|
||||
configparser = "*"
|
||||
crossterm = "*"
|
||||
hickory-resolver = { version = "0.24.0" }
|
||||
regex = "1.10.3"
|
||||
whois-rust = "1.6.0"
|
||||
ratatui = "*"
|
||||
regex = "*"
|
||||
whois-rust = "*"
|
||||
serde = { version = "*", features = ["derive"] }
|
||||
serde_json = "*"
|
||||
chrono = "*"
|
||||
addr = { version = "0.15.6", features = ["publicsuffix"] }
|
||||
|
||||
1
change_name.md
Normal file
1
change_name.md
Normal file
@ -0,0 +1 @@
|
||||
HelpMeDig
|
||||
45
src/app.rs
Normal file
45
src/app.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use ratatui::widgets::ListState;
|
||||
|
||||
use crate::config::*;
|
||||
use crate::logger::*;
|
||||
|
||||
pub enum AppRenderDir {
|
||||
Vertical,
|
||||
Horizontal,
|
||||
}
|
||||
|
||||
pub enum MenuState {
|
||||
Main,
|
||||
List,
|
||||
}
|
||||
|
||||
pub enum CurrentState {
|
||||
Lookup,
|
||||
Menu,
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
pub domain_input: String,
|
||||
pub whois_info: Vec<String>,
|
||||
pub dns_info: Vec<String>,
|
||||
pub render_direction: AppRenderDir,
|
||||
pub current_state: CurrentState,
|
||||
pub menu_state: MenuState,
|
||||
pub config: Config,
|
||||
pub state: ListState,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new() -> App {
|
||||
App {
|
||||
domain_input: String::new(),
|
||||
whois_info: vec![],
|
||||
dns_info: vec![],
|
||||
render_direction: AppRenderDir::Vertical,
|
||||
current_state: CurrentState::Lookup,
|
||||
menu_state: MenuState::Main,
|
||||
config: Config::load(),
|
||||
state: ListState::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,22 +1,29 @@
|
||||
use configparser::ini::Ini;
|
||||
use core::fmt;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
|
||||
pub struct Config {
|
||||
pub subdomains: Vec<String>,
|
||||
pub wildcard_test: String,
|
||||
pub data_dir: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for Config {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Subdomains: {:?}\nWildcard Test: {}", self.subdomains, self.wildcard_test)
|
||||
write!(
|
||||
f,
|
||||
"Subdomains: {:?}\nWildcard Test: {}",
|
||||
self.subdomains, self.wildcard_test
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn from_file(file_path: String) -> Config {
|
||||
let mut config = Ini::new();
|
||||
config.load(file_path).unwrap();
|
||||
|
||||
match config.load(file_path) {
|
||||
Ok(_) => {
|
||||
let subdomains = config.get("General", "subdomains").unwrap();
|
||||
let mut subvec: Vec<String> = vec![];
|
||||
|
||||
@ -24,10 +31,63 @@ impl Config {
|
||||
subvec.push(x.to_string());
|
||||
}
|
||||
|
||||
let wildcard_test = config.get("General", "wildcard_test");
|
||||
let wildcard_test = config.get("General", "wildcard");
|
||||
let datadir = config.get("General", "data_directory");
|
||||
|
||||
Config {
|
||||
subdomains: subvec,
|
||||
wildcard_test: wildcard_test.unwrap()
|
||||
wildcard_test: wildcard_test.unwrap(),
|
||||
data_dir: datadir.unwrap(),
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
println!("{:?}", err);
|
||||
Config::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Config {
|
||||
let mut config = Ini::new();
|
||||
let mut default_path = env::var_os("HOME").unwrap();
|
||||
default_path.push("/.config/dnslookup");
|
||||
|
||||
// Create the directories if they don't exist
|
||||
match fs::create_dir_all(&default_path) {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
println!("Unable to create directories, got {}", err)
|
||||
}
|
||||
}
|
||||
|
||||
config.set(
|
||||
"General",
|
||||
"wildcard",
|
||||
Some(String::from("dfjgnkdfjngkdfngjkd")),
|
||||
);
|
||||
config.set(
|
||||
"General",
|
||||
"subdomains",
|
||||
Some(String::from("www,ftp,mail,files")),
|
||||
);
|
||||
config.set(
|
||||
"General",
|
||||
"data_directory",
|
||||
Some(String::from(default_path.to_str().unwrap())),
|
||||
);
|
||||
|
||||
let mut conf_path = default_path.clone();
|
||||
conf_path.push("/config.ini");
|
||||
|
||||
config.write(&conf_path.clone()).unwrap();
|
||||
Config::from_file(conf_path.into_string().unwrap())
|
||||
}
|
||||
|
||||
pub fn load() -> Config {
|
||||
// Default path is $HOME/.config/dnslookup/config.ini
|
||||
let mut conf_path = env::var_os("HOME").unwrap();
|
||||
conf_path.push("/.config/dnslookup/config.ini");
|
||||
|
||||
Config::from_file(conf_path.into_string().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,9 +53,9 @@ impl fmt::Display for Domain {
|
||||
}
|
||||
|
||||
impl Domain {
|
||||
pub fn new(domain: String) -> Domain {
|
||||
pub fn new(domain: &String) -> Domain {
|
||||
Domain {
|
||||
domain_name: domain,
|
||||
domain_name: String::from(domain),
|
||||
subdomains: vec![],
|
||||
a_records: vec![],
|
||||
aaaa_records: vec![],
|
||||
@ -67,11 +67,56 @@ impl Domain {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_vec(&self) -> Vec<String> {
|
||||
let mut ret_vec: Vec<String> = vec![];
|
||||
for rec in &self.a_records {
|
||||
let mut tmp = String::from("A: ");
|
||||
tmp.push_str(rec.to_string().as_ref());
|
||||
ret_vec.push(tmp);
|
||||
}
|
||||
for rec in &self.aaaa_records {
|
||||
let mut tmp = String::from("AAAA: ");
|
||||
tmp.push_str(rec.to_string().as_ref());
|
||||
ret_vec.push(tmp);
|
||||
}
|
||||
for rec in &self.mx_records {
|
||||
let mut tmp = String::from("MX: ");
|
||||
tmp.push_str(rec.to_string().as_ref());
|
||||
ret_vec.push(tmp);
|
||||
}
|
||||
for rec in &self.txt_records {
|
||||
let mut tmp = String::from("TXT: ");
|
||||
tmp.push_str(rec.to_string().as_ref());
|
||||
ret_vec.push(tmp);
|
||||
}
|
||||
for rec in &self.ns_records {
|
||||
let mut tmp = String::from("NS: ");
|
||||
tmp.push_str(rec.to_string().as_ref());
|
||||
ret_vec.push(tmp);
|
||||
}
|
||||
for rec in &self.soa_records {
|
||||
let mut tmp = String::from("SOA: ");
|
||||
tmp.push_str(rec.to_string().as_ref());
|
||||
ret_vec.push(tmp);
|
||||
}
|
||||
for subdomain in &self.subdomains {
|
||||
let mut subdomain_name = String::from(subdomain.domain_name.clone());
|
||||
subdomain_name.push_str("\t");
|
||||
for rec in &subdomain.a_records {
|
||||
let mut tmp = String::from(&subdomain_name);
|
||||
tmp.push_str(" A: ");
|
||||
tmp.push_str(rec.to_string().as_ref());
|
||||
ret_vec.push(tmp);
|
||||
}
|
||||
}
|
||||
return ret_vec;
|
||||
}
|
||||
|
||||
pub fn append_subdomain(&mut self, subdomain: String) {
|
||||
let mut new_domain = String::from(subdomain);
|
||||
new_domain.push_str(".");
|
||||
new_domain.push_str(&self.domain_name);
|
||||
let subdomain = Domain::new(new_domain);
|
||||
let subdomain = Domain::new(&new_domain);
|
||||
self.subdomains.push(subdomain);
|
||||
// println!("Added: {}", new_domain);
|
||||
}
|
||||
|
||||
67
src/fsutil.rs
Normal file
67
src/fsutil.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use chrono::Local;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::error::Error;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use ratatui::{
|
||||
style::{Modifier, Style},
|
||||
text::{Line, Span},
|
||||
widgets::ListItem,
|
||||
};
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct DomainData {
|
||||
name: String,
|
||||
dns_records: Vec<String>,
|
||||
whois_info: Vec<String>,
|
||||
}
|
||||
|
||||
impl DomainData {
|
||||
pub fn new(domain: String, dns: Vec<String>, whois_data: Vec<String>) -> DomainData {
|
||||
DomainData {
|
||||
name: domain,
|
||||
dns_records: dns,
|
||||
whois_info: whois_data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_lookup(&self, config: &Config) -> Result<(), Box<dyn Error>> {
|
||||
let output = serde_json::to_string(&self).unwrap();
|
||||
let date = Local::now();
|
||||
let filepath = format!(
|
||||
"{}/lookups/{}-{}.json",
|
||||
config.data_dir,
|
||||
date.format("%Y-%m-%d_%H-%M-%S"),
|
||||
self.name
|
||||
);
|
||||
let path = format!("{}/lookups", config.data_dir);
|
||||
let data_dir = Path::new(&path).exists();
|
||||
if data_dir != true {
|
||||
match fs::create_dir_all(format!("{}/lookups", config.data_dir)) {
|
||||
Ok(_val) => {}
|
||||
Err(_err) => {}
|
||||
}
|
||||
}
|
||||
fs::write(filepath, output)?;
|
||||
|
||||
Ok(())
|
||||
// println!("{}", output);
|
||||
}
|
||||
|
||||
pub fn list_lookups(config: &Config) -> Vec<String> {
|
||||
let mut items: Vec<String> = vec![];
|
||||
for item in fs::read_dir(format!("{}/lookups", config.data_dir)).unwrap() {
|
||||
let item_name = item.unwrap();
|
||||
// items.push(ListItem::new(Line::from(Span::styled(
|
||||
// String::from(item_name.path().to_string_lossy().clone()),
|
||||
// Style::default(),
|
||||
// ))));
|
||||
items.push(String::from(item_name.path().to_string_lossy().clone()))
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
33
src/logger.rs
Normal file
33
src/logger.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::prelude::*;
|
||||
use std::path::Path;
|
||||
|
||||
enum LoggerType {
|
||||
Local(&'static str),
|
||||
}
|
||||
|
||||
pub struct Logger {
|
||||
l_type: LoggerType,
|
||||
}
|
||||
|
||||
impl Logger {
|
||||
pub fn new(path: &'static str) -> Logger {
|
||||
Logger {
|
||||
l_type: LoggerType::Local(path),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_log_line(&self, data: String) {
|
||||
match self.l_type {
|
||||
LoggerType::Local(path) => {
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(path)
|
||||
.unwrap();
|
||||
writeln!(file, "{}", data).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
148
src/main.rs
148
src/main.rs
@ -4,41 +4,125 @@ use crate::domain::Domain;
|
||||
mod config;
|
||||
use crate::config::Config;
|
||||
|
||||
mod app;
|
||||
use crate::app::*;
|
||||
|
||||
mod ui;
|
||||
use crate::ui::*;
|
||||
|
||||
mod fsutil;
|
||||
use crate::fsutil::*;
|
||||
|
||||
mod whois;
|
||||
use crate::whois::WhoisData;
|
||||
use whois::default;
|
||||
use whois::selector::*;
|
||||
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
mod logger;
|
||||
|
||||
fn main() {
|
||||
// let mut running = true;
|
||||
let config = Config::from_file("test.ini".to_string());
|
||||
use crossterm::event::{
|
||||
self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind,
|
||||
};
|
||||
use crossterm::execute;
|
||||
use crossterm::terminal::{
|
||||
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
|
||||
};
|
||||
use ratatui::backend::{Backend, CrosstermBackend};
|
||||
use ratatui::Terminal;
|
||||
use std::error::Error;
|
||||
use std::io::{self, stdout};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
enable_raw_mode()?;
|
||||
let mut stdout = stdout();
|
||||
execute!(stdout, EnterAlternateScreen)?;
|
||||
|
||||
let backend = CrosstermBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
||||
// Initialise the App
|
||||
let mut app = App::new();
|
||||
|
||||
// Run the app
|
||||
let _res = run_app(&mut terminal, &mut app);
|
||||
|
||||
disable_raw_mode()?;
|
||||
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
|
||||
terminal.show_cursor()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_app<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) -> io::Result<bool> {
|
||||
loop {
|
||||
let mut domain = String::new();
|
||||
print!("Enter domain name: ");
|
||||
io::stdout().flush().expect("Failed to flush buffer");
|
||||
match io::stdin().read_line(&mut domain) {
|
||||
Ok(_n) => {
|
||||
if domain.trim() == "q" {
|
||||
break;
|
||||
}
|
||||
let mut dns = Domain::new(String::from(domain.trim()));
|
||||
dns.apply_config(&config);
|
||||
dns.lookup_all_records();
|
||||
let whois = WhoisData::new(String::from(domain.trim()));
|
||||
println!("DNS: {}\n\n\nWhois: {}", dns, whois);
|
||||
}
|
||||
Err(err) => {
|
||||
println!("{}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
// let mut test = Domain::new("ventraip.com.au".to_string());
|
||||
// let config = Config::from_file("test.ini".to_string());
|
||||
// test.apply_config(&config);
|
||||
terminal.draw(|f| ui(f, app))?;
|
||||
|
||||
// test.lookup_all_records();
|
||||
// let whois = WhoisData::new(test.domain_name.clone());
|
||||
|
||||
// println!("Domain: {}\n{}", test.domain_name.clone(), whois);
|
||||
if let Event::Key(key) = event::read()? {
|
||||
if key.kind == event::KeyEventKind::Release {
|
||||
continue;
|
||||
}
|
||||
match app.current_state {
|
||||
CurrentState::Lookup => match key.code {
|
||||
KeyCode::Esc => {
|
||||
app.current_state = CurrentState::Menu;
|
||||
}
|
||||
KeyCode::Backspace => {
|
||||
app.domain_input.pop();
|
||||
}
|
||||
KeyCode::Char(char) => {
|
||||
app.domain_input.push(char);
|
||||
}
|
||||
KeyCode::Enter => {
|
||||
// This will do the lookup and populate the UI with the info
|
||||
if &app.domain_input == "" {
|
||||
// Ignore empty input
|
||||
continue;
|
||||
}
|
||||
let mut domain = Domain::new(&app.domain_input);
|
||||
domain.apply_config(&app.config);
|
||||
domain.lookup_all_records();
|
||||
app.dns_info = domain.to_vec();
|
||||
let whois_server = select_whois_server(app.domain_input.clone());
|
||||
match whois_server {
|
||||
Ok(mut whois_data) => {
|
||||
app.whois_info = whois_data.lookup(app.domain_input.clone());
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
// let whois = WhoisData::new(app.domain_input.clone());
|
||||
// app.whois_info = whois.to_vec();
|
||||
}
|
||||
KeyCode::Delete => {
|
||||
app.domain_input = String::new();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
CurrentState::Menu => match app.menu_state {
|
||||
MenuState::Main => match key.code {
|
||||
KeyCode::Esc => {
|
||||
app.current_state = CurrentState::Lookup;
|
||||
}
|
||||
KeyCode::Char('s') => {
|
||||
let domain_data = DomainData::new(
|
||||
app.domain_input.clone(),
|
||||
app.dns_info.clone(),
|
||||
app.whois_info.clone(),
|
||||
);
|
||||
domain_data.save_lookup(&app.config).unwrap();
|
||||
app.current_state = CurrentState::Lookup;
|
||||
}
|
||||
KeyCode::Char('l') => {
|
||||
app.menu_state = MenuState::List;
|
||||
}
|
||||
KeyCode::Char('q') => return Ok(false),
|
||||
_ => {}
|
||||
},
|
||||
MenuState::List => match key.code {
|
||||
KeyCode::Esc => {
|
||||
app.menu_state = MenuState::Main;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
362
src/ui.rs
Normal file
362
src/ui.rs
Normal file
@ -0,0 +1,362 @@
|
||||
use ratatui::{
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
prelude::*,
|
||||
style::{Color, Modifier, Style, Styled},
|
||||
text::{Line, Span, Text},
|
||||
widgets::{
|
||||
Block, Borders, Clear, HighlightSpacing, List, ListDirection, ListItem, ListState, Padding,
|
||||
Paragraph, Wrap,
|
||||
},
|
||||
Frame,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
app::{App, AppRenderDir},
|
||||
CurrentState, DomainData, MenuState,
|
||||
};
|
||||
|
||||
struct StatefulList<'a> {
|
||||
state: ListState,
|
||||
items: Vec<ListItem<'a>>,
|
||||
last_selected: Option<usize>,
|
||||
}
|
||||
|
||||
impl StatefulList<'_> {
|
||||
fn with_items<'a>(items: Vec<String>) -> StatefulList<'a> {
|
||||
let mut veclist = Vec::<ListItem>::new();
|
||||
for item in items {
|
||||
veclist.push(ListItem::new(Line::from(item)));
|
||||
}
|
||||
StatefulList {
|
||||
state: ListState::default(),
|
||||
items: veclist,
|
||||
last_selected: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn next(&mut self) {
|
||||
let i = match self.state.selected() {
|
||||
Some(i) => {
|
||||
if i >= self.items.len() - 1 {
|
||||
0
|
||||
} else {
|
||||
i + 1
|
||||
}
|
||||
}
|
||||
None => self.last_selected.unwrap_or(0),
|
||||
};
|
||||
self.state.select(Some(i));
|
||||
}
|
||||
|
||||
fn previous(&mut self) {
|
||||
let i = match self.state.selected() {
|
||||
Some(i) => {
|
||||
if i == 0 {
|
||||
self.items.len() - 1
|
||||
} else {
|
||||
i - 1
|
||||
}
|
||||
}
|
||||
None => self.last_selected.unwrap_or(0),
|
||||
};
|
||||
self.state.select(Some(i));
|
||||
}
|
||||
|
||||
fn unselect(&mut self) {
|
||||
let offset = self.state.offset();
|
||||
self.last_selected = self.state.selected();
|
||||
self.state.select(None);
|
||||
*self.state.offset_mut() = offset;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ui(f: &mut Frame, app: &mut App) {
|
||||
if f.size().width > 100 {
|
||||
app.render_direction = AppRenderDir::Horizontal;
|
||||
} else {
|
||||
app.render_direction = AppRenderDir::Vertical;
|
||||
}
|
||||
match app.current_state {
|
||||
CurrentState::Lookup => {
|
||||
match app.render_direction {
|
||||
/*
|
||||
Initial contianer is vertical but we have the large center Rect that we will split horizontally
|
||||
Constraint 1: 30px title bar
|
||||
Constraint 2: Rest of unused space
|
||||
Constraint 3: 30px input / key hints
|
||||
|
||||
Constraint 2.a: 50% of usable space for whois
|
||||
Constraint 2.b: 50% of usable space for dns
|
||||
*/
|
||||
AppRenderDir::Horizontal => {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([
|
||||
Constraint::Length(3),
|
||||
Constraint::Min(1),
|
||||
Constraint::Length(3),
|
||||
])
|
||||
.split(f.size());
|
||||
|
||||
let title_block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.style(Style::default())
|
||||
.padding(Padding::horizontal(2));
|
||||
|
||||
let title = Paragraph::new(Span::styled(
|
||||
"Dns Lookup tool",
|
||||
Style::default().fg(Color::Yellow),
|
||||
))
|
||||
.block(title_block);
|
||||
|
||||
f.render_widget(title, chunks[0]);
|
||||
|
||||
let data_layout = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||
.split(chunks[1]);
|
||||
|
||||
// Whois list
|
||||
let mut whois_list_items = Vec::<ListItem>::new();
|
||||
for record in &app.whois_info {
|
||||
whois_list_items.push(ListItem::new(Line::from(Span::styled(
|
||||
record,
|
||||
Style::default(),
|
||||
))))
|
||||
}
|
||||
let whois_list = List::new(whois_list_items);
|
||||
|
||||
f.render_widget(whois_list, data_layout[0]);
|
||||
|
||||
// DNS list
|
||||
let mut dns_list_items = Vec::<ListItem>::new();
|
||||
|
||||
for record in &app.dns_info {
|
||||
dns_list_items.push(ListItem::new(Line::from(Span::styled(
|
||||
record,
|
||||
Style::default(),
|
||||
))))
|
||||
}
|
||||
let dns_list = List::new(dns_list_items);
|
||||
|
||||
f.render_widget(dns_list, data_layout[1]);
|
||||
|
||||
let footer_chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||
.split(chunks[2]);
|
||||
|
||||
let domain_block = Block::new().borders(Borders::ALL).style(Style::default());
|
||||
let mut domain_string = String::from("Domain: ");
|
||||
domain_string.push_str(&app.domain_input);
|
||||
let domain_text = Paragraph::new(Text::styled(
|
||||
domain_string,
|
||||
Style::default().fg(Color::Blue),
|
||||
))
|
||||
.block(domain_block);
|
||||
f.render_widget(domain_text, footer_chunks[0]);
|
||||
|
||||
let key_hint_block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.style(Style::default());
|
||||
|
||||
let key_hint = Paragraph::new(Text::styled(
|
||||
"[ESC] Menu / [Enter] Check domain / [Del] Clear Input",
|
||||
Style::default().fg(Color::Red),
|
||||
))
|
||||
.block(key_hint_block);
|
||||
f.render_widget(key_hint, footer_chunks[1]);
|
||||
}
|
||||
/*
|
||||
Constraint 1: 30px title bar
|
||||
Constraint 2: 50% of unused space for whois
|
||||
Constraint 3: 50% of unused space for dns
|
||||
Constraint 4: 30px input / key hints
|
||||
*/
|
||||
AppRenderDir::Vertical => {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([
|
||||
Constraint::Length(3),
|
||||
Constraint::Percentage(50),
|
||||
Constraint::Percentage(50),
|
||||
Constraint::Length(3),
|
||||
])
|
||||
.split(f.size());
|
||||
|
||||
let title_block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.style(Style::default());
|
||||
|
||||
let title = Paragraph::new(Span::styled(
|
||||
"Dns Lookup tool",
|
||||
Style::default().fg(Color::White),
|
||||
))
|
||||
.block(title_block);
|
||||
|
||||
f.render_widget(title, chunks[0]);
|
||||
|
||||
// Whois list
|
||||
let mut whois_list_items = Vec::<ListItem>::new();
|
||||
for record in &app.whois_info {
|
||||
whois_list_items.push(ListItem::new(Line::from(Span::styled(
|
||||
record,
|
||||
Style::default(),
|
||||
))))
|
||||
}
|
||||
let whois_list = List::new(whois_list_items);
|
||||
|
||||
f.render_widget(whois_list, chunks[1]);
|
||||
|
||||
// DNS list
|
||||
let mut dns_list_items = Vec::<ListItem>::new();
|
||||
for record in &app.dns_info {
|
||||
dns_list_items.push(ListItem::new(Line::from(Span::styled(
|
||||
record,
|
||||
Style::default(),
|
||||
))))
|
||||
}
|
||||
let dns_list = List::new(dns_list_items);
|
||||
|
||||
f.render_widget(dns_list, chunks[2]);
|
||||
let footer_chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||
.split(chunks[3]);
|
||||
|
||||
let domain_block = Block::new().borders(Borders::ALL).style(Style::default());
|
||||
let mut domain_string = String::from("Domain: ");
|
||||
domain_string.push_str(&app.domain_input);
|
||||
let domain_text = Paragraph::new(Text::styled(
|
||||
domain_string,
|
||||
Style::default().fg(Color::Blue),
|
||||
))
|
||||
.block(domain_block);
|
||||
f.render_widget(domain_text, footer_chunks[0]);
|
||||
|
||||
let key_hint_block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.style(Style::default());
|
||||
|
||||
let key_hint = Paragraph::new(Text::styled(
|
||||
"[ESC] Quit / [Enter] Check domain",
|
||||
Style::default().fg(Color::Red),
|
||||
))
|
||||
.block(key_hint_block);
|
||||
f.render_widget(key_hint, footer_chunks[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
CurrentState::Menu => {
|
||||
match app.menu_state {
|
||||
MenuState::Main => {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([
|
||||
Constraint::Length(3),
|
||||
Constraint::Min(1),
|
||||
Constraint::Length(3),
|
||||
])
|
||||
.split(f.size());
|
||||
|
||||
let title_block = Block::new().borders(Borders::ALL).style(Style::default());
|
||||
|
||||
let title = Paragraph::new(Text::styled(
|
||||
"**** Menu ****",
|
||||
Style::default().fg(Color::Cyan),
|
||||
))
|
||||
.block(title_block);
|
||||
f.render_widget(title, chunks[0]);
|
||||
|
||||
let mut options_list = Vec::<ListItem>::new();
|
||||
options_list.push(ListItem::new(Line::from(Span::styled(
|
||||
"ESC) Return to lookup",
|
||||
Style::default(),
|
||||
))));
|
||||
|
||||
// TODO: Add code to save lookup
|
||||
options_list.push(ListItem::new(Line::from(Span::styled(
|
||||
"S) Save lookup",
|
||||
Style::default(),
|
||||
))));
|
||||
|
||||
// TODO: Add code to load and UI to list saved lookups
|
||||
options_list.push(ListItem::new(Line::from(Span::styled(
|
||||
"L) List previous lookups",
|
||||
Style::default(),
|
||||
))));
|
||||
|
||||
// TODO: Add code to read lookup and display
|
||||
options_list.push(ListItem::new(Line::from(Span::styled(
|
||||
"V) View previous lookup",
|
||||
Style::default(),
|
||||
))));
|
||||
|
||||
options_list.push(ListItem::new(Line::from(Span::styled(
|
||||
"Q) Quit",
|
||||
Style::default(),
|
||||
))));
|
||||
|
||||
let options = List::new(options_list);
|
||||
f.render_widget(options, chunks[1]);
|
||||
|
||||
let footer_block = Block::new().borders(Borders::ALL).style(Style::default());
|
||||
|
||||
let footer =
|
||||
Paragraph::new(Text::styled("[q] Quit", Style::default().fg(Color::Red)))
|
||||
.block(footer_block);
|
||||
f.render_widget(footer, chunks[2]);
|
||||
}
|
||||
MenuState::List => {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([
|
||||
Constraint::Length(3),
|
||||
Constraint::Min(1),
|
||||
Constraint::Length(3),
|
||||
])
|
||||
.split(f.size());
|
||||
|
||||
let title_block = Block::new().borders(Borders::ALL).style(Style::default());
|
||||
|
||||
let title = Paragraph::new(Text::styled(
|
||||
"**** List ****",
|
||||
Style::default().fg(Color::Cyan),
|
||||
))
|
||||
.block(title_block);
|
||||
f.render_widget(title, chunks[0]);
|
||||
|
||||
let item_list = DomainData::list_lookups(&app.config);
|
||||
let items = List::new(item_list)
|
||||
.block(Block::default().title("Lookups").borders(Borders::ALL))
|
||||
.style(Style::default())
|
||||
.highlight_style(Style::default().add_modifier(Modifier::BOLD))
|
||||
.highlight_symbol(">>")
|
||||
.highlight_spacing(HighlightSpacing::Always)
|
||||
.repeat_highlight_symbol(true);
|
||||
// f.render_stateful_widget(items, chunks[1], &mut app.state);
|
||||
|
||||
// let t_list = StatefulList::with_items(item_list);
|
||||
// f.render_stateful_widget(t_list, chunks[1], t_list.state);
|
||||
let items = ["Item 1", "Item 2", "Item 3"];
|
||||
let list = List::new(items)
|
||||
.block(Block::default().title("List").borders(Borders::ALL))
|
||||
.highlight_style(Style::new().add_modifier(Modifier::REVERSED))
|
||||
.highlight_symbol(">>")
|
||||
.repeat_highlight_symbol(true);
|
||||
|
||||
f.render_stateful_widget(list, chunks[1], &mut app.state);
|
||||
|
||||
let footer_block = Block::new().borders(Borders::ALL).style(Style::default());
|
||||
|
||||
let footer = Paragraph::new(Text::styled(
|
||||
"[ESC] Return to menu",
|
||||
Style::default().fg(Color::Red),
|
||||
))
|
||||
.block(footer_block);
|
||||
f.render_widget(footer, chunks[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
152
src/whois.rs
152
src/whois.rs
@ -1,152 +0,0 @@
|
||||
use core::fmt;
|
||||
use regex::Regex;
|
||||
use whois_rust::{WhoIs, WhoIsLookupOptions};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum RegistrantType {
|
||||
Registrant,
|
||||
Admin,
|
||||
Tech,
|
||||
Billing,
|
||||
}
|
||||
|
||||
struct RegexQuery {
|
||||
re: Regex,
|
||||
}
|
||||
|
||||
impl RegexQuery {
|
||||
fn new(expression: String) -> RegexQuery {
|
||||
let re = Regex::new(&expression).unwrap();
|
||||
RegexQuery { re }
|
||||
}
|
||||
|
||||
fn get_matches(&self, haystack: &str) -> Vec<String> {
|
||||
let mut results = vec![];
|
||||
for (_, [_, rex2]) in self.re.captures_iter(haystack).map(|c| c.extract()) {
|
||||
results.push(String::from(rex2.trim()));
|
||||
}
|
||||
results
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct NameServer {
|
||||
host: String,
|
||||
}
|
||||
|
||||
impl NameServer {
|
||||
fn new(host: String) -> NameServer {
|
||||
NameServer { host }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Registrant {
|
||||
name: String,
|
||||
org: String,
|
||||
email: String,
|
||||
rtype: RegistrantType,
|
||||
}
|
||||
|
||||
impl fmt::Display for Registrant {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "")
|
||||
}
|
||||
}
|
||||
|
||||
impl Registrant {
|
||||
fn new(name: String, org: String, email: String, rtype: RegistrantType) -> Registrant {
|
||||
Registrant {
|
||||
name,
|
||||
org,
|
||||
email,
|
||||
rtype,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Eligibility {
|
||||
e_type: String,
|
||||
id: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
pub struct WhoisData {
|
||||
registrar: String,
|
||||
domain_status: String,
|
||||
registrant: Vec<Registrant>,
|
||||
nameservers: Vec<NameServer>,
|
||||
dnssec: String,
|
||||
eligibility_type: Option<Eligibility>,
|
||||
}
|
||||
|
||||
impl fmt::Display for WhoisData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Registrar: {}\nStatus: {}\nRegistrant: {:?}\nNameservers: {:?}\nDNSSEC: {}",
|
||||
self.registrar, self.domain_status, self.registrant, self.nameservers, self.dnssec
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl WhoisData {
|
||||
pub fn new(domain: String) -> WhoisData {
|
||||
let whois = WhoIs::from_path("servers.json").unwrap();
|
||||
let result: String = whois
|
||||
.lookup(WhoIsLookupOptions::from_string(domain).unwrap())
|
||||
.unwrap();
|
||||
let registrar_regex =
|
||||
RegexQuery::new(String::from(r"(?i)(.*registrar:|registrar *name:)(.*)"));
|
||||
let domain_status_regex =
|
||||
RegexQuery::new(String::from(r"(?i)(.*domain status:|.*status:)(.*)"));
|
||||
// TODO: Capture the registrant info for each type
|
||||
let registrant_name_regex = RegexQuery::new(String::from(r"(?i)(registrant.*name:)(.*)"));
|
||||
let registrant_org_regex =
|
||||
RegexQuery::new(String::from(r"(?i)(registrant org.*:|registrant:)(.*)"));
|
||||
let registrant_email_regex = RegexQuery::new(String::from(r"(?i)(registrant email:)(.*)"));
|
||||
let nameserver_regex =
|
||||
RegexQuery::new(String::from(r"(?i)(nameservers*:|name servers*:)(.*)"));
|
||||
let dnssec_regex = RegexQuery::new(String::from(r"(?i)(.*dnssec:)(.*)"));
|
||||
|
||||
let registrar = WhoisData::return_regex(registrar_regex.get_matches(&result), 0);
|
||||
let domain_status = WhoisData::return_regex(domain_status_regex.get_matches(&result), 0);
|
||||
let reg_name = WhoisData::return_regex(registrant_name_regex.get_matches(&result), 0);
|
||||
let reg_org = WhoisData::return_regex(registrant_org_regex.get_matches(&result), 0);
|
||||
let reg_email = WhoisData::return_regex(registrant_email_regex.get_matches(&result), 0);
|
||||
|
||||
let mut nameservers = vec![];
|
||||
for nameserver in nameserver_regex.get_matches(&result) {
|
||||
nameservers.push(NameServer::new(nameserver));
|
||||
}
|
||||
let dnssec = dnssec_regex.get_matches(&result);
|
||||
|
||||
// println!("{:?}", registrar[0]);
|
||||
WhoisData {
|
||||
registrar: registrar.clone(),
|
||||
domain_status: domain_status.clone(),
|
||||
registrant: vec![Registrant::new(
|
||||
reg_name.clone(),
|
||||
reg_org.clone(),
|
||||
reg_email,
|
||||
RegistrantType::Registrant,
|
||||
)],
|
||||
nameservers: nameservers,
|
||||
dnssec: dnssec[0].clone(),
|
||||
eligibility_type: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn return_regex(caps: Vec<String>, index: usize) -> String {
|
||||
let data: String;
|
||||
match caps.get(index) {
|
||||
Some(tmp) => {
|
||||
data = tmp.to_string();
|
||||
}
|
||||
None => {
|
||||
data = String::from("None");
|
||||
}
|
||||
}
|
||||
data
|
||||
}
|
||||
}
|
||||
168
src/whois/au.rs
Normal file
168
src/whois/au.rs
Normal file
@ -0,0 +1,168 @@
|
||||
use crate::whois::whois_base::{
|
||||
NameServer, RegexQuery, Registrant, RegistrantType, Whois, WhoisData,
|
||||
};
|
||||
use whois_rust::{WhoIs, WhoIsLookupOptions};
|
||||
|
||||
struct Eligibility {
|
||||
e_type: Option<String>,
|
||||
e_name: Option<String>,
|
||||
e_id: Option<String>,
|
||||
r_id: Option<String>,
|
||||
}
|
||||
|
||||
impl Eligibility {
|
||||
fn new() -> Eligibility {
|
||||
Eligibility {
|
||||
e_id: Some(String::new()),
|
||||
e_name: Some(String::new()),
|
||||
e_type: Some(String::new()),
|
||||
r_id: Some(String::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct _Whois {
|
||||
base: WhoisData,
|
||||
eligibility: Eligibility,
|
||||
}
|
||||
|
||||
impl Whois for _Whois {
|
||||
fn new() -> Self {
|
||||
_Whois {
|
||||
base: WhoisData::new(),
|
||||
eligibility: Eligibility::new(),
|
||||
}
|
||||
}
|
||||
fn to_vec(&self) -> Vec<String> {
|
||||
let mut ret_vec: Vec<String> = vec![];
|
||||
let mut registrar = String::from("Registrar: ");
|
||||
registrar.push_str(&self.base.registrar.clone().unwrap());
|
||||
ret_vec.push(registrar);
|
||||
|
||||
let mut domain_status = String::from("Status: ");
|
||||
domain_status.push_str(&self.base.domain_status.clone().unwrap());
|
||||
ret_vec.push(domain_status);
|
||||
|
||||
let mut dnssec = String::from("DNSSEC: ");
|
||||
dnssec.push_str(&self.base.dnssec.clone().unwrap());
|
||||
ret_vec.push(dnssec);
|
||||
|
||||
let mut registrant_type = String::new();
|
||||
for registrant in &self.base.registrant.clone().unwrap() {
|
||||
match registrant.rtype {
|
||||
RegistrantType::Admin => {
|
||||
registrant_type.push_str("Admin ");
|
||||
}
|
||||
RegistrantType::Billing => {
|
||||
registrant_type.push_str("Billing ");
|
||||
}
|
||||
RegistrantType::Tech => {
|
||||
registrant_type.push_str("Tech ");
|
||||
}
|
||||
RegistrantType::Registrant => {
|
||||
registrant_type.push_str("Registrant ");
|
||||
}
|
||||
}
|
||||
let mut name = registrant_type.clone();
|
||||
name.push_str("Name: ");
|
||||
name.push_str(®istrant.name);
|
||||
ret_vec.push(name);
|
||||
|
||||
let mut org = registrant_type.clone();
|
||||
org.push_str("Organisation: ");
|
||||
org.push_str(®istrant.org);
|
||||
ret_vec.push(org);
|
||||
|
||||
let mut email = registrant_type.clone();
|
||||
email.push_str("Email: ");
|
||||
email.push_str(®istrant.email);
|
||||
ret_vec.push(email);
|
||||
}
|
||||
|
||||
for nameserver in &self.base.nameservers.clone().unwrap() {
|
||||
let mut tmp = String::from("Nameserver: ");
|
||||
tmp.push_str(&nameserver.host);
|
||||
ret_vec.push(tmp);
|
||||
}
|
||||
|
||||
let mut eligibility_name = String::from("Eligibility Name: ");
|
||||
eligibility_name.push_str(&self.eligibility.e_name.clone().unwrap());
|
||||
ret_vec.push(eligibility_name);
|
||||
|
||||
let mut eligibility_type = String::from("Eligibility Type: ");
|
||||
eligibility_type.push_str(&self.eligibility.e_type.clone().unwrap());
|
||||
ret_vec.push(eligibility_type);
|
||||
|
||||
let mut eligibility_id = String::from("Eligibility ID: ");
|
||||
eligibility_id.push_str(&self.eligibility.e_id.clone().unwrap());
|
||||
ret_vec.push(eligibility_id);
|
||||
|
||||
let mut registrant_id = String::from("Registrant ID: ");
|
||||
registrant_id.push_str(&self.eligibility.r_id.clone().unwrap());
|
||||
ret_vec.push(registrant_id);
|
||||
|
||||
return ret_vec;
|
||||
}
|
||||
fn lookup(&mut self, domain: String) -> Vec<String> {
|
||||
let whois = WhoIs::from_path("servers.json").unwrap();
|
||||
let result: String = whois
|
||||
.lookup(WhoIsLookupOptions::from_string(domain).unwrap())
|
||||
.unwrap();
|
||||
let registrar_regex =
|
||||
RegexQuery::new(String::from(r"(?i)(.*registrar:|registrar *name:)(.*)"));
|
||||
let domain_status_regex =
|
||||
RegexQuery::new(String::from(r"(?i)(.*domain status:|.*status:)(.* )"));
|
||||
// TODO: Capture the registrant info for each type
|
||||
let registrant_name_regex = RegexQuery::new(String::from(r"(?i)(registrant.*name:)(.*)"));
|
||||
let registrant_org_regex =
|
||||
RegexQuery::new(String::from(r"(?i)(registrant org.*:|registrant:)(.*)"));
|
||||
let registrant_email_regex = RegexQuery::new(String::from(r"(?i)(registrant email:)(.*)"));
|
||||
let nameserver_regex =
|
||||
RegexQuery::new(String::from(r"(?i)(nameservers*:|name servers*:)(.*)"));
|
||||
let dnssec_regex = RegexQuery::new(String::from(r"(?i)(.*dnssec:)(.*)"));
|
||||
let eligebility_name_regex =
|
||||
RegexQuery::new(String::from(r"(?i)(.*eligibility name:)(.*)"));
|
||||
let eligebility_type_regex =
|
||||
RegexQuery::new(String::from(r"(?i)(.*eligibility type:)(.*)"));
|
||||
let eligebility_id_regex = RegexQuery::new(String::from(r"(?i)(.*eligibility id:)(.*)"));
|
||||
let registrant_id_regex = RegexQuery::new(String::from(r"(?i)(.*registrant id:)(.*)"));
|
||||
|
||||
let registrar = WhoisData::return_regex(registrar_regex.get_matches(&result), 0);
|
||||
let domain_status = WhoisData::return_regex(domain_status_regex.get_matches(&result), 0);
|
||||
let reg_name = WhoisData::return_regex(registrant_name_regex.get_matches(&result), 0);
|
||||
let reg_org = WhoisData::return_regex(registrant_org_regex.get_matches(&result), 0);
|
||||
let reg_email = WhoisData::return_regex(registrant_email_regex.get_matches(&result), 0);
|
||||
|
||||
let mut nameservers = vec![];
|
||||
for nameserver in nameserver_regex.get_matches(&result) {
|
||||
nameservers.push(NameServer::new(nameserver));
|
||||
}
|
||||
let mut dnssec = dnssec_regex.get_matches(&result);
|
||||
if dnssec.len() == 0 {
|
||||
dnssec = vec![String::from("Failed to get DNSSEC")];
|
||||
}
|
||||
let eligebility_name =
|
||||
WhoisData::return_regex(eligebility_name_regex.get_matches(&result), 0);
|
||||
let eligebility_type =
|
||||
WhoisData::return_regex(eligebility_type_regex.get_matches(&result), 0);
|
||||
let eligebility_id = WhoisData::return_regex(eligebility_id_regex.get_matches(&result), 0);
|
||||
let registrant_id = WhoisData::return_regex(registrant_id_regex.get_matches(&result), 0);
|
||||
|
||||
// println!("{:?}", registrar[0]);
|
||||
self.base.domain_status = Some(domain_status.clone());
|
||||
self.base.registrant = Some(vec![Registrant::new(
|
||||
reg_name.clone(),
|
||||
reg_org.clone(),
|
||||
reg_email,
|
||||
RegistrantType::Registrant,
|
||||
)]);
|
||||
self.base.nameservers = Some(nameservers);
|
||||
self.base.dnssec = Some(dnssec[0].clone());
|
||||
self.base.registrar = Some(registrar.clone());
|
||||
self.eligibility.e_name = Some(eligebility_name.clone());
|
||||
self.eligibility.e_type = Some(eligebility_type.clone());
|
||||
self.eligibility.e_id = Some(eligebility_id.clone());
|
||||
self.eligibility.r_id = Some(registrant_id.clone());
|
||||
self.to_vec()
|
||||
}
|
||||
}
|
||||
117
src/whois/default.rs
Normal file
117
src/whois/default.rs
Normal file
@ -0,0 +1,117 @@
|
||||
use crate::whois::whois_base::{
|
||||
NameServer, RegexQuery, Registrant, RegistrantType, Whois, WhoisData,
|
||||
};
|
||||
use whois_rust::{WhoIs, WhoIsLookupOptions};
|
||||
pub struct _Whois {
|
||||
base: WhoisData,
|
||||
}
|
||||
|
||||
impl Whois for _Whois {
|
||||
fn new() -> _Whois {
|
||||
_Whois {
|
||||
base: WhoisData::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_vec(&self) -> Vec<String> {
|
||||
let mut ret_vec: Vec<String> = vec![];
|
||||
let mut registrar = String::from("Registrar: ");
|
||||
registrar.push_str(&self.base.registrar.clone().unwrap());
|
||||
ret_vec.push(registrar);
|
||||
|
||||
let mut domain_status = String::from("Status: ");
|
||||
domain_status.push_str(&self.base.domain_status.clone().unwrap());
|
||||
ret_vec.push(domain_status);
|
||||
|
||||
let mut dnssec = String::from("DNSSEC: ");
|
||||
dnssec.push_str(&self.base.dnssec.clone().unwrap());
|
||||
ret_vec.push(dnssec);
|
||||
|
||||
let mut registrant_type = String::new();
|
||||
for registrant in &self.base.registrant.clone().unwrap() {
|
||||
match registrant.rtype {
|
||||
RegistrantType::Admin => {
|
||||
registrant_type.push_str("Admin ");
|
||||
}
|
||||
RegistrantType::Billing => {
|
||||
registrant_type.push_str("Billing ");
|
||||
}
|
||||
RegistrantType::Tech => {
|
||||
registrant_type.push_str("Tech ");
|
||||
}
|
||||
RegistrantType::Registrant => {
|
||||
registrant_type.push_str("Registrant ");
|
||||
}
|
||||
}
|
||||
let mut name = registrant_type.clone();
|
||||
name.push_str("Name: ");
|
||||
name.push_str(®istrant.name);
|
||||
ret_vec.push(name);
|
||||
|
||||
let mut org = registrant_type.clone();
|
||||
org.push_str("Organisation: ");
|
||||
org.push_str(®istrant.org);
|
||||
ret_vec.push(org);
|
||||
|
||||
let mut email = registrant_type.clone();
|
||||
email.push_str("Email: ");
|
||||
email.push_str(®istrant.email);
|
||||
ret_vec.push(email);
|
||||
}
|
||||
|
||||
for nameserver in &self.base.nameservers.clone().unwrap() {
|
||||
let mut tmp = String::from("Nameserver: ");
|
||||
tmp.push_str(&nameserver.host);
|
||||
ret_vec.push(tmp);
|
||||
}
|
||||
|
||||
return ret_vec;
|
||||
}
|
||||
|
||||
fn lookup(&mut self, domain: String) -> Vec<String> {
|
||||
let whois = WhoIs::from_path("servers.json").unwrap();
|
||||
let result: String = whois
|
||||
.lookup(WhoIsLookupOptions::from_string(domain).unwrap())
|
||||
.unwrap();
|
||||
let registrar_regex =
|
||||
RegexQuery::new(String::from(r"(?i)(.*registrar:|registrar *name:)(.*)"));
|
||||
let domain_status_regex =
|
||||
RegexQuery::new(String::from(r"(?i)(.*domain status:|.*status:)(.* )"));
|
||||
// TODO: Capture the registrant info for each type
|
||||
let registrant_name_regex = RegexQuery::new(String::from(r"(?i)(registrant.*name:)(.*)"));
|
||||
let registrant_org_regex =
|
||||
RegexQuery::new(String::from(r"(?i)(registrant org.*:|registrant:)(.*)"));
|
||||
let registrant_email_regex = RegexQuery::new(String::from(r"(?i)(registrant email:)(.*)"));
|
||||
let nameserver_regex =
|
||||
RegexQuery::new(String::from(r"(?i)(nameservers*:|name servers*:)(.*)"));
|
||||
let dnssec_regex = RegexQuery::new(String::from(r"(?i)(.*dnssec:)(.*)"));
|
||||
|
||||
let registrar = WhoisData::return_regex(registrar_regex.get_matches(&result), 0);
|
||||
let domain_status = WhoisData::return_regex(domain_status_regex.get_matches(&result), 0);
|
||||
let reg_name = WhoisData::return_regex(registrant_name_regex.get_matches(&result), 0);
|
||||
let reg_org = WhoisData::return_regex(registrant_org_regex.get_matches(&result), 0);
|
||||
let reg_email = WhoisData::return_regex(registrant_email_regex.get_matches(&result), 0);
|
||||
|
||||
let mut nameservers = vec![];
|
||||
for nameserver in nameserver_regex.get_matches(&result) {
|
||||
nameservers.push(NameServer::new(nameserver));
|
||||
}
|
||||
let mut dnssec = dnssec_regex.get_matches(&result);
|
||||
if dnssec.len() == 0 {
|
||||
dnssec = vec![String::from("Failed to get DNSSEC")];
|
||||
}
|
||||
// println!("{:?}", registrar[0]);
|
||||
self.base.domain_status = Some(domain_status.clone());
|
||||
self.base.registrant = Some(vec![Registrant::new(
|
||||
reg_name.clone(),
|
||||
reg_org.clone(),
|
||||
reg_email,
|
||||
RegistrantType::Registrant,
|
||||
)]);
|
||||
self.base.nameservers = Some(nameservers);
|
||||
self.base.dnssec = Some(dnssec[0].clone());
|
||||
self.base.registrar = Some(registrar.clone());
|
||||
|
||||
self.to_vec()
|
||||
}
|
||||
}
|
||||
5
src/whois/mod.rs
Normal file
5
src/whois/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
pub mod au;
|
||||
pub mod default;
|
||||
pub mod selector;
|
||||
pub mod uk;
|
||||
pub mod whois_base;
|
||||
33
src/whois/selector.rs
Normal file
33
src/whois/selector.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use addr::parse_domain_name;
|
||||
|
||||
use super::{au, default, uk, whois_base::Whois};
|
||||
|
||||
pub fn select_whois_server(domain: String) -> Result<Box<dyn Whois>, String> {
|
||||
let domain = parse_domain_name(&domain.as_str());
|
||||
match domain {
|
||||
Ok(val) => match val.suffix() {
|
||||
"com.au" => Ok(Box::new(au::_Whois::new())),
|
||||
"org.au" => Ok(Box::new(au::_Whois::new())),
|
||||
"net.au" => Ok(Box::new(au::_Whois::new())),
|
||||
"co.uk" => Ok(Box::new(uk::_Whois::new())),
|
||||
"au" => Ok(Box::new(au::_Whois::new())),
|
||||
_ => Ok(Box::new(default::_Whois::new())),
|
||||
},
|
||||
Err(_) => Err(String::from("Failed to select whois server")),
|
||||
}
|
||||
}
|
||||
|
||||
// impl RegexQuery {
|
||||
// pub fn new(expression: String) -> RegexQuery {
|
||||
// let re = Regex::new(&expression).unwrap();
|
||||
// RegexQuery { re }
|
||||
// }
|
||||
|
||||
// pub fn get_matches(&self, haystack: &str) -> Vec<String> {
|
||||
// let mut results = vec![];
|
||||
// for (_, [_, rex2]) in self.re.captures_iter(haystack).map(|c| c.extract()) {
|
||||
// results.push(String::from(rex2.trim()));
|
||||
// }
|
||||
// results
|
||||
// }
|
||||
// }
|
||||
70
src/whois/uk.rs
Normal file
70
src/whois/uk.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use crate::whois::whois_base::{
|
||||
NameServer, RegexQuery, Registrant, RegistrantType, Whois, WhoisData,
|
||||
};
|
||||
use regex::Regex;
|
||||
use whois_rust::{WhoIs, WhoIsLookupOptions};
|
||||
|
||||
// (.*registrar:\n)(.*)
|
||||
|
||||
// (.*name servers:\n)((.*\n)+?^\n)
|
||||
|
||||
pub struct _Whois {
|
||||
base: WhoisData,
|
||||
}
|
||||
|
||||
impl Whois for _Whois {
|
||||
fn new() -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
_Whois {
|
||||
base: WhoisData::new(),
|
||||
}
|
||||
}
|
||||
fn to_vec(&self) -> Vec<String> {
|
||||
let mut ret_vec: Vec<String> = vec![];
|
||||
let mut registrar = String::from("Registrar: ");
|
||||
registrar.push_str(&self.base.registrar.clone().unwrap());
|
||||
ret_vec.push(registrar);
|
||||
|
||||
for nameserver in &self.base.nameservers.clone().unwrap() {
|
||||
let mut tmp = String::from("Nameserver: ");
|
||||
tmp.push_str(&nameserver.host);
|
||||
ret_vec.push(tmp);
|
||||
}
|
||||
|
||||
return ret_vec;
|
||||
}
|
||||
fn lookup(&mut self, domain: String) -> Vec<String> {
|
||||
let whois = WhoIs::from_path("servers.json").unwrap();
|
||||
let result: String = whois
|
||||
.lookup(WhoIsLookupOptions::from_string(domain).unwrap())
|
||||
.unwrap();
|
||||
|
||||
let registrar_regex = RegexQuery::new(String::from(r"(?mi)(.*registrar:.*\n)(.*)"));
|
||||
let nameserver_regex =
|
||||
RegexQuery::new(String::from(r"(?miR)(.*name servers:\r\n)((.+\.+.+)+)"));
|
||||
|
||||
let registrar = WhoisData::return_regex(registrar_regex.get_matches(&result), 0);
|
||||
let nameservers = nameserver_regex.captures(&result);
|
||||
// println!("{:?}", nameservers);
|
||||
|
||||
self.base.registrar = Some(registrar.clone());
|
||||
self.base.nameservers = Some(nameservers.clone());
|
||||
|
||||
self.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl _Whois {
|
||||
pub fn get_matches(&self, haystack: &str, expression: String) -> Vec<String> {
|
||||
let re = Regex::new(&expression).unwrap();
|
||||
let mut results = vec![];
|
||||
for (_, [_, rex2]) in re.captures_iter(haystack).map(|c| c.extract()) {
|
||||
results.push(String::from(rex2.trim()));
|
||||
println!("{:?}", rex2.trim())
|
||||
}
|
||||
println!("{:?}", results);
|
||||
results
|
||||
}
|
||||
}
|
||||
123
src/whois/whois_base.rs
Normal file
123
src/whois/whois_base.rs
Normal file
@ -0,0 +1,123 @@
|
||||
use crate::logger::Logger;
|
||||
use core::fmt;
|
||||
use regex::Regex;
|
||||
|
||||
pub trait Whois {
|
||||
fn new() -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
fn lookup(&mut self, domain: String) -> Vec<String>;
|
||||
fn to_vec(&self) -> Vec<String>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RegistrantType {
|
||||
Registrant,
|
||||
Admin,
|
||||
Tech,
|
||||
Billing,
|
||||
}
|
||||
|
||||
pub struct RegexQuery {
|
||||
re: Regex,
|
||||
}
|
||||
|
||||
impl RegexQuery {
|
||||
pub fn new(expression: String) -> RegexQuery {
|
||||
let re = Regex::new(&expression).unwrap();
|
||||
RegexQuery { re }
|
||||
}
|
||||
|
||||
pub fn get_matches(&self, haystack: &str) -> Vec<String> {
|
||||
let mut results = vec![];
|
||||
for (_, [_, rex2]) in self.re.captures_iter(haystack).map(|c| c.extract()) {
|
||||
results.push(String::from(rex2.trim()));
|
||||
}
|
||||
results
|
||||
}
|
||||
pub fn captures(&self, haystack: &str) -> Vec<NameServer> {
|
||||
// let logger = Logger::new("log.txt");
|
||||
let mut results: Vec<NameServer> = vec![];
|
||||
match self.re.captures(haystack) {
|
||||
Some(rex) => {
|
||||
for x in 1..rex.len() {
|
||||
if x == 1 {
|
||||
continue;
|
||||
};
|
||||
results.push(NameServer::new(String::from(
|
||||
rex.get(x).unwrap().as_str().trim(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
results
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NameServer {
|
||||
pub host: String,
|
||||
}
|
||||
|
||||
impl NameServer {
|
||||
pub fn new(host: String) -> NameServer {
|
||||
NameServer { host }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Registrant {
|
||||
pub name: String,
|
||||
pub org: String,
|
||||
pub email: String,
|
||||
pub rtype: RegistrantType,
|
||||
}
|
||||
|
||||
impl fmt::Display for Registrant {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "")
|
||||
}
|
||||
}
|
||||
|
||||
impl Registrant {
|
||||
pub fn new(name: String, org: String, email: String, rtype: RegistrantType) -> Registrant {
|
||||
Registrant {
|
||||
name,
|
||||
org,
|
||||
email,
|
||||
rtype,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct WhoisData {
|
||||
pub registrar: Option<String>,
|
||||
pub domain_status: Option<String>,
|
||||
pub registrant: Option<Vec<Registrant>>,
|
||||
pub nameservers: Option<Vec<NameServer>>,
|
||||
pub dnssec: Option<String>,
|
||||
}
|
||||
|
||||
impl WhoisData {
|
||||
pub fn new() -> WhoisData {
|
||||
WhoisData {
|
||||
registrar: None,
|
||||
domain_status: None,
|
||||
registrant: None,
|
||||
nameservers: None,
|
||||
dnssec: None,
|
||||
}
|
||||
}
|
||||
pub fn return_regex(caps: Vec<String>, index: usize) -> String {
|
||||
let data: String;
|
||||
match caps.get(index) {
|
||||
Some(tmp) => {
|
||||
data = tmp.to_string();
|
||||
}
|
||||
None => {
|
||||
data = String::from("None");
|
||||
}
|
||||
}
|
||||
data
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user