From 1e48775af24829c0526e998d56255e27cfc8f935 Mon Sep 17 00:00:00 2001 From: rncwnd Date: Mon, 1 Jul 2024 15:24:30 +0100 Subject: [PATCH] Push up exploritory work --- Cargo.lock | 604 ++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 17 ++ flake.lock | 117 ++++++++ flake.nix | 103 +++++++ pcaps/dns.cap | Bin 0 -> 4338 bytes src/README.md | 32 +++ src/main.rs | 73 +++++ src/protocols/dns.rs | 202 ++++++++++++++ src/protocols/icmp.rs | 1 + src/protocols/mod.rs | 32 +++ src/protocols/ssh.rs | 1 + src/util.rs | 48 ++++ 12 files changed, 1230 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 pcaps/dns.cap create mode 100644 src/README.md create mode 100644 src/main.rs create mode 100644 src/protocols/dns.rs create mode 100644 src/protocols/icmp.rs create mode 100644 src/protocols/mod.rs create mode 100644 src/protocols/ssh.rs create mode 100644 src/util.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..fd88c1c --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,604 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cc" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "dipper" +version = "0.1.0" +dependencies = [ + "bitflags 1.3.2", + "clap", + "etherparse", + "log", + "nom", + "pcap", + "simple-dns", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "etherparse" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "827292ea592108849932ad8e30218f8b1f21c0dfd0696698a18b5d0aed62d990" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libloading" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pcap" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e935fc73d54a89fff576526c2ccd42bbf8247aae05b358693475b14fd4ff79" +dependencies = [ + "bitflags 1.3.2", + "errno", + "libc", + "libloading", + "pkg-config", + "regex", + "windows-sys 0.36.1", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "simple-dns" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3625957337d21eb40a7125c2df5c92db5c0267195d66b297948c816ea9c33157" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a05852e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "dipper" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bitflags = "1.3.2" +clap = { version = "4.1.4", features = ["derive"] } +etherparse = "0.13.0" +log = "0.4.17" +nom = "7.1.3" +pcap = "1.0.0" +simple-dns = "0.7.0" +tracing = "0.1.37" +tracing-subscriber = "0.3.16" diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..4d94037 --- /dev/null +++ b/flake.lock @@ -0,0 +1,117 @@ +{ + "nodes": { + "crane": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1719249093, + "narHash": "sha256-0q1haa3sw6GbmJ+WhogMnducZGjEaCa/iR6hF2vq80I=", + "owner": "ipetkov", + "repo": "crane", + "rev": "9791c77eb7e98b8d8ac5b0305d47282f994411ca", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1719254875, + "narHash": "sha256-ECni+IkwXjusHsm9Sexdtq8weAq/yUyt1TWIemXt3Ko=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "2893f56de08021cffd9b6b6dfc70fd9ccd51eb60", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1718428119, + "narHash": "sha256-WdWDpNaq6u1IPtxtYHHWpl5BmabtpmLnMAx0RdJ/vo8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e6cea36f83499eb4e9cd184c8a8e823296b50ad5", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "crane": "crane", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1719541143, + "narHash": "sha256-YdHqW6EM5pXMwXHhC+KniBv3aquXuJrFar2XXaV7x+c=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "ed12832f267ab223cd085b0bd6ee3432caa69067", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..1700971 --- /dev/null +++ b/flake.nix @@ -0,0 +1,103 @@ +{ + # Note, a lot of this is boilerplate i just lifted from another project of + # mine. + # TODO: Refactor this. + description = "Dipper, a pure rust DPI system"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + rust-overlay.url = "github:oxalica/rust-overlay"; + flake-utils.url = "github:numtide/flake-utils"; + crane = { + url = "github:ipetkov/crane"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { + self, + nixpkgs, + rust-overlay, + flake-utils, + crane, + ... + }: + flake-utils.lib.eachDefaultSystem (system: let + pkgs = import nixpkgs { + inherit system; + overlays = [rust-overlay.overlays.default]; + }; + inherit (pkgs) lib; + commonPkgs = [ + pkgs.openssl + pkgs.pkg-config + pkgs.protobuf + pkgs.protolint + pkgs.grcov + pkgs.rustc.llvmPackages.llvm + pkgs.llvmPackages.bintools + pkgs.gcc + pkgs.libpcap + pkgs.tcpdump + pkgs.rust-bin.stable.latest.complete + ]; + + # We have protobuf files we want to include. To do this we need to apply a filter + # nix a functional language after all! + protoFilter = path: _type: null != builtins.match ".*proto$" path; + protoOrCargo = path: type: (protoFilter path type) || (craneLib.filterCargoSources path type); + craneLib = crane.lib.${system}; + dipperSrc = lib.cleanSourceWith { + src = craneLib.path ./.; + filter = protoOrCargo; + }; + dipper = craneLib.buildPackage { + src = dipperSrc; + nativeBuildInputs = [pkgs.protobuf]; + buildInputs = [ + commonPkgs + ]; + PROTOC = "${pkgs.protobuf}/bin/protoc"; + doCheck = false; + cargoTestCommand = ""; + }; + + shellPkgs = [ + pkgs.rust-analyzer-unwrapped + pkgs.gpsd + pkgs.cargo-cross + pkgs.rustup + pkgs.cargo2junit + pkgs.mosquitto + pkgs.poetry + pkgs.nodePackages.npm + pkgs.nodejs + ]; + + # shared ShellHook Elements. + sharedHook = '' + export PROTOBUF_LOCATION=${pkgs.protobuf} + export PROTOC_INCLUDE=$PROTOBUF_LOCATION/include + export PROTOC=$PROTOBUF_LOCATION/bin/protoc + export LLVM_TOOL_PATH=${pkgs.rustc.llvmPackages.llvm}/bin + export LD_LIBRARY_PATH=${pkgs.stdenv.cc.cc.lib}/lib/ + export RUST_LOG="info" + export REPORT=true + ''; + in + with pkgs; { + packages.default = dipper; + + apps.default = flake-utils.lib.mkApp { + drv = dipper; + }; + + devShells.default = mkShell { + buildInputs = [ + commonPkgs + shellPkgs + ]; + shellHook = sharedHook; + }; + }); +} diff --git a/pcaps/dns.cap b/pcaps/dns.cap new file mode 100644 index 0000000000000000000000000000000000000000..911d77b58cf242b48590842e5ab292129ea88a91 GIT binary patch literal 4338 zcmca|c+)~A1{MYw`2U}QfsuiMVN<%7)4GZ53~mezV9an}zL8@O!vl$pJZm`^Tp1WF z7#JKF90XI{53E?labU$Nkp^i7QwEjRw*p3t5F;2E7}(PD^V4%unUnK#85jf@7(u2U zn9R|L2#xW$V8CI45kgz3=YRt1&kUS7#P7$!!Y##56El= z21Y&x0sb=E;(|0og@TeI1^dLDoNeh|P9n=l_s6^q-}7FdpE+Z%!URgjF!xXmPSlwX6%`Hx``<%MJ$O$1&Is{ zJP=EgpR+JDGBAKK#CV8RV5b+Kl?FL|`Uj8&AdA3RBED(s?A>6egK{ea0ZR_>fUII* z3{+)M;4w4PHMBI+wbV5`8T)wTGrXK?IoZMP z2l)eH2gE+GO*Z*HpwjE>H;^qL+aP{0G5T-?j~`?h7(pgKT*<|d#=ro^5Hlg>gH4XO z5C!tXlrtbxL8gQK;1VzW;R+r<$bfUd49FLO4GaPbj4VGGSQyxu9yI>{7c?t7I>XCp z!ykP9!5$||&(>Sw_YcTq7Oh9ApcCbj_RKapXx1%^p6Xse^N5MoF-Xu za{^`x^-HhfR~ek-m+S6d zqL$m*3`!cH5C;V~%;hM?AI$J_(h=oiaA#luV~D{JOTflkLS63$3KWnP;4oKp%KP^i z)%DDo#mSID^*;k6$k-DG+ziDG3}6f~5Mnsk*wh@Ffi~!OIwf}%b6KFB0x^qV$aPG#J~W?5X&JB0XxA3 z>N9JQ^&n?7$S{~P7%-Ve1tWaMXvk>5Xuxb}WR6)$f^1-L;%3NXU;tx?*${icHbmd} z3ihNP$aIh`45ke-4DLS-qJmrD?M8eyKx;?_J^==9&Yb+@#GH)$;t~ds#ctl*49*M; zU<|PmVm;VmQ>fRxK$e0mZ;)j$Wl)^4SD_2xHAKiVK_cu{2RFE~2UYqELMs`Jlo=8@ zT%Ux3jVx}z2l7kS)B`J4S={7hkziZRV9F40`N1d;VI*sOPD)|{OMFRkfw)U*T4HHV ziLP5_QE`cGaArxWu3utqDqDPUW=U!>Q%W*hd~R_{axr&lNor1UWpPPrZZRv^j|>co z5C?6Y#LcjbfdPymzJvG_?4TJpzJnap{T}2$kUtqr8)O+;s&6QCfm0B;_kiNZ13U~& zpbWt$z`)0rSFD>_QNof}R03&>V#pMiWT47$LiKXOq z1s^0{*D#neIMf7wOvDH zYmtUWQ->1~A45H`jJ>`IWT>ivZv<3ZLin6j;7Fr3DDApt2d;IranhL_W$VpfC|=p~nH`)_dU qF9R75vW9Iv$QLD@WywVP0%V2#E+i|^BN3Y~)-xD>U@c1~(iZ?o;9Cd) literal 0 HcmV?d00001 diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..0d024ec --- /dev/null +++ b/src/README.md @@ -0,0 +1,32 @@ +# Dipper + +A highly experimental pure rust DPI engine. + +## Rationale + +nDPI exists but it's all C and there's a lot of macros, it's hard to use cleanly +from rust. + +Commercial DPI systems exist, but are prohibitivley expensive. + +Alternative "Kind of DPI" systems like Suricata exist and are great, but are +only part rust. + +## Tools used + +- Nom is used extensivley in order to parse wire formats. +- Etherparse is used to "chunk" packets into their various components. + + +## Goals + +### Short Term + +- Functional offline packet inspection. +- DNS, ICMP, HTTP, maybe SSH parsing and inspection. +- Standardised output format. + +### Long Term + +- Online analysis +- Plugin system? diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..a6835e0 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,73 @@ +mod protocols; +mod util; + +use std::path::PathBuf; + +use clap::Parser; +use pcap::*; +use tracing::*; + +use crate::protocols::*; +use crate::util::Stats; + +#[derive(Parser, Debug)] +#[command(author, version, about)] +struct Opts { + #[arg(short, long)] + pcap_file: Option, + #[arg(long, default_value_t = false)] + print_analysis: bool, +} + +#[tracing::instrument] +fn main() { + tracing_subscriber::fmt() + .pretty() + .with_thread_names(true) + .with_max_level(tracing::Level::TRACE) + .init(); + let opt = Opts::parse(); + let mut pcap = match opt.pcap_file { + Some(pf) => Capture::from_file(pf).expect("Invalid pcap file provided"), + None => { + warn!("Using example.pcap as no pcap was provided"); + Capture::from_file(PathBuf::from("./pcaps/ssh_test.pcap")) + .expect("./pcaps/ssh_test.pcap does not exist") + } + }; + info!("Pcap loaded"); + + let mut stats = Stats::new(); + + while let Ok(packet) = pcap.next_packet() { + stats.total_packets += 1; + match etherparse::SlicedPacket::from_ethernet(packet.data) { + Err(e) => { + error!( + "Slicing packet {} resulted in error {:?}", + stats.total_packets, e + ); + stats.errored_packets += 1; + continue; + } + Ok(sliced) => { + if !sliced.payload.is_empty() { + match match_protocol(sliced.payload.to_vec()) { + Ok(x) => { + stats.known_packets += 1; + trace!("Known packet type found: {:?}", x); + let extracted = extract_info(x, sliced.payload.to_vec()); + if opt.print_analysis { + info!("{:?}", extracted); + } + } + Err(_) => stats.unknown_packets += 1, + } + } + } + } + } + println!("pcap processing complete."); + println!("{}", stats); + //info!("There were {} packets in the pcap", pkts); +} diff --git a/src/protocols/dns.rs b/src/protocols/dns.rs new file mode 100644 index 0000000..ad8a45e --- /dev/null +++ b/src/protocols/dns.rs @@ -0,0 +1,202 @@ +use crate::util::*; +use nom::{ + bytes::complete::{tag, take}, + combinator::map, + multi::{length_data, many_till}, + sequence::tuple, + IResult, Parser, +}; + +use tracing::*; + +#[derive(Debug)] +pub enum DNSType { + Query, + Response, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct DNSValue { + pub txid: u16, + pub flags: u16, + pub question_count: u16, + pub answer_rrs: u16, + pub auth_rrs: u16, + pub additional_rrs: u16, + pub questions: Option>, + pub answers: Option>, + pub question_type: u16, + pub question_class: u16, + pub remainder: Option>, +} + +pub fn is_dns(payload: Vec) -> Result { + if is_dns_query(&payload) { + return Ok(DNSType::Query); + } + if is_dns_response(&payload) { + return Ok(DNSType::Response); + } + Err(()) +} + +fn is_dns_query(payload: &[u8]) -> bool { + payload[2] == 0x01 && payload[3] == 0x00 +} + +fn is_dns_response(payload: &[u8]) -> bool { + payload[2] == 0x81 && payload[3] == 0x80 +} + +fn ld(s: &[u8]) -> IResult<&[u8], &[u8]> { + length_data(nom::number::complete::u8)(s) +} + +fn parse_dns_string(payload: &[u8]) -> IResult<&[u8], (Vec<&[u8]>, &[u8])> { + let mut parser = many_till(ld, tag([0x00])); + parser.parse(payload) +} + +fn take_two_as_u16(payload: &[u8]) -> IResult<&[u8], u16> { + let mut parser = map(take(2_u8), |s: &[u8]| as_u16(s[0], s[1])); + parser.parse(payload) +} + +fn parse_dns_preamble(payload: &[u8]) -> IResult<&[u8], (u16, u16, u16, u16, u16, u16)> { + let mut parser = tuple(( + take_two_as_u16, + take_two_as_u16, + take_two_as_u16, + take_two_as_u16, + take_two_as_u16, + take_two_as_u16, + )); + parser.parse(payload) +} + +fn parse_query_postamble(payload: &[u8]) -> IResult<&[u8], (u16, u16)> { + let mut parser = tuple((take_two_as_u16, take_two_as_u16)); + parser.parse(payload) +} + +fn parsed_dns_string_to_real_string(data: Vec>) -> String { + let mut domain_name = String::from(""); + for (i, subpart) in data.into_iter().enumerate() { + let as_str = String::from_utf8_lossy(&subpart); + if i == 0 { + domain_name = as_str.to_string() + } else { + domain_name = format!("{}.{}", domain_name, as_str); + } + } + domain_name +} + +pub fn analyse_dns_query(payload: Vec) -> DNSValue { + let mut parser = tuple((parse_dns_preamble, parse_dns_string, parse_query_postamble)); + let result = parser.parse(&payload).unwrap(); + let remainder = result.0; + let preamble = result.1 .0; + let string_fragments = result.1 .1 .0; + let postamble = result.1 .2; + trace!("Preamble : {:02X?}", preamble); + trace!("strings {:02X?}", string_fragments); + trace!("postamble {:02X?}", postamble); + let mut string_parts: Vec> = Vec::new(); + for substring in string_fragments { + string_parts.push(substring.to_vec()); + } + let real_query_string = parsed_dns_string_to_real_string(string_parts); + + let real_remainder = if remainder.is_empty() { + None + } else { + Some(remainder.to_vec()) + }; + + DNSValue { + txid: preamble.0, + flags: preamble.1, + question_count: preamble.2, + answer_rrs: preamble.3, + auth_rrs: preamble.4, + additional_rrs: preamble.5, + question_type: postamble.0, + question_class: postamble.1, + remainder: real_remainder, + questions: Some(vec![real_query_string]), + answers: None, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_dns_txt_query_parse() { + let packet_bytes: [u8; 28] = [ + 0x10, 0x32, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x10, 0x00, 0x01, + ]; + let parsed_query = analyse_dns_query(packet_bytes.to_vec()); + + let expected = DNSValue { + txid: 0x1032, + flags: 0x0100, + question_count: 1, + answer_rrs: 0, + auth_rrs: 0, + additional_rrs: 0, + questions: Some(vec!["google.com".to_string()]), + answers: None, + question_type: 16, + question_class: 0x0001, + remainder: None, + }; + + assert!(parsed_query == expected); + } + + #[test] + fn test_parse_dns_string() { + let packet_bytes: [u8; 13] = [ + 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x01, + ]; + let (_, result) = parse_dns_string(&packet_bytes).unwrap(); + let mut string_parts: Vec> = Vec::new(); + for substring in result.0 { + string_parts.push(substring.to_vec()); + } + let real_string = parsed_dns_string_to_real_string(string_parts); + + assert!(real_string == *"google.com") + } + + #[test] + fn test_dns_a_record_query_parse() { + let packet_bytes: [u8; 32] = [ + 0x75, 0xc0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, + 0x77, 0x77, 0x06, 0x6e, 0x65, 0x74, 0x62, 0x73, 0x64, 0x03, 0x6f, 0x72, 0x67, 0x00, + 0x00, 0x01, 0x00, 0x01, + ]; + + let parsed_query = analyse_dns_query(packet_bytes.to_vec()); + + let expected = DNSValue { + txid: 0x75c0, + flags: 0x0100, + question_count: 1, + answer_rrs: 0, + auth_rrs: 0, + additional_rrs: 0, + questions: Some(vec!["www.netbsd.org".to_string()]), + answers: None, + question_type: 1, + question_class: 0x0001, + remainder: None, + }; + + assert!(parsed_query == expected); + } +} diff --git a/src/protocols/icmp.rs b/src/protocols/icmp.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/protocols/icmp.rs @@ -0,0 +1 @@ + diff --git a/src/protocols/mod.rs b/src/protocols/mod.rs new file mode 100644 index 0000000..fd85658 --- /dev/null +++ b/src/protocols/mod.rs @@ -0,0 +1,32 @@ +use dns::{DNSType, DNSValue}; + +mod dns; + +#[derive(Debug)] +pub enum ProtocolType { + DNS(DNSType), +} + +#[derive(Debug)] +pub enum ExtractedInfo { + DNSQuery(DNSValue), +} + +pub fn extract_info(ptype: ProtocolType, payload: Vec) -> Option { + match ptype { + ProtocolType::DNS(x) => match x { + DNSType::Query => { + return Some(ExtractedInfo::DNSQuery(dns::analyse_dns_query(payload))); + } + DNSType::Response => return None, + }, + } +} + +pub fn match_protocol(payload: Vec) -> Result { + match dns::is_dns(payload) { + Ok(x) => return Ok(ProtocolType::DNS(x)), + Err(_) => {} + }; + return Err(()); +} diff --git a/src/protocols/ssh.rs b/src/protocols/ssh.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/protocols/ssh.rs @@ -0,0 +1 @@ + diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..3e87538 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,48 @@ +use std::fmt; +use std::fmt::Display; + +pub fn as_u16(a: u8, b: u8) -> u16 { + (a as u16) << 8 | b as u16 +} + +pub struct Stats { + pub total_packets: usize, + pub known_packets: usize, + pub unknown_packets: usize, + pub errored_packets: usize, + pub empty_payload: usize, +} + +impl Stats { + pub fn new() -> Stats { + Stats { + total_packets: 0, + known_packets: 0, + unknown_packets: 0, + errored_packets: 0, + empty_payload: 0, + } + } + + fn percent_known(self) -> f64 { + self.total_packets as f64 / self.known_packets as f64 + } + + fn percent_error(self) -> f64 { + self.total_packets as f64 / self.errored_packets as f64 + } +} + +impl Display for Stats { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "Total: {}\nKnown: {}\nUnknown: {}\nErrored: {}\nEmpty: {}", + self.total_packets, + self.known_packets, + self.unknown_packets, + self.errored_packets, + self.empty_payload + ) + } +}