commit 85d85a4ae4692bc80f3d8f5115eaeef89b3a20c4 Author: Paul Lecuq Date: Fri May 27 13:59:17 2022 +0200 initial version of ipblc after blparser renaming diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..678ff0c --- /dev/null +++ b/.drone.yml @@ -0,0 +1,39 @@ +--- +kind: pipeline +type: docker +name: default + +steps: + - name: test and build + image: rust:1 + commands: + - apt-get update -y + - apt-get install -y libzmq3-dev libnftnl-dev libmnl-dev + - cargo build --verbose --all + - cargo test --verbose --all + when: + event: push + - name: release + image: rust:1 + commands: + - apt-get update -y + - apt-get install -y libzmq3-dev libnftnl-dev libmnl-dev + - cargo build --release --verbose --all + - cd target/release + - tar -czvf ipblc-${DRONE_TAG}.tar.gz ipblc + when: + event: tag + - name: publish + image: plugins/gitea-release + settings: + base_url: https://git.paulbsd.com + api_key: + from_secret: gitea_token + files: "target/release/*.tar.gz" + checksum: + - sha256 + - sha512 + environment: + PLUGIN_TITLE: "" + when: + event: tag diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3766596 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.json +*.swp +/*.gz +/perf* +/sample +/target \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f5197ee --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1277 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "serde", + "time", + "winapi", +] + +[[package]] +name = "clap" +version = "3.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" +dependencies = [ + "atty", + "bitflags", + "clap_lex", + "indexmap", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "err-derive" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34a887c8df3ed90498c1c437ce21f211c8e27672921a8ffa293cb8d6d4caa9e" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "rustversion", + "syn", + "synstructure", +] + +[[package]] +name = "error-chain" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "h2" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +dependencies = [ + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "ipblc" +version = "1.0.0" +dependencies = [ + "chrono", + "clap", + "ipnet", + "lazy_static", + "mnl", + "nftnl", + "nix", + "regex", + "reqwest", + "serde", + "serde_json", + "tokio", + "zmq", +] + +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + +[[package]] +name = "itoa" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" + +[[package]] +name = "js-sys" +version = "0.3.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "metadeps" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b122901b3a675fac8cecf68dcb2f0d3036193bc861d1ac0e1c337f7d5254c2" +dependencies = [ + "error-chain", + "pkg-config", + "toml", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "mnl" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1a5469630da93e1813bb257964c0ccee3b26b6879dd858039ddec35cc8681ed" +dependencies = [ + "libc", + "log", + "mnl-sys", +] + +[[package]] +name = "mnl-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9750685b201e1ecfaaf7aa5d0387829170fa565989cc481b49080aa155f70457" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "nftnl" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9201688bd0bc571dfa4c21ce0a525480c8b782776cf88e12571fa89108dd920" +dependencies = [ + "bitflags", + "err-derive", + "log", + "nftnl-sys", +] + +[[package]] +name = "nftnl-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b5c587b6a5e76a3a5d51e0a757ae66dbff38c277563485807ae979ce361b56" +dependencies = [ + "cfg-if", + "libc", + "pkg-config", +] + +[[package]] +name = "nix" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f17df307904acd05aa8e32e97bb20f2a0df1728bbc2d771ae8f9a90463441e9" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" + +[[package]] +name = "os_str_bytes" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" + +[[package]] +name = "reqwest" +version = "0.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustls" +version = "0.20.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360" +dependencies = [ + "base64", +] + +[[package]] +name = "rustversion" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" + +[[package]] +name = "ryu" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-util" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736b60249cb25337bc196faa43ee12c705e426f3d55c214d73a4e7be06f92cb4" + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +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.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" + +[[package]] +name = "web-sys" +version = "0.3.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d8de8415c823c8abd270ad483c6feeac771fad964890779f9a8cb24fbbc1bf" +dependencies = [ + "webpki", +] + +[[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-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[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_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[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_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "zmq" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aad98a7a617d608cd9e1127147f630d24af07c7cd95ba1533246d96cbdd76c66" +dependencies = [ + "bitflags", + "libc", + "log", + "zmq-sys", +] + +[[package]] +name = "zmq-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d33a2c51dde24d5b451a2ed4b488266df221a5eaee2ee519933dc46b9a9b3648" +dependencies = [ + "libc", + "metadeps", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4580562 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "ipblc" +version = "1.0.0" +edition = "2021" +authors = ["PaulBSD "] +description = "ipblc is a tool that search and send attacking ip addresses to ipbl" +repository = "https://git.paulbsd.com/paulbsd/ipblc" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +chrono = { version = "0.4", features = ["serde"] } +clap = "3.1" +ipnet = "2.5" +lazy_static = "1.4" +mnl = "0.2" +nftnl = "0.6" +nix = "0.24" +regex = "1.5" +reqwest = { version = "0.11", default-features = false, features = ["json","rustls-tls"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +tokio = { version = "1.18", features = ["full"] } +zmq = "0.9" + +# [target.aarch64-unknown-linux-gnu.dependencies] +# flate2 = { version = "1.0", features = ["cloudflare_zlib"] } + +# [target.aarch64-linux-android.dependencies] +# flate2 = { version = "1.0", features = ["zlib"] } + +# [target.armv7-unknown-linux-gnueabihf.dependencies] +# flate2 = { version = "1.0", features = ["zlib"] } + +# [target.x86_64-unknown-linux-gnu.dependencies] +# flate2 = { version = "1.0", features = ["cloudflare_zlib"] } + +[profile.release] +debug = false +opt-level = 3 \ No newline at end of file diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 0000000..2f5ddcb --- /dev/null +++ b/Cross.toml @@ -0,0 +1,2 @@ +[target.aarch64-unknown-linux-musl] +image = "cross-arm64:latest" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a9a1404 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM rustembedded/cross:aarch64-unknown-linux-musl + +RUN dpkg --add-architecture arm64 +RUN apt-get update +RUN apt-get install -y libasound2-dev:arm64 libzmq3-dev libnftnl-dev libmnl-dev libmnl0:arm64 libnftnl7:arm64 libmnl0:amd64 libnftnl0:arm64 +RUN apt-get clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..c1c2cc6 --- /dev/null +++ b/README.md @@ -0,0 +1,86 @@ +# ipblc + +[![Build Status](https://drone.paulbsd.com/api/badges/paulbsd/ipblc/status.svg)](https://drone.paulbsd.com/paulbsd/ipblc) + +## Summary + +ipblc is a tool that search and send attacking ip addresses to ipbl +It's notification features are based on zeromq + +## Howto + +### Build + +- Dev: + +```bash +cargo build +``` + +- Release (with compiler optimizations) + +```bash +cargo build --release +``` + +### Usage + +``` +USAGE: + ipblc [OPTIONS] + +OPTIONS: + -d Enable debugging + -h, --help Print help information + -s, --server Sets a ipbl server [default: https://ipbl.paulbsd.com] + -V, --version Print version informatio +``` + +### TODO + +- ✅ Config centralization (Main config in ipbl) +- ✅ Handles date in log +- ✅ fine grain file opening +- ✅ Handle zeromq data transfer +- ✅ Code optimizations (WIP) +- ✅ Error handing when fetching config + +### Date formats + +``` +nginx: 2022-02-09T10:05:02+01:00 +ssh: 2022-02-09T09:29:15.797469+01:00 +openvpn: 2022-02-09 09:58:59 +mail: 2022-02-09T09:59:31.228303+01:00 +``` + +## License + +```text +Copyright (c) 2021, 2022 PaulBSD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of this project. +``` diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..2c148f4 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,384 @@ +use crate::ip::*; +use crate::utils::*; + +use chrono::prelude::*; +use chrono::Duration; +use clap::{Arg, ArgMatches, Command}; +use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify, WatchDescriptor}; +use regex::Regex; +use reqwest::{Client, Error as ReqError, Response}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; +use std::path::Path; + +const SERVER: &str = "ipbl.paulbsd.com"; + +#[derive(Debug, Clone)] +pub struct Context { + pub blocklist: HashMap, + pub cfg: Config, + pub client: Client, + pub discovery: Discovery, + pub flags: Flags, + pub hostname: String, + pub instance: Box, + pub sas: HashMap, + pub hashwd: HashMap, +} + +#[derive(Debug, Clone)] +pub struct SetMap { + pub filename: String, + pub fullpath: String, + pub regex: Regex, + pub set: Set, + pub watchedfiles: HashMap, + pub wd: WatchDescriptor, +} + +#[derive(Debug, Clone)] +pub struct Flags { + pub debug: bool, + pub interval: usize, + pub server: String, +} + +impl Context { + pub async fn new() -> Self { + // Get flags + let debug = Context::argparse().is_present("debug"); + let server = Context::argparse() + .value_of("server") + .unwrap_or(format!("https://{}", SERVER).as_str()) + .to_string(); + + // Build context + let mut ctx = Context { + cfg: Config::new(), + flags: Flags { + debug: debug, + server: server, + interval: 60, + }, + hostname: gethostname(true), + discovery: Discovery { + version: "1.0".to_string(), + urls: HashMap::new(), + }, + client: Client::builder() + .user_agent(format!( + "{}/{}@{}", + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION"), + gethostname(false) + )) + .build() + .unwrap(), + sas: HashMap::new(), + instance: Box::new(Inotify::init(InitFlags::empty()).unwrap()), + blocklist: HashMap::new(), + hashwd: HashMap::new(), + }; + ctx.discovery = ctx.discovery().await.unwrap(); + print!("Loading config ... "); + match ctx.load().await { + Ok(_) => {} + Err(err) => { + println!("error loading config: {err}"); + std::process::exit(1); + } + } + ctx + } + + pub fn argparse() -> ArgMatches { + Command::new(env!("CARGO_PKG_NAME")) + .version(env!("CARGO_PKG_VERSION")) + .author(env!("CARGO_PKG_AUTHORS")) + .about(env!("CARGO_PKG_DESCRIPTION")) + .arg( + Arg::new("server") + .short('s') + .long("server") + .value_name("server") + .default_value("https://ipbl.paulbsd.com") + .help("Sets a http server") + .takes_value(true), + ) + .arg( + Arg::new("debug") + .short('d') + .takes_value(false) + .help("Enable debugging"), + ) + .get_matches() + } + + pub async fn discovery(&self) -> Result { + let resp: Result = self + .client + .get(format!("{server}/discovery", server = self.flags.server)) + .send() + .await; + let req = match resp { + Ok(re) => re, + Err(err) => return Err(err), + }; + let data: Discovery = match req.json().await { + Ok(res) => res, + Err(err) => return Err(err), + }; + Ok(data) + } + + pub async fn load(&mut self) -> Result<(), Box> { + self.cfg.load(self.to_owned()).await?; + self.create_sas().await?; + Ok(()) + } + + pub async fn get_blocklist(&self) -> Vec { + let mut res: Vec = vec![]; + for (_, v) in self.blocklist.iter() { + res.push(v.clone()); + } + res + } + + pub async fn gc_blocklist(&mut self) -> Vec { + let now: DateTime = Local::now().trunc_subsecs(0); + let delta: Duration = Duration::minutes(self.flags.interval as i64); + let mindate = now - delta; + let mut toremove: Vec = vec![]; + + // nightly, future use + //let drained: HashMap = ctx.blocklist.drain_filter(|k,v| v.parse_date() < mindate) + for (k, v) in self.blocklist.clone().iter() { + if v.parse_date() < mindate { + self.blocklist.remove(&k.to_string()).unwrap(); + toremove.push(v.clone()); + } + } + toremove + } + + pub async fn update_blocklist(&mut self, ip: &IpData) { + if !self.blocklist.contains_key(&ip.ip) { + self.blocklist.insert(ip.ip.clone(), ip.clone()); + } + } + + pub async fn create_sas(&mut self) -> Result<(), Box> { + for set in &self.cfg.sets { + let p = Path::new(set.path.as_str()); + if p.is_dir() { + let res = match self.hashwd.get(&set.path.to_string()) { + Some(wd) => *wd, + None => { + let res = self + .instance + .add_watch(set.path.as_str(), AddWatchFlags::IN_MODIFY) + .unwrap(); + self.hashwd.insert(set.path.to_string(), res); + res + } + }; + let fullpath: String = match set.filename.as_str() { + "" => set.path.clone(), + _ => { + format!( + "{path}/{filename}", + path = set.path, + filename = set.filename.clone() + ) + } + }; + self.sas.insert( + set.t.clone(), + SetMap { + filename: set.filename.clone(), + fullpath: fullpath, + set: set.clone(), + regex: Regex::new(set.regex.as_str()).unwrap(), + wd: res, + watchedfiles: HashMap::new(), + }, + ); + } + } + Ok(()) + } +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct Config { + pub sets: Vec, + #[serde(skip_serializing)] + pub trustnets: Vec, + pub zmq: HashMap, +} + +impl Config { + pub fn new() -> Self { + Self { + sets: vec![ + Set { + t: "smtp".to_string(), + filename: "mail.log".to_string(), + regex: "(SASL LOGIN authentication failed)".to_string(), + path: "/var/log".to_string(), + }, + Set { + t: "ssh".to_string(), + filename: "auth.log".to_string(), + regex: "(Invalid user|BREAK|not allowed because|no matching key exchange method found)".to_string(), + path: "/var/log".to_string(), + }, + Set { + t: "http".to_string(), + filename: "".to_string(), + regex: "(anonymousfox.co)".to_string(), + path: "/var/log/nginx".to_string(), + } + ,Set { + t: "openvpn".to_string(), + filename: "status".to_string(), + regex: "(UNDEF)".to_string(), + path: "/var/run/openvpn".to_string(), + }, + ], + trustnets: vec![ + "127.0.0.0/8".to_string(), + "10.0.0.0/8".to_string(), + "172.16.0.0/12".to_string(), + "192.168.0.0/16".to_string(), + ], + zmq: HashMap::from([("pubsub".to_string(),ZMQ{ + t: "pubsub".to_string(), + hostname: SERVER.to_string(), + port: 9999, + subscription: "ipbl".to_string(), + }),("reqrep".to_string(),ZMQ { + t: "reqrep".to_string(), + hostname: SERVER.to_string(), + port: 9998, + subscription: String::new(), + })]) + } + } + + pub async fn load(&mut self, ctx: Context) -> Result<(), ReqError> { + self.get_sets(&ctx).await?; + self.get_trustnets(&ctx).await?; + self.get_zmq_config(&ctx).await?; + Ok(()) + } + + async fn get_trustnets(&mut self, ctx: &Context) -> Result<(), ReqError> { + let resp: Result = ctx + .client + .get(format!( + "{server}/config/trustlist", + server = ctx.flags.server + )) + .send() + .await; + let req = match resp { + Ok(re) => re, + Err(err) => return Err(err), + }; + let data: Vec = match req.json::>().await { + Ok(res) => res, + Err(err) => return Err(err), + }; + self.trustnets = data; + Ok(()) + } + + async fn get_sets(&mut self, ctx: &Context) -> Result<(), ReqError> { + let resp: Result = ctx + .client + .get(format!("{server}/config/sets", server = ctx.flags.server)) + .send() + .await; + let req = match resp { + Ok(re) => re, + Err(err) => return Err(err), + }; + let data: Vec = match req.json::>().await { + Ok(res) => res, + Err(err) => return Err(err), + }; + self.sets = data; + Ok(()) + } + + async fn get_zmq_config(&mut self, ctx: &Context) -> Result<(), ReqError> { + let resp: Result = ctx + .client + .get(format!("{server}/config/zmq", server = ctx.flags.server)) + .send() + .await; + let req = match resp { + Ok(re) => re, + Err(err) => return Err(err), + }; + let data: HashMap = match req.json::>().await { + Ok(res) => { + let mut out: HashMap = HashMap::new(); + res.into_iter().map(|x| x).for_each(|x| { + out.insert(x.t.to_string(), x); + }); + out + } + Err(err) => return Err(err), + }; + self.zmq = data; + Ok(()) + } +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct Set { + #[serde(rename = "type")] + pub t: String, + pub filename: String, + pub regex: String, + pub path: String, +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct ZMQ { + #[serde(rename = "type")] + pub t: String, + pub hostname: String, + pub port: i64, + pub subscription: String, +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct Discovery { + pub version: String, + pub urls: HashMap, +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct URL { + pub key: String, + pub path: String, +} + +impl PartialEq for Set { + fn eq(&self, other: &Self) -> bool { + self.t == other.t + } +} + +impl Eq for Set {} + +impl Hash for Set { + fn hash(&self, state: &mut H) { + self.t.hash(state); + } +} diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..9a79785 --- /dev/null +++ b/src/db.rs @@ -0,0 +1,47 @@ +use crate::config::*; +use crate::ip::*; + +use futures::executor::block_on; +use rbatis; +use rbatis::crud::{Skip, CRUD}; +use rbatis::rbatis::Rbatis; +use rbatis::DateTimeUtc; +use std::process::exit; + +const DBTYPE: &'static str = "postgres"; + +pub async fn init(rb: &Rbatis, cfg: &Config) { + let connection: String = format!( + "{t}://{username}:{password}@{hostname}:{port}/{name}", + t = DBTYPE, + username = cfg.dbusername, + password = cfg.dbpassword, + hostname = cfg.dbhostname, + port = cfg.dbport, + name = cfg.dbname, + ) + .to_string(); + let link = rb.link(connection.as_str()).await; + match link { + Ok(_) => (), + Err(err) => { + println!("Please check connection parameters: {err}"); + exit(1); + } + } +} + +pub fn push_ip(ip: String, src: &String, rb: &Rbatis) { + let i = IP { + id: Some(0), + ip: Some(ip), + rdns: None, + src: Some(src.to_string()), + created: Some(DateTimeUtc::now()), + updated: Some(DateTimeUtc::now()), + }; + match block_on(rb.save(&i, &[Skip::Column("id")])) { + Ok(r) => println!("{r:?}"), + Err(err) => println!("{error}", error = err.to_string()), + }; +} diff --git a/src/firewall/mod.rs b/src/firewall/mod.rs new file mode 100644 index 0000000..8bf42d7 --- /dev/null +++ b/src/firewall/mod.rs @@ -0,0 +1,95 @@ +use crate::ip::*; + +use nftnl::{nft_expr, Batch, Chain, FinalizedBatch, ProtoFamily, Rule, Table}; +use std::{ffi::CString, io::*, net::Ipv4Addr}; + +pub fn init(tablename: &String) -> (Batch, Table) { + let mut batch = Batch::new(); + + let table = Table::new( + &CString::new(tablename.as_str()).unwrap(), + ProtoFamily::Ipv4, + ); + + batch.add(&table, nftnl::MsgType::Add); + batch.add(&table, nftnl::MsgType::Del); + + batch.add(&table, nftnl::MsgType::Add); + (batch, table) +} + +pub fn block( + tablename: &String, + ips_add: &Vec, + ret: &mut Vec, +) -> std::result::Result<(), Error> { + // convert chain + let ips_add = convert(ips_add); + let (mut batch, table) = init(tablename); + + // build chain + let mut chain = Chain::new(&CString::new(tablename.as_str()).unwrap(), &table); + chain.set_hook(nftnl::Hook::In, 1); + chain.set_policy(nftnl::Policy::Accept); + + // add chain + batch.add(&chain, nftnl::MsgType::Add); + + // build and add rules + for ip in ips_add.clone() { + let mut rule = Rule::new(&chain); + rule.add_expr(&nft_expr!(payload ipv4 saddr)); + rule.add_expr(&nft_expr!(cmp == ip)); + rule.add_expr(&nft_expr!(ct state)); + rule.add_expr(&nft_expr!(bitwise mask 10u32, xor 0u32)); + rule.add_expr(&nft_expr!(cmp != 0u32)); + rule.add_expr(&nft_expr!(counter)); + rule.add_expr(&nft_expr!(verdict drop)); + batch.add(&rule, nftnl::MsgType::Add); + } + + // validate and send batch + let finalized_batch = batch.finalize(); + send_and_process(&finalized_batch)?; + ret.push(format!( + "nftables: {length} ip in memory", + length = ips_add.len() + )); + Ok(()) +} + +fn send_and_process(batch: &FinalizedBatch) -> std::result::Result<(), Error> { + let seq: u32 = 2; + let socket = mnl::Socket::new(mnl::Bus::Netfilter)?; + socket.send_all(batch)?; + let mut buffer = vec![0; nftnl::nft_nlmsg_maxsize() as usize]; + while let Some(message) = socket_recv(&socket, &mut buffer[..])? { + match mnl::cb_run(message, seq, socket.portid())? { + mnl::CbResult::Stop => { + break; + } + mnl::CbResult::Ok => (), + } + } + Ok(()) +} + +fn socket_recv<'a>( + socket: &mnl::Socket, + buf: &'a mut [u8], +) -> std::result::Result, Error> { + let ret = socket.recv(buf)?; + if ret > 0 { + Ok(Some(&buf[..ret])) + } else { + Ok(None) + } +} + +fn convert(input: &Vec) -> Vec { + let mut output: Vec = vec![]; + for val in input { + output.push(val.ip.parse::().unwrap()); + } + output +} diff --git a/src/ip.rs b/src/ip.rs new file mode 100644 index 0000000..20914f3 --- /dev/null +++ b/src/ip.rs @@ -0,0 +1,252 @@ +use crate::config::Context; +use crate::utils::*; + +use chrono::prelude::*; +use ipnet::IpNet; +use lazy_static::lazy_static; +use regex::Regex; +use reqwest::Error as ReqError; +use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::fmt::{Display, Formatter}; +use std::io::{BufRead, BufReader, Read}; +use std::net::IpAddr; + +lazy_static! { + static ref R_IPV4: Regex = Regex::new(include_str!("regexps/ipv4.txt")).unwrap(); + static ref R_IPV6: Regex = Regex::new(include_str!("regexps/ipv6.txt")).unwrap(); + static ref R_DATE: Regex = Regex::new(include_str!("regexps/date.txt")).unwrap(); +} + +#[derive(Clone, Debug, Serialize, Deserialize, Eq)] +pub struct IpData { + pub ip: String, + pub src: String, + pub date: String, + pub hostname: String, +} + +impl IpData { + pub fn parse_date(&self) -> DateTime { + DateTime::parse_from_rfc3339(self.date.as_str()).unwrap() + } +} + +impl PartialEq for IpData { + fn eq(&self, other: &IpData) -> bool { + self.ip.as_bytes() == other.ip.as_bytes() && self.src == other.src + } + fn ne(&self, other: &IpData) -> bool { + !self.eq(other) + } +} + +impl Ord for IpData { + fn cmp(&self, other: &IpData) -> Ordering { + self.ip.as_bytes().cmp(&other.ip.as_bytes()) + } +} + +impl PartialOrd for IpData { + fn partial_cmp(&self, other: &IpData) -> Option { + Some(self.cmp(&other)) + } +} + +impl Display for IpData { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!( + f, + "ip: {ip}, src: {src}, date: {date}, hostname: {hostname}", + ip = self.ip, + src = self.src, + date = self.date, + hostname = self.hostname, + ) + } +} + +pub async fn push_ip(ctx: &Context, ip: &IpData, ret: &mut Vec) -> Result<(), ReqError> { + let result: String; + let mut data: Vec = vec![]; + + data.push(IpData { + ip: ip.ip.to_string(), + src: ip.src.to_string(), + date: ip.date.to_string(), + hostname: ip.hostname.to_string(), + }); + + let resp = ctx + .client + .post(format!("{server}/ips", server = ctx.flags.server)) + .json(&data) + .send() + .await?; + ret.push(format!("status: {status}", status = resp.status())); + + let res = resp.text().await.unwrap(); + + if res.trim().len() > 0 { + result = res.trim().to_string(); + } else { + result = "".to_string(); + } + + ret.push(format!("response: {result}")); + Ok(()) +} + +pub async fn _push_ip_bulk( + ctx: &Context, + ips: &Vec, + ret: &mut Vec, +) -> Result<(), ReqError> { + let result: String; + let mut data: Vec = vec![]; + + for ip in ips { + data.push(IpData { + ip: ip.ip.to_string(), + src: ip.src.to_string(), + date: ip.date.to_string(), + hostname: ip.hostname.to_string(), + }) + } + + let resp = ctx + .client + .post(format!("{server}/ips", server = ctx.flags.server)) + .json(&data) + .send() + .await?; + ret.push(format!("status: {status}", status = resp.status())); + + let res = resp.text().await.unwrap(); + + if res.trim().len() > 0 { + result = res.trim().to_string(); + } else { + result = "".to_string(); + } + + ret.push(format!("response: {result}")); + Ok(()) +} + +pub fn filter( + lines: Box, + list: &mut Vec, + trustnets: &Vec, + regex: &Regex, + src: &String, + lastprocess: &DateTime, +) -> isize { + let mut ips = 0; + let hostname = gethostname(true); + for line in BufReader::new(lines).lines() { + if let Ok(l) = line { + if regex.is_match(l.as_str()) { + let s_ipaddr: String; + + match R_IPV4.captures(l.as_str()) { + Some(sv4) => { + s_ipaddr = sv4.get(0).unwrap().as_str().to_string(); + } + None => { + continue; + /*match R_IPV6.captures(l.as_str()) { + Some(sv6) => { + s_ipaddr = sv6.get(0).unwrap().as_str().to_string(); + } + None => { + continue; + } + };*/ + } + }; + + let s_date: DateTime; + match R_DATE.captures(l.as_str()) { + Some(sdt) => { + s_date = parse_date(sdt); + if &s_date < lastprocess { + continue; + } + } + None => { + s_date = Local::now(); + } + }; + + let ipaddr: IpAddr = match s_ipaddr.parse() { + Ok(ip) => ip, + Err(err) => { + println!("unparseable IP: {err} {s_ipaddr}"); + continue; + } + }; + + if !is_trusted(&ipaddr, &trustnets) { + list.push(IpData { + ip: s_ipaddr, + src: src.to_owned(), + date: s_date.to_rfc3339().to_owned(), + hostname: hostname.to_owned(), + }); + ips += 1; + }; + } + } + } + ips +} + +fn parse_date(input: regex::Captures) -> DateTime { + let mut ymd: Vec = vec![]; + let mut hms: Vec = vec![]; + + let (daterange, hourrange) = (2..5, 5..8); + + for i in daterange { + ymd.push(input.get(i).unwrap().as_str().parse::().unwrap()); + } + for i in hourrange { + hms.push(input.get(i).unwrap().as_str().parse::().unwrap()); + } + + let date = Local + .ymd(ymd[0] as i32, ymd[1] as u32, ymd[2] as u32) + .and_hms(hms[0] as u32, hms[1] as u32, hms[2] as u32); + date +} + +fn is_trusted(ip: &IpAddr, trustnets: &Vec) -> bool { + for net in trustnets { + if net.contains(ip) { + return true; + } + } + false +} + +pub async fn _get_last(ctx: &Context) -> Result, ReqError> { + let resp = ctx + .client + .get(format!("{server}/ips/last", server = ctx.flags.server)) + .query(&[("interval", "3 hours")]) + .send() + .await; + + let req = match resp { + Ok(re) => re, + Err(err) => return Err(err), + }; + + let data: Vec = match req.json::>().await { + Ok(res) => res, + Err(err) => return Err(err), + }; + + Ok(data) +} diff --git a/src/ipblc/full.rs b/src/ipblc/full.rs new file mode 100644 index 0000000..94e6068 --- /dev/null +++ b/src/ipblc/full.rs @@ -0,0 +1,73 @@ +use crate::api; +use crate::config::*; +use crate::ip; +use crate::utils::*; + +use chrono::prelude::*; +use regex::Regex; +use std::sync::Arc; + +pub async fn process(ctx: Arc) { + let arcctx = Arc::clone(&ctx); + let hostname = arcctx.hostname.as_str().to_string(); + + loop { + let mut ret = String::new(); + ret.push_str(format!("host: {hostname}, ", hostname = hostname).as_str()); + let list = process_lines(arcctx.clone()).await; + + loop { + match api::push_ip_bulk(&list, arcctx.flags.server.clone(), &mut ret).await { + Ok(_) => { + break println!("{ret}"); + } + Err(err) => { + println!("{err}"); + sleep(1); + } + }; + } + sleep(ctx.flags.interval as u64); + println!("{ret}"); + } +} + +async fn process_lines(ctx: Arc) -> Vec { + let mut ret = String::new(); + let mut list: Vec = vec![]; + let trustnets = build_trustnets(ctx.cfg.trustnets.clone()); + let lastprocess = Local::now(); + + for folder in &ctx.cfg.folders { + for set in &folder.sets { + let regex = Regex::new(set.regex.as_str()).unwrap(); + let filenames = get_filenames(&set.filename); + let mut numip = 0; + + for filename in filenames { + numip = numip + + super::handle_file( + &mut list, + &filename, + set, + ®ex, + &lastprocess, + &trustnets, + ) + .await; + } + ret.push_str(format!("{t}: {numip}, ", t = set.t, numip = numip).as_str()); + } + } + + let oldlen = list.len(); + let newlen = dedup(&mut list); + ret.push_str( + format!( + "dedup ratio: {percent:.2}%, ", + percent = ((oldlen - newlen) as f64 / oldlen as f64) * 100. + ) + .as_str(), + ); + list +} diff --git a/src/ipblc/inc.rs b/src/ipblc/inc.rs new file mode 100644 index 0000000..e76a36b --- /dev/null +++ b/src/ipblc/inc.rs @@ -0,0 +1,180 @@ +use super::*; + +use chrono::prelude::*; +use std::collections::HashMap; +use std::sync::Arc; +use tokio::sync::mpsc::{channel, Receiver, Sender}; +use tokio::sync::Mutex; + +const BL_CHAN_SIZE: usize = 32; +const ZMQ_CHAN_SIZE: usize = 64; + +pub async fn process(ctx: &Arc>) { + println!( + "Launching {} version {}", + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION") + ); + + let (ipdatatx, mut ipdatarx): (Sender, Receiver) = channel(ZMQ_CHAN_SIZE); + + // initialize the firewall table + //firewall::init(&env!("CARGO_PKG_NAME").to_string()); + + // initialize zeromq sockets + let reqsocket; + let subsocket; + { + let ctxarc = Arc::clone(&ctx); + let zmqctx = ctxarc.lock().await; + reqsocket = zconnect(&zmqctx.cfg.zmq.get("reqrep").unwrap(), zmq::REQ) + .await + .unwrap(); + subsocket = zconnect(&zmqctx.cfg.zmq.get("pubsub").unwrap(), zmq::SUB) + .await + .unwrap(); + } + + listenpubsub(&ctx, ipdatatx.clone(), subsocket).await; + + let mut blrx = watchfiles(&ctx).await; + + let ctxarc = Arc::clone(&ctx); + tokio::spawn(async move { + compare_files_changes(&ctxarc, &mut blrx, &ipdatatx).await; + }); + + loop { + let mut ret: Vec = Vec::new(); + + // wait for logs parse and zmq channel receive + let ip = ipdatarx.recv().await.unwrap(); + + // lock the context mutex + let ctxarc = Arc::clone(&ctx); + let mut ctx = ctxarc.lock().await; + + // refresh context blocklist + ctx.update_blocklist(&ip).await; + ctx.gc_blocklist().await; + + // send ip list to ws and zmq sockets + if ip.hostname == ctx.hostname { + send_to_ipbl_ws(&ctx, &ip, &mut ret).await; + send_to_ipbl_zmq(&reqsocket, &ip).await; + } + + // apply firewall blocking + firewall::block( + &env!("CARGO_PKG_NAME").to_string(), + &ctx.get_blocklist().await, + &mut ret, + ) + .unwrap(); + + // log lines + println!("{ret}", ret = ret.join(", ")); + + // reload configuration from the server + match ctx.load().await { + Ok(_) => {} + Err(err) => { + println!("error loading config: {err}"); + } + } + } +} + +async fn watchfiles(ctx: &Arc>) -> Receiver { + let (bltx, blrx): (Sender, Receiver) = channel(BL_CHAN_SIZE); + let ctx = Arc::clone(ctx); + tokio::spawn(async move { + loop { + let events: Vec; + { + let ctx = ctx.lock().await; + events = ctx.instance.read_events().unwrap(); + } + + for event in events { + let date: DateTime = Local::now().trunc_subsecs(0); + bltx.send(FileEvent { + ie: event, + date: date, + }) + .await + .unwrap(); + } + } + }); + blrx +} + +async fn get_last_file_size(watchedfiles: &mut HashMap, path: &str) -> u64 { + let currentlen = match std::fs::metadata(&path.to_string()) { + Ok(u) => u.len().clone(), + Err(_) => 0u64, + }; + let lastlen = match watchedfiles.insert(path.to_string(), currentlen) { + Some(u) => u, + None => 0, + }; + lastlen +} + +async fn compare_files_changes( + ctx: &Arc>, + inotifyrx: &mut Receiver, + ipdatatx: &Sender, +) { + let mut trustnets; + loop { + let modifiedfiles = inotifyrx.recv().await.unwrap(); + let mut list: Vec = vec![]; + + let mut ctx = ctx.lock().await; + trustnets = build_trustnets(&ctx.cfg.trustnets); + + match modifiedfiles.ie.name { + Some(name) => { + let inotify_filename = name.to_str().unwrap(); + for sak in &mut ctx.clone().sas.keys() { + let sa = &mut ctx.sas.get_mut(sak).unwrap(); + if modifiedfiles.ie.wd == sa.wd { + let handle_filename: String; + if sa.filename.as_str() == "" { + handle_filename = format!("{}/{}", &sa.fullpath, inotify_filename); + } else if inotify_filename.starts_with(sa.filename.as_str()) { + handle_filename = sa.fullpath.to_owned(); + } else { + continue; + } + + let filesize = + get_last_file_size(&mut sa.watchedfiles, &handle_filename).await; + + match read_lines(&handle_filename, filesize) { + Some(lines) => { + filter( + lines, + &mut list, + &trustnets, + &sa.regex, + &sa.set.t, + &modifiedfiles.date, + ); + } + None => {} + }; + break; + } + } + drop(ctx); + for ip in list { + ipdatatx.send(ip).await.unwrap(); + } + } + None => {} + } + } +} diff --git a/src/ipblc/mod.rs b/src/ipblc/mod.rs new file mode 100644 index 0000000..e5883b5 --- /dev/null +++ b/src/ipblc/mod.rs @@ -0,0 +1,79 @@ +pub mod inc; + +use crate::config::*; +use crate::firewall; +use crate::ip::*; +use crate::utils::*; +use crate::zmqcom::*; + +use chrono::prelude::{DateTime, Local}; +use nix::sys::inotify::InotifyEvent; +use std::sync::Arc; +use tokio::sync::mpsc::Sender; +use tokio::sync::Mutex; + +pub struct FileEvent { + pub ie: InotifyEvent, + pub date: DateTime, +} + +impl std::fmt::Debug for FileEvent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{ie:?}", ie = self.ie) + } +} + +async fn send_to_ipbl_zmq(socket: &zmq::Socket, ip: &IpData) { + let msg = format!("{value}", value = serde_json::to_string(&ip).unwrap()); + socket.send(&msg, 0).unwrap(); + socket.recv_string(0).unwrap().unwrap(); +} + +async fn send_to_ipbl_ws(ctx: &Context, ip: &IpData, ret: &mut Vec) { + ret.push(format!("host: {hostname}", hostname = ctx.hostname)); + loop { + match push_ip(&ctx, &ip, ret).await { + Ok(_) => { + break; + } + Err(err) => { + println!("{err}"); + sleep(1); + } + }; + } +} + +async fn listenpubsub(ctx: &Arc>, txpubsub: Sender, socket: zmq::Socket) { + let ctx = ctx.lock().await; + let prefix = format!( + "{subscription} ", + subscription = ctx.cfg.zmq.get("pubsub").unwrap().subscription + ); + socket + .set_subscribe(ctx.cfg.zmq.get("pubsub").unwrap().subscription.as_bytes()) + .expect("failed setting subscription"); + drop(ctx); + + tokio::spawn(async move { + loop { + let msgs: Option = match socket.recv_string(0) { + Ok(s) => match s { + Ok(ss) => Some(ss), + Err(_) => None, + }, + Err(_) => None, + }; + match msgs { + Some(ss) => { + let msg = ss.strip_prefix(prefix.as_str()).unwrap(); + let tosend: IpData = serde_json::from_str(msg).unwrap(); + if tosend.hostname != gethostname(true) { + txpubsub.send(tosend).await.unwrap(); + } + } + None => {} + }; + } + }); +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d3ae296 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,17 @@ +mod config; +mod firewall; +mod ip; +mod ipblc; +mod utils; +mod zmqcom; + +use config::Context; +use std::sync::Arc; +use tokio::sync::Mutex; + +#[tokio::main] +pub async fn main() { + // Create a new context + let ctx = Arc::new(Mutex::new(Context::new().await)); + ipblc::inc::process(&ctx).await; +} diff --git a/src/regexps/date.txt b/src/regexps/date.txt new file mode 100644 index 0000000..caead4e --- /dev/null +++ b/src/regexps/date.txt @@ -0,0 +1 @@ +((\d{1,4})[-/](\d{2})[-/](\d{2})[T ](\d{2}):(\d{2}):(\d{2})((\.\d{6})?[+-](\d{2}):(\d{2}))?) \ No newline at end of file diff --git a/src/regexps/ipv4.txt b/src/regexps/ipv4.txt new file mode 100644 index 0000000..2a4c436 --- /dev/null +++ b/src/regexps/ipv4.txt @@ -0,0 +1 @@ +(?:[0-9]{1,3}\.){3}[0-9]{1,3} \ No newline at end of file diff --git a/src/regexps/ipv6.txt b/src/regexps/ipv6.txt new file mode 100644 index 0000000..d69593c --- /dev/null +++ b/src/regexps/ipv6.txt @@ -0,0 +1 @@ +((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$)) \ No newline at end of file diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..2b729a1 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,82 @@ +use ipnet::IpNet; +use lazy_static::lazy_static; +use nix::unistd; +use regex::Regex; +use std::boxed::Box; +use std::fs::File; +use std::io::*; +use std::path::Path; +use std::time::Duration; + +lazy_static! { + static ref R_FILE_GZIP: Regex = Regex::new(r".*\.gz.*").unwrap(); +} + +pub fn read_lines(filename: &String, offset: u64) -> Option> { + let mut file = match File::open(filename) { + Ok(f) => f, + Err(err) => { + println!("{err}"); + return None; + } + }; + file.seek(SeekFrom::Start(offset)).unwrap(); + let lines: Box = Box::new(BufReader::new(file)); + Some(lines) +} + +pub fn _dedup(list: &mut Vec) -> usize { + // Begin with sorting entries + list.sort(); + // Then deduplicate + list.dedup(); + // Return the length + list.len() +} + +pub fn build_trustnets(cfgtrustnets: &Vec) -> Vec { + let mut trustnets: Vec = vec![]; + for trustnet in cfgtrustnets { + match trustnet.parse() { + Ok(net) => trustnets.push(net), + Err(err) => { + println!("error parsing {trustnet}, error: {err}"); + } + }; + } + trustnets +} + +pub fn sleep(seconds: u64) { + std::thread::sleep(Duration::from_secs(seconds)); +} + +pub fn gethostname(show_fqdn: bool) -> String { + let mut buf = [0u8; 64]; + let hostname_cstr = unistd::gethostname(&mut buf).expect("Failed getting hostname"); + let fqdn = hostname_cstr + .to_str() + .expect("Hostname wasn't valid UTF-8") + .to_string(); + let hostname: Vec<&str> = fqdn.split(".").collect(); + if show_fqdn { + return fqdn; + } + hostname[0].to_string() +} + +pub fn _search_subfolders(path: &Path) -> Vec { + let dirs = std::fs::read_dir(path).unwrap(); + let mut folders: Vec = vec![]; + for dir in dirs { + let dirpath = dir.unwrap().path(); + let path = Path::new(dirpath.as_path()); + if path.is_dir() { + folders.push(dirpath.to_str().unwrap().to_string()); + for f in _search_subfolders(path) { + folders.push(f); + } + } + } + folders +} diff --git a/src/zmqcom.rs b/src/zmqcom.rs new file mode 100644 index 0000000..62061c3 --- /dev/null +++ b/src/zmqcom.rs @@ -0,0 +1,15 @@ +use crate::config::*; + +use zmq; + +const ZMQPROTO: &str = "tcp"; + +pub async fn zconnect(zmqcfg: &ZMQ, zmqtype: zmq::SocketType) -> Result { + let zctx = zmq::Context::new(); + let zmqhost = &zmqcfg.hostname; + let zmqport = zmqcfg.port; + let socket = zctx.socket(zmqtype).unwrap(); + let connectstring = format!("{ZMQPROTO}://{zmqhost}:{zmqport}"); + socket.connect(&connectstring.as_str())?; + Ok(socket) +} diff --git a/tests/test.py b/tests/test.py new file mode 100755 index 0000000..8744a73 --- /dev/null +++ b/tests/test.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 + +import zmq + +context = zmq.Context() +socket = context.socket(zmq.SUB) +socket.connect("tcp://sys01:9999") +socket.setsockopt_string(zmq.SUBSCRIBE, "ipbl") + +while True: + msg = socket.recv_string() + print(msg) diff --git a/tests/testfw.rs b/tests/testfw.rs new file mode 100644 index 0000000..9ecabab --- /dev/null +++ b/tests/testfw.rs @@ -0,0 +1,50 @@ +use ipnet::Ipv4Net; +use nftnl::{nft_set, set::Set, Batch, FinalizedBatch, ProtoFamily, Table}; +use std::{ffi::CString, io::*, net::Ipv4Addr}; + +fn main() -> std::result::Result<(), Error> { + let mut batch = Batch::new(); + let table = Table::new(&CString::new("aa").unwrap(), ProtoFamily::Inet); + let mut set: Set = nft_set!( + &CString::new("blacklist").unwrap(), + 1, + &table, + ProtoFamily::Inet + ); + let toadd = "9.9.9.8".parse::().unwrap(); + set.add(&toadd); + println!("2"); + batch.add(&set, nftnl::MsgType::Add); + let finalized_batch = batch.finalize(); + send_and_process(&finalized_batch)?; + Ok(()) +} + +fn send_and_process(batch: &FinalizedBatch) -> std::result::Result<(), Error> { + let socket = mnl::Socket::new(mnl::Bus::Netfilter)?; + socket.send_all(batch)?; + let portid = socket.portid(); + let mut buffer = vec![0; nftnl::nft_nlmsg_maxsize() as usize]; + let very_unclear_what_this_is_for = 2; + while let Some(message) = socket_recv(&socket, &mut buffer[..])? { + match mnl::cb_run(message, very_unclear_what_this_is_for, portid)? { + mnl::CbResult::Stop => { + break; + } + mnl::CbResult::Ok => (), + } + } + Ok(()) +} + +fn socket_recv<'a>( + socket: &mnl::Socket, + buf: &'a mut [u8], +) -> std::result::Result, Error> { + let ret = socket.recv(buf)?; + if ret > 0 { + Ok(Some(&buf[..ret])) + } else { + Ok(None) + } +}