From 1f4a1f6adaaa668fe38aefc674391e958d15ae3a Mon Sep 17 00:00:00 2001 From: shellrow Date: Sat, 13 Sep 2025 17:53:31 +0900 Subject: [PATCH] v0.5.0 full-refactoring --- Cargo.toml | 55 +- LICENSE | 2 +- README.md | 90 +- resources/ndb-default-ports.bin | Bin 2008 -> 0 bytes resources/ndb-http-ports.bin | Bin 130 -> 0 bytes resources/ndb-https-ports.bin | Bin 86 -> 0 bytes resources/ndb-os-family-fingerprint.bin | Bin 413267 -> 0 bytes resources/ndb-os-family.bin | Bin 330 -> 0 bytes resources/ndb-os-ttl.bin | Bin 122 -> 0 bytes resources/ndb-oui-vm.bin | Bin 208 -> 0 bytes resources/ndb-oui.bin | Bin 2577732 -> 0 bytes resources/ndb-subdomain.bin | Bin 13401 -> 0 bytes resources/ndb-tcp-service.bin | Bin 317055 -> 0 bytes resources/ndb-wellknown-ports.bin | Bin 1378 -> 0 bytes resources/nrev-default-ports.json | 1002 + resources/nrev-os-class-ttl.json | 17 + resources/nrev-os-db.json | 72429 ++++++++++++++++++++++ resources/nrev-port-probes.json | 1032 + resources/nrev-service-db.json | 5461 ++ resources/nrev-service-probes.json | 528 + resources/nrev-tls-oid-map.json | 29 + resources/nrev-top-subdomains.json | 1002 + resources/nrev-wellknown-ports.json | 687 + src/app.rs | 110 - src/capture/mod.rs | 1 + src/{pcap/mod.rs => capture/pcap.rs} | 180 +- src/cli/host.rs | 90 + src/cli/mod.rs | 354 + src/cli/ping.rs | 21 + src/cli/port.rs | 32 + src/cmd/domain.rs | 38 + src/cmd/host.rs | 84 + src/cmd/interface.rs | 23 + src/cmd/mod.rs | 7 + src/cmd/nei.rs | 64 + src/cmd/ping.rs | 48 + src/cmd/port.rs | 198 + src/cmd/trace.rs | 49 + src/config/db.rs | 18 + src/config/default.rs | 14 + src/config/mod.rs | 58 +- src/db/domain.rs | 6 + src/db/mod.rs | 515 +- src/db/model.rs | 67 - src/db/os.rs | 42 + src/db/oui.rs | 19 + src/db/port.rs | 15 + src/db/service.rs | 161 + src/db/tcp_service.rs | 5847 -- src/db/tls.rs | 39 + src/dep/mod.rs | 39 - src/dep/unix.rs | 5 - src/dep/windows.rs | 48 - src/dns/domain.rs | 21 - src/dns/mod.rs | 332 +- src/dns/probe.rs | 158 + src/dns/resolver.rs | 19 + src/dns/result.rs | 28 - src/dns/scanner.rs | 210 - src/endpoint.rs | 496 + src/fp/mod.rs | 1 - src/fp/setting.rs | 57 - src/fs.rs | 6 - src/handler/check.rs | 15 - src/handler/dns.rs | 191 - src/handler/host.rs | 266 - src/handler/interface.rs | 181 - src/handler/mod.rs | 226 - src/handler/neighbor.rs | 234 - src/handler/ping.rs | 517 - src/handler/port.rs | 385 - src/handler/trace.rs | 300 - src/host.rs | 162 - src/{interface/mod.rs => interface.rs} | 27 +- src/ip/mod.rs | 18 - src/json/host.rs | 37 - src/json/mod.rs | 2 - src/json/port.rs | 33 - src/log.rs | 85 + src/main.rs | 513 +- src/nei/arp.rs | 88 + src/nei/mod.rs | 54 + src/nei/ndp.rs | 114 + src/neighbor/mod.rs | 3 - src/neighbor/resolver.rs | 363 - src/neighbor/result.rs | 32 - src/neighbor/setting.rs | 89 - src/os/mod.rs | 327 + src/os/probe/mod.rs | 1 + src/os/probe/tcp.rs | 163 + src/output.rs | 36 - src/output/domain.rs | 81 + src/output/host.rs | 79 + src/output/interface.rs | 87 + src/output/mod.rs | 52 + src/output/nei.rs | 36 + src/output/ping.rs | 114 + src/output/port.rs | 233 + src/output/progress.rs | 28 + src/output/trace.rs | 83 + src/packet/arp.rs | 70 +- src/packet/frame.rs | 60 - src/packet/icmp.rs | 315 +- src/packet/mod.rs | 8 +- src/packet/ndp.rs | 100 +- src/packet/setting.rs | 170 - src/packet/tcp.rs | 219 +- src/packet/udp.rs | 246 +- src/pcap/setting.rs | 119 - src/ping/mod.rs | 68 +- src/ping/pinger.rs | 719 +- src/ping/probe/icmp.rs | 264 + src/ping/probe/mod.rs | 3 + src/ping/probe/tcp.rs | 276 + src/ping/probe/udp.rs | 265 + src/ping/result.rs | 66 +- src/ping/setting.rs | 48 +- src/probe.rs | 50 +- src/protocol.rs | 27 + src/protocol/mod.rs | 32 - src/scan/async_io.rs | 490 - src/scan/blocking.rs | 352 - src/scan/mod.rs | 60 +- src/scan/packet.rs | 203 - src/scan/payload.rs | 198 - src/scan/probe/icmp.rs | 207 + src/scan/probe/mod.rs | 4 + src/scan/probe/quic.rs | 165 + src/scan/probe/tcp.rs | 609 + src/scan/probe/udp.rs | 207 + src/scan/result.rs | 397 - src/scan/scanner.rs | 122 - src/scan/service.rs | 428 - src/scan/setting.rs | 376 - src/service/mod.rs | 281 + src/service/payload.rs | 48 + src/service/probe/dns.rs | 253 + src/service/probe/generic.rs | 122 + src/service/probe/http.rs | 267 + src/service/probe/mod.rs | 217 + src/service/probe/null.rs | 144 + src/service/probe/quic.rs | 217 + src/service/probe/tls.rs | 181 + src/sys/id.rs | 11 - src/sys/mod.rs | 3 - src/sys/os.rs | 14 - src/sys/time.rs | 20 - src/time.rs | 22 + src/tls/cert.rs | 90 - src/tls/key.rs | 16 - src/tls/mod.rs | 2 - src/trace/mod.rs | 118 +- src/trace/probe/mod.rs | 1 + src/trace/probe/udp.rs | 274 + src/trace/setting.rs | 63 - src/trace/tracer.rs | 344 - src/util/ip.rs | 39 + src/util/json.rs | 29 + src/util/mod.rs | 4 +- src/util/setting.rs | 26 - src/util/tree.rs | 16 - 161 files changed, 91149 insertions(+), 15725 deletions(-) delete mode 100644 resources/ndb-default-ports.bin delete mode 100644 resources/ndb-http-ports.bin delete mode 100644 resources/ndb-https-ports.bin delete mode 100644 resources/ndb-os-family-fingerprint.bin delete mode 100644 resources/ndb-os-family.bin delete mode 100644 resources/ndb-os-ttl.bin delete mode 100644 resources/ndb-oui-vm.bin delete mode 100644 resources/ndb-oui.bin delete mode 100644 resources/ndb-subdomain.bin delete mode 100644 resources/ndb-tcp-service.bin delete mode 100644 resources/ndb-wellknown-ports.bin create mode 100644 resources/nrev-default-ports.json create mode 100644 resources/nrev-os-class-ttl.json create mode 100644 resources/nrev-os-db.json create mode 100644 resources/nrev-port-probes.json create mode 100644 resources/nrev-service-db.json create mode 100644 resources/nrev-service-probes.json create mode 100644 resources/nrev-tls-oid-map.json create mode 100644 resources/nrev-top-subdomains.json create mode 100644 resources/nrev-wellknown-ports.json delete mode 100644 src/app.rs create mode 100644 src/capture/mod.rs rename src/{pcap/mod.rs => capture/pcap.rs} (63%) create mode 100644 src/cli/host.rs create mode 100644 src/cli/mod.rs create mode 100644 src/cli/ping.rs create mode 100644 src/cli/port.rs create mode 100644 src/cmd/domain.rs create mode 100644 src/cmd/host.rs create mode 100644 src/cmd/interface.rs create mode 100644 src/cmd/mod.rs create mode 100644 src/cmd/nei.rs create mode 100644 src/cmd/ping.rs create mode 100644 src/cmd/port.rs create mode 100644 src/cmd/trace.rs create mode 100644 src/config/db.rs create mode 100644 src/config/default.rs create mode 100644 src/db/domain.rs delete mode 100644 src/db/model.rs create mode 100644 src/db/os.rs create mode 100644 src/db/oui.rs create mode 100644 src/db/port.rs create mode 100644 src/db/service.rs delete mode 100644 src/db/tcp_service.rs create mode 100644 src/db/tls.rs delete mode 100644 src/dep/mod.rs delete mode 100644 src/dep/unix.rs delete mode 100644 src/dep/windows.rs delete mode 100644 src/dns/domain.rs create mode 100644 src/dns/probe.rs create mode 100644 src/dns/resolver.rs delete mode 100644 src/dns/result.rs delete mode 100644 src/dns/scanner.rs create mode 100644 src/endpoint.rs delete mode 100644 src/fp/mod.rs delete mode 100644 src/fp/setting.rs delete mode 100644 src/fs.rs delete mode 100644 src/handler/check.rs delete mode 100644 src/handler/dns.rs delete mode 100644 src/handler/host.rs delete mode 100644 src/handler/interface.rs delete mode 100644 src/handler/mod.rs delete mode 100644 src/handler/neighbor.rs delete mode 100644 src/handler/ping.rs delete mode 100644 src/handler/port.rs delete mode 100644 src/handler/trace.rs delete mode 100644 src/host.rs rename src/{interface/mod.rs => interface.rs} (80%) delete mode 100644 src/ip/mod.rs delete mode 100644 src/json/host.rs delete mode 100644 src/json/mod.rs delete mode 100644 src/json/port.rs create mode 100644 src/log.rs create mode 100644 src/nei/arp.rs create mode 100644 src/nei/mod.rs create mode 100644 src/nei/ndp.rs delete mode 100644 src/neighbor/mod.rs delete mode 100644 src/neighbor/resolver.rs delete mode 100644 src/neighbor/result.rs delete mode 100644 src/neighbor/setting.rs create mode 100644 src/os/mod.rs create mode 100644 src/os/probe/mod.rs create mode 100644 src/os/probe/tcp.rs delete mode 100644 src/output.rs create mode 100644 src/output/domain.rs create mode 100644 src/output/host.rs create mode 100644 src/output/interface.rs create mode 100644 src/output/mod.rs create mode 100644 src/output/nei.rs create mode 100644 src/output/ping.rs create mode 100644 src/output/port.rs create mode 100644 src/output/progress.rs create mode 100644 src/output/trace.rs delete mode 100644 src/packet/frame.rs delete mode 100644 src/packet/setting.rs delete mode 100644 src/pcap/setting.rs create mode 100644 src/ping/probe/icmp.rs create mode 100644 src/ping/probe/mod.rs create mode 100644 src/ping/probe/tcp.rs create mode 100644 src/ping/probe/udp.rs create mode 100644 src/protocol.rs delete mode 100644 src/protocol/mod.rs delete mode 100644 src/scan/async_io.rs delete mode 100644 src/scan/blocking.rs delete mode 100644 src/scan/packet.rs delete mode 100644 src/scan/payload.rs create mode 100644 src/scan/probe/icmp.rs create mode 100644 src/scan/probe/mod.rs create mode 100644 src/scan/probe/quic.rs create mode 100644 src/scan/probe/tcp.rs create mode 100644 src/scan/probe/udp.rs delete mode 100644 src/scan/result.rs delete mode 100644 src/scan/scanner.rs delete mode 100644 src/scan/service.rs delete mode 100644 src/scan/setting.rs create mode 100644 src/service/mod.rs create mode 100644 src/service/payload.rs create mode 100644 src/service/probe/dns.rs create mode 100644 src/service/probe/generic.rs create mode 100644 src/service/probe/http.rs create mode 100644 src/service/probe/mod.rs create mode 100644 src/service/probe/null.rs create mode 100644 src/service/probe/quic.rs create mode 100644 src/service/probe/tls.rs delete mode 100644 src/sys/id.rs delete mode 100644 src/sys/mod.rs delete mode 100644 src/sys/os.rs delete mode 100644 src/sys/time.rs create mode 100644 src/time.rs delete mode 100644 src/tls/cert.rs delete mode 100644 src/tls/key.rs delete mode 100644 src/tls/mod.rs create mode 100644 src/trace/probe/mod.rs create mode 100644 src/trace/probe/udp.rs delete mode 100644 src/trace/setting.rs delete mode 100644 src/trace/tracer.rs create mode 100644 src/util/ip.rs create mode 100644 src/util/json.rs delete mode 100644 src/util/setting.rs delete mode 100644 src/util/tree.rs diff --git a/Cargo.toml b/Cargo.toml index 94d698c..2700feb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "nrev" -version = "0.4.0" -edition = "2021" -authors = ["shellrow "] -description = "Simple and Fast Network Revealer/Mapper." +version = "0.5.0" +edition = "2024" +authors = ["shellrow "] +description = "Cross-platform Network Mapper" repository = "https://github.com/shellrow/nrev" homepage = "https://github.com/shellrow/nrev" documentation = "https://github.com/shellrow/nrev" @@ -14,34 +14,41 @@ license = "MIT" [dependencies] anyhow = { version = "1" } +tracing = { version = "0.1" } +tracing-subscriber = { version = "0.3", features = ["time", "chrono"] } serde = { version = "1", features = ["derive"] } serde_json = "1" -netdev = { version = "0.34", features = ["serde"] } -nex = { version = "0.19.1", features = ["serde"] } +bytes = "1" +netdev = { version = "0.37", features = ["serde"] } +nex = { version = "0.23", features = ["serde"] } futures = {version = "0.3", features = ["executor", "thread-pool"]} rustls = { version = "0.23", default-features = false, features = ["ring", "std"] } rustls-native-certs = "0.7" rustls-pemfile = "2.1" rustls-pki-types = "1.8" -tokio = { version = "1" } -tokio-rustls = { version = "0.26", default-features = false, features = ["ring"]} -hickory-resolver = { version = "0.24" } +x509-parser = "0.17" +tokio = { version = "1", features = ["macros", "rt-multi-thread", "time", "net", "io-util", "sync"] } +tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } +hickory-proto = "0.25" +hickory-resolver = { version = "0.25" } chrono = { version = "0.4", features = ["serde"] } -uuid = { version = "1.16", features = ["v4","v5","fast-rng","macro-diagnostics"] } -bincode = "1.3" -phf = { version = "0.11", features = ["macros"] } +uuid = { version = "1.3", features = ["v4","v5","fast-rng","macro-diagnostics"] } rand = "0.8" -clap = { version = "4.5", features = ["cargo"] } -indicatif = "0.17" -inquire = "0.7" +clap = { version = "4.5", features = ["derive", "cargo"] } +indicatif = { version = "0.18" } +tracing-indicatif = "0.3" +inquire = "0.6" ipnet = "2.11" -num_cpus = "1.16" +num_cpus = "1.17" termtree = "0.5" - -[target.'cfg(windows)'.dependencies] -winreg = "0.55" - -# The profile that 'cargo dist' will build with -[profile.dist] -inherits = "release" -lto = "thin" +ndb-oui = { version = "0.3", features = ["bundled"] } +ndb-tcp-service = { version = "0.3", features = ["bundled"] } +ndb-udp-service = { version = "0.3", features = ["bundled"] } +home = "0.5" +base64 = "0.22" +regex = "1.11" +fastrand = "2.3" +quinn = "0.11" +h3 = "0.0.8" +h3-quinn = "0.0.10" +http = "1.3" diff --git a/LICENSE b/LICENSE index ff072bd..81c348e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 shellrow +Copyright (c) 2025 shellrow Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index a7b918b..723407d 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ [crates-url]: https://crates.io/crates/nrev # nrev [![Crates.io][crates-badge]][crates-url] -Simple and Fast Network Revealer/Mapper. Written in Rust. -Designed to be used in network mapping, probe, and security tests. +Cross-platform Network Mapper. +Designed to be used in network scan, mapping and probes. ## Features - Port Scan @@ -12,7 +12,6 @@ Designed to be used in network mapping, probe, and security tests. - Traceroute - Neighbor Discovery - Subdomain scan -- Show Network Interfaces ## Supported platforms - Linux @@ -47,64 +46,60 @@ cargo binstall nrev ``` ## Basic Usage -### Default Port Scan -To scan the default 1000 ports on a target, simply specify the target +### Port Scan Example +To scan the default 1000 ports on a target, simply specify the target (-s for service detection, -o for OS detection) ``` -nrev --target scanme.nmap.org +nrev port yourcorpone.com -s -o +nrev port 192.168.1.10 -s -o ``` -Sub-commands and Options +### Sub-commands and Options ``` -Usage: nrev [OPTIONS] [COMMAND] +Usage: nrev [OPTIONS] Commands: - port Scan port. nrev port --help for more information - host Scan host in specified network or host-list. nrev host --help for more information - ping Ping to specified host. nrev ping --help for more information - trace Traceroute to specified host. nrev trace --help for more information - subdomain Find subdomains. nrev subdomain --help for more information - nei Resolve IP address to MAC address - interfaces Show network interfaces - interface Show default network interface - check Check dependencies (Windows only) - help Print this message or the help of the given subcommand(s) + port Scan ports on the target(s) (TCP/QUIC) + host Discover alive hosts (ICMP/UDP/TCP etc.) + ping Simple ping (ICMP/UDP/TCP) + trace Traceroute (UDP) + nei Neighbor discovery (ARP/NDP) + domain Subdomain enumeration + interface Show network interface(s) + help Print this message or the help of the given subcommand(s) Options: - -t, --target Specify the target host. IP address or Hostname - -i, --interface Specify the network interface - --noping Disable initial ping - -F, --full Scan all ports (1-65535) - -j, --json Displays results in JSON format. - -o, --save Save scan result in JSON format - Example: -o result.json - -h, --help Print help - -V, --version Print version + --log-level Global log level [default: info] [possible values: error, warn, info, debug, trace] + --log-file Log to file (in addition to stdout) + --log-file-path Log file path (default: ~/.nrev/logs/nrev.log) + --quiet Suppress all log output (only errors are shown) + -o, --output Save output to file (JSON format) + --no-stdout Suppress stdout console output (only save to file if -o is set) + -h, --help Print help + -V, --version Print version ``` +See `nrev -h` for more detail. + ## Examples ### Port scan -Scan default 1000 ports +Scan default 1000 ports and enable service and OS detection for open ports ``` -nrev port scanme.nmap.org +nrev port yourcorpone.com -s -o ``` Specify the ports ``` -nrev port scanme.nmap.org --ports 22,80,443,5000,8080 +nrev port yourcorpone.com --ports 22,80,443,5000,8080 ``` Specify the range ``` -nrev port scanme.nmap.org --range 20-100 -``` - -Scan well-known ports -``` -nrev port scanme.nmap.org --wellknown +nrev port yourcorpone.com --ports 20-100 ``` #### Settings -By default, nrev determines the waiting time until packet reception (before concluding the scan task) based on the results of the initial PING. -The initial PING is executed in the order of ICMP Ping, UDP Ping, TCP Ping (on port 80), and if successful, proceeds to the next scan task. +By default, nrev determines the connection timeout or waiting time until packet reception (before concluding the scan task) based on the results of the initial PING. +The initial PING is executed in the order of ICMP Ping, UDP Ping, TCP Ping, and if successful, proceeds to the next scan task. If all PING attempts fail, nrev exits before executing the scan. This step can be skipped by setting the `--noping` flag. For other settings, please refer to `nrev port -h` for details. @@ -113,45 +108,46 @@ ICMP Host scan ``` nrev host 192.168.1.0/24 ``` + ``` -nrev host +nrev host /path/to/list/hostlist.txt ``` TCP Host scan ``` -nrev host 192.168.1.0/24 -P TCP --port 80 +nrev host 192.168.1.0/24 --proto tcp --ports 80 ``` ### Ping Default ICMP Ping ``` -nrev ping 1.1.1.1 +nrev ping 1.1.1.1 -c 4 ``` UDP Ping ``` -nrev ping 1.1.1.1 -P UDP +nrev ping 1.1.1.1 --proto udp ``` TCP Ping ``` -nrev ping 1.1.1.1:443 -P TCP +nrev ping 1.1.1.1 --proto tcp --port 80 ``` ### Traceroute -TCP Ping +UDP Trace ``` nrev trace 8.8.8.8 ``` You can specify the interval in milliseconds for faster trace. ``` -nrev trace 8.8.8.8 --rate 500 +nrev trace 8.8.8.8 --interval-ms 500 ``` ### Subdomain scan ``` -nrev subdomain google.com +nrev subdomain yourcorpone.com --wordlist /path/to/wordlist/top-1000.txt ``` ### Neighbor (ARP/NDP) @@ -161,7 +157,7 @@ nrev nei 192.168.1.1 ### Specify the network interface ``` -nrev -i tun0 port 10.10.11.14 +nrev port 10.10.11.14 --interface tun0 ``` ## Privileges @@ -227,4 +223,4 @@ sudo chmod-bpf install - Place the Packet.lib file from the [Npcap SDK](https://npcap.com/#download) or WinPcap Developers pack in a directory named lib at the root of this repository. - You can use any of the locations listed in the %LIB% or $Env:LIB environment variables. - For the 64-bit toolchain, the Packet.lib is located in /Lib/x64/Packet.lib. - - For the 32-bit toolchain, the Packet.lib is located in /Lib/Packet.lib. + - For the 32-bit toolchain, the Packet.lib is located in /Lib/Packet.lib. \ No newline at end of file diff --git a/resources/ndb-default-ports.bin b/resources/ndb-default-ports.bin deleted file mode 100644 index 8320bedefe3c25ae180cf42c061761e0880836b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2008 zcmW;M`Ck)v9>?+T%zS3Plgvyeagq#4NG4%OLI4F16mgAb5uqX;wPL-cw4haK>k4Xj zvv{EO0I32(y^qy;b!$DkibjFD9;K)to)xyOTB}{~ZZ%pS`+oih?;l=wDfIslh(drE zgpmdb$bckdL?&cLG73W$WJM0-L@wk;(I^IWMF}Vw4M!Ph1R9A(q0#6qG!|u|acCOK zLb>PzG!xB2d1x;B1Qns>s2G)?QnVRuMF-GnbO|-0AJ8lG8X`xjfdT*;7{I`S2DHEd4+7{w1U(p_6G&hL6PQ7UFtC6XY!D81aDW0%a6ts95D9MZ zfES{`2hk7%vCtX1KpezFSLg=ap$8;DPv`}S&>NDV5BvrCf*<-pe;5D*VGs<4Hy{~? zKne_nR7it2AssSc1dN1HFdD|d+b|X~VH`|=NiYSb!ZgT&Y{-S_Faz@7Lzn{vFdr7c zC-8R&Kp`xL6|fRk!D=Xja@Yu)VGC@9ov;t~!@r;!f^ZT}!3DSojc@~QK@0o}&)_+P z0MmNfNSkOo?VuGpnvSP?(kb*ZdL3O!Z>9Is2kFc74f-a1i*BbM(;?c(#4>SA64Rf_ zVdgP~OgU4*Y-0{GhnU065#}4_D07`@Wu7w67!4M%5y#nor~> z^UL|Q{4u_oujT9bOZ-p#&-^|95B>%Jk`M8;APZi>CqxS|LafkN@C$DWYlQ7WwQyRv zCfpKU3Te6=U9N7fZk_Is?mxOKx~sZY-EG}N-6P#&9VgmFMf8YKVpnm1m@1}=8DfsO zP%IUz#0}ymakIEZ+$wGpw~IT(??h2=*E{qseL!ETKdf)nKhwWs7;l(hm}r<{_}0+e zp=YPIPO20sEs>T=%cM`G<?morbJVJ(*n~H(@xVq(*e^F({a;l6Jd@pcQMDA$C#&@ zcba#Zzc7DgK43meB1oQ%*%qTlSNrCcakO9EX%S@j*#85U+ymtkyGTM za;lsrPm{CccjcM#EP1w^Cx0k^B!4W=k^d^sl?&u$@@~0NZj!IbE%L8&yUd534SN!n zZJB8)wtQyUZrNe^r{!zQQ;XKhS$V5qby*{=G1mFkMb^dErPjmNR_haMZ`)W~W`}XM z@wQ2}>9#!E2HP>)3EL@~Gu#z^FPyVW_7wYA`x*N=`%8OY#}vm@N512Zqs`IoAQV#R zr}S3_CJN~)5kyrqm$CMmhf2TF;uS}9dNQ`RVJl`^GVsZiD_mCAZ$tFldb ztUOVWlW>ww*6DEiol~9DoLSBp&Xvv+&Iad2=L=^KSB2}0>zwPntKJoIO^(9Q*Bm$=^XB|Rz= zE+;f6eA#nDubjl(#EPWKq^hLPlVbb+t#82p5B~~(iNDm};BWU&9yn!S(;I`6^OFxI z$EL1N9hLS`T3OoQVVT1UhdI;T>HX3}>95kqj~Fz%DzpBb1(RAPFUcy(%FfQuxsrP| z_j>M)+(9#ZGvnvHntLbTUyw5o1&9C{paKw}155x1*nlRW4R8TIAOv&)F`y3^0;z@5 z7jZ>=kx;bf(=}^}_1|ywZqe;1*;BcQ4e{o;E2rNlH@u fjla72=9ZiFx9VD3{`>Jg+x_uv=i7I_yc_xxYw1Z) diff --git a/resources/ndb-http-ports.bin b/resources/ndb-http-ports.bin deleted file mode 100644 index c42e547ba0c9c39c38835f940d0cb75c5fe0b0c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 130 zcmcCzfPesoSxgKp6|7g-l{nNmyg7GpTkvG^E#P0se}ezIV27}|=w{Ib@ipR`#3Q65 zrIVzSrI$+Yk*=1lk*$^eC+i^ZDDNR(BR@fYqWlc`ukt2}iHep=B}zM#4l9W(Yba|f eYXM=3@&{!$^#rE{ZVU$)4lo`#a4g}~|Nj6zRVEq$ diff --git a/resources/ndb-https-ports.bin b/resources/ndb-https-ports.bin deleted file mode 100644 index 6c2267edafb5509b96a0243180efce5122395fe6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 86 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

K_d|y7YVJwwCTb^T-Blhu`_iR}Ar$wF(}w>Azj6 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 DV&*oH diff --git a/resources/ndb-os-ttl.bin b/resources/ndb-os-ttl.bin deleted file mode 100644 index c6a23fb927ac8d3f3352697ed3f8ba310f98e50c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 122 zcmZQ(fB;q~os*eYS|JSO_<%S%3ZZ$K6}mo|*{KTt!4B+Dx$?}sl>G8y7&jckZD4^) Vmjd;OLfOIjIf+G?#i$1T2LKpI8KD3G diff --git a/resources/ndb-oui-vm.bin b/resources/ndb-oui-vm.bin deleted file mode 100644 index 60f84e409a95e3cf8e0d12d1a75c25f43bbdeb2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 208 zcmZQ!fB+6CZD3$!U}|M%$p+2~8twk~$p_4Pc@vkm+B*2z9yQderoLH=? zIi#d|6Ti#-cVEgs{q2bOb^u^dS()by-TaseA`r3X*n7wS_y7Jg^FRNd{jY!a(@#g{ z|GM};bN_zv`Exlb^V#e<`cy3Hr_SHK{*Eo9`O+_Pa*C`>s*%xf7T5W3hNBAO1tPWe*M)%YVf`aikFwU(6SEHO^Q6 zGf6{mgfEJ6Sw`usn5^(cIay-ocR9%YNblcqgGTvkQ9k__E%XAVBz()HTuy2ZGa$;I zm#bpF{4p9KNdIG=SA|~jdmChoK@z>7lFU4$jUX!;hj$T#uW0dB%v!bzAf8rqbA_;L1 zKOtL68WHTrl^ai2E=lqmtl6UeZ`~3TSKOQ+DW`nyxR@==uJB)Rku9sljGgmIs44`el9-#W(zG`_w&Ut8Bd}=6RKH z>JA&?+KqmY|DLa_T|;9X3f)r{yDpdO94pT$SnOB+Il$R_`<9Tom_y1o1qqcB5Z7!o9=+aZ*n-rSMfghxGZ6+!HMdzT+E; z#OyBLIV~!ZmV+3x`(r?q?!D#Qe$F5A>MEadXAO?VYrkHbS{LS(M8M>>Z_~!HvxfsH#2XUfFZ}Z$Zn8I4qj?JoDZyYRLDJQ%xK;f~%t# z_mpR|dQujvBK?ou>N*^yU&=*?NB0JN-*GuFAYGq0h&Vvs`^uo#w`chcHtN53PrAO1 z>=&zg@vOTEbNnitPs`LFl64EGNOkkMM)NwF+ zm~~cXUwJaU&8ty9xg$plj^vB7Dlq9F;qEIxIug60P;ofADwg$n0WGkdcK21|W)EUd z<+mh%;zj;&XKk!4*7d$xq46qTl;l_gUU;x_?8OrdhUrM9nZ?lv3PPS6G=lb(X_Ms= zyQ_i+P}Ip#zjE^@eqrXy9s|&542Sf8a$^NY^2PEY$Gcn29n{+gYL{Nj$}h!grFJQC z1mk*j7sW8RAz(VQS3Wr}CpE05A;przkcBWk*0Oj(9dn?DZB~@`b^5TR)n#ymFJPH( z^_>q?xy28dUA;;uqa4OiYZqIg*qOa5xOh5A>S<9?Mge0azNnivqlmf zdj2_b1Vy}x_TRnP`Is+?=yEsNjnesgmP2*57xwTcu8Ej7%jYEpg@fB?aHx9jq`omu zG)Wq`VK_&}^P4+E*Spc_ZRhFA&~Q~2>t(5iG;y^%splc-aj0Bu=w?zc{(W+z&=GLQ z+BsDA&EPy1<+Pv-CSJH%mmT$bsLYE;&CBId#SSrCg;f+sXa7$MHG5kYQ4G7dBasi4 zefx`EhPR#28tE@~quy?0zPs-%&|8Y}YyublNm(h-Jx>qRgU#267|w7#&G9} ziix_OfbTc;!J(?W6NtR>wxIFtV01j{opdIx%$nWXwe7jB$xh+uzVhq+Lv&hC?xM1a z()FT#D5AWYisZhxjzLtfS4(VERDX7V&~YITRXe7$RWSh-l6(!8{k%7^o%<;6lAhYH z@Y&!>tx8H1;;sW7S{!f8E@jxHR##>TH%h*kch*J?*G63iep9|! zi74)jR<6(12Oc^ zRoC;SkwBgCDhz(D@9W$AG5?otJTV;b+Y;dz&m^Kzf_~c@l@HEfKIZ$-fIcoD7tC*M z`w8_|$N3D#?~Bz&WqJ*z*j{^6=zQHKbQ{vpCQMR1^(5B0K`*K4&(uN;;r znYA%H%csg5{|?bIO&A1lxU>JlI-Oro89^aLJ|LDyFJY*Jgwh-3`>dS7n5d3v!Z{or zJ9ne=RL`%A{8?y}Moq{X{9FP)klxs)%^tJ#|{K*lU!QQhM zUl0OvT;CVf&Z4YtqkNh_Acog*L*D*GBjaQ;qY;CTVISx1YX*Ab=^`g>$Y)YjV9sxq zM@MBBdiQ0?&c3*W?2?4lJHER$Sc?%D`)f0~^Z0Q|`86aCDA(w!bd#l_h zVMDQiX@{6FqA`gP1r;Z@fULNSUiHqeI(p=->gM>SxX+i@CEwQK=o(t6cFWayTjf3z zZGE`Q-@DBvUKZHXfBdj4rlRO-3GPNPwCoW;v3t84jfR)mkI@KG6!>@f|J`}OJ1!^u z(N#XDYh}@vEm!`u5@>ZdaFZgIEq5D%d0~Dx_4@X172UWX!QXz2)^5faVl_X!swF&& zV%eRc@^T;km+TbxPO#ZJ6Yt(MF5W61b9YFd#0*|^Il#NOu#-J;iU|#axA}6N)$DUM zFBbV^rZ%8OFA%3Peljz$;U{(e9DZS}^8Z;`)vM=+g8WasBgRw|A7dlTSLWy^lD}NS z&)eYtcdETbY>5~+orNsgVq5b-C^-OVtpy0xU_B*`rPIY@Pe>UVoiw~HC zJKOP2rNx;E{$$Uj0E->xK$d5Ddq3W(A|L0|2Z;OexqB6d8)7E<$GdzQT|BJd+&YKN z83)lLLQAkCm(e4Z4q|fx;+~2CUDeSISgu2{@AQ~7$*a0jqfJ0S7`{{SFe+|y@UI3+ ziGSa|`|sY;!#aUIfk+%RhJztB^G-E$Z&6S3X_5TzZdDu>rfS$%D#*{y)U34;t9L31 zlKdG52HCS}JcD)Q-^poHy}0R~l)q~*_bR`u*Gt;T;xP*rcKeFC_D*LN&t|ny?4RBH z7%XpNkHpjwd4)g~qKLCH|5J1cqTL9yzUy;Ylf^T1q#g=57}`eV;jErP-YwK#+U~@oHbAG{EDu?RXv-J%GnNGG{HEX zo$W|yUFXmd$?w(FJ*y|{C1y&e#9%i}MC4Y3?7c>&u!XadQqq7Z+l{*vplZ$pAeXD0 zPb6M=VGa@x5N)iAEsM{8y#U3Khz$15?Uv)aSalaB`L{K6C%p>dqV*f?6_1rm|G4Ih zRexE_>OSUPJ8aJN-Eoh9{Z5=Q_RI>!RbjKZ*Rf!~$6<3XKBRa+Qp>8G6(msyqwMOe z^Qy|NZkoq$oj~Ta>9W2mZ4$vO%9qRXwvwR4j}gq62r@rA%CbJxeo#JYMau$%d9n&XPxzJmz?~BE-BB@!7eSqS1#{ioad#pON4|>SgGc|>X z9fRTA5)9a(I+YMGyKahwKU*cn(|y!x^XWa64_DQ62O;WnPTpbKr=+U(^Lr{D;u#z@ z9QTk>47M3Bogpd{B%JpJZXlmXOt&z24A)b|os@Iz`h*nhpisTKVVx(dOh~8YJbxM& zq$7(x@r%p08}N*Ks?Nn!yFsL+X}Sr9VTEu*v6z|~(s$$g{<=I{inwEDtoe6W$1T60xGMjs9?2aZ=2_erY zc8K9Nz|WoKa>C_IP6&d|^JB(Kva-CT)|1a6`=F=nK3o!fZp7Q+psytrQ>j%0pi;R>~XAyH=k zSV$sW)qm}Dl*1aLm1MJQl;C7R0%(RVU4RAK4DsI4PKhS}n)n-c*aqV^Uq4Y1K=X}M zMJ(prMsQjFcJ-b^@|D)9@ozzEJRR1 zCMpu(cO!LltlG?C=mG@X5k`^Jmrb8LQ9&Sak11pwX}5+_z{u5>F<}(tD6EY2hNBQx>-f9 z-@kkNul^Lh%4f?u+6Uise_NUTynJ{-{(nKLv)GXOt(+1eH$lk$;jXSava_#z+C#jj zT;7fOmJXYNAl+stZX-LduPXeXK>wOnGM;D#7(NOuZLuHw%64)Na)L4mQinMg`2ia% z=0tS4z7Yv6dZfL_siubfvd(c@G^f^gFvRPro;4jW$|)mG@|jwo^9sC)sJE!fqYjo zolUnU)%4`7yqy(?2rtQSuvD`5}6@Phr&U@BGmp2sM|C4w8e(-wsUWEwNg>BB=+>lPTW zk>{Q&V`8X0tY{zAcm^5roydh*%lKIiAKS82#jhw~3(qBB2bN?Iy(JKL5@h|~tE7g0npud8Xs z;E%&Ng_692U`Ftu!JDSMX2hwpyv7hu%fIMMT^aLND5|^DLN$-U5x#hcQjsS}gv9Ad zQg2#X17%N_hLTc3FfAqh+q!%(nXV!igRa9sV_?6mYe|FR5;{ncEJ~zfkPOtcwQrl> zDg#PY&=Og;p2!>x$j@|ELfQCABxkYsjgr;j=(qI(DPTr_-W%7U^8;$Ho|LzDXE6Q# zS4X9SBl!aPLiu7UQL^aO#RA;+WAx#)V+#zFspG0bIhYTG5HFpUaIX@iD%Gcn6roJg z86~F0Ew1evYp+qP%2zWxH(qG=slk7bFRGR-!Vpb}=wA<$3(Y1KfYlFJ_!y(K-zz5f zclGDbbQg&m@dEKHTDf}|ujD^29+$90RjUXVwuO^x)Zl$+w5BVbp|-MEP!n1d8lZF@ zU!TQ2=zA(LgBx0Cb4xH;Z{y`Cbf|yM)y6Y8Y8UN<7fdM~#hG~iUBv(kaO3$KOoGQN zd1sQlojl)A&KMlwi(&yA`aU>!RZf=X#+sR9afHk-6#H~p6His15UX|iTxn(wanz5` zvN$wU`2hCoj?9c)P3<T3mL?NV#JBV*<(u|FpyD*GI3FCysGOpU=;=48USWb31(T zTH`k*q~V+7W4j1dw< z*A#JL6tse&jh@=RVanPGq^!jAs4t1va;n{QH#;w)432Q*qy$NG$BDoGCd%Dci8Xl{ zFT6^WA48<#D_bv?6U@E-8+VX+jnKgF8wba)k9<`tLYkAi<8@9>Ax4Re5zSKa4Z#$A zyc)FQMNv($O58((JIl6tdVp!D(aHq%A7~Pb@gi2T)A2a)u$sliMm7UZ__YHKZ^D!1 z8XB*5o6n9xs=a^Bi{*N>);wT?A*v8#-X<&dG>7;yP9%Uw+5j{GJz^`yeW~lkWqK}K zP0=D)w>p%4gNb@pul`y;qqLm?tJLA(9%8rxe z$nO^CTuKx0U;I_~j#}|EPS4Wwbj*ElaO+YkJI9OsON~e|8PVdeQ=@V+tlN58j|)E8 zV*dlrof&%$PA<&}FU`KoB(h&DHX;hx59QkNV_Dr5`IL-LY!*r5fOzqCea!14U-&Aj z1x0jPnc%RJL}+V$)oZU_PVTZogS-YKfIe9xhNgA1!QW68?!y?y5%&%wP7cFY?2VLg z>=ygl*VA7wKE~;+AfXuCu*$Il{$yJ>*!_ws>kQ;3X-ZUG#CnW@G|l#w?H@8seA-VK zCdNmz#x1`Ji>xZy7>DEdoaQ-kgE9hav9J1gI3DDSIc?S97-y-Attn4jh%qTdbu64b5iIbx3|B9XM!6FAx)WeRQCaBXjbL z(=D!zJqb3)UG2#pXxAgtzJ^-e0IsHC=gu&-oQFkGy!%O+dzhwa)WDVl4-RNs{(<~I zyoYMak7c!#Ynwkenk!e-cUqGgm z2gEOm`vaAqy<%|>k@-vlCYB(!gjczuodr>T%isf@mX*f01V{Ly5h0V`2sxq$%6uP( z=O&;=3kJA3KDe0!E@?c)mS_&RMY0|386SFIwL~3l!@fKgj46Iie))pgv_iQR@-F<6 zTip4P&IFW!T%(MFCKvI2fQWpVY>e6$NQyBH1BXGiPt$g@hz+2PfgHyAGCS=r$N3sDP*J3vZF>LD={3CO24$~bSS!_xF0y+~zlD)rTYH39 zOFo?-U#fE!`})Wi!4Ajq87x~3cRLK1>tQ$k%hh1#!{tuLd^*%Tf&?-6N|T!{3K7(I z70_Uo%B($P^T@iFHatELL>b>`^Ro18lzt)& z8{i%{?sp#NOI*=gB9|5_y9r^R7Bx2g1li>MAC4yO>H$TXy9F=7}q#0#6&O6UExU*;&j zIv!Hy8PKx6ySt}^{Smqnq`jm7BKC_ORKjmnF(r3Jbvs>;{_F&h!;pdH!`g?$s2m}b zXs>nWq(>Z=nkZm|}9^v1ZK=LjQ!qN)vl`)#6;wd0+X4tI>t0nz_f*$la= zi+TOansiSLrthq{YO1(DPESGKqe@h#N7-JF7Lo5+S z(O+S2v}f^7bykeL%8Ii#AS6$G*``!H7c+mBVzG*`&SO+h^^wOREJc4vuqP3T3$$Bk zygu@^n{kB}1280M@EDB2#S(DN|-;aWg8O_0UH!lCk8g#pc758C~6EOheY; zD4x~#P|P^UL3wB>fZ)qGKRgzF`1yAv9$*|$;ET@UXo~7^FH=gB&jCvFHJfLjl8fd_ z3U04t-Vu_%O7p7tjJo7i$(JK;W#k0+U)lAlMyoI!Rng1=7C~hbLN@kLSrQRoIT6&v zWa4cE?rS~+1{FSdYQ1 z*6gsxd$svaP#^QWo{cH99VSa{+Y?{(ndA@By+qw=hoi5ijl4c;RDl+i5ed|qh%U87 zLU8T0jo&v}oA0%o^tTPu$O;lq%Q>iyhNi(mGJLP95o!lDCFzX2YjGoH+}$AU;&+yQ zI3FHUDq9o~3ceKd^cn3PkW9m)ErUC`&s)^ydkwby+|O{H_@Iq)fg|%pZmO``Sv=TD z<+pUl2DZ49#M;F;xRa^~(<~E#rm-?$&#OV}!B=vkNCF*1X4X?!AGhpGg(qF#>wo&FTJBG zH0i%BE*!Ndaqy99Pa6AUReq*%LR?OE;?)e+Y91lPQ^To`He4-VpNX?}oNi6D06}nL zYhTzRWxS8bB8FJipCHCqr*yW^b*ur--u#i#IDFjD{k|X*o+tVwR%(blERSmBV26~y z55dE1tx!;^-5Ui<^R)sUx1sbNYc$zy3w2+RnL(&7^7DfGY_Sdt?wB%RQX`5m%9n?< zqXol?Hc1I4!IM$So`5W-b6NrM6DnhE}krI@hE~z-VEs4Pu0%Z8#8Jd$C*9`ZEkP9A>s_B3>W$ z^aSH${)~)FZ4KpM@aZZ9#`23GKbqeLzerfVGBbuKb z!661MnyRMY#FdAS%iA1EVX>EqaH%^( zzt03tlrzV7SP)c2@`)B>6{`h01juG|7gL$@n~ZK>_;_5PJ<)P?POH{ouFXq=H}O`j zwAvrntAB7@WOJ+Vg@m_FwFTzh(C{ftqfAEtIDRB> z+abUNE0KniqqMDIJ8vci_<}`=K+ljRLGmG%HX57Z-M(^IQbX+Fy)_;!xV;d;M7X}% z!DaPHvi?cz4hEm9Om!I}STP!LjDx{3YWzxeH_8;pWMPLR>9QXjsjPL97pT*}9nx-a z80+%6cCD~=v)jDlOBa#xDP8llB@!`OVssL9e9_|I5NioTkG96(kQ5tUQqN(?BR#K5uioglT!=a(LvDn}wK;h}~tNGIB0s(KiQK^jQaPK~RnjGl1Tg578s zmRq+rRo+B=6a_y^=DRz%LFVZJR~X(2D)MPMSrnqXR2ec`BUWAcog@y9Qdjh(ctk__ z)g>d24kKT2a*yJiHUXrXMe}XLlm`c}bBfnLX?mi=N^ilrYRKoTT%o*ilvAu)47(oY zcR@NpJMQa_s!TP$nv@HKGuG;^6PqpUj_T3+O?JT0ls5*vu;r>6Q?$xo!oZ|)M!eZ^ z-Mljf0^L^c9aoo3EtjXvAnJ14Czm8;62?=nEdP)%OURDIW@!4&!LdD#5e~ z8g2z=u+KD@UoJBq9N=T{Z&f#nOTNgL&m2J9G5rRI+&k>>T>luK1Nrie5=GDkE;(HB zWG;4Ck`?hr^gg`*D!I}oh2^=;lmlhTAxvsH9HHrCupEv&%+R#v1xMPKEF#ifBI8*z zfDP`_L@+yXplmwEsvXbj8`k>~o5RJ1Ef^>Z2Z5<8*ssU+1E#`M0)rb<32t6OEqscT zC{Pr&u!GzAcAy%OxjbA&#w4z5T&DKoDk@vLAsWHwPyB%h{y_Ot?A1Qbe<|&Z{VHEf ze5`A84fas2NF4&Tj!owh0KqQ97g&UV4UU1TEr)}Vp*s!*Zs^MBw$oG|L%pU%KPseV zs_7dG#uJenAy2gyCoD0L;l7%%K4xi?PbTNb9bC$N%p*pN=@n&R&xfXmJtmPTJQ%V2;Y>X)-i4N%s(#M7oh|w0HDc*$eLipX;Mu0AaC@ z7B_@kXqOvBk;pw-pldgk4$Mk}TNhX0$@|n$SwGcolG$z_X<26-67p4x37mLo)5Q3I z!NSS*G`g|*@n3Y?Bu@YjR7OabB}!0=idqh7e$gxQjn6-_bOr~-WNB;1|EOwhfd~KtN9H7|-Im%(bSDly;o~>r*Z|gY@9#)SumINy) zcOIxqd5Ru9^Ln9jkQj_9-Tq8hfG$11Ru*tlLKE>QH6Z+?^FUApyjE@yT_Zt!s&J!w z7}akH^AMq{n}~4Rs+jSuErL;WwMlh8qkDp@j&A5D#Rf%}?bg_PN4{VY)vR?jXr$7a zKH&I5(gp4yuG$By7^-a8&n``02#OfsX2;i%JoEQ&n#65fx(WnV>@rwz4CIbw21x!b5i84xz%L=P$s^`BJH|E?~pq>i0_>*^=@Hu28G_Jku z^?LQZm~%Ph`cv~oa9E#yV2lhmjXxW(W#2U6Wt_$dbUvRW&`NYz0+Di?6`Fg>1D89= z`bIBnvM99LzUNS#@rnSYQQ(7m$n@sEPxg5$Z5x5(kkr-kxhXY4hcw05=L~h^K z)j7}J9aeD+kpZ`_lC15jee*JY&oYiupIta*)XY>^BN~5R2>Gta8iP? zspcxoUxuNP?z8Vux|HiCfR_M(Z$r zWtnd{o4_!&tWdeRLi*B-K7he^s4XX+pl%Rl5qXC+)pHUX1^H9H$&0~FR)`Rz?HE1> z>NaIXF)i*XqAdcA27RUj&31{=HfrK+MS+{PqrU~yREEGLW+;<*t}bT#?GCgD=~e!W z-hiB*MO&w*eo^+}Dn%4)tSf49V zVe`nX6IP}<7ScoUFf%R;Mt-p{?=~;wl_^*F)7Bqe#Bs=O}*zYzBDz(=oCn$LtM0p;7|+NhrJR12_MbOh^@WhEA`CzDAF91 zw57v3i$Lo|aAL}*#!&_jaL0+w6Fc1GtMmr5XC&cVYPS%J&8Y)YR4}$_5p?FQEICG) zs5)I2?_8k5;0Rx!&j#ubax|5`Lm=rcFaKm>x9r*GBzYuiyW9O5Tcye~$>F(zLbz2H zD}{8XrH&f{={E?D-%{riL$c%W0T~vN)S$FZ+8lt~9uNmYVmoro^ku5Ch6z$`28lTMS=UO(Ml1{LzYBL`}d2I#NeqCSM_u(>w zlw7OH4cyK!>)zafS*SKf&U{&4`L?XY51S@~FXg)~u}<)+C?$!dlrJQe->Ca&>Sgj- zO^PJO?ZbvmgI<}xoAMmjURLIQh~!l1i@WUw#G>}4tIR1F!3Zf?E5_7w|afoH+c?km9z8Z z{mu#t#E=6qIb>7M_@c$p8f(B<-)OyUMc!#E$3Jit$KKI9wVkiaJb`UO8RuYV^?a40 zT$d(S8mK^=vJHo?t^^{D89VmFWz)()3JX z#Bare=++%zs4Cx!$wb;?sv&0Zwo%G$a)6%Cu1lsTTO3`ZJD)W0eRY|LnqQ5~T zDeH0@OsjhUCZQbHM*}tC7lsXhJ-=+6T=4oA}2u9D;#VwK7!nQ%K3sJw7^$4Xho=o<@YY(( zy(3@kh#Rw?>#V-_N);H4d2d^+(#GqUAHL9Wcxf#i%JJdluKglZoPY;wbBi;PD76~3 z4q~N#3>nk!y&6R}*i}`iCoTzLSG)M4O@{B4l}-B>g!;HK#0d3mjTgLs&rntTP(0~z z7(NI0LG>aIlMo>*Vv28vMYDF||BdoSiL@27ghAd@i!r&T6Tab7y;s#XLPTC~PB6f{ zSa-{mt(ZeV50_X{pPDH$LG%2*^4HnUzM10x;k*mr_M9Wc=u8|Rv37AXamvx&BVXw_ zKp!aFdO0Dl65j``5Pwsp3Br6Vr=)s=BYaUXlF@mdx}hiK3?=Kuf=ZA$aE{=M9C1t~ zyC8gh1@yHF%Ea>lRO-A#T=5&CC*uG4+PfLPVc#jY&QX(pJwO z;n=&NxzX;X=#V4gD;Q1dMI43F$tT9PwU@iM&tqgcVAK%XV+(M zDP!K&)4i02sC5WlTEI9@%h3KR@){HW^L5qc3IdFzQb39%Pg(0(C` z_9cyJH8nkr>1z^)hbh-1f+KB%HMNI0Ls_&8_F?m!>z>Vm}4qC$?Aw z{&GfGVl}*7YEK7)Pg64(2J=14ZjFr)3+J{=0QZjiT1*A@?$xW``6LH}Pg0BbvL9c< zO?luMCvarGfDw*6z=DG{+vj|kq!bbInL@qIAAL^J_npPUx6rwp4@WCNE$KPFyf()%;*nB(tIHz8i>MM&KF#0X((#G zz|XLaBNTsoqf-sjoDIk6&c(U*U~zb3r8nbDzB2LWpYIc$wknPHj#5n$O8Y-( zQE!T5`5)p=L?czsOEQ~FOJh69+^pcQ;UY+PgF=Bz5R(0@vpLFzgL<-FrqvzwvBB6? zoT^N0(spO0DzHmr6$t;*@)X?J)6P)kT(q#6$W>#ibsctw*9%0Y+}rddCeZ!kz1HiVc5_V?0XHTC|*N zNM)4bE^K^Z?vzr$duu$z*EU{CSOYdIcaM165)AY_q`*DMC@mvXI2aN={%%HTm6#(J zlxuD<=xkw1DT8Z1&{o2i2|7*Fl{6suFlF28;WhHlEA6@@7;7w%_$?-xFLN~UJeVY$ zsKqZ5r>{oL()jeM;ounDB+1~sl`BWX(M5))_Eg;lNBH6{@0hm!&cyzw7=pUec4_@j zk^Mq0Ee)@$vSBY@<8tzuddtM7B<*h6=fe6pT0=1=@m z_8rEn*_hz+H?+kaa=gQIJ`GWJxgO(H4gMUD{6n3KJ{FtM_Mg5{l6HWQ(N;?LJ@)W2Ab2v1G;I-Drcvqt452(hY_ySE8hN zQlRdf62W3KYjZk{EYo(m4+g7_j2E|Sx$2R#HrS8F&Ua`|ne$`~zFzo5dW-$gK)4YP zRy`>i4h0q~k_@r`0X%!dj=?XennWn~5vR8akAWPPwX+p<;$yWe42)L$7XJt-I&W0t z;nvhPjcwg?Wm#8;oc!SYcGe|#vn6SfhF-{D{Fv_8Luqm$aWTaK^%jS5zflHhGDWeM zTvCJmdT!CV_yv^%<12dz&5$B-h;v>m_uJ#ak+$1&$AwOXUMj6gnk=5CJTF@=)+^ta zn5#vzqfLRYHG-Rz(`Gg|0JwGH9P6T`k?>e$^mCjdgKV^D#^TQ2hgi;E0Esg>^d@62 zh((-jaHKXaPoB{MffC+;7sn}+Ljk}ZsIKHmlc1>7w92~^xk{v zZmy4Xq@uh&&dW!%y41VjEjD|W<0b;Kd~ofR>wf^opQEOOjZgI5a}N&828dpyq`_JrR6+v0|x2+mj8HjB@HMRcR$8HT}lDo!5e(!F0}&0E#JzuGaD3_R=}S;=7C zPJGyq;J$`)_?R#A`*Qd%o$z%SGC=N}x98N3KDVxkHZ@0Qr!$(0`26=;)om%!-|>jZ zbRqf9bbbX}zbLqBdIOGkH3s{^zf``39+S{>!Z^OevMSyHdWrM)l#Pz#A%3c;7>5Fu z9Fn5?yhe9(V>@H=or|wQT&kscLQaVlI3aIKjXh=AVJ}9%9p7%m;I9#x@@h7z9}4Uw z{YZmbTZ?3IBxXqM_2z*hUO{!V)v4K<(#PT`TQ`n@gSJYe8dUng!1N@Qnm#a+nF+xe zX3u@i<@*K)V*hCWd#;zmFpcCI1H>POI=FR~%F`Ef^r?qqNL$L_rei3YwPQ>963|h( z$Wc-`QJxUo*y=Wk_kwTg7z3{F@1b{vmPIxg8xu$k}H zozz;jevWJzpO$FQ)={2W&$5%UI?I_DH8fam%I&O;GN|0<_?*vkY5`)mG%{2|uk~W9 z*}1PQ+AD9Rv$1wDbC`A@*lcr|4>ZAVFssFi{K&wS!$=##;iKHTr$$QPS2TM*eyS^B z7cRMAy`jJ!nzon1m!5t@q0au%fvREj+QRYorG}m@mNK!=m^FM+920eV+F-c0ZylGW zBLmMS9Ktcggf=5$$wwlncb`sk&sJ|4tGz_7%#b?GVF)M&u*3ACTrI&E^n%y>c8E=O<-{x5Vx#jNrlHqM zUJc`n@=iX#ux!fK-$p2^gjsa=${9MuhCKCH9@5REU2S7_Uz`f>G+)%d#?xXYfFQSN zWb3kCigiLo0cf*Q#RShQDAE9vDWhlW#m+I(tVy)Q4Cr?x@u8lg_E0j5zE_m^4tDz; z?H?U#?ol5BpL#ZB1kS+_K_066i<;auToFCgX(ay?-r1{+AX1)RD$sn?mZvM>tCC-vgIq<3g> zbY4D9>&0jHGscL97}_)u4>j&(9{8nh+-ZRc08fgY9&Wh~YC2!HiXi7RN=o}wu?}0w z3ym$*42tT!h4QisxMb8@#O?OxpbI^Cy1X-uewaGoU?{g4qm*;;CJ4fO%qet;&FR1k z`{j8!FLB{nt%^AljI3;z{?sw84plMZ#_)A@d!i{JsyuyC`#mON3LgMb?6qi!=5si>z1f4HgT|zUq13>3U zaKBNkdKu&R74*XS0bA0`P3PT)jTV~U8|8MS2#e$uadquhR7Pgls&);N&G9sUdc zgjeBW=)#Ypg=Y+5;B6b@3sy6ve3eg`HY2pSU4{{Swnn;7@oaH*SjKaH>X*YY_u zyT;9z;M3nByXZ^Df|X9N;~NnlUuYUHF}1vksc9XK;@zw>In}ffO*llJjrcf+TUmjf zvC3hXrHNrPx<(v)K~=2Bi~QjZqLQkD_$#u^mzBy*7bp_@NbAyJ^=CTM)zI?Ehf%Ee z`5Lf|f8Hwpz}Z+r5>1(BL5z`2trmP^B?ts#pX*=OQ$2tZ9N`P;8xeL#eA(!*k7TXi zK8z@L1V{44(9l94eP{pZt*TXABlIMDMEVnq!daZ2Emv2Y%z7daYS@V%C`U7jS318pUQIIu?Sz;&O;?V~9o7x_}N_JsRq}wU63S zjdku^RGP3H3&7KY9;B}wg%JVk)B6t=D-VZ=#TR}exQeX0rD_U?UmmU3=$*iA(d6Qj zT1EvQr;4dJ%YR38%ZjUGFdwE@(X@%h-(D&G@IUACg6u)u;N`8$<#`NQxRS_1r{~h) zV3U08jq;VWyux|+1!b4R(M5&Br=VT^P7T(}vkw^((^9Hb;%NB8IJjcUQs%A^bb+JF zv3HN)3z%e`by6$!GRdwAobr&EfpLO?FV;d%Mgmg@1s|@uD_+&NxrSf_yf{ud@?(KJ zi%|bX5k$N-(RTmHSBN^N;hM2#hmm|fExKu6q3QdQQSE{1tYpRfNt4VWx5R`@3|~BY z@{F#Hd8rpyI1IAG-2#Oe+(8z7uMVm7;neu30l|qk)ntVdfeUI@Y?@$@TG8HfUneU4 zZ(wZdDJXG?yY@^g0QAo7jD5o%GwE;WovM+_aXL##L$Rysgtw{>*T_yDdIe92o{_=L&F=1XXuV?f6VatAe?U$^u>{e+f(P(#qpRfil@ zRi;qtxWpvo4>*joHrt26HG%MRwg=F@P!wGs%VHW&N!=Dls%lIVE$C`P*8q7QOJUYPI4vuqsDLj`p_27}*rGVP)S- zxWu?{o~we7f!MXbv+`nImC04i$V)1&{xj7AeIQPG5AdW zss_=v{9dAJ?`kkUPq^O(U7kjqf=Z-%9XTIC}~7K!AFSw@_8oLP-4L&XiA&y-x;bbh%R4?JkDa8AO@43Bm6^W zp!Rwn)i*V6oS?7o;D)$Qw9IUnd{75SKJy_V-QvqQ@9KxoNMgDtdkdBm&24v_>>v4C zCzH{c2z1S(0LJaX_yRS)(qv~Lg$bQQokVN1$I?-l8*jkYjgLv~qjAQC#7DlNVYcq( zc{Vdj=2+_wtn@ra!r9j6sSP?p_v~ur8%Ggiq}ANP#Z~o}%%oSLznYnNau*z@=4U#F zzDL}Kibn8PSv1j@=4kyi@%{EK3O&$YRe5?5pP#0~j08@6v2BU^zJq3dpFe9O>ajs) zLxPgN?+s09FmO}ke8QN>Aiu^Oz<=7Yo?(<1oV%ABw!YIqsiw}m)3N;M*l z5BmSaLKF?ZWb4wYNqQCc&4D(VfVc`7G5!uqxg{PN?jxf-4r|CXID6HVB~Ct8cRrqF zF@r=wZ>q1l5^4St4AjJ2;;1Xf`YP9iBIe~XkDR_Bend&kGY*EU0voXP49Q<_89&sw zMUl?vPFmbaz}yI@``azgil=-5Z-Mf|U~5AM7tAv;s!JF5!-_@}c3Cc9G6V<96Rq96 zp+V}j3dGl!!uB@_I<)7}2IGpXR~2DwB?o5Gi_*u@=FO*1oEDQq`8j&mVJ=AF<|EZ4;)c22)3;kcnI}170A!0sRex{3N_gE6c$RE6bOBUKP{N5L!JZ zvpC!^eK8HG$L`oYzSJ{8lzWQmOa6kzFqdGu^8WPDYWaIC(P+1rG&+l0tRlQE9DFKv zKmYyrSuxUv#1=Pf*x*xDS>nBVYlv$G0Es(s&w;X;ME~~i9~?mZ(3zm>L#SUAx!3-* z#A1E1;QW-|aMG|`?Ic=~Mr;G}n@K&5LQxc3VC;+%K6AK{)w{)w@z+*f&WbWe2LU>B z#2~_^Q4o9uWy5hPDJ!{NIPb77Kxg54q~i2EJ&wmI&rlsSJhK@M8F+pbPi|E_ahy?U zz&6LN8FmeOTWpSqD zdC}^7iqRTdy^xX%I775K4E~Bt!g#fDVVOaP`xc@WcO+_ehwuZc7iB<6p=Q7fFRh$9 z!mO)$RMOB_)yV!RMd;{tBYy-AK3r+hrfLB`04rtLDw zcVTGBH%}eOic!kHxUUuR@3Q8sEk;MerOCFEnY+N(CKKUzRDFVb@8!B})g^*Ec48pD zfS*t^H<4WK<+%o2Ee^6FhG~{;hQbNj6!Mw#?W~h}rqX~?vg~R93wD<;cS9I>jI!-1 zdgs-sv?W~_%+lKcHEDS_ZfAeizJ3Ns@Ub(yl)iR+tyh5H<3?l&Gt!t)yj)|oR*!7S zDVVF(qL|Rm`I6A=0r^S!y|Nqeg1;5;P7WF+p~LE4PUTrI1UJ6kmqwOvSdI7* zjTc=(gB5eVYs*2B;MVcVv=%FR31$8cuwB8Sg!nzZT>`?n_p{42^RG|>J^3zS~v^=z(N9BL%2mLyjEUE z7Vond3V-MPdBH7b7`_p#iX0B5tY9~ke9*XkGn0ZLL4Z$FH6EzGigRdgJkefYm zaT6t{#mcJfMex^dW-GZl#b4=R@ur08j8}Cm@BPPmZ{jr%C`iYng#HCSn%1wg4sq}4 z;by#=`$rhCUE}X*amhEBR+yP4(S6&n_e^NGET7O1nY(7u3$s+dV~r9+e#E+O!A)0A z8;)(QJ%g0EfXNl#Zz0+}O-CKU$J6$w1_U3ad8k>ATOu{t!y<~(&EOoCRQU|*2mL;q zCZFIAiES!rU%_dNa2(D3+3^>#Es<(_6PnT$dK-U5rz;9wap8!O5kC*A)O}Tp*Y&e_ zBx5?O4oBnlVg-4NqKr^1`v66nK40c5eNTrw`xbbnny=`bU813i*6QlBo z%fCp``vWa2!D3U`wk4y_w=l*73#+`FU7TO&fv^D>9W>zm#i8gcT+B#;25i_N{<=_k zA(RKr8K)p_H6X%Ds|rlBaZ$`amh>XDx0v(J4TN>PLgj^vA%7{ga##Th(D3MM4_h z({@GRt1{0$?lqDWhPM$S9=J1Q! z^FW=yZ_@P#-xIXK_9%-WLkO2pFIMy>h}{~CJKi{9zH7g-zo4@}>KjQxa~BwE?2@Ls zSL7X_Ad)5u4tf^32KX%sah5NKG|jA6XL@u(d|L)C&A_8n!TnmVRyqEItW3NL#N07X zIq8>mKE+)vIkgmV^x?F#96lg(K8~+qAFhrK-i2+`8R6Lkw@H@k`8kEJ+$IUEG!r^f zz$BGdkmiOH?4@4qN{nJ{b3>e$96_aa_>R7yZ7}wURPXwYecHxrhsrPLXI7%g?}EHW zyq(wl6`YbhwR~P`V_Do>Xfmkq-L^w9yu4~I(UyqcrS!`k{v)v@e<3FlH_*-Ji}s5m zmSnZfC{LpnGCh?pbN6&{dcX~~>1?>#pO6vHlG@1hZsIML$VNQwP+1rOqS?BU?Pl>; zHr(ILd0OG3Gu)X$%5(T@<5kn8mlvOe)v4(OEAMY38-G)0KR&(4P`gbF#$X%R?vx5{ z{VnBil%Y=UaMVp>)L>t*;-Zr69r4AFmupm*?VOc#^oenO2+W?&C}nlfJTjN79SYEL zce%TY%vZ}b$^aKD8(j)Cf;>2SqZg*cSLm(b+jTl9uiSaCa{R~jU0&fBj}JCjkMWFt zE>~;NE@BR&VXJF79{i317!AD3)ia}^7KH?`zot_<_`?cgVwQu!F{%>J^DkuuQ=d#? zu}ZhjJ1S#e<};XYi-N|3U^U-@Pt>rDabF+^XgnC)AnL8u^nHr>&G#wdH_F8(URTUA zM%5iQfjzz5MC6JI#ZB_|lD*Z;g7V=*^?){{q+;S*w2p3{6dg3~+7wfI_FO%26me*c zedVK+spEM4i6WmEYrEyZ=dC7?;wBlXIASKKgEZ}e&sDST7=8Q~*jt{0AMZx;O?!8@ z%|N3gay|2UNo&BOZB+4ADLH<6l0GO}?nNBM%kG{i8N;ZWt>-uX%)wzK;I$5Au_Fof zaCTo#k>byQl!F^;;H};xkd=R1FVsk}XrosMo}fqS$=za$oxyhy0O9L54Rk5Rdwn=ZXC6-;rO1cP}+$|v?kTB!e=t|zNt2;`lHKwvzY zKrMHa*xi2I5v0Cwuy4cLssJ5sP*aE+-)VA7JjXc?_sM|38H_JAl&;-p$Qq#+OWZLH z{mqiYvAHqo3niK0ALYs@G58?6MZn`|X_+tEGU1)7iUb*1$n?zU2Rr<=R0eFfvYZ@$ zNqs5pLSnfK#2nmrAFg)cukqjlF`h@taKS40gDb1NHb$yN4m*xtMT| zdJV^spes(-qV00Y-$;3$BRLuKo?TCca{2_H1B zq3kV+;&*hiq#Z(xJ6ZxyS0is+{56+@Qzo7mtVDrVfaD2#Xz$g-OP>%-nEPfF4(qQ3 zmrhyORD{5QZ9Oz>LW}xJYvG9X<$`OgY&%4YsA8Uv$?^`jShur0%C+Wb1nv`= zff$PvjBfdREe9Y)v<#ckZz4v7u$f`jez3pFQrznm4*2M{S!~RHT%e4_2+9n9sihfiY`o^AG-CO*=M*uSy)CcZL*aQ8N%rC#fxlYOH)~s&#O4j?Q~g=Tl(6 zUeqn!=c^J%uFW)q{9%NIIJY{+yrV26NA}7fackcu1ES_e+jSyX_N`Fs- zg`urh&K};(FDLU!NlSm&8%2K?T_&RhHFl{OBo!J?Ax#t4jxD-xuM9rQXXOp9JEzpQ zSfxW}zS>kS!z~YasW%@u_`;60owga>J}%-_&b{#EpowUj^r>Yyily;ubR4Akz4NaHTR5Y6?KNu`+Ywj62$K8ryv(=|*wd-Z3~U-8w97t+gS z;(x+o`~-f$I2<-FanlO+S6~h=Z6+DTH}Nv)tN3chu`~t+my;?CZmw=fviizKT#%VR zfvU(i279Usnnk{z5!8cP{9}!LS?mO>SnnNv^_7g@lpoapzH^8E_nkZRmBq*Cn1j=t zF&D>S%QV>13F^ys)i$vxyCh# zHHVd5k2Y*LU#&RE254S+vZJAmEV5r@zh>hU7j14T-GQ={C5n|+Ll8HGdw{Wg?P6Bd zkJ(+l;JOj}EUgfg4wNy>l_KCNsw9IWd=ZQ#`BKDRKE!83TrtbE1>!SU&etLktX_>$ zAw0YHd3*1CN#ZBOE+q4k)MM};AXtB$<#`X8j*Wu{U@z2QHHDT?IYvxZX!bjj4smed z)QUB}Hxj5R(;RL+w)4WuE+$Y2-;bpU3>?}D(1uZiMF{fEz+1J@?YFfL?OYm6@z^<( z8tB=lq!L0(&;)ld{J2AuDY7x?H#m|Q_C1ayhH0GDv`D<1lw4zjQ4*O9qZ{O^;|yTg zDv88c<*aj*m?J#=`A26?8Vow{Y0jI*B;RsjtOM6Xa3o(Kc7Z#uHViV1)!eor zwtSK|GFJ@Tq;rf>H5OlABbN_}IM`0)MIJ z9@!8rcK8GXwi^c+r%Z?i6szZwhOxtuO~eSca>a1v!%;bf@m}QAmBe2Q3gb7_?A(`I zZkQTrKyW=(QH{~%;6Bg(LbYx&(p<8@TKbYhDu!K0&cu;kJ0fChjh4gK4S^Yp*6;aj zv$pH7QHY2!I6IuHaYc+EExbh|Qq5IM_Ri0yIK!_foBmeZn9ggoDh+Or?)c>xtv2cP z7`pyxw5DlhaiiM*B~o~;#Y?CCF*Ptga?Cb~=C7+fU!$Ion_bDPZf7>d}j12Wje6q=IGVQY4Dx|3|Ad}iIP|x$_y#f9R^#y z9O}Vu_zrcKai)GAK8NP3$12&+V}Aye{R}!ZouEaUw^_*FR(W1L4(iEzN$v-Z%ooAc z^el{o*W0_i+;ciWMZY;9#nmxg6`)1kB&^Pal&6RHDAt{qW1g@&9F0*QFCA~T!~n5o zdggM1*nKZ%aMhyCm>fnZ7ia4SNeCG+`0X&vI!KBpjdfif4Eb=l?9kcYYs8*|Jz z4GlyFFbievq=d4A-JsULU^T=#k5)E5L-8*nDd(KK!yM-$((NDE*E7yoQfB7b2-HEU ztq>i2*X5Om-*hN*u3r^-HGD=G27J2>f7o5Z-Cwz`EGi;NER&gceEF&7OZA^+y# zG9i%(!dZA|gsSR3%&j^2g3mO#%_u=jeP2~IXai-klRIv;;I^^}xM-NPTHPbO7b2(> zf({0^SXn*p;ZEeKk9x)E+k`JbUyss2V>XSTG_prDnC{-sj3+Bw%Xw-2csnCo6JrpY zUWTeW@Dr-GPx1xMa2MxHb9I=6Zp}+`;VNou&CS*wir27ijAUQ!W@-Sby)!~t^K{rZ znt<94*bUjd9v5>~=CqgZjWykY_&&7e2YO@900)qKlziSUf_#}HTPl*gi1}*xjUaU# zsCvDCsa?{4Fd#TfWyj$!oil`Av>+g$rhGl*b&e)g&%WNP+mNt@b|p5=b2255YB$OkxpC0>Y6FI@?NjTDZ&s+t*h^N`^bqx-``8Fn_2O>cG6U zcR;^HA}j+P2E4Gb%77VCW)}Iy1LtgU#0*h+9XOuc=FXcd1LETBTJB|JbWX6X?QuU4 zlsyN^eJR@eRPkC1;th`Q1zJ0rjIDOEBVJpz>(xLCQ@-exkXHk&u6$uv8dpwjS}6W9 zN|81q^*C{ZzfLU5g1Al+?&nZD6Ik5V%theD)*|7^ST|`Y64tw7s=O+=}4hXb9??g0hF-bN$C*<8sKe0%b3YMDtxNmUo=% zHOK88d85o(lps{B=usOwy# zbY@POWbiJ&z;A7dnE0C@)7A4F9jr9}mKa@((809|u7O6KF3Z`f{#-8kWMbcPY$IO% zMyt%OM-m{V$!<~bamuzQ#rpFKQkv{%u;T;>Wl1&;SUPW1X(pFqu2(g;EZM!>ZP3@& zEmPHbf-`RLyecojk$i#w+_K#oUBcRwUvbJT)G8Y;Fp_rD+^zRNGj{@fHuZ-3&MeJ_7Ta}p{Bi$VjGyR!lXV7GTTSG0QS2P;(LGbWHz#gR%(fkpoq4fVs$2<_N*(H%SjZ(l227{?LzyF2j~IURs#q5byc)XA zY!gjyl{MqZ-%2s7N#+JO2%)`uzStC<0~a+qKhgXl4zQ!I2Tf+UIwD!$&`=|8N|Me4 zy_Y#hTTrYJCu)>5a9G8@)8K7OwwhAMhFer5b!u!ab96N3*{b{n8QlgLz_-4jb5sSP_gU1{t9lNGuf zoufflr(PTL(u{}XERn=zsxt?pNmnEJ>d@>FH-~+xOUfEzsNzkv>B~cLbt=j_Gc6Cb zILgpv1jkM?w7s|QRRyBq22L($Dp?dFC0`YaTa4#80!m4{4!d}2V4vPAr=E;5&SAiZEm3Y>7^pA+Q(eQhX!>0M|tU#JL z9eaZ{Z(OyQy{WA;ZSD4vTuGBQ60}9|As4&tMwghevTOeexkYfz|K!XmUm$rc98kinZx(ghGHI?Bk@f(i?0nmMZfpzzADI%#FIOF3@4lj#_9c= zf4!(XGgh8P(7Y^g0T^|+#n>{5hDgl*1;PGa3toOJt4ExAlU5ykfoN21nod=qNNO^J zUz8=jPIPq98=ei%Q|>u&bozPKF-9AgOQvTRpYTA+%S5m~u)R#N27Z1?hdedJ$B>Bg zHJs2+<@CDN*o46mz6eSee>)^<9v~Rg8TrTJh7HwGa=rxfH45VPJq_yWo^=n9Wp6f4ccby%3jr|ASu zA%Cf)DT6}`C8b^I@L^gieQ4JsVcNc~?pyX0bW-3@j_ zo7eAcjePiW%sA`8)V~Z?X2iLtsxihHo%91Dr5fxX9!!YwC7#0G@)REN5#tcvMu*VaXIW3md?kU;O-}2* z=f)wI<_k0-$<@K&7-jSr^d`mz)q-FvBZ~X*<9t2!v$@BKt!G9SZn%Lx?>!bvoMlc_ z7ovNZ!S+a|p~N?gv|gXV-K#o(;`_?ysQ0C<4|;0Gp5nGn+#II`)Wp!21r`%`Ok|y* z%EUh-eLDU9S$pz0+}P95swUb6%Y{Yt*p^{ZXVyjWET15iNb*I4Oy#ezjCW3@=0j75 z9G3b<9N7hDp^Ti>AgfhLdT=l}Mm^#|WCQvkFp0a=@^aRJ%>IJ(WelM`{=5 z0aB>*6J2`)BaiKgi!b;bFUsFVij!gmi-c^5_4c+VdUP`U%K#Vu%;|(U7;VU7U*LIm zEb@D$7eAdHg9GJ@`_40zp-?x6Rrgi!7JrR$Gr&4*f|If*V-?3_W&UR9X5mL;3|GIV412?1zMQ z0_P}w?PvZ?4(E53Zyp}`n)Z8Ur-AJj$;$SV+3WFknPh+LVXT zPo&DK=^QtnmrvAG4jN(6aT0wE=Xe2cwp?oQfx{Z?f}Yxp6ZtC61cnYGMdx(P9VVx| zz_h-e)08oymkk|r;^`j{xT%JMPpmG0Vcm)%b!2pBlMteekZVRx1mXBSdZzFuW(G2FH-R}w9?m?3KqBzdI73FY{WBrwpXgQ0PpZAyC5{IKp zB)ZF`Z-EnAOL=mP&5Wh>0E$w}SBYbgZ!hG$cCJa=)^?zYmC->ghuo#bO zx?;hFQ{@Hw0HS4BF(Jv~ws;Hvg0k;b{!mUo#^(&H5(h`AGP{^pWgm^bU%5jC!4QZG z*!@tvU__p07?CX||7J@-EQK<2yxPg`pR<^;5Q|Z<--<9`gqow~8hgYNx$4BYTYB>d z1q0O>74@|TamPmyPl~4a#t3EM7=ec6v%j9e;V4B*=;;*3hQ4G+OyWm0b~7qaFNc6I z#g)O>D4&wojZ9&*aLMe}601OGyjoP{Hamh35NOV>D<1vR*!GQk;Xq zG0L&0xCByeoc+O3FY#v760hr|Px}=8P2*Nc91Sez@c79=2CK*4SuN$<({hduTj`8q z$6_aQwsoAqWA&nR$VO}v$sb5oTIAHG*^Xh#xiF5GkLcmS*%IGvSYjBZ{3-D_u|sI= z6EBdYTdhCm6L`*YkhV#~zJe1X!E`3&0VpvHb!DJu5@~1IG=JbXL>GL$fkmOGD)XKr z)^tZUp(k22BbN+EyI*S81-0w>U@?@HPefbc-d}nx4xMBUG?1@y8Q?+?=G0Ke8rRKdiR%}2-Shs$iRP?39_M)-oO6%9}%(3SeWjuR*Q zD>!+20Oqi)S|d^#oPjdAgm567zU5vVynyDlk3OnJhL535QnrRi_gGE~y>QH7&so99 zCmyDCCf^V)!7$as=Q&nai*Xzk0Dt5^7#O;_SB=7FoZql(7eXSky|%lr z?SR8WnbN~Cd^1Y+oD3yl-C=Kibu40zP445wz*lb(n`FDt)q>{;+82o z#Csx9@a8^S_Bk3YEI#AEKgB*s#WnIn?@*Sn^*;_L*)yravf%DAI>hJ@C>e z#Wd!OX;r?|GF^%muvzf{5G!%TURmiY3t~@o91E1~`+CJlmqoD;Pzue#f6DK^v8ctNxahvMpH+Ev4?7g& z@W>tvPVmEnE@Arwy-_U~!U;9lHjO>IIr?KNyy@sI^6MCylaf9RJw3#9&z$cmq#!2>q;ZPU#<-IvjEI;4+n;uSflIvtH?@ zzaBv|l^^agUg+dkVg`MM@e^Ik8yZj>ePcXOoLOZFQN#)3xgX5+BiSkCbka zzdBf(2Cc$Bhpq#p^ufYlE^@>jjAyDUWB4K@FfAX0<=W=vMCe2*?fhc0H_m5M9y#LX z^*fzLzD{$7p7Mw^>sL7Z!>{mln&}GJ#dv8FtHFA0gj2lLlIfq)wBG(ot;oB^_{*zm zfj$+h;)SCOV&6HZQIGpiMD1H0pZ*Cs?%FoWfS7Q2>#bFoh~=E=g)@T!k2_vkCE^H8 zjnD*A9m5tU8FX@xW%N->h^HuZ*sPDM|LH#1;^Y`wD&`Fsio*6_>>sAzjGZRs~o`}s#QT?2M8FkiOHVPA00}NS+R)(U#mc_h2jZRP2p!irpxNG*DL8dhui zS`9{Qb{+Uy-zo84O4tEjQN}$C#(AfkG;;LcDOF0V0%XLgH=G#Z-~t(jXhwj!W2WUt z@WLumZCkobGZlzYC}q9M(PdI`ihuOz_r;b%_FrOVr+`3IE*dNzACiRf>v5{lbsRAy?gs=OOETWrWql3#bD1IdKn_EULkxKK-?kq25lYJHGJyHD!A_S1YnI;TB_KX;rGb@RXo zp2K~XaUmRbHtY^oxBn~IKlnDKCTXm{LPuH#--#n4$dB%XN(HK80Nf&eUi6e`4+Hqo z%bJT~KRh*2)Q=g( zg~P#qbv>BAZt{FKkX*eRQuFx?j9PvuT6Jmfj{H1L@p*0FHlovm*k@@(Us@GOsVm)} z(@^3p9gF3it@B2;+2~>QwUi^MA4mn%1 zIR>-N>y{Sp&@FvEB?ugsl{${@0(G?UgM6svlO+Z_t<`L`D8l%B@g!!V6;edPV^k1AiBv)m}i^%p+<557t-P#o7|{= z{4SOIm5yf~AqHW72(%hM7c(p8E3^{VK$ye-^gR0FN#iVDZx(!2;;17peNBfU8tPyN zs0~}Rn|^#HvA6H+6iR1T zHdr3jH_v*oz+h(R^7I-yS-1K5yCYv+d5WF5xi99-Vj)gU-GbfSwu06rXkT{uu|V-Y z5`TE=E#5((zTOfKSVKR_X|Fq+ojUquOq37YoVp%&u=OB~g9 z0FB0KGR@%msZfQVplSNLqs1o|j@V^6MCmS5wHCR~)#BDWrihKB9@Q|YBaA-qflEGiVy%KNcq`7^OgM>E>X?u>eTg34P%P8h(!I}y*3hauq@ z$>BiBF>Lai`xx)bl-5%meB5a9n%aiLLhIHvfv%2}LdlntpWqJEf#-;$Wc@7sT;&7r zYat)+$gsF2G=p+fe4;AJ??(e8Z)r~hw=Ug9Qpyt0E%J+KpfoTAQzea^N~P!-Bz3t; zzfxLToTSJ`0y7Y&-rn(Cro4G&W7sZ0dUxck|GH zvL=I7*2nhN&*+>XM7AuZB`sx(;ahza*S(Zrf<7Z+-%sjH=TG+-ratE#492-{}x}V!TD$p+pMOAg@_A0br)BOcQCt1$Dm4GT){%7 z&9uiZ1MaH|JwTV1^C=zQ7UBA+xb)LDr`zJXSa}|7v1f-{Fos7AQC_VB-q2U#OlLC_ z=wb7ZbSAcUJNUDgWW^)q5=;47C;h{hbE1fTbq@x}uQ0grGQo;#o33=wpNt0?)p-Za zfr?7K{I7I&jnWw{2mHBfy&C14M){d(nwH_#wKSBvEit9HXun~jZnG>OWt=KyU1o6O z({?^u1@!M5%ozvsrZGt@jl+}~?sOgb!p#io$sF%Z7r(*&O+>5fo-<#$dA#`>Ed6oG z*RU9D?3+oQQCTDRkdF#hL~Y|f`doZMcU1BtV!VkBKkqoZemp0v{ocG<|H14<;KclZ z$F|6(tAzO4P7pa{=6uYXI83&Tw}Am4tL%Pvlxu7ToDd~&)O{?%OLSy`pVOHSKj*W9 zzmDjDau{~-PDXIO`zLmeKik{MG`uZwxVpiN-0F$05@*UC^#V?dq&eI^!} zw~f<^68d0)Xhf5-3Fbnrf~RS~Q*y#Ei}minX(hzCaI=S49b~Z2v=pbEvo27Su$Ck| z9)sZi$MgUp7ay7Vl&Ka`q{L+EvF0%RYhj>ct(TFb0f()6VTc1S%&``^ozIIkq7gc; z#p2*}w!h0y2o>W)Dzmvy#NJIET&Whcc!x@;DgW+P+~Ag)g68%Gm;i{!DnX%+?u%5R zC9I11lg1s1k=vWqzrE=2^5b6<*p1@UpM9XZnR_*Q*4=TrX7^=cf06Z;KH0%?sRBN# z)~ikRSyg$9lh5@^CZ5;?1l7PJ22HbAb&83m7dhVnwI%ikS*r+BJyt44yA??vIwwZ=gS0NtubP?yaaGvf56x%dI;G(YM26;j zeQhE@jODLeB@?(v>G%?P(BcxOreH8dZtx@(*V#1BB^to(5G;DaVkN!Uf~%BvNnbod z#BqAVOEw}xw@5f#ol>6WFc>5g_*rsVY*4siJRd+=D=>yTX!sT_A7;pWZicJS&mnf}@`wGtKHDh|c_36E4Q8@?FqbGo4#W^tn{N}OPD3%w;$Lqug(*Bx;(JvpELmb5claG_ek)5~-Z z3$x?~5hG>0UcA6-$+J6L0(VdWY>b&#Z(ovT3{L(n!A~!fS&e+Mnb4vFa9w~~?8j>a?GJmYIl5>H zu+U=!rVA8(q*7ULDK`I&N^;_W1H4q?lGJ{rz+SLU!eK8K>Mu>y2_ z-i-I4%BgsJVk1Vdu1aQC=%9N%2iB$p$3zezSc-!`|f29Rxv*l*}2hywQ zG9aEenSE$G>^BB{9@k3*v%FW&VmVGs%nrTfxLT`U#yE{okti-i;c`FW(>od$u2j*O z53|V_S_Qed!S2h~qW?!`b>MpSHOt1qn&ugCaMz*qQ$BmRO771^CkDG|);L8r?v)a+ zm@j|eKYBZelM~ppGL#jor|maIjTr{A{*`78ey&c>xCEbD9XH&}QdrQz-mCw4T-N6J z((D7~c@8d7*?9&^z|j~mJm4@so?%@yaXAUDR)V%Yu@*YTg4nks3a(UviB|k~BXoo0 zstZo!hdaLU&Ye^0&g8g&ju`+aZdsi;B1)^K?e4g~rOIY-f*GH9sCS4KFkm z1&g9fr(zZUJ!Ccbv=N8>>%tioy@QgYWIiK3sH7i7a4d&she7dLE6>nowAvO7Razwb z6j8`tD;1h9Ojuj0E(ZIz-_hN8#CB<>E0Gqoz|Fb*p4kzbQ9f4j@ zR;V_;*1OhTi8Vvyu{c?lq#%Q_255D+cZCU**aznjQ*OKKo=VGCb^SN+m?;^o!&b`N z?4rOo+S{+U`SJyAvdCW@d;#idFN~k+iP`AOWs&-EF$UANK*JhmM+es{V!t)* zr$)%;jk<NhG3bxZmIyk%W)L>QBM9YSu)SkE@T&&M+Wi zW%?3N^_x)&IZU3~-WgxEi5d+!wdOi`(qU|7Q>zVC zJ3n8uzeMT9;!1@dv4oX6?i*i?*@MJfp%D?O(%_&oQREE1Rh1G6?WTP&U5~(Uc$$8~ zD>Uf5mEJQ5Zx{2Z4&Wzmo>Z70wK&;Oj#-?XY~WN~1{&g9m411#TB9QHjEa)Oa39{n zf&(`Tea8DozEl(Wtx%;a?Jr0SJ2SFmD(GOyB9ZP~TUJ;M(TUEJxR82y_|7WSF0-j9 zJL;+icFInR`%=<$Namfl9Ver~6mA#lO*6rGg?1>0fw+z#>nk#irh*YxGy|>XA~UTJ z>8+3?({ACl->ArxI>-u@^sLq(hP>aR0luL;6GH6zym1?K<`|_5;+^M^m}G;!jA?Pc zKJz_dw-QX{dk9WUbQP^mRde2cPy_ud@yh43);Dm#XFb;c{+HuDx(8YJPUDw#eh z?ONUcUCMm>asF|Zf68f+J19#Z-8?nHrm(o?ICQich*upm?VVC?zbG-=(g!+-W6brP zN=zx!kW?QEXX1D~4a=fbJB2Sxg2XgniSczR*mrD(QZ8l|*Ai*w0ujTf6(f>H+2(ci z_YJm@3-rNZ;WKT4u&>r5TO`uUP3-tW^Sy&xs4|+)ZwjdMdvtgbyZLzNue!LqFRG*m zMyR_S+)O2Gik?J|U~-hO23xL&Vnc}}HWk*0>gdZuPe7-4pYLnF7x5;Ufzr?t)Dx_c z*Rh+a`Ds+Tn{vu^a=4vU6+2bFaP+v8degZUWjLIq=X&rgsqAY?5@ui&_f$p=h`pqe z@kx$RV)>9_%0X}7bsc>BO3>h_cNKCys3lt5WQ;ND^m98!9!L`)vQ}b$kfP-6=XWCQ ze~w`Enmmyhy%_#Ur>6a*eIqaRJ`W^alV{8T;TM57 z@jzQ&Pf&*E%^iz&a|vIe3ND6E9c#l7i|332s+ng@)8!u*3Z zVwI2{^%6b>nDSP#7&VDZ6e}GmfHT&fy(TYV)@50oTZfZf)QqupB~Bx@gAt8 z&L1C&Exg<0c?M(2q^i`U+R;R5)1EJCHDMvBjpTVYOw`mPljo~H#pS*)Y^Cw`AI{B2 z40kEiNaN;gi_O@U9KND6Y5I@0>&LuY8?Wk4Ufe4d^YFsF@5*h<=F5>>No;2#nSKY*`FgWze_^hv(MVK2C{7$vmlf}s?<|(Z2Z=iEU$T9T-`)Q4~ z-#mjzLVpQ$qFfdtEpxrS_~(uZT1*DO1@_HWQ@ZONk&0-GM3|jA0ql| z`j$lYsei@`KE3F_IFpTtU7(_R2sW^;7bBYE4tG3DP(34ivd`zHd!M-q_%rrT)xUgN zz*P^&1r0odT@0p4Y4X~WPMs%~3_Bc$43RY*B5bTum_Tzmu?Bol^W;Dc!#Ska!qKf+teKBnQ z_l=ac%@{v(*fS|u6v1=VMmEWO@Qg6Wg;=ugx_Kl$O;`FU6}IO&43_LtRx| zT3|_#(nkLgTJ#06E}A!^*5I&To^H>z{UbkO6eklMKB@r?e}_Sgs0|Q3Y9)u(R%X^F zy{8BkTWJ)`?W;s!_!-2K;<|(F^;DRiK#~>cL>s(-@7Mt)JHgL8<%a$Kr<)S;NEKM7r=s>Q39+qfJrBTqYO=CWNl(@Bwv_nhtz#P|BG8)hoQ^Px5(kHkl_Gx?s&6A@Wprn z9}3X`R@f-y{FK2`s2n_AmKZYeeM%3v!!Sz%mhCH1&k$elA%TlBS1^2-i$B01A_`Zg zq;|G>tnb0A=#VkEA#t$?W(|UORjSL1YcRS>XH*jJ2$EzPc1;%$#=NTm9b?%SsJfoA zIkj(xC3&Y@U<4LcS`wfz6!Nk7$N%?_|Ht%VE&_|i7p$`P9yKvyBvsyt6$%b61MXz) zx)khdSHW#+WJONNJREih)+)LtF}>@`^yRD5^Al4HK{_U0b-KRpANi71G;Nn^wYxoc z@`#g9SOdH^xWl)4X~KOzWdeH{;h=ur=xa0@lJigf>vfw6VlCGH8C6dE+L$-PIj`nC zxE95psbrrOs2ZqNV{#0K`2p~H2AO-lmKC)ma0YrOu*EID{X+JShs>q|IVL zincOF_9N)Snk~ zO?{NVBjn~wRIe}xMA#^gCooo_6=8{+V7^)vr5HjM+Dn)?w)TFm=(zZLQ$P9{(iZ!r zj5HqFKk{{{VuXqY!}TGqcO98rrEi98IcnkZH?G*B6Xq&|OVlENnb+T_`4gj3c~RWP zDp!2vsYn_b2F4>w?7y{RHsqR&B%ZuE1&{$hcwG&!j*8U^FUGU&V01%zzZCDtf2D~h z_OH)E%oMGRmIZwh7X9GMR5W5WWJcQ;`b!PQ3D;Bv>H!e}?WctfQ7d|n=Ku7UDMnp| zWNk_FbIBzky|sz@SQ8^eHHJ=gLp_|hn{(?6LudHz-e$QX!z1>gkgqb)YcmK`%Eo#V;|g?1&ye zrS$i|!z}$#oU;NWU$1cOT|wo9n7o`Sv`LaM}5l2Ucw<;1>LG@ zK1i}x1TO;B^H`he4rbam0DtaWK9y-Fh~*)*p=4HvVAMAw!}T%wFq%zUj3aW+kCiyn z+ww`>xdw0e^8)YqupvEL^n=5xzMPZ!2J;^SZ1Kmut$yDKa~to~k>Q7+JTQ<(HU-imTO=bBg8#C@4MW zxp6p=kwj?G*yI(s>@`jHtX|EaTJe7aqQKK*QKazC-zc4Xi)fIgo$bOu)hLHmF!xSr zZV;pG)=ob!u2=o?q7c6T*@HztTu3E4DKlTK`nr%7Gd2^8TH(3sr8z&JnXz{i#=uBW zlLE)+_IV04mR`M}c(b;wY1q|PZ1FJFGjl`}m&M){WhpTnmR;{TE>vplVfbv(J5OoX z7@XjTh|~$xIow8L9hd!7*ryI70XFU|!?bRS1mjM5G~7{8nU`K*S{pphe077Z$2q!C zN?b6xz9YD%$!^38Oxlo~Iq6A&zDDhM>H%sq`6g%O)*L3D4PeiFA{rX5eMu+BLmXS0mzoDS8w|~t@oB*R%L082 zZ|IfBbfz=fGW;Fq=D0#tN6E$jA!^c(!zeJE@)PWa`_j!EHH~+$5U3&$!+}PD9cmbO zqNl6sJ6c=U+IHsL;fIK1!*xm`Qo~)K7u`E&|5}V=4kZi8aHa>T6zvpAd2{Ma;VEe4xiY%k@j*QCW+`CSJv%is2sf`6^_G)Af&!YX=XEl&FN zjp+f)3XI@#6=CVkV=3~JP7c9Fpq84~Z@1dlR%Q@yIIhYSiIWlZdZF#$Szc)MduE;$ znOfTKHPXv$8f$Q^lGYj8hDe49Z8^csYPSdl_&J;?y5C})aLG8D!Ri)tQZpGB7$1+x z$8qWTN~zW4ful1kEqCJiA7m2nxZYsM&w3ktjfytpK|e@vkf~?H`c9AzF*f>26--Rz zDYS)v0fB;#GpODZli55g|4!GQ!Pu(5BhHN7URKDNiMlv(6QxwE>eZTYLyW4PMlJ1m zyDuMZpgz){BB0m5My`zsofv6VQ!O@+CdV@i+uoVizcPR9pfR_tFWs2$$}y!Hak-9d z;~V8=h$hch2wagQhz-qy%EH3(gCjqqEnQZBAk9b%N*spRK#CXMzWPB4r~m&BS*NEFBC$&1u=k?&fasF z%q7Yge3_7BG_1jdDT|3sFW@cSp&i4+YIyY?MR77em26VVI0YxDJA@y;P3Tcy1n#D6 z65303f>)ujKpcAoWsIlA;<{SrbD!fOxUq;*Fc)!x;K8c!GwWT$+026POTLa#KG_#p zPH0zkdaGIunoM~opaDN1OdZW}epA!ihChQTLwHkdh^QcEQDVVsT2z{v^_%je!`o$Y;$CflIalN?MU@HIP!%=<2=jf=&DPm0gTG33HJoD4^SrR3{$M%4<_;D>dcsbbB|~K&b&8P5+%rM>JG6b;I-k5DOD&sgm+gvvUkfumPd|+DQH<^woXA&NH(665J8Q+T4~`o3Ff!F0kE7%@)*di}b?u{{ zR2^N!&t!8Q&`%kYa2Q?kFp>c~-Ax(Ci>MZl%!kK<`ltcB4~MUl`6>VMfvTQR>&ov) z(4icH?Zbbd?k%i@ zn21SU}`x+uw6i5IRn-vv@i@1-GOw~+!qL8*B1VK^L&2HIxH zVsIRd3VM~m=$^j?Li0eyAH0Y6!twluukQWDQZXjv9V~{*y-|f@@xQJ#HblI92(D1F zo}sf>b>oYoELJ5u!Zi<6!!$X-$bFy(z+zrY(Gt6bn&jqC?hRFb5-%RQHBMM@CG>HH zfEVb9gl;gF#)-onV#(LdV1StTi*EVq#F{{_`f=dA@Rc)Y%a6SM2Q679Mn+eAu@BWH zJw$6^CZ@>Wk)`pI-#`cTe6y~o7K5{EU)A6NYDx|)%p)q32<)>He~~kh$2ECiuUq5IP!Hc8J;Pef$J>TAFG&K!Ox6K`vX%O zNI?bM^xVb{Nct;T_;}x6$->8FDw0uPQ9-KeBv@jWk;Zs)szhrT1PZaqTZv`Lp)^7g ze_GUPNVUINC5bFLCexw_ze=^8wh6&%ZG%NSAEUw+0j4|okJXdJjV+CCi_)FA4~8h- zBA+3a5K;8X2GxN+;4PzDD3|OJ;D`>MDiO@;Z4F_m?MsNi@BCDyHwkW(>*FOg9b(L> zCf)dUHeoT%es>fp$i zz94$^JAzwlF9|HBmeH1oJ#T-)arX!Q`wN|`4&#;K(?#uz7xdzpK&14^d5(-~9o$wa zWL8usc%7X7Ct$SjftqOe*Asj?{*(vzP&&D&QU3`)fm&1!N7?~nCA3uRbTH#0;7_3n zxAvDzBqo7*q;``+bM0FAt^(oJhz${akz<4@2nr_usyXd^X2LR zN;R)^kUJ$lir}G2Phh9{cZ@Zol0@98uO<$t7q%$XAa*g_I~r#>6X! z<*YV*CA+CDQ_JtC%jX@iP;Y0LP#s*EZ~pkLIumL?wX%6a2r$9BufdM??|s0MQL!9 z(}yF|raGe3Fq{hI>1u13C{MrG1hnWAux8(?1?*ZKSnF_;cezCmK3NG169)=$L+}U- zZU&t@E{IGsATBU18{0)en~3Po zlsd<#J+BvM+Un6_6z7zc?b<`C+jYKtMT)D~&S|r-g4^hArPQY1xtnyjUtbd>&JbRi zGtI$($m{!hlh2ch(PT*tf+>+>dX_ITIW3nr8?*_b3?lZ&Lfi#X+c^zbwJ$jtEJ~51 zE9z>*4XqY8F`M1qH(y|K2?c5+fkG!?6=UN}UcNV5AvVIecwYxPoTk$$h8^a)lF6gNN2)YL(5pbnqs}2BMxQZ5d7+`= z>q`(-Dz0!O)aY=yy-Qt7+LxH%voHamT+oq6tYZL!m-gyni4xQz)6yTf#DMX_O(q#5 z0x|zJyUSOHTUf4t zaO5jO#^v|*3CisCN?Ag>1B_p@M4Wewn>9P6@C9!l3zZ)M- z+_=>2RYb+}r5m4e^pC?pW%|E~Cpu8M%PM^d2M62I|4HneVaP*qek~)^I);cJ?BzyU zOy;t{opwuj-DQ+ zH*uMw9!1;D65~(dupTxlIoRU;FQIz@EvLS3(A&&Z8+#A@GZI5(vP_yghTE#w0v$yV zTPQQCWe$fg7A_}lq2(UusJ}xyBC1mkb~HI$q69pejQfLhsGY_f>aD1)7zU zNU9agFSVoBL#oAd)vU`QKFY_jIzlW~B{+DfnsM3va*J_)nhQ%T<8suI6I`s4>{4DA z|M86t_OC2{?;zlhH&(JVnpC~i8LkfFqJ=a(NCJ}?mntz%F>2_zSk9=mJKTPBM|wnaFxSG1&Qs}mTDx(b#HTp zLfyx`RQ2*FTqt`GXNXj~;_U3ScgzH9H77fMlHcGHp`ADcvJAnZQd*7y3Us2+k zMrmgsHj$Fs!8ao3R`k~3awXSMe<~@M-^dJs6Z1o{ZAo5yv(hQLNEd6UipoRmK6(+a|$W~Vv|;33LV_!0S2{V|)2&dHbs z!{t0W|1d+KKzz@k>ls&TGB;B8;I+O{rz>$X+42EmcHt70wX^B0SK%O&^nerd12QvF zoE8QpaOrE_3jOau7Sk=?)ncb(>!TvGaZeRadxDNe>~lE$ZcO zX%rX?j;$%+&p(_++l%5R+7@#33Hrp8A8nCY-mX^hN`++g`wA&^zB7l#s#QckSV<20 z{*&~syr&xC_bw_I0?d4Re+!B786AEbH3%{?_l z!68jX1&1F6mX=bH<%gsZqrD(vwB3jMfnUcP>2WpC_GT9261V{%pkqt>T3sqK9Zc8N z0NKj!PQF$N*O$uky^+o;B1R3i$wS$jeT`7fYo!{Tx!bSM9Mph!SiaHiQ9E{ac=TFn zXYgc8D9F2sAIu|NaYj3X1(z%348#)z2CA1kjPgr#rjZ;Zb57cn>t}YMk#fBTr842b zU|i88-QZeJdiQW0Olu`tm?Q>^)1&J#!ApDDS7ZD@Hc|y)Bsjqj!K0McPBFadwy4Hj zVTYC2Vy8)I?R|0ccetRnkkMg(n#GNM{+CwsS-h=w3mBK*s?}C49S47l9AK4d*|UKV zkLDEtw;mUKr>da+8m)uTFqQeC#85$O;1|*o8ww#M0n+we-e@UTdUBjkC~ge+(OL2{ zyRi9HiNsPq-D12rf&kSjxe)OxhYg*{UYrG5*c+91Y$i^lm$T2`B7fdLI{I(mNb}%rPEH64CIUjZ>&U|%`uz~sT%YUIGhZN>zIUpqvH?KKhTzm z9&iI<%!3AG`ep?BC}E+acy$d@F~$J3KP1q-V}eY!y} z542|@J0V6p%ouy{Rf+Pla}X!hO5#oj-os`+B0R7+tNIJgb@9jwUI(wI;`;&xo8RjC zmLi7O^N_$8@LvNMi%DYk-ZVew(Xq?>`#-= z>6kGX10oM;6c&D~&|m#d`zK8uE|zAseCWusN1V6zQu#7NINW7nz2C!Pzfz(arGZ-J zs|)+}W3_;nfb4@9a&LtL42z+wB>7Qgrxj{upd@jbi810SHuvBV^@jWo(f{6WGL3p! zEPUL~JP=V3@dEhTLwMpJp=s%eE{EOZ%%t~~PqIe!yM4w}&^hgXTQAh8wlKow->J+t zA6b9(4gmRR;Ds1D(SxC)dW1SnaS~D78*H6KW_(&xYz1$u)SAultfj9xA}+qjdNoY+ z*k<);dk%U^Z}`*DPw+q$)&pZSO*JFI;vlLOVWht5(X@I18>8__+&-7FTPO{m!1h6b zvvzJWxaC#r3KU-v>9WN??>RQb>~YN6THO6z_z-T5CfYWQMf}r$)7;G|Ye(i0sMK ztpcw~F=yHv>Wd_YqVz9jT8Cq?-;5ne{P=b?v8BG_sVX$X%wd0z9nM`}LiuGn8K*d~ z)ZPpZPq6GmeEn2amk!?|+-RNjWSKcwLbg2DuWaJfaJvpuk}Tl@G#&MueHmG z!}_i5H$70xdsvs>N=OSj2@Mu$VTPuk2A8QGelo9-0y3*f5yVf8Rf@3{Uz$1EW{3rk zRl-@!_ll!sJQrU9YBFg{69>FBy5lP2aj2m8T~*Zl zEcRnGrr$6MauK|O67wa>Aa2mNiB}W1b%fw*?QMVoa>a_;vH`~44F0*&Z-!HHQx-nG z(P19XRr|t50cLiFp+r8XEHyY-CL9v@2GN*5yp4v8j17kNa#UuBAygBDkJggBfect= zt_G~umuku-O9ZM&p~TP_ta2$?Cch2FojYjwSNuenibPf0L43;&dFjWKWhjr_e$O?i zYH>58E?7S$ZSVwS56;vrZgJy{yNNHp!g34-Sk8UUhQ+(~Oy@bE6MTh&RI+*pWzU3> zBvB3RP}S}1>7=X?oM5;ldx9rGet`26nXPEb8hmTYZV>tWQO8Kh-+UtWwLAUo@W@XV zv(q~lGm?VCh6SQrE6M?UZ6%tD7jtNj%6SBPbrSAJ(T3n#3*K8P@e~Xc)91c(!H9!X z9$cuc#E+0{6Wp z4H5?*7<*-3T50I*OL0T;A-19+!rJ|eG09;0GT+Xq^gC>*z_&+&^$@#@uY*Z15TWF; zJT>SCU#}$Snib?Pox;fG1(0(P0w~?Z(rcu&k=2BITLZei@xBIgaJQ&dVETL|gI)GQ zbS+i>qU+h+WWM>rD+Pmz%1_b;p_AE^s_JZJ($}|Iz(JgxOIN+n$>E2Q4Ia?_X_YB` zS{xjJ;6qi$ig_TaG%aw!=F$XwJLzpO?*p~H(N9*13qRnEc<4zc#Pz(BHsEV9U5IN+ zLxCYL7!Od5A~ivWN2yxB6ZvFpGP#PuP9edjJB23rW|f%b-kl`$R&)pw2O_Sg{;SCX zjUTBW8-PdbkSxCB@~l{tU~yU-LcCGU$+Tze+UY%Q7C(HZFO5GVe5Ci94vbNzO>FR| znn|5=(^H%EtXiQa%1`bv*f3^nfLX%`&Gxh>*6a5on|aJ%zc(arQs82)souL^=$m?o zvdv$+OtXcd{ee5G25Ar%r+q&B05LlYa-hvI+-yjxy*4gd(^ewrCxlm3ni{2NAEa#r zX-Y8mP81>Iu{1KJvH|em9h9a<#rl4VT1LJ(af}lESg{Xl6!@*R@w?Hm=0&a;x1niFX}6U+0NL zEc7kk>(ibf7T(oiu)e5s0?Smpz8Mf(%}fczIPH;9P0A{r#M$3{IlzFHGXENqZUlRb%T3!C`GDom#6q<4orRR++Ali6wm0})y4WreP2ut=6N zhd1~V)fgp-{0fRUcb8a~AlO}Bb6Jgw?+^svxCX>BY34c8zW?O3$M7yX;b&CWcVyG@ zcf=C%RgBO81U9Tq?Rd()0+iJs|Iw7)5v-PU< zmP?60BX8@ACDDwg0Hd8zw{_Sb5{XT8;|q;}U8qS})Ww*ei>(x_HiTTvm$7bsUo|-@ zY*Pr&UeK>tdPc;asGbz3y!ZLaROX!$VGl**m6f8*&`c=1JWj;-`qIG*tEPv>MvG#e zsflPYp4jm4$Eev);W`^2FC6-a@~&_Cc| zJ^si4JD0?xN9y(FhxYr1o%;`u{2Z=viN5I=fl2jEK{Nlq37B`oJR!I0v^>~77UrS3qY7!PV^+!u{UC!nQb)^^w zI3L{kJ5Atml$S6 zF~60x^U)952H`!_5t?n$9uiSt5~9J04GW7#);(6fVl(ZV${J1Y2R2`U(k$^RwfRl1 z^V@&Qjqk^O{oG|bVdzt?(MS9l`yN#cPdB4E0}(3(JflL z8~rZ$5-)3WcuXNbsFz;>w-*7Zqlx%xQnWxrE8gOo*BdMmFY7Ve5s&nRq%yMv+SFLx z=oV8@yVejbeCn1&VrKBEAhIk!cGLVKfm&?h8t%c^}gPzgAQ|fPYIc;MA>R zQX|?AUth5Z)>9Q?G#`INB<6-bW`mpOVh1z!gTqie`o4ghl~NTrF+c3$!M+gGr2(Du z;Y!!-MnVVg8Yslomz!gf9pEfF^p7yF}4JEPdTpA;KOus zIzg*6HSmeMsvxe?8nbkPW=?!R1L9kVYm^>yWF)SNsk)pjcJ@DEPT&>-d zUxSpnweol9&%?icOHapDbz6MrP8$$i18w{vE?>Bud4+>vs}MXv72B)w`l`U7cslHe zJ8ni?qy&`C5qc?-aP`Llvj-ikS323$u}KN#KtuTJa~>OsmKY0ga1OS zur1cz4K7n!9K%9E%Q7`=Eyjs%8alBDtBdmzvrkGLFKtmrG+;NC5-&ju%ko?uZx&mL z9;4xxT1bXeH$)2=o3^OS%6#Fb9eO#`azcwu-c7I`N^92VGSCVD=YwgQfl*0~4^*vH zqV+JIGbTMd94xBZ@Ug{GqDAX~y%87_Ifudi(ugp{n<&{Ll693{(<^z zU)p&NZvWUwnXy>kH*%>ui6%E|)H`?sz~W%@g6AsvX0RRZptjP`aabQ5T=$Lv?kpeZ zgg^Nk()CI0dy_$H-133GfcXE$^_F7|!HX*8 zA)(0B+wmR-2U{0YKyeSHxKV+%#YyK{8O(L^KA+$-wCDtCIR-ZA-WU{Fsn>sH%Ck!r zp<3_Y?yvQAoTu3(Tx+Whbyi?pCY^u9@zH)kKO5}hs`69s4EVvbw6$dZ2-lr&lxxrr zu2X6nAWuoSA$1pGG@L`IRT6t-aooL({m?1XqWZg_tJAMyCiqTLqm7 z(ku(@Oysa`Y>a>!g`61A>kdO>9s1NC)v!jKcYARZpRlQqW-m# zXpXS8)Kzm8h}Rw48K0m;na|OM2wSbD{2WS?{O}0>AAA)yRRZ&jLth|nqEwlueKfV2 zs{>sycRyHum42c#iKS0hj!7<%A9f$yH`>SmiCEtj>E@QA(_r*nx~=MDZx0hZ5KUc3 z1xBM9t^Fczex(;ldh|x^#Gmly;WAZNvwLXj_umTY+r(V1U8m%Y5^ai#hHbxGkZ27? z*75>bDQE-LFE?up-axO0ZioplqQcND7K%`4G!Wy=gCm$t2qfGaRcF&>jfCXeikuuc zF+bpxE7eGdYFey8iPR*hWb_s96ZNlmr+9@wrcneS?bF#EeGgX?8l$fS|*utHGq{dF)%2hJT{jb9&?}HPM%Sin{iaT&c+VQ7XiT$C?IsDwn6AX}BG;%PA{mBsK zCGxsU)NhrjO-;kzL`@&!G6}ifTVDYJf8?CD63RUBwJR7CMR@*HJ|9DKD3?A^NQ^Z7 zdMS~z+cKHnE|T8;ZSb|)ITkhk@aJfQdIvkBvC<!0e{;m=N%1zu4lX}&CAWzDp7 z!(#u9I0WrA^+lMdnqEUa98r5A7RT9(8yuW@(6fb^WHVJ3PY4nwrk|MJFGB21@LyTIEtm#YW=8fRhl$78y9e=tm=Tt5XPVmFO!ZjN+ zA}>cKSDDK*y&U;I&?_AI<%@P%I^IF)YKCAAvQ^YC;xP6Fivz2eiKlv&f_5Odmo`|x zD&}y2JyL!+h!xpkt@=95{A_CY5Jfg{gE0wzfz*v1FO{02g-VVVJ?X>|E#6mOe%?wf zXQtB$F)hYdQ_@*ZDMbutdAf$mx-NnbQ(8rpCoI)kyEPCiSCCZ+xTr6n%nBF+ zPt}}`5@L`{?CX?LQTuvZyf1G5>?D=J2<(CB_9|G(ryGcCN#e!=dQXTE8^MRD%#X?hu#(u{LS#?S%RN2XKcq$idmQDrTyu*OG^Gh1s7C79R{? za)E_kZKy62i;OfLjo{fDnZ&Z7O)KBkqOLb_zt7VIZ?n$VlfR>Qupj0 zvk+zY9DW07F6&Kem>(8VRni$;fw%Qyz*Fxq4iQTNBIm+Sp#&>>kO>teZQ{rw^reln zd@xF3X6fq+wni%kgG54vzPfRSRPoKanEsnPhYmXq=ypWe{Kr`WMPP#f{5JOc;6 zQd5i-Qp4~>=0hONtxh9{p%yw9URQ@bP!%cPppPY%hLlZ=Aapkq0FTw`C&VWUuw!ne zgI(HgjD=i16taCJxfB@N*6`3oHqlok_Rvoh%I-kg^Ew&hS2(o@V@$u=6G zaxfTalpguY!}BF(LoYTfo&n&-cd(E>S`iMP4j9o_~uQ3mrWeN~vcs1f@X}^?SjI z?by^-K7 zKc$}bl$w9;G9~DA1qBZTK()={mc%ey8%Qi)Fqtlqan&FogZ=fo}i2B8~TVV z0b!abuoUaSm6X-LMY3CZO1(%0yk1tf+GT{;BtwR;tTcPEd3svHA;7IAUc;S-tlnZJ zAvQ@~4ey|q?c^NZX?!#TYzF&}Hs0xpRH+BE^6=p*txqec_Qh1Q+bm9|flo8jpz85v zTi+vXjv9u+E|1QM8U#$@kM;GJwqNzSq%)qlHD)wD$}|D+Y6BJbxVol~#oz=#luI)M zfrQZ#mod+LrrLKoGXGeomy~W60`237isUOh`-L=koR#Dv78}Z5V^d|@u<-4Ydwh=) zc7}wQCA2ff;SEmkgJfKXi?75yfo>>0@o1M5tJx5ol}sfxlGQOyq0*dX;zi5@;wNr? zK1YKW^k88K-C(o5a;TE$(%m1Es_&k`)8_iCwR(2qlXWcn=i>3Y^l63;ikxjTAzuOd zX>vCCl#Ws^GI26Jot&kZn}%c7)nhZI>W^mFYBC zsjj|)bh=rfJJP++aqsx)*oJdTn>Y~P+()0Rlze_Mxg7NK3Aq7r;uCA+ zR7Osjh(`lK?4C3=j8M0PB*#5CX!J$^BGG22nBpC0v9vwokS4>GOowg~UB zLnZ2~2WlYE^xJC{8e!8Bcnsg6X{IzLF$gq)!RGp<2AzildFB<+CK z`bwoeG|Rsz3$0Nlj@PJSv6M4(7mjxVay$IXKTG=Aez- z9d4dJS^DtJRSr+l$w+3vlDP{;X^9Ml#mYDHiMwR$i$^c3h4iY`^gCj-15x(eiJJLo zo9HwOCq}WLY#?5@*+gFn_F{8)SKekd$(*>`Tor4rP3w_Lg5jv&skAz1j%K(%DsfMX zr>89zf{sRm+bg3dG`xwX7sB>fY#zzNEi#Xd6y&Ezeo*J}=5OMmpYk&uHr46nLwj+2 z4QYO2*PP#(pZHDK1{bNc?V^V9sI$%si>Xapo|nzbg~3uInq1WkS`p~(G3H><(dMJc=Lp- zT`yg_P=zsc#Zj(0HLk6n80Ih_;#>BkJ`sMpFSZvonX$#mMg4sviyuot#5;k(x91*U z5tY8vYI%v|IZfksxN#yP{L#-4O>c`wSRN0mrUT27U>=5ieDZ((_qqzsl?vq9)7}t1 ziW%w0VgG!iv&NTE$^ERBILC=5$61VVQY(y{zS~OEE31126zFSGr9G51*dUvAxOKX_ z=C&UxdXn`KroG+cIsi*H;q9J8;?X%(>d7MZy01{3tPyL)IC`=;gA@GFUbt3O>SAwl zpR@D{?k2&lH`dRVITXQ-v<@djFcUpY`gB)23<)p&Ok)~`FHA)Tf%H2DHIV@k8#)RW z`}kL8RisD9zcQ-=mnpI1MBmjgiTQm7|4?`HwWh9MHGA#6Iu0k<9KJqu&}s@VROPIT zYL4xmZ=}&<)T4Qu9geyRfOE)m>>qbv2L{Kr-kTyJVq?a+)LVXkH zjRAXMRZ^WHZhuIM!5qs10yj7^8gfb>Vck-E7!Y?P9xH{4(uKv+v1fwip~5m7P7fGJ z?P272c|EviCU+*EQ%+jkJn+H$YS3w1e=RVggh3|`rhPel;Jd9bFmv^WtbobgxNj_c?>T0L16+A_H2`{Q;>q9~Vz1ySk5S%;gS9%pg5 ziAwN(F-JmIrHQ-5%Z7(7cmXwxXGQq{@5dc231Xa4>q<7A)w}jBp0DEivYtO6c0zIO zpy3n|H3wHJ1)mo;)tBm~N56=}i3vhnOTx1Ac(tr#+(Gs4^x|SLMdXFyN`tMP+huUL zP}T3t`uh`vu9p5dyz7BV-RI)0U-k=rt;MF`k9J3})+&()U*LJYU2gfc#JmpKjtdO) z1px(jP%6LeJ(}t?Di**ImpeXF!%`r}C8&#U>);ReMiUd$J6HmwNCWgeL;i&a_E~QW zCj9CH>Cu4bKB$@vM)g$wQR+T8ylWrS1n!~Z&HY8GI=#W*)ZZn1-Cnd<=m@*CLX zOtY>nc06+7%9e~`7u-P!_7n}~|5l~fj0IVoq}MkI-dw9XLe;Q`nWent_`?O}Vfl81 zf~{;Y{>O+ugR9jg{n->?@BmmO0)=-Mr!@Y(MxHLK@*7M$^?VTvgN|y%xY*MtI`VFx zHY*+<>Ch|KOO=*cflHMz$^Mz3LTuuRNF_E|8W?l5`z@4IM`u$r#e*Ud7+(xYwhfEoyM`OiT8TbUDex>kKTqo?oiAZ&DnxKY*nD1+TPE5`55bLh(9_lAB#8(-*0p?1{+r zh0qquBO6LbN73pvCXwc-ugCs8vfBtTjuiHocAm%}iOWktMeq$OLy@G8gSa~7QFqu5 z>_s!zFA#Cor}9F)Ymr-NyUVK*f|Rn%fZYW0N{vk!lXR~ z&gZiRQ#V@?IC*eOm4a~Uz#{s5f8@T+;^gyvW!ycDBwnE(Ic2UWxUwh3M~&Jv^;(7G zCEA}t(kb^Oj-(Ug#gr5=D*(P6j zO(iF&>9$v`rxYK8ks*wEbS4K6EG~N1{UlVxrY5mOQufV2_9{692vH}giPEt?RZq8%g77XSFK<&r9$MX>{?T*5R9eb}5ZWy9g* zq$J&|?PoQ85f*H%o_;GdfEwP9|p&p5kj{aT?ZFa0k%bCN{eo(1l9cbg{md`g$@l zVw?$jCn8*_Q74PQoW!s7lN}bPgys&JOg0Ra8?=Y=^^V@WHNOfAVksrls6hJx&h}azEcKRWKKt9)qJ_EN%BRrOWN2`uBvYxg6_!hH8nJ}P?bz^kgc~2Jx~{3wg=s}yf*8^zU@MI&rsAEf z7g}X#ypt$bem?qBtbPrT^P<(sOsXM)3`I@_Dr=XG6Wu<7z9bZfM-(ofM`y9&02=Oy z(379~`M%!b5c?j07Pn-4T(@}Jihd40jHvZV^b>FwCHTV7UNsvHvWp2Rf%u08j-U0J z_N@&?_Cp-8sr*n+(@PU77^MMmkjKzFdaEbdwL}{7T(Lz`v$I{huGnnT9Sr|R-z)2)E%Ut3?8g1sS646DnMdA!6RZn)I>&cx^e`H%lv|6#)~&|ihn+BLzw zBVR*WUGF{C_r9kyF`ApV=F{OCRVUBT`wArns`nG$z-QIo7hgkqR$+wT&BHl8st!x0 z7kEu0o4xzox2$@aZqtk;Hr=ALP=?vP%T~e^UQ>N7GbGeWJ}@}}F;=8$Tm)~d6gsP) zzQRkS4d@;ATJsOQG``$4y+xrFO4%5JAP!z4RwayYQL;Toq31(+T#+4GjO?!kjGo(I zL z=qpZ##kZQd!t!@V->GMHJqBCGLFoOcFousqqUAo*27+pEgm3q2>#Xky`X4UD;3PL}Kih0+NYl4RLo~ zV-MA+3;=xG%O)d!p8?S`pmaOjdo6ha1$9SY#2oR%b=ArO!ZXwnB%`(VK0NXg2V;js zZ9Vb0f5)MsDq--CH(u>KP-Tt6?VPSchm-s}NCg9DTiG$O%}{ckrYD2(WTXlw@h7y9 z!-$RI0pm96*mo$+rWl^LfB+}G8Qc=!9qqDKt-@L#`#vPwXl{r>AkpYs`> z9jJ>Ix!2X|MjLfFn9MuQK|ehc!rP22r5FTG>Im7Gztg{HKAKjZ8gL(n@iO9bUDNr`59Btfo5O z)?&J(P;e8wS~&$Wf+eR~lk6S-=O3ifdxpB=(PXNllN@f{A;RE%iR`Ch1p@VTx)%HE zt$7r%(?ew?<8o8x!)dBJ35+!FX&P~&`?}cKv!TiRRDI>y5HINyLAzqZkNFf|z{6&# zK2qX_kGRWi{D9Bt2D8$k@Q^5p9s6sXV>wG5ib~b9@{yH+JiuTd(Cz{{#wW);pmPZz}!5W|jtt-uO8KK=VFuDIKc((EY3@8;>f@X&~ z+!8}O3=UsFo6YOZEf$hC7BSj2`Xju9E2JsyDwMpMy(L zRj`;`Dne~a-&d2#`wy^rctQ+D0>o&9u85iG`8vhN{q~}>P`*MIA_`trKONd)tkPVD zf+Cx=>;CbhL#G?~ERhaD&x^q(C9%28JC@fM%T6DWM|fRn^Bm&FP1*;w*O31+*opVr zz2Tyo`KYf1OS{zJ%VsZRa@u31au*oJ5Pql;kwUj1apd2;_$_$0N`53`)@Y)j_0$IY zciwTbdn)!vNbUu5xuaE2Y=YtO>A?lBcHECepcPr51U-8 zm@ID4xZEN5cr~^MkE^Y+4+DbsCB4R^QMMbS$Di2f1Hlt|bVcT9PEA`={%(C&m-+kY zqWR(krjkx^$WeVLnAgXYke3KCc!4D#+c5yUc8jBV-t&;;mK(ri1nNWM-M_BU!sziV>?hw<2k z*B7Yc0rYjTrd|*Y)XCS0qe*JMBDOb}%r4N_g&qOoGo#WNSA6?C`C{1VC^JS6cNSRX zVDxjf>Hc7vW>h8Q@94kY=eZqZ`Hbve{)Rakw&?F2`Qle(svgf#3CeJ^!}@B$=cvLB z6Gt-SlzNG=F=}rd>Lp?Hse=0j3)ZQ6)I4VF|J^V-kbEN7~E`zA!awWyl(Y5 z!-r~q;rk8dICEhPh*|2s$n_^Wy5ceF=t8H3^3`Ll5n*EVW7ChZ*^HUCB~9m%u17q9gLkYKNVG44?ity zEwFO9`RuOR;Hy_%8rIbtryDUX3qglQ`NWlI z;M`Y-?ujWqSO#|#JH}ZcSm*i zW1Wqbe=i@mD=K~#CDSm)!_Nfm?}2}Uo6yEL_+Mo&$EyAPly!8hMO{C4nJR%9MhE7@ z-jXNU;zmMs)RyzLn!R%QfK#CD1&J}d6-ph#F$;9iC{bNR_L&nOerEP%0U zF4J`njF$E6ZKot|;dMhhM1*^lx|t%K3to|Fq&-bY#-BmT3hw-xXYsT@8Vup*rx_&} z5*ZOIaX5GK)NrXvn~U>QQp~i`0kFwATQ>R;wY&znScS~E`u=rOp0CL%EGD_eGIVgY zCKL?5tPy^v>SIyBweinYw>$kXlJ@25pE1~T74Z&r6gyn67S<^ob2sh!zCY)@ASr6=$|$ zb+e+hAoh0)WFg*9V;tbmup`PFUTmab}BAmh4x~4?! zm^`?F()jNiFeMx^hB*wjGz9lK?8u$v5x9%;>&Zn5$30J+h zBU!n(#Bnn*nYi1h`X;XT`W%e4JO|4R%Azh+Nkri3={We^t=p_ zk8=-EVcuy+yEEKJWpIkHL5bu?o>_}=HHIu#c!T0Pq6FdwO0HLB1^e(pTkkt;u6z+8 z(?-Q-@%Xr<%VH`3hb@|oh39Dp`^%AZt6|ohMbS-Es+wdoXmD9gsY z08OyGp}vs|ZtZ-RHN4XF_q3L)-{8bxY(jRCJOSe?lF{Wxq1l z5SJ;rrO3NNN69`Vxx=oFP=qP_a!l~=r_D{VqNzj-eM=l8PfPK2LLG=O$=BK)=(76m ztvUmC?VT^bJS#413_x6Si3Q_@O=-6{B29=QtQNpWD`~!W2`lao%r?+igTB2UaLe!>JJWYbBMjg zzWH(FYSm}ajZj=I+Vj+4?`ok67nm5nw9=%KXZPBdg?PD%@~Uau{prMd{?X zVJo)_AEMO%p)ddPeZlK3cBMBv^{KboP6*Av(L%SJ@fbMVcz*lMec>siT092CFc$UY z<23MA2d$xQUp@Bnx4J{RgyV}w&r6iEA>oCp7_sMXLOj81E9Dx?2JD0KoLE|yw=A{D zE2wB4VOZI^oM(Cyi{%!!dm0k@4&I3#)!{$H)Bf z`oFukH#or$QG;C?Em;R4Q3Ke$IHnz)419)zs?=onvct;%X~psernIn6JNo9E0>NmSu%2AQ`joaZ%fKVy3rEj3%Rj^r;64e)*G9q&VHO0wL&-4zsxd?S$#WVpu-<7A zcrJt9ZnZwy!H7BaPKhp^et<3hkkJd|uq3RWf0*SLox$CBMU|N8BeWhUrh2j9#Axh~ zHW@hRSe_m2H|S7ZW@(m1zH9#_LwQ%*YpgzL+#<7`?W4D^R5pXF7X|lo@*`s11-Jlr z7%jd!6lVzY-F!0#ivx1AA<*DGR6D*9=M~r!&oS|CI`uP2VPhE!l_!!IE(dK$BLrw2 z)2mMRXRNi7qLIw#k;alm;zF`%+c^pLg96oUUnz4~eJL-{r;|=m!Jv^FSF;o>gc9Xo zu_tUZXV}K!)?!A&fA|KaP?T3fO{g`i$Y3mFOVc?2%P@Ea)sy?hEWH?fm@-+8*h@xH zvAPuL$DF3a^V1Z=6{sy4oZyG|Xwpx%6t0{ea=Xx_#%zhP1Kx!+c$CqkU43a^MeVGt z9wbAU%*A2P9!Fo$dWVNQSPboyi?fV*t2KB#Hn zaC_@JnyxQ99oBcEf=v0%4*M`ddz}x|Ta_1oRNu+C42X%LUn*72jt+jMtq?9#s!X3G zKrqr`5R0wpi2fAln1a^GsDp^Uvl3>SS>ud^1r^EufW5gap$WnP9t)Wqq|A>&w^o&P z^7n4`c;M|-(-(U^^LaJM5l4G7qn&lPQq5J=_{*GVUgqEq>h<-eNNBtme7w|7Q5Gkc zgXsXhExTOfKqc%5T)bZ9+5p_*mTl5~T^$)QMuf)CSF^~>N1q3b=J2m9&Y9d`gj97y zgWSOgmHCQNBbV+=EfFL39hYO5tGKlutKX;m%#SKYpXFx|T69GljE`e;+VN3fcHqdo+|EtH0h%qBGGN>vQHWs!9%Xy24dA}XT92jRKZZWah;7)3A|G?L3vWABm3UOkh_%yxRYwIVQ zP9)-4`^r{}|LC=qFk#;aLn{Q8Q8d36wC2fGB5*^@mGBL zb@1vsz;q-^@k-mUJNU$IsX~jO`>M{jztj0*aPktr+}7K_`HvUnkXlthoT&;_)HH3c zi?J45Tg-L0^c6on8kNniM4?xe!Jyb_QL^rDui$)H6^Y}A221<#u`IE)TCGv}Q25eF#?cVglJd30a+YE(h&sh=2w(HCgFuG+W5q^%p? zAoxQqbl>6hJM~jChKAO8V|T$0U8;_jp_Ue!Hc| zNuu35Nx!EdAn{VxI=RfEB)ti~NxQ22f}B+~3=D|L1V5&9xV~Ez=q|)VPCToFCnz}? zT0;DdvVgb*8%DDI%5GY4wTeq=cY$^p#lN{VCq~u120?YDg*S!?u#G{Z^&Ax#^^)@N z7#I~8$eEV7zqeHbuG-hG@V0n|N}5F->h5b|LnZ^vNZSVNmIKZ5NfUG$(?#NZv9J`A zmry7xc(2b_(oY(*UAr}>{e!*|S+BfI$24*ca4xYR>f3)uMR+RH=CyptqRxW)qRBD3 zU^ev+u1Ah-=~{F$&A1`Zz0k!Vy2BSOm; zSbljlZmS0hEyda?^M%VOiIGKLwc#Nq`PHb3Aul!9j$e84hn&EV8$CYCr^RKtCSw5Z_|t)I%+3r_0= z&i1CW_aBrcV9e4UszrTR%o-p4zfuj21FaF3XC)X7G<1?5o|!qj^bQMd?@1@3=gTSK z=PAB@^VT6T{5~TXU9BFS{eywVNFZB~M48QbAS@OmhpGK==$FW&y@{xxz8v!$gS$J8t}xkPHTBmRY!ORbWW&BRz&>r>DlxvKVKq%=7+r#X&iqt zjU7z&zW$O9P3L~fH()WVOlyCnCWWOhzIJoED(@r-ozy58jLrA5>RP0}hEtLqr42k8 z)bzHLs?Ep<>HdMQ+LSbeH3SZg8RExT*g7;%1&f_>=R$PJIOz3cafH}?siw(=LkNz9OB}mM3D?xD ze7&vdb0yv;>GGFtukfQVgOygv*_+ppOQkl%CPwSISLcB$=I4MG()Xu0skyusw|mO1 zZ2&ObWA!8T$D?y`z>{_atBD-l870~LUgBNFj7}V4Gb*biP5jtT%w(QRV4fP2V3q1- z!GmQ|YW>IkUMYzfJj29D%m^+~5*5Ob?_==M5Q_X@Izu(8)ViGc@b&-V^zG4@VtoV- z9P=kd`GA}eDqQ7Rr+ z!HEr9wY&TGG18MaNl|XP-rlL2kY@%X`> zx%fQmMaI=}II-q+$GSfBwjoHloR3wujSbdg8az+w(*CnQdzljBmq&DVf}4cQ*I+~pA!5u&0 z7dVVUzXG)?F*|S2C?-XI-;d&Ptb;Z!=IV7bR&lNB#^*0G6N6VZ^+jgGZaAzq?aggp zmmlIb8h9CJeHp^{jjR+{GFCSdP>#ghaH)3V8x;~fU6YI*j6P1u_^^I}&!^64wK<$* zrVs807Tfo7UfY%=p`#GbRslPcLbhe8GvT1&m(PAaGlcjZ`%@OL|<>|7Pt82 z*W2xFQT?rTT+cd59JrC{&AkGdws5M`6KAma*8c&a2(5|qnv6d;Nfx)z=DfY_w{OK< z6*3231zurXp+r6!42{2s+y&S>fZQ1ko~9%|oTB#}yrEn%VubHHsqA>BHaHnB|B1uF zCtAdhiylyomdad!?E@vJMcqC16F^OlCG0#}FvP~?*KY1QXcU+Zvhg%OKmCnTg&5{H zyi?cZLwiNusLwY2g4i9eGGLda#^Y4vURLwla=w^Q>2f%kpaK~w&$ByYF-XF7dv*ox zp``m+%oRBC-Z}lkV5pc&j7&C%=kz?ezAAhkqid8TS@`qPbbD+GS=?L6`Ey+%kzlWQElGo%8RR%c&FRdm2{w?pfuRCTr5Fg?c!pkU`aLJ zgWW^+MU1*bND(GdA@xmipn}Xb+(#*Ta@Nc8+xiE2kKjapNZ=g1g$2;^`r=9@;SnZo zmv_Fcq{Z&zI|@Ip^pg6q-bha5-`xJ42yVT0%L-J{*HWf%LnUnNFZA?5N$t;Kuy9N> zUAJ_1T%{y^j1NR6*B#{+ae_$WQrbf8_UPg^D!H9=Ra501w%SAWPZaM46r{>^`q}C? zr1A7K_4xJ``kIfbM!`#lk?y;9y@HKU@y;{@Em>PsYK> zrtPbEmed3|kt(h+G2#iqus}RW$sL_GR~u-36zT@68fX|`@U2SaQsq_7vKQo?7AG0B z3sI(o6$S@lUq?~oaLJTQo@tAAb0=T63a>cKfmK10B)&)H55BYOE62AgJ)4nL=ck!! zU^2oA_=`B(mm4Wi4V-|h-&8yS#Iq#LgAGx#MP_6% zN4p5}e1majv;#nJp^_(f+&%_RX?6_pjs;Z$9rvdL4BK1t1OS7k@x$!aOw8%3W&28% z&;fV#EZ0gLC-R>-7@kyzrtjsHdz%i&xNNa_C1huey)G5_Ii;)+u_#s z>iLPJq75Qcp-p*+5vZ^<56zKL$ABPwrR$(t!;)XCeovg&+tnlSS3m)b=|;W?_B`#I8*xE_gFAOb zr!RoL+`?mvl0qu6ms@cBrD-4s#nVeqWi!FfXqa@2y01#p8!U?OP+z!;gD;MgjNH)S z#xnz1;b(^$3k&mgxCz7(tqPn)gM+t9v*w>c>7l?NRs}(iP`4Ee?xQ4(90OF2JSt1A z%)w9aOR*3$rR|o-l}g0NDB&+x-YrjzSxS!nqFb$mjMiTlUvTDW7#nP8Zwt-V=zAs} z6`n&S-OtmZ;ZZa-fSXLqtY0L{VbCam{Va``HmBMvz~bZ-_9HTdT|!dSw}HE;o$Q*# z52%uxbP)D98v>txR{xT9R3a266d;l9k1}bZ7*EzM1f-tfH*d*SjL{Qru+IlvHhSf*;VdXH(q;Yp4x6CrFy7 z(kOq&D9Dc^J;S_Oc=ENWh{H{;6H%soh3i;q?TckeXDe~I!rEGJJeW|gz@KI{yC%px z&0snymk4}c^DH^In=XBo>QON-elJ$O#Lr^2q1x;B{>}gD435F@^MuNw{LPJvt)m}^ z3MK?)cKt}x&|;Sl5j^TGvFpI{p3!)AWO@GsAJ~ZUna>S)*!y>U;Xj%WFgWnjPlb?> zaH&O;4*L@xEto*y_J74cR;|}f=NvMI$!VR#JxVvKS&k`%AE~Nritgu+JOu_k zxi8vqa0=~bt;s|dCHcMTx|V_Zs#0TvWQ%)pAZwH0hYAY%a0%A2LGs{chra(_W#XiU z=Wo-~xRu2TlKzn)D7xuS$>-@_Ivt(yFt|5S**L+R;7zgAf;ETbWEjV(Igi1`Dj3CI zip-~)I*{>~$`4C0ad^^X!azWxe#|M7O0U->Xaj`bw;$!pOou&fD~dnOQ4Rj73b~^S zeu>`WhUcBQ)%bY_bN3TX54X!Z%sJMvpTx+9xq?%wUVZBXmIsaDrsBgAnNMw9a8u^T z=-d~Tf_Ty+XoPh287wN|HHzpu7TnK&Wi%UJ_OH~t;4-xrKO;}0BDEOs6t(=UR5l$= zUkqr%8DRD^ssZ^Cq?7Vmc(kTHI_#N>gh)Dm0AEHr!Z{aT3k4nVvg%CV!bd8BoR~_x zftoEAi>iZGl93dDT&whPvUgsjnoO)aSZ?2_8kVHK0qj&7x1ZgrL(ghuP@*R>w2GY}@fS z;Kv`l0f!SWJe+ZPaidVQK6Tc7+2=`pg-L+t$_y-K7fvXPn7fGrf3!`NZ2dM%@dcm9 z&~P^OpyX#*j6<+o-&R=VpSn$!ux4IB6{yt1=oD_xp~INIl4D+}C~3iyRaInsn|y>= z3PjPo-Zjt(u2v<{8ArZI(-})@D42Key0Jc}2!LQQdM;r72H)J6C0~<(3H<5 zi!-#pe4!`Yk!!5fBO5vp3-wqj6N~PB(80gx@r*sBwrNr1c>}5{={G;sQU2yh306tT zG{r0w89mG0CFV(sqNJ*Le|4*u$#I_4!*MsHj zG(m0)BrWf8dwFKuA?-8!QRx!hc<_B|onGc}%c9z$<9zJ> z7x&E&!<2^j^NBZBg;TqTyE`bpEYSkEWbnWka}xOpUSFjultY=+tPS-YZhU-1@$^NY zGFe(0sL=so@Spm2rCOOUJ%xXkj7@MNKcKY=60mO2BEzmwDC8{SD)0PJ<=lV;XS=Xz}^Z$OOJ{5;Y0FrCQ#l}`c(5B#aahx`;#@h9xf$~li5@jUueO-(>( zb%h2b+P~T0#zBd<zVVulM&UN`LrF$qp43WPYpz+r*cZ=9g(7DbkC-^O(Q7#Jt`9 zD>PTVg^9S?yDa9COyt_LNw$CpHMC@n7zLk{-p^JTyN$Z{pWJO7X3JK$UH=zqfwoXa zl>JDmXe;MvaPUdfl_YCx<2&{^*cFey@bnn#S#Hm-bye^UB?#^577OC|Lbb6#LcoiUHx;pFUi`1Jzq-SZ5yt4=O* zxbfmV+Yb(%92!UTfF{=DFfl(+vu|^*RNI4&%j@EvGR=Ta0`1_y7oU!XlQcu728FM| z@UQ0f1v|{e7oTD$p_6XZiQ=$7`8y&mz5q3oe#BsF%$y`{-69?V^HW0y;!8*u+Q!Xb zY%hL-E>6cvyD|$4phEqyxI$vo2L{j_CW@o}>OQ zbG!N5qiyQb2_k!$VLi&n#m|mZorf8YfF%@fM`|uyw16T}h^-~5xZlw}08Gi|Wd=uC zh50W9q)fjzqWi|+1V22ns!G9PV&1RnD{@xisJq!u0!2IJH8jU#Zid4c0$+R=g@>&M z40#weB6xch;OCffzFl9O@MKwRUOjnvae@wwp+({=PtWF$i#>2nQYEo1tL@rDzS`8R z5lm7>Q0$tipV4;3+x7CjeI}Wviv{XfY91vAV}j7n0nGDefBpACJ=-q0 z$MQpBet|E?vkdV!b*(2~W6HS+{9c`#0LSju9FfRD`zym_@bZ6Um<%pc!HO=9hud|T zQMCg`mwdF2ahcunraxVr1V=<^5pK09)c>6EY=`09KvuH&DPZJ+FG*Ha0Umy(p0jvCTQhD+9&p>(S}qa9Mcz^_Uknw z<=K*(Y;mhlzH6TQB2)AU8KJ((r?@$6eR#ogD-lkQ)6*>XT^21CvS$UM)gj6VE%}O4 zcwgr7AMmOyh!kxN`B&}`(Dgc*K@eJyHOuov#9iXRgen$6+rAQ{b}q*l6ck|w!|#el8C}scP^~k4qJuYvXPmq`368HX z2nCgk6K=|b;jsTY5qn)WF;K-2uAFaZ88UrMoz-I5BOJ;m-NVK4SS4u2Ifd4WLgQCS z`s)Ate}YBRwj{sQkJzs}nwk!pjgC;UAJ=I#G*nd4PhvGTTVjP$Fq;n*`Jzl4CJg)M z@bHogSY!{_RKMDKcYKK_+yOYw^K%M+hczl16EVKjvp@QpS4g8(PCfbx87g_Tm8{^# zQcBfcDqr@QPV*mVMXeKMQVeJ|KLqoE2aQV4!SHA}9`^M!iK8#7q~O$dWsy|pr?&;# z)CKRSE}h??7TjFb+P>1^mL!UjkT3Jhm&N^Fy*%S_a5y>3MoGk9lBpg!F4n~iYA9WY z7B^*ygfmP$CwP4oX=B(;IEuIA@We@aAAYh@q_`J1<$Ow=(qW+rsieO^C(&rx8SqCrb%XhO_R)2l%>n-RrzCb$PcwRfqTV{+DaD~m-!9-Yognv8f9M3 zWYBg``Exy!)+Y7+s{~$h#OkUdIR1Xt=dV~$uxG7A7wq|0EX%M_{3+>lbngsJZe5qz z`SQ(lji@W4Y094kYjSyT8hin#DMj3rcRWGF!d2R*&X;S-NfEkdS_84TG1@7ukr!6d zlXa1HKIDePaDbo+eO+uZLj-TpIfA~F6Ds+vgn~i^*kGn%A_8Ar1z(_~cQ!dnPmsSv zt|mBJOFRj57~hmmg}Kdt}4(TXj{0!yeP&PmpZ& zz;7|26HfB8KZn!9vx%M?0TGdMsN&*yiwQHwm1Zv*oZtu94|8Q}6w2V>Pzmg~K;*pG z-tZg{OD?o4cD$6*=osVnwVIemA+fz1hChhW0ctM8MvNcEn)R&{nKh~|(|mX??@Jjc z*wa}|Pr%haejzR1H{J(hTD}9{Ch160W!iB^q z$J^>&a~JUOh>r%YY}`m`F-IJDQTQAhi{WWsOYr;b8*&KB;PuslgMtRbsGO0G4fdyO zhuMp#tNat@sIxo_c;c{l3N7LHhLeehJoE?0!n&GdV^{2U;h(PB8GJELt=hO#Qy@< ztSMwdCXTIbG|`!$x)JwlkY<5c-FFSIrU-cmb`cUX&n}JnBFl6(+wSGH&dVm=(DUCZ zN5%^%rAqf(BoVG?gb<@e5c^?8S469?ro}ZVn*7+JFgXklR0@ToYIBPiIyEcen~rlI zS1DPmbB#j{hbiN`4!{c@fgvL_`2<0LovK=4Qn z^bga|{7?8Za(3TnIPVxetf`jB-|_^62_AX9WiP&j5n*Ta+4137gBz<5;pA_$H2GKa z?ff2#b`z%`wmI#e8pDk)X<%A%sG0odwz5U@!k0R>VRq0cX6*N~JWm&Nt6_2iupUuY zv>OqnxQXVQj;0rf=-PPAKt6C{en8yoc8$JU(%7fNEqs9@$f&x4p@OjC8ITm1CO`|+9*LMq_IAX~M#c{LZWz45+K@u)&~d+6EJ<}7aCAy6an`YPNo zKWC@p>Pd!8BW-`vR37`>=fHmT9z9$l`e@fnO)@olfV2i?~orr9LT8(VJh zlUwdf5G6u)zg_2C28WFfVw#*tFj$2{gx-81;)!w1EOjuF!-+I5LiDt~5xxd7zulVf z6=jmaEx&X-|H?vVWQ`eec zkQMS0=-PKA5^y_TSvaau0f?`mP%&7RvSu6T2!HZbg-5WT?=qhkXmE>0^Xnx%NVn~} zeSCycsq6Rjp3>Oi)IM}YRpXb=>&+r7ltv;@(s%`bNFutPuPq!`sAAa^InS8G z$oQ5H06XHx_zsn?6DScPOpKii^&OJo_GrNsaQ@|^bJ^Sd!VPTHxAYCHrMi_ zIFZ@BMmwjRlEPsaP)Hj>PVx5&%~4dQTBXt#CH9fYa)WWlbNaWZg~%}aGGu2L+kQF^CK*ZD6#JbY&SgKqW6c!-Ykk; zptO96_{#l!tAm?~n@2-FEq2tkHxHQ3KgtaweilWzd%R_KWf zm5S5({cV9h>*OQEb3dRtbp2&0KV-YWcSls_-yNwnAgYZOe?ej+u2ixcqPl*LniW3S zVk$Y1J25u$Ud1O|JoTEJYC!bgdv#U@KtK&AndJ_nqM$C&fJjpG+U+#(vMN3?`XoCX zW~U?&;z7hX_r;4g3GH)56`I&&ob5pyL_e_2zT|@iXGsOt@~cpU#U<3zPHrPCAL|=JI=<> z+B)a$&#!X~4A8+X7ON)jw`@mJV|L&NX&QGnq$>3R;(5$3jtkYd^DNo^^INXsX}P#- zYW@cQf#(0SPniKZmUEu#%ph~Hd3!4W{De%SQeheB;UX@B3)F#w={s;%RKx#p^4DST zVdu$~gk%$v^;SZR!TO<@*&IWav8>c)47MIH;h5BDx@^SH&dm0vXVROM?gfL%3mWGw zA}xMa5;wD6q0`W}XRuQ!r1q*tm1?4W-X(pb+MttJuuwW2?2qcWaaL4-{(^kc&F_Ww zvv&B&4)?fT>D(4c4%7)~v2Bj9iWWEHY9;p37O9NIsGt&HF&ysAriMG@q_4rmWU*1T znyp^Cg8>YmUivC|X?~Wi^j+GNhdj1wvG7*P1BR1f4yE8$89Z=PB0-L>Zj0;o@_$es zP%ZayAbzOD0?Y#uxD7t2gXySTV5;0h1xVXpU6<2#3+hjlNQnnMy?ky!Pv1*d-CH|;H^DLi+FPePbWlA}H@JfU{Hr!BR zX(3py?pIKHqHYaeN}0~l%Lq-Hso^`Eh{k}REQ(3s9t%EJ_1@z8{-{90FtsY+#QZRa zDH-#B`>~m3m>R?>c@nYIRCOH4z5+7+UYHC<>VgJ)o+w%%;wjz2Y@7}iaH;TBM}!N-5=4OJvC1pwEXBNQ|bToWuSHw0*=(9~jBqnHqR^ z)nk!&vAR1=$5bwe1MU$$+}=A>q6zLo2iN8nnuT}4^nh7@(lF&Yl4E?GwMDTV^A(R{ zSpBm)-SUiBj3T<(9Wr{Zh1;|j%@;dL(|2KObeWkZ@bUxRKDg#B<5i`Vb864(eK(k_#lbx5tZR2fp}G zdeS^11kPQzSUL#YRpQiHOR4m&tvT%q@K37ensIdKD$4uV@pP`uC=$?+Tm>oyGma9w zFO0mHZ_xPvP7}(BMNTVUHLY7azC~Xqc|6Mca|{NjVGo>`ANIf`CEOaWr7E>{(!h4X zwIrr76fq9`Rrh=_@zjjk-c3NSd zRKe|)sMADCCVmF-UBlrQsCB*ua$2nsZm2HQ`nEW^IEhe#lKr&4NA}$On&ODqd~oDK z6~EIA-9ht#Mg$B_@I%CFo6`yJ_JUl z>|Eqjokyct`mNMDWs4%`!`D6b$NkCPaH=(C4of|{q^bObSmzCfeQcBSSCd!$p-S)E@_Jg*rm)ywh-70hM>91s?omiR zEsL`j&DZxS&>T*_AV!g!{wOVl*vIFE(aE_DUNYtK!&y8><_f9HK`v-avZdOfuD>eO zk0XDJ9v(bL$r#ME36);~Lc~sblKFtY> zTQ`UuJzv(y_$T=>@fwkFbW7}rCtubGJ8LFoAGn*Cb z&Aim68V)y^`L2V|QweQ`ePhH8cahldV`N@2Rz+$0#5R%FX1BzV%356Zk$h)}fD!qQ z{LSKGF1F=uzB=+a&5wtpvnjPBVs^k3^-lB~%><1Fje5#D!BiI&hPP53HMregc<0)w zRL)TEjfvFSP0`^U9+I9CFkH~ZO}#uMr*IfY5Dn81Uyq2mo>FdayF%dquHc3_{8LgS zziz=95W|Ezb04NaHHs%+^o$zG>c`%TUhBwoJXXP$s5U-<_u{(vCk-J7t`$Nnl1U%s&Nu!2zswoM^UJWKB9f$rlcd}jAZzJ?J?rbE1nzNk_m()BRX zbEE6)Fy%!R#2*Hl&eT6UPsijr22h`qLl%D9UujW^T~Lqivz%#ukSFJp(mEtgkj9K6 z03Sj!l*j{V59>gan$kO%l5{5<5OXE@s?ENs{@UuKC& z+~XFnO551r#;aljB0dN|T=TE{G5l~%crDP2YOdr<98Dh1er7^Oiqy|~O)|7d=@2$@ z@X4y1rp3&tI^<8leE%}(F!<{-+MEuzZt#*tC#HW(zz#CCIWLPxIU@t=Yz{YD zBE}AxpS+32b!b?3bIN$J!^!D(S;F!93sSe`|C^5$o;q9q6yHsigVLK+x^w@HX0XH6 z@+E8)+zYAT%fB^lxZ`-}+K=nIsu+0DZ!n5EFf~O2Vqu1ZFTosbXSL|Gh?4y&JF@IvL7L@x&9AT+NdPNEeYX;M1r}0Z$Bu52x39ZIau=GZK2E7WIow<@FEqdqf>l>?8o{si zh}e}LV!;iMOyE{MRF(0UX?i5vMHOAJf4YQ)!bU6E!J($+ieG+Ns|a@RiF2urS-9RN zTLdX+mss3>a!0fE1(`BEZH=r-{a%N~moHrvTc_&~b2AyVK1CPPf*}R;c=U6%@S!%%*8V-Hivoyu$yv0;-JD4o8mg>8ChAE9NQmTZ3f3w0kOCCAmCpY3F zX%VbioEMBBIvBXk@Hlm~m=Fu5S#i@1PGr25^0Q!Bl&Ubk96eHyv|CZ%BF1_<-+Ul?`$xDB?X&EMS4yf|0jWe; zXfVZ8$K!_wYw$yqBXl{^3|3+s15FaNLBQbIDj+do1Af7L##OS|HgeraPxRnIrQM@q z0Sd`A25vFhRm)`8P3$aa5odN&v;7tRByZ{HS@=+;SHsiM42Uv`I6;R1l=)NL2wqkR zRvLuWkfl6*2Al1X9uW~$1 zE2?Nj%hXe`c_=XiH+X(TX~)%GG#Y=FjBwuZko40x7dIG|Mefha?>sJB}oLx>BvEq4!8BW*CQf1wPZv zbcx+W1?;8t!$+YhW1$u|hH__T)S9<|QAnZ@`Z%=P$=7*kEfE4kc0$Uu;;GW%AE zeQ=SUR@C+ki16^Ye~rlBdOs_KznY1LQSkglc$cpml|BtfyH?$Z*leEGSHuqG+YeM| zzZ8orj6tJu@1T5S)K`dIQ@J{X#~f33m3azA5OKLiIa!;2x zuoBhxw|qYbV+Z@41|f13F0T0U1_a%Qv0NH@z$~`+38}9+7mo-MgcpBYv#mt27A&^Ux@Nr!j{! zBoKP-+oIi~*$;z+V0zd<&tvF;SYi)cT&wl@0g%&TekV=edHRUkBb%_GN-O96+{jxh z9>DG>$+OuBr~N!%EGfqUJ)34Uv5Aq76kk(6*)jcnJEut^fAavxj+US7_}kg!WKw=x zQNjpDRnNCob>ljE{{&_R>$)`lSQ{u0-u^8y_Pi>SA6!5<~`Agh>1La(ilm1=}MKQ{%<#tPboOqF(-$F3E zM3uni@->JSt-hK5h_sQ3ao{>pI9#X3-m`2(Uq|VxBYHfOtd>CMbdbFCNbK};z*Ch( z&#sV6fa+#47>irq+}>+H9CGkt0+u=3@JIqT?2-b_I?DBZXSw~5NaMg|Jk|iiwF46P z*;AirlF;bM)QmDI8oq?LJJ}leNHzX05f&1!47Wruc5=OemI31|P*;52k_9C&sL*mvbrwTaK%zX90}g-oAqUlhtt%E1}-^p`)&QQ zgB;sO(^og5gBqmkP&>?9j4ikRHc5bm&ptS5zR*$T%av6&XAP6Zjl*sA_)eg}*EdQK z_G-J*xw6EY;Ek0IC%K*BL?MSU%*cr zqx_&Msz~rzaZirrAk`b;(80SZCHCgq5~+1s2Td%UPGNuuSFlAJ!sUaTC@rF+$}g}{ z#uRZ5C*$p}&HNB%`3JXA_e{E)mC{O^pCLH0y|oJ*Pbm!!F4fj}2m@DSW%9RSn^9NN z2Q~Ckc#>A-Lq=BMaOX^i%?WW*R6btx^6A+mJt6}pMzM_*ZNVG+!I9{XfIR=lobudZ zSk$ocB`?;wq#^-EUqX4Fo}SB~PkuVEPX6p}m@k|hNl}45OX?M_)|Y zo1?l4rQjqnV))=D(>!=#6_}&qLGsInw8tE_WWp-5;9`yNr=x6|ozSYXD7wN*mv(66 zMiY67XYO2>^VP{mIBXP9o~;m`M(QjV$>7A~(z>ICcMtf&N0o6hnfnJ~dQ{Ab#X>{z zW@o4@o~)kF!!(~xKksS2m*7NxfLeuUV0-WwyfAQ$Hm0b6<&iE*r;RiSQe2T|#S z=4W}`KWUu)d6%hrnj+_PgE!+E5$}FeUo~k8GHwg)zd-EgwsqISR#r#S2-kk`QIXFjvT0E!s`spFFjFFVIYpYlG{66%~iTu!#2a(rR zDaeRBA50w8h5gJ&%nn*hT{XW`Fnrrcaqpder)&R;IS$upVd|9@W9tN!v9|{k%G_sEQ4)M ze$v~n;d5!F{C2k|)m%8?V6fl$E%_qhLHdy3P=MZ6BQZC~eTnp(k3;#wK3 z(k=Edm5lj4nl! ze)=O?d~RjJGs(_izfc`Z%6G)Gp4esP4W_! zZiTtc=if2RC`<{(vsJd*Hi};JB5q|<8|AINTD}N!TA}M8+)QLO#LZIp;94a?lRE@2 zJk?C%Cr)Nh&A>iV2QDV3)H5B7Eu0s7u75n=Rr|^B^ab@&1{0^{_Y#2=azzI_c6DRy zqNGiu4F^5xHf@@HIlwN_q3_Q6Uex~+(2p-HJX_wCl4(vpX>e%u>fmUa-_!m8fLHQW@_SOhY8PA| z9`xTZx#lwcoYBmazm1rWTDW~3Ws33?DO;u_aPWyMl?cv@`9l6jO`O=TZ&a)4zhe^K zIG^$u;!hK=3Y$*g>vSjpTqEf5%#&o1P-&Y?e6gR9#jIWoY1}xBMW;I}>14P0V!r__ zBLrP1Tu+CS$qVSTovb}SW>I>bqGhax0322c7rd=T8j#=s7DRStQN&m1XDX(9@TcHx z^YaWgYd@DTlWTkvJWlKD|EP}~7G9;$Gc_|mOYvF$*S^Z2lM2mLwpGT1<#0Zid&PK&FYC*k zuEl)<_7nmYB6_vg^kf96+F1an`AFRvpZd7t2hi) zs^o=Os=iScF-}s6HF3hq0W-^lwH+LkGejrSJ4Nq$1sEG{mCa?9NYCl;{T-rA}6KxV4uNhCWa- zy1?jpnO;ciQE+0%8QT61TCo&&iYoC-TEqx+7AV z?+BDk9V;-sg}i9_JIFO?@1ZXc9HFJxcJ-Lk0qyXgu(^D-;HU>Np_7m-;v2^Z&W~w4 z-HPF!X~SNN14az)(6y;ECGt}w(r6j20nvZ2M1F*na8TCCWv?bS-zsyq5!7$m6oM1^!SJA_ar?raJs7f&ZHn}jh({=UkskAUJ{?Az#24s|yIrEYYS+R( z)CN;W;}<#AUin+B^ZQ^z)8TxR5~43D7;t>O8QwWmcD#z zt(o0f4&;jt!BwlxX|<|uG^7&{FMMRK} zH1Z6_e%d|}jXiyXIxdx`Cc7OP58W6B8x_d4$i)axvH5BYwdyLDa;vvA&M`$<_&bv* z9qg>QT%!sp!gxzGS$brq0mhC>XJ7Lh-n~MGoqPcfv_vI?k5DD`qQrdI#X=Kkf#otZ zDaB;i<{ScfwyLO>Q}t`=e~xnrTL#xrt6V z{qtlhm-T6O4>z^$I6@aOtyBKJo^dW%;`FGU3%37h|J~k~(_qi_xSQ}52#TI#gF$3V zNr6UOrR%7W*HE!Vvt5-zXU2;%c)#W1w;%4OG7b?)uQZ|FB0MyvMB=I5y~>ZLu0gwxh5(8D!%rG8`~F3mr0Tb4>LU^4;9KC^eo}vkiKqOldL`m)6c` za({URl|DE>@Y(nZNQ=E*()EAI=I{pvI{>9(m8`ON=JtqLoHRo#0Iy|qn`~qMnU-_YGd^-2D=@BQw6nqaDkI? z2X|2!djuW*7IKEnp4c#p;35^VLV!qlQqe{X%lPRfRQY_h^tGyo2(CuiuPp(rM9OzuDTgwPj1uH&CW< zflLB%IPwDoqwNbQ$~g(5mn%~^MG?&g$A)&D!MgNG+ra=@-dASeW~QjCfHPFsCx1sC zsoX>Pfjh6$myRFs{L;ViqRpUUfik&Yhar%~#l>fLFW$bUjhkUUEj?cHcSHvHs=>3X ze;~8E`cA?jHacm;Js8^pKVdROGQN;04P3$gyPJj~zSU2dw6e>WZ4jIOlYjJZ)vu4n z=jO;O)sD}Fd<;zl4CAemdWJ5U=p$0IT7!6duh;VQ;fGEqQ27ky04Apst<30gBA#RA zXbOfFQqN6;E7&-GYMZHq~L=KX_DRHD4=ukRx$Pe>wCa_$nM1Ou;m~mLNA&EPEh~%m!nM3=kYrZVr+hFt{ z+9~9G9pki~&v6c}NoB-;hRS?Vqd8ZLQX}BRyWilQ`jUF6E8W!d9@SxolU{K(t7msH z^7cb2Q{;BSyf3Nk5zlU;*S+Obxpbe4obEsIl1j-kwFb8Mnx~2QiEEXh|9s>O`|8I| zqKXA7=}7C|EKWwVs}+*PBCOL7oXl_@;EdF6-GW8qK%WgU%{#t5d9D)c@n$uXynF74 z;6^S|bX~n7e~b31aA=U@-T%pFUfg-7(7<#R3c5+k4sJGBu-At{;Ib6esHalYQDv+l16of#n=>v zqod2D`88*MzFrf1^Iiq$1nyiOT=`ozGsYd>`^=gJD)-e^?i;jwU40QGdZxoOwAv$I zAO?RZuj}MxI=@28m+Q^n#ICz-FVlN9bt&v<7!tRoN5`gl|JTf2U-2k)=P-Uoe0zsG zT?=Bn^j1=ZdS&JSov}0;iH1hrzG#L-b)vcIj$W|2VHJE)_BQGGWyk zDo3!^N_r4@SMz$JP0uXWFuA;V*ZlP5jzUO>(B=QbX^W1KKZrn|7d6j<`~#8|*Nky1_vY12nn4K`i6t z@vDlxfn_2sWPKE-UH4dE>am(7?*Yk&(~CrA8htMPyhJAEI}!~i0ADbZmhj8rp!@i zaia^gXR4VjRg5S;xzeL^N0{^2M4EiXp#A_r~}d$Ql{6) zHJ_EL_7LM*6BeX`8KYTW9EruRkyEPD&S3C1lfe*tpVFx`AwtS`pE|k2vXc!ek3P7e z+?vSwh`t#{(v4y*WgJhefr?I~6l~Yn4eB%oQ_GF|R({s{t86fya+BomhnbF?)p@!9hOy#sWZNsaw;_4oV9FmQvwCyMx19~Mu~19 z(w$aYsymD(_oW?^75L9SX#Tl5Ql)0=I~dX1e97d|cS!=f)t-L9{yld@fIg_OwHDcx z_Dvyvh@B-rA~M~SOVQVLcYyJh?G8gxEyEX3=4e%f*=-sRw%ETr=Bu|F$;%RJxkz?_ zPSlLF8xT27A5^~P#kYYp@1su!*p4yvG}|>OKd77?Z{ZQcsUd?h_)id@A2n(G9`E0) z%4z)||CZpV*U6-ozVPi+?1xOsXscPhxFpFroV@&j(%>n4qmTc0v#4H{>90kIKJ@=L zC&kWX02osd4Tv5G6`m{25y>Ut0Og68JY=6&p3!ikKwU$v zW7in+C6>+z$+=fFN}OtmA^n^9`y1a(xviH4B!Jjk}9%>D|Cpx3j!N}3@HJ1Iw8iaJO?LdKr z6s)s0W@LX-?+{wUnC>qBNuKMlmZniNR|T$E z082zx$;};Z9k|%>{iIA!oNcHUP@*M{hL1jJQ><*UMUF9T1BkFpF z*NaJ+xT9$?OkrskEe-dRH?RNx2HMwoA1Obp>o!;LrIKe4Ak*ui&KI#5TtP$P!Ii3` z8Xb=Zq80cus~%jYGW|b@& zO}!!TQ*W$7Qi7v6U7B4#OLytBAyK%;g^NT7&w z$d83Y99E2ZZSQY!xKzP|Ra-^&CqkZuYP=2$DK%U#F@o|VA^Y_sN^7Q?RbsJ!17oIl z(tLU<4u{Clxk1qi&23_N38QPcQzLsAe23PTjfw~GS|v?`qHnDZBlIgoV(x9trxNcC z(BBJfU3Vu#_-ancdyqDE>U5Gkh!^0VM)#2%N52Y>!Kk8dr+zp8_?YVg~v3l3w z>pI_{!(aL-B??GuR`aMCz+I0X8+B0~qBoWHg*6~KL9`}V3grd;({nRf`~T!poSyqp z+h`Ix%%{VXojZD1aGX&;AmCduW^*<<1x5#wjxfCfvWB~1XXcUs>pR1 z4|URRr$IJQM-^%r*4Jgf;0su6dd07*NT)cEhp%W&S2y(<)2kRtvlzBRHcs~UU!!XX z1Ds%zFd#<#p-N5bZ5wz)^lD*hJh_s?tA#E4{3#J<&Kf?)WVHEDRY=|SdcPC+i53`J zN@a~04tH6GU;|XIMu}5(U#_$a-C^DS;5wx*6AjwSH>59Or0D-C|NcNfBwNbZ1#kLU z{Kux>K=o@rkS-`s<{IP9Kz+|ti*|+eLpIWI*qkZ&L2nRmdrDmRiqsy)bNr4zZ+t#6)~9P9 z`q`BbNHyflBfTudW;2|7r&aqaAFT<6lcKublru752V)OZvQ4icL9qXHTNvzhkwDkY zddywS%lR79XUMi3d~$6xz&g2~X%t-k#*&0S@l44RRh!r1BcKUM+<3?6Nmc6XZz_|I zPl5v01pVlhhg1~?SDR9MtQ&xH0N1Oj019{EP#wY(41`Wk_bz#ch%;%mela*ZJxor| zj*ZN>6(4OLcc`J7;ocdK5B@Z4G|D`D0qMAWfB+sYY3W=1=QyIT9Q|Cvph7YU-7>_& zh8nhH@Y?F$O&7?hS!;rxgHIie!@uT>I#gfOa55bBkB8dxz~N+?^^eEs3ve{da@o5; zT{~3G6-=S3K?c<94Q?EZHPDymwSU>|pw8(ywT@EIgYu zA=tkx><1VlVUqwb3SUP`e?5gg@zb>vhdQFU4-00&90>!GPQw48QlQJ=$ke!Yy%Dm1 zZmWCg(EY^fDltC8OZlczg^I2s%d*jtka(pwX7d>1O3f|mXH!{YRfd7vYuxP|`Ucl) ziH;2}kF>#=3pO{LNun3EP9!c@&GZfm*8=HSlmx^{X#^XLohmrbxY~=q?ecz(o^U+R z;F^N{2kgjf$30XQx?Wa2o@niD^*MQo_VYLBncH$c;94cf$x+YPigXt7_~atmHeyNomy+Hy1n- zN5m~uqlKps1DAcPev91=y8Hl_dKsCH(fIdwFC!gpJUw~?rBE15D-1rpqv1}B0Y`{J zNL`z81IX+^W6OhJyO~LNqeH3(us6_~Qxk+74EsNWUf!tKgyXM3t+F0jha2A@PwlEt zxQDunMf28)Xa?+JEx1Z4^%#B39}!%n-;NkAlTn_xPl_*f#TIYjb<}><#IRit&(L2Z zxK6Fc48EJgdbZ_JAx8gvtFof1+Lkv2yy1;fcXmD;3vZ%;7VOt4>OaEwDq;8G%(wc~rF6beloZp32E)h3Re&^Jn#&*|+Y z$4V)4ik!^?U43aI8%(h*?S7MP;a{+IN}}hO+$9cPG8cK=_}_-BE~rGA1-wnac5}BtLk@4owm#U%j`Tq3OMu|b?y$3La?Y# zb#SZ2aK2nc5*OZJ*d?{%X9?ncCC%qurb90K!%_a5_NuW+*0{YzzOWUlR9%4!s6FFw ztY`*brOEhzMg_yaf6r*xus7l>;7@bziN!O5f2sxNpV8z4c>=V`9E=>4{SV9&o~HSb z>XrO$Xd}445)=wbkU+XtawfK(2b7G21}9!F<&(zMT}`Gfe<$P5!E=<7AkassYo7B% zh>-_!6kOwbIDDP*KMlWjGimWA+BB>>-_F+>JF$p#%6qt_)kB=-7x}cj>(dN%*n3)o z#;n*EKl>5A3(+%gsv%vALCtfd<4ZC=I=G`f^YW&fk&oMGF6;#w=7SgWz&ZHJaMB=j zfy-3rprf{ABt7!giRD~GO-x*@%Ar%mQrfgs%`k#KH6w?M_KLk#>0?3|Xa`2ak{I>- zp?!o0s>DG}HU<|yYSIyLnAe4CRLmUJGKtztDPrgpGH5Zrx4t$r#ibG%BC|DEwf0?t z>a8}AofefifvJHyxbr$CCk(;Jdf6mpJ2r=tbUd95Gt9^kn-acz$?{D8Q|_0X90j7< zmulvC1!;3bY9?-uKa^9cRAv-b=u6bxiSeMO@g3VOl~eYbV$HZ1aVO4z2dff!vNV!d zvnfuN(x?VU+bmg^hrG>LaQMq9W%)0s_{Y&CBs|Ge?R*B3nGZBXXmK(awok3E!5pJ? zx@6gr$cRN#>K@y7N}F=zM#Ylh9tU^7qc6TJ_wrFXnf6I1#AhR!9~f-+ySHSge>654 zTm4RHk@YrIrHF$9+?bE;mCu5icc1tEUR* zwM*B&0u!OTda*auELCDt9{4Ejaf9x-^3ke4!WU;+*3quD7+cb+O$iQy5~uuY%m=M_ z;D{e$$3T_Glk~T=mku>)+hT0w0ODiv8y^0XG<6GiRcTVJIgFiuq`pmjzjuxTqykAy z5z6wDC}+jDZzZxu=#?W*3fHz3d7!pheBONtrO$M;-Y&opl^+`1W{8o6S2gnIPp?0I ze1E3IU@_+IK{u(S30d1ahCNe)y~wQ#hp7Y?x%Rh|b7T8?MRb(+8k>N>;I7i#G>dkV z;d^z$j2`c5$$KLEBz|Zg5MSGwRoCn92t{$Lh+D>08W?B3s1uWmq;u|6rG~{&3L?eY zZw~wv%ggfWegzLAcgJE}*T_8XF7bP9VwB4&l3yDTmydp0B`WAw^?Ll3uWB)`O2%J< zB~sta*_}6_7i_ZA1kFI=Kihc%pP*$Y&oX7+d#CB29+eG-`CjQ3EN+Cv z+a(nO;!QHB*A;AbIbv0(m!sT633xdDViGRMbb+CTx#3(pyedB{@j?y`hG(QGi$J`l zc^>}1*!!)H@_4kLdkI05ODO~eDe(ekW>7eV%e;CFt|V#;pDLNvw#3=FN4m{HaJg5I zP14su3%{)IO$KJdQ*n=}2~aFyivzV(p2dT?h>u4n&_X z13iY`CFwO}O~kXv<|&P%p&1%d%`}5s8l0IQ0?yS=dc|wX45`}b4PCBWiuK=B4Vbo0xjH; zaT$sDPjeJ_x689}SUioF=-SFI}YHwS)j<{^%_6P6JG2)zdD?E>&L308l zkxe|>6SQdKNU%|JS#ZLPo2YBumw}Gzf7DG{6#OJDeo(nAJMd>c${$p_Hdi70ikdy~ z72fY92ZBXWLuS9p52(Kd0m$HHO33KJiIF198;S{Hw`GQ!!ULjG z_!!lU!dg_Wd_}T4H)pLun=vsGWdd5fCr7t0+MniU~8ja}-(_{hd zxA4`Z{b|0}htkNLBVtJlSs=f%K;Kxm24%!o@oE1rMRz{E{cD%02MmH~R95}^-yAOl zMp33D_%$?&r^xM-c1s!TokD}6Vqwt}E2p9wBhj|-;A}{y#C~Y85KwStJ}H%tU_yZN zR5YfE5jueB(5O1@Cgi+1_@vYhzOqI;*J}!0gRvA+a|DCWc9V@#;-^pCgZ&?~dcg%S zfGZc%#DZ2>&EVeR|WF`T1FxL$T&IeZu{{MfHG>CpZia+dQL>T`}QnTQM6K)u`8x#FHXerJk{5=sjtll->urJkDkkz|oM+^*Q`{71pO!d4&|`6(35B!F6ye z-Wj)I%pcoR@Ehq^H$u&vIG! z{+g%Tm$dfk#T8q17a-qKMk*q`5sT=bQz@6Jc{6(-5>_`W+&><>0w6^ffoiGZAK ze`jC4++fb=QhivFjx0`0-CZIBh9vmjP=$m0sCUe)>a48(!_g!0caibV_Z@JQsHWsg zU8kxUtm1*C~-B z2lb{vT{`)c!@I9k?R-)^LPM*xDF<A}=~P9> zbc%n!BOAicM?FP`kx)K08^MkhJmD(rYjFCPVpdt@bi=mVG~{N zA+e6KfY{F6+{p+0*wv%?cKQ1+rSe9Daj@|dC?(h_wTllYXCoxBWYi=qmcl2Sfhb%q zd}k#>Oq#C55vx8%hl2pTFHY@m!RD8njHk)rsO|12t6uDlvcWLbI%$V-Vj7|?nsw|) ztfnBLd3Bf4W#DjXpN^|fFh^~^+rQ~qa@<}>;DpOC^Nx!}>*A#XZ;YD`{5?{N@t`EGD7)nICnPb>c z5v-Jo$;-3J=^#(Fe-<$mT2n16BH!dZmBAf+vSsxEH=c%fr+}{-*m6`~s4CI?W%)o( z?BLGb&_cQ4<*={G;08qOD?c68^Z`S@30WsGdI*bcAn|c1FTpoynQHpIdLU)U-?2w2 z9e#YhZ<=qAR{^_j9Kr3iJwpZydsR_tTNFK4YMUZQ4(j4;Ko;Y$$S?2+lkpI9?jF9T zt#gUwrf*x#{dF)dat}3$@>gm5R{cuKbFiyE`6;O4{H`|J4ZYFCWdcWj%tUOjQL~=a zG{-A=q!BB#K2dP4`o5cZ?rT_o)Nsn~ElX}7v4#Zgkvvq@G+p17QqZnufMA@+Mk|Nl zEmc32B!hG4!PKTq(t-T&4WwssZ|&x+li2?HDpp#*>}f!pz?*>ndDl3AF2|B=D|jOv z`_8O=QUJTEMYXVPNsa%v#h&+3!%D~gGmLj!_E#!yaGBbqgK{P+c1qpPVRTPJy5T({ zVUP;x_S&S3BDP2WN@opmPt{=&RH?p$@F_-#J1NSzNX6*(&1=IeNc_NL9uZ{$S1K*T zSVicx(9zn&Le7l>_U@+PCQ8+n@z##N(wwt6c{%=S>Oq>6gv(Tx9>Y-%XLn8BCm4Au z$j6Xl)?BbiI0rXUUG@kP8(yLz?Lvof6WG~X35#}yU%$ci+6+RPzpRP}G64rewv8)P z0nU)cB#n6a>JBFoScHg?xH7nL1}ncmL-JxTMBr=34VB`hg>AeJ_=f! z(iRQ}N_dgBfE_lBOVl9@j}Zj?t@dvqE-+Veg`qSikY}4lpy;H11mEnFy`7(4I-m7x(=>TGVLK+yj_m8 z*GKP_`2q3zIs9R064W{VjmfQd=1nh1pt;4#%ZrnUT=4~}aQpmv$}?xc?&aK9VtZB` zY#&9d{AAM$Y*1nd%T1|}HS%Jcx@h9_SlQO-xjkF*?nU#aY~kQIak<(9!+sbRX=x11_?6{WM!6<2Fw}C{*VHzP@yfOfeliXsQn*%YXLFh8sH0dddSaWD+H6*`uUfI# zOT|ag^@h2Uwp%czx*Uq46qAtZ40dWbY@aarxZkM0#q8>0T@RNub}Ysv-=w!pLqzw_ zY=J?r;C`6us1_@}@4Ipu%!MN@#}1pX#yu@BtKT6&Xj&VLTQpvzo8W-YQQ`l~q(417 zIT@0>0w?AN+@u>O$CWBb`T7{o<=F| z*+}G$*3(X3oxK`0$wSy^KpKr|++ra*M@IKVZtOp0S!<~}5{R3b7YMeT-2gr(ugZv#_qOvQ$7j3SX z{>|BA4*SC^N|%dLiuPqo20Ny^A8rb&&QL6^HFv}&&(2v7?aqsM9aUOSZ$y1qE$Ht* z6_<vXQa(F7yj*=Y|?R{}dJ zQ1AejPg=29pI!wIRlaeAmZ!_(N{1&HoZtsc>lQ;*M!>{4p&xGAe?)v9Tdv2};n*_! zgr7^)@smJP0g-?1$B!b`Br_97JR=Sx$Ny$&W<&f`1sR?DsAi>4C%rBZ$k%K!2hCTt zm&I4IJ}Y+^&r+3AjGoVRr9CN)5hOqSp(oDQuwEDN6vE`9&q1*3ASku3$xSfe7|&PR zQ4+Fc0x2a6af`ZR8|o`r^B*YYm?3G4{DByQTJNRLA6ng_5ZR2M#UW7lENjSJ%#qH-|3X%6i14h6!#^q2M)4ZZ+Mh4@Y> z76rq|@m%%g*i6-QMbSxI6~X!_9lK<;Q##o+d9C_}+|Nf81=+O0=xeIHewLZ_@vj9z zeRb=|O~Ik$FqrELkrT00e4*+yqT>4pAJvdVf$H10bX^({7S-clEA<^!zoU@kS}XU7 z6PM%A(Uw1IN?nGYY~N=!BTNn&vTg67uS#`$mi^sHWbUDKA$|M-y%VXiVv*k#bJW}1 ztijitOggx&Mnv{ym?b~ifG&6PlU`jwF6TjV7-3SR63*Ghl67yvGnMY1>D%@bPr={N zf9_FR&^YW*MTfEaL8q7KC?>fWR1S!fBTOJ%E$dbAwn}dKvKAcTs#1*Q0Sm1QRz8_Hnep}|=7AOLPG z*x-AWz=o5lyor{^0=qX6^-{v{othK8f(o{5KR1{EhhsIsjmulfKB&Wl3RN$${$cq5 z?x-|$SYfUJWI9_j8jKxiW>xTTCfBtTb*|e*ow%D4RlZ$)!@pkeXbYCJ?PCuy`~H&- zM4Y4f4gvUDuoUXy!G%|kXmQHt8*F=?!XODWmqr<9j-!mWoz!rL`J^)&B6y^V zwhG5-oYeo)iB`#C8Oih9Qk*HJ#rf0#QbnN9D^!! zQ@f1PS6ybO=aVyNx^$Hp>}3RW|Mn90R4vS4glbW*0m<3pKa!W2vK|NqUv@bts&R$B z(0pUy#Qd;CtvzOG=7L3<9yGX%pS z_758Mm#DE6(L!c&DK(cy*8C`t+}st&J&hcGMq8mx!E>T+TOV~4r;58Zo2^M1t zHGTz1_}dDOz3b>b)Vw>NUP6PZuJ~MFWR&BF?J77}ewyf!6_-luNBga0+F)l?@*Qra zTz;U%hZbw4joMRz;?Yy8Jj{pG?(ir1di48L)={v2aI%hA-NqtHoI!{-ubA;Hx70~ioOIh{f=EKXCtOEI1IJVwga%`|4}8P=cjff zdV0sTNCn_kcen)n*ie*apjSY#qy|Im>DlU>kqX59gTAKE9oL)F;=2yUbMUF{@P(QE z+j4e?#<T>rnB%>au~CuJl!W7R3&oA0_&EFTbsSOiO;H#s0efU)AIARmLn*|1)`Md4RBao;XPEzp_j*fAZq$O?q%Q40>Pi4=TPzQE4}vM+Xl68 zJsm__dJ!t^9$q*CnwOsD*@)jPefz*kcq)#&27+#7KziP5DSlZ!T+y{-`Q6_wA7;rdd_=U%Ap)31n57ZjHUJjF@; zj89PrX62@)6-q2~1etJwJq>j{UrK4SlO~!XWik5XKwpLZ)ICK5Rd#=YZE&;R{ze<# z;p8`%weSocS2wqN@)mLO_G3ba%kS{;*4j+aVfA;!xQwr<96IyrzdF%IY<;Rn#R4>L z-RI^#A_~wzTNFLZ_cze8ujgH+Liy`3J3g(J*Z=O`++d8&GIF>o_n>n%eOsoPe2#Qa9eP#VDEFgA#d^Q_=XUtky^zDWgAY>sW0k<##{obe6wAP4E352a zQlKIO<_6h;!PpS3bc8<`Es{I2d|%r+uD{n3D5r`>jQoWl(>zn{8{YI{N%w{U@g-=~ zaboBOG?Oveh78>RZlA?A$MO}O>1p}}{~J?rcUUjR_wLY6`~=bztpp?*&9@e8bb_un z#7VNgk>_iMV6h;W7!?3$U=-}5vVrvf zC+*#K+ep@QL0|(j&yheks%!NsxvDJ5vNa71k_Sn&L{dwnCEIr?G9_h+WU6_vCBMwZ zTn*-8t~Tbb@9M?o`y=A}BQrASRdfTp*Ve~!NxV#MUwsMRO}$O&^{El6Wc|R2N?9e-hVs}|njDpj z7m2FS%`KP>LO_{;pDEZZ71Kl1Voi{uPr4*-#>@Q(QsL1v5Ch#L1}k;s^;`M6O=L|& zbrGi;kjzCAYZhJxd0QS1Z%G{gFz2<|6VMe*4J^O zFT--04RTV5#W>twlh1CJUDWD1c|$#~KlS}wr(*F$Z>Xm2#{wd7@lpF%TAX|Y9SLg- zXemmaeh%r)pofyea>FwY3{^q;VaAIzPE;rUuzcAdu&(7&f{|K*!QGD=qo4%m{6y{D z@xwC%#rU&HfxdLp7ZJB7N|YD*>_rTEIur$~A^?RbOxEA8j3n)?_l(s@ z>wp;TYa6^D>(mY014BNASwYD~+;Ev=k@~52-~?E5ak9Rri{j1EsOQPIASG++%QGQ)s{06 zMvhAgYU6H1L4 z@|Oz+u~JEQCx-NllM<14kH2Nq+)enrL9+ z=P=Leb}XJm$y=Hl%(FjKha!fe7dS$&N|PHzxdnmlqJQ;&6)u587(lJrEOz3>TGb!1h4nSaFGUTJD1KCgfkOWOxut6pn#+j12?j#^zD@E^+D~7u%J$KT77~6_06}YM( z#w6Wqbtbp7C!SgSx$WS+R+8ncbNYvg{p|DP!wl{^FoNYA75laJ$9U>Ps)g7<1jW>S z=Ahbapjz&xRSI6KF5PYSd64j@t_b{PxR+9=WI3mk|J=~(GFi{^1G*ra6oF*c%zVHF zlu}JE(#gX)Db-<2^28J(!>oP@o>pVG*Q3#xJ1l>@Ev`FkaW9p`BU@K7(NpiRMDi?e z)%a&{Po>w(2Pvd~?r@tNX1pp`rexYrKO?(gF{J_uEa?tN2wO-@EkZ4?l~#)zgc{nV z)~FbacdZ(E!F`oTdt=x%?ed9Y-C(T6XmAtJ3f3uwlIJs!PMkm1SNQR)lKv){K6Lg7 zmR{&^f0)!Cq-%W1C2bY{+29&VrNRm&>O&QcSh99a(m}9L>9m!j(eUDaeBe|Ehm&#n zw1T_EX=r|imbwGBq#^I4RFu?&1__utt8C6eJkb`NYX5*M)8%6}Eq%<{!Wh%=lUuJx zHt(JnE)t2v?T2g{?12hg`O?^gqu<;fIGh~){H-On(r z6V0|?IZ`4<9))+c3Q@5y;m;K~J?>1)_qoov;`#2jUe z25e_R2XFp&_vE{b*}&aYtfSuH0{f?vx`7cY01bc<<6zIE1IcCmAo-cGDURq2C8rbx z7T4ZXymS#Q+*hrBet8y`=sa;RVz6v^)eXCq2Op?fF81e~MzjIZPp=GMT%Mv!Ypx@9 zh#^9}W26}e#NH|Kf9j0QIpymE*Oyil>|hJELquuDKl|2;@E;r=u4y!AMtUY!#eIe|qn8CF&dh%+OU_`@Bqgf|>o4LCC$9{0^ z0M3UMJO(VvFxa^ewtnS99aA^RzOC~KS&v1rH}}=EXP-K0`=rqEyauC_HzsK7yt31N z>#Y8Pd@1ec38HV$V_!yYu>a1mYNHRJruKLUX9t4+jMExyvjO&QkrTC$1T$4fqt{Z= ze{#{gk_aM|2Eph_4OfIwy&4Mtq0;%Ne92}wt#rQ|?B8OmdUUAymJ%7)E{q0z@96oW zS*V7hbbsN|BQ|{7KY@4$8b+wEoW&{$kBxcu9fl#EGGMhF3$ zKSyLQn0JcBTD&t{z?M)qk44{oZ`I}pTB^UxdZWe za8`e$glGTwjgJ=p^#Qn8rb>LPuzd%47>V(YQJ(KhZ%4>EKFc=?CczQk!;Z)&_F!zg zh|2D3_#{w=`9uSaI8w`ur&i-zoXOMTUJL$+{YkV;KhR}-`Rz_#USDzx?ugvJwa)4x z_V*PEXsU~Ev}$31Cx0o`pYGt6cG7rdRmn$Lc8gxebJ~W2QR5OFl7w}tl%JvesoGG& zV3qFfGj}`TPN@}shEkRbXmH7VWXP8*nX^c24MXTjwkzo^Q!hU!3)P7}H>yAF>BW)ij z{XfVGOfQh;VexZWTNjdX+?`QWTdW^X%0x(E_3z-Pv!-j5os}zX5AHD5RduuIE~)T? zU)&7fCG#it7CIQYouY=v37$m7-dKiT@zfj*%%V3bYPcCwnzi@!anXdd<%QsLzmCagrQFc`P$Fo0H!Hse_g0Pf^^tAgM2!*H-`HrI zsv%nM_**6S8%)21POULghZCC=7rHfl{Va^x=zo5hZ8iwaws7>p( zLh<=#{(=UjD#M8-b-dw~@rAJ`!?Xp4t#dyG3&Un!XMOY-US2I#iC-e!>5+nq*gr9f z8)K0MUt=p_6J&X7zYm8+aG@gv4A!{hd-prtTf+K70kOhz{|nks%ZFIfMHtIZoh$%hvgXZW7`klXU<1ohQG^ zZ4Oucj8v2ghELc|TU47$p{@(cne=_|^8Z z{3>@RqYzxt2}R_gajThuRHujP4Ay6KfX_3=DzKRb&&@XoR;l4RDsm)6tj;ud!eaDO zLx|+3WGIJMCN7|*8Mefe6tyaw2JQg<ep_FX=eh9LX0EqooZs8`r zzIX7UpNcj?`V;JzzsO|O;QaW}s_!v}wnT`4CMofv{{4J;ugU0`75D${6joxG`#A9? z+CcG_{48SsZ?S;_`^SFBY8$L;HY;_EAaPtl@b$i39DAY9pWTxP4v4O@3-~Wlf0i%% zh3bu9)A*WOVR16P4z@yRZ#0972OU#|m0%DNl+GCYZz%yv`VIv610^^ygjH}aCAce0 z&k;VUN-1#VRO*&Tm8#ae|KawiHD{=)HHXJP3vQ?46rSPf3q%xYj`-0yw}Mrwfsc?K zzW%aNM=LS*ev7`)QWvd6o#q9@pB6Rgrln@-8D>vHCFTMLgU0zdW=d%26fU#qRkd(f z4sHGnb1O&)^%b|~gTf#rg&OQOJh?<*dm*1)UNN9DKLvHXMDs0-qoITlEN8PipC(+a z9@=zOK4u?ADHj`@;D_zo+I~%Dbd$D+eeys9u*#&|*FR!gYx#CcsB3FmrrnmQ_`8s} zI708tq9V3aTKDXfRwsWWDvhtQmD$9npkKZ#aYPAdA8~hj!@+RGGj0HPC;S>ph-pXC zBr{D70UO_ptH4%kdMIA`K&KAwwOX(~{omFr= zqsh-P{ful52>k(hmBR`A;7ITedV=g9`(dVI@aBH@q;tjvqt5*a&5xXuBsfUgce_9C zk-_0laWpkk)7wg(Mvj1pXnYy0Cp=I@{KV1&Q@yilGfM9;7FOMx^+)t&w(GwOF;7%= zT>ss$Z}f?5vL0Vwd)~W3!=IVXAP|g-YMYYQh<^F<+DptW=%EKIO(MaG{D3Er);#s< z_5EW%rPSn2VY}5 zE@X7VS4~C&rgzMX<}Lb-udp3m_B!BVI^fIU+7*&|ds95MQY?##$v=q~M$L7@o`Msi zB#T@U@aoHD;smJ0k|`(tZm|sVS@|UC)iIIp$DekJ)uZ&cBK;8?eiJ!tUZ$Q6yFEyL z9`KQ1C;5@L8)fDl9Wma!g$so;(trpP^(C|@9W2-Djz*>}25&+8USzeyJPh_p=PjCE zy$7Usiy|S}jyid-UcQt|{(A!qu?KwDk2&pckOGsrB@pmgTLs z(z7TincF}1C9$V@X)=*0B8cVaggUh^f^B1w=;Ji$1lJC0?5K1#5AhRXYky>#3gsZV zZFHA3ml#nt8OuN+=wS4Is>aHrLJ`?eoDhp|xc<3)W$I4cx~tTL=x%WX=Lz?dIQoF9 zjGm+EKAN*X@d1gG2CwzytdLD})Gg>P5{uh#i!SObTEVkci|px%DM7?=I=aolSYHU~ zQF?U|?2yW{_pOh{lR{e;*tu3>@!_tzX3n8Z{(%lVWc3bHzWWETZsUfT@5@wWqwkBo`@@dBEqzoeaCmoNTmi+NXr%LemMOcCznfgnSG1lM z_u1a>S(2Rt`zWgVe1iMFBl~I_$0Mo7qAky=)FX|} zM#Zvvh>X#YoZ8VF|>s`>FN2Q&)F< zOr^S0Nzz~E6CPUPs;L~Y)O_h_2Rhjv)K&UYh@~rczun|h2bI#(K3uxXFWEW2t;4Q1 zg0iW)$5^fOb|nqL;m2f*hgcJ~#q$){m{_k#^^cFza$~ZlhCvJt06ii5RcTR_G%VDN z`Kui5-wUmPcd#M6u|z$dJ7qs$p%R-|WxW;|xkb@URq1qE8cI`D;j~mr3wF{|=M%(C zwzwnY)Qd;A@V~L751QSq#-RrBjz~ZEzUUe@n z(D3Y4_Z9x0b7Rw1W)5mybBFutq7A%{%$d-)r6(-Ei%$bozMPo~(6{ zAz&hS>myW%7IZ!vXum+4VzupSDar1TiG>$dIeCI8E&PU~|HCQf4kws6f%&Cqr;qvH zFSY>EnA8dC(ji)rHkMrdBttBmhZ}@lKS#f7@!Dwf?V;S9=Ttc@PR{dbCDZ~jJZ$(= z4l32Dt^i`rI508%M%CFhQX2ucHa>LFBnQ`08XxV!?F}j?cQUv#aRRwjwQe$4yU({9 zFY{Y$z}aiJVGiS-X9e`De7Y6_`Ii6rKeG)SYc?14drzg@;QeO)v_R)~8d$_iFUZWS zX$g2NwJAlDl%YOcY;bjBia^uGVr34aNr~E$T&MUtjIZmu5JjXh)+?vD8g)$q54}&o zSgRR5m6nl3vy=iCQj+cD#YB6q7+{!nBq0Z@G>vBmpBa{^QMcPNrO%6TZ%;Bl$XSSy zX%ZIZ;BIQ!U8bTga?vIDmeC($OvDciMfaxjudrh(ffR!)a+raXdYtN@QapN!YH*B; zT`8Q@Zbh4~BOOb62^tk}qOHLi(D&M`DDxP536XcoEgCrD=qagT>Ay)J{1^))!fIPf!T-~Eubeq zlN6l*r1)EVDiY)F)kzxRdL^mBg*9<#LJltcHii4Fbo4&^_N~8JXY@1>FV?|(s#E5= zM9UzkE4&S{8ATN=QrUXlhMxu5GYo=UqZ5Su;Ga-Fq@)QTlRigk|Psur0Ult zmpI?1xme^-sNC&N;g!HDYHbF~WqjdFL|yQ6NzY5f{+`x7stXQdO~tJjqqWx>2^oXS zO!H^%y@L%&@LDDA(Gd<9a(JlCA05d7b4fi}a(=fCPRG8s4ZSYVHNJaA1xkr zA59UY3>`Xnjss>AokM@5S}lK5u*Ke6T``yvhZvpqL2wX9qoKq50iyrF&Pev<_p+_eAC=SG1_$BP(UL5Wj^!Hl~}@ zVN~=k?$MMAV>(0>mZ(j&5;?ez8mpK)1djdKBl~l>P7dL`5A1@{BjW8V88<*1nOO2E zL*2+1fL=Z1(j!BA)euXp!yb^>`Ot`entv&C(w_mbf3Eb0nS&zOd`h;(b*>TBT=rG{ zMZ;diZN@nTti}Fli+3pM=Q=;GAr+y z+t?6T2P#(6^}3{#l)u0804iM(vt4b_T!CDd*iKG}k9NU}tCT%mVp{uMx#E3^IZd&C zJpD9Mp)h2C?$N?0hMQr!_#=yqKEqvAw2n3~MBz{7;U<=|sji=}j0qPwA|GlE)6dLK zkiR3R%$IfAKBW2NZ%*GQuGU2(jRtQtF?kc)PK(m`sF*x#(4K(O*WsjX`e;heRKw>H z&5Ec|en9Cs!)TAi_<{Z?ixbQ`m^|*GqHm73no2!PgHIl`xDjW?olXsvzhiR1S9rRV zsy?|YQ>;*9jS+0oJE_ua9NkEFq4Xk_bQh;11c&8>)MaxeNnI9gKir2}mVK5Nb4nZe zoA-%m>4%!dE_vfQ!zl*D_M|eyG@GF~$Ghb)an9gIvd}?whUwJyJ*Poz63Jn{J$SxD z1*{>}{S;Ce5+-#ZWL*Z=RD!Ln%MT-!@&XCMQGXQ7s9fOb^5v6bbT4>Xr7O~q#Yq}2 z@Pcs!%M`XS`NY{sEmJgZtxu}PMrl)`KBV28CX)g9@KFzruZ=XFR4|y7Cyf{m6o^i= z#R$chuZ_^#2*&$(BPz~Gv*AlDQ7dD?lZVz1RKe) z#d-Kf>FK$mcyk>IY6<$UCeDh-CBVrLBp2^R;I+Q_a1UYCoMih{%7 zvqjBu8p&0}Wt6JSbOq#Ey>SYR!IfMqHlR#;m*VwBl`BjYnDv_{JD8z$gIccIm>OJN zqaLlYe0<2X*Mx(jQ3V^Kgm-@Mn^AE1$$+a;D_Euu&Ku0!TjT@vj#{j4@z^o(13>%G zauGnA(>*{eIuaVUi9Hodlf{dwU5H#m(*>L#o;Y9^zD#obWc+Sp%4dgb=M(3q*|!&W zRb_F2hGa+@*-%vgMy>{a5PhpAE}GtzBPGc02E7$%SQ!vef{wHYXAj5D2eK@R?ypX* z|7Y{0@JcHAQVb9g&Q2>?u!iMp%qchuYWTrU%9l8{R9c9kgRcUTKT=C?{7}wUn9WOp z>o7it)!fm>`M%iHQm_t=->O7BLm&kCg?kJP5nr65czlhR7OZl~Lo*HB%x2Y}zB24n z&LYk-pWd8BU^DS5SfoUpqOaHzEjCFs7VGH`mT5D-{NsrK34aPz2146cH?~nHmZ7U6 zrIf?<;~Keu5~#^FXuYgSAjCPJz=~^M%-F@eAUOy;+k*8V*gmRTHCR6@ltt9!lEnVO zrG7ihpk z)tcD5J@_vzt-U+omULGe+SB+m7?gK#{7xHg{qq^Q_}AKK3wWCuH_6l&={TaejVY-;PFT|Px^*Giq^WU>-Y z!h0h1+~Ams;VT?RFN?_o>`Jmo;xbf)%=xQ7xi4}AFUTe9n_7TiJFt&TF|t>wYP;8< zT&N5DAPxpC$_^@{NYN$cEGsUTlup12et?UGt$m!AE~W*7uueiF~%IyotVQ>g;?G+V7irTrK83=U&W zjx@RiPIlB&{P{n{MYt&J^q$~SsvF-F-w-b-v~er()_AV3e-u4-^)kQY32`{N9L6+9 zU;8-7(BT%0i89w<;Zzd4UBA?y)K@^JXmuz_1!N)ydk<68s`b^5b0{S!8Mr4Q6Wd-s zTLiu@dK9{cvAd)gA~vdGwGL$Y6gU{a|0=;aml`zCoGa;Vj;1QIjyNi_sX0@on;_88IGJan^nO;eb&RW&8^a^h;KQw3W z9vrt+eD+t^H{`O=!D{gKTxcv!*%}xnsYuS$aFK&>fk<1B_I2=@N}?ve5h^@G9fIwL zx_o=qf%T(HKa8$mu&i}_?T1&RSBd?}^wQcwe|}^sL*?WdL8zQh zYt`7cSAg?OdUj~N{K(?Ui4RsPwHi^4-KHK!OeNg95oPOxD=3ksGh`OypYaL?`#XyZ zDKcvwd;f;VAN`P@QL)VMl7o?bt_a^9azWeP^@{@X99bKDRK#@+#th^aZ3Z0ClPV>a z|E(cgcxe^P=;8oVsnFS!z+yWUr7B!ePVY-DO`<)NKZYHN&GSbi3T;jC!wgN|;KhKL zGSF5*Eba&-PJCt-B{6E7w`o}IE*15`9W~m)-FF^NiMTho?u~jwt+{j47~CysAf><<147HK}W!Qbe& z@FkPyj>PC;Irp^loh%DqH7V){n%(FB;hw`{T*UHkPr$-E6PQ%(&M&6!88W%HrSN^% zY1|iDzK5NDKQFWwCvim`w2D;I+v@8qjot)b6a5#&7~dlboa66sN(4`#1leHHPkdr}wiLvn@|x9f)qiYDwYu9MO@OMEEs`Z{}hPM2;Qhu1%?xPY{{Y$j$e+sG;e{PRD{U9*mYcSg- zRE_(5K?y?)g&{&Csy~mUxEM+84XpNk6$~Hh>S-p`=s^;D_cbNa6#r~e>TC@M6H+r& zn!t|i?$doVpYK(Myjv8BCit}xT1bn*CVOjCI=dFb)0JB)it_|BZ<`mBe8 zhiQhaDE<~4cVDQPN>wzbh^RH?uxTf=MIG~% zn(4&$r6$J_OwJnYm(SKKmf0h98RX(+k1;vo2aVc93}2Dyu)MFxT+bY#Nky-uf^+;C ztyn>4lq3fK)Ahchv*j9%^KcRS3c}$SQQp0ueaYUvre^(`a z{nPjnr5*7VO**QcTdX^MT}<;blAYq`lMY8cQ(j4_0PHs>(#GrH_(YA;_pthrpf}({ zJG>=Ub)p^e+E-oZL&~lV0t*yRWW7lgZlZHIt%tl1?IK< z$NRj^$8;EXw4)w^bt#if($#6k5SRSjXgGn#QweDVKH5^~Ni`lE>5JhL6`Rt?48s7J%Pk1mq00Bwlqzb0;!1gN?;O+#H@sjz88xV zln~rwHb_@QoUIFNUPDPuHb^KzkA-ZQ{GhsUGu(h@(DViLw<|TB%m6aM*nTPTSyk>% zGjE(KCmt?oy&goEaKkpEW$CqM*Wu5Grh$K|7UNlo!Usg~cp?mjw^rKW%$j71rK)O* z8|!wN6*R1fgO0@v#?0Skr1aRIZ4&t*8Kl>#N|~P!P_)zT;gD*6>)*A$d%K;QUyrFFb`kgTA++zB$(G9=}}wbr0t~s_NoaN_E}wS@#MK+*j_UiEUy6tcFKs z!4==E6?!VUaLLcz%h72~{l-;Q@|h%_FU9J>U6oj}EzCzp?mOr#sm=pTenvF#9vMT6 z`$lJt!%F?8@s>@Nn*K?A2WvDP)sLF5**rrM?tgBwC1aJu>u&_%Wb^jg(4HR`g8~$5^PZ(nQU~9F4zp<`!{=`6)-yA9#yM}fAo*=?EibP|chSfMV1;KnLiTX{A? zT1ZZ+wwSU*v+$*o1r;&3ud-K|4riB4vyhj**4~tZeci+T@9exvR}ZI2pGnBt$ z2Vh@iquu|h_E993ap0@^xE{Qeib6_e28D@5n4w#(=PI_Muc?^$63lM@45gI0A57Ul zOUw@I7ix@mO2O~Lz30O9xJu|W3Mk< zT>Y31C!?=uGXj>Zy1AnRPj9nH2HP)XJU+D;LnhE}1Y+n(0=+%BiRxWJ4^f55XEAQK zzt%Se%hZE9S|c$uyC_J3#CFbTRgA?Bj32#edPzQHOZpRtq5QtOdUX+OnV-6uLViv2 zd_h+WF^&r6#amsRuV{DeKntoTWq(a;5P!c~K}H~YUHALqWmVZ7b-V3hr%RG^aNBwC zC6~SN=}ozuP3htUPRtKq;YvzI(?!yHmOcIKf!9xv|Y~S5qB1H^KgZo%t%un{0uJs1KU1=y1hm z*UbxFUyu5Ylyrlb_#_4kF>R*)O)NVIL&5lTO3b4SX+W^7xQoP*E_$?;I1&9cbvFD65)G{u+Pu_X-H*r|6l-JnQ?VCZedmT++3wyUdq& z1xEd;7uR8w+1f@@HVY&WhQt05ZR{wL z>T`JPhh@UoRD3HJBkq*L^(4`lTlPq4e1KU&u<_J0ZSeO#w=ed*NL$i3dhZv<9vns? zm+7D5b-;SeIriba4vX5)d^*;SfvLgR7L*(ok#IG3Lv>hF%J%EEMq_Ph+pmcY!}_K# z0loVo(2EVS2%vP))lLld;;L7Lf5PF-fpSgH^^xY_$|EBogFYe$Bds*?3QGGI`Q}@W zg*+ApR~jZ-d-U%@$3fdgTtzuR2W@wSS5O@kte-T-zmB>*xHow*o&Wh?vl$I*{Qx9-$NDV)A&YeA;5MSP;R9@wc~1wQ0Ek*%q33O6(mUPj-I-DdCGktt=mYA@Mrd zK9rzRj^@`=y`IAez-f2k9XWJxNj1WGzC?7KJt7MFn2{Y3gC9YlVOAmPPURvEpOYVg zIfAw?N-le#5t}KP65w*xduV1JGlqSgX=ksWegt=-6g9ZcUSkiUf_(Jh3i7_MBP<=R zUmz15Dk7e#hkY1SezY~F%9byPFWG0{g$rQRD9 zxxBgyMUO@80W~NGU-bscKdk%1PshX%dDA+FJOMb-p{&?rA79>b=>3$;Gk2QE7KtYj zsqpT1IyuxoK6_jtmJ!64-l<&vaL_fg z$*4F8_TOCex26L0PDdfMif3eTLb7ov~V%yRpSf%dX z9~_H)c>IGUqFAWHbtlKAAD`LUJOA`yaHNrmkx1c%G=?wFBjdZ z01*q-v)e@&V^sKh3t~u;`Z4h3p*x0sQl-yb7pk<~b)k16wbcUbv?cN#6@tG0)1_H< zDZ!X_=>;ilK#rep9-LH36JMob zrBpKNr_g{iEvp9>l^ESRDr#65mhvT)Cd1~y*Rk4+n}h1&hHl>KGO*i2nhWhj`8#3@ z_{vGc5zzjD3y5#)AJ7*}IzFjWs>wISaF$R~#zP-`N?$A~<9UVQP)!w#uL#x3$;__& zz0Ra{E>eV=wCBqv_xq>PrbpSzKB5QahrlWAD<-ctcjigSzkso=cyt)L(EMu^B8SIq z9RhSRL;cK*t1L2a8rj|UAwR`G?d^_RXb#;@xgc#FHo4m}l|d)y(pg+)>i8hW0Qbh( zYQH1LI^CdLws>Yd$w9+mf&-%#ny58bXa-BOn|Kl9OTNqznPGS0lB9eVj18~(DAmYp z%ICIDfc=2+?W;AP*rJ$y?5iFzc-^FY)BR0s4{1eAHtb42cXCHP#!sHyaiJeNx$8pJ z3wK?p!Nreu+%Vl&4jMilb%^@GlQ?(6Xny)v=aM9DBZ(HWwEmr=?!=B#W_Mn|ydnRx zs3E+3N#k&IQ4G#UPt+?cM%?99@+~PFx$Yh9l6+NZ79xLt=RK9k3|B)ChEX@-$BMru zP`-T$gJnpC;1ntN{B^k-Y!3gv1 zXp_71@lQHDUH-;~M0IXogV?*Y)g(0fQIu(aK>dd+oNZEaM-8qWEuW6IsBBBAx@Lx0 z6!q|Sl_+HRE&AK(a6sZFbM#{!JIfvh;pEyxqqBU4A6}tybcv*vb#lJQHgL~2z0rQg zBVxJP96ACW%LaS7FOH3^+MzEo{9MeDL)V@0*jY@ExJc=5MG*4ns+*(zfE;Bwo)}mm zj+_NwpV%L^dMMKDQYExF!C0ZQLGL_`tyKf+A8)ho#BC)kYrcqQ#de?&K-x zYP_&x8c~w0Ec(v2RwUUBIS$#HE1@?S93KXFc-(DKas1}Hir~-fUT0r{*eWNP6qZrr z5RAg#F9m$gVJ~;wNzJB{&1{0w+&=YWhiy-%)%NTbUza$XV{kdhl849OkWPayqCO4u z;~Wpqdjl?({+#BA?fZM8CH&p{lIk`7Oj=*+ckweM&p%!C2IuXPo;Ztfi;8v-jlP~} za`(;yiw}=av^?VyiJ@NPSd^rJ%IvELM{wk3#UkT3A-#GZ;b&q6DF>p*Q>uQE) zvttal??+l%VVNdo@1*GZn!^>$*=LONVzkiUMCN-f(YWyig>ld5KPp7&X7t<#jVbXA zYRs+vlN5-h4MGdKV$`VfSYcDugA zoWu9N%gn`W60G)ADWgmB*-hfqEp+M@>8iT!H)RdN*)rkwJN3uN} zUYw84KT+=^2GYdH$kXK0%oKT1 z8eBY~>6jm&ZVHm|L(+Z~-c)0AtsFX1b8CZY0;PyHg zjy}Q)rM%&KwRz0CT2o|k%@TURwdhzh>K!&A2) zh77a`07z%48B=%yoe+yuTa=E|@UTdz0Yx1cq7X?Mz+xq1bc=_hy{$c)EUpuie64^F z{Qj2e%vtZcH`ERQ7GZhBT*7UY3@I3*(gY+#}$+YJ4g&f)4T~kH*sJe zV~M&hO=2i~*JO9Z+o|Dqm2u47`N_!{V#L9;t3G(RjYH#e$E1y~Ohj1Yj{>dDY1$L7 z>#V~U9gaQ>Zqn=Sn2$~j9dyfHzt(CAaYBj=qv2(ol1`}MnE0l2jpDaA$G*t0jj-|( zjgZxLayYrhj@BRh^=ldjt=k962mDD*K+MEie{9@G>B$I!FWHPiU1F$+Y zC`232I2ii~ua$0k^QU6*2i*b|MOP@X?Bq_uOR3PyQMx#t=2QBGiB0dPNz&<#dZ&XV z9dwfZ@T%J|qj4z}e;5gX;`zlC?`RH;eIQoYm44Kl?sJ&>MFz_^H|g;P2Fv z>^4>#6~k#W?DH5IuUa(k|gV>%->gf{D@BAI5C->)}F2`&kqbc)s-S8aljW15-i~`&?G| zv7(Cffl})~Hpqm>xEqqY!)|1Z3ju`walEVAdY2cNl`!rN_`((^y}?*Xt4`p)=+BkV z?vYA}FE$esX1-L~whZw=X>zBy1D;l`J5v(cT|dz5F*v~w@U+>AwMhms)0|uO=GYev zp39(?RrivKX%;8l@Ock^XVkg-oR`#mfUgCu#&}wY^_Ycs*21SWzsGj+YgyFP zI}cS?Jwf+9iFA;zh{N%hO#$x<8$b6iQ4snyquufORsYo`I#A1R_rYgZ*?T6%ii^<` zb7(D=3baX)&CsyOq@4t-m6T7BFDV(Rd{MzRBQ;o|v@4T;9`9x))g|Ju5drT9Ln0a5 zOxmGhNxX_m#6u-oPL$x55QEr$9R`o61bdPd7?ruv;&bA4rDa=vtuI*IAsF=r1&h3l z@x@}$7L9!3N(%kYmyLcnxPmIe7sbow0ZJ78A_jZn1ygQ#>E0;ip0~!v%H?MScD`Ib zpyh^d)QXg($i422E(Hgh3Z<`46gP3XI9c*jSe%@od~A{jx2fy9g%740eHR!6_f9H;>7T__E*3nvdTshw*X9uurx^kV z&-^niGo-*huJBny4&>_#f7rgMmKe@gNQTsEeRs*~TNr-Uu!?=9;inBg-F>_KKi%6{ zEHCr~i%;^|OuOldvd9*c5OF`V-N)ay+(xN2UEUTFS>h2Pj;Nl#08zXif3ZE{o8u!b zQ5u~?g6h!j3(bJ;sPVk;8Ma9+LZe42kWs4E4hF-mT^|zQo;p+`| z^y2tx!`(iLI)7Tl!|d;v??NUfIQcu=hG>)mDYb~GroN(3kC{89#7QedJHq5ND<7*B zQW6@rW?1mGl!o&myuDyE+;m{9sqmduuu_HU=w%x*i%l zwp?l3GK*rpmvB;5?6zi`{I*z5x!uH&fp^UW&^MJal;|!tdqLMx>M#c5gQI8tJ&sQ# zs*2T0)7PkSDv`9q)fW5rjk&JgzNS+jlCP6%Dx@UB0~)HTf1S4~Y;U@)-Vg+$_JCmA zt{T;oqxq_NF5YUsTc=#WEl&S{Mdm<97U>u}G%Q+aQm_F?ANwPCPL;jq*>Wb$5cxd> zBfecOQQI~{A9|`=mP(Gt;x5*(${msbqONfTA)fwT~LS@AiiznC|`N*jEm|hsXF4whDK}V4U%-_w!RH`v~tX z_gZSj5X+|260>nX)qbgN(9k0;CVQ^qbxhFtNPN}f7N*y2VcQEv&VW7Jeo20=@d&7m_@G|E4aYyF#+D!j12Zo~Mq!5O4)2tL;WNd6)$0~@ zD}xjKfC(4NN95XyJ2g~~e5D~wgDHZXEBZW$BWu)rO=qihIUj2sip4UWcCnJdSHae* zn|OyLBpebA&Fr>JXHkx?;iIEzHoi`PA}^%`3)&VXy36nGaFi7WnyRlal=>yKtmu%hH{Th!cbKZFu>zfZllEBPr3R3Y=Wk zLTf}qEfT|;>Us3gY`qL#NIR+>AA^0xp8E${|NzY%m z1_OPC;CU`X;pZC$Q-DGD6}7j@MVkI7-_8*8f>#pA(ARSqj6CFlsp_ud@}U&AMH4`9 z^@hTM!c(h0+t%15^{UIBSe}2oJKUE6(q#De(1bud1C|{v_N0rxJ5Q!ANffoF%GLUf zFX?!q~6YS3@@?B&iV)T#d~ab&ksh7$w$a5S|vYAdx*?h-*S|L z?ek@MJDPjGF3&JFY&}&);>9DJEU*;nm4I)K{kX+ZUT$PeCU?nTbKKTA1i>47E%CpR zr{QY>p{$#fIu-g3^E->cxqV5_0?KMBJ>TSr{UR5S${2C})#QhS`>bN;BKx|BrpyHk zUxdw!%Jj*cDm1}+%t#Wbj5l#%NvYnWXc+5Ys!hBLh0akl4@(R8_H)M!H4Lobg;Rn zZEXXWe!;D%zZgJS)Fi|9K1scmawl4-^K@aX2HCc~%2=5Gk;>^tz!gpx+s3vAC&{Up zCPSQjlv8_>q)rS9#vD%&Jy2 z+NiS&eWe}k>-ISv`)L;dNrq0o^)uYj5#%=#mG`p}9Nf+w`~XIy89)NjZ(y;JKpWU2 z#-$rgQSA8s!I;B2Dd*&9lKl>dOT^rZ9UXi+?wBp%GC)K9v3aR)jyrye+eUoID{837 zh!EJYjXO$bFh%o>8)rZx@za+ZBEorpTeh@=tHtOJw?tc$sIKD+4o^1n4T_16VOm^a zYI2Q;3er*0_`~rIUtf4y+-JA22&xFwDddX}UCxlU4JU>NF2;d;1!9V_Kb&06b0$ts3aLQM zBO~eF9Q*3SboH3O;Mpi@4aVlVSdZ)?U%|beiZqN1yT^15P2MEdi5ayzr3f?h1&Ale z$wOP&4(Zb2T26n|0piOM<-_l_UewV_Tx;qPKh#|{x9s5Z=SxOr9hR;zn2-Uh0A9(M zAI8@wjxeSWReU-g%V506s<>Z7Ig1~nI7WjBnVO_LLK;rU57Jn;p<4L)i93Z~{J6y3 zmZ`*pCwZOUFH5Sk#F!7Gr-u5_mhJ9P&HI2VER0DaO*v@JdvNGf54|d1GPEVUr$Rt% z<|j7o)2<4-^YXDgDb_lqL@;Os{?}FXyZF*WR6`cvq#EELh6d8Gb$qp9$EMkhC=nfY zx}vbgSWx9e0|#v|BndQ9Q4@?)P>Vd8E7W)DM1%I-0w>20cyn;;jaAv~WSB6pI4xf} zv)JO~6x|@vv;J&@Hh?-tQKWulf?301=SL!%PM;tn@@d=o6F*Eh6Ldt6AUlL?(ErNK%nyR;>5kBM?7)EtWXP=#p9^(JFO0 z5n9vl@phU{*IOX7Y)TW}qJ~hw0`(B>kYA5wO2laLGF#&*$&(DOKX%QNjkQX@JxutP zK1f_ESlJ@3DGtbB>okanxFfPyH0I@C^u%d1wei;iDLERK6DUvG;1zgAb&z&Ygf3o8 z8mEHk?rQq1uu_8%J6P*j=2O!DdN%VBrZ+NqwVkap%5(X<>Q4y{iJ#kuF%`v^Z1k1d zlEv{cJGhyWZf}In@z{TEir^~g)#p@WrIPI!^Xrz_PpT^h%YNcJ)IYT^U~FB<*Iky> z0$Nx2YIeFVRzgf6m^@8xzXa=*%Kou1{&5XB16L_2RCw^Xs>@=G<0L!HH41DndS#XH z(ynvNd94Rnu~g{^n*2)G3*GV-SGhvai3W?6s!VV1NvY#NiQ$|6FiInI>}wa%l>nKv zORb$ImXqEjC||(HqC-!Nu+*$NfC5ph4x$KnT-9d>V^i)p;=3ZP-2Py9DMppPUJ+x3 z%NBycv=;TNmcjjy^q#s)-MJIU;awZz-TDXAH&S*17__|Z+ zhauP&0pR0SRJl4O`00o;8zWy{F11?IV6n{ZAZ%co!ALSkE%qEL@GfMC!n9qIYglZ~ z7m7UUNiKMPrNjB_iD*G<-W|C5>J>dWRHkqhB}p6)fBP0{{Rc|4K$i^AS%jspf{au=v5Glt?^kq(9Mjr!xXObJ zb^Y9Wk-4{bupltB;U#lxai6iovm7DRrFX$b#y54c1^|6Nx?_92}K` z=`)Hw5G5nM7>v0+7$Xh$mF*c5C%G<(^yH&H1pdSw>t)|~$P=2hgM4#=!$?l#pe%9z zsCo*=QKc@DK^Q8P#E35C+dKLR!5bSN6^i^yF#wrMHi3EnWCUle2P z5;aGQBA;ttDUvZTGH~KtDne%oT9hL*;nmZtG$6=*m6g=mSxrl9IZ_Li9^zp@Rtws< z1o<#{a+R8Bv4T+sXE_PYVl<`5rcaVcQ#}HPT{ZMeKY_75!(cU38BwbzUc%Hy=Uds3 zIaTL3Z4ys5e_+nr zhLYXEnD6ii^^eqpJIM3p73J@+MF;+^X0D_%h)28WSQIe^4t!PrU@#ni<`eY{(o90$ zRb0Hdi;BGy%=4O-^LLaP#1Wc0P>LAjaIK-~faif0qx>zySZjI8EV!YP!5KO^W3ulj zRWvM4KA|FEZ@4$oRtZn}U~Z6B43>FItA??3sPgBkFuD7hjt*RX)kv@b`>K>SepqgHpZ2*l7B0#__n zY1fOLc>Ljy^kT!^lpxdg$Z&qD7QmG{x6#*P{mogyP0jgGb1+93v#9-B4XA$2;FEX~ z1=m=?;3d~X=Dgs@5#yFsH5)ME!#_H%)Ca;phJt|wPvbx^ zfB?};<>%fqs=I>{3egk&XIaZE-;~My;*1j zKH}P8!Sk7niuy($saA+rN8eGN8jE6gRe#8M+%?_v=??*BJN?)?CE0$yDo2P*Q&$&^ zwjUW92vu6?Acj^?20c1HQmP%^t`INDbT$cba)se$+0rOLDzAeVRtabm$Y*_ZADaa8 zS<;8yCGoI;#~-O0yE*2&#Y%`zyqq zFurcj^s`va)n8AHUf1Z5-f+Xb{f!$U$#kgf)PfI(WfUbH(ydq5!T0iYdzd5)his`{ zPhy-m%&vEeYmDFd;=K#wswHt)6w#M`?cO+l&d`x_#lvMVRv`OqBi3lZyWe_8$S%Wv zj*k8CK(yjoqO46zVhygD{tdm(7viBsp(H4`NRtl#B5g*ihV6!2ectQp%^7GxPB(( zD&gT!x)dkV6qB%7flFM!1ndU)D4`F|SLs2FUERS!;9nK54^Vm(%yDm_qrj z93^3n5%bbl^_^j~S|{yk;~s~TPVW@GmfB~yKr=_;==hzcVxz)NI2BbA2P0y|SN5G` zGjOMyf*;ai?*xQ+_Ia9erNHa^PJJl|&Q%hr?zfI^zylv>N3C7Lm+!RDb|-tv4+gwK zd(wISl5>BE!^%fIp~_4g?3JWr@OT<5A9W4ALlRh{I=wH~OEHqTn2d8CFN-~yu=`qQ ziuk%cky~R#_-UOJ^LR%X>bsQg*0VyJ=g_9evp5;G&pupyPEPx+vt)xVZNq@Oyz@1<`3&s{Xk(un6!#yz(*XLYfUAHm z4+CUl>W}E_?=U~kgn9KSIGn7OI@HXv>QrNR`zsX6GcVWxQ9pv0s8rr%a}*nu&-6~9 z>d4@ThMel;+3tG)WhvoYDyigu!^zG;i zZuwYRyEhW=2TxThSW|JoBglPe-{@SFkkt$6XLODuEkA@$yU(Xj?~tsG1W$5SgTXZr zQ$@yq)GF}v`K~bGevPaE2G1;3pC!3-#31tpftMHpBZ*tQ1~C_J|4P0#F#S)N>{4Vr zn#A8(tAvOW@NeSypKN2yr zYiRRsVL84S5Q*mb8i$Ie5HVah!EKfLP$UoIZmgEC#pXyuVQaKa#YU+@+Ad*xfXPw* z5_>y8dba)e!Ary}MUkY%OU!c_)2RLE!FJ!af#Zg?`(RmUcPA+$)x!n%YjC>!%)zwN zf(?~Tc`|`*FUMQS-m-Zc^_}z62hYbR^6=UP3fSK^Zf*OjLDK;(>lfTRi*2+9A5hY` z@b*DZv!mlm<)G+$H>q|4h5=~sMew-l*l8nhx9VqW-7dlKRU^{Wc7GH)`)ECs6^f=ve41M$rw2oP%oePEr?@fcX(oz#ohIbsC*OVpc5<4it!v@ zX4n@!v0Uh+dxycHaq#C7RWa-9-?XDn*&jZ-e&nIF(?Y_@6NA&lAZ6(c^iXavz9{Yi z=qMdyeg)D+pC0IF2bTQNr-v6fSCL3jp~8sS*HLOlEB?i1K2odGVjMGcDJmiTBu*aP z(05G0O4A;rSNOAf<6sX}N51d z47uSZ?EF}gc2`>{?&Rh0b&Z|0Jse?EDNhWpZ|W8mty1E+n>*DPH}2@b_0tr0U8tnH z>q3>gyDn5B-F2Z-<*p0W)4S_JrO90vsz}{+q0;283stOQ@XY(N?2YLzA&!VsKSpuK z(xa|V0&n%n$9gU6H6XV9;zYwJqtBSs0A=*wUD(m!iZaSHNuzUO6b%}*J2oF&Q9068 zzSzKnqRw~0H4n<=mvxh$ocR73f1zc|QlkMWwv3@1nCBBnNk$sr(lLz0M(qc6bVd6z@tz?O`DVSn@%CW^z;$l;du|-Oj zMk9bXkU-h}s2e~?Q?=sQ;RNYYm|K!b2n5Lk zb=N-DD}i;TjU#H>sO}l8HEk7!60z5D9VNL|j#eg%``(^oQ4ZtTQ8qH=yNGACa?O}7 zii;{yo|TV}Inq98Ls*P!TIUI9BdSK=6531S1RCKwpOK|IXbyRJ2$UpIzW=a>)<^?_ zxUgMc!*xniMq2{Iphw?e;UL*#ElUQ^r&10n--ek{RU%$vpT3twk87yFgK3ouLu7!6 zIFXzx81ha$)SQr5ue3SF6W+e8E0I)-L29NqD-v8#D}hFL^6AXh1qWFpAn6yBa&fN> zpbH>ap_IClyY?;W2bs*(yM#Mxs(~~I5elnk?PG}&D(W)e*cj(@`deH*khPp~W19va z+m{r&1OD;v!GTA2rmuOyuUEbJmb2<>4S#$#9o|wYx?il(*y}}$?upHZi`2Yety1yn zXG@)w3a6j_3&3sFwAZj$>o8DT^jvDEG>f(osIOSWxVMMw>y<_~JFk-ND`8(>D|UB~ zQG~l{@0%S=6fFCZd{`_~5>KyXis!VTmy0+9Q$jkTZKoDt*uHFWoE7hjMPKIwTMRCN zLa?p7rKFA;3n(P(7qTYeSf~Kctt6hV3XJyj`kvsLzUNqRolJer<7wxkIT?B|Iv;B% zqoE?>=1Sve0EfcA$&&nuSls7TXHfm-erDs>!-W~7z_1GOW5Ch;d`3ix(S*8~a0PK$ zOC5MyDRyj&qZ=DU{SR1aI4-7qonr@M!ew{qLom3yyD_@)MULmiT)LZd>F;&8et~fM zC!Sr!WE%-;!s_XS5KP`*cei4d+K_{M0`2BbTNDDDYChy^RkT-MuN)?7;aVy&^ViZ( z??FpqfiZ8}wFwb__s<{B|JIlyj4P-jdz!6RC5D*tixJmb;A4f7A_xb=TCWR62rN!) zkCC6yjt!|vBrN7rZiZrqB8o$JT;-k(gf{&DCm)lip{hAeu5jSF8dK# zqG(xlMh7afUQ{d+yq{9dPG;=eN=f0a3spYux={7kT^FkMy6Zv}a=R{62{(Qa5tvU% zE5ymKNpGyD!+wkXH*`f_b>GvL#Gi&u2hKKp0HrLD6*}CnGV(iO)GD`net=n^TKMY1 z%WPpZH~MjjgYFuuUqZ_e51|BQ-YcN>R-CC-V;}XsfYJ^~3=g${9h_f zJ!S}4Qh6mdjG#`_`|4GFEkPF&v;iXe>;Kx98SbO}82OUVbZYNQJ+k}|V>Sm$mC_z^ z3|`885p2GNNKczhFYg!JRcUia1=|m1>||ea8Q(@qA#TG ziagDNlt(PVM`wlKW}C>tI@Ecl-Ix4C2mKjfjzw@sB_7DNSq@1@HHbJt2s&7!Bs0$L zF>_$8b~`Z=VD+rp6Xd5JLUYQ#qC%S|h!_L1@Rc@FjMdC)#(t_q+aUh{xw7g-CXO9? zU&QF#CT)_8!wJ&8F?3+D|e{LhxA?|6Nk3^T~ZYSf1F4o0}sp#~?$J_QheM*gXZ0BH{z=06<0gnX!>{;LtAx}kpVc3TeJ;dGdU{IIAO6m`;B{Mv2=cztsgMa zzE)9%Oq|8$4wdC7MTP^lp?UhEN_0<bKN#;|q4@$>UOoO0NL4iU%NzmLQ zSfqq(^vluZHQB0lcH)O|ZG_L;V_z3(d%s*1Ke@Mdy zZsIKnd9GXmIeiWn)1eevWD+1_uDebr?58L3h0Vnyc>F}7UvF})F}5h;9Xry-hv^*M z=XD~zMM*F|1#hN=ho&-*Vi1v9i6fHzNDGS(2BS|Sas>lKXvUsP3Eud^kv2p%gjhP~ zUd$$SzTit0Q#89;&I5{*HY# zr7v4@l1^x3xDpf_J+6Mf$d^}DaYyU^t&Z>PK4uTuhMK!Y(J!a+2M*QsJzTW3FNmRq zd!Ze>gs(2dobsYXDhpjh#J`v-qk7%;TND+NN(@&PjDlJh-; zUaI2PhlY6(D+u3dyC(Q0_MrW73mlAaQ9nbFeV2cs+6IN-8~)EVmaAms_#KH;{5!Sv z2A7~THHR#U?xGrc2&khI@mM2f7W8#RZ0`F{i25*R6dE| z{4ko7M!Q^nqp%fvdsK8vI;5J-uKlZGMBROGPXO+vKYucgh8Pd+h5WQ)B*k zADPAezE*Q+-dRI{mxwn=I~MxLh^r;&jUc+8IQXf&zb`74WER8SWxHQ7i6df8rApE% zvgE7> zeZ0Ciacw;Hf*x6jlabHy83=~mxp;h$F!~lgnQAxF@fXd8#GhEvD4)hws;vLPVuZKH z)jCYXV`hV#=7TNfQ2%FLDz~&DKT=`&pJG|DxPz+O=``Ogiq#bjc!!g#J6^$xd_P7J z9Sm-YZ)c_Q6jL{YAK)RSavu=4ueL<|1Jxb5_Ng}5)Q3v)N2r|p3QH+`$lBSGjuL~> z+hMuRg6Gt5<7mH4H4=Zuw1Jk6X6s-GtG3PW>?c4G`>awlnSZ@5i-~3{4$#8h{D7B$ zmvCX47eS+{HwwqT|5)lwOAD)ZvdCM?p)qRaD^!!V`YSBP_i`Q8gK~L1sjAq6>&sR- zE42IZ=X99hx5WGyY`#(&l|0&n#7XDN?N^w#(|~M^Nf@3Q_3)&FQA+(m{@zZjzIf8~ zYMbO^Dn|}i@5{cbv9rdsvO1Yq=COlCI%Tn~jtX=Q+{)T~z2sGPn?YpjltsaQuS9evQ%pZFRFgmE0LFo&hTgpPUMG5)0bfF zTZV{wqv0SOk*8P`VMD$&vO9c|Aex4e1^Zo!_KJT~+$+F0&lTSQAQd!4<jZ z(I&Ju-5b%nS;;?$Q7doX8I{!()q<!t-YIYAfG63;|u4}}ROTh>u<7{cIsvU%zPbWfA?QvPleceV!pYhu~&8Lr)}Bw@1&O%DnDsl0BiN5)~qg znZtpi;7$+K=-6`&Wq8#C}{F*P(y@vKLvGgqzqOQL9yGjwJ5%{y>3&j#K zItrnI#a_~BrPgIJsn1};HPkr-F?5T{-Ft?HiAC!*y=b7BL36DuK)Ap@AF5T{-<9Qh zK&j-gI;G})w#HUhIr7Y5Z!dnM-*%hi$fNv zjO*abW2L6>?cim>GW8jx6cd^P4mK=b+)S0=QMobBW!fObE-PxgW`&C=G1_eO?u;VFWvAQk zEd7-t(h=)bTKBr$?(h5W4(O~DoX8L1AyT4CMYw}MQ?WAGQ?NH*LpoCWGu=T}8=YZH zY`##i>UVV-o#SO5@m>tJ8tmS;(|0@ z7xf{J$E`Kn2+0M-GgBH2z#dx}T|dabYXeyb^P#qlG(eYxpGI?wE;ETuQN6dQ!3%t8 zVxa7T3(gx)&NZQ-UgXK0>LUlYlh?X+Ns|LP3) zchzdiKC~Fr^;0;urGJV1AhWvQCIpABDREes2+ z^`bSn5I+KS3c=MAQ~&C(RiYXlqL>uaJBh_Y+9IxAYob#NbG?vh!lUM(d)Pqr!^3Lx zf!@4b!fvPD>0ndq@P(AlaW|$b!C?^jFYx^#@dLxEkTwBpjAAQzVHL5j`>$J)qVtR6 zxPql9a-G35nTboHpJ6Pf`0rZTLy;kQB{PG%g4|KOkJ|q5_!Z?-o>DDw7}5MyIl(CQ z#z$9LPbU~=lU<$gqg$L@o$xhc2VZG@r#J>4xWnX8O>AU+!uvu z$IXC!f%!{ihLCJyP&DxF*w_9x(64uAXtIJ^P50rMU zPTE8C%X-q+K0#H2{IHgV^>IDeE+w8ezHSc1ojyj3!3SMVw2c9SA_@!w`c>tGwTFF` zrD2EvyjBEvR*FdxIUYR~exSI+m}oLE%^>Q{AFxy<-xw@G0thtKEEal`BZU7y;ylB8 zO;nIve~oAx&|ohID%eI6>z$Ny3O3?OF6bx?tXV&QQf7o^i_w~nM~l&K2c`^sX+^~> z`pMHQ=xo?X-@zLwv2p+z1z2+cSu&a~lGejCIJQcw#)k|e@U5dagKJKVy<`keM`!8S;FXKiODBJ5{`pDxwlxDzJD~Jh^yZ>I9p2H;@p=MXKMDu30hB~e{nQTDwjvNj*0pNJ%V-5*Nvm)~Y** zLP=;*gfsWZjwN>*w|SkrIj+(dMVy;_gnG z*|< z?{N-wBIHGQONM_d;;QRU=7c>w%A-E=a3rU_!P zIJ!RsT7_K&d5syT;ttzU;u~tFNqF zF~9%*P|-U@yq2C+Sa+>~i1mE8LN~tNbO@j4dilGCgDp;8{9YpX=nCd%tv7!EhTP~= zvA%!G`Mwr$r<>dB3rAfD`Ro^WnwdL(wnk>aJb{2j?&#yPX0!-(2X|Aw`}gu0>F~b9 z$6`;AaE(Xwc0XnlZQ;ag};vdJ;Z^4uSshNi4743=lH#n-w1hVVir&-wiwqm zw6V63@TIM16`Glr!v$pxFfwJzT2vT@5vGcX3EDnhDr^5^w^6zr&LEtSHpAT}-V%!P zRkNpvi(*=#UP!#fbAG7P$F60T7^4pcw9oLNQc;#o&<8{XYBrI!AhGO%BUG}|Vb>c^ z`WfHI0P|6$XIGHGg{#!O?N4rGR>>VNM3XK1Lv?S%a!oorR60W|kNG{m2Wj14G$o$j zi(M96rP2=lxN_8Yl`kBv6Hgh=3eHsgKpZN)-IVAZ#p7>4gp>QqRwx}ZbaO`Ck@yEY zr7Ew;NHiFFrn*L)oWidO!-|A%?iG;Ip%NelEg|@K^-N)8vGu4ettUUmDGpVdT}_L8 z4jq-!$Y6JU^h&K;y@It_&AC|?B`wxcn~Yh3Cm|<==g{&b{4h%{gUPc;$48op)Vml! zp;1RSU?LdX5_KLNX%I5KL-Lib&!RS!_>xkYYKYR-H3Or>36flKr0*r-Cd1;*lZC4! zFQg`1%t&{o|Ci14dZha0hiZY|%wC#U+>j;_Rr5&6>jT>6f=o&VuPjc^N>ly~U2_e= zz5WaK22wRxgp%51y}*dm2Q42a{`{xwP`VSxnPe+dGzoU;%W;3k(IM^VF-%3;@bb4g z-%+mOOH0KWvs$jT48vi+UR2gIH7+8r{3Oe+X$M$-rW=-6I&F%!4h07;?5zo&IF}mG zxGuk!6;Hnb5ms@eZG5_qD>ef+L+~7_TSO5FW0=dw=sNb z92fUEQa-5v3pO0d=FP+5>ou=%HBOG0glIp-wB;AToLsA|s_Cw_1#@!j@A=Vx2) zXo&mr%bW7IHFP5FR^_nScgc=40yKyeq#C;4$@i10;?#erBFMo4EBdW z@=~7jQl}P8O_KiOq!GXJtia z0dYGn?u#f-a@Y_kuOjO=Sn93rC4}8-@rd=R;ORkn_2Av%;Smhy5DH7mm(2OzV**Uo~K=R@YUEAA7 zRO#86%XjPIrF*c$2|CrmnH~f%SEA|KQiI56P-;h|**&tymV6mvIZ7DQE2RtObTMuo zd`@jyGREnjF4pv^JNPmpDty_bkwl2FQ8O`EcP?O1zC?1j%T#rpjM8(-10=l(wrwW9 z=3l~xc`D89BnFDJe%74C(A*C{U$Q@x7}FOW(u|HM_J=K{Jx9mB0P|v;Nz#OtrCp#d zTYgAnw2zhc5f`#A#)QR+>BWA&5piLf!oianazS5F3GeGlQjWPv#IuOS<(nQN`B#dd zH1UOanOyzS^PCp!@x9rrZ-d-99|oB88}#=gUiRhoBg6S1$T z+$j{}3n_P*qKh?aVp{r7OryiUS1%*nZGY0Y9sgP-+{I@8ZM<6FkwpteipCnf%>1rw zV#3&0T23&KM>HKu7r{pL*_OB4`?=DLo;nqGGR=DG!yoTivz^DC*yfx<3lkoGkGI`& z+!hH!oY?P~7*yMCqs_E1h!1A`H-?fNY_kksUWpW(96cy?!VWR*lIWLG znU}h^R-tk`g-(clEvIyAsbK z4bb-@ls&$R(iXt_vFOB-eeEa#U6XN$+xJlG6IHoVu{C69!ePW+C%uSnYU6%Lr7&)9MKbUmS_byON$L%I=*03MdCQBO4 z0EO%1AXZbr53+5}R)jwaq<vz6h@g@TzJFRBvaPskUl|L@|y2HtS zVt>IG8m}LvK>GrDy0>M%44go|aB|n;5MLun@}wz342v8^drwdP#3-eO2V6-X&cpfl z&6AE6A#S+uQdCUR2{gzhHJd!rq87u#1)4T9Mt`4&*Y;8x|t2Qff8*;EF6) z*N^mvgCUD@4{f#EB1POlRo--dU(9d@G}T0K^H9XJDBQy9zs9uOjHyK#B`mMI;yc%M z9T?ZDB$t%$+kC0jgT&#n!Zlj^arKO&yV9YC4z`H~*Qi;VqGcV}ytZL?*cBWGtJ7ji zw8lsC27W|~WMz$Gg2yOrVhqM-Z2f|Y39+pdDK(xOc|DcV>0ORZ`J&B4iLD{BcO#!R zlzdo&h5|e2Xd=9jpF?o{s7J0;0;SDE%rut=I6<-+cFw$Xy1V3NDws^>h_T8$ZHwrx+C$qXd6C`DG%-dvs|EZoEvSw?3{{S|4WUtXo`Z z*|^2Y6%H%>sclx{OE=TS`Zmwciod#TF!*JHMx4c0%%xtTjsk@tsNv}e*D&o|ut%*P zTUd+|dR8IUn9q9TUJm09UrLxPayETbw|PomYo0+iFl|2Q%yCetBEtE7EhoH$)#Jmu zWK{_eUPOQL&j(jKa?`QE;Ak9OePpO$qg4QU&$)4iTo!d&!B{Ij`-JEV7p)$ zO4A>z2c%QZuU@-@=x}niUfm%Lc!{b^>9f~h?{~l6{+dG|!|bb?O+#^KrF2aGhAEY6 ztq!r@Q;}Ut_Q3~Ifu3&UL--*E7B_Dquies1UaKE@oT8px9j^k+Rt2$LtX&BkeXGAz z5H57QPS9h({AMLiuvV4ZpGr71vr5aF492riB-_dt!{dIYJTt{$P5Q@oaH-%juWL_p z%uXw{ANUv;i-b^2Z<}xSM(I+f0DQ@PnTy3+%3Sa;uVIh-;9E~?vePCCNJrUq+KgoF6v%(TR0n5Sw^q9isKp;4jm!ShC6 zK)maF224>9qrKVXZ~XHu$JBitNj%Q_!M9M7#Q=9SIy`+MleX9_@t3S> zEl&xui=?Ui!F!U3jX4-ROv$fX<E#Un0Ov^aG=1M`vQzwCh2&wwA z&J>AZVw%%0DVTjxqKdAcn0X?pHprRbNwK)jD1yR5d^#1LCS-EE$Z6dZ2Rer@+)VdQ zs&(yY7MrKZDTy(pw^|_Gzr4z>u2b%fgXX%H+Wb~&P_}f6s#Xek;`%F2w>(|97oT2=iw-{| zLB?cStNCp2;XdgzxJcz`YNapx)~gzF2>{1kk37snKsW8av4!b1anS z(m}lO77F*}pC&+qGfj)s;KrG5&J@GDE6M)0eu4=1U2cgZq~}XOcSxhavs4+*&|!0V zTYV*`Ca&#nM3eGGo~Pw~zSr5Tcsz;glkdz4X^Uz zGHt@l@D$~nJdS8o^Z?`F8?Po#Bmy*(Fms!!zEjFOE1$<`19#875n#+p3tjK+{e7o; zYMP~Raop=vVq!53ww~p8>U_E`i?2|P={$B2SH6td2k+E|wI0B(dUc#f&~@`PRy;Un zv+f()rbcX*U3Vq$`m57Yf#EGSkyxZs-dXXymt>-~!;b-XZc^CUh3M?t%#g!vmAp)q z5~SPT+-+^o7ZG=+gN^AAXLC+*Vx(Rsd2!yow5b~+#`smO!=W*$Q+$wP`XzC-@kx%0 zh4eNyR1gt^z8tf+MqH~{oRZx*Y~J&)U~w%al7};;_e6vd$AR&x=wJ6 zz~6MxoWo7Bh&Y;j<>h&~x{;D1Y6`^rpUcR*`gxr<&9O%YO zQC$7@Kf9y$Cvzq6Pt{V~(Y@iTAQ3r5AF!1sDH12YUBvR!@k}K+Bq*%V>`~)$7B{^S zQ6=X<)zq8ygGnG|J*B~kWR$MH7wv8*Fb1!p-&tGjNxz}*`I6t+!I;?gLpc#wM67L@ zlfsFSt}&}_#UCQ$m{w>CJ_YA(P1Xyfz=4{SJDFYwngG1pWlEfV+p$yA(GB*b371Nf z6CJ2!Iq0MGl{+@*?*m2BOw(gOkJBvipYCajE7h*-mT1up9hG_^@iunn3nT{^F}PTb zbq0vVh*UjEagF%?Dr-yy-{ZZiwWkG|X~0cNwbo+4Mb=Fo;Y06LxWdag1=H@UVqh_j z1<0J)U3iz~R{M%eNi-B@%gle1cne7qC z()XI1Zy)|YJGN<2jOKk|gG+v@=uZyTN-<=VduW=QBZyA@&0?p)S+NRD0nCaQSZ(=GXAO?4=Z8x3XDkv* z3I4rOXL{Ys#+^cEK^Tla-$<#!0M4h|v*1Fd&Nm21iy}|+MKE)OT1hi_MJ3H0(g#x% zZPJM}S^VR_KSP3zkI22;4~V@%QpzL-F^-}8R<;4pKmM--?nleJ{AbT<^a#B=yHrV(y)q=x9uz2Na;O69##c7;*ys*ec9pf4CLa7;!% zfiL~MuB>@Rm4rBUks7UM(z0M#3|_fyba+d3v*VN{f|+s4;YgcP_olN#LpBDq(y{T{ zDo{V8Y~u@@lQh4Hkw+wYj}yyn?slYVb-F~O0HntMSNA3s8`-c{>FO9nGlmq&XZ#lVHd-`3c3t&Az(WbahLiYVBc#Bnk$diECreAX8!*!I5%| z@gv4)7KaZXyWSem1Vy#+7z-dX-w!5J3FT0dj7GrB~p-D1wj z2e8a7E|6T?Jea;1Re0rRG*@D{F@`#JEe#0hFqqU7$_)y&BgRMNLINRTTFtkU^qTyNMC?SM9(Gdp|dHN24Uw zMhoK{`AXWL!6N;_E!UwR9f}@qB0VM)kq*~*SaTnJ{p%RXs5vHv@`zjP6^7xLmGycC zPM(fh#L~G6?x<3;Jb)#$!?+uKQW+f&5dB+DiZ7GR#56&M0HX;8d#6)GxAv8?5?F%- z&^|T9u~e0gw){ImZ3i_P`8#rueO;`KE)^Avj!QGjRDKZWsjbWjwp(@YN%1XT!Z4(E zW-#7IEydtM70VdSTP^VnKGR_TnZk_T^thubjw#fNg|CG)$dE}iVDGD4Gqh}&UwzS( zeuESIut3SNv>cC^w7$;u1bJr|hN3wq7S~Rse8p{nC13aY@og^^Nz8z!V1E;5ng^Sv zA`vbmC=>xv<^+ScmV})~QnNw~$fxibnDqES*~;f48M@_L{ERKYAJ65BSrNXxonW zb*}U%GGC9lobIKj@k4IZ4L;p6$ZUsNptM%)dR}ahGyk9yEr}c35mRsc;82*yD4zBc zkPOCK+T2JRq6z$qnF_%E_`*;qRde9vg{N7od+H8I^9z{Z7x}HwSA~#QqaiC^?1;Hs z=;5+{^v$&$ije$S9Mr34I0uZcQsd0jtv@>KP^7#hC+m3l>1sD_vquKKjJEcVV$NCxmv&EX%693@6 z)v(PbunRE$h)Q|3k0J&8lb8y5bG=>71WsqSrA%)nBQUse=2{foRYmnAU)*bEl>z3n zM&ybwX_YleT#p(&@ib2^rKo%^SBJ}eLu$XypPnhg_InJ5N_iKDleAdPbMIY=>Vm$~HG9A$ zz*SlBF2t&$I))o#5w#(tCe)Q`wP)%u8dtRaExuNkw$VGM9TP(d!48_}C;$w1T zNIm}~x+c|fJLUJi*Lwra_9Tu7Fkd{oqxR`*Wp{M2b-Uza!(u1_O&U24w)u%4H@fQ@ zm6*-;qR9adE-@^!)t3Z)HRF1TFSQ+Pph>}WptqiQV~&5)`}S-K;oCyq7uxpj?hBNZ z=}8fs$PYHNp~+8rKWBvcI@_JXX+5QIDSVxRf2q}Gu_toToy1s*YJt8GcYyYTqJWVp z32xpbvQ?_@I%ObbXwuxF<10aEmrK(EGD83uDri{YiM6PVKQGqk-dyzPgmTz% zF7H)q>xDh#1Qiq9m~R;R1^mdf<1gK%I__e!cR6u7-NInE7hNf>mR7edwe;r6llp{b+TyUd9eyEQHS&pCniT3 zE#H&ac14V67bog+zCo+XIj=K-Cpku$zWf%Rv>Z{*oR<>MR!Q;KJaUSB-7WIZXT`=3 z!nGK5>k7ZOaynmin-+`ey1&p|7h>B(=S6xgQa8}${qRyGk4zsxc(m*dPVmEK9)0N! z^|lYy?S^`r{2h_)CrYCO6bUVnCPSkhXzu<}~9Y=xokI0bwfSf0T_>8YP3dQ*Nk zCyq`wVnbz(cul7^N%XQnEx>Z6EjJwYqzTDWKXka(JCkfB&4}(OR@RU&x&TD`4zK@{ z)4zIMzFw~@Y&(dt;b9u#k^Fv4NKzMt#eSV=KsZK(fp&JVW|g$*XOn@nA>Sm%hXl*j zq>AzW^L|5l$5P&@`TaDd|NIYV(#^?A_=h!tT7cItmOKC{n3&*X1UFs!ad36n?~Q_$ zsuO7-BcY}~shJpaWSsO1u2d7)4GHa%T{@g}F?<_(M3C8x1#6y#&4ToOP+eA}$R;rG zME#4wlT`(lO@w&6|1UNDS}?d1^n3Wt%3Dn2mNt)$(%TwpnagUvjm$&*$aR_#2i>qZ%{<51m>Wt4L9qSHW!~ zPR^yJCK{yIkGY)iSyR2hy|k2dmw6kwPU));pNljYrE+7if9?+yP~1hyOOi^zU6l9S z5rd)S!y#mnf-z6;S?d0h6PN|nV$(cGue46)y84EbM9svaHfP3@b@C^i9I(16GA(M! zJzSvlHAGAzMd2)|L@-1EI^R0)aKQKc0MN-&96`Mqd5Fc{XoOL%-H3x1R7#WSw*6lQ zzDQrD-QoujH}d~r8BKimG*N1>a;36j554P)JTkwxp_K@oG+XdlwTVi|3328*GOn zqei~2S;4g>M-I{4)RpW8AJdNvoebg8!nvoz$zXr(gA?itObx4p9A{Ru!FVMr*aK4c z<)k=e`kW1xgdo&9IQ!v+DUUR*PXe*+M}(qO8Y)y)QP>kdO{!`ZzN=Cv8XQ7R z%yf*5#qu3&%aqVK?9}Q>cgQ%wWhw$kSC{C4JyY|WSj5Sv^s0I4UMq2;f(%6o*_e@O zi<4~Js6oSnG)rk%%{KRymJmAlGWoEa1`kn>uJjv#6~Hg(;J%#YmRx%MGaY@gK5f;q zgJ%GjsScT6^5Bcc=;RcDgT=(8Y3sS zNNEvjW3W^edOJDMI>){U^rV`umoWHgKp5;cHuj!ssG&Z8e*arJKXe)r@qO|EMRyhI3t7YurQ@J$f8>qW{bxWkk( zgBY!$f^Ag=RQ?G*=9~p`7Gqz0_Rov+^?ZqRe6O(wyzY&PN~A%5gXe!qQ-Qc)3}eM9 zjkIxppo~NQHYXvxy^6#?$~}CKd-8t=wf9o+j!KwR!l~&fhAgp(SE4f6LZUWbeWNrg z&6dF+xw{TxFjQcEywSXaY%RVE>P8mDbR%C&iXFp0`%=&WLrhIaR5RGJXT#>p`r1;b z5v9C=!}T=Wr*E)?RC%{5i=PgPrWy!?&HB87!*V2~AS9j`*E#m3rZ>pAli_#VC&3W! z@>8&AC2luhsLK*94jk&9LcAM;raok_P}MZb|KGl){~YzuFl8QQc96nL2!wB|6>0DkM{vNH4a=50rU_}u*=0}bqKPq1! z{>g(xoV4siKXcT$`(UV$u!#{sh2@9F4m}e%@$Sb6*YPKzPYT9 zoT#xl!6=&h3a5)ESc24CnB@6qaXAn6PZsO>cXTK@8Gr00FD^foCk-!J=hzpXUL#0W zq2io*1r~?95inj~dAd_r3D5Q24a?=jo_;Yf!0E;B|V7pWr zr&%vUZmZ_20w?(4PfO=;50$|`UrL0#D9xtN)ofPvpR`Ae!A8jw5mc9)F@g3@mFYik zPm9N@s7}8lF%;~H1V_A$+}|m=UnBSkJLHjUg4o6`Lnq9;9zBGe;ROX1{@n6`)UHk8 z6cpDBpJK4zjX=#h(AG#`92lsfqm&$M`6K-(P?s&8YDmLnPp34JVK+BRo;%0BLiNMh zM24mo5v+prrfH^dZJG)ne^0^Lz^7!r~j^J6jknB2M^qBi|9% z*VJu<<}kjEZ=9j17_kY=cbmch<>gI|;p<#6$Z`HqluIpra~O=$zqEDt#X7NIA=>4^ zFdteA4W89z;RkBeB4RK{)Q1Pd!ENhSYQ-Rg9LH9<4lyp=BApJDMoo!1x}uQl5Z9U} zh+>OQgm*}C4mCbs=Q{qn`6hj%_|AfBS)tAJdhC=^E24vszvHCxlNqepArdLigLYlG zS}Ar=V!Rmi58X0{T@7`>Y!6f@Gfj<|2E?8W@zyb>bf9D`N+nvVKT@G0mIK;6VZOGt zKk9$P)KVS20-Ts1{*Gr|X2EjbYlS)PjaGRJn%EAS?;BpDDGg&-c{+dA0Ay1m%n@Fo z3UqgJVNzoF34v{n42-c=u!mDhT>fq6@})hOXD#920qq4n@Wi3$PNop{}}A85n9`c?RXfG>5z6tL2b(zJ=JIu_lfk7?9cYyRH^>&in0uz6 zXq2Bvi(t{Dve`YQhGCv1_Jy>=gQ9SKAzKtSKR` zSzaSs6U&I?=XH(`wb>wgATJRaq~hUV`wRQhR_U`=?>1@ruGMoq-Npr5rP6hfUiPFm zROvjugjOlVqMp+S*Q#dRsgOK?JS=(y3}#{$HQX?l53j50aknU*mq@XoYCvquuzcb} zumGiDvAQ8!Q+nbZ<`U}4Xk-t)tQ~wxQMRzAxat8*FC|D|q}w8YUoH+4pGK%nzuqgPe*yQ|bDHi8jo&-;l$eWW*a2)a78~ zRm9pAS95Cd#HjBSM;`*b3$p6yu~(UUww|p;k6;i(@HWNbP>tZ7%($bUX-(KUKGM|S z-seZptqf@C*S>^xwwD`Cnj}b!636B#^o6S*vMxLd>u=;ZA2PHNk{^JRjRPH7Jd*cS=HVw zpJ4)8G3Ci3eux{%zC83F|LapZ!*!zJiD1y;X7Jv_n_-Ig4FBI?Pk z_*Sk?wOrp~*TOS$p}Ky1>F4sXe4>Is2C?-L`y1cGI=+2P>0pB-z(J|~JBfe9A$@tN z=x*3r4W$vcnqqg3edXu{Do3aJSf}|~yiFqe`KTW|$UnI1^r?Fhzua#*8GW573 zUpHz@FDO4;0JCXeBu57$EWY#he{??7v|N0tRN=5F?w;#t zE)r%PEEXHebc$JP1;iRZo-6?aV zqXC-HiKyU|;1=;&!HH1Sc3jR-@jBE%r^U%o1{BokDcpS(q$h(6{haUBkONN44~SHu z7Ib}!YFfnXg6AtmqY=YUoUA;Og6X;nu2CAs@U7wr-bk`E;#xVb@5S(%J@`+Yv(Jbx%{Y=u5 zYzbGPnvo7m!f*VG`;n|4Wr`t0B+m(Pbp2|$O7`8TxG(AL6P(Bo;Ayr(6ak+H$kEk? zi?V_}dxzX<`~QCRNf+QeICj2=DKTOi)JG%*Aa<))a<|&8uAYnes58~Pc8m9MJ|s?z z=p$y<^wZd#UI%wNy&9#1tGy5ONmz_@xexh#z5Je>qF2K7v5ej7O>iqCn9yv1+eM5@ z2%%BaW=5RCc*)r9-Uhd`oc@b;ts#~eI6TX&?E!m=-R&T_+ho*Ff8RUn(;Oupjk7a2 zC@yWQF4X0T<5;x2*iGLDH?>}j^eZ>dc{Nf~bfoVbJsgL@t-1*5{yW40rGdfZ#3nZm zA2+t9qu@sHrdQ|$KjxYownq%Hch`q3cB?eF71HTo8f?yJ)H$4-4NNnI*e$Z)7AALa z{WRgWJM0ex%ns>fi9$O|i?N$_gPXF_jE!xG?X7H6Ob{3R*sCAhwHMs=8d0(Zz5snC z2D>j{oED4c*y2ut+aSjh0k>67Q=OQ0PWv9Olv7auki%Wfxa}5+rH>l)>^gP>@&_7- zm%|}GR!#wPb#;~CCyXUqXp2A3sa_q2Okq+Xt1qjTq-2A^W6>0GsXMamd%eP>hbFiB zls}b=kxv>o7>smU-6N|<*f;r~IHY3l_b#|26-TN+#D36K%|98tVaOf~<-&-fnn?_{ z1uH4x21c39D|8`kzVH;iQXY$yid%3zUezI71N+O9*&&chWue3vZ z{gZ~YoZjZf1E(}|%VKPshU2*vWoc}Yo!~{W2Jo7%#eZ{(GjTp!J=CcY*(zZB+NO3? zKtWo78P&@lV$UNxAz@_5TQ0Ri)FN)l_VXC>j}aU&e?2P}46q1J{tESZfzaGTa(X+8 zt))fi=peM1k2OsieQ|)j0WIFu_I+L}u~W|(Anr0>t)IyGiERUKM;Wmjk?46NQ39T! z!_qP*HX9SWK}h!5zc0~|dOD_-Bp3sF@xPd;7dd6GTa-RXPp|IEu4>kTle-drvDplh zcTks{+~8~!i`a5P1t3LGd%Ee(D9J3QOq`d}m1CSo*A?;$oIh!Cqy81*1XV}2<|Hoi zUmu5zqT3(zhG>JxJu+DPBF0`TWDZ$6=%vf&igL!_mvG-zv;Ab5Ea1S4-6m8R#!zP; zzLs;9GY(5)dYp~EMvKQLpL%^FgO-W`CC%%^{vd5_YKZ}!ue?5P@s&9oduYhRpdEv+Nb&pgrp1Cj^v3J;d|uMg{GkVfTz*(V zrCK8$J+_pP^@y#p$a)tfF5>M+@69oJ4)e=LlZ!Ev(dF;0pNA}l5ts{P_bzy)0r9i( zrloiECpV*F^I2Qk0z=Kh55+embfPmR`m@3dJB=uTZ;o3Grq}Drdz1Z+fkD%Cr`xsz zM?S-|fpqm^xzS{F2Tjk?*w+Yo3>@PaTym$HIdLsw5xY&uQ;;nERDLUcsi?znJ;%7f z8)YWcK7KEs(Xow>vWTn=rxU?Y{VI+ylL1@o%{Ry79OP!*d}9O_ zKMAo{I=0CAH)xa4Bv%y4jgan1CgfmTXL;M=AW~K>mHc%BgTJ-+;H@$XeGxM;JMGcH zu-&>+FWb@W)#aQXf5Gy-TudMl76vC`;WQ(IrduQAlxNLVI%@|b)2rn^c04)XcOUM_YF0W z;RJ1(=f2~BO!&6H+*QftdWizjJ9V@9qx`nTB7PrY^ip{{W7Q{dkOjEa>SAvha+oVjh)26fuFgU7OEBH2w&FaeHD4!!cjfr2xB7@- zK1$}O_z#pP`@{OK#dCi9&7?l7W9GNtWHcs*e;E%%+G)SjyOxSVuR`Ju7O~`|yLLRi z%iiepYTO%L_p@H?o+0}gARTlA=N@?>F%Cq%P9@IG-l^KbQ*p6=DxXmvLM@8eXAi~} zMID59kugf=)zg@ce~0~!ZxJ-_S~^K@1g|;5T-3(|dp-{vF1DOd7pX8?9ahtrrY-Rn z$@#8jrhrkwKI&>48jJtL#D3Q@OOWOB9bQGvydXvw(5*`esedeG2B2IDabiyPagCv3 z+Y)jBqtT#TlE&zVG19w4t1%^%u{(qsMGprL<-Zy4Kn&j!l*ZT{Naj2#=SbkWhXaF- zCE|x1EiK_yUzU%>tavo(*4sAephc`~z4^uw8ZnxY8c&rmFiCOG%W?)MqEi^a_sqQM z_Tmqeo~hA|kse*mscTylH4zS4b_OE6)5pF3iWEZZNd!DI66iQk-GPhfVI1*78`@fg zMZ^jIphfV==PMV8TTmhy3|Fu4>v%lrpykyVAlUO6y*8NTNQ`RhWwJ?dT<1$um3s#V zaEw(RDBV-dMRr8F;?5e7EMpe#K})MR%f{zv=i=%w?%M+==7)=PkRq-%MpoNJ4=L~{ z$Jw z^J(}1DQnRu_bQbiV+MeegD4LbLbM`d9eKH zb2V_!ar_E|l=Sx8Ev1L~XHeN{foWlo3~-fd75~db((uvC5dJC|O^{Td(M=JmhU6N= z7C^g*3%GhHD@hM0j}mOU`?^A`ZT8K3<#u#ttn#~x#4LYrIp^<{xp4{74{wRwFItMk zGOsZ93bcXvoiU9O`9rkqEZX|;P>UH}q~)m28U zLUqny^IhOKj=g=zt4`7KPUbK3To9wxV(jveA$7`XP(deUjUq6D6Zk=@rN}zB>S6Dd z8R^F`olIsb!_OSP#V$oufJ5a-JmnW32k7g;x3Or;DL-uKN;lQq^wX!tPb`ONo0)Z} zsu15{aymXmm%j(5&H^Xq2Q+FIZ$9`H`bMFD-p6w^9opt>IBYQh*Y2;MxZ}k3e|;JD zYEC8^-7y{*W&VdPmFEh1Ru$&Y(;aVd@>J`Ow2ieLwsfB?9Z#eI2M>c_m*oq7YhT-? ztwav@VT)m0Ni8OnR8pqF-uEE4vf)tw{##xVkw-1EO&9YDgJQaL!8xq@EYt}UbV9G%;-5ZJQP0;(p}51I z>r&u3EQ*P}Q{1RHYEeF3Yqo(`m~4$0>P&23DEYaL;iz|=X3J+f4lJ&fImB)fY5-`% z0@bR>8I~fp%~d0Z{HR6GLm0x#`kZEmg||mT}c0^ zEz;49<$KUMQgCvLR&SWmfDuy3CwTiNqFip}?MEudY5V-qKj+5cPjkdv>+#sP3Ax7@ z4l>O(81MJ@GgK&z5d)8ziAU=Gq|;xVclazkT@J=2?oo@4od5M4Gwhcdumy$!f*&MP zvE1gUI8sJJ$=$nzo%GCI5cI>SRN5kcv~9ls{!p#|#mPrv`5@2YS2X?(X>VT};n3dR zw8a_DB+NX0=x}WgLsy$45E}9Aq%Br3Ol=D%GM8bJGKWcrp)9m3Q*2R;lW@Dx^cRD5 z`OPfa5(mVT_aI$iQY>faOE{;q%3w;bh&6%1;K3a_xg?*AmH$}mgWho+S;kC*``3KZ z7pE7-R;B`Sv46UMVJ47hj|SJw_KVaZNdhM?cUu*li67=EGhWDkygu2>CscJYY0H4fYgC zZ8Nl=)lR(@Cvfn}WImZ|Cd`Em#M*C#4w1nf$LCkww0F_t<`|sdhYZQoS(5KBBgY*b z_k!D;j0eV-MEwG|zAxJ>3-rcvH&VG;VG0X%$V@<7heVSfcO^Hq_pQA3J<~0Ux;4== zB)IgtD8W3*z%1IL3824%yO^I3loqp#N?V&=t`hr$P5p_T36ed(!9QyObd=_~7mw?&NI}vO5e{zC zh&nBj@0j)y-C9YZw3~q5i}6DU_Q1Wg{azv2{{a!6Z~EFKz+!yXAlW6hBcy$vt;r=c zZOtW^T#8mCCYG&!YTqGKlGo2{`z>9NKwLkj1l>#T;RxLJT3L$){6y$2Yf-(hK%rNv z;lw8HnncaLtVQ#PcT2}Y{o2G^d>ollEcfKJoXy0c#r+eE?;~zg$(}X!*8Fc?P-IGh zybs15d@pDDEyWyhJdreOk>GYcoKJ91*D}Ac)CyH2l?4_czP}#>9w|qHWO3uf@`(7?;K(cgM>1= zIUG*1DTYZ=r!ZR}s!h0UTH@D*b%FkYT5m`Ew8R7n#6P5=Uf8h7&$b(2nkGCPPgDSi z@wsV7M=sZ5KM6`}S}u8)RW;HzWOtHWNzHaD)T-D$?vJ>~&E<&O_TIG zquC+7<040=`*ZCZyV~_)r*7{Sjll^0yL1izfXfI~ijpVwMIR@j@p=|KKi4*&ipub*2@$2=P;! z9r%%T*9Z($*S(BxErS#M5R}DsGfK{DF+tQLXVEr7?JA+}*dNr-s~bU^S}*s`=r$)t z67zQ}V}mcDJHXq#6 z2IlF*95YCHW-NNyrmvpT7dm&N66Z|oF)Wr!7bN;wDx|0 z-fIKnOhNEMyIjBSCs??W2|8{}kwTp@O~+wjr}h5U&V}A?@ac!U?buDi4|wa$kS$t0Eg4}V zR$sNKmZnNrT}r|C^RwQ-P1Xf6T}Vg<`_@*pf||SG2fkhA^M8k)Fy>Mm1`SJR0BkSB zrJEhdjSZKgLhu~6^z1IH*kR0KBd1K0E8ZYji_$1W=;au>*!(iY{VVvTq;AQ6lR|>5 z$hB(eihd&8c`-*pP+vQRSPUl3!n+q|kFkLvCrZA^t;8c9$CSqoYkiYcpyIaziV->r zOfeDuAO9UzH+)~g+o}4CWZs+pxHq9z=&(P$wnv3?8%Jo?kCMAs&+q!fq1N&UPWnUj zVYK=J`B8Glgj(d^D8Yy$%84HyC*~ivLt8o%OLhGXgleQM;)!;wIV~?yL{7$ex?k=i zR|&ra`=l*v(%JJepFL|>i+C=!ZPk=9?*I{M^z)9|?7KvRQDE(lK<;N(O}Rn+X=1yQ%)- zF-o~oxwV;GPiUwZoJ_9khr8Lq7FTPVD72Ni&r9uL>0nzN;~J&UO9-er_Kli{!AzWJ zeFj{qbm(%NF8|fZTIM`m!hV*DAPMl>D6ojlmq?8^95UFf_w?RmU%@BV-{^35FvfPV z2yIl{n?6);4*tf^;$ZZZ)fyeACVhBCP7hp@n^<+btqdNi0$XPf@V#9qEWvH--)YNz zls?C($y%jzZL&;^dl+^YY?Las6ATcTr2JkMVbN;b1lOs&KDBwS+Q6Pz;y}?2Q~KP& zl)*;*Ov6O+G;!VLX?qvSw}|JDU@H+KedURCH0qC$ctJzeDDrL;O5`ZNQbOHHHUoR5)H-?C zEK#m}PM%J@SU*3@=||#BwcsbP+|Q4!+o6*ZUhRh_r$LjFNCR?)8)hx;p#(a4fNOBspYx+wi~~U5o-rNF9tSV1v}u|@FIN@0 z5iw+^1oVQ-RBG6C%&cO~mP^lUXvAtBM{cCFiqvm}*Q@nk9giVK^PJ@~REQraxWU?b z@#}4~*A~t3F%)eIB$5s`hMJw@0l*6di zP$lpkH4Yv#f3U4e|GhaJt@*Sf{Sz;mYYDv^xP{Vxib|8!Q=vIF4of?%xJL3?NmrVB z${=D2A!4LS+aOr5gWALGY*MS`m!*a*ZpHkD0WMI)q?U&`RU9S@HXjB@Y%SzYqNmTZ3CF`kew4`oj4Rxoy`%qx&fJ-o(q5)jTCrN5p04tRf^`C4A~ZpsO{ zg+)oTUnKsyS8emz?77n1*hyRVIpIIRhunT?~(4Js8Q=$y70jP2=R-FU__4fp*I&BzG}Y?}x*z#T4(3U&JQD1^j5c zAzHD*lGdmkar7gwa&OR`=tMhjS`_{CN|~Q3 z^j1ZWa*~O~_0QHOleZ~TB-85wmx4cIUs$7_rf2#gC4)uLr6~;#QAmoWWjZ;YSeoNf zeO~hMR%?5!gqS|h%5VMQ8O4IZ;!nf2-oZI43d9yO)laFLq~labi;Pcjz}O>Bf1ED0 zI|unRzvUVYNK(ai&3;(jB)c#)`_df+V19^t+gikEF%Df+@-2fykzk#Rvy*ltOi1;xI;n2YQm+nBf-}q^GgPUZguuOz`F2DM6mWr3Q0H zyIBzzjOiGO5iD7$ksc9#cVdi@FF!ZZ3P4N2O%AAk4^aa~i6ehgU5cI8cg^uuyv&6> zE%z|j1rO5{hMhbb?{si864tAEf5p2Rn0V|yr3zAZk>pV(h6yE^4_k@hoeI?KVSRfG z`AbG+a7`KZcCk|~#nr&&x#cKr3sp(;+{7JqVl>Yc$EkSi++Cu3u4H~u;NODNa@!4V zP43iaYwQM}Z zBk=<=JW1;C$wHlKb6r2e?iAfrzikHEx!2;xTPGL&CzNRg`|s!2U7T7g<`=jB7pL`? zNM1NCy2&OQLol=2ggOkTyDOJ}J*9J*coTSW4&KpF_)rxxXovn72V>v*;DE0Ak5d${ z@CpNPY>j`k`MnO*j2y0?o{AFoV#KSqk*fY3)x9 z!2^}EwrA$Gnqn3wmZP>Q#m}Ob@Qg}ZT4g1hQ;EujIANhC5Hc09Jp|JgCuPAXG>uD5po&o zyrD^Ht_ktVufyaI20F$2Om&K4@ptYYmhgAVH#~fc9_q)*^}bCDSnVaHB{2+P-^)T+ zK_qbxS)wYxV46%H5nxoemS7uk^eF-LeQkc8LzQEo;tqPB%>U-Z2XJD3K#dXnWbk=G zZ~S%iP~>~{f%n-JVsGIjl@lW9njf$Y=IY4B zIBbS2Xq!KTkO>?AH?C~HHU#zF%x#|z~ z?p7&2RPt1v+)qy4P6)8ccXWhVx-dFzo*pXq(e z;T6|L1rEvqZ0^pH%C3w49@-;xG@@#80+-^_b;=K}RLc(WO!25_c@Xgr-bw}71=4<` zWQinBTt5d<-g%_DWN-d`gUoB480c{Q4x+O+rZ6MJUI`DM|8awJNNv*K`qd_VNhOi3 za|!lB$#ML!o+C-qcjq?P^YAvi@e|&5^fVu-0eI5upGrLxkCosb<#yujl?Zo=+8${F z&qWEz{h&!J4z}X0M@oEMjGSD7*jYqLEX{wLbDFU_CA?Ao9aHUAnw?8rP z&|H04sj9`IxzcT{dZZSFPMRQ}#hlYT@Sebx-G6Jz(lpfO5gvHBhl zaQSg5w&TBfiJI^b+EAS@@-vc7*yx{|+>q|j<%7EAwbd>1&sO~G*Ph);W zhhY#wageEFZKl&vIe1x=4{I2W{D>BF1Dj_7-Vqcey|_HP416~o6^-5XcQkyxobYoP zoZtu0a8&E{W7txic@TWemNveJv+c|9Bd-1+F z?Z{wp$K2OVb-hhM1b!6THKxUdX-jgrv9oU_7E?Ig%3Kmr&xJIR(7>TEg z{FYHfVl?=P$d!&NhQ06K3-Q2lV+EsylLWfW_r-T<`v*Hpm%F9$gHn_>g$K{@>9{ur z>$fbL@977y?RJ?Le7ohy`O#~L4zBwBkp`^`8jJlOuWyI zGZl_+pww~afh^w?yLPG_Ss+-;1GgXkfq8?}|7{fyqNAi2w{P z*iof+*te^P;rjN4ySKqg2%_NOKO)MGpPcI`rm$zq2|6=aWGHcg_BGzYl}gN# z>9BIdtMKRXnD=K$=lv`Qj;`|Jotp5W|0m$PrpC zU@G1Ut{`%XNGQImv@MfRk?eg#vE;C7&A~<~^`6eqhGI6UsBIHJ*9ojGn7T@_y>A#V z2?dm9xW)czFsZo4?+Y(6@&=AjpQ%m^iim`Qx=MiA8ZqUkOufJs8}c=EOtvp%T>JLr zm|67|ogimDMwcCKoU|=!lP}E_X}Lf<;{QU{>acvi!Fskr?^x+>T>L&21}Cj9oAvM6 zaaRR4Ob-OO`g48~i*ZRk`K)~dU$faozu@`gS*K4MoZyG%A?PWcgZZ2CPH7aqvtnnTE6(Z*C49q$AYj3phvBw3juD{e&Lp ze%^x?8L9hGLiNH8$g9odClyq8z&vi@~u}u;j2L@YDv+l-c>1fDP zXux)f>PtL_3rxTK=0~y-QQO-xrWZXvq355)D`M9=w&rV#Ap3GlUtB|+i7AHq^D(tpaCFK-Ner1$4S)Rd?EFLUWiFZeH z$d_i0F^fa=4yta%jv$~MtD%6j7#yx(1xm%P2YAj8OWa7ck119ss_YtfA-W>*3lX=u z=84MA%N#?);YguSv6zmOR+d>8rJMXVXwURTGsFy_;?=V2ahb}_OH9{5B}11wvcs$_ zsP7LhQ|iq&a2TwLscIb#Kj1Xk0A_vv&Pj; zse>&IR(-MkX>hHIRLn8OK3`~$AB%lHpspZTxq7J11`yk*xS+;yHmI}Q=)+ZSX8PYN zwGpM5NgCNgwMtHk5)630lqykC1-%Q@wj2n~EZ$1V`vY7*_sjJk%=~va`JfWSv4gqoU@7)Mhru(A!c!n61D^vL=|NghhQw$s=uP{_T+EQqYzpR zcS#96z0Xm_<4p&PJv-fCFiON-iPb2*Po9kco{)?j_V*C;BmT8~MX0GvIm_2H$%(Od zQcNA&{#sG!JV%AA9||PnLNStasCsy!_dxzOD{c8laD@`>SlHX$ zs`?AhzQag-sV8OpSWCnWm6AzJG+c<2ZZ&(xNTu4b*VQ$N+Lv*1Sj%?oA!%NfX$bEtz?!? zp>gFy^(enBV_Z$Xpq&=An{-&#-EQN<1StfR*Q=9wl4k-;`R8aN_k{FG-rsBgEljr1q}NM z%^BQ5$)k%w0EejT{)Ct{KCbD1CK|ySi_O{cOFWLO#cc_9O zV)_sN_+6hF;eEbTQzp1PFRDIuCWoy?FgKO(i2RR!>0%M4DNJxm->61>fmZI1`6hek zM4Q97uBO8^9se&F9xz2SZAtq^HRS<5Dw>8oQnnCN#gFJIz9Mw|2l_sM)OfMMP1;*L zH@}Q80=<^uo6s5gWr+VoI=oR9K)v&Y+ZST1hbqTUu0G=>#K{=3m%FGI0-V7ILr0&`kz@bD9|9*jhV7meDbMKRY~7hS5E!!1H$ z_W%7a|J!Mg_P_Zh!B5F)FOkvoHGM3y*}jex!HhfD2}?TU493N3)HK)at44S5J7Vcd z;1m}xsTi|s>F^`W(&i+S$Z+h#D7ff2PGImhv+WDO{#$zghLATIpf2$f&#~0viNi zaiJI07)w_z_S6PK4>p6vQ;g<-H<;O1Akc)_89{3i+(apoYBqIPVg&50V1Vw_)^*-0 zQOeoCx7DV>#Q76?H|A*R7!!MMl{QbI@PL9usN zDSW=j;ZX8%0*f2(($-DisnE^R-lUfeRESuN|nGJ|MQi&E4>PwD5C3%L$@+h>g`N#i` z)NUvvL75glsM794g11oa7yt(b&DXU(I-8wV5ZPfRX)p^2yjhp_gTsl8Z+sHX0Q$Nq zk_*p-(lQ>6M1|TnGT79v+T@JzO4Yuvpj^T;Q;-V~-(i%=NbOZiqP}o+=QfDl}(sfY6G(ZI$A8D}2rLqW9gRn@Y zSg=*|h3P94-2B4JngZUc1H`S=Pi1^*U-K`TX)ALefQv+$;h1R>HBWEw9!kY$8(7WA z%jSDHj6Eu+rgciC7ywv<~w!beo#pBlk17TuJkMUM&`Todrpg7>!cEr6y?8 zu`q+mA_CgijH12f0|FbGVry_Cun`>31EtfQqCwbnCD06B5OjaraN)$kH;jq)gF0b; zF7I+}mj=Q#Smb&=qoE})50o-@)Z7o0Fp+eG-q_V#U%=s7BNY>Bec84*-fLiJhoD{e z`2EjhDC05}vWS3+;!dN%!R-Y8UJG|R>(8Izd!|rxaK9xp-Yb1h5b!`(;p}ft);KKP zj^_CuS__NqYTejEAhIrNbw(BE_e!jWBvBVeen|XWNX|r)OngOZZ&N%Ja3}C8V#61L z!~I@qvp>fS!~3fLi+cx$ofNXFjhIr+HqF5rlvK@#NhAc&RYsg#%mT^li&1aNy8@$p z?idFrM)L#+9t@d6`ACK2;I+lr&Gv`+TpnS&=-5TjbFeyziAb4SB#RO8b~N-cJ)pU8xg2fN`Sg`h(z*Ei_}y zT?`}R$8+;K&gjYD{EoAAj%<1px~LynyacbbqS)7{nyG^5zQRL9yhh&X3iAk>ty*X} z26t0W*6Ahq^t95N_0vn?)A%82xttG`dV5c11TX2{!R>T^sBL-O5*e~^Fm`SZl|nOg zX6i!Mqc{}|8yO?qR>)+U&FolV4p0wXRK@8SeBx`~SN3W#KA>&iT`XT1PSS4nhOelq z@pXAG^r@3a9hSHyw1d03*aX1>l=!n5a>Kut+M0{F2rf|izD5vZg=}m(o`@w>yv3h$ zs5Y)FbFwIuIS>nF2M_eJcgH#|?2HokMV?{S)lz!`TtE#qKX{!DnN-aVc(`84`((C$ zERb%_k8H3sAIErAvF6nm6Hz=4wXr7>lo!Yz=J9ecdc2gfhZrRzesh}h#Hz0}x2sm> zI>@e0X4O|-*aJ@F2kMEzg_?$8VvXf@&5R~C>kKYZLN!X|-grgdEHOrELpS-?3ml4e zujcNtuYApBl0D{guq>{Bo;a^KQXkXFEMGoQWE&9M*&`)6v~iz4fIU%A5HIe~cjT_9 zf>%{4lh3wVFSXRwVZT&~X|z-@U+TJ}3U;Ivmmw%CshwP~;KbDVMqgBk?G!yDzu_xT zsv0bxrC4ZKVaw(!@tswV>rIueX%kwk&DSK;KdQ<28dr(8eFZ-xTPD_fRP$iRmHN`f z>fbB4K=}d|2X7Gd)%()d^cx&eU^A4h1~N8wyVTXmpHe_NjpR{4C?vqpy_yoLZc|re|U&CES<7 zo*^_ZUDarT5<7O{x6%X#VFrZUG*yv#(}7|I9hmCrO{ebtX?}-P>D44fqGdf>B-kWL z*TWtZ*Df*X9{YM)L{y#-tWhsIu{%Y@DtN34#t8xj517ft!$(|SGPzrD4^O$s0>#58 zyoXe}0oV5Ir6Zu$rlsmKT;wQO(R7Ngg}5weTa1@hsvUntcO(Qb>8K!{VYkq30XB29 zH#e@$_&m{IpS<0bW~eDSxY|KS+Vb4&|-M!u_l?K zy(NFhiNNMNbyV)6Z5CRD&UL;6akWL%cU1U-89(Obo$B1g?ua7Q)NQ9}h~<_Mu&a)G zsCMaY+&%VXyXWhthYAm(!eOvHIZhqEWcLHc$sqaal5)-AZLh5ip@T39SfviyBi0LJ zV$s*mYmcu{3T6>8?e4@2K)eVdA;lmU?U!Z#f*|&UNXpsgn_J?7i;AEu# z0|^L;f!rLEB)C<>?kMRyXYDv~_i&ip+A?4|AzCb?qnh(>)v`DbDz#BLyo9G;n<*2= zc1a0)M+cj)%@xN8%9dA*^AdY@0JSW3Abg!}Zw|j83NR^_9L5nA&wumdWJ5(s%0vlo%R1gBVI)*6SERF;6M_%xQusM5%q|DoA8U$P=JD`_+d^zIF;m^n! z@b$VEdr}Adq?!gWSV%3Hf$sFh9CI)dOb)=5g}hm2&^^vncwgq2R|JW|Bv0a{uQn2z zeVN?d=E*oqyXg}$mf)G=TCID-m*}46MmJ_ z$D{Y{AOH7!i7a;7+V%%&M4{EXt+S&PoNmy;06FoDEeTFc(mdR1wvV%+i{O=&j&~$U zKL~Z#3svLZ@j_ppyX%FXhUj1yp4 zede(xhGT4-i^z{J{SVB_zz0&}cTmC;TFlfvM{Fv`%SX%_d9G&r$ue2&Ct==a*Eb5? z>+~+D*kl5J@Cr(KVj_Kl$wXf$F$mWvtdf^!w4t1~uiBT^B85P_tUWpe9EPxj zTLEegY>Vjo_}W@=K>U;4G`h$23fo;SQytj^P7pmL3ns=Y>yo9(1ba$#?Cnh_0PzAX znWY7urZERMQ18@ER(J2`GN62w?Eulnc`>dSj<6W*lpqUhU)ksnfynlAQZLKL$EsUs zhhKwncIS^JOf6);qo$!o$S*FtldRE52cJz%;2phQzW7$Y58B+XD87iHLU^xhD_-2}QD5+BjH&7omnN+|rEwVNu6-_#&!zaBx(xg)`$O~hG zG14QK9-#Ng&n)qW#D^r`fBBF91C1b%;SqifZT34sd9Ah*G$6K)lfTh^hYwTqVe(!F zsRTx^eZ~A3*`0fBpJ&kzuhf_;w&ycO3s6yYF#0qVh2zfHR8%vC3|Lb$8{J8$xHi96 zt&A3QI72-)y#fP=YbV}ni(Ku43}1YZQDX?Pxqn&o0JV z%RJgo%rDpuq#Cd8i^V_wPqe>kXCL}$r#HndlE|-1rgm8jk9M2EZ;8_QA*fIi%L;Kn z?kBMcUB5ViZ@&je)|S-$6BWTHYY{;gDV1=GlZ*7S+c)eNnPy@`XqhyAj7Y*0wQVnZ znElZEsH5kIc|_}6Dfn9Y^>*Y(Cq0@~!+fUI!}7N|+rd**lg0U$274LfMh+wRgv3bk zb7^^J=%(t`Mxjb>{;FE{|4M&=#fAfxD>Udxs?Uj;VvW=qQ&_B=vA!_eQWahdJV&$E z<%9uWhwGFw&c#JZnrrYS+(`v1TJ+sl>vMI*S)7~~P_7==$vOV>rdsophPQb`mUB@G zW2bs!(bk8nScXQ0R~~86yT#tPtBI#zCsZx|eOVM3gf!(RGT85f1apv`1&{oC1sgcH ziw1=->%cS=qJALQ-^6FegzvAqvKc@)_2t$Ut1}AUdKx@iXI*^iourfAIWw^wv^(Bp zSNLv9mE+B=NsOmfE7+_!rc&ZUrM?~deBm-xhLKjj_*zwWj2{r=%_Paurk6;oGI(1e zjvCxbYk|a$$mjIEStRrmZmxQj2Kl%0x-7m@SrrVHh)&iz?|psA8{HH7&y6 z?OH^yHYF7~6kiK8Fr{b8Vt+W~ghd!0ZlTl)=fP}^5FT|Pi<^&cGmux>fpG`r6OYyA zFuPr*6xAuCrsRe*7s0ewkDgIzNcN1sA+m&(dm6CNf?j$Kgxx z50%7>%AZbG3;`OP;0JVMM0mp_OSE1T`V5YruWGgG z-fT=c>M$zHF)8E8pM=P8UwjTvK{sRR`=Wu!@y!^)Q~Z#lgY7oO1~*WdC(=5r@}(Xl zU>rU9zpy9sX=R03_#R3|Fv*JzVz|^Pi6xx$85R)cfUX)YOgvmEXopx!uU6%16m{sz zRKkg4JN{Zjqra8QyPTZO0K8{Z-@&s~l%=EVt1DP@Wa0+1p}8M5-yC*^8>l7=x-_YM z^`$oaZiHPl;%J5=1-kuj`e*38Gkl9U)_zaDYmsF54& z43OZw`&wB!pr6rgS8XPTG3P>_*Jd@Zb%Nr!bBXl)GwwZLBG@dm&7xmxTc-^N;)Wfs<|b; zveV>y!l%P%G4b|S@`at*V)MMJ2AVWP+;XP(j(tJrZm}g?sp=Mc;Lyk>o+Y;Hp0yv* zvdd`0HivFolmhov5h1<)rl_~0$Br1x{<9s10!^`=02@&|-ss7M*0|g(GwldyF}Abj zaf;nQ<<3q<3f@Cc_pTSJjJep%?{BMb169N=cBLCWvq0h^c%X_FAo z{QQ~HayZ$?P;L1IpJD@J1XY9g;ur9ZN`p20OgAvBK#R0?QgD5VWd?N-L!NS4T!It% zLAYHWNY%GWTmONG1{STPhQt9hwHzy(wwN9>Xca;3A+A*ouA81;^}5%4|LV@Z;6#4t zqwmVREY^#?M7m*EIrxyT+k?PyyA%3C{0orQYA_z^8uojmUp<(Biahr&v7hdva4$}su6<+sK^R-(HuF(rC$ zpjtdJ!ZaxTY;K7!ixg|-pNt{mVJi4`yG&_p)Ei%2z^0?XGFW^XHkiDeqPN|sKnwzc z3vHmQB%g2d-1K zc9E$q4RfG#6^q~^r7JePpwdr_X^I$ogrYq&gsrB^A=x;zyL_P}f=;OT8`eUv7E3EZ z?+=K2IQ^{7thz;$_f)4mTa1=lD>OJmrji|;+WpMe=>7ws{P8WUOVN-)qpF(7` z-`|&(T;u)4{wxq8@6~G|?n-oTqU!2k+~@B{Z2n|y6JJHCY$yMpuWZaZ1LWi_)c*%8 z(Ju4bG_Z&c)%nq%BA0xTliN6$1Vw|r+HB42vSW-sYNx>UFW%;ohZ^%nlfrJfeB_l5 zg37i~&7lfCgg}2Cq?cm`YaNDTm@P-s<+4I>)O1mKaWNP-6D;ros(20&Z<9A51q$|W zAa-oz9dzRKMShPR(|2(22k)SXorB&5MGF4J6)~IB*C>wA!lpo$EgcNR@e}ALbEfmV zY5~4S{nlW|2r=g9`56hd=HU@#=uo@pWuucZS1W&Sd8!g6hI&44*we{?h{ogRa3Yko z$kVCz@irI{22&+?UcuYHb%|;v?%c})hd|}uj(YH+QYSj}JQgTyBgGI01tNHsCZuHf zyqa@w42YbtT0Npn?z&%8JnKD&F&kOrksUK(<=U1*bfodH^Zo!?JTqzG27=HRI-ZLE85dIy694zakaM8CEiO45E5LBYP=*v{?)BR`g7ME`=9Sf8mB)(x?>cH7G zjFT#9_Rz3r{ym@2S>|x#&_~Q@ABK?RN%%i!6kEhWDGgSubeAI6VUlBXADw;pyhk?RliS(gjPS*jkbm@%Dr)sNezG(3Z&BPZd`zAj#Sn9xC zPnni&>I)b9PsOr}!$KX=;M(bdWx0P5kafL45ivfb^E0+cqr1>CtjRN55!eL zW#Qr!EL?sFB$uy2l-V)GH{ZM0;%K2!GDQwHLvO{D9_BtrMK> z7%s^)x$M09UTPt%0WoQq`K&UWfr`f#9db;|xn|dAyoHL~L3wvKD^l%VVR2#yZy~&o zCM?nxDiWg1lqj!mzm)6cnTDP$t|k99TWR6(?@45T-p3LDT3tt@{JFeCNRk+w`eKDOV- zmkdhaQ%Xo_R1p_VDbe+|XR$$o{Zt$BAV1MyIBFKH2CltYso*HzAPHx#m1w|;`N6iE z@p~XcU;`x}_QyYf)!Wx4NAbwOW0Hw+Gt0N{y2Op{>1i zP3?);9-U_0EFP#7h$cD0y056@JB-xoD`eKrgJ}l1QjO9xbQO{`NfvGqt1=MdHh!9? zi(GxAw!}!5MEaz5tChCtD-hB5b-BJRmkfdtV|eZm?Y(S8h4FVy!qj75e|UqT%hEEM zHzAfIDq$m$X9&DM#*U5>?P>W~%+YR!dtk71n3sB@ZO#rq-Sa<^=i%!QZ}R1%@@)em z4wL_%wKrRiMQ!C`=Q_avEI&)_1f;f_KxuIq?A-!-FpiwMUTP5_dNA9ucz}kO(r&J7g3@Uf{mAn|!}QH8%;{0jP7T%y#a zn|1U3H+>~@r|$gePJ~#ggwEjEKLUFv&hi;L98W=VuDKSz=!c~ZBM}ZA*&Y2?j3f6S zsww%6{7p-(%_gnf5RK_jM^MeEY7(PR(`|3CmCTY1r$JALZ~Z?0#53%1h7LOD8kS-? zR&h8f=647A!3Ce0_!f^O#2S4twM-p}%!docAw70G#rlZv-brm*5k7uu*Hlnrzrk{= zf6)m=oLZzr+n=0JTg0Eb(d|>F1PjNnGd$^}BbM=opk!?@mb8xtnv^H)g+5dZ?Ew?8 z*1t6O%tm)Qr5MSv+-rxSaJhgZE%aBUT#NzsC;4Qsapr7CMR!rPTml6zl2o{@s#}3y)FO<>KLY zjv24BwR7D2%Eb6mIo;70qw`6BNLP(ViPLCxeyDO?%BN(GwqSl+rvxF51})iJr*oju zPKb|naqy1!e7l}z;iu?xD%rRLbX{#o2E^I3CqK2c6q~Sl(!cDVtXDe!&tXg+Teb3e zY{KR#tYLU%53;~2iGyY!6DK5v#Fd}L4%CKjqHyYE7ka~IX;at_%_q9+lbs^S>U6u+rX+9<@#m7AR3xlU&9O-7>FB za9vH7hisfvELIUFjOzxY9H_DpgP4UUNZUvgr1f)zi>TU~|32ye0!xs7Acun`7_Cu< z&Zq*dQD1Q&KHIp1N~k>yKrB;Ta)22oNKqUAAK}ymEHP(%Em4axsl%rX`Zf;iK?_eM z{ws9#AVBe7ua}La8r(*OnQR)8wQpF|O$?dD>Jzk|+0m^X7gB;e5w`^z(krKKH3K@p zmz`s#^#;!k^?DO+uf{mv7Rc#)_gK(4cR0fj4J|rat%7chdrUWdoh9$F+DtecAvV&y zfX&97Ea&BH*;`UG@)*-0kt0*At2x+kC{%zeEkph$&OE$D;BE~CcTlSfJbx$`_scu2 z3?XjoCs?J_yHCG0tULUlsLD}rjGz21%b$$f(F*))CBaU$Tu)II!bQYx2u-v{FBv)7 zuVp)Kr&WN%-mi3^;!m0z?KmCQsJG@A1M!&O!+hp z*Vl?o-R68&O4y$@dj)$OT_Ld{&s5!%X|#BVTe>5N=^kxnoDSYRQ<;gtMY%d%F8*sE z3Wveh(KuhKFfrg5S$a&yhv=?t2^{3wuFJU%np9wQh`zG)LQ{D{rRl{jiom4zLW;bt z2vImKI@2msAV#HV)YuH~Wga1EN%FB$@09bC0Dr2#0Y6M6I-k8@;^1j#%$LHStzPz3r+o*N}g zXxDh?xi)ej*6Iwg=-^yTDs@(?*eO?crA{OF=xchc4bFr=3k32|F*ZQl?7A>`VN+%Hs(TPb~h(|0 z_<|l~(LfCRdsRg-4rl(US`7JN1ICxIo(UjI$Lkt-IJk(aqEe->uGVUK1)L54rf1I| z`+DV~Ypyi?DR4Q2t5IjZolc?u!9)KD`}Q+D@8|$CE71>f1&4UCLNiq`O5e zysXT5A)el456xUMtWrN_A5v%yK17D=unLT#)~kCcAun21P!lIy1q9HkHr-dO44anGBtG&+xE zd~>6gM0^p=M}48R_x^J7Ue5E2z=wfhgyV-rd3%q%i)$S9jYy&o+f{~k1b5}_$`x=r zwKF)7qKhku9`#T2ZueF@7&r`dsJw?0;7fuBS(W1na+xu#L+3aSH+V+iLB$YvPc8{h$9Jx9I(D|Cb|4gcCut$B|@i;E@Bj(9no zhTaTgQ;+;^c$Q0Y`I$Cnujg6l`ieV8a* zgeM-*htQczR^yD0w*V7g;-!|0fFd8@DJHiJP?=@ITli89LPU4KYP(DNr#x;Fxhn7p zW^ZF9$D3*{>2!SZx8rWc-13$o%jv^qZ687z zC0tMS9d8q}l~TH@28ea~`FxdwH$ZR_Cn&{2d-NSC34=E;HCg=vH!W5ajUKgC~fH^o}}9}#<>W>8EbCqEuh3HAa)4EwE>$^qB6Y6ruz z;gc62_;NFtQ==!|ezwabQ@!yZs{j7=dO2LJ%M_6Etzy!rsRDI$$X2{6K;UR z4K+c?ZRtlTN5@`8xNjjj)_J#~r6f9aBBfu7Y-$qLbMn@)uwu&&LQE+4n^HtO-1r)C z!T$imDfwuhkAzt*G`H;Yku6OWkYlMz_g`?Hdc{(w?gfliX%7%jwVozY9`eJJmiqKc zMLEc<5N8cHmALjOZW|KXjn#1WD^xARNkzlLVM*(x*NU&~b#@z4c~o_ZTc~2BMUQ>Y zm}re^-LQR~`b?fntW3AfbY7`Ndt72XF>G4uVS@3X>*^+Qh;A(taha0wkR9lGetbxy~N(Nc-VLeJn0-IqeCklOo zRk0-oqiXZFS1PNo;M2rJa_tgK9Hr#q(Nyp5-(Q!rSvlj`C)Vt$gxs{6&b`Xba9fqp zn4b(+*Jm2Z0YY<^(T%g_nR%`GJeWjZXx_d9OySI~KSqm`KBf9@j`^WfCme>Rd2a(n zK^n0N{#uE&Tg(qcA$UetNWhJc?|S>xrY7TxA>FCPcdYtTqE^T-SWpipt`n_>$P(il z2es-?4maO2+8|ZOa|H4}7Q-<=hR4}(JcbiKHomNobA0zW6Q41c5kuCu zN!RK)2d87`=2P`E1A`2uk>V-JLbvrIqn1_mTFKE3lSSq*jc?+1(*mHo z0Yv?QBSL4_y=#?j;RRK;p33l&^?Uu49%t{7VVigam8R^7^?f+v#4?S0F3F3!MIjQA zR>7^-la)zK+#vpXrPAPyRe1I~@UF~mDhf1UcvtX4J<8oo8QaxBF$e1ZseQkg0af4m7BzdjKbMHn9*fx`@NJW;% zCQ*Wx9maFF)rX%|m+tgOll&~FgbFZ=I-=r6s!I2Ys)F`?!2KacF$j`o6H>~`I>-Id z`Ps0mhm;s?CZ5cj>5RCe67ODax}U#IOa1v<>OJs~N=e@^-hS}LCPn5?e{!K7DpJ5| zI+CAwR%25jk2dg^(%I>N(0cT*l_XFB2(QD{lIEJnp%Ss7HYcNuR(r`jRbD8{Q*~*o zUnEU6%`2=HlL?|sFe8Q;ySTyFsRrTri$yP|l1y~EgR9$$N*XcpT9{kW?0JH{*Lb8@ z+M0ahK>PgH#z~@8o`e(hsik$E`~cSt`U%U(ar~`4x7ys-iZaLdFiYV&BCB)Q`;z36 z5_f^C&lk8vrmfXr`f`|}N8LT(tT#DMMg4NsO((?~BZ+z7JW8%m;}e+#sNtGu0mSgh z7#0f(!9+`a(ms2to{C@M9$lcxj)_&}Z?Q$IzU-`)pD^EC8{#;O-J!eApVsJLs9fyK z(eebci}N=fmIlt7+bl<;+i8&{i*s;3vt&?O_5>m*fKVDUmy<&At9@{0P5GmpJ>!<`oE*$DZ*98N@#B;>U?;&)tS~8>O$?vU- zEG9^}^l9~b1>CHp!Jexe>?|=(0fR<)5(9Qm-aP+Sc=>CIJZw-WJ0H)UGO}lKbmPh6oWcf()An1c>xN2ajTl zW7t5*!FGPTEl#U{1f|wvbn}|Jd0?(jH}YZpN6w@V5}cmM#6zNKa~rK9Gkj64HVc>? z3ejO|0dsB^6`v1kC7GB{0e$plLd7^>J9d6jL{w~#E2sc}yZj_GX7vidnf%bagdF|o zE!0=!iS$t;)Wo5zAzG%izNf8k1)TflTv*xW-mP%3QR*Y=BByc*ukeA|?Z6OJ&TAYJ zaq;nMj>4V!8&==>{^4K(PtYS}HF10{6RfBEEe*o(LfVUHFE_0fe4Y)jT-6W75b&<- z<*thAZ!BcOIxkVYHMk9;IZM)r6i7yQA~ba7;--+)dO! z3~Du@%foaJa``)S_jql|TGt%{#)Rm>;YA0;A!0y_y&L@)KMlMd()L`PVL^a`^v`Af8owetV7YpjP)eTpXaW z*>p?)5pgoeO==OgL&nh`@fbLr)wMCmT`R@Jb{rLS+&ct%!tGr?N2Rp`9#{Uo* z!B+H%E-_HKjR_N{*q05Ml#Ga~oSm}lGGHIRGEPi`ApJs0vQnc4r%FL1S1_mRr26Dw z_q9p#7?}+;|Ko%9}l$o~D`+29EsahqJOubwUlzhXgt?*NUk1i!a=l~Um@)q9MO zc$|q?(tbB>+1Xc@m$Wcgm(`FeZNRD#IXlV`RP({D_ERRNFEROx^z3lPQDC=KlpQrt zV;TRXm@Pja^V|iDemj@~xlZJ`S>5R6&^4h@e*$phQ>Tv1K&|9ImqcULrWZ?e|3!@e z4~4^0G*E4psbOb>H#Uv(9h7^?^P+A-e1m}JhY}UlC^AUSLB~bZV!puSC~UIMgTIi; zQTPF02{t#2bfXWXs`OcLSNszt7gAQi4~xZB{yoZ9v_A41dKQjvW8*w zqk#qq{fDfFs8mh=a=!jh*Gs!A>0_|cRaTsH=ZLd+i{&TOVj;1f?Nwv9pbf8tJV8*U zRBsot^k+&GsaWx<>vo9j4r(_J<5WxfN7lKG%(H>2-lGax*4y`e0#MtxTY|esJ~;nENE6{8QiQ804i4UM&xG8JtoGH1tI=si#{lgqi zEN;2S#>gIP6fFyt9~>cDzucHc95Bvw-5yTvo>ttAKNPgh@MrShsk6iPVP46(7sY5v>lmzTzlulQG!!L3g$}6ki%!e)yy1G|xe4m8&rGrY7 z9%P}0e$S^HG)LAE5do@fO|8}X^L&OJ)Rn5>0e*F@(sQ?1&3`W|?eHczlONX6S5@cP z*?4>+1B@t&iCu+Wjrj|FQeZX<-@Zv1Azg`zBcb^RjqH4dLw+8ZcxGD{c7hB zJM4;7qN`}fP;`x0K5Wq+xkbX4VDM|W28?Kv-Q z^?_ta42`h%{Hde$R7u$njk?shQy3!U28`}{1*Kt$jjwSE`R)!2A%>R7b*on1U1@lH zyF@+GTr(*g4q7uZFRw8EO(;w#50{JH-zc+MJK7`rgqNxI-U*6&s(!5VTiD^W!X1!2 zg}V1to>vL*baW}iwj}`r%WKxMW|F(Cnzh)_s6^3r!o+x7XvfR@AA@xqy{V=-_CF&L zNKdwaZz@=^|ZWkAzoTD#CD>BiY4~U#ReIQa9h-M0X&dZyXNy7q{nS;DItmP zpcwgV3oM}qL?pldOAVHV@MO0*R(A!Q2e2a^NEaln`_2v zfZ2F-65UaGL_VJun{qy(|2$xc%dN}0Cs^*I!aMj!G#EyIiPELB8rxc-mGzD!JqjF= z>Wjl5<`6b#PfR!p+oH_|nGUb-9clN&i5p@_3EFzsOzW((1rdM}&@=&gQ~2xq>_4*m zqLPUbC>Z)7b7htPgl!1BLsmx2s8$nvKXts9F8aa-N7^-s`K@K!ubLXM98sKn-I?Tl8nd($OxqLq%6tqP;2$525eto*HP7Bi*L7g6^%^!dp85ITH$?ywxF!%O2Oj7gw)~|2^1+t zj3hMq=Ssfv-=zT$l~`g8uty6I+1cr=#P?7yoJYc{Wcpqr9b9f|YBwfUf_gQp0d?&T zC)}*EPBre~`I!^QBnDtgXrD$DsYB1z&)uy+oYN&ksR5=0v(*WYH)XZBUX{EXG4hF5 z?i0p$Q`H>hNmIB8yOhQO%l6cSSG2P_?hXG+gq1$4hskHvWH3nUMY$oVc$8p#Rw|5Q zG1qT_k4WsMx$H1~Y#=Hcs^W&-rCJ0IyU*_~5IRl78`R_B86x}3ig7(kJ+2a=`|OCP zk=q9>&PQaPBYG6%T5^|Ey!OiNMrQrdOHXWW*k&s;96l={cJ*=iHdOIA6)2c{ZO&(< z#HjB@&xOSu^$%iqirQfbxvkE-85K9%xt8#I#J6ppAe)1V89Fjm?%_ivXCPLJi|%f%GUj#K&K<&=CiN_<2R%3x=x)o=+{@JPUZ*Oq-IME8QZOXEQHHsz7_3jh559YCO$xntk0!$Ch9$$E9D-4#pWjVq$=85GK z*Z8*_k>=T|RRm9ABS5<##g7B_jV4m2yHy5XK~^o%Cx(p1VchJ4F9>K`{Z<+b#*YU- z)yV@M{~^&^QLr0u1xZ52PE4W~bFj_nUaD(LI@F*?p*Cg<7`ik1C#Al)4OTr{&dL$W zKxr}w&g2J3+xAXt>;2(+rj8}~TaWclh1N=X&)0~L;iK{^Ic$V1SBONIRq`U52QTHR z*133$tw!t>Di;O=&#+lZwdAGJrVJ{%L5zxS65=Q$w*45Y@`}DTf?tBWCODHHlAGOU ze(0}AcBd*Xe<%O75+m5dr-x;wo^)cF+qc6Mexi}lma>0%F=#S5i)!d?D`R3Ddxu9ZC%xgsV zRUHuihc*g5Hkgi2?F~-4&wa z{z_+rgAg~%2g)6fQitZ%&(!fwt2^k3{O}&7zNQMrt7_aA4oj&jxVbAyYI>yxrbxCq zMXCc|*%kFJOWTAgX~BWiQ!2sH8%0f&Sf13&C6|T*rwEQ$q`JK7Gc^kd$Kv_s!v=_J zB()?Ps*0TKvcfPss_hOVGi-6={8Gu)ueEt1ye!~1kMDWgfV1!3-+iC3#b0Zvc7o*F z1tJz)=x{yHr+M06>qwe&G$hh~R}Sp{BvxOGAq}w2r%S#i@x2Iwz;PPI#t`2&PxX8X z=Vi6nkhX#0`^OI=ywpx;YY(qqu&+YDK)Xi1s{>zoYL$IXwZ>Ue%9ez6(Ie^`l{SrE zD~+DI5(Mq06kv+dtTLBTEJngKiiKnvq)G@)ZADngiHts!xquHZ=G*m|j!vF9Toe1r zM^M^D5?l2dH{zZ<+`Kc0x2?L^1lAJ_;E{k9H4lgX(8=;zMd=aR?tXqeQ`^pCzCe<5 z*!0!z{S3FhUC@aMY`2a)Y*w_3hD#r-#dt}E<*~Z-;B>n24gR7{SGn5nBJmj^Fv<{M z+}6d!5_?sv(hU)an zjrPJScJc1(HC~t`>#*zq7#*x%P+kY_|K>*6jMfvxT0~poP7>E%!cky+0UJ$)|m zYrB(>k~T;c{1%x(k2y8#fH_e)p;_BJ)syN0Q+GExc}c*W%phV_xGK7UMUSplr>ij> zpFjLPxV|0!`xv7ZVVu;&fXh&-5avqmos#J(^7pr^M-4>=>^9K3yHu#OO(QE2J}*uR zv|ZOxK0&8v)z8^4dKZj&-T)f>_1-gz6zi~PT?!*UhSZ|%Egp}1PGet*UeVA+3 z0`X_bdq%#e%?62bLvn4git5dO(JGL?sbEE4SV{974G=fDI;D-na=R$b={`ZU?=}wg z2#@!vc&yc6_j69Gm7by!$53x@NEK&_1T zH_QYpkcC8@G2psH*9hqoV{TAd`1kSwl+euqxg&BvJlrTXeZLVCAXk@>w>| zkQMNXFey=|#lVciGDjDpA$eD&S0AQa-c!#bPOehr)=w-OEXifU1T*Al`11J+5YZA} zqB)jJj!lQ+_xD0uCgIQBx`|CpJwf*P2*wj{Di}u;F=*Z4bhyk4de5rm^$MBx6r{x2 zND>0R@(@a`(8HwblC^F^rLFs+U1BY-s}eIuH&YocK#fc=C>oqKh|1=twL;yC?R;J0 z0MqU3a82n*>hayDbZd?CUvrxB@^|XHT1758hha8CsYZ*#NIyi>z?R0G5X#->eA*#cAro=s?;qM+%ZhRP1^PstmCwj*9nQ}B&wlzz?R)RzvP#Y^vp8>}q1sNwJbSQl!%WMJVqu#Jd{EysVs-%MTJ@ z*7?Zo3(u>B77_~1siiQ%02X|)h!&}y3g-YAYky9|(c}7=Nxq;8!Eh7(hP){oc%^hh zE0#Tel+OQVc6zKNnmm9;UNTjnnDszfe7!l}mYXrF2hYco z>^u1-LyxY-Omfjh5^`a-kv9=*$RYKqdM`-rm(_!&-OJwz`^l!R zb`TW!0uLeeXX5+RX|$26Z}W@%>~whcU&4oUIKvOa?)mA(`$>P4jei?Y`jW?ao^%#k z?d_khdq`2zgHn2*$-^C}*jb|-_9CetN0QkWR-Ah*39vXo+((Dq0jeE7-N@)&YSwW1S=bnbeg057 z6RU_8sAhdq&F+>Dm4>H?Z`$HavF1 z0b~`v^28{h)2tO@tlME4SZQwm19G>kQX{j(<~c|)OPj>ntrpV?ErBT+i8qxGW`DxK zndB9ec!lc4h@fglObyh%MI?9nO1_GZ z+_Q;yACFRB|Fl*ZdsM=ugwEtN-aWRZJN&)4gzC>ZdK*kp1H*D4V&r+QYw{D-^1p10 zHMshBdH~F401LveHlFur!(Zg50YQ)htWah~W}lrg?N9#e2$|d$gNVV5NRC=`)21X&Q9( z7{_rTQS<2IsLg!7dMNJDW{r1rm^aK$o=l$f8WIk7)FRm~iV&;qJzXdPx{Yey&Z=a) z^Af}TmY+#D#Jpp)PRSP{*o^|9D}y*eZB`VEqKN78e*8#L;c>VX9y4ynfdlkZN%f+b zVcW(((%s^5HvUmhil-5)em#Di7uOS;1>|s@NeR!4UQkK%;`q0#KD>E6Iu3_N_2a}{ znME}ITAo;G6vu3V7he2lQ1OXt9ERKcL$-cc&YxuKZ1p5-%*c@|D)r^4GnE-M)VVx% zuhf=WSg6#;o@f6N9wlHt2^=c$?#l#E_Z1Qx;Azg(o|EAMd#e4X1KdrGPc?t(&-aOw zm(~QP{gx^Di8>G6KYJ!ZS@?K;&Y%rfujzcF~y6CmW%J_wN0>xBbca8Fdrj>}@~8 zFGm?h1)uqyY|Ous^-6VY*4Ap&IYBdjUHt722d|ayWK)jnopK1R6={V2P-JeVm z5NQd9J%_DLhAr=%{Y1|r<~(?N%7;NDEe%7<%9MG9cU4jyPpf;(AlD}*{>l?8&CZYy zgrWkSGer!CXM3Qd926y5OjaweQA0>(>ajN|6JpGYP~Vlv-9uw(8j=pXP?@9jwp2NM zN^PP8FZ7+VB#%<}QbIjNxE|>ln)XSYEHHRlt3d5+E0>{7Q=2&JI7z`T?7yg$ol2kC z+2be8uqTFhylLY^7f|x03d;~Au~%uMRZ6sba$XxD+bJGhfg?X6Fdo!bxF@K@2FdoN zP--<-i-9<8F<6o4RfRshP_$`s5+jR0#Qsv>Q#DcF z;a1*};D}p}kd!)LvtTF7d;HIE!Kd&T500~j+7PgF7=oS4=xZtEpiO02+)vc^v~dpyR2%MbUF=(CDWFhqpy3w3l7H{Uq1TS^(oeTEHF9Oz909(RY$&trw1 zd-XL2OMI^S<-20KM9(Un>MB@*y)eIBIbd{06ILi3FM5SeMua-CC=kekwtC~keAy3e zwrEpfU27_I&Ll_mt*WGzrnJsXP=pnFQruI?CeDuY(+l>`fU54#9NZe-H-RmWUPe8u z)Rk44gB>T6Or4`ZvKoVy+`fd8>CqM$>tAk{he6#Y-ewp^gxqVmS;aQEN6RGf_cxyH zzMWHxPo(nhX#AV_LoVolIvQv0htSx|HAqX+pHc7p0nyM0)hy5y(_zQr<6&+%9#$gS ze?n z!X+?bq1z$r!)C&zl@2Q+MW+GhJ*EmwoZvrJG}=3uC3+xkHW$=f0(Kn=5zCI2sbn1^npx(HNBf>0e}b4VqeV*VBJtqO z)mBOl<4k(bQoKy*d%tCUW=Wy@1|LKkama0my&m>Ldnyc%$9;AOtG_-6MMb^o`Yi=Igr>3Y!+0JB*D8rA6%> zvD#0GfuMg;?e+{kY4I&_Uf{%3c9@k9A;5bYC?ALo2f}vvkhCL6e}6kIFwWg7 z^91wrCU}}nV!b*!LUV_FP5E~P7Wf3CyIk+VWOrStIyRLRGkCUQAyUmEzJsfD=MZ(d zg4Tx)&yV`~i08?prdKf{v?aPA{Y|1@_i8{BD~PeWen)0#D!ASXLNpvy)m(ni!1< z{RO~>Th_7FhURCz5&C`{)9)8>cKlm!be_-xtS;2GW5k$tm1EM|8yIPTt456mo>pnq zz3RbuLmoj4iw}GL_Y=pQ63?py%3KVp=fv|Ylxf152ZkfP4sk`_y$sHU9i0C4E7qtE z?~AE+m~|la@~^2{^-sIxj`$Oo-w_*pt=YRMY7;${yA@!H%Ib4kePP=_f(~LPA=MCx zzMmG0EqW<#{LENUZlW?Yd;nEjAqNm5nrKG2$F+l=d?Hm~5&n1XF1|c5dfgtKb|U}3 z6@=y&Bg~j9SInr%FVM3@evohUM0k#?-wQP#YF?2F|SmFEN2wRy>^gdQGdbFHZ#pZTQg(6_nftzgN zXLZQ-oQt2t=F#rcWA&h&Vks4!M|_EZvrv{>I~Yx4>4%orz^X(qp~f#iSWf}s?DR5v ze3j4_cm`psd;!6D{2HH)Rx05iUG=09aiKeQ)q`l;kqh+{lU(`H!OPdBT3H??8qQ#e zYQB9j({ENwDg?ya2YJ?J#Q$yeedJI(9hZx__6?@ zQ9?S<4nx^gS2S#v%=a4xe~8QMvRZG!!H^{N3H>=B4`Nr+&z(IqM5`(F-TdY=vSQ*I zFTg8Cr3~0+?K+mvO3(`5(4&?Y_haZ2xe3M#u0KZ+2|Y%$fuoB$N%U+ z)Gp~M#>ny?@8!#|NwWJbQ_oTVUH?D`k@^&|XKn7oMH-A1lsY*G$18V}c*9rqywHkC zd)HMZM}qQ1u%3}aNs9z+6q}VZhF*w8PXco*Hqu*-Dq_qMY+fuJCusjZ?D121oDF+zvQbK9 z42{k>;*cgbfG-(6Kn}92)@@bjIu1ElshZ(ACVoKH0ooq$$(!441*wxqDC79z`n7@i zsZ6Jj!XQ?X${Hjl{xIDicK=qQO$jDyC8>i7LuRh6M<6j$xbh#z(GzQwXS7_SdHrHT zdK8RP?IVNHA{D1OQX)hM(!nh_NJSDvv{XqMf**Q~WnmDv#e)@?VgwV^#NvWSD&XMC zSH&S66th~>FqUM<koObbPQk8jB-IWUj-FccEM$tocC#`3be9P)ie=p|J z677NKLNHc&EhPH5>hIk*qSjH;b)sv0amnzV!x?^<97Q)%QVo+pj}L-S;ABu4WsShA z%O!Hhm_)n#Q(57upRt!e@Kbr5xy~NKJyOS`tJ!{er}LxG1?3bl*cH-^<;U%Pb}2pK z(29pK?B=UkMe5OsS1kDDj>NvKDXR%hqgIT1K050oAD6M;uE)_8k}skJ4hFDBtI{pG zFY$fLSJKtOw!_}|f@*;L?Y295LzTqc@mpv2(kKEBhH&!1H1c|Ke*AtoKGQyT9`8^f zx=Mjps1unk?{n@+z!)D@6%sIUW_QwI(N$5Jk0wrTQB4B=11-R2xQ_3or_o}mIwbrf z?g#NWyIwPDM{Z`3Dq=%j2gv=Ae@D%iSagX76N`3TsqAdhJ&;;=epX^6+BTY~JbhzR zS-Y!8)c@uq*3ElaF{yWJ^eS$7aC7vk^}`&=CnnNbx>rx>>6=5$Ryq3Z zmrxPekq*h|Fif+|VMI@S%i-_pX9(@n+7&WS-&pY|IKUipV6SM12Ao|X-^HiPM*I1K zo||87ssR2>IC-pU^yJr}5bTn*s=XWd)W(ryw*%t}H<97&%K`qPM|2Y1WRw>nUHt#WSc z+v6SV+loNZ1P2qmx=S{4@>u1%^zru!t9#}h3FlS|ioy>>NGBzA8{)fm8B1Rk*TA^O zJh3q&!w|$KW%X6qp}c%GhgL$XfOr!QTass34JS)psZ|gQ+h+~SZo;ub&eJWdui~^I z;dtC~!zPb-U)@8Z3TbAh11X7ZC7>PHdXf#qGe#`V%QYOSt!>k0DgE^W4>bPd*G5<% zPKxfU@#&-r?#5$D3|DD8&D+X6#bmoF*A>+l;z+f&nX7~Cd@g}@zLa1%5ya~aHLuNg ztVXkcb?A5twFqEiHqL($S5jGJ=46Xt8}jpcZXNc05X^A*4i_7g+Rw+o9d`2*%3BJ_b!ly5mSf2+p zU@7ljL4jN0xNmX#66N}MSarPamq~5~ntP1RMAUDf0;Xo0$9 zm|e(d2{E>KyMk}B>!M|u?<^KwM3WNs(73VDnw_xUG9@@ERJmD>Ejz|xHGg!E8mcYs ztA3i(WjB|Wku;wKqv4Mv?W+OE`x*>8O0%9mPY;Zvo}_TxmUIV7Ha%*w7iAM zK!eC5%B1E#TN!2-lT;;mMQR3CT24{I(?hb1i>c-U;ZM9<($A0QQ-{k)x?EK6mbA=0 zZfBEYjZ$kqeveH1yDs@?z-Z|cdi)H}y3tp$qDz$CT^CRY`BjLs%hL7W)ea-OA6`lL ztaKe>wSTVQI?IVRScx{jD=tL{R|A~i!lJsm$x zU4zZ&9-1$7ibSXz9lPVvj&q}4P(%x$O)fMDKx{7tg{v8V#gi(*iLbDLW0=;0!|Ev} zONu|Nl3NK&R$A3i-*eb*Eql7-8dhY9xlY9t*Wk%=*sdX^0nL=$XSI=|!_D#*Et2j? zHiGf^3+Hui#jLSXh2U6PXMR{{G8yp-%iYALxyXZ9k;fHO0#3kE-j$_JSWK?A(SwyreZH5N6*qZ|_tcpgEoBnRRd~YUf7pV7lc0!` zDuz{ELeMQ{{X$)k#95@e;Z{n7XjlYy16iX-$Y>ovpu?e_iwrAs^-0rESgZmq*ph9g%UM!pbW;%-(`$;c}O8}S^% z&85P}eKMY5q$bJd&v?G@@1M~VlF!A^UGVt=Oz@Lh&E*#mHiA=R4SB$geq(KG%4#pW zQc5`6o*V?XcEC7J3;5fG!PL?af>NPOph*%|oq2)*m|_{y8|N^*toTWi5%oBE%lWyI zQvc%;qj@(abxgr{eRzQ34Fyp*HHvmV8#YtrT(6kAPV(Ft;CkCXeTRrVt!C3@CLzt2 zpPqQ<@G9J0sq}b>DY~#&$WH`k&V-RFJJd>!8=1pcn)4K(o7o(WP70#{qb_4upu3n? z@R3h%HK#jZ+bpPXDfwVyIrj_0?#9j7^pL{Y+kibqyxplaJDzD>y-))F8 z!GM#iR3dgBzhD^FnK}(URtA*NL#){H0t3l5E`i_&SR< zJ!-@?ii`Yl&uT@Nzn`C+2&X3Z6YN+w*tL7*L@W#IRbK_4#KgryC#n%QK6aBVt;n(i zBEm#A4K@XAQ%s|U-%??Thy9icODt2$Jp|K{A;|BVRu;PP8i2(BJISGhWVKnK_gXQzLG$Y`Yo1u)zr_@Qj> zw$*RGTQ1k48}l6n`!|$E-#9p{9Rn(!y|GHpJqmoWOm*MA{PI|+;(FhOO5uA%b#XtX za3pRY7uCgzX??&Men1pIIA!EWK5@j}rcJBk{A-s{^%<8bA3v2^sYo0Nb}Q+0`ZmfU z=;t6#cOJrvD`7h)JNi}gbP%IPUk$1qLjI)^G_w-^BX9@e>}c5U*1d1LxTRH#9-#ti zKJU*-(jM2Y0g-kOs+~p0T}k>7dpARDnLa0CdmYzJA|c0L>Cl3_^UftbP!I!W?uRT# z!VJhJA;w=lWyJRFzyGhBas|oqkFXv72fVw_SVVMpQ=&r?w)nLC_x~vh76wN-TOnfl zR!$o3yU4;N6+6}OV1Pbkra`xKA{!ApDDJEIa*!$ZBxKXLXjnlNPYh|%3Y7M{%`%(47O))tRVEP-_226w4&SvMsF+pP{2x0buGtY zC21bQpW;I~!Pt@ya7=oo+57$VtZS>0_*1C<%yR5c#O1#>!kkuO6mJ*38fDH6>19(XZXQA={(kX zUTI~@Z>4nKqrv!F>0|qekF3%-*ms9Nh7G5^clgu0qLPj}LIE{J0UTAnrQ7kA#IyOL zqwVx=yS9pEk5!{??pUpu^jQZHOLIo%&pI_Of2q(qsRQuki^TxBNJPlhoc`PdUKV$ZUF05wW19216lC{ycx*jkGk^W9kk!x zxIj+Dj7QC*lvVj!Y48%x_j0}}s6i1sAHYR7t6D)`D+m>}SF(Vp$`XSG<4JvpTgu2Y z#LrPYTxK`-*SFD2>)Aouerd`|_wE3Jre@i{svo}Ah!^&wS zd^ZPpp4^v??xxYpFB^@L;m?eoN7SE0D>UZ^VwDdR$Ld6w4lK+EFZ=Y-DGS-gF;fv#Dw-SdF;sz4RGv<|ICQlWS7Y z@wWJcVcn19eIA1nF)0pus2z8;om-9w8~^z$^e_kuO&$xlagpX1uv*P7G&m5{g0w~q zwXQFXFjg>vYb-3fjJB!$qI9n@+ncRo z0&X1L6bs9(k3zB8*uW07ngL31$=oQu_)u;hFO{{A9hS;;2(yGe*-`(}KK%}!z3HgP z?a!|(jm0}ak()4nt#DvK3XrBye3h$qxgTn{QVC{(ptive^=e2?a_YTm4+{Mrt?yjtnCdvP|fW z2-xW0G4&ui?^8;3=(m!;6Q|wgeGbnC=cDntc84UEjlw=f9+R#HYsOM5z`Hmg&(u6p zU6qjI{?So?bXqY|>#^_W1d4*`h6ZXY_n&P_`yx`@#>Zdt9bvk!~5|3 z?7Y_>Q^O80^<8xY3bHkzU~`2>DS>8FLv!>=5c7w$CAfZycBr&nH6eTLbA$EWXuCGZI6K0GRMxp>l6x5xsG8?C85yuKpy2VF^7BxK zczFym(hg^BCe~9c7or`N$r|Nz2;w2OwXCYsB?Lh`pdLjGiM-PpVbz^~b{?7yI zaHt2UeYON`n3cOiEj4;okoiH#I85?@lu4*^R>^sOad9r8S)ND1nf!nxxfCz4TFyy+ z`u_LRrB+`!j6OvR)RC;#JVf5_YIU6X2J8Tp6BN$Gc-;>h8K7qDNX%0yVr{lFd{&$C z`TO~3BHnT8GlGM=9PY4)2CcgDD(|3fcA~xhJ@&1?s}0^B<(@6)lg;MLABwdETglKI zw#W9tg@pEPrJaLnjs9r|BgW&1mut)b$7F9+;oD!{iaaGrM)I1-6^SL!NytC?0xEj1 zF2)$gcc;BnfYFfvKUm6gw6rN{(HDk(Hz#SOPs}dv)mI}G=h9h0wR7sNA}p=N3*vL5 zQg(zWLOGvmi8*n*_z?$0Rc6@-_-nMR)3HVQ0lxaIl>lsIokR3(o?mY!G(ZDxe&ij# z##)uYWJpqz_84 zy+%umwz|>7BSc3Q>zt~>AvAFLL6XXSx@M9STOH_lyKpb7ZpMJ!%QsKh;P8K=Bsk7T zS}`{kuzRltW@4URMaJwwBwDHNlT6lNj8UpT|m z(MQ{7W5YVfkN8*HwWgU6<1%aC27jkSu$oW=k*4K?HOwHExYYyR;)a9zh!F0?wK}Xb)fq$ zRIQgMgoI3-4d?T%OaT!d7=0dX#CIm*gocp=u|jcJo8Z|k9?KGRPZsO2(7g{c#orK1 z)$e;HGsBVRM2hveMh2;@3S2dU`ongE|j!de8<|s_gWI55ooJg2&JL*QyUW5OsmTKURr( zT&)lpwh{IoM~}JVL|ftL$yry#t_~sxwokJ|MLb0PD6_cHy+~$qQfvq4PF72`#JXn` zpns-j0RLX;_7}`^mj@-?3bqGzXDD*|V6_U~Jvca)sVTePGNs#h=*m*>!|rhOVd`8M zw^Yh?y+5Wat*j(Q7w*VSg;m-s9=^x<^0WH79cmNRQypAAoD8V$;Ln|3OKAyh1Tc$f zbxM*57*>?ist-_)i}p>+6^a+47wht%3{c2jPc!v!r_jgSDK(*<6=j#pd+OxGjV+CI zORQJ1*e$<&!NY5q#^YqhRitR*DoSrIReDzRkAeyqaP1-BrJ0njTBep*aK zuJ492r|fP)qfap2>rVW@$k1iS&9>2viyZQ6|NaOy%{=uEW`@Pu#y?m3JUW2g1?7Qx zXb$68w!11~m5SBjIi^eKITnx|FWu0>Xm@n#%3%A?YO~%_{|hj!Z&)em7$Lq#1WI}O zJ+6It2!wO-iZyGO@u((BVG=^+nbXIP4y>?7Tc>452{Nvv(zMqb%`g?Sq&tpdrBoJrFy6ep2z@GgmS3#x}S6M@Xg_#67*0_*1dGs!*5f0 zt}?E0yrrLz*jzLr>}9h2Fz6`$H9^7bxYN-Y&IrnHX(e zS}KlJsGe583cJ)sHpKGc4R72bDpUE;E){ciU9q@_crMyhT_AopzUqG;#Sm~I6^h;E zhYw}h{})vdj~j(jttlN?ubBvw@}3ns_%kYU#6MI?cz*p)xXG)Jd}3nR8$2+^f*`2t zyM(70i`Aw|nvgWB!UO#5X7u<3Gv8}ok3AE)s zbT{hl#I-v2D58(stFSvR=l8eh4g6yeT*O~o%m_|+2rwubZfYAyz_l-Q@fcrh89kJ$ zv%`g?=h*yNkL}pE!_;`FE}PiA+f4Z_@n@D_N4L>~{}&2ya6hHAzjhfVI}~st8Fag5 zM1wfEdR0#qTPj)Xc37L_IqG%MgzDsxd8NSqIA-`cJ9)(BHvE8CtEoVLoF|8Wt(4cx zC;7O4rjuES&qs-T9xPOgaR5mob0Fyq8)W@#BAUU*wOyPR*}56`$6_VJy;}%jv6_wV zDrDQB`zjr^4x?7Yw`W~0&^Q%|W0*UFuDC6u^@WN;v|qj}Hv^p&>ak>pNS?QZ%VLRU zwWSx%$F|%&4TM8%XIwtBL@sOUQFifU@}%D$^@w+~^myStS_|el5sU(LT6predeaWt zG~A*C-Ou4Ui0i2>>+HKsrm(msHs2%kdyT?Fr~G5=QCc^*0#vaPiurKyw{QcG!7M*4 zSNG1`$-aBHU97R=JKEIZ?UXSbZRa15jG;y8#6_byFji=8LymH_toDUuusr zG?Rd1fQo`g_`W-QQ&wJzAxtxH_fnlp0b{N2Y8%~9N&olTEzZ-Zpi$s4ru;nO9IPJV zXK$&aR%+@ZLrBn3`RBr7^^Bdw7`ek*(W(5?0!)W*@33$o$*4>1yOMvWtT4{+X3Uh1 zfRS5TU=n1_#0mNUcBS1Z0*|7y92@U8jw(4Z`aEXElXW8ujdiLW|6X3N3h;A^aEEOV zg^{%MHmgE)=k`xmgW|q9&8ac?#%6THEQbIS_fqoxwY zms*X6KpA?E(c9*caPsIlYCjf?mX$1W%E|!0;>3QbLc5p$;&?1?1{^+j^JX?DbbOwl z$`ai+V85hMuzUE%>N%x*n;1(Z7s1|os->g&e4Rur6<1QilV(jrSWP@C#9vcctsZnw zkG++I?z`|MRk`Ddwtd2%yLpmTfezP8WZqZ+(&HL6wz62TKT1kRXK1GdZI~{Cqcc&Y z@k7?KF<+??3_4vdI@@&?M4ZDJegJDkE!$}^MRz3-A?QD$PrTA$Vpq2_O{kK;+u^-e zrn>(58QOS1O+@d{<)HKEc_in&QL=^wdcGyePSi27cw(qGDVnvqNF+br%)pg+W<55p z^!VMcRe_vJFZA1Lmd)EdscuIo!Y#iG+Y5|YhOlc&so2D`K^cK(tXx6TW71YK|LyS2 z>qFWE!^J6V9^?0dc>deW%f@dW9UVJeOlju`nb(UOd|Kzbaz2n|pM2a^rN!`cc>Fk9 zEh#O4Gxvjke7j85uzQ=`eQ`mR784w*yTN=(X+gY9GSk<3#4jh#OxK3}m(VHkSzx(~ zrCvCty@+Iu)l#y8g6bS`c6?ZiONq$w-=ST8k9Wu!Wa^?zm{ut}fMq+>4x6a?3v9y; zX8e#}dyJJKwhdtyKfltAx1xI5;?iSy{!v!4Bl`#stft<+4dJj>N$Cvp8K&Lk1Cy^E z##2K*-u>1#0aNmL=16R#xkM}{e#e&CG)uSz&_!T9-qjZ*aKAHNNt)N*S*E`g}g2P8hIVC1^zP>l%q{ zCAn~|b*`5VXYhJ9g?gLH&y^p>9T0vOP*#8QBXlI*Y7kE}=2b zoH=}BMUtMCkC!|hfwil5HQ`ye!Ib-yljEjJOsBJQLj~Y# zMfYq>DIqWe#zXixQgn8#XmSGMe+Kh`AHw0rjL(Os)bFa zLs1sClv;O}Y zw&xE!RpS(>fZOvB+VDyA{r(koDq5BlurchEzt@IOo&d|c36mcLsO3@dNkE2;4C zO-G6Ee1%>i1r1P-60fJ4XsC-Q`Fg}S5A_6U4F5}ZlESTD@s)mo-vHIPJXQe)CfV|w zc2vNQtLZIB?d?H0W&vk!tF4slk_1IH_6nLy&qxZ;T6tx)y2g~5k{UMJPXdF9eUMhP zz6#_)!ZTm%$&I&DZw0z|qj}V3NE!{erZhFlrlU^B{x1jP-^OZYIh61vbySjuLW!KW z3Rva}B$AHtkgAb7cJRlAR9K>PdRkNq;l51fAWpF>tA=!qkUtfV{Mf{dG(|B!t9%US zOPg#E_`=ifNSSt?JDBhnH^otbU|g?X*pcbq7N|nt|}Ez zwx1DwDRnqBaOQq!esC*xlxe%-%;wo6Ua;^;Jo&*Z3a74bqSs~|U3Gk*kvEfIq^EaH z_lX`sy%2$hCjTv>sR`uTV}*N}!##9ocIi+$Ee@5a(>s?Z(@K-EQIee!R_AP7GTqZ- zL1;R4zW6jKJsJ+4zyiX?jKIRrPAeY@hm1&q#G1091d z!DczB@k1zG?bTD`@Qu|hUge|yXwgHmfY^B&f|n!=?)k1J4ZlxpqRui{ z7oQiUdaXTbb-Tjjs}Mve5$(4r1U*VHdo}GiSS>G^V;dHCw0Yg2K@m-r2*+oyMMKRQrSdX*ey*L};eUbi5 zaa*~=H&$9W#o1<+w@&3zbe?+z<+?3ik@F+=If}oLGoe&uPF7FBXu* zWGNn@f7X+1-qPZ%`fzwr{EF&ytMK=DheA*dG!JjnuoV*SX`$7M2uGtK8lWtB9NmR{D~;s7sIK|@7VZ!jO7vw%J8_NTTLcIZNeO=6^AY#vl5-P$+d}PU_u-MvdNIad38+x zE1g-#6U0huhI6dSKCjgY?okPaWtuXwPn#*M(`>QxhXXv=MyJdXL+g$A2g_8d?d2T7 zLe)6;U8p%?J&@ZCHit6NVd?9PrudP?iu)-Ux*-nt%WKLU;%eTYTrAOWg^Rt73oY$* z7g4S8Px@V|f)1pP zhZ5oWqP!?Jw`bHS0#?~A{(%Pl<6cU8!-dRgDyL={d5pJ2Y7m|`(N6{IRm2Tp^;MJQ zk}ZfF#?GSt?QeBu9aqu-Msw4w+VAW=k|KYx#8;1NwERPC<2^^MAFpswD-e2NMg(dlf0NtgJ) zBB!5V4p6=jLzwSq)r!?heTa;7%5pR3HwDHG@k6_37?vsZRafU%BDWck6fC4?g>TZB zy5$DPB~&gBwnqrfS^cWVHIG5|UGGTKH+Tjm5{#=TX&zNK#X;wBL%oE!)}O^ICW+If zB)Xrw2e((^>~$vxQjSQ$N?at4$1`nP47T?Eg6zkE1h=x9K4)_sj5XXyjPe%54*q_! zi-*)|`WD5*16-5TBHjtk@I!?_CZORB||YL$U&q*TJ;LAzzV76)DMfCxX1Zm2}->l21uM)Qa#(E@eWoZfkb zo(Jvp4vAlqY?V4WO5MkZFQ6Xul++Qh?_VcOW4F+FrnLSS>JRYYl-~c^Wt3J$2}g>y zRu2(BBsRh(LyorBkD{?En*(;|!)-t$yKOKeoSz3DX(c!yUW*^*&0NptsLJDaJ1a3m zsaUCL5^(D*HcLfu1*5Y0!@8J1Xs4^McYVN0RYp%-V0o=UUt+|$61ELizF-{o3b{kn z+vM-v$Kp_Xu>Bc#7w(~YC~C!_4bHW)ofzHTI^#wc2dq*;K^O>4D9SGL`~ns#tIHb3 zN~4kpTuCV?#JoD>UW|ctij@?m!&}CJPc?18&G=nC$FQoslkE(HVN9zNT$f$+!A0wp zsJi3va105_T^9__nG7D|ExP}6Fqy3ZviL6L|3!CD$=F9HeW(>V10n&7E5i>Hm+E~< z{;$Q3;@4FKGm{1tt$?+OepxTE~=h#4W#&6N7a4`2qXt`0yV zUT$Uhvmd6>a$n#CDA9?u{vH2&7>=*tOn$(3fO;rd=QXMcqriN(1R6%sv#D&mT%tA2 zW~dWVJr<3~8XZJymHIA9bl64vD((g`+yw2dR|$q}H<&sr9kxTfkW&fPCOsaYEkrj} zf;?W{-4zu&>4v>f95RB0P48yBRu72aZ}+x(So7hDljNinoTDd+`01ImMr@Kvbhoy$ z^Fp=OJu-uy*UJ6&Tc(0E6e?*77Ymo@-%Dt!R{J+ZbH`ONTaxrW&SoegZIU8^-SE~856acIR!X-WdUvH=$3(n*Rx1EuS@lpplqoD2!hPBaA z7~pOmW^*7fZXY4MXE!8J;#LcQ=e4V&b*O%`e_vMr~$r0d4;R4PMSE$uq!g z@v%vsRx+2Lcbz=p@c?cLnCv@0u=V={JF*J8F~(#_uU1}$7u5wyh0>nz`Tm@Wj zsUB8`GyJf6xW#9Pl(&~EDIuq-*>9x|sB$>N4=Z;*6HNc5MG(;&zM!|cDXuVfXLB3}?|&P1kNam-EeG$j z--em|a#VYPUDRHi?1<1A+}m2~EmI@}qXN!kd_v*35s!NY#H`|Q--RlB!Nn0RS{FYB zjzc`p*at|0RvQUvfp=?rb($A3`6=!1WeM7-N0cI#AR{cwyLb~(W`#BPI@HXXml>xaCJycV0W*JsWu29&bkFWxJin$GRH2C z)T$h4RL)~*&H;vGZT`+bfc`@5WBlf7BwsFP#Rmv*)dB;~&hMeHl=JND$z^m*ZGz;x z;zs%lXqR`vb!y#3^siOKA3q|GoxAS9&NZrl@7*5-r$v=+Ns$q?ii)dXuU6q}$kZCA z8|VV25@9 zO%17l(gfa>-#1Tkt$uQ!^r`1>epb3=FoiOf7-r@2bI6zuy$xGYkZ-KU#O>9WbtH-% zO0UT_h*$N@+qLg@6}`JA?Cs@V_*pae_FJZ;n*VEyJ~b0Hc|0~5+wvIytXg=;p85TG zkWM76EX3JK)dDcf0QCQB@Ie~4L+Vxg=2o>mguPg2N?zoiLsg!;+d2|?5 zW{-xLnhJ*qbtqum6+b{6u`Hswwl>UiKO(Y{%)PQ6pTDtkOhm+P-r;}$9!`W{bkfED zIjv^1dFiTh-7W^-V!PTMms61rHd@vDY&&e5F7CaSb2B$qP~FY9_|V+-v0(Y~_UWUY zQ?FNN`{4sT`?L}ReA2#|50;}{ykY@kxcGz-d2PY+BQ>%%N*UfKGSS&x;wnt`z#cz5Twuz-vT89qXy0kae zrdZ~$y!FeYp1-P_!zpv2J-3pfFq9v=IhI)u5vl2I^C5O9$~M0;pDpJf&>n(r8HbU{ zE^h#Z_ZDeN!YrDd+OdmDwXkt+<(SjT792lA97^x}#MotjU0l=uDt|`}`JQ74B@b?Q zxt%ZXuXRXlz>RzH-+DJ{N2%**ReZo0#tq++7_K^ZT6esq6=3$q-Fz}SKcl8aJnSY; zdq>USz08o>P|6Z|;HI3g;t#`<1>37e=dw165?(e*E97%Z4lB?46~4sKPKdkVd-g=E*; zd#xVH1#n{`Hr@TUCI4Bi&=GQ$+TLqKe-%QI9f*p#3YvMf(|?g9=yYNKsZ4Ruf;2N`|r)hQaVGm<9DN1x7cRDDRL^W#IQ z5sG?;`SR0|mJNHH9ihzQrcALqE6?nsib3Qg%QJ`*^rA8jEkdR!?CMz-u)|>h;x2w- zWtp_h_};|X`26Vnyhk>HOgD)dwbGG$I-yu_CMX7k3t~1*8sYFQ+=>E8lc}V1P{e*s&*?Bq1{j(0o~2hX!0r;Ng{`?NMUgr|}Qo zv&ZLl&a~o=;xoYHyQd$KvA>rKoC^SU_$m1qWMHFJio5#hvyeU2v42Tg2F#|t$P&MK!s50Bl z&z(bw>8HRc50lkmj?sipRCHP!f@^#0K27r!)ax{Wt%Wz<)Xi~Hg;nU+Th{%?~YMQ{DK#`VwN z)XFfCV57;xr(kRDW0ISwyb z^C;TT$kXa^i41#-ae5r>-VUp1@NLdos~dHvRGLSIZ6~gcBXX=0Rc5IzSNXY@(|4eB zeO%3<)NkKW|M586!l!{$!|dp^69x4K+N9u@?ed~pP|ka_^Ju7Unolv-6Q`5XfmrEY z2q^kg{r@FwPJVgH|Aapia%(VPJp31ztwtAtM|isAXMBO8PU<0lCiNTtUi~iP?L)Z& z+oObhJMQA-`QP}gVjf_PhDXj&ZM`by7hFRO&%`ELqf<799es8D>E0yzr|Ry!EW|Uh z=`!*WupA?EcqWTc@Q~`9DeujHw7Oee=>Z6 zk`m)?`|xD(bjtW!!Dr&ha#P)-4nJ6|v=q8CJ#xp0v;Jo} z4?-KU#f#-85+&@q{7{ghoN5R>MSglr6j-P37&N*sH`I0Yl|xBWR3h?|uLxrw(A zLMPt(Vw89 z=5|P>>7tvtAMm|5f0s_UiE>*!r_!XDbT?@Az>On@^5#F8& zj|9JJtd`?$N{h&%n?sU?drKvPTgeJ^P7IK0eKnn>_!-LRa9- z{ZRP&v0%Q$i9IjgSox+1S?L*1?Iqxv?nFDftrwgeHiYnB-oRrbhJex$8|WgMcR2s6^r1ACHSR| zDh@b^#PO4hCn^NPZYl1gDzQS0h^P2};JLe5c7Z0w4?G*Dr?lOrb zs=NAEqIT#}>iG^$YenhUnye!izzs>zgH33}_~-2mN*!Oz;Sg(0iUBJI4PK-XxlX{S z*8<5RN*wKn)qr9o`UbuMEh{HRCUi7A?ggqi={7s9kN{A8rjI5-Q$?c?ES4(Ko!?c? z5M~Z5aQJSPHEC2IaZ9W59Bsjjkb^*x;<26JR8Sj_p%h3bj9TqG`kXr}?+0WRLKnka z-B5keJlBWXm1Ou~l2iASzgspD9wDdGsN28HsZ7h?ZHEBg_yWe` z;0NT^pMhJ$OEy3|74U^e=z@qWAirjly1#0we_RoS`zy`$Mq{`R*Gnq%z~EH)0U7@V zoIe7<2R5hiwZ&7*Jo;TdaN2KU&m`=Ho6fOfeidnXYW(??qoD+e z5oHUo5bcmmEcN8_fJx?T^_pz-$lPmU7{3rV&b=&QDf!rGzs3vILm@!%?a84po-weSirb{ZfgJx+6tOzzwDxK4$cYDn-Bj z5-LxRx5cLt88LhckCR6#dI}?8Q4^s?Q?mZ?$YkuC(r>jGP7aW5Li1fPTCC`Znpz3e za1uR&sVKJ->2H+an_gVuriCiIfo3lS1Ft0yKOfYE9R+q%$CIBZOPZ`<{L#3}Iiu{`1`)J!5+)c&t>8Eo{q#O2m zDlJZi{oWu=di~FXZr^a)2+#2qp-1Ac9L<%sh#zX?6_GUi$|TO=q7n8^i(GK+YJR>| z4195D?{((_1L)QDXz){4Do20?HpPl-BMS+Bx!+LV^Zjf7eOBbbV?X>?cL+WnNrzb4 zqaPt1j!e(k;NL6tchh0_0#ae*t~c0H-<>HWdYwqsv1prUdUWN|6U^8uw4=oAnfV+p zoVEQ|Xgrv2r?(sV1xFBUDX?~Y;plkt4|6Vg*nn${7X!s*YlGKT;s=+MmTepA*Te{< zsW66}Un#7kA3wOGvJaE7jAn*BaKyNuiPU3Fim#dNP#Cx~_LZ4nTGgmr`_acTy$U~6 z-zlo*`}vk#EU0_|9U{am>@tVGu`s@x)QpCLO3G9y;zsBC_lLfc^x?I%AX}=XV{w8A z*tc?p2xZgK@#Un$><$g!z5KG+f3c`UuSRBYd41^*UXrehw|+^wE-up$?CHhnr@vo} zf1=mR;$-~Oq>Z8Y+R+ye>%a@B((oD4TJi5tU* zdhf&M1b%V%SI4FV3%`(|*qE9p**R~(q#*Ow7#><6%}62!3H;jut@=vPFF88&qE{8& zw9&BsbXWv7$Co z8e5SM+VJ|k|NEqWN`2R&=oKp8k1^p&s&<%*M7&MVy=Aqf|5nUdrGz@k`T|lkQADt2 zu;h8N7zfH_ilJ{+a(Z2_sC{3lBb6M<8D^YPtP*49Y3Deco`egPdZoJM_FKMY)RTC_ zunSi5-p!~AUS4JT@!&Z9JQxp1fW#N)$vVuM_@HEan$7Q|@F8(W&|ujtpZ-xh(81GH zyqoe(*pEBzJ_ch8pm%n`6+nq7WUk0UrBY~L&d3UG{ek}clN>#g@)2%!@c zgkwp)3eX=LD&l2bhtXWD>7MZ=q^=xw^qcz*mZy$HRh#_1Mht#od`uwt2qksn(0qNS z?JS9xcc83@uIB4NUu;VGL7i54=xO5}VgzKd{Hj`rP;0J@FAaTOs^0rbiDx>ONlw=V zd8J_JG1rLHOvM;F!;pgh1*G{|z}S3QC%6$T;V)WPWwCk+V@$$VbqXO$+JZKLbQpx0 zvw_NnS-36Bpv3wmn=iMUq3RVDo8Ar?9T@dvkSdYd9Par!i#8qRWm@U3tSVd&B{&qw z%K7e)?>U^j!ikl+9Y6bhbpg{*@AUPX^!vPXhn_ew-DNP1&a*&lR_(k*T?xLr(~-|O z3CIa!ktzpIUoiYyZ;2P>*>pD20-IDykfyU_G{ppX%ut-%e+`yga}Ni||C!#>gtw?Y z@5a}24zuM}GOBoHh)q8<#4gde2;CD!jjULb!AqzLZF*HBK*MA)V#)Ms(KLKvC<+{o z2Jmz98yxl<@mPPXANeV>6w#~kI|V8+UV0i_qps~?_L#lyt~8cKY;5KB*Y$%vhn-$3 zmv#?uiEQW{f7ns;gKW?xO( zHSLgd?I3D#rN0AYK!N<5UxHmxa!+qC+X7;ls+Gamjf$L(Drdg9wEK9!%F#%Q+v2dY zoJLtZ)>ow|ie54#9lhoeBtF|tC6YTBsm1BOTq$>Oe%`+voa^BwZsTyicJ$9hwtVHN zq=+Kgn~@_D5wA86u2BVR}3SS$ER`~g! z5_N==V@|b|*ldwl9Q8xlRd?1inl8OR^rfZJ?pCH8(m86{-^veoVC6g~@Rr29@4m|P z4i!MFX{nJR;`?7@qFV6Qnkbyjk%q=^HQAajMv21-y1JpwZ?HouEm5I|*S^;wUc}SD zLD*H#^)uu5zM%BE7pj z`*xLo$-Xj*9T`N?Y%%lx(ASv4+k)WLyhrWK;iUJt%ofsyd04DXYG^e5#TTBU#$W8~ zjM9M^F0##I#7WlE2{EUu66xSP9pbF8zsR@OPe)HF(Qq_OQzi*p4kzQ4kt`-{7J>`a zkc2s(9c5QsY6qWS>VDLx@$hG-IVHZuD0~T2ub#@ibTTlmU~2ln74vtE=^(Z;>u0yk zVLD?^O-HmzEjH*xpzP9NcRQb%2EzsHAY^JbH^~2<=Py5IqS>LY;de(_iOXu8|-YF2Qi60|+ zg4TQJF|1QOh@nw6S3$!7RHiv3D$oF}Sjs!PC`$T)bXDtL|cj_|3FLa#EA)94V<{^fT z{*Opd(~mG6VPNJ4kEYl$xJE6l4kbU)6yBZl@@qDvJJw;_Q+pvbx6xy?R~cwJ>8IUv zyWZ&P0;4q$Mp7XPn90NeVG}i)$K@L5>B?+M)$c8m(^ox3`1cy+k@_PPBkIgWgTccV z_YLNS%al;5WcdU)t8~F_EU{tF1ih>d-av(}X?p}^hs@iyLBbDW6vBY6-Yd|-@J8?d zi!-RYUB?*)%k)f$5f;JNThp@n*i!gKJFf!=tWCOtnRJsg*5LlyL}nr>H*l!Fd(8=CA7 z*J=ZY^lF}c_YF@C@>d1|6c?)A3&+M1$7DuxMKBY=aCqzL`d+1WHNDTKXny&Xv2o&h zW@dc{-ztTj6-c+qD7YEDT;*w_TqCz@yQCU?>2Ond##Krj7yTg$Vdpba z6tGmr&S&2c{SLlM70hS%dBzh}A|B<7jf^p;{YJcS$H%X%aG{_u4>wYYc9y~3pLeuQ z-eNQX!G$+@+fV7{(I_N#eSeg+<@H z&(-`JcoF(I-_UnWY{b^9@pqcQ4N_1VLY=HB*XAn#YyCD7aZcWxzA-9uz-RoZ(ZTrm z)0oD)0TB-BYgNT6ofq9XIViCV{HoZ9p}MqJDN}_j^FBi#!=K3b4aP!yQ~T8^R1`$W z{r89OuY~pSvWFWkwq#wvSo5m0v>`nGh|xdU>kMzNF-~bzpN;Rg3>*<}ke0%26;D5| zQF=Vi%lq5MbwRC|So*4Z*Gokfg|Lb4>qfDfD z-V}A^;_S`BrBem;;}*SY%H^236%O;{>L9JiD=7(se?OoiOzUWg8wNMlL&^0ES{O@u z50AaUfvVQ@Z{rTCgAPt7rmHVk6gWPf(PSgnJ!ZI%%01C`5G$s&;GiMeNQD5dRMK@N zxF3J!DYhlL$;V=goSSXsYXZ6m4vA9oNw&cMY}TY?;w}0K)U3Yx4R(HGWG02vIiHVt zFfBAH^EkLfUA00eXeF}YHN;Kf>uXjeQ5^ZzIhi&wszM}z)PJp*&od;W1P@or9S$yE z3-vH61&lm&`Ni}bs=2@D)DP~ZIw+V2c>E?K7a*?DRdBJAbZ3r!C-4H$NkY6V9wNNZ z*Tg={O5UqXI%OGwD&O@gR;&RMZQ(X=ak0Jai`MJfBiQagfU%thh3Oi{%w|%U8t^*NQCh z7^U|PggAUVOCCC6f7%|+R+;LUu$44!)MJ;C;UML^;?H(M-%&!x)Q|i8d#=sp9A;w5 zI`~GV=d;1ZSR^8y>Vm`9tzYY_W;+?|#eAVDEDl!&KggX;YYDox12Xdkw9h=HyJ}fJ z_d+FX4kTvKgBYXhTr;@fyOoTcJbuok`@O?mCFE{_m?tD3cbFK1bz~TB@KhDgVhdwb zs5X}-2IF1cTuTqQAqH|`(t()&y6~csw$^RntyHi6a*dI6*exCe;QCz}itX+J$*Ye5 z2dJ!1emX9Z^BcL=4=w$1IsM+tW|iVH z?P>iitK8SO_Oi!uj%HkRB@wSyHg6%ig1%Pu85u)(Sx*iKy-3hfz0M9-g+l1p#P!-) z<^2A>f49o0Q(PdFgr5@mjT>qeWIwlb@)|8WH`m%vi+Htw4I}9|+cm71=5z}@R^{09 zaxihF1}l2VK3!@BzeVkm!Pl~$m|>B=nT5svn1eOHX}a$GkkWsn3K*}i^hRxhCfT2? zYs4*7#{GM5q9X6bZ?dP|rw33iD@iC0VmG2%jIUF*18tr=m~QXTPaLI3lqv=>GEyW% z8%3LDXN_I*3oNXF|Lv&|gQp@FV8SWlzaf8fd_fr1I;aS@EWy+K()KR(c55+0$uxS7fN4oqLRLM?CNMRNW%SW;CqWln95Camhb6P(}&7%F|SFc7C+=W*8J zYgPy7#f*PEr35E_VoQ`dZJtm%rDZ}a&hfUrQMx=wmdS1Tdd7>1^ChApLK9&lcdVCE zV;6H@XT>*7EhonJ8hym}RgU=8d;x2>SW30N_9rzM4|Z|5K=<{fs_Cc6g;ZS7t|mss z1u40{V}2HCnhrdP)ZmQ$kJApb+ys=QO%3uE|Ht$!QwVzRVPf${69a@r{Hm4n`B>P51vd zrk3f5VAEeT?`OkzkD?)rcbK2a}?s(f8M1a^Gmk;XA888p|?h zkcItGI!|8@vj@=Cp?fof{i;2RA3Bp5_5uePxGWxVY!7_w2zt&(U zr0Wan3rSsl{eWgI>+O+)iI8#F>S^!Lmyx3B74~AeWN4aLw0NNeWRT;aMm;SD`~TG) zDDm#yXkgf)D6Js zg)K6rGKUnJpG;}iEp)iiGysoPG5yEHbemR!MU!URej0oVoqi$1f>qPx+w+Tyv%&9l zBUuDmwKhm_k@|Vy=DNk?Po39iaP1rAk->p`(ZErBq_*^ZmTRF>1d8zh%WSM|wxRX| z(@f!~hA-P8Ga6rfYr9f6cfya|q@u)}B;rPdL&J9UclV{4M`*8dgF=3qA4h+~ z5s)8D$&K~9oBZ&If6~`zA|q+LhA~JgG}tCeY|v~1w=wm+Q~Zr}3>1P?$-z|CAw`*6 zKwNI#$bL7rAoV7qRrtBxPX7GX9%CqgXNpb9iEBwxS`#2!E;Y&q&)e;Mb+UD?0aPGSZp z_`%d6wD3MZN|WByzq+>}PSCg;J&^HbtIUjYhTe^NXO&v~;B2Qorjn*Bq{ULVH-NG@ zCSg*g3D#TDu}6%h$<|1qFs(fEL@^MEk-+ogrYKC|;qc@xN?(19{zgCd&0udJM)t4+S{YL8Tlg;#hhrBMU8 zhZ;*B;v*Q^GM$^0C0Je=a{!!f&wpu^VL!Z6Msy7;YKFl$6KLnu*a%-``OJB1cvTfr zr#SO#oHKg4h&_dATqC;5s$Ra-;nvZvyFcvrhWAz-=$XN&jwZzY$wR2hqEmlAGAY7g z$g1gGb&;?=0fG8@%a2I)hCrP$!$q)}pOO58fert?ezJgKdPPUc7M3isG-k@rh|#7q zzZ33no1#o9y%GHl+dp6>)h-mjCR&Fre^Z8JF+OgA`juoXde$`m37X7pHR0d9XX8-% z3QgN#`(#T4mH4WD_Pd&gm!ic~rqf6*(!~hYw)>8Lka4#+pw%XS%N1b*k-fvN>YfnZ zm=fz}OAt#*Wh3zB3pl&kY*DTk1yxpy6V!gK%C$>Q9XA@?;lV0yjU|Kr9RKOonHXM( za=n#2iNFz}tt5j5)4+_$wZdCdFE!YFh6IB0;4&ro=Ut`*Zh7rkGkYvXew&OW3!bIo z8(NCEN()A;h##I}-CYe`K1-SHDvdwyG9~qAy0HCd(9w2vFE!^>N{JVdLIE5JrR3A{ zJ1W#Zjd`8L5=)C7#v|Hzz5Fh_{7GF+0+R~1LZzo!J>&5|w{Ysucy=+x%l-i6ldg(p zwe{X%Pve!NDOxX6!!Y2f{quE~@G+H=$H(m#jEDNfK6{Ajr>CbKKnIt;vRD3Y8o|D@ z@?!NUmiF|uBQIcMX-`X}9pwuwsPVIvsH5<203Hbr7G}ixDW8ZT-dvUGk|{|Rqo!i^ zD22JDkgV0UasOyUs;6XkYV&GJS`nv1>mZiKw9~32X}gnBA`(1{Df-j#*cgmHbA|L5 zXjqgzCB|pUOkdN9_=wc-Wn5e0IW$FE0-yllM3d4C3i9?`D=X(e062oKQ^O^$WDN5 z*HrHq3occGXOmBE3+_GfMpTm)vhhiwm190QkU_FYd0R@nf2+&SZLLHkz|Ts!EM1%3Nf-NJSVE0Vbo3| zT?w7UTN9^!&85ko#TY>Y?!oa42oc{ zH8JncjsL>pXoAq>RGZ4+&xnQRM}~5V$cu?F^2U9(9lIgJ>0)M&FUg#uyVk>cu%v9Y z7#s{PX>FP-t-=BY))!?$CF)AfH&=?7!C-MpTs#``qcB}I|(Xvv;{5kPDlo2I@>trLYv0?*%e0^Jw)OBh zC=H^BWQKsU=HL;>mQRCTX|d`RrSHjR@e@7S@I)1P>2zF-a74-W4E}-C@D-KjC?cnu zULIm>ggK_DMuP{k-U|XxS2;I4?HTeQzXq;8yQy&a>PgNYV96)wF-EVbd&!7bE>Bt3Lc7_Cz`iQSBzX@0-40m9%N)ZRqlkx@-) ze-qcV6FgBB)U)DxBkn1>$OQ{|!?bxwdgEWh=Igjt<)GAc7uiipj^Z%#7nE*mXP5f1DARU}M#+1ed%5o0(IeG??c?m77;xWE*D9@=LsfGJ@Z; zn@2E$1u5EKyh6n`38c zVUQx9UCy)ZAv8k3d<%*$F*g4!&27J~%>Yn6B`Cr{x7yL?ofPL)^@Y=YCsq7zwO7*yR z54+kv;u)XcJKWcVl*s@gq!W^eSJpcUhW=Ew|-@iU6^w;YR5Ch^+q@Xmy5e>Qm)+eyi9R>3sf861r#ZXAa*Q$mzVB+7v6`QZj+NP;Jx z-GJ-Er($vk1Ah9pLlu8~yWMQ&K89^!bT^bj)A>dQKdpFPh!Z&RX7K(~zYETrQYt$6 zqC-NSG0;Wa3=YeODv|Dh#mY1WUM4t^AMQiHkzA%U>f&L_5E&mMRm8sa-EVZQOvVFp zZTxBfZLpZW_sfZN^8ynhJ22QVAv6pS2e>V!l9K>>sxQwwCC4K$ywj{0Fd1HOf*-0R z-Ex@Tsl%;{=D16EuXq!SRZ4M-q93nh`sZ~qthnz0gCNZGuH0_848)PAHv_T2w8qY)5BUzN_n_a=y4zF z4JtgiO4BNZtSwsN{yF`dl+e7R&gd(&0)ENlH&;Zf$%DJsO31E z$oK%9bU3H-)mL<$3y#;$0a@|y(|fw^@n>{_RSTBZOjl)jM=4H>q;QfvE6pRt8^H{Nc|h&Z}0pq1ErST+mdqD54PWC|OOm zt1EPc)vqD8eM4o}gSXp#fe!*(>^rH9GYnu|PKK(A7@SPN1cq&g+P72bp$o{D{B@5! zh?t#mg*v465><D!JPO7%pZ=f+hTC}2FrLQg$$mjV;^Vf)r<;I-5 z;OS~$e#Q_EsQc$+#SRCIJbCr`eDHH{52ZBNKVatbIlq87pH*Bcl2Bpnir_9vZBhuG z7k!-?W3jtKOAfv4rOH8L+M)u2zEgwI)gS4d*vgiHd~ye+!OjXjPQc`7m00YzB5Kd= zsyIDbWm{CqUX#ZW!`2>*hU4JFRiV9r&jR2zfXC$;7wlHMp{5c$b*(9qayHg(61Nt|i1r zF>G+<9`%)buL2QERcs)5Nq+{5BsN7CKh>8ZxQQySeN=^`_EDQbIjj^AT&IqpPPRsH zm;BwLhRX2X6DXd7(6{D@bRDk6!kZk;mR_xpgy+Wz8B1kOmE)` z=5JtXQ2Q7QbTIlzrOfocK5A!KJw(HCk- zZh{Tc9x!-OSQJ#zEPCQJc9@PnDep>dtNb02JHFwI^kzQGt~5PXuuN+f?X`r?Brdv! z6Y{;5q#DP?N_*Tn_!RB?4ln6dKEEpm+I7QXye&h=!NMrbo@AolUd?GH5@&&dx2FVZ z)Qan3PR>T0$M)TKa2bq@&2vuHHW($=2%;q`OIv@-y&bjlrNG)R4aUO?d}?WYzm{}1 zNBbYo$StmrO7y*TQkq0ka_TJxME|{tu8#DDOD~7yLk?Tn1Lk^*@09Y?$Wid!SGtdw zbiT|tWaY%-Qv+|j^|9$d3G#hd20|Rujk?4gu6%d&6;yzYpyH$Vp!Oy)SQCV!8vdS= zO&7%#BsTf`$(>Ry6b+X02VQ0X@(brP$(Um0O=x$c*<_vAcOp1fD)0V$ z3oURY-+E=7Kjs)#maC>{@Y6f9uWcCK;>1(B$=~gKX#ef=!)ka@-)+T26X(ShA4iN~ zI1x^^@AXijFZgY##mRL(|Iw&X)@U*j#ro>*tC*8v<2cd^8jzl9IsSllAH_g@gOlZDmdw(dX6HCd+o9peeUWeRg|$rFgH#P}0wBv{QC zRKdubb}7NqSurCUmA~sPMdY6+<^?#d z&zA4=6I51k1aVCV0wU++;AKXh?`e|GQB&RhsY{`5aDpG|%kKM`AXtv1>%JN^7JE}f zs%-J0QHFS+83}{0XhTdoFd)7h2TD8$VIrXKE2S;Qi9s#v<5Gl(P$ab5tUA{m`+1sby^-_8UbP*GI3)(lf(l)LsY*9AU~*l9mdO8ksICdW{BNW+3rz5xB_HiKA8P6I8? z_a?Ea>Dj$nZ6;McyEmmUEN4a>?JV3am0IWM6NR!vo%QK3Vx-b=71fV#MsM^VBZX1c zOC2b17e0U=f8#z0Zebt?nq}mCjdZ%u_1J3Z94NVsFb^S{&Q*#K^JGL2l)mS zY{}q*T4**Wv#ViYm!kS{1{h}}^G&DT$|HzCBzm@wizcvWR06LcP3-lX?@Rf`i* z_R*3q-n&UolTz||vA~4ep$2;_wkm%`PYOMjA9S=J=5XA>eL>f@gH^RB{92{p6l2_1 z;98_$gIl@`^PMb?7&5fd;v{&ylJUj7Z}cxd{$l=@e(=#T zE}tp|<4~D8L>l_UQA6MNXGtHnYq1b090dQ66nsAxw2jzgcodI_!FryfZ>)rDk8OU& zy>=Krm@U>!Y)JU>TXeNlshNd@y6jSPcZ&#iO`Q#vUF9;EN4`EEKl1$zPR<=kMc-d( z`2_6@ACbsLS|&!GJl?(jL_h1^F=id+NQ}c*J8Z%A>hwH&y#w)AE~xeZqjnQNU@XN& zaFJSwaKU^QHqI;x;Kcl(O_O4u>U*jf{dHaFrxIh48$#drQ<-G50@~@23ZldIcp(~^ z>p=|Xbi;>T?OC!cmxD#d!;ivj1Z41o(aD_1j)-61 zvjkmEq%^Qcsqe5WvVZC<`_c0*pr=JI4*P2F*xB;A$7qwf0%zIqND_+;-5~s~4oW!A zW?~p{lL3vP5L1@Bnv!<5eKZ$$6bG_ilsSS$-E z&gbD3oS15TIrv*x=uuPIPo1BjNJf-56&8Y(Cq!3L#S|7ke}E&LEQ$EZZB$V+`3Bt@ z4pLqX?9aamGdCGpPu-CN2`iA+4KUjmP1Ers-@hQbF3+S_8#s7oi){8CNYa}^YDTry zq{G2&BCo#7Jd8B$^TqM!Q;X%Bk@Q@CHjf|iZA32nZX?6>6v4~QihdB_#Qad?SL+CO z^1VeEwjrexS6if=y*Yc+4Xgs6Xx~F3;}jibse}=OOs|nPWL#Daa`PQRaD0*CxSa7^ z5qi;-EhfL99xc+G6&rorGlia2GWul;STa*>GR{ob@d_>pI*uWj~@GYLR(Ei59n zU^S!uPCws$oTD`s)OgBQiagto5Awz8%}CG~NUb-q;sqnykM>BT zftMUXZSvFe4JGhrM7$pPE_qOCjWPaCb=Y}_dYl*d>vsqm3SpD~h zJ}cgZkY01&i7`S1Giq!WSA^rdKlDjbGLn0VBv`!QJf?>)86B{L%hdhxte5=o5~9J=UXq@I%QWZf zc`vDU9*Q>2NI`m3ffGAp5HkgvULoljK&CDwk)1+kB0JcKx^`dG)L;kA05qTe%f* z8HUT%YyIyFgFaq{Xa7&+QSPER*{7LK3;9HiLXV~<0AtO}U%qRchQkn!O_`$2HV%o^&C$Ii^{Jnqn|5Pg*{AEd*631aS3xx)*1rCg?Q8P}_R$aiideZPHs0YoB; zDWU|N8ewqqAMglFlfJ1?MVPaI-LbmP>g9KM?YGJZ(BoO$`&vZcFm~J2E43+gat}2J z;IE$HOZe#y#y;J*K0DuFwKrLp4Cs6kTWxNhIl;k7>{4)nM%VD(lGGgx9m(>>p41W4 zSb#Lt<1JWdWtFWTgL(DS4bze)~&o7Q;0i!j9&jeaRyF(X*O~t93NGAm8UKhN- z68#u{Fd3moABn;K44_x5B%ju1K*~F*Z!DdazH7MwT(;;IXs&JwM-F}P^7oThDV0li z6;b@jX9WAK&lV=(I}?nMM}sp-x$PhyZT7h;c;!Qt&XMUP+4kBLH62fI;LT6l#jhWJ zOGe$~Rle9_rm|#XrNds*AHaH^!YdPqueVD17;1{$xteqJiN!T(LjuuNR2uH0Q}7D5 zJuMpI`&$t<^*#3;$Z$v(<7s(Mnjtpn=fOovtCuK;%2&ra2*F~ckY@L{u)l)KG>_qQ zo(qMSROv0oQn!WTu;}kJy~qsFB%RoanyupGoo|(mn`as*mq&pu1s1kEMk#Vw;!I)Z zcp!=4qlV27^C|qiJ(7QCdwreFwMy5)H+WdEAfDhBV4Ul<2I{1fu=xSgGS_I^8$L=M zVCVT}ij8B8*1?FcLq1I@M&i*o+3S&>5QkClS}en7sRIiQmtl!@)!SyV?po|n`sM)x zGF*omTjx(k-^nF#OZ7%s@8-mq{CHN(86GMl=4hz zs!nycsC}<|D}X;rC?fDqkTVO$gKcp($j=-VzNl3`eHVanp1mHx9ODruj-9U^RaqyP zKegQS=looY{bFN>o#I;SU?7{UdMk2p@@X&AoN0rhw-xA#S>UWJQJdJdW=f%^1`HXA zw3;pU%U*Ze%kz~?;epIo|HwD|24Wmf@M^YrG8+)cwH>wf5oEekPmcl7byK>Wz$GmT z7hS%@Pi(W2q?H*L_dK9ZhFsh~q3QYN17nPjy?HFP4Lq^TlXGUb^KaKr%C4HPmy%J8 zp3jshf)n{+-3)+Xp?wzu(KGJQtA!GiIAYT6zGG6uxgXYe{w94hc@xnJeU||@7)QPK z9MS#*8^AXh_#2v*$U;yz66{x|?`_l&~Dr1{vBi7)}-ZggES%43;7iV4|P;{x_;y1Cs)|rkHThJh+8f z`(6admca0~OoMV3CulL5*gxyL^IpZO1X5ukXeiz=kZ(yK!4a7159C|K5l-a$4-BUZGbEPs6xdCT z4boh2BHk+d!j8B)~pX~iL8aMK>$Q`1s>G2maftPmiEQyyxJX>cbBLevB3 zdl~e91sOvPAtDK={z%GLAnBgajY~> z*e*%;R0E$8e889u1kMQuVf{$&sSTQrF8kv_N@8%32P?AhO0$w0dxf%d9!cUS z-c+S;|6wB=J7Z24F`pZ}t>$8!nFt@BXc4c89p^nIUCb0lo8G~a$Hd|q;c~|t_P3AX zchTDsEzADN%oF0!xxJ?WLbzwAYT+9YeNQDjcqk;pL_H7^T3`5<)a4#2p`Y12WjKHk60w8rpX%EuU_Wt678#E#@e*%3h0SbTv=N=pH&kFlB(4@Qrbzib zWpTX*t977cJ()rHA+M6&D_}4KmuLwa$o4Bk#&7WbnqbVjI(`N59Ji@@Gfqb1;1()9 z$60nI0+(wj7*~4(g|8BXyrNu-nfyI`v?12j0$^F{+;HXgngMq;*XG7%TJb}QCxitx2RZ~n4)Ez0YV%*tT|gC!CkzOV1ykS@{h3txB4$e!Tva}jOA(82z@ z-+uc|j>0_-0z_i6`6g{NX0PwFVwL0!zDn3BAT{6XL2?{$g8?_z;R?O>O=YE2N7NU` zw|Yp|_juv^Ey)#FUf8C;;D%Be5%sG1eh;JUK|G1teavBBkkFJ#ACz5;Z%f12sJRf+ zTO91FZ})IHniz&cO20(+5%}iJZ#OcG()fPWT$S1C z|8ztoxC1-`8)Z_$>%q8po}jh8w6YIwq=Fx+9x~ZiImv^#Iu;RL`Pc!`WJK`EY02F( zfNLIGHs1q+{s?kRVys}nQVa-Jr>>bvT&s72*Y%+u}rz7sH!^!Bjz!}pK)&Y#D*qQbJkum!i>t;1zBJeoRn{*tn{zqq z-SA?Ilb!@yS`>#=N!KI{qiThr2!l--3yzwoFHss$LPl>G^uo#Kd^Rw42S2RMi82G` zszu~rv8fWTX-xr&CBw6=D)0k5q4Oxu@{_Avz;r~>zyNApoF%V%qruO?;qsjyI&i2w zpcM!y!r^69xHj_i2Hd#cU)T&rnqDG(8nHIfTL9?;Q7o+Inu z=Gk&E_9RVvI?o>@8-bTNn4G=|J+Hn^glQ}a?;6*|HX1eVHqN`Y+Jj-N^>f+rr0Y}+ zo(yvV!bFWvZbR0oz8F2 zD|d5Cy@gm(VS1H@M*SgfE=x(#eUo0`#1+cypl|C0}rGQByI+X9oV&gy? zq245B3HdG;&u&1*4XnVJvWxiy?Vo)3$EAf%Sc=?_d?T@4v zjOv;ewVyiQR^zl-+>2RAGlck0yt-P+$Ec_h{}36s;6#2f^xm-je0Pm8QWn7Sx0>PY zFuI0;iZT;cmcG|NornoqZG$D>uHqU|;7mN#RM3fG>~`LK4&;^ZwK3joXGM9eqaQ6! zkXs*N)jg%tiz(v!7uVNZF@s@MV}v!D=KA@gO-0)`>2UeZOG+$aeD@7L8!<1F{TIG^ zN<|&E-WnZJHQHdI2+2BS_mr~Pc$jZZY)KL>7>wdB=UdU614GES=RktyCZG0wD=dS( zk}-{Ve|t*RaJ9;eF%|EEJws@4mY&jcf48=~5`F_32ZE8P zB(toPcVelcH`!rO;BVVPr%=k@{Nm>M@SQjMuZQLVR3Cx;6Qe=JzIGcPKVEIMa@Bx# z&ePxHTXxg_IZtaBSycTu=fQdR?KRRBk~iRE*V8K)>*}5ECY!=2>Aa^k|9w##^fHuj ztuHAd9d>D8V(W69erd?2zB;;)$cIiXTqTS2x(MI!%{EY^SU{cA^izXHu<&xZI(T^{ zUz{Xl{0}S97r~IOXs*1$n~P|PYDaxluZP85w$8kIWwCpByS3;WbU4qP4#{>H&vsgB zUD`FjMkTFD9)q?(FA{O=SRZNMZ&3-f&71-4E9XnmySLj?>>?_Kf-5YmHRt0vvjC|Vh$>UD0&A2;LG>N=7h?0eby%5~%zH+{0hs`@X;x^SlQc?ID?k$L+ysr?_5$dThb-z3~sZqk}RT_{W)u>#aOw zqZqxokHbKX3SQ_rr(4M1jqT_k?x-|jD^ngUtxvH3 z+D2CRd(;xExeQ;f2*U-I=Lb<0yO*MeS2LR{xl z=I1$BZ^0@TXp!LWV(V~m4G7dV`PXU@?@Y_Z&h*1?bX^Mu>)z>p`0e+~a>_M(Jcy`A zCnLb0^*Fx#LzTRpT$^uF!S2cvr-t&Q?$89^gej9TC|hmtMRu`GZt+9 zEx4VHwVTCEUQHI$YMH6LmO7v%I#t}SJ3LShSK6vLg|Vuw$Q@Mn4^WFI2a0|l!TvzS z%pSS?1-12Znd-&4$;Gy4`Or*K7Fq&ogpd=IiRu(ZLm^|7UbGI-HzIA|MolhMpx?zax|BUd>;V zZ1A_#hS$3-%fH^?QA3#=92&X0lpRjG^Kz>-noUZ2`+{*B@?7O^7~p~_$zx>?7AF!J zXfbuop?C)ZqI;GHi=u&OH~f_3-~3V;E`tm<2YBmzOgTUr%Kfy z=n9LnOm+Gj?0H_(@gnc7)HOoTYF*rDOOugAXyk{gfg61Rr7u)Q6;pn<0Z(n!JEf3r zX?^kZxe`a2<~vPZgmYDbG<=|gMpif77xJwtq*7FHbs^n&so+3ks2_=UN;+?MKJ1P_ zJtQ5$$P_JPn@qxhSVPB>l@;9K1(t-ov*sn@^H9f1d)=bgK~z~ZE+0*L4L?$_e54V= z(T!BCUMfuIe3Svvf3L-L@JC$vnTr-Zu@BxzMcDzk>5LxAiNingP-0x|ELw`<&XmRbGo2=MZn;X`9U1$)FxGf!DzF5g; zvIZsJZneh|aRLRJ~0ud{wQB3K7uv@ zM^5p7IW?Xb&5g`^LW7i^ym1F5p6(0Bp`07f0Tnu8ql3oGS-Dz85Go{0t;YXNa{}$w zeDQ{`#xedMjN(H=GYUasr^XkU6>xD5xfhgldYzraL~m>oSfIgq;fu#sE$!!KF8p0 z#ykvmgS)XJ#`hn*quNLqoc$=?cP}h}l@47xN!&c$Rjm;AE+)SXOTK``cmw#`Ff$`! z=lg>23-sTBzR{;Zby#1ap7x3Fey`z%XIWRi0(|f-+pM?!IXxQ|dzx-xyx!Y6DKL=+ zCxV}Au%3w6DSoeBis!wvlHBtydrt$Vhqh$gdHFj@e;pHBwfwz`zRyFMJwbnnMbU3j zH%DhxW;68bBSjO>Tr&;oslgLf{GlNH2J>dL^_;_AxvqPTW$%@?u>8VJQl^49oTPK) zH!Y%C`FpjRPSIU^o!#k7K;q(w(tVdh%vw%@~DFB2f-WX zXnr?T!7Jl1@;mFuk%sBhFmqTwSw)yJs!&)-ThCy;rm3_9Ig_lrS9ZE|1)^}4l$HtRI!X7`S=UtewD%&t4B8cXuguV zo8?B*stJ>|rGsOkeD(CCcWRn!Q`!>4Ga#%ex=VX1XHsF>!)*52Nn49edP|bczM={k z9&1EN#FK}Bf*nGcEKuo!*fl@e64#!Ris;1n_Zm+njvX^UYTl|D<&vcg#5gFdse{Lw zMYBi@2_CO9Y*aiTS1Q-Z4aCxKaO##M^giyXZPlOUUo#bA0-~?2@~D#+|HxN^BnG3a5sy1@oU81qFv1q2%|u`8!!54*l%6;RN^HlkAvn=Vkq%|z zYHB79ov*$|vVxea+Ip2(f&%KIjcWA!8XicCZ`#98{>G*8D>SJLABUnLswNh-f8PGf z#E`GPW=;|*ix}M`;G@ONSiC{4HdK~KU4EzNh=a)xV=_*My#iQ2zWeHUA9RNNPx#Y3 zgxH>%eI+y;ta*V%FA8^JFI1wk!53Sb{4PXz$gUhtPD=FUi>cQ8N+-YHqwxvp#DEx` z_)2I*qvqwt$NnwG=T(AJ0Y}+aA~WOwU6IctZbV4@D0mcoqQ@Zq{7e|~Q`^oJLV^W9* zVa)oWJLu9&e7P=owDD)lLsVF#l9d5I%eNFPgydsm@$FZsfmDk^GE_+$#E~K;Ij|}x z(lyeGK~uD!4E8i75oI`=k=@yaT)ZEh^#|XmL(Slc+hJ4hOi38mweZk}a#Sd}I8He`~FhfC`m*A@JaCD76zWaqK&N!K@r!tfUSVA-+NLdD|DrykzuSeZlN; zF*CbB&OwZ1;IkYpGV1T#QA4V8dC}{iQRA>EB0M@fyus%Z)g(xRB|9fZpMlmb@rj>E zMVVr@lV&UvOTg6lYyGK);=pHGW5?Ih9!Yna%~(4WSPWM_lHA}~^5UgFvjarsD`}-4 z%9reMK&O;o^x>3D!Py(#>Oh(GwX_|S=!;Fk(`hiN34It_@1RoHj3~yDqz1*2#a4H- z!+5cKICRNrwSR$ph05gL(Q!v+H1ddB?BAm$HL0W1{d#7a91UrU@XQw4s*G`8SeeK3 z)`gQ*Pi}*m_)^-FY=MaiV;$mZG16afz7eR6Rr<&WYcTm)4q;zNdxRtknZnKw6`Yu{ z>$S^!t4@3{ajgSULzu4|>yoK#IW-6~w3vy7DcBb>ATo(*EzSJL-i%!Y;Ys z1PQ-mp$>d&?9#V#H7+ILejIkjsLa*o-hw5zf-#x*_r{7^$8Pg3xQ#GUbi3J5Q6Ppx zvQpNjXD4<`On9ulnaSSA=n~3qVhwIoR|ItcwTs8yfjR3`m4K^9LF94l9dM9qw>j>0 zVFFxhpOj;$@#Y64yE#{5>^6JBZQ$zp2lVUVe`C1T;;O0YGrgUdj@@@ZxG%bie9ezn zT6}IWvQcs*I$6FXwq5K_2f>|2sIZp)g=7}Q7-ukheX)$5fe*nwqzvi~${7uLV$-d< z?L|87CjZ4P&EP^)%q|j_dm8(IuDg!u5uedAXL`r&wuq9-DD<(L^nx$AxZi?j&h-lf z-o|FC}(1rU-M`y^Ck&#t9I+VMvu2FN)-?iJw(#u%|8SR)|p-Mym#UCi#K$ zzp^gXpyaQymk7!73^9vG@D(nx!4*amyGKZqz46H8yV0`&%mldjS$zwadVQBpwxUk@ z@yizbwXih}v6mtF@$`1n-s?}Q<5|?Y$06PI7x2-5+ffu5Tz!icVes}a4L)dmhhBJh z`Gl65!wKrKi-*{Eka{Uix~p=%2Dc*}8f+UYqS;#OAtN#KHj`{tAWw5lkEg?E7FmKK zV=od?vSjE@^Nq&+i9tC~Z*0%x*nRrJih$eCMUZ$i)M9W0Y`s?Jw=$}?heyF3Pdj7d zv!EjYSQJ|6#$V)MHFw5hHKk%F%{O%7zaqc~@U&N3MG00&9LxWIO5l7LBiNhE#eTt~Fw4eK)ckGKN30t87{>cqkpT>RIe%LT(`r zX^a}r6&!0tP>uoC6y9V3fZZkQ7*yN;I6YC7^O!PTk z&v|!9#=*5YLcKrwrqHh?9x?Zmc^_wzCa*AQ;pf%`4;c#5S*z_TqfJqW}f@xB#V)p1i6wS-%|K?>^>n& zL5^K6$CwmC+;W4RhRVWG2ydJcMhpejl!~LKNbWy}fV6FmobxT|qe5fZe8rsxf9cOl zSo%vH07-28(y<$m2~Z^TN@%Az1gcyLt{#HeO{j42CWGZoXA9kd2OIbeJc{@w`2rgr zwc5t0kMP|cDHN!g2CmQ5L|L5cdnIfX8j>v>h#ySMY!7d?nnGpVuF0ZOht-^H(LQ&S zxiGH^BmFcY-G4KHEjWzO*p4z1E{*h`gv`~9Cx>f7AhyII??~_ShcT)MNbiD?1bPEa zuk|>FJY#fr*6E|aE1xG=JVqPq%te{rDCK|i%yH*1w0m7bpPDdcoTnI50=!#8-djNG*i=3*>$=a6iJdB(%> zr%$J}k^quVr)@7nPC>=Z$iQSychjPCQMqTIt7>nBUkKG2RK)%g7M^u_mGFWVXbSZ+buXvP^j6TFOimgvb?jR)8VXbW*eyc);p6!7(;U7_ z-U2u=KS-6rG;%WEqlB(5P*Da`9zpftEd(d>!_0U@V{Z}4cS(&vj=J`@Cx%0&-Iw&f zL9od7MF)`0oCgUQ_78r@J+oG~eeAs`-pMyHK_vI_9Dw9q>@xZnc-a{uB}ddP1tdps z3Ungs$@>PKOR=UHhSZrYR>QE3d%bVayD&}3I_0UgSTw^sD4jU`XmOb%)epY*SD(h| z&&fRMa(u7kPgyj^$+*rR_CE5FiY`=ty0F#_FXMq?}M9O|dSw%*9Y?T{SF3lz9 z=jzdtAyh~Kc4^z|WORnP$)8Stj@_#v=xMv$JmOcH|f&?B3vW zN?9#`lZ(Z!rKl&j8D>)N>Km?ET;Vh=+RI);YLC(8Z=Rp$iIdtEbAfOYBt2Z1ek;q_ zL-}5v*e7t-=9`7q>JUS3wpAI;Puy$JY1fQy?&Va>ESBg6PGf}U>@`HH8RG~~1(kSW zKMNOZIPMPIYmgl3)8T}k-7sK5tkbK!aNlc?S-)!)T+(a5D_i9?Uen1XwkVW>WE$tf zsi-0t@T0v63F1rkRfenSm|ushN2iT_>>Y*_!8}}k7je}lh!`k)s`c_br(Z?;_t}0H ziyF1PhIxvxGI&wk-r7@%$(ykm43!v%$;n^^N=0vTx%V5g6J4>Uw{Q}Y;)!dLqWyNE z90k`goLV=~VJ*Ily;CSj&-1NRRpeB&iC5@Pd6nf`G==u9CZ##14Hm_29nx(&U!y#K ztTU%AcAF_KzZPoQSLZ*sppU+UV*1ckEZ8g>j%%w)v9COUs(Jr>f~*zl3>N8J(byMJ zHuF6oS5b6&yk*Xw#mVuum`Oo}S(;kjL+qxM+`Q>Ho0ez_ua&5R6Zv6-oOsCPmV>&l zJcUA1Ji9PqY3(@}^}X#YPvON!2v9?m(Jka4DtYeTL=4~khKxne&O1q6pu5E4`pJ$n z$Nh%uO>B64N!_NGA86B~7iVl`Lw*A8c8~tzn%e0wrBQ_I?>AV<$FtWkf|msuHF3lW z+gH{>{@R0ct{{0D@HBrtXvouH1{DFWRC)u5QEr8NC)j4v3a};mbI?$mkBYg6ku;R-vQq63hto z_A?T=#fh?FNqUpK!`Qv3caY_s{C(8F?4As$j1iAoc-?`r2(qie2&sUZN8WHSN(_!Y zJmdZX1Xh&2{{g!}t;xr$cEQMv8_#YZ_dZh7_|qEY-ZRIOY?xaWD8nB z+}wc=%0swYrzmj2>06Sg5=VIIhXyY(9v?&)xIV-%;>KuI4Znhf9*kDs{&68A_+%u@ zL!S)FVNqjUjCG5euj!r9T4GMd4-IZG7=D(m)9&!VI1~5*I*|ktV++NI9T%!U0t3J~ zqCzZK4^}q+#T44cZWJ;BNWNTp1aQ#=<3?`0Jai1x*7(VQ)(17p`F7`>3zPiyo3r(R zW#uIs%v98Lap)xmnBQ&JTeOx(MYFNBaa39~zYh(yVw_(ib#AcaM_PFIV?TqCEC~eeW{W;Y4PK~{^t$c?&adIZddIO!XAw) z7n8;|Iig^2Fb(s6{l8TWB*HFJrF}B)4mv|am8Yk|!4|QfD)mz$b|l{UNFHPn8Q~ME zeA>_-Fq5)CV~Yn0S7Ouez2$hO8jGWXm#(&R?dD|wU#9I8pe1Oa$$ZH`CEfBX--Ot= zTZ^55kX4-G9Ic9T?J;Vx9?IA)LM9<@F;rNc^WQqG_do$wglnbB7q~|dG_MCs?yyDe z%Mt1ZlFKV;XjJTo-@x33esDC8xps{@KK}jpfrzY zCGJi`w>U%hKQS3J05Gg%x<+-kPJ`8Ska&a~0!?OiSWijYPIr{m@VhRLOqy3xf(Bn_DsI9KWjc{9NSzy-txVLWc$M*N7q7QO$sq zHBA4=H@wt<79rAUa0gL*aE4XgBsP_O!h51Vla3NMKVS@p$sBFd7?iZQ>idLVg#t-T z%(-cv<*t$^8RuYuRw^3gu&6~8bsN;=TE=KcEQg_fLF;1A>bBkPDqV4TC*|^L%Xo_c z?bn8(HIwhyzt?Hy#OSPG*pEqI_yv}2getlXNj**nX=l5-q2VbQi8!#SOu?5kiel*y z-6n;(&L_W;!q^FgaUeMgE78xP6YIM^5x?s;#Q6ltsB36TRJDm0{(hV$+PbOLweHTHlDL3|UnM|uq@dYLb?V!h7DI*7%KZ1!n|>Il~%xw*Z)2K8UUpcX?s zaR*zlTIQm;i*F6HFgI{kpq+UnT!HTJfns_l>D^ElxLATKhqx`+)l(_U(BufSTgj>A z@AhR5?Q+CuN@P)}mrrmaKWuU{1S+C~^%@j8-Y(FVJpIb=usBJ-g2imN>3_rg}P7!B`wKfrt(Z2mCN@zO)bDRFZME$*V&Z`#2%h8qFzxaj%irx+hM_w6 zEAEjox$(A4!X;Hj))`Y7+y`;vF?KP3 z;1?OtvPFHRX>L&$?Rc}RQWNDJ7XI)?d3p3DLhTum_-ut{hLj1!a2+{L7$K;BL$qI( z*h?G~5)|V5i4C`fpjL~iuWC2S!8 z$cnmtkuM63IB=-ZzS$CMWBZ$QSwZ}~gicsEdzsnZ*bqiB7M zPw@{7hH*@cry3gqZICVvi=Mti$bnqkVDghwo*b^NE*oP!edkbrk*+J9EK?>R7&*M~ zzR%G|96bl1$2<|iJ!7ho`FujvDLMNjWngDDk@g(MEKgb!0{frG>$S}IXW2_1>D98;WYzCSVE>xm?| zqXuj0Lvg=(9C-c1;AGI9@Xpnhp&mp*KNzLIE5#b{N%9B38kyUhGc6&sHg&q8-^Uo z6vCerI%UTe#^Z-Ma?8>1N!&s${0(;_PoiHjo#kunI;}$o8E`NdL}^-2gPSxZ7T$)m$%BawrPGp|VL%&Oc-yd1 z*hhdO%J&gyai-w;ByN7h7f5YspTf`t_Fv?AA8ho1-hwloxLU(K%3(#%?_KKTqq3t*tUu)l2fCA%p$tTjL`xdo* zmG37&W1@S}Cq3cMcDC|P*$N#pJq!?}jFrFJg&d~C2Im8XC}<>`>8?2Z!(Dl&!d_2p zd6yN?5onlU7)6FwuTUDy!EfCbjF1i92}<}kobfejn;3__bK5*>7$zIsV3<9KJ6zk` z60gwO8cDNgLS30RAon~p< zTi=F&40QB}a1=M(fcB$8<$?!oG8y4;@?wix7~i`FcfpY4E5!XYhk+Pdg~BnjSJ8E8 za2CuRN-+h6``rUhQivoZ3ga$q3F%mhkiD43o|A0LD1dGBi63kh%F{+2?nh2TL5(io zC|@hd9vmc9v@dhW9!|3C=Jj}^DkL#nHc>6C*(Ko9*q#&`_%e<{508k0R;pwTUK`?KD_N7Xg$n82m`7KzzGtRYrCi zTmxNGM6c8}22RWm@gm5#zN-MaUw@uyPeu7V8L#HC*AJP*1o@>Hp{g1(aa)rzJUUcD zpoGdqjZd`Y9Og+gHE=C-*=#3(Q-6Pp5n?>H4tw_tT4y%eY>wKR9Y7c|Sm{tY;YvhX=9mZZpgPO?uu%QyAAHbtW;{&#F7OYD=>Y#+h8rJgf`cYYWWkRrDbd} z=y%}WjBXz{`E;0bZ!G?Ctq9&E)J6JdU6W!){R-IToI&WvUWHtSVm=*pFU~2c4QSsR znkW?WNHfQ0=*0%LDE`f+0~A*De-O6`FW+o{uikr{`llPv61v2)k@&d-C;jfH4zvTx z28%_)xJ2^U+J!toYSCq;JoP~07M8l(Py~cIBf*K+JJ@Dq?lznbkkC*gAC9?(4ky;o zi+w^!+L91)e_N`pO^kqho2K=?;WE6;XItUN6f*`3Kdxk|x2#FCXFyu4J|nkuLfUY+ zDrTbLkGJoeRO-@(xDZ;X>Ix4~iIDQYsaqEJA-VFLe=MbkE7_MtQC`#RI{<$$n&M}A z&ICi+fpr;17Bg?(H_QcDNh!$Zk`O~9o+D)h9+4K8<$Ki#NHa?uk(?B3KpVL?`wwtK z>C7c|0RX;B`wbh4fNQ+LJHC zr8XunKi!k#p2}~2-UQ0xETe5;kzrP?#!9nU0Zy3lo$xNiXxiQyKiF$9hIG1xtxj^X z$PCYneFh~jMUW{;LAM#6IpP*h+3a*MLaCE@$;cLnm&uXTOsrb1oMxW`mkO=1Ct{f~ z)xukRzXK}N=&~_GZy0`$gKc7DuVMHLN5tGnT~EUy<%eP!ThCCW4#|Rz#1DaF+H+T9SPWqURJ;QWS^Z(DxqTpc&Xd4nYsWh$o7ApE9h?9KU%V>04 zT=ONg-ixHpL)3%A6>F~1MTdXDc$z&ACNPF>%bB*(BSu~_3dXUAt?rWjhB{#8QlVOz z#u;(((6$fz4H_S({mDRM#DWrOdb^mwPV&6{=~bZNI&}SWDVqSC~zpY3o0BIMHP;H74W{Q7mVye11EMyCjME_*%0_fQ}Q85R#$S*E@^2SLW^n`DNp{-|H4*;V0WXq5|6 z)JY;kC7JBq8l7$SQV{bJxs6|_MLF!p>L8|ICtbyMh&)EO88w9Yw6AH&m^TkxJIOJ3 zUbCM<_c2355#RMiu;1twTJTL2_&zfb7P?!#CN&U)I$KTh1NAL()jOA>}%z`#m?z$+F>manrYTA;N*qeW`dxVG_2se5Vz2v@0-BIko8=aGj)*? zV>U#xj=7Hy)VDylbGJJfF!UjR>uPA@=-4TtLBbaSsjaXk+H=m}sv|zm>JA#AOmsv+ zb@}j`+iP($yuLBYMU-JSyDl7;m||LNzLO&mM?KHY{t7P?Zp|(W6hlFXH;$=khD>*gJxY~U(;|_Y*$~iIZ~sXWiWQwS^(y?>^!1YE40~~A^l#ILfVv) zf~%|(YIp3_LU!||m`}@xa>>(3tYMY*%?~x4R8n1$`9PUzu*><2y$UrN9v-j?^!CWL zu`)5F=H;7ay6W3z=LS?BnJyo+9*A4JBj6sPRfM)!Qbbo$btFcMSMz=*b{FSdsj4zO zH)G81KsQys58w|dq1pFgBx$nqdo@jovqx8V++14U@_|d6b~-YL^Sd6FPA4%xfF+`V zt;m+xZNh|#Nioakl`yTrRk08EGrA4MZbiQ+-w0t@oR-@QDkKg|f)UI=>q2hnA@bHY zX`rrmdDJauIaeK>KLo~JBaCvI>O@Q%C8sbrG2=&txY+TijR;cZ6#TAKGRIWy;I07(g#@@-)GHYPurr9$J2J2jT#^v(5jr4q(yCqH)IXS4uH*8EGc%0 z(coAp1Z@doUlVeL4tnY=Fr$cfA^vOs29CnE?+AIrB}$yL^{OOqF*sSnqK~mT-=u-t zast8B!w5;fge3wuO5#~e3o7b$)~gd+qg~IX+$|Ew^B$wQ9)_3 zlXquB=$Gg_?*6%h{y#$3xqbclmNuo;?rsgM7D1ZK!+NS3bBU368K6dmD_R-*-or4gGueK13 zKsKDC(ExPF^3dxKi!S~wuQi2nnTdN*3)mRi8@tz9Q1H6KEft`*Wiy5+hJ2aX+M)V&m>}%}Z=g=#6pX9sd~5?{F*>TzM#Y zbD=KeB}I((cJWiy4IH&9;?QhufhsYthU7qgna8{Osm?~64k9}y3qH?8`Qf%}O83bm ztM`<(RfN>&r>>32a1^Oqm#8QYk>I^BX`BNX}HobVK2@llcClz3&Q*Gj%WN9wpt8 zc=sp0h|<@!9u`KB7wxRta9tEgC;*O@f*M|NU5v{WH>@BSgU8vItc_{_-N3K%j~$#; zyG4hxg1E>B>1eOxrQjw3rAeM+v-5UT7@OU2icBlMtEj6bq^l6~07<1e^ffE$aA$^O80CGvgs;*7;;+E*~FgG7Y3VqJmP)T{EN8_h%;#Bu-Y)Z zBmoKYXoiF`=`oebd5rZN`za*NK&}J)4bINc*l7Z3$A8G1akc|L45;OpNKR}Y_vx)1 zH$g)Fi$BX|jHmRi7{viu&k7cN8*K0Ry~{MckA0)2k3BrAhL%-WW7tKXvJXhm5tBOU zoDZ2bR)~{IQ$mU+jk9WsDqHAzaYefqB!G;O05Y;^_ZlUx;a(^?cHn40$j?d+!NRx; z&cO|6c(eA_?Tm;c+fivyV*mL7RS{?zIu*9svY{by0}C09-Oa-TQc~;H3XCyTwQiz! zY)G!r>!0-}V&OE5OASZ4LDivb z;r!S(^uHRI{ykL>&Y=f>pn6+Yykj-S;#j4|UH2is2Hy6zo=q&(g2u?{Z;;i41Z#04 zCq0yf4JUIlC29xnq0U={Sg_Xb(@*5vKlr`!6fvMp9I2HltiHh+j6vCd4~>)-hQbHD z7miPZk&qrjp&U&<#}n{F4h^}+k(j>XW)Lhc)s-aNMJ>+4=95rc>W<36h*MV}n?lK- zM-!();*4T&ksm<~rcz7{AFDXnqG6AaF9XG-+ZO-pJ97mooZ){rx4-`zF8_8T@l1U# zfB)Zp|6g$uqlpR;(v0V_sg|yk<@ug~A<=7iONOi&#rys{cyi|S*aEw_CXM9y2|NuX zPI>Vg%;VLLvKScqUMGAdlcF4Z1`qNCz1*6)&6YGU9)CNv`6t|ILdy!q{xzNQ#4?k; zxK3+e1EuvTO5pW3(X$mYW0a_L6J?M>0#>Mru ztxu(f9Uah z_8`6$gjj-E{=Hg}0tv0*6R{K2l8EwI12iHI?=bUi=D z@m;WR>PTd-2mvXT-y{z7kBC`vQp8RTiMb#! zkS`0)TXc(_-tYc<^B#y1DZBms-^~TpASW3L&ldl%LIJuIHeVoRZnw@Z>t^+(xyzoN zo`?Mj@#nA%${oX<_Cm70EJv6|=~o%jc3>Ce`$X=%z2j0sDY%3AA{FMP%uP>mH5!N!JN@R4hCX!rQ4%UN##e%!|9)t>dh4KH6<)g|^IzUVrI;{y3AER9FDr90S^hW9N>s-3)w!F*YM4jE=fyasGd`FWV^jh zoVAeiV+;yF@DN#3UT`K~wCVE}l6{D4T-?RnJ;dTZ7Fq(P-6;QbYO*l zSO%2x@yZ!g*y|}(=($yeC|c?VYZBq3b!|5>=1OcvN+wRLi)`R2{Nr?d5VvX14@U8& z)3~Yc=9krm52vtZbi~6|o84O1)Z@-mj`ygS7UTNz3X6pB_QqCs5N|`LDt=9A#Ty$D z3V1$wyJ>`rAVTShANvyN5=B=1<(=iv6^tLl$q9?5vG(x(;iLqjKMdTFUFP7(=k)#y z2}8t5G#QO9DI|!?>`vONLgpkAzMeb65V|!kg&&JP8U=U*rM6kf@lhaUe6p@Cl7ZOh&IJNLhImQoe-Yd}8Ah=-7CxiE{q=fJ}3CyQAm)*8q)ocD) z;)Ag^tToLiD(n;J|8QS&wGHG$#g&vGT-0aqz2r(`MXqm&egM7>_ zoy?bYm|l$$#ziBUj#DX9C6A;-PBwOVk;E(-zX10m+=ghC}5$VP+%J@@{y_Z z3FpWQ>m1=&z2-C0m^)2obOnvGPJ=|u8C(k3turZwMeTlaOiBY@hR7a(n~U#6BemAZ zk&?^gBBT_;5>Z{?zU}tPF`RdtC}1X-!|pE-bp-wtIrr$7WV>e=$P=8pi5!;)|0aT3 zThtFt=tDI)o4|23i!P2Q|Bh9}#CoHp~mQ)#>Q3>-RkPk2_ zU?S9xP10bMej6#Mef~0VkmOzA1%9v=Bk=x>7T28|SqP6-?`%@8xJUqpQh+cEiUq!| zYPOO3JsxZW6Q?tz1nHH7Bk35&ok5Ht(^rclg(^mP7|&HoOqoJugg>-wvVF=;bRIL=M*;+FMQ$y zw{6H9f|@ZTH&!2c+Y&<(9yC#32CBno(a1Koz2Obcx-ea1Reo3@N(#!G49Z7UbY7B=NNH+Imr`ph6wfKJzV4T*%P}rCEkU&nd+!RI4SfDEY2b9 zsR;|tvaUZC4<`w_$ziDY}M6O9B8m(L+`w&&GfjWE(UJBJ>*l~Mq>DK?TZUoUT~ z8#I+yC-6H+?3loIj37M5iP&m1sNfhtIJpu!pX5}XP*NJ4omK6ZdsNy(Pe2{scY9lY zFTpyoq#;SjHGFr|wwTkCsPVq)9gWU7o)FF#iqI`Aee04W21By0Ux-VJSAlD^gY=+> z@@h=^sW6<0S5jRSInl2I36W1mgR}mH-MfM_`C=&7Z9MzaQ9E{A>Cl6vm!d zL%GL5@Oh$IPF+ygzu{qU3?$qTD9x<)*p3b4l@}X4Bfj|ZcC z;3%heB?@=fk5v9n<=T}m?3Lk;^sB((AR4cn2_8aXs7=_L+xk;=Aj}f7k#zBA@zZNQ zlEH8rDGzI6Y3Xm^BmIfu4J?T7aQJYgv+FpA4_EtgE-Q<$st=?1;>6H8k9m|x^E?Kt z&|1Ygql!#OR8qdshnfSGFPyl1Ouy)mrQ~|PX;v7EF;0cuSjoi6=Z>s|3k+(D8r)A) zx+xWQLq(iIj;@4bh}X86HZAknh@~h1yzYw{K+zrMm;KyUi9+L6K`(V~!xQ&9kVb|jsGtyFL)E=>Oy@d|4d|;clSVbI z9n)Ab*v&9`kR1^U9gvg1jj6TZBLzuHpAU&p#`Oy1tL=h*W8%c^;RsNyM9U|axPz%A zfTdaBVO_(qpm3Byg#;c~ifG`WjqYnJ5+9U?wcMS=YsmRCEtuMj6f-< ziaB}KfL(Q`jJK!5r#4JTd6M5jmd&a=o5f);p0Pwfp>U}j-3f(2x4Kag5?VLJpQEAA zkg+6(Pa4hAxn&JhTQ0K3AytVoLtrIx0OG{TI*JnR)=Om8d~$SM20D8_YR-q0A|jfK zcg6gfky;u*QM~qLOn04Pr+V&qMkp7?R|ATCe571ZYm~~1#VEeAF-E9m`?#`hcw)%t zD9ICs@HGu#ffR}}N-4(*X~Ge)9pfMF(HfyvTs^d{6n0mSgluA6=P(`rh@bAZm2-S+ z^tDytQKxN>D(9I^wN(IHKlT_@GPrLN=0L3e22=OXGdz3qIgFC;Xbu<*HvPMMFjvjD zNCJPdd&KSx=jHfDIIrSnS?|`ikBEU$3XA6<-3VOMyN}H>r?zfzmamuXF5<^@JR_95 zNRt`&-}Cnxam&W$Pd_N<-@lUl??=j){*_b%#G+J=MubtK;;OWir}8=BV)?~t1^kJvh%N7Y>{aM{h6b$JQwM1pdjqs*qW_ zomhX2#`;t>|3HO+TzwIN`b%IBe3Q@Y7kf6jB+rMF;Vx)je3Q z7IZpk?079vX(6-_SjccQ1NCLIA^cwM95+Cspe4ETfPXq<0OSwZE}lt`*MkMI~(_ zquC^Rpg8o_U^zy)A8jV-xu(AXg~gzSKd{aIiuFtGQeRm0ino*eUA?AyuP|Z;YjgpR z`{eUbOuZe5=h)T==Who%HuwTAW2B^^WzV)wX9?tiyy|rNNld&f6PQu5?>2w8n0&jhM>vDMAYy z(BRk6HKK!olM)v@ zsDGRy7s)1H zyuzNEsP!KbgiKIq51GEB`*4zDJo!S1+f;NTwwY+0P@2ERky_)(QlKd8&agP^B4m}% z9Zd)ak`8F%0HJnDj5n}oVl(m8pG#@;xOsS_?||5@Qhl^{SH%TzNaitsfr1o}B^RF9 zJ>sKw`6PNdJ`c$~aH-r00rG#QQKXC@d;v)e8-4mPM{nm1+GOGnLj{v8bd{dILRKS= z6a)tTgH#2gj4fQ4t_{rEl|{zWwtlo3XsYiyFwX3OjGB-K#CV2FYN+%Sqp&LK?+!UY z`b#uCM$#{{{)qA5(wtYzGL&#W8-Dm9eXBW65L&l|*qHRJDiGHkLPC%}j4m9Xg3`?3 zJqfQRfOzDR#(&5Wg6uRgN0vPN#Cw#4axxOjMw5vz5DBR@94Rfk zNuJBJT~JsOV>4)1O>9T`zKzqoJ1L<@@X#y#bUK&a*g(GY;4Ma1EHQF3+WAIZo3UZ2 zjPRkP{#VME_)x^wJslgwmmlAf2JAROxcho&Q}Fx$4rdE@l=(tR2uh^5<3(;VDIb&u zV&IgF*7H6OnL?16w7ti3QHM}i(H*9Aww|qj;T=0@;90J%gf%ezduDiWncA)T{cm($FML5+2UcngB^>E5Lqx9UTZ5$4};Ok%VJ(5Kf% zJ=8QKhPmS!p=xo}e17B#%I6sW7G4+?8ZOqmehHE8XzGF8oe@=vLJpB(jTkp{L>>>| zJ;{`iA0d4R!la5~fNzDE5>2!${x=sx4f#U8u{tcm8uHD#%Dup%d5468dVOW<(>4Av z1K$b)-rr^LT%=0PLaq=ofy|*}LHr{?9OmKTwuw^MBK~A9X6Xv1Vn)@D9 zv5xEck*!}Pb_Le%jX9POZo*FW8REXb-v zLQ)X)K%XO7(k43^ytlGS4E-1zi>^~X6tX0gOeUIb1SwCIG!Tu-U965nSaQj9%{p93nz*w;<{i7FLwN+uUU=4Uy$R+5c* zW##bz?Dytm|AE!;b&Yb|SG$8(De%+n5_>G)TKV;Dr@w(g$AB9#Zo6oc5G<{)V!#x* zyL(evU$eqE&iD%MlhJu3`P6ZIaHn*^TXi?v*@`ZMvrgMSAiw9Xk?fDO;uErdybvl} z+;(*L0{horRHqA>Kgx4G5?zSEPe*b`BV|K{q#vk>yq&`{%Kb>3MLS(ScVr*D>n@A# zh#H1`R<~p__!jbeAT50XwtW#SJuT2NYZe!_zuLuhlhm6XkFTWzK0JFu;d?zr$7cH=|B!jOnNt zgPb9m*^e2BqS3fDJ8$PRQ$`o3p2WiW)=FoN@Dt7#eMH32?8y&z$f6F}J#x+PXOq&1 zQK^e;+KnDG<#IZ;N(WCbSh{X_isWMxG6(Fo;rL_ zDLg=`RU?hJ&&%0hEIwUJASl=^ke(Sf^Tlv}q(`?h?ls&c8bfw(R&!+RnRQQ`XpBeU z!s8>-L|kuwF}sBLZkg56NK6`t;X>CAXxTJTKM@92kN&rlSO38b)a~;`b{)fQ)K`dK z(j&`Rp$HcIo1>u}!r;_>i6K~;Z{Ih1vtwN;U8FU;Eb}234@U8g7Pu9qWz3vATw#b# z5p++CywsEV3J!wW@PRfqWJN4D@9~egdSllmCg!UpW1Ku#&Ra@iVq~g+br1O32~z1G zvp1Fi#$b6&4~In;XCOj}n}& z5cutqIxI2gp=3sM9{xf40OhB8=-Jv;jSu(Wz=U|M^4jWFn~J)Ke6|ybAHMnUN?1aSKOWn)tbYnxXQ_1+quhpo6vU0z|ETbgYm-qBqIQVpv+0 zoES;7@np7>kno}N>qK4PG&F#%6AYgA94x4ado@+s;f_K5^9{b;F~~TFd~Lv)e37m` z!XUh|cS+gz z`vy~&+XylQ}w&0^4Zb#-#_k3drb8mPM!2#Ns-s$f&3%(t^MdRlz&~x zwF2QaaE@th{|=COgJegaP$C-3$FMxT_IXXovQGv{!p?T@2YWYJ- zUvrEM99=Sl5Hk9})ifA9!0=?RZ>!A)v9j!J&^f12weB#?l}DSS4XWjCQyrag+Y-a; zCpr9)Ivot+A8>SSR;j>D9U`7a^kYZUKyOniE5cxb0?|;_i6dp;Bo}zH?+<9%iMXjQi(YI={21Lg@KIqJ{t52pTIXicLN|Bnsdntd=HegW;{9*s&Bq&;MGKtC z7vYZ`mjcxl@DN(%Kt5AliT&2sugfj8*Za!mua?_mvGwr{5(ed-tA)2JHWCjWr2#Uo z-osgKdl5(mmihwX7Cn@v_jial;4CIcUrt`wgUEaD&(v?@=l<1u)3guuN+nJSkQd>(EBjK}Z}a}k5~jR- zaf{P`RmY!$=k32U_usHz(2U2&!_x4DJ!UiziYemrwjxC%Zm{%&_;+&mey0M5G~)#u z4Fq9TMRFlq1A1tzwFq9We|S5C3zdChmB_(G=F(B%L>JRp-HK3e;x3brRRLS}ss(G! ze=;j4TUdSJjZxHkBfh~d#N4OE~_i3NrWl|^A6F=aa*P}XCf_^{J%5g=OG_*& zF$QK)B_?D&c+=iA%ilh>eA5bN_+t4Z?E2XBd186dp7s~E#`E+T0AWqU4`W*TtAG>5 zSUY3-p$$ex(6rXt9KF0@5%5XKYmk5FU9GEo+c*jsCmmm;h#N45e`QMwJ|ZSIADkFj zK+BJR+eCK1<164h`4i<>n1O%#K$CDMtz@`SiJibrF_2&||HAI(!$A%gC>3{&_lJr* zi5&#PM)HWW$j?>DJgS za8!Q|Nd}BO$Z1GWJ|Jvp^sR7}Lw8Q_6`eS<2@R7P` zJl_-#=SzwAkJvodgMuy(g)@8+``a|5`8>UrR`>o?xwe zk`PCsPwXO3PIU|G`HPD}Qu(bCK}-WAe(!`u{ZiL0u1~%b;(dc4R=sfYbKq3#qF*Bz zom#p_h_jDSo5CIH2m-hwbQ{|()-CsS(by2aCyl53pUvqfhRy>o9o(dJAW80n;5^bK z7tclsz^RpQhi7m4!y%W+;A~ioQ2u`|>TPPpeFa35s6*i~QzVAdLfsa*5{j)?Dcs~~kB@R!>F`zQtv0=HW z#412gNPgxDjU23%vws|X(W3J8Ixgvocy?F+h)0Qp@*KwiAG=s>P~T|V+Zh;5y21|e zMM?hMnfUm>P$&T_r~elT#xT*&I#u_d#rN;QDhFR>KIhBSILna{w1}!;A?Ltk(p8as zz8GL=2P(85zEwvoz}-IS;{HWGJDuA2wf+Khdx45_tMywMh4voO3fc6A~TCQ=t5h zodRE`{Py;@e2@=LsUB;b4NlL}qB&$om^6>z-5K0cSJyZj+}?4Nh;aHP5HChI@9{_y z*HiRriA%U&g2+KWmQEDYEd_24Vm{T2Lj0Dl8Z%s+7@+}_x$S8~ml!n_hNJVgsRE|x)*iqyujOM>P6n(*65SvioS=d&pc9M=iR2ZUN4eUQG^IqCkcJ&eQ zhWK3_@)4jy2~n@@+CHVh^%IAw3>TXsl?X}>EXf<>aB(LXi1{7RqC+AAx!OQl)ni!& zF*5p)!Ivr*KM6?)jFGl|W)u-(aSDMl6J6k)6*rn|Ud^~)+p!T)KNokhLEWLh{~vQ3O1EEt z5u%zuZly{$;a&-;4~9i|GP>vwDSd!5^~K?n)Cn}1e2qlY<@TC-h{2g=4~^{v-{^rC zZU(QZb-ofyD>;|F92?2^v0NaN=Ki9!K~IGt0P)o-ahOB015_d0pxn)-!V;_Q0$ZG; zsP5hggw{7}y&=6pe~_Pzuo%4W6@Ich$)}^JVjyDj_~kP_74kW~|9tt>Y3^>|bHC(^ zHaNRHo*efgx=$xiTdu0z{IT5DKhk0rj9h_wSFev$ce-&6hJyWe+IV^5jfwDH;OP6F z7{wxhm?e|&bcaiPA?zLEB(VD_Ui)|wQYI8VFiW87f-4V<18d7LuC}#OLc+}%QY9z> zH>n?}gAo4_g*zld=pwoF2JuJAU*d!~2^kPx-09Vz0C=&tJn%aso({g}*mL==n(|Ss z`(jS@TI2nvij6{@idU$rA#ax>hI0%?jC|XG3adhWO`=Z;-kR^4fqtM225xjvjqPAx`sm~Iv7*UJES8jh$ds%N84~m(jcpi(*eJWnq+7?6z{Ll>StGVL zEiz)|o9kSM57rC(!TAh1x0dY3?K>Nnk}7&lPcoth=MT<_6g- z@l6?WBfM)i%N=xJ4yAC0FS2sCLM24fjq4~8xIm}#-9wG$GJi9dlESXr%=}06ctJj~ zO5#S2E$D8Pps+B~ic&7Xr?iYwVdSV2C;%>M z%UX|*+>}{*SNcw#PW2H5Z&i;6*oRyLt{m>n!AtGgIk35X*)-aq zLKiwBFG1qu;`*ZLq=yu&X}#hAC%sI6A+MYJ-TCnx$%3_yFVP&B*GwTGp^1gG3jbfG zjPnNW37e)9zsF(0Cb%&ZfUgBs4n01-eXz_3mX`i5aU@2B!KD1h#v5eKLG}z z%8jFjNsJW3uiimLpme}S%|D{+9Sv#X>|8>P_h_XXiRcu88I;f0AbY!QnY<(z@7*Tx zSveF7;DuZYeI&900~cRNPW<0amkNc>m!)l*Wgi*iyvR6iY6WJ0XH=y z`J$cLKJEr8c^aFAuP}hCUzrlTd4TaKv@L$jdYv3eJ5xm;Wc}${t3XfSh)&`-5O}#s zX&nTlRb>tSdblK{Lx2`6UZ=t`-)g+C=c__=v9n@&g(FAceN;{^IZu2{$jy)y;T)Ae zAXd%)YGi=Hu*#Wod$w-S#_#|BAO0t#7&SA~6)-N*L!N{IuG{YUaAdVh_(jwgm!q@a zl9QxB-~(MD(|WevZ74H|)Adpz%fc)_oy_t{&M}A&jp1t#YTtm9j~5(qosGYXlsY(q z1@3X^Q$nvp<;B3bs_vK-?60AFqIBE-TEo<@{p z>!OwhWC%RT!)hswz&iZ5#E>kY``3mB|C`kmF$g6BmEE%)>O)u5;x*#?NuFqo(YRb85aUL*c)L9)o{fZmrg*uyGs;zbO8z33cfXuuAuf25&GcU&?f0&eiK{ zTbQhIc0DSu`q`VZ8+*CF9g(k)qFT=e)Zz?At1=7?PR>pC_9FGPzI@4VRb}ldZCiu0 z)8=Mfw-OhRyPxoAlrTJ!f2IryKc|Ai3%b*+`3mqco`3Tqvwv!2T~h+&C7&9a-0zqNaOZ%wdUbgAd98c zVNod;I0;;qsE7Mxl@MYWa0uvE^J>$CbwwD*7ZQJ>6D5A_s{^cAqj(QHHl|Ku$R~l6 z^i}<(F6odl*ohcL*CwJN_L2h0u)b@B`<9EF8yqGa;<0?8Fsg-Dd|B`f8!YBKD?H)~ zlcRm$dqQ86c7ePMD+mPB2^>C1$o?>zZWU_}LrTP{g$;QhpunS6s&9FJG~V-`By@t4 zz)m&@SHl;T8dw_b$I5B-I%<)vyt{INE{fip1=arM2qaSfIlW2o;+W5?chWOb=90OsZkjw`Iswq4rtWrMS;dzi0SG%AX* zz3B@NOh+Y9a&Ho+1mH;!?(FSw9FTPVAI*ajXO~r{0&AF)>?+J2J^Ux3TCiN^>zj7X z*h>zPtiHgDl=N&qW0Z~wYv#*?Zo865M@wNs9P%6B$AKy^z8;OhR7I^k7o6dXJMsQu z$)t2SzO!Bg)va`G+N_Nkyv|dQFp)hIepQv9rgW8vqVH%exYx>Rv1p)s*&I^AkPe~K zSyJAjR`Uz1bZ}6RxwJAB?jpgk|7C7?>Xhty5l9^T?g2)jm0iSl*dTbpsB@0?nR($@ zE;ysn?d=9}8S928c6%X`&x&#An&3EyT8&4;zHN9_kPic^zoG+Sw{Z(HC+y=k? z>O|(Fcp-QK-BlV=WIQyh6>d{&tq><(TF8k}AT+tGnl)tzaaK%~rGn}Np*uzq$vWjb zaO4F`H*59 zTVY?m^lE7jv$ocT!ET&sa=m=%ViLd$iYg|Af1T(CPv?u4bNKF>o89^^X2po1CL)Uf zsp9MPW7b=Pms}JXqaw#1*w=_A&%1*P6$*^i0mJ!=FGM$pB@DlKWqqAI1PMNja&0-3 z+`e?I9`xmPCzLDQiBd~qX>0hajFj*!gY~_(RLcsuvmC!H9g_!T)BE2hc^Cdez9WT6 zN2_*q(z)}>N(M#=u6h&OmqR`$c8X&J;gejBhWQok69v+7m5>5NbIHA{86`B_a;j^} zdg*SU7$*akm1+o=#F%}3I%Eva`hOdR`zhoC=`>g1&7NB+q43`EL4I!PYsT@6c-m1H44GPUWVg|TT0kx zo9%83!mP3%@@3&=_{Tm6^;)x@)KtJUe)3L}%jutVq%N&nL=ky;5J%)9k^ zE-G=nY~w{p^?{~MQlK}XN6FwPkCNcnz-ZJdxHT?DR}?<@7!Oc|93NtE?luGKrO+5R zn8dn7Y?_o_-Lp2_?T+h%%D{EK+|@sEQ54F4O6~C1R$}ny*w$C0k|#~0#9n)C^$Bj% z93h(}11Ji>)L0$IeBvP^NLgG@kZbZtu@9W7FL3)sYrOFiC@Hw|Shwo7u{4YZCidg& z!1?iZIalo(>DVKl!F9WweS%l_KJF86tRNH*Yze|+LJZa~M8FTU2*FeN;^V5>F6+lm zOLbLaWLbTZ+JdMO@7O`OH2K@^Xjn|_+tD!jYAHszz2EgG90?y&f7BNis()iszjcZ~ zaz~mvd%HjqI|+3Tt7?uUgqK=p+@DZl$mhiCb^IXQ+X(5Q0Xz4$Mu`OoNkOz(SFahS zASN}1E}INbXvhhI_RJNkcuQt~8Enq_6Y&%4Dfi;%AcP{6ma>X`xy13!8Zvo&Yk3u> z;yW*-Qqr@Jg0N^^Ru-vj-aTPQ^m`e0>pgf49diJ=q2{**p z^l0M|F*rjSk6tl6`)yR7)2JfOv#IzWW5lH|9GeH%u{&?!EV4IyrG{^?7_NK$S5qqBFIL8IypMO#;^2hjoIc*+Xlc$7r4EO8T*&ZI zEKx*IO>L$+G1$_%;8OY*x4S~7k1mSCg~NoxhZsgFYKWt5vIx`1dI8?NX36Ku4Q7c` z?73*MPz^fkmRu%$OekL=GYEI!k-sCA?qdvkWC-jzVzOte?IU;zVqu5e`a3w8Z%OZc z;W$SqQOc{LTT-GZkT?~&^?h&#@J&m4va_Z%e)7IkdoUy!>DP6A_0y|=rDPSH$rrz5 z?`+d3r#j?;OybSLU@b8sVR8q81_fL!;TsRBMLN4jRB9LV@6D^z*xmogAY`GzUem~k zvp*yo0gu-m{A6F~CJ`)-voFYU#6?N*8P6qf{33h<-QV8izvUM#wLpzgu)nS&dQQkO za$3Rv4zbNuQTWM&#=_en!^nBj9~Q&nl7d|0OzP|=5*teed-6aMy}5qcRukBhYN_)< z2MQG*bZddmZT;K|VJH=_jJ_*h{QIs zLd^sfkr*Kzh$c0e*wCxSA_xr;3;%$VUm+F9nRKtN=9Z`0;0z6;+a1a*#oUyYjq1A{ zYf16h&QAN?dxTAS0)XL&z!xoiEDL+&_h;lAQYHA+zu^D>lX>yPs8m8T-KKi#*G;4X zgcKp;R=1Yp)h_tSr4d^#Uj-UqGfUnIMGo=TN1}nng_3Jb@?5AS5KBFll(xiXLt(=) z%BrcJbh!imLtXB$sVR4OBqT9ySxz{#f>~*M6z;z;3LO4h6W!@?Q5fy%Jua&SGT&38 zDgJc3W2*qnL2L%%i7zOs9DI&c>77*A0%k6G)!ZSwPOV7eL%T(vsUSpGi z6o#m-7HDAr8XzPirw8-RvikkHdPFxUE{(#OY7YhSnOQ>lb_^g~n(+>d{ZtASNC@xa&LG}_-MTxxeLLkbT`XPh+ydp!_?F(W+@iUmHCYW)N~mbxA> z64@fPAR%Lis4u;0Ya?`mg)#tT7ysBbD@Dt2U^+C5IGqnoeLp|SZ>Sv+*LMimMkW-p zbd1p+4>#T&{f5A>hVaEBd}Nj>A}U@&qK@*ikFEn7>#Nc@+Z-r+3#mFLpAedQ?Aoj) zjWcwby3WdCeBDQznf|aF5e*?-M{#*I9(CP>H2%tjeBp3jukXRoZ1YcvAuA0d=q484 z4rw~dqLYh0!^{=IM!`vPCBAS}9DM2h_ee&FtD(KX;d@r-+0 zx)XxB?4nw%oJe)&+%a-+c?LzVKN=4Uo(bYf!ldq5X~Pln`7LhULCXN8#$v&u3R$IE zr!!o7K`w1Rtzq0v?e+oQH}2A_>&u?W8a~f|yt6c?8r#Mi(tGJQ<+z9*4PQ(7hW>BZ zi@pE?>j1nt#hiDq#`gKbO&C&hh=%|!QOi9~+cNa9IpYpK?d!!D)fZ zxxqQNHB`ijPEju$p$F}IL;zJxT7{(VgU@<`^Tz|vMN#m@) zQN#;zhq~hn;i;a;1qnNs`Xe#&%OmPqNF0LNaQXR^M~S#XX*F0Ia+nwT%3McCUGY@D zt%5sk-Qs>?OhscWzYkcej_bqPE8x`WQDc|h9a+fEfhqX@4OsfKDd=^mfocpZY402+ zY$iw6!Sx+adbkj#_cU+?Kk;fLNAg`8U8F^eoTCyzyyx0ThU!iOm7-IqX)P&GfZenZ z0$aB`Ta@9Hn~(?O9I7!&Q1AG@h_msjEd!TFt3X_9Q~Oxgv~$Oe zz&!03Z#CkTmr$}W3XeHQ>353vZ!dUn3sSTP2@4SeFdvOOP(oy|R$Hzru_ZD|nwuDm ziLD$AItUoGTN^qf7V|kOaxezX7cObOy01S|YAL`~i7Vz90{`=bE`S#{M znWDmpy#DLO?Uw8O`mY&Kuubuo{i;1xh6Ck*YKSh&!52#?)k%#C0|kOcQU<2&f;Y>R z!TL&3?OhIBRT$6@BA+A2db+MR6vo8cCn{rlfqanz;VU7s4ku_ZT$o_~MQ%MTa*#$z z>150v#{_-VL>K|4o!Zh9DxBd9XtgmRfgyaKDwFXWi`9h~Vhf=+9 zZKXnEj7YX-53FnhchI>J z!nS6H)*p*;pw3>Pyq_2-{ae;y9 zmGPw$0_4>OyODPz@qKy-UmkU!!e3S!TbLslvT_3-Lh}%hgyKOcl9&rJ8Ra%oIf7=P zLm;um^kqg4s_7*jf%llgPL##UBJlTpaRF!gKX$09V_=u~k2uXjEkSGxYQFYz|1gghP&J@fQe7uD*Kl0Ha6}_q(Na2PGACStXcZ?Wz(dSuxrUZJ1rrvXI0|Qm z8VgrKc$}XHx`d+lgF?rDOMKMX&F_TeIm;tANXRSlC&*UtB)&K}gP(9d-%NkDccsSn z)$KOBg-afhS}~@oIp}@sHH+wuQwE+ zAwsp-10RGq>o)7@uGvPX;R~Fe1Us;IY0DDtrJOus-$hzlVn)8m5o(PZ4?#;pVf$3< z_?9xXPpol63sT(t4;#K~jbyVVtXfBMLSc-nZ3UJ;>JkQK)yQcwMeA{`1<;?OaUIZ+gGNebjNuj~V9ly#1;BbRo_VS?)I-5M{5 zt|g86f`le?0rsU?n6vaUBric%$a(|CjQ6C%MpFw!GXBg79sZNt4nTs@UeiMy53E6O41cLD)XbYQ^*TVnKYWlD$sFky!RyBFzq?&;i0zjk*t{!Ec%$%uPW& zt7byH*u6)kN(Oakhp4RjMwVdOEF+g;NIcR(?`%|A*}m`w!zH|xHemLqiN*^sSzmzk zC+|c(w^5cpS|q(YhVua5Nis(4HBx@?gu#N`JTAACVg{<(#0d2hCK1StD`_4(y{DSe z2Uc4^OZ4NYRsJGSHcAQVAPbOL&BPGuk;pyf;U9K%^1%w0k8S|L2R5{Cc|h*r)!$ja z05Jy9k}tNLJl@|@462sgqq~HP??^#7nz^QP-U|k!WwME9NFM{$E}>nfQUS5fzU)AM zdLezV0ZU?()pX`yWBP#o2v=3?d41&q#}Jp)#_~K6OV}>1_&Wv>K4k~-Wbr}p?+O08 zQ{rV{(wrisC#8yXw+Kcl*X`aKMWd2fA<7EwADo7nrJz)Ru7Q+;BF-Gre0D|lCKcIa z6ddT+iuusOq44`NdFS}KFLLs3PdRxg`hfq)0{epJyjp&#(AkU&tnuDR)sLvljNC9U zt#-k8K4{xJ$zr6*CRnF6!XpTmGWUYhuU`d3X$bC3Te$ytk!mc{q5Cg3if_1-=#hwS zPSz==@!mrvPOD=aq2bbf{u37%AGu-DAsWX$!c`feC2zGYFRAKij75!ceZ35MN8tY2 z4sgBjk`#=7FVFu}-XNzSx5lE36~OaJZoj=~1x?7ihVoynq+VHOefdl;>G=OVcI!HXvPItA-G>@Bm zAzumdOOA?pG5-sd4Tbm4sCa%2SK#B{m#7XueiMBO)Pi_q&lVLI03WdxB29{3hO{NH zhU(8ysOe@^7=$0tu#u^dYE_MaeBm=fawSRwtP@*f?Il2xPE@0Gv?ZK+_r9Lb5ntkT z6jnYQ2ArZ!kC4d(Y<{_r9a4-Dj}F-;j=qHR>FcCYs{|4M{$Joln=f{=-~Y$|>%Sy~ znj<9PXwnnp2qKzKM||%jY)o{7B5OSBCL0-eLVWR=W=1C9h(;KJC2vP1 z`kFKn=tvXkVMyQkIcXo;Z8fl^SQ;Z~85sqeRlD9MZrzZTV^GI^=GHMVZefJ{A*VIAPBpir)&?I|6+;q{b1>1~RIWloV>nUKJEyXb z=#GG65aE07HJ>4>se)*vTo@60CA=5^x~D-*w;D0bKo*20-waI(9)?S>%Qw@yWU8{k zxO-4nhDs$c7OO;Bgz2(pmsfd6EQSgC7Qz+WLyu?>P8~^M)WqW}yzCs8-DU;4*3^6B z^-N{&ecQj{@sZDzHt8$Yv4rqBi-F#@)H;z%wzrR?7h^~gA}w3MnP&Z&#Cuc-@fO-I zL%xu!YTovW&u27!4Ayyjv3g*P;Ri)k0oc5`qmzR8KH^FXi9cd~Rs7q#SC(+fe6sFlU?$%rT}HoHd!6t4PdOk$?u_wn)Z3k6)jQFMunt~pPk0ubjhe{@KXVJ@NaKO3Wlmy%BE5niVX^-cx6&1;=ch(@kZfFy$q zMh0B_kk5}9DBMw{74GhkO+yt0wwu|B`FjRSw7~eJcDkX$@Beiv-QinY+{Tg8;^m|s z%eV5;`ba{PmlO*Ir?QknMvZUIOmJ)(e7yzY3*5o-IU9bAt&}eZ^4}0Bz{4{h2+Spl z-&`SohRW=~Wk^pBF*atH0*#I7-}Hr;_3*QBb0`ceOm1U*jrXEVP2Dd=tp3cjX|wKN z@b_Nr<_(HTVvp==0~c`3Ea27QVIW3~IejiEYIqom1$@DL?+Wld#irov4&Bv8H5uWm z1GDQ$JnZQBHF(#9Aq1|evf_c%Sm?aM2{@7s?hq7$j==EBS7u;Bp>m`ee0q=`Y;C5c z0`bk_C^I~Hu@d$DmB=|l%-oPdGgA@DKWfq&HA8TOX_S*uvoxpk*283H= z(ohW|u1ds45~XoieP3Eks^(L*ytX_D8XNQYJE`=PMC{lDGU~whSmyow3FidxAL5+A ztb752`(rA~4810T-MSmzuGpk}$#4cngu5^I?Xh{A#JlW#hq(9CHX=%WA{AEr4t4Yx zeC%NhCQWz<>Bm%a?)W8a#cm*?5Tw8xsV4O87_3H;)OlT0OakyoI!%O#!%zUR9h0R15~`Cq zXn^!I8*rjxQ^nKeLVgaq*1k7V;;?H?;v{l8Mtt;+uY>RAqP>BxUp8D!jV%G}CV3Dk z$#_8*%S~J?1ns&nI~Q^upWDM)k=D^A(I2En>eKDcvpXJ+F9e$#SDIkYTi97khqlp&31Cg+0g}Q zxFpH)Eotn;LbPTe~pflA+0vLj;-Mn~d7X$-Nou4Y_% zjZ%liQFd_YwA8${sZqqRP-E`VkhBB!jSJLs4H$$p7$NK_9B{feBRw5ootmJmZ@+bm z!tT(K z+E%+@5&c?GPA~BElz821*n+gch^uTK3y}2!6>3o3RdaMNpdzL)o)494l`!tca&sMx z2lvp~5^gchWXEw1P!($K!QXIF7v)|Vlv$JK9RL`8dD zUO)ybl}pM#6Y7d1^5EMS9SuLQeg$I0W}-Fwn2T?y{Wp_Nj^cx}gTEBI^0v0y$6(ZV zj(!U_cSy}qz)6Ud4C{gx4B>_pltYKaQF3r1k{o-41Xh-=60ej^pqx}v1CeNdh(+@) zG$lSDj2QS;*oj>PYH1(D_jnN`Z>SM)L^(x0qCK;4Im6Mb+djt1G>gDoJ460>jU0Qc z=qZdjLg#>aq#bk8=`@uL6cT{sWutfkXwwUJ4=om8iZK*R=<7c@s?@-$xTS(jjNPAS zr(lRXWP&)>56TE6GS1dE>p>&D-89uFro`Y7x5)~wUa)uA$_U~_XVRA;n@6t?&TVuk zr@RvkF^87rYmmvrmHm*+L)~&9Ibb}87@3}R_NMHHN5@flFq5e$i^19Y_kXvAr}70H z{rCn1S~3%yA!yCnMab-d2lK;2=>7s{_LcSdYn-7LdGjF2F);`AX&`GRHN@w-{W~3U z#PB{wdzoS)G|(u6B~H6(Y!!il(%mPvD!wb3G#T8%#A2ABP=s}m@j z9k=*@v6c0`L&z|Iu$pcuZh()d(Ww6*?nqrlpITpEwK;9(i_7NDR*VvF({Z!YK+K>U zr26yJkb#}THchA?A%Vycg>4X<))!5z6yezV__F1!`hqxpZ52_2>CA%@lZr$zn%cxq ztIBbRaO4I+0rlqg$&KNf;0J|3QLplmwl;Hf<*!g zYRwc4;{qE$$NxMj^0yujW6N87zN_Hqqz6RYn5rTz_C&sK+FaD9JdOrur-Rt9eYqkf zA)nzvpm#xG>rz0p&luuDqXTgl9&tP(l^%*-w1CGNS^uwr+_niT3gb}B@R~h?LAa~t zwo`_|qDj!!jGLDj%NIMP(4^g04SmVP*{|SnKxkn9tUC@dlVBX8v4UdRpBVnd)|NM$O5+9GE${8j?A;tZW zD3UKDrCRtk1r7gF{<%T9Lv4w~1@GVU;wgD+J;0`mYaXimt$^n`qA0+Ux+ zGn^2y+7vh^ixrF<(dwd>7?#%;DZ_3blIY$tIsv<5X2M(%dFhxyxYS~(;$o(eZ{XMD zH)f0zv z3JFEdtM#H<{;l&DBYlWbT_C2Pu)CpUjFh1ne^~rrh6E!h%H7~PJL0h*p2TM5D=*K{ zavq&XMtr{K!%TlMc#GB~IY^zW<4xqPhwLI!9)>cBnwki4Rz}X}^T0&Hf^PBFZ0je3 zZGu(oVJKv{@rsTrZsZIMgv&jw!+hmuj|-uxqOB!(kGINth-`Kt#-OC(gMpu{Cqx&S z*?Gh>#GMzn>-rHK#oEKNiF@)erZ0War4fx(3Y*}k@srZbSjh!O^2H7qG8L@?GjSER z_dE|nB-aOHQD@-~vsivTo`@=h9R*p^$e#Gf*E0sraI;FA?$J=tJlvn1O+!TTr%WlrWS+<`6^V zW9vPMt$0h_`yqwM`P<=H4*Gm%n-h-)xUH3s(_KsnW&b1)5x;^_4?e051s1WLpR97^ z8gP6fOcXrnl~)v2@);*N?Tf?5S;8B)HW z;NcLb(y3pC6d=Q5q%Jh>9$-9{x#4h_C~ps3QXwx0@@zp8zO-zV2D_Px_E^rAS?4}v z48kNSCwSBfG{$x5>>3?Tpkqm3h!%>l%Gy930Xq^bO{h9;-7Jdi%s*;UowwNV{8~)ii&2ohJxJx zL0$IWK`=%lM;O&V>zBP;^~z(M4_H?cntWC$nzxAPh+-alP?{99ZIJx>2%$(%t%30f z6lTMN&7Z3VjTdM9xq%7&B_sqv`u7HH6KvIq!A{;l!lz_|;{@TGKW`*DzNn~75hped zRRsPVK3!;xH#QNN7>+WV?iE{7Dhj-2t|uS%!ahK$un*Rg+5zV6U2E+eMt!iu#|M(gbQKHZWA;3zJRR`SB2Cy`x`M369FMEKvZ??t-C={)B)oJLh@bP2D`QwS z3%OFLQVG^qO1L3If{gOvQQUJ^bjupdOu@@HN=5YZz;AkimGcNs|GSpAa^Gv54ypMPYx{v^=1rr zP{^e*F7k3ToYXW9VG|<{D=D;G7{c2+;vKwF1HXe!fz}AxPs|tja}W;bZ`!GJ_2zmI%K)+U@Rj`T`|)Lk)Ew?yVE7F1 z1)ku5Q}gFQTy&6KDR&WNFfk}XW8w3VdgJW;62G~x=zj;cQe6Fqh+i$_-cVX&yJ~nT z5hueVA>{_bE48q5QCbOBwJ|8i))j&{j%ntnS0Mq1{CklnoRna!?_!bKc#egGo1~Ar zP{$phQIzouf8G}bS13V%Ui84TOzccsWSBJ34fji6^LMLFbptb<@0mEUV;zwPkC1dp zL37JK-?PE^J$(`3z76R-hSkR{R&h+*!(cGp&!DhEQH>2kVZfokP;;1o6L{e+?g7=fR_-#kYm zM{EPpxZ%FmEn(kYP(d~=V&7iGeoT47e|%myi=z%&lF-egzQF60@7f1kg{WG2P%nRDLrJEwpW!D;lv3CTqijH+FrUWsUm2cU6%cLUf7_+&waK%U}_O_#VW{AP7(`PN!At&h%W=l}N=c z=H{CU5O*lEx!i1E)?gUuIIxB|DBRkPQ-g09 zmHhp;r3(sbd?+XwGHRS7(Wsr&mWiAgsk_(|)8pgF?sjAvd=<@PoOkUB(dh7P6Vhpn zi}&wGL&^({5}UmnSQVpsJ#QXdKTTr2+ocUltTYMl##bP|sYWHF(fDEdc({HHA1dUZC_qjqjwr4SuyGhb!U=LgS~NkHkqSmZDc7TLoC{mJMzVDq#xy#zdUP7^C@ag^o(=b`2ghellEY)qD|< z_b!zt&o>7AWSsc%b&M1g&jW~^W#h!W!Dd^e%-@ikqH~FpI^WvvKb_0Hg1PiCzABOb z9cPPkQy_X-_G~DF|5h}?Wqaya!W2dgAXo~7r9Z(Zg_lH)fg_#OH}~MS+VYhXgTNK> z_&JCJ(Y!n%^Lv5*aCvm1bj0t>rUZuA*vtDvr|9+u^=U`KyRLf!#{8I$j)K?OhlQ|q zP~p?ay8m!?xHDX$Ovp%q1{M%Z2DBF&LIB~B@V+5+NLehv%DXg#>4kPiv{M zVuBW6rfRN|p0<$ep@V`up)TcyR(L-?JKf*i^3E>jG5?n%ec_{dk^Z~ zNL)7Le2^^PdHq=O5;oXX!!ZLjxoblPi2ejb8*p1u?FPpCz!z=UL+h0zf8dfSp@y|# zz(xx>w^(tC0}SNb`^5<=qqnXL0dYK`DYw+_rRr5F<6p6tHTu{lL=r8pVY& zF9){Ctj3zqNmhu-4zZyAX<%36*JXufrVDy`@@t$v`64Q9t~V{$wsY;wSa8 zs?F|RcDXI8jrG;VTks8z@wL9|`8vnf?r^3_#4(VsYmDoU?PsRVD3G|-PwlPFyEmV1 z>vF|gP7w6@YfVKS7C> zR>GOZE2i8YlQb)X=~SWnx5&JxnWNm$PwR2d>ctW4(~=8oaID}DzxIubKc9_;LHt~# z@@k-NN18dnd)H;mxrae`*y(*K^P=x+7xE(OPmVSwMMji#<5-WR{bDe`_Mq1+8>tIm zS*{ce$ak6=1-Da`y=h_0#bb&X&_DO*6$%DAm93msSe(P??G|l{f-!s(qSv%yBE7RF zx51L4h@PDzNurBc?-&aI^P~t{)g0`_kQ7z{G}$-7F2v9MQ>`4r^}~$*E`w9aw-|xP zna5x}x-Jlzzu`U}kB+a4W8rXR>nhpCDz7g#A>ScU+#+WcK}BBL#Axn}%Y2D0TgZce zX%QRCHz_(rK9+m$2O}3dNTS%)i z1-;P3N|+tor4%Gqsv(VV+Va#$QsK0vCJ{5kFd1K&_^?~tU<(!$&Ia$RCV5D)^g859 zcn2Fy^2@k64c;3ler;nY$`n4Ql2?LXXQH(&b!I@8rd{J5onThP01x{s&Ub@k#`%+@uFW&cuX9f2t z@mQAeLFope%LntIq~JW1+uF9((dfVmVqZLw4ucL7(UPNu)uW=OKoNl_|@ zidf7Ru{CN0oVK-KF*V8!E;3wTh&b4=mVr@?48Qhe22@*68EqR%9pXgWLyX{yiOcqG zw*mde_vYCY)}1zQgqp;r)w!b{zez*tg}is2!)b4m*9A-ds_IN&($Av;$dNB_4;S-= zGprOy+~p>6~M14inv1cwo+Ocr7Ey#AKhf# zi?GBDL-^w8s-Ay>SLKoKjQH=m2J36jLkYwgDGMBm6mKeG^p}B-fOVcJg5cPj^Tknl zr42#)XH=oI3WO#S@$q!NK>F4sWDMo2EoBW3U%X#l=ETC@XvJCVx%^-XO8Uj6dPZSj zFtlH&WE)W!ApV;rWE51Z<{`P2eAP$QZyri>-1w zy)7EYYAZOy7bs$S+<;;OZFbOcFE)}dsE`JQc6QrG>W{=v4q{Zr3rQdDtDAcL(Au;a zg`wKvt0nS|MEudvzgygd&7EI|><{G%&#~NE3LAqxxfMQW$O3`5&m3Bx4Jr|%$HzLm zIKGI8^^gRji@2IdK02X@H!jyb6}7>`QE%drluEOv9TXH2#Dg*+*c~wh^@@DCy{r~1 zCS(A@j##Xeqb{~8lv7Nl7$P@^(~-|{Z9e3Ln4l^ddbe#VM2r`cPAz<54tmJrYD;v9ccg+80V7C6kQWipxoy{PVsj8ZE)v0-c9%bcA~1 zX3c_#Frte&_`+hkeuVC{Zm7->M}O?g1!>-Zi(yDnXt13?LS#Am25Mpmjpx_&jVO@# z=?m!4o*F&HUw!P_Ca432VU9M-&_(__BxE?nBka(xiUuArj@TWVU@RkI&9N|WFGH^o z!{LYvK@9%G?Brkn^)32LM~cQEzASnuWbV@T`t^8O3b; z+rO-u>Im6+d@^EG#YiYc>o-y5JtT2J#JsK-UEBCvV|mk#-zMcg$JG2w`m7fEHPa-(BS=z8C!4v%%cJOrdrh%cN8&_vdMs@W)>UEKlw#@HIb7`_gRsZ+0~OBka}1C zm76!8m6=8nK%9Itykq&Ym41!mKy{WFwetmHabU}}@u3)n!0V7x;jC&l^LlL?&KvBM zsz_W2!}zyvZ!l4RnKuWpnF@AJv`!j>Pz>^-W9X78$x+$O{kZ6xG($k*_`9hEEV4Ki3eWy0eh(mH&zx%?{7M9!pH z39b`5mWZD;IgaLcVOe}(V(!I~vcO=r(#U2gltxR*W-ihx`r0U6xPLl@%|OA&SxM8~(!z`5USw8jO-?*y)uODcn27=~?%~ zX4NuR2srD0$neelINN0F7+XKq+7}`!0SnBP+=9f>ElA6NzH#(WS~j)>Q=`MzqGNyH zbC{P~`7c`>$YZo;Q=W_i9+!|60wq-SXXxg<)fC>pW)p^R$Pn?Nx$D$*KBwiSG1~Uc zVKPi3OY}PMYWC4>L(B#q3}Bgx2|z#`CW~rbhYS%{QfOFrTdtMHB73BEO4vfA5>=c) zaX-7Gir^bW)4#HRMvX`#IaLwIMMw;xl1XNd_TC_trq7a*2$Fl~?V%)k9f%Ln6G0_y zdqxoNQ%N6Gw;bIB_m%Yf1#ilV{zSouJ8zJq0bL?eWQ_Ip1yQ+MqIu5jn(yCW_)0%Y z+HUO0D0w&y%(|LG)uZ~LFxE$V?W3BoIQ5f$Ul0)6mv5EKssK0G) z(CyK(cxe39H$_dxYQZq|IISF?Q%mW~83ZCxC%LqByar>V>aa+J^9wJ80*8h(Y#qs~ zp_XZ2O4A8hD)Mem9X<+8;JwqBxEDf(ivF)!Akkyj|JC_~)gT8#&mlVn(!@|3fjoce zhQyYi0?)Z;;)QS9u~YEXeDl@r*eYl<4M*caP7i|uCg_j~6oxz%mo+F%A*HzN3Tu@~ zvTftSDkQ1sPeG{(XDQE;U|p49Y=hX!`1aE`WZ6V0D&S$Cwu9;~=9NhT1M&q1)%pQR zlX#W3D;Wy0%lrrFk{1!FKL-*A)jc?)uC<0>cScjK9bW%zeU{t+;3MA5tF>)eOuVOW z98gt5DvL`{Zi^h_(NmamEVk~x{22}U-SKE>wQz%l-1&D0I^WyCyK^O&5lv3wZC>ZEO_gi}0}qk)P533=`4F!)rEpGc2x_M0OroZKvq z6@yX`Hsaj6&lQMPG2t0}fdghpIDX2Cj&F^TT)KAJO~jQVdSFA9&q#WYAz7u52;z2UOnzhSP9l5}d}j z)F!@moDv-7w5jJaG%DtwR2TtR@GLB81oOYSjEaz30tr}1VeVVy7pdv{85b&8wOb6BpnLQVpoPR8PYjF6 zY!p?=l)4Q=dJ0fmY^zDNV`-zp8NNU$2ze&f`x;l)LT-vb-_H<>=8G9=>InO3z)2ab zbNn|ZBRR#Uit&8wrN7(QIIMhz6qdw>?#I}1@=ch@ZoOOLe{m}*oZ){Xi>`iHHs~2z zMN>udg!VA0X;^CQLDZ=u7)FtxTgXb$eXbhAZa75u`IA)%A1EZI=nvrjhcANWKd{p1 zK(G<}Ig>-JWa#y;;n8iWE9A$$>`yqXocs|H_9j0^vIeQ71D=9JK|95j#XuM7h4iu0wfxjeJk4)V$2<$AaX@2Jd*~ zjMx$#6SKaESWAxJf=}3A%9Ykyx^RVeO!2rQ%Mo4J-Pg=o+u5QJ5LfOT#RZ=LF->$r zstf8t2JgNX^xdeNrxQp8m8=L)0FN|rijD54koe-)R*})!axq|Db>a36i7&)ShS=hg z@f+eDH_gtgp;PbSWn`g3A`B#efnD;sZylE!tMes(KXF|as^mScfQGafy>hJTs93BB zjL}@%4!v2m2QuN%bSxRv`tr4PvEd`eo4yeJ3OO#MNE)d=V@eT&UBoedcZM7nr_amV zS-ZNSEk|s^4*7#s4W0TIQe2FlQF@CsMrzi?=#nHZidguw8#pA6Rxnf`1Znpltq#R> z!8cJ+)(spHQjv9yBmhbgVx7&6^17tb(+zSvx=0`zBlJSSrtp_yF(FCDWmA71Ew*c_ zV#K)o+^v<8ZC$HWPzIpK!?gWa*?c*LGkgI`lk3>2^A(HH3e2V`fTwUIhC5%%+>Q0} za$v==XIR(%AEFojscVC{ynG6Hn=uibWCLIR^C91cqE?)&Y)Os6pf(pfJmC)O+42=m z?W^6GA=O4l*brc6vc8kJjjC)R!^R2HTvkrR%2m$ zM}34LlSZ$&nB)gn1;-#B4~m3Gq!&0PsMt_^54-#VcIE4Gy@o8|!foA|4 zhvtz+S*&HlPa%g!AJr%&xc0a_#M$6q%Glrb<1CjbF~d!46a0vggO-ZNJ0cARH%`Da z#RrhjFa6dxRfb#1bXpaZ*#^sXnXEPnDKo0(6G|%9mXMYh_ha0>P~0OozIp0%Jq*tJkempcCK8@PvW?5{ zj)gLkT7!Y9JGBVZjv?Z!F#JnK*bLs$?MIKO)lO_d3J2=C(mN#UDC>2*K-7~KgTlM|HJsHCqF-?doQmftJ9t%#Vz_Rt z`CZymo3PV2I zmXOldSbecZ_fM^m@K3n?7m{W$otYAh_{-_dA-Ba9QlKOo;Ga!QRj@l3*-WxZRMv!| z82$Z!|NZ}wQ#ad0xG=E}6am~_1H{;SXcWpP6`aWzIvebTfc;TmhVX3gh9z#7pAxt9 zk@aj+B>fC&ZN;MkY1fS7;NaU#`ehHj5L)`hfsxySFTmU#3*dyr8QeHrW#TVwB>&Ww z^LqIk{`*%ZFBlBL*j&}9OO$t>7t0UHTZuKf#i-E>t?a z*@)#D`GU%g(R|0{a0{L2zMk=-C2p$IqQ|whCl~|xVF|6{-`nUU@hWse^gnEO)dx|e+DwV~I5qVK_=jPMxwVfpeD5z7F=@=x4 zlOlrA!q%e{>u6Y&=`UmHXatUQm7@I$#tYZ%(@`m0Vwpim9d3_t}o>m^`f^0me~%zpL}QyrTN9%}CTKJr2q+b5cl zeQ>h~wqryW#y`}ClgBCp#0ef_%!PlHzdM^0r^Vo$8PngLW%`TZ#aVwKx?y#=jyy|9 zFfl30Zhp32aa}b2R*9%KI)Zinila-ahbGNtMTOkAq73QJXX>&NecF1#2R`Z zVT9Ox+|Cd=n9=^!TX&i=g~+WtTjsYL!e7IzC}BJItMh$et>7nMhTAk8j#!c* zq-b8QB`}ajJ5SL7fP|+!ZFJPe6a^mKKv}Dqr!@x>jk!$;4`gQqSaTHmG+$7=fdg+lg33J7JCUot^m- zvSpy%<$4Hqqc=ta6AT)Ar5g>j^wgcHUM2^_EBb~}FE8`)dFPx@AvlvSH2ZsUANXpH zPWQ@}nR!B!{-p}T;p)#&v)rPS>ZVTRL*v5QET_}z_yW7rNXf+Q zF{##fC4xMBNWqzW0YRh4rqpG=sigN24nRaX3&}P{^-3wh^hgj(n~)(2NpY@H4Sw;; zls&vfdqN^aznTI(1F`q>rOt5$TBy~*N1ArT?Hq?|8P`i3xdxwM+=8o(H@Wahp(GaH zgbxt1XOyFl1s-AcC$kHHGxfy`DZ<*z2Y0dXsxXok+5V0}gX;zt{SA7^+5j~%l3n6* z>IW-eI7G48Y}XK-oEn8~s35x3g#UdUJQPCP^oqNBUuuR3MH9ozw>ND}r5P(Kp zo?G^~2QR05u}sSZ$6`Ulzu3wPP^4&-imAU3c`S}sAhE&#yx)mMM@XghVi=}vb43b? zVRF_Li0XtPTLsHec?S?XX@U#^q~XZ^3KJCZB>oZ3C&wyu_GcrafNk6vJ#FEe-eK4* z#(84lBg7WSB7TP2V|5R~PD86#s>?gFS05uXFC@2+s3tg^tdcJ{lP|tR;)HkhPk8yP z*07;`Af85Nj7OV0D6cnj*S%Y#lwJ5^;Qz4BA~W9>KcGMjSM8S-{2#4iz@vUq4yjGy zBTs|!tc9ok$1v71?5InhaguZJBU>%NDZ!GE9iwT&1pAf?mK}O$GvQx17@KraKUTL1 z|8Pi*fkHc^V%K9zQG?w*^}>;(D#25B0oxi0&E&X!wEWS6Gx=f#&R>M9 zUbrzG{{=T{IYn>BcDCgk0gE$hwgvH2>K+JLFpPe@6D3vp@Doq#B1y}!n0OYQ;r|Uh`}4HpU>)wVIKuvmhK@H1{&WgF~|1Bh34Egt=6u6CUFz&A%Pl#@k7EvwV2l~$mI^Q(w!m@a4?2VK{y z{vBGq1+Y7RqRYhnAt7xBLTl5zo5l(jVx)-X;c7X~4890L6*w&2mNTQVzoC-ZI3EJ- z<-B)x5*5<}`X1LlywL5A{;2T& zoTnDwceI{?$Zfl#Rl@_flw2`O1q9C`cx>TaxjnI!HA`fq43KaF=Wrs zsU*!U^OT8|hHq$?c*VpX$`=FqL|1Xq*9R<*WGcxL5M$lsBV`{m^P*m@Z>hRRnVJsxDMJ1IpkI9VOB2k-f^ThQ}S9(K`8Q48^i z00_xDP9PEM?fQ)Nv%#&I!l8tx6{)RH6h*``)e%t?l8UgtlvgeBmm{$uw}`ZhzDGA>jztXLpz0tb<#_S^ zqkKq3Q$8pEoNsoCj=Ngc)`3qfK8y{_D5%7zzyFt8 zxZATbQqB@mij3;|4sNC!+CYNA)lx|xXtkHagAj6xT%Z?~@EOo|NUX9XJR2yb%Q#XI z?wL`$c)o3yeJ+#15YndLA(oq_|v_245`^A=kSU-jyB&xfK(Qace$P13#bbM_XYxo=V% zmRcBX!J`#!KguWtXw$y*ZZzb0Lb@&L;hQJgBL*hlEb#d>4~qk22T_UZ0Uyd2huPI3 zl#++}R&&sX(-e}EuzZj1Lc%3*XRoQ`+r8)QBIuw0tIKn@1a6|(T+wK%@;w#s#}f9 z{*n$4jd6T-b^D}i*YTHdQ*=-*J9nuG1|}q8$Xg=TWxdEBxj8k?@<#}tG&2m|%QxnR zgZ*|-z-Eub#*hRDE`y7XhwLRsm}OX)R+13Uv%z+bm~Z@A3AsYL2ROB$@Z}^c47+`U z6aWYq9o57-xNc=? zqQ?_v-aps;3C#>*m7^+F9+dE?bPjnyK0!rCgCN`B)!=<`9EpvFOdvg+Rb=Sfo_oYn z`MPs!#Pyeu4Mb?NYdBN*j>IeDZ^UgI-y28vICCw#+Z6(>y_Q31ob_60uG&mg{_b$Q z9tCF^je9_!kHUMYivVcw@mK}6;|$?*=8GFp^A9YcqsAgrz?72OH@w3+NcoJ6*d5tKEW5>YsE?iu|NNKD zat4N=;a`jfWN`hg*eVFV&=-tvy=0p9yUB47@e^lp2A|<=1;C> z*+45C(rNz~i`PBJ8h=;HMM|B`KqO*CY)Jf^87f>+5hS7ub48V4*;9^n3^KL|{tXT3 zgx*UFE8Gpnyr^6bm8(}EQxk1L$%_9!X+2gCUKdVws)vV64e@#66OEJLp_e1(RpXgC+8uL2a4MfwNve!#B`i=M^#~l0*4t_qE;vJe98y^ijroJkC3`)(p-% zO}m~#eTFMBcFBBj>=7P1NEILHO62@EbJaeE^IGV86S_Z&~v*DF&*6i)6n4E8}h$>`A7Qa2RKMe8%rgghPDbGk>{MguuE*#rHd3PWCc!C{9_QmO0Rn|IED|a zw8!#f00%g}fDQ=%?!SZ{4GiL+ZC&(s+HlrQ5!|Uzo|tmKNTw?W!bVhdB|K93$t52I zANexrRrBS;Cn=kQB~C^Rg=vY0H!YqZ5Diksg{mz&&21Ieeq)BHDE?b;*3k$GRnL{X z!wg0wWg|^3p}W~U?%0O$54=uy+%$YYVuYBny<;Qz=lr{~DXs>`Z=*3bu)?|IgvMiNHyFZt%@Z9P8f_oy)l&g6@Il|NEUi^2RG9+aM|?S1)~ zNfsI-;)J56WmNoxv6Eiugs8(s59aZzyZ}s+e z)>OAk?9#*?^qsW}__jId@NSVLWzVh9gu5-dl8c{`CLhYuke$)H6(JrX1SV$1@C@p?GG;LEqp zep@+^y&YhON3M6rPr?ftP1uk_MtfT$F<2t!J)|aKw1rwc@fV{nct`3PwEI6H;oLIC zDxkJf^f{MXzvSN(DzMOZSQ(|$ZCie?P2Hy!0f~hpb;5!kEo%OQdco(QGN^!%o zPz(|yghv!U_8vOg*^eII2FCP(*iT*OOZ+0Q3sMoAK0y0 zV#*xD2+!we4!`alg{($jc$AO`E}pQ5{5o&9R2hh~N$=B+QSnk#1f%lU zb5v1UV8t|y028B|NTk*U1NdjZcxsLo?dKu)6mTYApg_D@W3Q>2s;KGyeV}&?i{q<& zTy$w87%aO%F>og?`;Zf)KP*0-^p8)d#$}XFXqb<+!D%8qmfD6M1M_n zgZpL!`iy2nfv8dP@qKwQS#6~qUPXigi#ci)^SYlO!EI8$FpXYuEF6A{d;f8c+8UN%qXD@(JlaE34NL@<=mJ`T)C^GE^$ z#_~@&`h+RFgmRG>GB}`RaVmeX2b@2L|Eh)VMhTw7 zM=3ZVZ5%lWcR#Ejp-!+#aKsyMOje7{9OkGXv;QXtA{F)(p+hZG@V*^7((tx+q85gVvrA8l7vHqfgL#^r--giiL=s{I_1 z?v6NwQ+9{8Y|HwtWjQP{k`k9lFyCf(_44j{yIn(jSch$eKZIN&Bj|DM;<4t4#CY95 z62QlZzNdr5VVpnWXt5Z6PwyrJX{W#cXQ`Ll+Po0^MHZM!;PgKDAKU*>p z_@biWF^od}g1g`q&95*DB?T93)E%m58l7UmnAv1_4I}sx;|dXZXkRNX5b>_eg7^S! zMBIA&F;Fl%Tkt(#+4)Sw=@BS6_*TdD9R2MFE$=Ojv8|QzY*^3qflE1S9DxW2ReiX& z_g6fk23z-*$R++W7t9HnM5r$@AAuOglTvPWI6>nB`qoSSk8Ra$oyS8kX1s-Vlcd|h zLiz$~fHpWa9)A*(G4-g_b0meExU$v<@JeK}>ms!7GCQTKL)O!{SmNEG;a@3s*aha&``Ad$QD z_y6O*wj14kf%M2~8SW*=2g2ll0yzm*569=!5sA=$JR?P}nc4D9^js9IeN%F$$(6rG z(*{sEMcjWO35X;zt)U3gT2&Zph4x5Wv2}F14P;AG36Wd;w1hk$blq?-5r5g06LNu| z1h7BJPp!k;U_=nr_t~)M_48<(jSoNBa0_?WQME#*^2UZIfT3UD3mYBTn6}Fa&yK?={J)$@^SdxHmpy9$Ji|r=&o=PD&j8t@DP-;P(e*`+zJw^3yeDvbQ z<;X=CFYCVP^XTBCOmI{bgd`#+?mlu+03C-PRmfOD(wtgGvHUPc;arCtA|NVgMJ3~M z5vR%oK7^zplNEAH>v6+NSmSIAKh)miq~t@$BGPH$dfXJ2BUhcBEm z$M?Z!gT%)h^{t%N7@KfMFOAKSudIv^J*=UR)9ND@ht-RdMG3+UJuEKiSHZ_*qBADQ z7Z{U9T5eA)UzWzQb6B6_lGv+E{@vJ%`7&g9JcJM8(FR_g%Nkpr8)?)m+<{49r%~;%F);=qk_RWG>VgveQ+cm+$XPa zaysq~uIglYW(Hc@1cPO|tL4Z8Vt|MW9kikQpI%q6^<6Fup#B*q`1quPB zfF>LK{_k_qilG!D&}HQ78$507%J~o&OX7T&8asybh;e&KqtOo)po$|2tAs=HZZP}eXyZl%8PC_S-+z~pukHfh0=xxRdumP&Iw#2kB=c$C)_d<6jX~{&b z4-Gy*>Vz+(tgC#;V>9flBgP{#;~2!?&`Aha#|FaZKrR$Q-gh|-7=z(yQ~i3y2t^)I zRO8RB9>T$Nch~iVUU7rn!B!(h;ud)*A|PPUw7}Z@g~93l>`RBi82PscXSQDf@gy6p zetTa3wrOv&F=8>(yC$4$#|1(ay|`X&c_Yc^gagvCfAAc1+T{rxITW{mnc(E_eLw6F zG%4WTTvI>@mcA`1lBW+LpZvlQyTAhQ)3el$cOD6jNM{kvlbPmZGd|52KO+| zP%OPKxL`QJz9SwiH}1061OWuiVmgap^F5M zfYJIE)iYEe@ioeEy8O zNl54@mkpNDml+VD%XW2e#hpOhEN{SV9C50K1Rm$Zei!_@HiN_9od8Sf+6gHNv>s(Wbb;rxbN9e=#r8K#uVg`LuAh3jG2P;&$C zq{4^|^b^}I9=k#LOKPx_+(raTdG^PB?^CwvkicgDr%TGeB6p8_MhOc!jX!rLKT#u#qb zCC&@Cn!>w>H#VKVFu?_|MNQ@-B^)sx2(fdCBP8A`q|Er(>rW8w@BWjyVLl$hF|WQk z$)xEg@&u6=sAgIFiDOLSA0qS_iwYIj&6d|-$6%pdQ;GYUrZQHyy>;C!s8cn~+L7KWL)@py2^jDgCHhZY?-d1Q%|N)=7u4~a2I0>>vJ)}(MI z$0xa<6e6@lhIqdxy4V#+XzwBaMOodiVV|AYh=InEc=7m*Q5c}c6{sS35f#Q!VbCD% zp1De$GbDp(4Jf5}hg275#f92~6dlBagc8*Y{H#|<8>v^vD;_zGB_nim2Wq+6`jTDz zxM!Ul+%`v(ArgV_T*Cfj=z8s?{T;o)s$td^9nZuN^M~0{i$pOzY$ybJflY%xzT3xU zX=SRxS-wKb$5U(siV<#=?m3cstokQFt*qE*k);jUF?!_$0ok=B00tJaG-QM2$Kl@( zIWUSP>Xo-s+k}@G;t)PfS8oy%5+M;rad6obS7k{*p}|NGIVvWLOidSErGZHAjKL z2Rf;s^IvaQ^r#S{fD9>kaIa!7!@LF|dn|z=F>hhy^9>X>23Rvq9ng>xqt`;$ zM5AiT9F1YRMKX;X7Y3Ej6IgAak)~hAKupdHL6{=+pf)v}#AU-7?ka4kZkuRdDlECL zrIh`0c;34>EJu3OpIlKm~O;l4@(&2Ouq=Z}!c{yHN z<5O7S!#rT*alvw=pI4(qK)DD6jv%ZRg!LHsm0CJ)NOey_-F&Z7d#>AHvr!jQM)6~U&Eu>;gt zh{;cE;*7@5+l9Dx>QFk)4&J&)>lNKg_?Z5kZwZNUTR%Bpy2g7CL3!bBOGn$mh3$i` zPRen4X#*=yMV;95j=X~-^s2}DBeEKKX$#Ke3o4MY$Dmi1gNWO4+77Ef8|7BxoqJn6 zy{g++_V%L8ze(hbhcq8~r!zsa(7MloJ00OeTdv^+R&C!ThK8oW2zOnp!@lDka}4s? z+fPk}h0e%zF215FfOJwF> zSK9$IG!4!MH=F1opn=5`R7`Ot@)A%Wx!AtPMw0sEKBb#Q@J=fKL7q@ajsh8_+W}RD zwG>plAatZKMbgwiW4p(fPAA*@hxw|0<`yKD5Uy&e6AYp*lzoDciKq!6edf(XjMfiE z6^@PK7afnJ*b3PS)NP2R*b3_6Ql)tzAPT0@*l@lRc#PtI)L__7_r&lXqD(woZM+xr zae1_g;us;Fdc}(y7*`~|cmV$f4k^O1u_vKw#Or@DQbi3Mbu!XL4zZv5qAH)=HVYV& zTpopKT}Gq9cnv=jx4UTDHEtkk;|kLYjR_aScOH8hnq7QyRN{e!aA}`LVax&(@j~Yd zo`&*@vjIQ+$I2-~2EM2aVHDqmL;I2UG%*Bd4hIw513UCoA?+|m#D5gY0blMlgwYto z*N6JO^0;otmH-_%Q(u7jJ9XP)7z!Fb&1)m?7|Itsu$fki=bdP)!PjlbXMvy7J6^#UABz!u5p&Wl#4Eg{NC)2aa;3V5VI)c#>a3G$ z0iOYHC5;ja4Sr1l!_87GuA2?E{ol-GsW3&a+>_==K*Pt-i)xNyrn<(hiAz=SHHC-Y z@tW|D&^5wahZqtW3KXL0;y8`z#CTv|B6TXzY=BpXV-b5o=_K}CzEHYqK-IEZm(&yt zhHa(-NU#+l`V9SxPJD~Ve#d!YUl)3Xq~1{S_C?Q-FF3EUXY+-@SgLvO8%kbcjuF4| zF=4(qfEl7ZyL9XlgQjE}X5C@{UkD&a;zaf;U6q3O_p0d#8+$E^1ZqLU90}pP6cykL zrT45a6%08<#oM0b5E6-J1Thsy&qMB$pSLZN!6%kgP~$zFKE62}`3b|;{dG09y8xfF z7y~#S!IuVS&BH^x99I9cIfWVv-SqZG6@kkEp(?&(`)6;f>+2P=8L?q!89KlJWqH5; zOPJ@6KS{B{$IGD|z$6@UlNv1D#8N8}5~F;5ts+9vPlUCbYp6gW*xlB5`R9Kt+Xj>%R*|D z97-(+55|;L2IEGYs*M}ZO2s?+vTATuioavZmn7ED9FJvT7++%PK8o8F)du2(d5sY$ zrnrE=T>Jt-H9VqOqH%_FY)nwRJO`urf~vOyed6+H!JSPEazde9*fhcq9^I3U8-=2! zcQDC$$;)SMp-|{LMii33G}@I--j64`ZDT{F;4 zOk!nxnM2ZRs+hzX8sjW)@lv%_B4qPK^_+Ks3}W&h&lE8qFB_qab!_o{Su{Zq0j?I? z)KakcULKHg6CHqphx1Hrjc#9JXm5%_jply*l6+^aB~H6U6sPTqo7uqRs`-jhe+1zS zfk0;_@gk8+gPlV$LziD?_4|L49IZa(Gr!1$;stfa%KZOLTrSd+I})wIpuWiHZE+8m zOY4JBIKvm1Y&1n!!N~%}5-xzX4JlX`A%>&uprUkol6SilU;uXAyJ2`HcT6Z;G!WpR z!S$_8Jt9`==TbqZRPG4H>-!w~$vwG+1%ka^Z-f z9LG3lOvj*GB;UX4c$0)R$C=V{r&!{R&|1iLpXC!1>0WXu7 zK|Lm@7>r<@_78<(AtYPrAxeP-8of1p&tQ;LG{`{R_dvfbKt}OU$9sn04SnMXJyzP! zAf%-lK)mOgh&`k)d?0OwQ^N`$;-}O;4ry3EZV?`Z5M{_;;XNUmu&{mzWX5qhDjyJ( zWKIt-qR{wat}b0&r<&ZGlpWNK(SE}g7s%%QwqsgzOK(=9&)cB(@DI<*EG|Ju^VkpI${H1&JM=%ts11zDE*kr2#k4R zW>~lhLL!!pX2QASP9oM*low~QL42_xxmXA12O|zbj8ecv{}jB7uWTHbrRo5~KL~7F z!X#6=FpjTfd^#ARJoi6-ggnf!c~U+Co%hB+wY%QFzLC!!EBGd6yorc0&#HH= zxWbaL!;nEml9sTeZI%|Xlnull#}^{S)58Lcqn6Q$SdK^J-2NCS7)NNgh{Mq1`xwjs z1)^MX!=qbYq;MazO(8;kr8l`$5iM$&z<3miu_pZWAyrB_U4eGRF~n-DCA~0MD(7lS zL3C^>d?GsZc)p0g?BEOOQxuaX8fTKHhS;n)out}ZA1>cd*0`bGIog8D)bh1!6t`qU z`jul8rq-*2kJLek+qtC2C$u|9xx)Dw{BmCO5gg-XsBk9Hva_NX_s^`)DYBg$`wB-R zPcbDBu-!_L%(vXJuu!Y|I60*{gpWK+GAYpq2s8~4#7*Z{)i*}o3t=2+@ZMx5vDAt~ zarM2J%7kb0WzyHD5@8SuBzhpgDF*5n+egO0xF;^;Si$xc|HG2G9K^0+m6Ki}qY7Fe zAql!{xq&rC)|-&DwaHhTh+g$t|9F@v{Z_ZG6!rj)L zD7~^;MlusZ!W6v$O6%j)7`yFS95;uFGyOgY6N9P(d1A{OZVbT^p1OhUw`sG#cH3N?A>!|fQ7t(+ob3Uv)F%_w3? zGqSGu*g(GW(an*^hQ6MZuH9S`+f<{^=yrT>-Jg8yT-rn+p313Ic+Y(k7qlTk3ObdD zF1}>GDsha*1d~ZGD5c=Io_z+NF#WIwCQLX-gTnVX!e8Dr^@@=s;#im~;ROmQQaVS- zDu=fGXS45tp}ZLN*}pGz!A*oE120R@2cH&py%0^kqgF)ROPRlpGKJPzx%q4fy~{&@ zmt~@(OW`wgYfKqVAk8U-m5i3N_-!;f%~7ZD4E#YF)jT9fDMz1r_5688M?Nq#1AJkU zU)~`6B28$c-3TzA?^8uO^MMEyo(1Ccl={M;e|C0$nRojKX1R%_o0B#)b?oH#|02l> zpo=)o-sa2d2<=_hk;~DMqflTU)eqe6_!tkzI}Q{slrnK4rRa?$Ms0HJ9u3J)klc%2 zYwv!fVyJQU?#F+Hf(o9_SUY=6urF**DxI)OsYQ%rVQnsnjS)(%kpCpV23_fPv*PY2 zZo%CgjdD2-6wb-0_p?ezpx6TT@)P3rj^jh&NaFc~<~OCELU}^v3p&Fc=~}Fq4)f$I z8y`XCwivWFK%p?C1HM8eXVFA$_mC(Bfh>fUTz`3u_eFEc>iG~zoL^8$(0+DTFY(Pr zo%Y1l>ljn)blo?bIb3%%v>3r5e=_#3TP#K^utnxLQ> zslOMQ5l9AS7faL_ub_lJOPpJ4QJ`Ci=wP{3TDX86NtxSh3hsAXX=ao zIU+a3VgK+91nMhW-{Z`pkfG%WRhbR!dX7b$p$JEsU{K=4p3C>hbjD?g+5%fC0&Gg1 zaE0IyY*xSj-?&fO=)U!}BncNG{6a0sz=Uhsk+g8BE*@sp#vVJ3_%0&7BNnxzXW_9s zznGlD7eGDFz|_?Gs*ePnt4?LPkQL7G#dWn3>KdkCCwhS7j-7>@=xCe|&JiYmYfg#6 z5;q5-_F7z3_3|JSGPiuW0QJ-oWfO@Ba|A9N8?_DhlJ-U=iMYDMHb{=bz!0Y$q8r|K zZWfI=?J4!dF}YY<3r_v&_y6nn|FfckA})RX4amcH<_?KYuuh*5yAsy`GR?}v)5y|x z0#UYncP9=xrOTh_RVTSRm|XQ0>0&$wf@`z>;zKk`1!VaLKz6v zd4*kFh)PyA4JeL_;&2+t`VVPiCZC(-4bq0GG!Uu#_0MqrEGRySBUu?CDGXhg6jQ`sc4da_Fqh!{hXrNbP{gsacpO4s z=@}xE07tFEYsKKKvxVmx1h&x5rf86>{Up)7NVf|QO+FH$BMO~-q>A);hIaRkFJXq= zt{e>FM<(X22&r3optL-PJxo=nS7!JEwPCQBMPG_I7#v*-1xPRNmnZ$R z14b!{d&L>Px4KvOUXHtk6M`)FwvhOHQQv?+*H$tTCU7*gR7ztDqa6?M>Y*uj}T$xtL0W*9UOYEY~mweiO?KgwwX*&6I)2Z3VyV z=XP}$jr(*1KaOPY$U0J06s$ZFYH8itJZpBs$O&{TF5GYz2VG<&@}Acyp(S*!KgZ7E zsvXVSjipA!t*0>B_rx}PAWuLQdWF~LB7pf!VHDt-R+&*1Go*0QJangaHHksIB9Wt% z)3F;^P~}LU6FN?jBVZ1WuJjGxD&%iL{nWBTfeC#c#E4>jnRG7?d0SrXOb(e=UT+cg z0yW}z(Oy@JaYgx{F%qzw<$ZMrIh5E%A!Ex>h>V_3w{Oi6(l|RsZx?eM;U7ZQ^7dLn z6(UJe=R9O`ffa=~`?{rHg*e{6472Y?fAVIft~u@qV5C;6dobZjcZ4fECN&HI@icG@ zV(95{Iy^iF6Ha(HN)_2dV-Ko}6tIkO@#*feU`g_G~)sS+fd|ragYHdTI z#K>B>2V0NurHml$<_RS-q+WRi*CytMS_J3jtZt>Gng)}B$^A!Nf;T=9mJbRuo?>5NoY)6Wtuzn))@o^x!S} z7OmeGGbkmn{Ax5tLrIa`dU9_`%6*1b2eH}l!@HMA-oC$BSJa;s&hSO-$2`DXT?sO@ z?Tmg%18JGXMxd_15nvA3?n;gcWPQdpW3KO2q@zV$*Uc3^)bl~THT4?#upmoHg z{*&3&8q4y!UM91{Oqx{OD+8$t?L`dh2SP5cK?4n@OUfo=;K8d%-4F#l;VX#DDf!LQaq${`|SN zjz{7!K7wER>VfK(b?u0!G~SaBv3=+ZhmRFf-w*;}_L;&viYSDPiY&*~A3PVeea_S1 zm%MYjUDzT_!7vV?QpMbu7=Zdd2RO^exmfkwJ%X*79~Z@)kmdu7*yQ-)0tVp*e(75dL7bEo ze+%3I#bOGs;(zpbK^V+5S1B!kS`g{xgg5f#&KSA>=)Y@~0^(g`AT2mT@{duwcoz2p zr-JxJdjQYnOA9eGg_VZyNbJ1FYQ)Gj5mJD>)@g;u@&$-g>xwNx>yJ2eG2+L*u*k1L z9JCo)P7{r-Wi3=}5f*L?aFDn~WKPDuT61RwnexP9rP zSD2TllNXCH$p(Y@s)o!m7TyLPh`)IBLp~5`z{g+(RE)vQ9={H;Go7RzS3dNq$Ptl7~uef-K$dhUwB=|meH33GT~tT zz2vn|tXUq#nqv0*w@O-uV>+S@UU8s-SRh7cjbwwhk-T^P=EEY^2(IG@I5;L^%nxOC zV{>f+EcMKSgc^HEaXp>X4(` zuoh6=Ac>F`Avgelq*nLaMa%?pgcdyL{TxY4jO;3)mLyfZj9KsxE&M;rd%OS-J#p#{ zcwG4e>*iCl=G!Zfu!r%tc_{hYv*ykbR4YJnlh9G$1qTu(jjPRLv*rmRmduf892k$` zn*krIB`&_olGhSPjL?vwqC_qZj;(tDV5~v9p)wx8KXF4uQiVT=hAc64FClid=k7Ui z5r=dY_z5~yZ%(Kb8l0U>imV)m{NJ6Br2=hQD)H2RXH0CtD8WYa0lXu!c9ze`%8~45 zv?`kJonscAhG!@wu_z{@+fO$XVZayPkrD}RaG92Jc zcR@mz4CNUR)m#spaAxBfdGeXYJ)HFTxcHddRXp(gcXPBK23fOhVyp1(_Dsz*Pd^8G zy+_J1RZt3d=SbUG*WBp_Vs7HJQkV^22&fH+1UGA(>fC9}Zfpnh5~d^*U*3XhpaOQC zUkX{^IRbUQd4lxV$tpJ(`!BA5J3E4KxSvmDeECwM0pAp*|YTnl?RPcaCVsembHg%bf5S~_;_3tXYdSAt5#UC z5I`QS4_Ap{e61QZo+$ZUSdT05vKHn<-ZZg!^-po$>tDht+|qXooT)Fqxchjbxr0aW zo8nR)Qi2=&N6Is>2mMox_||Q6=a?*rx#6O-?W+lAP<7^=mE{aoIKvk+s2ne!E=M52 z?R)lclW!VZpnu-B8NSNoyKtf%KLmH(S@qo7WO)U$SfscU5<`f`4KdeyN=ssKCfb*H z&d#o4k3sF5t292pEXIgj(;ge2A6^zD*meUoqT2vb#p%RL1$Pr=gv?V1UF=QTxLq6LEnpZmG2+u_g zfIXXuMkX{TOKf{}n^|=UuhTM20u@KHWkSUq}->9MhHci#e(4 zSQ)rvf2gFm6EZNIp@jqdw*{l92JhTbio!|E!icDF1PnYE#`v%Q%_zjgC>}uMT`ZLI zp`zA7C#viU;p?5hl%bOe-nza{aR1@)eN&p*H++0zF<|aCVwRiIx@crCByY&agK_@L z;F$)2#@evV2G7XHbO$^v7s3Pj2Rr{%^gdR1Tb=~LnS62GeqP@-*;V^@xR$2KVi$x< z@bA%Srnii)wf^MjX!ZNQucy34@kOS;nWyx%ko#bWn)U5-b!sJ|#_km*P94{(Ln?$) z+%xT}|BYFBgPoF7?*~nEy>F~05Yy;i)F|*>L0s5yIhVMrMg&++$g_M?*0e zr<^+>yMdv{v1&K5%Z+o%A*07l+~4#*xM0y@2HAFJmg<1m()c;?>=+BIcN;wET=xQ> z({Dp#nKOLc+^TjcOynj8lj-31e_t#U3c`DPd2*oQ40{(B@t@5WP}oRc+{Xw}6lJia=&aG;oEg^YyB-9R0xX)XO&u>^<0qzxBk*iF zqCz#>5eRUJ&rv-gx+t}F!MppO+5+yp*l_;YR5U-x!K3y;yg@3`8fmHENn*pxupSLB zO8u6Pr&1!(>YR^9JQv`Q?^5WX#fJ^4$>(pgGa(O)zM6mGA_yp1(a#h+ z#0aE=h`6bscIVNDpkd61kXB}VKh@SO)Y;oC#WC)X z3ON8?@97H}094)DdTL2Sh#%_gyFpwBaOMA;jm^`>di&*b;!VPme^v2>BjpYhDxlAErj;1klD>mNVq)nKu>Ra z@B>cjyEkSQ6~>d@N#qvf`KVbyG*|jk{I1o$E zyIekOvXASCOmo%yTw=5jnKiHq8F3^=!BQD}1YdzaYTzt{kDT|A!gv*2_{x!yvX#P; z`{N3QjjPJ$-4HLs$LIt$Z@GbnHLoloLMgPH^2NeN#I)HK8_Yk#-355JHuV+dJTU_C zNcqd&K5VZSAvaGaEPdB59w?G#<%Ker-w81+GFu;1-rf;f;IylV@!Sc{^>EczGvqfg zGeu$CV74!@G4Eq9N{g9`FBao;KC#(K8t)#OsEZ)1f`3I_6{gEq9fmDP{t$YlK~EgX zymCeFv;`-q$-?R9ni)u6N6NN{)W0j|$i}M8TKz#TUOGiCEPRJ4*{N4^+w)OOOX^YT zA4CGDo#1%)5!0P+%4Zsq#JJ<`BldAvK;NH0B0{yJfeehr#TT(SS|>2h&zB3NoIW(1 zQ{vx#t1`H|L4^^`fb9e!^;dfa!vgx_YEeBkRY6n2;2niEGBd7(o?cU-t8%?cHHup^ zu4bO6=F@tCUl&W7=NfAz@Yx+6_=)1wQ=PDq{=Z^^eC^?CURN9RIif%&M)>B%;AdDz zZ;p^9{3e#DA^Q8oauy;r^e8-8wG$he8yY;l3k6iyI^7u+A5BWmIzA>8!>$!1lm+?m zm1qLo_6DZb*S9!87__;EO3iIgytlNmr>CvKN6zJXUb_}F#5lfK3l-apT=TAG-i`w| zU-Q&Xc+KY;r;l=2Ie+;$6JEYKtti2J?~}pnlc-zAe)SEJ6O>THCYw`7B1Zdp&~hsa z^d%p2SW!Q@93h@oEqJWK=&Zj`6D2Dbohd&kS3Dqz2?CCVs0x28?TJ)sx^gEAZ_<5} z%@7Wa#Z_H_J$F;l>wZQbE3O70>TPeDa0i67^f^5D=(}f|04N;HdwgT|0wo6=s!$~&K-b~TUK2?cd+HQiZhcmQ)2s41RGr}U+2}|+EM+b_g zf|&`#Gvt8B?O9iH&nQpln7{5E^HoyUeb0&tuI6=heSlMH8&6*!R)_iHUFLko5e$CetOW-cy%G*Uvqfqf>75{0`Z=L} zMmilLtdk!>`=710HW!dMt;ZEM;hl4d47&}CZeAn?Mrp}9MQ~oR`tXk5*34QUMr^&) zv}>I{omiN{Iu`gWADi`E+iv(Q2F7JaSfc;+eJt@p^eIql8XtArnN3a|A7Ovb&@nvf zJ}YdF4u9PP_vCd;kGMvu$7l4NFMnLafm}~%r5Y%IIdU%)!70D2=go9cPpGmP3`x9J zZNjW2h6Ku}ef_|1`LdvFaYAO* z?0+=YGjZ03%RxxwdpfrlY)4Zer-OlCs60U-(v^kG53{%660Re*dRU5|tK``w(v0>CoL@t@fnoeQ zx}TB627d1e6$69!&ST;x!f1Y2^aS$@ldE7ljlpsr#p*e@s#kY95j~y8fMItE*Vfv4)u$&qpzlikX!hpV`^6%0n?%JhX5{LRZe(c)`VlLGnoTHMg zuw|eNq#Q=@ZG`+kEDnx8PI?Op{h&jB*;nF;Q%IbUCK!Rb1@G0~^VGRT70?PpIQ?NI{?}(`bY1B^r!gN-au|yZ z4=pg#0d=OIj;4)Kg}e~cl+n!Cv-z^%{0`Bj<(iNb`F);~KxkmLcn zcYx7RkLd)NC2^YlMGCEPvSU-`%a(Ipykkp!4c_T#ok+dJ1NxFCe`?kT<=o0l;u>Tu zqqZB1sbLgfyg$3zG;`ehN4De)Y4AjVnq9~*y{SCMwQ21 zS8K|CVssakqz(A7jiWMZO5&k>*)yua!UENZvWNKKRA?n*k3`wST`c>*sTTI=6Yu5y zLZ8vuQE}GCZdmDGjx|Lw?`vQzn5HOBZ5UUV&-+?3I1t_7elaj=7~mNwG5Bj#cYt-? zw(Bu4nvjWK`+@@D#|6xMTX{lkQod6utr*Ea(?WbYMLQ?v^=XW95okin;D5vE&E8&= z`8U}j=3vIa{sI443FOwYgAhwZBq|PFVLsg;#p$>>u}nl7C2r2J9{;TVyr~z(k|{45 zV=vz#Ql}WmlG(`+2 zK0xmodJTweK2OvQi-G(D&*b`lGiERGe~V;${5fA7^qTek7S|-jB=G_YO-Or$0eopN zuBEoGYl*}(M%h+GnG8$$uc}*!vE!A^2vgXQLB-Xvuy+6Z9t<%!0=TKZ?>S54`QT&)RDkVZ){_AWT_qeZ3sewmqCU~aceQCmnaU9@yf z1~~DS{;AB*KGo7@fLS)gs_npwT5DMO-^q(k3u}|24Vdx~S~>m!H0e%wD8l-GlnIT- z{qr-<6mepQho$|m_DptNRX?x#ZgqcO-_9A6Hjuu7*z5S$eWz~z4y*Cke>Ur8aP})G z_isT=hyVZEyirSe_(smgVKQir=ksj^B!)az@L$kYAjZxFDfhJ6Ic^E&NeoK8f|BLu zvx1TqAHTHIL+*epvFPq5mWoSbckty&q^2-#p*hw79>y*hDDOa5OUbRUVw*xTvQ}&% zHP5;cSIzfUm~b%TFLHeoSerD>CUX$PCbt=d3kN!>SMbl@O{jet>|XPEubL{QBsgLL z4m`d@Gi1bE_`2i6OOem&JJHFfpf?BEY=rIyYw2qorw|LEjUHMnOXJ-%`uGLJ&|B+7*g=5T;Nkg~ zGW^p&z~TywuDV9nFMazkx`44O_j6z4=2A#r&|e<4$Xe)fmJOEi9?-NvIJ;9r`MwQB z2zk?3ekS6@EIkGd6g!uQT*!P-78eGa3j?W|iD~kM*U6}hduDS(7Zz})zGzn=M@2W} zLAXL>86wUajKrHAze8-fD3SO=pjk!v+@|ZCD++t-6&uMnZ6t>Wrl<`83PvomMkm0~HewN#YM&T(lC(RjRhEf7fyW#%&G4Pszy+SoO;N=6nQ`Koo z?9!T3$eN(Gc>iC{-x$42Z+U!Sd?xK$I>TZ6ns%lYjG##4Domy?tOjMLmgWuIhJ!NG zUo2D=iQ0^&bSL)azQh`>kRFXw$dg4Z(+5{NNP42iSxC*$k(~NGUKP5$=;>z?ZS;$!#*6ntg~d^Ec;R)uB% z3(;!qReTvh?se)5#1W^YqlDmV4C?2#gfp0+KP;u&2%CRCVGQQliGE_cc~QY3TR2vty)M*ib^n9=hUEI||hQc)I7l={1 zj-Wjj$525ZR)16$whBRkq)$I2 zrRY8*aiP1ix+n3Cb(q+?XuJf)0{7Ms4ZlSa@)jmmsz5@I`49>R@O41zUU4eA23spv z9k+0Ug&YW%!3;YRFT^#`ERDC>Pg%Y3U+0eyKhJljJK1<$bM8 zI2!Tg{f_HNXs=^W;@b$Xk0^xf7GpHX-LA{e6zCdfaC;;wP4QB`ql0;}Tr=V?6^7!uZAS{c zCl_OXo6#J~_Fvk&_H#=ooJQ(2M=ku^Dk7W#$wXN{TY^mklS|isbI=O~o@p3}+y+7{b@;1pbI$2jUjqSl;em2gpK^FEB$< zYbd1Q7$QdtG|Wr>Ib!@CSF)5aZXyD!WAn&38@=LUk{?_Zyb6fN108hb_)ZB3$v2LR z98G2JZIV53CSUByo`eMG1OjA$s~NFYYE}lOH7m!FK@kLoYB6>hFfuUt05ny2w$SK$ zGv|GzK=P-)d|(WNH~n4=Ba1p!LSBp>Lf6&Ab#GNT!xx$KIN`xeSs)=r23i@3Ps6gB z6DQxwS3SB9xCNmxagTH#i&hEP^U9p`tIC3#vE7s%~HHoJbW=$XRi8@v+~_CplFB!I^xK ziNn@8Xd`!J$X79}rUyu9w||)U<-^{!RtD#3QmyaA$wXB`u=6^p0$0hCsHT-_#2OK5 zj<14a4u{1Mg}V!?PQaP^0;=k;xmyWAhKW3fOBvy?==LC2@2tlaHE_F8l(;n$l2?@d z;det+@zXLBjK^AR5H_AQa`dBJqPvd8g74Qwrw3SsQ>seDiI~14u;BS9yC>s(c*>jD zz{L3oNh@+xRIG8td1)xTuT{i7J&wDAI&QwY+f+7LM32dcTC} zz$2?L4k^swU2@XM@d&vv&U#%G*sUtsIKUVQUx00HSKCgh3q328I8$h=cGuXl1dWm_ zMrp(!kGB$Zx5|@3B7S01?5i37fyH*cz)3pjcF{=f&PKQghh6+1Br(2g(rE`FGe`a; z%ICxuIcdDVX;UZJH&x(cuZz=3!3R2HrNAId>zDL6x2duEOI zc2Ibn3hSEXQN$)ewLgtijnWb?Wb#m%sOwJ65j2KlL0Ie~wq?k>@rv@z!g@Nm#FO^9 zU4qb<0aAq>!|{t@sE~c509D{>w)rbH9OBa&mt%qt7fa-)Tu5W6ArvL%I}lfr84Ewa zPC?nX+0p4QCzkBi+0jMdQdI;^2YoAhiL>)aaHR+|oYU=e~#vZcR%P4kE`7EOVKcO4~66&SLh149=F%lz!IF{ z3uGDMP>T$G!OP_cKDd#3m2@J?x3pw5Hfh6YtuLheKxZ7dIHz-7-^4q%Y~sFj{2rX) zZV;-b|wSAg@zS9lfLUj9(Pe!Gb(7r^(yxGzB;1x%5`HI1PgSvPH5qGBjNVhqAJ)Y)7$ zoPUkegccZrVuPD`*}YTE0qAi6c6Vtk`j0Va3Nh)cpC55Jd`vEpFDu4!#X@XSS0i@v zLTWeGdf^l8Jn=NX%k>z{%CpT5VVS{lCSvR3%Lp8yhqhVT>`7wy7gE`icnaD*oCsu} zAyQnN@=nk=L&;tjC46V-SDI)Gh_NUoIM&C@Q>DjJ-~eO0=0U?^GH$_s0Ii$nl^72O zY4WgY)<`jmItnmYAW2@;=M@9_Qe@KJJ+~-3qpGPelIK8Bk<3!YFuwda-XaYMS2HJF z;k~=Ms~lG0c5wV4)HX_F;Py*eHG(oBW{UkZuxiTj!THUNl|4g)5j>4rvv?C<_Vn*@ zZ?~%<)8`FVnN><_wq&Z1ex*vw8=u zO-6cH;7|KfhmGBTH20Wo><$l`yo=QfSnZMZI(Y`!(#7>_Yb0V5@5`s-28pQm2S*h8 z#5L#-W8p?TEQJ%52I(3$I6tEo&cNil`SNItqM9YBXt)5Ts?{Y` z%ccqetF=c$&IWagk>C&Bg))d+cp|+ytn#N3wZnvMfl++vbG~?nvv++)5o>UE)|)V% z995}$jtqpG@Tb2HZZ>?npZ=Nwg|qQpAZEx7Xb*$q3hXwZENga^jo5&`xT%M|T9W1I z^;OvIL)6cw-pH3Y5=sNDxmACNcUTM)srQ&3UuP+v9#$H2qSD!9p^W#gO@pO(f-R%RVAFw8Gi2az7hF3Ck26%*!GoV?tVsR(D z0_6>VPil;RGvb2yzXeqW{#+oPo|bbkduw0x3?X`IFy7I+Bi`Qc z-9I?Jhwugj3wjpSY=wjGpi1!SN#RFU`f5c*{-mW4LcrG zUudz4f{dPxSa(WH0fia$ag+=&-vA%KBn(4lle|0WU!qYQr%+*JTrH(cTfA8>-^(G( z$;H`mewjNLxW;?iFCmOVk`prJaOsHoRPnWV7pPIgYBm#_j&g|Dgu^(zlSA(UQFe?D zeR6kl2*L2hcTbSQxq67`9gb{;iG3HxRO2ozgZhr{5nXV{y24wgK$NIn&@Zb|GDOh} z=~qraPV)7-wO0i&bRYFqgOO9de~jeIHPlXg7Uh)og~EHnA+72u27m~B|%F*|~oL|sKZXo=pVJZ`T?Q2J`w}L|&7Tl`yWp!&)cZm}$ zfOr~TMVibZA=hVCZO}M7Lss}ois7(K z#CKT%(negwQoeXwtHxf8xg_k7kc6eYN17kX{JHuH!yv(*R9h&v4Sms*PfnD-ivCn! zlNc5IHSIEFGZaQZ0-nKY(L{9PkXogepPnE0nu-EgVemGBoUDb{L*3jd z3q534`30V#W&Lc)`wh<2R#J8?GMv;IO~}RX14;DtF+l7CYB-e3B?``loMQzN;wvOs zfnd5`HMa$QLk90i!sK=5C{}nWCHi;AdW!ab2pmF3f9)N?gZ7yYP~c2`fqF_!MB9@h zKFIAf5iN%G%{5#TI036W#+!-3M0;+trkq~$gB3!2tGt8#Hoc=hVPL8eC!WAJ&`KN` zWU<+FW{qLqO}7YmM4!PIJtaJ)pK!G>*r%|U`a zo<>tkiAW3^bC+s!6;@MYFkkO!mg@2C4MsebDywhf+@lzV@`k&|&a6MvRCBUUwY+;N zLJ*R$oHZa@8`Nt?=M_ec(Rw{^5dvv%tGn&XBT@(Axz*DBJNhIHOh`t@w?b|E57s{R zehegzq@l0sTT9GKtUCkn$T^YYm{e%8PA>n(xyHxjBm078@=4u9wqNh$lfIf%02Y%M zi^zrCC>M{_6LO+$R-<727+U&6O!@pV5H5q${@L--xZs;A3=0Zh^(Xmp@^yUm=A==i zPdz%e3|2AAlosnaP-qQG2h9152Y9dl^^2} z1Yg!bdpQu-8zm?)+TlUm=?I%*v^2F1u`zv-)9LoXsdH~zcXp6}slL#uoauG+g--uo z{_4;=o-~F+y0(s?c%&sHPU#{`<^-&fWMo7lBfdGz58!1w~+swHHAg~&s#AO5_k7|Ypd{8 z{^N4Q@e;m~QcRKAR;((9kQC1F1>ef;1xKL5Cs)T5BpdXj5exYb(#)I3?OpcvZy!E< z|4l-94>?sPf6Z&D7vYe=LM$M;%TOlKZ|_c>o^)J0BvJvVA6f%U>OUJ@Kx6DUNVVNU zG&qGepWD@)k`S@i_XPyngtXGniy5=TJNK^C@8Nnv)}YKlbW(kJ@dq=xa$>LJ3y5y1+N0RkLd1!}TRecTH=LF|Rm9B21J+@tX{{)jb-5L$BtdA0 zA*n&|hAyA$HQ!G@M~TLJ9|HN%+ulD&lR3&5g?Fxza6&>-mhQaTJ~qpoUIc@)e6?)1 zwz-)*AdXRmvOj-Xv`ap|0?{ipBvMhFj4(*3)mdEJ+~6+W%>qhnS4XMBzfTS?9(Ce~ z73wHesF8P7oQOM1qr^QHGO85gQVyHFNr89SLgFf5NU1Ugr*}EuF1dDsA!fGA>=S$u zO5R2s1R#vCl!zT(!Rz@py(E-&fO)uhQHm*VW z8_Okd#3+0aB=)XQ(_)Qo;>5XdoG2XN74^ftkRgTi2AmJ#B;5hV@VyWqMK? zhf)|_j16<)!R9e>oI(16z>acm~M?!KZ|buT0`;cZPDi5Rpl z2aDGG9`-fB#Dhr(G8tq#$$;Hte|>&mXVR)n)bSWSiJ@?r9{x_P z;@A8d&2jyNjeLcSMqqe!rfn)LtA+q-SIktAD!Z}T0|ea^{Jok}t# zQTLgb01zM{5+sU%C}q9{fe;x<0KvjVBJ~9`pD}A@t^R^}>$m!#{)O4*?%O=V!$C~VgWcM!T7BV`WZ50L{x8bYgxu}eOK7%B?x#O}ch zRnV-2dxDb+n$W~7%H{p*-#|4)lL58UpdjWHSQO1xX!E!XyJH*-7g~(~EJ~+XqOiNn z<%l;DIcih440>I2m`h_5xbCp+DoALPwsa8rz5z`gO?cuRYpPAxVmGQv({G982mu|v z;n148xLvzWo3(}cRzI76)#hL6WB*>x#<$e-79*x_at-vv%rw!!pC6!s7i^Gi|QWPT)Wp|uXTDdsc>2|tF*I}!$|p_ZBY@%owmIs+oUWf1^nvOta|1m zpTx=VqkLKN1pLc0SdgbVYD1x%KO5pmKUt9-sm<1c?6Ot-f(YG;H?r7R$Cd#kZuz%KQ_^43!va=1 zZFgW~xZ?-O8jodcwCPfM=i|X5ztR@Hz=`<*;VK#Mi5NVlkt}mBBJtUthC-=edc~Jo zTLp`DNMwv7Ma=tR!(=2Ic|oxp%@WN!HVTlA=Wa3c#y13sgfOzVToO9JQCR3PU3Aw16plz6rPt- z-9F6T+4Hn0-22hvZAt4{{@$%4+LS2vG-R}5#KoPCU|C2~q*O1*#xyh{dik_kd@MBW z(_j>af}1QZc%E5>%eyP!gej=J-8Let^` zyTobrC8tS1Z0c^J<$-NVm6M`4$zftsH3Fj~2tUMzG;~OQvKe2`Q=L$`g2WXBCeGaSO9J%T+h^Fs9`xbA}P zh&{G>QQ(!JuC&lby~KK8?KkFrD*!UV$tdFBH#E42lP4e1@Z1%*Mt0^l`N?~f+uh?> zkb-0|j|HO{1O0aBBkf+GBdea~p~XRBe%KlK^qePLCO?zt+*kX8Vc@}D_NAg68xYs- zp5BCd?PImMtnRoLEE1Extpob4IP0epQvj+j{#%2|{k7A->8t6$H8nwyCZA_>olS#l z!6S5Gi|qTx#c8rHJT9P(8-;<4$^EpNtn$x{j0jHT2O-w}MgCf@5otk51oj|)zphx^ zFf0~E75Wz>f~i$NT-~-c8ZHl0&IjEO$l+Sf)IjQfz#_#Ds2Kz)HU7T-T9n`7Y6bUW ztd-M_NDri#8xhbxAU9?TQ0%)xeUM#ZTZBzcePD2H>UO?S7-cSI&J`gc>lc^pZPBh{byv7eD3z{Pk(O%{<0KokokD$UM%YB)@?~VXdBPCx1KoUH z-fb#Mrh^>koxGEHxp1E!91X_E&eP66z)%zT0k2~gUTmyG@qS-yaMWHXsUh(q9(%xR zgmQhD&*xC^dh#I{KIZafmTwU4f2dvxcMIkDzs|A4!$UXXj|xp{!8StA4%pCXAblV< zmeeHFMI_0g4kxas%j^ur;B}aa#rv^lf;xd%>iTt0_IuK^mV$b;nT(eS6_iAY{n-rxDX@^^AOZ+p?1=Lq4y?SY^hw03JM8)DHk zyN^h4qfps^H{)xWr%K^taS~~Khl|5a9~rUNc`-l##kn{RC+8&!JRt>QMOZQd(9SM* zHtNKkxqtibn`GljKj&KNm4eX&;(GP1*j|Bj+LR;i*nQiKA0WPlKKwZ%Y`hlLg=#O+ z=;a-E+pdMX3fpi4Y74yq61mRLPN5D?>*V6ZvKxiX2oy9{mIDzr$l zQ`z)3D<0ctr?pM*)b7PnvT%vCuANIKl>Gj7d4tRfRq(`UkdqwetNHq~5O77~EOuKt}f9avx-e0?G>txPLVHoNBKMI4P|X&j>8J>;B`@w zUY;09vNBSZqj*P`E8LFCgSI1D)D6ZoQw%VB?~c9GQ*lrl&QPg#v)1MS4nC~HB19aL z@Qq9`=DnhE4X`zXO`N3*;|?Dv=Y&x!rA^Fnba(=Ks(ity2C&jowdcpPa?t%aTL^70 z=h&j}eHL{M=k@WsSDT;Xhx#Y%T0|`y1!n^*w0xnloC?O*-O7m>R&s2BLQ_M|DJ5Z# z_aK%dzv!tqR$r)sn8(PR6Jnz~wB}~lSA}W_nH5NcnQwWLN^ByIyIv_&KtE3pi|+~* z4%vU{R}t5A*$#E%X*;+OQOJvYKrUm8%tG9c%@WlNua;w+jiwS>gfiUoSf`iFvvR0< znBr7Vtv3O*^V<)9p~{61KSC$9sXp8yJbV>9lETA7+vFpPm&N#?5E`7|2V>I0uRMvp zMCbt-=Nh8S8X)3&NN~qww7EH$8p=gayfal4pA~vNV^CFJ!&T)nB}Q;#yA)o^*jI(B z#htSj+y;nqBx^0D=-Yf|oP-$f6dE;|urYVPQE?f#-~ASQlTeooCLhm>`_FTZFEH{% z@q=*n7)rMkdlM!gcC%2abT^;Pi*q9Y_Fp0=LOpO{uX3Cei&BW*k~)Tq@aIZlF*c!E zpq&6`J2_h3`Y%4)zIbAiK`xH%MF2BS^EVM>=>cF5Bagp>PC^aTA{B2S~ zBR;+-*Wdzxs0nZ2HJ6uQKM`mW46Es~LgDY#b^9ef;RdaQpQsbdJi`W+5V+cWj<^B4 z!+#Nm4bUU6kqb$Ky)W2lF({*G+Z((@s76k+PZMc;^UBSg;6#3?wCmLoQU&(R0X6eg zC10zUSGR_YCT?GP>J5%P8{BZc#Twj1nAq{o)HGUAKPhzvCTLg^iJ9Z7P_>*RivbD6 znnF&DO=o{cLI>_bjl!?U&}U*<3@f!Q4Mq~ia)aZlZEV$qnk9wzuy=)w=nSE^e|3w;U{~q>4dHo-3c5;jLlZfvKK zI_0d*elf|*`)XWhmbAf0>@2}jv$Mnv_n{BN7wUnYN!kcfU@WfLTIjBRJ8ln$r@!D7 zKfZ%1UMtiXqg4q_Q);sdgA<9l77P7mAN=x!82qkmc2yuRs9-RUI939O=TrK5`JiL{ zL7Gp4j&~4k5ol~ks14|+{vaa=o*!xe*RXp|JBJtnMDV15tSzK2c?Yp!YEPCj!S!~97Kx8p`jYf78PgTK@-q@p2O{sJ^aR)q! zc#bArM^ffuuM^65+8x6fDfJ)(n6$Dkb5}HK#7pz!6BM$KAl2p0B7g5756wK+1h@G9 zdO;12Ka;dw#)69|(;Ny7!@EVNnCW!iqcXxhLuvoXaAcdc;y>Jr5ib>P|HuO#4!Y-O zG>#=N*!&>P7bKX3Eu9IWI~ehB*HH4)$=UF5aGcSi6r9Kprw?pfZE}kB?bH-p(wAJ{ zV%lv5=C7#Viil{NlOmOp9XJmRGyuZ^#1Hn=YIg!WJ}VUWvFtT)+Zd82zT4p)w|OZ} z#&+TJ4ku#_h|M02(V1m!R*J{lYo45ft2~|MrVA>47-Fxa;ZW|#pvJ3((TGn7*stIe zai}eB+|2AnZPoEcO#_N5n$52Y9cYWVc4F6uXDk1OXN+%Jc15>ZFF@PynabQ?Z0esM zIJRx`W|-ci>#bUm#NRpoYIA6u@VArOBN)??{(@@v!;ZS!F~qP`oi z6Ivfguz?zyDW4&*=%tyEens_Cn+@Y|Y;Ea%w__qv+5P}>l3Do;Ava{rVh2H%JDic^ za6bXx*pUBsH@*gAk}UNL7A)$+V93sp$}Bw#rIq48{|6e$ zp~<_=X!+;=_~-w#gCYi>92!87%zIQDDJ?QFqT8|dDYyg89cu6tf!d8S>{`UoINtk# z1N=5EM|oh22`Db*0ftd+Q)A?u~u4mO0nY#3L&f?;jO4XN*wm>?fb{P0R?vu=Q}5B^-I`Zp&g7AKtw z1D+fe`FwMYg!S(cK4fVtgfc0>ehG7!wkWVjpRv(r_ z+<}Tg_rAQ$_s~_CW+E-xD@hP_)-d8xhp{1{J;HkpJ5wC~71Jkfx`!R{5b(n$kwhjV zNnnd!En&HDwm2-aD?D<5)Ta1A)!7EYfeF9V;bc0}J?*Qx*HJ z(1H4xuWsKWcEWrSgN-HaQnPo@H|CI=`0#u2V{n!>W8^V?rZ^%m2rkzGiLGj?s1wE}v>aonWvC?_w)z&m4YR18gn1 znfp6}5}v|?<1?CV(U{4%QvgoP51Zrz;&-vbgf^4M*(Sd!VJh*2-{2n%-nB_R()08F zZ}4waCBYt#r#D6bBCziwCW2uww2#dIq<5b`9# z#5<;XHlf4>~%lJfs5OPdZf3iVCZj#32j%4k))8X z(Our0{YjwM=9v0g6u%jETCo^Qcs@}&DT`%MGE)`JnA1pXvD5N?y5$!W*`#4k94iWG z^KX2(8lK6^t4Rs$Wdi|}wr-1XZk$fc{P3VEJw-1S!y0Xlx?;Cr^o8nia91I@hu>gP zGKyu|wz)MPS#r~6)361;^Nis{hv{U4&H9?n-tTuMLAB{M&c`1{qqFk~<0-_@;XBV; z+r%0;=|}vaoN5||!#wJKAQaeDdG0I*yUxaJ9bGJPBf?;G!8~Jlms9%#oo1D&%%UodPUL5=h!Jd>S7%!mLuO ztdE&py;k!51W7JeDADJ!DKS+Q6ot&guIjEf>&N5h^DLMT=y%mNokuG9rS%g#hc&5jmsQdx?LdnAdXaBdjB*g0qU+=q zGd7RU6w}ivD(|Jw_;^$O;;aXQVTe>y=_O*)os;pG`}~_IO~e_^su;GFZ?6>&?tsJb zW;{Bl7#j?AJKB-`={Z4YH)i)pUYg$UoXbI@-*#~dV8&@5>0MBNxm{KC6{Y+V5QH(& zISZB17_GiGhpUn=VKA(Ra`u3G<+(pN%ub{_)JP5|o$VT)G+cAU+Y!Hr*)we}kZS$; z2z4=kaw`HxO#*&E^1o0d42u%SilPA?FeKZQAc#UjH)V4g0v6Xrwmt2h+k;_+FsWErkMV5 zKc`FC6`bIQa*3XF_S8^#;GAL zkhTk7XlM0Kn?B@oiN0#K)pPb}DooZgiT(=@T(g!3H|UwpVqH!!@s1&F^Ozh}3mi+WG#pNR z7mwonC$g1fZMG7$HE2bKa&tIqII6$1@xX z?5*o)$e>Mnas>1DGEXmQyjYy1mv=bXkN6;M$`de>qY!#ivi_79VM1vPjF^#*Q6wea z)1Ppyp_T!?Y|eU=LW`5L-Y{Bf+vXw}ot&F=e0`ylbL`5txE9)%k+B`|*)}K1AI0tU z{c5|WNkd$lFgX2_UV9fa?pl+40an7aS`0Kp&*Hj2-I7P#CCtPHRrytgcDI~0gA@D^ zj~L(%;bHVgR2h$}t-gm~gg>`fht`m3&pqxE=Es17^QJtn3I-w#M)(3plVYFI7D52g z`9QIc3!}T)cmNKo&6WpTz~D2+537jPh?fe_mW+DaJ1%%V@qJ9Ja6JtTF2DhdLm^;i zC{A#qjO?*^JZ=(N9gmBJ;YQ(E^T+EV2k}Bhqk$O5QV$(}<4&Ombd-+K$#t-#Tv&`r zsvm(n!lc+dg{Mnk{wS_-8o>JzFTKF;PW;0Ah2i4y&Ar6+^h}Ug7*ZoX9_|ntBc1sc zSt08UT_EC^_lwIz7sxCWC@W69!S-XIlVceyxC#E(U{{fYQ7QkUwjdTFkS|a_2ng)h zgZgcv6qEx^tJSIA%;MyCf$S}p?&~i%- z=!`ks6mpwZxs#g&w+$812;Fas%}BG?Ek?+B`@lGbdPoMmTBZdD-C}C8MCM?bBkQ=G z>xnlC^$+$vxCTA%2^)-r@b&FHN4z<@7@xJ&&`6qDA8ju=UIr)l0WRzDKmT9!I78Kf z@r8HOJ>cy^?et$~A>-kpR=be2J!BBl;xITdDTR`s)ebb^9-%_BcGOMhnG3`?oOBR? zTbGD3o5BqonU}CYmir0ph)FrM$Ek8~6xn8;GUv| z4*D29V6!f*B8$ZIh|6r7r3ICR2sY2g+yNZMnp{AC#|UTo&s!SJ+3|Y}SrSVT0~Rw* zK|#OWUV)z3<);!2@d9lHw)mRgXLG*5I2T+qSEI2c+=#k{A(tPfYsIxKRUsjwyHqo~ zq$35Aol;mE$gIKt`#|^zx1-kJ%!1kLzE&z}(8C5$n~3Ex%rl|j-xI%*`bQS(fcX~8$6?J#NL>?Hz~?hRH8yv@t>2xhK~>R#?db) z=IoE31YDO>BlG-kdPIQxgu3IA#kYM=)WQVBAwu=qdC!2Tvhb8FG-%FYEPjKz$0QJg z{cuA}ci2XrorSl;9`-`=@OW-cd=>Gc+N>=9;c|71N@x)Y)pU6~{u?(yi<9x+vYj6l zs*#^(=)ik~&X`zYA__FgiDSMzE~f_Kz>*k^J_a$i7+6=J!e3*Sb_GsxzfdcQB`85j zDtKb_2Hjw9IFTRB+3hr@I1XBt%0;o5R^!`myotf;Xe9BQgxUuODt{uDrIc$9Cm@s-W_Xww z`F>O~sBI|Q7ZML+Ajp87j0BkMP{;JZvy=_GXY>jzPT&L&KcFxzoea)Sp}ZnF-+j-} zLhq^Ud_-B~jFzv1Q2G16;si3Hls!!g*G)e!SNC8l;MfvlNOokg;5|Zr$%S<@o?|+! zzK$?5FWrfPV{?j?)!@!8m9Z(xc@#Od6c*Be` zgOlO@cymAfhVan){i7}N9?^mKcK>~J1Ve3djueS%IVl(nu~>9A)3u^?1^8Gh7Ot#m zKFgJ4z<^)jYe!!q)FGp5#J9@3lsmPUy^yJHh-so(de z`2wX|yp6%Ng&YkPzHF|rg#D8 z*H$QAXm49Ap#w{JY57ckfz%(jD{K0m=tUz4Xn;VcNEl9bDK*dP!tHE=q}4kLwgK^t zuubREy@p$`uC&s?VLzPW*+p%lHn)qAEnXt;g*GVh4taj1{twge4!>@=x#G{{B$7#u zVG9*X7mvk8tl>*yB;t#a(H2`z=)z@iT6~4Q#<90(*9bD$Liv)KZY0`i??%Ch$fI2G zduzMR19R3N%r5iiJ&Fjh8NB2PeomhEo_E^}D;SYbeWdd%^!X_=`dkjn%GB({82>$P z6-HX%ji6ISsip-m^Hp#+9>K>=3s3#RvHFkQEmSDuMY*{~i5Oi3gOTS3XLq;ggx1a@ z&mP-EFgSY8=irN_&UUydNwLhHzNdxZdi;q-#ke{=d>ZC=Jt4|t7=bue;D^gYy9Uwi zO+L-_?2CA_KWUTy;rh^UnPA72jg+3G;N~doJU8`JGP+{6i`$Q|gt(MP4mV{h;)=8h zX3neS)%&vO^NdMwf*+prw|`t$+rRqttXary-O=WlktVg-aFWqnChi`c4vu&1C7x(T z^&J`p498=t&O^Wi7~X&Ec)^p(@cN%1GsKk9Y|-HP0asr4Ag*@XgfqkL(2N+ZCE!}r zJQA^Jlg*^)P>O2$4Tp{xb&EUCGuw1Cy}hIIrpV_1>g;ff^<9utZq2%R6-C(>Ftop4Ku0__CCxoFu;8+dLxn|HhxNBc%Q^6S90ZDs|VcCF% z&EktTis*Y57{0?rUas@GnwJiuee9pHK@CGE=aViDVYi>vQ=N3>RCfD0pCY|Sl3=l$ zL^NNU1EvcKLGhI-XTWvmIl3=+GMJw=PumY# ztK>sxY-E~4Lu{yp)y<9#a_HOOENXc*tCrlXh_8|?M;{tH-{QB&ZWh`ST}Mu!N;VVourvFc`I3hlaAXjSw;j01wd4&~8dkixCd`*#)532$3xy^~2fx!$hk ze|0+raDrUK;@ZbXUy|+&eYqcO zoYikrwn*4e^ev@=_&bc(xKrrU^j4>ZWCt_TxVPFzVzB(Ol>Mge-g1rjz)G8a1r8NV zxQXVyTkN-G<74jV>y5VyrT_ew-m#bsv~PjY2^2pRZQf^G9*X>NKHpw}CG&|}1@FgQ zG~AHi@J7&6_qT9)n~E0h+){Z`>R%eVBtxVhZrAyYE1o#7MNs2fyUovnP62;H%a0jf zbT}EKO`_FHrr^j@4Vh@y1M$T+ZA%I!sO`F2F?wM!lxIC-dj#biHJ93KEvI{^Hkz%s zTzw830Z$}Vwp+z&*ZZEch3dzA94I~+&>(J8h~Q$HA{3ubsBORFoeW4$FWQT=P0|9R zXN!uHFSM_Su{)uy3o1|Tp=az?;mONUIlrkEGfj%KI1z3ysIVO>&$G7B7SF(QRj#?p zE!wf?r>)rN@A>+AT>gE_RW2B*Ie$lDa8vM*cU)q5?j+&B7>DZNsQeRIh^vzOoWWS( z<#LnE_AC6K*eJE46ptdk#p{InAe}+<=Z7mMYg%lJ`C~5s;(7Ymv3nANjIbe2=!}u^) z*9S5*1g=KaTzbVyANtr`DZ=WFP8f;n3MXE?yH3B&nlkL}A@i)xvFaXHtzZeqL#BKfeqAIP9+Q>Ub>tL=Jkd`qv7SORC;MeKcO4N!xPP-TGZiHg3E z#qu`m?Of6)VmF~DK}BNyUpnd*$t(-wVMJ8h1T09N&#`}N-m$@Ex5S!`ZxC%FmJE&M zHsCPh-e@pQ+NAoN;J-y%a$u^dJE-N;?x2@)l^C!?tG0<&PLS1dKdu(ks|Lfk#lvG` z87hkm#JZ>bqyE1+K{q(T4~aDJDw4k;YgsrU3-w|%-x2UhMywyQR8$*{PV)@D;xm4c03u*la$Dowm`-j zW0u6wkg*CB&rrfo!D+xP2q8A~GKd>>XS>{j2y;obogo9Nq#1xwF&Nb-k>j^ETgm%! zc@J%S#W5i^svk2Ho1IXIkE?srAeUnz*g1f)dWvpqckc~OC)x4%nh6gUCu2CKSJ!!2 zb0*`X+-534IyMZgu_muu?7jK+XL)`S4gDOMe7g z{IW0hZ0uIF3Ak2L(W%gdG+GSSk$qq@^E4z>OK|SX&6eulBD3ma@6oak9Oow2tlP7G zh=a55iAVTZUC^kAg+ohPFh0hmM(P`9w*7gsLv&IVw36Y3S6;@opYBlIQ4X)L(d zCLQUQR}z%%aYP(;k?lrpa%_Nk6rt|{SDO+nDfKiEV%O3t;AoOon~P)u_GA8*xct5y z>n-#t{tniR>2^I~MBCwp>$HO=3WH0dfig3xZbip4ZOuV`+AfvbYbBJEP>BO1SlZ!! z2F+A#fWiRfqmugDY#zeqSRvb#jEBThjh!y%(yY;LX*)W)^dn3hlL=arm)fxH9CX;` z2U&;^{o7TsF)2Om&uo)%fUN>f?Xu!{6MsQ>5YsptT&JDbjkxnuRb+SNWsx%uVbP9D zLz&E0*C>0~{EbKX#3u!)11%3r>RJ|L>zjH|182*OdF+@ z_wKB=%VIt*=m1)5V?K~P7nQ0-lp@wio3SIsu+I?z=h#`?kiFR0g)-bPSL+QXoCVhg zyA|MX_F!6GrlQO*J~=xajL%MvDS{3s$DcRR*$6EJFbu73YUNuNW0C9WR&;Z16L7FK z3H7dER}Y9*a^N7h+XznbBPlR#I*!Lw-L*M6Y(MQs_Sf#Wc{wb_R zY&9LuEt66HO^WLlxheF#MWH8Z;j#+h$p!f8zU>c|xBJ6#u|M2i@1Jd|eW@Y*)0Kdl z`p*<3Ah9>0g5zrLR27m~J-d^|H7heBuWgwuQX#((xdwCwh(RM+%6SK(CHi(TAE!7fLfHe{Z8R6-trd1e0?ifa?~pp z!~R{~RM(jG)oplb%1crDHmUIc&|t-%;5rvizVUqibNBLH;&CDFbJ3-CbU5kG;ri;z zz`ErH?gl+#IYb3T+*b-7F$@$iIPhP&t1{w~D`S!=HP?w`BGfWQqTk%-)ff44j<`wp zz|EnpbYJ{} z_u43my-pX1OE&EvAg&sG=RDEhpxm zpja&#oc8u{z=DVy#t)>_!Ao_BB@Tg>IBFpgqrgP$LKCFmzjec>(g%Y}RL|$XxK{$k zvc(UT;iyRW!J3Dvl1r1;a!sbMtKfDji|8MNXl3OK(FIGGW|q$o#}sEX4HQW)qoXn| zua^Em!6L3=d`C+K^;8_wa@B{Ev!V(hwoYmDWwU3*r}%V9eW%(ywMljj0T|DgN#}aT zOYHDv&mSdrnaU2>_6K8-Ip|I=*q-%8U-<=}Cxye>wbX#NFGIKJdw1uD`Q6gnPj_y+uiD3(0h`r%1EG zF87sjQo$O)4&G4rok?+HG$nUSU=T#$Ox=8`b~g$x*OfjVWybsGi~>6mxz0fVEs03Y zOZ!i`c7AG$=&O_<@furQp`6vkHa$Ifm~JeFNoieurh`GeF%uEYe_sXT@Z_wMo}6Zh zQ%4pj$mD`H?4O+vZ3%Dp%+;U;kJl{)$^#omaiiv|!!kg-v{+wv2=rav6Jvl|8?mO7 zVhyvYbo6^viX4mC@E7+z4hNYzPms%cm7@<=VN!9!(b3%}gDNkO^}~3c!`{w-;TzKj zecge?RF*xJDvub4_YJj&VwqrQQ!b;kJ1kWOo(%v61dd>Xf3G5WvA!`T23G=b{ZYYX z`q*rS9?y3ZZEs?+|5Ow4h&@=v(^5iBbz3A}EHG#B^`lZc>P{|}#U|x&{fW#7kB3ZI@fmpJMOxlTM%VBUc+$LVU>*zp@k&1`#{)*E>jId_?=~B?kcm0GD z^E>Xh1{leW?1HFdROQDbD<)eKBV!ZJi8LF-U25Btnd49zP>iUn=8@H&atnNn{oilT zb^gcF8LaZ%Oe}U%vW{{Um|K|?TcNRguDkUyOyAMjn7g%uX1A_8)8=w>MrJSPcz23Y z68*Z*oThUa17g~0q0+f-&Wqt?3ixf{Qbw%x5EJ=oKIF9eQrz8*b$hqi9|M}z+PaYm zel>3-Gk2SBuJy3V;aK6j`C=1u3hq(dfTq6EgPyZWs>ey_2%^_+o=$rO}RG^ zE4%Ff`k((N*W3U4pZ}Ljb!pw%1lyZ;?yRbOhOT&2xx^d0IkawJ0#VId{2eLfo9&|e z)J>1! zZ5$(+r<+#S z-Enk1AJ)G!dE!pO8pFD7I1ea`dW>gaVY{vm4+<94Mms6s z&{Wxwdnhpyl+p1JO|aIU$@vBRFc2$bWHs-k({ibIleBI z-|&BF7ZWFaDKk+C*>NXijxX(c~jM}b$Mep6~gGVd}^xIJT`e@iQ=Tg+4z=`XHc6MKX z7I@NM)1#k^_Cz&OQXIsxQNJ~&i*Q!*>gV6q?6|z_ldzP~EH=OxCbjpJfBuZ`DP}b6 zNPe4xE!Rk{Q9F1%6<(h4mgf`}BX9l^@mkEu6+=?J0!o=2O zjXYW>Xe`w?jH4M~w1T{D@Gw=NDMoI6m@oJ+gA@FKL5q(08|KLzU{-kya)6Ia{v|R0 z->5rCB=(y~nIHclFJJD2*G=(dNPU;t&jfADG7sdOZ=42kus6IeERfh zl5N193g0eSe=RrD>)>W;sE@1FOop9PAXuy)ALebl4l;)wu0crq$Mig-J?&uVPst@J zlNq}AVDzuKZi!=ST@{FnyPt6M8HYD5^kR=wC-)<$yfF|1inGIbTJ0PpI)S>~N*_w8 zisC3a+}sNSPY$2_>?w!c?UX~>M-C%EVw(oV9{)`BZg+)g&F5o&fy3Bt7YOAcMq^e_ z7&?|0P_Z|4Iha@8!WJdji58)+={CMIRJPngWghP=Z=tQD9L6^|w&x_R2h8Q#7nFc( z=A}*kR$;5}T5`opt}ERAIsPx*bq61KPp`eJ8>H<_xgsYPMi!TkFN|e&^VJ!nl@2Fo zO)~mmWK<&0D-ad;%A9_;!3cNeS7s%~R`gVfrmS}LDhJPo;9IY@ZMixCVtSW%;+i+6u6MZahh(PejjB_cS%jfwe<=w&P znpcq@cU?6F+5kH8c7*NwNTM^`1_*$TzAn!Il;ixP_&5~BNZF$=em!k$o~3L4DATb`SJ7_Iz zhNyMhCV8$BJu31Wp)Q~SDA=Ae2j8wc>B->qlf-y9c!I6%1A`293En?C@ha3QC@_)t zt)X^@zrS>JckSL!=cxP8Ocde=#$E%fKkni|YaXa{UzAYRSH*@D1rAT7pkN9^XuC!j zP6*`||IOK0gS{^L8*odpC^oVOD%j~16{`0GEgZ9$ zA{D(>CHnlVlbugGXS$yloOI5{-J^)9bD(nEgU-y?V?9lV0h1B>hSO~ z6=?KUfb`X3#Rk`2H#z_+(tqEU(;Gx{sml$v8p6u~R#9y^k7 zFz!JUg4opq1Vj8n4;f&L`F5NJ7-{~DXs7YDRJ${zCkrW;xy4&s{AZur(Jg(u|(s`0lGZFXW5$nKd6 zS<^8(cuf^WjOFzl&M>bgZU&Hot6x#yTu8=XMSWvY^b21zbnsKU^o1UWNPCQIsEbly zQp457V)=z{$!e=^>p2&obey(6AJ(&o^CX9_k15GK__#Sr%weXijhv|(tU+Wdf* zx=~sWx4h%BCyv-OFI8A^1R@Qi$25o>ZYu5IiQdi3&IhBT>^yNFK5{rc87CR4<<3XQ z0Lol+uz8w}Z+&^d`%X}cH$$TvDo2ayE+=3tzlj=CFI9S-##T-!Vnmc=?F0dlmnyd~ z8BrxM|G@pq;RGpgUqD@hk>NX9*?6g2r&%k=H|NgbuuZm$t(TX&Ne+=Tw!!BzoMv&5 zKCxpzc&QsEa{FLQ537IWsxuf1ZM)p$2pMBRK9pf36QF`_c6U%6@QsMWJf)c8f0=U0 zf&y)dU>?_>gVC{2iN_+9O5hc_PKYu3UelllPunhk`G~Uv`Dhh*sXZ#cap%5)NfuO8j5?R;x3+m z#DF!t-Lh03-Y?V=XuT?-V?&c=Ms8O4{IYwP$j)+s(TK?eeRaDQ5nVcit zP~#B?%PRU3FBoO}`+7i2c<1uDWO-aagH+OanNjvv+_ zc{0nk9U1Zm@58*5$x(U?;(TGEJ<*{^In2E;`d0WeV*I@BFiem3KLrgR`I}=AeSlio za-fna6Mz5pRd9`(Ie7K_FSHRQhR~d5Ih}m<{ONB8uUUG)VmhVB4;((Oi9t5umkk-;4G!BwJVVvqi6zLOb;RuK+1 zu{SEc$0(u7%S>z6Eq3Q7a7lL<*>6;kQv_A3tDbVhSd54X_LUuLM|V&#Mqn$R>xVlm zO4FRT1_P!$B?jhmv6?6w6ERpuA6hpmpmP5F?Csm9^i0g*^Aq_6VN%4PW}(yhMr9P{ z=Peinbdx5=JGTttH!7P)8R~nwlk=x;l^tbL@Pi-D(4K1it4|aeWbonJEZo6BsPv+W zV3i}b$DlQDpb zKVzEstxD7BpQg53+iC;5l^{|aJV9j%WHhS^ij&(0F^aZeb_Q3e1mQsFrd;l2+%|~k z!ViJ#TxL))3vQrtG+LDXs+wioHh>fPA@O!=Bo6jgB`RG{KOO81mam*d0Vkghl07`! zR}a=g@;ELPcH9}VX9`$q8TjkhN@J5*Qq2B^HkQK_ zCNGyecGXntIxj}BsCXV@Ky2<)6{FJ46g6>{0%kOy!@XNy=IJ-6o?O_V$t z%*wz^N_~=2zJi&fXK^PMJ|MWMO8z-Is?1@f(nux7EXn$ai-?N-5Oqx5>++VtRAR|N z-F30bJemVlBHp^&0XO8GPJ5$}S4LbnOt(1=DJR^lj2hqRJ9JdUkBYC-hi1g>%3+XR zOmA8#YCcgEoEO{8Z)cjsO}xR+F~RPrV7r3qE(gqE43{~8i%CRBb434;b2Bb31ELS} zGCVJq`TeS(<~wva5tiz95Tf&ve$sK)Nx4tS-$q`VV$xALMPSFI*ip&^<2y7#AbQHz zQ6cRjCm0cFO*bbyQp>cO@^c zbR3958)><)V&l+NX+1^83ieSw^AM~vfTF(U-1(9QaoE9aq&_LJk+!IY>prK&HoToP zN`pUJDS%zwa`9rw(kXpWlfw^W>2x*3i_-cMhp}{;9ptDa$NR^@Hraj2gV7Y$nVJ>` zn9mCT{VRS@Z@T6O;m`I5sbHqU*?^Tm8^YjVX+$5TX7ceCDb&S$#weO#q)^Ynho*Zs zmMwaoSE23JRgra?@s!>)aS_$DT@~1)d?gIb+7*{r2X(hu+f~6mf=7!C)<0;(IGp@Z zt5Xny~( zEx-*ulSIG*<|g9mLNkG+;3wJb=Xqdm_TEtCWb;L-g~kR@sG`e5MH%dnOE@s6+(RAK z#TEM&m1l(DPjD=umJ={MA(I4YbC(d*9|dEla(ueE#a4Nv-MxTi?5EC zSO`PpIcq(MSPbR|F>&j=q#-a5p5J5qe|T@wU>QbqFgjW)$H)0SYNPiiv{i_&SD3Mg zMdo=_{-bANnF=)64kg!2n`s&B{lJ~^D9_Gvwwm6Q5E||x76)S&k(x|RX3(9kH?U23 zl4Nj$bMF^%m5O;~&c~WlP$O$uU6%DuUa4TLI6V{!E>x+lxNSG*}1j&qQrCc?5qoo2cNyZx%mbVu%A=9>_a z_qCBv9D8sy(JWIDMq50vo=vZ5$qM%CVMiRq6T^&g$dfNlDmpR5IQ$KUQAM|3ZE>o< zSuspU3=fS%5nQUWdxRumq&uI{{&3i|#cr6+ZC}Web5ErL zEV5|A^D%YQS1~-vAp=*doby0@d#xQ3gNLeV%+Sx@+_p?Cp&&V5xL=o`5oF35*rhCM zVR9RunD$jD)80jT+Rb|8QzaIft0nwdrQC@T0Ku?P z*HP=zbIY6-;7?g^x=i_WIMi!Q?vK8MZqt&;@_D<~9yY{ylW2siukw8Y?*&RtYvl~a z_tp;E{34U6U1TU!wvl!Li+Og!c#Ol6xnYv~YPM1^)4EA#sMQkf_KTZoVob7s4@08| zsors()K}4$C=A-^XofU#)Bx_Q*uz7>QvH(qJTZ$61MuV;M}DZ`^m#*(&8Od^iv+K3 z(AD;{7DXphx9yYr<(0#;gCn14rhFXl&MUbEXgKWuYP>t?a+%+5S9-)ooF|fb!Dx(} z)0;a)_;OAW%DAk?*Uy~7vlu-x5FR)|DnoNa@Drnpy!DU>U(R^@4NalXbJkc}oSdUB z4)iW~&egTFU;2@|M@V$A{Vx!*O=%K1oTO+Rw={aE^^T?!MU&TrEDUemKoBRN%De(c zFbwvji#Acx^G$b!$Q##5RP3n39Wzr`BRRq=PI00~4e8(>QpH=Z8e!}K58PE=IAKSJOmwhBV4GoCj)-0r<+zdXj^ zgA=ILtf!|GJ%gcI(XnbNM-{EljL_;#@Qom@I^kKZ!;p=9`E?t9TWiZlyEHY{AE~Q5 z?|gm;`JQM75hK`A-k~-^L~M)Lq;~{E?*S|;8TY%3@>^Lk$Zmj<%KBC%9nOd;c{ELl znc@#q%$+hK79%7UG8(*C)_dn&U(JG_*lWS0@)+v?eTV!WQ5pi6E?s@}pS>1Z7&#NV z5^`am$P^A(@7+r+R{jxN2K{d|=x7=(L-<}e86n;#7khLgYX0JPS5xnuw_z`~s4WBm zA{}On{VimN4#pq+F8V2-r1Y1QV*olx5zAzO*F~M8alTliX;el7-r?G-$GCxT3-9Hb zq~}Gci69R9aW~dPv$6#$)}N>je@o?*QUkaS8`D76J#+;=x%LHewR(!8JTVoW z%cHy;t+;4`6a3KVvOC+yootW8`lmmyk;QQGsKItko*9QEJ)aDYo44|X25?gl8Y(7S za}N9AxHr?$y!^F>cYj>wUmv{lsdUscUg=qysbn%ZJv~F24qEDnv09@HY^K=ZZa(tJ z;rO|={2IKLIx**4a2Xb-DtZ?CXmi^ze*N;@!E+vh50>SOwype~biUL_?B%=c8UHEz z6>oT6E`K8w-C$mQ{pwNNNx01C9H1-Y=li|Uih)#Op_{X|GAQGXyf`@zQzP` zANh%Sef=&|t#S&25g`Mo^=YsgOKX$Ej}<1&X(s#n$ect6i8Zg^ynFMQ1@;E}Aiw9Q zs~z!Bf1WnDLOWKR7f{5BX=tFPprMvJ;AdqN069dH^WPZ&w#W#yP=M7>T3>hT9?zrH z@oVYK#wq)OoeclJukn|QYJRi+nqN_Bh~bTx^t)PXQ;sS&k=JNjrEW6Pd6s!^l~bJ= zoZyEm=(U>kwmmi6=B*w}|HNWLs~rt!2{Q*$Ve|x%xWQ{2nm@)i=l2Yy7cFFh%8!_| zUG30|T&}hNVw0f8sFu8(7(vb*n}J-eUivu45#6*}@YI$VMcg-r6`HS!VV4Va=R1-J zF^;5oHfV5T=MF3$xmsm3U2f*p*KWnDEyfGm$S%G)a<#@lyZLk@?bj(97VFjFW+6}Z z%S9fM&t`h9!w(FW?%F*>drqf&W5!+uC-Q?y1}TD}(lye%Ut*M}*YvxH{R`V0)Wuoe zS%W$6x3^d!tFwJ)?>Wq1Yw`q*4sbRy{IvH9Ok;>8ijlT(R95v+B~e!ua&3o68Qt{_2LXRRFm_$;NRkeKb;he4gujC$qehypJVy=ei)szNwB-#C zB8kCT-~SMdx9VN#tbz2=JAN4P1Fxyh6!UZ$bR)Kj)^I@3@*Mt+o@Pj5V4asB|J5;7-AELv5_BE$OpkG+Hx5@@->(4=(F}O#h>v7=$XjS z*S!GynATDWPUMGqOhx(lYcGe;*M!Spj9<-<4wsP%W(OIzvSzvH1t)G1PlM0Wu;_7x z;2IL}smLv^ZGgdrYIU9#Uy(Jt8B;qs%z5yJQ*Z;7QE|?3&ZhJ~#K?C*wrqiZUWhm& za}N7-!;zA8r!xBq|4|$ohGmGO%Rr^|c(uK~#mLZnFxU*$+HXask!GD=01x)mWX=*C z(O7a=51N}_{ap9mbdG2UYKJH&4kJ9j%x6;O-6)xrNBTV%W$k-sw;B$VvUb5uVmi4* z#gb#nLmFa?aR&!+7}&M?3Bem{&fal8hr-uYna>WyAgME21tn97W zKh};89<0L3A?C}6Lu}MaJFQU$K)2p>mScdvsJTJRWg7hq-GPy~GrI-#ABBTBd2$r9 zay%PZmS0IT%_;2^gKI(+eXI(y9J!-qwC3WNBY5g|)LG#_A(z*TAJ$7Sd@9+TYg%~3 znEN66VHPHo-mO>n$;hdEix(ocG{fblu`hme1~v-$g9oc%f4YZ#gGI@$h1h;H_7@yK z6nE%0XsOM_@DNIl;C3qFNR0!J!MP@!T8wN;Bt}3n;z%E70>Q;9>8EC#@;SXpVhEZh z#Kg8I&r*gH@9i1}E7yR*UQ0BRfH8mv8dB`!*!G#lX27q}Cn+Nm5*Q(mR45kViuRG4 zJVr%48;;X2C<@{{0Vn1MtdH0-J5XbKyx1IqvPXI9fUytahk0`}Juewnw4wXwI}+n8 z(@?nt*Qq3*7oX97bC^?-EygljZozAXvyxh(Y;X^i*D-96r*2eXi?-9pN-y!M-x9jVYHV^TJY_me- zwKM_T;I%bT?}wPHu!LT`=A;gmd-yggL5rfVsavjmOmX*N%_yA3zgjZLOF2%qzLIz* zXla8iRZP{8BrE{((&i%az*0K{NYNqs^!Jq_e5X`rx`rd)>tRu-M);XZfW{ zqS=a|z&TdL^Z7ld8nFUAxJ65VY4%)YVjrPIQD=#`TExcKGnDnAAHaXH;_@K&=ANvO zh-~^;!)?I|rc8Il$rdN`Sd57aK^Tl@+SMsJe0VizVHh$v%k5kQ_vui{ zU}6zGVbsPVenNEWR4mgg)P!o8kVU~$RFqEE<%yWJ94p|&{9r~YzM@r>e!Tt?6Y813 zoX4ZNL=M}L5k}D6&wllc!*gQzFF8CGJv2N%70XNj9>ahFu}K7Lj*~x>L*6hd2xmv! zlO0A6b=2-&ON7Nx*O;{&+(UP|kEjR`0jBN{?8WImpcs-N(`~MM4-Pkk73g^qOZqOq zTGVw!b$K?hbT!4%ZndOH8;r5GwX4n(i%FL6Ju;TQK1Giz-@>G4mUqjd@f2ad=cq=* zm($!MPCQd-?9Ejut@KpN*g&jY5y6nFFv6V9up3Mn&H+YsjQr9lY}yaP!ZjK&Ol~>z zN7G-SF5_oT~1}~t7YPYzOe>&nqbyxx_f5LtZvj=3-wd|ye^rY?To}%fXPIpB2 zzEZii{Cs1r4t0EeCRX==c&Xir-U0|4)6lgjGDS7UFr(n|85g&mq8us=B&w#MJ1{pr zOBmQz&Avt}8Mr1E&5`vaVdkD_#LHj=G1GO;&<6JLYqeE-UoTVmUCIFnoR}YEdkF%9~f!@Y8+ytGJ@|@&kz~)Lcyve5)Okmxn`&|&o8lh zUaQGDLOV$W(X`7bv5<)=e|&5ssS$e#ef;Hi%@t^d7}y=UMn^cMf==ZaPj#I|x^~+Z z)@wCP!FgjE5Yy%{STu~6wy5FpT7}M7Rnuz01w|bFS`|0pI$2hFPU4`xayy3U*}lf0 z#lu|rb`TB=6M>=MiF|LL=?A_0y%!9_$lurpr?Ju?%&sT;5=Vc5iV;o?v9!XB<-Ewn z>Kq;yU|*Tf$J|XEPMkW|lFx{S@pNCK;?^`l;xqvxOT=_VlF2f*c#F?XUPJqjne1`S zkp$w*5Ayke*uK%eD@S{%04S7U$w8f?-QTW38@?L1BI4u_ZFms3hglYT0~O1&8}M^M zbF+{K*xQEAiSYG6P2u)DUFg99TCw-KpIdCk4cn*?G@A3r*k=4jcSR`8CE}f$?Mf`{ zE}MIEUL6jkO6PdHz)>S*)uP~K)e;qs)A^heWl{X3=#7dXY&vk7Y&UP66-=yC^+ZS; ziWU4A^(>_GifI~Gs|O4=W}mMlMLtt>BBlB#uI(Hx z?qE})U%hhlb#WqzU+Qj6OwV|ZFQY|47)eu-*xJAJ45If?2}awX`wB0?uQ%AQL-E0( za+;suX}-otvX))LF8sr`#G^X(e4$1u9VdNw8oU9d)U7k-au2qOJTe@R?O>xUgX%v*IqPqug1 zf9bPZ6kSz5Dun%g4So@tR?y1^gIv13dHU>X?0efav#Sk=7*%gocwKC}<5!_MxAf6= zw*s1@;K1ME5*{SQmx1a==Gdjku;|xLAW%RSOq&e}>_Iy zj;fxl3q%^{>neHr=IPV)Akea&N6c}BXSC|c9d5QjsDW1G+cStsBjFi*YhG$_Q^y6b zu6r~{Gq;zBSHfq~0{kz};)#^N4j3 zw}pU|{BUB_`p~ip9;!mu1I5Y`Ij^*-ET+7gmzUfE*eJmrbl)8;RF&nLc1o}}wC|0_anwux-;0V>dT^`GP%GA9kt#XF`+1=(>uWE5m_KNy;?0vZ=m+L4|0skq^`(;N)4?ZF3B|ny-MNF79Zp8n9cid_~EiD2g{v zpFg7(lgd@>oA_z}nXFp)PRM!-o~c`Jr<_hj@$lOP*EJF69?|0QmtY6#YOtwSO`!{Y z=R{&IU+DP=r8DHUtFjJr4}v$<9zn#gcwsumwqVukh9~k4N?~J0AtuB#;yR5GOPfE9Uuv!{)+OZgHx91Fg}N7KNP?0Xprh{tF=p;e z9eiL_scetXI zz6G)BY^c3Pzd6ierd*(t-hk(pll|WQV5j+*X{ZZdkk47oWk7QX>t%MJx%ygYzb4bn z3K%@lFf$>_jV}!DqH;BYVJB;wi$<_oIMK1usMHB~ABE|PmJ)F?vByCA0>QL;wz2LO z3AiXE(xXa?{fce5o!MKC%`l%`6%4}|5HagKk(ls3Ns2dj55dCgh2yF@zgG<0cH}2h z(^{1M6ld3*Q)2YVfqQTfex^#7Y2iGr7To@cv6Gm5O(t{}u zW?812r<9Q(=d(+f)8VAM{@h#{o5sTyn2V>Dar?1tnXQ0EQNhW`D zVFpju{N3~X9-~E_Z4s{#)e>Ci_qHvv(_l<-gruva`N4*bV%otIgozQczy?Fdix}nS z!g^`%BYy@@*MlPTg~WzA=LkF4c5^%@m`Ucmx*u}+JKRW13!bLJ_eTMz`nszIzs2Fy z#BI917S;~Y`eVg6HXybJ`?|Fq6-Z?T%M0I_*h_2}C+ch4rSq>Ed&HlSm}y_D+d5w~ zHiAFnMnzvEzldL;odcJPMGw4?3RHHto+4n_<&_SbOJgCgzDiU24HaFGMjBxTe^(m& zYKD%hX%E*csTJ_PI7qVQN`{YQ)tzq|iybTi-uRM6MSOKrJWCNVQ&?X&&))eNoD_HY z8aeyi2@Qr&X%c_^!dJE8_z~Ltr1OnFQZOigHWjsJ!;}JhlAatrt}_44>skrGj~55y zvR988qc=!>;@0v5MbvN7cfLicC4l)EMl4+Z_7xxZ6CVerl`aqQVO^Ds?#ti5hJH); zov)1S%t4LUsqT2zVQ_*U%xZ7!T-|p*B{S`HjGIMy2ll*+ID|x?Dz7gXaI>fhJ6L`} zD521^V&Yw**?adw1+4q$dU{%xo~j=>!4IhXQzaq=(LI>VEN~TH99csY2}7m@jK@W` zr|jKpZ*u*xBpDheey?LY9P)u=KQ#5@k|+k&$VDtv+IcbbB*#GVE@k0_Kj;na3Vk4hRi;4neK1cOT+=={^%f;PIQ>% zdbKos5=!5wgKv>9&o3x%v&I1?ROXb)NjK z?;S{uT%_CGz-21Z;1kYmaz{t5)1@zCQ^6Mbg{G6iwfTj@)yAs3rdQ2QgbD!fz;zLhy8rTOjdcm zZj#3cBd_zDl2$Wua#ZABgph{E(I#buQVFeqyLiRN8b5lD#E2Dvz(cZ)5!{aiHRUEM zupiS4R4h)l=^Js&SdyzWlm2jyTIKbvX50`XN`*5GI1=DK#nYoVInX>AaDwDZTFIX-f|MsOtvS>nu(<4!E`*TC*?I@@v=4aEL& z4_8U8IEzBVL@v;P3o=@ajm^gb)95z!G*T)dG9G<-l~`eHE>NgNH_zg_tl$>y{XpJC zrB}LU-6FS+E-^7uf9a_Ot4&4KdER>JL{FS*Be+gI#KrB}q#{v60pnpVH!0}OF7K)o zJ)>fJ`i+qn02~T_Yo%ey6)Kry*qCed#iky280+JzxPqQ3b3|fpo3R+Vfy!k%!!`g7 zrw3eu8UKXC9@R#^-g|V7QzMzR;t_J3QATmLE5nc&l}{6 z2&L?huIF#5(7LPp+f7Ad#iC|Y8F`utBXtfp5@Pd;@R{OWM&%YNlxDFYs{J=N9|j}j zt$}XWx6d+D8FEzYal$)acWLC7Dz9>UE;7wUEZ7uc*un;XARmO2idw*ch|es~)~p9}cI5jfJII9?TQUHN=_fPF=`rR=`dGXr9z>a9$y>1RGTXmOU=;2pT8p2zLANHIXiew z2Pd6walZxU*}>@jRanvdV2O6#T7X0RT|3MdYlDjv`^Bl&TU#9S`sHm^a7_nFbQqxd zXgg9$%Yi(ZREU_i%|~CXjZS|kzdzTESBw2oCrr2KcdLMst%XJqF<+Z2Jy@$sQe-U;o+D~58te2e~MCxFCds}ByP$G`i=G*HddqwQ_fPs zhQt}3P|Lma#oXgk8mO;Nc}D55emWEvO#3TC3)7?>#N%J6xSB%#V%B3qg2Q^cV0jgq z1NcJ85VLM~%$-*Ljt$z211{|!Du|Z1)4ds$Gl;>>dR$%(^X;OKQ-K9-2h8tM3&0)5 z=2hQ3gHO}y?Ot&=Ls>rM(7~7%^L(SyjJ*#z;%+NAksp$*)t_6wQ~lOIihRxmAb;=P zpA@*{qPAR^Zk=xGJH9AFE74U@XhE_S^Og|m5(E`d4@W7LhTP@;R}4K zx-V=kYfg;y<$atLMQk{wKFWV%SjAvT#b2W2$-2ePcs0|qU5G2Jd8ol{)vC6$u~LdL z;yk){ztn(Kni{i7?HS_eQqUb46SxwykJhNc!Dt$O{hB@5&aA-#eW~&;FN`_v)t48r zvyPK?Q0=9Pdk=}jh=S;W4zabaqF~suWbfX55E|(_*P;-U@7FE&JO_=_a<<3Kp14!3Bx5m_D=OFb=?DFk zH|dMY?W}Y)PGE6j66YoC+!9JpAnuCv&i`FkvMt(Uwl?OzHhR0ip;ltv9bKHAPcm-$ zgYGCXKYY>-N8{helWdrU)f*TJUv%EBFz^Tfadi@lJk^UK-HLqR&6M`c9W~RS=E-y0 z{ClOL=6;C~Os)g#-~$iyvb?Iwn_Ac1iNQ2|qK><77V;74Pw8c~rLHA5L_xN?FeVYN zwA7~eMOmHkr+ME%t-eyBeZO5Iys*+Xr4H+>Hbv%@O6&yfN2HL9?l-X(rbk~?M$4q@CR$cPaROnO+Y!>iYR({*JRjJ#+F3>DX4 zS#7GRU>I}{Mh&0rnG5=8z!*IIfRvXjah~ud4E4tBdS9<@Ds6De$h1V_=xR|J7W-5R z`e@h_Bi3jx4BlJIVzWJ&b81qHqEAwJbphxp{SIPy9?=VRXFkbqgpX@Mn?mrzqpRET zg&`6x?>HvJF@5-2t?ZBaW_pcA8k|_-l~7;!s+QPWztI%G(@H%o{K+6#^cwHy&Z}$b z60R~K_(7TQ73qei3@0lTLxbVvBha-0lUQ>e-)N!|boOP1(d9;CaC3B{@9gDR%p&Z; zTnedRCq2PKRer(Di-gHc&xI_8oq;T05C^a0hhT3Wk){CE=IhQo`CX2}e=Yv87-m1* zOsS@52k?BkxhsrP-YynOnVihE<${h5luBABzn|Y@r0LEY+#KnsQSe5SYJW5|;S13- z#EzFUe_m^G-NcW+vx*)n^Wo;KwJVFm^&3!826L?JEvvR8$6I_-3eLYERr-l_{rY#y&T~WTb*<_qN>x7t7_X_OJOV6XERvz5i_R z#~EKz{x-TMdXVAk!_UB%UEEL9wYK=X+Qt`%<6uzuR&_@tMt%7g9H2BSRpl#nM@-i@ zg)B>ISA%6)8Y|Phd~<4jE%^9;imsb0rBNl;$qg>lO>caOq%*Y0;#L3*4^et%MJ3>^ zTB=AWDGLx1c?BuhY?$c7`fJ3$^wr?l8bN3}pV2M?7KYRL?!tE&C$&}$zwTmhKs`si z$o~wKtMB5Ho}UlKAl&758ob+|eXDYZQYA$3r`L3$h~;ck2xS{KL8D!ZytB$)7ivwi zin$z#(UT3fMC&@0HIW%PL19Fbco)A49-8XL_vVw%GycR56doh^%5d}&#v#q9d_rr? z;AB)@QLFrRga(1w7<7_i_tOI!Lku=aPw9OVdqHY-50Y2c%@B@qA;ZgfDsk+gegi~U z?5cJsfkZ@!s2b}XZDpD=(5Mg>!|R04a)y`Xp9?N}+{WiC!n=@?dj1>nZq=0P9HJ{^ zF#%-;$CTKUjK|DUUl1l*A6( zT!`P!`+FZodxPLxRdnA=g)r*-s3G6)o0{QGp16|JRw$_RxKxIY?vvPIrrQ760G*h& z@OSi}B}H5vd@o!V4)W6G_jOd-K9+NIi@98JITBY@tqpJMB&(wdwWpFZPJL{^ZXxKX z+zE%)9VOL(9q*~mp))&9xZ604rHY29C|!)&9=`Vbyt&u$_1_&(I)OZ8%?OFX&}Eu^6)~KA`S(RWFHtG4&WORP)?!PzqnqU<9e@^~ zd&U;$7EAbV_~CTWO{KYcVuOHY_=C_?M`h5t!b@6j#JDP&I@M7rJXAFF4+hAN) zj~zxo!pq_1^fyUG;YSP{#<~TEfM-pitrV#G&f`KrzWn?6{Ym~6u~#nj<9!G-e$ses z)L7~&N$3cX_`!gyR!}k+#6HgVWXBb5h-wQ=FlwD=D~n zs1lp(u7+pdLoATwM61I=N?P>I)uoh)S}px*B?f6YQYfO=s&tEDSNqq12cP+xmi}N` zEqKl+e@Dy&U)=o>ABUsRf{D1qt6)tgPr6WxGteuvm8UP`?v2u}7>Lwlg1wc~(tlmu zSK(w;tJy{QZ_ebh7#r#g0Y;H7d`B(19^2b}2{*cd;EJ^_7vkh>iJbSSiu1iEM>r@E zCTj|)#o=f~20=v}@q#_jnXZ0w-$GohVSa&o&~Oa9S{D7RX(iExc8svAa-ZWk5Snw5 zxZ4sV(LpNoh8T9!a8t6x+aYFOpzV$qw3Q`~8{8--=;d=&ZuRWf9x?XRaXl%dgKVbx zG8Wf@Bp9lw4)hh;hCC8etG=DM;%=_pu}O2E^~KqTgKV62k25MCVr)9S?BaZYu7a_T z*L}KEF0smrubf?CISDN0)vbwaMh>QYfp)f)3Su9-BaX>(Pu*E$@gYTMe#lRDc(>E# zE3~oKbt-g*VX%-mrnG!@_Qz|X`dn$Co;b=v06Vd#Wure8U+5`$p|p%!X;PR)(J|58 z80}3qCm41rX2FU4ASoPmX%7}dZ5PH)f?_Z(2527aC9DqfhInwj3hHohl%9X+p78}N zZkhvQ(RaZOGA8A8QQV6#1$PDqAJWpkVB53-zAV?=#)+>>^k$Y5f5>zS+9muE8^%n@ z??qx@P;o6hXzgF@%139fq~kqxF+(p^l=pm2kcBbJ9oNe@gX#my3@7aS!Aqa&-Shv`YF zZomnqW!E`*rsnlgCCua5oNkWA@^$1kNCUIjmwFGYI}==?Z7tXjC-zP|rLnCq(@50& zeEAK+b4%w?ut<}QnzbJ8_g|Z!>FYHGH=Z6HDwX9i9QhR@g|z7{#tqEQO^FDWgbJc# zYaDZJIqWX;NsT*p7GJ~-?gJ_nH@CNlhtt;`c3k9F`KIVnEjjF%c-;osR!hF)xY6MM8R^`8FSI6j(ACaZ zE_l!!*5*k1!g1qVF3Tl9i1-R0L8mX0P~TVY7-xoGd_uecOlaVo`J(Y`4c&oGd zpp)2F89u$t*K)AOw>3CiK<&_eUqe$SgBQAzI*Ry?;t-6J3a`neUKW~k>2UDu;A-7~ zGcZLIcl1at!lgTMd-^K6BbZb!Df5NpvU6066Ro|n7>{)}XY%9(_B-|@a4R-C2*Aei<>k;zVAb&I%3`k; z+VR+D?;dT;p}1W2EC>wp+jTLbdT^Mjcwe2WR&C8DSXrQ>hxvvk1j4)Px{jCmaX;C=YF|=UZ zy@tL>nuYXTPh*}x zVY_#0Ew+E0LH8|MsjKD~ZV__IhboH}BeW6pLu;EXy?37WTdd|XpK899!`SsDBw&c- zLT@`mwcaD5=-E3T_xM!i)uQZH%-OX#P_=-vUh8WzNr5(a#*I!a*Rsm zaD%fcMg6Y!lrwdsI6CA`L;O&}k=#Hnh2HsiEJbBBH-V8EYO2gMJE!*Ya;@6)Cs2}W zoO(0E$6&b8h9n71Ykix+NX?zxNo@mHJu#;Au$n@O#=hAKX(RR6P?AnENscZ?!Aq%o z4)MR0ym8uG#I3zZUO{}Ufx6Zf8(PgHG=AoxS$pc9;s3xKb78 zF}`e(-%YrgI9z*WF*K!9wBMHVkq!z~>rVG_%_1so^a86vdd#}hBYD4k& zogwGsJw$MbbrCZQ-#mW5n?>N?gvJvNM++R2m8@-bIL8k zVG5%i^L7S}e1nR_c`-Xdz_-h_1&p&cD0nxDqL~71_$(tGU$)j`Nc@RQc1@ZVt)dE0 zHcdXw!G~6)FFWr_QU)HuOcLVXCY!Q3&c4OC?awmrF(&PBBH=%Tu%yWplxH*&hhg&l z#*M>Y^s8T;0cLT6WWl;cEHvqw&1!WV)K~Csx-Gec<1S5G=5gyn_MxiSI`AFFF=tb8 zIT%y1SjaJ2MAh<9o|wCs&vNaN`2Vx^ZrhC`S(e~hJ&*Gfm{nboo?Rh@>wd{zT?3M! zM3*QjMatd7YxM&}!X>0gLdlD}6hAayFmLl#KQ{ZA**=DgQ6Z^&byc-C!vPo!cJ4Ru z2B3zEU#?K`(M8}wI%`v;f&GCC^+89`qTZ_m?=S z5qWI|N}5|cN4t3Hm_kQzBtM|^S*DO-=on6J?P`t2AgOyc#u?=L4X!;oq;+ho(0gO& zD7?EbFmI>m5HP>^p|omnbn)Z2L0Vn0FD^e#|A5TNXRI(IwuLy3kNq0;7tz+nmL5rj zf8@!kHlAdQ0f;W;5e0_Ii64-i4r#y48pWD&xe{eQ#k`o-;+Z@h7?CUdfNY#82EJEI zBjnOO);P4ErPBoc8y2lUp*t!x9o=8>OOX%IW8v9AZRd0k`Uo1$)8`J=Y=@)HAi5dE zA?D$SfhW^6pFoIF{u*3%ch?@O((}4lz(L^$J_s(UWx=P$r028MEm~1MaF-o?VYfV8 z7U;ZH8eu#%*e!U?2xIw7N|wppFDDnPk@jK|9LW!;qbsZAwf9!SikDNc{gpq75ik-cbPjg4z2C)h z+0$mM0BA^uL(mM*%Bt0zx;ni~E_z?v6zUEmE_N|Qc)xsj0|dLP@((VRrEl|ZkvW1P z7U2uQt)D&BDVfC3oVxiELsG&!r&93mD@pD1eXxl`+}Kxyn?xvFMO&P>WCsMdTD4V~ z^(~Y`HskQ0QqHc{t_;6V;JYMC2f_z#%)OH@rN@-DQ-5(spIC~9ZE6foyYe@fP{CJF ziI|MxR)j;I>LYNOVuKee-F_~z36k=Me1C`k{P9Z2=7{6V(fB8}XQoP;d)|rij2!jF zLemo47FQE|Y8R{N7oz~ToQ-8Lo>w*!FiNhPD~wN2Djo;(kf9fps=`bJvmdTWau*MZ zIrO!4D76a@LrZAw)!?uwzd8I5p`G_d-!OAi`G*wAg58y7>w+eVp9oI&$X*o0i<-9R zhku{P7f6|-aIr`aTvLSaX)|5)cbh-|pYE_45F$!nwvCF(9Q{)@F_IXil#|I6we7Y2 zRAM|UzNb%c<4|H799h4PFl4lqtG}I=vmk>WLq{KJm^&E!1SLY0PM(DhX1AR@@v`^; z(n>^ZAbW@3YD-q)YY(ueS2shdv+aQ-BH)MQ+NC5m>XPLkD*g4J(U@#}uFe;W-Q4iN z30AUe4NoM9vLxpNuCRDp0a2aXh$s6h?|39e$2_MZEx2rSgto3!=q*J~>8!;_`<2B= zLxS;A>18SpRnZz}3;B>KPZO|_tT_AC9p2xeIsIv_t-SCj4{UIC>X1D9ar;|5OSNY; zal@JA$AS-@5dZ)i#>~J~MuNjwU4aFPp^7(e4|i0aM_A%0;)R2}V17ahr|V7v?% zCAE@d0WIzUDWOKpi5hZW`|L`anBO6zKkn$=CC=FT99k{s9);M^zLF>c8&E%{DIG$@ zm@_{FL$duprO(2H(I} zUz+0uuZe#QaY0O-sTQ+0Rh)#sfBPc-QMD9gLvEC z3ca(R>J5f6%8&p{U4$6EBvojhYwJ`E)`+m#Q2i(tmo5xGTP;S3K*`KowKW|Ug*$kq zT8iD(KhTk5b-|;OTj$L56T^qa>={Aq z3+govM;Ff%<3J7aMjt01WSN-3$pe@!`zhds_48uFAU|H-> z(;8YjRMJE^tdOrlS|VO8{Y`@B`B3F(EDcfcaTyRXxTNO6UNCj80UqwlwoTu5WHae5 z!fBkF?4}4tK+sA#)P8}V(or$hbYzRxYXGJ6fctFpa8=}V<0CnCB(vtQrM>~MiUjOs z?F?yb1aF8GP+vT1;Y;Voj|U&-(`>NJ9?92<*S~X5@aEl@cvXEUrO6M?1W0>$fCMt}it!1voN4pgtoBkao7tqmyJ};YXZ&r8t_jen+MKK{}i0{ z$nCLC?jucsKidHBq&;jvfG&Iqw`tsYR^0P+*hZfC0ct0+lWUJs!uvg%%{CN_ z?JbM^kkIf`y}v#SL#5ZqqTuGL8vCiZN1oWd4#pv#McsKO9bP6+$ktU9YW@2tRx$%Uk1*8dJKb~!<5Ax^h}yV}18wwV zv(%J4$&pt)NY#omIKmGIfQrixca@}A3FqX+V8i%WW-#8&DbR~r_b;`q-f77YkE zURpYJ>~oIkB)(4DAhSGY`eikBzooijuhPZKBEw+nOH=PBgG75836bIr5au{)qp{j5 z*Uo8ngX&BN-B4DKHG=SDWAu};zdb*Rc$h}+BN|-)QlJu~8U%7P+De-kEGQj) zwL3C#K(w^gL>#8$9622tzj7F)WhxFz)kD=|(mrH@0>*ye2BZ&*;lg}a9~?PoX3|{pF=)wT+VJ{_p%P- z#Xgu9yG5KnMVbB#Ba+dmI36RT1;-8YS$_XDGNZ;985C;#*Q@rT7IXJQv^&?IE>GhN z&D!f+Z$|c)WDuPuy-sp*iZ&2|$aTBRobo}jOcyGF1cY9$R8pPIzKRSXxec+*uYban zeML3LzR`RjHf$ky)Jvt*uJaTnyzN9~zRu!#Ry_*7idv$)*|c5I8(^_ensv{q=Gdgx zj{0)YcRvTioAJ@a!L3rW5Xs^5dHO{&DJ(|0i7nZ$tylvigI2ENHO`_Ju2V@pSel$E zO(X~Q+qK5emS`#g$aeOO`caYvape|aSEZkaEsdlgptP8P*k?5|yD%Kr_Z8P?S-vP9 zR=&}S!}$Z)%i5!qXuR|5gT_1YXG#1wtaD#qEp0|HR#n^V64z4q6Q$>id(0h&w}Rgo z7;7uLFRF(tQ7PTYF&`DSIoTfZ3$LamZt{cENrx)G!`4h$zcotT*BN?`&KIYAw!_gW zI)EaX5uxNsO>FxTYfBQv?7ullMhs`fb%seY2z9bpM64}5$5rLke0+ZLwi&Yog@v4y z*z812<@K|+6N#Z@-oa*Z$p&rOX}(|%d9Jg?G}jOXF|31tDv+q}$he*t@rp4Si%V8d zh&B6)sf%=RU)-kJ(%WHObnUDtU7xQ;`54MJxiN8It-cg{2!$L;8GR*Zi>*UN^cg$d zA(X`JYpKOWfK19ZbsvXoXFT-QU8NC7#=8|_h+I8!X!SZnMGVQ4biotqpi*)3GHSib}mf_sK^*xWq%EY)=y4GfyOceJD|X^xVBXR z3&NMjG6u>!&^J=yC;`>YS2N?Ir_bn4#W1G9+L}s86M~eTtFg480PCs*G$}(!UF2u5 zDMa`|W&;Ja!miSa&3NqOJbxCm!yv>jwhIdio*gx8Tj=~!ltS4S`%e;#`&0{^QKxW8 zMhQJ0?z2U7`G|S|@lpid0j19Rc&P{ehX3`+6BbAC%b-5Bv<_q|p)_@^@ee8AexAZEG zJI8)BX~(o9==r%l2CrAhP}r{F(vArxEHjEB;;{?Hz$JkK#;C^aFlt>z{@WW8#HdbJ z=6MA_2#OL4df#TWDYbKWcSU~O#r#?l!|Pdft6aW(f;krpwBw|zWN?HZCK;qu zERE$PAZjY5N-uzW_;MpruTD@YLW`5Q%6Y3AjNCw3+Rbv*9xWJZ0g3=?PgChm+l(=4 zV*j*i092l&qVXE8`+Rhsl51ETF3P}#+sX|uuVKm4O=v(}sL4Oqvj8``OaM{Gq{Edp zHgpKw!53Zi{NfdVlc)LLO=OD<6_^sSwza!z zVNsK#D4rM8E zbXgWTcs-v@`U#gTx?W2+IKmH@+W1tL!npEK{S?eQr4dGqJk;LfYJymCeKfROu9n4l zF_JM6)Jz-;sV$Nw?64!kgcureYkT;$3QjEk}4{xz1@;9Gdw`g3%=Nw&ETN-LQE4(M^~&_M5Qh$zZ`O#a9?YZh zVu=|2{}J{>_mF^K#$yxJ6yLPrzMo(VF=lviyWB8`qi%-AI0&fM{PXfiCD`~M(8*Y& zV*nQ!dROl@X{VECVfQ>xg4eKnZ$jY$46Rt+lid?nK5g^4TgQoxo`1s@?Hv08+cVRS zVivRIpTnr(&Qo~0?bJY5!!j7pkUn=`5p+AHdQC){=EWe^xxmDgw*VJuIt^Org{9PG zszph{1~lJ%_t!iq3hD(wk!r;Ii zhMfu{+&lJ^Ho#c-)$C5E>p0xFN!9vo;T9@lq^tRDuDuT&YzVb|SYR~tKVSCxBue=^ zyv_TtO5UNS9-tKBU6&o|#$Keef52Na_|>_69TrCjS{vTE-GLK0kO5NuSO>>`hy|L1 z&Hli}v@?hi`1JnChH`jV>V5M&nla~({hZEdi<>Ua4;TR}E{+vi!ZjQht;14E`9=2g z5m`>(sn!BV<_Ba-VWd%|!~_?%-b25*Ov3<7oS?&w7#S5XD$V?>^ETqY=nRaBxCNGP zrU{p~^bnGb7+Z)*28$t8dWS*5)=C~mlS-O->*&~bj%4>cu7y~p@8O$VW3&H^3eeC^ z^)w7cHx*OVyNPi|M$%O(^e(MZd#}sU5;6T^PAic3(Y9=;5OC{wOU;_M8=tLKX)b*{ zO%b*msv=}@l?T=pp;oCL(D*mtJ)weGb*E(fQg9IBv4`62=Cq$si9rJCW| z!LIWm7sW%awxj{(-#6p`Ei}*5vKRVV*oRMmM@{NBXSmx%8RGlzbj(5U< zb)D{D9swQj!piVN^B$r{l@|wRh&GOvmPYWY(G+p8abCK|zNGb?0R|c&mU)$2s^$x-z;DyZOqmU_nJDbPd)dz**Q>MlQ;OO0t<{2wy#_ajpvY0F zeq_0ey6es{Q3+w+<&kD5Sqz?%NCekTf0U0do$#ROP9?`=4u5Nc#N!CbOKpt$PG@DG zr#wKV+(nM@t+V(C{aP056g+d1>)O2BKqKB)`D`X><<#qlZOB^_c4{0@ZgEHr3j-w! zenI&=8qCWfY5BLB$#RBzC28c&^_~qq)jUTZ7Jo$=L?-*e8-kn54K&-#q~EsZTAyt& zCVyj~ZS4|oniWz2xm3APVoRik5tBiRG7 ztC?qpP(yc@n`k}n6`ELKrzp7`3|^zLp})_b!~yg3-`#2Z`^8sZ%H%$!ISi8aXA<9# z!8lv4ron$zPWZ#pd|$*<Pha&=mBm6Kj$@4fWu9QMpnxu(ZeWHylkO{K- zZ9=HD%fs-PagFbKC;fTa#dAif?sJMFC;0)>e5K|w`joAnYMaz$cyy3lx07=o-oa+F zdA-@j|2?k}jqjltgJa3@;m!yySq#7OAO&{DH&w)|XHRHqL*0?M(vSrQ!naR6M`j~* zTNQ%DBBJD!*=EFnd-osyC&82hzaqRYhuL}DBONIZ=% z2Gx`5yD(nfKcE+PGNJBmvG+-|Rq$Imy{(Mk1Pz*t@83_j-wsFU>jy6>Rv;7TyQ_Ka z$trzK=G|(l`B4tnI6+7r`Usa~qF>z?+JQ!JBtI1B_%)g0UQLIad~^+1UOud*DapkE zv-8z8`|gRtzwVI!LQg$0G&y7rp{E9%)R1kh&FFLOWBYE3)}wf>pUrs~sWiNFVs=zK z_BPt9+iXm2-N8U425-Bg6Dma_!nsC6*1`2>ZO6-2u)YOkx?bwAR4X}#TiY`=gZ{qC zXK;dZsSe6#hI}u_xMZnHjo0b3Xc}4(;&6$bm}S>@S)+*8M$PU`$x|%$jhw2zxoc}z z5*y-sj`LKLn21qUHbtci3gX~svF3S@yX>gs?PoKDcpfx7O>CGv8zkJkT%NiXfa)-pt~0g znB9_@YG$MlNw=(_a>=XfIt^nbyGn8*PbRA;>gx`MPNDC`I9P&Nq$5pQAuh+{UMKsg1JAL=T^kfN@cU&G$oRZ443R#_XaVDfRnD0#@sTBT?m zRT}YK7g6H{T9_9+9K?Kfh=coRe=Ei8C+Hda^wc#KvSZ9#JKaie2H7yB(3{0|fsUp5 zhEoP_29f;%TK^FBH_~duXyDO@<@B!+hPX;e)t$w zn!(FTlt9@XnjeAKW&YR?QG242W+!`u889sg1&iTBLF;OCF@`7aC1-V8d|F3qHJ@)F-JD{c_~)#4 zh3d0*M%AF8F6U-e8Phw3QP=d9?5U)5((sCEq{HQANR1jwd1uvQ`)D$V9ELVIp2PSs zG@T8>ptD!gMX{$|pZ>G8Cl~<`ToRtOGqk62Q`j18h>X_}&utg^n!Mf9Bwc*bg{rUy ztUpP6N@x(HDY^m8`L4u9(moS-XI_Qd01 zQLTjC!)xOBY=0re+ z3Tu77%VKXlz(}XDPBiZ!kKOz*L&P_Y;!3wBylBZCU5JAt)Bbx%e>Zsjr}b?XdpNC= zMK+{i;jrJRn(F6sD3O07anZq%W9befVFHnm^4S+Ugni$K$hgfg*Lm?1Ne@|6e2>7X zcYIZ^wOsaeDzX9%arOgRcX@@-<2)HuOpNRpxez z(drS6h^Q132M?-m+6d7KmqjZjZ?QOPN&h!ApAR-@kPoWVjqn&I;-zzb6ca9!A2zUh z$x5x!m`_uSpPrEXlFg|>5vRz?EYdMXu#7{cnDTU`_HXGurV4B%ya%!A9F{*AB@!BC z8#1As<}UZp9;=s&{0alu$e95_sjC+Z{C$IwHvA7dnjfhDSK?(i$l_`x=trk>5zJ%&F+Yt{sSuW&tT1k(UKDU?Hx zkK!9WiY75m;}!F|ET(AyAMAO7LyhF32V@`A^;aKy{3<h?x_Ox}zUy>j&>?V70ritW(8uEJJX{Z7q(mxORrXzk7qu zMRW^8`z~AjhAJ2AU`;*oogGglM+4ME`X|Q5YeOEc&$4~r*6|uKW<(uy(yU-;qB_0X zoRa#>D;=9j2ym296}7m0R7&bqZ3!yMFTnMsxh0u_!LlPRC=xK=eUnG1H4BJ98YK>f zxQ}o1h<;Pj>sUvC6C)|6al(9VK)-;&fg*2;34?vQB+g0o1$}=<@Rw26FIL zp_ki~q)dz;U9F!wXw!Ff#M%F+7#37v1miOq%Y%F*_`)hp(N@<`994H>m}h^!Z7NuY zD74L=uI5~a#XKiv>a1;m(xp@Odt8UZWrpC_Q`0#Ql`t`EE=B!*&T2*Ckt1qI6Dvs8 zc$AMc<>zXi=2Hr61A@*qpXdH5MYbAR4N(zl%?i*7&FIAss!C~w8Q=$ryvTsp2EFO%Xj2mdM_{nfY zpu9h1(r~y&lCdW98MSj-iU)}r3Y;VJqha6sW|bYZyOJy=E?jUWZE39*9`EwQ!v;k} z>^r?D4pys|z!QA4%1(|10>mx3(+*eCk*c3KR;@dElKx`xiUzR7Q28M!Q#9U`7Ay5} zDe`d5tVAnsbzREe0bL?KB%?d|q3Lk=6Z^l$B#D)xVTnVa^+#ku7DxRN{4I_47_u;x z;{#+YBcSw|m01qEB1k=lwT;mRd(atPJ4{p_t5W*M5)l2fDhbfsUE z=-r}kl8!{SxOy=OxpohwUK5PM$0ZLS#$D_jov3q*?>j=L6}0`T-9_a%S}ciQRHHP+ z0s8fAD^ErvaJl;#83(Xe8AS7Iia6i563(N1wYbQbIb^W}MP=pHb+)#dD}W3&cuaUq zEW*EEbHIGf7QP2;fMtgi>#!jnY@S5C*WE+a>%Md>o{SoP8NT3ToPN~^>543rs!=69>)?O@XBD|$k+=ItQ=zuIpus;@SETEPWu$v&D zea|>iU<2Z-mMz*U(W}idB11QHD4`9>w}EQ%D5HLxAIDcp3=loYkRnRE&EQJNEwp96 zM<(R__%~#b@#7Ld-XkY2cxg%Ken$b%QvC^VK^V-0h>$)WJc+|F{7%ESi)^I`DKm$RIZ$6ebo zC2KT0fKyLG7hiV z^yov@KuOM!B~y&lzBOM1|=`lxO3QLAUUfhbdr3Z%=oF8iLFfD zBv_eC@KQCr#e5kR(}cX-;j($!OxfvZKlo2ueBDo5{QAKMs*oP0`A_t~)%+%l%~WK` zh?C?#Socujsz;`yqz%JC#JJ|16Hi_+41E7glU0V6cE7ssZ!tGZi2Qp##v0H$PznQ+ ze_~GG6j%;bZAD7<7Z{+(gD8JVKW%k$5^(w-XqS}WN z*dO9GGT3*`{QJ4|MWl*jQF#HiSJjH|Z#XMcCZQV;{MubwMrXe#+!_3-8zX$J##nqm zp3cJdd*qP>#?ngWg;{9*a!mz+Jj~I3VxkgDFr?(fxJPRNgG95okok{y)0vD8hTbmO z;qD-)xP%_0(JzOqs|_j#z8xmI)8c$BTBIX~QD2msMv;7$4N@dJs~Xv}k7=o9Oi+kn z`)-(@p&d&KJ!LZS`tR<$1ekg`ga_!aBt}-iZEb~zH2AMDGz449Foc7_9aY(TnL>lc zmm((v_Ma}K;Q1DpF`E8LiWrSEgFQ)@;ZOBLqq2CA-Q$Qou2ehBq;|zRgya(k#x_7_ z@y#wf=?aSAOp_QKEi-D zFcnkvQwLx8xSFOF!?Ne`lfTzr<7-{dupnt0$e1c|>5X5wjVD%Y+5_qtsMbrV`vdcj zK(=dKqrQ12;zl!2VNOO$3;|eHK^ok8h zubY!vtat19J#DMnTgNS5L;vc6?e_MuHz0+AFsMPR0Sv|(tvyt+C(vF+i8p-QMsW zI>&y3U_Tz<5f4jpBVc(ZGYcvmVcQ*WBR=mn=LG!<^n8PH8DIaucpKXL{*h&g7%9^C z)_O0xrT!%-ADE?R!=n2x*!E?~+}Imi|H}_p7Qr1Nr5)$owEJ7{nG?E{x7n=CN{veIA$B zA5XbV4S%@c%vb^{3&7~0wJ0#c5t=~o>!>M#GbO$iS_KZiu*0fmoGzfKLY1JC&0zCo zF=j$s4W{pcoG#h45_^!ft9$+&dn5gH$g7COZ;*%<#>D{n=_p zQRG^swZSa^2O6>Uso^>7LdMQJR9m)kHRJU}P=vY-G4y3ry~s$G`AR-`?RuS&frQz) zZ;@zG@I!f}&0JqQQt}co)@@v?sSALFj~WAa3gXvS6iM+<%%q#oAWH;wYBRps>YlF>4W<{BK~hvt+{S+A0AA8u%*tIW9V7FSHmb?@v+_||&r zcrXWOj&JyZtIYYeN`Ah70cxUGvyyRavC|{g7{{`DbqSz@%N_WpiU$h~+pHfF%_PTs zESRDTN+bu(HLkd@$tX+R3G%7oN!;f+M@JDOPC{SUHzc^u?!bona?8U>jF#b*tq$k_zB_?A$%qb6bt6W_g5D&^uD7+RY`eHaFqDdbMdiJ#ern&7 z;47F0+8AgW3Gsb!+mxcwZw2-9Q|+ffj2se^>Via(V_QkuRGOZfaO9h3HgKxt1JXxS`nonmJTsoA2W9@qp4ASpN?oKspg;@7Y_PqMFd=mnu7?J@JBmc{U z5Y~UN9Tl*@qh**FKjbNb|CEdQ-r^le*qU{;!HUI*_B`QdDhIXoYn}8Z82^Z8#em9d z*Vy2W%G*|UkD|9?PKH8kHr5wSm;ImXW{w96Ni}@2hm&C!V zRIq-|B@yRJ!+;hupBtT}15tWetoq$P=b0Aj%HP4qsV01aGc?JHA-{yfB@zm5jZ*Qo zX=XIK=a(SPAA^>QZ%e=&B@(TM=L1{WQ~watWKY>sbZ5Kr^F z0;i1%lfk8zU3&)&hxOC@NpW*W$}!N_-EBrUceOREUX1UbPB5m79MfPVx60hIbs<_= zyjr#K#I(}5&@Ho=lpfshU5$&K{gCu6R-Ic^4N}R&RDyPj$?Y@zyF3^Mi^ErxonpSm z^7l;*zKZ)D+BY@YuQR{RxVW}S+Q=tCJCGUUs0M`2ukT)fqF8jdSZF>AaV9k@;lrQ= zdW9=xmK0x+*e~=spe6Wz1?Ooy&q`eA99%1>ccy9g?yBreR`UYtr}koZ7|*pfb5mCD zD#3R0A5wLYQtkjtg9Gy$lJJpXf+}A$E*DF-w#h0qTR9STpq}vC5C>0?Z$!{rJmA}B z8bBtF#+wEHU-fBwTcp*?ZDU-K4PI_r+bZ;3eait`>gEF&7FU@=c)3b%FqCKTb1IV{ z))+u-!AfU?6rFg{Xo?a)t3vygy zN8rObsw;)UPir?&Vl%;&N&b^3ff!Q#DY(%p7j2)KTQ!-8(`kvj1aF}&@G!k+RA?cT zMWHKvp90og(I-ZX8Z2-eRMo7QMwKU!b^sf*f^U%aGr6ynH(Vlxi3~ zmkFam@$OupN9e05Mte!Z%{HxW;2@k5@|S{Y2I?Db|4X z6ziJ+P|>COK#ZIts^7H-s&yyjo2y5@twp@%g-0tb4bZqs#0#1JKw3dqdDHa&mumj+ zQ<-%BLoaiyT}wYF7W*rAz&I1}hkIg>U~@`Kg&Y@2;7p-~Pg@2(QB zT_7%m+!u-vi{%x3B(r|N)UA4!p-0mEQR&aZfYdoqnl*AUyL;xUiJ`|TZ-i^{939e7 z%UFGPU)YZp|F=kCCs%dQ?02*A8{XHK_g@b?Cb5R;TEO8B`oP6NNMoi{V z>w>$*kF3yM(iR1ms+4PV{_nXGJTbz@O>uLmp3}F?oRkl#Dr)B$kw_?uWH7|@;4+nH zB_9H|&<#JG!_mzmtsOQc)VT9Gmf7sIzyzaW1O^wFp$o|&pb$9rhf1udaVyHho(-;? z_`oJ9kz$dknR`0nd3Sid1ME;`EDCkrGbmEFT4^?hC>Ewnwc298`Zc}*U*Z2I2J7qm z2UB$T!)#9aKZsvC|Gw1!WRlI&=ecH<6GPe~z%ZXhrXT1da&xO6-dFq&|0zwL9<{=G z_)l~OHou^U09qtBBML{}rpz)JK)!YcrL*GZcIruOwhwmOcK~2jnC|vul)?z#aH25L zSbc5TDtdRYMA83A8G=g&LG7}4jKPrQDgg@t6|>GW^%RREA;+l%*Ci8ioCbJ+uj)_m zhV$_kOqs9CVz<|r)JGZ$L0n!kDk#Dn4AAnC(()O` zV4(Yw4!v|(PR3f9d8D)~$>wl3jAOqVTwQDqK=yLIp&f$C}i!>N12WWUL zoKXlR`ZsLQX2T^?t;k3Do_a+@5fY3HSeyA&d$?-KpV3-uf)qjBQNhN+8WIJuk|@+# zu@&??I{e~y^hNpfUsIqHBWWEy1Zrzl+B{EZxA}amZFL>?ywoP>1AtY; z;VOmO_wY@FUYH1E@XH&o;u!r?mzcG2%Y_r8H39A0_K({BEA0x6qI0}vmlIp-wsw`$ z>~Lb~lP2GAvX)@|e9=pIn{(uNkTF_Zd&%GlRVr?;mS`ZjaZ0{MoK9g`g)a3K`f%|s ze&)*&k0vY+zl#1dBw5`r(!?;&4lR4l*K=jd=G$vog+ z&rzq4)c)F&m7-B+E?wKy3M8gx7^I|@N?qe&RJRgXp0r=3g>J3}8fS!6Fg7HBXPP&%erlNE2<1^*CHP(ABfl);#q$ZLP1! zXSNCHhqw}C3_0HYD92VhS>C6AX#NawseLR$^|!T!dl8J8USrTOy_Fa?jPiIx)V4JN zsyKrSP+>eo#TGVN+Y}P7Kh?f8AJmB)gmlUGCx*iPvb)MxSqw1}FI}{h@mUO~L<{4j zgZYo`1ID|q5Cr;}K2tb27$Ty!5->uK2+z_RXi-${JA`BCOYx285Bo#@Ml8j7LH^cO zSqsv}h>gYbzq%xKVkrg1U@UlhtJEJj-EF1f4n{)x9=rn9E7%hXJtEb=B z;D5V^6Zt7uJC*nj>$DE{W*o*B;t!tT2E;=t1c+6D)E=uosN^w+N1352i`J7%=Y2t& zj%M_I-VhyvhLT00L*CIjCMU?+9OvrucCb1S0DIq2SsG^ur@~D_wbEkl;tCP$^bZLu z)^P03<(299pVh;&70qEx$7bx;hr$!E*^TwmE?r*L4e zT~1K#20rA1dxJRjuk7jA?b1P-KQ1D*iMl&SFk~ zm`~xWsin+xJ)w+X-{26Fg`=q!a-OQE+8mb}W%$zb;l0%1W*hv@7Dw5JX`pY0%2$i2 zA@Y6IIob^xHmc4nB=VUb7IyrPp(uA+>+!&@NCRPy8eScaa~e7x?PlHweuGM8DqgxeiN{V9X)j7-;q{F($I~E{5nba49mrIzOay zbX>9viW&66Wya!SwDWle3j$X}4X8{8HjyAC6VIe_Q3OGSL}Ls{ddDlYP&LI3j3?+o zQ4G~Gv6zyxT5sQJ9q+210TwWYMNEghgTd3(IzCYM$1BRO@tBS(kFpuBYmLxp!%@lC zN}iTTeakeh+F_L}O^WZ7%Byd{;W7VpMt33cY@>xps|;so^`q+{A10?3mn#MGpHn;$ zyUXie5tr(^`A=VesrqN$eyIw@w_mE_@a>nXCHVGBRr7rNrOM9UeyMWjw_mEN<=Zb+ zjq>f6s>po%rOLtIeyOsQw_mE%{^m=4Q6YO`d~hO%IJ$?_$!2$BNdhcMAo|qGM4Uir zc{@K%P{OW7ETWeoOu@LkgMnNpxk@>PWJt}%=Q$aU#kdM{T};|ZBUMT+SFX{Pf=UdW zqECf*jD{&C6{{>e=PM42;jJjHMluyC@gAnvOQm&}~kU zKs;}@29@iSg_w^5w-5J>*hWd}i&pmGdV%{AZQCo{h))+&#>-3fTGix}XDcH?XPM_T zX%FbwH8{c#Vzt&@R9Og;0RO;O@j+#aRV(_x>VjGxsa!<9?Q*ry;DN*bY*zChEqf0)^by4OV(^bXgOT&61KX@+j5G}aAx;q8@9 zuhYkTywT48;@%$Jn}JQAq7Sp;XOu=V)K!vrZ(*U#Y=gQ*Ianu)(E6C<>({tFr03qE z&{y-+y1*QS?4kGH-ScCMMT-;NGCJJe+dJI2MY4h)po%}c*yku&-Xn_)P3sXTj=uEz z)$_ikG~JusouGbp%%cU2Y0mPCtNvZugx#`RLf%RX0dSI+*@%~0L^2T;*7Oz1`BvwTLGE-0SpWKF}g=R){0|jtwBf8N%vXj7VHfJHx|#vxY8u zZb<7nCRq@hqhEV7-=ZOgJEpknXspp6qj-I6BKZ@c>JE}RXwoCO(Z11uC z>^NUijN#99hi2OoW;ULnJwcye{MzCOt+YC|C#%b&92I-^gwTLu8cR;+JIvS}LNwt^NIebk&Zg zg>aYK0(p_qhoAZEXfMjge_2GPG7+4q#sasMLzBt3*OVo^Mw`@SqzE# zBSqeNfZ^;a1@vb4h^qz9v4bzHRTa;{7>-LQU@FDNV$uGYM&{cf^n2~=%P&2hR^V`y zoG0z!_2osc9bh=SN(SG)Lc2k;214dIH~p*LMd#)hC&UCplk40J zhS$C1{5*kcyMJ{vOs0N?~tOR(*qo zy)eFjY;NRtZzf~k(2A`(&jk!&PZu~Dq`?t>kkcqt7*Rt)_|6S3`ldsdiD9)S2&oNPSf@8}nj;c(!)oFuPdO0{7Z9Ag)f^ z^!4G-7k0c;^1R?<@$AF%^3ussayJYC2|x588H{Gq!Y?4;{OOT8T2q@NRjJKAQx=eI zV8iHoFd%g9efxz3!@DI+ObI~zLYGf`qkVN8offjociLh@Ft+&)>Io{0X64Q!a-ApD z_h>--O0Mh*PMPO$dBdvb!gqaeC6h4~3YJjD(oj~mS=hMQ{z|%ntX}I

XxnCyFhL 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�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ΠcC+Di6=6Bp5Ks+{0+RXQt2_`Ljtafc7DRjtkNwA&pmALyhjj_-TB bLi2h)!4}vr5Dps9^;`j>U3T@iFwXx!sRsfh diff --git a/resources/ndb-subdomain.bin b/resources/ndb-subdomain.bin deleted file mode 100644 index 5bba888ccc001e4e6ba5a99287ae5429958cfc64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13401 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 diff --git a/resources/ndb-tcp-service.bin b/resources/ndb-tcp-service.bin deleted file mode 100644 index 99e21f5cd18e549b303e8a934b77d96d564cfbd9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 317055 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

7dG;a1vCvw=Wgmp$g9oG7ipIVo@6+lbyNV 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 Dl-U}0 diff --git a/resources/ndb-wellknown-ports.bin b/resources/ndb-wellknown-ports.bin deleted file mode 100644 index ad9bba8a31db6ce257a219965ed097d02e35f3cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1378 zcmW;Lg<22>7zNOK@2?`Fb9U#ZPK>FugRtkUSwjWHMC=6YA}rGG0Bi&sY_Yq$XU|3j z3*Ek*cR2CF{|Ld584hrS6LKIIaw8A&A|LXj01BZ9ilGEbp$y8R9LmEP6;KhCP#INF z71dB3HBb|^P#bmO0#~@f9SzY4jnM>6(G1P;J6fP6TB8lxq8-|!13ID;JkS|k&=sEW zf;YOs2i?&FJ<%I|;ETTSLqGJ#01U(+48{-)#c+(kNcbZFff$9+7=y7Ghw+$ziI{}R zn1Ub#BLtzCifNdR8JLMMgd+lxh(a`GVK(MqF6LoA7GNP3VKJ6qDVAY5R$wJ!5Q{jh z!fLF+TCBr*Y(P9VA_0lmgw5E3t=NX`*nuP@V<&cDH}+sJ_F+E`AO#0;2#0Y5M{x|t zaRMiC3a4=fXK@baaRC=`372sNS8)y3aRWDT3%79xcX1D?c!&4+fHb7zBS6xaiJ6&& z4$MkNW}_3cGY50BAPccDi?Aq*vjnTN25YhwYqJjPvL5TR0bS@yH@dSS8?iB4(Sx1Y zgPO{LZsk#4X)m1&!R|C1oRc>-uLp4%kHBnPFQ**V@A8M&qYOOYEt3TCF z?bSgY)k%NJL!H${UF9h+d8?az)LlK)Q@zw%edMdY@>4(c*8mOFAPv?K4b?CW*9eW2 zzXBAfQ5vl=8mn;{uL+u{Nt&!F3R18_6soD3rs?5A{418MQgU^Xs+gIz7}Yq z7HP4TXsMQIxmIYUVic=5teQRJY=4x){Zf&fsb+k_AX Tenor Multipath Switch FTP server \\(Version VxWorks([\\w._-]+)\\) ready\\.\\r\\n", + "regex_literal_tokens": [ + ") ready.", + "220 <", + "> Tenor Multipath Switch FTP server (Version VxWorks" + ], + "cpe": [ + "cpe:/o:windriver:vxworks:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ftp", + "regex": "^220 -= HyNetOS FTP Server =-\\r\\n500 Command \\(null\\) not understood\\r\\n", + "regex_literal_tokens": [ + "220 -= HyNetOS FTP Server =-500 Command (null) not understood" + ], + "cpe": [ + "cpe:/o:hyperstone:hynetos/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ftp", + "regex": "^220 Welcome to Solar FTP Server \\(http://solarftp\\.com\\)\\r\\n", + "regex_literal_tokens": [ + "220 Welcome to Solar FTP Server (http://solarftp.com)" + ], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ftp", + "regex": "^220 Aos FTP Server ready\\.\\r\\n", + "regex_literal_tokens": [ + "220 Aos FTP Server ready." + ], + "cpe": [ + "cpe:/o:eth:a2/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ftp", + "regex": "^220 FreeFloat Ftp Server \\(Version ([\\w._-]+)\\)\\.\\r\\n", + "regex_literal_tokens": [ + "220 FreeFloat Ftp Server (Version" + ], + "cpe": [ + "cpe:/a:freefloat:freefloat_ftp_server:$1/", + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ftp", + "regex": "^220(?:-.*\\r\\n220)* [\\d.]+ FTP Server \\(Apache/([\\w._-]+) \\(Ubuntu\\) (.*)\\) ready\\.\\r\\n", + "regex_literal_tokens": [ + "(Ubuntu)", + ") ready.", + "220", + "FTP Server (Apache/" + ], + "cpe": [ + "cpe:/a:apache:http_server/", + "cpe:/o:canonical:ubuntu_linux/", + "cpe:/o:canonical:ubuntu_linux/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ftp", + "regex": "^220 Wind River FTP server ([\\w._-]+) ready\\.\\r\\n", + "regex_literal_tokens": [ + "220 Wind River FTP server", + "ready." + ], + "cpe": [ + "cpe:/a:windriver:ftp_server:$1/", + "cpe:/o:windriver:vxworks/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ftp", + "regex": "^220-TiMOS-B-([\\w._-]+) both/hops ALCATEL SR ([\\w._-]+) Copyright \\(c\\) \\d+-\\d+ Alcatel-Lucent\\.\\r\\n220-All rights reserved\\. All use subject to applicable license agreements\\.\\r\\n220-Built on (.*) by builder in /rel[\\w._-]+/[\\w._-]+/[\\w._-]+/panos/main\\r\\n220-\\r\\n220-This is a Maxcom, system restricted to authorized individuals\\. This system is subject to monitoring\\. Unauthorized users, access, and/or modification will be prosecuted\\.\\r\\n220 FTP server ready\\r\\n", + "regex_literal_tokens": [ + "/panos/main220-220-This is a Maxcom, system restricted to authorized individuals. This system is subject to monitoring. Unauthorized users, access, and/or modification will be prosecuted.220 FTP server ready", + "220-TiMOS-B-", + "Alcatel-Lucent.220-All rights reserved. All use subject to applicable license agreements.220-Built on", + "Copyright (c)", + "both/hops ALCATEL SR", + "by builder in /rel" + ], + "cpe": [ + "cpe:/h:alcatel:$2_service_router/", + "cpe:/o:alcatel:timos:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ftp", + "regex": "^220 ([\\w.-]+) FTP server \\(StarOS\\) ready\\.\\r\\n", + "regex_literal_tokens": [ + "220", + "FTP server (StarOS) ready." + ], + "cpe": [ + "cpe:/o:cisco:staros/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ftp", + "regex": "^220- FTP Server \\(RTOS-UH\\) ready\\. \\(c\\)IEP Version: ([\\d.]+)\\r\\n220 Connection is automatically closed if idle for 10 Minutes\\r\\n", + "regex_literal_tokens": [ + "220 Connection is automatically closed if idle for 10 Minutes", + "220- FTP Server (RTOS-UH) ready. (c)IEP Version:" + ], + "cpe": [ + "cpe:/o:universitathanover:rtos-uh/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ftp", + "regex": "^220-Debian GNU/Linux (\\d+)\\r\\n220 ProFTPD ([\\w._-]+) Server ", + "regex_literal_tokens": [ + "220 ProFTPD", + "220-Debian GNU/Linux", + "Server" + ], + "cpe": [ + "cpe:/o:debian:debian_linux:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ftp", + "regex": "^220 MinWin FTP server ready\\.\\r\\n", + "regex_literal_tokens": [ + "220 MinWin FTP server ready." + ], + "cpe": [ + "cpe:/o:microsoft:windows_10:::iot/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ftp", + "regex": "^220 ([\\w._-]+) FTP server \\(U(?:LTRIX|ltrix) Version ([\\d.]+) ([^)]+)\\) ready\\.\\r\\n", + "regex_literal_tokens": [ + ") ready.", + "220", + "FTP server (U", + "LTRIX", + "Version", + "ltrix" + ], + "cpe": [ + "cpe:/o:dec:ultrix:$2/" + ] + }, + { + "probe_id": "tcp:null", + "service": "http", + "regex": "^HTTP/1\\.1 \\d\\d\\d .*\\r\\nServer: micro_httpd\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.1", + "Server: micro_httpd" + ], + "cpe": [ + "cpe:/o:acme:micro_httpd/" + ] + }, + { + "probe_id": "tcp:null", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\nServer: Menuet\\r\\nConnection: close\\r\\nContent-Length: 0\\d+\\r\\nContent-Type: image/bmp\\r\\n\\r\\n", + "regex_literal_tokens": [ + "Content-Type: image/bmp", + "HTTP/1.1 200 OKServer: MenuetConnection: closeContent-Length: 0" + ], + "cpe": [ + "cpe:/o:menuetos:menuetos/" + ] + }, + { + "probe_id": "tcp:null", + "service": "http", + "regex": "^HTTP/1\\.1 500 Internal Server Error\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: LG HDCP Server\\r\\n.*500Internal Server Error$", + "regex_literal_tokens": [ + "500Internal Server Error", + "HTTP/1.1 500 Internal Server Error", + "Server: LG HDCP Server" + ], + "cpe": [ + "cpe:/h:lg:lw5700/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ibm-hmc", + "regex": "^\\xab\\xab\\xab\\xab\\xa0\\x81\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00$", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/a:ibm:hardware_management_console/", + "cpe:/o:ibm:aix/" + ] + }, + { + "probe_id": "tcp:null", + "service": "imap", + "regex": "^\\* OK \\[[^\\[]+\\] Dovecot \\(Ubuntu\\) ready\\.\\r\\n", + "regex_literal_tokens": [ + "* OK [", + "] Dovecot (Ubuntu) ready." + ], + "cpe": [ + "cpe:/a:dovecot:dovecot/", + "cpe:/o:canonical:ubuntu_linux/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:null", + "service": "imap", + "regex": "^\\* OK ([-.\\w]+) Cyrus IMAP4 v([-.\\w\\+]+)-Red Hat [-.\\w\\+]+ server ready\\r\\n", + "regex_literal_tokens": [ + "* OK", + "-Red Hat", + "Cyrus IMAP4 v", + "server ready" + ], + "cpe": [ + "cpe:/a:cmu:cyrus_imap_server:$2/", + "cpe:/o:redhat:linux/" + ] + }, + { + "probe_id": "tcp:null", + "service": "imap", + "regex": "^\\* OK (?:\\[CAPABILITY IMAP4[^\\]]*?\\] )?([-\\w_.]+) Cyrus IMAP4? v([-\\w_.]+)-Debian", + "regex_literal_tokens": [ + "* OK", + "-Debian", + "Cyrus IMAP", + "[CAPABILITY IMAP4" + ], + "cpe": [ + "cpe:/a:cmu:cyrus_imap_server:$2/", + "cpe:/o:canonical:ubuntu_linux/", + "cpe:/o:debian:debian_linux/" + ] + }, + { + "probe_id": "tcp:null", + "service": "imap", + "regex": "^\\* OK \\[CAPABILITY IMAP4rev1 [^]]*\\] ([-.\\w]+) Cyrus IMAP [^ -]*-Debian-(\\d[\\w.]+)[\\w+-]* server ready\\r\\n", + "regex_literal_tokens": [ + "* OK [CAPABILITY IMAP4rev1", + "-Debian-", + "Cyrus IMAP", + "server ready" + ], + "cpe": [ + "cpe:/a:cmu:cyrus_imap_server:$2/", + "cpe:/o:debian:debian_linux/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:null", + "service": "imap", + "regex": "^\\* OK ([\\w._-]+) IMAP2bis Service ([\\w._()-]+) at .* ([-+]\\d+)", + "regex_literal_tokens": [ + "* OK", + "IMAP2bis Service" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel/", + "cpe:/o:slackware:slackware_linux:3.5/" + ] + }, + { + "probe_id": "tcp:null", + "service": "imap-proxy", + "regex": "^\\* BYE concurrent connection limit in avast! exceeded\\(pass:\\d+, processes:([\\w._-]+)\\[\\d+\\]\\)\\r\\n", + "regex_literal_tokens": [ + "* BYE concurrent connection limit in avast! exceeded(pass:", + ", processes:" + ], + "cpe": [ + "cpe:/a:avast:antivirus/", + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:null", + "service": "imap-proxy", + "regex": "^ BYE concurrent connection limit in AVG exceeded\\(pass:\\d+, processes:([\\w._-]+)\\[\\d+\\]\\)\\r\\n", + "regex_literal_tokens": [ + ", processes:", + "BYE concurrent connection limit in AVG exceeded(pass:" + ], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:null", + "service": "imap-proxy", + "regex": "^\\* BYE Cannot connect to IMAP server ([\\w._-]+) \\([^)]*\\), connect error \\d+\\r\\n", + "regex_literal_tokens": [ + "), connect error", + "* BYE Cannot connect to IMAP server" + ], + "cpe": [ + "cpe:/a:avast:antivirus/", + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ipmi-advertiserd", + "regex": "^\\x0e\\x00\\x00\\x00\\x00\\x00\\x00$", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:supermicro:intelligent_platform_management_firmware/" + ] + }, + { + "probe_id": "tcp:null", + "service": "lmtp", + "regex": "^220 ([\\w.-]+) Dovecot \\(Ubuntu\\) ready\\.\\r\\n", + "regex_literal_tokens": [ + "220", + "Dovecot (Ubuntu) ready." + ], + "cpe": [ + "cpe:/a:dovecot:dovecot/", + "cpe:/o:canonical:ubuntu_linux/" + ] + }, + { + "probe_id": "tcp:null", + "service": "nntp", + "regex": "^200 NNTP Service ([\\w._-]+) Version: [\\w._-]+ Posting Allowed \\r\\n", + "regex_literal_tokens": [ + "200 NNTP Service", + "Posting Allowed", + "Version:" + ], + "cpe": [ + "cpe:/o:microsoft:windows_2000/" + ] + }, + { + "probe_id": "tcp:null", + "service": "nntp", + "regex": "^200 NNTP-service ([\\w._-]+) Version: [\\w._-]+ Posting Allowed \\r\\n", + "regex_literal_tokens": [ + "200 NNTP-service", + "Posting Allowed", + "Version:" + ], + "cpe": [ + "cpe:/o:microsoft:windows_2000/" + ] + }, + { + "probe_id": "tcp:null", + "service": "nntp", + "regex": "^200 Service NNTP ([\\w._-]+) Version: [\\w._-]+ Posting Allowed \\r\\n", + "regex_literal_tokens": [ + "200 Service NNTP", + "Posting Allowed", + "Version:" + ], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:null", + "service": "nntp", + "regex": "^200 Servicio NNTP ([\\w._-]+) Version: [\\w._-]+ Posting Allowed \\r\\n", + "regex_literal_tokens": [ + "200 Servicio NNTP", + "Posting Allowed", + "Version:" + ], + "cpe": [ + "cpe:/o:microsoft:windows::::es/" + ] + }, + { + "probe_id": "tcp:null", + "service": "nntp", + "regex": "^200 Servi\\xe7o NNTP ([\\w._-]+) Version: [\\w._-]+ Posting Allowed \\r\\n", + "regex_literal_tokens": [ + "200 Servio NNTP", + "Posting Allowed", + "Version:" + ], + "cpe": [ + "cpe:/o:microsoft:windows::::pt/" + ] + }, + { + "probe_id": "tcp:null", + "service": "nntp", + "regex": "^200 NNTP Service Microsoft\\xae Internet Services (\\d[-.\\w]+) Version: \\d+\\.\\d+\\.\\d+\\.\\d+ Posting Allowed \\r\\n", + "regex_literal_tokens": [ + "200 NNTP Service Microsoft Internet Services", + "Posting Allowed", + "Version:" + ], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:null", + "service": "nntp", + "regex": "^200 ([-\\w_.]+) InterNetNews NNRP server INN ([\\d.]+) .* \\(Debian\\) ready \\(posting ok\\)\\.\\r\\n", + "regex_literal_tokens": [ + "(Debian) ready (posting ok).", + "200", + "InterNetNews NNRP server INN" + ], + "cpe": [ + "cpe:/a:isc:inn:$2/", + "cpe:/o:debian:debian_linux/" + ] + }, + { + "probe_id": "tcp:null", + "service": "nntp-proxy", + "regex": "^5?02 concurrent connection limit in avast! exceeded\\(pass:\\d+, processes:([\\w._-]+)\\[\\d+\\]\\)\\r\\n", + "regex_literal_tokens": [ + ", processes:", + "02 concurrent connection limit in avast! exceeded(pass:" + ], + "cpe": [ + "cpe:/a:avast:antivirus/", + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:null", + "service": "pop3", + "regex": "^\\+OK ([-\\w_.]+) Cyrus POP3 v(\\S+?)[-_]?Debian\\S+ server ready", + "regex_literal_tokens": [ + "+OK", + "Cyrus POP3 v", + "Debian", + "server ready" + ], + "cpe": [ + "cpe:/a:cmu:cyrus_imap_server:$2/", + "cpe:/o:debian:debian_linux/" + ] + }, + { + "probe_id": "tcp:null", + "service": "pop3", + "regex": "^\\+OK Microsoft Windows POP3 Service Version 1.0 <", + "regex_literal_tokens": [ + "+OK Microsoft Windows POP3 Service Version 1", + "0 <" + ], + "cpe": [ + "cpe:/o:microsoft:windows_2000/" + ] + }, + { + "probe_id": "tcp:null", + "service": "pop3", + "regex": "^\\+OK Server Ready\\r\\n", + "regex_literal_tokens": [ + "+OK Server Ready" + ], + "cpe": [ + "cpe:/o:cisco:vpn_3000_concentrator_series_software/" + ] + }, + { + "probe_id": "tcp:null", + "service": "pop3-proxy", + "regex": "^-ERR Cannot connect to POP server ([\\w._-]+) \\([^)]*\\), connect error \\d+\\r\\n", + "regex_literal_tokens": [ + "), connect error", + "-ERR Cannot connect to POP server" + ], + "cpe": [ + "cpe:/a:avast:antivirus/", + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:null", + "service": "remote-rac", + "regex": "^\\x10\\x00\\x00\\x00\\t\\xe7\\xa0o\\xde&\\xdc\\xfec\\xbf\\xb91\\xef\\xc3\\?\\xc9\\x10\\x00\\x00\\x00\\xa1\\xcasZ6\\[\\xdf\\x0cc\\xbf\\xb91\\xef\\xc3\\?\\xc9\\x08\\x00\\x19\\xdbh\\x06\\xa1\\xfc\\x91\\xce$", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:null", + "service": "remote-rac", + "regex": "^\\x02\\x00\\x00\\x00\\xfe\\x00\\x00\\x00\\x00\\x01\\x00\\x00.{256}$", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:null", + "service": "rethinkdb-intracluster", + "regex": "^RethinkDB ([\\w._~-]+ubuntu[\\w._~-]+) cluster\\n\\xab\\xa6\\x04\\^\\x11!M\\xd6\\x99\\xb6\\xb5\\xbe\\x1cxR\\xdd\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x7f\\x00\\x00\\x01\\x7f\\x00\\x01\\x01Wq\\x00\\x00$", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:canonical:ubuntu_linux/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:null", + "service": "sieve", + "regex": "^\\\"IMPLEMENTATION\\\" \\\"Cyrus timsieved v([\\w._-]+-Red Hat[- ][\\w._+-]+)\\\"\\r\\n", + "regex_literal_tokens": [ + "\"IMPLEMENTATION\" \"Cyrus timsieved v", + "-Red Hat" + ], + "cpe": [ + "cpe:/a:cmu:cyrus_imap_server:$1/", + "cpe:/o:redhat:linux/" + ] + }, + { + "probe_id": "tcp:null", + "service": "sieve", + "regex": "^\\\"IMPLEMENTATION\\\" \\\"Cyrus timsieved v([\\w._-]+-Debian[- ][\\w._+-]+)\\\"\\r\\n", + "regex_literal_tokens": [ + "\"IMPLEMENTATION\" \"Cyrus timsieved v", + "-Debian" + ], + "cpe": [ + "cpe:/a:cmu:cyrus_imap_server:$1/", + "cpe:/o:debian:debian_linux/" + ] + }, + { + "probe_id": "tcp:null", + "service": "sieve", + "regex": "^\\\"IMPLEMENTATION\\\" \\\"Cyrus timsieved v([\\w_.]+)-OS X ([^\"]+)\\\"\\r\\n", + "regex_literal_tokens": [ + "\"IMPLEMENTATION\" \"Cyrus timsieved v", + "-OS X" + ], + "cpe": [ + "cpe:/a:cmu:cyrus_imap_server:$1/", + "cpe:/o:apple:mac_os_x:$2/" + ] + }, + { + "probe_id": "tcp:null", + "service": "sieve", + "regex": "^\\\"IMPLEMENTATION\\\" \\\"Dovecot \\(Ubuntu\\) Pigeonhole\\\"\\r\\n\\\"SIEVE\\\" \\\"[\\w._;-]+(?:\\s+[\\w._;-]+)*\\\"\\r\\n\\\"NOTIFY\\\" \\\"mailto\\\"\\r\\n\\\"SASL\\\" \\\"[\\w._;-]*(?:\\s+[\\w._;-]+)*\\\"\\r\\n\\\"STARTTLS\\\"\\r\\n\\\"VERSION\\\" \\\"([\\w._-]+)\\\"\\r\\nOK \\\"[^\"]*\\\"\\r\\n$", + "regex_literal_tokens": [ + "\"\"NOTIFY\" \"mailto\"\"SASL\" \"", + "\"\"STARTTLS\"\"VERSION\" \"", + "\"IMPLEMENTATION\" \"Dovecot (Ubuntu) Pigeonhole\"\"SIEVE\" \"", + "\"OK \"" + ], + "cpe": [ + "cpe:/o:canonical:ubuntu_linux/" + ] + }, + { + "probe_id": "tcp:null", + "service": "smtp", + "regex": "^220 [\\*\\d\\ ]{2,300}\\r\\n", + "regex_literal_tokens": [ + "220" + ], + "cpe": [ + "cpe:/o:cisco:pix_firewall_software/" + ] + }, + { + "probe_id": "tcp:null", + "service": "smtp", + "regex": "^220 ([-\\w_.]+) running IBM MVS SMTP CS V2R10 on .*\\r\\n", + "regex_literal_tokens": [ + "220", + "running IBM MVS SMTP CS V2R10 on" + ], + "cpe": [ + "cpe:/o:ibm:mvs/" + ] + }, + { + "probe_id": "tcp:null", + "service": "smtp", + "regex": "^220 [-\\w_]+ ESMTP ([-\\w_.]+) \\(Debian/GNU\\)\\r\\n", + "regex_literal_tokens": [ + "(Debian/GNU)", + "220", + "ESMTP" + ], + "cpe": [ + "cpe:/o:debian:debian_linux/" + ] + }, + { + "probe_id": "tcp:null", + "service": "smtp", + "regex": "^220 ESMTP \\(Debian/GNU Mewwwwwww\\)\\r\\n", + "regex_literal_tokens": [ + "220 ESMTP (Debian/GNU Mewwwwwww)" + ], + "cpe": [ + "cpe:/o:debian:debian_linux/" + ] + }, + { + "probe_id": "tcp:null", + "service": "smtp", + "regex": "^220 ([\\w._-]+) [\\w._-]+ ESMTP Postfix \\(Debian/GNU\\)", + "regex_literal_tokens": [ + "220", + "ESMTP Postfix (Debian/GNU)" + ], + "cpe": [ + "cpe:/o:debian:debian_linux/" + ] + }, + { + "probe_id": "tcp:null", + "service": "smtp", + "regex": "^220 ([-\\w_.]+) running IBM MVS SMTP CS (\\w+) on .*\\r\\n", + "regex_literal_tokens": [ + "220", + "running IBM MVS SMTP CS" + ], + "cpe": [ + "cpe:/o:ibm:mvs/" + ] + }, + { + "probe_id": "tcp:null", + "service": "smtp", + "regex": "^220-\\S+ Sendmail ([\\d.]+)/A/UX ([\\d.]+) ready at .*\\r\\n220 ESMTP spoken here\\r\\n", + "regex_literal_tokens": [ + "/A/UX", + "220 ESMTP spoken here", + "220-", + "Sendmail", + "ready at" + ], + "cpe": [ + "cpe:/a:sendmail:sendmail:$1/", + "cpe:/o:apple:a_ux:$2/" + ] + }, + { + "probe_id": "tcp:null", + "service": "smtp", + "regex": "^220-([-\\w_.]+) Sendmail IBM OS/2 SENDMAIL VERSION ([\\w./]+) ready at .*\\r\\n220 ESMTP spoken here\\r\\n", + "regex_literal_tokens": [ + "220 ESMTP spoken here", + "220-", + "Sendmail IBM OS/2 SENDMAIL VERSION", + "ready at" + ], + "cpe": [ + "cpe:/a:sendmail:sendmail:$2/", + "cpe:/o:ibm:os2/" + ] + }, + { + "probe_id": "tcp:null", + "service": "smtp-proxy", + "regex": "^421 concurrent connection limit in avast! exceeded\\(pass:0, processes:([\\w._-]+)\\[\\d+\\]\\)\\r\\n", + "regex_literal_tokens": [ + "421 concurrent connection limit in avast! exceeded(pass:0, processes:" + ], + "cpe": [ + "cpe:/a:avast:antivirus/", + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:null", + "service": "smtp-proxy", + "regex": "^421 Cannot connect to SMTP server ([\\w._-]+) \\([^)]*\\), connect error \\d+\\r\\n", + "regex_literal_tokens": [ + "), connect error", + "421 Cannot connect to SMTP server" + ], + "cpe": [ + "cpe:/a:avast:antivirus/", + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ssh", + "regex": "^SSH-([\\d.]+)-OpenSSH_([\\w._-]+) Debian-(\\S*maemo\\S*)\\r?\\n", + "regex_literal_tokens": [ + "-OpenSSH_", + "Debian-", + "SSH-", + "maemo" + ], + "cpe": [ + "cpe:/a:openbsd:openssh:$2/", + "cpe:/o:debian:debian_linux/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ssh", + "regex": "^SSH-([\\d.]+)-OpenSSH_([\\w._-]+)[ -]{1,2}Debian[ -_](.*ubuntu.*)\\r\\n", + "regex_literal_tokens": [ + "-OpenSSH_", + "Debian", + "SSH-", + "ubuntu" + ], + "cpe": [ + "cpe:/a:openbsd:openssh:$2/", + "cpe:/o:canonical:ubuntu_linux/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ssh", + "regex": "^SSH-([\\d.]+)-OpenSSH_([\\w._-]+)[ -]{1,2}Ubuntu[ -_]([^\\r\\n]+)\\r?\\n", + "regex_literal_tokens": [ + "-OpenSSH_", + "SSH-", + "Ubuntu" + ], + "cpe": [ + "cpe:/a:openbsd:openssh:$2/", + "cpe:/o:canonical:ubuntu_linux/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ssh", + "regex": "^SSH-([\\d.]+)-OpenSSH_([\\w._-]+)[ -]{1,2}Debian[ -_]([^\\r\\n]+)\\r?\\n", + "regex_literal_tokens": [ + "-OpenSSH_", + "Debian", + "SSH-" + ], + "cpe": [ + "cpe:/o:debian:debian_linux/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ssh", + "regex": "^SSH-([\\d.]+)-OpenSSH_[\\w.]+-FC-([\\w.-]+)\\.fc(\\d+)\\r\\n", + "regex_literal_tokens": [ + "-FC-", + "-OpenSSH_", + ".fc", + "SSH-" + ], + "cpe": [ + "cpe:/a:openbsd:openssh:$2/", + "cpe:/o:fedoraproject:fedora_core:$3/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ssh", + "regex": "^SSH-([\\d.]+)-OpenSSH_([\\w._-]+) NetBSD_Secure_Shell-([\\w._+-]+)\\r?\\n", + "regex_literal_tokens": [ + "-OpenSSH_", + "NetBSD_Secure_Shell-", + "SSH-" + ], + "cpe": [ + "cpe:/a:openbsd:openssh:$2/", + "cpe:/o:netbsd:netbsd/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ssh", + "regex": "^SSH-([\\d.]+)-OpenSSH_([\\w._-]+) Trisquel_GNU/linux_([\\d.]+)(?:-\\d+)?\\r\\n", + "regex_literal_tokens": [ + "-OpenSSH_", + "SSH-", + "Trisquel_GNU/linux_" + ], + "cpe": [ + "cpe:/o:trisquel_project:trisquel_gnu%2flinux:$3/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ssh", + "regex": "^SSH-([\\d.]+)-OpenSSH_([\\w._-]+) SolidFire Element \\r\\n", + "regex_literal_tokens": [ + "-OpenSSH_", + "SSH-", + "SolidFire Element" + ], + "cpe": [ + "cpe:/o:netapp:element_software/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ssh", + "regex": "^SSH-([\\d.]+)-ROSSSH\\r\\n", + "regex_literal_tokens": [ + "-ROSSSH", + "SSH-" + ], + "cpe": [ + "cpe:/o:mikrotik:routeros/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ssh", + "regex": "^SSH-([\\d.]+)-DOPRA-([\\w._-]+)\\n", + "regex_literal_tokens": [ + "-DOPRA-", + "SSH-" + ], + "cpe": [ + "cpe:/o:huawei:dopra_linux/" + ] + }, + { + "probe_id": "tcp:null", + "service": "ssh", + "regex": "^SSH-([\\d.]+)-SC123/SC143 CHIP-RTOS V([\\d.]+)\\r\\n", + "regex_literal_tokens": [ + "-SC123/SC143 CHIP-RTOS V", + "SSH-" + ], + "cpe": [ + "cpe:/o:beck-ipc:chip-rtos:$2/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfd\\x18\\xff\\xfb\\x01\\x1b\\[2J\\x1b\\[\\?7l\\x1b.*HP [-.\\w]+ ProCurve Switch ([-.\\w]+)\\r\\n\\rFirmware revision ([-.\\w]+)\\r\\n\\r\\r", + "regex_literal_tokens": [ + "Firmware revision", + "ProCurve Switch", + "[2J[?7l" + ], + "cpe": [ + "cpe:/h:hp:procurve_switch_$1/", + "cpe:/o:hp:procurve_switch_software:$2/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\x1b\\[20;1H\\r\\n\\r\\x1b\\[\\?25h\\x1b\\[20;11H\\x1b\\[21;1HSession Terminated, Connect again\\r\\n\\r\\x1b\\[\\?25h\\x1b\\[21;1H\\xff\\xfd\\x18\\xff\\xfb\\x01\\x1b\\[2J\\x1b\\[\\?7l\\x1b\\[[34];23r\\x1b\\[\\?6l\\x1b\\[1;1H\\x1b\\[\\?25l\\x1b\\[1;1HHP [-.\\w]+ ProCurve Switch ([-.\\w]+)\\r\\n\\rFirmware revision ([-.\\w]+)\\r\\n\\r\\r", + "regex_literal_tokens": [ + ";23r[?6l[1;1H[?25l[1;1HHP", + "Firmware revision", + "ProCurve Switch", + "[20;1H[?25h[20;11H[21;1HSession Terminated, Connect again[?25h[21;1H[2J[?7l[" + ], + "cpe": [ + "cpe:/h:hp:procurve_switch_$1/", + "cpe:/o:hp:procurve_switch_software:$2/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfd\\x18\\xff\\xfb\\x01\\x1b\\[2J\\x1b\\[\\?7l\\x1b.*ProCurve [\\w._-]+ Switch ([\\w._-]+)\\r\\r\\nSoftware revision ([\\w._-]+)\\r\\r\\n", + "regex_literal_tokens": [ + "ProCurve", + "Software revision", + "Switch", + "[2J[?7l" + ], + "cpe": [ + "cpe:/h:hp:procurve_switch_$1/", + "cpe:/o:hp:procurve_switch_software:$2/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfd%\\xff\\xfb\\x01\\xff\\xfb\\x03\\xff\\xfd'\\xff\\xfd\\x1f\\xff\\xfd\\x00\\xff\\xfb\\x00", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:microsoft:windows_xp/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\r\\nNo more connections are allowed to telnet server\\. Please try again later\\.\\x00", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:microsoft:windows_xp/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x01\\xff\\xfb\\x03\\xff\\xfd\\x03\\xff\\xfd\\x1f\\xff\\xfd\\x18Windows NT Workstation ([\\d.]+) \\(build \\d+\\) Service Pack (\\d+)\\r\\nRemotelyAnywhere Telnet Server ([\\d.]+)\\r\\n", + "regex_literal_tokens": [ + "(build", + ") Service Pack", + "RemotelyAnywhere Telnet Server", + "Windows NT Workstation" + ], + "cpe": [ + "cpe:/o:microsoft:windows_nt:$1:sp$2/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\xfd\\xff\\xfb\\x01\\n\\r\\n\\rFabric OS \\(tm\\) Release v([\\w.]+)\\n\\r\\n\\r", + "regex_literal_tokens": [ + "Fabric OS (tm) Release v" + ], + "cpe": [ + "cpe:/o:brocade:fabric_os:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\r\\nWelcome to slush\\. \\(Version ([\\d.]+)\\)\\r\\n\\r\\n\\r\\n\\xff\\xfb\\x01\\xff\\xfb\\x03([-\\w_. ]+) login: ", + "regex_literal_tokens": [ + "Welcome to slush. (Version", + "login:" + ], + "cpe": [ + "cpe:/o:systronix:tinios/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x01\\r\\nWelcome to NetLinx v([\\d.]+) Copyright AMX ", + "regex_literal_tokens": [ + "Copyright AMX", + "Welcome to NetLinx v" + ], + "cpe": [ + "cpe:/o:harman:amx_firmware:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x01\\r\\nWelcome to NetLinx v([\\d.]+) , AMX LLC\\r\\n>", + "regex_literal_tokens": [ + ", AMX LLC>", + "Welcome to NetLinx v" + ], + "cpe": [ + "cpe:/o:harman:amx_firmware:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfd\\x01\\xff\\xfd\\x1f\\xff\\xfb\\x01\\xff\\xfb\\x03Fritz!Box user: ", + "regex_literal_tokens": [ + "Fritz!Box user:" + ], + "cpe": [ + "cpe:/o:avm:fritzos/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfd\\x01\\xff\\xfd!\\xff\\xfb\\x01\\xff\\xfb\\x03\\(none\\) login: ", + "regex_literal_tokens": [ + "!(none) login:" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x03\\xff\\xfb\\x01\\xff\\xfb\\x03\\xff\\xfb\\x01\\r\\n\\r\\nPIX passwd: ", + "regex_literal_tokens": [ + "PIX passwd:" + ], + "cpe": [ + "cpe:/o:cisco:pix_firewall_software/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x03\\xff\\xfd\\x03\\xff\\xfb\\x01\\r\\nPrecise/RTCS v([\\d.]+) Telnet server\\r\\n\\r\\x00\\r\\nService Port Manager Active\\r\\x00\\r\\n Ends Session\\r\\x00\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:precise:mqx:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x03\\xff\\xfb\\x01\\xff\\xfe\\x01\\xff\\xfd\\x00\\r\\nser2net port \\d+ device (/dev/[-\\w_]+) \\[\\d+ \\w+\\] \\(Debian GNU/Linux\\)\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:debian:debian_linux/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x01\\xff\\xfb\\x03\\xff\\xfe\\x01\\n\\rTerminal shell v1\\.0\\n\\r\\rCopyright \\xa9\\d+ Netopia, Inc\\. All rights reserved\\.\\n\\r\\rNetopia Model ([\\w-]+) Wireless DSL Ethernet Switch\\n\\rRunning Netopia SOC OS version ([\\d.]+ \\(build \\w+\\))\\n", + "regex_literal_tokens": [ + "(build", + "Netopia, Inc. All rights reserved.Netopia Model", + "Terminal shell v1.0Copyright", + "Wireless DSL Ethernet SwitchRunning Netopia SOC OS version" + ], + "cpe": [ + "cpe:/o:netopia:soc_os:$2/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x01\\xff\\xfb\\x03\\xff\\xfe\\x01\\n\\rTerminal shell v1\\.0\\n\\r\\rCopyright \\xa92008 Motorola, Inc\\. All rights reserved\\.\\n\\r\\rNetopia Model ([\\d-]+)(?: AnnexA)? High-Power Wireless DSL Ethernet Managed Switch\\n\\rRunning Netopia SOC OS version ([\\w.-]+ \\(build \\w+\\))\\n", + "regex_literal_tokens": [ + "(build", + "AnnexA", + "High-Power Wireless DSL Ethernet Managed SwitchRunning Netopia SOC OS version", + "Terminal shell v1.0Copyright 2008 Motorola, Inc. All rights reserved.Netopia Model" + ], + "cpe": [ + "cpe:/o:netopia:soc_os:$2/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^(?:\\x1b\\[23;1H\\r\\n\\r\\x1b\\[\\?25h\\x1b\\[23;11H\\x1b\\[24;1HSession Terminated, Connect again\\r\\n\\r\\x1b\\[\\?25h\\x1b\\[24;1H)?\\xff\\xfd\\x18\\xff\\xfb\\x01\\x1b\\[2J\\x1b\\[\\?7l\\x1b\\[3;23r\\x1b\\[\\?6l\\x1b\\[1;1H\\x1b\\[\\?25l\\x1b\\[1;1HProCurve (J\\w+) Switch (\\d+)\\r\\n\\rFirmware revision ([^\\r\\n]+)\\r\\n", + "regex_literal_tokens": [ + "Firmware revision", + "Switch", + "[23;1H[?25h[23;11H[24;1HSession Terminated, Connect again[?25h[24;1H", + "[2J[?7l[3;23r[?6l[1;1H[?25l[1;1HProCurve J" + ], + "cpe": [ + "cpe:/h:hp:procurve_switch_$2/", + "cpe:/o:hp:procurve_switch_software:$3/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x01\\xff\\xfd\\x18\\xff\\xfb\\x03$", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:tandem:guardian/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfd\\x01\\xff\\xfd\\x1f\\xff\\xfd!\\xff\\xfb\\x01\\xff\\xfb\\x03\\r\\r\\n\\r\\n\\r.*\\xaf\\xaf\\xaf\\xaf\\xaf\\r\\n\\r Kernel ([\\w._-]+) \\(00:17:54\\)\\r\\n\\rdreambox login: ", + "regex_literal_tokens": [ + "(00:17:54)dreambox login:", + "Kernel" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfd\\x01\\xff\\xfd\\x1f\\xff\\xfd!\\xff\\xfb\\x01\\xff\\xfb\\x03\\r\\r\\n\\r\\n\\r\\r\\n\\rWelcome to DreamBox\\.\\r\\n\\rRunning under Kernel ([\\w._-]+) \\.\\r\\n\\rBased on (Gemini [\\w._-]+ GUI)\\.\\r\\n\\rKernel and utilities compiled by SatDream\\.\\r\\n\\r\\r\\n\\r\\r\\n\\rhttp://www\\.satderam\\.ru , info@satdream\\.ru , dreambox@satdream\\.ru\\r\\n", + "regex_literal_tokens": [ + "!Welcome to DreamBox.Running under Kernel", + ".Based on Gemini", + "GUI.Kernel and utilities compiled by SatDream.http://www.satderam.ru , info@satdream.ru , dreambox@satdream.ru" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfc\\x01\\xff\\xfb\\x01\\xff\\xfb\\x03\\xff\\xfe\\x18\\xff\\xfd\\x1f\\xff\\xfb\\x1f\\xff\\xfb\\\"\\xff\\xfb\\x05TiMOS-([\\w._-]+) cpm/hops ALCATEL SR (\\w+)", + "regex_literal_tokens": [ + "\"TiMOS-", + "cpm/hops ALCATEL SR" + ], + "cpe": [ + "cpe:/o:alcatel-lucent:timos:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x01\\xff\\xfb\\x01\\xff\\xfb\\x01\\xff\\xfb\\x03\\xff\\xfd\\x18\\xff\\xfd\\x1f\\r\\n\\*{78}\\r\\n\\* Copyright \\(c\\) 2010-2\\d\\d\\d Hewlett-Packard Development Company, L\\.P\\. {10}\\*\\r\\n\\* Without the owner's prior written consent, {33}\\*\\r\\n\\* no decompiling or reverse-engineering shall be allowed\\. {20}\\*\\r\\n\\*{78}\\r\\n\\r\\n\\r\\nLogin authentication\\r\\n\\r\\n\\r\\nUsername:", + "regex_literal_tokens": [ + "* Copyright (c) 2010-2", + "** Without the owner's prior written consent,", + "** no decompiling or reverse-engineering shall be allowed.", + "Hewlett-Packard Development Company, L.P.", + "Login authenticationUsername:" + ], + "cpe": [ + "cpe:/o:hp:comware/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfc\\x01\\xff\\xfb\\x01\\xff\\xfb\\x03\\xff\\xfd\\x18\\xff\\xfb\\x18\\xff\\xfd\\x1f\\xff\\xfb\\x1f\\xff\\xfb\\\"\\xff\\xfb\\x05Username:", + "regex_literal_tokens": [ + "\"Username:" + ], + "cpe": [ + "cpe:/o:oneaccess:oneos/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfd\\x18\\xff\\xfb\\x01\\x1b\\[2J\\x1b\\[\\?7l\\x1b\\[3;23r\\x1b\\[\\?6l\\x1b\\[1;1H\\x1b\\[\\?25l\\x1b\\[1;1HProCurve J\\w+ Switch ([\\w-]+)\\r\\n\\rSoftware revision ([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "Software revision", + "Switch", + "[2J[?7l[3;23r[?6l[1;1H[?25l[1;1HProCurve J" + ], + "cpe": [ + "cpe:/h:hp:procurve_switch_$1/", + "cpe:/o:hp:procurve_switch_software:$2/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfd\\x01\\xff\\xfd\\x1f\\xff\\xfd!\\xff\\xfb\\x01\\xff\\xfb\\x03\\r\\r\\nWelcome to Linux \\(([\\w._-]+)\\) for MIPS\\r\\n\\rKernel ([\\w._-]+) Treckle on an MIPS\\r\\n\\r[\\w._-]+ login: ", + "regex_literal_tokens": [ + "!Welcome to Linux (", + ") for MIPSKernel", + "Treckle on an MIPS", + "login:" + ], + "cpe": [ + "cpe:/h:zksoftware:$1/", + "cpe:/o:linux:linux_kernel:$2/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^Eltin\\r\\n Ethernut Nut/OS witamy\\.\\r\\nkey=[0-9A-F]+\\r\\n$", + "regex_literal_tokens": [ + "Eltin Ethernut Nut/OS witamy.key=" + ], + "cpe": [ + "cpe:/o:ethernut:nut_os::::pl/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x01\\xff\\xfb\\x03\\xff\\xfd\\x03\\xff\\xfd\\x1fWELCOME\\r\\n NO UNAUTHORIZED LOGIN\\r\\n Private property\\r\\nlogin: ", + "regex_literal_tokens": [ + "WELCOME NO UNAUTHORIZED LOGIN Private propertylogin:" + ], + "cpe": [ + "cpe:/h:patton:sn4638/", + "cpe:/o:patton:smartware/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x01\\xff\\xfb\\x03XMR-2: Console access 2047\\r\\n\\r\\nUsername: ", + "regex_literal_tokens": [ + "XMR-2: Console access 2047Username:" + ], + "cpe": [ + "cpe:/o:brocade:ironware/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x01\\xff\\xfd\\x01\\xff\\xfb\\x03\\xff\\xfd\\x03\\xff\\xfb\\x05\\xff\\xfd\\x05Welcome to InterNiche Telnet Server ([\\w._-]+)\\r\\n\\r\\n\\r\\nlogin: ", + "regex_literal_tokens": [ + "Welcome to InterNiche Telnet Server", + "login:" + ], + "cpe": [ + "cpe:/o:micrium:uc%2fos-iii/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfd\\x01\\xff\\xfd\\x1f\\xff\\xfb\\x01\\xff\\xfb\\x03\\r\\r\\nWelcome to Vyatta\\r\\n\\rvyatta login: ", + "regex_literal_tokens": [ + "Welcome to Vyattavyatta login:" + ], + "cpe": [ + "cpe:/a:brocade:vyatta_vrouter_software/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfd\\x03\\xff\\xfb\\x03\\xff\\xfd\\x01\\xff\\xfb\\x01\\r\\n\\r\\x1b\\[2J\\x1b\\[0;0H\\x1b\\[K\\x1b\\[1;0H\\x1b\\[K\\x1b\\[2;0H\\x1b\\[K\\x1b\\[3;0H\\x1b\\[K\\x1b\\[4;0H\\x1b\\[K\\x1b\\[5;0H\\x1b\\[K\\x1b\\[6;0H\\x1b\\[K\\x1b\\[7;0H\\x1b\\[K\\x1b\\[8;0H\\x1b\\[K\\x1b\\[9;0H\\x1b\\[K\\x1b\\[10;0H\\x1b\\[K\\x1b\\[11;0H\\x1b\\[K\\x1b\\[12;0H\\x1b\\[K\\x1b\\[13;0H\\x1b\\[K\\x1b\\[14;0H\\x1b\\[K\\x1b\\[15;0H\\x1b\\[K\\x1b\\[16;0H\\x1b\\[K\\x1b\\[17;0H\\x1b\\[K\\x1b\\[18;0H\\x1b\\[K\\x1b\\[19;0H\\x1b\\[K\\x1b\\[20;0H\\x1b\\[K\\x1b\\[21;0H\\x1b\\[K\\x1b\\[22;0H\\x1b\\[K\\x1b\\[23;0HArrowKey/TAB/BACK=Move SPACE=Toggle ENTER=Select ESC=Back", + "regex_literal_tokens": [ + "[2J[0;0H[K[1;0H[K[2;0H[K[3;0H[K[4;0H[K[5;0H[K[6;0H[K[7;0H[K[8;0H[K[9;0H[K[10;0H[K[11;0H[K[12;0H[K[13;0H[K[14;0H[K[15;0H[K[16;0H[K[17;0H[K[18;0H[K[19;0H[K[20;0H[K[21;0H[K[22;0H[K[23;0HArrowKey/TAB/BACK=Move SPACE=Toggle ENTER=Select ESC=Back" + ], + "cpe": [ + "cpe:/o:linksys:srw2024/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfd\\x01\\xff\\xfd\\x1f\\xff\\xfd!\\xff\\xfb\\x01\\xff\\xfb\\x03\\r\\r\\nIngenic linux machine\\r\\n\\rKernel ([\\w._-]+) on an mips\\r\\n\\r\\(none\\) login: ", + "regex_literal_tokens": [ + "!Ingenic linux machineKernel", + "on an mips(none) login:" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfd\\x18\\xff\\xfd \\xff\\xfd#\\xff\\xfd'\\xff\\xfd\\$\\xff\\xfd!", + "regex_literal_tokens": [ + "#'$!" + ], + "cpe": [ + "cpe:/h:cisco:asr_9010/", + "cpe:/o:cisco:ios_xr:3/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x03\\xff\\xfb\\x01HGFMA-B> GET / HTTP/1\\.0\\r\\nGET: Command not found\\.\\r\\nHGFMA-B> \\r\\nHGFMA-B> ", + "regex_literal_tokens": [ + "HGFMA-B> GET / HTTP/1.0GET: Command not found.HGFMA-B> HGFMA-B>" + ], + "cpe": [ + "cpe:/o:hay_systems:hsl_2.75g_femtocell/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x03\\xff\\xfb\\x01\\xff\\xfb\\x00\\xff\\xfd\\x00Auto-sensing\\.\\.\\.\\r\\n \\x1b\\[6n\\x08\\x08\\x08\\x08\\r \\x1b\\[!\\x08\\x08\\x08\\r\\x01\\x01\\x01\\x01\\x01\\x01\\x01\\x01\\x01\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\r\\n\\r\\n WELCOME!\\r\\n\\r\\nLegion \\(#(\\d+)\\)\\r\\nRunning Worldgroup by GALACTICOMM\\r\\nONLINE \\d+ BAUD AT \\d+:\\d\\d \\d+-\\w+-\\d\\d\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:microsoft:windows_nt/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x03\\xff\\xfd\\x03\\xff\\xfb\\x01\\r\\nRTCS v([\\w._-]+) Telnet server\\r\\npress Ctrl-L to enable/disable debug output\\r\\x00\\r\\n\\r\\x00\\r\\nService Port Manager Active\\r\\x00\\r\\n Ends Session\\r\\x00\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/h:emersonnetworkpower:liebert_nxc/", + "cpe:/o:precise:mqx:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x01\\xff\\xfb\\x03\\r\\n\\r\\n Welcome to OpenVMS \\(TM\\) VAX Operating System, Version V([\\d.]+) \\r\\n\\r\\n\\rUsername: ", + "regex_literal_tokens": [ + "Username:", + "Welcome to OpenVMS (TM) VAX Operating System, Version V" + ], + "cpe": [ + "cpe:/o:hp:openvms:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfd\\x01\\xff\\xfd\\x1f\\xff\\xfd!\\xff\\xfb\\x01\\xff\\xfb\\x03\\r\\r\\n\\r\\n\\rNVS\\r\\n\\rLinux (2\\.\\d+\\.\\d+)(?:[\\w._-]+)? on a armv\\w+ \\(\\d\\d:\\d\\d:\\d\\d\\)\\r\\n\\r([\\w._-]+) login: ", + "regex_literal_tokens": [ + "!NVSLinux 2.", + "login:", + "on a armv" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x01\\xff\\xfb\\x03\\xff\\xfd\\x03\\xff\\xfd\\x1f\\r\\nlogin: ", + "regex_literal_tokens": [ + "login:" + ], + "cpe": [ + "cpe:/h:patton:sn4638/", + "cpe:/o:patton:smartware/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x03\\xff\\xfd\\x03\\xff\\xfb\\x01\\r\\nPrecise/RTCS v([\\w._-]+) Telnet server\\r\\n\\x1b\\[0m\\x1b\\[2J\\x1b\\[1;1H\\x1b\\[\\?25l\\x1b\\[0;30;47m\\x1b\\[0;34;47m\\*{80}\\r\\x00\\r\\n\\* {78}\\*\\r\\x00\\r\\n\\*{80}\\r\\x00\\r\\n\\* {12}Remote Status {13}\\* {12}Remote Control {13}\\*\\r\\x00\\r\\n\\*{80}\\r\\x00\\r\\n\\* Exciter #: ", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/h:harris:flexstar_hdx-fm/", + "cpe:/o:precise:mqx:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfe\\x01\\xff\\xfb\\x01\\xff\\xfb\\x03\\xff\\xfd\\x03\\xff\\xfd\\x1fUser name: ", + "regex_literal_tokens": [ + "User name:" + ], + "cpe": [ + "cpe:/o:microsoft:windows_10:::iot/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x01\\xff\\xfb\\x03\\xff\\xfb\\x00\\xff\\xfd\\x01\\xff\\xfd\\x00\\r\\x00\\n\\r\\x00\\n-{77}\\r\\x00\\nModel name {7}: ([\\w-]+)\\r\\x00\\nMAC address {6}: ([A-F0-9:]+)\\r\\x00\\nSerial No {8}: (\\d+)\\r\\x00\\nFirmware version : (([\\d.]+) Build \\d+)\\r\\x00\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/h:moxa:$1/", + "cpe:/o:moxa:$SUBST(1,\"-\",\"_\")_firmware:$5/" + ] + }, + { + "probe_id": "tcp:null", + "service": "telnet", + "regex": "^\\xff\\xfb\\x03\\xff\\xfd\\x03\\xff\\xfb\\x01\\r\\nPrecise/RTCS v(\\d[\\w._-]+) Telnet server\\r\\n\\x1b\\[2J\\r\\nUsername: ", + "regex_literal_tokens": [ + "Precise/RTCS v", + "Telnet server[2JUsername:" + ], + "cpe": [ + "cpe:/o:precise:mqx:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "vspe", + "regex": "^\\nADA38072\\r\\nAD_80099\\r\\nABA39071\\r\\nAB_07096\\r\\nACA40064\\r\\nAC_00090\\r\\nADA41066\\r\\nAD_81100\\r\\nABA42065\\r\\nAB_08097\\r\\nACA43067\\r\\nACA44068\\r\\nAC_01091\\r\\nADA45070\\r\\nAD_81100\\r\\nADA45070\\r\\nADA45070\\r\\nADA45070\\r\\nABA46069\\r\\nAB_09098\\r\\n", + "regex_literal_tokens": [ + "ADA38072AD_80099ABA39071AB_07096ACA40064AC_00090ADA41066AD_81100ABA42065AB_08097ACA43067ACA44068AC_01091ADA45070AD_81100ADA45070ADA45070ADA45070ABA46069AB_09098" + ], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:null", + "service": "zos-commserver", + "regex": "^EZY1315E \\d\\d/\\d\\d/\\d\\d \\d\\d:\\d\\d:\\d\\d INVALID TRANID=\\r\\n\\r\\n PARTNER INET ADDR=[\\d.]+ PORT= \\d+ ", + "regex_literal_tokens": [ + "EZY1315E", + "INVALID TRANID= PARTNER INET ADDR=", + "PORT=" + ], + "cpe": [ + "cpe:/o:ibm:z%2fos/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "airdroid", + "regex": "^#connected,all connect count: 1\\{\\\"event\\\":\\\"device_status\\\",\\\"data\\\":\\{\\\"wifi_name\\\":\\\"([^\\\"]+)\\\",\\\"wifi_signal\\\":\\d+,\\\"battery\\\":\\d+,\\\"batterycharging\\\":\\w+,\\\"gsm_signal\\\":\\d+,\\\"sms_unread\\\":\\d+,\\\"sdcard\\\":\\d+,\\\"updateinfo\\\":null\\}\\}", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/a:airdroid:airdroid/", + "cpe:/o:google:android/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "elm-agent", + "regex": "^ELM Manager Agent ([\\w._-]+)\\r\\nCopyright \\xa9 \\d+-\\d+ TNT Software, Inc\\.\\r\\n", + "regex_literal_tokens": [ + "Copyright", + "ELM Manager Agent", + "TNT Software, Inc." + ], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "elm-manager", + "regex": "^ELM Enterprise Manager ([\\w._-]+)\\r\\nCopyright \\xa9 \\d+-\\d+ TNT Software, Inc\\.\\r\\n", + "regex_literal_tokens": [ + "Copyright", + "ELM Enterprise Manager", + "TNT Software, Inc." + ], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "finger", + "regex": "^Punix version ([\\d./()]+) - Current Time \\(since boot\\) \\d+:\\d\\d:\\d\\d\\r\\nName pid stat pc cpusec stack pr/sy idle tty\\r\\n", + "regex_literal_tokens": [ + "- Current Time (since boot)", + "Name pid stat pc cpusec stack pr/sy idle tty", + "Punix version" + ], + "cpe": [ + "cpe:/o:christopher_williams:punix:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "finger", + "regex": "^Login Name Tty Idle Login Time Office Office Phone\\r\\n", + "regex_literal_tokens": [ + "Login Name Tty Idle Login Time Office Office Phone" + ], + "cpe": [ + "cpe:/o:debian:debian_linux/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "finger", + "regex": "^finger: /var/adm/lastlog open error\\nNo one logged on\\r\\n", + "regex_literal_tokens": [ + "finger: /var/adm/lastlog open errorNo one logged on" + ], + "cpe": [ + "cpe:/o:sun:sunos:5.10/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "finger", + "regex": "^finger: /var/adm/lastlog open error\\nLogin Name", + "regex_literal_tokens": [ + "finger: /var/adm/lastlog open errorLogin Name" + ], + "cpe": [ + "cpe:/o:sun:sunos:5.10/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "finger", + "regex": "^finger: /usr/adm/lastlog open error\\nLogin +Name +TTY Idle +When +Office\\r\\n", + "regex_literal_tokens": [ + "Name", + "Office", + "TTY Idle", + "When", + "finger: /usr/adm/lastlog open errorLogin" + ], + "cpe": [ + "cpe:/o:dec:osf_1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "finger", + "regex": "^\\nDebian GNU/Linux Copyright \\(c\\) 1993-1999 Software in the Public Interest\\n\\n Your site has been rejected for some reason\\.\\n\\n This may be caused by a missing RFC 1413 identd on your site\\.\\n\\n", + "regex_literal_tokens": [ + "Debian GNU/Linux Copyright (c) 1993-1999 Software in the Public Interest Your site has been rejected for some reason. This may be caused by a missing RFC 1413 identd on your site." + ], + "cpe": [ + "cpe:/a:debian:cfingerd/", + "cpe:/o:debian:debian_linux/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "finger", + "regex": "^Debian GNU/Linux Copyright \\(C\\) 1993-1999 Software in the Public Interest\\n.*You haven't specified a user\\.\\n\\n A general listing is not provided to the public\\.", + "regex_literal_tokens": [ + "Debian GNU/Linux Copyright (C) 1993-1999 Software in the Public Interest", + "You haven't specified a user. A general listing is not provided to the public." + ], + "cpe": [ + "cpe:/a:debian:cfingerd/", + "cpe:/o:debian:debian_linux/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "ftp", + "regex": "^220 Service ready\\.\\r\\n501 Syntax Error\\.\\r\\n", + "regex_literal_tokens": [ + "220 Service ready.501 Syntax Error." + ], + "cpe": [ + "cpe:/o:hay_systems:hsl_2.75g_femtocell/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "gntp", + "regex": "^GNTP/1\\.0 -ERROR NONE\\r\\nError-Code: 301\\r\\nError-Description: Growl does not recognize the protocol beginning with \\r\\n\\r\\n\\r\\nOrigin-Software-Name: Growl\\r\\nOrigin-Software-Version: ([\\d.]+)\\r\\nOrigin-Platform-Version: ([\\d.]+)\\r\\nOrigin-Machine-Name: (.*)\\r\\nOrigin-Platform-Name: Mac OS X\\r\\n\\r\\n\\r\\n", + "regex_literal_tokens": [ + "GNTP/1.0 -ERROR NONEError-Code: 301Error-Description: Growl does not recognize the protocol beginning with Origin-Software-Name: GrowlOrigin-Software-Version:", + "Origin-Machine-Name:", + "Origin-Platform-Name: Mac OS X", + "Origin-Platform-Version:" + ], + "cpe": [ + "cpe:/a:growl:growl:$1/", + "cpe:/o:apple:mac_os_x:$2/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nContent-Type: text/html\\r\\n\\r\\nmongodb ([\\w._-]+):\\d+ .*

db version v([\\w._-]+), pdfile version ([\\w._-]+)\\ngit hash: ([0-9a-f]{40})\\nsys info: Linux [\\w._-]+ ([\\w._-]+) .* BOOST_LIB_VERSION=([\\w._-]+)\\n\\ndbwritelocked:  \\d+ \\(initial\\)\\nuptime:    ([^\\n]+)\\n",
+      "regex_literal_tokens": [
+        "(initial)uptime:",
+        ", pdfile version",
+        "",
+        "
db version v",
+        "BOOST_LIB_VERSION=",
+        "HTTP/1.0 200 OKContent-Type: text/htmlmongodb",
+        "dbwritelocked:",
+        "git hash:",
+        "sys info: Linux"
+      ],
+      "cpe": [
+        "cpe:/o:linux:linux_kernel:$5/"
+      ]
+    },
+    {
+      "probe_id": "tcp:generic_lines",
+      "service": "http",
+      "regex": "^HTTP/1\\.0 200 OK\\r\\nContent-Type: text/html\\r\\n\\r\\n<html><head><title>mongodb ([\\w._-]+):\\d+ .*
db version v([\\w._-]+), pdfile version ([\\w._-]+)\\ngit hash: nogitversion\\nsys info: Linux [\\w._-]+ ([\\w._-]+) .* BOOST_LIB_VERSION=([\\w._-]+)\\n\\ndblocked:  \\d+ \\(initial\\)\\nuptime:    ([^\\n]+)\\n",
+      "regex_literal_tokens": [
+        "(initial)uptime:",
+        ", pdfile version",
+        "",
+        "
db version v",
+        "BOOST_LIB_VERSION=",
+        "HTTP/1.0 200 OKContent-Type: text/htmlmongodb",
+        "dblocked:",
+        "git hash: nogitversionsys info: Linux"
+      ],
+      "cpe": [
+        "cpe:/a:mongodb:mongodb:$2/",
+        "cpe:/o:linux:linux_kernel:$4/"
+      ]
+    },
+    {
+      "probe_id": "tcp:generic_lines",
+      "service": "http",
+      "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nServer: sfcHttpd\\r\\nContent-Length: 0\\r\\nConnection: close\\r\\n\\r\\nHTTP/1\\.1 400 Bad Request\\r\\nServer: sfcHttpd\\r\\nContent-Length: 0\\r\\nConnection: close\\r\\n\\r\\n",
+      "regex_literal_tokens": [
+        "HTTP/1.1 501 Not ImplementedServer: sfcHttpdContent-Length: 0Connection: closeHTTP/1.1 400 Bad RequestServer: sfcHttpdContent-Length: 0Connection: close"
+      ],
+      "cpe": [
+        "cpe:/o:supermicro:intelligent_platform_management_firmware/"
+      ]
+    },
+    {
+      "probe_id": "tcp:generic_lines",
+      "service": "http",
+      "regex": "^HTTP/1\\.0 400 Bad Request\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: doubleTwist Sync \\(Android\\)\\r\\n",
+      "regex_literal_tokens": [
+        "HTTP/1.0 400 Bad Request",
+        "Server: doubleTwist Sync (Android)"
+      ],
+      "cpe": [
+        "cpe:/o:google:android/"
+      ]
+    },
+    {
+      "probe_id": "tcp:generic_lines",
+      "service": "http",
+      "regex": "^HTTP/0\\.0 400 Bad request\\r\\nServer: Aos HTTP Server/([\\w._-]+)\\r\\nHTTP/0\\.0 400 Bad request\\r\\nServer: Aos HTTP Server/[\\w._-]+\\r\\nHTTP/0\\.0 400 Bad request\\r\\nServer: Aos HTTP Server/[\\w._-]+\\r\\nHTTP/0\\.0 400 Bad request\\r\\nServer: Aos HTTP Server/[\\w._-]+\\r\\nHTTP/0\\.0 400 Bad request\\r\\nServer: Aos HTTP Server/[\\w._-]+\\r\\nHTTP/0\\.0 400 Bad request\\r\\nServer: Aos HTTP Server/[\\w._-]+\\r\\nHTTP/0\\.0 400 Bad request\\r\\nServer: Aos HTTP Server/[\\w._-]+\\r\\nHTTP/0\\.0 400 Bad request\\r\\nServer: Aos HTTP Server/[\\w._-]+\\r\\nHTTP/0\\.0 400 Bad request\\r\\nServer: Aos HTTP Server/[\\w._-]+\\r\\n",
+      "regex_literal_tokens": [
+        "HTTP/0.0 400 Bad requestServer: Aos HTTP Server/"
+      ],
+      "cpe": [
+        "cpe:/o:eth:a2/"
+      ]
+    },
+    {
+      "probe_id": "tcp:generic_lines",
+      "service": "http",
+      "regex": "^HTTP/1\\.1 400 Bad Request\\r\\nCONNECTION: close\\r\\n\\r\\n$",
+      "regex_literal_tokens": [
+        "HTTP/1.1 400 Bad RequestCONNECTION: close"
+      ],
+      "cpe": [
+        "cpe:/o:freebsd:freebsd:8.0/"
+      ]
+    },
+    {
+      "probe_id": "tcp:generic_lines",
+      "service": "ident",
+      "regex": "^: USERID : UNIX : CacheFlow Server\\r\\n",
+      "regex_literal_tokens": [
+        ": USERID : UNIX : CacheFlow Server"
+      ],
+      "cpe": [
+        "cpe:/o:bluecoat:cacheos/"
+      ]
+    },
+    {
+      "probe_id": "tcp:generic_lines",
+      "service": "asf-rmcp",
+      "regex": "^\\x00\\x00\\x00\\x02\\t\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00$",
+      "regex_literal_tokens": [],
+      "cpe": [
+        "cpe:/o:supermicro:intelligent_platform_management_firmware/"
+      ]
+    },
+    {
+      "probe_id": "tcp:generic_lines",
+      "service": "jrpgt",
+      "regex": "^<<jrpgt!>>\\x7c$",
+      "regex_literal_tokens": [
+        "<<jrpgt!>>|"
+      ],
+      "cpe": [
+        "cpe:/o:microsoft:windows/"
+      ]
+    },
+    {
+      "probe_id": "tcp:generic_lines",
+      "service": "pmcd",
+      "regex": "^\\x00\\x00\\x00\\x14\\x00\\x00p\\x00\\x00\\x00\\x03.\\xff\\xff\\xfc\\x11\\x02\\x00..$",
+      "regex_literal_tokens": [],
+      "cpe": [
+        "cpe:/o:sgi:irix:6.5/"
+      ]
+    },
+    {
+      "probe_id": "tcp:generic_lines",
+      "service": "telnet",
+      "regex": "^\\xff\\xfd\\x03\\xff\\xfb\\x03\\xff\\xfd\\x18\\xff\\xfd\\x17Please wait\\. The connection to your station is still in the process of being established\\. Your last input has been discarded\\.\\r\\nPlease wait\\. The connection to your station is still in the process of being established\\. Your last input has been discarded\\.\\r\\n",
+      "regex_literal_tokens": [
+        "Please wait. The connection to your station is still in the process of being established. Your last input has been discarded.Please wait. The connection to your station is still in the process of being established. Your last input has been discarded."
+      ],
+      "cpe": [
+        "cpe:/o:burroughs:mcp/"
+      ]
+    },
+    {
+      "probe_id": "tcp:generic_lines",
+      "service": "telnet",
+      "regex": "^\\xff\\xfd\\x18\\xff\\xfa\\x18\\x01\\xff\\xf0\\xff\\xfb\\x03\\xff\\xfd\\x01\\xff\\xfd\\x1f\\xff\\xfb\\x05\\xff\\xfd!\\xff\\xfb\\x01TELNET_SERVER V([\\d.]+) RTOS-UH \\(c\\)IEP,1995-\\d\\d\\d\\d ready\\r\\nUsername:",
+      "regex_literal_tokens": [
+        "!TELNET_SERVER V",
+        "RTOS-UH (c)IEP,1995-",
+        "readyUsername:"
+      ],
+      "cpe": [
+        "cpe:/o:universitathanover:rtos-uh/"
+      ]
+    },
+    {
+      "probe_id": "tcp:generic_lines",
+      "service": "uucp",
+      "regex": "^login: login: login: $",
+      "regex_literal_tokens": [
+        "login: login: login:"
+      ],
+      "cpe": [
+        "cpe:/o:netbsd:netbsd/"
+      ]
+    },
+    {
+      "probe_id": "tcp:generic_lines",
+      "service": "upnp",
+      "regex": "^ 501 Not Implemented\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: 149\\r\\nServer: Net-OS (\\d+)\\.xx UPnP/([\\d.]+)\\r\\n\\r\\n<HTML><HEAD><TITLE>501 Not Implemented

Not Implemented

The HTTP Method is not implemented by this server\\.\\r\\n", + "regex_literal_tokens": [ + ".xx UPnP/", + "501 Not ImplementedContent-Type: text/htmlConnection: closeContent-Length: 149Server: Net-OS", + "501 Not Implemented

Not Implemented

The HTTP Method is not implemented by this server." + ], + "cpe": [ + "cpe:/o:digi:net%2bos:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "telnet", + "regex": "^\\xff\\xfb\\x01\\n\\rSSH service name not present in rcvd msg\\n\\rSSH Session task 0x\\w+: Version Exchange Failed\\n\\r\\n\\r\\n\\rSSH service name not present in rcvd msg\\n\\r", + "regex_literal_tokens": [ + ": Version Exchange FailedSSH service name not present in rcvd msg", + "SSH service name not present in rcvd msgSSH Session task 0x" + ], + "cpe": [ + "cpe:/a:cisco:telnet/", + "cpe:/o:cisco:aironet_350/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "telnet", + "regex": "^\\x1b\\[24;1HUsername: \\x1b\\[\\?25h\\x1b\\[24;1H\\x1b\\[\\?25h\\x1b\\[24;11H\\x1b\\[24;11H\\x1b\\[\\?25h\\x1b\\[24;11H\\x1b\\[24;1H\\r\\n\\r\\x1b\\[\\?25h\\x1b\\[24;11H\\xff\\xfd\\x18\\xff\\xfb\\x01\\x1b\\[2J\\x1b\\[\\?7l\\x1b\\[3;23r\\x1b\\[\\?6l\\x1b\\[1;1H\\x1b\\[\\?25l\\x1b\\[1;1HProCurve (\\w+) Switch (\\w+)\\r\\n\\rSoftware revision ([\\w.]+)\\r\\n", + "regex_literal_tokens": [ + "Software revision", + "Switch", + "[24;1HUsername: [?25h[24;1H[?25h[24;11H[24;11H[?25h[24;11H[24;1H[?25h[24;11H[2J[?7l[3;23r[?6l[1;1H[?25l[1;1HProCurve" + ], + "cpe": [ + "cpe:/h:hp:procurve_switch_$2/", + "cpe:/o:hp:procurve_switch_software:$3/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^ 501 Not Implemented\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: AsusWRT/([\\d.]+) UPnP/([\\w.]+) MiniUPnPd/([\\w.]+)\\r\\n", + "regex_literal_tokens": [ + "501 Not Implemented", + "MiniUPnPd/", + "Server: AsusWRT/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:asus:asuswrt:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^ 501 Not Implemented\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: FedoraCore/(\\d+) UPnP/([\\w._-]+) MiniUPnPd/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "501 Not Implemented", + "MiniUPnPd/", + "Server: FedoraCore/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:fedoraproject:fedora_core:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^ 501 Not Implemented\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: Debian/([\\w.]+) UPnP/([\\w._-]+) MiniUPnPd/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "501 Not Implemented", + "MiniUPnPd/", + "Server: Debian/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:debian:debian_linux:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^ 501 Not Implemented\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: Debian/([\\w.]+) UPnP/([\\w._-]+) miniupnpd/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "501 Not Implemented", + "Server: Debian/", + "UPnP/", + "miniupnpd/" + ], + "cpe": [ + "cpe:/o:debian:debian_linux:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^ 501 Not Implemented\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: Ubuntu/([\\w._-]+) UPnP/([\\w._-]+) miniupnpd/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "501 Not Implemented", + "Server: Ubuntu/", + "UPnP/", + "miniupnpd/" + ], + "cpe": [ + "cpe:/o:canonical:ubuntu_linux:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^ 501 Not Implemented\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: Linux/(([234]\\.[\\d.]+)[\\w._-]+) UPnP/([\\w._-]+) [Mm]ini[Uu][Pp]n[Pp]d/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "501 Not Implemented", + "Server: Linux/", + "UPnP/", + "ini" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$2/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^ 501 Not Implemented\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: SmoothWall Express/([\\d.]+) UPnP/([\\d.]+) MiniUPnPd/([\\d.]+)\\r\\n", + "regex_literal_tokens": [ + "501 Not Implemented", + "MiniUPnPd/", + "Server: SmoothWall Express/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:smoothwall:smoothwall:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: 149\\r\\nServer: Debian/([\\w._/-]+) DLNADOC/([\\w._-]+) UPnP/([\\w._-]+) MiniDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "DLNADOC/", + "HTTP/1.1 501 Not ImplementedContent-Type: text/htmlConnection: closeContent-Length: 149Server: Debian/", + "MiniDLNA/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:debian:debian_linux:$1/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: 149\\r\\nServer: RedHatEnterpriseServer/([\\w._/-]+) DLNADOC/([\\w._-]+) UPnP/([\\w._-]+) MiniDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "DLNADOC/", + "HTTP/1.1 501 Not ImplementedContent-Type: text/htmlConnection: closeContent-Length: 149Server: RedHatEnterpriseServer/", + "MiniDLNA/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel/", + "cpe:/o:redhat:enterprise_linux:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: 149\\r\\nServer: Fedora/([\\w._-]+) DLNADOC/([\\w._-]+) UPnP/([\\w._-]+) MiniDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "DLNADOC/", + "HTTP/1.1 501 Not ImplementedContent-Type: text/htmlConnection: closeContent-Length: 149Server: Fedora/", + "MiniDLNA/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:fedoraproject:fedora:$1/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: 149\\r\\nServer: RAIDiator/([\\w._-]+) DLNADOC/([\\w._-]+) UPnP/([\\w._-]+) MiniDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "DLNADOC/", + "HTTP/1.1 501 Not ImplementedContent-Type: text/htmlConnection: closeContent-Length: 149Server: RAIDiator/", + "MiniDLNA/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:netgear:raidiator:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: 149\\r\\nServer: Ubuntu/([\\w._-]+) DLNADOC/([\\w._-]+) UPnP/([\\w._-]+) MiniDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "DLNADOC/", + "HTTP/1.1 501 Not ImplementedContent-Type: text/htmlConnection: closeContent-Length: 149Server: Ubuntu/", + "MiniDLNA/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:canonical:ubuntu_linux:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: 149\\r\\nServer: Gentoo/([\\w._-]+) DLNADOC/([\\w._-]+) UPnP/([\\w._-]+) MiniDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "DLNADOC/", + "HTTP/1.1 501 Not ImplementedContent-Type: text/htmlConnection: closeContent-Length: 149Server: Gentoo/", + "MiniDLNA/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:gentoo:linux:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: 149\\r\\nServer: SUSE LINUX/n/a DLNADOC/([\\w._-]+) UPnP/([\\w._-]+) MiniDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.1 501 Not ImplementedContent-Type: text/htmlConnection: closeContent-Length: 149Server: SUSE LINUX/n/a DLNADOC/", + "MiniDLNA/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:suse:suse_linux/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: 149\\r\\nServer: Linux/([\\w._-]+) DLNADOC/([\\w._-]+) UPnP/([\\w._-]+) MiniDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "DLNADOC/", + "HTTP/1.1 501 Not ImplementedContent-Type: text/htmlConnection: closeContent-Length: 149Server: Linux/", + "MiniDLNA/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: 149\\r\\nServer: (?:Linux )?(([234]\\.[\\d.]+)[\\w._-]+) DLNADOC/([\\w._-]+) UPnP/([\\w._-]+) MiniDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "DLNADOC/", + "HTTP/1.1 501 Not ImplementedContent-Type: text/htmlConnection: closeContent-Length: 149Server:", + "Linux", + "MiniDLNA/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$2/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: 149\\r\\nServer: OpenWrt Linux/([\\w._-]+) DLNADOC/([\\w._-]+) UPnP/([\\w._-]+) MiniDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "DLNADOC/", + "HTTP/1.1 501 Not ImplementedContent-Type: text/htmlConnection: closeContent-Length: 149Server: OpenWrt Linux/", + "MiniDLNA/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: 149\\r\\nServer: FreeBSD/([\\w._-]+) DLNADOC/([\\w._-]+) UPnP/([\\w._-]+) MiniDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "DLNADOC/", + "HTTP/1.1 501 Not ImplementedContent-Type: text/htmlConnection: closeContent-Length: 149Server: FreeBSD/", + "MiniDLNA/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:freebsd:freebsd:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: 149\\r\\nServer: RAIDiator/([\\w._-]+) DLNADOC/([\\w._-]+) UPnP/([\\w._-]+) ReadyDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "DLNADOC/", + "HTTP/1.1 501 Not ImplementedContent-Type: text/htmlConnection: closeContent-Length: 149Server: RAIDiator/", + "ReadyDLNA/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:netgear:raidiator:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: 149\\r\\nServer: Linux[ /]([\\d.]+) DLNADOC/([\\w._-]+) UPnP/([\\w._-]+) ReadyDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "DLNADOC/", + "HTTP/1.1 501 Not ImplementedContent-Type: text/htmlConnection: closeContent-Length: 149Server: Linux", + "ReadyDLNA/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: 149\\r\\nServer: ([\\d._-]+)ReadyNAS DLNADOC/([\\w._-]+) UPnP/([\\w._-]+) ReadyDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.1 501 Not ImplementedContent-Type: text/htmlConnection: closeContent-Length: 149Server:", + "ReadyDLNA/", + "ReadyNAS DLNADOC/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: 149\\r\\nServer: (?:Linux )?(([234]\\.[\\d.]+)[\\w._-]+) DLNADOC/([\\w._-]+) UPnP/([\\w._-]+) ReadyDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "DLNADOC/", + "HTTP/1.1 501 Not ImplementedContent-Type: text/htmlConnection: closeContent-Length: 149Server:", + "Linux", + "ReadyDLNA/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$2/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^ 501 Not Implemented\\r.*\\nServer: RedHatEnterpriseServer/([\\d.]+) UPnP/([\\d.]+) MiniUPnPd/([\\d.]+)\\r\\n", + "regex_literal_tokens": [ + "501 Not Implemented", + "MiniUPnPd/", + "Server: RedHatEnterpriseServer/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel/", + "cpe:/o:redhat:enterprise_linux:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^ 501 Not Implemented\\r.*\\nServer: EXOS/OpenWrt UPnP/([\\d.]+) MiniUPnPd/([\\d.]+)\\r\\n", + "regex_literal_tokens": [ + "501 Not Implemented", + "MiniUPnPd/", + "Server: EXOS/OpenWrt UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/0\\.0 400 Bad Request\\r\\nServer: Windows/([\\w._-]+\\.2600)/Service Pack (\\d+), UPnP/([\\d.]+), TVersity Media Server/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + ", TVersity Media Server/", + ", UPnP/", + ".2600/Service Pack", + "HTTP/0.0 400 Bad RequestServer: Windows/" + ], + "cpe": [ + "cpe:/o:microsoft:windows_xp::sp$2/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/0\\.0 400 Bad Request\\r\\nServer: Windows/([\\w._-]+)\\.6001/Service Pack (\\d+), UPnP/([\\d.]+), TVersity Media Server/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + ", TVersity Media Server/", + ", UPnP/", + ".6001/Service Pack", + "HTTP/0.0 400 Bad RequestServer: Windows/" + ], + "cpe": [ + "cpe:/o:microsoft:windows_vista::sp$2/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 \\d\\d\\d .*\\r\\nDATE: .*\\r\\nConnection: Keep-Alive\\r\\nServer: LINUX/([\\w._-]+) UPnP/([\\d.]+) BRCM400/([\\d.]+)\\r\\n", + "regex_literal_tokens": [ + "BRCM400/", + "Connection: Keep-AliveServer: LINUX/", + "DATE:", + "HTTP/1.1", + "UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 \\d\\d\\d .*\\r\\nDATE: .*\\r\\nConnection: Keep-Alive\\r\\nServer: LINUX/([\\w._-]+) UPnP/([\\d.]+) ZyXEL-UPnP/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "Connection: Keep-AliveServer: LINUX/", + "DATE:", + "HTTP/1.1", + "UPnP/", + "ZyXEL-UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/1\\.1 400 Bad Request\\r\\nServer: Symbian/([\\w._-]+) UPnP/([\\d.]+)\\r\\nContent-Length: 151\\r\\n\\r\\n\\n\\n400 Bad Request\\n\\n

Bad Request

\\n
\\n$", + "regex_literal_tokens": [ + "Content-Length: 151400 Bad Request

Bad Request


", + "HTTP/1.1 400 Bad RequestServer: Symbian/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:symbian:symbian/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "upnp", + "regex": "^HTTP/0\\.0 400 Bad Request\\r\\nSERVER: Linux/([\\w._-]+) UPnP/([\\w._-]+) SKY DLNADOC/([\\w._-]+)\\r\\n\\r\\n", + "regex_literal_tokens": [ + "HTTP/0.0 400 Bad RequestSERVER: Linux/", + "SKY DLNADOC/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "unreal-media", + "regex": "^\\xb1\\x36\\x00\\x00\\x19\\x00\\x00\\x00\\x30\\x05\\xff\\x8f\\x00\\x00\\x00\\x00\\x88\\xff.\\x03.\\xef.\\x00$", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "finger", + "regex": "^finger: GET: no such user\\nfinger: /: no such user\\nfinger: HTTP/1\\.0: no such user\\n$", + "regex_literal_tokens": [ + "finger: GET: no such userfinger: /: no such userfinger: HTTP/1.0: no such user" + ], + "cpe": [ + "cpe:/o:netbsd:netbsd/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "gopher", + "regex": "^iUnable to locate requested resource\\.\\t\\t([\\w._-]+)\\t\\d+\\r\\n\\.\\r\\n", + "regex_literal_tokens": [ + "iUnable to locate requested resource." + ], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "gopher", + "regex": "^Error: File or directory not found!\\r\\n______________________________________________________________________\\r\\n Gophered by Gophernicus/([\\w._-]+) on archlinux/rolling ", + "regex_literal_tokens": [ + "Error: File or directory not found!______________________________________________________________________ Gophered by Gophernicus/", + "on archlinux/rolling" + ], + "cpe": [ + "cpe:/o:archlinux:arch_linux/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.[01] 401 Unauthorized\\r\\nDate: .*\\r\\nWWW-Authenticate: Basic realm=\\\"PIX\\\"", + "regex_literal_tokens": [ + "401 UnauthorizedDate:", + "HTTP/1.", + "WWW-Authenticate: Basic realm=\"PIX\"" + ], + "cpe": [ + "cpe:/o:cisco:pix_firewall_software/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d (?s:.*?)Server: Cherokee/([-.\\w]+) \\(Debian GNU/Linux\\)\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/a:cherokee-project:cherokee:$1/", + "cpe:/o:debian:debian_linux/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d (?s:.*?)Server: Cherokee/([-.\\w]+) \\(Ubuntu\\)\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/a:cherokee-project:cherokee:$1/", + "cpe:/o:canonical:ubuntu_linux/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d (?s:.*?)Server: Cherokee/([-.\\w]+) \\(openSUSE Build Service\\)\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/a:cherokee-project:cherokee:$1/", + "cpe:/o:novell:opensuse/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d (?s:.*?)Server: Cherokee/([-.\\w]+) \\(Gentoo Linux\\)\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/a:cherokee-project:cherokee:$1/", + "cpe:/o:gentoo:linux/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nServer: eHTTP v([\\w._-]+)\\r\\n.*\\s*([\\w._-]+)\\s*-\\s*(?:HP )?(?:\\w+ )?ProCurve Switch ([\\w._-]+)", + "regex_literal_tokens": [ + "<title>", + "HTTP/1.0 200 OKServer: eHTTP v", + "ProCurve Switch" + ], + "cpe": [ + "cpe:/a:ehttp:ehttp:$1/", + "cpe:/h:hp:procurve_switch_$3/", + "cpe:/o:hp:procurve_switch_software/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nServer: eHTTP v([\\w._-]+)\\r\\n.*<title>\\s*(?:HP )?(?:\\w+\\s+)?ProCurve Switch ([\\w._-]+)", + "regex_literal_tokens": [ + "<title>", + "HTTP/1.0 200 OKServer: eHTTP v", + "ProCurve Switch" + ], + "cpe": [ + "cpe:/a:ehttp:ehttp:$1/", + "cpe:/h:hp:procurve_switch_$2/", + "cpe:/o:hp:procurve_switch_software/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nServer: eHTTP v([\\w._-]+)\\r\\n.*<title>\\s*([\\w._-]+)\\s*-\\s*(?:HP )?(?:\\w+ )?ProCurve ([\\w._-]+) Switch", + "regex_literal_tokens": [ + "<title>", + "HTTP/1.0 200 OKServer: eHTTP v", + "ProCurve", + "Switch" + ], + "cpe": [ + "cpe:/a:ehttp:ehttp:$1/", + "cpe:/h:hp:procurve_switch_$3/", + "cpe:/o:hp:procurve_switch_software/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nServer: eHTTP v([\\w._-]+)\\r\\n.*<title>\\s*(?:HP )?(?:\\w+\\s+)?ProCurve ([\\w._-]+) Switch", + "regex_literal_tokens": [ + "<title>", + "HTTP/1.0 200 OKServer: eHTTP v", + "ProCurve", + "Switch" + ], + "cpe": [ + "cpe:/a:ehttp:ehttp:$1/", + "cpe:/h:hp:procurve_switch_$2/", + "cpe:/o:hp:procurve_switch_software/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nServer: eHTTP v([\\w._-]+)\\r\\n.*<title>\\s*([ \\w._-]+?)\\s*-\\s*(?:HP )?(?:\\w+ )?ProCurve Switch ([\\w._-]+)", + "regex_literal_tokens": [ + "<title>", + "HTTP/1.0 200 OKServer: eHTTP v", + "ProCurve Switch" + ], + "cpe": [ + "cpe:/a:ehttp:ehttp:$1/", + "cpe:/h:hp:procurve_switch_$3/", + "cpe:/o:hp:procurve_switch_software/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 401 Unauthorized\\r\\nServer: eHTTP v([\\w._-]+)\\r\\n(?:[^\\r\\n]+\\r\\n)*?WWW-Authenticate: Basic realm=\\\"HP ([-.\\w]+)\\\"\\r\\n\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.1 401 UnauthorizedServer: eHTTP v", + "WWW-Authenticate: Basic realm=\"HP" + ], + "cpe": [ + "cpe:/a:ehttp:ehttp:$1/", + "cpe:/o:hp:procurve_switch_software/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 401 Unauthorized\\r\\nServer: eHTTP v([\\w._-]+)\\r\\n(?:[^\\r\\n]+\\r\\n)*?WWW-Authenticate: Basic realm=\\\"ProCurve (J\\w+)\\\"\\r\\n\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.1 401 UnauthorizedServer: eHTTP v", + "WWW-Authenticate: Basic realm=\"ProCurve J" + ], + "cpe": [ + "cpe:/a:ehttp:ehttp:$1/", + "cpe:/h:hp:procurve_switch_$2/", + "cpe:/o:hp:procurve_switch_software/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\nServer: Web Server\\r\\nContent-Type: text/html\\r\\nCache-Control: no-cache\\r\\nPragma: no-cache\\r\\n\\r\\n\\r\\n <!DOCTYPE HTML PUBLIC \\\"-//W3C//DTD HTML 4\\.0 Transitional//EN\\\">\\n<HTML>\\n<HEAD>\\n <TITLE>Login", + "regex_literal_tokens": [ + "HTTP/1.1 200 OKServer: Web ServerContent-Type: text/htmlCache-Control: no-cachePragma: no-cache Login" + ], + "cpe": [ + "cpe:/h:hp:procurve_switch_1810g/", + "cpe:/o:hp:procurve_switch_software/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nServer: eHTTP v([\\w._-]+)\\r\\n.*HP Virtual Stack\\n\\n", + "regex_literal_tokens": [ + "HP Virtual Stack", + "HTTP/1.0 200 OKServer: eHTTP v" + ], + "cpe": [ + "cpe:/a:ehttp:ehttp:$1/", + "cpe:/h:hp:procurve_switch_2626/", + "cpe:/o:hp:procurve_switch_software/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^Server: Apache/([-\\.\\w]+) \\(Ubuntu\\)\\r?$", + "regex_flags": "mi", + "regex_literal_tokens": [ + "Server: Apache/", + "(Ubuntu)" + ], + "cpe": [ + "cpe:/a:apache:http_server:$1/", + "cpe:/o:canonical:ubuntu_linux/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^Server: Apache/([-\\.\\w]+) \\(Debian\\)\\r?$", + "regex_flags": "mi", + "regex_literal_tokens": [ + "Server: Apache/", + "(Debian)" + ], + "cpe": [ + "cpe:/a:apache:http_server:$1/", + "cpe:/o:debian:debian_linux/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^Server: Apache/([-\\.\\w]+) \\(CentOS\\)\\r?$", + "regex_flags": "mi", + "regex_literal_tokens": [ + "Server: Apache/", + "(CentOS)" + ], + "cpe": [ + "cpe:/a:apache:http_server:$1/", + "cpe:/o:centos:centos/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^Server: Apache/([-\\.\\w]+) \\((?:Red Hat|RHEL)\\)\\r?$", + "regex_flags": "mi", + "regex_literal_tokens": [ + "Server: Apache/", + "(Red Hat|RHEL)" + ], + "cpe": [ + "cpe:/a:apache:http_server:$1/", + "cpe:/o:redhat:enterprise_linux/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.[01] \\d\\d\\d.*\\r\\nDate: .*\\r\\nServer: Apache-AdvancedExtranetServer/(\\d[-.\\w]+) \\(Mandriva Linux/PREFORK-([-\\w_.]+)\\) (.*)\\r\\n", + "regex_literal_tokens": [ + "(Mandriva Linux/PREFORK-", + "Date:", + "HTTP/1.", + "Server: Apache-AdvancedExtranetServer/" + ], + "cpe": [ + "cpe:/a:apache:http_server:$1/", + "cpe:/o:mandriva:linux/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.[01] \\d\\d\\d.*\\r\\nServer: nginx/([\\d.]+) \\(Ubuntu\\)\\r\\n", + "regex_literal_tokens": [ + "(Ubuntu)", + "HTTP/1.", + "Server: nginx/" + ], + "cpe": [ + "cpe:/a:igor_sysoev:nginx:$1/", + "cpe:/o:canonical:ubuntu_linux/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nDate: .+\\r\\nServer: Tomcat/([-.\\w]+)\\r\\nContent-Type: text/html\\r\\nContent-Length: \\d+\\r\\nServlet-Engine: Tomcat/[-.\\w]+ \\(Java ([-.\\w]+); SunOS ([-.\\w]+) (\\w+); java\\.vendor=Sun Microsystems Inc\\.\\)\\r\\n", + "regex_literal_tokens": [ + "(Java", + "; SunOS", + "; java.vendor=Sun Microsystems Inc.)", + "Content-Type: text/htmlContent-Length:", + "HTTP/1.0 200 OKDate:", + "Server: Tomcat/", + "Servlet-Engine: Tomcat/" + ], + "cpe": [ + "cpe:/a:apache:tomcat:$1/", + "cpe:/a:sun:jre:$2/", + "cpe:/o:sun:sunos:$3/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d .*\\r\\nServer: EHTTP/([\\d.]+)\\r\\nPragma:no-cache\\r\\nContent-Type:text/html\\r\\n\\r\\n \\n\\n \\n(.*) \\n- HP \\w+ ProCurve Switch (\\w+)\\n", + "regex_literal_tokens": [ + "- HP", + "", + "HTTP/1.0", + "Pragma:no-cacheContent-Type:text/html ", + "ProCurve Switch", + "Server: EHTTP/" + ], + "cpe": [ + "cpe:/a:ehttp:ehttp:$1/", + "cpe:/h:hp:procurve_switch_$3/", + "cpe:/o:hp:procurve_switch_software/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d (?s:.*?)Connection: close\\r\\nContent-Length: \\d+\\r\\nContent-Type: text/html.*\\r\\n\\r\\n<!DOCTYPE html\\nPUBLIC.*\\n<title>MikroTik RouterOS Managing Webpage\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:mikrotik:routeros/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Content-Type: text/html.*\\r\\n\\r\\nRouterOS router configuration page", + "regex_literal_tokens": [ + "RouterOS router configuration page", + "Content-Type: text/html", + "HTTP/1.0 200 OK" + ], + "cpe": [ + "cpe:/o:mikrotik:routeros/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 403 Forbidden\\r\\nDate: .*\\r\\nServer: Microsoft-WinCE/([\\w._-]+)\\r\\nContent-Type: text/html\\r\\nContent-Length: 125\\r\\n\\r\\nAccess DeniedAccess denied\\.

The action requested is forbidden\\.$", + "regex_literal_tokens": [ + "Content-Type: text/htmlContent-Length: 125Access DeniedAccess denied.

The action requested is forbidden.", + "HTTP/1.1 403 ForbiddenDate:", + "Server: Microsoft-WinCE/" + ], + "cpe": [ + "cpe:/h:crestron/", + "cpe:/o:microsoft:windows_ce:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d (?s:.*?)content-length: \\d+\\r\\ncontent-type: text/html\\r\\ndate: .*MikroTik RouterOS Managing Webpage", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:mikrotik:routeros/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d .*\\r\\nServer: Thy/([\\d.]+) Debian/[\\w/]+ \\([^)]+\\) GnuTLS/([\\d.]+) zlib/([\\d.]+)\\r\\n", + "regex_literal_tokens": [ + ") GnuTLS/", + "Debian/", + "HTTP/1.0", + "Server: Thy/", + "zlib/" + ], + "cpe": [ + "cpe:/o:debian:debian_linux/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d .*\\r\\nServer: Thy/([\\d.]+) Debian \\(\\w+\\) GnuTLS/([\\d.]+) zlib/([\\d.]+)\\r\\n", + "regex_literal_tokens": [ + ") GnuTLS/", + "Debian (", + "HTTP/1.0", + "Server: Thy/", + "zlib/" + ], + "cpe": [ + "cpe:/o:debian:debian_linux/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nDate: .*\\r\\nServer: Java/[\\d.]+\\r\\nContent-type: text/html\\r\\nContent-length: \\d+\\r\\n\\r\\n.*TINIWebServer.*Current temperature ([\\d.]+) F
", + "regex_literal_tokens": [ + "TINIWebServer", + "Content-type: text/htmlContent-length:", + "Current temperature", + "F
", + "HTTP/1.0 200 OKDate:", + "Server: Java/" + ], + "cpe": [ + "cpe:/o:systronix:tinios/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 302 Found\\r\\nLocation: http://www\\.cfauth\\.com/\\?cfru[\\w=]+\\r\\nCache-Control: no-cache\\r\\nPragma: no-cache\\r\\n", + "regex_literal_tokens": [ + "Cache-Control: no-cachePragma: no-cache", + "HTTP/1.1 302 FoundLocation: http://www.cfauth.com/?cfru" + ], + "cpe": [ + "cpe:/o:bluecoat:cacheos/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d .*\\nContent-Length: \\d+\\n.*Cable Modem Description :.*

ZyXEL Prestige (\\w+), HW V([\\d.]+), SW ZyNOS V([\\d.]+)\\(", + "regex_literal_tokens": [ + ", HW V", + ", SW ZyNOS V", + "Cable Modem Description :", + "

ZyXEL Prestige", + "Content-Length:", + "HTTP/1.0" + ], + "cpe": [ + "cpe:/o:zyxel:zynos/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.[01] \\d\\d\\d .*\\r\\nDate: .*\\r\\nServer: Embperl/([\\w.]+) Apache/([\\w.]+) \\(Fedora\\)\\r\\n", + "regex_literal_tokens": [ + "(Fedora)", + "Apache/", + "Date:", + "HTTP/1.", + "Server: Embperl/" + ], + "cpe": [ + "cpe:/a:apache:http_server:$2/", + "cpe:/a:ecos:embperl:$1/", + "cpe:/o:fedoraproject:fedora/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.[01] \\d\\d\\d .*\\r\\nDate: .*\\r\\nServer: Embperl/([\\w.]+) Apache/([\\w.]+) \\(Debian GNU/Linux\\) (.*)\\r\\n", + "regex_literal_tokens": [ + "(Debian GNU/Linux)", + "Apache/", + "Date:", + "HTTP/1.", + "Server: Embperl/" + ], + "cpe": [ + "cpe:/a:apache:http_server:$2/", + "cpe:/a:ecos:embperl:$1/", + "cpe:/o:debian:debian_linux:$3/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.[01] \\d\\d\\d .*\\r\\nDate: .*\\r\\nServer: Embperl/([\\w.]+) Apache/([\\w.]+) \\(Debian GNU/Linux\\)\\r\\n", + "regex_literal_tokens": [ + "(Debian GNU/Linux)", + "Apache/", + "Date:", + "HTTP/1.", + "Server: Embperl/" + ], + "cpe": [ + "cpe:/a:apache:http_server:$2/", + "cpe:/a:ecos:embperl:$1/", + "cpe:/o:debian:debian_linux/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.[01] \\d\\d\\d .*\\r\\nDate: .*\\r\\nServer: PicoWebServer\\r\\n", + "regex_literal_tokens": [ + "Date:", + "HTTP/1.", + "Server: PicoWebServer" + ], + "cpe": [ + "cpe:/o:microsoft:windows_ce/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 302 Redirect\\r\\nServer: GoAhead-Webs\\r\\nDate: .*\\r\\nPragma: no-cache\\r\\nCache-Control: no-cache\\r\\nContent-Type: text/html\\r\\nLocation: http://Netlinx/WebControl\\.asp\\r\\n\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.0 302 RedirectServer: GoAhead-WebsDate:", + "Pragma: no-cacheCache-Control: no-cacheContent-Type: text/htmlLocation: http://Netlinx/WebControl.asp" + ], + "cpe": [ + "cpe:/a:goahead:goahead_webserver/", + "cpe:/o:harman:amx_firmware/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d .*mikrotik routeros > administration.*font-size: 9px\\\">mikrotik routeros ([\\d.]+) administration", + "regex_literal_tokens": [ + "mikrotik routeros > administration", + "HTTP/1.0", + "administration", + "font-size: 9px\">mikrotik routeros" + ], + "cpe": [ + "cpe:/o:mikrotik:routeros:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d (?s:.*?)Server: BeOS/PoorMan\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:be:beos/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d (?s:.*?)Server: Novell-Agent ([\\w._-]+) \\(Linux\\)\\r\\n.*GroupWise Monitor - Status", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/a:novell:groupwise/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d (?s:.*?)Server: Allegro-Software-RomPager/(\\d[-.\\w]+)\\r\\n\\r\\n.*ExtremeWare Management Interface", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/a:allegro:rompager:$1/", + "cpe:/o:extremenetworks:extremeware/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nDate: .*\\r\\nContent-Length: \\d+\\r\\nContent-Type: text/html\\r\\nServer: HyNetOS/([\\d.]+)\\r\\n\\r\\n\\r\\n\\r\\nEverFocus EDSR Applet \\(([\\d.]+)\\)", + "regex_literal_tokens": [ + ")", + "EverFocus EDSR Applet (", + "Content-Length:", + "Content-Type: text/htmlServer: HyNetOS/", + "HTTP/1.0 200 OKDate:" + ], + "cpe": [ + "cpe:/o:hyperstone:hynetos:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d .*\\r\\nServer: XOS (\\w+)\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.0", + "Server: XOS" + ], + "cpe": [ + "cpe:/o:extremenetworks:extremeware_xos/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d .*<title>Smart VoIP IAD Web Configuration Pages", + "regex_literal_tokens": [ + "Smart VoIP IAD Web Configuration Pages", + "HTTP/1.0" + ], + "cpe": [ + "cpe:/h:patton:sl4020/", + "cpe:/o:patton:smartware/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 \\d\\d\\d (?s:.*?)Server: Web Server\\r\\n.*\\nCisco Systems, Inc\\. VPN 3000 Concentrator \\[vpn-conc-3030\\]\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:cisco:vpn_3000_concentrator_series_software/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\nDate:.*Welcome to VMware ESX Server ([\\d.]+)\\n\\n", + "regex_literal_tokens": [ + "", + "Welcome to VMware ESX Server", + "HTTP/1.1 200 OKDate:" + ], + "cpe": [ + "cpe:/o:vmware:esx:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\nDate: .*document\\.write\\(\\\"<title>\\\" \\+ ID_EE?SX_Welcome \\+ \\\"", + "regex_literal_tokens": [ + "HTTP/1.1 200 OKDate:", + "SX_Welcome + \"", + "document.write(\"\" + ID_E" + ], + "cpe": [ + "cpe:/o:vmware:esxi/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d .*\\r\\nDate: .*\\r\\nServer: GG/([\\d.]+) \\(Unix\\) Debian GNU/Linux\\r\\nWWW-Authenticate: Basic realm=\\\"gg zone\\\"\\r\\n", + "regex_literal_tokens": [ + "(Unix) Debian GNU/LinuxWWW-Authenticate: Basic realm=\"gg zone\"", + "Date:", + "HTTP/1.0", + "Server: GG/" + ], + "cpe": [ + "cpe:/o:debian:debian_linux/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 401 Authorization Required\\r\\nconnection: Close\\r\\ncontent-type: text/html\\r\\nserver: NEWS/([\\w._-]+ \\(Funk\\)) \\(Windows 2000\\)\\r\\n", + "regex_literal_tokens": [ + "(Funk) (Windows 2000)", + "HTTP/1.0 401 Authorization Requiredconnection: Closecontent-type: text/htmlserver: NEWS/" + ], + "cpe": [ + "cpe:/o:microsoft:windows_2000/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 400 Bad Request\\r\\nDate: .*\\r\\nRIPT-Server: iTunesLib/([-\\w_.]+) \\(Mac OS X\\)\\r\\n", + "regex_literal_tokens": [ + "(Mac OS X)", + "HTTP/1.1 400 Bad RequestDate:", + "RIPT-Server: iTunesLib/" + ], + "cpe": [ + "cpe:/a:apple:apple_tv/", + "cpe:/o:apple:mac_os_x/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 401 Unauthorized\\r\\nServer: mini_httpd/([-\\w_.]+)/astlinux (\\w+)\\r\\nDate: .*\\r\\nCache-Control: no-cache,no-store\\r\\nWWW-Authenticate: Basic realm=\\\"\\.\\\"\\r\\n", + "regex_literal_tokens": [ + "/astlinux", + "Cache-Control: no-cache,no-storeWWW-Authenticate: Basic realm=\".\"", + "Date:", + "HTTP/1.0 401 UnauthorizedServer: mini_httpd/" + ], + "cpe": [ + "cpe:/a:acme:mini_httpd:$1/", + "cpe:/o:linux:linux_kernel:$2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nServer: http server ([\\w._-]+)\\r\\n(?:[^\\r\\n]+\\r\\n)*?Content-length: 291\\r\\n.*if\\(location\\.hostname\\.indexOf\\(':'\\) == -1\\)\\{location\\.href='http://'\\+location\\.hostname\\+':'\\+8080\\+'/';\\n\\}", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:linux:linux_kernel:2.6/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 301 Moved Permanently\\r\\nDate: .*\\r\\nLocation: https://([\\w._-]+)/\\r\\nConnection: close\\r\\nContent-Type: text/html\\r\\nContent-Length: 56\\r\\n\\r\\n<HTML><BODY><H1>301 Moved Permanently</H1></BODY></HTML>$", + "regex_literal_tokens": [ + "/Connection: closeContent-Type: text/htmlContent-Length: 56<HTML><BODY><H1>301 Moved Permanently</H1></BODY></HTML>", + "HTTP/1.1 301 Moved PermanentlyDate:", + "Location: https://" + ], + "cpe": [ + "cpe:/o:vmware:esxi/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 301 Moved Permanently\\r\\nDate: .*\\r\\nLocation: https://([\\w._-]+)/\\r\\nConnection: close\\r\\nContent-Length: 0\\r\\n\\r\\n$", + "regex_literal_tokens": [ + "/Connection: closeContent-Length: 0", + "HTTP/1.1 301 Moved PermanentlyDate:", + "Location: https://" + ], + "cpe": [ + "cpe:/o:vmware:esx:3.5/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 401 Unauthorized\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: THEO\\+Server/([\\d.]+)\\r\\n(?:[^\\r\\n]+\\r\\n)*?WWW-Authenticate: Basic realm=\\\"THEOS Web-based Maintenance\\\"\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.1 401 Unauthorized", + "Server: THEO+Server/", + "WWW-Authenticate: Basic realm=\"THEOS Web-based Maintenance\"" + ], + "cpe": [ + "cpe:/o:theos:theos/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\nServer: Web Server\\r\\n.*top\\.location\\.href = \\\"/hp_login\\.html\\\";\\r\\n</script>\\r\\n\\r\\n\\r\\n<BODY style=\\\"text-align: center\\\" onload=\\\"document\\.forms\\[0\\]\\.login\\.focus\\(\\);CheckError\\(\\)\\\">\\r\\n<FORM METHOD=\\\"POST\\\" ACTION=\\\"/hp_login\\.html\\\">", + "regex_literal_tokens": [ + "HTTP/1.1 200 OKServer: Web Server", + "top.location.href = \"/hp_login.html\";</script><BODY style=\"text-align: center\" onload=\"document.forms[0].login.focus();CheckError()\"><FORM METHOD=\"POST\" ACTION=\"/hp_login.html\">" + ], + "cpe": [ + "cpe:/h:hp:procurve_switch_1810g/", + "cpe:/o:hp:procurve_switch_software/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nServer: RapidLogic/([\\w._-]+)\\r\\n.*<title>OneAccess WCF", + "regex_literal_tokens": [ + "OneAccess WCF", + "HTTP/1.0 200 OKServer: RapidLogic/" + ], + "cpe": [ + "cpe:/a:rapidlogic:httpd:$1/", + "cpe:/o:oneaccess:oneos/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\nCONTENT-ENCODING: gzip\\r\\n(?:[^\\r\\n]+\\r\\n)*?SERVER: Linux/([\\w._-]+) Motorola/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.1 200 OKCONTENT-ENCODING: gzip", + "Motorola/", + "SERVER: Linux/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\nDATE: .*\\r\\nCONTENT-TYPE: httpd/unix-directory\\r\\nCONTENT-LENGTH: 0\\r\\nALLOW: GET, POST, HEAD, OPTIONS\\r\\nSERVER: Linux/([\\w._-]+) Motorola/([\\w._-]+)\\r\\n\\r\\n$", + "regex_literal_tokens": [ + "CONTENT-TYPE: httpd/unix-directoryCONTENT-LENGTH: 0ALLOW: GET, POST, HEAD, OPTIONSSERVER: Linux/", + "HTTP/1.1 200 OKDATE:", + "Motorola/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 302 Moved Temporarily\\r\\nPragma: no-cache\\r\\nLocation: https://[\\w._-]+/\\r\\n.*Redirect Notification.*

Please click here to go to the correct location\\.", + "regex_literal_tokens": [ + "/\">here to go to the correct location.", + "

Please click Redirect Notification", + "HTTP/1.0 302 Moved TemporarilyPragma: no-cacheLocation: https://" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:2.6/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\n.*XBMC\\r\\n\\t\\t.*", + "regex_literal_tokens": [ + "", + "XBMC", + "HTTP/1.1 200 OK" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.[01] 200 OK\\r\\n.*XBMC\\s*.*", + "regex_literal_tokens": [ + "200 OK", + "" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\nContent-Length: 134\\r\\nExpires: .*\\r\\nContent-Type: text/html\\r\\nDate: .*\\r\\n\\r\\n\\n\\nXBMC Web Media Manager \\n\\n\\n\\n$", + "regex_literal_tokens": [ + "XBMC Web Media Manager ", + "Content-Type: text/htmlDate:", + "HTTP/1.1 200 OKContent-Length: 134Expires:" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 401 Unauthorized\\r\\nContent-Length: 0\\r\\n(?:[^\\r\\n]+\\r\\n)*?WWW-Authenticate: Basic realm=XBMC\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.1 401 UnauthorizedContent-Length: 0", + "WWW-Authenticate: Basic realm=XBMC" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nContent-Length: \\d+\\r\\nExpires: .*\\r\\nLast-Modified: .*\\r\\nContent-Type: text/html\\r\\nAccept-Ranges: bytes\\r\\nDate: .*\\r\\n\\r\\n.*XBMC \\x7c Web interface", + "regex_literal_tokens": [ + "", + "XBMC | Web interface", + "Content-Type: text/htmlAccept-Ranges: bytesDate:", + "Expires:", + "HTTP/1.0 200 OKContent-Length:", + "Last-Modified:" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: CMSHTTPD/([\\w._-]+) z_VM/([\\w._-]+) ([^\\r\\n]+)\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.0 200 OK", + "Server: CMSHTTPD/", + "z_VM/" + ], + "cpe": [ + "cpe:/o:ibm:z%2fvm:$2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: HyNetOS/([\\w._-]+)\\r\\n.*(CS\\d+) SNMP/Web Adapter", + "regex_literal_tokens": [ + "CS", + "HTTP/1.1 200 OK", + "SNMP/Web Adapter", + "Server: HyNetOS/" + ], + "cpe": [ + "cpe:/o:hyperstone:hynetos:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: ShellHTTPD/([\\w._-]+)\\r\\n.*Dachstein LEAF Firewall", + "regex_literal_tokens": [ + "Dachstein LEAF Firewall", + "HTTP/1.0 200 OK", + "Server: ShellHTTPD/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:2.2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 505 HTTP Version not supported\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: Good\\.iWare WebDAV Server for iPhone\\r\\n.*If you have any questions, please contact support@goodreader\\.net", + "regex_literal_tokens": [ + "HTTP/1.1 505 HTTP Version not supported", + "If you have any questions, please contact support@goodreader.net", + "Server: Good.iWare WebDAV Server for iPhone" + ], + "cpe": [ + "cpe:/o:apple:iphone_os/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 505 HTTP Version not supported\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: GoodReader for iPad\\r\\n.*If you have any questions, please contact support@goodreader\\.net", + "regex_literal_tokens": [ + "HTTP/1.1 505 HTTP Version not supported", + "If you have any questions, please contact support@goodreader.net", + "Server: GoodReader for iPad" + ], + "cpe": [ + "cpe:/o:apple:iphone_os/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nConnection: close\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: AvatronHTTP \\(com\\.avatron\\.AirSharingHD,([\\w._-]+)\\)\\r\\n\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.0 200 OKConnection: close", + "Server: AvatronHTTP (com.avatron.AirSharingHD," + ], + "cpe": [ + "cpe:/h:apple:ipad/", + "cpe:/h:apple:iphone/", + "cpe:/o:apple:iphone_os/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: zVWS ([\\w._-]+) Velocity Software, Inc\\. on z/VM (V\\d+R[\\d.]+)\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.0 200 OK", + "Server: zVWS", + "Velocity Software, Inc. on z/VM V" + ], + "cpe": [ + "cpe:/o:ibm:z%2fvm:$2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nServer: Barrelfish\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.0 200 OKServer: Barrelfish" + ], + "cpe": [ + "cpe:/o:barrelfish:barrelfish/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 403 Forbidden\\r\\nDate: .*\\r\\nServer: Helix Mobile Server/([\\w._-]+) \\(win-x86_64-vc10\\)\\r\\n", + "regex_literal_tokens": [ + "(win-x86_64-vc10)", + "HTTP/1.0 403 ForbiddenDate:", + "Server: Helix Mobile Server/" + ], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nContent-Length: 0\\r\\n\\r\\n$", + "regex_literal_tokens": [ + "HTTP/1.0 200 OKContent-Length: 0" + ], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 505 HTTP Version Not Supported\\r\\n.*VMware View", + "regex_literal_tokens": [ + "VMware View", + "HTTP/1.1 505 HTTP Version Not Supported" + ], + "cpe": [ + "cpe:/o:vmware:esx/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\nCONTENT-ENCODING: gzip\\r\\nEXPIRES: .*\\r\\nCONTENT-LENGTH: \\d+\\r\\nLAST-MODIFIED: .*\\r\\nDATE: .*\\r\\nCONTENT-TYPE: text/html; charset=UTF-8\\r\\nCACHE-CONTROL: max-age=0, no-cache, public\\r\\nSERVER: Linux/([\\w._-]+) Motorola/([\\w._-]+) DAV/2\\r\\n", + "regex_literal_tokens": [ + "CONTENT-LENGTH:", + "CONTENT-TYPE: text/html; charset=UTF-8CACHE-CONTROL: max-age=0, no-cache, publicSERVER: Linux/", + "DATE:", + "DAV/2", + "HTTP/1.1 200 OKCONTENT-ENCODING: gzipEXPIRES:", + "LAST-MODIFIED:", + "Motorola/" + ], + "cpe": [ + "cpe:/o:google:android/", + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.[01] \\d\\d\\d [^\\r\\n]*\\r\\nCONTENT-TYPE: text/html\\r.*\\nServer: IBM_CICS_Transaction_Server/([\\w._-]+)\\(zOS\\)\\r\\n", + "regex_literal_tokens": [ + "(zOS)", + "CONTENT-TYPE: text/html", + "HTTP/1.", + "Server: IBM_CICS_Transaction_Server/" + ], + "cpe": [ + "cpe:/o:ibm:z%2fos/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nConnection: close\\r\\nContent-Encoding: gzip\\r\\nContent-Type: text/html; charset=iso-8859-1\\r\\n\\r\\n\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x02\\x03\\xa5\\x93Mo", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/h:hp:procurve_switch_1800/", + "cpe:/o:hp:procurve_switch_software/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\nConnection: close\\r\\nContent-Type: text/html; charset=utf-8\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: webcam 7\\r\\n\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.1 200 OKConnection: closeContent-Type: text/html; charset=utf-8", + "Server: webcam 7" + ], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 401 Unauthorized\\nWWW-Authenticate: Basic realm='unRAID SMU'\\n$", + "regex_literal_tokens": [ + "HTTP/1.1 401 UnauthorizedWWW-Authenticate: Basic realm='unRAID SMU'" + ], + "cpe": [ + "cpe:/o:lime_technology:unraid_server:4/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\nConnection: Close\\r\\nPragma: no-cache\\r\\nCache-Control: private, max-age=0\\r\\nDate: .*\\r\\nExpires: -1\\r\\nContent-Type: text/html\\r\\nTransfer-Encoding: chunked\\r\\nRefresh: 60; URL=\\r\\n\\r\\n[0-9a-f]+\\r\\n([\\w._-]+) unRAID Server", + "regex_literal_tokens": [ + "", + "Expires: -1Content-Type: text/htmlTransfer-Encoding: chunkedRefresh: 60; URL=", + "HTTP/1.1 200 OKConnection: ClosePragma: no-cacheCache-Control: private, max-age=0Date:", + "unRAID Server" + ], + "cpe": [ + "cpe:/o:lime_technology:unraid_server:4/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Etag: ([\\w._ -]+)\\r\\n.*\\xef\\xbb\\xbfAirDroid", + "regex_literal_tokens": [ + "AirDroid", + "Etag:", + "HTTP/1.1 200 OK" + ], + "cpe": [ + "cpe:/a:airdroid:airdroid:$1/", + "cpe:/o:google:android/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Etag: ([\\w._ -]+)\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: AirDroid-g\\r\\n", + "regex_literal_tokens": [ + "Etag:", + "HTTP/1.1 200 OK", + "Server: AirDroid-g" + ], + "cpe": [ + "cpe:/a:airdroid:airdroid:$1/", + "cpe:/o:google:android/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: AirDroid ([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.1 200 OK", + "Server: AirDroid" + ], + "cpe": [ + "cpe:/a:airdroid:airdroid:$1/", + "cpe:/o:google:android/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 500 Server Error\\r\\nContent-Length: 0\\r\\nServer: HBHTTP POGOBASIC - ([\\w._-]+) - Linux\\r\\n", + "regex_literal_tokens": [ + "- Linux", + "HTTP/1.1 500 Server ErrorContent-Length: 0Server: HBHTTP POGOBASIC -" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 404 Not Found\\r\\nContent-Length: 0\\r\\nServer: HBHTTP POGOBASIC - ([\\w._-]+) - Linux\\r\\n", + "regex_literal_tokens": [ + "- Linux", + "HTTP/1.1 404 Not FoundContent-Length: 0Server: HBHTTP POGOBASIC -" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nServer: Mini web server ([\\w._-]+) ZTE corp 2005\\.\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.0 200 OKServer: Mini web server", + "ZTE corp 2005." + ], + "cpe": [ + "cpe:/h:zte:zxv10_w300/", + "cpe:/o:montavista:linux_kernel:2.4.17/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\nConnection: keep-alive\\r\\nContent-Length: \\d+\\r\\nContent-Type: text/html; charset=utf-8\\r\\n\\r\\nPlayBook WebInspector", + "regex_literal_tokens": [ + "Content-Type: text/html; charset=utf-8PlayBook WebInspector", + "HTTP/1.1 200 OKConnection: keep-aliveContent-Length:" + ], + "cpe": [ + "cpe:/h:rim:blackberry_playbook_tablet/", + "cpe:/o:rim:blackberry_playbook_os:2.0/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: TMeter\\r\\n.*([\\w._-]+) Unicode\\r\\n\\tIn capture\\r\\n\\t([^<]*)\\r\\n", + "regex_literal_tokens": [ + "", + "", + "HTTP/1.1 200 OK", + "Server: TMeter", + "UnicodeIn capture" + ], + "cpe": [ + "cpe:/a:trafficreg:tmeter:$1/", + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\nContent type: text/xml; charset=utf-8\\r\\n.*TMeter\\r\\n\\tCopyright \\(c\\) \\d\\d\\d\\d Trafficreg Software\\r\\n\\t([\\d.]+) Unicode\\r\\n", + "regex_literal_tokens": [ + "TMeterCopyright (c)", + "HTTP/1.1 200 OKContent type: text/xml; charset=utf-8", + "Trafficreg Software", + "Unicode" + ], + "cpe": [ + "cpe:/a:trafficreg:tmeter:$1/", + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\nServer: Integrity\\nContent-type: text/html\\n\\n\\n\\n\\nWelcome to INTEGRITY", + "regex_literal_tokens": [ + "HTTP/1.0 200 OKServer: IntegrityContent-type: text/htmlWelcome to INTEGRITY" + ], + "cpe": [ + "cpe:/o:hay_systems:hsl_2.75g_femtocell/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nDate: .*\\r\\nServer: BQTWWW/([\\w._-]+) \\(RSX\\) \\(RSX-11M-PLUS V([\\w._-]+)\\)\\r\\n", + "regex_literal_tokens": [ + "(RSX) (RSX-11M-PLUS V", + "HTTP/1.0 200 OKDate:", + "Server: BQTWWW/" + ], + "cpe": [ + "cpe:/o:dec:rsx_11m_plus:$2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.[01] 200 OK\\r\\nContent-Type: text/xml;charset=utf-8\\r\\nContent-Length: \\d+\\r\\nConnection: close\\r\\nX-Plex-Protocol: 1\\.0\\r\\nCache-Control: no-cache(?:\\r\\nDate: .*)?\\r\\n\\r\\n<\\?xml version=\\\"1\\.0\\\" encoding=\\\"UTF-8\\\"\\?>\\n]*friendlyName=\\\"([^\"]*)\\\" [^>]*platform=\\\"Linux\\\" platformVersion=\\\"(((?:2\\.)?\\d\\.\\d+)[^\"]+)\\\" [^>]*version=\\\"([^\"]+)", + "regex_literal_tokens": [ + "200 OKContent-Type: text/xml;charset=utf-8Content-Length:", + "\\n]*friendlyName=\\\"([^\"]*)\\\" [^>]*platform=\\\"Linux\\\" platformVersion=\\\"(((?:2\\.)?\\d\\.\\d+)[^\"]+)\\\"", + "regex_literal_tokens": [ + "200 OKContent-Type: text/xml;charset=utf-8Content-Length:", + "\\n]*friendlyName=\\\"([^\"]*)\\\" [^>]*platform=\\\"Linux\\\" platformVersion=\\\"(((?:2\\.)?\\d\\.\\d+)[^\"]+)\\\" [^>]*version=\\\"([^\"]+)", + "regex_literal_tokens": [ + "200 OKX-Plex-Protocol: 1.0Content-Type: text/xml;charset=utf-8Content-Length:", + "\\n]*friendlyName=\\\"([^\"]*)\\\" [^>]*platform=\\\"Linux\\\" platformVersion=\\\"(((?:2\\.)?\\d\\.\\d+)[^\"]+)\\\"", + "regex_literal_tokens": [ + "200 OKX-Plex-Protocol: 1.0Content-Type: text/xml;charset=utf-8Content-Length:", + "\\n\\n\\n\\nLogin", + "regex_literal_tokens": [ + "Connection: closeContent-Type: text/htmlLogin", + "HTTP/1.1 401 Authorization RequiredDate:", + "Server: Expires: 0Set-Cookie: SESSION=; path=/;Expires: 0Vary: Accept-EncodingContent-Length:" + ], + "cpe": [ + "cpe:/o:arubanetworks:arubaos/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.[01] 302 Hotspot redirect\\r\\nCache-Control: no-cache\\r\\nConnection: close\\r\\nContent-Length: \\d+\\r\\nContent-Type: text/html\\r\\nDate: .*\\r\\nExpires: 0\\r\\nLocation: .*\\r\\n\\r\\n", + "regex_literal_tokens": [ + "302 Hotspot redirectCache-Control: no-cacheConnection: closeContent-Length:", + "Content-Type: text/htmlDate:", + "Expires: 0Location:", + "HTTP/1." + ], + "cpe": [ + "cpe:/a:mikrotik:hotspot/", + "cpe:/o:mikrotik:routeros/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nCache-control: no-cache\\r\\nConnection: Close\\r\\n\\r\\n(?:\\r\\n)?\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n", + "regex_literal_tokens": [ + "", + "", + "HTTP/1.0 200 OKCache-control: no-cacheConnection: Close" + ], + "cpe": [ + "cpe:/o:kcodes:netusb/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 302 Moved Temporarily\\r\\nContent-Type: application/x-gzip\\r\\nLocation: https://idrac(?::\\d+)?/start\\.html\\r\\nDate: .* GMT\\r\\nETag: \\w{3} \\w{3} \\d\\d \\d\\d:\\d\\d:\\d\\d \\d\\d\\d\\d ([-A-Z]+)\\r\\n", + "regex_literal_tokens": [ + "/start.htmlDate:", + "GMTETag:", + "HTTP/1.0 302 Moved TemporarilyContent-Type: application/x-gzipLocation: https://idrac" + ], + "cpe": [ + "cpe:/o:dell:idrac8_firmware/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\nContent-Type: text/html\\r\\nContent-Length: \\d+\\r\\nCache-Control: private, no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0\\r\\nExpires: 0\\r\\nPragma: no-cache\\r\\n\\r\\nZentriOS Web App", + "regex_literal_tokens": [ + "Cache-Control: private, no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0Expires: 0Pragma: no-cacheZentriOS Web App", + "HTTP/1.1 200 OKContent-Type: text/htmlContent-Length:" + ], + "cpe": [ + "cpe:/o:zentri:zentrios/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\n(?:Connection: close\\r\\n)?Server: fec/1\\.0 \\(Funkwerk BOSS\\)\\r\\n", + "regex_literal_tokens": [ + "Connection: close", + "HTTP/1.0 200 OK", + "Server: fec/1.0 (Funkwerk BOSS)" + ], + "cpe": [ + "cpe:/o:funkwerk:boss/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\n(?:Connection: close\\r\\n)?Server: boss/1\\.0 \\(BOSS\\)\\r\\n", + "regex_literal_tokens": [ + "Connection: close", + "HTTP/1.0 200 OK", + "Server: boss/1.0 (BOSS)" + ], + "cpe": [ + "cpe:/o:funkwerk:boss/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 404 Not Found\\r\\nServer: Linux, HTTP/1\\.1, MyNet(N\\d+) Ver ([\\d.]+)\\r\\nDate:", + "regex_literal_tokens": [ + "Date:", + "HTTP/1.1 404 Not FoundServer: Linux, HTTP/1.1, MyNetN", + "Ver" + ], + "cpe": [ + "cpe:/h:wdc:my_net_$1/", + "cpe:/o:wdc:my_net_firmware:$2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 200 OK\\r\\nDate: .*\\r\\nConnection: close\\r\\nContent-Type: text/html\\r\\nX-Frame-Options: DENY\\r\\nContent-Length: \\d+\\r\\n\\r\\n\\n\\n\\n\\n \\n \\n\\n\\n", + "regex_literal_tokens": [ + " ", + "Connection: closeContent-Type: text/htmlX-Frame-Options: DENYContent-Length:", + "HTTP/1.1 200 OKDate:" + ], + "cpe": [ + "cpe:/o:vmware:esxi/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 200 OK\\r\\nServer: httpd/2\\.0\\r\\nx-frame-options: SAMEORIGIN\\r\\nx-xss-protection: 1; mode=block\\r\\nDate: .*\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\n\\r\\n\\n\\n", + "regex_literal_tokens": [ + "Content-Type: text/htmlConnection: close", + "HTTP/1.0 200 OKServer: httpd/2.0x-frame-options: SAMEORIGINx-xss-protection: 1; mode=blockDate:" + ], + "cpe": [ + "cpe:/o:asus:wrt_firmware/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 \\d\\d\\d .*\\r\\nDate: .*\\r\\nServer: WatchGuard\\r\\n", + "regex_literal_tokens": [ + "Date:", + "HTTP/1.1", + "Server: WatchGuard" + ], + "cpe": [ + "cpe:/o:watchguard:fireware/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.0 \\d\\d\\d (?s:.*?)Server: ExtremeWare/([\\d.]+)\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:extremenetworks:extremeware_xos:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.[01] \\d\\d\\d (?s:.*?)Server: Linux/(([\\d.]+?)(?:\\.x)?) UPnP/([\\d.]+) Avtech/([\\d.]+)\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:linux:linux_kernel:$2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 \\d\\d\\d .*\\r\\nServer: MQX HTTPSRV/[\\d.]+ - Freescale Embedded Web Server v([\\d.]+)\\r\\n", + "regex_literal_tokens": [ + "- Freescale Embedded Web Server v", + "HTTP/1.1", + "Server: MQX HTTPSRV/" + ], + "cpe": [ + "cpe:/o:freescale:mqx/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.1 \\d\\d\\d .*\\nServer: MQX HTTP - Freescale Embedded Web Server\\n", + "regex_literal_tokens": [ + "HTTP/1.1", + "Server: MQX HTTP - Freescale Embedded Web Server" + ], + "cpe": [ + "cpe:/o:freescale:mqx/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http-proxy", + "regex": "^HTTP/1\\.1 407 Proxy Authentication Required\\r\\nProxy-Authenticate: NTLM\\r\\nProxy-Authenticate: BASIC realm=\\\"DOMBUD\\\"\\r\\nCache-Control: no-cache\\r\\nPragma: no-cache\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.1 407 Proxy Authentication RequiredProxy-Authenticate: NTLMProxy-Authenticate: BASIC realm=\"DOMBUD\"Cache-Control: no-cachePragma: no-cache" + ], + "cpe": [ + "cpe:/o:bluecoat:cacheos/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http-proxy", + "regex": "^HTTP/1\\.0 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: PAW Server ([\\w._-]+-android) \\(Brazil/2\\.0\\)\\r\\n", + "regex_literal_tokens": [ + "-android (Brazil/2.0)", + "HTTP/1.0 200 OK", + "Server: PAW Server" + ], + "cpe": [ + "cpe:/o:google:android/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http-proxy", + "regex": "^HTTP/1\\.0 \\d\\d\\d .*\\r\\nMime-Version: 1\\.0\\r\\nDate: .*\\r\\nVia: 1\\.0 ([\\w.-]+):\\d+ \\(Cisco-WSA/([\\w._-]+)\\)\\r\\n", + "regex_literal_tokens": [ + "(Cisco-WSA/", + "HTTP/1.0", + "Mime-Version: 1.0Date:", + "Via: 1.0" + ], + "cpe": [ + "cpe:/o:cisco:asyncos:$2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http-proxy", + "regex": "^HTTP/1\\.0 403 Forbidden\\r\\nConnection: close\\r\\nContent-Length: 51\\r\\nContent-type: text/html\\r\\n\\r\\nAccess denied: authentication configuration missing", + "regex_literal_tokens": [ + "HTTP/1.0 403 ForbiddenConnection: closeContent-Length: 51Content-type: text/htmlAccess denied: authentication configuration missing" + ], + "cpe": [ + "cpe:/o:smoothwall:smoothwall/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "smtp", + "regex": "^220 ([\\w._-]+)\\r\\n500 5\\.5\\.1 Unrecognized command\\r\\n", + "regex_literal_tokens": [ + "220", + "500 5.5.1 Unrecognized command" + ], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.[01] 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: Linux-amd64-([\\w._-]+), UPnP/([\\d.]+), PMS/(.*?)\\r\\n", + "regex_literal_tokens": [ + ", PMS/", + ", UPnP/", + "200 OK", + "HTTP/1.", + "Server: Linux-amd64-" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.[01] 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: Linux-([\\w_.-]+), UPnP/([\\d.]+), PMS/(.*?)\\r\\n", + "regex_literal_tokens": [ + ", PMS/", + ", UPnP/", + "200 OK", + "HTTP/1.", + "Server: Linux-" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.[01] 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: Windows_XP-([\\w_.-]+), UPnP/([\\d.]+), PMS/(.*?)\\r\\n", + "regex_literal_tokens": [ + ", PMS/", + ", UPnP/", + "200 OK", + "HTTP/1.", + "Server: Windows_XP-" + ], + "cpe": [ + "cpe:/o:microsoft:windows_xp:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.[01] 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: Windows_Vista-x86-([\\w._-]+), UPnP/([\\d.]+), PMS/(.*?)\\r\\n", + "regex_literal_tokens": [ + ", PMS/", + ", UPnP/", + "200 OK", + "HTTP/1.", + "Server: Windows_Vista-x86-" + ], + "cpe": [ + "cpe:/o:microsoft:windows_vista:$1::x32/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.[01] 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: Windows_Vista-x86_64-([\\w._-]+), UPnP/([\\d.]+), PMS/(.*?)\\r\\n", + "regex_literal_tokens": [ + ", PMS/", + ", UPnP/", + "200 OK", + "HTTP/1.", + "Server: Windows_Vista-x86_64-" + ], + "cpe": [ + "cpe:/o:microsoft:windows_vista:$1::x64/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.[01] 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: Windows_7-x86-([\\w._-]+), UPnP/([\\d.]+), PMS/(.*?)\\r\\n", + "regex_literal_tokens": [ + ", PMS/", + ", UPnP/", + "200 OK", + "HTTP/1.", + "Server: Windows_7-x86-" + ], + "cpe": [ + "cpe:/o:microsoft:windows_7:$1::x32/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.[01] 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: Windows_7-x86_64-([\\w._-]+), UPnP/([\\d.]+), PMS/(.*?)\\r\\n", + "regex_literal_tokens": [ + ", PMS/", + ", UPnP/", + "200 OK", + "HTTP/1.", + "Server: Windows_7-x86_64-" + ], + "cpe": [ + "cpe:/o:microsoft:windows_7:$1::x64/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.0 200 (?s:.*?)Server: Linux/([\\w_.-]+), UPnP/([\\w_.-]+), Free UPnP Entertainment Service/ReadyNAS\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.0 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: Linux/([\\w_.-]+), UPnP/([\\w_.-]+), Free UPnP Entertainment Service/([^\\r\\n]+)\\r\\n", + "regex_literal_tokens": [ + ", Free UPnP Entertainment Service/", + ", UPnP/", + "HTTP/1.0 200 OK", + "Server: Linux/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.0 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?Server: FreeBSD/([\\w_.-]+), UPnP/([\\w_.-]+), Free UPnP Entertainment Service/([^\\r\\n]+)\\r\\n", + "regex_literal_tokens": [ + ", Free UPnP Entertainment Service/", + ", UPnP/", + "HTTP/1.0 200 OK", + "Server: FreeBSD/" + ], + "cpe": [ + "cpe:/o:freebsd:freebsd:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 400 Bad Request\\r\\nSERVER: ipOS/([\\d.]+) UPnP/([\\d.]+) ipGENADevice/([\\d.]+)\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.1 400 Bad RequestSERVER: ipOS/", + "UPnP/", + "ipGENADevice/" + ], + "cpe": [ + "cpe:/o:ubicom:ipos:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.0 \\d\\d\\d .*\\r\\nSERVER: ipos/([\\w._-]+) +UPnP/([\\d.]+) (?:ADSL2\\+ (?:Modem )?Router )?(T[DL]-\\w+)/([\\w._/-]+)\\r\\n", + "regex_literal_tokens": [ + "ADSL2+", + "HTTP/1.0", + "Modem", + "Router", + "SERVER: ipos/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:ubicom:ipos:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.0 \\d\\d\\d .*\\r\\nSERVER: ipos/([\\w._-]+) +UPnP/([\\d.]+) (RNX-\\w+)/([\\w._/-]+)\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.0", + "RNX-", + "SERVER: ipos/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:ubicom:ipos:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.0 \\d\\d\\d .*\\r\\nSERVER: ipos/([\\w._-]+) UPnP/([\\d.]+) Archer[ _]([^/]+)/([\\w._/-]+)\\r\\n", + "regex_literal_tokens": [ + "Archer", + "HTTP/1.0", + "SERVER: ipos/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:ubicom:ipos:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.[01] \\d\\d\\d .*\\r\\nSERVER: Linux/([\\w._+-]+), UPnP/([\\d.]+), Portable SDK for UPnP devices/([\\w._~-]+)\\r\\n", + "regex_literal_tokens": [ + ", Portable SDK for UPnP devices/", + ", UPnP/", + "HTTP/1.", + "SERVER: Linux/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.[01] \\d\\d\\d .*\\r\\nSERVER: Linux, UPnP/([\\d.]+), Portable SDK for UPnP devices/([\\w._~-]+)\\r\\n", + "regex_literal_tokens": [ + ", Portable SDK for UPnP devices/", + "HTTP/1.", + "SERVER: Linux, UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.[01] \\d\\d\\d .*\\r\\nSERVER: Linux/([\\w._+-]+) UPnP/([\\d.]+) DLNADOC/([\\d.]+) Portable SDK for UPnP devices/([\\w._~-]+)\\r\\n", + "regex_literal_tokens": [ + "DLNADOC/", + "HTTP/1.", + "Portable SDK for UPnP devices/", + "SERVER: Linux/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 \\d\\d\\d (?s:.*?)Server: Linux/([\\w._+-]+) DLNADOC/([\\d.]+) UPnP/([\\d.]+) MiniDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.[01] \\d\\d\\d (?s:.*?)SERVER: Linux/([-+\\w_.]+), UPnP/([\\d.]+), Intel SDK for UPnP devices ?/([\\w._~-]+)\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.[01] \\d\\d\\d (?s:.*?)SERVER: Linux/([-+\\w_.]+) UPnP/([\\d.]+) DLNADOC/([\\w._-]+) Intel_SDK_for_UPnP_devices/([\\w._~-]+)\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.0 \\d\\d\\d .*\\r\\nDate: .*\\r\\nConnection: close\\r\\nServer: Microsoft-Windows-NT/(\\d[-.\\w]+) UPnP/(\\d[-.\\w]+) UPnP-Device-Host/(\\d[-.\\w]+)\\r\\n", + "regex_literal_tokens": [ + "Connection: closeServer: Microsoft-Windows-NT/", + "Date:", + "HTTP/1.0", + "UPnP-Device-Host/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:microsoft:windows_nt:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 200 .*\\r\\nSERVER: Linux/((2\\.[46]\\.\\d+|\\d\\.\\d+)\\S*), UPnP/([\\d.]+), MediaTomb/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + ", MediaTomb/", + ", UPnP/", + "HTTP/1.1 200", + "SERVER: Linux/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?SERVER: FreeBSD/([\\w._-]+), UPnP/([\\d.]+), MediaTomb/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + ", MediaTomb/", + ", UPnP/", + "HTTP/1.1 200 OK", + "SERVER: FreeBSD/" + ], + "cpe": [ + "cpe:/o:freebsd:freebsd:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?SERVER: OpenBSD/([\\w._-]+), UPnP/([\\d.]+), MediaTomb/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + ", MediaTomb/", + ", UPnP/", + "HTTP/1.1 200 OK", + "SERVER: OpenBSD/" + ], + "cpe": [ + "cpe:/o:openbsd:openbsd:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?SERVER: SunOS/([\\w._-]+), UPnP/([\\d.]+), MediaTomb/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + ", MediaTomb/", + ", UPnP/", + "HTTP/1.1 200 OK", + "SERVER: SunOS/" + ], + "cpe": [ + "cpe:/o:sun:sunos:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 \\d\\d\\d (?s:.*?)Server: *Linux/([\\w._-]+), UPnP/([\\w._-]+), TwonkyVision UPnP SDK/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/a:packetvideo:twonky/", + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 \\d\\d\\d (?s:.*?)Server: *Linux/2\\.x\\.x, UPnP/([\\w._-]+), pvConnect UPnP SDK/([\\w._-]+), Twonky UPnP SDK/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/a:packetvideo:twonky/", + "cpe:/o:linux:linux_kernel:2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 \\d\\d\\d .*Server: *Linux/([\\w._-]+), UPnP/([\\w._-]+), pvConnect UPnP SDK/([\\w._-]+)\\r\\n.*(?:TwonkyMedia|TwonkyMedia server media browser|TwonkyVision Configuration)", + "regex_literal_tokens": [ + ", UPnP/", + ", pvConnect UPnP SDK/", + "", + "", + "HTTP/1.1", + "Linux/", + "Server:", + "TwonkyMedia", + "TwonkyMedia server media browser", + "TwonkyVision Configuration" + ], + "cpe": [ + "cpe:/a:packetvideo:twonky/", + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 \\d\\d\\d (?s:.*?)Server: *Linux/([\\w._-]+), UPnP/([\\w._-]+), pvConnect UPnP SDK/([\\w._-]+)\\r\\n.*<title>MediaServer Restriced Access", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/a:packetvideo:twonky/", + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 \\d\\d\\d (?s:.*?)Server: *Linux/2\\.x\\.x, UPnP/([\\w._-]+), pvConnect UPnP SDK/([\\w._-]+), TwonkyMedia UPnP SDK/([\\w._-]+)\\r\\n\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/a:packetvideo:twonky/", + "cpe:/o:linux:linux_kernel:2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 401 Unauthorised\\r\\n(?:[^\\r\\n]+\\r\\n)*?WWW-Authenticate: Digest realm=\\\"([\\w._-]+)\\\", nonce=\\\"\\w+\\\", algorigthm=MD5, qop=\\\"auth\\\" \\n.*Server: *Linux/2\\.x\\.x, UPnP/([\\d.]+), pvConnect UPnP SDK/([\\w._-]+), Twonky UPnP SDK/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "\", algorigthm=MD5, qop=\"auth\"", + "\", nonce=\"", + ", Twonky UPnP SDK/", + ", pvConnect UPnP SDK/", + "HTTP/1.1 401 Unauthorised", + "Linux/2.x.x, UPnP/", + "Server:", + "WWW-Authenticate: Digest realm=\"" + ], + "cpe": [ + "cpe:/a:packetvideo:twonky/", + "cpe:/o:linux:linux_kernel:2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 \\d\\d\\d (?s:.*?)Server: *Linux/2\\.x\\.x, UPnP/([\\w._-]+), pvConnect UPnP SDK/([\\w._-]+)\\r\\n\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/a:packetvideo:twonky/", + "cpe:/o:linux:linux_kernel:2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 \\d\\d\\d (?s:.*?)Server: Windows NT/[\\w._-]+, UPnP/([\\w._-]+), pvConnect UPnP SDK/([\\w._-]+), TwonkyMedia UPnP SDK/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/a:packetvideo:twonky/", + "cpe:/o:microsoft:windows_nt/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 401 Unauthorised\\r\\n(?:[^\\r\\n]+\\r\\n)*?WWW-Authenticate: Basic realm=\\\"([\\w._-]+)\\\"\\n.*Server: *Linux/2\\.x\\.x, UPnP/([\\w._-]+), pvConnect UPnP SDK/([\\w._-]+), Twonky UPnP SDK/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + ", Twonky UPnP SDK/", + ", pvConnect UPnP SDK/", + "HTTP/1.1 401 Unauthorised", + "Linux/2.x.x, UPnP/", + "Server:", + "WWW-Authenticate: Basic realm=\"" + ], + "cpe": [ + "cpe:/a:packetvideo:twonky/", + "cpe:/o:linux:linux_kernel:2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.0 \\d\\d\\d (?s:.*?)SERVER: Linux/([\\w._-]+) UPnP/([\\w._-]+) myigd/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.0 \\d\\d\\d (?s:.*?)SERVER: Linux/([\\w._-]+), UPnP/([\\w._-]+), Everest/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 404 Bad Request\\r\\nCONTENT-LENGTH: 0\\r\\nCONTENT-TYPE: text/html\\r\\n\\r\\n$", + "regex_literal_tokens": [ + "HTTP/1.1 404 Bad RequestCONTENT-LENGTH: 0CONTENT-TYPE: text/html" + ], + "cpe": [ + "cpe:/o:supermicro:intelligent_platform_management_firmware/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 200 OK\\r\\n.*SERVER: EPSON_Linux UPnP/([\\d.]+) Epson UPnP SDK/([\\d.]+)\\r\\n.*(?:Epson )?(Stylus (?:Office |Photo )?\\w+)", + "regex_literal_tokens": [ + "", + "", + "Epson", + "Epson UPnP SDK/", + "HTTP/1.1 200 OK", + "Office", + "Photo", + "SERVER: EPSON_Linux UPnP/", + "Stylus" + ], + "cpe": [ + "cpe:/h:epson:$3/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?SERVER: EPSON_Linux UPnP/([\\d.]+) Epson UPnP SDK/([\\d.]+)\\r\\n.*<meta name=\\\"Author\\\" content=\\\"SEIKO EPSON\\\">.*path\\.indexOf\\(\\\"/PRESENTATION/HTML/TOP/INDEX\\.HTML\\\", 0\\);", + "regex_literal_tokens": [ + "<meta name=\"Author\" content=\"SEIKO EPSON\">", + "Epson UPnP SDK/", + "HTTP/1.1 200 OK", + "SERVER: EPSON_Linux UPnP/", + "path.indexOf(\"/PRESENTATION/HTML/TOP/INDEX.HTML\", 0);" + ], + "cpe": [ + "cpe:/h:epson:stylus_nx230/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 200 OK\\r\\n(?:[^\\r\\n]+\\r\\n)*?SERVER: EPSON_Linux UPnP/([\\d.]+) Epson UPnP SDK/([\\d.]+)\\r\\n\\r\\n<!DOCTYPE HTML PUBLIC \\\"-//W3C//DTD HTML 4\\.01//EN \\\"\\r\\n\\\"http://www\\.w3\\.org/TR/html4/strict\\.dtd\\\">\\r\\n<html>\\r\\n<head>\\r\\n<meta http-equiv=\\\"Content-Type\\\" content=\\\"text/html; charset=utf-8\\\">\\r\\n<meta name=\\\"Author\\\" content=\\\"SEIKO EPSON\\\">", + "regex_literal_tokens": [ + "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN \"\"http://www.w3.org/TR/html4/strict.dtd\"><html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><meta name=\"Author\" content=\"SEIKO EPSON\">", + "Epson UPnP SDK/", + "HTTP/1.1 200 OK", + "SERVER: EPSON_Linux UPnP/" + ], + "cpe": [ + "cpe:/h:epson:wf-2540/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 200 OK\\r\\nDate: .*\\r\\nServer: Linux/2\\.x UPnP/([\\w._-]+) Avtech/([\\w._-]+)\\r\\nConnection: close\\r\\nLast-Modified: .*..\\xbe\\x40..\\xbe..\\x03\\r\\n", + "regex_literal_tokens": [ + "Avtech/", + "Connection: closeLast-Modified:", + "HTTP/1.1 200 OKDate:", + "Server: Linux/2.x UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 200 OK\\r\\nDate: .*\\r\\nServer: Linux/2\\.x UPnP/([\\w._-]+) Avtech/([\\w._-]+)\\r\\nConnection: close\\r\\nLast-Modified: .*\\xb2\\xe8\\xbe\\x1c\\xb2\\xe8\\xbe\\x38\\x62\\x03\\r\\n", + "regex_literal_tokens": [ + "Avtech/", + "Connection: closeLast-Modified:", + "HTTP/1.1 200 OKDate:", + "Server: Linux/2.x UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 404 Not Found\\r\\nConnection: close\\r\\nDate: .* GMT\\r\\nServer: RTOS/([\\w._-]+) UPnP/([\\w._]+) ([\\w._-]+)\\s*/([\\w._-]+)\\r\\nX-AV-Server-Info: av=5\\.0; cn=\\\"Sony Corporation\\\"; mn=\\\"BRAVIA ", + "regex_literal_tokens": [ + "GMTServer: RTOS/", + "HTTP/1.1 404 Not FoundConnection: closeDate:", + "UPnP/", + "X-AV-Server-Info: av=5.0; cn=\"Sony Corporation\"; mn=\"BRAVIA" + ], + "cpe": [ + "cpe:/h:sony:bravia_$3:$4/", + "cpe:/o:greenhills:rtos:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 400 Bad Request\\r\\nContent-Type: \\r\\nContent-Length: 0\\r\\nConnection: close\\r\\n\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.1 400 Bad RequestContent-Type: Content-Length: 0Connection: close" + ], + "cpe": [ + "cpe:/o:samsung:bada:1.2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 \\d\\d\\d .*\\r\\nSERVER: Linux/([\\w._-]+) UPnP/([\\w._-]+) DLNADOC/([\\w._-]+) INTEL_NMPR/([\\w._-]+) LGE_DLNA_SDK/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "DLNADOC/", + "HTTP/1.1", + "INTEL_NMPR/", + "LGE_DLNA_SDK/", + "SERVER: Linux/", + "UPnP/" + ], + "cpe": [ + "cpe:/h:lg:lw5700/", + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.0 404 Not Found\\r\\nSERVER: PACKAGE_VERSION HUAWEI, UPnP, HUAWEI SDK for UPnP devices/ \\r\\nCONTENT-LENGTH: 48\\r\\nCONTENT-TYPE: text/html\\r\\n\\r\\n<html><body><h1>404 Not Found</h1></body></html>$", + "regex_literal_tokens": [ + "HTTP/1.0 404 Not FoundSERVER: PACKAGE_VERSION HUAWEI, UPnP, HUAWEI SDK for UPnP devices/ CONTENT-LENGTH: 48CONTENT-TYPE: text/html<html><body><h1>404 Not Found</h1></body></html>" + ], + "cpe": [ + "cpe:/o:huawei:vxworks/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 400 Bad Request\\r\\nContent-Type: text/html; charset=\\\"utf-8\\\"\\r\\nServer: Linux/([\\w._-]+) CyberHTTP/([\\d.]+)\\r\\nContent-Length: 0\\r\\nDate: .*\\r\\n\\r\\n", + "regex_literal_tokens": [ + "Content-Length: 0Date:", + "CyberHTTP/", + "HTTP/1.1 400 Bad RequestContent-Type: text/html; charset=\"utf-8\"Server: Linux/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 404 Not Found\\r\\nDATE: .*\\r\\nConnection: Keep-Alive\\r\\nServer: LINUX/([\\w._-]+) UPnP/([\\d.]+) BRCM400-UPnP/([\\d.]+)\\r\\n", + "regex_literal_tokens": [ + "BRCM400-UPnP/", + "Connection: Keep-AliveServer: LINUX/", + "HTTP/1.1 404 Not FoundDATE:", + "UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 404 Not Found\\r\\nSERVER: Linux/([\\w._-]+) UPnP/([\\w._-]+) Motorola-DLNA-Stack-DLNADOC/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.1 404 Not FoundSERVER: Linux/", + "Motorola-DLNA-Stack-DLNADOC/", + "UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.0 404 Not Found\\r\\nSERVER: ipos/([\\w._-]+) UPnP/([\\w._-]+) (RNX-[\\w._-]+)/1\\.0\\r\\n", + "regex_literal_tokens": [ + "/1.0", + "HTTP/1.0 404 Not FoundSERVER: ipos/", + "RNX-", + "UPnP/" + ], + "cpe": [ + "cpe:/o:ubicom:ipos:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.0 404 Not Found\\r\\nSERVER: ipos/([\\w._-]+) UPnP/([\\w._-]+) (TL-[\\w._-]+)/1\\.0\\r\\n", + "regex_literal_tokens": [ + "/1.0", + "HTTP/1.0 404 Not FoundSERVER: ipos/", + "TL-", + "UPnP/" + ], + "cpe": [ + "cpe:/o:ubicom:ipos:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 200 OK\\r\\nContent-Type: text/html\\r\\nConnection: close\\r\\nContent-Length: \\d+\\r\\nServer: Linux (([234]\\.[\\d.]+)[\\w._-]+) DLNADOC/([\\w._-]+) UPnP/([\\w._-]+) ReadyDLNA/([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "DLNADOC/", + "HTTP/1.1 200 OKContent-Type: text/htmlConnection: closeContent-Length:", + "ReadyDLNA/", + "Server: Linux", + "UPnP/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 404 Not Found\\r\\nSERVER: Linux/((2\\.[46]\\.\\d+|\\d\\.\\d+)\\S*) UPnP/([\\d.]+) DiXiM/([\\d.]+)\\r\\n", + "regex_literal_tokens": [ + "DiXiM/", + "HTTP/1.1 404 Not FoundSERVER: Linux/", + "UPnP/" + ], + "cpe": [ + "cpe:/a:digion:dixim_media_player:$4/", + "cpe:/o:linux:linux_kernel:$2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 200 OK\\r.*\\nS(?:erver|ERVER): (Windows_[^-]+)_(R\\d+)-([^-]+)-[\\d.]+, UPnP/([\\d.]+), UMS/([\\d.]+)\\r\\n", + "regex_literal_tokens": [ + ", UMS/", + ", UPnP/", + ": Windows_", + "ERVER", + "HTTP/1.1 200 OK", + "erver" + ], + "cpe": [ + "cpe:/o:microsoft:$1:$2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 200 OK\\r.*\\nS(?:erver|ERVER): (Windows_[^-]+)-([^-]+)-[\\d.]+, UPnP/([\\d.]+), UMS/([\\d.]+)\\r\\n", + "regex_literal_tokens": [ + ", UMS/", + ", UPnP/", + ": Windows_", + "ERVER", + "HTTP/1.1 200 OK", + "erver" + ], + "cpe": [ + "cpe:/o:microsoft:$1/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "upnp", + "regex": "^HTTP/1\\.1 200 OK\\r.*\\nS(?:erver|ERVER): Mac_OS_X-([^-]+)-(\\d.[\\w._-]+), UPnP/([\\d.]+), UMS/([\\d.]+)\\r\\n", + "regex_literal_tokens": [ + ", UMS/", + ", UPnP/", + ": Mac_OS_X-", + "ERVER", + "HTTP/1.1 200 OK", + "erver" + ], + "cpe": [ + "cpe:/a:universal_media_server:universal_media_server:$4/", + "cpe:/o:apple:mac_os_x:$2/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "uucp", + "regex": "^login: Password: $", + "regex_literal_tokens": [ + "login: Password:" + ], + "cpe": [ + "cpe:/o:debian:debian_linux/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:http_options", + "service": "http", + "regex": "^HTTP/1\\.0 501 Not Implemented\\r\\nConnection: close\\r\\nServer: Android Webcam Server v([\\w._-]+)\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.0 501 Not ImplementedConnection: closeServer: Android Webcam Server v" + ], + "cpe": [ + "cpe:/o:google:android/" + ] + }, + { + "probe_id": "tcp:http_options", + "service": "http", + "regex": "^HTTP/1\\.1 501 Not Implemented\\r\\nDate: .* GMT\\r\\nConnection: close\\r\\nContent-Type: text/html\\r\\nContent-Length: 54\\r\\n\\r\\n<HTML><BODY><H1>501 Not Implemented</H1></BODY></HTML>$", + "regex_literal_tokens": [ + "GMTConnection: closeContent-Type: text/htmlContent-Length: 54<HTML><BODY><H1>501 Not Implemented</H1></BODY></HTML>", + "HTTP/1.1 501 Not ImplementedDate:" + ], + "cpe": [ + "cpe:/o:vmware:esxi:4.1/" + ] + }, + { + "probe_id": "tcp:http_options", + "service": "http", + "regex": "^HTTP/1\\.0 404 Not Found\\r\\nContent-Type: text/html\\r\\nContent-Length: \\d+\\r\\nServer: \\r\\n\\r\\n<html><head><title>404 Not Found\\n

404 Not Found

\\n/:
This item has not been found
\\n
[\\w._-]+:\\d+
\\n\\n$", + "regex_literal_tokens": [ + "/\">", + "", + "HTTP/1.0 404 Not FoundContent-Type: text/htmlContent-Length:", + "Server: 404 Not Found

404 Not Found

/:
This item has not been found

506 - IO Error

$", + "regex_literal_tokens": [ + "HTTP/1.1 506 Content-Type: text/htmlServer: JavaWeb/0

506 - IO Error

" + ], + "cpe": [ + "cpe:/a:airdroid:airdroid/", + "cpe:/o:google:android/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:dns_version_bind_req", + "service": "login", + "regex": "^\\x00\\r\\nSorry, shell is locked\\.\\r\\n$", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:brocade:fabric_os/" + ] + }, + { + "probe_id": "tcp:dns_version_bind_req", + "service": "login", + "regex": "^\\x01rlogind: Host name for your address \\([\\d.]+\\) unknown\\.\\r\\n", + "regex_literal_tokens": [ + ") unknown.", + "rlogind: Host name for your address (" + ], + "cpe": [ + "cpe:/o:apple:a_ux/" + ] + }, + { + "probe_id": "tcp:dns_version_bind_req", + "service": "upnp", + "regex": "^HTTP/1\\.0 414 Request-URI Too Long\\r\\nServer: Linux/([\\w._-]+) UPnP/([\\w._-]+) fbxigdd/([\\w._-]+)\\r\\nConnection: close\\r\\n\\r\\n$", + "regex_literal_tokens": [ + "Connection: close", + "HTTP/1.0 414 Request-URI Too LongServer: Linux/", + "UPnP/", + "fbxigdd/" + ], + "cpe": [ + "cpe:/o:linux:linux_kernel:$1/" + ] + }, + { + "probe_id": "tcp:help", + "service": "ftp", + "regex": "^220 .*\\r\\n214-CesarFTP server ([\\w.]+) supports the following commands:\\r\\n", + "regex_literal_tokens": [ + "214-CesarFTP server", + "220", + "supports the following commands:" + ], + "cpe": [ + "cpe:/a:aclogic:cesarftpd:$1/", + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:help", + "service": "ftp", + "regex": "^220-Rival Group FTP Server\\r\\n220-Unauthorized access prohibited\\r\\n220 All activity is logged\\.\\r\\n214-CesarFTP server ([\\w._-]+) supports the following commands:\\r\\n214-ABOR ACCT ALLO APPE CDUP CWD DELE HELP LIST\\r\\n214-MDTM MKD MODE NLST NOOP PASS PASV PORT PWD \\r\\n214-QUIT REIN REST RETR RMD RNFR RNTO SITE SMNT\\r\\n214-STAT STOR STOU STRU SYST TYPE\\r\\n214-\\r\\n214-CesarFTP server [\\w._-]+ supports specific commands\\r\\n214-invoked with the SITE command:\\r\\n214-\\r\\n214-SITE MSG\\r\\n214-\\r\\n214 \\r\\n", + "regex_literal_tokens": [ + "220-Rival Group FTP Server220-Unauthorized access prohibited220 All activity is logged.214-CesarFTP server", + "supports specific commands214-invoked with the SITE command:214-214-SITE MSG214-214", + "supports the following commands:214-ABOR ACCT ALLO APPE CDUP CWD DELE HELP LIST214-MDTM MKD MODE NLST NOOP PASS PASV PORT PWD 214-QUIT REIN REST RETR RMD RNFR RNTO SITE SMNT214-STAT STOR STOU STRU SYST TYPE214-214-CesarFTP server" + ], + "cpe": [ + "cpe:/a:aclogic:cesarftpd:$1/", + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:help", + "service": "smtp", + "regex": "^220 ([-\\w_.]+) ESMTP (?:[^(]+? )?\\(Ubuntu\\)\\r\\n502 5\\.5\\.2 Error: command not recognized\\r\\n", + "regex_literal_tokens": [ + "(Ubuntu)502 5.5.2 Error: command not recognized", + "220", + "ESMTP" + ], + "cpe": [ + "cpe:/o:canonical:ubuntu_linux/" + ] + }, + { + "probe_id": "tcp:help", + "service": "smtp", + "regex": "^220 ([-\\w_.]+) ESMTP\\r\\n214-Gentoo Linux qmail-([-\\w.]+)\\r\\n214 qmail home page: http://pobox\\.com/~djb/qmail\\.html\\r\\n", + "regex_literal_tokens": [ + "214 qmail home page: http://pobox.com/~djb/qmail.html", + "220", + "ESMTP214-Gentoo Linux qmail-" + ], + "cpe": [ + "cpe:/a:djb:qmail/", + "cpe:/o:gentoo:linux/" + ] + }, + { + "probe_id": "tcp:help", + "service": "smtp", + "regex": "^220 .* ESMTP\\r\\n214-Gentoo Linux qmail-([-\\w.]+)\\r\\n214 qmail home page: http://pobox\\.com/~djb/qmail\\.html\\r\\n", + "regex_literal_tokens": [ + "214 qmail home page: http://pobox.com/~djb/qmail.html", + "220", + "ESMTP214-Gentoo Linux qmail-" + ], + "cpe": [ + "cpe:/a:djb:qmail/", + "cpe:/o:gentoo:linux/" + ] + }, + { + "probe_id": "tcp:help", + "service": "vulnserver", + "regex": "^Welcome to Vulnerable Server! Enter HELP for help\\.\\nValid Commands:\\nHELP\\nSTATS \\[stat_value\\]\\nRTIME \\[rtime_value\\]\\nLTIME \\[ltime_value\\]\\nSRUN \\[srun_value\\]\\nTRUN \\[trun_value\\]\\nGMON \\[gmon_value\\]\\nGDOG \\[gdog_value\\]\\nKSTET \\[kstet_value\\]\\nGTER \\[gter_value\\]\\nHTER \\[hter_value\\]\\nLTER \\[lter_value\\]\\nKSTAN \\[lstan_value\\]\\nEXIT\\n$", + "regex_literal_tokens": [ + "Welcome to Vulnerable Server! Enter HELP for help.Valid Commands:HELPSTATS [stat_value]RTIME [rtime_value]LTIME [ltime_value]SRUN [srun_value]TRUN [trun_value]GMON [gmon_value]GDOG [gdog_value]KSTET [kstet_value]GTER [gter_value]HTER [hter_value]LTER [lter_value]KSTAN [lstan_value]EXIT" + ], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "ssl", + "regex": "^\\x16\\x03\\x00\\x00\\*\\x02\\x00\\x00&\\x03.*vCenterServer_([\\w._-]+)", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:vmware:esxi:$1/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x00........\\x00\\x00\\x00\\x00........\\x80\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*\\tMacintosh\\x05\\x06AFPX03\\x06AFP2\\.2\\x0eAFPVersion 2\\.1\\x0eAFPVersion 2\\.0\\x0eAFPVersion 1\\.1.\\tDHCAST128", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x:10.1/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x00........\\x00\\x00\\x00\\x00........\\x83\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*\\tMacintosh\\x06\\x06AFP3\\.1\\x06AFPX03\\x06AFP2\\.2\\x0eAFPVersion 2\\.1\\x0eAFPVersion 2\\.0\\x0eAFPVersion 1\\.1.\\tDHCAST128.*[\\x04\\x05]([\\w.-]+)\\x00", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x:10.2/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x00........\\x00\\x00\\x00\\x00........\\x83\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*\\tMacintosh\\x06\\x06AFP3\\.1\\x06AFPX03\\x06AFP2\\.2\\x0eAFPVersion 2\\.1\\x0eAFPVersion 2\\.0\\x0eAFPVersion 1\\.1.\\tDHCAST128", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x:10.2/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x00........\\x00\\x00\\x00\\x00........\\x83\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*\\tMacintosh\\x03\\x06AFP3\\.1\\x06AFPX03\\x06AFP2\\.2.\\x06Recon1\\rClient Krb v20\\x00.*[\\x04\\x05]([\\w.-]+)\\x01.afpserver/([\\w.@-]+)\\x00", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x:10.2/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x00........\\x00\\x00\\x00\\x00........\\x83\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*\\tMacintosh\\x03\\x06AFP3\\.1\\x06AFPX03\\x06AFP2\\.2.\\tDHCAST128.*[\\x04\\x05]([\\w.-]+)\\x01.afpserver/([\\w.@-]+)\\x00", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x:10.3/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x00........\\x00\\x00\\x00\\x00........\\x83\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*\\tMacintosh\\x03\\x06AFP3\\.1\\x06AFPX03\\x06AFP2\\.2.\\tDHCAST128.*[\\x04\\x05]([\\w.-]+)\\x00", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x:10.3/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x00........\\x00\\x00\\x00\\x00........\\x83\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*\\tMacintosh\\x03\\x06AFP3\\.1\\x06AFPX03\\x06AFP2\\.2.\\tDHCAST128", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x:10.3/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x00........\\x00\\x00\\x00\\x00........\\x8f\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*\\tMacintosh\\x04\\x06AFP3\\.2\\x06AFP3\\.1\\x06AFPX03\\x06AFP2\\.2.\\x06Recon1\\rClient Krb v2\\x0fNo User Authent\\x00.*[\\x04\\x05]([\\w.-]+)\\x01.afpserver/([-\\w_.@]+)\\x00", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x_server:10.5/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x00........\\x00\\x00\\x00\\x00........\\x8f\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*\\tMacintosh.\\x06AFP3\\.3\\x06AFP3\\.2\\x06AFP3\\.1\\x06AFPX03\\x06AFP2\\.2.\\tDHCAST128.*[\\x04\\x05]([\\w.-]+)\\x01.afpserver", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x:10.5/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x00........\\x00\\x00\\x00\\x00........\\x8f\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*\\tMacintosh.\\x06AFP3\\.3\\x06AFP3\\.2\\x06AFP3\\.1\\x06AFPX03\\x06AFP2\\.2.\\tDHCAST128", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x:10.5/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x00........\\x00\\x00\\x00\\x00........\\x8f\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*?(i?Mac(?:mini|Pro|Book(?:Air|Pro)?)?\\d+,\\d+)\\x04\\x06AFP3\\.3\\x06AFP3\\.2\\x06AFP3\\.1\\x06AFPX03.\\tDHCAST128.*[\\x04\\x05]([\\w.-]+)\\x01.afpserver", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x:10.5/", + "cpe:/o:apple:mac_os_x:10.6/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x00........\\x00\\x00\\x00\\x00........\\x8f\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*?(i?Mac(?:mini|Pro|Book(?:Air|Pro)?)?\\d+,\\d+)\\x04\\x06AFP3\\.3\\x06AFP3\\.2\\x06AFP3\\.1\\x06AFPX03.\\tDHCAST128.*[\\x04\\x05]([\\w.-]+)\\x00\\x00", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x:10.6/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x80........\\x00\\x00\\x00\\x00........\\x8f\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*?(i?Mac(?:mini|Pro|Book(?:Air|Pro)?)?\\d+,\\d+)\\x04\\x06AFP3\\.3\\x06AFP3\\.2\\x06AFP3\\.1\\x06AFPX03.\\tDHCAST128.*[\\x04\\x05]([\\w.-]+)\\x01.afpserver", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x:10.5/", + "cpe:/o:apple:mac_os_x:10.6/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x80........\\x00\\x00\\x00\\x00........\\x8f\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*\\tMacintosh.\\x06AFP3\\.3\\x06AFP3\\.2\\x06AFP3\\.1\\x06AFPX03\\x06AFP2\\.2.\\tDHCAST128.*[\\x04\\x05]([\\w.-]+)\\x01.afpserver", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x:10.5/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x00........\\x00\\x00\\x00\\x00........\\x9f\\xf3.([^\\x00\\x01]+)[\\x00\\x01].*?(i?Mac(?:mini|Pro|Book(?:Air|Pro)?)?\\d+,\\d+)\\x05\\x06AFP3\\.4\\x06AFP3\\.3\\x06AFP3\\.2\\x06AFP3\\.1\\x06AFPX03", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/a:apple:afp_server/", + "cpe:/o:apple:mac_os_x:10.10/", + "cpe:/o:apple:mac_os_x:10.11/", + "cpe:/o:apple:mac_os_x:10.9/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x00........\\x00\\x00\\x00\\x00........\\x9f\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*?(i?Mac(?:mini|Pro|Book(?:Air|Pro)?)?\\d+,\\d+)\\x05\\x06AFP3\\.4\\x06AFP3\\.3\\x06AFP3\\.2\\x06AFP3\\.1\\x06AFPX03\\x06\\tDHCAST128\\x04DHX2\\x06Recon1\\rClient Krb v2\\x03GSS\\x0fNo User Authent.*\\x1b\\$not_defined_in_RFC4178@please_ignore$", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x:10.6/", + "cpe:/o:apple:mac_os_x:10.7/", + "cpe:/o:apple:mac_os_x:10.8/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x00........\\x00\\x00\\x00\\x00........\\x9f\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*?(i?Mac(?:mini|Pro|Book(?:Air|Pro)?)?\\d+,\\d+)\\x05\\x06AFP3\\.4\\x06AFP3\\.3\\x06AFP3\\.2\\x06AFP3\\.1\\x06AFPX03\\x05\\tDHCAST128\\x04DHX2\\x06Recon1\\rClient Krb v2\\x03GSS.*\\x1b\\$not_defined_in_RFC4178@please_ignore", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x:10.6/", + "cpe:/o:apple:mac_os_x:10.7/", + "cpe:/o:apple:mac_os_x:10.8/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "afp", + "regex": "^\\x01\\x03\\x00\\x00........\\x00\\x00\\x00\\x00........\\x9f\\xfb.([^\\x00\\x01]+)[\\x00\\x01].*?(i?Mac(?:mini|Pro|Book(?:Air|Pro)?)?\\d+,\\d+)\\x05\\x06AFP3\\.4\\x06AFP3\\.3\\x06AFP3\\.2\\x06AFP3\\.1\\x06AFPX03\\x05\\tDHCAST128\\x04DHX2\\x06Recon1\\x03GSS\\x0fNo User Authent", + "regex_literal_tokens": [], + "cpe": [ + "cpe:/o:apple:mac_os_x:10.8/" + ] + }, + { + "probe_id": "tcp:tls_session", + "service": "pop3-proxy", + "regex": "^ERR concurrent connection limit in avast! exceeded\\(pass:\\d+, processes:([\\w._-]+)\\[\\d+\\]\\)\\r\\n", + "regex_literal_tokens": [ + ", processes:", + "ERR concurrent connection limit in avast! exceeded(pass:" + ], + "cpe": [ + "cpe:/a:avast:antivirus/", + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "^HTTP/1\\.[01] \\d\\d\\d[\\s\\S]*?\\r\\nServer: Microsoft-IIS/([-.\\w]+)\\r\\n", + "regex_literal_tokens": [ + "HTTP/1.", + "Server: Microsoft-IIS/" + ], + "cpe": [ + "cpe:/a:microsoft:internet_information_services:$1/", + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:http_get", + "service": "http", + "regex": "[\\s\\S]*?IIS Windows Server", + "regex_literal_tokens": [ + "IIS Windows Server" + ], + "cpe": [ + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:generic_lines", + "service": "ftp", + "regex": "^220 Microsoft FTP Service\\b", + "regex_literal_tokens": [ + "220 Microsoft FTP Service" + ], + "cpe": [ + "cpe:/a:microsoft:internet_information_services/", + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:help", + "service": "ftp", + "regex": "^220 Microsoft FTP Service\\b", + "regex_literal_tokens": [ + "220 Microsoft FTP Service" + ], + "cpe": [ + "cpe:/a:microsoft:internet_information_services/", + "cpe:/o:microsoft:windows/" + ] + }, + { + "probe_id": "tcp:null", + "service": "mysql", + "regex": "5\\.5\\.5-([0-9.]+)-MariaDB-0ubuntu[^\\s\\x00]*", + "regex_literal_tokens": [ + "5.5.5-", + "MariaDB", + "0ubuntu" + ], + "cpe": [ + "cpe:/a:mariadb:mariadb:$1/", + "cpe:/o:canonical:ubuntu_linux/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:null", + "service": "mysql", + "regex": "5\\.5\\.5-([0-9.]+)-MariaDB(?:-[^\\s\\x00]+)?", + "regex_literal_tokens": [ + "5.5.5-", + "MariaDB" + ], + "cpe": [ + "cpe:/a:mariadb:mariadb:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "mysql", + "regex": "([0-9]+\\.[0-9]+\\.[0-9]+)-0ubuntu[^\\s\\x00]*", + "regex_literal_tokens": [ + "0ubuntu" + ], + "cpe": [ + "cpe:/a:mysql:mysql:$1/", + "cpe:/o:canonical:ubuntu_linux/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:null", + "service": "mysql", + "regex": "MySQL Community Server.*?([0-9]+\\.[0-9]+\\.[0-9]+)", + "regex_literal_tokens": [ + "MySQL", + "Community", + "Server" + ], + "cpe": [ + "cpe:/a:mysql:mysql_community_server:$1/" + ] + }, + { + "probe_id": "tcp:null", + "service": "mysql", + "regex": "(?is)blocked because of many connection errors;.*mariadb-admin\\s+flush-hosts", + "regex_literal_tokens": [ + "blocked because of many connection errors", + "mariadb-admin", + "flush-hosts" + ], + "cpe": [ + "cpe:/a:mariadb:mariadb/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:null", + "service": "mysql", + "regex": "(?is)blocked because of many connection errors;.*mysqladmin\\s+flush-hosts", + "regex_literal_tokens": [ + "blocked because of many connection errors", + "mysqladmin", + "flush-hosts" + ], + "cpe": [ + "cpe:/a:mysql:mysql/", + "cpe:/o:linux:linux_kernel/" + ] + }, + { + "probe_id": "tcp:null", + "service": "mysql", + "regex": "([0-9]+\\.[0-9]+\\.[0-9]+)(?:-log)?", + "regex_literal_tokens": [ + "-log" + ], + "cpe": [ + "cpe:/a:mysql:mysql:$1/" + ] + } + ] +} \ No newline at end of file diff --git a/resources/nrev-service-probes.json b/resources/nrev-service-probes.json new file mode 100644 index 0000000..303a476 --- /dev/null +++ b/resources/nrev-service-probes.json @@ -0,0 +1,528 @@ +{ + "meta": { + "name": "Probe Payload Database", + "version": "1.0" + }, + "probes": [ + { + "id": "tcp:null", + "protocol": "tcp", + "name": "NULL", + "payload": "", + "payload_encoding": "raw", + "wait_ms": 6000, + "ports": [] + }, + { + "id": "tcp:generic_lines", + "protocol": "tcp", + "name": "GenericLines", + "payload": "\r\n\r\n", + "payload_encoding": "raw", + "wait_ms": null, + "ports": [ + 21, + 23, + 35, + 43, + 79, + 98, + 110, + 113, + 119, + 199, + 214, + 264, + 449, + 505, + 510, + 540, + 587, + 616, + 628, + 666, + 731, + 771, + 782, + 1000, + 1010, + 1040, + 1041, + 1042, + 1043, + 1080, + 1212, + 1220, + 1248, + 1302, + 1400, + 1432, + 1467, + 1501, + 1505, + 1666, + 1687, + 1688, + 2010, + 2024, + 2600, + 3000, + 3005, + 3128, + 3310, + 3333, + 3940, + 4155, + 5000, + 5400, + 5432, + 5555, + 5570, + 6112, + 6432, + 6667, + 6668, + 6669, + 6670, + 7144, + 7145, + 7200, + 7780, + 8000, + 8138, + 9000, + 9001, + 9002, + 9003, + 9801, + 11371, + 11965, + 13720, + 15000, + 15001, + 15002, + 18086, + 19150, + 26214, + 26470, + 30444, + 31416, + 34012, + 56667 + ] + }, + { + "id": "tcp:http_get", + "protocol": "tcp", + "name": "GetRequest", + "payload": "GET / HTTP/1.0\r\n\r\n", + "payload_encoding": "raw", + "wait_ms": null, + "ports": [ + 1, + 70, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 88, + 113, + 139, + 143, + 280, + 497, + 505, + 514, + 515, + 540, + 554, + 591, + 620, + 631, + 783, + 888, + 898, + 900, + 901, + 1026, + 1042, + 1080, + 1214, + 1220, + 1234, + 1314, + 1344, + 1503, + 1610, + 1611, + 1830, + 1900, + 2001, + 2002, + 2030, + 2064, + 2160, + 2306, + 2396, + 2525, + 2715, + 2869, + 3000, + 3002, + 3052, + 3128, + 3280, + 3372, + 3531, + 3689, + 3872, + 4000, + 4444, + 4567, + 4660, + 4711, + 5000, + 5060, + 5222, + 5269, + 5280, + 5427, + 5432, + 5800, + 5801, + 5802, + 5803, + 5900, + 5985, + 6103, + 6346, + 6544, + 6600, + 6699, + 6969, + 7002, + 7007, + 7070, + 7100, + 7402, + 7776, + 8000, + 8001, + 8002, + 8003, + 8004, + 8005, + 8006, + 8007, + 8008, + 8009, + 8010, + 8080, + 8081, + 8082, + 8083, + 8084, + 8085, + 8088, + 8118, + 8181, + 8530, + 8880, + 8881, + 8882, + 8883, + 8884, + 8885, + 8886, + 8887, + 8888, + 9000, + 9001, + 9030, + 9050, + 9080, + 9090, + 9999, + 10000, + 10001, + 10005, + 11371, + 13013, + 13666, + 13722, + 14534, + 15000, + 17988, + 18264, + 31337, + 40193, + 50000, + 55555 + ] + }, + { + "id": "tcp:https_get", + "protocol": "tcp", + "name": "HttpsGetRequest", + "payload": "GET $PATH HTTP/1.1\r\nHost: $HOST\r\nConnection: close\r\nAccept-Encoding: identity\r\n\r\n", + "payload_encoding": "raw", + "wait_ms": null, + "ports": [ + 443, + 1443, + 2443, + 4433, + 4434, + 4443, + 4444, + 5443, + 7443, + 8009, + 8010, + 8181, + 8443, + 8883, + 9443, + 10443, + 11443, + 12443, + 30443, + 40443, + 44443, + 49592, + 50443, + 55443, + 60443 + ] + }, + { + "id": "tcp:http_options", + "protocol": "tcp", + "name": "HTTPOptions", + "payload": "OPTIONS / HTTP/1.0\r\n\r\n", + "payload_encoding": "raw", + "wait_ms": null, + "ports": [ + 80, + 81, + 82, + 83, + 84, + 85, + 631, + 641, + 2301, + 3128, + 5232, + 6000, + 8080, + 8888, + 9999, + 10000, + 10031, + 37435, + 49400 + ] + }, + { + "id": "udp:dns_version_bind_req", + "protocol": "udp", + "name": "DNSVersionBindReq", + "payload": "AAYBAAABAAAAAAAAB3ZlcnNpb24EYmluZAAAEAAD", + "payload_encoding": "base64", + "wait_ms": null, + "ports": [ + 53, + 1967, + 2967, + 26198 + ] + }, + { + "id": "tcp:dns_version_bind_req", + "protocol": "tcp", + "name": "DNSVersionBindReqTCP", + "payload": "AB4ABgEAAAEAAAAAAAAHdmVyc2lvbgRiaW5kAAAQAAM=", + "payload_encoding": "base64", + "wait_ms": null, + "ports": [ + 53, + 135, + 512, + 513, + 514, + 543, + 544, + 628, + 1029, + 2068, + 2105, + 2967, + 5000, + 5323, + 5520, + 5530, + 5555, + 5556, + 6543, + 7000, + 7008, + 13783 + ] + }, + { + "id": "tcp:help", + "protocol": "tcp", + "name": "Help", + "payload": "HELP\r\n", + "payload_encoding": "raw", + "wait_ms": 7500, + "ports": [ + 1, + 7, + 21, + 25, + 79, + 113, + 119, + 515, + 587, + 1111, + 1311, + 2401, + 2627, + 3000, + 3493, + 6560, + 6666, + 6667, + 6668, + 6669, + 6670, + 12345, + 14690, + 22490 + ] + }, + { + "id": "tcp:tls_session", + "protocol": "tcp", + "name": "SSLSessionReq", + "payload": "FgMAAFMBAABPAwA/R9f3uizu6rJgfvMA/YJ7udWWyHeb5sTbPD3bb+8QbgAAKAAWABMACgBmAAUABABlAGQAYwBiAGEAYAAVABIACQAUABEACAAGAAMBAA==", + "payload_encoding": "base64", + "wait_ms": null, + "ports": [ + 261, + 271, + 322, + 324, + 443, + 444, + 448, + 465, + 548, + 563, + 585, + 636, + 684, + 853, + 989, + 990, + 992, + 993, + 994, + 995, + 1241, + 1311, + 1443, + 2000, + 2221, + 2252, + 2376, + 2443, + 3443, + 4433, + 4443, + 4444, + 4911, + 5061, + 5349, + 5443, + 5550, + 5868, + 5986, + 6251, + 6380, + 6443, + 6679, + 6697, + 7000, + 7210, + 7272, + 7443, + 8009, + 8181, + 8194, + 8443, + 8531, + 8883, + 9001, + 9443, + 10443, + 14443, + 15002, + 16379, + 44443, + 60443 + ] + }, + { + "id": "tcp:tls_session", + "protocol": "tcp", + "name": "TLSSessionReq", + "payload": "FgMAAGkBAABlAwNVHKfkcmFuZG9tMXJhbmRvbTJyYW5kb20zcmFuZG9tNAAADAAvAAoAEwA5AAQA/wEAADAADQAsACoAAQADAAIGAQYDBgICAQIDAgIDAQMDAwIEAQQDBAIBAQEDAQIFAQUDBQI=", + "payload_encoding": "base64", + "wait_ms": null, + "ports": [ + 443, + 444, + 465, + 636, + 989, + 990, + 992, + 993, + 994, + 995, + 1241, + 1311, + 2252, + 3388, + 3389, + 4433, + 4444, + 5061, + 6679, + 6697, + 8443, + 8883, + 9001 + ] + }, + { + "id": "udp:quic", + "protocol": "udp", + "name": "QUIC", + "payload": "DYnBnBwq//zxUTk5OQA=", + "payload_encoding": "base64", + "wait_ms": null, + "ports": [ + 80, + 443 + ] + } + ] +} \ No newline at end of file diff --git a/resources/nrev-tls-oid-map.json b/resources/nrev-tls-oid-map.json new file mode 100644 index 0000000..4e590fc --- /dev/null +++ b/resources/nrev-tls-oid-map.json @@ -0,0 +1,29 @@ +{ + "sig": { + "1.2.840.113549.1.1.5": "sha1WithRSAEncryption", + "1.2.840.113549.1.1.11": "sha256WithRSAEncryption", + "1.2.840.113549.1.1.12": "sha384WithRSAEncryption", + "1.2.840.113549.1.1.13": "sha512WithRSAEncryption", + "1.2.840.113549.1.1.14": "sha224WithRSAEncryption", + "1.2.840.113549.1.1.4": "md5WithRSAEncryption", + "1.2.840.113549.1.1.10": "RSASSA-PSS", + "1.2.840.10045.4.1": "ecdsa-with-SHA1", + "1.2.840.10045.4.3.1": "ecdsa-with-SHA224", + "1.2.840.10045.4.3.2": "ecdsa-with-SHA256", + "1.2.840.10045.4.3.3": "ecdsa-with-SHA384", + "1.2.840.10045.4.3.4": "ecdsa-with-SHA512", + "1.3.101.112": "Ed25519", + "1.3.101.113": "Ed448", + "1.2.840.10040.4.3": "dsa-with-sha1" + }, + "pubkey": { + "1.2.840.113549.1.1.1": "rsaEncryption", + "1.2.840.10045.2.1": "id-ecPublicKey", + "1.3.101.112": "Ed25519", + "1.3.101.113": "Ed448", + "1.3.101.110": "X25519", + "1.3.101.111": "X448", + "1.2.840.10040.4.1": "id-dsa", + "1.2.840.113549.1.3.1": "dhKeyAgreement" + } +} diff --git a/resources/nrev-top-subdomains.json b/resources/nrev-top-subdomains.json new file mode 100644 index 0000000..7dfa24f --- /dev/null +++ b/resources/nrev-top-subdomains.json @@ -0,0 +1,1002 @@ +[ + "www", + "mail", + "ftp", + "localhost", + "webmail", + "smtp", + "webdisk", + "pop", + "cpanel", + "whm", + "ns1", + "ns2", + "autodiscover", + "autoconfig", + "ns", + "test", + "m", + "blog", + "dev", + "www2", + "ns3", + "pop3", + "forum", + "admin", + "mail2", + "vpn", + "mx", + "imap", + "old", + "new", + "mobile", + "mysql", + "beta", + "support", + "cp", + "secure", + "shop", + "demo", + "dns2", + "ns4", + "dns1", + "static", + "lists", + "web", + "www1", + "img", + "news", + "portal", + "server", + "wiki", + "api", + "media", + "images", + "www.blog", + "backup", + "dns", + "sql", + "intranet", + "www.forum", + "www.test", + "stats", + "host", + "video", + "mail1", + "mx1", + "www3", + "staging", + "www.m", + "sip", + "chat", + "search", + "crm", + "mx2", + "ads", + "ipv4", + "remote", + "email", + "my", + "wap", + "svn", + "store", + "cms", + "download", + "proxy", + "www.dev", + "mssql", + "apps", + "dns3", + "exchange", + "mail3", + "forums", + "ns5", + "db", + "office", + "live", + "files", + "info", + "owa", + "monitor", + "helpdesk", + "panel", + "sms", + "newsletter", + "ftp2", + "web1", + "web2", + "upload", + "home", + "bbs", + "login", + "app", + "en", + "blogs", + "it", + "cdn", + "stage", + "gw", + "dns4", + "www.demo", + "ssl", + "cn", + "smtp2", + "vps", + "ns6", + "relay", + "online", + "service", + "test2", + "radio", + "ntp", + "library", + "help", + "www4", + "members", + "tv", + "www.shop", + "extranet", + "hosting", + "ldap", + "services", + "webdisk.blog", + "s1", + "i", + "survey", + "s", + "www.mail", + "www.new", + "premium", + "data", + "docs", + "ping", + "ad", + "legacy", + "router", + "de", + "meet", + "cs", + "av", + "sftp", + "server1", + "stat", + "moodle", + "facebook", + "test1", + "photo", + "partner", + "nagios", + "mrtg", + "s2", + "mailadmin", + "dev2", + "ts", + "autoconfig.blog", + "autodiscover.blog", + "games", + "jobs", + "image", + "host2", + "gateway", + "preview", + "www.support", + "im", + "ssh", + "correo", + "control", + "ns0", + "vpn2", + "cloud", + "archive", + "citrix", + "webdisk.m", + "voip", + "connect", + "game", + "smtp1", + "access", + "lib", + "www5", + "gallery", + "redmine", + "es", + "irc", + "stream", + "qa", + "dl", + "billing", + "construtor", + "lyncdiscover", + "painel", + "fr", + "projects", + "a", + "pgsql", + "mail4", + "tools", + "iphone", + "server2", + "dbadmin", + "manage", + "jabber", + "music", + "webmail2", + "www.beta", + "mailer", + "phpmyadmin", + "t", + "reports", + "rss", + "pgadmin", + "images2", + "mx3", + "www.webmail", + "ws", + "content", + "sv", + "web3", + "community", + "poczta", + "www.mobile", + "ftp1", + "dialin", + "us", + "sp", + "panelstats", + "vip", + "cacti", + "s3", + "alpha", + "videos", + "ns7", + "promo", + "testing", + "sharepoint", + "marketing", + "sitedefender", + "member", + "webdisk.dev", + "emkt", + "training", + "edu", + "autoconfig.m", + "git", + "autodiscover.m", + "catalog", + "webdisk.test", + "job", + "ww2", + "www.news", + "sandbox", + "elearning", + "fb", + "webmail.cp", + "downloads", + "speedtest", + "design", + "staff", + "master", + "panelstatsmail", + "v2", + "db1", + "mailserver", + "builder.cp", + "travel", + "mirror", + "ca", + "sso", + "tickets", + "alumni", + "sitebuilder", + "www.admin", + "auth", + "jira", + "ns8", + "partners", + "ml", + "list", + "images1", + "club", + "business", + "update", + "fw", + "devel", + "local", + "wp", + "streaming", + "zeus", + "images3", + "adm", + "img2", + "gate", + "pay", + "file", + "seo", + "status", + "share", + "maps", + "zimbra", + "webdisk.forum", + "trac", + "oa", + "sales", + "post", + "events", + "project", + "xml", + "wordpress", + "images4", + "main", + "english", + "e", + "img1", + "db2", + "time", + "redirect", + "go", + "bugs", + "direct", + "www6", + "social", + "www.old", + "development", + "calendar", + "www.forums", + "ru", + "www.wiki", + "monitoring", + "hermes", + "photos", + "bb", + "mx01", + "mail5", + "temp", + "map", + "ns10", + "tracker", + "sport", + "uk", + "hr", + "autodiscover.test", + "conference", + "free", + "autoconfig.test", + "client", + "vpn1", + "autodiscover.dev", + "b2b", + "autoconfig.dev", + "noc", + "webconf", + "ww", + "payment", + "firewall", + "intra", + "rt", + "v", + "clients", + "www.store", + "gis", + "m2", + "event", + "origin", + "site", + "domain", + "barracuda", + "link", + "ns11", + "internal", + "dc", + "smtp3", + "zabbix", + "mdm", + "assets", + "images6", + "www.ads", + "mars", + "mail01", + "pda", + "images5", + "c", + "ns01", + "tech", + "ms", + "images7", + "autoconfig.forum", + "public", + "css", + "autodiscover.forum", + "webservices", + "www.video", + "web4", + "orion", + "pm", + "fs", + "w3", + "student", + "www.chat", + "domains", + "book", + "lab", + "o1.email", + "server3", + "img3", + "kb", + "faq", + "health", + "in", + "board", + "vod", + "www.my", + "cache", + "atlas", + "php", + "images8", + "wwww", + "voip750101.pg6.sip", + "cas", + "origin-www", + "cisco", + "banner", + "mercury", + "w", + "directory", + "mailhost", + "test3", + "shopping", + "webdisk.demo", + "ip", + "market", + "pbx", + "careers", + "auto", + "idp", + "ticket", + "js", + "ns9", + "outlook", + "foto", + "www.en", + "pro", + "mantis", + "spam", + "movie", + "s4", + "lync", + "jupiter", + "dev1", + "erp", + "register", + "adv", + "b", + "corp", + "sc", + "ns12", + "images0", + "enet1", + "mobil", + "lms", + "net", + "storage", + "ss", + "ns02", + "work", + "webcam", + "www7", + "report", + "admin2", + "p", + "nl", + "love", + "pt", + "manager", + "d", + "cc", + "android", + "linux", + "reseller", + "agent", + "web01", + "sslvpn", + "n", + "thumbs", + "links", + "mailing", + "hotel", + "pma", + "press", + "venus", + "finance", + "uesgh2x", + "nms", + "ds", + "joomla", + "doc", + "flash", + "research", + "dashboard", + "track", + "www.img", + "x", + "rs", + "edge", + "deliver", + "sync", + "oldmail", + "da", + "order", + "eng", + "testbrvps", + "user", + "radius", + "star", + "labs", + "top", + "srv1", + "mailers", + "mail6", + "pub", + "host3", + "reg", + "lb", + "log", + "books", + "phoenix", + "drupal", + "affiliate", + "www.wap", + "webdisk.support", + "www.secure", + "cvs", + "st", + "wksta1", + "saturn", + "logos", + "preprod", + "m1", + "backup2", + "opac", + "core", + "vc", + "mailgw", + "pluto", + "ar", + "software", + "jp", + "srv", + "newsite", + "www.members", + "openx", + "otrs", + "titan", + "soft", + "analytics", + "code", + "mp3", + "sports", + "stg", + "whois", + "apollo", + "web5", + "ftp3", + "www.download", + "mm", + "art", + "host1", + "www8", + "www.radio", + "demo2", + "click", + "smail", + "w2", + "feeds", + "g", + "education", + "affiliates", + "kvm", + "sites", + "mx4", + "autoconfig.demo", + "controlpanel", + "autodiscover.demo", + "tr", + "ebook", + "www.crm", + "hn", + "black", + "mcp", + "adserver", + "www.staging", + "static1", + "webservice", + "f", + "develop", + "sa", + "katalog", + "as", + "smart", + "pr", + "account", + "mon", + "munin", + "www.games", + "www.media", + "cam", + "school", + "r", + "mc", + "id", + "network", + "www.live", + "forms", + "math", + "mb", + "maintenance", + "pic", + "agk", + "phone", + "bt", + "sm", + "demo1", + "ns13", + "tw", + "ps", + "dev3", + "tracking", + "green", + "users", + "int", + "athena", + "www.static", + "www.info", + "security", + "mx02", + "prod", + "1", + "team", + "transfer", + "www.facebook", + "www10", + "v1", + "google", + "proxy2", + "feedback", + "vpgk", + "auction", + "view", + "biz", + "vpproxy", + "secure2", + "www.it", + "newmail", + "sh", + "mobi", + "wm", + "mailgate", + "dms", + "11192521404255", + "autoconfig.support", + "play", + "11192521403954", + "start", + "life", + "autodiscover.support", + "antispam", + "cm", + "booking", + "iris", + "www.portal", + "hq", + "gc._msdcs", + "neptune", + "terminal", + "vm", + "pool", + "gold", + "gaia", + "internet", + "sklep", + "ares", + "poseidon", + "relay2", + "up", + "resources", + "is", + "mall", + "traffic", + "webdisk.mail", + "www.api", + "join", + "smtp4", + "www9", + "w1", + "upl", + "ci", + "gw2", + "open", + "audio", + "fax", + "alfa", + "www.images", + "alex", + "spb", + "xxx", + "ac", + "edm", + "mailout", + "webtest", + "nfs01.jc", + "me", + "sun", + "virtual", + "spokes", + "ns14", + "webserver", + "mysql2", + "tour", + "igk", + "wifi", + "pre", + "abc", + "corporate", + "adfs", + "srv2", + "delta", + "loopback", + "magento", + "br", + "campus", + "law", + "global", + "s5", + "web6", + "orange", + "awstats", + "static2", + "learning", + "www.seo", + "china", + "gs", + "www.gallery", + "tmp", + "ezproxy", + "darwin", + "bi", + "best", + "mail02", + "studio", + "sd", + "signup", + "dir", + "server4", + "archives", + "golf", + "omega", + "vps2", + "sg", + "ns15", + "win", + "real", + "www.stats", + "c1", + "eshop", + "piwik", + "geo", + "mis", + "proxy1", + "web02", + "pascal", + "lb1", + "app1", + "mms", + "apple", + "confluence", + "sns", + "learn", + "classifieds", + "pics", + "gw1", + "www.cdn", + "rp", + "matrix", + "repository", + "updates", + "se", + "developer", + "meeting", + "twitter", + "artemis", + "au", + "cat", + "system", + "ce", + "ecommerce", + "sys", + "ra", + "orders", + "sugar", + "ir", + "wwwtest", + "bugzilla", + "listserv", + "www.tv", + "vote", + "webmaster", + "webdev", + "sam", + "www.de", + "vps1", + "contact", + "galleries", + "history", + "journal", + "hotels", + "www.newsletter", + "podcast", + "dating", + "sub", + "www.jobs", + "www.intranet", + "www.email", + "mt", + "science", + "counter", + "dns5", + "2", + "people", + "ww3", + "www.es", + "ntp1", + "vcenter", + "test5", + "radius1", + "ocs", + "power", + "pg", + "pl", + "magazine", + "sts", + "fms", + "customer", + "wsus", + "bill", + "www.hosting", + "vega", + "nat", + "sirius", + "lg", + "11285521401250", + "sb", + "hades", + "students", + "uat", + "conf", + "ap", + "uxr4", + "eu", + "moon", + "www.search", + "checksrv", + "hydra", + "usa", + "digital", + "wireless", + "banners", + "md", + "mysite", + "webmail1", + "windows", + "traveler", + "www.poczta", + "hrm", + "database", + "mysql1", + "inside", + "debian", + "pc", + "ask", + "backend", + "cz", + "mx0", + "mini", + "autodiscover.mail", + "rb", + "webdisk.shop", + "mba", + "www.help", + "www.sms", + "test4", + "dm", + "subscribe", + "sf", + "passport", + "red", + "video2", + "ag", + "autoconfig.mail", + "all.edge", + "registration", + "ns16", + "camera", + "myadmin", + "ns20", + "uxr3", + "mta", + "beauty", + "fw1", + "epaper", + "central", + "cert", + "backoffice", + "biblioteca", + "mob", + "about", + "space", + "movies", + "u", + "ms1", + "ec", + "forum2", + "server5", + "money", + "radius2", + "print", + "ns18", + "thunder", + "nas", + "ww1", + "webdisk.webmail", + "edit", + "www.music", + "planet", + "m3", + "vstagingnew", + "app2", + "repo", + "prueba", + "house", + "ntp2", + "dragon", + "pandora", + "stock", + "form", + "pp", + "www.sport", + "physics", + "food", + "groups", + "antivirus", + "profile", + "www.online", + "stream2", + "hp", + "d1", + "nhko1111", + "logs", + "eagle", + "v3", + "mail7", + "gamma", + "career", + "vpn3", + "ipad", + "dom", + "webdisk.store", + "iptv", + "www.promo", + "hd", + "mag", + "box", + "talk", + "hera", + "f1", + "www.katalog", + "syslog", + "fashion", + "t1", + "private", + "soporte", + "teste", + "scripts", + "welcome" +] diff --git a/resources/nrev-wellknown-ports.json b/resources/nrev-wellknown-ports.json new file mode 100644 index 0000000..c606f70 --- /dev/null +++ b/resources/nrev-wellknown-ports.json @@ -0,0 +1,687 @@ +[ + 1, + 2, + 3, + 5, + 7, + 9, + 11, + 13, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 25, + 27, + 29, + 31, + 33, + 37, + 38, + 39, + 41, + 42, + 43, + 44, + 45, + 46, + 48, + 49, + 50, + 52, + 53, + 54, + 55, + 56, + 58, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 76, + 78, + 79, + 80, + 82, + 83, + 84, + 85, + 86, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 256, + 257, + 259, + 260, + 261, + 262, + 263, + 264, + 265, + 266, + 267, + 268, + 269, + 271, + 280, + 281, + 282, + 283, + 284, + 286, + 287, + 308, + 309, + 310, + 311, + 312, + 313, + 314, + 315, + 316, + 317, + 318, + 319, + 320, + 321, + 322, + 323, + 324, + 333, + 344, + 345, + 346, + 347, + 348, + 349, + 350, + 351, + 352, + 353, + 354, + 355, + 356, + 357, + 358, + 359, + 360, + 361, + 362, + 363, + 364, + 365, + 366, + 367, + 368, + 369, + 370, + 371, + 372, + 373, + 374, + 375, + 376, + 377, + 378, + 379, + 380, + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394, + 395, + 396, + 397, + 398, + 399, + 400, + 401, + 402, + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410, + 411, + 412, + 413, + 414, + 415, + 416, + 417, + 418, + 419, + 420, + 421, + 422, + 423, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 440, + 441, + 442, + 443, + 444, + 445, + 446, + 447, + 448, + 449, + 450, + 451, + 452, + 453, + 454, + 455, + 456, + 457, + 458, + 459, + 460, + 461, + 462, + 463, + 464, + 465, + 466, + 467, + 468, + 469, + 470, + 471, + 472, + 473, + 474, + 475, + 476, + 477, + 478, + 479, + 480, + 481, + 482, + 483, + 484, + 485, + 486, + 487, + 488, + 489, + 490, + 491, + 492, + 493, + 494, + 495, + 496, + 497, + 498, + 499, + 500, + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 514, + 515, + 516, + 518, + 519, + 520, + 521, + 522, + 523, + 524, + 525, + 526, + 527, + 528, + 529, + 530, + 531, + 532, + 533, + 534, + 535, + 536, + 537, + 538, + 539, + 540, + 541, + 542, + 543, + 544, + 545, + 546, + 547, + 548, + 549, + 550, + 551, + 552, + 553, + 554, + 555, + 556, + 557, + 558, + 559, + 560, + 561, + 562, + 563, + 564, + 565, + 566, + 567, + 568, + 569, + 570, + 571, + 572, + 573, + 574, + 575, + 576, + 577, + 578, + 579, + 580, + 581, + 582, + 583, + 584, + 586, + 587, + 588, + 589, + 590, + 591, + 592, + 593, + 594, + 595, + 596, + 597, + 598, + 599, + 600, + 601, + 602, + 603, + 604, + 605, + 606, + 607, + 608, + 609, + 610, + 611, + 612, + 613, + 614, + 615, + 616, + 617, + 618, + 619, + 620, + 621, + 622, + 623, + 624, + 625, + 626, + 627, + 628, + 629, + 630, + 631, + 632, + 633, + 634, + 635, + 636, + 637, + 638, + 639, + 640, + 641, + 642, + 643, + 644, + 645, + 646, + 647, + 648, + 649, + 650, + 651, + 652, + 653, + 654, + 655, + 656, + 657, + 658, + 660, + 661, + 662, + 663, + 664, + 665, + 666, + 667, + 668, + 669, + 670, + 671, + 672, + 673, + 674, + 675, + 676, + 677, + 678, + 679, + 680, + 681, + 682, + 683, + 684, + 685, + 686, + 687, + 688, + 689, + 690, + 691, + 692, + 693, + 694, + 695, + 696, + 697, + 698, + 699, + 700, + 701, + 702, + 704, + 705, + 706, + 707, + 709, + 710, + 711, + 712, + 713, + 714, + 715, + 729, + 730, + 731, + 741, + 742, + 744, + 747, + 748, + 749, + 750, + 751, + 752, + 753, + 754, + 758, + 759, + 760, + 761, + 762, + 763, + 764, + 765, + 767, + 769, + 770, + 771, + 772, + 773, + 774, + 775, + 776, + 777, + 780, + 800, + 801, + 802, + 810, + 828, + 829, + 830, + 831, + 832, + 833, + 847, + 848, + 853, + 854, + 860, + 861, + 862, + 873, + 886, + 887, + 888, + 900, + 901, + 902, + 903, + 910, + 911, + 912, + 913, + 953, + 989, + 990, + 991, + 992, + 993, + 995, + 996, + 997, + 998, + 999, + 1000, + 1001, + 1010, + 1021, + 1022 +] diff --git a/src/app.rs b/src/app.rs deleted file mode 100644 index a9222c7..0000000 --- a/src/app.rs +++ /dev/null @@ -1,110 +0,0 @@ -use crate::sys; -use clap::{crate_description, crate_name, crate_version}; -use std::sync::{Mutex, OnceLock}; - -// APP information -pub const CRATE_BIN_NAME: &str = "nrev"; -pub const CRATE_UPDATE_DATE: &str = "2024-07-21"; -pub const CRATE_REPOSITORY: &str = "https://github.com/shellrow/nrev"; - -/// Global Mutex lock guard for quiet mode -pub static QUIET_MODE: OnceLock> = OnceLock::new(); - -/// Check if quiet mode is enabled -pub fn is_quiet_mode() -> bool { - match QUIET_MODE.get() { - Some(mutex) => match mutex.try_lock() { - Ok(guard) => *guard, - Err(_) => false, - }, - None => false, - } -} - -pub fn set_quiet_mode(enabled: bool) -> Result<(), String> { - let mutex: &Mutex = QUIET_MODE.get_or_init(|| Mutex::new(false)); - match mutex.try_lock() { - Ok(mut guard) => { - *guard = enabled; - Ok(()) - } - Err(_) => Err("Failed to lock mutex".to_string()), - } -} - -pub enum AppCommands { - PortScan, - HostScan, - Ping, - Trace, - Subdomain, - Neighbor, - Interfaces, - Interface, - CheckDependencies, -} - -impl AppCommands { - pub fn from_str(s: &str) -> Option { - match s { - "port" => Some(AppCommands::PortScan), - "host" => Some(AppCommands::HostScan), - "ping" => Some(AppCommands::Ping), - "trace" => Some(AppCommands::Trace), - "subdomain" => Some(AppCommands::Subdomain), - "nei" => Some(AppCommands::Neighbor), - "interfaces" => Some(AppCommands::Interfaces), - "interface" => Some(AppCommands::Interface), - "check" => Some(AppCommands::CheckDependencies), - _ => None, - } - } -} - -pub fn show_app_desc() { - if is_quiet_mode() { - return; - } - println!( - "{} v{} ({}) {}", - crate_name!(), - crate_version!(), - CRATE_UPDATE_DATE, - sys::os::get_os_type() - ); - println!("{}", crate_description!()); - println!("{}", CRATE_REPOSITORY); - println!(); - println!("'{} --help' for more information.", CRATE_BIN_NAME); - println!(); -} - -pub fn show_banner_with_starttime() { - if is_quiet_mode() { - return; - } - println!( - "{} v{} {}", - crate_name!(), - crate_version!(), - sys::os::get_os_type() - ); - println!("{}", CRATE_REPOSITORY); - println!(); - println!("Starting at {}", sys::time::get_sysdate()); - println!(); -} - -pub fn exit_with_error_message(message: &str) { - println!(); - println!("Error: {}", message); - std::process::exit(1); -} - -pub fn show_error_with_help(message: &str) { - println!(); - println!("Error: {}", message); - println!(); - println!("'{} --help' for more information.", CRATE_BIN_NAME); - println!(); -} diff --git a/src/capture/mod.rs b/src/capture/mod.rs new file mode 100644 index 0000000..a5bf742 --- /dev/null +++ b/src/capture/mod.rs @@ -0,0 +1 @@ +pub mod pcap; diff --git a/src/pcap/mod.rs b/src/capture/pcap.rs similarity index 63% rename from src/pcap/mod.rs rename to src/capture/pcap.rs index 9dec797..f527bc1 100644 --- a/src/pcap/mod.rs +++ b/src/capture/pcap.rs @@ -1,40 +1,16 @@ -pub mod setting; use std::net::IpAddr; -//use std::sync::mpsc::Sender; -use crate::interface; -use crate::packet::frame::PacketFrame; -use nex::datalink::RawReceiver; +use nex::datalink::async_io::AsyncRawReceiver; use nex::net::interface::Interface; use nex::packet::frame::Frame; use nex::packet::frame::ParseOption; -use nex::packet::{ethernet::EtherType, ip::IpNextLevelProtocol}; +use nex::packet::{ethernet::EtherType, ip::IpNextProtocol}; use serde::{Deserialize, Serialize}; use std::collections::HashSet; -use std::sync::{Arc, Mutex}; use std::time::Duration; use std::time::Instant; - -/// Packet capture message -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct CaptureReport { - pub bytes: usize, - pub packets: usize, - pub start_time: String, - pub end_time: String, - pub duration: Duration, -} - -impl CaptureReport { - pub fn new() -> CaptureReport { - CaptureReport { - bytes: 0, - packets: 0, - start_time: String::new(), - end_time: String::new(), - duration: Duration::from_secs(0), - } - } -} +use futures::stream::StreamExt; +use tokio::sync::oneshot; +use crate::interface; /// Packet capture options #[derive(Serialize, Deserialize, Debug, Clone)] @@ -55,7 +31,7 @@ pub struct PacketCaptureOptions { /// Ether types to filter. If empty, all ether types will be captured pub ether_types: HashSet, /// IP protocols to filter. If empty, all IP protocols will be captured - pub ip_protocols: HashSet, + pub ip_protocols: HashSet, /// Capture duration limit pub capture_timeout: Duration, /// Read Timeout for read next packet (Linux, BPF only) @@ -153,126 +129,51 @@ impl PacketCaptureOptions { } /// Start packet capture -pub fn start_capture( - rx: &mut Box, +pub async fn start_capture( + rx: &mut Box, capture_options: PacketCaptureOptions, - stop: &Arc>, -) -> Vec { + ready_tx: oneshot::Sender<()>, + stop_rx: &mut oneshot::Receiver<()>, +) -> Vec { let mut frames = Vec::new(); let start_time = Instant::now(); + ready_tx.send(()).unwrap(); loop { - match rx.next() { - Ok(packet) => { - let mut parse_option: ParseOption = ParseOption::default(); - if capture_options.tunnel - || (cfg!(any(target_os = "macos", target_os = "ios")) - && capture_options.loopback) - { - let payload_offset; - if capture_options.loopback { - payload_offset = 14; - } else { - payload_offset = 0; + tokio::select! { + _ = &mut *stop_rx => break, + // Check if capture timeout is reached + // Read next packet + next_read = rx.next() => { + match next_read { + Some(Ok(packet)) => { + let mut parse_option: ParseOption = ParseOption::default(); + if capture_options.tunnel || (cfg!(any(target_os = "macos", target_os = "ios")) && capture_options.loopback) { + let payload_offset = if capture_options.loopback { 14 } else { 0 }; + parse_option.from_ip_packet = true; + parse_option.offset = payload_offset; + } + if let Some(frame) = Frame::from_buf(&packet, parse_option) { + if filter_packet(&frame, &capture_options) { + frames.push(frame); + } + } else { + eprintln!("Error parsing packet"); + } } - parse_option.from_ip_packet = true; - parse_option.offset = payload_offset; - } - let frame: Frame = Frame::from_bytes(&packet, parse_option); - if filter_packet(&frame, &capture_options) { - let packet_frame = PacketFrame::from_nex_frame(&frame); - frames.push(packet_frame); - /* match msg_tx.send(packet_frame) { - Ok(_) => {} - Err(_) => {} - } */ - } - } - Err(_) => {} - } - match stop.lock() { - Ok(stop) => { - if *stop { - break; - } - } - Err(_) => {} - } - if Instant::now().duration_since(start_time) > capture_options.capture_timeout { - break; - } - } - frames -} - -/* /// Start packet capture -pub fn start_capture( - capture_options: PacketCaptureOptions, - stop: &Arc>, - interface: Interface, -) -> Vec { - let mut frames = Vec::new(); - let config = nex::datalink::Config { - write_buffer_size: 4096, - read_buffer_size: 4096, - read_timeout: Some(capture_options.read_timeout), - write_timeout: None, - channel_type: nex::datalink::ChannelType::Layer2, - bpf_fd_attempts: 1000, - linux_fanout: None, - promiscuous: capture_options.promiscuous, - }; - let (mut _tx, mut rx) = match nex::datalink::channel(&interface, config) { - Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), - Ok(_) => { - //thread_log!(warn, "Unknown channel type"); - return frames; - }, - Err(_e) => { - //thread_log!(error, "Error happened {}", e); - return frames; - }, - }; - let start_time = Instant::now(); - loop { - match rx.next() { - Ok(packet) => { - let mut parse_option: ParseOption = ParseOption::default(); - if interface.is_tun() || (cfg!(any(target_os = "macos", target_os = "ios")) && interface.is_loopback()) { - let payload_offset; - if interface.is_loopback() { - payload_offset = 14; - } else { - payload_offset = 0; + Some(Err(e)) => { + eprintln!("Error reading packet: {}", e); + break; } - parse_option.from_ip_packet = true; - parse_option.offset = payload_offset; + None => {} } - let frame: Frame = Frame::from_bytes(&packet, parse_option); - if filter_packet(&frame, &capture_options) { - let packet_frame = PacketFrame::from_nex_frame(&frame); - frames.push(packet_frame); - /* match msg_tx.send(packet_frame) { - Ok(_) => {} - Err(_) => {} - } */ - } - } - Err(_) => {} - } - match stop.lock() { - Ok(stop) => { - if *stop { + if start_time.elapsed() >= capture_options.capture_timeout { break; } } - Err(_) => {} - } - if Instant::now().duration_since(start_time) > capture_options.capture_timeout { - break; } } frames -} */ +} fn filter_packet(frame: &Frame, capture_options: &PacketCaptureOptions) -> bool { if let Some(datalink) = &frame.datalink { @@ -364,10 +265,7 @@ fn filter_ether_type(ether_type: EtherType, capture_options: &PacketCaptureOptio } } -fn filter_ip_protocol( - protocol: IpNextLevelProtocol, - capture_options: &PacketCaptureOptions, -) -> bool { +fn filter_ip_protocol(protocol: IpNextProtocol, capture_options: &PacketCaptureOptions) -> bool { if capture_options.ip_protocols.len() == 0 || capture_options.ip_protocols.contains(&protocol) { return true; } else { diff --git a/src/cli/host.rs b/src/cli/host.rs new file mode 100644 index 0000000..23285d8 --- /dev/null +++ b/src/cli/host.rs @@ -0,0 +1,90 @@ +use anyhow::{Result, Context}; +use std::{net::IpAddr, path::Path}; +use ipnet::IpNet; +use std::fs; +use crate::endpoint::Host; + +/// Resolve one target specification line (CIDR / IP / hostname) +async fn expand_one_target(t: &str) -> Result> { + let mut out = Vec::new(); + let resolver = crate::dns::resolver::get_resolver()?; + + // CIDR + if let Ok(net) = t.parse::() { + for ip in net.hosts() { + out.push(Host::new(ip)); + } + return Ok(out); + } + + // IP + if let Ok(ip) = t.parse::() { + out.push(Host::new(ip)); + return Ok(out); + } + + // Hostname + let ips = resolver.lookup_ip(t).await.with_context(|| format!("resolve {t}"))?; + for ip in ips { + out.push(Host::with_hostname(ip, t.to_string())); + } + Ok(out) +} + +/// Expand targets from a file (each line: CIDR / IP / hostname / @file) +async fn expand_file(path: &Path) -> Result> { + let text = fs::read_to_string(path) + .with_context(|| format!("read target list file {}", path.display()))?; + + // Normalize lines and prepare for recursive processing + let mut hosts = Vec::new(); + let mut nested_inputs = Vec::new(); + + for line in text.lines() { + let s = line.trim(); + if s.is_empty() || s.starts_with('#') { continue; } // Skip empty lines/comments + nested_inputs.push(s.to_string()); + } + + // Recursively interpret each entry in the file + for entry in nested_inputs { + let nested = expand_one_target(&entry).await?; + hosts.extend(nested); + } + + Ok(hosts) +} + +/// Parse target specifications (CIDR / IP / hostname / @file / existing file path) +pub async fn parse_target_hosts(inputs: &[String]) -> Result> { + let mut out = Vec::new(); + + for raw in inputs { + let s = raw.trim(); + if s.is_empty() { continue; } + + // 1. Check if it's a file (with '@' hint or existing file path) + let (is_file_hint, path_str) = if let Some(stripped) = s.strip_prefix('@') { + (true, stripped) + } else { + (false, s) + }; + + let path = Path::new(path_str); + if is_file_hint || path.is_file() { + // Interpret as file + let hosts = expand_file(path).await?; + out.extend(hosts); + continue; + } + + // 2. Interpret as regular target + let hosts = expand_one_target(s).await?; + out.extend(hosts); + } + + // Sort by IP & remove duplicates + out.sort_by_key(|e| e.ip); + out.dedup_by_key(|e| e.ip); + Ok(out) +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 0000000..085d4c0 --- /dev/null +++ b/src/cli/mod.rs @@ -0,0 +1,354 @@ +pub mod port; +pub mod host; +pub mod ping; + +use std::path::PathBuf; + +use clap::{command, value_parser, ArgAction, Args, Parser, Subcommand, ValueEnum}; + +use crate::{config::default::{DEFAULT_BASE_TARGET_UDP_PORT, DEFAULT_PORTS_CONCURRENCY}, endpoint::TransportProtocol, protocol::Protocol}; + +/// nrev - Fast Network Mapper +#[derive(Parser, Debug)] +#[command(author, version, about = "nrev - Cross-platform Network Mapper\nhttps://github.com/shellrow/nrev", long_about = None)] +pub struct Cli { + /// Global log level + #[arg(long, default_value = "info")] + pub log_level: LogLevel, + + /// Log to file (in addition to stdout) + #[arg(long, action = ArgAction::SetTrue, default_value_t = false)] + pub log_file: bool, + + /// Log file path (default: ~/.nrev/logs/nrev.log) + #[arg(long, value_name = "FILE", value_parser = value_parser!(PathBuf))] + pub log_file_path: Option, + + /// Suppress all log output (only errors are shown) + #[arg(long, action = ArgAction::SetTrue, default_value_t = false)] + pub quiet: bool, + + /// Save output to file (JSON format) + #[arg(short, long, value_name = "FILE", value_parser = value_parser!(PathBuf))] + pub output: Option, + + /// Suppress stdout console output (only save to file if -o is set) + #[arg(long, action = ArgAction::SetTrue, default_value_t = false)] + pub no_stdout: bool, + + /// Subcommands + #[command(subcommand)] + pub command: Command, +} + +/// Log level +#[derive(Copy, Clone, Debug, ValueEnum, Eq, PartialEq)] +pub enum LogLevel { + Error, + Warn, + Info, + Debug, + Trace, +} + +impl LogLevel { + /// Convert to `tracing::Level` + pub fn to_level_filter(&self) -> tracing::Level { + match self { + LogLevel::Error => tracing::Level::ERROR, + LogLevel::Warn => tracing::Level::WARN, + LogLevel::Info => tracing::Level::INFO, + LogLevel::Debug => tracing::Level::DEBUG, + LogLevel::Trace => tracing::Level::TRACE, + } + } +} + +/// Subcommands +#[derive(Subcommand, Debug)] +pub enum Command { + /// Scan ports on the target(s) (TCP/QUIC) + Port(PortScanArgs), + + /// Discover alive hosts (ICMP/UDP/TCP etc.) + Host(HostScanArgs), + + /// Simple ping (ICMP/UDP/TCP) + Ping(PingArgs), + + /// Traceroute (UDP) + Trace(TraceArgs), + + /// Neighbor discovery (ARP/NDP) + Nei(NeighborArgs), + + /// Subdomain enumeration + Domain(DomainScanArgs), + + /// Show network interface(s) + Interface(InterfaceArgs), +} + +/// Port scan methods. Default: Connect +#[derive(Copy, Clone, Debug, ValueEnum, Eq, PartialEq)] +pub enum PortScanMethod { Connect, Syn } + +/// Host scan protocols. Default: ICMP +#[derive(Copy, Clone, Debug, ValueEnum, Eq, PartialEq)] +pub enum HostScanProto { Icmp, Udp, Tcp } + +impl HostScanProto { + /// Convert to TransportProtocol (if applicable) + pub fn to_transport(&self) -> Option { + match self { + HostScanProto::Icmp => None, + HostScanProto::Udp => Some(TransportProtocol::Udp), + HostScanProto::Tcp => Some(TransportProtocol::Tcp), + } + } + /// Convert to &str + pub fn as_str(&self) -> &str { + match self { + HostScanProto::Icmp => "icmp", + HostScanProto::Udp => "udp", + HostScanProto::Tcp => "tcp", + } + } +} + +/// Traceroute protocol (currently only UDP is supported) +#[derive(Copy, Clone, Debug, ValueEnum, Eq, PartialEq)] +pub enum TraceProto { Udp } + +impl TraceProto { + /// Convert to &str + pub fn as_str(&self) -> &str { + match self { + TraceProto::Udp => "udp", + } + } + /// Convert to Protocol + pub fn to_protocol(&self) -> Protocol { + match self { + TraceProto::Udp => Protocol::Udp, + } + } +} + +/// Port scan arguments +#[derive(Args, Debug)] +pub struct PortScanArgs { + /// Target IP or hostname + #[arg(required = true)] + pub target: Vec, + + /// Port spec: "top-1000" | "1-1024,80,443" | "22-25" + #[arg(short, long, default_value = "top-1000")] + pub ports: String, + + /// Transport to scan (now tcp only; udp/quic later) + #[arg(long, default_value = "tcp", value_parser = ["tcp","udp","quic"])] + pub proto: String, + + /// Scanning method (default: connect) + #[arg(long, value_enum, default_value_t = PortScanMethod::Connect)] + pub method: PortScanMethod, + + /// Enable service detection (banner/TLS/etc.) + #[arg(short='s', long, default_value_t = false, action=ArgAction::SetTrue)] + pub service_detect: bool, + + /// Enable OS fingerprinting + /// for open ports, send one SYN to collect OS-fingerprint features + #[arg(short='o', long, default_value_t = false, action=ArgAction::SetTrue)] + pub os_detect: bool, + + /// Enable QUIC probing on UDP ports (e.g., 443/udp) + #[arg(long, action=ArgAction::SetTrue)] + pub quic: bool, + + /// SNI for QUIC/TLS probing (defaults to target name) + #[arg(long)] + pub sni: Option, + + /// Network interface name to bind + #[arg(long)] + pub interface: Option, + + /// Concurrency (tasks) + #[arg(long, default_value_t = DEFAULT_PORTS_CONCURRENCY)] + pub concurrency: usize, + + /// Base connect timeout in ms (auto-adapted by RTT) + #[arg(long, value_parser = value_parser!(u64).range(1..=10_000))] + pub connect_timeout_ms: Option, + + /// Read timeout in ms (auto-adapted by RTT) + #[arg(long, value_parser = value_parser!(u64).range(1..=10_000))] + pub read_timeout_ms: Option, + + /// Wait time after last send (ms) + #[arg(short='w', long, value_parser = value_parser!(u64).range(10..=5000))] + pub wait_ms: Option, + + /// Task timeout in ms + #[arg(long, default_value_t = 30000, value_parser = value_parser!(u64).range(1..=60_000))] + pub task_timeout_ms: u64, + + /// Scan ports in user-specified order (default is randomized) + #[arg(long, action=ArgAction::SetTrue)] + pub ordered: bool, + + /// Skip initial ping + #[arg(long, action=ArgAction::SetTrue)] + pub no_ping: bool, +} + +/// Host scan arguments +#[derive(Args, Debug)] +pub struct HostScanArgs { + /// Targets (CIDR, range, or list). + #[arg(required = true)] + pub target: Vec, + + /// Protocol to use (default: ICMP) + #[arg(long, value_enum, default_value_t = HostScanProto::Icmp)] + pub proto: HostScanProto, + + /// Port spec: "80" | "80,443" | "22-25" + #[arg(short, long, default_value = "80")] + pub ports: String, + + /// Wait time after last send (ms) + #[arg(short='w', long, default_value_t = 300, value_parser = value_parser!(u64).range(10..=5000))] + pub wait_ms: u64, + + /// Timeout per probe (ms) + #[arg(long, default_value_t = 600, value_parser = value_parser!(u64).range(50..=5000))] + pub timeout_ms: u64, + + /// Network interface name to bind + #[arg(long)] + pub interface: Option, + + /// Concurrency (in-flight probes) + #[arg(long, default_value_t = 512)] + pub concurrency: usize, + + /// Scan hosts in user-specified order (default is randomized) + #[arg(long, action=ArgAction::SetTrue)] + pub ordered: bool, +} + +/// Simple ping arguments +#[derive(Args, Debug)] +pub struct PingArgs { + /// Target IP or hostname + #[arg(required = true)] + pub target: String, + + /// Protocol to use (default: ICMP) + #[arg(long, value_enum, default_value_t = Protocol::Icmp)] + pub proto: Protocol, + + /// Target port + #[arg(short, long, default_value_t = 80)] + pub port: u16, + + /// Number of probes + #[arg(short, long, default_value_t = 4, value_parser = value_parser!(u32).range(1..=10_000))] + pub count: u32, + + /// Interval between probes (ms) + #[arg(short, long, default_value_t = 1000)] + pub interval_ms: u64, + + /// Per-probe timeout (ms) + #[arg(long, default_value_t = 1000)] + pub timeout_ms: u64, + + /// Network interface name to bind + #[arg(long)] + pub interface: Option, +} + + +/// Traceroute arguments +#[derive(Args, Debug)] +pub struct TraceArgs { + /// Target host or IP + #[arg(required = true)] + pub target: String, + + /// Protocol + #[arg(long, value_enum, default_value_t = TraceProto::Udp)] + pub proto: TraceProto, + + /// Destination port (for UDP) + #[arg(long, default_value_t = DEFAULT_BASE_TARGET_UDP_PORT)] + pub port: u16, + + /// Max TTL/hops + #[arg(long, default_value_t = 64, value_parser = value_parser!(u8).range(1..=255))] + pub max_hops: u8, + + /// Interval between probes (ms) + #[arg(short, long, default_value_t = 1000)] + pub interval_ms: u64, + + /// Per-hop timeout (ms) + #[arg(long, default_value_t = 1000)] + pub timeout_ms: u64, + + /// Network interface name to bind + #[arg(long)] + pub interface: Option, +} + +/// Neighbor discovery arguments +#[derive(Args, Debug)] +pub struct NeighborArgs { + /// Target IP (IPv4 -> ARP, IPv6 -> NDP). + #[arg(required = true)] + pub target: String, + + /// Network interface name to bind + #[arg(short='i', long)] + pub interface: Option, + + /// Timeout waiting for replies (ms) + #[arg(long, default_value_t = 500)] + pub timeout_ms: u64, +} + +/// Subdomain scan arguments +#[derive(Args, Debug)] +pub struct DomainScanArgs { + /// Base domain (e.g., example.com) + #[arg(required = true)] + pub domain: String, + + /// Wordlist path + #[arg(short, long)] + pub wordlist: Option, + + /// Concurrency + #[arg(long, default_value_t = 256)] + pub concurrency: usize, + + /// Total scan timeout (ms) + #[arg(long, default_value_t = 30000)] + pub timeout_ms: u64, + + /// Per-lookup timeout (ms) + #[arg(long, default_value_t = 2000)] + pub resolve_timeout_ms: u64, +} + +/// Network interface arguments +#[derive(Args, Debug)] +pub struct InterfaceArgs { + /// Show all interfaces + #[arg(short, long, action=ArgAction::SetTrue)] + pub all: bool, +} diff --git a/src/cli/ping.rs b/src/cli/ping.rs new file mode 100644 index 0000000..c7841a3 --- /dev/null +++ b/src/cli/ping.rs @@ -0,0 +1,21 @@ +use std::net::IpAddr; + +use anyhow::{Result, Context}; +use crate::endpoint::Host; + +/// Parse a single target host (IP or hostname) +pub async fn parse_target_host(host_str: &str) -> Result { + let resolver = crate::dns::resolver::get_resolver()?; + match host_str.parse::() { + Ok(ip) => Ok(Host::new(ip)), + Err(_) => { + let ips = resolver.lookup_ip(host_str).await + .with_context(|| format!("resolve {host_str}"))?; + // If multiple IPs are returned, use the first one (ips: LookupIp) + for ip in ips { + return Ok(Host::with_hostname(ip, host_str.to_string())); + } + Err(anyhow::anyhow!("no IPs found")) + } + } +} diff --git a/src/cli/port.rs b/src/cli/port.rs new file mode 100644 index 0000000..24f6db7 --- /dev/null +++ b/src/cli/port.rs @@ -0,0 +1,32 @@ +use anyhow::{Result, bail}; +use std::collections::BTreeSet; +use crate::endpoint::{Port, TransportProtocol}; + +/// Get top N ports from the default port list +fn top_ports(n: usize) -> Vec { + let top_ports: Vec = crate::db::port::get_default_ports(); + top_ports.into_iter().take(n).collect() +} + +/// Parse port specification string into a list of ports +pub fn parse_ports(spec: &str, tr: TransportProtocol) -> Result> { + let mut set = BTreeSet::new(); + + if let Some(nstr) = spec.strip_prefix("top-") { + let n: usize = nstr.parse()?; + for p in top_ports(n) { set.insert(Port::new(p, tr)); } + } else { + for part in spec.split(',').map(|s| s.trim()).filter(|s| !s.is_empty()) { + if let Some((a,b)) = part.split_once('-') { + let start: u16 = a.parse()?; + let end: u16 = b.parse()?; + if start > end { bail!("invalid range: {part}"); } + for p in start..=end { set.insert(Port::new(p, tr)); } + } else { + let p: u16 = part.parse()?; + set.insert(Port::new(p, tr)); + } + } + } + Ok(set.into_iter().collect()) +} diff --git a/src/cmd/domain.rs b/src/cmd/domain.rs new file mode 100644 index 0000000..cf6edbb --- /dev/null +++ b/src/cmd/domain.rs @@ -0,0 +1,38 @@ +use std::{path::PathBuf, time::Duration}; + +use anyhow::Result; +use crate::{cli::DomainScanArgs, util::json::{save_json_output, JsonStyle}}; + +/// Run subdomain scan +pub async fn run(args: DomainScanArgs, no_stdout: bool, output: Option) -> Result<()> { + let resolve_timeout = Duration::from_millis(args.resolve_timeout_ms); + let base = crate::dns::lookup_domain(&args.domain, resolve_timeout).await; + let settings = crate::dns::probe::DomainScanSetting { + base_domain: base.name.clone(), + word_list: if let Some(wl_path) = args.wordlist { + let content = std::fs::read_to_string(wl_path)?; + content.lines().map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect() + } else { + crate::db::domain::get_subdomain_wordlist() + }, + timeout: Duration::from_millis(args.timeout_ms), + resolve_timeout: resolve_timeout, + concurrent_limit: args.concurrency, + }; + let scanner = crate::dns::probe::DomainScanner::new(settings); + let result = scanner.run().await?; + if !no_stdout { + crate::output::domain::print_domain_tree(&base, &result); + } + if let Some(path) = &output { + match save_json_output(&result, path, JsonStyle::Pretty) { + Ok(_) => { + if !no_stdout { + tracing::info!("JSON output saved to {}", path.display()); + } + }, + Err(e) => tracing::error!("Failed to save JSON output: {}", e), + } + } + Ok(()) +} diff --git a/src/cmd/host.rs b/src/cmd/host.rs new file mode 100644 index 0000000..e0001ec --- /dev/null +++ b/src/cmd/host.rs @@ -0,0 +1,84 @@ +use std::{path::PathBuf, time::Duration}; +use rand::seq::SliceRandom; +use rand::thread_rng; +use anyhow::Result; +use crate::{cli::{HostScanArgs, HostScanProto}, endpoint::{Endpoint, Host, Port, TransportProtocol}, output::ScanResult, scan::HostScanner, util::json::{save_json_output, JsonStyle}}; +use crate::probe::ProbeSetting; + +/// Run host scan +pub async fn run(args: HostScanArgs, no_stdout: bool, output: Option) -> Result<()> { + let mut target_hosts: Vec = crate::cli::host::parse_target_hosts(&args.target).await?; + if target_hosts.is_empty() { anyhow::bail!("no targets resolved"); } + + let mut ports: Vec = Vec::new(); + match args.proto { + HostScanProto::Tcp => { + ports = crate::cli::port::parse_ports(&args.ports, TransportProtocol::Tcp)?; + } + _ => {} + } + + if !args.ordered { + // Randomize the order of targets and ports + target_hosts.shuffle(&mut thread_rng()); + ports.shuffle(&mut thread_rng()); + } + + let mut target_endpoints: Vec = Vec::new(); + + for host in target_hosts { + let mut endpoint = Endpoint::new(host.ip); + endpoint.hostname = host.hostname; + for port in &ports { + endpoint.upsert_port(port.clone()); + } + target_endpoints.push(endpoint); + } + + let interface: netdev::Interface = if let Some(if_name) = args.interface { + match crate::interface::get_interface_by_name(if_name.to_string()) { + Some(iface) => iface, + None => anyhow::bail!("interface not found"), + } + } else { + match netdev::get_default_interface() { + Ok(iface) => iface, + Err(_) => anyhow::bail!("failed to get default interface"), + } + }; + + let probe_setting = ProbeSetting { + if_index: interface.index, + target_endpoints: target_endpoints, + host_concurrency: args.concurrency, + port_concurrency: args.concurrency, + task_timeout: Duration::from_secs(30), + connect_timeout: Duration::from_millis(args.timeout_ms), + wait_time: Duration::from_millis(args.wait_ms), + send_rate: Duration::from_millis(1), + }; + + let host_scanner = HostScanner::new(probe_setting.clone(), args.proto); + if !probe_setting.target_endpoints.is_empty() { + tracing::info!("Starting {} host scan. Target: {} host(s), {} port(s)", args.proto.as_str().to_uppercase(), probe_setting.target_endpoints.len(), probe_setting.target_endpoints[0].ports.len()); + } + let mut hostscan_result: ScanResult = host_scanner.run().await?; + hostscan_result.sort_endpoints(); + tracing::info!("{} Host scan completed in {:?}", args.proto.as_str().to_uppercase(), hostscan_result.scan_time); + + // Print result as a tree + if !no_stdout { + crate::output::host::print_report_tree(&hostscan_result); + } + if let Some(path) = &output { + match save_json_output(&hostscan_result, path, JsonStyle::Pretty) { + Ok(_) => { + if !no_stdout { + tracing::info!("JSON output saved to {}", path.display()); + } + }, + Err(e) => tracing::error!("Failed to save JSON output: {}", e), + } + } + Ok(()) +} diff --git a/src/cmd/interface.rs b/src/cmd/interface.rs new file mode 100644 index 0000000..17d8f8a --- /dev/null +++ b/src/cmd/interface.rs @@ -0,0 +1,23 @@ +use netdev::Interface; +use anyhow::Result; +use crate::cli::InterfaceArgs; + +/// Show network interfaces +pub fn show(args: &InterfaceArgs) -> Result<()> { + let ifaces: Vec = if args.all { + // Show all interfaces + let ifaces = netdev::get_interfaces(); + ifaces + } else { + // Show default interface + let ifaces = match netdev::get_default_interface() { + Ok(iface) => vec![iface], + Err(e) => { + return Err(anyhow::anyhow!("Failed to get default interface: {}", e)); + } + }; + ifaces + }; + crate::output::interface::print_interface_tree(&ifaces); + Ok(()) +} diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs new file mode 100644 index 0000000..cef52d8 --- /dev/null +++ b/src/cmd/mod.rs @@ -0,0 +1,7 @@ +pub mod port; +pub mod host; +pub mod ping; +pub mod trace; +pub mod nei; +pub mod domain; +pub mod interface; diff --git a/src/cmd/nei.rs b/src/cmd/nei.rs new file mode 100644 index 0000000..f28ea0e --- /dev/null +++ b/src/cmd/nei.rs @@ -0,0 +1,64 @@ +use std::{net::IpAddr, path::PathBuf, time::Duration}; + +use crate::{cli::NeighborArgs, endpoint::Host, nei::NeighborDiscoveryResult, util::json::{save_json_output, JsonStyle}}; +use anyhow::Result; + +/// Run neighbor discovery (ARP for IPv4, NDP for IPv6) +pub async fn run(args: NeighborArgs, no_stdout: bool, output: Option) -> Result<()> { + let interface: netdev::Interface = if let Some(if_name) = args.interface { + match crate::interface::get_interface_by_name(if_name.to_string()) { + Some(iface) => iface, + None => anyhow::bail!("interface not found"), + } + } else { + match netdev::get_default_interface() { + Ok(iface) => iface, + Err(_) => anyhow::bail!("failed to get default interface"), + } + }; + let dst_host: Host = crate::cli::ping::parse_target_host(&args.target).await?; + let nd_result: NeighborDiscoveryResult = match dst_host.ip { + IpAddr::V4(ipv4) => { + let recv_timeout = Duration::from_millis(args.timeout_ms); + let mut arp_result = crate::nei::arp::send_arp(ipv4, &interface, recv_timeout).await?; + match dst_host.hostname { + Some(hostname) => { + arp_result.hostname = Some(hostname); + }, + None => { + let timeout = Duration::from_millis(200); + arp_result.hostname = crate::dns::reverse_lookup(IpAddr::V4(ipv4), timeout).await; + }, + } + arp_result + } + IpAddr::V6(ipv6) => { + let recv_timeout = Duration::from_millis(args.timeout_ms); + let mut ndp_result = crate::nei::ndp::send_ndp(ipv6, &interface, recv_timeout).await?; + match dst_host.hostname { + Some(hostname) => { + ndp_result.hostname = Some(hostname); + }, + None => { + let timeout = Duration::from_millis(200); + ndp_result.hostname = crate::dns::reverse_lookup(IpAddr::V6(ipv6), timeout).await; + }, + } + ndp_result + } + }; + if !no_stdout { + crate::output::nei::print_neighbor_tree(&[nd_result.clone()]); + } + if let Some(path) = &output { + match save_json_output(&nd_result, path, JsonStyle::Pretty) { + Ok(_) => { + if !no_stdout { + tracing::info!("JSON output saved to {}", path.display()); + } + }, + Err(e) => tracing::error!("Failed to save JSON output: {}", e), + } + } + Ok(()) +} diff --git a/src/cmd/ping.rs b/src/cmd/ping.rs new file mode 100644 index 0000000..b0570e8 --- /dev/null +++ b/src/cmd/ping.rs @@ -0,0 +1,48 @@ +use std::{path::PathBuf, time::Duration}; + +use crate::{cli::PingArgs, endpoint::Host, ping::{pinger::Pinger, setting::PingSetting}, protocol::Protocol, util::json::{save_json_output, JsonStyle}}; +use anyhow::Result; + +/// Run ping command +pub async fn run(args: PingArgs, no_stdout: bool, output: Option) -> Result<()> { + let interface: netdev::Interface = if let Some(if_name) = args.interface { + match crate::interface::get_interface_by_name(if_name.to_string()) { + Some(iface) => iface, + None => anyhow::bail!("interface not found"), + } + } else { + match netdev::get_default_interface() { + Ok(iface) => iface, + Err(_) => anyhow::bail!("failed to get default interface"), + } + }; + let dst_host: Host = crate::cli::ping::parse_target_host(&args.target).await?; + let mut ping_setting: PingSetting = match args.proto { + Protocol::Icmp => PingSetting::icmp_ping(&interface, dst_host, args.count)?, + Protocol::Tcp => PingSetting::tcp_ping(&interface, dst_host, args.port, args.count)?, + Protocol::Udp => PingSetting::udp_ping(&interface, dst_host, args.count)?, + _ => { + anyhow::bail!("Unsupported protocol"); + } + }; + ping_setting.send_rate = Duration::from_millis(args.interval_ms); + ping_setting.receive_timeout = Duration::from_millis(args.timeout_ms); + + let pinger = Pinger::new(ping_setting); + tracing::info!("Pinging {} with {}...", args.target, args.proto.as_str().to_uppercase()); + let ping_result = pinger.run().await?; + if !no_stdout { + crate::output::ping::print_ping_tree(&ping_result); + } + if let Some(path) = &output { + match save_json_output(&ping_result, path, JsonStyle::Pretty) { + Ok(_) => { + if !no_stdout { + tracing::info!("JSON output saved to {}", path.display()); + } + }, + Err(e) => tracing::error!("Failed to save JSON output: {}", e), + } + } + Ok(()) +} diff --git a/src/cmd/port.rs b/src/cmd/port.rs new file mode 100644 index 0000000..9697e69 --- /dev/null +++ b/src/cmd/port.rs @@ -0,0 +1,198 @@ +use std::{path::PathBuf, time::Duration}; +use rand::seq::SliceRandom; +use rand::thread_rng; +use anyhow::Result; +use crate::{cli::PortScanArgs, endpoint::{Endpoint, Host, Port, PortState, TransportProtocol}, output::{port::{print_report_tree, ScanReport}, ScanResult}, probe::ProbeSetting, scan::PortScanner, service::{ServiceDetector, ServiceProbeConfig}, util::json::{save_json_output, JsonStyle}}; + +/// Run port scan +pub async fn run(args: PortScanArgs, no_stdout: bool, output: Option) -> Result<()> { + let mut rep = ScanReport::new(); + // Parse target hosts + let target_hosts: Vec = crate::cli::host::parse_target_hosts(&args.target).await?; + if target_hosts.is_empty() { anyhow::bail!("no targets resolved"); } + let first_host = target_hosts[0].clone(); + // Parse transport protocol + let transport: TransportProtocol = TransportProtocol::from_str(&args.proto).ok_or_else(|| anyhow::anyhow!("invalid transport"))?; + // Parse ports + let mut ports: Vec = crate::cli::port::parse_ports(&args.ports, transport)?; + + if !args.ordered { + // Randomize the order of ports + ports.shuffle(&mut thread_rng()); + } + + // Create target endpoints from hosts and ports + let mut target_endpoints: Vec = Vec::new(); + + for host in target_hosts { + let mut endpoint = Endpoint::new(host.ip); + endpoint.hostname = host.hostname; + for port in &ports { + endpoint.upsert_port(port.clone()); + } + target_endpoints.push(endpoint); + } + + // Get network interface + let interface: netdev::Interface = if let Some(if_name) = args.interface { + match crate::interface::get_interface_by_name(if_name.to_string()) { + Some(iface) => iface, + None => anyhow::bail!("interface not found"), + } + } else { + match netdev::get_default_interface() { + Ok(iface) => iface, + Err(_) => anyhow::bail!("failed to get default interface"), + } + }; + + // Initial ping to check reachability and measure latency + let initial_rtt = if args.no_ping { + Duration::from_millis(200) + } else { + match crate::ping::initial_ping(&interface, &first_host, Some(ports[0].number)).await { + Ok(rtt) => rtt, + Err(e) => { + tracing::warn!("Initial ping failed: {}. Proceeding with default RTT.", e); + Duration::from_millis(200) // Default RTT if ping fails + } + } + }; + + let conn_timeout = if let Some(ct) = args.connect_timeout_ms { + Duration::from_millis(ct) + } else { + // adapt timeout based on RTT + let adapted = (initial_rtt.as_millis() as f64 * 1.5) as u64; + Duration::from_millis(adapted.clamp(50, 5000)) + }; + + let wait_time = if let Some(wt) = args.wait_ms { + Duration::from_millis(wt) + } else { + // adapt wait time based on RTT + let adapted = (initial_rtt.as_millis() as f64 * 2.0) as u64; + Duration::from_millis(adapted.clamp(100, 5000)) + }; + + // Create probe setting + let probe_setting = ProbeSetting { + if_index: interface.index, + target_endpoints: target_endpoints, + host_concurrency: args.concurrency, + port_concurrency: args.concurrency, + task_timeout: Duration::from_millis(args.task_timeout_ms), + connect_timeout: conn_timeout, + wait_time: wait_time, + send_rate: Duration::from_millis(1), + }; + + let transport = TransportProtocol::from_str(&args.proto).unwrap(); + + if !probe_setting.target_endpoints.is_empty() { + tracing::info!("Starting {} port scan on {} host(s), {} port(s)", args.proto.to_uppercase(), probe_setting.target_endpoints.len(), probe_setting.target_endpoints[0].ports.len()); + } + + // Run port scan + let port_scanner = PortScanner::new(probe_setting.clone(), transport, args.method); + let portscan_result: ScanResult = port_scanner.run().await?; + tracing::info!("{} Port scan completed in {:?}", args.proto.to_uppercase(), portscan_result.scan_time); + let mut endpoint_results = portscan_result.endpoints.clone(); + + let mut active_endpoints = portscan_result.get_active_endpoints(); + + if active_endpoints.is_empty() { + tracing::info!("No open ports found"); + } + + rep.apply_port_scan(portscan_result); + + if transport != TransportProtocol::Quic && args.quic { + let port_scanner = PortScanner::new(probe_setting.clone(), TransportProtocol::Quic, args.method); + let quic_portscan_result = port_scanner.run().await?; + endpoint_results.extend(quic_portscan_result.endpoints.clone()); + let active_quic_endpoints = quic_portscan_result.get_active_endpoints(); + // Merge active QUIC endpoints with active TCP endpoints + active_endpoints.extend(active_quic_endpoints); + + rep.apply_port_scan(quic_portscan_result); + } + + for endpoint in &endpoint_results { + let mut open_ports: Vec = Vec::new(); + for (port, port_result) in &endpoint.ports { + if port_result.state == PortState::Open { + open_ports.push(port.number); + } + } + tracing::info!("{}: Open ports: {:?}", endpoint.ip, open_ports); + } + + if args.os_detect { + // OS detection + let os_probe_setting = ProbeSetting { + target_endpoints: active_endpoints.clone(), + if_index: probe_setting.if_index, + host_concurrency: probe_setting.host_concurrency, + port_concurrency: probe_setting.port_concurrency, + task_timeout: probe_setting.task_timeout, + connect_timeout: probe_setting.connect_timeout, + wait_time: probe_setting.wait_time, + send_rate: probe_setting.send_rate, + }; + tracing::info!("Starting OS detection on {} host(s)", os_probe_setting.target_endpoints.len()); + let os_detector = crate::os::OsDetector::new(os_probe_setting); + let os_probe_result = os_detector.run().await?; + tracing::info!("OS detection completed in {:?}", os_probe_result.probe_time); + if os_probe_result.endpoints.len() == 0 { + tracing::info!("No OS detected"); + } + for endpoint in &os_probe_result.endpoints { + tracing::debug!("[OS] Guess {}: {:?}", endpoint.ip, endpoint.cpes); + } + + rep.apply_os_probe(os_probe_result); + } + + if args.service_detect { + // service detection + let service_probe_setting = ServiceProbeConfig { + timeout: Duration::from_secs(2), + max_concurrency: args.concurrency, + max_read_size: 1024 * 1024, + sni: true, + skip_cert_verify: true, + }; + + let service_detector = ServiceDetector::new(service_probe_setting); + if !active_endpoints.is_empty() { + tracing::info!("Starting service detection on {} host(s), {} port(s)", active_endpoints.len(), active_endpoints[0].ports.len()); + } + + let service_result = service_detector.run_service_detection(active_endpoints).await?; + tracing::info!("Service detection completed in {:?}", service_result.scan_time); + + service_result.results.iter().for_each(|result| { + tracing::debug!("[SERVICE] {}:{} {} {} {:?} {:?}", result.ip, result.port, result.transport.as_str().to_uppercase(), result.probe_id.as_str(), result.service_info.banner, result.service_info.cpes); + }); + + rep.apply_service_detection(service_result); + } + + rep.finish(); + + if !no_stdout { + print_report_tree(&rep); + } + if let Some(path) = &output { + match save_json_output(&rep, path, JsonStyle::Pretty) { + Ok(_) => { + if !no_stdout { + tracing::info!("JSON output saved to {}", path.display()); + } + }, + Err(e) => tracing::error!("Failed to save JSON output: {}", e), + } + } + Ok(()) +} diff --git a/src/cmd/trace.rs b/src/cmd/trace.rs new file mode 100644 index 0000000..c7a5940 --- /dev/null +++ b/src/cmd/trace.rs @@ -0,0 +1,49 @@ +use std::{path::PathBuf, time::Duration}; + +use crate::{cli::TraceArgs, endpoint::Host, protocol::Protocol, trace::{TraceSetting, Tracer}, util::json::{save_json_output, JsonStyle}}; +use anyhow::Result; + +/// Run traceroute +pub async fn run(args: TraceArgs, no_stdout: bool, output: Option) -> Result<()> { + let interface: netdev::Interface = if let Some(if_name) = args.interface { + match crate::interface::get_interface_by_name(if_name.to_string()) { + Some(iface) => iface, + None => anyhow::bail!("interface not found"), + } + } else { + match netdev::get_default_interface() { + Ok(iface) => iface, + Err(_) => anyhow::bail!("failed to get default interface"), + } + }; + let dst_host: Host = crate::cli::ping::parse_target_host(&args.target).await?; + let mut trace_setting: TraceSetting = match args.proto.to_protocol() { + Protocol::Udp => TraceSetting::udp_trace(&interface, &dst_host)?, + _ => { + anyhow::bail!("Unsupported protocol"); + } + }; + trace_setting.dst_port = Some(args.port); + trace_setting.hop_limit = args.max_hops; + trace_setting.send_rate = Duration::from_millis(args.interval_ms); + trace_setting.receive_timeout = Duration::from_millis(args.timeout_ms); + + let tracer = Tracer::new(trace_setting); + tracing::info!("Trace route to {} with {}...", args.target, args.proto.as_str().to_uppercase()); + let trace_result = tracer.run().await?; + tracing::info!("Trace complete."); + if !no_stdout { + crate::output::trace::print_trace_tree(&trace_result, dst_host); + } + if let Some(path) = &output { + match save_json_output(&trace_result, path, JsonStyle::Pretty) { + Ok(_) => { + if !no_stdout { + tracing::info!("JSON output saved to {}", path.display()); + } + }, + Err(e) => tracing::error!("Failed to save JSON output: {}", e), + } + } + Ok(()) +} diff --git a/src/config/db.rs b/src/config/db.rs new file mode 100644 index 0000000..a3fb18d --- /dev/null +++ b/src/config/db.rs @@ -0,0 +1,18 @@ +/// Default port list +pub const DEFAULT_PORTS_JSON: &str = include_str!("../../resources/nrev-default-ports.json"); +/// OS class by TTL values +pub const OS_CLASS_TTL_JSON: &str = include_str!("../../resources/nrev-os-class-ttl.json"); +/// Well-known ports +pub const WELLKNOWN_PORTS_JSON: &str = include_str!("../../resources/nrev-wellknown-ports.json"); +/// OS database and fingerprints +pub const OS_DB_JSON: &str = include_str!("../../resources/nrev-os-db.json"); +/// Service database and fingerprints +pub const SERVICE_DB_JSON: &str = include_str!("../../resources/nrev-service-db.json"); +/// probe types and definitions +pub const SERVICE_PROBES_JSON: &str = include_str!("../../resources/nrev-service-probes.json"); +/// Port and probe type mappings +pub const PORT_PROBES_JSON: &str = include_str!("../../resources/nrev-port-probes.json"); +/// TLS OID mappings +pub const TLS_OID_MAP_JSON: &str = include_str!("../../resources/nrev-tls-oid-map.json"); +/// Top subdomain words for subdomain scanning +pub const TOP_SUBDOMAIN_WORDS_JSON: &str = include_str!("../../resources/nrev-top-subdomains.json"); diff --git a/src/config/default.rs b/src/config/default.rs new file mode 100644 index 0000000..bf9be71 --- /dev/null +++ b/src/config/default.rs @@ -0,0 +1,14 @@ +/// Default local TCP port for sending probes +pub const DEFAULT_LOCAL_TCP_PORT: u16 = 44322; +/// Default local UDP port for sending probes +pub const DEFAULT_LOCAL_UDP_PORT: u16 = 53445; +/// Default base target UDP port for traceroute or ping +pub const DEFAULT_BASE_TARGET_UDP_PORT: u16 = 33435; +/// Default hop limit (TTL) +pub const DEFAULT_HOP_LIMIT: u8 = 64; +/// Default ping count for ping command +pub const DEFAULT_PING_COUNT: u32 = 4; +/// Default concurrency for host scanning +pub const DEFAULT_HOSTS_CONCURRENCY: usize = 50; +/// Default concurrency for port scanning +pub const DEFAULT_PORTS_CONCURRENCY: usize = 100; diff --git a/src/config/mod.rs b/src/config/mod.rs index 46783d2..57f1104 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,22 +1,38 @@ -pub const DEFAULT_LOCAL_TCP_PORT: u16 = 44322; -pub const DEFAULT_LOCAL_UDP_PORT: u16 = 53445; -pub const DEFAULT_BASE_TARGET_UDP_PORT: u16 = 33435; -pub const DEFAULT_HOP_LIMIT: u8 = 64; -pub const DEFAULT_PING_COUNT: u32 = 4; -pub const DEFAULT_HOSTS_CONCURRENCY: usize = 50; -pub const DEFAULT_PORTS_CONCURRENCY: usize = 100; -pub const PCAP_WAIT_TIME_MILLIS: u64 = 10; +use std::path::PathBuf; -// Database -pub const DEFAULT_PORTS_BIN: &[u8] = include_bytes!("../../resources/ndb-default-ports.bin"); -pub const HTTP_PORTS_BIN: &[u8] = include_bytes!("../../resources/ndb-http-ports.bin"); -pub const HTTPS_PORTS_BIN: &[u8] = include_bytes!("../../resources/ndb-https-ports.bin"); -pub const OS_FAMILY_FINGERPRINT_BIN: &[u8] = - include_bytes!("../../resources/ndb-os-family-fingerprint.bin"); -pub const OS_TTL_BIN: &[u8] = include_bytes!("../../resources/ndb-os-ttl.bin"); -pub const OS_FAMILY_BIN: &[u8] = include_bytes!("../../resources/ndb-os-family.bin"); -pub const OUI_BIN: &[u8] = include_bytes!("../../resources/ndb-oui.bin"); -pub const OUI_VM_BIN: &[u8] = include_bytes!("../../resources/ndb-oui-vm.bin"); -pub const SUBDOMAIN_BIN: &[u8] = include_bytes!("../../resources/ndb-subdomain.bin"); -pub const TCP_SERVICE_BIN: &[u8] = include_bytes!("../../resources/ndb-tcp-service.bin"); -pub const WELLKNOWN_PORTS_BIN: &[u8] = include_bytes!("../../resources/ndb-wellknown-ports.bin"); +pub mod db; +pub mod default; + +/// User configuration directory name +pub const USER_CONFIG_DIR_NAME: &str = ".nrev"; + +/// Get user configuration directory path, create it if not exists +pub fn get_config_dir_path() -> Option { + match home::home_dir() { + Some(mut path) => { + path.push(USER_CONFIG_DIR_NAME); + if !path.exists() { + match std::fs::create_dir_all(&path) { + Ok(_) => {} + Err(e) => { + tracing::error!("Failed to create config dir: {:?}", e); + return None; + } + } + } + Some(path) + } + None => None, + } +} + +/// Get user configuration file path +pub fn get_user_file_path(file_name: &str) -> Option { + match get_config_dir_path() { + Some(mut path) => { + path.push(file_name); + Some(path) + } + None => None, + } +} diff --git a/src/db/domain.rs b/src/db/domain.rs new file mode 100644 index 0000000..925a2c2 --- /dev/null +++ b/src/db/domain.rs @@ -0,0 +1,6 @@ +use crate::config::db::TOP_SUBDOMAIN_WORDS_JSON; + +/// Get top subdomain wordlist +pub fn get_subdomain_wordlist() -> Vec { + serde_json::from_str(TOP_SUBDOMAIN_WORDS_JSON).unwrap_or_default() +} diff --git a/src/db/mod.rs b/src/db/mod.rs index 508e5b5..61ab9e3 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,402 +1,129 @@ -pub mod model; -pub mod tcp_service; -use crate::packet::frame::PacketFrame; -use nex::packet::ethernet::EthernetHeader; - -use crate::config; -use crate::ip; -use std::collections::HashMap; -use std::net::IpAddr; - -pub fn get_oui_detail_map() -> HashMap { - let mut oui_map: HashMap = HashMap::new(); - let ds_oui: Vec = bincode::deserialize(config::OUI_BIN).unwrap_or(vec![]); - for oui in ds_oui { - oui_map.insert(oui.mac_prefix, oui.vendor_name_detail); - } - oui_map -} - -pub fn get_vm_oui_map() -> HashMap { - let mut oui_map: HashMap = HashMap::new(); - let ds_oui: Vec = bincode::deserialize(config::OUI_VM_BIN).unwrap_or(vec![]); - for oui in ds_oui { - oui_map.insert(oui.mac_prefix, oui.vendor_name_detail); - } - oui_map -} - -pub fn get_tcp_map() -> HashMap { - let mut tcp_map: HashMap = HashMap::new(); - let ds_tcp_service: Vec = - bincode::deserialize(config::TCP_SERVICE_BIN).unwrap_or(vec![]); - for port in ds_tcp_service { - tcp_map.insert(port.port, port.service_name); - } - tcp_map -} - -pub fn get_default_ports() -> Vec { - let default_ports: Vec = bincode::deserialize(config::DEFAULT_PORTS_BIN).unwrap_or(vec![]); - default_ports -} - -pub fn get_wellknown_ports() -> Vec { - let wellknown_ports: Vec = - bincode::deserialize(config::WELLKNOWN_PORTS_BIN).unwrap_or(vec![]); - wellknown_ports -} - -pub fn get_http_ports() -> Vec { - let http_ports: Vec = bincode::deserialize(config::HTTP_PORTS_BIN).unwrap_or(vec![]); - http_ports -} - -pub fn get_https_ports() -> Vec { - let https_ports: Vec = bincode::deserialize(config::HTTPS_PORTS_BIN).unwrap_or(vec![]); - https_ports -} - -pub fn get_os_ttl_map() -> HashMap { - let mut os_ttl_map: HashMap = HashMap::new(); - let ds_os_ttl: Vec = bincode::deserialize(config::OS_TTL_BIN).unwrap_or(vec![]); - for os_ttl in ds_os_ttl { - os_ttl_map.insert(os_ttl.initial_ttl, os_ttl.os_description); - } - os_ttl_map -} - -pub fn get_os_ttl_list() -> Vec { - let ds_os_ttl: Vec = bincode::deserialize(config::OS_TTL_BIN).unwrap_or(vec![]); - ds_os_ttl -} - -pub fn get_subdomain() -> Vec { - let subdomain: Vec = bincode::deserialize(config::SUBDOMAIN_BIN).unwrap_or(vec![]); - subdomain -} - -pub fn get_os_family_fingerprints() -> Vec { - let ds_os_fingerprints: Vec = - bincode::deserialize(config::OS_FAMILY_FINGERPRINT_BIN).unwrap_or(vec![]); - ds_os_fingerprints -} - -pub fn get_os_family_list() -> Vec { - let os_families: Vec = bincode::deserialize(config::OS_FAMILY_BIN).unwrap_or(vec![]); - os_families -} - -pub fn is_vm_fingerprint(fingerprint: &model::OsFingerprint) -> bool { - if fingerprint.os_family == "Player".to_string() - && fingerprint.device_type == "specialized".to_string() - { - return true; - } - false -} - -pub fn in_vm_network(ether_header: EthernetHeader) -> bool { - let vm_oui_map: HashMap = get_vm_oui_map(); - let mac = ether_header.source.address(); - if mac.len() > 16 { - let prefix8 = mac[0..8].to_uppercase(); - vm_oui_map.contains_key(&prefix8) - } else { - vm_oui_map.contains_key(&mac) - } -} - -pub fn verify_os_family_fingerprint(fingerprint: &PacketFrame) -> model::OsFamilyFingerprint { - let os_family_list: Vec = get_os_family_list(); - let os_family_fingerprints: Vec = get_os_family_fingerprints(); - let in_vm: bool = if let Some(ether_header) = &fingerprint.ethernet_header { - in_vm_network(ether_header.clone()) - } else { - false - }; - - // 0. Check TTL - let os_ttl_list: Vec = get_os_ttl_list(); - let initial_ttl = if let Some(ipv4_header) = &fingerprint.ipv4_header { - ip::guess_initial_ttl(ipv4_header.ttl) - } else { - if let Some(ipv6_header) = &fingerprint.ipv6_header { - ip::guess_initial_ttl(ipv6_header.hop_limit) - } else { - 0 - } - }; - let mut tcp_window_size = 0; - let mut tcp_options: Vec = vec![]; - if let Some(ref tcp_header) = fingerprint.tcp_header { - tcp_window_size = tcp_header.window; - for option in &tcp_header.options { - tcp_options.push(option.kind.name()); - } - } - let tco_option_pattern = tcp_options.join("-"); - let mut os_ttl_info: model::OsTtl = model::OsTtl { - initial_ttl: initial_ttl, - os_description: String::new(), - os_family: String::new(), - }; - for os_ttl in os_ttl_list { - if os_ttl.initial_ttl == initial_ttl { - os_ttl_info.initial_ttl = os_ttl.initial_ttl; - os_ttl_info.os_description = os_ttl.os_description; - os_ttl_info.os_family = os_ttl.os_family; - } - } - // 1. Select OS Fingerprint that match tcp_window_size and tcp_option_pattern - let mut matched_fingerprints: Vec = vec![]; - for f in &os_family_fingerprints { - let mut window_size_match: bool = false; - let mut option_pattern_match: bool = false; - if f.tcp_window_sizes.contains(&tcp_window_size) { - window_size_match = true; - } - for option_pattern in &f.tcp_option_patterns { - if option_pattern == &tco_option_pattern { - option_pattern_match = true; - } - } - if window_size_match && option_pattern_match { - matched_fingerprints.push(f.clone()); - } - } - if matched_fingerprints.len() == 1 { - return matched_fingerprints[0].clone(); - } else if matched_fingerprints.len() > 1 { - // Check VM Fingerprint - if in_vm { - for f in &matched_fingerprints { - if &f.os_family == "Player" { - let mut vmf = f.clone(); - vmf.os_family = format!("{} (Probably in VM Network)", vmf.os_family); - return vmf; - } - } - } - // Search fingerprint that match general OS Family - matched_fingerprints.reverse(); - for f in &matched_fingerprints { - if os_ttl_info.os_family == f.os_family.to_lowercase() { - return f.clone(); - } - } - for f in matched_fingerprints { - if os_family_list.contains(&f.os_family) { - return f; - } - } - } - // 2. Select OS Fingerprint that match tcp_option_pattern and have most closely tcp_window_size - let mut matched_fingerprints: Vec = vec![]; - for f in os_family_fingerprints { - let mut window_size_match: bool = false; - let mut option_pattern_match: bool = false; - for window_size in &f.tcp_window_sizes { - if tcp_window_size - 100 < *window_size && *window_size < tcp_window_size + 100 { - window_size_match = true; - } - } - for option_pattern in &f.tcp_option_patterns { - if option_pattern == &tco_option_pattern { - option_pattern_match = true; - } - } - if window_size_match && option_pattern_match { - matched_fingerprints.push(f.clone()); - } - } - if matched_fingerprints.len() == 1 { - return matched_fingerprints[0].clone(); - } else if matched_fingerprints.len() > 1 { - // Check VM Fingerprint - if in_vm { - for f in &matched_fingerprints { - if &f.os_family == "Player" { - let mut vmf = f.clone(); - vmf.os_family = format!("{} (Probably in VM network)", vmf.os_family); - return vmf; +pub mod service; +pub mod os; +pub mod port; +pub mod tls; +pub mod oui; +pub mod domain; + +use std::time::{Duration, Instant}; +use futures::StreamExt; +use anyhow::Result; + +/// Initialization function type +type InitFn = fn() -> Result<()>; + +/// Database initialization task +struct DbTask { + name: &'static str, + init: InitFn, +} + +const TASK_TCP_SERVICE: DbTask = DbTask { name: "tcp_service_db", init: || service::init_tcp_service_db() }; +const TASK_UDP_SERVICE: DbTask = DbTask { name: "udp_service_db", init: || service::init_udp_service_db() }; +const TASK_PORT_PROBE : DbTask = DbTask { name: "port_probe_db", init: || service::init_port_probe_db() }; +const TASK_SVC_PROBE : DbTask = DbTask { name: "service_probe_db", init: || service::init_service_probe_db() }; +const TASK_RESP_SIGS : DbTask = DbTask { name: "response_signatures", init: || service::init_response_signatures_db() }; +const TASK_TLS_OID : DbTask = DbTask { name: "tls_oid_map", init: || tls::init_tls_oid_map() }; +const TASK_OS_DB : DbTask = DbTask { name: "os_db", init: || os::init_os_db() }; +const TASK_OUI_DB : DbTask = DbTask { name: "oui_db", init: || oui::init_oui_db() }; + +/// Database initialization result record +#[derive(Debug, Clone)] +pub struct InitRecord { + pub name: &'static str, + pub elapsed: Duration, + pub ok: bool, + pub error: Option, +} + +/// Database initialization report +#[derive(Debug, Clone)] +pub struct InitReport { + pub total: Duration, + pub records: Vec, +} + +/// Database initializer (for once-only initialization) +pub struct DbInitializer { + tasks: Vec<&'static DbTask>, +} + +impl DbInitializer { + /// Create new initializer + pub fn new() -> Self { Self { tasks: Vec::new() } } + /// Add TCP service DB + pub fn with_tcp_services(mut self) -> Self { self.tasks.push(&TASK_TCP_SERVICE); self } + /// Add UDP service DB + pub fn with_udp_services(mut self) -> Self { self.tasks.push(&TASK_UDP_SERVICE); self } + /// Add port probe DB + pub fn with_port_probe(mut self) -> Self { self.tasks.push(&TASK_PORT_PROBE); self } + /// Add service probe DB + pub fn with_service_probe(mut self)-> Self { self.tasks.push(&TASK_SVC_PROBE); self } + /// Add response signatures DB + pub fn with_response_sigs(mut self)-> Self { self.tasks.push(&TASK_RESP_SIGS); self } + /// Add TLS OID map + pub fn with_tls_oids(mut self) -> Self { self.tasks.push(&TASK_TLS_OID); self } + /// Add OS DB + pub fn with_os_db(mut self) -> Self { self.tasks.push(&TASK_OS_DB); self } + /// Add OUI DB + pub fn with_oui_db(mut self) -> Self { self.tasks.push(&TASK_OUI_DB); self } + /// Add all databases + pub fn with_all() -> Self { + Self::new() + .with_tcp_services() + .with_udp_services() + .with_port_probe() + .with_service_probe() + .with_response_sigs() + .with_tls_oids() + .with_os_db() + .with_oui_db() + } + + /// Run initialization tasks, return report + pub async fn init(self) -> InitReport { + use futures::stream; + + // Remove duplicate tasks (even if the same preset is stacked, it will only be done once) + let mut uniq: Vec<&'static DbTask> = Vec::new(); + for t in self.tasks { + if !uniq.iter().any(|u| u.name == t.name) { + uniq.push(t); + } + } + + let uniq_count = uniq.len(); + tracing::debug!("Initializing databases ({} task(s))...", uniq_count); + let t0 = Instant::now(); + + let results = stream::iter(uniq) + .map(|task| async move { + let start = Instant::now(); + let res = (task.init)(); + let ok = res.is_ok(); + let err = res.err().map(|e| e.to_string()); + InitRecord { + name: task.name, + elapsed: start.elapsed(), + ok, + error: err, } - } - } - // Search fingerprint that match general OS Family - matched_fingerprints.reverse(); - for f in &matched_fingerprints { - if os_ttl_info.os_family == f.os_family.to_lowercase() { - return f.clone(); - } - } - for f in matched_fingerprints { - if os_family_list.contains(&f.os_family) { - return f; - } - } - } - // 3. from TTL - return model::OsFamilyFingerprint { - os_family: os_ttl_info.os_family, - tcp_window_sizes: vec![tcp_window_size], - tcp_option_patterns: vec![tco_option_pattern], - }; -} + }) + .buffer_unordered(uniq_count) + .collect::>() + .await; -pub fn get_os_family( - fingerprint: &PacketFrame, - os_family_list: &Vec, - os_family_fingerprints: &Vec, -) -> model::OsFamilyFingerprint { - let in_vm: bool = if let Some(ether_header) = &fingerprint.ethernet_header { - in_vm_network(ether_header.clone()) - } else { - false - }; + let total = t0.elapsed(); - // 0. Check TTL - let os_ttl_list: Vec = get_os_ttl_list(); - let initial_ttl = if let Some(ipv4_header) = &fingerprint.ipv4_header { - ip::guess_initial_ttl(ipv4_header.ttl) - } else { - if let Some(ipv6_header) = &fingerprint.ipv6_header { - ip::guess_initial_ttl(ipv6_header.hop_limit) - } else { - 0 - } - }; - let mut tcp_window_size = 0; - let mut tcp_options: Vec = vec![]; - if let Some(ref tcp_header) = fingerprint.tcp_header { - tcp_window_size = tcp_header.window; - for option in &tcp_header.options { - tcp_options.push(option.kind.name()); - } - } - let tco_option_pattern = tcp_options.join("-"); - let mut os_ttl_info: model::OsTtl = model::OsTtl { - initial_ttl: initial_ttl, - os_description: String::new(), - os_family: String::new(), - }; - for os_ttl in os_ttl_list { - if os_ttl.initial_ttl == initial_ttl { - os_ttl_info.initial_ttl = os_ttl.initial_ttl; - os_ttl_info.os_description = os_ttl.os_description; - os_ttl_info.os_family = os_ttl.os_family; - } - } - // 1. Select OS Fingerprint that match tcp_window_size and tcp_option_pattern - let mut matched_fingerprints: Vec = vec![]; - for f in os_family_fingerprints { - let mut window_size_match: bool = false; - let mut option_pattern_match: bool = false; - if f.tcp_window_sizes.contains(&tcp_window_size) { - window_size_match = true; - } - for option_pattern in &f.tcp_option_patterns { - if option_pattern == &tco_option_pattern { - option_pattern_match = true; + for r in &results { + if r.ok { + tracing::debug!("DB init ok: {} ({:?})", r.name, r.elapsed); + } else { + tracing::error!("DB init failed: {} ({:?}) - {}", r.name, r.elapsed, r.error.as_deref().unwrap_or("?")); } } - if window_size_match && option_pattern_match { - matched_fingerprints.push(f.clone()); - } - } - if matched_fingerprints.len() == 1 { - return matched_fingerprints[0].clone(); - } else if matched_fingerprints.len() > 1 { - // Check VM Fingerprint - if in_vm { - for f in &matched_fingerprints { - if &f.os_family == "Player" { - let mut vmf = f.clone(); - vmf.os_family = format!("{} (Probably in VM Network)", vmf.os_family); - return vmf; - } - } - } - // Search fingerprint that match general OS Family - matched_fingerprints.reverse(); - for f in &matched_fingerprints { - if os_ttl_info.os_family == f.os_family.to_lowercase() { - return f.clone(); - } - } - for f in matched_fingerprints { - if os_family_list.contains(&f.os_family) { - return f; - } - } - } - // 2. Select OS Fingerprint that match tcp_option_pattern and have most closely tcp_window_size - let mut matched_fingerprints: Vec = vec![]; - for f in os_family_fingerprints { - let mut window_size_match: bool = false; - let mut option_pattern_match: bool = false; - for window_size in &f.tcp_window_sizes { - if tcp_window_size - 100 < *window_size && *window_size < tcp_window_size + 100 { - window_size_match = true; - } - } - for option_pattern in &f.tcp_option_patterns { - if option_pattern == &tco_option_pattern { - option_pattern_match = true; - } - } - if window_size_match && option_pattern_match { - matched_fingerprints.push(f.clone()); - } - } - if matched_fingerprints.len() == 1 { - return matched_fingerprints[0].clone(); - } else if matched_fingerprints.len() > 1 { - // Check VM Fingerprint - if in_vm { - for f in &matched_fingerprints { - if &f.os_family == "Player" { - let mut vmf = f.clone(); - vmf.os_family = format!("{} (Probably in VM network)", vmf.os_family); - return vmf; - } - } - } - // Search fingerprint that match general OS Family - matched_fingerprints.reverse(); - for f in &matched_fingerprints { - if os_ttl_info.os_family == f.os_family.to_lowercase() { - return f.clone(); - } - } - for f in matched_fingerprints { - if os_family_list.contains(&f.os_family) { - return f; - } - } - } - // 3. from TTL - return model::OsFamilyFingerprint { - os_family: os_ttl_info.os_family, - tcp_window_sizes: vec![tcp_window_size], - tcp_option_patterns: vec![tco_option_pattern], - }; -} + tracing::debug!("DB init done: {:?} total", total); -// return HashMap -pub fn get_fingerprint_map(fingerprints: &Vec) -> HashMap { - let mut fingerprint_map: HashMap = HashMap::new(); - let os_family_list: Vec = get_os_family_list(); - let os_family_fingerprints: Vec = get_os_family_fingerprints(); - for f in fingerprints { - let os_fingerprint = get_os_family(&f, &os_family_list, &os_family_fingerprints); - if let Some(ipv4_header) = &f.ipv4_header { - fingerprint_map.insert(IpAddr::V4(ipv4_header.source), os_fingerprint.os_family); - } else { - if let Some(ipv6_header) = &f.ipv6_header { - fingerprint_map.insert(IpAddr::V6(ipv6_header.source), os_fingerprint.os_family); - } - } + InitReport { total, records: results } } - fingerprint_map } diff --git a/src/db/model.rs b/src/db/model.rs deleted file mode 100644 index a977eca..0000000 --- a/src/db/model.rs +++ /dev/null @@ -1,67 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Oui { - pub mac_prefix: String, - pub vendor_name: String, - pub vendor_name_detail: String, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct TcpService { - pub port: u16, - pub service_name: String, - pub service_description: String, - pub wellknown_flag: u32, - pub default_flag: u32, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct UdpService { - pub port: u16, - pub service_name: String, - pub service_description: String, - pub wellknown_flag: u32, - pub default_flag: u32, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct OsFingerprint { - pub cpe: String, - pub os_name: String, - pub os_vendor: String, - pub os_family: String, - pub os_generation: String, - pub device_type: String, - pub tcp_window_sizes: Vec, - pub tcp_option_patterns: Vec, -} - -impl OsFingerprint { - pub fn new() -> OsFingerprint { - OsFingerprint { - cpe: String::new(), - os_name: String::new(), - os_vendor: String::new(), - os_family: String::new(), - os_generation: String::new(), - device_type: String::new(), - tcp_window_sizes: vec![], - tcp_option_patterns: vec![], - } - } -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct OsFamilyFingerprint { - pub os_family: String, - pub tcp_window_sizes: Vec, - pub tcp_option_patterns: Vec, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct OsTtl { - pub os_family: String, - pub os_description: String, - pub initial_ttl: u8, -} diff --git a/src/db/os.rs b/src/db/os.rs new file mode 100644 index 0000000..ee0a34f --- /dev/null +++ b/src/db/os.rs @@ -0,0 +1,42 @@ +use std::{collections::HashMap, sync::OnceLock}; +use anyhow::Result; +use crate::{config, os::{OsClass, OsClassTtl, OsDb}}; + +pub static OS_DB: OnceLock = OnceLock::new(); + +/// Initialize OS database +pub fn init_os_db() -> Result<()> { + let os_db: OsDb = serde_json::from_str(config::db::OS_DB_JSON) + .expect("Invalid nrev-os-db.json format"); + OS_DB + .set(os_db) + .map_err(|_| anyhow::anyhow!("Failed to set OS_DB in OnceLock"))?; + Ok(()) +} + +/// Get reference to OS database +pub fn os_db() -> &'static OsDb { + OS_DB.get().expect("OS_DB not initialized") +} + +/// Get initial TTL to OS class mapping +pub fn get_ttl_class_map() -> HashMap { + let mut ttl_class_map: HashMap = HashMap::new(); + let ds_os_ttl: Vec = + serde_json::from_str(config::db::OS_CLASS_TTL_JSON).expect("Invalid os-class-ttl.json format"); + for os_ttl in ds_os_ttl { + ttl_class_map.insert(os_ttl.initial_ttl, os_ttl.os_class.as_str().to_string()); + } + ttl_class_map +} + +/// Get initial TTL mapping +pub fn get_class_ttl_map() -> HashMap { + let mut class_ttl_map: HashMap = HashMap::new(); + let ds_os_ttl: Vec = + serde_json::from_str(config::db::OS_CLASS_TTL_JSON).expect("Invalid os-class-ttl.json format"); + for os_ttl in ds_os_ttl { + class_ttl_map.insert(os_ttl.os_class, os_ttl.initial_ttl); + } + class_ttl_map +} diff --git a/src/db/oui.rs b/src/db/oui.rs new file mode 100644 index 0000000..c0f0ff4 --- /dev/null +++ b/src/db/oui.rs @@ -0,0 +1,19 @@ +use ndb_oui::OuiDb; +use anyhow::Result; +use std::sync::OnceLock; + +pub static OUI_DB: OnceLock = OnceLock::new(); + +/// Initialize OUI database +pub fn init_oui_db() -> Result<()> { + let oui_db = OuiDb::bundled(); + OUI_DB + .set(oui_db) + .map_err(|_| anyhow::anyhow!("Failed to set OUI_DB in OnceLock"))?; + Ok(()) +} + +/// Get reference to OUI database +pub fn oui_db() -> &'static OuiDb { + OUI_DB.get().expect("OUI_DB not initialized") +} diff --git a/src/db/port.rs b/src/db/port.rs new file mode 100644 index 0000000..08d0ac6 --- /dev/null +++ b/src/db/port.rs @@ -0,0 +1,15 @@ +use crate::config; + +/// Get default port list +pub fn get_default_ports() -> Vec { + let default_ports: Vec = serde_json::from_str(config::db::DEFAULT_PORTS_JSON) + .expect("Invalid default-ports.json format"); + default_ports +} + +/// Get well-known port list +pub fn get_wellknown_ports() -> Vec { + let wellknown_ports: Vec = serde_json::from_str(config::db::WELLKNOWN_PORTS_JSON) + .expect("Invalid wellknown-ports.json format"); + wellknown_ports +} diff --git a/src/db/service.rs b/src/db/service.rs new file mode 100644 index 0000000..9c9621f --- /dev/null +++ b/src/db/service.rs @@ -0,0 +1,161 @@ +use ndb_tcp_service::TcpServiceDb; +use ndb_udp_service::UdpServiceDb; +use anyhow::Result; +use std::{collections::HashMap, sync::OnceLock}; + +use crate::{config, endpoint::Port, service::probe::{PortProbeDb, ProbePayload, ProbePayloadDb, ResponseSignature, ResponseSignaturesDb, ServiceProbe}}; + +pub static TCP_SERVICE_DB: OnceLock = OnceLock::new(); +pub static UDP_SERVICE_DB: OnceLock = OnceLock::new(); +pub static PORT_PROBE_DB: OnceLock>> = OnceLock::new(); +pub static SERVICE_PROBE_DB: OnceLock> = OnceLock::new(); +pub static RESPONSE_SIGNATURES_DB: OnceLock> = OnceLock::new(); + +/// Get a reference to the initialized TCP service database. +pub fn tcp_service_db() -> &'static TcpServiceDb { + TCP_SERVICE_DB.get().expect("TCP_SERVICE_DB not initialized") +} + +/// Get a reference to the initialized UDP service database. +pub fn udp_service_db() -> &'static UdpServiceDb { + UDP_SERVICE_DB.get().expect("UDP_SERVICE_DB not initialized") +} + +/// Get a reference to the initialized Port Probe database. +pub fn port_probe_db() -> &'static HashMap> { + PORT_PROBE_DB.get().expect("PORT_PROBE_DB not initialized") +} + +/// Get a reference to the initialized Service Probe database. +pub fn service_probe_db() -> &'static HashMap { + SERVICE_PROBE_DB.get().expect("SERVICE_PROBE_DB not initialized") +} + +/// Get a reference to the initialized Response Signatures database. +pub fn response_signatures_db() -> &'static Vec { + RESPONSE_SIGNATURES_DB.get().expect("RESPONSE_SIGNATURES_DB not initialized") +} + +/// Initialize TCP Service database +pub fn init_tcp_service_db() -> Result<()> { + let tcp_service_db = TcpServiceDb::bundled(); + TCP_SERVICE_DB + .set(tcp_service_db) + .map_err(|_| anyhow::anyhow!("Failed to set TCP_SERVICE_DB in OnceLock"))?; + Ok(()) +} + +/// Initialize UDP Service database +pub fn init_udp_service_db() -> Result<()> { + let udp_service_db = UdpServiceDb::bundled(); + UDP_SERVICE_DB + .set(udp_service_db) + .map_err(|_| anyhow::anyhow!("Failed to set UDP_SERVICE_DB in OnceLock"))?; + Ok(()) +} + +/// Initialize Port Probe database +pub fn init_port_probe_db() -> Result<()> { + let port_probe_db: PortProbeDb = serde_json::from_str(config::db::PORT_PROBES_JSON) + .expect("Invalid port-probes.json format"); + + let mut map: HashMap> = HashMap::new(); + for (port, probes) in port_probe_db.map { + let service_probes: Vec = probes + .into_iter() + .map(|probe| ServiceProbe::from_str(&probe).expect("Invalid service probe format")) + .collect(); + for service_probe in service_probes { + let port = Port::new(port, service_probe.transport()); + map.entry(port).or_insert_with(Vec::new).push(service_probe); + } + } + PORT_PROBE_DB + .set(map) + .map_err(|_| anyhow::anyhow!("Failed to set PORT_PROBE_DB in OnceLock"))?; + Ok(()) +} + +/// Initialize Service Probe database +pub fn init_service_probe_db() -> Result<()> { + let probe_payload_db: ProbePayloadDb = serde_json::from_str(config::db::SERVICE_PROBES_JSON) + .expect("Invalid service-probes.json format"); + let mut service_probe_map: HashMap = HashMap::new(); + for probe_payload in probe_payload_db.probes { + let service_probe: ServiceProbe = ServiceProbe::from_str(&probe_payload.id) + .expect("Invalid service probe format"); + service_probe_map.insert(service_probe, probe_payload); + } + SERVICE_PROBE_DB + .set(service_probe_map) + .map_err(|_| anyhow::anyhow!("Failed to set SERVICE_PROBE_DB in OnceLock"))?; + Ok(()) +} + +/// Initialize Response Signatures database +pub fn init_response_signatures_db() -> Result<()> { + let response_signatures_db: ResponseSignaturesDb = serde_json::from_str(config::db::SERVICE_DB_JSON) + .expect("Invalid nrev-service-db.json format"); + RESPONSE_SIGNATURES_DB + .set(response_signatures_db.signatures) + .map_err(|_| anyhow::anyhow!("Failed to set RESPONSE_SIGNATURES_DB in OnceLock"))?; + Ok(()) +} + +/// Get the service name for a given TCP port +pub fn get_tcp_service_name(port: u16) -> Option { + match TCP_SERVICE_DB.get() { + Some(db) => { + match db.get(port) { + Some(service) => Some(service.name.clone()), + None => None, + } + } + None => None, + } +} + +/// Get the service names for a list of TCP ports +pub fn get_tcp_service_names(ports: &[u16]) -> HashMap { + let mut result = HashMap::new(); + if let Some(db) = TCP_SERVICE_DB.get() { + for &port in ports { + if let Some(service) = db.get(port) { + result.insert(port, service.name.clone()); + } else { + result.insert(port, "Unknown".to_string()); + } + } + } + result +} + +/// Get the map of port to service probes +pub fn get_port_probes() -> HashMap> { + let mut port_probe_map: HashMap> = HashMap::new(); + let port_probe_db: PortProbeDb = serde_json::from_str(config::db::PORT_PROBES_JSON).expect("Invalid os-ttl.json format"); + for (port, probes) in port_probe_db.map { + for probe in probes { + let service_probe: ServiceProbe = ServiceProbe::from_str(&probe).expect("Invalid service probe format"); + port_probe_map.entry(port).or_insert_with(Vec::new).push(service_probe); + } + } + port_probe_map +} + +/// Get the map of service probes to their payloads +pub fn get_service_probes() -> HashMap { + let mut service_probe_map: HashMap = HashMap::new(); + let probe_payload_db: ProbePayloadDb = serde_json::from_str(config::db::SERVICE_PROBES_JSON).expect("Invalid service-probes.json format"); + for probe_payload in probe_payload_db.probes { + let service_probe: ServiceProbe = ServiceProbe::from_str(&probe_payload.id).expect("Invalid service probe format"); + service_probe_map.insert(service_probe, probe_payload); + } + service_probe_map +} + +/// Get the list of response signatures +pub fn get_service_response_signatures() -> Vec { + let response_signatures_db: ResponseSignaturesDb = serde_json::from_str(config::db::SERVICE_DB_JSON).expect("Invalid nrev-service-os-db.json format"); + response_signatures_db.signatures +} diff --git a/src/db/tcp_service.rs b/src/db/tcp_service.rs deleted file mode 100644 index 7bab6b4..0000000 --- a/src/db/tcp_service.rs +++ /dev/null @@ -1,5847 +0,0 @@ -use phf::{phf_map, Map}; - -pub(crate) static PORT_SERVICE_MAP: Map = phf_map! { - 1u16 => "tcpmux", - 2u16 => "compressnet", - 3u16 => "compressnet", - 5u16 => "rje", - 7u16 => "echo", - 9u16 => "discard", - 11u16 => "systat", - 13u16 => "daytime", - 17u16 => "qotd", - 18u16 => "msp", - 19u16 => "chargen", - 20u16 => "ftp-data", - 21u16 => "ftp", - 22u16 => "ssh", - 23u16 => "telnet", - 25u16 => "smtp", - 27u16 => "nsw-fe", - 29u16 => "msg-icp", - 31u16 => "msg-auth", - 33u16 => "dsp", - 37u16 => "time", - 38u16 => "rap", - 39u16 => "rlp", - 41u16 => "graphics", - 42u16 => "nameserver", - 43u16 => "nicname", - 44u16 => "mpm-flags", - 45u16 => "mpm", - 46u16 => "mpm-snd", - 48u16 => "auditd", - 49u16 => "tacacs", - 50u16 => "re-mail-ck", - 52u16 => "xns-time", - 53u16 => "domain", - 54u16 => "xns-ch", - 55u16 => "isi-gl", - 56u16 => "xns-auth", - 58u16 => "xns-mail", - 62u16 => "acas", - 63u16 => "whois++", - 64u16 => "covia", - 65u16 => "tacacs-ds", - 66u16 => "sql*net", - 67u16 => "bootps", - 68u16 => "bootpc", - 69u16 => "tftp", - 70u16 => "gopher", - 71u16 => "netrjs-1", - 72u16 => "netrjs-2", - 73u16 => "netrjs-3", - 74u16 => "netrjs-4", - 76u16 => "deos", - 78u16 => "vettcp", - 79u16 => "finger", - 80u16 => "http", - 82u16 => "xfer", - 83u16 => "mit-ml-dev", - 84u16 => "ctf", - 85u16 => "mit-ml-dev", - 86u16 => "mfcobol", - 88u16 => "kerberos", - 89u16 => "su-mit-tg", - 90u16 => "dnsix", - 91u16 => "mit-dov", - 92u16 => "npp", - 93u16 => "dcp", - 94u16 => "objcall", - 95u16 => "supdup", - 96u16 => "dixie", - 97u16 => "swift-rvf", - 98u16 => "tacnews", - 99u16 => "metagram", - 101u16 => "hostname", - 102u16 => "iso-tsap", - 103u16 => "gppitnp", - 104u16 => "acr-nema", - 105u16 => "csnet-ns", - 106u16 => "3com-tsmux", - 107u16 => "rtelnet", - 108u16 => "snagas", - 109u16 => "pop2", - 110u16 => "pop3", - 111u16 => "sunrpc", - 112u16 => "mcidas", - 113u16 => "auth", - 115u16 => "sftp", - 116u16 => "ansanotify", - 117u16 => "uucp-path", - 118u16 => "sqlserv", - 119u16 => "nntp", - 120u16 => "cfdptkt", - 121u16 => "erpc", - 122u16 => "smakynet", - 123u16 => "ntp", - 124u16 => "ansatrader", - 125u16 => "locus-map", - 126u16 => "nxedit", - 127u16 => "locus-con", - 128u16 => "gss-xlicen", - 129u16 => "pwdgen", - 130u16 => "cisco-fna", - 131u16 => "cisco-tna", - 132u16 => "cisco-sys", - 133u16 => "statsrv", - 134u16 => "ingres-net", - 135u16 => "epmap", - 136u16 => "profile", - 137u16 => "netbios-ns", - 138u16 => "netbios-dgm", - 139u16 => "netbios-ssn", - 140u16 => "emfis-data", - 141u16 => "emfis-cntl", - 142u16 => "bl-idm", - 143u16 => "imap", - 144u16 => "uma", - 145u16 => "uaac", - 146u16 => "iso-tp0", - 147u16 => "iso-ip", - 148u16 => "jargon", - 149u16 => "aed-512", - 150u16 => "sql-net", - 151u16 => "hems", - 152u16 => "bftp", - 153u16 => "sgmp", - 154u16 => "netsc-prod", - 155u16 => "netsc-dev", - 156u16 => "sqlsrv", - 157u16 => "knet-cmp", - 158u16 => "pcmail-srv", - 159u16 => "nss-routing", - 160u16 => "sgmp-traps", - 161u16 => "snmp", - 162u16 => "snmptrap", - 163u16 => "cmip-man", - 164u16 => "cmip-agent", - 165u16 => "xns-courier", - 166u16 => "s-net", - 167u16 => "namp", - 168u16 => "rsvd", - 169u16 => "send", - 170u16 => "print-srv", - 171u16 => "multiplex", - 172u16 => "cl/1", - 173u16 => "xyplex-mux", - 174u16 => "mailq", - 175u16 => "vmnet", - 176u16 => "genrad-mux", - 177u16 => "xdmcp", - 178u16 => "nextstep", - 179u16 => "bgp", - 180u16 => "ris", - 181u16 => "unify", - 182u16 => "audit", - 183u16 => "ocbinder", - 184u16 => "ocserver", - 185u16 => "remote-kis", - 186u16 => "kis", - 187u16 => "aci", - 188u16 => "mumps", - 189u16 => "qft", - 190u16 => "gacp", - 191u16 => "prospero", - 192u16 => "osu-nms", - 193u16 => "srmp", - 194u16 => "irc", - 195u16 => "dn6-nlm-aud", - 196u16 => "dn6-smm-red", - 197u16 => "dls", - 198u16 => "dls-mon", - 199u16 => "smux", - 200u16 => "src", - 201u16 => "at-rtmp", - 202u16 => "at-nbp", - 203u16 => "at-3", - 204u16 => "at-echo", - 205u16 => "at-5", - 206u16 => "at-zis", - 207u16 => "at-7", - 208u16 => "at-8", - 209u16 => "qmtp", - 210u16 => "z39.50", - 211u16 => "914c/g", - 212u16 => "anet", - 213u16 => "ipx", - 214u16 => "vmpwscs", - 215u16 => "softpc", - 216u16 => "CAIlic", - 217u16 => "dbase", - 218u16 => "mpp", - 219u16 => "uarps", - 220u16 => "imap3", - 221u16 => "fln-spx", - 222u16 => "rsh-spx", - 223u16 => "cdc", - 224u16 => "masqdialer", - 242u16 => "direct", - 243u16 => "sur-meas", - 244u16 => "inbusiness", - 245u16 => "link", - 246u16 => "dsp3270", - 247u16 => "subntbcst_tftp", - 248u16 => "bhfhs", - 256u16 => "rap", - 257u16 => "set", - 259u16 => "esro-gen", - 260u16 => "openport", - 261u16 => "nsiiops", - 262u16 => "arcisdms", - 263u16 => "hdap", - 264u16 => "bgmp", - 265u16 => "x-bone-ctl", - 266u16 => "sst", - 267u16 => "td-service", - 268u16 => "td-replica", - 269u16 => "manet", - 271u16 => "pt-tls", - 280u16 => "http-mgmt", - 281u16 => "personal-link", - 282u16 => "cableport-ax", - 283u16 => "rescap", - 284u16 => "corerjd", - 286u16 => "fxp", - 287u16 => "k-block", - 308u16 => "novastorbakcup", - 309u16 => "entrusttime", - 310u16 => "bhmds", - 311u16 => "asip-webadmin", - 312u16 => "vslmp", - 313u16 => "magenta-logic", - 314u16 => "opalis-robot", - 315u16 => "dpsi", - 316u16 => "decauth", - 317u16 => "zannet", - 318u16 => "pkix-timestamp", - 319u16 => "ptp-event", - 320u16 => "ptp-general", - 321u16 => "pip", - 322u16 => "rtsps", - 323u16 => "rpki-rtr", - 324u16 => "rpki-rtr-tls", - 333u16 => "texar", - 344u16 => "pdap", - 345u16 => "pawserv", - 346u16 => "zserv", - 347u16 => "fatserv", - 348u16 => "csi-sgwp", - 349u16 => "mftp", - 350u16 => "matip-type-a", - 351u16 => "bhoetty", - 352u16 => "bhoedap4", - 353u16 => "ndsauth", - 354u16 => "bh611", - 355u16 => "datex-asn", - 356u16 => "cloanto-net-1", - 357u16 => "bhevent", - 358u16 => "shrinkwrap", - 359u16 => "nsrmp", - 360u16 => "scoi2odialog", - 361u16 => "semantix", - 362u16 => "srssend", - 363u16 => "rsvp_tunnel", - 364u16 => "aurora-cmgr", - 365u16 => "dtk", - 366u16 => "odmr", - 367u16 => "mortgageware", - 368u16 => "qbikgdp", - 369u16 => "rpc2portmap", - 370u16 => "codaauth2", - 371u16 => "clearcase", - 372u16 => "ulistproc", - 373u16 => "legent-1", - 374u16 => "legent-2", - 375u16 => "hassle", - 376u16 => "nip", - 377u16 => "tnETOS", - 378u16 => "dsETOS", - 379u16 => "is99c", - 380u16 => "is99s", - 381u16 => "hp-collector", - 382u16 => "hp-managed-node", - 383u16 => "hp-alarm-mgr", - 384u16 => "arns", - 385u16 => "ibm-app", - 386u16 => "asa", - 387u16 => "aurp", - 388u16 => "unidata-ldm", - 389u16 => "ldap", - 390u16 => "uis", - 391u16 => "synotics-relay", - 392u16 => "synotics-broker", - 393u16 => "meta5", - 394u16 => "embl-ndt", - 395u16 => "netcp", - 396u16 => "netware-ip", - 397u16 => "mptn", - 398u16 => "kryptolan", - 399u16 => "iso-tsap-c2", - 400u16 => "osb-sd", - 401u16 => "ups", - 402u16 => "genie", - 403u16 => "decap", - 404u16 => "nced", - 405u16 => "ncld", - 406u16 => "imsp", - 407u16 => "timbuktu", - 408u16 => "prm-sm", - 409u16 => "prm-nm", - 410u16 => "decladebug", - 411u16 => "rmt", - 412u16 => "synoptics-trap", - 413u16 => "smsp", - 414u16 => "infoseek", - 415u16 => "bnet", - 416u16 => "silverplatter", - 417u16 => "onmux", - 418u16 => "hyper-g", - 419u16 => "ariel1", - 420u16 => "smpte", - 421u16 => "ariel2", - 422u16 => "ariel3", - 423u16 => "opc-job-start", - 424u16 => "opc-job-track", - 425u16 => "icad-el", - 426u16 => "smartsdp", - 427u16 => "svrloc", - 428u16 => "ocs_cmu", - 429u16 => "ocs_amu", - 430u16 => "utmpsd", - 431u16 => "utmpcd", - 432u16 => "iasd", - 433u16 => "nnsp", - 434u16 => "mobileip-agent", - 435u16 => "mobilip-mn", - 436u16 => "dna-cml", - 437u16 => "comscm", - 438u16 => "dsfgw", - 439u16 => "dasp", - 440u16 => "sgcp", - 441u16 => "decvms-sysmgt", - 442u16 => "cvc_hostd", - 443u16 => "https", - 444u16 => "snpp", - 445u16 => "microsoft-ds", - 446u16 => "ddm-rdb", - 447u16 => "ddm-dfm", - 448u16 => "ddm-ssl", - 449u16 => "as-servermap", - 450u16 => "tserver", - 451u16 => "sfs-smp-net", - 452u16 => "sfs-config", - 453u16 => "creativeserver", - 454u16 => "contentserver", - 455u16 => "creativepartnr", - 456u16 => "macon-tcp", - 457u16 => "scohelp", - 458u16 => "appleqtc", - 459u16 => "ampr-rcmd", - 460u16 => "skronk", - 461u16 => "datasurfsrv", - 462u16 => "datasurfsrvsec", - 463u16 => "alpes", - 464u16 => "kpasswd", - 465u16 => "submissions", - 466u16 => "digital-vrc", - 467u16 => "mylex-mapd", - 468u16 => "photuris", - 469u16 => "rcp", - 470u16 => "scx-proxy", - 471u16 => "mondex", - 472u16 => "ljk-login", - 473u16 => "hybrid-pop", - 474u16 => "tn-tl-w1", - 475u16 => "tcpnethaspsrv", - 476u16 => "tn-tl-fd1", - 477u16 => "ss7ns", - 478u16 => "spsc", - 479u16 => "iafserver", - 480u16 => "iafdbase", - 481u16 => "ph", - 482u16 => "bgs-nsi", - 483u16 => "ulpnet", - 484u16 => "integra-sme", - 485u16 => "powerburst", - 486u16 => "avian", - 487u16 => "saft", - 488u16 => "gss-http", - 489u16 => "nest-protocol", - 490u16 => "micom-pfs", - 491u16 => "go-login", - 492u16 => "ticf-1", - 493u16 => "ticf-2", - 494u16 => "pov-ray", - 495u16 => "intecourier", - 496u16 => "pim-rp-disc", - 497u16 => "retrospect", - 498u16 => "siam", - 499u16 => "iso-ill", - 500u16 => "isakmp", - 501u16 => "stmf", - 502u16 => "mbap", - 503u16 => "intrinsa", - 504u16 => "citadel", - 505u16 => "mailbox-lm", - 506u16 => "ohimsrv", - 507u16 => "crs", - 508u16 => "xvttp", - 509u16 => "snare", - 510u16 => "fcp", - 511u16 => "passgo", - 512u16 => "exec", - 513u16 => "login", - 514u16 => "shell", - 515u16 => "printer", - 516u16 => "videotex", - 517u16 => "talk", - 518u16 => "ntalk", - 519u16 => "utime", - 520u16 => "efs", - 521u16 => "ripng", - 522u16 => "ulp", - 523u16 => "ibm-db2", - 524u16 => "ncp", - 525u16 => "timed", - 526u16 => "tempo", - 527u16 => "stx", - 528u16 => "custix", - 529u16 => "irc-serv", - 530u16 => "courier", - 531u16 => "conference", - 532u16 => "netnews", - 533u16 => "netwall", - 534u16 => "windream", - 535u16 => "iiop", - 536u16 => "opalis-rdv", - 537u16 => "nmsp", - 538u16 => "gdomap", - 539u16 => "apertus-ldp", - 540u16 => "uucp", - 541u16 => "uucp-rlogin", - 542u16 => "commerce", - 543u16 => "klogin", - 544u16 => "kshell", - 545u16 => "appleqtcsrvr", - 546u16 => "dhcpv6-client", - 547u16 => "dhcpv6-server", - 548u16 => "afpovertcp", - 549u16 => "idfp", - 550u16 => "new-rwho", - 551u16 => "cybercash", - 552u16 => "devshr-nts", - 553u16 => "pirp", - 554u16 => "rtsp", - 555u16 => "dsf", - 556u16 => "remotefs", - 557u16 => "openvms-sysipc", - 558u16 => "sdnskmp", - 559u16 => "teedtap", - 560u16 => "rmonitor", - 561u16 => "monitor", - 562u16 => "chshell", - 563u16 => "nntps", - 564u16 => "9pfs", - 565u16 => "whoami", - 566u16 => "streettalk", - 567u16 => "banyan-rpc", - 568u16 => "ms-shuttle", - 569u16 => "ms-rome", - 570u16 => "meter", - 571u16 => "meter", - 572u16 => "sonar", - 573u16 => "banyan-vip", - 574u16 => "ftp-agent", - 575u16 => "vemmi", - 576u16 => "ipcd", - 577u16 => "vnas", - 578u16 => "ipdd", - 579u16 => "decbsrv", - 580u16 => "sntp-heartbeat", - 581u16 => "bdp", - 582u16 => "scc-security", - 583u16 => "philips-vc", - 584u16 => "keyserver", - 586u16 => "password-chg", - 587u16 => "submission", - 588u16 => "cal", - 589u16 => "eyelink", - 590u16 => "tns-cml", - 591u16 => "http-alt", - 592u16 => "eudora-set", - 593u16 => "http-rpc-epmap", - 594u16 => "tpip", - 595u16 => "cab-protocol", - 596u16 => "smsd", - 597u16 => "ptcnameservice", - 598u16 => "sco-websrvrmg3", - 599u16 => "acp", - 600u16 => "ipcserver", - 601u16 => "syslog-conn", - 602u16 => "xmlrpc-beep", - 603u16 => "idxp", - 604u16 => "tunnel", - 605u16 => "soap-beep", - 606u16 => "urm", - 607u16 => "nqs", - 608u16 => "sift-uft", - 609u16 => "npmp-trap", - 610u16 => "npmp-local", - 611u16 => "npmp-gui", - 612u16 => "hmmp-ind", - 613u16 => "hmmp-op", - 614u16 => "sshell", - 615u16 => "sco-inetmgr", - 616u16 => "sco-sysmgr", - 617u16 => "sco-dtmgr", - 618u16 => "dei-icda", - 619u16 => "compaq-evm", - 620u16 => "sco-websrvrmgr", - 621u16 => "escp-ip", - 622u16 => "collaborator", - 623u16 => "oob-ws-http", - 624u16 => "cryptoadmin", - 625u16 => "dec_dlm", - 626u16 => "asia", - 627u16 => "passgo-tivoli", - 628u16 => "qmqp", - 629u16 => "3com-amp3", - 630u16 => "rda", - 631u16 => "ipps", - 632u16 => "bmpp", - 633u16 => "servstat", - 634u16 => "ginad", - 635u16 => "rlzdbase", - 636u16 => "ldaps", - 637u16 => "lanserver", - 638u16 => "mcns-sec", - 639u16 => "msdp", - 640u16 => "entrust-sps", - 641u16 => "repcmd", - 642u16 => "esro-emsdp", - 643u16 => "sanity", - 644u16 => "dwr", - 645u16 => "pssc", - 646u16 => "ldp", - 647u16 => "dhcp-failover", - 648u16 => "rrp", - 649u16 => "cadview-3d", - 650u16 => "obex", - 651u16 => "ieee-mms", - 652u16 => "hello-port", - 653u16 => "repscmd", - 654u16 => "aodv", - 655u16 => "tinc", - 656u16 => "spmp", - 657u16 => "rmc", - 658u16 => "tenfold", - 660u16 => "mac-srvr-admin", - 661u16 => "hap", - 662u16 => "pftp", - 663u16 => "purenoise", - 664u16 => "oob-ws-https", - 665u16 => "sun-dr", - 666u16 => "doom", - 667u16 => "disclose", - 668u16 => "mecomm", - 669u16 => "meregister", - 670u16 => "vacdsm-sws", - 671u16 => "vacdsm-app", - 672u16 => "vpps-qua", - 673u16 => "cimplex", - 674u16 => "acap", - 675u16 => "dctp", - 676u16 => "vpps-via", - 677u16 => "vpp", - 678u16 => "ggf-ncp", - 679u16 => "mrm", - 680u16 => "entrust-aaas", - 681u16 => "entrust-aams", - 682u16 => "xfr", - 683u16 => "corba-iiop", - 684u16 => "corba-iiop-ssl", - 685u16 => "mdc-portmapper", - 686u16 => "hcp-wismar", - 687u16 => "asipregistry", - 688u16 => "realm-rusd", - 689u16 => "nmap", - 690u16 => "vatp", - 691u16 => "msexch-routing", - 692u16 => "hyperwave-isp", - 693u16 => "connendp", - 694u16 => "ha-cluster", - 695u16 => "ieee-mms-ssl", - 696u16 => "rushd", - 697u16 => "uuidgen", - 698u16 => "olsr", - 699u16 => "accessnetwork", - 700u16 => "epp", - 701u16 => "lmp", - 702u16 => "iris-beep", - 704u16 => "elcsd", - 705u16 => "agentx", - 706u16 => "silc", - 707u16 => "borland-dsj", - 709u16 => "entrust-kmsh", - 710u16 => "entrust-ash", - 711u16 => "cisco-tdp", - 712u16 => "tbrpf", - 713u16 => "iris-xpc", - 714u16 => "iris-xpcs", - 715u16 => "iris-lwz", - 729u16 => "netviewdm1", - 730u16 => "netviewdm2", - 731u16 => "netviewdm3", - 741u16 => "netgw", - 742u16 => "netrcs", - 744u16 => "flexlm", - 747u16 => "fujitsu-dev", - 748u16 => "ris-cm", - 749u16 => "kerberos-adm", - 750u16 => "rfile", - 751u16 => "pump", - 752u16 => "qrh", - 753u16 => "rrh", - 754u16 => "tell", - 758u16 => "nlogin", - 759u16 => "con", - 760u16 => "ns", - 761u16 => "rxe", - 762u16 => "quotad", - 763u16 => "cycleserv", - 764u16 => "omserv", - 765u16 => "webster", - 767u16 => "phonebook", - 769u16 => "vid", - 770u16 => "cadlock", - 771u16 => "rtip", - 772u16 => "cycleserv2", - 773u16 => "submit", - 774u16 => "rpasswd", - 775u16 => "entomb", - 776u16 => "wpages", - 777u16 => "multiling-http", - 780u16 => "wpgs", - 800u16 => "mdbs_daemon", - 801u16 => "device", - 802u16 => "mbap-s", - 810u16 => "fcp-udp", - 828u16 => "itm-mcell-s", - 829u16 => "pkix-3-ca-ra", - 830u16 => "netconf-ssh", - 831u16 => "netconf-beep", - 832u16 => "netconfsoaphttp", - 833u16 => "netconfsoapbeep", - 847u16 => "dhcp-failover2", - 848u16 => "gdoi", - 853u16 => "domain-s", - 854u16 => "dlep", - 860u16 => "iscsi", - 861u16 => "owamp-control", - 862u16 => "twamp-control", - 873u16 => "rsync", - 886u16 => "iclcnet-locate", - 887u16 => "iclcnet_svinfo", - 888u16 => "cddbp", - 900u16 => "omginitialrefs", - 901u16 => "smpnameres", - 902u16 => "ideafarm-door", - 903u16 => "ideafarm-panic", - 910u16 => "kink", - 911u16 => "xact-backup", - 912u16 => "apex-mesh", - 913u16 => "apex-edge", - 953u16 => "rndc", - 989u16 => "ftps-data", - 990u16 => "ftps", - 991u16 => "nas", - 992u16 => "telnets", - 993u16 => "imaps", - 995u16 => "pop3s", - 996u16 => "vsinet", - 997u16 => "maitrd", - 998u16 => "busboy", - 999u16 => "puprouter", - 1000u16 => "cadlock2", - 1001u16 => "webpush", - 1010u16 => "surf", - 1021u16 => "exp1", - 1022u16 => "exp2", - 1025u16 => "blackjack", - 1026u16 => "cap", - 1029u16 => "solid-mux", - 1033u16 => "netinfo-local", - 1034u16 => "activesync", - 1035u16 => "mxxrlogin", - 1036u16 => "nsstp", - 1037u16 => "ams", - 1038u16 => "mtqp", - 1039u16 => "sbl", - 1040u16 => "netarx", - 1041u16 => "danf-ak2", - 1042u16 => "afrog", - 1043u16 => "boinc-client", - 1044u16 => "dcutility", - 1045u16 => "fpitp", - 1046u16 => "wfremotertm", - 1047u16 => "neod1", - 1048u16 => "neod2", - 1049u16 => "td-postman", - 1050u16 => "cma", - 1051u16 => "optima-vnet", - 1052u16 => "ddt", - 1053u16 => "remote-as", - 1054u16 => "brvread", - 1055u16 => "ansyslmd", - 1056u16 => "vfo", - 1057u16 => "startron", - 1058u16 => "nim", - 1059u16 => "nimreg", - 1060u16 => "polestar", - 1061u16 => "kiosk", - 1062u16 => "veracity", - 1063u16 => "kyoceranetdev", - 1064u16 => "jstel", - 1065u16 => "syscomlan", - 1066u16 => "fpo-fns", - 1067u16 => "instl_boots", - 1068u16 => "instl_bootc", - 1069u16 => "cognex-insight", - 1070u16 => "gmrupdateserv", - 1071u16 => "bsquare-voip", - 1072u16 => "cardax", - 1073u16 => "bridgecontrol", - 1074u16 => "warmspotMgmt", - 1075u16 => "rdrmshc", - 1076u16 => "dab-sti-c", - 1077u16 => "imgames", - 1078u16 => "avocent-proxy", - 1079u16 => "asprovatalk", - 1080u16 => "socks", - 1081u16 => "pvuniwien", - 1082u16 => "amt-esd-prot", - 1083u16 => "ansoft-lm-1", - 1084u16 => "ansoft-lm-2", - 1085u16 => "webobjects", - 1086u16 => "cplscrambler-lg", - 1087u16 => "cplscrambler-in", - 1088u16 => "cplscrambler-al", - 1089u16 => "ff-annunc", - 1090u16 => "ff-fms", - 1091u16 => "ff-sm", - 1092u16 => "obrpd", - 1093u16 => "proofd", - 1094u16 => "rootd", - 1095u16 => "nicelink", - 1096u16 => "cnrprotocol", - 1097u16 => "sunclustermgr", - 1098u16 => "rmiactivation", - 1099u16 => "rmiregistry", - 1100u16 => "mctp", - 1101u16 => "pt2-discover", - 1102u16 => "adobeserver-1", - 1103u16 => "adobeserver-2", - 1104u16 => "xrl", - 1105u16 => "ftranhc", - 1106u16 => "isoipsigport-1", - 1107u16 => "isoipsigport-2", - 1108u16 => "ratio-adp", - 1110u16 => "webadmstart", - 1111u16 => "lmsocialserver", - 1112u16 => "icp", - 1113u16 => "ltp-deepspace", - 1114u16 => "mini-sql", - 1115u16 => "ardus-trns", - 1116u16 => "ardus-cntl", - 1117u16 => "ardus-mtrns", - 1118u16 => "sacred", - 1119u16 => "bnetgame", - 1120u16 => "bnetfile", - 1121u16 => "rmpp", - 1122u16 => "availant-mgr", - 1123u16 => "murray", - 1124u16 => "hpvmmcontrol", - 1125u16 => "hpvmmagent", - 1126u16 => "hpvmmdata", - 1127u16 => "kwdb-commn", - 1128u16 => "saphostctrl", - 1129u16 => "saphostctrls", - 1130u16 => "casp", - 1131u16 => "caspssl", - 1132u16 => "kvm-via-ip", - 1133u16 => "dfn", - 1134u16 => "aplx", - 1135u16 => "omnivision", - 1136u16 => "hhb-gateway", - 1137u16 => "trim", - 1138u16 => "encrypted_admin", - 1139u16 => "evm", - 1140u16 => "autonoc", - 1141u16 => "mxomss", - 1142u16 => "edtools", - 1143u16 => "imyx", - 1144u16 => "fuscript", - 1145u16 => "x9-icue", - 1146u16 => "audit-transfer", - 1147u16 => "capioverlan", - 1148u16 => "elfiq-repl", - 1149u16 => "bvtsonar", - 1150u16 => "blaze", - 1151u16 => "unizensus", - 1152u16 => "winpoplanmess", - 1153u16 => "c1222-acse", - 1154u16 => "resacommunity", - 1155u16 => "nfa", - 1156u16 => "iascontrol-oms", - 1157u16 => "iascontrol", - 1158u16 => "dbcontrol-oms", - 1159u16 => "oracle-oms", - 1160u16 => "olsv", - 1161u16 => "health-polling", - 1162u16 => "health-trap", - 1163u16 => "sddp", - 1164u16 => "qsm-proxy", - 1165u16 => "qsm-gui", - 1166u16 => "qsm-remote", - 1167u16 => "cisco-ipsla", - 1168u16 => "vchat", - 1169u16 => "tripwire", - 1170u16 => "atc-lm", - 1171u16 => "atc-appserver", - 1172u16 => "dnap", - 1173u16 => "d-cinema-rrp", - 1174u16 => "fnet-remote-ui", - 1175u16 => "dossier", - 1176u16 => "indigo-server", - 1177u16 => "dkmessenger", - 1178u16 => "sgi-storman", - 1179u16 => "b2n", - 1180u16 => "mc-client", - 1181u16 => "3comnetman", - 1182u16 => "accelenet", - 1183u16 => "llsurfup-http", - 1184u16 => "llsurfup-https", - 1185u16 => "catchpole", - 1186u16 => "mysql-cluster", - 1187u16 => "alias", - 1188u16 => "hp-webadmin", - 1189u16 => "unet", - 1190u16 => "commlinx-avl", - 1191u16 => "gpfs", - 1192u16 => "caids-sensor", - 1193u16 => "fiveacross", - 1194u16 => "openvpn", - 1195u16 => "rsf-1", - 1196u16 => "netmagic", - 1197u16 => "carrius-rshell", - 1198u16 => "cajo-discovery", - 1199u16 => "dmidi", - 1200u16 => "scol", - 1201u16 => "nucleus-sand", - 1202u16 => "caiccipc", - 1203u16 => "ssslic-mgr", - 1204u16 => "ssslog-mgr", - 1205u16 => "accord-mgc", - 1206u16 => "anthony-data", - 1207u16 => "metasage", - 1208u16 => "seagull-ais", - 1209u16 => "ipcd3", - 1210u16 => "eoss", - 1211u16 => "groove-dpp", - 1212u16 => "lupa", - 1213u16 => "mpc-lifenet", - 1214u16 => "kazaa", - 1215u16 => "scanstat-1", - 1216u16 => "etebac5", - 1217u16 => "hpss-ndapi", - 1218u16 => "aeroflight-ads", - 1219u16 => "aeroflight-ret", - 1220u16 => "qt-serveradmin", - 1221u16 => "sweetware-apps", - 1222u16 => "nerv", - 1223u16 => "tgp", - 1224u16 => "vpnz", - 1225u16 => "slinkysearch", - 1226u16 => "stgxfws", - 1227u16 => "dns2go", - 1228u16 => "florence", - 1229u16 => "zented", - 1230u16 => "periscope", - 1231u16 => "menandmice-lpm", - 1232u16 => "first-defense", - 1233u16 => "univ-appserver", - 1234u16 => "search-agent", - 1235u16 => "mosaicsyssvc1", - 1236u16 => "bvcontrol", - 1237u16 => "tsdos390", - 1238u16 => "hacl-qs", - 1239u16 => "nmsd", - 1240u16 => "instantia", - 1241u16 => "nessus", - 1242u16 => "nmasoverip", - 1243u16 => "serialgateway", - 1244u16 => "isbconference1", - 1245u16 => "isbconference2", - 1246u16 => "payrouter", - 1247u16 => "visionpyramid", - 1248u16 => "hermes", - 1249u16 => "mesavistaco", - 1250u16 => "swldy-sias", - 1251u16 => "servergraph", - 1252u16 => "bspne-pcc", - 1253u16 => "q55-pcc", - 1254u16 => "de-noc", - 1255u16 => "de-cache-query", - 1256u16 => "de-server", - 1257u16 => "shockwave2", - 1258u16 => "opennl", - 1259u16 => "opennl-voice", - 1260u16 => "ibm-ssd", - 1261u16 => "mpshrsv", - 1262u16 => "qnts-orb", - 1263u16 => "dka", - 1264u16 => "prat", - 1265u16 => "dssiapi", - 1266u16 => "dellpwrappks", - 1267u16 => "epc", - 1268u16 => "propel-msgsys", - 1269u16 => "watilapp", - 1270u16 => "opsmgr", - 1271u16 => "excw", - 1272u16 => "cspmlockmgr", - 1273u16 => "emc-gateway", - 1274u16 => "t1distproc", - 1275u16 => "ivcollector", - 1277u16 => "miva-mqs", - 1278u16 => "dellwebadmin-1", - 1279u16 => "dellwebadmin-2", - 1280u16 => "pictrography", - 1281u16 => "healthd", - 1282u16 => "emperion", - 1283u16 => "productinfo", - 1284u16 => "iee-qfx", - 1285u16 => "neoiface", - 1286u16 => "netuitive", - 1287u16 => "routematch", - 1288u16 => "navbuddy", - 1289u16 => "jwalkserver", - 1290u16 => "winjaserver", - 1291u16 => "seagulllms", - 1292u16 => "dsdn", - 1293u16 => "pkt-krb-ipsec", - 1294u16 => "cmmdriver", - 1295u16 => "ehtp", - 1296u16 => "dproxy", - 1297u16 => "sdproxy", - 1298u16 => "lpcp", - 1299u16 => "hp-sci", - 1300u16 => "h323hostcallsc", - 1303u16 => "sftsrv", - 1304u16 => "boomerang", - 1305u16 => "pe-mike", - 1306u16 => "re-conn-proto", - 1307u16 => "pacmand", - 1308u16 => "odsi", - 1309u16 => "jtag-server", - 1310u16 => "husky", - 1311u16 => "rxmon", - 1312u16 => "sti-envision", - 1313u16 => "bmc_patroldb", - 1314u16 => "pdps", - 1315u16 => "els", - 1316u16 => "exbit-escp", - 1317u16 => "vrts-ipcserver", - 1318u16 => "krb5gatekeeper", - 1319u16 => "amx-icsp", - 1320u16 => "amx-axbnet", - 1321u16 => "pip", - 1322u16 => "novation", - 1323u16 => "brcd", - 1324u16 => "delta-mcp", - 1325u16 => "dx-instrument", - 1326u16 => "wimsic", - 1327u16 => "ultrex", - 1328u16 => "ewall", - 1329u16 => "netdb-export", - 1330u16 => "streetperfect", - 1331u16 => "intersan", - 1332u16 => "pcia-rxp-b", - 1333u16 => "passwrd-policy", - 1334u16 => "writesrv", - 1335u16 => "digital-notary", - 1336u16 => "ischat", - 1337u16 => "menandmice-dns", - 1338u16 => "wmc-log-svc", - 1339u16 => "kjtsiteserver", - 1340u16 => "naap", - 1341u16 => "qubes", - 1342u16 => "esbroker", - 1343u16 => "re101", - 1344u16 => "icap", - 1345u16 => "vpjp", - 1346u16 => "alta-ana-lm", - 1347u16 => "bbn-mmc", - 1348u16 => "bbn-mmx", - 1349u16 => "sbook", - 1350u16 => "editbench", - 1351u16 => "equationbuilder", - 1352u16 => "lotusnote", - 1353u16 => "relief", - 1354u16 => "XSIP-network", - 1355u16 => "intuitive-edge", - 1356u16 => "cuillamartin", - 1357u16 => "pegboard", - 1358u16 => "connlcli", - 1359u16 => "ftsrv", - 1360u16 => "mimer", - 1361u16 => "linx", - 1362u16 => "timeflies", - 1363u16 => "ndm-requester", - 1364u16 => "ndm-server", - 1365u16 => "adapt-sna", - 1366u16 => "netware-csp", - 1367u16 => "dcs", - 1368u16 => "screencast", - 1369u16 => "gv-us", - 1370u16 => "us-gv", - 1371u16 => "fc-cli", - 1372u16 => "fc-ser", - 1373u16 => "chromagrafx", - 1374u16 => "molly", - 1375u16 => "bytex", - 1376u16 => "ibm-pps", - 1377u16 => "cichlid", - 1378u16 => "elan", - 1379u16 => "dbreporter", - 1380u16 => "telesis-licman", - 1381u16 => "apple-licman", - 1382u16 => "udt_os", - 1383u16 => "gwha", - 1384u16 => "os-licman", - 1385u16 => "atex_elmd", - 1386u16 => "checksum", - 1387u16 => "cadsi-lm", - 1388u16 => "objective-dbc", - 1389u16 => "iclpv-dm", - 1390u16 => "iclpv-sc", - 1391u16 => "iclpv-sas", - 1392u16 => "iclpv-pm", - 1393u16 => "iclpv-nls", - 1394u16 => "iclpv-nlc", - 1395u16 => "iclpv-wsm", - 1396u16 => "dvl-activemail", - 1397u16 => "audio-activmail", - 1398u16 => "video-activmail", - 1399u16 => "cadkey-licman", - 1400u16 => "cadkey-tablet", - 1401u16 => "goldleaf-licman", - 1402u16 => "prm-sm-np", - 1403u16 => "prm-nm-np", - 1404u16 => "igi-lm", - 1405u16 => "ibm-res", - 1406u16 => "netlabs-lm", - 1407u16 => "tibet-server", - 1408u16 => "sophia-lm", - 1409u16 => "here-lm", - 1410u16 => "hiq", - 1411u16 => "af", - 1412u16 => "innosys", - 1413u16 => "innosys-acl", - 1414u16 => "ibm-mqseries", - 1415u16 => "dbstar", - 1416u16 => "novell-lu6.2", - 1417u16 => "timbuktu-srv1", - 1418u16 => "timbuktu-srv2", - 1419u16 => "timbuktu-srv3", - 1420u16 => "timbuktu-srv4", - 1421u16 => "gandalf-lm", - 1422u16 => "autodesk-lm", - 1423u16 => "essbase", - 1424u16 => "hybrid", - 1425u16 => "zion-lm", - 1426u16 => "sais", - 1427u16 => "mloadd", - 1428u16 => "informatik-lm", - 1429u16 => "nms", - 1430u16 => "tpdu", - 1431u16 => "rgtp", - 1432u16 => "blueberry-lm", - 1433u16 => "ms-sql-s", - 1434u16 => "ms-sql-m", - 1435u16 => "ibm-cics", - 1436u16 => "saism", - 1437u16 => "tabula", - 1438u16 => "eicon-server", - 1439u16 => "eicon-x25", - 1440u16 => "eicon-slp", - 1441u16 => "cadis-1", - 1442u16 => "cadis-2", - 1443u16 => "ies-lm", - 1444u16 => "marcam-lm", - 1445u16 => "proxima-lm", - 1446u16 => "ora-lm", - 1447u16 => "apri-lm", - 1448u16 => "oc-lm", - 1449u16 => "peport", - 1450u16 => "dwf", - 1451u16 => "infoman", - 1452u16 => "gtegsc-lm", - 1453u16 => "genie-lm", - 1454u16 => "interhdl_elmd", - 1455u16 => "esl-lm", - 1456u16 => "dca", - 1457u16 => "valisys-lm", - 1458u16 => "nrcabq-lm", - 1459u16 => "proshare1", - 1460u16 => "proshare2", - 1461u16 => "ibm_wrless_lan", - 1462u16 => "world-lm", - 1463u16 => "nucleus", - 1464u16 => "msl_lmd", - 1465u16 => "pipes", - 1466u16 => "oceansoft-lm", - 1467u16 => "csdmbase", - 1468u16 => "csdm", - 1469u16 => "aal-lm", - 1470u16 => "uaiact", - 1471u16 => "csdmbase", - 1472u16 => "csdm", - 1473u16 => "openmath", - 1474u16 => "telefinder", - 1475u16 => "taligent-lm", - 1476u16 => "clvm-cfg", - 1477u16 => "ms-sna-server", - 1478u16 => "ms-sna-base", - 1479u16 => "dberegister", - 1480u16 => "pacerforum", - 1481u16 => "airs", - 1482u16 => "miteksys-lm", - 1483u16 => "afs", - 1484u16 => "confluent", - 1485u16 => "lansource", - 1486u16 => "nms_topo_serv", - 1487u16 => "localinfosrvr", - 1488u16 => "docstor", - 1489u16 => "dmdocbroker", - 1490u16 => "insitu-conf", - 1492u16 => "stone-design-1", - 1493u16 => "netmap_lm", - 1494u16 => "ica", - 1495u16 => "cvc", - 1496u16 => "liberty-lm", - 1497u16 => "rfx-lm", - 1498u16 => "sybase-sqlany", - 1499u16 => "fhc", - 1500u16 => "vlsi-lm", - 1501u16 => "saiscm", - 1502u16 => "shivadiscovery", - 1503u16 => "imtc-mcs", - 1504u16 => "evb-elm", - 1505u16 => "funkproxy", - 1506u16 => "utcd", - 1507u16 => "symplex", - 1508u16 => "diagmond", - 1509u16 => "robcad-lm", - 1510u16 => "mvx-lm", - 1511u16 => "3l-l1", - 1512u16 => "wins", - 1513u16 => "fujitsu-dtc", - 1514u16 => "fujitsu-dtcns", - 1515u16 => "ifor-protocol", - 1516u16 => "vpad", - 1517u16 => "vpac", - 1518u16 => "vpvd", - 1519u16 => "vpvc", - 1520u16 => "atm-zip-office", - 1521u16 => "ncube-lm", - 1522u16 => "ricardo-lm", - 1523u16 => "cichild-lm", - 1524u16 => "ingreslock", - 1525u16 => "prospero-np", - 1526u16 => "pdap-np", - 1527u16 => "tlisrv", - 1529u16 => "coauthor", - 1530u16 => "rap-service", - 1531u16 => "rap-listen", - 1532u16 => "miroconnect", - 1533u16 => "virtual-places", - 1534u16 => "micromuse-lm", - 1535u16 => "ampr-info", - 1536u16 => "ampr-inter", - 1537u16 => "sdsc-lm", - 1538u16 => "3ds-lm", - 1539u16 => "intellistor-lm", - 1540u16 => "rds", - 1541u16 => "rds2", - 1542u16 => "gridgen-elmd", - 1543u16 => "simba-cs", - 1544u16 => "aspeclmd", - 1545u16 => "vistium-share", - 1546u16 => "abbaccuray", - 1547u16 => "laplink", - 1548u16 => "axon-lm", - 1549u16 => "shivahose", - 1550u16 => "3m-image-lm", - 1551u16 => "hecmtl-db", - 1552u16 => "pciarray", - 1553u16 => "sna-cs", - 1554u16 => "caci-lm", - 1555u16 => "livelan", - 1556u16 => "veritas_pbx", - 1557u16 => "arbortext-lm", - 1558u16 => "xingmpeg", - 1559u16 => "web2host", - 1560u16 => "asci-val", - 1561u16 => "facilityview", - 1562u16 => "pconnectmgr", - 1563u16 => "cadabra-lm", - 1564u16 => "pay-per-view", - 1565u16 => "winddlb", - 1566u16 => "corelvideo", - 1567u16 => "jlicelmd", - 1568u16 => "tsspmap", - 1569u16 => "ets", - 1570u16 => "orbixd", - 1571u16 => "rdb-dbs-disp", - 1572u16 => "chip-lm", - 1573u16 => "itscomm-ns", - 1574u16 => "mvel-lm", - 1575u16 => "oraclenames", - 1576u16 => "moldflow-lm", - 1577u16 => "hypercube-lm", - 1578u16 => "jacobus-lm", - 1579u16 => "ioc-sea-lm", - 1580u16 => "tn-tl-r1", - 1581u16 => "mil-2045-47001", - 1582u16 => "msims", - 1583u16 => "simbaexpress", - 1584u16 => "tn-tl-fd2", - 1585u16 => "intv", - 1586u16 => "ibm-abtact", - 1587u16 => "pra_elmd", - 1588u16 => "triquest-lm", - 1589u16 => "vqp", - 1590u16 => "gemini-lm", - 1591u16 => "ncpm-pm", - 1592u16 => "commonspace", - 1593u16 => "mainsoft-lm", - 1594u16 => "sixtrak", - 1595u16 => "radio", - 1596u16 => "radio-sm", - 1597u16 => "orbplus-iiop", - 1598u16 => "picknfs", - 1599u16 => "simbaservices", - 1600u16 => "issd", - 1601u16 => "aas", - 1602u16 => "inspect", - 1603u16 => "picodbc", - 1604u16 => "icabrowser", - 1605u16 => "slp", - 1606u16 => "slm-api", - 1607u16 => "stt", - 1608u16 => "smart-lm", - 1609u16 => "isysg-lm", - 1610u16 => "taurus-wh", - 1611u16 => "ill", - 1612u16 => "netbill-trans", - 1613u16 => "netbill-keyrep", - 1614u16 => "netbill-cred", - 1615u16 => "netbill-auth", - 1616u16 => "netbill-prod", - 1617u16 => "nimrod-agent", - 1618u16 => "skytelnet", - 1619u16 => "xs-openstorage", - 1620u16 => "faxportwinport", - 1621u16 => "softdataphone", - 1622u16 => "ontime", - 1623u16 => "jaleosnd", - 1624u16 => "udp-sr-port", - 1625u16 => "svs-omagent", - 1626u16 => "shockwave", - 1627u16 => "t128-gateway", - 1628u16 => "lontalk-norm", - 1629u16 => "lontalk-urgnt", - 1630u16 => "oraclenet8cman", - 1631u16 => "visitview", - 1632u16 => "pammratc", - 1633u16 => "pammrpc", - 1634u16 => "loaprobe", - 1635u16 => "edb-server1", - 1636u16 => "isdc", - 1637u16 => "islc", - 1638u16 => "ismc", - 1639u16 => "cert-initiator", - 1640u16 => "cert-responder", - 1641u16 => "invision", - 1642u16 => "isis-am", - 1643u16 => "isis-ambc", - 1644u16 => "saiseh", - 1645u16 => "sightline", - 1646u16 => "sa-msg-port", - 1647u16 => "rsap", - 1648u16 => "concurrent-lm", - 1649u16 => "kermit", - 1650u16 => "nkd", - 1651u16 => "shiva_confsrvr", - 1652u16 => "xnmp", - 1653u16 => "alphatech-lm", - 1654u16 => "stargatealerts", - 1655u16 => "dec-mbadmin", - 1656u16 => "dec-mbadmin-h", - 1657u16 => "fujitsu-mmpdc", - 1658u16 => "sixnetudr", - 1659u16 => "sg-lm", - 1660u16 => "skip-mc-gikreq", - 1661u16 => "netview-aix-1", - 1662u16 => "netview-aix-2", - 1663u16 => "netview-aix-3", - 1664u16 => "netview-aix-4", - 1665u16 => "netview-aix-5", - 1666u16 => "netview-aix-6", - 1667u16 => "netview-aix-7", - 1668u16 => "netview-aix-8", - 1669u16 => "netview-aix-9", - 1670u16 => "netview-aix-10", - 1671u16 => "netview-aix-11", - 1672u16 => "netview-aix-12", - 1673u16 => "proshare-mc-1", - 1674u16 => "proshare-mc-2", - 1675u16 => "pdp", - 1676u16 => "netcomm1", - 1677u16 => "groupwise", - 1678u16 => "prolink", - 1679u16 => "darcorp-lm", - 1680u16 => "microcom-sbp", - 1681u16 => "sd-elmd", - 1682u16 => "lanyon-lantern", - 1683u16 => "ncpm-hip", - 1684u16 => "snaresecure", - 1685u16 => "n2nremote", - 1686u16 => "cvmon", - 1687u16 => "nsjtp-ctrl", - 1688u16 => "nsjtp-data", - 1689u16 => "firefox", - 1690u16 => "ng-umds", - 1691u16 => "empire-empuma", - 1692u16 => "sstsys-lm", - 1693u16 => "rrirtr", - 1694u16 => "rrimwm", - 1695u16 => "rrilwm", - 1696u16 => "rrifmm", - 1697u16 => "rrisat", - 1698u16 => "rsvp-encap-1", - 1699u16 => "rsvp-encap-2", - 1700u16 => "mps-raft", - 1701u16 => "l2tp", - 1702u16 => "deskshare", - 1703u16 => "hb-engine", - 1704u16 => "bcs-broker", - 1705u16 => "slingshot", - 1706u16 => "jetform", - 1707u16 => "vdmplay", - 1708u16 => "gat-lmd", - 1709u16 => "centra", - 1710u16 => "impera", - 1711u16 => "pptconference", - 1712u16 => "registrar", - 1713u16 => "conferencetalk", - 1714u16 => "sesi-lm", - 1715u16 => "houdini-lm", - 1716u16 => "xmsg", - 1717u16 => "fj-hdnet", - 1718u16 => "h323gatedisc", - 1719u16 => "h323gatestat", - 1720u16 => "h323hostcall", - 1721u16 => "caicci", - 1722u16 => "hks-lm", - 1723u16 => "pptp", - 1724u16 => "csbphonemaster", - 1725u16 => "iden-ralp", - 1726u16 => "iberiagames", - 1727u16 => "winddx", - 1728u16 => "telindus", - 1729u16 => "citynl", - 1730u16 => "roketz", - 1731u16 => "msiccp", - 1732u16 => "proxim", - 1733u16 => "siipat", - 1734u16 => "cambertx-lm", - 1735u16 => "privatechat", - 1736u16 => "street-stream", - 1737u16 => "ultimad", - 1738u16 => "gamegen1", - 1739u16 => "webaccess", - 1740u16 => "encore", - 1741u16 => "cisco-net-mgmt", - 1742u16 => "3Com-nsd", - 1743u16 => "cinegrfx-lm", - 1744u16 => "ncpm-ft", - 1745u16 => "remote-winsock", - 1746u16 => "ftrapid-1", - 1747u16 => "ftrapid-2", - 1748u16 => "oracle-em1", - 1749u16 => "aspen-services", - 1750u16 => "sslp", - 1751u16 => "swiftnet", - 1752u16 => "lofr-lm", - 1753u16 => "predatar-comms", - 1754u16 => "oracle-em2", - 1755u16 => "ms-streaming", - 1756u16 => "capfast-lmd", - 1757u16 => "cnhrp", - 1758u16 => "tftp-mcast", - 1759u16 => "spss-lm", - 1760u16 => "www-ldap-gw", - 1761u16 => "cft-0", - 1762u16 => "cft-1", - 1763u16 => "cft-2", - 1764u16 => "cft-3", - 1765u16 => "cft-4", - 1766u16 => "cft-5", - 1767u16 => "cft-6", - 1768u16 => "cft-7", - 1769u16 => "bmc-net-adm", - 1770u16 => "bmc-net-svc", - 1771u16 => "vaultbase", - 1772u16 => "essweb-gw", - 1773u16 => "kmscontrol", - 1774u16 => "global-dtserv", - 1775u16 => "vdab", - 1776u16 => "femis", - 1777u16 => "powerguardian", - 1778u16 => "prodigy-intrnet", - 1779u16 => "pharmasoft", - 1780u16 => "dpkeyserv", - 1781u16 => "answersoft-lm", - 1782u16 => "hp-hcip", - 1784u16 => "finle-lm", - 1785u16 => "windlm", - 1786u16 => "funk-logger", - 1787u16 => "funk-license", - 1788u16 => "psmond", - 1789u16 => "hello", - 1790u16 => "nmsp", - 1791u16 => "ea1", - 1792u16 => "ibm-dt-2", - 1793u16 => "rsc-robot", - 1794u16 => "cera-bcm", - 1795u16 => "dpi-proxy", - 1796u16 => "vocaltec-admin", - 1797u16 => "uma", - 1798u16 => "etp", - 1799u16 => "netrisk", - 1800u16 => "ansys-lm", - 1801u16 => "msmq", - 1802u16 => "concomp1", - 1803u16 => "hp-hcip-gwy", - 1804u16 => "enl", - 1805u16 => "enl-name", - 1806u16 => "musiconline", - 1807u16 => "fhsp", - 1808u16 => "oracle-vp2", - 1809u16 => "oracle-vp1", - 1810u16 => "jerand-lm", - 1811u16 => "scientia-sdb", - 1812u16 => "radius", - 1813u16 => "radius-acct", - 1814u16 => "tdp-suite", - 1815u16 => "mmpft", - 1816u16 => "harp", - 1817u16 => "rkb-oscs", - 1818u16 => "etftp", - 1819u16 => "plato-lm", - 1820u16 => "mcagent", - 1821u16 => "donnyworld", - 1822u16 => "es-elmd", - 1823u16 => "unisys-lm", - 1824u16 => "metrics-pas", - 1825u16 => "direcpc-video", - 1826u16 => "ardt", - 1827u16 => "asi", - 1828u16 => "itm-mcell-u", - 1829u16 => "optika-emedia", - 1830u16 => "net8-cman", - 1831u16 => "myrtle", - 1832u16 => "tht-treasure", - 1833u16 => "udpradio", - 1834u16 => "ardusuni", - 1835u16 => "ardusmul", - 1836u16 => "ste-smsc", - 1837u16 => "csoft1", - 1838u16 => "talnet", - 1839u16 => "netopia-vo1", - 1840u16 => "netopia-vo2", - 1841u16 => "netopia-vo3", - 1842u16 => "netopia-vo4", - 1843u16 => "netopia-vo5", - 1844u16 => "direcpc-dll", - 1845u16 => "altalink", - 1846u16 => "tunstall-pnc", - 1847u16 => "slp-notify", - 1848u16 => "fjdocdist", - 1849u16 => "alpha-sms", - 1850u16 => "gsi", - 1851u16 => "ctcd", - 1852u16 => "virtual-time", - 1853u16 => "vids-avtp", - 1854u16 => "buddy-draw", - 1855u16 => "fiorano-rtrsvc", - 1856u16 => "fiorano-msgsvc", - 1857u16 => "datacaptor", - 1858u16 => "privateark", - 1859u16 => "gammafetchsvr", - 1860u16 => "sunscalar-svc", - 1861u16 => "lecroy-vicp", - 1862u16 => "mysql-cm-agent", - 1863u16 => "msnp", - 1864u16 => "paradym-31port", - 1865u16 => "entp", - 1866u16 => "swrmi", - 1867u16 => "udrive", - 1868u16 => "viziblebrowser", - 1869u16 => "transact", - 1870u16 => "sunscalar-dns", - 1871u16 => "canocentral0", - 1872u16 => "canocentral1", - 1873u16 => "fjmpjps", - 1874u16 => "fjswapsnp", - 1875u16 => "westell-stats", - 1876u16 => "ewcappsrv", - 1877u16 => "hp-webqosdb", - 1878u16 => "drmsmc", - 1879u16 => "nettgain-nms", - 1880u16 => "vsat-control", - 1881u16 => "ibm-mqseries2", - 1882u16 => "ecsqdmn", - 1883u16 => "mqtt", - 1884u16 => "idmaps", - 1885u16 => "vrtstrapserver", - 1886u16 => "leoip", - 1887u16 => "filex-lport", - 1888u16 => "ncconfig", - 1889u16 => "unify-adapter", - 1890u16 => "wilkenlistener", - 1891u16 => "childkey-notif", - 1892u16 => "childkey-ctrl", - 1893u16 => "elad", - 1894u16 => "o2server-port", - 1896u16 => "b-novative-ls", - 1897u16 => "metaagent", - 1898u16 => "cymtec-port", - 1899u16 => "mc2studios", - 1900u16 => "ssdp", - 1901u16 => "fjicl-tep-a", - 1902u16 => "fjicl-tep-b", - 1903u16 => "linkname", - 1904u16 => "fjicl-tep-c", - 1905u16 => "sugp", - 1906u16 => "tpmd", - 1907u16 => "intrastar", - 1908u16 => "dawn", - 1909u16 => "global-wlink", - 1910u16 => "ultrabac", - 1911u16 => "mtp", - 1912u16 => "rhp-iibp", - 1913u16 => "armadp", - 1914u16 => "elm-momentum", - 1915u16 => "facelink", - 1916u16 => "persona", - 1917u16 => "noagent", - 1918u16 => "can-nds", - 1919u16 => "can-dch", - 1920u16 => "can-ferret", - 1921u16 => "noadmin", - 1922u16 => "tapestry", - 1923u16 => "spice", - 1924u16 => "xiip", - 1925u16 => "discovery-port", - 1926u16 => "egs", - 1927u16 => "videte-cipc", - 1928u16 => "emsd-port", - 1929u16 => "bandwiz-system", - 1930u16 => "driveappserver", - 1931u16 => "amdsched", - 1932u16 => "ctt-broker", - 1933u16 => "xmapi", - 1934u16 => "xaapi", - 1935u16 => "macromedia-fcs", - 1936u16 => "jetcmeserver", - 1937u16 => "jwserver", - 1938u16 => "jwclient", - 1939u16 => "jvserver", - 1940u16 => "jvclient", - 1941u16 => "dic-aida", - 1942u16 => "res", - 1943u16 => "beeyond-media", - 1944u16 => "close-combat", - 1945u16 => "dialogic-elmd", - 1946u16 => "tekpls", - 1947u16 => "sentinelsrm", - 1948u16 => "eye2eye", - 1949u16 => "ismaeasdaqlive", - 1950u16 => "ismaeasdaqtest", - 1951u16 => "bcs-lmserver", - 1952u16 => "mpnjsc", - 1953u16 => "rapidbase", - 1954u16 => "abr-api", - 1955u16 => "abr-secure", - 1956u16 => "vrtl-vmf-ds", - 1957u16 => "unix-status", - 1958u16 => "dxadmind", - 1959u16 => "simp-all", - 1960u16 => "nasmanager", - 1961u16 => "bts-appserver", - 1962u16 => "biap-mp", - 1963u16 => "webmachine", - 1964u16 => "solid-e-engine", - 1965u16 => "tivoli-npm", - 1966u16 => "slush", - 1967u16 => "sns-quote", - 1968u16 => "lipsinc", - 1969u16 => "lipsinc1", - 1970u16 => "netop-rc", - 1971u16 => "netop-school", - 1972u16 => "intersys-cache", - 1973u16 => "dlsrap", - 1974u16 => "drp", - 1975u16 => "tcoflashagent", - 1976u16 => "tcoregagent", - 1977u16 => "tcoaddressbook", - 1978u16 => "unisql", - 1979u16 => "unisql-java", - 1980u16 => "pearldoc-xact", - 1981u16 => "p2pq", - 1982u16 => "estamp", - 1983u16 => "lhtp", - 1984u16 => "bb", - 1985u16 => "hsrp", - 1986u16 => "licensedaemon", - 1987u16 => "tr-rsrb-p1", - 1988u16 => "tr-rsrb-p2", - 1989u16 => "mshnet", - 1990u16 => "stun-p1", - 1991u16 => "stun-p2", - 1992u16 => "ipsendmsg", - 1993u16 => "snmp-tcp-port", - 1994u16 => "stun-port", - 1995u16 => "perf-port", - 1996u16 => "tr-rsrb-port", - 1997u16 => "gdp-port", - 1998u16 => "x25-svc-port", - 1999u16 => "tcp-id-port", - 2000u16 => "cisco-sccp", - 2001u16 => "dc", - 2002u16 => "globe", - 2003u16 => "brutus", - 2004u16 => "mailbox", - 2005u16 => "berknet", - 2006u16 => "invokator", - 2007u16 => "dectalk", - 2008u16 => "conf", - 2009u16 => "news", - 2010u16 => "search", - 2011u16 => "raid-cc", - 2012u16 => "ttyinfo", - 2013u16 => "raid-am", - 2014u16 => "troff", - 2015u16 => "cypress", - 2016u16 => "bootserver", - 2017u16 => "cypress-stat", - 2018u16 => "terminaldb", - 2019u16 => "whosockami", - 2020u16 => "xinupageserver", - 2021u16 => "servexec", - 2022u16 => "down", - 2023u16 => "xinuexpansion3", - 2024u16 => "xinuexpansion4", - 2025u16 => "ellpack", - 2026u16 => "scrabble", - 2027u16 => "shadowserver", - 2028u16 => "submitserver", - 2029u16 => "hsrpv6", - 2030u16 => "device2", - 2031u16 => "mobrien-chat", - 2032u16 => "blackboard", - 2033u16 => "glogger", - 2034u16 => "scoremgr", - 2035u16 => "imsldoc", - 2036u16 => "e-dpnet", - 2037u16 => "applus", - 2038u16 => "objectmanager", - 2039u16 => "prizma", - 2040u16 => "lam", - 2041u16 => "interbase", - 2042u16 => "isis", - 2043u16 => "isis-bcast", - 2044u16 => "rimsl", - 2045u16 => "cdfunc", - 2046u16 => "sdfunc", - 2047u16 => "dls", - 2048u16 => "dls-monitor", - 2049u16 => "nfs", - 2050u16 => "av-emb-config", - 2051u16 => "epnsdp", - 2052u16 => "clearvisn", - 2053u16 => "lot105-ds-upd", - 2054u16 => "weblogin", - 2055u16 => "iop", - 2056u16 => "omnisky", - 2057u16 => "rich-cp", - 2058u16 => "newwavesearch", - 2059u16 => "bmc-messaging", - 2060u16 => "teleniumdaemon", - 2061u16 => "netmount", - 2062u16 => "icg-swp", - 2063u16 => "icg-bridge", - 2064u16 => "icg-iprelay", - 2065u16 => "dlsrpn", - 2066u16 => "aura", - 2067u16 => "dlswpn", - 2068u16 => "avauthsrvprtcl", - 2069u16 => "event-port", - 2070u16 => "ah-esp-encap", - 2071u16 => "acp-port", - 2072u16 => "msync", - 2073u16 => "gxs-data-port", - 2074u16 => "vrtl-vmf-sa", - 2075u16 => "newlixengine", - 2076u16 => "newlixconfig", - 2077u16 => "tsrmagt", - 2078u16 => "tpcsrvr", - 2079u16 => "idware-router", - 2080u16 => "autodesk-nlm", - 2081u16 => "kme-trap-port", - 2082u16 => "infowave", - 2083u16 => "radsec", - 2084u16 => "sunclustergeo", - 2085u16 => "ada-cip", - 2086u16 => "gnunet", - 2087u16 => "eli", - 2088u16 => "ip-blf", - 2089u16 => "sep", - 2090u16 => "lrp", - 2091u16 => "prp", - 2092u16 => "descent3", - 2093u16 => "nbx-cc", - 2094u16 => "nbx-au", - 2095u16 => "nbx-ser", - 2096u16 => "nbx-dir", - 2097u16 => "jetformpreview", - 2098u16 => "dialog-port", - 2099u16 => "h2250-annex-g", - 2100u16 => "amiganetfs", - 2101u16 => "rtcm-sc104", - 2102u16 => "zephyr-srv", - 2103u16 => "zephyr-clt", - 2104u16 => "zephyr-hm", - 2105u16 => "minipay", - 2106u16 => "mzap", - 2107u16 => "bintec-admin", - 2108u16 => "comcam", - 2109u16 => "ergolight", - 2110u16 => "umsp", - 2111u16 => "dsatp", - 2112u16 => "idonix-metanet", - 2113u16 => "hsl-storm", - 2114u16 => "ariascribe", - 2115u16 => "kdm", - 2116u16 => "ccowcmr", - 2117u16 => "mentaclient", - 2118u16 => "mentaserver", - 2119u16 => "gsigatekeeper", - 2120u16 => "qencp", - 2121u16 => "scientia-ssdb", - 2122u16 => "caupc-remote", - 2123u16 => "gtp-control", - 2124u16 => "elatelink", - 2125u16 => "lockstep", - 2126u16 => "pktcable-cops", - 2127u16 => "index-pc-wb", - 2128u16 => "net-steward", - 2129u16 => "cs-live", - 2130u16 => "xds", - 2131u16 => "avantageb2b", - 2132u16 => "solera-epmap", - 2133u16 => "zymed-zpp", - 2134u16 => "avenue", - 2135u16 => "gris", - 2136u16 => "appworxsrv", - 2137u16 => "connect", - 2138u16 => "unbind-cluster", - 2139u16 => "ias-auth", - 2140u16 => "ias-reg", - 2141u16 => "ias-admind", - 2142u16 => "tdmoip", - 2143u16 => "lv-jc", - 2144u16 => "lv-ffx", - 2145u16 => "lv-pici", - 2146u16 => "lv-not", - 2147u16 => "lv-auth", - 2148u16 => "veritas-ucl", - 2149u16 => "acptsys", - 2150u16 => "dynamic3d", - 2151u16 => "docent", - 2152u16 => "gtp-user", - 2153u16 => "ctlptc", - 2154u16 => "stdptc", - 2155u16 => "brdptc", - 2156u16 => "trp", - 2157u16 => "xnds", - 2158u16 => "touchnetplus", - 2159u16 => "gdbremote", - 2160u16 => "apc-2160", - 2161u16 => "apc-2161", - 2162u16 => "navisphere", - 2163u16 => "navisphere-sec", - 2164u16 => "ddns-v3", - 2165u16 => "x-bone-api", - 2166u16 => "iwserver", - 2167u16 => "raw-serial", - 2168u16 => "easy-soft-mux", - 2169u16 => "brain", - 2170u16 => "eyetv", - 2171u16 => "msfw-storage", - 2172u16 => "msfw-s-storage", - 2173u16 => "msfw-replica", - 2174u16 => "msfw-array", - 2175u16 => "airsync", - 2176u16 => "rapi", - 2177u16 => "qwave", - 2178u16 => "bitspeer", - 2179u16 => "vmrdp", - 2180u16 => "mc-gt-srv", - 2181u16 => "eforward", - 2182u16 => "cgn-stat", - 2183u16 => "cgn-config", - 2184u16 => "nvd", - 2185u16 => "onbase-dds", - 2186u16 => "gtaua", - 2187u16 => "ssmc", - 2188u16 => "radware-rpm", - 2189u16 => "radware-rpm-s", - 2190u16 => "tivoconnect", - 2191u16 => "tvbus", - 2192u16 => "asdis", - 2193u16 => "drwcs", - 2197u16 => "mnp-exchange", - 2198u16 => "onehome-remote", - 2199u16 => "onehome-help", - 2201u16 => "ats", - 2202u16 => "imtc-map", - 2203u16 => "b2-runtime", - 2204u16 => "b2-license", - 2205u16 => "jps", - 2206u16 => "hpocbus", - 2207u16 => "hpssd", - 2208u16 => "hpiod", - 2209u16 => "rimf-ps", - 2210u16 => "noaaport", - 2211u16 => "emwin", - 2212u16 => "leecoposserver", - 2213u16 => "kali", - 2214u16 => "rpi", - 2215u16 => "ipcore", - 2216u16 => "vtu-comms", - 2217u16 => "gotodevice", - 2218u16 => "bounzza", - 2219u16 => "netiq-ncap", - 2220u16 => "netiq", - 2221u16 => "ethernet-ip-s", - 2222u16 => "EtherNet/IP-1", - 2223u16 => "rockwell-csp2", - 2224u16 => "efi-mg", - 2225u16 => "rcip-itu", - 2226u16 => "di-drm", - 2227u16 => "di-msg", - 2228u16 => "ehome-ms", - 2229u16 => "datalens", - 2230u16 => "queueadm", - 2231u16 => "wimaxasncp", - 2232u16 => "ivs-video", - 2233u16 => "infocrypt", - 2234u16 => "directplay", - 2235u16 => "sercomm-wlink", - 2236u16 => "nani", - 2237u16 => "optech-port1-lm", - 2238u16 => "aviva-sna", - 2239u16 => "imagequery", - 2240u16 => "recipe", - 2241u16 => "ivsd", - 2242u16 => "foliocorp", - 2243u16 => "magicom", - 2244u16 => "nmsserver", - 2245u16 => "hao", - 2246u16 => "pc-mta-addrmap", - 2247u16 => "antidotemgrsvr", - 2248u16 => "ums", - 2249u16 => "rfmp", - 2250u16 => "remote-collab", - 2251u16 => "dif-port", - 2252u16 => "njenet-ssl", - 2253u16 => "dtv-chan-req", - 2254u16 => "seispoc", - 2255u16 => "vrtp", - 2256u16 => "pcc-mfp", - 2257u16 => "simple-tx-rx", - 2258u16 => "rcts", - 2260u16 => "apc-2260", - 2261u16 => "comotionmaster", - 2262u16 => "comotionback", - 2263u16 => "ecwcfg", - 2264u16 => "apx500api-1", - 2265u16 => "apx500api-2", - 2266u16 => "mfserver", - 2267u16 => "ontobroker", - 2268u16 => "amt", - 2269u16 => "mikey", - 2270u16 => "starschool", - 2271u16 => "mmcals", - 2272u16 => "mmcal", - 2273u16 => "mysql-im", - 2274u16 => "pcttunnell", - 2275u16 => "ibridge-data", - 2276u16 => "ibridge-mgmt", - 2277u16 => "bluectrlproxy", - 2278u16 => "s3db", - 2279u16 => "xmquery", - 2280u16 => "lnvpoller", - 2281u16 => "lnvconsole", - 2282u16 => "lnvalarm", - 2283u16 => "lnvstatus", - 2284u16 => "lnvmaps", - 2285u16 => "lnvmailmon", - 2286u16 => "nas-metering", - 2287u16 => "dna", - 2288u16 => "netml", - 2289u16 => "dict-lookup", - 2290u16 => "sonus-logging", - 2291u16 => "eapsp", - 2292u16 => "mib-streaming", - 2293u16 => "npdbgmngr", - 2294u16 => "konshus-lm", - 2295u16 => "advant-lm", - 2296u16 => "theta-lm", - 2297u16 => "d2k-datamover1", - 2298u16 => "d2k-datamover2", - 2299u16 => "pc-telecommute", - 2300u16 => "cvmmon", - 2301u16 => "cpq-wbem", - 2302u16 => "binderysupport", - 2303u16 => "proxy-gateway", - 2304u16 => "attachmate-uts", - 2305u16 => "mt-scaleserver", - 2306u16 => "tappi-boxnet", - 2307u16 => "pehelp", - 2308u16 => "sdhelp", - 2309u16 => "sdserver", - 2310u16 => "sdclient", - 2311u16 => "messageservice", - 2312u16 => "wanscaler", - 2313u16 => "iapp", - 2314u16 => "cr-websystems", - 2315u16 => "precise-sft", - 2316u16 => "sent-lm", - 2317u16 => "attachmate-g32", - 2318u16 => "cadencecontrol", - 2319u16 => "infolibria", - 2320u16 => "siebel-ns", - 2321u16 => "rdlap", - 2322u16 => "ofsd", - 2323u16 => "3d-nfsd", - 2324u16 => "cosmocall", - 2325u16 => "ansysli", - 2326u16 => "idcp", - 2327u16 => "xingcsm", - 2328u16 => "netrix-sftm", - 2329u16 => "nvd", - 2330u16 => "tscchat", - 2331u16 => "agentview", - 2332u16 => "rcc-host", - 2333u16 => "snapp", - 2334u16 => "ace-client", - 2335u16 => "ace-proxy", - 2336u16 => "appleugcontrol", - 2337u16 => "ideesrv", - 2338u16 => "norton-lambert", - 2339u16 => "3com-webview", - 2340u16 => "wrs_registry", - 2341u16 => "xiostatus", - 2342u16 => "manage-exec", - 2343u16 => "nati-logos", - 2344u16 => "fcmsys", - 2345u16 => "dbm", - 2346u16 => "redstorm_join", - 2347u16 => "redstorm_find", - 2348u16 => "redstorm_info", - 2349u16 => "redstorm_diag", - 2350u16 => "psbserver", - 2351u16 => "psrserver", - 2352u16 => "pslserver", - 2353u16 => "pspserver", - 2354u16 => "psprserver", - 2355u16 => "psdbserver", - 2356u16 => "gxtelmd", - 2357u16 => "unihub-server", - 2358u16 => "futrix", - 2359u16 => "flukeserver", - 2360u16 => "nexstorindltd", - 2361u16 => "tl1", - 2362u16 => "digiman", - 2363u16 => "mediacntrlnfsd", - 2364u16 => "oi-2000", - 2365u16 => "dbref", - 2366u16 => "qip-login", - 2367u16 => "service-ctrl", - 2368u16 => "opentable", - 2370u16 => "l3-hbmon", - 2371u16 => "rda", - 2372u16 => "lanmessenger", - 2373u16 => "remographlm", - 2374u16 => "hydra", - 2375u16 => "docker", - 2376u16 => "docker-s", - 2377u16 => "swarm", - 2379u16 => "etcd-client", - 2380u16 => "etcd-server", - 2381u16 => "compaq-https", - 2382u16 => "ms-olap3", - 2383u16 => "ms-olap4", - 2384u16 => "sd-request", - 2385u16 => "sd-data", - 2386u16 => "virtualtape", - 2387u16 => "vsamredirector", - 2388u16 => "mynahautostart", - 2389u16 => "ovsessionmgr", - 2390u16 => "rsmtp", - 2391u16 => "3com-net-mgmt", - 2392u16 => "tacticalauth", - 2393u16 => "ms-olap1", - 2394u16 => "ms-olap2", - 2395u16 => "lan900_remote", - 2396u16 => "wusage", - 2397u16 => "ncl", - 2398u16 => "orbiter", - 2399u16 => "fmpro-fdal", - 2400u16 => "opequus-server", - 2401u16 => "cvspserver", - 2402u16 => "taskmaster2000", - 2403u16 => "taskmaster2000", - 2404u16 => "iec-104", - 2405u16 => "trc-netpoll", - 2406u16 => "jediserver", - 2407u16 => "orion", - 2408u16 => "railgun-webaccl", - 2409u16 => "sns-protocol", - 2410u16 => "vrts-registry", - 2411u16 => "netwave-ap-mgmt", - 2412u16 => "cdn", - 2413u16 => "orion-rmi-reg", - 2414u16 => "beeyond", - 2415u16 => "codima-rtp", - 2416u16 => "rmtserver", - 2417u16 => "composit-server", - 2418u16 => "cas", - 2419u16 => "attachmate-s2s", - 2420u16 => "dslremote-mgmt", - 2421u16 => "g-talk", - 2422u16 => "crmsbits", - 2423u16 => "rnrp", - 2424u16 => "kofax-svr", - 2425u16 => "fjitsuappmgr", - 2426u16 => "vcmp", - 2427u16 => "mgcp-gateway", - 2428u16 => "ott", - 2429u16 => "ft-role", - 2430u16 => "venus", - 2431u16 => "venus-se", - 2432u16 => "codasrv", - 2433u16 => "codasrv-se", - 2434u16 => "pxc-epmap", - 2435u16 => "optilogic", - 2436u16 => "topx", - 2437u16 => "unicontrol", - 2438u16 => "msp", - 2439u16 => "sybasedbsynch", - 2440u16 => "spearway", - 2441u16 => "pvsw-inet", - 2442u16 => "netangel", - 2443u16 => "powerclientcsf", - 2444u16 => "btpp2sectrans", - 2445u16 => "dtn1", - 2446u16 => "bues_service", - 2447u16 => "ovwdb", - 2448u16 => "hpppssvr", - 2449u16 => "ratl", - 2450u16 => "netadmin", - 2451u16 => "netchat", - 2452u16 => "snifferclient", - 2453u16 => "madge-ltd", - 2454u16 => "indx-dds", - 2455u16 => "wago-io-system", - 2456u16 => "altav-remmgt", - 2457u16 => "rapido-ip", - 2458u16 => "griffin", - 2459u16 => "xrpl", - 2460u16 => "ms-theater", - 2461u16 => "qadmifoper", - 2462u16 => "qadmifevent", - 2463u16 => "lsi-raid-mgmt", - 2464u16 => "direcpc-si", - 2465u16 => "lbm", - 2466u16 => "lbf", - 2467u16 => "high-criteria", - 2468u16 => "qip-msgd", - 2469u16 => "mti-tcs-comm", - 2470u16 => "taskman-port", - 2471u16 => "seaodbc", - 2472u16 => "c3", - 2473u16 => "aker-cdp", - 2474u16 => "vitalanalysis", - 2475u16 => "ace-server", - 2476u16 => "ace-svr-prop", - 2477u16 => "ssm-cvs", - 2478u16 => "ssm-cssps", - 2479u16 => "ssm-els", - 2480u16 => "powerexchange", - 2481u16 => "giop", - 2482u16 => "giop-ssl", - 2483u16 => "ttc", - 2484u16 => "ttc-ssl", - 2485u16 => "netobjects1", - 2486u16 => "netobjects2", - 2487u16 => "pns", - 2488u16 => "moy-corp", - 2489u16 => "tsilb", - 2490u16 => "qip-qdhcp", - 2491u16 => "conclave-cpp", - 2492u16 => "groove", - 2493u16 => "talarian-mqs", - 2494u16 => "bmc-ar", - 2495u16 => "fast-rem-serv", - 2496u16 => "dirgis", - 2497u16 => "quaddb", - 2498u16 => "odn-castraq", - 2499u16 => "unicontrol", - 2500u16 => "rtsserv", - 2501u16 => "rtsclient", - 2502u16 => "kentrox-prot", - 2503u16 => "nms-dpnss", - 2504u16 => "wlbs", - 2505u16 => "ppcontrol", - 2506u16 => "jbroker", - 2507u16 => "spock", - 2508u16 => "jdatastore", - 2509u16 => "fjmpss", - 2510u16 => "fjappmgrbulk", - 2511u16 => "metastorm", - 2512u16 => "citrixima", - 2513u16 => "citrixadmin", - 2514u16 => "facsys-ntp", - 2515u16 => "facsys-router", - 2516u16 => "maincontrol", - 2517u16 => "call-sig-trans", - 2518u16 => "willy", - 2519u16 => "globmsgsvc", - 2520u16 => "pvsw", - 2521u16 => "adaptecmgr", - 2522u16 => "windb", - 2523u16 => "qke-llc-v3", - 2524u16 => "optiwave-lm", - 2525u16 => "ms-v-worlds", - 2526u16 => "ema-sent-lm", - 2527u16 => "iqserver", - 2528u16 => "ncr_ccl", - 2529u16 => "utsftp", - 2530u16 => "vrcommerce", - 2531u16 => "ito-e-gui", - 2532u16 => "ovtopmd", - 2533u16 => "snifferserver", - 2534u16 => "combox-web-acc", - 2535u16 => "madcap", - 2536u16 => "btpp2audctr1", - 2537u16 => "upgrade", - 2538u16 => "vnwk-prapi", - 2539u16 => "vsiadmin", - 2540u16 => "lonworks", - 2541u16 => "lonworks2", - 2542u16 => "udrawgraph", - 2543u16 => "reftek", - 2544u16 => "novell-zen", - 2545u16 => "sis-emt", - 2546u16 => "vytalvaultbrtp", - 2547u16 => "vytalvaultvsmp", - 2548u16 => "vytalvaultpipe", - 2549u16 => "ipass", - 2550u16 => "ads", - 2551u16 => "isg-uda-server", - 2552u16 => "call-logging", - 2553u16 => "efidiningport", - 2554u16 => "vcnet-link-v10", - 2555u16 => "compaq-wcp", - 2556u16 => "nicetec-nmsvc", - 2557u16 => "nicetec-mgmt", - 2558u16 => "pclemultimedia", - 2559u16 => "lstp", - 2560u16 => "labrat", - 2561u16 => "mosaixcc", - 2562u16 => "delibo", - 2563u16 => "cti-redwood", - 2564u16 => "hp-3000-telnet", - 2565u16 => "coord-svr", - 2566u16 => "pcs-pcw", - 2567u16 => "clp", - 2568u16 => "spamtrap", - 2569u16 => "sonuscallsig", - 2570u16 => "hs-port", - 2571u16 => "cecsvc", - 2572u16 => "ibp", - 2573u16 => "trustestablish", - 2574u16 => "blockade-bpsp", - 2575u16 => "hl7", - 2576u16 => "tclprodebugger", - 2577u16 => "scipticslsrvr", - 2578u16 => "rvs-isdn-dcp", - 2579u16 => "mpfoncl", - 2580u16 => "tributary", - 2581u16 => "argis-te", - 2582u16 => "argis-ds", - 2583u16 => "mon", - 2584u16 => "cyaserv", - 2585u16 => "netx-server", - 2586u16 => "netx-agent", - 2587u16 => "masc", - 2588u16 => "privilege", - 2589u16 => "quartus-tcl", - 2590u16 => "idotdist", - 2591u16 => "maytagshuffle", - 2592u16 => "netrek", - 2593u16 => "mns-mail", - 2594u16 => "dts", - 2595u16 => "worldfusion1", - 2596u16 => "worldfusion2", - 2597u16 => "homesteadglory", - 2598u16 => "citriximaclient", - 2599u16 => "snapd", - 2600u16 => "hpstgmgr", - 2601u16 => "discp-client", - 2602u16 => "discp-server", - 2603u16 => "servicemeter", - 2604u16 => "nsc-ccs", - 2605u16 => "nsc-posa", - 2606u16 => "netmon", - 2607u16 => "connection", - 2608u16 => "wag-service", - 2609u16 => "system-monitor", - 2610u16 => "versa-tek", - 2611u16 => "lionhead", - 2612u16 => "qpasa-agent", - 2613u16 => "smntubootstrap", - 2614u16 => "neveroffline", - 2615u16 => "firepower", - 2616u16 => "appswitch-emp", - 2617u16 => "cmadmin", - 2618u16 => "priority-e-com", - 2619u16 => "bruce", - 2620u16 => "lpsrecommender", - 2621u16 => "miles-apart", - 2622u16 => "metricadbc", - 2623u16 => "lmdp", - 2624u16 => "aria", - 2625u16 => "blwnkl-port", - 2626u16 => "gbjd816", - 2627u16 => "moshebeeri", - 2628u16 => "dict", - 2629u16 => "sitaraserver", - 2630u16 => "sitaramgmt", - 2631u16 => "sitaradir", - 2632u16 => "irdg-post", - 2633u16 => "interintelli", - 2634u16 => "pk-electronics", - 2635u16 => "backburner", - 2636u16 => "solve", - 2637u16 => "imdocsvc", - 2638u16 => "sybaseanywhere", - 2639u16 => "aminet", - 2640u16 => "ami-control", - 2641u16 => "hdl-srv", - 2642u16 => "tragic", - 2643u16 => "gte-samp", - 2644u16 => "travsoft-ipx-t", - 2645u16 => "novell-ipx-cmd", - 2646u16 => "and-lm", - 2647u16 => "syncserver", - 2648u16 => "upsnotifyprot", - 2649u16 => "vpsipport", - 2650u16 => "eristwoguns", - 2651u16 => "ebinsite", - 2652u16 => "interpathpanel", - 2653u16 => "sonus", - 2654u16 => "corel_vncadmin", - 2655u16 => "unglue", - 2656u16 => "kana", - 2657u16 => "sns-dispatcher", - 2658u16 => "sns-admin", - 2659u16 => "sns-query", - 2660u16 => "gcmonitor", - 2661u16 => "olhost", - 2662u16 => "bintec-capi", - 2663u16 => "bintec-tapi", - 2664u16 => "patrol-mq-gm", - 2665u16 => "patrol-mq-nm", - 2666u16 => "extensis", - 2667u16 => "alarm-clock-s", - 2668u16 => "alarm-clock-c", - 2669u16 => "toad", - 2670u16 => "tve-announce", - 2671u16 => "newlixreg", - 2672u16 => "nhserver", - 2673u16 => "firstcall42", - 2674u16 => "ewnn", - 2675u16 => "ttc-etap", - 2676u16 => "simslink", - 2677u16 => "gadgetgate1way", - 2678u16 => "gadgetgate2way", - 2679u16 => "syncserverssl", - 2680u16 => "pxc-sapxom", - 2681u16 => "mpnjsomb", - 2683u16 => "ncdloadbalance", - 2684u16 => "mpnjsosv", - 2685u16 => "mpnjsocl", - 2686u16 => "mpnjsomg", - 2687u16 => "pq-lic-mgmt", - 2688u16 => "md-cg-http", - 2689u16 => "fastlynx", - 2690u16 => "hp-nnm-data", - 2691u16 => "itinternet", - 2692u16 => "admins-lms", - 2694u16 => "pwrsevent", - 2695u16 => "vspread", - 2696u16 => "unifyadmin", - 2697u16 => "oce-snmp-trap", - 2698u16 => "mck-ivpip", - 2699u16 => "csoft-plusclnt", - 2700u16 => "tqdata", - 2701u16 => "sms-rcinfo", - 2702u16 => "sms-xfer", - 2703u16 => "sms-chat", - 2704u16 => "sms-remctrl", - 2705u16 => "sds-admin", - 2706u16 => "ncdmirroring", - 2707u16 => "emcsymapiport", - 2708u16 => "banyan-net", - 2709u16 => "supermon", - 2710u16 => "sso-service", - 2711u16 => "sso-control", - 2712u16 => "aocp", - 2713u16 => "raventbs", - 2714u16 => "raventdm", - 2715u16 => "hpstgmgr2", - 2716u16 => "inova-ip-disco", - 2717u16 => "pn-requester", - 2718u16 => "pn-requester2", - 2719u16 => "scan-change", - 2720u16 => "wkars", - 2721u16 => "smart-diagnose", - 2722u16 => "proactivesrvr", - 2723u16 => "watchdog-nt", - 2724u16 => "qotps", - 2725u16 => "msolap-ptp2", - 2726u16 => "tams", - 2727u16 => "mgcp-callagent", - 2728u16 => "sqdr", - 2729u16 => "tcim-control", - 2730u16 => "nec-raidplus", - 2731u16 => "fyre-messanger", - 2732u16 => "g5m", - 2733u16 => "signet-ctf", - 2734u16 => "ccs-software", - 2735u16 => "netiq-mc", - 2736u16 => "radwiz-nms-srv", - 2737u16 => "srp-feedback", - 2738u16 => "ndl-tcp-ois-gw", - 2739u16 => "tn-timing", - 2740u16 => "alarm", - 2741u16 => "tsb", - 2742u16 => "tsb2", - 2743u16 => "murx", - 2744u16 => "honyaku", - 2745u16 => "urbisnet", - 2746u16 => "cpudpencap", - 2747u16 => "fjippol-swrly", - 2748u16 => "fjippol-polsvr", - 2749u16 => "fjippol-cnsl", - 2750u16 => "fjippol-port1", - 2751u16 => "fjippol-port2", - 2752u16 => "rsisysaccess", - 2753u16 => "de-spot", - 2754u16 => "apollo-cc", - 2755u16 => "expresspay", - 2756u16 => "simplement-tie", - 2757u16 => "cnrp", - 2758u16 => "apollo-status", - 2759u16 => "apollo-gms", - 2760u16 => "sabams", - 2761u16 => "dicom-iscl", - 2762u16 => "dicom-tls", - 2763u16 => "desktop-dna", - 2764u16 => "data-insurance", - 2765u16 => "qip-audup", - 2766u16 => "compaq-scp", - 2767u16 => "uadtc", - 2768u16 => "uacs", - 2769u16 => "exce", - 2770u16 => "veronica", - 2771u16 => "vergencecm", - 2772u16 => "auris", - 2773u16 => "rbakcup1", - 2774u16 => "rbakcup2", - 2775u16 => "smpp", - 2776u16 => "ridgeway1", - 2777u16 => "ridgeway2", - 2778u16 => "gwen-sonya", - 2779u16 => "lbc-sync", - 2780u16 => "lbc-control", - 2781u16 => "whosells", - 2782u16 => "everydayrc", - 2783u16 => "aises", - 2784u16 => "www-dev", - 2785u16 => "aic-np", - 2786u16 => "aic-oncrpc", - 2787u16 => "piccolo", - 2788u16 => "fryeserv", - 2789u16 => "media-agent", - 2790u16 => "plgproxy", - 2791u16 => "mtport-regist", - 2792u16 => "f5-globalsite", - 2793u16 => "initlsmsad", - 2795u16 => "livestats", - 2796u16 => "ac-tech", - 2797u16 => "esp-encap", - 2798u16 => "tmesis-upshot", - 2799u16 => "icon-discover", - 2800u16 => "acc-raid", - 2801u16 => "igcp", - 2802u16 => "veritas-tcp1", - 2803u16 => "btprjctrl", - 2804u16 => "dvr-esm", - 2805u16 => "wta-wsp-s", - 2806u16 => "cspuni", - 2807u16 => "cspmulti", - 2808u16 => "j-lan-p", - 2809u16 => "corbaloc", - 2810u16 => "netsteward", - 2811u16 => "gsiftp", - 2812u16 => "atmtcp", - 2813u16 => "llm-pass", - 2814u16 => "llm-csv", - 2815u16 => "lbc-measure", - 2816u16 => "lbc-watchdog", - 2817u16 => "nmsigport", - 2818u16 => "rmlnk", - 2819u16 => "fc-faultnotify", - 2820u16 => "univision", - 2821u16 => "vrts-at-port", - 2822u16 => "ka0wuc", - 2823u16 => "cqg-netlan", - 2824u16 => "cqg-netlan-1", - 2826u16 => "slc-systemlog", - 2827u16 => "slc-ctrlrloops", - 2828u16 => "itm-lm", - 2829u16 => "silkp1", - 2830u16 => "silkp2", - 2831u16 => "silkp3", - 2832u16 => "silkp4", - 2833u16 => "glishd", - 2834u16 => "evtp", - 2835u16 => "evtp-data", - 2836u16 => "catalyst", - 2837u16 => "repliweb", - 2838u16 => "starbot", - 2839u16 => "nmsigport", - 2840u16 => "l3-exprt", - 2841u16 => "l3-ranger", - 2842u16 => "l3-hawk", - 2843u16 => "pdnet", - 2844u16 => "bpcp-poll", - 2845u16 => "bpcp-trap", - 2846u16 => "aimpp-hello", - 2847u16 => "aimpp-port-req", - 2848u16 => "amt-blc-port", - 2849u16 => "fxp", - 2850u16 => "metaconsole", - 2851u16 => "webemshttp", - 2852u16 => "bears-01", - 2853u16 => "ispipes", - 2854u16 => "infomover", - 2855u16 => "msrp", - 2856u16 => "cesdinv", - 2857u16 => "simctlp", - 2858u16 => "ecnp", - 2859u16 => "activememory", - 2860u16 => "dialpad-voice1", - 2861u16 => "dialpad-voice2", - 2862u16 => "ttg-protocol", - 2863u16 => "sonardata", - 2864u16 => "astronova-main", - 2865u16 => "pit-vpn", - 2866u16 => "iwlistener", - 2867u16 => "esps-portal", - 2868u16 => "npep-messaging", - 2869u16 => "icslap", - 2870u16 => "daishi", - 2871u16 => "msi-selectplay", - 2872u16 => "radix", - 2874u16 => "dxmessagebase1", - 2875u16 => "dxmessagebase2", - 2876u16 => "sps-tunnel", - 2877u16 => "bluelance", - 2878u16 => "aap", - 2879u16 => "ucentric-ds", - 2880u16 => "synapse", - 2881u16 => "ndsp", - 2882u16 => "ndtp", - 2883u16 => "ndnp", - 2884u16 => "flashmsg", - 2885u16 => "topflow", - 2886u16 => "responselogic", - 2887u16 => "aironetddp", - 2888u16 => "spcsdlobby", - 2889u16 => "rsom", - 2890u16 => "cspclmulti", - 2891u16 => "cinegrfx-elmd", - 2892u16 => "snifferdata", - 2893u16 => "vseconnector", - 2894u16 => "abacus-remote", - 2895u16 => "natuslink", - 2896u16 => "ecovisiong6-1", - 2897u16 => "citrix-rtmp", - 2898u16 => "appliance-cfg", - 2899u16 => "powergemplus", - 2900u16 => "quicksuite", - 2901u16 => "allstorcns", - 2902u16 => "netaspi", - 2903u16 => "suitcase", - 2904u16 => "m2ua", - 2905u16 => "m3ua", - 2906u16 => "caller9", - 2907u16 => "webmethods-b2b", - 2908u16 => "mao", - 2909u16 => "funk-dialout", - 2910u16 => "tdaccess", - 2911u16 => "blockade", - 2912u16 => "epicon", - 2913u16 => "boosterware", - 2914u16 => "gamelobby", - 2915u16 => "tksocket", - 2916u16 => "elvin_server", - 2917u16 => "elvin_client", - 2918u16 => "kastenchasepad", - 2919u16 => "roboer", - 2920u16 => "roboeda", - 2921u16 => "cesdcdman", - 2922u16 => "cesdcdtrn", - 2923u16 => "wta-wsp-wtp-s", - 2924u16 => "precise-vip", - 2926u16 => "mobile-file-dl", - 2927u16 => "unimobilectrl", - 2928u16 => "redstone-cpss", - 2929u16 => "amx-webadmin", - 2930u16 => "amx-weblinx", - 2931u16 => "circle-x", - 2932u16 => "incp", - 2933u16 => "4-tieropmgw", - 2934u16 => "4-tieropmcli", - 2935u16 => "qtp", - 2936u16 => "otpatch", - 2937u16 => "pnaconsult-lm", - 2938u16 => "sm-pas-1", - 2939u16 => "sm-pas-2", - 2940u16 => "sm-pas-3", - 2941u16 => "sm-pas-4", - 2942u16 => "sm-pas-5", - 2943u16 => "ttnrepository", - 2944u16 => "megaco-h248", - 2945u16 => "h248-binary", - 2946u16 => "fjsvmpor", - 2947u16 => "gpsd", - 2948u16 => "wap-push", - 2949u16 => "wap-pushsecure", - 2950u16 => "esip", - 2951u16 => "ottp", - 2952u16 => "mpfwsas", - 2953u16 => "ovalarmsrv", - 2954u16 => "ovalarmsrv-cmd", - 2955u16 => "csnotify", - 2956u16 => "ovrimosdbman", - 2957u16 => "jmact5", - 2958u16 => "jmact6", - 2959u16 => "rmopagt", - 2960u16 => "dfoxserver", - 2961u16 => "boldsoft-lm", - 2962u16 => "iph-policy-cli", - 2963u16 => "iph-policy-adm", - 2964u16 => "bullant-srap", - 2965u16 => "bullant-rap", - 2966u16 => "idp-infotrieve", - 2967u16 => "ssc-agent", - 2968u16 => "enpp", - 2969u16 => "essp", - 2970u16 => "index-net", - 2971u16 => "netclip", - 2972u16 => "pmsm-webrctl", - 2973u16 => "svnetworks", - 2974u16 => "signal", - 2975u16 => "fjmpcm", - 2976u16 => "cns-srv-port", - 2977u16 => "ttc-etap-ns", - 2978u16 => "ttc-etap-ds", - 2979u16 => "h263-video", - 2980u16 => "wimd", - 2981u16 => "mylxamport", - 2982u16 => "iwb-whiteboard", - 2983u16 => "netplan", - 2984u16 => "hpidsadmin", - 2985u16 => "hpidsagent", - 2986u16 => "stonefalls", - 2987u16 => "identify", - 2988u16 => "hippad", - 2989u16 => "zarkov", - 2990u16 => "boscap", - 2991u16 => "wkstn-mon", - 2992u16 => "avenyo", - 2993u16 => "veritas-vis1", - 2994u16 => "veritas-vis2", - 2995u16 => "idrs", - 2996u16 => "vsixml", - 2997u16 => "rebol", - 2998u16 => "realsecure", - 2999u16 => "remoteware-un", - 3000u16 => "remoteware-cl", - 3001u16 => "origo-native", - 3002u16 => "remoteware-srv", - 3003u16 => "cgms", - 3004u16 => "csoftragent", - 3005u16 => "geniuslm", - 3006u16 => "ii-admin", - 3007u16 => "lotusmtap", - 3008u16 => "midnight-tech", - 3009u16 => "pxc-ntfy", - 3010u16 => "gw", - 3011u16 => "trusted-web", - 3012u16 => "twsdss", - 3013u16 => "gilatskysurfer", - 3014u16 => "broker_service", - 3015u16 => "nati-dstp", - 3016u16 => "notify_srvr", - 3017u16 => "event_listener", - 3018u16 => "srvc_registry", - 3019u16 => "resource_mgr", - 3020u16 => "cifs", - 3021u16 => "agriserver", - 3022u16 => "csregagent", - 3023u16 => "magicnotes", - 3024u16 => "nds_sso", - 3025u16 => "arepa-raft", - 3026u16 => "agri-gateway", - 3027u16 => "LiebDevMgmt_C", - 3028u16 => "LiebDevMgmt_DM", - 3029u16 => "LiebDevMgmt_A", - 3030u16 => "arepa-cas", - 3031u16 => "eppc", - 3032u16 => "redwood-chat", - 3033u16 => "pdb", - 3034u16 => "osmosis-aeea", - 3035u16 => "fjsv-gssagt", - 3036u16 => "hagel-dump", - 3037u16 => "hp-san-mgmt", - 3038u16 => "santak-ups", - 3039u16 => "cogitate", - 3040u16 => "tomato-springs", - 3041u16 => "di-traceware", - 3042u16 => "journee", - 3043u16 => "brp", - 3044u16 => "epp", - 3045u16 => "responsenet", - 3046u16 => "di-ase", - 3047u16 => "hlserver", - 3048u16 => "pctrader", - 3049u16 => "nsws", - 3050u16 => "gds_db", - 3051u16 => "galaxy-server", - 3052u16 => "apc-3052", - 3053u16 => "dsom-server", - 3054u16 => "amt-cnf-prot", - 3055u16 => "policyserver", - 3056u16 => "cdl-server", - 3057u16 => "goahead-fldup", - 3058u16 => "videobeans", - 3059u16 => "qsoft", - 3060u16 => "interserver", - 3061u16 => "cautcpd", - 3062u16 => "ncacn-ip-tcp", - 3063u16 => "ncadg-ip-udp", - 3064u16 => "rprt", - 3065u16 => "slinterbase", - 3066u16 => "netattachsdmp", - 3067u16 => "fjhpjp", - 3068u16 => "ls3bcast", - 3069u16 => "ls3", - 3070u16 => "mgxswitch", - 3071u16 => "xplat-replicate", - 3072u16 => "csd-monitor", - 3073u16 => "vcrp", - 3074u16 => "xbox", - 3075u16 => "orbix-locator", - 3076u16 => "orbix-config", - 3077u16 => "orbix-loc-ssl", - 3078u16 => "orbix-cfg-ssl", - 3079u16 => "lv-frontpanel", - 3080u16 => "stm_pproc", - 3081u16 => "tl1-lv", - 3082u16 => "tl1-raw", - 3083u16 => "tl1-telnet", - 3084u16 => "itm-mccs", - 3085u16 => "pcihreq", - 3086u16 => "jdl-dbkitchen", - 3087u16 => "asoki-sma", - 3088u16 => "xdtp", - 3089u16 => "ptk-alink", - 3090u16 => "stss", - 3091u16 => "1ci-smcs", - 3093u16 => "rapidmq-center", - 3094u16 => "rapidmq-reg", - 3095u16 => "panasas", - 3096u16 => "ndl-aps", - 3098u16 => "umm-port", - 3099u16 => "chmd", - 3100u16 => "opcon-xps", - 3101u16 => "hp-pxpib", - 3102u16 => "slslavemon", - 3103u16 => "autocuesmi", - 3104u16 => "autocuelog", - 3105u16 => "cardbox", - 3106u16 => "cardbox-http", - 3107u16 => "business", - 3108u16 => "geolocate", - 3109u16 => "personnel", - 3110u16 => "sim-control", - 3111u16 => "wsynch", - 3112u16 => "ksysguard", - 3113u16 => "cs-auth-svr", - 3114u16 => "ccmad", - 3115u16 => "mctet-master", - 3116u16 => "mctet-gateway", - 3117u16 => "mctet-jserv", - 3118u16 => "pkagent", - 3119u16 => "d2000kernel", - 3120u16 => "d2000webserver", - 3121u16 => "pcmk-remote", - 3122u16 => "vtr-emulator", - 3123u16 => "edix", - 3124u16 => "beacon-port", - 3125u16 => "a13-an", - 3127u16 => "ctx-bridge", - 3128u16 => "ndl-aas", - 3129u16 => "netport-id", - 3130u16 => "icpv2", - 3131u16 => "netbookmark", - 3132u16 => "ms-rule-engine", - 3133u16 => "prism-deploy", - 3134u16 => "ecp", - 3135u16 => "peerbook-port", - 3136u16 => "grubd", - 3137u16 => "rtnt-1", - 3138u16 => "rtnt-2", - 3139u16 => "incognitorv", - 3140u16 => "ariliamulti", - 3141u16 => "vmodem", - 3142u16 => "rdc-wh-eos", - 3143u16 => "seaview", - 3144u16 => "tarantella", - 3145u16 => "csi-lfap", - 3146u16 => "bears-02", - 3147u16 => "rfio", - 3148u16 => "nm-game-admin", - 3149u16 => "nm-game-server", - 3150u16 => "nm-asses-admin", - 3151u16 => "nm-assessor", - 3152u16 => "feitianrockey", - 3153u16 => "s8-client-port", - 3154u16 => "ccmrmi", - 3155u16 => "jpegmpeg", - 3156u16 => "indura", - 3157u16 => "e3consultants", - 3158u16 => "stvp", - 3159u16 => "navegaweb-port", - 3160u16 => "tip-app-server", - 3161u16 => "doc1lm", - 3162u16 => "sflm", - 3163u16 => "res-sap", - 3164u16 => "imprs", - 3165u16 => "newgenpay", - 3166u16 => "sossecollector", - 3167u16 => "nowcontact", - 3168u16 => "poweronnud", - 3169u16 => "serverview-as", - 3170u16 => "serverview-asn", - 3171u16 => "serverview-gf", - 3172u16 => "serverview-rm", - 3173u16 => "serverview-icc", - 3174u16 => "armi-server", - 3175u16 => "t1-e1-over-ip", - 3176u16 => "ars-master", - 3177u16 => "phonex-port", - 3178u16 => "radclientport", - 3179u16 => "h2gf-w-2m", - 3180u16 => "mc-brk-srv", - 3181u16 => "bmcpatrolagent", - 3182u16 => "bmcpatrolrnvu", - 3183u16 => "cops-tls", - 3184u16 => "apogeex-port", - 3185u16 => "smpppd", - 3186u16 => "iiw-port", - 3187u16 => "odi-port", - 3188u16 => "brcm-comm-port", - 3189u16 => "pcle-infex", - 3190u16 => "csvr-proxy", - 3191u16 => "csvr-sslproxy", - 3192u16 => "firemonrcc", - 3193u16 => "spandataport", - 3194u16 => "magbind", - 3195u16 => "ncu-1", - 3196u16 => "ncu-2", - 3197u16 => "embrace-dp-s", - 3198u16 => "embrace-dp-c", - 3199u16 => "dmod-workspace", - 3200u16 => "tick-port", - 3201u16 => "cpq-tasksmart", - 3202u16 => "intraintra", - 3203u16 => "netwatcher-mon", - 3204u16 => "netwatcher-db", - 3205u16 => "isns", - 3206u16 => "ironmail", - 3207u16 => "vx-auth-port", - 3208u16 => "pfu-prcallback", - 3209u16 => "netwkpathengine", - 3210u16 => "flamenco-proxy", - 3211u16 => "avsecuremgmt", - 3212u16 => "surveyinst", - 3213u16 => "neon24x7", - 3214u16 => "jmq-daemon-1", - 3215u16 => "jmq-daemon-2", - 3216u16 => "ferrari-foam", - 3217u16 => "unite", - 3218u16 => "smartpackets", - 3219u16 => "wms-messenger", - 3220u16 => "xnm-ssl", - 3221u16 => "xnm-clear-text", - 3222u16 => "glbp", - 3223u16 => "digivote", - 3224u16 => "aes-discovery", - 3225u16 => "fcip-port", - 3226u16 => "isi-irp", - 3227u16 => "dwnmshttp", - 3228u16 => "dwmsgserver", - 3229u16 => "global-cd-port", - 3230u16 => "sftdst-port", - 3231u16 => "vidigo", - 3232u16 => "mdtp", - 3233u16 => "whisker", - 3234u16 => "alchemy", - 3235u16 => "mdap-port", - 3236u16 => "apparenet-ts", - 3237u16 => "apparenet-tps", - 3238u16 => "apparenet-as", - 3239u16 => "apparenet-ui", - 3240u16 => "triomotion", - 3241u16 => "sysorb", - 3242u16 => "sdp-id-port", - 3243u16 => "timelot", - 3244u16 => "onesaf", - 3245u16 => "vieo-fe", - 3246u16 => "dvt-system", - 3247u16 => "dvt-data", - 3248u16 => "procos-lm", - 3249u16 => "ssp", - 3250u16 => "hicp", - 3251u16 => "sysscanner", - 3252u16 => "dhe", - 3253u16 => "pda-data", - 3254u16 => "pda-sys", - 3255u16 => "semaphore", - 3256u16 => "cpqrpm-agent", - 3257u16 => "cpqrpm-server", - 3258u16 => "ivecon-port", - 3259u16 => "epncdp2", - 3260u16 => "iscsi-target", - 3261u16 => "winshadow", - 3262u16 => "necp", - 3263u16 => "ecolor-imager", - 3264u16 => "ccmail", - 3265u16 => "altav-tunnel", - 3266u16 => "ns-cfg-server", - 3267u16 => "ibm-dial-out", - 3268u16 => "msft-gc", - 3269u16 => "msft-gc-ssl", - 3270u16 => "verismart", - 3271u16 => "csoft-prev", - 3272u16 => "user-manager", - 3273u16 => "sxmp", - 3274u16 => "ordinox-server", - 3275u16 => "samd", - 3276u16 => "maxim-asics", - 3277u16 => "awg-proxy", - 3278u16 => "lkcmserver", - 3279u16 => "admind", - 3280u16 => "vs-server", - 3281u16 => "sysopt", - 3282u16 => "datusorb", - 3283u16 => "Apple Remote Desktop (Net Assistant)", - 3284u16 => "4talk", - 3285u16 => "plato", - 3286u16 => "e-net", - 3287u16 => "directvdata", - 3288u16 => "cops", - 3289u16 => "enpc", - 3290u16 => "caps-lm", - 3291u16 => "sah-lm", - 3292u16 => "cart-o-rama", - 3293u16 => "fg-fps", - 3294u16 => "fg-gip", - 3295u16 => "dyniplookup", - 3296u16 => "rib-slm", - 3297u16 => "cytel-lm", - 3298u16 => "deskview", - 3299u16 => "pdrncs", - 3300u16 => "ceph", - 3302u16 => "mcs-fastmail", - 3303u16 => "opsession-clnt", - 3304u16 => "opsession-srvr", - 3305u16 => "odette-ftp", - 3306u16 => "mysql", - 3307u16 => "opsession-prxy", - 3308u16 => "tns-server", - 3309u16 => "tns-adv", - 3310u16 => "dyna-access", - 3311u16 => "mcns-tel-ret", - 3312u16 => "appman-server", - 3313u16 => "uorb", - 3314u16 => "uohost", - 3315u16 => "cdid", - 3316u16 => "aicc-cmi", - 3317u16 => "vsaiport", - 3318u16 => "ssrip", - 3319u16 => "sdt-lmd", - 3320u16 => "officelink2000", - 3321u16 => "vnsstr", - 3326u16 => "sftu", - 3327u16 => "bbars", - 3328u16 => "egptlm", - 3329u16 => "hp-device-disc", - 3330u16 => "mcs-calypsoicf", - 3331u16 => "mcs-messaging", - 3332u16 => "mcs-mailsvr", - 3333u16 => "dec-notes", - 3334u16 => "directv-web", - 3335u16 => "directv-soft", - 3336u16 => "directv-tick", - 3337u16 => "directv-catlg", - 3338u16 => "anet-b", - 3339u16 => "anet-l", - 3340u16 => "anet-m", - 3341u16 => "anet-h", - 3342u16 => "webtie", - 3343u16 => "ms-cluster-net", - 3344u16 => "bnt-manager", - 3345u16 => "influence", - 3346u16 => "trnsprntproxy", - 3347u16 => "phoenix-rpc", - 3348u16 => "pangolin-laser", - 3349u16 => "chevinservices", - 3350u16 => "findviatv", - 3351u16 => "btrieve", - 3352u16 => "ssql", - 3353u16 => "fatpipe", - 3354u16 => "suitjd", - 3355u16 => "ordinox-dbase", - 3356u16 => "upnotifyps", - 3357u16 => "adtech-test", - 3358u16 => "mpsysrmsvr", - 3359u16 => "wg-netforce", - 3360u16 => "kv-server", - 3361u16 => "kv-agent", - 3362u16 => "dj-ilm", - 3363u16 => "nati-vi-server", - 3364u16 => "creativeserver", - 3365u16 => "contentserver", - 3366u16 => "creativepartnr", - 3372u16 => "tip2", - 3373u16 => "lavenir-lm", - 3374u16 => "cluster-disc", - 3375u16 => "vsnm-agent", - 3376u16 => "cdbroker", - 3377u16 => "cogsys-lm", - 3378u16 => "wsicopy", - 3379u16 => "socorfs", - 3380u16 => "sns-channels", - 3381u16 => "geneous", - 3382u16 => "fujitsu-neat", - 3383u16 => "esp-lm", - 3384u16 => "hp-clic", - 3385u16 => "qnxnetman", - 3386u16 => "gprs-data", - 3387u16 => "backroomnet", - 3388u16 => "cbserver", - 3389u16 => "ms-wbt-server", - 3390u16 => "dsc", - 3391u16 => "savant", - 3392u16 => "efi-lm", - 3393u16 => "d2k-tapestry1", - 3394u16 => "d2k-tapestry2", - 3395u16 => "dyna-lm", - 3396u16 => "printer_agent", - 3397u16 => "cloanto-lm", - 3398u16 => "mercantile", - 3399u16 => "csms", - 3400u16 => "csms2", - 3401u16 => "filecast", - 3402u16 => "fxaengine-net", - 3405u16 => "nokia-ann-ch1", - 3406u16 => "nokia-ann-ch2", - 3407u16 => "ldap-admin", - 3408u16 => "BESApi", - 3409u16 => "networklens", - 3410u16 => "networklenss", - 3411u16 => "biolink-auth", - 3412u16 => "xmlblaster", - 3413u16 => "svnet", - 3414u16 => "wip-port", - 3415u16 => "bcinameservice", - 3416u16 => "commandport", - 3417u16 => "csvr", - 3418u16 => "rnmap", - 3419u16 => "softaudit", - 3420u16 => "ifcp-port", - 3421u16 => "bmap", - 3422u16 => "rusb-sys-port", - 3423u16 => "xtrm", - 3424u16 => "xtrms", - 3425u16 => "agps-port", - 3426u16 => "arkivio", - 3427u16 => "websphere-snmp", - 3428u16 => "twcss", - 3429u16 => "gcsp", - 3430u16 => "ssdispatch", - 3431u16 => "ndl-als", - 3432u16 => "osdcp", - 3433u16 => "opnet-smp", - 3434u16 => "opencm", - 3435u16 => "pacom", - 3436u16 => "gc-config", - 3437u16 => "autocueds", - 3438u16 => "spiral-admin", - 3439u16 => "hri-port", - 3440u16 => "ans-console", - 3441u16 => "connect-client", - 3442u16 => "connect-server", - 3443u16 => "ov-nnm-websrv", - 3444u16 => "denali-server", - 3445u16 => "monp", - 3446u16 => "3comfaxrpc", - 3447u16 => "directnet", - 3448u16 => "dnc-port", - 3449u16 => "hotu-chat", - 3450u16 => "castorproxy", - 3451u16 => "asam", - 3452u16 => "sabp-signal", - 3453u16 => "pscupd", - 3454u16 => "mira", - 3455u16 => "prsvp", - 3456u16 => "vat", - 3457u16 => "vat-control", - 3458u16 => "d3winosfi", - 3459u16 => "integral", - 3460u16 => "edm-manager", - 3461u16 => "edm-stager", - 3462u16 => "edm-std-notify", - 3463u16 => "edm-adm-notify", - 3464u16 => "edm-mgr-sync", - 3465u16 => "edm-mgr-cntrl", - 3466u16 => "workflow", - 3467u16 => "rcst", - 3468u16 => "ttcmremotectrl", - 3469u16 => "pluribus", - 3470u16 => "jt400", - 3471u16 => "jt400-ssl", - 3472u16 => "jaugsremotec-1", - 3473u16 => "jaugsremotec-2", - 3474u16 => "ttntspauto", - 3475u16 => "genisar-port", - 3476u16 => "nppmp", - 3477u16 => "ecomm", - 3478u16 => "stun-behavior", - 3479u16 => "twrpc", - 3480u16 => "plethora", - 3481u16 => "cleanerliverc", - 3482u16 => "vulture", - 3483u16 => "slim-devices", - 3484u16 => "gbs-stp", - 3485u16 => "celatalk", - 3486u16 => "ifsf-hb-port", - 3487u16 => "ltctcp", - 3488u16 => "fs-rh-srv", - 3489u16 => "dtp-dia", - 3490u16 => "colubris", - 3491u16 => "swr-port", - 3492u16 => "tvdumtray-port", - 3493u16 => "nut", - 3494u16 => "ibm3494", - 3495u16 => "seclayer-tcp", - 3496u16 => "seclayer-tls", - 3497u16 => "ipether232port", - 3498u16 => "dashpas-port", - 3499u16 => "sccip-media", - 3500u16 => "rtmp-port", - 3501u16 => "isoft-p2p", - 3502u16 => "avinstalldisc", - 3503u16 => "lsp-ping", - 3504u16 => "ironstorm", - 3505u16 => "ccmcomm", - 3506u16 => "apc-3506", - 3507u16 => "nesh-broker", - 3508u16 => "interactionweb", - 3509u16 => "vt-ssl", - 3510u16 => "xss-port", - 3511u16 => "webmail-2", - 3512u16 => "aztec", - 3513u16 => "arcpd", - 3514u16 => "must-p2p", - 3515u16 => "must-backplane", - 3516u16 => "smartcard-port", - 3517u16 => "802-11-iapp", - 3518u16 => "artifact-msg", - 3519u16 => "nvmsgd", - 3520u16 => "galileolog", - 3521u16 => "mc3ss", - 3522u16 => "nssocketport", - 3523u16 => "odeumservlink", - 3524u16 => "ecmport", - 3525u16 => "eisport", - 3526u16 => "starquiz-port", - 3527u16 => "beserver-msg-q", - 3528u16 => "jboss-iiop", - 3529u16 => "jboss-iiop-ssl", - 3530u16 => "gf", - 3531u16 => "joltid", - 3532u16 => "raven-rmp", - 3533u16 => "raven-rdp", - 3534u16 => "urld-port", - 3535u16 => "ms-la", - 3536u16 => "snac", - 3537u16 => "ni-visa-remote", - 3538u16 => "ibm-diradm", - 3539u16 => "ibm-diradm-ssl", - 3540u16 => "pnrp-port", - 3541u16 => "voispeed-port", - 3542u16 => "hacl-monitor", - 3543u16 => "qftest-lookup", - 3544u16 => "teredo", - 3545u16 => "camac", - 3547u16 => "symantec-sim", - 3548u16 => "interworld", - 3549u16 => "tellumat-nms", - 3550u16 => "ssmpp", - 3551u16 => "apcupsd", - 3552u16 => "taserver", - 3553u16 => "rbr-discovery", - 3554u16 => "questnotify", - 3555u16 => "razor", - 3556u16 => "sky-transport", - 3557u16 => "personalos-001", - 3558u16 => "mcp-port", - 3559u16 => "cctv-port", - 3560u16 => "iniserve-port", - 3561u16 => "bmc-onekey", - 3562u16 => "sdbproxy", - 3563u16 => "watcomdebug", - 3564u16 => "esimport", - 3565u16 => "m2pa", - 3566u16 => "quest-data-hub", - 3567u16 => "dof-eps", - 3568u16 => "dof-tunnel-sec", - 3569u16 => "mbg-ctrl", - 3570u16 => "mccwebsvr-port", - 3571u16 => "megardsvr-port", - 3572u16 => "megaregsvrport", - 3573u16 => "tag-ups-1", - 3574u16 => "dmaf-server", - 3575u16 => "ccm-port", - 3576u16 => "cmc-port", - 3577u16 => "config-port", - 3578u16 => "data-port", - 3579u16 => "ttat3lb", - 3580u16 => "nati-svrloc", - 3581u16 => "kfxaclicensing", - 3582u16 => "press", - 3583u16 => "canex-watch", - 3584u16 => "u-dbap", - 3585u16 => "emprise-lls", - 3586u16 => "emprise-lsc", - 3587u16 => "p2pgroup", - 3588u16 => "sentinel", - 3589u16 => "isomair", - 3590u16 => "wv-csp-sms", - 3591u16 => "gtrack-server", - 3592u16 => "gtrack-ne", - 3593u16 => "bpmd", - 3594u16 => "mediaspace", - 3595u16 => "shareapp", - 3596u16 => "iw-mmogame", - 3597u16 => "a14", - 3598u16 => "a15", - 3599u16 => "quasar-server", - 3600u16 => "trap-daemon", - 3601u16 => "visinet-gui", - 3602u16 => "infiniswitchcl", - 3603u16 => "int-rcv-cntrl", - 3604u16 => "bmc-jmx-port", - 3605u16 => "comcam-io", - 3606u16 => "splitlock", - 3607u16 => "precise-i3", - 3608u16 => "trendchip-dcp", - 3609u16 => "cpdi-pidas-cm", - 3610u16 => "echonet", - 3611u16 => "six-degrees", - 3612u16 => "dataprotector", - 3613u16 => "alaris-disc", - 3614u16 => "sigma-port", - 3615u16 => "start-network", - 3616u16 => "cd3o-protocol", - 3617u16 => "sharp-server", - 3618u16 => "aairnet-1", - 3619u16 => "aairnet-2", - 3620u16 => "ep-pcp", - 3621u16 => "ep-nsp", - 3622u16 => "ff-lr-port", - 3623u16 => "haipe-discover", - 3624u16 => "dist-upgrade", - 3625u16 => "volley", - 3626u16 => "bvcdaemon-port", - 3627u16 => "jamserverport", - 3628u16 => "ept-machine", - 3629u16 => "escvpnet", - 3630u16 => "cs-remote-db", - 3631u16 => "cs-services", - 3632u16 => "distcc", - 3633u16 => "wacp", - 3634u16 => "hlibmgr", - 3635u16 => "sdo", - 3636u16 => "servistaitsm", - 3637u16 => "scservp", - 3638u16 => "ehp-backup", - 3639u16 => "xap-ha", - 3640u16 => "netplay-port1", - 3641u16 => "netplay-port2", - 3642u16 => "juxml-port", - 3643u16 => "audiojuggler", - 3644u16 => "ssowatch", - 3645u16 => "cyc", - 3646u16 => "xss-srv-port", - 3647u16 => "splitlock-gw", - 3648u16 => "fjcp", - 3649u16 => "nmmp", - 3650u16 => "prismiq-plugin", - 3651u16 => "xrpc-registry", - 3652u16 => "vxcrnbuport", - 3653u16 => "tsp", - 3654u16 => "vaprtm", - 3655u16 => "abatemgr", - 3656u16 => "abatjss", - 3657u16 => "immedianet-bcn", - 3658u16 => "ps-ams", - 3659u16 => "apple-sasl", - 3660u16 => "can-nds-ssl", - 3661u16 => "can-ferret-ssl", - 3662u16 => "pserver", - 3663u16 => "dtp", - 3664u16 => "ups-engine", - 3665u16 => "ent-engine", - 3666u16 => "eserver-pap", - 3667u16 => "infoexch", - 3668u16 => "dell-rm-port", - 3669u16 => "casanswmgmt", - 3670u16 => "smile", - 3671u16 => "efcp", - 3672u16 => "lispworks-orb", - 3673u16 => "mediavault-gui", - 3674u16 => "wininstall-ipc", - 3675u16 => "calltrax", - 3676u16 => "va-pacbase", - 3677u16 => "roverlog", - 3678u16 => "ipr-dglt", - 3679u16 => "Escale (Newton Dock)", - 3680u16 => "npds-tracker", - 3681u16 => "bts-x73", - 3682u16 => "cas-mapi", - 3683u16 => "bmc-ea", - 3684u16 => "faxstfx-port", - 3685u16 => "dsx-agent", - 3686u16 => "tnmpv2", - 3687u16 => "simple-push", - 3688u16 => "simple-push-s", - 3689u16 => "daap", - 3690u16 => "svn", - 3691u16 => "magaya-network", - 3692u16 => "intelsync", - 3693u16 => "easl", - 3695u16 => "bmc-data-coll", - 3696u16 => "telnetcpcd", - 3697u16 => "nw-license", - 3698u16 => "sagectlpanel", - 3699u16 => "kpn-icw", - 3700u16 => "lrs-paging", - 3701u16 => "netcelera", - 3702u16 => "ws-discovery", - 3703u16 => "adobeserver-3", - 3704u16 => "adobeserver-4", - 3705u16 => "adobeserver-5", - 3706u16 => "rt-event", - 3707u16 => "rt-event-s", - 3708u16 => "sun-as-iiops", - 3709u16 => "ca-idms", - 3710u16 => "portgate-auth", - 3711u16 => "edb-server2", - 3712u16 => "sentinel-ent", - 3713u16 => "tftps", - 3714u16 => "delos-dms", - 3715u16 => "anoto-rendezv", - 3716u16 => "wv-csp-sms-cir", - 3717u16 => "wv-csp-udp-cir", - 3718u16 => "opus-services", - 3719u16 => "itelserverport", - 3720u16 => "ufastro-instr", - 3721u16 => "xsync", - 3722u16 => "xserveraid", - 3723u16 => "sychrond", - 3724u16 => "blizwow", - 3725u16 => "na-er-tip", - 3726u16 => "array-manager", - 3727u16 => "e-mdu", - 3728u16 => "e-woa", - 3729u16 => "fksp-audit", - 3730u16 => "client-ctrl", - 3731u16 => "smap", - 3732u16 => "m-wnn", - 3733u16 => "multip-msg", - 3734u16 => "synel-data", - 3735u16 => "pwdis", - 3736u16 => "rs-rmi", - 3737u16 => "xpanel", - 3738u16 => "versatalk", - 3739u16 => "launchbird-lm", - 3740u16 => "heartbeat", - 3741u16 => "wysdma", - 3742u16 => "cst-port", - 3743u16 => "ipcs-command", - 3744u16 => "sasg", - 3745u16 => "gw-call-port", - 3746u16 => "linktest", - 3747u16 => "linktest-s", - 3748u16 => "webdata", - 3749u16 => "cimtrak", - 3750u16 => "cbos-ip-port", - 3751u16 => "gprs-cube", - 3752u16 => "vipremoteagent", - 3753u16 => "nattyserver", - 3754u16 => "timestenbroker", - 3755u16 => "sas-remote-hlp", - 3756u16 => "canon-capt", - 3757u16 => "grf-port", - 3758u16 => "apw-registry", - 3759u16 => "exapt-lmgr", - 3760u16 => "adtempusclient", - 3761u16 => "gsakmp", - 3762u16 => "gbs-smp", - 3763u16 => "xo-wave", - 3764u16 => "mni-prot-rout", - 3765u16 => "rtraceroute", - 3766u16 => "sitewatch-s", - 3767u16 => "listmgr-port", - 3768u16 => "rblcheckd", - 3769u16 => "haipe-otnk", - 3770u16 => "cindycollab", - 3771u16 => "paging-port", - 3772u16 => "ctp", - 3773u16 => "ctdhercules", - 3774u16 => "zicom", - 3775u16 => "ispmmgr", - 3776u16 => "dvcprov-port", - 3777u16 => "jibe-eb", - 3778u16 => "c-h-it-port", - 3779u16 => "cognima", - 3780u16 => "nnp", - 3781u16 => "abcvoice-port", - 3782u16 => "iso-tp0s", - 3783u16 => "bim-pem", - 3784u16 => "bfd-control", - 3785u16 => "bfd-echo", - 3786u16 => "upstriggervsw", - 3787u16 => "fintrx", - 3788u16 => "isrp-port", - 3789u16 => "remotedeploy", - 3790u16 => "quickbooksrds", - 3791u16 => "tvnetworkvideo", - 3792u16 => "sitewatch", - 3793u16 => "dcsoftware", - 3794u16 => "jaus", - 3795u16 => "myblast", - 3796u16 => "spw-dialer", - 3797u16 => "idps", - 3798u16 => "minilock", - 3799u16 => "radius-dynauth", - 3800u16 => "pwgpsi", - 3801u16 => "ibm-mgr", - 3802u16 => "vhd", - 3803u16 => "soniqsync", - 3804u16 => "iqnet-port", - 3805u16 => "tcpdataserver", - 3806u16 => "wsmlb", - 3807u16 => "spugna", - 3808u16 => "sun-as-iiops-ca", - 3809u16 => "apocd", - 3810u16 => "wlanauth", - 3811u16 => "amp", - 3812u16 => "neto-wol-server", - 3813u16 => "rap-ip", - 3814u16 => "neto-dcs", - 3815u16 => "lansurveyorxml", - 3816u16 => "sunlps-http", - 3817u16 => "tapeware", - 3818u16 => "crinis-hb", - 3819u16 => "epl-slp", - 3820u16 => "scp", - 3821u16 => "pmcp", - 3822u16 => "acp-discovery", - 3823u16 => "acp-conduit", - 3824u16 => "acp-policy", - 3825u16 => "ffserver", - 3826u16 => "warmux", - 3827u16 => "netmpi", - 3828u16 => "neteh", - 3829u16 => "neteh-ext", - 3830u16 => "cernsysmgmtagt", - 3831u16 => "dvapps", - 3832u16 => "xxnetserver", - 3833u16 => "aipn-auth", - 3834u16 => "spectardata", - 3835u16 => "spectardb", - 3836u16 => "markem-dcp", - 3837u16 => "mkm-discovery", - 3838u16 => "sos", - 3839u16 => "amx-rms", - 3840u16 => "flirtmitmir", - 3841u16 => "shiprush-db-svr", - 3842u16 => "nhci", - 3843u16 => "quest-agent", - 3844u16 => "rnm", - 3845u16 => "v-one-spp", - 3846u16 => "an-pcp", - 3847u16 => "msfw-control", - 3848u16 => "item", - 3849u16 => "spw-dnspreload", - 3850u16 => "qtms-bootstrap", - 3851u16 => "spectraport", - 3852u16 => "sse-app-config", - 3853u16 => "sscan", - 3854u16 => "stryker-com", - 3855u16 => "opentrac", - 3856u16 => "informer", - 3857u16 => "trap-port", - 3858u16 => "trap-port-mom", - 3859u16 => "nav-port", - 3860u16 => "sasp", - 3861u16 => "winshadow-hd", - 3862u16 => "giga-pocket", - 3863u16 => "asap-tcp", - 3864u16 => "asap-tcp-tls", - 3865u16 => "xpl", - 3866u16 => "dzdaemon", - 3867u16 => "dzoglserver", - 3868u16 => "diameter", - 3869u16 => "ovsam-mgmt", - 3870u16 => "ovsam-d-agent", - 3871u16 => "avocent-adsap", - 3872u16 => "oem-agent", - 3873u16 => "fagordnc", - 3874u16 => "sixxsconfig", - 3875u16 => "pnbscada", - 3876u16 => "dl_agent", - 3877u16 => "xmpcr-interface", - 3878u16 => "fotogcad", - 3879u16 => "appss-lm", - 3880u16 => "igrs", - 3881u16 => "idac", - 3882u16 => "msdts1", - 3883u16 => "vrpn", - 3884u16 => "softrack-meter", - 3885u16 => "topflow-ssl", - 3886u16 => "nei-management", - 3887u16 => "ciphire-data", - 3888u16 => "ciphire-serv", - 3889u16 => "dandv-tester", - 3890u16 => "ndsconnect", - 3891u16 => "rtc-pm-port", - 3892u16 => "pcc-image-port", - 3893u16 => "cgi-starapi", - 3894u16 => "syam-agent", - 3895u16 => "syam-smc", - 3896u16 => "sdo-tls", - 3897u16 => "sdo-ssh", - 3898u16 => "senip", - 3899u16 => "itv-control", - 3900u16 => "udt_os", - 3901u16 => "nimsh", - 3902u16 => "nimaux", - 3903u16 => "charsetmgr", - 3904u16 => "omnilink-port", - 3905u16 => "mupdate", - 3906u16 => "topovista-data", - 3907u16 => "imoguia-port", - 3908u16 => "hppronetman", - 3909u16 => "surfcontrolcpa", - 3910u16 => "prnrequest", - 3911u16 => "prnstatus", - 3912u16 => "gbmt-stars", - 3913u16 => "listcrt-port", - 3914u16 => "listcrt-port-2", - 3915u16 => "agcat", - 3916u16 => "wysdmc", - 3917u16 => "aftmux", - 3918u16 => "pktcablemmcops", - 3919u16 => "hyperip", - 3920u16 => "exasoftport1", - 3921u16 => "herodotus-net", - 3922u16 => "sor-update", - 3923u16 => "symb-sb-port", - 3924u16 => "mpl-gprs-port", - 3925u16 => "zmp", - 3926u16 => "winport", - 3927u16 => "natdataservice", - 3928u16 => "netboot-pxe", - 3929u16 => "smauth-port", - 3930u16 => "syam-webserver", - 3931u16 => "msr-plugin-port", - 3932u16 => "dyn-site", - 3933u16 => "plbserve-port", - 3934u16 => "sunfm-port", - 3935u16 => "sdp-portmapper", - 3936u16 => "mailprox", - 3937u16 => "dvbservdsc", - 3938u16 => "dbcontrol_agent", - 3939u16 => "aamp", - 3940u16 => "xecp-node", - 3941u16 => "homeportal-web", - 3942u16 => "srdp", - 3943u16 => "tig", - 3944u16 => "sops", - 3945u16 => "emcads", - 3946u16 => "backupedge", - 3947u16 => "ccp", - 3948u16 => "apdap", - 3949u16 => "drip", - 3950u16 => "namemunge", - 3951u16 => "pwgippfax", - 3952u16 => "i3-sessionmgr", - 3953u16 => "xmlink-connect", - 3954u16 => "adrep", - 3955u16 => "p2pcommunity", - 3956u16 => "gvcp", - 3957u16 => "mqe-broker", - 3958u16 => "mqe-agent", - 3959u16 => "treehopper", - 3960u16 => "bess", - 3961u16 => "proaxess", - 3962u16 => "sbi-agent", - 3963u16 => "thrp", - 3964u16 => "sasggprs", - 3965u16 => "ati-ip-to-ncpe", - 3966u16 => "bflckmgr", - 3967u16 => "ppsms", - 3968u16 => "ianywhere-dbns", - 3969u16 => "landmarks", - 3970u16 => "lanrevagent", - 3971u16 => "lanrevserver", - 3972u16 => "iconp", - 3973u16 => "progistics", - 3974u16 => "citysearch", - 3975u16 => "airshot", - 3976u16 => "opswagent", - 3977u16 => "opswmanager", - 3978u16 => "secure-cfg-svr", - 3979u16 => "smwan", - 3981u16 => "starfish", - 3982u16 => "eis", - 3983u16 => "eisp", - 3984u16 => "mapper-nodemgr", - 3985u16 => "mapper-mapethd", - 3986u16 => "mapper-ws_ethd", - 3987u16 => "centerline", - 3988u16 => "dcs-config", - 3989u16 => "bv-queryengine", - 3990u16 => "bv-is", - 3991u16 => "bv-smcsrv", - 3992u16 => "bv-ds", - 3993u16 => "bv-agent", - 3995u16 => "iss-mgmt-ssl", - 3996u16 => "abcsoftware", - 3997u16 => "agentsease-db", - 3998u16 => "dnx", - 3999u16 => "nvcnet", - 4000u16 => "terabase", - 4001u16 => "newoak", - 4002u16 => "pxc-spvr-ft", - 4003u16 => "pxc-splr-ft", - 4004u16 => "pxc-roid", - 4005u16 => "pxc-pin", - 4006u16 => "pxc-spvr", - 4007u16 => "pxc-splr", - 4008u16 => "netcheque", - 4009u16 => "chimera-hwm", - 4010u16 => "samsung-unidex", - 4011u16 => "altserviceboot", - 4012u16 => "pda-gate", - 4013u16 => "acl-manager", - 4014u16 => "taiclock", - 4015u16 => "talarian-mcast1", - 4016u16 => "talarian-mcast2", - 4017u16 => "talarian-mcast3", - 4018u16 => "talarian-mcast4", - 4019u16 => "talarian-mcast5", - 4020u16 => "trap", - 4021u16 => "nexus-portal", - 4022u16 => "dnox", - 4023u16 => "esnm-zoning", - 4024u16 => "tnp1-port", - 4025u16 => "partimage", - 4026u16 => "as-debug", - 4027u16 => "bxp", - 4028u16 => "dtserver-port", - 4029u16 => "ip-qsig", - 4030u16 => "jdmn-port", - 4031u16 => "suucp", - 4032u16 => "vrts-auth-port", - 4033u16 => "sanavigator", - 4034u16 => "ubxd", - 4035u16 => "wap-push-http", - 4036u16 => "wap-push-https", - 4037u16 => "ravehd", - 4038u16 => "fazzt-ptp", - 4039u16 => "fazzt-admin", - 4040u16 => "yo-main", - 4041u16 => "houston", - 4042u16 => "ldxp", - 4043u16 => "nirp", - 4044u16 => "ltp", - 4045u16 => "npp", - 4046u16 => "acp-proto", - 4047u16 => "ctp-state", - 4049u16 => "wafs", - 4050u16 => "cisco-wafs", - 4051u16 => "cppdp", - 4052u16 => "interact", - 4053u16 => "ccu-comm-1", - 4054u16 => "ccu-comm-2", - 4055u16 => "ccu-comm-3", - 4056u16 => "lms", - 4057u16 => "wfm", - 4058u16 => "kingfisher", - 4059u16 => "dlms-cosem", - 4060u16 => "dsmeter_iatc", - 4061u16 => "ice-location", - 4062u16 => "ice-slocation", - 4063u16 => "ice-router", - 4064u16 => "ice-srouter", - 4065u16 => "avanti_cdp", - 4066u16 => "pmas", - 4067u16 => "idp", - 4068u16 => "ipfltbcst", - 4069u16 => "minger", - 4070u16 => "tripe", - 4071u16 => "aibkup", - 4072u16 => "zieto-sock", - 4073u16 => "iRAPP", - 4074u16 => "cequint-cityid", - 4075u16 => "perimlan", - 4076u16 => "seraph", - 4078u16 => "cssp", - 4079u16 => "santools", - 4080u16 => "lorica-in", - 4081u16 => "lorica-in-sec", - 4082u16 => "lorica-out", - 4083u16 => "lorica-out-sec", - 4085u16 => "ezmessagesrv", - 4087u16 => "applusservice", - 4088u16 => "npsp", - 4089u16 => "opencore", - 4090u16 => "omasgport", - 4091u16 => "ewinstaller", - 4092u16 => "ewdgs", - 4093u16 => "pvxpluscs", - 4094u16 => "sysrqd", - 4095u16 => "xtgui", - 4096u16 => "bre", - 4097u16 => "patrolview", - 4098u16 => "drmsfsd", - 4099u16 => "dpcp", - 4100u16 => "igo-incognito", - 4101u16 => "brlp-0", - 4102u16 => "brlp-1", - 4103u16 => "brlp-2", - 4104u16 => "brlp-3", - 4105u16 => "shofar", - 4106u16 => "synchronite", - 4107u16 => "j-ac", - 4108u16 => "accel", - 4109u16 => "izm", - 4110u16 => "g2tag", - 4111u16 => "xgrid", - 4112u16 => "apple-vpns-rp", - 4113u16 => "aipn-reg", - 4114u16 => "jomamqmonitor", - 4115u16 => "cds", - 4116u16 => "smartcard-tls", - 4117u16 => "hillrserv", - 4118u16 => "netscript", - 4119u16 => "assuria-slm", - 4120u16 => "minirem", - 4121u16 => "e-builder", - 4122u16 => "fprams", - 4123u16 => "z-wave", - 4124u16 => "tigv2", - 4125u16 => "opsview-envoy", - 4126u16 => "ddrepl", - 4127u16 => "unikeypro", - 4128u16 => "nufw", - 4129u16 => "nuauth", - 4130u16 => "fronet", - 4131u16 => "stars", - 4132u16 => "nuts_dem", - 4133u16 => "nuts_bootp", - 4134u16 => "nifty-hmi", - 4135u16 => "cl-db-attach", - 4136u16 => "cl-db-request", - 4137u16 => "cl-db-remote", - 4138u16 => "nettest", - 4139u16 => "thrtx", - 4140u16 => "cedros_fds", - 4141u16 => "oirtgsvc", - 4142u16 => "oidocsvc", - 4143u16 => "oidsr", - 4145u16 => "vvr-control", - 4146u16 => "tgcconnect", - 4147u16 => "vrxpservman", - 4148u16 => "hhb-handheld", - 4149u16 => "agslb", - 4150u16 => "PowerAlert-nsa", - 4151u16 => "menandmice_noh", - 4152u16 => "idig_mux", - 4153u16 => "mbl-battd", - 4154u16 => "atlinks", - 4155u16 => "bzr", - 4156u16 => "stat-results", - 4157u16 => "stat-scanner", - 4158u16 => "stat-cc", - 4159u16 => "nss", - 4160u16 => "jini-discovery", - 4161u16 => "omscontact", - 4162u16 => "omstopology", - 4163u16 => "silverpeakpeer", - 4164u16 => "silverpeakcomm", - 4165u16 => "altcp", - 4166u16 => "joost", - 4167u16 => "ddgn", - 4168u16 => "pslicser", - 4169u16 => "iadt", - 4170u16 => "d-cinema-csp", - 4171u16 => "ml-svnet", - 4172u16 => "pcoip", - 4174u16 => "smcluster", - 4175u16 => "bccp", - 4176u16 => "tl-ipcproxy", - 4177u16 => "wello", - 4178u16 => "storman", - 4179u16 => "MaxumSP", - 4180u16 => "httpx", - 4181u16 => "macbak", - 4182u16 => "pcptcpservice", - 4183u16 => "cyborgnet", - 4184u16 => "universe_suite", - 4185u16 => "wcpp", - 4186u16 => "boxbackupstore", - 4187u16 => "csc_proxy", - 4188u16 => "vatata", - 4189u16 => "pcep", - 4190u16 => "sieve", - 4192u16 => "azeti", - 4193u16 => "pvxplusio", - 4195u16 => "aws-wsp", - 4197u16 => "hctl", - 4199u16 => "eims-admin", - 4300u16 => "corelccam", - 4301u16 => "d-data", - 4302u16 => "d-data-control", - 4303u16 => "srcp", - 4304u16 => "owserver", - 4305u16 => "batman", - 4306u16 => "pinghgl", - 4307u16 => "trueconf", - 4308u16 => "compx-lockview", - 4309u16 => "dserver", - 4310u16 => "mirrtex", - 4311u16 => "p6ssmc", - 4312u16 => "pscl-mgt", - 4313u16 => "perrla", - 4314u16 => "choiceview-agt", - 4316u16 => "choiceview-clt", - 4317u16 => "opentelemetry", - 4320u16 => "fdt-rcatp", - 4321u16 => "rwhois", - 4322u16 => "trim-event", - 4323u16 => "trim-ice", - 4325u16 => "geognosisman", - 4326u16 => "geognosis", - 4327u16 => "jaxer-web", - 4328u16 => "jaxer-manager", - 4329u16 => "publiqare-sync", - 4330u16 => "dey-sapi", - 4331u16 => "ktickets-rest", - 4332u16 => "getty-focus", - 4333u16 => "ahsp", - 4334u16 => "netconf-ch-ssh", - 4335u16 => "netconf-ch-tls", - 4336u16 => "restconf-ch-tls", - 4340u16 => "gaia", - 4343u16 => "unicall", - 4344u16 => "vinainstall", - 4345u16 => "m4-network-as", - 4346u16 => "elanlm", - 4347u16 => "lansurveyor", - 4348u16 => "itose", - 4349u16 => "fsportmap", - 4350u16 => "net-device", - 4351u16 => "plcy-net-svcs", - 4352u16 => "pjlink", - 4353u16 => "f5-iquery", - 4354u16 => "qsnet-trans", - 4355u16 => "qsnet-workst", - 4356u16 => "qsnet-assist", - 4357u16 => "qsnet-cond", - 4358u16 => "qsnet-nucl", - 4359u16 => "omabcastltkm", - 4360u16 => "matrix_vnet", - 4368u16 => "wxbrief", - 4369u16 => "epmd", - 4370u16 => "elpro_tunnel", - 4371u16 => "l2c-control", - 4372u16 => "l2c-data", - 4373u16 => "remctl", - 4374u16 => "psi-ptt", - 4375u16 => "tolteces", - 4376u16 => "bip", - 4377u16 => "cp-spxsvr", - 4378u16 => "cp-spxdpy", - 4379u16 => "ctdb", - 4389u16 => "xandros-cms", - 4390u16 => "wiegand", - 4391u16 => "apwi-imserver", - 4392u16 => "apwi-rxserver", - 4393u16 => "apwi-rxspooler", - 4395u16 => "omnivisionesx", - 4396u16 => "fly", - 4400u16 => "ds-srv", - 4401u16 => "ds-srvr", - 4402u16 => "ds-clnt", - 4403u16 => "ds-user", - 4404u16 => "ds-admin", - 4405u16 => "ds-mail", - 4406u16 => "ds-slp", - 4407u16 => "nacagent", - 4408u16 => "slscc", - 4409u16 => "netcabinet-com", - 4410u16 => "itwo-server", - 4411u16 => "found", - 4413u16 => "avi-nms", - 4414u16 => "updog", - 4415u16 => "brcd-vr-req", - 4416u16 => "pjj-player", - 4417u16 => "workflowdir", - 4419u16 => "cbp", - 4420u16 => "nvme", - 4421u16 => "scaleft", - 4422u16 => "tsepisp", - 4423u16 => "thingkit", - 4425u16 => "netrockey6", - 4426u16 => "beacon-port-2", - 4427u16 => "drizzle", - 4428u16 => "omviserver", - 4429u16 => "omviagent", - 4430u16 => "rsqlserver", - 4431u16 => "wspipe", - 4432u16 => "l-acoustics", - 4433u16 => "vop", - 4442u16 => "saris", - 4443u16 => "pharos", - 4444u16 => "nv-video", - 4445u16 => "upnotifyp", - 4446u16 => "n1-fwp", - 4447u16 => "n1-rmgmt", - 4448u16 => "asc-slmd", - 4449u16 => "privatewire", - 4450u16 => "camp", - 4451u16 => "ctisystemmsg", - 4452u16 => "ctiprogramload", - 4453u16 => "nssalertmgr", - 4454u16 => "nssagentmgr", - 4455u16 => "prchat-user", - 4456u16 => "prchat-server", - 4457u16 => "prRegister", - 4458u16 => "mcp", - 4460u16 => "ntske", - 4484u16 => "hpssmgmt", - 4485u16 => "assyst-dr", - 4486u16 => "icms", - 4487u16 => "prex-tcp", - 4488u16 => "awacs-ice", - 4500u16 => "ipsec-nat-t", - 4535u16 => "ehs", - 4536u16 => "ehs-ssl", - 4537u16 => "wssauthsvc", - 4538u16 => "swx-gate", - 4545u16 => "worldscores", - 4546u16 => "sf-lm", - 4547u16 => "lanner-lm", - 4548u16 => "synchromesh", - 4549u16 => "aegate", - 4550u16 => "gds-adppiw-db", - 4551u16 => "ieee-mih", - 4552u16 => "menandmice-mon", - 4553u16 => "icshostsvc", - 4554u16 => "msfrs", - 4555u16 => "rsip", - 4556u16 => "dtn-bundle", - 4559u16 => "hylafax", - 4563u16 => "amahi-anywhere", - 4566u16 => "kwtc", - 4567u16 => "tram", - 4568u16 => "bmc-reporting", - 4569u16 => "iax", - 4570u16 => "deploymentmap", - 4573u16 => "cardifftec-back", - 4590u16 => "rid", - 4591u16 => "l3t-at-an", - 4593u16 => "ipt-anri-anri", - 4594u16 => "ias-session", - 4595u16 => "ias-paging", - 4596u16 => "ias-neighbor", - 4597u16 => "a21-an-1xbs", - 4598u16 => "a16-an-an", - 4599u16 => "a17-an-an", - 4600u16 => "piranha1", - 4601u16 => "piranha2", - 4602u16 => "mtsserver", - 4603u16 => "menandmice-upg", - 4604u16 => "irp", - 4605u16 => "sixchat", - 4606u16 => "sixid", - 4646u16 => "dots-signal", - 4658u16 => "playsta2-app", - 4659u16 => "playsta2-lob", - 4660u16 => "smaclmgr", - 4661u16 => "kar2ouche", - 4662u16 => "oms", - 4663u16 => "noteit", - 4664u16 => "ems", - 4665u16 => "contclientms", - 4666u16 => "eportcomm", - 4667u16 => "mmacomm", - 4668u16 => "mmaeds", - 4669u16 => "eportcommdata", - 4670u16 => "light", - 4671u16 => "acter", - 4672u16 => "rfa", - 4673u16 => "cxws", - 4674u16 => "appiq-mgmt", - 4675u16 => "dhct-status", - 4676u16 => "dhct-alerts", - 4677u16 => "bcs", - 4678u16 => "traversal", - 4679u16 => "mgesupervision", - 4680u16 => "mgemanagement", - 4681u16 => "parliant", - 4682u16 => "finisar", - 4683u16 => "spike", - 4684u16 => "rfid-rp1", - 4685u16 => "autopac", - 4686u16 => "msp-os", - 4687u16 => "nst", - 4688u16 => "mobile-p2p", - 4689u16 => "altovacentral", - 4690u16 => "prelude", - 4691u16 => "mtn", - 4692u16 => "conspiracy", - 4700u16 => "netxms-agent", - 4701u16 => "netxms-mgmt", - 4702u16 => "netxms-sync", - 4703u16 => "npqes-test", - 4704u16 => "assuria-ins", - 4711u16 => "trinity-dist", - 4725u16 => "truckstar", - 4727u16 => "fcis", - 4728u16 => "capmux", - 4730u16 => "gearman", - 4731u16 => "remcap", - 4733u16 => "resorcs", - 4737u16 => "ipdr-sp", - 4738u16 => "solera-lpn", - 4739u16 => "ipfix", - 4740u16 => "ipfixs", - 4741u16 => "lumimgrd", - 4742u16 => "sicct", - 4743u16 => "openhpid", - 4744u16 => "ifsp", - 4745u16 => "fmp", - 4749u16 => "profilemac", - 4750u16 => "ssad", - 4751u16 => "spocp", - 4752u16 => "snap", - 4753u16 => "simon", - 4756u16 => "RDCenter", - 4774u16 => "converge", - 4784u16 => "bfd-multi-ctl", - 4786u16 => "smart-install", - 4787u16 => "sia-ctrl-plane", - 4788u16 => "xmcp", - 4800u16 => "iims", - 4801u16 => "iwec", - 4802u16 => "ilss", - 4803u16 => "notateit", - 4827u16 => "htcp", - 4837u16 => "varadero-0", - 4838u16 => "varadero-1", - 4839u16 => "varadero-2", - 4840u16 => "opcua-tcp", - 4841u16 => "quosa", - 4842u16 => "gw-asv", - 4843u16 => "opcua-tls", - 4844u16 => "gw-log", - 4845u16 => "wcr-remlib", - 4846u16 => "contamac_icm", - 4847u16 => "wfc", - 4848u16 => "appserv-http", - 4849u16 => "appserv-https", - 4850u16 => "sun-as-nodeagt", - 4851u16 => "derby-repli", - 4867u16 => "unify-debug", - 4868u16 => "phrelay", - 4869u16 => "phrelaydbg", - 4870u16 => "cc-tracking", - 4871u16 => "wired", - 4876u16 => "tritium-can", - 4877u16 => "lmcs", - 4879u16 => "wsdl-event", - 4880u16 => "hislip", - 4883u16 => "wmlserver", - 4884u16 => "hivestor", - 4885u16 => "abbs", - 4888u16 => "xcap-portal", - 4889u16 => "xcap-control", - 4894u16 => "lyskom", - 4899u16 => "radmin-port", - 4900u16 => "hfcs", - 4901u16 => "flr_agent", - 4902u16 => "magiccontrol", - 4912u16 => "lutap", - 4913u16 => "lutcp", - 4914u16 => "bones", - 4915u16 => "frcs", - 4940u16 => "eq-office-4940", - 4941u16 => "eq-office-4941", - 4942u16 => "eq-office-4942", - 4949u16 => "munin", - 4950u16 => "sybasesrvmon", - 4951u16 => "pwgwims", - 4952u16 => "sagxtsds", - 4953u16 => "dbsyncarbiter", - 4969u16 => "ccss-qmm", - 4970u16 => "ccss-qsm", - 4971u16 => "burp", - 4984u16 => "webyast", - 4985u16 => "gerhcs", - 4986u16 => "mrip", - 4987u16 => "smar-se-port1", - 4988u16 => "smar-se-port2", - 4989u16 => "parallel", - 4990u16 => "busycal", - 4991u16 => "vrt", - 4999u16 => "hfcs-manager", - 5000u16 => "commplex-main", - 5001u16 => "commplex-link", - 5002u16 => "rfe", - 5003u16 => "fmpro-internal", - 5004u16 => "avt-profile-1", - 5005u16 => "avt-profile-2", - 5006u16 => "wsm-server", - 5007u16 => "wsm-server-ssl", - 5008u16 => "synapsis-edge", - 5009u16 => "winfs", - 5010u16 => "telelpathstart", - 5011u16 => "telelpathattack", - 5012u16 => "nsp", - 5013u16 => "fmpro-v6", - 5015u16 => "fmwp", - 5020u16 => "zenginkyo-1", - 5021u16 => "zenginkyo-2", - 5022u16 => "mice", - 5023u16 => "htuilsrv", - 5024u16 => "scpi-telnet", - 5025u16 => "scpi-raw", - 5026u16 => "strexec-d", - 5027u16 => "strexec-s", - 5028u16 => "qvr", - 5029u16 => "infobright", - 5030u16 => "surfpass", - 5032u16 => "signacert-agent", - 5033u16 => "jtnetd-server", - 5034u16 => "jtnetd-status", - 5042u16 => "asnaacceler8db", - 5043u16 => "swxadmin", - 5044u16 => "lxi-evntsvc", - 5045u16 => "osp", - 5048u16 => "texai", - 5049u16 => "ivocalize", - 5050u16 => "mmcc", - 5051u16 => "ita-agent", - 5052u16 => "ita-manager", - 5053u16 => "rlm", - 5054u16 => "rlm-admin", - 5055u16 => "unot", - 5056u16 => "intecom-ps1", - 5057u16 => "intecom-ps2", - 5059u16 => "sds", - 5060u16 => "sip", - 5061u16 => "sips", - 5062u16 => "na-localise", - 5063u16 => "csrpc", - 5064u16 => "ca-1", - 5065u16 => "ca-2", - 5066u16 => "stanag-5066", - 5067u16 => "authentx", - 5068u16 => "bitforestsrv", - 5069u16 => "i-net-2000-npr", - 5070u16 => "vtsas", - 5071u16 => "powerschool", - 5072u16 => "ayiya", - 5073u16 => "tag-pm", - 5074u16 => "alesquery", - 5075u16 => "pvaccess", - 5080u16 => "onscreen", - 5081u16 => "sdl-ets", - 5082u16 => "qcp", - 5083u16 => "qfp", - 5084u16 => "llrp", - 5085u16 => "encrypted-llrp", - 5086u16 => "aprigo-cs", - 5087u16 => "biotic", - 5093u16 => "sentinel-lm", - 5094u16 => "hart-ip", - 5099u16 => "sentlm-srv2srv", - 5100u16 => "socalia", - 5101u16 => "talarian-tcp", - 5102u16 => "oms-nonsecure", - 5103u16 => "actifio-c2c", - 5106u16 => "actifioudsagent", - 5107u16 => "actifioreplic", - 5111u16 => "taep-as-svc", - 5112u16 => "pm-cmdsvr", - 5114u16 => "ev-services", - 5115u16 => "autobuild", - 5117u16 => "gradecam", - 5120u16 => "barracuda-bbs", - 5133u16 => "nbt-pc", - 5134u16 => "ppactivation", - 5135u16 => "erp-scale", - 5137u16 => "ctsd", - 5145u16 => "rmonitor_secure", - 5146u16 => "social-alarm", - 5150u16 => "atmp", - 5151u16 => "esri_sde", - 5152u16 => "sde-discovery", - 5154u16 => "bzflag", - 5155u16 => "asctrl-agent", - 5156u16 => "rugameonline", - 5157u16 => "mediat", - 5161u16 => "snmpssh", - 5162u16 => "snmpssh-trap", - 5163u16 => "sbackup", - 5164u16 => "vpa", - 5165u16 => "ife_icorp", - 5166u16 => "winpcs", - 5167u16 => "scte104", - 5168u16 => "scte30", - 5172u16 => "pcoip-mgmt", - 5190u16 => "aol", - 5191u16 => "aol-1", - 5192u16 => "aol-2", - 5193u16 => "aol-3", - 5194u16 => "cpscomm", - 5195u16 => "ampl-lic", - 5196u16 => "ampl-tableproxy", - 5197u16 => "tunstall-lwp", - 5200u16 => "targus-getdata", - 5201u16 => "targus-getdata1", - 5202u16 => "targus-getdata2", - 5203u16 => "targus-getdata3", - 5209u16 => "nomad", - 5215u16 => "noteza", - 5221u16 => "3exmp", - 5222u16 => "xmpp-client", - 5223u16 => "hpvirtgrp", - 5224u16 => "hpvirtctrl", - 5225u16 => "hp-server", - 5226u16 => "hp-status", - 5227u16 => "perfd", - 5228u16 => "hpvroom", - 5229u16 => "jaxflow", - 5230u16 => "jaxflow-data", - 5231u16 => "crusecontrol", - 5232u16 => "csedaemon", - 5233u16 => "enfs", - 5234u16 => "eenet", - 5235u16 => "galaxy-network", - 5236u16 => "padl2sim", - 5237u16 => "mnet-discovery", - 5245u16 => "downtools", - 5248u16 => "caacws", - 5249u16 => "caaclang2", - 5250u16 => "soagateway", - 5251u16 => "caevms", - 5252u16 => "movaz-ssc", - 5253u16 => "kpdp", - 5254u16 => "logcabin", - 5264u16 => "3com-njack-1", - 5265u16 => "3com-njack-2", - 5269u16 => "xmpp-server", - 5270u16 => "cartographerxmp", - 5271u16 => "cuelink", - 5272u16 => "pk", - 5280u16 => "xmpp-bosh", - 5281u16 => "undo-lm", - 5282u16 => "transmit-port", - 5298u16 => "presence", - 5299u16 => "nlg-data", - 5300u16 => "hacl-hb", - 5301u16 => "hacl-gs", - 5302u16 => "hacl-cfg", - 5303u16 => "hacl-probe", - 5304u16 => "hacl-local", - 5305u16 => "hacl-test", - 5306u16 => "sun-mc-grp", - 5307u16 => "sco-aip", - 5308u16 => "cfengine", - 5309u16 => "jprinter", - 5310u16 => "outlaws", - 5312u16 => "permabit-cs", - 5313u16 => "rrdp", - 5314u16 => "opalis-rbt-ipc", - 5315u16 => "hacl-poll", - 5316u16 => "hpbladems", - 5317u16 => "hpdevms", - 5318u16 => "pkix-cmc", - 5320u16 => "bsfserver-zn", - 5321u16 => "bsfsvr-zn-ssl", - 5343u16 => "kfserver", - 5344u16 => "xkotodrcp", - 5349u16 => "stun-behaviors", - 5352u16 => "dns-llq", - 5353u16 => "mdns", - 5354u16 => "mdnsresponder", - 5355u16 => "llmnr", - 5356u16 => "ms-smlbiz", - 5357u16 => "wsdapi", - 5358u16 => "wsdapi-s", - 5359u16 => "ms-alerter", - 5360u16 => "ms-sideshow", - 5361u16 => "ms-s-sideshow", - 5362u16 => "serverwsd2", - 5363u16 => "net-projection", - 5397u16 => "stresstester", - 5398u16 => "elektron-admin", - 5399u16 => "securitychase", - 5400u16 => "excerpt", - 5401u16 => "excerpts", - 5402u16 => "mftp", - 5403u16 => "hpoms-ci-lstn", - 5404u16 => "hpoms-dps-lstn", - 5405u16 => "netsupport", - 5406u16 => "systemics-sox", - 5407u16 => "foresyte-clear", - 5408u16 => "foresyte-sec", - 5409u16 => "salient-dtasrv", - 5410u16 => "salient-usrmgr", - 5411u16 => "actnet", - 5412u16 => "continuus", - 5413u16 => "wwiotalk", - 5414u16 => "statusd", - 5415u16 => "ns-server", - 5416u16 => "sns-gateway", - 5417u16 => "sns-agent", - 5418u16 => "mcntp", - 5419u16 => "dj-ice", - 5420u16 => "cylink-c", - 5421u16 => "netsupport2", - 5422u16 => "salient-mux", - 5423u16 => "virtualuser", - 5424u16 => "beyond-remote", - 5425u16 => "br-channel", - 5426u16 => "devbasic", - 5427u16 => "sco-peer-tta", - 5428u16 => "telaconsole", - 5429u16 => "base", - 5430u16 => "radec-corp", - 5431u16 => "park-agent", - 5432u16 => "postgresql", - 5433u16 => "pyrrho", - 5434u16 => "sgi-arrayd", - 5435u16 => "sceanics", - 5443u16 => "spss", - 5445u16 => "smbdirect", - 5450u16 => "tiepie", - 5453u16 => "surebox", - 5454u16 => "apc-5454", - 5455u16 => "apc-5455", - 5456u16 => "apc-5456", - 5461u16 => "silkmeter", - 5462u16 => "ttl-publisher", - 5463u16 => "ttlpriceproxy", - 5464u16 => "quailnet", - 5465u16 => "netops-broker", - 5470u16 => "apsolab-col", - 5471u16 => "apsolab-cols", - 5472u16 => "apsolab-tag", - 5473u16 => "apsolab-tags", - 5475u16 => "apsolab-data", - 5500u16 => "fcp-addr-srvr1", - 5501u16 => "fcp-addr-srvr2", - 5502u16 => "fcp-srvr-inst1", - 5503u16 => "fcp-srvr-inst2", - 5504u16 => "fcp-cics-gw1", - 5505u16 => "checkoutdb", - 5506u16 => "amc", - 5507u16 => "psl-management", - 5550u16 => "cbus", - 5553u16 => "sgi-eventmond", - 5554u16 => "sgi-esphttp", - 5555u16 => "personal-agent", - 5556u16 => "freeciv", - 5557u16 => "farenet", - 5565u16 => "dp-bura", - 5566u16 => "westec-connect", - 5567u16 => "dof-dps-mc-sec", - 5568u16 => "sdt", - 5569u16 => "rdmnet-ctrl", - 5573u16 => "sdmmp", - 5574u16 => "lsi-bobcat", - 5575u16 => "ora-oap", - 5579u16 => "fdtracks", - 5580u16 => "tmosms0", - 5581u16 => "tmosms1", - 5582u16 => "fac-restore", - 5583u16 => "tmo-icon-sync", - 5584u16 => "bis-web", - 5585u16 => "bis-sync", - 5586u16 => "att-mt-sms", - 5597u16 => "ininmessaging", - 5598u16 => "mctfeed", - 5599u16 => "esinstall", - 5600u16 => "esmmanager", - 5601u16 => "esmagent", - 5602u16 => "a1-msc", - 5603u16 => "a1-bs", - 5604u16 => "a3-sdunode", - 5605u16 => "a4-sdunode", - 5618u16 => "efr", - 5627u16 => "ninaf", - 5628u16 => "htrust", - 5629u16 => "symantec-sfdb", - 5630u16 => "precise-comm", - 5631u16 => "pcanywheredata", - 5632u16 => "pcanywherestat", - 5633u16 => "beorl", - 5634u16 => "xprtld", - 5635u16 => "sfmsso", - 5636u16 => "sfm-db-server", - 5637u16 => "cssc", - 5638u16 => "flcrs", - 5639u16 => "ics", - 5646u16 => "vfmobile", - 5666u16 => "nrpe", - 5670u16 => "filemq", - 5671u16 => "amqps", - 5672u16 => "amqp", - 5673u16 => "jms", - 5674u16 => "hyperscsi-port", - 5675u16 => "v5ua", - 5676u16 => "raadmin", - 5677u16 => "questdb2-lnchr", - 5678u16 => "rrac", - 5679u16 => "dccm", - 5680u16 => "auriga-router", - 5681u16 => "ncxcp", - 5683u16 => "coap", - 5684u16 => "coaps", - 5688u16 => "ggz", - 5689u16 => "qmvideo", - 5693u16 => "rbsystem", - 5696u16 => "kmip", - 5700u16 => "supportassist", - 5705u16 => "storageos", - 5713u16 => "proshareaudio", - 5714u16 => "prosharevideo", - 5715u16 => "prosharedata", - 5716u16 => "prosharerequest", - 5717u16 => "prosharenotify", - 5718u16 => "dpm", - 5719u16 => "dpm-agent", - 5720u16 => "ms-licensing", - 5721u16 => "dtpt", - 5722u16 => "msdfsr", - 5723u16 => "omhs", - 5724u16 => "omsdk", - 5725u16 => "ms-ilm", - 5726u16 => "ms-ilm-sts", - 5727u16 => "asgenf", - 5728u16 => "io-dist-data", - 5729u16 => "openmail", - 5730u16 => "unieng", - 5741u16 => "ida-discover1", - 5742u16 => "ida-discover2", - 5743u16 => "watchdoc-pod", - 5744u16 => "watchdoc", - 5745u16 => "fcopy-server", - 5746u16 => "fcopys-server", - 5747u16 => "tunatic", - 5748u16 => "tunalyzer", - 5750u16 => "rscd", - 5755u16 => "openmailg", - 5757u16 => "x500ms", - 5766u16 => "openmailns", - 5767u16 => "s-openmail", - 5768u16 => "openmailpxy", - 5769u16 => "spramsca", - 5770u16 => "spramsd", - 5771u16 => "netagent", - 5777u16 => "starfield-io", - 5780u16 => "vts-rpc", - 5781u16 => "3par-evts", - 5782u16 => "3par-mgmt", - 5783u16 => "3par-mgmt-ssl", - 5785u16 => "3par-rcopy", - 5793u16 => "xtreamx", - 5813u16 => "icmpd", - 5814u16 => "spt-automation", - 5841u16 => "shiprush-d-ch", - 5842u16 => "reversion", - 5859u16 => "wherehoo", - 5863u16 => "ppsuitemsg", - 5868u16 => "diameters", - 5883u16 => "jute", - 5900u16 => "rfb", - 5910u16 => "cm", - 5911u16 => "cpdlc", - 5912u16 => "fis", - 5913u16 => "ads-c", - 5963u16 => "indy", - 5968u16 => "mppolicy-v5", - 5969u16 => "mppolicy-mgr", - 5984u16 => "couchdb", - 5985u16 => "wsman", - 5986u16 => "wsmans", - 5987u16 => "wbem-rmi", - 5988u16 => "wbem-http", - 5989u16 => "wbem-https", - 5990u16 => "wbem-exp-https", - 5991u16 => "nuxsl", - 5992u16 => "consul-insight", - 5993u16 => "cim-rs", - 5999u16 => "cvsup", - 6064u16 => "ndl-ahp-svc", - 6065u16 => "winpharaoh", - 6066u16 => "ewctsp", - 6068u16 => "gsmp-ancp", - 6069u16 => "trip", - 6070u16 => "messageasap", - 6071u16 => "ssdtp", - 6072u16 => "diagnose-proc", - 6073u16 => "directplay8", - 6074u16 => "max", - 6075u16 => "dpm-acm", - 6076u16 => "msft-dpm-cert", - 6077u16 => "iconstructsrv", - 6084u16 => "reload-config", - 6085u16 => "konspire2b", - 6086u16 => "pdtp", - 6087u16 => "ldss", - 6088u16 => "doglms", - 6099u16 => "raxa-mgmt", - 6100u16 => "synchronet-db", - 6101u16 => "synchronet-rtc", - 6102u16 => "synchronet-upd", - 6103u16 => "rets", - 6104u16 => "dbdb", - 6105u16 => "primaserver", - 6106u16 => "mpsserver", - 6107u16 => "etc-control", - 6108u16 => "sercomm-scadmin", - 6109u16 => "globecast-id", - 6110u16 => "softcm", - 6111u16 => "spc", - 6112u16 => "dtspcd", - 6113u16 => "dayliteserver", - 6114u16 => "wrspice", - 6115u16 => "xic", - 6116u16 => "xtlserv", - 6117u16 => "daylitetouch", - 6121u16 => "spdy", - 6122u16 => "bex-webadmin", - 6123u16 => "backup-express", - 6124u16 => "pnbs", - 6130u16 => "damewaremobgtwy", - 6133u16 => "nbt-wol", - 6140u16 => "pulsonixnls", - 6141u16 => "meta-corp", - 6142u16 => "aspentec-lm", - 6143u16 => "watershed-lm", - 6144u16 => "statsci1-lm", - 6145u16 => "statsci2-lm", - 6146u16 => "lonewolf-lm", - 6147u16 => "montage-lm", - 6148u16 => "ricardo-lm", - 6149u16 => "tal-pod", - 6159u16 => "efb-aci", - 6160u16 => "ecmp", - 6161u16 => "patrol-ism", - 6162u16 => "patrol-coll", - 6163u16 => "pscribe", - 6200u16 => "lm-x", - 6209u16 => "qmtps", - 6222u16 => "radmind", - 6241u16 => "jeol-nsdtp-1", - 6242u16 => "jeol-nsdtp-2", - 6243u16 => "jeol-nsdtp-3", - 6244u16 => "jeol-nsdtp-4", - 6251u16 => "tl1-raw-ssl", - 6252u16 => "tl1-ssh", - 6253u16 => "crip", - 6267u16 => "gld", - 6268u16 => "grid", - 6269u16 => "grid-alt", - 6300u16 => "bmc-grx", - 6301u16 => "bmc_ctd_ldap", - 6306u16 => "ufmp", - 6315u16 => "scup", - 6316u16 => "abb-escp", - 6317u16 => "nav-data-cmd", - 6320u16 => "repsvc", - 6321u16 => "emp-server1", - 6322u16 => "emp-server2", - 6324u16 => "hrd-ncs", - 6325u16 => "dt-mgmtsvc", - 6326u16 => "dt-vra", - 6343u16 => "sflow", - 6344u16 => "streletz", - 6346u16 => "gnutella-svc", - 6347u16 => "gnutella-rtr", - 6350u16 => "adap", - 6355u16 => "pmcs", - 6360u16 => "metaedit-mu", - 6370u16 => "metaedit-se", - 6379u16 => "redis", - 6382u16 => "metatude-mds", - 6389u16 => "clariion-evr01", - 6390u16 => "metaedit-ws", - 6417u16 => "faxcomservice", - 6418u16 => "syserverremote", - 6419u16 => "svdrp", - 6420u16 => "nim-vdrshell", - 6421u16 => "nim-wan", - 6432u16 => "pgbouncer", - 6440u16 => "heliosd", - 6442u16 => "tarp", - 6443u16 => "sun-sr-https", - 6444u16 => "sge_qmaster", - 6445u16 => "sge_execd", - 6446u16 => "mysql-proxy", - 6455u16 => "skip-cert-recv", - 6456u16 => "skip-cert-send", - 6464u16 => "ieee11073-20701", - 6471u16 => "lvision-lm", - 6480u16 => "sun-sr-http", - 6481u16 => "servicetags", - 6482u16 => "ldoms-mgmt", - 6483u16 => "SunVTS-RMI", - 6484u16 => "sun-sr-jms", - 6485u16 => "sun-sr-iiop", - 6486u16 => "sun-sr-iiops", - 6487u16 => "sun-sr-iiop-aut", - 6488u16 => "sun-sr-jmx", - 6489u16 => "sun-sr-admin", - 6500u16 => "boks", - 6501u16 => "boks_servc", - 6502u16 => "boks_servm", - 6503u16 => "boks_clntd", - 6505u16 => "badm_priv", - 6506u16 => "badm_pub", - 6507u16 => "bdir_priv", - 6508u16 => "bdir_pub", - 6509u16 => "mgcs-mfp-port", - 6510u16 => "mcer-port", - 6513u16 => "netconf-tls", - 6514u16 => "syslog-tls", - 6515u16 => "elipse-rec", - 6543u16 => "lds-distrib", - 6544u16 => "lds-dump", - 6547u16 => "apc-6547", - 6548u16 => "apc-6548", - 6549u16 => "apc-6549", - 6550u16 => "fg-sysupdate", - 6551u16 => "sum", - 6556u16 => "checkmk-agent", - 6558u16 => "xdsxdm", - 6566u16 => "sane-port", - 6568u16 => "canit_store", - 6579u16 => "affiliate", - 6580u16 => "parsec-master", - 6581u16 => "parsec-peer", - 6582u16 => "parsec-game", - 6583u16 => "joaJewelSuite", - 6600u16 => "mshvlm", - 6601u16 => "mstmg-sstp", - 6602u16 => "wsscomfrmwk", - 6619u16 => "odette-ftps", - 6620u16 => "kftp-data", - 6621u16 => "kftp", - 6622u16 => "mcftp", - 6623u16 => "ktelnet", - 6624u16 => "datascaler-db", - 6625u16 => "datascaler-ctl", - 6626u16 => "wago-service", - 6627u16 => "nexgen", - 6628u16 => "afesc-mc", - 6629u16 => "nexgen-aux", - 6632u16 => "mxodbc-connect", - 6640u16 => "ovsdb", - 6653u16 => "openflow", - 6655u16 => "pcs-sf-ui-man", - 6656u16 => "emgmsg", - 6670u16 => "vocaltec-gold", - 6671u16 => "p4p-portal", - 6672u16 => "vision_server", - 6673u16 => "vision_elmd", - 6678u16 => "vfbp", - 6679u16 => "osaut", - 6687u16 => "clever-ctrace", - 6688u16 => "clever-tcpip", - 6689u16 => "tsa", - 6690u16 => "cleverdetect", - 6697u16 => "ircs-u", - 6701u16 => "kti-icad-srvr", - 6702u16 => "e-design-net", - 6703u16 => "e-design-web", - 6714u16 => "ibprotocol", - 6715u16 => "fibotrader-com", - 6716u16 => "princity-agent", - 6767u16 => "bmc-perf-agent", - 6768u16 => "bmc-perf-mgrd", - 6769u16 => "adi-gxp-srvprt", - 6770u16 => "plysrv-http", - 6771u16 => "plysrv-https", - 6777u16 => "ntz-tracker", - 6778u16 => "ntz-p2p-storage", - 6785u16 => "dgpf-exchg", - 6786u16 => "smc-jmx", - 6787u16 => "smc-admin", - 6788u16 => "smc-http", - 6789u16 => "radg", - 6790u16 => "hnmp", - 6791u16 => "hnm", - 6801u16 => "acnet", - 6817u16 => "pentbox-sim", - 6831u16 => "ambit-lm", - 6841u16 => "netmo-default", - 6842u16 => "netmo-http", - 6850u16 => "iccrushmore", - 6868u16 => "acctopus-cc", - 6888u16 => "muse", - 6900u16 => "rtimeviewer", - 6901u16 => "jetstream", - 6924u16 => "split-ping", - 6935u16 => "ethoscan", - 6936u16 => "xsmsvc", - 6946u16 => "bioserver", - 6951u16 => "otlp", - 6961u16 => "jmact3", - 6962u16 => "jmevt2", - 6963u16 => "swismgr1", - 6964u16 => "swismgr2", - 6965u16 => "swistrap", - 6966u16 => "swispol", - 6969u16 => "acmsoda", - 6970u16 => "conductor", - 6997u16 => "MobilitySrv", - 6998u16 => "iatp-highpri", - 6999u16 => "iatp-normalpri", - 7000u16 => "afs3-fileserver", - 7001u16 => "afs3-callback", - 7002u16 => "afs3-prserver", - 7003u16 => "afs3-vlserver", - 7004u16 => "afs3-kaserver", - 7005u16 => "afs3-volser", - 7006u16 => "afs3-errors", - 7007u16 => "afs3-bos", - 7008u16 => "afs3-update", - 7009u16 => "afs3-rmtsys", - 7010u16 => "ups-onlinet", - 7011u16 => "talon-disc", - 7012u16 => "talon-engine", - 7013u16 => "microtalon-dis", - 7014u16 => "microtalon-com", - 7015u16 => "talon-webserver", - 7016u16 => "spg", - 7017u16 => "grasp", - 7018u16 => "fisa-svc", - 7019u16 => "doceri-ctl", - 7020u16 => "dpserve", - 7021u16 => "dpserveadmin", - 7022u16 => "ctdp", - 7023u16 => "ct2nmcs", - 7024u16 => "vmsvc", - 7025u16 => "vmsvc-2", - 7026u16 => "loreji-panel", - 7030u16 => "op-probe", - 7031u16 => "iposplanet", - 7070u16 => "arcp", - 7071u16 => "iwg1", - 7072u16 => "iba-cfg", - 7073u16 => "martalk", - 7080u16 => "empowerid", - 7099u16 => "lazy-ptop", - 7100u16 => "font-service", - 7101u16 => "elcn", - 7117u16 => "rothaga", - 7121u16 => "virprot-lm", - 7128u16 => "scenidm", - 7129u16 => "scenccs", - 7161u16 => "cabsm-comm", - 7162u16 => "caistoragemgr", - 7163u16 => "cacsambroker", - 7164u16 => "fsr", - 7165u16 => "doc-server", - 7166u16 => "aruba-server", - 7167u16 => "casrmagent", - 7168u16 => "cnckadserver", - 7169u16 => "ccag-pib", - 7170u16 => "nsrp", - 7171u16 => "drm-production", - 7172u16 => "metalbend", - 7173u16 => "zsecure", - 7174u16 => "clutild", - 7200u16 => "fodms", - 7201u16 => "dlip", - 7202u16 => "pon-ictp", - 7215u16 => "PS-Server", - 7216u16 => "PS-Capture-Pro", - 7227u16 => "ramp", - 7228u16 => "citrixupp", - 7229u16 => "citrixuppg", - 7234u16 => "asa-gateways", - 7236u16 => "display", - 7237u16 => "pads", - 7244u16 => "frc-hicp", - 7262u16 => "cnap", - 7272u16 => "watchme-7272", - 7273u16 => "oma-rlp", - 7274u16 => "oma-rlp-s", - 7275u16 => "oma-ulp", - 7276u16 => "oma-ilp", - 7277u16 => "oma-ilp-s", - 7278u16 => "oma-dcdocbs", - 7279u16 => "ctxlic", - 7280u16 => "itactionserver1", - 7281u16 => "itactionserver2", - 7282u16 => "mzca-action", - 7283u16 => "genstat", - 7365u16 => "lcm-server", - 7391u16 => "mindfilesys", - 7392u16 => "mrssrendezvous", - 7393u16 => "nfoldman", - 7394u16 => "fse", - 7395u16 => "winqedit", - 7397u16 => "hexarc", - 7400u16 => "rtps-discovery", - 7401u16 => "rtps-dd-ut", - 7402u16 => "rtps-dd-mt", - 7410u16 => "ionixnetmon", - 7411u16 => "daqstream", - 7421u16 => "mtportmon", - 7426u16 => "pmdmgr", - 7427u16 => "oveadmgr", - 7428u16 => "ovladmgr", - 7429u16 => "opi-sock", - 7430u16 => "xmpv7", - 7431u16 => "pmd", - 7437u16 => "faximum", - 7443u16 => "oracleas-https", - 7471u16 => "sttunnel", - 7473u16 => "rise", - 7474u16 => "neo4j", - 7478u16 => "openit", - 7491u16 => "telops-lmd", - 7500u16 => "silhouette", - 7501u16 => "ovbus", - 7508u16 => "adcp", - 7509u16 => "acplt", - 7510u16 => "ovhpas", - 7511u16 => "pafec-lm", - 7542u16 => "saratoga", - 7543u16 => "atul", - 7544u16 => "nta-ds", - 7545u16 => "nta-us", - 7546u16 => "cfs", - 7547u16 => "cwmp", - 7548u16 => "tidp", - 7549u16 => "nls-tl", - 7551u16 => "controlone-con", - 7560u16 => "sncp", - 7563u16 => "cfw", - 7566u16 => "vsi-omega", - 7569u16 => "dell-eql-asm", - 7570u16 => "aries-kfinder", - 7574u16 => "coherence", - 7588u16 => "sun-lm", - 7606u16 => "mipi-debug", - 7624u16 => "indi", - 7626u16 => "simco", - 7627u16 => "soap-http", - 7628u16 => "zen-pawn", - 7629u16 => "xdas", - 7630u16 => "hawk", - 7631u16 => "tesla-sys-msg", - 7633u16 => "pmdfmgt", - 7648u16 => "cuseeme", - 7663u16 => "rome", - 7672u16 => "imqstomp", - 7673u16 => "imqstomps", - 7674u16 => "imqtunnels", - 7675u16 => "imqtunnel", - 7676u16 => "imqbrokerd", - 7677u16 => "sun-user-https", - 7680u16 => "pando-pub", - 7683u16 => "dmt", - 7687u16 => "bolt", - 7689u16 => "collaber", - 7697u16 => "klio", - 7700u16 => "em7-secom", - 7707u16 => "sync-em7", - 7708u16 => "scinet", - 7720u16 => "medimageportal", - 7724u16 => "nsdeepfreezectl", - 7725u16 => "nitrogen", - 7726u16 => "freezexservice", - 7727u16 => "trident-data", - 7728u16 => "osvr", - 7734u16 => "smip", - 7738u16 => "aiagent", - 7741u16 => "scriptview", - 7742u16 => "msss", - 7743u16 => "sstp-1", - 7744u16 => "raqmon-pdu", - 7747u16 => "prgp", - 7775u16 => "inetfs", - 7777u16 => "cbt", - 7778u16 => "interwise", - 7779u16 => "vstat", - 7781u16 => "accu-lmgr", - 7786u16 => "minivend", - 7787u16 => "popup-reminders", - 7789u16 => "office-tools", - 7794u16 => "q3ade", - 7797u16 => "pnet-conn", - 7798u16 => "pnet-enc", - 7799u16 => "altbsdp", - 7800u16 => "asr", - 7801u16 => "ssp-client", - 7810u16 => "rbt-wanopt", - 7845u16 => "apc-7845", - 7846u16 => "apc-7846", - 7847u16 => "csoauth", - 7869u16 => "mobileanalyzer", - 7870u16 => "rbt-smc", - 7871u16 => "mdm", - 7878u16 => "owms", - 7880u16 => "pss", - 7887u16 => "ubroker", - 7900u16 => "mevent", - 7901u16 => "tnos-sp", - 7902u16 => "tnos-dp", - 7903u16 => "tnos-dps", - 7913u16 => "qo-secure", - 7932u16 => "t2-drm", - 7933u16 => "t2-brm", - 7962u16 => "generalsync", - 7967u16 => "supercell", - 7979u16 => "micromuse-ncps", - 7980u16 => "quest-vista", - 7981u16 => "sossd-collect", - 7982u16 => "sossd-agent", - 7997u16 => "pushns", - 7999u16 => "irdmi2", - 8000u16 => "irdmi", - 8001u16 => "vcom-tunnel", - 8002u16 => "teradataordbms", - 8003u16 => "mcreport", - 8004u16 => "p2pevolvenet", - 8005u16 => "mxi", - 8006u16 => "wpl-analytics", - 8007u16 => "warppipe", - 8008u16 => "http-alt", - 8009u16 => "nvme-disc", - 8015u16 => "cfg-cloud", - 8016u16 => "ads-s", - 8019u16 => "qbdb", - 8020u16 => "intu-ec-svcdisc", - 8021u16 => "intu-ec-client", - 8022u16 => "oa-system", - 8023u16 => "arca-api", - 8025u16 => "ca-audit-da", - 8026u16 => "ca-audit-ds", - 8027u16 => "papachi-p2p-srv", - 8032u16 => "pro-ed", - 8033u16 => "mindprint", - 8034u16 => "vantronix-mgmt", - 8040u16 => "ampify", - 8041u16 => "enguity-xccetp", - 8042u16 => "fs-agent", - 8043u16 => "fs-server", - 8044u16 => "fs-mgmt", - 8051u16 => "rocrail", - 8052u16 => "senomix01", - 8053u16 => "senomix02", - 8054u16 => "senomix03", - 8055u16 => "senomix04", - 8056u16 => "senomix05", - 8057u16 => "senomix06", - 8058u16 => "senomix07", - 8059u16 => "senomix08", - 8066u16 => "toad-bi-appsrvr", - 8067u16 => "infi-async", - 8070u16 => "ucs-isc", - 8074u16 => "gadugadu", - 8077u16 => "mles", - 8080u16 => "http-alt", - 8081u16 => "sunproxyadmin", - 8082u16 => "us-cli", - 8083u16 => "us-srv", - 8084u16 => "websnp", - 8086u16 => "d-s-n", - 8087u16 => "simplifymedia", - 8088u16 => "radan-http", - 8090u16 => "opsmessaging", - 8091u16 => "jamlink", - 8097u16 => "sac", - 8100u16 => "xprint-server", - 8101u16 => "ldoms-migr", - 8102u16 => "kz-migr", - 8115u16 => "mtl8000-matrix", - 8116u16 => "cp-cluster", - 8117u16 => "purityrpc", - 8118u16 => "privoxy", - 8121u16 => "apollo-data", - 8122u16 => "apollo-admin", - 8128u16 => "paycash-online", - 8129u16 => "paycash-wbp", - 8130u16 => "indigo-vrmi", - 8131u16 => "indigo-vbcp", - 8132u16 => "dbabble", - 8140u16 => "puppet", - 8148u16 => "isdd", - 8153u16 => "quantastor", - 8160u16 => "patrol", - 8161u16 => "patrol-snmp", - 8162u16 => "lpar2rrd", - 8181u16 => "intermapper", - 8182u16 => "vmware-fdm", - 8183u16 => "proremote", - 8184u16 => "itach", - 8190u16 => "gcp-rphy", - 8191u16 => "limnerpressure", - 8192u16 => "spytechphone", - 8194u16 => "blp1", - 8195u16 => "blp2", - 8199u16 => "vvr-data", - 8200u16 => "trivnet1", - 8201u16 => "trivnet2", - 8204u16 => "lm-perfworks", - 8205u16 => "lm-instmgr", - 8206u16 => "lm-dta", - 8207u16 => "lm-sserver", - 8208u16 => "lm-webwatcher", - 8230u16 => "rexecj", - 8243u16 => "synapse-nhttps", - 8270u16 => "robot-remote", - 8276u16 => "pando-sec", - 8280u16 => "synapse-nhttp", - 8282u16 => "libelle", - 8292u16 => "blp3", - 8293u16 => "hiperscan-id", - 8294u16 => "blp4", - 8300u16 => "tmi", - 8301u16 => "amberon", - 8313u16 => "hub-open-net", - 8320u16 => "tnp-discover", - 8321u16 => "tnp", - 8322u16 => "garmin-marine", - 8351u16 => "server-find", - 8376u16 => "cruise-enum", - 8377u16 => "cruise-swroute", - 8378u16 => "cruise-config", - 8379u16 => "cruise-diags", - 8380u16 => "cruise-update", - 8383u16 => "m2mservices", - 8400u16 => "cvd", - 8401u16 => "sabarsd", - 8402u16 => "abarsd", - 8403u16 => "admind", - 8404u16 => "svcloud", - 8405u16 => "svbackup", - 8415u16 => "dlpx-sp", - 8416u16 => "espeech", - 8417u16 => "espeech-rtp", - 8423u16 => "aritts", - 8442u16 => "cybro-a-bus", - 8443u16 => "pcsync-https", - 8444u16 => "pcsync-http", - 8445u16 => "copy", - 8450u16 => "npmp", - 8457u16 => "nexentamv", - 8470u16 => "cisco-avp", - 8471u16 => "pim-port", - 8472u16 => "otv", - 8473u16 => "vp2p", - 8474u16 => "noteshare", - 8500u16 => "fmtp", - 8501u16 => "cmtp-mgt", - 8502u16 => "ftnmtp", - 8554u16 => "rtsp-alt", - 8555u16 => "d-fence", - 8567u16 => "dof-tunnel", - 8600u16 => "asterix", - 8610u16 => "canon-mfnp", - 8611u16 => "canon-bjnp1", - 8612u16 => "canon-bjnp2", - 8613u16 => "canon-bjnp3", - 8614u16 => "canon-bjnp4", - 8615u16 => "imink", - 8665u16 => "monetra", - 8666u16 => "monetra-admin", - 8675u16 => "msi-cps-rm", - 8686u16 => "sun-as-jmxrmi", - 8688u16 => "openremote-ctrl", - 8699u16 => "vnyx", - 8710u16 => "semi-grpc", - 8711u16 => "nvc", - 8733u16 => "ibus", - 8750u16 => "dey-keyneg", - 8763u16 => "mc-appserver", - 8764u16 => "openqueue", - 8765u16 => "ultraseek-http", - 8766u16 => "amcs", - 8767u16 => "core-of-source", - 8768u16 => "sandpolis", - 8769u16 => "oktaauthenticat", - 8770u16 => "dpap", - 8778u16 => "uec", - 8786u16 => "msgclnt", - 8787u16 => "msgsrvr", - 8793u16 => "acd-pm", - 8800u16 => "sunwebadmin", - 8804u16 => "truecm", - 8873u16 => "dxspider", - 8880u16 => "cddbp-alt", - 8881u16 => "galaxy4d", - 8883u16 => "secure-mqtt", - 8888u16 => "ddi-tcp-1", - 8889u16 => "ddi-tcp-2", - 8890u16 => "ddi-tcp-3", - 8891u16 => "ddi-tcp-4", - 8892u16 => "ddi-tcp-5", - 8893u16 => "ddi-tcp-6", - 8894u16 => "ddi-tcp-7", - 8899u16 => "ospf-lite", - 8900u16 => "jmb-cds1", - 8901u16 => "jmb-cds2", - 8908u16 => "dpp", - 8910u16 => "manyone-http", - 8911u16 => "manyone-xml", - 8912u16 => "wcbackup", - 8913u16 => "dragonfly", - 8937u16 => "twds", - 8953u16 => "ub-dns-control", - 8954u16 => "cumulus-admin", - 8980u16 => "nod-provider", - 8989u16 => "sunwebadmins", - 8990u16 => "http-wmap", - 8991u16 => "https-wmap", - 8997u16 => "oracle-ms-ens", - 8998u16 => "canto-roboflow", - 8999u16 => "bctp", - 9000u16 => "cslistener", - 9001u16 => "etlservicemgr", - 9002u16 => "dynamid", - 9005u16 => "golem", - 9008u16 => "ogs-server", - 9009u16 => "pichat", - 9010u16 => "sdr", - 9020u16 => "tambora", - 9021u16 => "panagolin-ident", - 9022u16 => "paragent", - 9023u16 => "swa-1", - 9024u16 => "swa-2", - 9025u16 => "swa-3", - 9026u16 => "swa-4", - 9050u16 => "versiera", - 9051u16 => "fio-cmgmt", - 9060u16 => "CardWeb-IO", - 9080u16 => "glrpc", - 9083u16 => "emc-pp-mgmtsvc", - 9084u16 => "aurora", - 9085u16 => "ibm-rsyscon", - 9086u16 => "net2display", - 9087u16 => "classic", - 9088u16 => "sqlexec", - 9089u16 => "sqlexec-ssl", - 9090u16 => "websm", - 9091u16 => "xmltec-xmlmail", - 9092u16 => "XmlIpcRegSvc", - 9093u16 => "copycat", - 9100u16 => "pdl-datastream", - 9101u16 => "bacula-dir", - 9102u16 => "bacula-fd", - 9103u16 => "bacula-sd", - 9104u16 => "peerwire", - 9105u16 => "xadmin", - 9106u16 => "astergate", - 9107u16 => "astergatefax", - 9111u16 => "hexxorecore", - 9119u16 => "mxit", - 9122u16 => "grcmp", - 9123u16 => "grcp", - 9131u16 => "dddp", - 9160u16 => "apani1", - 9161u16 => "apani2", - 9162u16 => "apani3", - 9163u16 => "apani4", - 9164u16 => "apani5", - 9191u16 => "sun-as-jpda", - 9200u16 => "wap-wsp", - 9201u16 => "wap-wsp-wtp", - 9202u16 => "wap-wsp-s", - 9203u16 => "wap-wsp-wtp-s", - 9204u16 => "wap-vcard", - 9205u16 => "wap-vcal", - 9206u16 => "wap-vcard-s", - 9207u16 => "wap-vcal-s", - 9208u16 => "rjcdb-vcards", - 9209u16 => "almobile-system", - 9210u16 => "oma-mlp", - 9211u16 => "oma-mlp-s", - 9212u16 => "serverviewdbms", - 9213u16 => "serverstart", - 9214u16 => "ipdcesgbs", - 9215u16 => "insis", - 9216u16 => "acme", - 9217u16 => "fsc-port", - 9222u16 => "teamcoherence", - 9255u16 => "mon", - 9278u16 => "pegasus", - 9279u16 => "pegasus-ctl", - 9280u16 => "pgps", - 9281u16 => "swtp-port1", - 9282u16 => "swtp-port2", - 9283u16 => "callwaveiam", - 9284u16 => "visd", - 9285u16 => "n2h2server", - 9287u16 => "cumulus", - 9292u16 => "armtechdaemon", - 9293u16 => "storview", - 9294u16 => "armcenterhttp", - 9295u16 => "armcenterhttps", - 9300u16 => "vrace", - 9306u16 => "sphinxql", - 9310u16 => "sapms", - 9312u16 => "sphinxapi", - 9318u16 => "secure-ts", - 9321u16 => "guibase", - 9339u16 => "gnmi-gnoi", - 9343u16 => "mpidcmgr", - 9344u16 => "mphlpdmc", - 9345u16 => "rancher", - 9346u16 => "ctechlicensing", - 9374u16 => "fjdmimgr", - 9380u16 => "boxp", - 9387u16 => "d2dconfig", - 9388u16 => "d2ddatatrans", - 9389u16 => "adws", - 9390u16 => "otp", - 9396u16 => "fjinvmgr", - 9397u16 => "mpidcagt", - 9400u16 => "sec-t4net-srv", - 9401u16 => "sec-t4net-clt", - 9402u16 => "sec-pc2fax-srv", - 9418u16 => "git", - 9443u16 => "tungsten-https", - 9444u16 => "wso2esb-console", - 9445u16 => "mindarray-ca", - 9450u16 => "sntlkeyssrvr", - 9500u16 => "ismserver", - 9535u16 => "mngsuite", - 9536u16 => "laes-bf", - 9555u16 => "trispen-sra", - 9559u16 => "p4runtime", - 9592u16 => "ldgateway", - 9593u16 => "cba8", - 9594u16 => "msgsys", - 9595u16 => "pds", - 9596u16 => "mercury-disc", - 9597u16 => "pd-admin", - 9598u16 => "vscp", - 9599u16 => "robix", - 9600u16 => "micromuse-ncpw", - 9612u16 => "streamcomm-ds", - 9614u16 => "iadt-tls", - 9616u16 => "erunbook_agent", - 9617u16 => "erunbook_server", - 9618u16 => "condor", - 9628u16 => "odbcpathway", - 9629u16 => "uniport", - 9630u16 => "peoctlr", - 9631u16 => "peocoll", - 9640u16 => "pqsflows", - 9666u16 => "zoomcp", - 9667u16 => "xmms2", - 9668u16 => "tec5-sdctp", - 9694u16 => "client-wakeup", - 9695u16 => "ccnx", - 9700u16 => "board-roar", - 9747u16 => "l5nas-parchan", - 9750u16 => "board-voip", - 9753u16 => "rasadv", - 9762u16 => "tungsten-http", - 9800u16 => "davsrc", - 9801u16 => "sstp-2", - 9802u16 => "davsrcs", - 9875u16 => "sapv1", - 9876u16 => "sd", - 9877u16 => "x510", - 9888u16 => "cyborg-systems", - 9889u16 => "gt-proxy", - 9898u16 => "monkeycom", - 9900u16 => "iua", - 9909u16 => "domaintime", - 9911u16 => "sype-transport", - 9925u16 => "xybrid-cloud", - 9929u16 => "nping-echo", - 9950u16 => "apc-9950", - 9951u16 => "apc-9951", - 9952u16 => "apc-9952", - 9953u16 => "acis", - 9954u16 => "hinp", - 9955u16 => "alljoyn-stm", - 9966u16 => "odnsp", - 9978u16 => "xybrid-rt", - 9979u16 => "visweather", - 9981u16 => "pumpkindb", - 9987u16 => "dsm-scm-target", - 9988u16 => "nsesrvr", - 9990u16 => "osm-appsrvr", - 9991u16 => "osm-oev", - 9992u16 => "palace-1", - 9993u16 => "palace-2", - 9994u16 => "palace-3", - 9995u16 => "palace-4", - 9996u16 => "palace-5", - 9997u16 => "palace-6", - 9998u16 => "distinct32", - 9999u16 => "distinct", - 10000u16 => "ndmp", - 10001u16 => "scp-config", - 10002u16 => "documentum", - 10003u16 => "documentum_s", - 10004u16 => "emcrmirccd", - 10005u16 => "emcrmird", - 10006u16 => "netapp-sync", - 10007u16 => "mvs-capacity", - 10008u16 => "octopus", - 10009u16 => "swdtp-sv", - 10010u16 => "rxapi", - 10020u16 => "abb-hw", - 10050u16 => "zabbix-agent", - 10051u16 => "zabbix-trapper", - 10055u16 => "qptlmd", - 10080u16 => "amanda", - 10081u16 => "famdc", - 10100u16 => "itap-ddtp", - 10101u16 => "ezmeeting-2", - 10102u16 => "ezproxy-2", - 10103u16 => "ezrelay", - 10104u16 => "swdtp", - 10107u16 => "bctp-server", - 10110u16 => "nmea-0183", - 10113u16 => "netiq-endpoint", - 10114u16 => "netiq-qcheck", - 10115u16 => "netiq-endpt", - 10116u16 => "netiq-voipa", - 10117u16 => "iqrm", - 10125u16 => "cimple", - 10128u16 => "bmc-perf-sd", - 10129u16 => "bmc-gms", - 10160u16 => "qb-db-server", - 10161u16 => "snmptls", - 10162u16 => "snmptls-trap", - 10200u16 => "trisoap", - 10201u16 => "rsms", - 10252u16 => "apollo-relay", - 10260u16 => "axis-wimp-port", - 10261u16 => "tile-ml", - 10288u16 => "blocks", - 10321u16 => "cosir", - 10443u16 => "cirrossp", - 10540u16 => "MOS-lower", - 10541u16 => "MOS-upper", - 10542u16 => "MOS-aux", - 10543u16 => "MOS-soap", - 10544u16 => "MOS-soap-opt", - 10548u16 => "serverdocs", - 10631u16 => "printopia", - 10800u16 => "gap", - 10805u16 => "lpdg", - 10809u16 => "nbd", - 10860u16 => "helix", - 10880u16 => "bveapi", - 10933u16 => "octopustentacle", - 10990u16 => "rmiaux", - 11000u16 => "irisa", - 11001u16 => "metasys", - 11095u16 => "weave", - 11103u16 => "origo-sync", - 11104u16 => "netapp-icmgmt", - 11105u16 => "netapp-icdata", - 11106u16 => "sgi-lk", - 11109u16 => "sgi-dmfmgr", - 11110u16 => "sgi-soap", - 11111u16 => "vce", - 11112u16 => "dicom", - 11161u16 => "suncacao-snmp", - 11162u16 => "suncacao-jmxmp", - 11163u16 => "suncacao-rmi", - 11164u16 => "suncacao-csa", - 11165u16 => "suncacao-websvc", - 11172u16 => "oemcacao-jmxmp", - 11173u16 => "t5-straton", - 11174u16 => "oemcacao-rmi", - 11175u16 => "oemcacao-websvc", - 11201u16 => "smsqp", - 11202u16 => "dcsl-backup", - 11208u16 => "wifree", - 11211u16 => "memcache", - 11235u16 => "xcompute", - 11319u16 => "imip", - 11320u16 => "imip-channels", - 11321u16 => "arena-server", - 11367u16 => "atm-uhas", - 11371u16 => "hkp", - 11489u16 => "asgcypresstcps", - 11600u16 => "tempest-port", - 11623u16 => "emc-xsw-dconfig", - 11720u16 => "h323callsigalt", - 11723u16 => "emc-xsw-dcache", - 11751u16 => "intrepid-ssl", - 11796u16 => "lanschool", - 11876u16 => "xoraya", - 11967u16 => "sysinfo-sp", - 11971u16 => "tibsd", - 12000u16 => "entextxid", - 12001u16 => "entextnetwk", - 12002u16 => "entexthigh", - 12003u16 => "entextmed", - 12004u16 => "entextlow", - 12005u16 => "dbisamserver1", - 12006u16 => "dbisamserver2", - 12007u16 => "accuracer", - 12008u16 => "accuracer-dbms", - 12010u16 => "edbsrvr", - 12012u16 => "vipera", - 12013u16 => "vipera-ssl", - 12109u16 => "rets-ssl", - 12121u16 => "nupaper-ss", - 12168u16 => "cawas", - 12172u16 => "hivep", - 12300u16 => "linogridengine", - 12302u16 => "rads", - 12321u16 => "warehouse-sss", - 12322u16 => "warehouse", - 12345u16 => "italk", - 12753u16 => "tsaf", - 12865u16 => "netperf", - 13160u16 => "i-zipqd", - 13216u16 => "bcslogc", - 13217u16 => "rs-pias", - 13218u16 => "emc-vcas-tcp", - 13223u16 => "powwow-client", - 13224u16 => "powwow-server", - 13400u16 => "doip-data", - 13720u16 => "bprd", - 13721u16 => "bpdbm", - 13722u16 => "bpjava-msvc", - 13724u16 => "vnetd", - 13782u16 => "bpcd", - 13783u16 => "vopied", - 13785u16 => "nbdb", - 13786u16 => "nomdb", - 13818u16 => "dsmcc-config", - 13819u16 => "dsmcc-session", - 13820u16 => "dsmcc-passthru", - 13821u16 => "dsmcc-download", - 13822u16 => "dsmcc-ccp", - 13823u16 => "bmdss", - 13894u16 => "ucontrol", - 13929u16 => "dta-systems", - 13930u16 => "medevolve", - 14000u16 => "scotty-ft", - 14001u16 => "sua", - 14033u16 => "sage-best-com1", - 14034u16 => "sage-best-com2", - 14141u16 => "vcs-app", - 14142u16 => "icpp", - 14143u16 => "icpps", - 14145u16 => "gcm-app", - 14149u16 => "vrts-tdd", - 14150u16 => "vcscmd", - 14154u16 => "vad", - 14250u16 => "cps", - 14414u16 => "ca-web-update", - 14500u16 => "xpra", - 14936u16 => "hde-lcesrvr-1", - 14937u16 => "hde-lcesrvr-2", - 15000u16 => "hydap", - 15002u16 => "onep-tls", - 15345u16 => "xpilot", - 15363u16 => "3link", - 15555u16 => "cisco-snat", - 15660u16 => "bex-xr", - 15740u16 => "ptp", - 15999u16 => "programmar", - 16000u16 => "fmsas", - 16001u16 => "fmsascon", - 16002u16 => "gsms", - 16020u16 => "jwpc", - 16021u16 => "jwpc-bin", - 16161u16 => "sun-sea-port", - 16162u16 => "solaris-audit", - 16309u16 => "etb4j", - 16310u16 => "pduncs", - 16311u16 => "pdefmns", - 16360u16 => "netserialext1", - 16361u16 => "netserialext2", - 16367u16 => "netserialext3", - 16368u16 => "netserialext4", - 16384u16 => "connected", - 16385u16 => "rdgs", - 16619u16 => "xoms", - 16665u16 => "axon-tunnel", - 16789u16 => "cadsisvr", - 16900u16 => "newbay-snc-mc", - 16950u16 => "sgcip", - 16991u16 => "intel-rci-mp", - 16992u16 => "amt-soap-http", - 16993u16 => "amt-soap-https", - 16994u16 => "amt-redir-tcp", - 16995u16 => "amt-redir-tls", - 17007u16 => "isode-dua", - 17184u16 => "vestasdlp", - 17185u16 => "soundsvirtual", - 17219u16 => "chipper", - 17220u16 => "avtp", - 17221u16 => "avdecc", - 17223u16 => "isa100-gci", - 17225u16 => "trdp-md", - 17234u16 => "integrius-stp", - 17235u16 => "ssh-mgmt", - 17500u16 => "db-lsp", - 17555u16 => "ailith", - 17729u16 => "ea", - 17754u16 => "zep", - 17755u16 => "zigbee-ip", - 17756u16 => "zigbee-ips", - 17777u16 => "sw-orion", - 18000u16 => "biimenu", - 18104u16 => "radpdf", - 18136u16 => "racf", - 18181u16 => "opsec-cvp", - 18182u16 => "opsec-ufp", - 18183u16 => "opsec-sam", - 18184u16 => "opsec-lea", - 18185u16 => "opsec-omi", - 18186u16 => "ohsc", - 18187u16 => "opsec-ela", - 18241u16 => "checkpoint-rtm", - 18242u16 => "iclid", - 18243u16 => "clusterxl", - 18262u16 => "gv-pf", - 18463u16 => "ac-cluster", - 18634u16 => "rds-ib", - 18635u16 => "rds-ip", - 18668u16 => "vdmmesh", - 18769u16 => "ique", - 18881u16 => "infotos", - 18888u16 => "apc-necmp", - 19000u16 => "igrid", - 19007u16 => "scintilla", - 19020u16 => "j-link", - 19191u16 => "opsec-uaa", - 19194u16 => "ua-secureagent", - 19220u16 => "cora", - 19283u16 => "keysrvr", - 19315u16 => "keyshadow", - 19398u16 => "mtrgtrans", - 19410u16 => "hp-sco", - 19411u16 => "hp-sca", - 19412u16 => "hp-sessmon", - 19539u16 => "fxuptp", - 19540u16 => "sxuptp", - 19541u16 => "jcp", - 19790u16 => "faircom-db", - 19998u16 => "iec-104-sec", - 19999u16 => "dnp-sec", - 20000u16 => "dnp", - 20001u16 => "microsan", - 20002u16 => "commtact-http", - 20003u16 => "commtact-https", - 20005u16 => "openwebnet", - 20013u16 => "ss-idi", - 20014u16 => "opendeploy", - 20034u16 => "nburn_id", - 20046u16 => "tmophl7mts", - 20048u16 => "mountd", - 20049u16 => "nfsrdma", - 20057u16 => "avesterra", - 20167u16 => "tolfab", - 20202u16 => "ipdtp-port", - 20222u16 => "ipulse-ics", - 20480u16 => "emwavemsg", - 20670u16 => "track", - 20999u16 => "athand-mmp", - 21000u16 => "irtrans", - 21010u16 => "notezilla-lan", - 21212u16 => "trinket-agent", - 21213u16 => "cohesity-agent", - 21221u16 => "aigairserver", - 21553u16 => "rdm-tfs", - 21554u16 => "dfserver", - 21590u16 => "vofr-gateway", - 21800u16 => "tvpm", - 21845u16 => "webphone", - 21846u16 => "netspeak-is", - 21847u16 => "netspeak-cs", - 21848u16 => "netspeak-acd", - 21849u16 => "netspeak-cps", - 22000u16 => "snapenetio", - 22001u16 => "optocontrol", - 22002u16 => "optohost002", - 22003u16 => "optohost003", - 22004u16 => "optohost004", - 22005u16 => "optohost004", - 22125u16 => "dcap", - 22128u16 => "gsidcap", - 22222u16 => "easyengine", - 22273u16 => "wnn6", - 22305u16 => "cis", - 22333u16 => "showcockpit-net", - 22335u16 => "shrewd-control", - 22343u16 => "cis-secure", - 22347u16 => "wibukey", - 22350u16 => "codemeter", - 22351u16 => "codemeter-cmwan", - 22537u16 => "caldsoft-backup", - 22555u16 => "vocaltec-wconf", - 22763u16 => "talikaserver", - 22800u16 => "aws-brf", - 22951u16 => "brf-gw", - 23000u16 => "inovaport1", - 23001u16 => "inovaport2", - 23002u16 => "inovaport3", - 23003u16 => "inovaport4", - 23004u16 => "inovaport5", - 23005u16 => "inovaport6", - 23053u16 => "gntp", - 23294u16 => "5afe-dir", - 23333u16 => "elxmgmt", - 23400u16 => "novar-dbase", - 23401u16 => "novar-alarm", - 23402u16 => "novar-global", - 23456u16 => "aequus", - 23457u16 => "aequus-alt", - 23546u16 => "areaguard-neo", - 24000u16 => "med-ltp", - 24001u16 => "med-fsp-rx", - 24002u16 => "med-fsp-tx", - 24003u16 => "med-supp", - 24004u16 => "med-ovw", - 24005u16 => "med-ci", - 24006u16 => "med-net-svc", - 24242u16 => "filesphere", - 24249u16 => "vista-4gl", - 24321u16 => "ild", - 24323u16 => "vrmg-ip", - 24386u16 => "intel_rci", - 24465u16 => "tonidods", - 24554u16 => "binkp", - 24577u16 => "bilobit", - 24666u16 => "sdtvwcam", - 24676u16 => "canditv", - 24677u16 => "flashfiler", - 24678u16 => "proactivate", - 24680u16 => "tcc-http", - 24754u16 => "cslg", - 24922u16 => "find", - 25000u16 => "icl-twobase1", - 25001u16 => "icl-twobase2", - 25002u16 => "icl-twobase3", - 25003u16 => "icl-twobase4", - 25004u16 => "icl-twobase5", - 25005u16 => "icl-twobase6", - 25006u16 => "icl-twobase7", - 25007u16 => "icl-twobase8", - 25008u16 => "icl-twobase9", - 25009u16 => "icl-twobase10", - 25576u16 => "sauterdongle", - 25604u16 => "idtp", - 25793u16 => "vocaltec-hos", - 25900u16 => "tasp-net", - 25901u16 => "niobserver", - 25902u16 => "nilinkanalyst", - 25903u16 => "niprobe", - 26000u16 => "quake", - 26133u16 => "scscp", - 26208u16 => "wnn6-ds", - 26257u16 => "cockroach", - 26260u16 => "ezproxy", - 26261u16 => "ezmeeting", - 26262u16 => "k3software-svr", - 26263u16 => "k3software-cli", - 26486u16 => "exoline-tcp", - 26487u16 => "exoconfig", - 26489u16 => "exonet", - 27010u16 => "flex-lmadmin", - 27017u16 => "mongodb", - 27345u16 => "imagepump", - 27442u16 => "jesmsjc", - 27504u16 => "kopek-httphead", - 27782u16 => "ars-vista", - 27876u16 => "astrolink", - 27999u16 => "tw-auth-key", - 28000u16 => "nxlmd", - 28001u16 => "pqsp", - 28010u16 => "gruber-cashreg", - 28200u16 => "voxelstorm", - 28240u16 => "siemensgsm", - 28589u16 => "bosswave", - 29000u16 => "saltd-licensing", - 29167u16 => "otmp", - 29999u16 => "bingbang", - 30000u16 => "ndmps", - 30001u16 => "pago-services1", - 30002u16 => "pago-services2", - 30003u16 => "amicon-fpsu-ra", - 30100u16 => "rwp", - 30260u16 => "kingdomsonline", - 30400u16 => "gs-realtime", - 30999u16 => "ovobs", - 31016u16 => "ka-sddp", - 31020u16 => "autotrac-acp", - 31337u16 => "back-orifice", - 31400u16 => "pace-licensed", - 31416u16 => "xqosd", - 31457u16 => "tetrinet", - 31620u16 => "lm-mon", - 31685u16 => "dsx_monitor", - 31765u16 => "gamesmith-port", - 31948u16 => "iceedcp_tx", - 31949u16 => "iceedcp_rx", - 32034u16 => "iracinghelper", - 32249u16 => "t1distproc60", - 32400u16 => "plex", - 32483u16 => "apm-link", - 32635u16 => "sec-ntb-clnt", - 32636u16 => "DMExpress", - 32767u16 => "filenet-powsrm", - 32768u16 => "filenet-tms", - 32769u16 => "filenet-rpc", - 32770u16 => "filenet-nch", - 32771u16 => "filenet-rmi", - 32772u16 => "filenet-pa", - 32773u16 => "filenet-cm", - 32774u16 => "filenet-re", - 32775u16 => "filenet-pch", - 32776u16 => "filenet-peior", - 32777u16 => "filenet-obrok", - 32801u16 => "mlsn", - 32811u16 => "retp", - 32896u16 => "idmgratm", - 33000u16 => "wg-endpt-comms", - 33060u16 => "mysqlx", - 33123u16 => "aurora-balaena", - 33331u16 => "diamondport", - 33333u16 => "dgi-serv", - 33334u16 => "speedtrace", - 33434u16 => "traceroute", - 33656u16 => "snip-slave", - 33890u16 => "digilent-adept", - 34249u16 => "turbonote-2", - 34378u16 => "p-net-local", - 34379u16 => "p-net-remote", - 34567u16 => "dhanalakshmi", - 34962u16 => "profinet-rt", - 34963u16 => "profinet-rtm", - 34964u16 => "profinet-cm", - 34980u16 => "ethercat", - 35000u16 => "heathview", - 35001u16 => "rt-viewer", - 35002u16 => "rt-sound", - 35003u16 => "rt-devicemapper", - 35004u16 => "rt-classmanager", - 35005u16 => "rt-labtracker", - 35006u16 => "rt-helper", - 35100u16 => "axio-disc", - 35354u16 => "kitim", - 35355u16 => "altova-lm", - 35356u16 => "guttersnex", - 35357u16 => "openstack-id", - 36001u16 => "allpeers", - 36524u16 => "febooti-aw", - 36602u16 => "observium-agent", - 36700u16 => "mapx", - 36865u16 => "kastenxpipe", - 37475u16 => "neckar", - 37483u16 => "gdrive-sync", - 37601u16 => "eftp", - 37654u16 => "unisys-eportal", - 38000u16 => "ivs-database", - 38001u16 => "ivs-insertion", - 38002u16 => "cresco-control", - 38201u16 => "galaxy7-data", - 38202u16 => "fairview", - 38203u16 => "agpolicy", - 38800u16 => "sruth", - 38865u16 => "secrmmsafecopya", - 39681u16 => "turbonote-1", - 40000u16 => "safetynetp", - 40404u16 => "sptx", - 40841u16 => "cscp", - 40842u16 => "csccredir", - 40843u16 => "csccfirewall", - 41111u16 => "fs-qos", - 41121u16 => "tentacle", - 41230u16 => "z-wave-s", - 41794u16 => "crestron-cip", - 41795u16 => "crestron-ctp", - 41796u16 => "crestron-cips", - 41797u16 => "crestron-ctps", - 42508u16 => "candp", - 42509u16 => "candrp", - 42510u16 => "caerpc", - 43000u16 => "recvr-rc", - 43188u16 => "reachout", - 43189u16 => "ndm-agent-port", - 43190u16 => "ip-provision", - 43191u16 => "noit-transport", - 43210u16 => "shaperai", - 43439u16 => "eq3-update", - 43440u16 => "ew-mgmt", - 43441u16 => "ciscocsdb", - 44123u16 => "z-wave-tunnel", - 44321u16 => "pmcd", - 44322u16 => "pmcdproxy", - 44323u16 => "pmwebapi", - 44444u16 => "cognex-dataman", - 44445u16 => "acronis-backup", - 44553u16 => "rbr-debug", - 44818u16 => "EtherNet/IP-2", - 44900u16 => "m3da", - 45000u16 => "asmp", - 45001u16 => "asmps", - 45002u16 => "rs-status", - 45045u16 => "synctest", - 45054u16 => "invision-ag", - 45514u16 => "cloudcheck", - 45678u16 => "eba", - 45824u16 => "dai-shell", - 45825u16 => "qdb2service", - 45966u16 => "ssr-servermgr", - 46336u16 => "inedo", - 46998u16 => "spremotetablet", - 46999u16 => "mediabox", - 47000u16 => "mbus", - 47001u16 => "winrm", - 47557u16 => "dbbrowse", - 47624u16 => "directplaysrvr", - 47806u16 => "ap", - 47808u16 => "bacnet", - 48000u16 => "nimcontroller", - 48001u16 => "nimspooler", - 48002u16 => "nimhub", - 48003u16 => "nimgtw", - 48004u16 => "nimbusdb", - 48005u16 => "nimbusdbctrl", - 48048u16 => "juka", - 48049u16 => "3gpp-cbsp", - 48050u16 => "weandsf", - 48128u16 => "isnetserv", - 48129u16 => "blp5", - 48556u16 => "com-bardac-dw", - 48619u16 => "iqobject", - 48653u16 => "robotraconteur", - 49000u16 => "matahari", - 49001u16 => "nusrp", - 49150u16 => "inspider", -}; diff --git a/src/db/tls.rs b/src/db/tls.rs new file mode 100644 index 0000000..6bfda5c --- /dev/null +++ b/src/db/tls.rs @@ -0,0 +1,39 @@ +use anyhow::Result; +use serde::Deserialize; +use std::{collections::HashMap, sync::OnceLock}; + +use crate::config::db::TLS_OID_MAP_JSON; + +/// Structure representing the TLS OID mappings. +#[derive(Debug, Deserialize)] +pub struct TlsOidMap { + /// Mapping of OID to signature algorithm names. + pub sig: HashMap, + /// Mapping of OID to public key algorithm names. + pub pubkey: HashMap, +} + +/// Global static instance of the TLS OID map, initialized once. +pub static TLS_OID_MAP: OnceLock = OnceLock::new(); + +/// Get a reference to the initialized TLS OID map. +pub fn tls_oid_map() -> &'static TlsOidMap { + TLS_OID_MAP.get().expect("TLS_OID_MAP not initialized") +} + +/// Initialize the TLS OID map from the bundled JSON data. +pub fn init_tls_oid_map() -> Result<()> { + let map: TlsOidMap = serde_json::from_str(&TLS_OID_MAP_JSON).expect("invalid nrev-tls-oid-map.json"); + TLS_OID_MAP.set(map).map_err(|_| anyhow::anyhow!("Failed to set TLS_OID_MAP in OnceLock"))?; + Ok(()) +} + +/// Get the name of a TLS version given its numeric representation. +pub fn oid_sig_name(oid: &str) -> String { + tls_oid_map().sig.get(oid).cloned().unwrap_or_else(|| oid.to_string()) +} + +/// Get the name of a public key algorithm given its OID. +pub fn oid_pubkey_name(oid: &str) -> String { + tls_oid_map().pubkey.get(oid).cloned().unwrap_or_else(|| oid.to_string()) +} diff --git a/src/dep/mod.rs b/src/dep/mod.rs deleted file mode 100644 index 0e95fb0..0000000 --- a/src/dep/mod.rs +++ /dev/null @@ -1,39 +0,0 @@ -#[cfg(not(target_os = "windows"))] -mod unix; -use std::{error::Error, fmt}; - -#[cfg(not(target_os = "windows"))] -pub use self::unix::*; - -#[cfg(target_os = "windows")] -mod windows; -#[cfg(target_os = "windows")] -pub use self::windows::*; - -// Custom error type for dependency check -#[derive(Debug)] -pub struct DependencyError { - pub dependency: String, - pub message: String, -} - -impl DependencyError { - pub fn new(dependency: &str, message: &str) -> Self { - Self { - dependency: String::from(dependency), - message: String::from(message), - } - } -} - -impl fmt::Display for DependencyError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}: {}", self.dependency, self.message) - } -} - -impl Error for DependencyError { - fn description(&self) -> &str { - &self.message - } -} diff --git a/src/dep/unix.rs b/src/dep/unix.rs deleted file mode 100644 index 51e39d1..0000000 --- a/src/dep/unix.rs +++ /dev/null @@ -1,5 +0,0 @@ -use super::DependencyError; - -pub fn check_dependencies() -> Result<(), DependencyError> { - Ok(()) -} diff --git a/src/dep/windows.rs b/src/dep/windows.rs deleted file mode 100644 index 06e5767..0000000 --- a/src/dep/windows.rs +++ /dev/null @@ -1,48 +0,0 @@ -use super::DependencyError; -use winreg::enums::HKEY_LOCAL_MACHINE; -use winreg::RegKey; - -const NPCAP_SOFTWARE_NAME: &str = "Npcap"; - -pub fn check_dependencies() -> Result<(), DependencyError> { - if npcap_installed() { - Ok(()) - } else { - Err(DependencyError::new("Npcap", "Npcap is not installed. \nOn Windows, Npcap is required for some features. \nPlease install Npcap from https://npcap.com/#download")) - } -} - -pub fn get_os_bit() -> String { - if cfg!(target_pointer_width = "32") { - return "32-bit".to_owned(); - } else if cfg!(target_pointer_width = "64") { - return "64-bit".to_owned(); - } else { - return "unknown".to_owned(); - } -} - -// Get software installation status -pub fn software_installed(software_name: String) -> bool { - let hklm: RegKey = RegKey::predef(HKEY_LOCAL_MACHINE); - let os_bit: String = get_os_bit(); - let npcap_key: RegKey = if os_bit == "32-bit" { - match hklm.open_subkey(format!("SOFTWARE\\{}", software_name)) { - Ok(key) => key, - Err(_) => return false, - } - } else { - match hklm.open_subkey(format!("SOFTWARE\\WOW6432Node\\{}", software_name)) { - Ok(key) => key, - Err(_) => return false, - } - }; - let _version: String = npcap_key.get_value("").unwrap_or(String::new()); - true -} - -/// Check if npcap is installed. -/// This function only check if npcap is installed, not check version. -pub fn npcap_installed() -> bool { - software_installed(NPCAP_SOFTWARE_NAME.to_owned()) -} diff --git a/src/dns/domain.rs b/src/dns/domain.rs deleted file mode 100644 index f8b7e7f..0000000 --- a/src/dns/domain.rs +++ /dev/null @@ -1,21 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::net::IpAddr; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct CertEntry { - pub id: u64, - pub issuer_ca_id: u32, - pub issuer_name: String, - pub common_name: String, - pub name_value: String, - pub not_before: String, - pub not_after: String, - pub serial_number: String, - pub entry_timestamp: String, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Domain { - pub domain_name: String, - pub ips: Vec, -} diff --git a/src/dns/mod.rs b/src/dns/mod.rs index 0b3da16..475902c 100644 --- a/src/dns/mod.rs +++ b/src/dns/mod.rs @@ -1,300 +1,70 @@ -pub mod domain; -pub mod result; -pub mod scanner; -use std::net::IpAddr; -use std::time::Duration; +use std::{net::IpAddr, time::Duration}; +use anyhow::Result; +use serde::{Deserialize, Serialize}; -#[cfg(not(any(unix, target_os = "windows")))] -use hickory_resolver::config::{ResolverConfig, ResolverOpts}; -use hickory_resolver::Resolver; +use crate::endpoint::Host; -use futures::stream::{self, StreamExt}; +pub mod resolver; +pub mod probe; -use hickory_resolver::AsyncResolver; -use std::collections::HashMap; -use std::str::FromStr; -use std::thread; - -#[cfg(not(target_os = "windows"))] -const DEFAULT_TIMEOUT: Duration = Duration::from_millis(200); -#[cfg(not(target_os = "windows"))] -const DEFAULT_TIMEOUT_GLOBAL: Duration = Duration::from_millis(1000); -#[cfg(target_os = "windows")] -const DEFAULT_TIMEOUT: Duration = Duration::from_millis(20); -#[cfg(target_os = "windows")] -const DEFAULT_TIMEOUT_GLOBAL: Duration = Duration::from_millis(1000); - -pub fn lookup_host_name(host_name: &str) -> Option { - let ip_vec: Vec = resolve_domain(host_name); - let mut ipv6_vec: Vec = vec![]; - for ip in ip_vec { - match ip { - IpAddr::V4(_) => { - return Some(ip); - } - IpAddr::V6(_) => { - ipv6_vec.push(ip); - } - } - } - if ipv6_vec.len() > 0 { - return Some(ipv6_vec[0]); - } else { - None - } -} - -pub async fn lookup_host_name_async(host_name: String) -> Option { - let ip_vec: Vec = resolve_domain_async(host_name).await; - let mut ipv6_vec: Vec = vec![]; - for ip in ip_vec { - match ip { - IpAddr::V4(_) => { - return Some(ip); - } - IpAddr::V6(_) => { - ipv6_vec.push(ip); - } - } - } - if ipv6_vec.len() > 0 { - return Some(ipv6_vec[0]); - } else { - None - } -} - -pub fn lookup_ip_addr(ip_addr: &IpAddr) -> Option { - let names: Vec = resolve_ip(ip_addr); - if names.len() > 0 { - return Some(names[0].clone()); - } else { - return None; - } -} - -pub async fn lookup_ip_addr_async(ip_addr: String) -> String { - let ips: Vec = resolve_ip_async(ip_addr).await; - if ips.len() > 0 { - return ips[0].clone(); - } else { - return String::new(); - } -} - -#[cfg(any(unix, target_os = "windows"))] -fn resolve_domain(host_name: &str) -> Vec { - let mut ips: Vec = vec![]; - let resolver = Resolver::from_system_conf().unwrap(); - match resolver.lookup_ip(host_name) { - Ok(lip) => { - for ip in lip.iter() { - ips.push(ip); - } - } - Err(_) => {} - } - ips -} - -#[cfg(not(any(unix, target_os = "windows")))] -fn resolve_domain(host_name: &str) -> Vec { - let mut ips: Vec = vec![]; - let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap(); - match resolver.lookup_ip(host_name) { - Ok(lip) => { - for ip in lip.iter() { - ips.push(ip); - } - } - Err(_) => {} - } - ips -} - -#[cfg(any(unix, target_os = "windows"))] -fn resolve_ip(ip_addr: &IpAddr) -> Vec { - let mut names: Vec = vec![]; - let mut system_conf = hickory_resolver::system_conf::read_system_conf().unwrap(); - if crate::ip::is_global_addr(ip_addr) { - system_conf.1.timeout = DEFAULT_TIMEOUT_GLOBAL; +/// Lookup a host by name or IP address string. +pub async fn lookup_host(host: &str, timeout: Duration) -> Result { + if let Ok(ip) = host.parse::() { + // Reverse lookup for IP address + let hostname = reverse_lookup(ip, timeout).await.unwrap_or_else(|| ip.to_string()); + Ok(Host { hostname: Some(hostname), ip: ip }) } else { - system_conf.1.timeout = DEFAULT_TIMEOUT; - } - let resolver = Resolver::new(system_conf.0, system_conf.1).unwrap(); - match resolver.reverse_lookup(*ip_addr) { - Ok(rlookup) => { - for record in rlookup.as_lookup().record_iter() { - match record.data() { - Some(data) => { - let name = data.to_string(); - if name.ends_with(".") { - names.push(name[0..name.len() - 1].to_string()); - } else { - names.push(name); - } - } - None => {} - } - } - names - } - Err(_) => { - return names; - } - } -} - -#[cfg(not(any(unix, target_os = "windows")))] -fn resolve_ip(ip_addr: IpAddr) -> Vec { - let mut names: Vec = vec![]; - let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default()).unwrap(); - match resolver.reverse_lookup(ip_addr) { - Ok(rlookup) => { - for record in rlookup.as_lookup().record_iter() { - match record.data() { - Some(data) => { - let name = data.to_string(); - if name.ends_with(".") { - names.push(name[0..name.len() - 1].to_string()); - } else { - names.push(name); - } - } - None => {} - } - } - names - } - Err(_) => { - return names; + // Resolve hostname to IP address + let ips = lookup_ip(host, timeout).await.unwrap_or_default(); + match ips.first() { + Some(ip) => Ok(Host { hostname: Some(host.to_string()), ip: *ip }), + None => Err(anyhow::anyhow!("failed to resolve host")), } } } -#[cfg(any(unix, target_os = "windows"))] -async fn resolve_domain_async(host_name: String) -> Vec { - let mut ips: Vec = vec![]; - let resolver = AsyncResolver::tokio_from_system_conf().unwrap(); - match resolver.lookup_ip(host_name).await { - Ok(lip) => { - for ip in lip.iter() { - ips.push(ip); - } - } - Err(_) => {} - } - ips +/// Lookup a domain and return its associated IP addresses. +pub async fn lookup_domain(hostname: &str, timeout: Duration) -> Domain { + let ips = lookup_ip(hostname, timeout).await.unwrap_or_default(); + Domain { name: hostname.to_string(), ips } } -#[cfg(not(any(unix, target_os = "windows")))] -async fn resolve_domain_async(host_name: String) -> Vec { - let mut ips: Vec = vec![]; - let resolver = - AsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default()).unwrap(); - match resolver.lookup_ip(host_name).await { - Ok(lip) => { - for ip in lip.iter() { - ips.push(ip); - } - } - Err(_) => {} +/// Perform a DNS lookup for the given hostname with a timeout. +pub async fn lookup_ip(hostname: &str, timeout: Duration) -> Option> { + let resolver = resolver::get_resolver().ok()?; + match tokio::time::timeout( + timeout, + async move { resolver.lookup_ip(hostname).await } + ).await { + Ok(Ok(ips)) => Some(ips.iter().collect()), + _ => None, } - ips } -#[cfg(any(unix, target_os = "windows"))] -async fn resolve_ip_async(ip_addr: String) -> Vec { - let ip_addr: IpAddr = IpAddr::from_str(ip_addr.as_str()).unwrap(); - let mut names: Vec = vec![]; - let mut system_conf = hickory_resolver::system_conf::read_system_conf().unwrap(); - if crate::ip::is_global_addr(&ip_addr) { - system_conf.1.timeout = DEFAULT_TIMEOUT_GLOBAL; - } else { - system_conf.1.timeout = DEFAULT_TIMEOUT; +/// Perform a reverse DNS lookup for the given IP address with a timeout. +pub async fn reverse_lookup(ip: IpAddr, timeout: Duration) -> Option { + let resolver = resolver::get_resolver().ok()?; + match tokio::time::timeout( + timeout, + async move { resolver.reverse_lookup(ip).await } + ).await { + Ok(Ok(names)) => names.iter().next().map(|n| n.to_string()), + _ => None, } - let resolver = AsyncResolver::tokio(system_conf.0, system_conf.1); - match resolver.reverse_lookup(ip_addr).await { - Ok(rlookup) => { - for record in rlookup.as_lookup().record_iter() { - match record.data() { - Some(data) => { - let name = data.to_string(); - if name.ends_with(".") { - names.push(name[0..name.len() - 1].to_string()); - } else { - names.push(name); - } - } - None => {} - } - } - names - } - Err(_) => { - return names; - } - } -} - -#[cfg(not(any(unix, target_os = "windows")))] -async fn resolve_ip_async(ip_addr: String) -> Vec { - let mut names: Vec = vec![]; - let resolver = - AsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default()).unwrap(); - match resolver - .reverse_lookup(IpAddr::from_str(ip_addr.as_str()).unwrap()) - .await - { - Ok(rlookup) => { - for record in rlookup.as_lookup().record_iter() { - match record.data() { - Some(data) => { - let name = data.to_string(); - if name.ends_with(".") { - names.push(name[0..name.len() - 1].to_string()); - } else { - names.push(name); - } - } - None => {} - } - } - names - } - Err(_) => { - return names; - } - } -} - -pub async fn lookup_ips_async(ips: Vec) -> HashMap { - let mut tasks = stream::iter(ips) - .map(|ip| async move { - let names = resolve_ip_async(ip.to_string()).await; - (ip, names) - }) - .buffer_unordered(10); - let mut results: HashMap = HashMap::new(); - while let Some(result) = tasks.next().await { - results.insert( - result.0, - result.1.first().unwrap_or(&String::new()).to_string(), - ); - } - results -} - -pub fn lookup_ips(ips: Vec) -> HashMap { - let rt: tokio::runtime::Runtime = tokio::runtime::Runtime::new().unwrap(); - let handle = thread::spawn(move || rt.block_on(async { lookup_ips_async(ips).await })); - handle.join().unwrap() } -pub fn lookup_host(host: &str) -> Vec { - resolve_domain(host) +/// A domain with its associated IP addresses +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Domain { + pub name: String, + pub ips: Vec, } -pub fn lookup_addr(addr: &IpAddr) -> Vec { - resolve_ip(addr) +/// Result of domain scan +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct DomainScanResult { + /// List of scanned domains + pub domains: Vec, + /// Time from start to end of scan. + pub scan_time: Duration, } diff --git a/src/dns/probe.rs b/src/dns/probe.rs new file mode 100644 index 0000000..90ef6f2 --- /dev/null +++ b/src/dns/probe.rs @@ -0,0 +1,158 @@ +use futures::stream::{self, StreamExt}; +use rand::{distributions::Alphanumeric, Rng}; +use tokio::time::timeout; +use std::sync::Arc; +use std::{net::IpAddr, time::Instant}; +use std::time::Duration; +use anyhow::Result; +use tracing_indicatif::span_ext::IndicatifSpanExt; + +use crate::dns::{Domain, DomainScanResult}; + +/// Settings for domain scanning +pub struct DomainScanSetting { + /// Base Domain Name of scan target. + pub base_domain: String, + /// Word-list of name + pub word_list: Vec, + /// Timeout setting of domain scan. + pub timeout: Duration, + /// Resolve timeout setting of domain scan. + pub resolve_timeout: Duration, + /// Concurrent limit of domain scan. + pub concurrent_limit: usize, +} + +impl DomainScanSetting { + /// Create a new DomainScanSetting with default timeouts and concurrency. + pub fn new(base_domain: String, word_list: Vec) -> Self { + Self { + base_domain, + word_list, + timeout: Duration::from_secs(10), + resolve_timeout: Duration::from_secs(2), + concurrent_limit: 100, + } + } +} + +/// Domain Scanner +pub struct DomainScanner { + pub settings: DomainScanSetting, +} + +impl DomainScanner { + /// Create a new DomainScanner with the given settings. + pub fn new(settings: DomainScanSetting) -> Self { + Self { settings } + } + /// Run the domain scan and return the results. + pub async fn run(&self) -> Result { + scan_subdomain(&self.settings).await + } +} + +/// Normalize a domain label by trimming trailing dots and converting to lowercase. +fn normalize_label(s: &str) -> String { + s.trim_end_matches('.').to_ascii_lowercase() +} + +/// Check if the base domain has wildcard DNS records. +async fn is_wildcard_domain(resolver: &hickory_resolver::TokioResolver, base: &str, rt: Duration) -> bool { + let rand_label: String = rand::thread_rng() + .sample_iter(&Alphanumeric).take(10).map(char::from).collect(); + let test = format!("{}.{}", rand_label, base); + match timeout(rt, resolver.lookup_ip(test)).await { + Ok(Ok(lip)) => !lip.as_lookup().is_empty(), + _ => false, + } +} + +/// Scan subdomains based on the provided settings. +pub async fn scan_subdomain(setting: &DomainScanSetting) -> Result { + let base = normalize_label(&setting.base_domain); + + let target_domains: Vec = setting.word_list + .iter() + .map(|w| format!("{}.{}", normalize_label(w), base)) + .collect(); + + let resolver = Arc::new(super::resolver::get_resolver()?); + + let wildcard = is_wildcard_domain(&resolver, &base, setting.resolve_timeout).await; + + let header_span = tracing::info_span!("subdomain_scan"); + header_span.pb_set_style(&crate::output::progress::get_progress_style()); + header_span.pb_set_message(&format!("Subdomain Scan ({})", base)); + header_span.pb_set_length(target_domains.len() as u64); + header_span.pb_set_position(0); + header_span.pb_start(); + + let start_time = Instant::now(); + + // Parallel resolution stream + let results = stream::iter(target_domains) + .map(|domain_name| { + let resolver = resolver.clone(); + let rt = setting.resolve_timeout; + async move { + let mut d = Domain { name: domain_name.clone(), ips: Vec::new() }; + match timeout(rt, resolver.lookup_ip(domain_name)).await { + Ok(Ok(lip)) => { + let mut uniq: Vec = lip.iter().collect(); + uniq.sort(); + uniq.dedup(); + d.ips = uniq; + } + _ => {} + } + d + } + }) + .buffer_unordered(setting.concurrent_limit); + + // Collect results with timeout handling + tokio::pin!(results); + let deadline = start_time + setting.timeout; + let mut domains: Vec = Vec::new(); + + loop { + let now = Instant::now(); + if now >= deadline { break; } + let remaining = deadline - now; + + tokio::select! { + _ = tokio::time::sleep(remaining) => { + // Deadline reached: return what has been collected so far + break; + } + maybe = results.next() => { + match maybe { + Some(d) => { + if !d.ips.is_empty() { + domains.push(d); + } + header_span.pb_inc(1); + } + None => break, // All done + } + } + } + } + + drop(header_span); + + if wildcard { + if let Some(first) = domains.first().map(|d| d.ips.clone()) { + let all_same = domains.iter().all(|d| d.ips == first); + if all_same { domains.clear(); } + } + } + + domains.sort_by(|a, b| a.name.cmp(&b.name)); + + Ok(DomainScanResult { + domains, + scan_time: start_time.elapsed().min(setting.timeout), + }) +} diff --git a/src/dns/resolver.rs b/src/dns/resolver.rs new file mode 100644 index 0000000..6e1592c --- /dev/null +++ b/src/dns/resolver.rs @@ -0,0 +1,19 @@ +use anyhow::Result; +use hickory_resolver::TokioResolver; + +/// Get a DNS resolver instance +#[cfg(any(unix, target_os = "windows"))] +pub fn get_resolver() -> Result { + // Use system DNS configuration + match TokioResolver::builder_tokio() { + Ok(resolver) => Ok(resolver.build()), + Err(e) => Err(anyhow::anyhow!("Failed to create TokioAsyncResolver: {}", e)), + } +} + +#[cfg(not(any(unix, target_os = "windows")))] +pub fn get_resolver() -> Result { + use hickory_resolver::name_server::TokioConnectionProvider; + let builder = TokioResolver::builder_with_config(ResolverConfig::default(), TokioConnectionProvider::default()); + return Ok(builder.build()); +} diff --git a/src/dns/result.rs b/src/dns/result.rs deleted file mode 100644 index 8588f16..0000000 --- a/src/dns/result.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::scan::result::ScanStatus; - -use super::domain::Domain; -use serde::{Deserialize, Serialize}; -use std::time::Duration; - -/// Result of domain scan -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DomainScanResult { - /// HashMap of domain. - /// - /// (Domain, IP Addresses) - pub domains: Vec, - /// Time from start to end of scan. - pub scan_time: Duration, - /// Scan job status - pub scan_status: ScanStatus, -} - -impl DomainScanResult { - pub fn new() -> DomainScanResult { - DomainScanResult { - domains: vec![], - scan_time: Duration::from_millis(0), - scan_status: ScanStatus::Error(String::from("Scan not started")), - } - } -} diff --git a/src/dns/scanner.rs b/src/dns/scanner.rs deleted file mode 100644 index b7d5e1e..0000000 --- a/src/dns/scanner.rs +++ /dev/null @@ -1,210 +0,0 @@ -use super::domain::Domain; -use super::result::DomainScanResult; -use futures::{stream, StreamExt}; -use std::net::IpAddr; -use std::sync::mpsc::{channel, Receiver, Sender}; -use std::sync::{Arc, Mutex}; -use std::time::{Duration, Instant}; -use tokio::time::timeout; - -#[cfg(not(any(unix, target_os = "windows")))] -use hickory_resolver::config::{ResolverConfig, ResolverOpts}; -use hickory_resolver::AsyncResolver; - -use crate::scan::result::ScanStatus; - -/// Structure for domain scan -/// -/// Should be constructed using DomainScanner::new -#[derive(Clone)] -pub struct DomainScanner { - /// Base Domain Name of scan target. - pub base_domain: String, - /// Word-list of name - pub word_list: Vec, - /// Timeout setting of domain scan. - pub timeout: Duration, - /// Resolve timeout setting of domain scan. - pub resolve_timeout: Duration, - /// Concurrent limit of domain scan. - pub concurrent_limit: usize, - /// Result of domain scan. - pub scan_result: DomainScanResult, - /// Sender for progress messaging - tx: Arc>>, - /// Receiver for progress messaging - rx: Arc>>, -} - -impl DomainScanner { - /// Construct new UriScanner - pub fn new() -> Result { - let (tx, rx) = channel(); - let domain_scanner = DomainScanner { - base_domain: String::new(), - word_list: vec![], - timeout: Duration::from_millis(30000), - resolve_timeout: Duration::from_millis(1000), - concurrent_limit: 100, - scan_result: DomainScanResult::new(), - tx: Arc::new(Mutex::new(tx)), - rx: Arc::new(Mutex::new(rx)), - }; - Ok(domain_scanner) - } - /// Set base Domain of scan target. - pub fn set_base_domain(&mut self, base_domain: String) { - self.base_domain = base_domain; - } - /// Add word to word-list - pub fn add_word(&mut self, word: String) { - self.word_list.push(word); - } - /// Set word-list - pub fn set_word_list(&mut self, word_list: Vec<&str>) { - self.word_list.clear(); - for word in word_list { - self.word_list.push(word.to_string()) - } - } - /// Set scan timeout - pub fn set_timeout(&mut self, timeout: Duration) { - self.timeout = timeout; - } - async fn scan_domain(&self) -> Result, ()> { - match timeout( - self.timeout, - scan_subdomain( - self.base_domain.clone(), - self.word_list.clone(), - &self.tx, - self.resolve_timeout, - self.concurrent_limit, - ), - ) - .await - { - Ok(domains) => { - return Ok(domains); - } - Err(_) => { - return Err(()); - } - } - } - /// Run scan with current settings. - /// - /// Results are stored in DomainScanner::scan_result - pub async fn run_scan(&mut self) { - let start_time = Instant::now(); - let res = self.scan_domain().await; - match res { - Ok(domains) => { - self.scan_result.domains = domains; - self.scan_result.scan_status = ScanStatus::Done; - } - Err(_) => { - self.scan_result.scan_status = ScanStatus::Timeout; - } - } - self.scan_result.scan_time = Instant::now().duration_since(start_time); - } - /// Return scan result. - pub fn get_result(&mut self) -> DomainScanResult { - return self.scan_result.clone(); - } - /// Run scan and return result - pub async fn scan(&mut self) -> DomainScanResult { - self.run_scan().await; - self.scan_result.clone() - } - /// Get progress receiver - pub fn get_progress_receiver(&self) -> Arc>> { - self.rx.clone() - } -} - -#[cfg(any(unix, target_os = "windows"))] -async fn resolve_domain(host_name: String) -> Vec { - let mut ips: Vec = vec![]; - let resolver = AsyncResolver::tokio_from_system_conf().unwrap(); - match resolver.lookup_ip(host_name).await { - Ok(lip) => { - for ip in lip.iter() { - ips.push(ip); - } - } - Err(_) => {} - } - ips -} - -#[cfg(not(any(unix, target_os = "windows")))] -async fn resolve_domain(host_name: String) -> Vec { - let mut ips: Vec = vec![]; - let resolver = - AsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default()).unwrap(); - match resolver.lookup_ip(host_name).await { - Ok(lip) => { - for ip in lip.iter() { - ips.push(ip); - } - } - Err(_) => {} - } - ips -} - -async fn scan_subdomain( - base_domain: String, - word_list: Vec, - ptx: &Arc>>, - resolve_timeout: Duration, - concurrent_limit: usize, -) -> Vec { - let mut result: Vec = vec![]; - let scan_results: Arc>> = Arc::new(Mutex::new(vec![])); - let mut target_domains: Vec = vec![]; - for word in word_list { - target_domains.push(format!("{}.{}", word, base_domain)); - } - let results = stream::iter(target_domains) - .map(|domain| async move { - let mut d: Domain = Domain { - domain_name: domain.clone(), - ips: vec![], - }; - match timeout(resolve_timeout, resolve_domain(domain.clone())).await { - Ok(ips) => { - d.ips = ips; - match ptx.lock() { - Ok(lr) => match lr.send(domain) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - } - Err(_) => match ptx.lock() { - Ok(lr) => match lr.send(domain) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - }, - } - d - }) - .buffer_unordered(concurrent_limit); - results - .for_each(|domain| async { - if domain.ips.len() > 0 { - scan_results.lock().unwrap().push(domain); - } - }) - .await; - for domain in scan_results.lock().unwrap().iter() { - result.push(domain.to_owned()); - } - result -} diff --git a/src/endpoint.rs b/src/endpoint.rs new file mode 100644 index 0000000..9257c5a --- /dev/null +++ b/src/endpoint.rs @@ -0,0 +1,496 @@ +use std::net::{IpAddr, SocketAddr}; +use std::collections::BTreeMap; +use netdev::MacAddr; +use serde::{Serialize, Deserialize}; + +mod ports_vec { + use super::*; + use serde::{Serializer, Deserializer}; + + #[derive(Serialize, Deserialize)] + struct Item { + pub port: Port, + #[serde(flatten)] + pub rest: PortResult, + } + + pub fn serialize(map: &BTreeMap, s: S) -> Result + where S: Serializer { + let vec: Vec<&PortResult> = map.values().collect(); + vec.serialize(s) + } + + pub fn deserialize<'de, D>(d: D) -> Result, D::Error> + where D: Deserializer<'de> { + let vec = >::deserialize(d)?; + Ok(vec.into_iter().map(|pr| (pr.port, pr)).collect()) + } +} + +/// Transport protocol type +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Ord, PartialOrd)] +#[serde(rename_all = "lowercase")] +pub enum TransportProtocol { + Tcp, + Udp, + Quic, +} + +impl TransportProtocol { + /// Create a TransportProtocol from a string representation. + pub fn from_str(s: &str) -> Option { + match s.to_lowercase().as_str() { + "tcp" => Some(TransportProtocol::Tcp), + "udp" => Some(TransportProtocol::Udp), + "quic" => Some(TransportProtocol::Quic), + _ => None, + } + } + /// Get the string representation of the TransportProtocol. + pub fn as_str(&self) -> &'static str { + match self { + TransportProtocol::Tcp => "tcp", + TransportProtocol::Udp => "udp", + TransportProtocol::Quic => "quic", + } + } +} + +/// Network port with transport protocol +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Ord, PartialOrd)] +pub struct Port { + pub number: u16, + pub transport: TransportProtocol, +} + +impl Port { + /// Create a new Port instance. + pub fn new(number: u16, transport: TransportProtocol) -> Self { + Self { number, transport } + } + /// Get the SocketAddr for the given IP address and this port. + pub fn socket_addr(&self, ip: IpAddr) -> SocketAddr { + SocketAddr::new(ip, self.number) + } +} + +impl From<(u16, TransportProtocol)> for Port { + fn from(t: (u16, TransportProtocol)) -> Self { Self { number: t.0, transport: t.1 } } +} +impl std::fmt::Display for Port { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}:{}", match self.transport { TransportProtocol::Tcp=>"tcp", TransportProtocol::Udp=>"udp", TransportProtocol::Quic=>"quic" }, self.number) + } +} + +/// Port state +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum PortState { + Open, + Closed, + Filtered, +} + +impl PortState { + /// Create a PortState from a string representation. + pub fn from_str(s: &str) -> Option { + match s.to_lowercase().as_str() { + "open" => Some(PortState::Open), + "closed" => Some(PortState::Closed), + "filtered" => Some(PortState::Filtered), + _ => None, + } + } + /// Get the string representation of the PortState. + pub fn as_str(&self) -> &'static str { + match self { + PortState::Open => "open", + PortState::Closed => "closed", + PortState::Filtered => "filtered", + } + } +} + +/// Node type +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub enum NodeType { + Gateway, + Hop, + Destination, +} + +impl NodeType { + /// Create a NodeType from a string representation. + pub fn from_str(s: &str) -> Option { + match s.to_lowercase().as_str() { + "gateway" => Some(NodeType::Gateway), + "hop" => Some(NodeType::Hop), + "destination" => Some(NodeType::Destination), + _ => None, + } + } + /// Get the string representation of the NodeType. + pub fn as_str(&self) -> &'static str { + match self { + NodeType::Gateway => "gateway", + NodeType::Hop => "hop", + NodeType::Destination => "destination", + } + } + /// Get the display name of the NodeType. + pub fn name(&self) -> String { + match *self { + NodeType::Gateway => String::from("Gateway"), + NodeType::Hop => String::from("Hop"), + NodeType::Destination => String::from("Destination"), + } + } +} + +/// Service information detected on a port +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct ServiceInfo { + pub name: Option, + pub product: Option, + pub version: Option, + pub quic_version: Option, + pub banner: Option, + pub raw: Option, + pub cpes: Vec, + pub tls_info: Option, +} + +/// TLS information extracted from a TLS handshake +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct TlsInfo { + pub version: Option, + pub cipher_suite: Option, + pub alpn: Option, + pub sni: Option, + pub subject: Option, + pub issuer: Option, + /// Not before date in RFC2822 format + pub not_before: Option, + /// Not after date in RFC2822 format + pub not_after: Option, + pub san_list: Vec, + pub serial_hex: Option, + /// Signature algorithm name + pub sig_algorithm: Option, + /// Public key algorithm name + pub pubkey_algorithm: Option, +} + +/// Result of probing a specific port +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PortResult { + pub port: Port, + pub state: PortState, + pub rtt_ms: Option, + #[serde(default)] + pub service: ServiceInfo, +} + +/// OS guess information +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct OsGuess { + pub family: Option, + pub confidence: Option, + pub ttl_observed: Option, +} + +impl OsGuess { + pub fn with_family(self, family: String) -> Self { + Self { + family: Some(family), + confidence: None, + ttl_observed: None, + } + } + pub fn with_confidence(self, confidence: f32) -> Self { + Self { + family: self.family, + confidence: Some(confidence), + ttl_observed: self.ttl_observed, + } + } + pub fn with_ttl_observed(self, ttl: u8) -> Self { + Self { + family: self.family, + confidence: self.confidence, + ttl_observed: Some(ttl), + } + } +} + +/// Representation of a host (IP address and optional hostname) +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Host { + pub ip: IpAddr, + pub hostname: Option, +} + +impl Default for Host { + fn default() -> Self { + Self { + ip: IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED), + hostname: None, + } + } +} + +impl Host { + /// Create a new Host instance. + pub fn new(ip: IpAddr) -> Self { + Self { ip, ..Default::default() } + } + /// Create a new Host instance with the specified hostname. + pub fn with_hostname(ip: IpAddr, hostname: String) -> Self { + Self { ip, hostname: Some(hostname), ..Default::default() } + } +} + +/// Representation of an endpoint with IP, hostname, MAC address, tags, and ports +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Endpoint { + pub ip: IpAddr, + pub hostname: Option, + pub mac_addr: Option, + pub tags: Vec, + pub ports: Vec, +} + +impl Default for Endpoint { + fn default() -> Self { + Self { + ip: IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED), + hostname: None, + mac_addr: None, + tags: Vec::new(), + ports: Vec::new(), + } + } +} + +impl Endpoint { + /// Create a new Endpoint instance. + pub fn new(ip: IpAddr) -> Self { + Self { ip, ..Default::default() } + } + /// Create a new Endpoint instance with the specified hostname. + pub fn with_hostname(ip: IpAddr, hostname: String) -> Self { + Self { ip, hostname: Some(hostname), ..Default::default() } + } + /// Add a port to the endpoint if it does not already exist. + pub fn upsert_port(&mut self, port: Port) { + if !self.ports.contains(&port) { + self.ports.push(port); + } + } + /// Merge another Endpoint into this one, combining tags and ports. + pub fn merge(&mut self, other: Endpoint) { + if self.hostname.is_none() { self.hostname = other.hostname; } + if self.mac_addr.is_none() { self.mac_addr = other.mac_addr; } + + for t in other.tags { + if !self.tags.contains(&t) { + self.tags.push(t); + } + } + + for p in other.ports { + if !self.ports.contains(&p) { + self.ports.push(p); + } + } + } + /// Get the SocketAddr instances for the specified transport protocol. + pub fn socket_addrs(&self, transport: TransportProtocol) -> Vec { + self.ports + .iter() + .filter(|p| p.transport == transport) + .map(|p| p.socket_addr(self.ip)) + .collect() + } +} + +/// Result of scanning an endpoint, including ports and OS guess +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct EndpointResult { + pub ip: IpAddr, + pub hostname: Option, + pub mac_addr: Option, + pub vendor_name: Option, + pub os: OsGuess, + #[serde(default)] + pub tags: Vec, + #[serde(default, with = "ports_vec")] + pub ports: BTreeMap, + pub cpes: Vec, +} + +impl Default for EndpointResult { + fn default() -> Self { + Self { + ip: IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED), + hostname: None, + mac_addr: None, + vendor_name: None, + os: OsGuess::default(), + tags: Vec::new(), + ports: BTreeMap::new(), + cpes: Vec::new(), + } + } +} + +impl EndpointResult { + /// Create a new EndpointResult instance. + pub fn new(ip: IpAddr) -> Self { + Self { ip, ..Default::default() } + } + /// Create a new EndpointResult instance with the specified hostname. + pub fn with_hostname(ip: IpAddr, hostname: String) -> Self { + Self { ip, hostname: Some(hostname), ..Default::default() } + } + /// Add or update a PortResult in the endpoint's ports map. + pub fn upsert_port(&mut self, pr: PortResult) { + self.ports.insert(pr.port, pr); + } + /// Merge another EndpointResult into this one, combining tags, ports, and OS guess. + pub fn merge(&mut self, other: EndpointResult) { + if self.hostname.is_none() { self.hostname = other.hostname; } + if self.mac_addr.is_none() { self.mac_addr = other.mac_addr; } + if self.vendor_name.is_none() { self.vendor_name = other.vendor_name; } + + //self.cpes = other.cpes; + let incoming: Vec = other + .cpes + .into_iter() + .filter(|c| !self.cpes.contains(c)) + .collect(); + + self.cpes.extend(incoming); + + if other.os.confidence.unwrap_or(0.0) > self.os.confidence.unwrap_or(0.0) { + self.os = other.os; + } else if self.os.ttl_observed.is_none() && other.os.ttl_observed.is_some() { + self.os.ttl_observed = other.os.ttl_observed; + } + + for t in other.tags { + if !self.tags.contains(&t) { + self.tags.push(t); + } + } + + for (k, v) in other.ports { + // check service data exists + if let Some(existing) = self.ports.get_mut(&k) { + if existing.service.banner.is_none() { + self.ports.insert(k, v); + } + } else { + self.ports.insert(k, v); + } + } + } + /// Get the SocketAddr instances for the specified transport protocol. + pub fn socket_addrs(&self, transport: TransportProtocol) -> Vec { + self.ports + .keys() + .filter(|p| p.transport == transport) + .map(|p| p.socket_addr(self.ip)) + .collect() + } + /// Convert to a simpler Endpoint representation. + pub fn to_endpoint(&self) -> Endpoint { + Endpoint { + ip: self.ip, + hostname: self.hostname.clone(), + mac_addr: self.mac_addr, + tags: self.tags.clone(), + ports: self.ports.keys().cloned().collect(), + } + } + /// Get a list of open ports. + pub fn get_open_ports(&self) -> Vec { + self.ports + .iter() + .filter(|(_, v)| v.state == PortState::Open) + .map(|(k, _)| *k) + .collect() + } + /// Get an active Endpoint if there are any open ports. + pub fn active_endpoint(&self) -> Option { + let open_ports = self.get_open_ports(); + if open_ports.is_empty() { + None + } else { + Some(Endpoint { + ip: self.ip, + hostname: self.hostname.clone(), + mac_addr: self.mac_addr, + tags: self.tags.clone(), + ports: open_ports, + }) + } + } +} + +impl From for EndpointResult { + fn from(ip: IpAddr) -> Self { EndpointResult::new(ip) } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::{to_string_pretty, from_str}; + + #[test] + fn ports_roundtrip() { + let mut ep = EndpointResult::new("93.184.216.34".parse().unwrap()); + ep.upsert_port(PortResult { + port: Port::new(80, TransportProtocol::Tcp), + state: PortState::Open, + rtt_ms: Some(23), + service: ServiceInfo { + name: Some("http".into()), + ..Default::default() + }, + }); + ep.upsert_port(PortResult { + port: Port::new(443, TransportProtocol::Tcp), + state: PortState::Open, + rtt_ms: None, + service: ServiceInfo { + name: Some("https".into()), + tls_info: Some(TlsInfo { + version: Some("TLS 1.2".into()), + cipher_suite: Some("TLS_AES_128_GCM_SHA256".into()), + alpn: Some("h2".into()), + sni: Some("example.com".into()), + subject: Some("CN=example.com".into()), + issuer: Some("CN=Example CA".into()), + not_before: Some("2023-01-01T00:00:00Z".into()), + not_after: Some("2024-01-01T00:00:00Z".into()), + san_list: vec!["example.com".into(), "www.example.com".into()], + serial_hex: Some("1234567890abcdef".into()), + sig_algorithm: Some("sha256WithRSAEncryption".into()), + pubkey_algorithm: Some("RSA".into()), + }), + ..Default::default() + }, + }); + + let json = to_string_pretty(&ep).unwrap(); + + assert!(json.contains("\"ports\": [")); + + let back: EndpointResult = from_str(&json).unwrap(); + assert_eq!(back.ports.len(), 2); + assert!(back.ports.contains_key(&Port::new(80, TransportProtocol::Tcp))); + assert!(back.ports.contains_key(&Port::new(443, TransportProtocol::Tcp))); + } +} diff --git a/src/fp/mod.rs b/src/fp/mod.rs deleted file mode 100644 index 792f3ae..0000000 --- a/src/fp/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod setting; diff --git a/src/fp/setting.rs b/src/fp/setting.rs deleted file mode 100644 index d98f900..0000000 --- a/src/fp/setting.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::protocol::Protocol; -use serde::{Deserialize, Serialize}; -use std::net::IpAddr; - -#[derive(Deserialize, Serialize, Clone, Copy, Debug, PartialEq, Eq)] -pub enum FingerprintType { - IcmpEcho, - IcmpTimestamp, - IcmpAddressMask, - IcmpInformation, - IcmpUnreachable, - TcpSynAck, - TcpRstAck, - TcpEcn, -} - -impl FingerprintType { - pub fn protocol(&self) -> Protocol { - match self { - FingerprintType::IcmpEcho => Protocol::ICMP, - FingerprintType::IcmpTimestamp => Protocol::ICMP, - FingerprintType::IcmpAddressMask => Protocol::ICMP, - FingerprintType::IcmpInformation => Protocol::ICMP, - FingerprintType::IcmpUnreachable => Protocol::UDP, - FingerprintType::TcpSynAck => Protocol::TCP, - FingerprintType::TcpRstAck => Protocol::TCP, - FingerprintType::TcpEcn => Protocol::TCP, - } - } -} - -#[derive(Deserialize, Serialize, Clone, Debug)] -pub struct FingerprintSetting { - pub if_index: u32, - pub dst_hostname: String, - pub dst_ip: IpAddr, - pub protocol: Protocol, - pub fingerprint_type: FingerprintType, - pub count: u32, - pub receive_timeout: u64, - pub probe_timeout: u64, -} - -impl Default for FingerprintSetting { - fn default() -> Self { - Self { - if_index: 0, - dst_hostname: "localhost".to_string(), - dst_ip: IpAddr::V4(std::net::Ipv4Addr::LOCALHOST), - protocol: Protocol::ICMP, - fingerprint_type: FingerprintType::IcmpEcho, - count: 1, - receive_timeout: 1000, - probe_timeout: 30000, - } - } -} diff --git a/src/fs.rs b/src/fs.rs deleted file mode 100644 index 9a35ff9..0000000 --- a/src/fs.rs +++ /dev/null @@ -1,6 +0,0 @@ -use std::fs; -use std::path::PathBuf; - -pub fn save_text(file_path: &PathBuf, contents_text: String) -> Result<(), std::io::Error> { - fs::write(file_path, contents_text) -} diff --git a/src/handler/check.rs b/src/handler/check.rs deleted file mode 100644 index 7ad01dd..0000000 --- a/src/handler/check.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::dep; -use clap::ArgMatches; - -pub fn check_dependencies(_arg: &ArgMatches) { - match dep::check_dependencies() { - Ok(_) => { - println!("All dependencies are installed."); - std::process::exit(0); - } - Err(e) => { - println!("Error: {}", e); - std::process::exit(1); - } - } -} diff --git a/src/handler/dns.rs b/src/handler/dns.rs deleted file mode 100644 index a8b2529..0000000 --- a/src/handler/dns.rs +++ /dev/null @@ -1,191 +0,0 @@ -use crate::db; -use crate::dns::domain::Domain; -use crate::dns::{result::DomainScanResult, scanner::DomainScanner}; -use crate::util::tree::node_label; -use clap::ArgMatches; -use indicatif::{ProgressBar, ProgressDrawTarget}; -use std::path::PathBuf; -use std::{thread, time::Duration}; -use termtree::Tree; -use tokio::runtime::Runtime; - -use crate::output; - -pub fn handle_subdomain_scan(args: &ArgMatches) { - output::log_with_time("Initiating subdomain scan...", "INFO"); - let host_args = match args.subcommand_matches("subdomain") { - Some(matches) => matches, - None => return, - }; - let target: String = match host_args.get_one::("target") { - Some(target) => target.to_owned(), - None => return, - }; - - let domain_ips: Vec = crate::dns::lookup_host(&target); - if domain_ips.is_empty() { - output::log_with_time("Failed to resolve domain", "ERROR"); - return; - } - let target_domain: Domain = crate::dns::domain::Domain { - domain_name: target, - ips: domain_ips, - }; - - let timeout = match host_args.get_one::("timeout") { - Some(timeout) => Duration::from_millis(*timeout), - None => Duration::from_secs(30), - }; - - let word_list: Vec = match host_args.get_one::("wordlist") { - Some(file_path) => match std::fs::read_to_string(&file_path) { - Ok(contents) => { - let mut word_list: Vec = Vec::new(); - for word in contents.lines() { - let word = word.trim(); - if word.is_empty() { - continue; - } - word_list.push(word.to_owned()); - } - word_list - } - Err(_) => vec![], - }, - None => db::get_subdomain(), - }; - - let mut domain_scanner = match DomainScanner::new() { - Ok(scanner) => scanner, - Err(e) => panic!("Error creating scanner: {}", e), - }; - domain_scanner.set_base_domain(target_domain.domain_name.clone()); - domain_scanner.word_list = word_list; - domain_scanner.set_timeout(timeout); - - print_option(&domain_scanner); - - // Display progress with indicatif - if !crate::app::is_quiet_mode() { - println!("[Progress]"); - } - let bar = ProgressBar::new(domain_scanner.word_list.len() as u64); - if crate::app::is_quiet_mode() { - bar.set_draw_target(ProgressDrawTarget::hidden()); - } - bar.enable_steady_tick(Duration::from_millis(120)); - bar.set_style(output::get_progress_style()); - bar.set_position(0); - bar.set_message("SubdomainScan"); - - let rx = domain_scanner.get_progress_receiver(); - let rt = Runtime::new().unwrap(); - // Run scan - let handle = thread::spawn(move || rt.block_on(async { domain_scanner.scan().await })); - // Print progress - while let Ok(_domain) = rx.lock().unwrap().recv() { - bar.inc(1); - } - bar.finish_with_message("SubdomainScan"); - let result: DomainScanResult = handle.join().unwrap(); - // Print results - if args.get_flag("json") { - let json_result = serde_json::to_string_pretty(&result).unwrap(); - println!("{}", json_result); - } else { - show_domainscan_result(&result, target_domain); - } - output::log_with_time(&format!("Scan completed in {:?}", result.scan_time), "INFO"); - match args.get_one::("save") { - Some(file_path) => { - match crate::fs::save_text(file_path, serde_json::to_string_pretty(&result).unwrap()) { - Ok(_) => { - output::log_with_time( - &format!("Saved to {}", file_path.to_string_lossy()), - "INFO", - ); - } - Err(e) => { - output::log_with_time(&format!("Failed to save: {}", e), "ERROR"); - } - } - } - None => {} - } -} - -fn print_option(setting: &DomainScanner) { - if crate::app::is_quiet_mode() { - return; - } - println!(); - let mut tree = Tree::new(node_label("SubdomainScan Config", None, None)); - let mut setting_tree = Tree::new(node_label("Settings", None, None)); - setting_tree.push(node_label( - "Words", - Some(&setting.word_list.len().to_string()), - None, - )); - setting_tree.push(node_label( - "Timeout", - Some(&format!("{:?}", setting.timeout)), - None, - )); - setting_tree.push(node_label( - "Resolve timeout", - Some(&format!("{:?}", setting.resolve_timeout)), - None, - )); - setting_tree.push(node_label( - "Concurrent limit", - Some(&setting.concurrent_limit.to_string()), - None, - )); - tree.push(setting_tree); - let mut target_tree = Tree::new(node_label("Target", None, None)); - target_tree.push(node_label("Domain Name", Some(&setting.base_domain), None)); - tree.push(target_tree); - println!("{}", tree); -} - -fn show_domainscan_result(scan_result: &DomainScanResult, target_domain: Domain) { - if !crate::app::is_quiet_mode() { - println!(); - } - let mut tree = Tree::new(node_label( - &format!("SubdomainScan Result - {}", target_domain.domain_name), - None, - None, - )); - let mut domain_tree = Tree::new(node_label(&target_domain.domain_name, None, None)); - let mut ipv4_tree = Tree::new(node_label("IPv4 Addresses", None, None)); - let mut ipv6_tree = Tree::new(node_label("IPv6 Addresses", None, None)); - for ip in &target_domain.ips { - if ip.is_ipv4() { - ipv4_tree.push(node_label(&ip.to_string(), None, None)); - } else { - ipv6_tree.push(node_label(&ip.to_string(), None, None)); - } - } - domain_tree.push(ipv4_tree); - domain_tree.push(ipv6_tree); - let mut subdomains_tree = Tree::new(node_label("Subdomains", None, None)); - for domain in &scan_result.domains { - let mut subdomain_tree = Tree::new(node_label(&domain.domain_name, None, None)); - let mut ipv4_tree = Tree::new(node_label("IPv4 Addresses", None, None)); - let mut ipv6_tree = Tree::new(node_label("IPv6 Addresses", None, None)); - for ip in &domain.ips { - if ip.is_ipv4() { - ipv4_tree.push(node_label(&ip.to_string(), None, None)); - } else { - ipv6_tree.push(node_label(&ip.to_string(), None, None)); - } - } - subdomain_tree.push(ipv4_tree); - subdomain_tree.push(ipv6_tree); - subdomains_tree.push(subdomain_tree); - } - domain_tree.push(subdomains_tree); - tree.push(domain_tree); - println!("{}", tree); -} diff --git a/src/handler/host.rs b/src/handler/host.rs deleted file mode 100644 index 1475e69..0000000 --- a/src/handler/host.rs +++ /dev/null @@ -1,266 +0,0 @@ -use crate::host::Host; -use crate::json::host::HostScanResult; -use crate::scan::result::ScanResult; -use crate::scan::scanner::HostScanner; -use crate::scan::setting::{HostScanSetting, HostScanType}; -use crate::util::tree::node_label; -use clap::ArgMatches; -use indicatif::{ProgressBar, ProgressDrawTarget}; -use ipnet::Ipv4Net; -use netdev::Interface; -use std::collections::HashMap; -use std::net::{IpAddr, Ipv4Addr}; -use std::path::PathBuf; -use std::str::FromStr; -use std::thread; -use std::time::Duration; -use termtree::Tree; - -use crate::output; - -pub fn handle_hostscan(args: &ArgMatches) { - output::log_with_time("Initiating host scan...", "INFO"); - let host_args = match args.subcommand_matches("host") { - Some(matches) => matches, - None => return, - }; - let target: String = match host_args.get_one::("target") { - Some(target) => target.to_owned(), - None => return, - }; - let scan_type: HostScanType = match host_args.get_one::("protocol") { - Some(protocol) => HostScanType::from_str(protocol), - None => HostScanType::IcmpPingScan, - }; - let timeout = match host_args.get_one::("timeout") { - Some(timeout) => Duration::from_millis(*timeout), - None => Duration::from_millis(10000), - }; - let port: u16 = match host_args.get_one::("port") { - Some(port) => *port, - None => 80 as u16, - }; - let default_waittime: Duration = Duration::from_millis(200); - let wait_time = match host_args.get_one::("waittime") { - Some(wait_time) => Duration::from_millis(*wait_time), - None => default_waittime, - }; - let send_rate = match host_args.get_one::("rate") { - Some(send_rate) => Duration::from_millis(*send_rate), - None => Duration::from_millis(0), - }; - let target_ips: Vec = match Ipv4Net::from_str(&target) { - Ok(ipv4net) => { - // convert hosts to Vec - ipv4net.hosts().map(|x| IpAddr::V4(x)).collect() - } - Err(_) => { - match Ipv4Addr::from_str(&target) { - Ok(ip_addr) => Ipv4Net::new(ip_addr, 24) - .unwrap() - .hosts() - .map(|x| IpAddr::V4(x)) - .collect(), - Err(_) => { - // Check if target is host-list file - match std::fs::read_to_string(&target) { - Ok(hosts) => { - let mut ips: Vec = Vec::new(); - for host in hosts.lines() { - let host = host.trim(); - if host.is_empty() { - continue; - } - match IpAddr::from_str(host) { - Ok(ip) => ips.push(ip), - Err(_) => continue, - } - } - ips - } - Err(_) => vec![], - } - } - } - } - }; - // Add scan target - let mut targets: Vec = Vec::new(); - for ip in target_ips { - let host: Host = Host::new(ip, String::new()).with_ports(vec![port]); - targets.push(host); - } - let interface: Interface = if let Some(if_name) = args.get_one::("interface") { - match crate::interface::get_interface_by_name(if_name.to_string()) { - Some(iface) => iface, - None => return, - } - } else { - match netdev::get_default_interface() { - Ok(iface) => iface, - Err(_) => return, - } - }; - let mut scan_setting = HostScanSetting::default() - .set_if_index(interface.index) - .set_scan_type(scan_type) - .set_targets(targets) - .set_timeout(timeout) - .set_wait_time(wait_time) - .set_send_rate(send_rate); - // Print options - print_option(&target, &scan_setting, &interface); - if !host_args.get_flag("random") { - scan_setting.randomize_ports(); - scan_setting.randomize_hosts(); - } - if !crate::app::is_quiet_mode() { - println!("[Progress]"); - } - // Display progress with indicatif - let bar = ProgressBar::new(scan_setting.targets.len() as u64); - if crate::app::is_quiet_mode() { - bar.set_draw_target(ProgressDrawTarget::hidden()); - } - //bar.enable_steady_tick(120); - bar.set_style(output::get_progress_style()); - bar.set_position(0); - bar.set_message("HostScan"); - let host_scanner = HostScanner::new(scan_setting); - let rx = host_scanner.get_progress_receiver(); - // Run scan - let handle = thread::spawn(move || host_scanner.scan()); - // Print progress - while let Ok(_host) = rx.lock().unwrap().recv() { - bar.inc(1); - } - let mut hostscan_result: ScanResult = handle.join().unwrap(); - bar.finish_with_message(format!("HostScan ({:?})", hostscan_result.scan_time)); - if hostscan_result.hosts.len() == 0 { - output::log_with_time("No results found", "INFO"); - return; - } - hostscan_result.sort_ports(); - hostscan_result.sort_hosts(); - let os_family_map: HashMap = - crate::db::get_fingerprint_map(&hostscan_result.fingerprints); - for host in &mut hostscan_result.hosts { - host.os_family = os_family_map - .get(&host.ip_addr) - .unwrap_or(&String::new()) - .to_string(); - } - let result: HostScanResult = HostScanResult::from_scan_result(&hostscan_result); - // Print results - if args.get_flag("json") { - let json_result = serde_json::to_string_pretty(&result).unwrap(); - println!("{}", json_result); - } else { - show_hostscan_result(&result); - } - output::log_with_time("Scan completed", "INFO"); - match args.get_one::("save") { - Some(file_path) => { - match crate::fs::save_text(file_path, serde_json::to_string_pretty(&result).unwrap()) { - Ok(_) => { - output::log_with_time( - &format!("Saved to {}", file_path.to_string_lossy()), - "INFO", - ); - } - Err(e) => { - output::log_with_time(&format!("Failed to save: {}", e), "ERROR"); - } - } - } - None => {} - } -} - -fn print_option(target: &str, setting: &HostScanSetting, interface: &Interface) { - if crate::app::is_quiet_mode() { - return; - } - println!(); - let mut tree = Tree::new(node_label("HostScan Config", None, None)); - let mut setting_tree = Tree::new(node_label("Settings", None, None)); - setting_tree.push(node_label( - "Protocol", - Some(setting.protocol.to_str()), - None, - )); - setting_tree.push(node_label( - "ScanType", - Some(setting.scan_type.to_str()), - None, - )); - setting_tree.push(node_label("InterfaceName", Some(&interface.name), None)); - setting_tree.push(node_label( - "Timeout", - Some(&format!("{:?}", setting.timeout)), - None, - )); - setting_tree.push(node_label( - "WaitTime", - Some(&format!("{:?}", setting.wait_time)), - None, - )); - setting_tree.push(node_label( - "SendRate", - Some(&format!("{:?}", setting.send_rate)), - None, - )); - tree.push(setting_tree); - let mut target_tree = Tree::new(node_label("Target", None, None)); - match Ipv4Net::from_str(&target) { - Ok(ipv4net) => { - target_tree.push(node_label("Network", Some(&ipv4net.to_string()), None)); - } - Err(_) => match Ipv4Addr::from_str(&target) { - Ok(ip_addr) => { - let net = Ipv4Net::new(ip_addr, 24).unwrap(); - target_tree.push(node_label("Network", Some(&net.to_string()), None)); - } - Err(_) => { - target_tree.push(node_label("List", Some(target), None)); - } - }, - } - tree.push(target_tree); - println!("{}", tree); -} - -fn show_hostscan_result(hostscan_result: &HostScanResult) { - if !crate::app::is_quiet_mode() { - println!(); - } - let oui_map: HashMap = crate::db::get_oui_detail_map(); - let mut tree = Tree::new(node_label("HostScan Result", None, None)); - let mut hosts_tree = Tree::new(node_label("Hosts", None, None)); - for host in &hostscan_result.hosts { - let mut host_tree = Tree::new(node_label(&host.ip_addr.to_string(), None, None)); - host_tree.push(node_label("Host Name", Some(&host.hostname), None)); - host_tree.push(node_label("TTL", Some(&host.ttl.to_string()), None)); - host_tree.push(node_label("OS Family", Some(&host.os_family), None)); - if !crate::ip::is_global_addr(&host.ip_addr) { - let vendor_name = if host.mac_addr.address().len() > 16 { - let prefix8 = host.mac_addr.address()[0..8].to_uppercase(); - oui_map.get(&prefix8).unwrap_or(&String::new()).to_string() - } else { - oui_map - .get(&host.mac_addr.address()) - .unwrap_or(&String::new()) - .to_string() - }; - host_tree.push(node_label( - "MAC Address", - Some(&host.mac_addr.to_string()), - None, - )); - host_tree.push(node_label("Vendor Name", Some(&vendor_name), None)); - } - hosts_tree.push(host_tree); - } - tree.push(hosts_tree); - println!("{}", tree); -} diff --git a/src/handler/interface.rs b/src/handler/interface.rs deleted file mode 100644 index 005226a..0000000 --- a/src/handler/interface.rs +++ /dev/null @@ -1,181 +0,0 @@ -use std::path::PathBuf; - -use crate::output; -use crate::util::tree::node_label; -use clap::ArgMatches; -use netdev::mac::MacAddr; -use netdev::Interface; -use termtree::Tree; - -pub fn show_default_interface(args: &ArgMatches) { - let iface: Interface = match netdev::get_default_interface() { - Ok(interface) => interface, - Err(_) => { - println!("Failed to get default interface"); - return; - } - }; - if args.get_flag("json") { - let json_result = serde_json::to_string_pretty(&iface).unwrap(); - println!("{}", json_result); - } else { - show_interface_tree(&iface); - } - match args.get_one::("save") { - Some(file_path) => { - match crate::fs::save_text(file_path, serde_json::to_string_pretty(&iface).unwrap()) { - Ok(_) => { - output::log_with_time( - &format!("Saved to {}", file_path.to_string_lossy()), - "INFO", - ); - } - Err(e) => { - output::log_with_time(&format!("Failed to save: {}", e), "ERROR"); - } - } - } - None => {} - } -} - -pub fn show_interfaces(args: &ArgMatches) { - let interfaces: Vec = netdev::get_interfaces(); - if args.get_flag("json") { - let json_result = serde_json::to_string_pretty(&interfaces).unwrap(); - println!("{}", json_result); - } else { - show_interfaces_tree(&interfaces); - } - match args.get_one::("save") { - Some(file_path) => { - match crate::fs::save_text( - file_path, - serde_json::to_string_pretty(&interfaces).unwrap(), - ) { - Ok(_) => { - output::log_with_time( - &format!("Saved to {}", file_path.to_string_lossy()), - "INFO", - ); - } - Err(e) => { - output::log_with_time(&format!("Failed to save: {}", e), "ERROR"); - } - } - } - None => {} - } -} - -pub fn show_interface_tree(iface: &Interface) { - let mut tree = Tree::new(node_label("Interface", None, None)); - tree.push(node_label("Index", Some(&iface.index.to_string()), None)); - tree.push(node_label("Name", Some(&iface.name), None)); - if let Some(friendly_name) = &iface.friendly_name { - tree.push(node_label("Friendly Name", Some(friendly_name), None)); - } - if let Some(desc) = &iface.description { - tree.push(node_label("Description", Some(desc), None)); - } - tree.push(node_label("Type", Some(&iface.if_type.name()), None)); - tree.push(node_label( - "MAC", - Some(&iface.mac_addr.unwrap_or(MacAddr::zero()).to_string()), - None, - )); - let mut ipv4_tree = Tree::new(node_label("IPv4 Addresses", None, None)); - for ipv4 in &iface.ipv4 { - ipv4_tree.push(node_label(&ipv4.addr().to_string(), None, None)); - } - tree.push(ipv4_tree); - - let mut ipv6_tree = Tree::new(node_label("IPv6 Addresses", None, None)); - for ipv6 in &iface.ipv6 { - ipv6_tree.push(node_label(&ipv6.addr().to_string(), None, None)); - } - tree.push(ipv6_tree); - - if let Some(gateway) = &iface.gateway { - let mut gateway_tree = Tree::new(node_label("Gateway", None, None)); - gateway_tree.push(node_label("MAC", Some(&gateway.mac_addr.to_string()), None)); - let mut ipv4_tree = Tree::new(node_label("IPv4 Addresses", None, None)); - for ipv4 in &gateway.ipv4 { - ipv4_tree.push(node_label(&ipv4.to_string(), None, None)); - } - gateway_tree.push(ipv4_tree); - let mut ipv6_tree = Tree::new(node_label("IPv6 Addresses", None, None)); - for ipv6 in &gateway.ipv6 { - ipv6_tree.push(node_label(&ipv6.to_string(), None, None)); - } - gateway_tree.push(ipv6_tree); - tree.push(gateway_tree); - } - if iface.dns_servers.len() > 0 { - let mut dns_tree = Tree::new(node_label("DNS Servers", None, None)); - for server_addr in &iface.dns_servers { - dns_tree.push(node_label(&server_addr.to_string(), None, None)); - } - tree.push(dns_tree); - } - - println!("{}", tree); -} - -pub fn show_interfaces_tree(interfaces: &Vec) { - let mut tree = Tree::new(node_label("Interfaces", None, None)); - for iface in interfaces { - let mut iface_tree = Tree::new(node_label(&iface.name, None, None)); - iface_tree.push(node_label("Index", Some(&iface.index.to_string()), None)); - iface_tree.push(node_label("Name", Some(&iface.name), None)); - if let Some(friendly_name) = &iface.friendly_name { - iface_tree.push(node_label("Friendly Name", Some(friendly_name), None)); - } - if let Some(desc) = &iface.description { - iface_tree.push(node_label("Description", Some(desc), None)); - } - iface_tree.push(node_label("Type", Some(&iface.if_type.name()), None)); - iface_tree.push(node_label( - "MAC", - Some(&iface.mac_addr.unwrap_or(MacAddr::zero()).to_string()), - None, - )); - let mut ipv4_tree = Tree::new(node_label("IPv4 Addresses", None, None)); - for ipv4 in &iface.ipv4 { - ipv4_tree.push(node_label(&ipv4.addr().to_string(), None, None)); - } - iface_tree.push(ipv4_tree); - - let mut ipv6_tree = Tree::new(node_label("IPv6 Addresses", None, None)); - for ipv6 in &iface.ipv6 { - ipv6_tree.push(node_label(&ipv6.addr().to_string(), None, None)); - } - iface_tree.push(ipv6_tree); - - if let Some(gateway) = &iface.gateway { - let mut gateway_tree = Tree::new(node_label("Gateway", None, None)); - gateway_tree.push(node_label("MAC", Some(&gateway.mac_addr.to_string()), None)); - let mut ipv4_tree = Tree::new(node_label("IPv4 Addresses", None, None)); - for ipv4 in &gateway.ipv4 { - ipv4_tree.push(node_label(&ipv4.to_string(), None, None)); - } - gateway_tree.push(ipv4_tree); - let mut ipv6_tree = Tree::new(node_label("IPv6 Addresses", None, None)); - for ipv6 in &gateway.ipv6 { - ipv6_tree.push(node_label(&ipv6.to_string(), None, None)); - } - gateway_tree.push(ipv6_tree); - iface_tree.push(gateway_tree); - } - - if iface.dns_servers.len() > 0 { - let mut dns_tree = Tree::new(node_label("DNS Servers", None, None)); - for server_addr in &iface.dns_servers { - dns_tree.push(node_label(&server_addr.to_string(), None, None)); - } - iface_tree.push(dns_tree); - } - tree.push(iface_tree); - } - println!("{}", tree); -} diff --git a/src/handler/mod.rs b/src/handler/mod.rs deleted file mode 100644 index 2bb7ec5..0000000 --- a/src/handler/mod.rs +++ /dev/null @@ -1,226 +0,0 @@ -pub mod check; -pub mod dns; -pub mod host; -pub mod interface; -pub mod neighbor; -pub mod ping; -pub mod port; -pub mod trace; - -use crate::db::model::OsFamilyFingerprint; -use crate::host::Host; -use crate::json::port::PortScanResult; -use crate::scan::result::ScanResult; -use crate::scan::scanner::{PortScanner, ServiceDetector}; -use crate::scan::setting::{PortScanSetting, PortScanType, ServiceProbeSetting}; -use clap::ArgMatches; -use indicatif::{ProgressBar, ProgressDrawTarget}; -use netdev::mac::MacAddr; -use std::collections::HashMap; -use std::net::IpAddr; -use std::path::PathBuf; -use std::thread; -use std::time::Duration; - -use crate::output; - -pub fn default_probe(target_host: &str, args: &ArgMatches) { - output::log_with_time("Initiating port scan...", "INFO"); - let target_host_name: String; - let target_ip_addr: IpAddr; - if crate::host::is_valid_ip_addr(target_host) { - target_ip_addr = target_host.parse().unwrap(); - target_host_name = - crate::dns::lookup_ip_addr(&target_ip_addr).unwrap_or(target_host.to_string()); - } else { - target_host_name = target_host.to_string(); - target_ip_addr = match crate::dns::lookup_host_name(target_host) { - Some(ip) => ip, - None => return, - }; - } - let target_ports: Vec = if args.get_flag("full") { - // Use full ports (1-65535) - (1..=65535).collect() - } else { - // Use default 1000 ports - crate::db::get_default_ports() - }; - let interface: netdev::Interface = if let Some(if_name) = args.get_one::("interface") { - match crate::interface::get_interface_by_name(if_name.to_string()) { - Some(iface) => iface, - None => return, - } - } else { - match netdev::get_default_interface() { - Ok(iface) => iface, - Err(_) => return, - } - }; - // Check reachability by ping (one-shot) - let default_waittime: Duration; - if args.get_flag("noping") { - default_waittime = Duration::from_millis(200); - } else { - match crate::handler::ping::initial_ping( - interface.index, - target_ip_addr, - target_host_name.clone(), - ) { - Ok(rtt) => { - default_waittime = crate::util::setting::caluculate_wait_time(rtt); - } - Err(e) => { - output::log_with_time( - &format!("{} You can disable this initial ping by --noping", e), - "ERROR", - ); - return; - } - } - } - let target_host: Host = - Host::new(target_ip_addr, target_host_name.clone()).with_ports(target_ports); - let mut result: PortScanResult = PortScanResult::new(target_ip_addr, target_host_name); - let mut scan_setting = PortScanSetting::default() - .set_if_index(interface.index) - .set_scan_type(PortScanType::TcpSynScan) - .add_target(target_host.clone()) - .set_timeout(Duration::from_millis(10000)) - .set_wait_time(default_waittime) - .set_send_rate(Duration::from_millis(0)); - // Print options - port::print_option(&scan_setting, &interface); - // Randomize ports and hosts - scan_setting.randomize_ports(); - scan_setting.randomize_hosts(); - if !crate::app::is_quiet_mode() { - println!("[Progress]"); - } - // Display progress with indicatif - let bar = ProgressBar::new(scan_setting.targets[0].ports.len() as u64); - if crate::app::is_quiet_mode() { - bar.set_draw_target(ProgressDrawTarget::hidden()); - } - //bar.enable_steady_tick(120); - bar.set_style(output::get_progress_style()); - bar.set_position(0); - bar.set_message("PortScan"); - let port_scanner = PortScanner::new(scan_setting); - let rx = port_scanner.get_progress_receiver(); - // Run port scan - let handle = thread::spawn(move || port_scanner.scan()); - // Print port scan progress - while let Ok(_socket_addr) = rx.lock().unwrap().recv() { - bar.inc(1); - } - let mut portscan_result: ScanResult = handle.join().unwrap(); - bar.finish_with_message(format!("PortScan ({:?})", portscan_result.scan_time)); - - if portscan_result.hosts.len() == 0 { - output::log_with_time("No results found", "INFO"); - return; - } - - portscan_result.sort_ports(); - portscan_result.sort_hosts(); - - // Set port scan result to host - result.host.ports = portscan_result.hosts[0].get_open_ports(); - - // Run service detection - let probe_setting: ServiceProbeSetting = ServiceProbeSetting::default( - target_host.ip_addr, - target_host.hostname, - portscan_result.hosts[0].get_open_port_numbers(), - ); - let service_detector = ServiceDetector::new(probe_setting); - let service_rx = service_detector.get_progress_receiver(); - let bar = ProgressBar::new(portscan_result.hosts[0].get_open_port_numbers().len() as u64); - if crate::app::is_quiet_mode() { - bar.set_draw_target(ProgressDrawTarget::hidden()); - } - bar.enable_steady_tick(Duration::from_millis(120)); - bar.set_style(output::get_progress_style()); - bar.set_position(0); - bar.set_message("ServiceDetection"); - let sd_start_time = std::time::Instant::now(); - let service_handle = thread::spawn(move || service_detector.run()); - // Print progress - while let Ok(_socket_addr) = service_rx.lock().unwrap().recv() { - bar.inc(1); - } - let sd_elapsed_time = sd_start_time.elapsed(); - bar.finish_with_message(format!("ServiceDetection ({:?})", sd_elapsed_time)); - let service_result = service_handle.join().unwrap(); - // Set service detection result to host - for port in &mut result.host.ports { - if let Some(result) = service_result.get(&port.number) { - port.service_name = result.service_name.clone(); - port.service_version = result.service_detail.clone().unwrap_or(String::new()); - } - } - // OS detection - if result.host.get_open_port_numbers().len() > 0 { - if let Some(fingerprint) = portscan_result - .get_syn_ack_fingerprint(result.host.ip_addr, result.host.get_open_port_numbers()[0]) - { - let os_fingerprint: OsFamilyFingerprint = - crate::db::verify_os_family_fingerprint(&fingerprint); - result.host.os_family = os_fingerprint.os_family; - } - } - // Set vendor name - if !crate::ip::is_global_addr(&result.host.ip_addr) { - if let Some(h) = portscan_result.get_host(result.host.ip_addr) { - if h.mac_addr != MacAddr::zero() { - let oui_map: HashMap = crate::db::get_oui_detail_map(); - let vendor_name = if h.mac_addr.address().len() > 16 { - let prefix8 = h.mac_addr.address()[0..8].to_uppercase(); - oui_map.get(&prefix8).unwrap_or(&String::new()).to_string() - } else { - oui_map - .get(&h.mac_addr.address()) - .unwrap_or(&String::new()) - .to_string() - }; - result.host.mac_addr = h.mac_addr; - result.host.vendor_name = vendor_name; - } - } - } - result.host.ttl = portscan_result.hosts[0].ttl; - result.port_scan_time = portscan_result.scan_time; - result.service_detection_time = sd_elapsed_time; - result.total_scan_time = portscan_result.scan_time + sd_elapsed_time; - result.scan_status = portscan_result.scan_status; - // Print results - if args.get_flag("json") { - let json_result = serde_json::to_string_pretty(&result).unwrap(); - println!("{}", json_result); - } else { - port::show_portscan_result(&result.host); - } - - output::log_with_time( - &format!("Total elapsed time {:?} ", result.total_scan_time), - "INFO", - ); - - match args.get_one::("save") { - Some(file_path) => { - match crate::fs::save_text(file_path, serde_json::to_string_pretty(&result).unwrap()) { - Ok(_) => { - output::log_with_time( - &format!("Saved to {}", file_path.to_string_lossy()), - "INFO", - ); - } - Err(e) => { - output::log_with_time(&format!("Failed to save: {}", e), "ERROR"); - } - } - } - None => {} - } -} diff --git a/src/handler/neighbor.rs b/src/handler/neighbor.rs deleted file mode 100644 index a6d4600..0000000 --- a/src/handler/neighbor.rs +++ /dev/null @@ -1,234 +0,0 @@ -use crate::neighbor::resolver::DeviceResolver; -use crate::neighbor::result::DeviceResolveResult; -use crate::neighbor::setting::AddressResolveSetting; -use crate::output; -use crate::util::tree::node_label; -use clap::ArgMatches; -use netdev::Interface; -use std::net::IpAddr; -use std::path::PathBuf; -use std::str::FromStr; -use std::thread; -use std::time::Duration; -use termtree::Tree; - -pub fn handle_neighbor_discovery(args: &ArgMatches) { - let nei_args = match args.subcommand_matches("nei") { - Some(matches) => matches, - None => return, - }; - let target: String = match nei_args.get_one::("target") { - Some(target) => target.to_owned(), - None => return, - }; - let dst_ip: IpAddr = match IpAddr::from_str(&target) { - Ok(ip_addr) => ip_addr, - Err(_) => { - output::log_with_time("Invalid IP Address", "ERROR"); - return; - } - }; - match dst_ip { - IpAddr::V4(_) => { - output::log_with_time("Initiating ARP...", "INFO"); - } - IpAddr::V6(_) => { - output::log_with_time("Initiating NDP...", "INFO"); - } - } - let interface: netdev::Interface = if let Some(if_name) = args.get_one::("interface") { - match crate::interface::get_interface_by_name(if_name.to_string()) { - Some(iface) => iface, - None => return, - } - } else { - match netdev::get_default_interface() { - Ok(iface) => iface, - Err(_) => return, - } - }; - let count: u32 = match nei_args.get_one::("count") { - Some(count) => *count, - None => 1, - }; - let timeout = match nei_args.get_one::("timeout") { - Some(timeout) => Duration::from_millis(*timeout), - None => Duration::from_secs(30), - }; - let wait_time = match nei_args.get_one::("waittime") { - Some(wait_time) => Duration::from_millis(*wait_time), - None => Duration::from_secs(1), - }; - let send_rate = match nei_args.get_one::("rate") { - Some(send_rate) => Duration::from_millis(*send_rate), - None => Duration::from_secs(1), - }; - let mut setting: AddressResolveSetting = match dst_ip { - IpAddr::V4(ipv4) => AddressResolveSetting::arp(&interface, ipv4, count).unwrap(), - IpAddr::V6(ipv6) => AddressResolveSetting::ndp(&interface, ipv6, count).unwrap(), - }; - setting.probe_timeout = timeout; - setting.receive_timeout = wait_time; - setting.send_rate = send_rate; - print_option(&setting, &interface); - let resolver: DeviceResolver = DeviceResolver::new(setting).unwrap(); - let rx = resolver.get_progress_receiver(); - let handle = thread::spawn(move || resolver.resolve()); - for r in rx.lock().unwrap().iter() { - if r.probe_status.kind == crate::probe::ProbeStatusKind::Done { - output::log_with_time( - &format!( - "{} [{:?}] {} Bytes from MAC:{}, IP:{}, RTT:{:?}", - r.seq, r.protocol, r.received_packet_size, r.mac_addr, r.ip_addr, r.rtt - ), - "INFO", - ); - } else { - output::log_with_time( - &format!("{} [{:?}] {}", r.seq, r.protocol, r.probe_status.message), - "ERROR", - ); - } - } - match handle.join() { - Ok(resolve_result) => match resolve_result { - Ok(r) => { - // Print results - if args.get_flag("json") { - let json_result = serde_json::to_string_pretty(&r).unwrap(); - println!("{}", json_result); - } else { - show_resolve_result(&r); - } - match args.get_one::("save") { - Some(file_path) => { - match crate::fs::save_text( - file_path, - serde_json::to_string_pretty(&r).unwrap(), - ) { - Ok(_) => { - output::log_with_time( - &format!("Saved to {}", file_path.to_string_lossy()), - "INFO", - ); - } - Err(e) => { - output::log_with_time(&format!("Failed to save: {}", e), "ERROR"); - } - } - } - None => {} - } - if r.probe_status.kind == crate::probe::ProbeStatusKind::Done { - output::log_with_time("Resolve Success", "INFO"); - } else { - output::log_with_time( - &format!("Resolve Failed: {}", r.probe_status.message), - "ERROR", - ); - } - } - Err(e) => { - output::log_with_time(&format!("Resolve Failed: {}", e), "ERROR"); - } - }, - Err(e) => { - output::log_with_time(&format!("Resolve Failed: {:?}", e), "ERROR"); - } - } -} - -fn print_option(setting: &AddressResolveSetting, interface: &Interface) { - if crate::app::is_quiet_mode() { - return; - } - println!(); - // Options - let mut tree = Tree::new(node_label("NeighborResolve Config", None, None)); - let mut setting_tree = Tree::new(node_label("Settings", None, None)); - setting_tree.push(node_label("Interface", Some(interface.name.as_str()), None)); - setting_tree.push(node_label( - "Protocol", - Some(format!("{:?}", setting.protocol).as_str()), - None, - )); - setting_tree.push(node_label( - "Count", - Some(setting.count.to_string().as_str()), - None, - )); - setting_tree.push(node_label( - "Timeout", - Some(format!("{:?}", setting.probe_timeout).as_str()), - None, - )); - setting_tree.push(node_label( - "Wait Time", - Some(format!("{:?}", setting.receive_timeout).as_str()), - None, - )); - setting_tree.push(node_label( - "Send Rate", - Some(format!("{:?}", setting.send_rate).as_str()), - None, - )); - tree.push(setting_tree); - // Target - let mut target_tree = Tree::new(node_label("Target", None, None)); - target_tree.push(node_label( - "IP Address", - Some(setting.dst_ip.to_string().as_str()), - None, - )); - tree.push(target_tree); - println!("{}", tree); -} - -fn show_resolve_result(resolve_result: &DeviceResolveResult) { - if !crate::app::is_quiet_mode() { - println!(); - } - let mut tree = Tree::new(node_label("NeighborResolve Result", None, None)); - // Responses - let mut responses_tree = Tree::new(node_label("Responses", None, None)); - for response in &resolve_result.results { - let source_ip_addr: String = if response.ip_addr.to_string() != response.host_name - && !response.host_name.is_empty() - { - format!("{}({})", response.host_name, response.ip_addr) - } else { - response.ip_addr.to_string() - }; - let mut response_tree = Tree::new(node_label( - "Sequence", - Some(response.seq.to_string().as_str()), - None, - )); - response_tree.push(node_label( - "MAC Address", - Some(&response.mac_addr.address()), - None, - )); - response_tree.push(node_label("IP Address", Some(&source_ip_addr), None)); - response_tree.push(node_label( - "Protocol", - Some(format!("{:?}", response.protocol).as_str()), - None, - )); - response_tree.push(node_label( - "Received Bytes", - Some(response.received_packet_size.to_string().as_str()), - None, - )); - response_tree.push(node_label( - "RTT", - Some(format!("{:?}", response.rtt).as_str()), - None, - )); - - responses_tree.push(response_tree); - } - tree.push(responses_tree); - - println!("{}", tree); -} diff --git a/src/handler/ping.rs b/src/handler/ping.rs deleted file mode 100644 index 31918ab..0000000 --- a/src/handler/ping.rs +++ /dev/null @@ -1,517 +0,0 @@ -use crate::output; -use crate::ping::{pinger::Pinger, result::PingResult, setting::PingSetting}; -use crate::protocol::Protocol; -use crate::util::tree::node_label; -use clap::ArgMatches; -use netdev::Interface; -use std::net::{IpAddr, SocketAddr}; -use std::path::PathBuf; -use std::str::FromStr; -use std::thread; -use std::time::Duration; -use termtree::Tree; - -pub fn oneshot_ping( - if_index: u32, - dst_ip: IpAddr, - protocol: Protocol, - port: Option, -) -> Result { - let interface: Interface = match crate::interface::get_interface_by_index(if_index) { - Some(interface) => interface, - None => return Err("Failed to get interface information".to_string()), - }; - let setting: PingSetting = match protocol { - Protocol::ICMP => PingSetting::icmp_ping(&interface, dst_ip, 1).unwrap(), - Protocol::TCP => PingSetting::tcp_ping(&interface, dst_ip, port.unwrap_or(80), 1).unwrap(), - Protocol::UDP => PingSetting::udp_ping(&interface, dst_ip, 1).unwrap(), - _ => { - return Err("Unsupported protoco".to_string()); - } - }; - let pinger: Pinger = match Pinger::new(setting) { - Ok(pinger) => pinger, - Err(e) => return Err(format!("Failed to create pinger: {}", e)), - }; - match pinger.ping() { - Ok(ping_result) => { - if ping_result.probe_status.kind == crate::probe::ProbeStatusKind::Done { - Ok(ping_result) - } else { - Err(format!( - "Failed to ping: {}", - ping_result.probe_status.message - )) - } - } - Err(e) => Err(format!("Failed to ping: {}", e)), - } -} - -pub fn initial_ping( - if_index: u32, - target_ip_addr: IpAddr, - target_host_name: String, -) -> Result { - // 1. Check reachability by ICMP ping (one-shot) - match super::ping::oneshot_ping(if_index, target_ip_addr, Protocol::ICMP, None) { - Ok(ping_result) => { - let response = &ping_result.stat.responses[0]; - if target_host_name != target_ip_addr.to_string() { - output::log_with_time( - &format!( - "[ICMP] {}({}) is up. RTT:{:?}", - target_host_name, target_ip_addr, response.rtt - ), - "INFO", - ); - } else { - output::log_with_time( - &format!("[ICMP] {} is up. RTT:{:?}", target_ip_addr, response.rtt), - "INFO", - ); - } - return Ok(crate::sys::time::ceil_duration_millis( - response.rtt.mul_f64(1.5), - )); - } - Err(e) => { - output::log_with_time(&format!("[ICMP] {}", e), "ERROR"); - output::log_with_time( - &format!( - "[ICMP] {}({}) is down or unreachable.", - target_host_name, target_ip_addr - ), - "ERROR", - ); - } - } - // 2. Check reachability by UDP ping (one-shot) - match super::ping::oneshot_ping(if_index, target_ip_addr, Protocol::UDP, None) { - Ok(ping_result) => { - let response = &ping_result.stat.responses[0]; - if target_host_name != target_ip_addr.to_string() { - output::log_with_time( - &format!( - "[UDP] {}({}) is up. RTT:{:?}", - target_host_name, target_ip_addr, response.rtt - ), - "INFO", - ); - } else { - output::log_with_time( - &format!("[UDP] {} is up. RTT:{:?}", target_ip_addr, response.rtt), - "INFO", - ); - } - return Ok(crate::sys::time::ceil_duration_millis( - response.rtt.mul_f64(1.5), - )); - } - Err(e) => { - output::log_with_time(&format!("[UDP] {}", e), "ERROR"); - output::log_with_time( - &format!( - "[UDP] {}({}) is down or unreachable.", - target_host_name, target_ip_addr - ), - "ERROR", - ); - } - } - // 3. Check reachability by TCP ping (one-shot) - match super::ping::oneshot_ping(if_index, target_ip_addr, Protocol::TCP, Some(80)) { - Ok(ping_result) => { - let response = &ping_result.stat.responses[0]; - if target_host_name != target_ip_addr.to_string() { - output::log_with_time( - &format!( - "[TCP] {}({}) is up. RTT:{:?}", - target_host_name, target_ip_addr, response.rtt - ), - "INFO", - ); - } else { - output::log_with_time( - &format!("[TCP] {} is up. RTT:{:?}", target_ip_addr, response.rtt), - "INFO", - ); - } - return Ok(crate::sys::time::ceil_duration_millis( - response.rtt.mul_f64(1.5), - )); - } - Err(e) => { - output::log_with_time(&format!("[TCP] {}", e), "ERROR"); - output::log_with_time( - &format!( - "[TCP] {}({}) is down or unreachable.", - target_host_name, target_ip_addr - ), - "ERROR", - ); - } - } - Err(format!( - "Failed to initial ping to {}({})", - target_host_name, target_ip_addr - )) -} - -pub fn handle_ping(args: &ArgMatches) { - output::log_with_time("Initiating ping...", "INFO"); - let ping_args = match args.subcommand_matches("ping") { - Some(matches) => matches, - None => return, - }; - let interface: netdev::Interface = if let Some(if_name) = args.get_one::("interface") { - match crate::interface::get_interface_by_name(if_name.to_string()) { - Some(iface) => iface, - None => return, - } - } else { - match netdev::get_default_interface() { - Ok(iface) => iface, - Err(_) => return, - } - }; - let target: String = match ping_args.get_one::("target") { - Some(target) => target.to_owned(), - None => return, - }; - let count: u32 = match ping_args.get_one::("count") { - Some(count) => *count, - None => 4, - }; - let maxhop: u8 = match ping_args.get_one::("maxhop") { - Some(maxhop) => *maxhop, - None => 64, - }; - let mut protocol: Protocol = match ping_args.get_one::("protocol") { - Some(target) => match Protocol::from_str(&target) { - Some(protocol) => protocol, - None => { - output::log_with_time("Invalid protocol", "ERROR"); - return; - } - }, - None => Protocol::ICMP, - }; - let mut port: u16 = match ping_args.get_one::("port") { - Some(port) => *port, - None => 80, - }; - let dst_ip: IpAddr = match IpAddr::from_str(&target) { - Ok(ip_addr) => ip_addr, - Err(_) => match SocketAddr::from_str(&target) { - Ok(socket_addr) => { - port = socket_addr.port(); - if protocol == Protocol::ICMP { - protocol = Protocol::TCP; - } - socket_addr.ip() - } - Err(_) => match crate::dns::lookup_host_name(&target) { - Some(ip_addr) => ip_addr, - None => { - output::log_with_time("Failed to resolve domain", "ERROR"); - return; - } - }, - }, - }; - let timeout = match ping_args.get_one::("timeout") { - Some(timeout) => Duration::from_millis(*timeout), - None => Duration::from_secs(30), - }; - let wait_time = match ping_args.get_one::("waittime") { - Some(wait_time) => Duration::from_millis(*wait_time), - None => Duration::from_secs(1), - }; - let send_rate = match ping_args.get_one::("rate") { - Some(send_rate) => Duration::from_millis(*send_rate), - None => Duration::from_secs(1), - }; - let mut setting: PingSetting = match protocol { - Protocol::ICMP => PingSetting::icmp_ping(&interface, dst_ip, count).unwrap(), - Protocol::TCP => PingSetting::tcp_ping(&interface, dst_ip, port, count).unwrap(), - Protocol::UDP => PingSetting::udp_ping(&interface, dst_ip, count).unwrap(), - _ => { - output::log_with_time("Unsupported protocol", "ERROR"); - return; - } - }; - setting.dst_hostname = target - .split(":") - .collect::>() - .get(0) - .unwrap() - .to_string(); - setting.hop_limit = maxhop; - setting.receive_timeout = wait_time; - setting.probe_timeout = timeout; - setting.send_rate = send_rate; - - let target_addr: String = - if setting.dst_ip.to_string() != setting.dst_hostname && !setting.dst_hostname.is_empty() { - format!("{}({})", setting.dst_hostname, setting.dst_ip) - } else { - setting.dst_ip.to_string() - }; - - print_option(&setting, &interface); - - let pinger: Pinger = Pinger::new(setting).unwrap(); - let rx = pinger.get_progress_receiver(); - let handle = thread::spawn(move || pinger.ping()); - for r in rx.lock().unwrap().iter() { - let source: String = if r.ip_addr.to_string() != r.host_name && !r.host_name.is_empty() { - format!("{}({})", r.host_name, r.ip_addr) - } else { - r.ip_addr.to_string() - }; - if r.probe_status.kind == crate::probe::ProbeStatusKind::Done { - if let Some(port) = r.port_number { - output::log_with_time( - &format!( - "{} [{:?}] {} Bytes from {}:{}, HOP:{}, TTL:{}, RTT:{:?}", - r.seq, - r.protocol, - r.received_packet_size, - source, - port, - r.hop, - r.ttl, - r.rtt - ), - "INFO", - ); - } else { - output::log_with_time( - &format!( - "{} [{:?}] {} Bytes from {}, HOP:{}, TTL:{}, RTT:{:?}", - r.seq, r.protocol, r.received_packet_size, source, r.hop, r.ttl, r.rtt - ), - "INFO", - ); - } - } else { - if let Some(port) = r.port_number { - output::log_with_time( - &format!( - "{} [{:?}] {}:{} {}", - r.seq, r.protocol, source, port, r.probe_status.message - ), - "ERROR", - ); - } else { - output::log_with_time( - &format!( - "{} [{:?}] {} {}", - r.seq, r.protocol, source, r.probe_status.message - ), - "ERROR", - ); - } - } - } - match handle.join() { - Ok(ping_result) => match ping_result { - Ok(ping_result) => { - if ping_result.probe_status.kind == crate::probe::ProbeStatusKind::Done { - // Print results - if args.get_flag("json") { - let json_result = serde_json::to_string_pretty(&ping_result).unwrap(); - println!("{}", json_result); - } else { - show_ping_result(&ping_result, target_addr); - } - match args.get_one::("save") { - Some(file_path) => { - match crate::fs::save_text( - file_path, - serde_json::to_string_pretty(&ping_result).unwrap(), - ) { - Ok(_) => { - output::log_with_time( - &format!("Saved to {}", file_path.to_string_lossy()), - "INFO", - ); - } - Err(e) => { - output::log_with_time( - &format!("Failed to save: {}", e), - "ERROR", - ); - } - } - } - None => {} - } - } else { - output::log_with_time( - &format!("Failed to ping: {}", ping_result.probe_status.message), - "ERROR", - ); - } - } - Err(e) => println!("{:?}", e), - }, - Err(e) => println!("{:?}", e), - } -} - -fn print_option(setting: &PingSetting, interface: &Interface) { - if crate::app::is_quiet_mode() { - return; - } - println!(); - // Options - let mut tree = Tree::new(node_label("Ping Config", None, None)); - let mut setting_tree = Tree::new(node_label("Settings", None, None)); - setting_tree.push(node_label("Interface", Some(interface.name.as_str()), None)); - setting_tree.push(node_label( - "Protocol", - Some(format!("{:?}", setting.protocol).as_str()), - None, - )); - setting_tree.push(node_label( - "Count", - Some(setting.count.to_string().as_str()), - None, - )); - setting_tree.push(node_label( - "Hop Limit", - Some(setting.hop_limit.to_string().as_str()), - None, - )); - setting_tree.push(node_label( - "Timeout", - Some(format!("{:?}", setting.probe_timeout).as_str()), - None, - )); - setting_tree.push(node_label( - "Wait Time", - Some(format!("{:?}", setting.receive_timeout).as_str()), - None, - )); - setting_tree.push(node_label( - "Send Rate", - Some(format!("{:?}", setting.send_rate).as_str()), - None, - )); - tree.push(setting_tree); - // Target - let mut target_tree = Tree::new(node_label("Target", None, None)); - target_tree.push(node_label( - "IP Address", - Some(setting.dst_ip.to_string().as_str()), - None, - )); - if setting.dst_ip.to_string() != setting.dst_hostname && !setting.dst_hostname.is_empty() { - target_tree.push(node_label("Host Name", Some(&setting.dst_hostname), None)); - } - if let Some(port) = setting.dst_port { - target_tree.push(node_label("Port", Some(port.to_string().as_str()), None)); - } - tree.push(target_tree); - println!("{}", tree); -} - -fn show_ping_result(ping_result: &PingResult, target_addr: String) { - if !crate::app::is_quiet_mode() { - println!(); - } - let mut tree = Tree::new(node_label( - &format!("Ping Result - {}", target_addr), - None, - None, - )); - // Responses - let mut responses_tree = Tree::new(node_label("Responses", None, None)); - for response in &ping_result.stat.responses { - let mut response_tree = Tree::new(node_label( - "Sequence", - Some(response.seq.to_string().as_str()), - None, - )); - response_tree.push(node_label( - "IP Address", - Some(&response.ip_addr.to_string()), - None, - )); - response_tree.push(node_label( - "Protocol", - Some(format!("{:?}", response.protocol).as_str()), - None, - )); - response_tree.push(node_label( - "Received Bytes", - Some(response.received_packet_size.to_string().as_str()), - None, - )); - response_tree.push(node_label( - "HOP", - Some(response.hop.to_string().as_str()), - None, - )); - response_tree.push(node_label( - "TTL", - Some(response.ttl.to_string().as_str()), - None, - )); - response_tree.push(node_label( - "RTT", - Some(format!("{:?}", response.rtt).as_str()), - None, - )); - - responses_tree.push(response_tree); - } - tree.push(responses_tree); - - // Statistics - let mut stat_tree = Tree::new(node_label("Statistics", None, None)); - stat_tree.push(node_label( - "Transmitted", - Some(format!("{}", ping_result.stat.transmitted_count).as_str()), - None, - )); - stat_tree.push(node_label( - "Received", - Some(format!("{}", ping_result.stat.received_count).as_str()), - None, - )); - stat_tree.push(node_label( - "Loss", - Some( - format!( - "{}%", - 100.0 - - (ping_result.stat.received_count as f64 - / ping_result.stat.transmitted_count as f64) - * 100.0 - ) - .as_str(), - ), - None, - )); - stat_tree.push(node_label( - "Min", - Some(format!("{:?}", ping_result.stat.min).as_str()), - None, - )); - stat_tree.push(node_label( - "Max", - Some(format!("{:?}", ping_result.stat.max).as_str()), - None, - )); - stat_tree.push(node_label( - "Avg", - Some(format!("{:?}", ping_result.stat.avg).as_str()), - None, - )); - tree.push(stat_tree); - - println!("{}", tree); -} diff --git a/src/handler/port.rs b/src/handler/port.rs deleted file mode 100644 index 472f263..0000000 --- a/src/handler/port.rs +++ /dev/null @@ -1,385 +0,0 @@ -use crate::db::model::OsFamilyFingerprint; -use crate::host::{Host, PortStatus}; -use crate::json::port::PortScanResult; -use crate::output; -use crate::scan::result::ScanResult; -use crate::scan::scanner::{PortScanner, ServiceDetector}; -use crate::scan::setting::{PortScanSetting, PortScanType, ServiceProbeSetting}; -use crate::util::tree::node_label; -use clap::ArgMatches; -use indicatif::{ProgressBar, ProgressDrawTarget}; -use netdev::mac::MacAddr; -use netdev::Interface; -use std::collections::HashMap; -use std::net::IpAddr; -use std::path::PathBuf; -use std::thread; -use std::time::Duration; -use termtree::Tree; - -pub fn handle_portscan(args: &ArgMatches) { - output::log_with_time("Initiating port scan...", "INFO"); - let port_args = match args.subcommand_matches("port") { - Some(matches) => matches, - None => return, - }; - let target: String = match port_args.get_one::("target") { - Some(target) => target.to_owned(), - None => return, - }; - let target_host_name: String; - let target_ip_addr: IpAddr; - let target_ports: Vec; - if crate::host::is_valid_ip_addr(&target) { - target_ip_addr = target.parse().unwrap(); - target_host_name = crate::dns::lookup_ip_addr(&target_ip_addr).unwrap_or(target.clone()); - } else { - target_host_name = target.clone(); - target_ip_addr = match crate::dns::lookup_host_name(&target) { - Some(ip) => ip, - None => return, - }; - } - if port_args.contains_id("ports") { - // Use specific ports (delimiter: ',') - target_ports = port_args - .get_many::("ports") - .unwrap_or_default() - .copied() - .collect(); - } else if port_args.contains_id("range") { - // Use specific range (delimiter: '-') - // 0: start, 1: end - let range: Vec = port_args - .get_many::("range") - .unwrap_or_default() - .copied() - .collect(); - target_ports = (range[0]..=range[1]).collect(); - } else if port_args.get_flag("wellknown") { - // Use well-known ports - target_ports = crate::db::get_wellknown_ports(); - } else { - if port_args.get_flag("full") { - // Use full ports (1-65535) - target_ports = (1..=65535).collect(); - } else { - // Use default 1000 ports - target_ports = crate::db::get_default_ports(); - } - } - let interface: netdev::Interface = if let Some(if_name) = args.get_one::("interface") { - match crate::interface::get_interface_by_name(if_name.to_string()) { - Some(iface) => iface, - None => return, - } - } else { - match netdev::get_default_interface() { - Ok(iface) => iface, - Err(_) => return, - } - }; - // Check reachability by ping (one-shot) - let default_waittime: Duration; - if port_args.get_flag("noping") { - default_waittime = Duration::from_millis(200); - } else { - match crate::handler::ping::initial_ping( - interface.index, - target_ip_addr, - target_host_name.clone(), - ) { - Ok(rtt) => { - default_waittime = crate::util::setting::caluculate_wait_time(rtt); - } - Err(e) => { - output::log_with_time( - &format!("{} You can disable this initial ping by --noping", e), - "ERROR", - ); - return; - } - } - } - let scan_type: PortScanType = match port_args.get_one::("scantype") { - Some(scan_type) => match scan_type.as_str() { - "CONNECT" => PortScanType::TcpConnectScan, - _ => PortScanType::TcpSynScan, - }, - None => PortScanType::TcpSynScan, - }; - let timeout = match port_args.get_one::("timeout") { - Some(timeout) => Duration::from_millis(*timeout), - None => Duration::from_millis(10000), - }; - let wait_time = match port_args.get_one::("waittime") { - Some(wait_time) => Duration::from_millis(*wait_time), - None => default_waittime, - }; - let send_rate = match port_args.get_one::("rate") { - Some(send_rate) => Duration::from_millis(*send_rate), - None => Duration::from_millis(0), - }; - let target_host: Host = - Host::new(target_ip_addr, target_host_name.clone()).with_ports(target_ports); - let mut result: PortScanResult = PortScanResult::new(target_ip_addr, target_host_name); - let mut scan_setting = PortScanSetting::default() - .set_if_index(interface.index) - .set_scan_type(scan_type) - .add_target(target_host.clone()) - .set_timeout(timeout) - .set_wait_time(wait_time) - .set_send_rate(send_rate); - // Print options - print_option(&scan_setting, &interface); - if !port_args.get_flag("random") { - scan_setting.randomize_ports(); - scan_setting.randomize_hosts(); - } - if !crate::app::is_quiet_mode() { - println!("[Progress]"); - } - // Display progress with indicatif - let bar = ProgressBar::new(scan_setting.targets[0].ports.len() as u64); - if crate::app::is_quiet_mode() { - bar.set_draw_target(ProgressDrawTarget::hidden()); - } - //bar.enable_steady_tick(120); - bar.set_style(output::get_progress_style()); - bar.set_position(0); - bar.set_message("PortScan"); - let port_scanner = PortScanner::new(scan_setting); - let rx = port_scanner.get_progress_receiver(); - // Run port scan - let handle = thread::spawn(move || port_scanner.scan()); - // Print port scan progress - while let Ok(_socket_addr) = rx.lock().unwrap().recv() { - bar.inc(1); - } - let mut portscan_result: ScanResult = handle.join().unwrap(); - bar.finish_with_message(format!("PortScan ({:?})", portscan_result.scan_time)); - - if portscan_result.hosts.len() == 0 { - output::log_with_time("No results found", "INFO"); - return; - } - - portscan_result.sort_ports(); - portscan_result.sort_hosts(); - - // Set port scan result to host - result.host.ports = portscan_result.hosts[0].get_open_ports(); - - // Run service detection - let probe_setting: ServiceProbeSetting = ServiceProbeSetting::default( - target_host.ip_addr, - target_host.hostname, - portscan_result.hosts[0].get_open_port_numbers(), - ); - let service_detector = ServiceDetector::new(probe_setting); - let service_rx = service_detector.get_progress_receiver(); - let bar = ProgressBar::new(portscan_result.hosts[0].get_open_port_numbers().len() as u64); - if crate::app::is_quiet_mode() { - bar.set_draw_target(ProgressDrawTarget::hidden()); - } - bar.enable_steady_tick(Duration::from_millis(120)); - bar.set_style(output::get_progress_style()); - bar.set_position(0); - bar.set_message("ServiceDetection"); - let sd_start_time = std::time::Instant::now(); - let service_handle = thread::spawn(move || service_detector.run()); - // Print progress - while let Ok(_socket_addr) = service_rx.lock().unwrap().recv() { - bar.inc(1); - } - let sd_elapsed_time = sd_start_time.elapsed(); - bar.finish_with_message(format!("ServiceDetection ({:?})", sd_elapsed_time)); - let service_result = service_handle.join().unwrap(); - // Set service detection result to host - for port in &mut result.host.ports { - if let Some(result) = service_result.get(&port.number) { - port.service_name = result.service_name.clone(); - port.service_version = result.service_detail.clone().unwrap_or(String::new()); - } - } - // OS detection - if result.host.get_open_port_numbers().len() > 0 { - if let Some(fingerprint) = portscan_result - .get_syn_ack_fingerprint(result.host.ip_addr, result.host.get_open_port_numbers()[0]) - { - let os_fingerprint: OsFamilyFingerprint = - crate::db::verify_os_family_fingerprint(&fingerprint); - result.host.os_family = os_fingerprint.os_family; - } - } - // Set vendor name - if !crate::ip::is_global_addr(&result.host.ip_addr) { - if let Some(h) = portscan_result.get_host(result.host.ip_addr) { - if h.mac_addr != MacAddr::zero() { - let oui_map: HashMap = crate::db::get_oui_detail_map(); - let vendor_name = if h.mac_addr.address().len() > 16 { - let prefix8 = h.mac_addr.address()[0..8].to_uppercase(); - oui_map.get(&prefix8).unwrap_or(&String::new()).to_string() - } else { - oui_map - .get(&h.mac_addr.address()) - .unwrap_or(&String::new()) - .to_string() - }; - result.host.mac_addr = h.mac_addr; - result.host.vendor_name = vendor_name; - } - } - } - result.host.ttl = portscan_result.hosts[0].ttl; - result.port_scan_time = portscan_result.scan_time; - result.service_detection_time = sd_elapsed_time; - result.total_scan_time = portscan_result.scan_time + sd_elapsed_time; - result.scan_status = portscan_result.scan_status; - // Print results - if args.get_flag("json") { - let json_result = serde_json::to_string_pretty(&result).unwrap(); - println!("{}", json_result); - } else { - show_portscan_result(&result.host); - } - - output::log_with_time( - &format!("Total elapsed time {:?} ", result.total_scan_time), - "INFO", - ); - - match args.get_one::("save") { - Some(file_path) => { - match crate::fs::save_text(file_path, serde_json::to_string_pretty(&result).unwrap()) { - Ok(_) => { - output::log_with_time( - &format!("Saved to {}", file_path.to_string_lossy()), - "INFO", - ); - } - Err(e) => { - output::log_with_time(&format!("Failed to save: {}", e), "ERROR"); - } - } - } - None => {} - } -} - -pub fn print_option(setting: &PortScanSetting, interface: &Interface) { - if crate::app::is_quiet_mode() { - return; - } - println!(); - let mut tree = Tree::new(node_label("PortScan Config", None, None)); - let mut setting_tree = Tree::new(node_label("Settings", None, None)); - setting_tree.push(node_label( - "Protocol", - Some(setting.protocol.to_str()), - None, - )); - setting_tree.push(node_label( - "ScanType", - Some(setting.scan_type.to_str()), - None, - )); - setting_tree.push(node_label("InterfaceName", Some(&interface.name), None)); - setting_tree.push(node_label( - "Timeout", - Some(format!("{:?}", setting.timeout).as_str()), - None, - )); - setting_tree.push(node_label( - "WaitTime", - Some(format!("{:?}", setting.wait_time).as_str()), - None, - )); - setting_tree.push(node_label( - "SendRate", - Some(format!("{:?}", setting.send_rate).as_str()), - None, - )); - tree.push(setting_tree); - - let mut target_tree = Tree::new(node_label("Target", None, None)); - for target in &setting.targets { - target_tree.push(node_label( - "IP Address", - Some(&target.ip_addr.to_string()), - None, - )); - if target.ip_addr.to_string() != target.hostname && !target.hostname.is_empty() { - target_tree.push(node_label("Host Name", Some(&target.hostname), None)); - } - if target.ports.len() > 10 { - target_tree.push(node_label( - "Port", - Some(format!("{} port(s)", target.ports.len()).as_str()), - None, - )); - } else { - target_tree.push(node_label( - "Port", - Some(format!("{:?}", target.get_ports()).as_str()), - None, - )); - } - } - tree.push(target_tree); - println!("{}", tree); -} - -pub fn show_portscan_result(host: &Host) { - if !crate::app::is_quiet_mode() { - println!(); - } - let target_addr: String = - if host.ip_addr.to_string() != host.hostname && !host.hostname.is_empty() { - format!("{}({})", host.hostname, host.ip_addr) - } else { - host.ip_addr.to_string() - }; - let mut tree = Tree::new(node_label( - &format!("PortScan Result - {}", target_addr), - None, - None, - )); - let mut host_tree = Tree::new(node_label("Host Info", None, None)); - host_tree.push(node_label( - "IP Address", - Some(&host.ip_addr.to_string()), - None, - )); - host_tree.push(node_label("Host Name", Some(&host.hostname), None)); - if host.mac_addr != MacAddr::zero() { - host_tree.push(node_label( - "MAC Address", - Some(&host.mac_addr.to_string()), - None, - )); - } - if !host.vendor_name.is_empty() { - host_tree.push(node_label("Vendor Name", Some(&host.vendor_name), None)); - } - if !host.os_family.is_empty() { - host_tree.push(node_label("OS Family", Some(&host.os_family), None)); - } - let mut port_info_tree = Tree::new(node_label("Port Info", None, None)); - for port in &host.ports { - if port.status == PortStatus::Open { - let mut port_tree = Tree::new(node_label(&port.number.to_string(), None, None)); - port_tree.push(node_label("Status", Some(&port.status.name()), None)); - port_tree.push(node_label("Service Name", Some(&port.service_name), None)); - port_tree.push(node_label( - "Service Detail", - Some(&port.service_version), - None, - )); - port_info_tree.push(port_tree); - } - } - host_tree.push(port_info_tree); - tree.push(host_tree); - println!("{}", tree); -} diff --git a/src/handler/trace.rs b/src/handler/trace.rs deleted file mode 100644 index bb39a5e..0000000 --- a/src/handler/trace.rs +++ /dev/null @@ -1,300 +0,0 @@ -use crate::config::DEFAULT_BASE_TARGET_UDP_PORT; -use crate::output; -use crate::ping::result::TracerouteResult; -use crate::probe::ProbeStatusKind; -use crate::trace::setting::TraceSetting; -use crate::trace::tracer::Tracer; -use crate::util::tree::node_label; -use clap::ArgMatches; -use netdev::Interface; -use std::net::{IpAddr, SocketAddr}; -use std::path::PathBuf; -use std::str::FromStr; -use std::thread; -use std::time::Duration; -use termtree::Tree; - -pub fn handle_traceroute(args: &ArgMatches) { - output::log_with_time("Initiating traceroute...", "INFO"); - let trace_args = match args.subcommand_matches("trace") { - Some(matches) => matches, - None => return, - }; - let interface: Interface = if let Some(if_name) = args.get_one::("interface") { - match crate::interface::get_interface_by_name(if_name.to_string()) { - Some(iface) => iface, - None => return, - } - } else { - match netdev::get_default_interface() { - Ok(iface) => iface, - Err(_) => return, - } - }; - let target: String = match trace_args.get_one::("target") { - Some(target) => target.to_owned(), - None => return, - }; - let mut port: u16 = match trace_args.get_one::("port") { - Some(port) => *port, - None => DEFAULT_BASE_TARGET_UDP_PORT, - }; - let maxhop: u8 = match trace_args.get_one::("maxhop") { - Some(maxhop) => *maxhop, - None => 64, - }; - let dst_ip: IpAddr = match IpAddr::from_str(&target) { - Ok(ip_addr) => ip_addr, - Err(_) => match SocketAddr::from_str(&target) { - Ok(socket_addr) => { - port = socket_addr.port(); - socket_addr.ip() - } - Err(_) => match crate::dns::lookup_host_name(&target) { - Some(ip_addr) => ip_addr, - None => { - output::log_with_time("Failed to resolve domain", "ERROR"); - return; - } - }, - }, - }; - let timeout = match trace_args.get_one::("timeout") { - Some(timeout) => Duration::from_millis(*timeout), - None => Duration::from_secs(30), - }; - let wait_time = match trace_args.get_one::("waittime") { - Some(wait_time) => Duration::from_millis(*wait_time), - None => Duration::from_secs(1), - }; - let send_rate = match trace_args.get_one::("rate") { - Some(send_rate) => Duration::from_millis(*send_rate), - None => Duration::from_secs(1), - }; - let mut setting: TraceSetting = TraceSetting::udp_trace(&interface, dst_ip).unwrap(); - setting.dst_hostname = target - .split(":") - .collect::>() - .get(0) - .unwrap() - .to_string(); - setting.dst_port = port; - setting.hop_limit = maxhop; - setting.receive_timeout = wait_time; - setting.probe_timeout = timeout; - setting.send_rate = send_rate; - - let target_addr: String = - if setting.dst_ip.to_string() != setting.dst_hostname && !setting.dst_hostname.is_empty() { - format!("{}({})", setting.dst_hostname, setting.dst_ip) - } else { - setting.dst_ip.to_string() - }; - - print_option(&setting, &interface); - - let tracer: Tracer = Tracer::new(setting).unwrap(); - let rx = tracer.get_progress_receiver(); - let handle = thread::spawn(move || tracer.trace()); - for r in rx.lock().unwrap().iter() { - if r.probe_status.kind == crate::probe::ProbeStatusKind::Done { - output::log_with_time( - &format!( - "{} {} Bytes from {}, HOP:{}, TTL:{}, RTT:{:?}, NodeType: {}", - r.seq, - r.received_packet_size, - r.ip_addr, - r.hop, - r.ttl, - r.rtt, - r.node_type.name() - ), - "INFO", - ); - } else { - output::log_with_time(&format!("{} {}", r.seq, r.probe_status.message), "ERROR"); - } - } - match handle.join() { - Ok(trace_result) => match trace_result { - Ok(trace_result) => { - // Print results - if args.get_flag("json") { - let json_result = serde_json::to_string_pretty(&trace_result).unwrap(); - println!("{}", json_result); - } else { - show_trace_result(&trace_result, target_addr); - } - output::log_with_time( - &format!("Traceroute completed in: {:?}", trace_result.elapsed_time), - "INFO", - ); - match args.get_one::("save") { - Some(file_path) => { - match crate::fs::save_text( - file_path, - serde_json::to_string_pretty(&trace_result).unwrap(), - ) { - Ok(_) => { - output::log_with_time( - &format!("Saved to {}", file_path.to_string_lossy()), - "INFO", - ); - } - Err(e) => { - output::log_with_time(&format!("Failed to save: {}", e), "ERROR"); - } - } - } - None => {} - } - } - Err(e) => println!("{:?}", e), - }, - Err(e) => println!("{:?}", e), - } -} - -fn print_option(setting: &TraceSetting, interface: &Interface) { - if crate::app::is_quiet_mode() { - return; - } - println!(); - // Options - let mut tree = Tree::new(node_label("Traceroute Config", None, None)); - let mut setting_tree = Tree::new(node_label("Settings", None, None)); - setting_tree.push(node_label("Interface", Some(interface.name.as_str()), None)); - setting_tree.push(node_label( - "Protocol", - Some(format!("{:?}", setting.protocol).as_str()), - None, - )); - setting_tree.push(node_label( - "Hop Limit", - Some(setting.hop_limit.to_string().as_str()), - None, - )); - setting_tree.push(node_label( - "Timeout", - Some(format!("{:?}", setting.probe_timeout).as_str()), - None, - )); - setting_tree.push(node_label( - "Wait Time", - Some(format!("{:?}", setting.receive_timeout).as_str()), - None, - )); - setting_tree.push(node_label( - "Send Rate", - Some(format!("{:?}", setting.send_rate).as_str()), - None, - )); - tree.push(setting_tree); - // Target - let mut target_tree = Tree::new(node_label("Target", None, None)); - target_tree.push(node_label( - "IP Address", - Some(setting.dst_ip.to_string().as_str()), - None, - )); - if setting.dst_ip.to_string() != setting.dst_hostname && !setting.dst_hostname.is_empty() { - target_tree.push(node_label("Host Name", Some(&setting.dst_hostname), None)); - } - target_tree.push(node_label( - "Port", - Some(setting.dst_port.to_string().as_str()), - None, - )); - tree.push(target_tree); - println!("{}", tree); -} - -fn show_trace_result(trace_result: &TracerouteResult, target_addr: String) { - if !crate::app::is_quiet_mode() { - println!(); - } - let mut tree = Tree::new(node_label( - &format!("Traceroute Result - {}", target_addr), - None, - None, - )); - // Responses - let mut responses_tree = Tree::new(node_label("Responses", None, None)); - for response in &trace_result.nodes { - match response.probe_status.kind { - ProbeStatusKind::Done => { - let mut response_tree = Tree::new(node_label( - "Sequence", - Some(response.seq.to_string().as_str()), - None, - )); - response_tree.push(node_label( - "Status", - Some(&response.probe_status.kind.name()), - None, - )); - response_tree.push(node_label( - "IP Address", - Some(&response.ip_addr.to_string()), - None, - )); - response_tree.push(node_label( - "Protocol", - Some(format!("{:?}", response.protocol).as_str()), - None, - )); - response_tree.push(node_label( - "Received Bytes", - Some(response.received_packet_size.to_string().as_str()), - None, - )); - response_tree.push(node_label( - "HOP", - Some(response.hop.to_string().as_str()), - None, - )); - response_tree.push(node_label( - "TTL", - Some(response.ttl.to_string().as_str()), - None, - )); - response_tree.push(node_label( - "RTT", - Some(format!("{:?}", response.rtt).as_str()), - None, - )); - response_tree.push(node_label( - "NodeType", - Some(&response.node_type.name()), - None, - )); - responses_tree.push(response_tree); - } - _ => { - let mut response_tree = Tree::new(node_label( - "Sequence", - Some(response.seq.to_string().as_str()), - None, - )); - response_tree.push(node_label( - "Status", - Some(&response.probe_status.kind.name()), - None, - )); - response_tree.push(node_label( - "Message", - Some(&response.probe_status.message), - None, - )); - responses_tree.push(response_tree); - } - } - } - tree.push(responses_tree); - tree.push(node_label( - "Status", - Some(&trace_result.probe_status.kind.name()), - None, - )); - println!("{}", tree); -} diff --git a/src/host.rs b/src/host.rs deleted file mode 100644 index f045c7f..0000000 --- a/src/host.rs +++ /dev/null @@ -1,162 +0,0 @@ -use netdev::mac::MacAddr; -use serde::{Deserialize, Serialize}; -use std::net::IpAddr; - -use crate::dns; - -/// Status of the scanned port -#[derive(Deserialize, Serialize, Clone, Copy, Debug, PartialEq)] -pub enum PortStatus { - Open, - Closed, - Filtered, - Unknown, -} - -impl PortStatus { - pub fn id(&self) -> String { - match *self { - PortStatus::Open => String::from("open"), - PortStatus::Closed => String::from("closed"), - PortStatus::Filtered => String::from("filtered"), - PortStatus::Unknown => String::from("unknown"), - } - } - pub fn name(&self) -> String { - match *self { - PortStatus::Open => String::from("Open"), - PortStatus::Closed => String::from("Closed"), - PortStatus::Filtered => String::from("Filtered"), - PortStatus::Unknown => String::from("Unknown"), - } - } -} - -/// Port Information -#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] -pub struct Port { - /// Port number - pub number: u16, - /// Port status - pub status: PortStatus, - /// Service name - pub service_name: String, - /// Service version - pub service_version: String, -} - -impl Port { - pub fn new(number: u16) -> Self { - Self { - number: number, - status: PortStatus::Unknown, - service_name: String::new(), - service_version: String::new(), - } - } -} - -/// Host Information -#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] -pub struct Host { - /// IP address of the host - pub ip_addr: IpAddr, - /// Host name - pub hostname: String, - /// List of ports - pub ports: Vec, - /// MAC address of the host - pub mac_addr: MacAddr, - /// Vendor name - pub vendor_name: String, - /// OS Family - pub os_family: String, - /// TTL - pub ttl: u8, -} - -impl Host { - pub fn new(ip_addr: IpAddr, hostname: String) -> Self { - Self { - ip_addr: ip_addr, - hostname: hostname, - ports: Vec::new(), - mac_addr: MacAddr::zero(), - vendor_name: String::new(), - os_family: String::new(), - ttl: 0, - } - } - pub fn with_port_range(mut self, start: u16, end: u16) -> Self { - for port in start..end { - self.ports.push(Port::new(port)); - } - self - } - pub fn with_ports(mut self, ports: Vec) -> Self { - for port in ports { - self.ports.push(Port::new(port)); - } - self - } - pub fn get_ports(&self) -> Vec { - self.ports.iter().map(|port| port.number).collect() - } - pub fn get_open_port_numbers(&self) -> Vec { - self.ports - .iter() - .filter(|port| port.status == PortStatus::Open) - .map(|port| port.number) - .collect() - } - pub fn get_open_ports(&self) -> Vec { - self.ports - .iter() - .filter(|port| port.status == PortStatus::Open) - .map(|port| port.clone()) - .collect() - } -} - -/// Node type -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub enum NodeType { - DefaultGateway, - Relay, - Destination, -} - -impl NodeType { - pub fn id(&self) -> String { - match *self { - NodeType::DefaultGateway => String::from("default_gateway"), - NodeType::Relay => String::from("relay"), - NodeType::Destination => String::from("destination"), - } - } - pub fn name(&self) -> String { - match *self { - NodeType::DefaultGateway => String::from("DefaultGateway"), - NodeType::Relay => String::from("Relay"), - NodeType::Destination => String::from("Destination"), - } - } -} - -// Check if the target is an IP address -pub fn is_valid_ip_addr(target: &str) -> bool { - match target.parse::() { - Ok(_) => true, - Err(_) => false, - } -} - -// Check if the target is a valid hostname -pub fn is_valid_hostname(target: &str) -> bool { - dns::lookup_host_name(target).is_some() -} - -// Check if the target is valid -pub fn is_valid_target(target: &str) -> bool { - is_valid_ip_addr(target) || is_valid_hostname(target) -} diff --git a/src/interface/mod.rs b/src/interface.rs similarity index 80% rename from src/interface/mod.rs rename to src/interface.rs index 49e16db..e595889 100644 --- a/src/interface/mod.rs +++ b/src/interface.rs @@ -5,6 +5,7 @@ use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr}, }; +/// Get interface information by IP address. pub fn get_interface_by_ip(ip_addr: IpAddr) -> Option { for iface in netdev::interface::get_interfaces() { for ip in iface.ipv4.clone() { @@ -21,6 +22,7 @@ pub fn get_interface_by_ip(ip_addr: IpAddr) -> Option { return None; } +/// Get interface information by index. pub fn get_interface_by_index(index: u32) -> Option { for iface in netdev::interface::get_interfaces() { if iface.index == index { @@ -30,6 +32,7 @@ pub fn get_interface_by_index(index: u32) -> Option { return None; } +/// Get interface information by name. pub fn get_interface_by_name(name: String) -> Option { for iface in netdev::interface::get_interfaces() { if iface.name == name { @@ -39,31 +42,35 @@ pub fn get_interface_by_name(name: String) -> Option { return None; } -pub fn get_interface_ipv4(iface: &Interface) -> Option { +/// Get the first IPv4 address of the interface. +pub fn get_interface_ipv4(iface: &Interface) -> Option { for ip in iface.ipv4.clone() { - return Some(IpAddr::V4(ip.addr())); + return Some(ip.addr()); } return None; } -pub fn get_interface_global_ipv6(iface: &Interface) -> Option { +/// Get the first global IPv6 address of the interface. +pub fn get_interface_global_ipv6(iface: &Interface) -> Option { for ip in iface.ipv6.clone() { if nex::net::ip::is_global_ipv6(&ip.addr()) { - return Some(IpAddr::V6(ip.addr())); + return Some(ip.addr()); } } return None; } -pub fn get_interface_local_ipv6(iface: &Interface) -> Option { +/// Get the first local IPv6 address of the interface. +pub fn get_interface_local_ipv6(iface: &Interface) -> Option { for ip in iface.ipv6.clone() { if !nex::net::ip::is_global_ipv6(&ip.addr()) { - return Some(IpAddr::V6(ip.addr())); + return Some(ip.addr()); } } return None; } +/// Get all IP addresses of the interface as strings. pub fn get_interface_ips(iface: &Interface) -> Vec { let mut ips: Vec = Vec::new(); for ip in iface.ipv4.clone() { @@ -75,6 +82,7 @@ pub fn get_interface_ips(iface: &Interface) -> Vec { ips } +/// Get all local IP addresses on the specified interface. pub fn get_local_ips(if_index: u32) -> HashSet { let interface = get_interface_by_index(if_index).unwrap(); let mut ips: HashSet = HashSet::new(); @@ -90,6 +98,7 @@ pub fn get_local_ips(if_index: u32) -> HashSet { ips } +/// Get all local IP addresses on the default interface. pub fn get_default_local_ips() -> HashSet { // Default interface IP addresses let default_interface = netdev::get_default_interface().unwrap(); @@ -106,6 +115,7 @@ pub fn get_default_local_ips() -> HashSet { ips } +/// Get all local IP addresses on the specified interface. pub fn get_interface_local_ips(iface: &Interface) -> HashSet { let mut ips: HashSet = HashSet::new(); for ip in iface.ipv4.clone() { @@ -120,6 +130,7 @@ pub fn get_interface_local_ips(iface: &Interface) -> HashSet { ips } +/// Get a map of all local IP addresses to their corresponding interface names. pub fn get_local_ip_map() -> HashMap { let mut ip_map: HashMap = HashMap::new(); for iface in netdev::interface::get_interfaces() { @@ -144,6 +155,7 @@ pub fn get_usable_interfaces() -> Vec { usable_interfaces } +/// Get the MAC address of the interface, or MacAddr::zero() if not available. pub fn get_interface_macaddr(iface: &Interface) -> MacAddr { match &iface.mac_addr { Some(mac_addr) => mac_addr.clone(), @@ -151,9 +163,10 @@ pub fn get_interface_macaddr(iface: &Interface) -> MacAddr { } } +/// Get the MAC address of the gateway, or MacAddr::zero() if not available. pub fn get_gateway_macaddr(iface: &Interface) -> MacAddr { match &iface.gateway { Some(gateway) => gateway.mac_addr.clone(), None => MacAddr::zero(), } -} +} \ No newline at end of file diff --git a/src/ip/mod.rs b/src/ip/mod.rs deleted file mode 100644 index 28ec06e..0000000 --- a/src/ip/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::net::IpAddr; - -pub fn is_global_addr(ip_addr: &IpAddr) -> bool { - match ip_addr { - IpAddr::V4(ipv4) => nex::net::ip::is_global_ipv4(&ipv4), - IpAddr::V6(ipv6) => nex::net::ip::is_global_ipv6(&ipv6), - } -} - -pub fn guess_initial_ttl(ttl: u8) -> u8 { - if ttl <= 64 { - 64 - } else if 64 < ttl && ttl <= 128 { - 128 - } else { - 255 - } -} diff --git a/src/json/host.rs b/src/json/host.rs deleted file mode 100644 index 7678123..0000000 --- a/src/json/host.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::time::Duration; - -use serde::{Deserialize, Serialize}; - -use crate::{ - host::Host, - scan::result::{ScanResult, ScanStatus}, -}; - -/// Result of hostscan -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct HostScanResult { - /// List of scanned Host info and their respective ports - pub hosts: Vec, - /// Time taken to scan - pub scan_time: Duration, - /// Status of the scan task - pub scan_status: ScanStatus, -} - -impl HostScanResult { - /// Constructs a new PortScanResult - pub fn new() -> HostScanResult { - HostScanResult { - hosts: vec![], - scan_time: Duration::from_millis(0), - scan_status: ScanStatus::Error("Scan not started".to_string()), - } - } - pub fn from_scan_result(scan_result: &ScanResult) -> HostScanResult { - HostScanResult { - hosts: scan_result.hosts.clone(), - scan_time: scan_result.scan_time.clone(), - scan_status: scan_result.scan_status.clone(), - } - } -} diff --git a/src/json/mod.rs b/src/json/mod.rs deleted file mode 100644 index 4322a91..0000000 --- a/src/json/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod host; -pub mod port; diff --git a/src/json/port.rs b/src/json/port.rs deleted file mode 100644 index 49ae324..0000000 --- a/src/json/port.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::{net::IpAddr, time::Duration}; - -use serde::{Deserialize, Serialize}; - -use crate::{host::Host, scan::result::ScanStatus}; - -/// Result of portscan -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct PortScanResult { - /// Scanned Host info and their respective ports - pub host: Host, - /// Time taken to scan - pub port_scan_time: Duration, - /// Service detection time - pub service_detection_time: Duration, - /// Total scan time - pub total_scan_time: Duration, - /// Status of the scan task - pub scan_status: ScanStatus, -} - -impl PortScanResult { - /// Constructs a new PortScanResult - pub fn new(ip_addr: IpAddr, hostname: String) -> PortScanResult { - PortScanResult { - host: Host::new(ip_addr, hostname), - port_scan_time: Duration::new(0, 0), - service_detection_time: Duration::new(0, 0), - total_scan_time: Duration::new(0, 0), - scan_status: ScanStatus::Error("Scan not started".to_string()), - } - } -} diff --git a/src/log.rs b/src/log.rs new file mode 100644 index 0000000..ca37e20 --- /dev/null +++ b/src/log.rs @@ -0,0 +1,85 @@ +use anyhow::Result; +use tracing::level_filters::LevelFilter; +use std::fs::File; +use tracing_indicatif::IndicatifLayer; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; +use tracing_subscriber::{fmt, prelude::*, registry, filter::Targets}; + +use crate::cli::Cli; +use crate::time::LocalTimeOnly; + +/// Initialize the logger based on command-line arguments. +pub fn init_logger(cli_args: &Cli) -> Result<()> { + let indicatif_layer = IndicatifLayer::new(); + + // Format layer for console output (using a writer that coexists with the indicator) + let console_fmt = fmt::layer() + .with_target(false) + .with_timer(LocalTimeOnly) + .with_writer(indicatif_layer.get_stderr_writer()); + + // Console log filter + let console_filter = Targets::new() + .with_default(LevelFilter::OFF) + .with_target("nrev", cli_args.log_level.to_level_filter()); + + if !cli_args.no_stdout { + if cli_args.quiet { + // Quiet mode: suppress all logs except errors + let quiet_filter = LevelFilter::ERROR; + + registry() + .with(indicatif_layer) + .with(console_fmt.with_filter(quiet_filter)) + .init(); + return Ok(()); + } + + if !cli_args.log_file { + // Registry-based layer stacking + registry() + .with(indicatif_layer) + .with(console_fmt.with_filter(console_filter)) + .init(); + return Ok(()); + } + } + + // Determine log file path + let log_file_path = cli_args.log_file_path.clone() + .unwrap_or_else(|| crate::config::get_user_file_path("nrev.log").unwrap()); + + // Open log file in append mode + let file = File::options().create(true).append(true).open(&log_file_path)?; + + // File-specific fmt layer + let file_fmt = fmt::layer() + .with_ansi(false) + .with_target(false) + .with_timer(LocalTimeOnly) + .with_writer(file); + + #[cfg(debug_assertions)] + { + // debug: log_level for screen, ERROR only for file + let file_filter = LevelFilter::ERROR; + + registry() + .with(indicatif_layer) + .with(console_fmt.with_filter(console_filter)) + .with(file_fmt.with_filter(file_filter)) + .init(); + } + + #[cfg(not(debug_assertions))] + { + // release: no output to screen, ERROR only for file + let file_filter = LevelFilter::ERROR; + + registry() + .with(file_fmt.with_filter(file_filter)) + .init(); + } + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 8bb433c..1b930bd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,448 +1,103 @@ -// Core +pub mod cli; +pub mod cmd; pub mod config; -pub mod db; -pub mod dep; +pub mod endpoint; pub mod dns; -pub mod fp; -pub mod fs; -pub mod host; +pub mod scan; +pub mod output; +pub mod capture; pub mod interface; -pub mod ip; -pub mod json; -pub mod neighbor; pub mod packet; -pub mod pcap; +pub mod time; +pub mod log; +pub mod service; +pub mod db; +pub mod os; pub mod ping; -pub mod probe; pub mod protocol; -pub mod scan; -pub mod sys; -pub mod tls; -pub mod trace; +pub mod probe; pub mod util; -// CLI -pub mod app; -pub mod handler; -pub mod output; +pub mod nei; +pub mod trace; -use app::{AppCommands, CRATE_REPOSITORY}; -use clap::{crate_description, crate_name, crate_version, value_parser}; -use clap::{Arg, ArgMatches, Command}; -use std::env; -use std::path::PathBuf; +use clap::Parser; +use cli::{Cli, Command}; -fn main() { - let args: Vec = env::args().collect(); - if args.len() < 2 { - app::show_app_desc(); - std::process::exit(0); - } - let arg_matches: ArgMatches = parse_args(); - match app::set_quiet_mode(arg_matches.get_flag("quiet")) { - Ok(_) => {} - Err(e) => { - println!("Failed to set quiet mode.{}", e); - std::process::exit(1); - } - } - let subcommand_name = arg_matches.subcommand_name().unwrap_or(""); - let app_command = AppCommands::from_str(subcommand_name); - app::show_banner_with_starttime(); - check_deps(); - match app_command { - Some(AppCommands::PortScan) => { - handler::port::handle_portscan(&arg_matches); - } - Some(AppCommands::HostScan) => { - handler::host::handle_hostscan(&arg_matches); - } - Some(AppCommands::Ping) => { - handler::ping::handle_ping(&arg_matches); - } - Some(AppCommands::Trace) => { - handler::trace::handle_traceroute(&arg_matches); - } - Some(AppCommands::Subdomain) => { - handler::dns::handle_subdomain_scan(&arg_matches); +use crate::db::DbInitializer; + +#[tokio::main] +async fn main() { + // Parse command line arguments + let cli = Cli::parse(); + // Initialize logger + let _ = log::init_logger(&cli); + // Start nrev + let start_time = std::time::Instant::now(); + tracing::info!("nrev v{} started", env!("CARGO_PKG_VERSION")); + + match cli.command { + Command::Port(args) => { + DbInitializer::with_all().init().await; + let r = cmd::port::run(args, cli.no_stdout, cli.output).await; + match r { + Ok(_) => {}, + Err(e) => tracing::error!("Port scan failed: {}", e), + } } - Some(AppCommands::Neighbor) => { - handler::neighbor::handle_neighbor_discovery(&arg_matches); + Command::Host(args) => { + let db_ini = DbInitializer::new(); + db_ini.with_os_db().with_oui_db().init().await; + + let r = cmd::host::run(args, cli.no_stdout, cli.output).await; + match r { + Ok(_) => {}, + Err(e) => tracing::error!("Host scan failed: {}", e), + } } - Some(AppCommands::Interfaces) => { - handler::interface::show_interfaces(&arg_matches); + Command::Ping(args) => { + let db_ini = DbInitializer::new(); + db_ini.with_os_db().with_oui_db().init().await; + + let r = cmd::ping::run(args, cli.no_stdout, cli.output).await; + match r { + Ok(_) => {}, + Err(e) => tracing::error!("Ping failed: {}", e), + } } - Some(AppCommands::Interface) => { - handler::interface::show_default_interface(&arg_matches); + Command::Trace(args) => { + let db_ini = DbInitializer::new(); + db_ini.with_oui_db().init().await; + + let r = cmd::trace::run(args, cli.no_stdout, cli.output).await; + match r { + Ok(_) => {}, + Err(e) => tracing::error!("Trace failed: {}", e), + } } - Some(AppCommands::CheckDependencies) => { - handler::check::check_dependencies(&arg_matches); + Command::Nei(args) => { + let db_ini = DbInitializer::new(); + db_ini.with_oui_db().init().await; + + let r = cmd::nei::run(args, cli.no_stdout, cli.output).await; + match r { + Ok(_) => {}, + Err(e) => tracing::error!("Neighbor discovery failed: {}", e), + } } - None => match arg_matches.get_one::("target") { - Some(target_host) => { - if crate::host::is_valid_target(target_host) { - handler::default_probe(target_host, &arg_matches); - } else { - app::show_error_with_help(&format!("Invalid target: {}", target_host)); - } + Command::Domain(args) => { + let r = cmd::domain::run(args, cli.no_stdout, cli.output).await; + match r { + Ok(_) => {}, + Err(e) => tracing::error!("Domain scan failed: {}", e), } - None => { - app::show_error_with_help("No target specified"); + } + Command::Interface(args) => { + let r = cmd::interface::show(&args); + match r { + Ok(_) => {}, + Err(e) => tracing::error!("Show interfaces failed: {}", e), } - }, - } -} - -fn parse_args() -> ArgMatches { - let app_description: &str = crate_description!(); - let app: Command = Command::new(crate_name!()) - .version(crate_version!()) - .about(format!("{} \n{}", app_description, CRATE_REPOSITORY)) - .allow_external_subcommands(true) - .arg(Arg::new("target") - .help("Specify the target host. IP address or Hostname") - .short('t') - .long("target") - .value_name("target") - .display_order(1) - .value_parser(value_parser!(String)) - ) - .arg(Arg::new("interface") - .help("Specify the network interface") - .short('i') - .long("interface") - .value_name("interface_name") - .display_order(2) - .value_parser(value_parser!(String)) - ) - .arg(Arg::new("noping") - .help("Disable initial ping") - .long("noping") - .num_args(0) - ) - .arg(Arg::new("full") - .help("Scan all ports (1-65535)") - .short('F') - .long("full") - .num_args(0) - ) - .arg(Arg::new("json") - .help("Displays results in JSON format.") - .short('j') - .long("json") - .num_args(0) - ) - .arg(Arg::new("save") - .help("Save scan result in JSON format - Example: -o result.json") - .short('o') - .long("save") - .value_name("file_path") - .value_parser(value_parser!(PathBuf)) - ) - .arg(Arg::new("quiet") - .help("Quiet mode. Suppress output. Only show final results.") - .short('q') - .long("quiet") - .num_args(0) - ) - .subcommand(Command::new("port") - .about("Scan port. nrev port --help for more information") - .arg(Arg::new("target") - .help("Specify the target. IP address or Hostname") - .value_name("target") - .value_parser(value_parser!(String)) - .required(true) - ) - .arg(Arg::new("ports") - .help("Specify the ports. Example: 80,443,8080") - .short('p') - .long("ports") - .value_name("ports") - .value_delimiter(',') - .value_parser(value_parser!(u16)) - ) - .arg(Arg::new("range") - .help("Specify the port range. Example: 1-100") - .short('r') - .long("range") - .value_name("range") - .value_delimiter('-') - .value_parser(value_parser!(u16)) - ) - .arg(Arg::new("scantype") - .help("Specify the scan-type") - .short('T') - .long("scantype") - .value_name("scantype") - .value_parser(value_parser!(String)) - ) - .arg(Arg::new("service") - .help("Enable service detection") - .short('S') - .long("service") - .num_args(0) - ) - .arg(Arg::new("random") - .help("Don't randomize targets. By default, nrev randomizes the order of targets.") - .short('R') - .long("random") - .num_args(0) - ) - .arg(Arg::new("wellknown") - .help("Use well-known ports") - .short('W') - .long("wellknown") - .num_args(0) - ) - .arg(Arg::new("full") - .help("Scan all ports (1-65535)") - .short('F') - .long("full") - .num_args(0) - ) - .arg(Arg::new("noping") - .help("Disable initial ping") - .long("noping") - .num_args(0) - ) - .arg(Arg::new("timeout") - .help("Set timeout in ms - Example: --timeout 10000") - .long("timeout") - .value_name("timeout") - .value_parser(value_parser!(u64)) - ) - .arg(Arg::new("waittime") - .help("Set wait-time in ms (default:100ms) - Example: -w 200") - .short('w') - .long("waittime") - .value_name("waittime") - .value_parser(value_parser!(u64)) - ) - .arg(Arg::new("rate") - .help("Set send-rate in ms - Example: --rate 1") - .long("rate") - .value_name("duration") - .value_parser(value_parser!(u64)) - ) - ) - .subcommand(Command::new("host") - .about("Scan host in specified network or host-list. nrev host --help for more information") - .arg(Arg::new("target") - .help("Specify the target network") - .value_name("target") - .required(true) - ) - .arg(Arg::new("protocol") - .help("Specify the protocol") - .short('P') - .long("protocol") - .value_name("protocol_name") - .value_parser(value_parser!(String)) - ) - .arg(Arg::new("port") - .help("Specify the port. Example: --port 80") - .short('p') - .long("port") - .value_name("port") - .value_parser(value_parser!(u16)) - ) - .arg(Arg::new("random") - .help("Don't randomize targets. By default, nrev randomizes the order of targets.") - .short('R') - .long("random") - .num_args(0) - ) - .arg(Arg::new("timeout") - .help("Set timeout in ms - Example: --timeout 10000") - .long("timeout") - .value_name("timeout") - .value_parser(value_parser!(u64)) - ) - .arg(Arg::new("waittime") - .help("Set wait-time in ms (default:100ms) - Example: -w 200") - .short('w') - .long("waittime") - .value_name("waittime") - .value_parser(value_parser!(u64)) - ) - .arg(Arg::new("rate") - .help("Set send-rate in ms - Example: --rate 1") - .long("rate") - .value_name("duration") - .value_parser(value_parser!(u64)) - ) - ) - .subcommand(Command::new("ping") - .about("Ping to specified host. nrev ping --help for more information") - .arg(Arg::new("target") - .help("Specify the target. IP address or Hostname") - .value_name("target") - .required(true) - ) - .arg(Arg::new("count") - .help("Set number of requests or pings to be sent") - .short('c') - .long("count") - .value_name("count") - .value_parser(value_parser!(u32)) - ) - .arg(Arg::new("port") - .help("Specify the port. Example: 80") - .short('p') - .long("port") - .value_name("port") - .value_parser(value_parser!(u16)) - .required(false) - ) - .arg(Arg::new("maxhop") - .help("Set max hop(TTL) for ping or traceroute") - .long("maxhop") - .value_name("maxhop") - .value_parser(value_parser!(u8)) - ) - .arg(Arg::new("protocol") - .help("Specify the protocol") - .short('P') - .long("protocol") - .value_name("protocol_name") - .value_parser(value_parser!(String)) - ) - .arg(Arg::new("timeout") - .help("Set timeout in ms - Example: --timeout 10000") - .long("timeout") - .value_name("timeout") - .value_parser(value_parser!(u64)) - ) - .arg(Arg::new("waittime") - .help("Set wait-time in ms (default:100ms) - Example: -w 200") - .short('w') - .long("waittime") - .value_name("waittime") - .value_parser(value_parser!(u64)) - ) - .arg(Arg::new("rate") - .help("Set send-rate in ms - Example: --rate 100") - .long("rate") - .value_name("duration") - .value_parser(value_parser!(u64)) - ) - ) - .subcommand(Command::new("trace") - .about("Traceroute to specified host. nrev trace --help for more information") - .arg(Arg::new("target") - .help("Specify the target. IP address or Hostname") - .value_name("target") - .required(true) - ) - .arg(Arg::new("port") - .help("Specify the port. Example: 33435") - .short('p') - .long("port") - .value_name("port") - .value_parser(value_parser!(u16)) - .required(false) - ) - .arg(Arg::new("maxhop") - .help("Set max hop(TTL) for ping or traceroute") - .long("maxhop") - .value_name("maxhop") - .value_parser(value_parser!(u8)) - ) - .arg(Arg::new("timeout") - .help("Set timeout in ms - Example: --timeout 10000") - .long("timeout") - .value_name("timeout") - .value_parser(value_parser!(u64)) - ) - .arg(Arg::new("waittime") - .help("Set wait-time in ms (default:100ms) - Example: -w 200") - .short('w') - .long("waittime") - .value_name("waittime") - .value_parser(value_parser!(u64)) - ) - .arg(Arg::new("rate") - .help("Set send-rate in ms - Example: --rate 100") - .long("rate") - .value_name("duration") - .value_parser(value_parser!(u64)) - ) - ) - .subcommand(Command::new("subdomain") - .about("Find subdomains. nrev subdomain --help for more information") - .arg(Arg::new("target") - .help("Specify the target apex-domain") - .value_name("target") - .required(true) - ) - .arg(Arg::new("wordlist") - .help("Specify the wordlist file path") - .short('w') - .long("wordlist") - .value_name("file_path") - .value_parser(value_parser!(PathBuf)) - ) - .arg(Arg::new("timeout") - .help("Set timeout in ms - Example: --timeout 10000") - .long("timeout") - .value_name("timeout") - .value_parser(value_parser!(u64)) - ) - ) - .subcommand(Command::new("nei") - .about("Resolve IP address to MAC address") - .arg(Arg::new("target") - .help("Specify the target IP address") - .value_name("target") - .required(true) - ) - .arg(Arg::new("count") - .help("Set number of requests or pings to be sent") - .short('c') - .long("count") - .value_name("count") - .value_parser(value_parser!(u32)) - ) - .arg(Arg::new("timeout") - .help("Set timeout in ms - Example: --timeout 10000") - .long("timeout") - .value_name("timeout") - .value_parser(value_parser!(u64)) - ) - .arg(Arg::new("waittime") - .help("Set wait-time in ms (default:100ms) - Example: -w 200") - .short('w') - .long("waittime") - .value_name("waittime") - .value_parser(value_parser!(u64)) - ) - .arg(Arg::new("rate") - .help("Set send-rate in ms - Example: --rate 1") - .long("rate") - .value_name("duration") - .value_parser(value_parser!(u64)) - ) - ) - .subcommand(Command::new("interfaces") - .about("Show network interfaces") - ) - .subcommand(Command::new("interface") - .about("Show default network interface") - ) - .subcommand(Command::new("check") - .about("Check dependencies (Windows only)") - ) - ; - app.get_matches() -} - -fn check_deps() { - match crate::dep::check_dependencies() { - Ok(_) => {} - Err(e) => { - println!("Dependency error:"); - println!("{}", e); - println!("Exiting..."); - std::process::exit(1); } } + tracing::info!("nrev v{} completed in {:?}", env!("CARGO_PKG_VERSION"), start_time.elapsed()); } diff --git a/src/nei/arp.rs b/src/nei/arp.rs new file mode 100644 index 0000000..43e3ab5 --- /dev/null +++ b/src/nei/arp.rs @@ -0,0 +1,88 @@ +use anyhow::Result; +use netdev::Interface; +use nex::packet::{ + arp::ArpOperation, + frame::{Frame, ParseOption}, +}; +use std::{net::{IpAddr, Ipv4Addr}, time::{Duration, Instant}}; +use futures::stream::StreamExt; +use futures::future::poll_fn; +use nex::datalink::async_io::{async_channel, AsyncChannel}; +use crate::nei::NeighborDiscoveryResult; + +/// Send an ARP request to the specified IPv4 address on the given interface and wait for a reply. +pub async fn send_arp(ipv4_addr: Ipv4Addr, iface: &Interface, recv_timeout: Duration) -> Result { + let next_hop = crate::util::ip::next_hop_ip(iface, IpAddr::V4(ipv4_addr)) + .ok_or_else(|| anyhow::anyhow!("No next hop found for {}", ipv4_addr))?; + + // Create sender + let config = nex::datalink::Config { + write_buffer_size: 4096, + read_buffer_size: 4096, + read_timeout: Some(recv_timeout), + write_timeout: None, + channel_type: nex::datalink::ChannelType::Layer2, + bpf_fd_attempts: 1000, + linux_fanout: None, + promiscuous: false, + }; + + let AsyncChannel::Ethernet(mut tx, mut rx) = async_channel(&iface, config)? + else { + unreachable!(); + }; + + let arp_packet = crate::packet::arp::build_arp_packet(iface, next_hop); + + let start_time = Instant::now(); + + match poll_fn(|cx| tx.poll_send(cx, &arp_packet)).await { + Ok(_) => { + }, + Err(e) => eprintln!("Failed to send packet: {}", e), + } + + loop { + match tokio::time::timeout(recv_timeout, rx.next()).await { + Ok(Some(Ok(packet))) => { + let frame = Frame::from_buf(&packet, ParseOption::default()).unwrap(); + match &frame.datalink { + Some(dlink) => { + if let Some(arp) = &dlink.arp { + if arp.operation == ArpOperation::Reply + && arp.sender_proto_addr == next_hop + { + let rtt = Instant::now().duration_since(start_time); + let ndp_result = NeighborDiscoveryResult { + mac_addr: arp.sender_hw_addr, + vendor: super::lookup_vendor(&arp.sender_hw_addr), + ip_addr: IpAddr::V4(arp.sender_proto_addr), + hostname: None, + rtt, + protocol: crate::protocol::Protocol::Arp, + if_name: iface.name.clone(), + if_friendly_name: iface.friendly_name.clone(), + if_index: iface.index, + }; + return Ok(ndp_result); + } + } + } + None => continue, + } + } + Ok(Some(Err(e))) => { + tracing::error!("Failed to receive packet: {}", e); + anyhow::bail!("Failed to receive packet: {}", e); + }, + Ok(None) => { + tracing::error!("Channel closed"); + anyhow::bail!("Channel closed"); + }, + Err(_) => { + tracing::error!("Request timeout"); + anyhow::bail!("Request timeout"); + } + } + } +} diff --git a/src/nei/mod.rs b/src/nei/mod.rs new file mode 100644 index 0000000..eee8259 --- /dev/null +++ b/src/nei/mod.rs @@ -0,0 +1,54 @@ +pub mod arp; +pub mod ndp; + +use serde::{Deserialize, Serialize}; +use netdev::mac::MacAddr; +use std::{net::{IpAddr, Ipv4Addr}, time::Duration}; + +use crate::protocol::Protocol; + +/// Result of Neighbor Discovery +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct NeighborDiscoveryResult { + pub mac_addr: MacAddr, + pub vendor: Option, + pub ip_addr: IpAddr, + pub hostname: Option, + /// Round Trip Time (microsecond) + pub rtt: Duration, + /// Protocol + pub protocol: Protocol, + pub if_name: String, + pub if_friendly_name: Option, + pub if_index: u32, +} + +impl NeighborDiscoveryResult { + /// Construct a new NeighborDiscoveryResult instance + pub fn new() -> NeighborDiscoveryResult { + NeighborDiscoveryResult { + mac_addr: MacAddr::zero(), + vendor: None, + ip_addr: IpAddr::V4(Ipv4Addr::UNSPECIFIED), + hostname: None, + rtt: Duration::from_millis(0), + protocol: Protocol::Icmp, + if_name: String::new(), + if_friendly_name: None, + if_index: 0, + } + } +} + +/// Lookup the vendor name for a given MAC address using the OUI database. +pub fn lookup_vendor(mac: &MacAddr) -> Option { + let oui_db = crate::db::oui::oui_db(); + if let Some(oui) = oui_db.lookup_mac(mac) { + if let Some(vendor_detail) = &oui.vendor_detail { + return Some(vendor_detail.clone()); + } else { + return Some(oui.vendor.clone()); + } + } + None +} diff --git a/src/nei/ndp.rs b/src/nei/ndp.rs new file mode 100644 index 0000000..5ea0b2f --- /dev/null +++ b/src/nei/ndp.rs @@ -0,0 +1,114 @@ +use anyhow::Result; +use netdev::Interface; +use nex::packet::{ + frame::{Frame, ParseOption}, + icmpv6::Icmpv6Type, +}; +use std::{net::{IpAddr, Ipv6Addr}, time::{Duration, Instant}}; +use futures::stream::StreamExt; +use futures::future::poll_fn; +use nex::datalink::async_io::{async_channel, AsyncChannel}; +use crate::nei::NeighborDiscoveryResult; + +/// Send an NDP (Neighbor Discovery Protocol) request to the specified IPv6 address on the given interface and wait for a reply. +pub async fn send_ndp(ipv6_addr: Ipv6Addr, iface: &Interface, recv_timeout: Duration) -> Result { + let src_ip = iface + .ipv6 + .iter() + .map(|n| n.addr()) + .find(|ip| ip.segments()[0] == 0xfe80) + .unwrap_or_else(|| iface.ipv6[0].addr()); + let next_hop = crate::util::ip::next_hop_ip(iface, IpAddr::V6(ipv6_addr)) + .ok_or_else(|| anyhow::anyhow!("No next hop found for {}", ipv6_addr))?; + + // Create sender + let config = nex::datalink::Config { + write_buffer_size: 4096, + read_buffer_size: 4096, + read_timeout: Some(recv_timeout), + write_timeout: None, + channel_type: nex::datalink::ChannelType::Layer2, + bpf_fd_attempts: 1000, + linux_fanout: None, + promiscuous: false, + }; + + let AsyncChannel::Ethernet(mut tx, mut rx) = async_channel(&iface, config)? + else { + unreachable!(); + }; + + let arp_packet = crate::packet::ndp::build_ndp_packet(iface, next_hop); + + let start_time = Instant::now(); + + match poll_fn(|cx| tx.poll_send(cx, &arp_packet)).await { + Ok(_) => { + }, + Err(e) => eprintln!("Failed to send packet: {}", e), + } + + loop { + match tokio::time::timeout(recv_timeout, rx.next()).await { + Ok(Some(Ok(packet))) => { + let mut parse_option = ParseOption::default(); + if iface.is_tun() { + parse_option.from_ip_packet = true; + parse_option.offset = if iface.is_loopback() { 14 } else { 0 }; + } + + if let Some(frame) = Frame::from_buf(&packet, parse_option) { + if let Some(ip_layer) = &frame.ip { + if let Some(icmpv6) = &ip_layer.icmpv6 { + if icmpv6.icmpv6_type == Icmpv6Type::NeighborAdvertisement { + if let Some(ipv6_hdr) = &ip_layer.ipv6 { + if let Some(dlink) = &frame.datalink { + if let Some(eth) = &dlink.ethernet { + // eth.source is the MAC address of the device that replied + if ipv6_hdr.destination == src_ip + && ipv6_hdr.source == ipv6_addr + { + let rtt = Instant::now().duration_since(start_time); + let ndp_result = NeighborDiscoveryResult { + mac_addr: eth.source, + vendor: super::lookup_vendor(ð.source), + ip_addr: IpAddr::V6(ipv6_hdr.source), + hostname: None, + rtt, + protocol: crate::protocol::Protocol::Ndp, + if_name: iface.name.clone(), + if_friendly_name: iface.friendly_name.clone(), + if_index: iface.index, + }; + return Ok(ndp_result); + + } else { + eprintln!( + "Received NDP reply from unexpected source: {}", + ipv6_hdr.source + ); + continue; + } + } + } + } + } + } + } + } + } + Ok(Some(Err(e))) => { + tracing::error!("Failed to receive packet: {}", e); + anyhow::bail!("Failed to receive packet: {}", e); + }, + Ok(None) => { + tracing::error!("Channel closed"); + anyhow::bail!("Channel closed"); + }, + Err(_) => { + tracing::error!("Request timeout"); + anyhow::bail!("Request timeout"); + } + } + } +} diff --git a/src/neighbor/mod.rs b/src/neighbor/mod.rs deleted file mode 100644 index 6fa8815..0000000 --- a/src/neighbor/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod resolver; -pub mod result; -pub mod setting; diff --git a/src/neighbor/resolver.rs b/src/neighbor/resolver.rs deleted file mode 100644 index 7de7411..0000000 --- a/src/neighbor/resolver.rs +++ /dev/null @@ -1,363 +0,0 @@ -use netdev::interface::Interface; -use nex::datalink::{RawReceiver, RawSender}; -use nex::packet::arp::ArpOperation; -use nex::packet::frame::{Frame, ParseOption}; -use nex::packet::icmpv6::Icmpv6Type; -use std::net::IpAddr; -use std::sync::mpsc::{channel, Receiver, Sender}; -use std::sync::{Arc, Mutex}; -use std::time::{Duration, Instant}; - -use super::result::DeviceResolveResult; -use super::setting::AddressResolveSetting; -use crate::host::NodeType; -use crate::packet::setting::PacketBuildSetting; -use crate::probe::{ProbeResult, ProbeStatus}; -use crate::protocol::Protocol; - -/// Device Resolver structure. -/// -/// Supports ARP and NDP. -pub struct DeviceResolver { - /// Probe Setting - pub probe_setting: AddressResolveSetting, - /// Sender for progress messaging - tx: Arc>>, - /// Receiver for progress messaging - rx: Arc>>, -} - -impl DeviceResolver { - /// Create new DeviceResolver instance with setting - pub fn new(setting: AddressResolveSetting) -> Result { - // Check interface - if crate::interface::get_interface_by_index(setting.if_index).is_none() { - return Err(format!( - "Pinger::new: unable to get interface. index: {}", - setting.if_index - )); - } - let (tx, rx) = channel(); - let pinger = DeviceResolver { - probe_setting: setting, - tx: Arc::new(Mutex::new(tx)), - rx: Arc::new(Mutex::new(rx)), - }; - return Ok(pinger); - } - /// Run arp/ndp - pub fn resolve(&self) -> Result { - run_resolver(&self.probe_setting, &self.tx) - } - /// Get progress receiver - pub fn get_progress_receiver(&self) -> Arc>> { - self.rx.clone() - } -} - -fn run_resolver( - setting: &AddressResolveSetting, - msg_tx: &Arc>>, -) -> Result { - let interface: Interface = match crate::interface::get_interface_by_index(setting.if_index) { - Some(interface) => interface, - None => { - return Err(format!( - "run_ping: unable to get interface by index {}", - setting.if_index - )) - } - }; - let config = nex::datalink::Config { - write_buffer_size: 4096, - read_buffer_size: 4096, - read_timeout: Some(setting.receive_timeout), - write_timeout: None, - channel_type: nex::datalink::ChannelType::Layer2, - bpf_fd_attempts: 1000, - linux_fanout: None, - promiscuous: false, - }; - // Create a channel to send/receive packet - let (mut tx, mut rx) = match nex::datalink::channel(&interface, config) { - Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), - Ok(_) => return Err("run_ping: unable to create channel".to_string()), - Err(e) => return Err(format!("run_ping: unable to create channel: {}", e)), - }; - match setting.protocol { - crate::protocol::Protocol::ARP => { - let result = run_arp(&mut tx, &mut rx, setting, msg_tx); - return Ok(result); - } - crate::protocol::Protocol::NDP => { - let result = run_ndp(&mut tx, &mut rx, setting, msg_tx); - return Ok(result); - } - _ => { - return Err("run_ping: unsupported protocol".to_string()); - } - } -} - -pub(crate) fn run_arp( - tx: &mut Box, - rx: &mut Box, - setting: &AddressResolveSetting, - msg_tx: &Arc>>, -) -> DeviceResolveResult { - let mut result = DeviceResolveResult::new(); - result.protocol = Protocol::ARP; - let mut parse_option: ParseOption = ParseOption::default(); - if setting.tunnel { - let payload_offset = if setting.loopback { 14 } else { 0 }; - parse_option.from_ip_packet = true; - parse_option.offset = payload_offset; - } - result.start_time = crate::sys::time::get_sysdate(); - let start_time = Instant::now(); - let mut responses: Vec = Vec::new(); - let packet_setting: PacketBuildSetting = - PacketBuildSetting::from_address_resolve_settomg(setting); - let arp_packet: Vec = crate::packet::arp::build_arp_packet(packet_setting.clone()); - for seq in 1..setting.count + 1 { - let send_time = Instant::now(); - match tx.send(&arp_packet) { - Some(_) => {} - None => {} - } - loop { - match rx.next() { - Ok(packet) => { - let recv_time: Duration = Instant::now().duration_since(send_time); - let frame: Frame = Frame::from_bytes(&packet, parse_option.clone()); - // Datalink - if let Some(datalink_layer) = &frame.datalink { - // Ethernet - if let Some(_ethernet_header) = &datalink_layer.ethernet { - // ARP - if let Some(arp_header) = &datalink_layer.arp { - if IpAddr::V4(arp_header.sender_proto_addr) != setting.dst_ip - || IpAddr::V4(arp_header.target_proto_addr) - != packet_setting.src_ip - { - continue; - } - if arp_header.operation == ArpOperation::Reply { - let probe_result: ProbeResult = ProbeResult { - seq: seq, - mac_addr: arp_header.sender_hw_addr, - ip_addr: setting.dst_ip, - host_name: setting.dst_hostname.clone(), - port_number: None, - port_status: None, - ttl: 0, - hop: 0, - rtt: recv_time, - probe_status: ProbeStatus::new(), - protocol: Protocol::ARP, - node_type: NodeType::Destination, - sent_packet_size: arp_packet.len(), - received_packet_size: packet.len(), - }; - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - } - } - } - Err(_e) => { - let probe_result = ProbeResult::timeout( - seq, - setting.dst_ip, - setting.dst_hostname.clone(), - Protocol::ARP, - arp_packet.len(), - ); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - let wait_time: Duration = Instant::now().duration_since(send_time); - if wait_time > setting.receive_timeout { - let probe_result = ProbeResult::timeout( - seq, - setting.dst_ip, - setting.dst_hostname.clone(), - Protocol::ARP, - arp_packet.len(), - ); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - if seq < setting.count { - std::thread::sleep(setting.send_rate); - } - } - let probe_time = Instant::now().duration_since(start_time); - result.end_time = crate::sys::time::get_sysdate(); - result.elapsed_time = probe_time; - if responses.len() > 0 { - result.probe_status = ProbeStatus::new(); - } else { - result.probe_status = ProbeStatus::with_error_message("No response".to_string()); - } - result.results = responses; - result -} - -pub(crate) fn run_ndp( - tx: &mut Box, - rx: &mut Box, - setting: &AddressResolveSetting, - msg_tx: &Arc>>, -) -> DeviceResolveResult { - let mut result = DeviceResolveResult::new(); - result.protocol = Protocol::NDP; - let mut parse_option: ParseOption = ParseOption::default(); - if setting.tunnel { - let payload_offset = if setting.loopback { 14 } else { 0 }; - parse_option.from_ip_packet = true; - parse_option.offset = payload_offset; - } - result.start_time = crate::sys::time::get_sysdate(); - let start_time = Instant::now(); - let mut responses: Vec = Vec::new(); - let packet_setting: PacketBuildSetting = - PacketBuildSetting::from_address_resolve_settomg(setting); - let ndp_packet: Vec = crate::packet::ndp::build_ndp_packet(packet_setting.clone()); - for seq in 1..setting.count + 1 { - let send_time = Instant::now(); - match tx.send(&ndp_packet) { - Some(_) => {} - None => {} - } - loop { - match rx.next() { - Ok(packet) => { - let recv_time: Duration = Instant::now().duration_since(send_time); - let frame: Frame = Frame::from_bytes(&packet, parse_option.clone()); - // Datalink - if let Some(datalink_layer) = &frame.datalink { - // Ethernet - if let Some(ethernet_header) = &datalink_layer.ethernet { - if let Some(ip_layer) = &frame.ip { - // IPv6 - if let Some(ipv6_header) = &ip_layer.ipv6 { - if IpAddr::V6(ipv6_header.source) != setting.dst_ip - || IpAddr::V6(ipv6_header.destination) - != packet_setting.src_ip - { - continue; - } - // ICMPv6 - if let Some(icmpv6_header) = &ip_layer.icmpv6 { - if icmpv6_header.icmpv6_type - == Icmpv6Type::NeighborAdvertisement - { - let probe_result: ProbeResult = ProbeResult { - seq: seq, - mac_addr: ethernet_header.source, - ip_addr: setting.dst_ip, - host_name: setting.dst_hostname.clone(), - port_number: None, - port_status: None, - ttl: ipv6_header.hop_limit, - hop: crate::ip::guess_initial_ttl( - ipv6_header.hop_limit, - ) - ipv6_header.hop_limit, - rtt: recv_time, - probe_status: ProbeStatus::new(), - protocol: Protocol::NDP, - node_type: NodeType::Destination, - sent_packet_size: ndp_packet.len(), - received_packet_size: packet.len(), - }; - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - } - } - } - } - } - Err(_e) => { - let probe_result = ProbeResult::timeout( - seq, - setting.dst_ip, - setting.dst_hostname.clone(), - Protocol::NDP, - ndp_packet.len(), - ); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - let wait_time: Duration = Instant::now().duration_since(send_time); - if wait_time > setting.receive_timeout { - let probe_result = ProbeResult::timeout( - seq, - setting.dst_ip, - setting.dst_hostname.clone(), - Protocol::NDP, - ndp_packet.len(), - ); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - if seq < setting.count { - std::thread::sleep(setting.send_rate); - } - } - let probe_time = Instant::now().duration_since(start_time); - result.end_time = crate::sys::time::get_sysdate(); - result.elapsed_time = probe_time; - if responses.len() > 0 { - result.probe_status = ProbeStatus::new(); - } else { - result.probe_status = ProbeStatus::with_error_message("No response".to_string()); - } - result.results = responses; - result -} diff --git a/src/neighbor/result.rs b/src/neighbor/result.rs deleted file mode 100644 index fbfc3de..0000000 --- a/src/neighbor/result.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::{ - probe::{ProbeResult, ProbeStatus}, - protocol::Protocol, -}; -use serde::{Deserialize, Serialize}; -use std::time::Duration; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DeviceResolveResult { - pub results: Vec, - pub probe_status: ProbeStatus, - /// start-time in RFC 3339 and ISO 8601 date and time string - pub start_time: String, - /// end-time in RFC 3339 and ISO 8601 date and time string - pub end_time: String, - /// Elapsed time - pub elapsed_time: Duration, - pub protocol: Protocol, -} - -impl DeviceResolveResult { - pub fn new() -> DeviceResolveResult { - DeviceResolveResult { - results: Vec::new(), - probe_status: ProbeStatus::new(), - start_time: String::new(), - end_time: String::new(), - elapsed_time: Duration::from_millis(0), - protocol: Protocol::ARP, - } - } -} diff --git a/src/neighbor/setting.rs b/src/neighbor/setting.rs deleted file mode 100644 index 143461f..0000000 --- a/src/neighbor/setting.rs +++ /dev/null @@ -1,89 +0,0 @@ -use crate::protocol::Protocol; -use netdev::Interface; -use serde::{Deserialize, Serialize}; -use std::net::{Ipv4Addr, Ipv6Addr}; -use std::{net::IpAddr, time::Duration}; - -#[derive(Deserialize, Serialize, Clone, Debug)] -pub struct AddressResolveSetting { - pub if_index: u32, - pub dst_hostname: String, - pub dst_ip: IpAddr, - pub protocol: Protocol, - pub count: u32, - pub receive_timeout: Duration, - pub probe_timeout: Duration, - pub send_rate: Duration, - pub tunnel: bool, - pub loopback: bool, -} - -impl Default for AddressResolveSetting { - fn default() -> Self { - Self { - if_index: 0, - dst_hostname: "localhost".to_string(), - dst_ip: IpAddr::V4(Ipv4Addr::LOCALHOST), - protocol: Protocol::ARP, - count: 1, - receive_timeout: Duration::from_secs(1), - probe_timeout: Duration::from_secs(30), - send_rate: Duration::from_secs(1), - tunnel: false, - loopback: false, - } - } -} - -impl AddressResolveSetting { - pub fn arp( - interface: &Interface, - dst_ipv4_addr: Ipv4Addr, - count: u32, - ) -> Result { - if interface.is_tun() { - return Err(format!("ARP: tun interface is not supported")); - } - if interface.is_loopback() { - return Err(format!("ARP: loopback interface is not supported")); - } - let setting = AddressResolveSetting { - if_index: interface.index, - dst_ip: IpAddr::V4(dst_ipv4_addr), - dst_hostname: dst_ipv4_addr.to_string(), - count: count, - protocol: Protocol::ARP, - receive_timeout: Duration::from_secs(1), - probe_timeout: Duration::from_secs(30), - send_rate: Duration::from_secs(1), - tunnel: false, - loopback: false, - }; - Ok(setting) - } - pub fn ndp( - interface: &Interface, - dst_ipv6_addr: Ipv6Addr, - count: u32, - ) -> Result { - if interface.is_tun() { - return Err(format!("NDP: tun interface is not supported")); - } - if interface.is_loopback() { - return Err(format!("NDP: loopback interface is not supported")); - } - let setting = AddressResolveSetting { - if_index: interface.index, - dst_ip: IpAddr::V6(dst_ipv6_addr), - dst_hostname: dst_ipv6_addr.to_string(), - count: count, - protocol: Protocol::NDP, - receive_timeout: Duration::from_secs(1), - probe_timeout: Duration::from_secs(30), - send_rate: Duration::from_secs(1), - tunnel: false, - loopback: false, - }; - Ok(setting) - } -} diff --git a/src/os/mod.rs b/src/os/mod.rs new file mode 100644 index 0000000..a03d1ed --- /dev/null +++ b/src/os/mod.rs @@ -0,0 +1,327 @@ +pub mod probe; + +use nex::packet::{frame::Frame, tcp::{TcpHeader, TcpOptionKind}}; +use serde::{Deserialize, Serialize}; +use anyhow::Result; +use crate::{output::port::OsProbeResult, probe::ProbeSetting}; + +/// OS Detector using TCP SYN packets +pub struct OsDetector { + pub settings: ProbeSetting, +} + +impl OsDetector { + /// Construct a new OsDetector instance + pub fn new(settings: ProbeSetting) -> Self { + Self { settings } + } + + /// Run the OS detection probe and return the results. + pub async fn run(&self) -> Result { + probe::tcp::run_os_probe(self.settings.clone()).await + } +} + +/// Metadata about the OS database +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Meta { + pub name: String, + pub version: String, +} + +/// A record in the OS signature database +#[derive(Serialize, Deserialize, Debug)] +struct OsSigRecord { + signature: SignatureKey, + cpe: Vec, +} + +/// TCP/IP signature keys for matching +#[derive(Serialize, Deserialize, Debug, Default)] +struct SignatureKey { + order_key: Option, + set_key: Option, + win_bucket: Option>, +} + +/// The OS database structure +#[derive(Serialize, Deserialize)] +pub struct OsDb { + meta: Meta, + signatures: Vec, +} + +/// Signature features extracted from a packet frame +#[derive(Debug)] +pub struct SignatureFeatures { + pub order_key: String, + pub set_key: String, + pub window: u16, + pub ttl_class: Option, +} + +/// Result of OS matching +pub struct OsMatchResult { + pub family: String, + pub confidence: u8, + pub evidence: String, + pub cpes: Vec, +} + +impl OsMatchResult { + pub fn new() -> Self { + OsMatchResult { + family: String::new(), + confidence: 0, + evidence: String::new(), + cpes: Vec::new(), + } + } +} + +/// OS class with initial TTL and description +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct OsClassTtl { + pub os_class: OsClass, + pub os_description: String, + pub initial_ttl: u8, +} + +/// OS classes for classification +#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum OsClass { + UnixLike, + Windows, +} + +impl OsClass { + pub fn as_str(&self) -> &str { + match self { + OsClass::UnixLike => "UnixLike", + OsClass::Windows => "Windows", + } + } +} + +/// Extracts TCP options from a Frame and compresses NOP options. +fn tcp_option_tokens(tcp: &TcpHeader) -> Vec<&'static str> { + let mut toks = Vec::with_capacity(tcp.options.len()); + + for opt in &tcp.options { + let t = match opt.kind() { + TcpOptionKind::MSS => "MSS", + TcpOptionKind::SACK_PERMITTED => "SACK", + TcpOptionKind::TIMESTAMPS => "TS", + TcpOptionKind::WSCALE => "WS", + TcpOptionKind::NOP => "NOP", + _ => continue, + }; + toks.push(t); + } + toks +} + +/// Compress consecutive NOP options in TCP header +fn tcp_option_token_set(tcp: &TcpHeader) -> Vec<&'static str> { + let mut toks = Vec::with_capacity(tcp.options.len()); + + for opt in &tcp.options { + let t = match opt.kind() { + TcpOptionKind::MSS => "MSS", + TcpOptionKind::SACK_PERMITTED => "SACK", + TcpOptionKind::TIMESTAMPS => "TS", + TcpOptionKind::WSCALE => "WS", + TcpOptionKind::NOP => "NOP", + _ => continue, + }; + toks.push(t); + } + + // Compress consecutive NOPs + let mut out = Vec::with_capacity(toks.len()); + let mut prev_nop = false; + for t in toks { + if t == "NOP" { + if !prev_nop { + out.push(t) + } + prev_nop = true; + } else { + prev_nop = false; + out.push(t); + } + } + out +} + +/// Create an order key string from TCP option tokens +fn order_key(tokens: &[&str]) -> String { + tokens.join(",") +} + +/// Create a set key string from TCP option tokens +fn set_key(tokens: &[&str]) -> String { + // Order options by priority, then alphabetically + const PRI: [&str; 5] = ["MSS", "SACK", "TS", "WS", "NOP"]; + use std::collections::BTreeSet; + + let set: BTreeSet<&str> = tokens.iter().copied().collect(); + let mut head: Vec<&str> = PRI.iter().copied().filter(|t| set.contains(*t)).collect(); + let mut tail: Vec<&str> = set + .difference(&PRI.iter().copied().collect()) + .copied() + .collect(); + tail.sort_unstable(); + head.extend(tail); + format!("{{{}}}", head.join(",")) +} + +/// Initial TTL class based on IPv4 or IPv6 header +fn ttl_class_from_packet(frame: &Frame) -> Option { + // Extract TTL from IPv4 or Hop Limit from IPv6 + if let Some(ip) = &frame.ip { + if let Some(ipv4) = &ip.ipv4 { + let ttl = ipv4.ttl; + return Some(match ttl { + 0..=64 => 64, + 65..=128 => 128, + _ => 255, + }); + } + if let Some(ipv6) = &ip.ipv6 { + let h = ipv6.hop_limit; + return Some(match h { + 0..=64 => 64, + 65..=128 => 128, + _ => 255, + }); + } + } + None +} + +/// Extract signature features from a Frame +pub fn extract_signature(frame: &Frame) -> Option { + if let Some(transport) = &frame.transport { + if let Some(tcp) = &transport.tcp { + let tokens = tcp_option_tokens(tcp); + let token_set = tcp_option_token_set(tcp); + let sig = SignatureFeatures { + order_key: order_key(&tokens), + set_key: set_key(&token_set), + window: tcp.window, + ttl_class: ttl_class_from_packet(frame), + }; + return Some(sig); + } + } + None +} + +/// Derive a human-readable OS family name from CPE strings +fn family_from_cpe(cpes: &[String]) -> String { + // Extract vendor and product from a CPE string + for c in cpes { + if let Some(rest) = c.strip_prefix("cpe:/") { + // /o:vendor:product:... + let mut parts = rest.split(':'); + let _part_type = parts.next(); // 'o' or 'a' or 'h' + if let (Some(vendor), Some(product)) = (parts.next(), parts.next()) { + return format!("{} {}", vendor, product); + } + } + } + String::new() +} + +fn score_signature(sig: &SignatureKey, feat: &SignatureFeatures) -> (u8, Vec<&'static str>) { + let mut score: u8 = 0; + let mut evid: Vec<&'static str> = Vec::new(); + + if let Some(ok) = &sig.order_key { + if ok == &feat.order_key { + score = score.saturating_add(60); + evid.push("order"); + } + } + if let Some(sk) = &sig.set_key { + if sk == &feat.set_key { + score = score.saturating_add(40); + evid.push("set"); + } + } + if let Some(buckets) = &sig.win_bucket { + if buckets.iter().any(|&w| w == feat.window) { + score = score.saturating_add(20); + evid.push("win"); + } + } + if let Some(_ttl) = feat.ttl_class { + score = score.saturating_add(10); + evid.push("ttl?"); + } + + (score, evid) +} + +pub fn match_tcpip_signatures(frame: &Frame) -> Option { + let os_db: &'static OsDb = crate::db::os::os_db(); + let feat = extract_signature(frame)?; + + let mut best: Option<(u8, &OsSigRecord, Vec<&'static str>)> = None; + + for rec in &os_db.signatures { + if rec.signature.order_key.is_none() + && rec.signature.set_key.is_none() + && rec.signature.win_bucket.is_none() + { + continue; + } + + let (score, evid) = score_signature(&rec.signature, &feat); + let replace = match &best { + None => true, + Some((best_score, _best_rec, best_evid)) => { + score > *best_score || (score == *best_score && evid.len() > best_evid.len()) + } + }; + if replace { + best = Some((score, rec, evid)); + } + } + + let (score, rec, evid) = best?; + if score < 40 { + return None; + } + + // Build the result + let mut out = OsMatchResult::new(); + out.cpes = rec.cpe.clone(); + out.family = family_from_cpe(&out.cpes); + out.confidence = score.min(100); + out.evidence = { + let mut parts = Vec::new(); + if evid.contains(&"order") { + parts.push(format!("order={}", feat.order_key)); + } + if evid.contains(&"set") { + parts.push(format!("set={}", feat.set_key)); + } + if evid.contains(&"win") { + parts.push(format!("win={}", feat.window)); + } + if let Some(ttl) = feat.ttl_class { + if evid.contains(&"ttl?") { + parts.push(format!("ttl~{}", ttl)); + } + } + if parts.is_empty() { + "heuristic".to_string() + } else { + parts.join(", ") + } + }; + + Some(out) +} diff --git a/src/os/probe/mod.rs b/src/os/probe/mod.rs new file mode 100644 index 0000000..fcb722b --- /dev/null +++ b/src/os/probe/mod.rs @@ -0,0 +1 @@ +pub mod tcp; diff --git a/src/os/probe/tcp.rs b/src/os/probe/tcp.rs new file mode 100644 index 0000000..5f93e9f --- /dev/null +++ b/src/os/probe/tcp.rs @@ -0,0 +1,163 @@ +use futures::stream::StreamExt; +use futures::future::poll_fn; +use nex::datalink::async_io::{async_channel, AsyncChannel}; +use nex::packet::frame::{Frame, ParseOption}; +use tracing_indicatif::span_ext::IndicatifSpanExt; +use std::collections::BTreeMap; +use anyhow::Result; +use crate::config::default::DEFAULT_LOCAL_TCP_PORT; +use crate::endpoint::{EndpointResult, OsGuess, Port, PortResult, PortState, ServiceInfo, TransportProtocol}; +use crate::output::port::OsProbeResult; +use crate::probe::ProbeSetting; + +/// Run OS detection probe using TCP SYN packets and return the results. +pub async fn run_os_probe( + setting: ProbeSetting, +) -> Result { + let mut result = OsProbeResult::new(); + let interface = match crate::interface::get_interface_by_index(setting.if_index) { + Some(interface) => interface, + None => return Err(anyhow::anyhow!("Interface not found")), + }; + // Create sender + let config = nex::datalink::Config { + write_buffer_size: 4096, + read_buffer_size: 4096, + read_timeout: Some(setting.wait_time), + write_timeout: None, + channel_type: nex::datalink::ChannelType::Layer2, + bpf_fd_attempts: 1000, + linux_fanout: None, + promiscuous: false, + }; + + let AsyncChannel::Ethernet(mut tx, mut rx) = async_channel(&interface, config)? + else { + unreachable!(); + }; + + let mut parse_option: ParseOption = ParseOption::default(); + if interface.is_tun() || (cfg!(any(target_os = "macos", target_os = "ios")) && interface.is_loopback()) { + let payload_offset = if interface.is_loopback() { 14 } else { 0 }; + parse_option.from_ip_packet = true; + parse_option.offset = payload_offset; + } + let start_time = std::time::Instant::now(); + for target in setting.target_endpoints { + let header_span = tracing::info_span!("os_probe"); + header_span.pb_set_style(&crate::output::progress::get_progress_style()); + header_span.pb_set_message(&format!("OS Probe ({})", target.ip)); + header_span.pb_set_length(target.ports.len() as u64); + header_span.pb_set_position(0); + header_span.pb_start(); + + let mut detected: bool = false; + for port in &target.ports { + let packet = + crate::packet::tcp::build_tcp_syn_packet(&interface, target.ip, port.number, false); + + // Send a packet using poll_fn. + match poll_fn(|cx| tx.poll_send(cx, &packet)).await { + Ok(_) => { + // TODO! + } + Err(e) => tracing::error!("Failed to send packet: {}", e), + } + loop { + match tokio::time::timeout(setting.wait_time, rx.next()).await { + Ok(Some(Ok(packet))) => { + let frame = match Frame::from_buf(&packet, parse_option.clone()) { + Some(frame) => frame, + None => { + eprintln!("Failed to parse packet: {:?}", packet); + continue; + } + }; + if frame.ip.is_none() || frame.transport.is_none() { + continue; + } + let ttl: u8; + let ip = frame.ip.as_ref().unwrap(); + if let Some(ipv4) = &ip.ipv4 { + if ipv4.source != target.ip { + continue; + } + ttl = ipv4.ttl; + } else if let Some(ipv6) = &ip.ipv6 { + if ipv6.source != target.ip { + continue; + } + ttl = ipv6.hop_limit; + } else { + continue; + } + if let Some(transport) = &frame.transport { + if let Some(tcp) = &transport.tcp { + if tcp.destination != DEFAULT_LOCAL_TCP_PORT { + continue; + } + if tcp.options.len() == 0 { + continue; + } + } else { + continue; + } + } else { + continue; + } + tracing::debug!("Matching frame...: {:?}", frame.transport.as_ref().unwrap().tcp); + match crate::os::match_tcpip_signatures(&frame) { + Some(os_match) => { + let port_result = PortResult { + port: Port::new(port.number, TransportProtocol::Tcp), + state: PortState::Open, + service: ServiceInfo::default(), + rtt_ms: None, + }; + let endpoint_result = EndpointResult { + ip: target.ip, + hostname: target.hostname.clone(), + ports: BTreeMap::from([(port_result.port.clone(), port_result)]), + mac_addr: target.mac_addr, + vendor_name: None, + os: OsGuess { + family: Some(os_match.family), + confidence: Some(os_match.confidence as f32), + ttl_observed: Some(ttl), + }, + tags: target.tags.clone(), + cpes: os_match.cpes, + }; + result.endpoints.push(endpoint_result); + result.fingerprints.push(frame); + + detected = true; + break; + } + None => { + tracing::debug!("No matching OS found"); + } + } + } + Ok(Some(Err(e))) => { + tracing::error!("Failed to receive packet: {}", e); + } + Ok(None) => { + break; + } + Err(e) => { + tracing::debug!("Timeout while waiting for response: {}", e); + break; + } + } + } + if detected { + break; + } + header_span.pb_inc(1); + } + drop(header_span); + } + result.probe_time = start_time.elapsed(); + Ok(result) +} diff --git a/src/output.rs b/src/output.rs deleted file mode 100644 index 39cd034..0000000 --- a/src/output.rs +++ /dev/null @@ -1,36 +0,0 @@ -use indicatif::ProgressStyle; - -pub const SECTION_DIVIDER: &str = "────────────────────────────────────────"; - -pub fn log(message: &str, level: &str) { - if crate::app::is_quiet_mode() { - return; - } - println!("[{}] {}", level, message); -} - -pub fn log_with_time(message: &str, level: &str) { - if crate::app::is_quiet_mode() { - return; - } - let now: String = crate::sys::time::get_systime(); - println!("[{}] [{}] {}", now, level, message); -} - -pub fn log_with_datetime(message: &str, level: &str) { - if crate::app::is_quiet_mode() { - return; - } - let now: String = crate::sys::time::get_sysdate(); - println!("[{}] [{}] {}", now, level, message); -} - -pub fn get_progress_style() -> ProgressStyle { - ProgressStyle::default_bar() - .template( - "{spinner:.green} {msg} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})", - ) - .unwrap() - .tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏", "✓"]) - .progress_chars("#>-") -} diff --git a/src/output/domain.rs b/src/output/domain.rs new file mode 100644 index 0000000..7e9db2e --- /dev/null +++ b/src/output/domain.rs @@ -0,0 +1,81 @@ +use termtree::Tree; +use std::net::IpAddr; +use crate::{dns::{Domain, DomainScanResult}, output::tree_label}; + +/// Print the domain scan results in a tree structure. +pub fn print_domain_tree(base_domain: &Domain, res: &DomainScanResult) { + // Create the root of the tree + let mut root = Tree::new(format!( + "Subdomains of {} — found: {} (elapsed: {:?})", + base_domain.name, + res.domains.len(), + res.scan_time + )); + + // base domain node + let mut base_node = Tree::new(tree_label(&base_domain.name)); + + if !base_domain.ips.is_empty() { + let mut v4 = Vec::new(); + let mut v6 = Vec::new(); + for ip in &base_domain.ips { + match ip { + IpAddr::V4(x) => v4.push(x), + IpAddr::V6(x) => v6.push(x), + } + } + if !v4.is_empty() { + let mut a = Tree::new(tree_label("A")); + for ip in v4 { + a.push(Tree::new(ip.to_string())); + } + base_node.push(a); + } + if !v6.is_empty() { + let mut aaaa = Tree::new(tree_label("AAAA")); + for ip in v6 { + aaaa.push(Tree::new(ip.to_string())); + } + base_node.push(aaaa); + } + } + + // Add subdomains under the base domain + let mut doms = res.domains.clone(); + doms.sort_by(|a, b| a.name.cmp(&b.name)); + + for d in doms { + let mut node = Tree::new(d.name); + + let mut v4 = Vec::new(); + let mut v6 = Vec::new(); + for ip in d.ips { + match ip { + IpAddr::V4(x) => v4.push(x), + IpAddr::V6(x) => v6.push(x), + } + } + + if !v4.is_empty() { + let mut a = Tree::new(tree_label("A")); + for ip in v4 { + a.push(Tree::new(ip.to_string())); + } + node.push(a); + } + if !v6.is_empty() { + let mut aaaa = Tree::new(tree_label("AAAA")); + for ip in v6 { + aaaa.push(Tree::new(ip.to_string())); + } + node.push(aaaa); + } + + base_node.push(node); + } + + root.push(base_node); + + println!("Scan report(s)"); + println!("{}", root); +} diff --git a/src/output/host.rs b/src/output/host.rs new file mode 100644 index 0000000..1a7e843 --- /dev/null +++ b/src/output/host.rs @@ -0,0 +1,79 @@ +use termtree::Tree; +use crate::output::{tree_label, ScanResult}; + +/// Print the scan report results in a tree structure. +pub fn print_report_tree(result: &ScanResult) { + let mut root = Tree::new(tree_label("Scan report(s)")); + + // Create a tree for each endpoint + for ep in &result.endpoints { + // Endpoint title + let title = if let Some(hn) = &ep.hostname { + format!("{} ({})", ep.ip, hn) + } else { + ep.ip.to_string() + }; + let mut ep_root = Tree::new(title); + + // Link-layer info + if ep.mac_addr.is_some() && !nex::net::ip::is_global_ip(&ep.ip) { + let mut mac_node = Tree::new(tree_label("link")); + if let Some(mac) = ep.mac_addr { + mac_node.push(Tree::new(tree_label(format!("mac: {}", mac)))); + } + if let Some(vendor) = &ep.vendor_name { + mac_node.push(Tree::new(tree_label(format!("vendor: {}", vendor)))); + } + ep_root.push(mac_node); + } + + // OS info + if ep.os.family.is_some() || ep.os.ttl_observed.is_some() || !ep.cpes.is_empty() { + let mut os_node = Tree::new(tree_label("os")); + if let Some(f) = &ep.os.family { + os_node.push(Tree::new(tree_label(format!("family: {}", f)))); + } + if let Some(ttl) = ep.os.ttl_observed { + os_node.push(Tree::new(tree_label(format!("TTL: {}", ttl)))); + } + if let Some(conf) = ep.os.confidence { + os_node.push(Tree::new(tree_label(format!("confidence: {:.2}", conf)))); + } + if !ep.cpes.is_empty() { + let mut cpe_node = Tree::new(tree_label("cpes")); + for c in &ep.cpes { + cpe_node.push(Tree::new(c.clone())); + } + os_node.push(cpe_node); + } + ep_root.push(os_node); + } + + // Port information + if !ep.ports.is_empty() { + for (port, pr) in &ep.ports { + let mut pnode = Tree::new(tree_label(format!("{}/{}", port.number, port.transport.as_str().to_uppercase()))); + pnode.push(Tree::new(tree_label(format!("state: {:?}", pr.state)))); + if let Some(name) = &pr.service.name { + pnode.push(Tree::new(tree_label(format!("service: {}", name)))); + } + if let Some(banner) = &pr.service.banner { + pnode.push(Tree::new(tree_label(format!("banner: {}", banner)))); + } + if let Some(prod) = &pr.service.product { + pnode.push(Tree::new(tree_label(format!("product: {}", prod)))); + } + if !pr.service.cpes.is_empty() { + let mut c = Tree::new(tree_label("cpes")); + for cp in &pr.service.cpes { + c.push(Tree::new(cp.clone())); + } + pnode.push(c); + } + ep_root.push(pnode); + } + } + root.push(ep_root); + } + println!("{}", root); +} diff --git a/src/output/interface.rs b/src/output/interface.rs new file mode 100644 index 0000000..9238132 --- /dev/null +++ b/src/output/interface.rs @@ -0,0 +1,87 @@ +use termtree::Tree; +use netdev::Interface; + +use crate::output::tree_label; + +/// Print the network interfaces in a tree structure. +pub fn print_interface_tree(ifaces: &[Interface]) { + let mut root = Tree::new(tree_label("Interfaces")); + for iface in ifaces { + let mut node = Tree::new(format!( + "{}{}", + iface.name, + if iface.default { " (default)" } else { "" } + )); + + node.push(Tree::new(format!("index: {}", iface.index))); + + if let Some(fn_name) = &iface.friendly_name { + node.push(Tree::new(format!("friendly_name: {}", fn_name))); + } + if let Some(desc) = &iface.description { + node.push(Tree::new(format!("description: {}", desc))); + } + + node.push(Tree::new(format!("type: {:?}", iface.if_type))); + node.push(Tree::new(format!("state: {:?}", iface.oper_state))); + if let Some(mac) = &iface.mac_addr { + node.push(Tree::new(format!("mac: {}", mac))); + } + if let Some(mtu) = iface.mtu { + node.push(Tree::new(format!("mtu: {}", mtu))); + } + + if !iface.ipv4.is_empty() { + let mut ipv4_tree = Tree::new(tree_label("IPv4")); + for net in &iface.ipv4 { + ipv4_tree.push(Tree::new(net.to_string())); + } + node.push(ipv4_tree); + } + + if !iface.ipv6.is_empty() { + let mut ipv6_tree = Tree::new(tree_label("IPv6")); + for (i, net) in iface.ipv6.iter().enumerate() { + let mut label = net.to_string(); + if let Some(scope) = iface.ipv6_scope_ids.get(i) { + label.push_str(&format!(" (scope_id={})", scope)); + } + ipv6_tree.push(Tree::new(label)); + } + node.push(ipv6_tree); + } + + if !iface.dns_servers.is_empty() { + let mut dns_tree = Tree::new(tree_label("DNS")); + for dns in &iface.dns_servers { + dns_tree.push(Tree::new(dns.to_string())); + } + node.push(dns_tree); + } + + if let Some(gw) = &iface.gateway { + let mut gw_node = Tree::new(tree_label("Gateway")); + // GW MAC + gw_node.push(Tree::new(format!("MAC: {}", gw.mac_addr))); + // GW IPv4/IPv6 + if !gw.ipv4.is_empty() { + let mut gw_tree = Tree::new(tree_label("IPv4")); + for ip in &gw.ipv4 { + gw_tree.push(Tree::new(ip.to_string())); + } + gw_node.push(gw_tree); + } + if !gw.ipv6.is_empty() { + let mut gw_tree = Tree::new(tree_label("IPv6")); + for ip in &gw.ipv6 { + gw_tree.push(Tree::new(ip.to_string())); + } + gw_node.push(gw_tree); + } + node.push(gw_node); + } + + root.push(node); + } + println!("{}", root); +} diff --git a/src/output/mod.rs b/src/output/mod.rs new file mode 100644 index 0000000..784b041 --- /dev/null +++ b/src/output/mod.rs @@ -0,0 +1,52 @@ +use std::time::Duration; +use nex::packet::frame::Frame; +use serde::{Deserialize, Serialize}; +use crate::endpoint::{Endpoint, EndpointResult}; + +pub mod port; +pub mod progress; +pub mod host; +pub mod ping; +pub mod trace; +pub mod nei; +pub mod domain; +pub mod interface; + +/// Convert a string into a tree label. +fn tree_label>(s: S) -> String { + s.into() +} + +/// The overall scan result containing endpoints and metadata. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ScanResult { + pub endpoints: Vec, + pub scan_time: Duration, + pub fingerprints: Vec, +} + +impl ScanResult { + /// Construct a new, empty ScanResult. + pub fn new() -> Self { + Self { + endpoints: Vec::new(), + scan_time: Duration::new(0, 0), + fingerprints: Vec::new(), + } + } + + /// Get a list of all endpoints from the scan result. + pub fn get_endpoints(&self) -> Vec { + self.endpoints.iter().map(|e| e.to_endpoint()).collect() + } + + /// Get a list of active endpoints (those with active results). + pub fn get_active_endpoints(&self) -> Vec { + self.endpoints.iter().filter_map(|e| e.active_endpoint()).collect() + } + + /// Sort the endpoints by their IP addresses. + pub fn sort_endpoints(&mut self) { + self.endpoints.sort_by_key(|e| e.ip); + } +} diff --git a/src/output/nei.rs b/src/output/nei.rs new file mode 100644 index 0000000..e0fb348 --- /dev/null +++ b/src/output/nei.rs @@ -0,0 +1,36 @@ +use termtree::Tree; +use crate::nei::NeighborDiscoveryResult; + +/// Print the neighbor discovery results in a tree structure. +pub fn print_neighbor_tree(entries: &[NeighborDiscoveryResult]) { + if entries.is_empty() { + println!("No neighbors found."); + return; + } + + let mut root = Tree::new("Neighbors".to_string()); + + for e in entries { + let title = match &e.hostname { + Some(h) => format!("{} ({})", e.ip_addr, h), + None => format!("{}", e.ip_addr), + }; + let mut node = Tree::new(title); + + node.push(Tree::new(format!("MAC: {}", e.mac_addr.address()))); + + if let Some(vendor) = &e.vendor { + node.push(Tree::new(format!("Vendor: {}", vendor))); + } + + node.push(Tree::new(format!("Interface: {} (idx={})", e.if_name, e.if_index))); + + node.push(Tree::new(format!("Protoco: {}", e.protocol.as_str().to_uppercase()))); + + node.push(Tree::new(format!("RTT: {:.3}ms", e.rtt.as_secs_f64() * 1e3))); + + root.push(node); + } + + println!("{}", root); +} diff --git a/src/output/ping.rs b/src/output/ping.rs new file mode 100644 index 0000000..d3dd00b --- /dev/null +++ b/src/output/ping.rs @@ -0,0 +1,114 @@ +use std::time::Duration; +use netdev::MacAddr; +use termtree::Tree; + +use crate::{ping::result::PingResult, probe::ProbeStatusKind, protocol::Protocol}; + +/// Format a Duration as milliseconds with three decimal places. +fn fmt_ms(d: &Duration) -> String { + // milliseconds with three decimal places + format!("{:.3} ms", (d.as_nanos() as f64) / 1_000_000.0) +} + +/// Format a floating-point number as a percentage with one decimal place. +fn pct(loss: f64) -> String { + format!("{:.1}%", loss) +} + +/// Print the ping scan results in a tree structure. +pub fn print_ping_tree(res: &PingResult) { + let s = &res.stat; + + let title = match &res.hostname { + Some(hostname) => format!("{} ({})", res.ip_addr, hostname), + None => format!("{}", res.ip_addr), + }; + + // Packet loss rate + let sent = s.transmitted_count as f64; + let recv = s.received_count as f64; + let loss = if sent > 0.0 { ((sent - recv) / sent) * 100.0 } else { 0.0 }; + + let mut root = Tree::new(title); + + // summary + let first_res = res.first_response(); + let mut summary = Tree::new("Summary".to_string()); + if let Some(r) = first_res { + if r.mac_addr != MacAddr::zero() && !nex::net::ip::is_global_ip(&r.ip_addr) { + summary.push(Tree::new(format!("MAC: {}", r.mac_addr))); + } + } + summary.push(Tree::new(format!("IP: {}", res.ip_addr))); + if let Some(hostname) = &res.hostname { + summary.push(Tree::new(format!("Hostname: {}", hostname))); + } + summary.push(Tree::new(format!("Protocol: {}", format!("{:?}", res.protocol).to_uppercase()))); + match res.protocol { + Protocol::Icmp => {} + Protocol::Tcp => { + if let Some(port) = res.port_number { + summary.push(Tree::new(format!("Port: {}", port))); + } + } + Protocol::Udp => {} + _ => {} + } + summary.push(Tree::new(format!("Received/Sent: {}/{}", s.received_count, s.transmitted_count))); + summary.push(Tree::new(format!("Packet loss: {}", pct(loss)))); + summary.push(Tree::new(format!("Elapsed: {:?}", res.elapsed_time))); + if let Some(min) = &s.min { + let mut rtt = Tree::new("RTT".to_string()); + rtt.push(Tree::new(format!("MIN: {}", fmt_ms(min)))); + if let Some(avg) = &s.avg { rtt.push(Tree::new(format!("AVG: {}", fmt_ms(avg)))); } + if let Some(max) = &s.max { rtt.push(Tree::new(format!("MAX: {}", fmt_ms(max)))); } + summary.push(rtt); + } + root.push(summary); + + // replies + if !s.responses.is_empty() { + let mut replies = Tree::new("Replies".to_string()); + for r in &s.responses { + match r.probe_status.kind { + ProbeStatusKind::Done => { + let head = format!( + "#{} {} bytes from {}, RTT={}, TTL={}, HOP={}", + r.seq, + r.received_packet_size, + r.ip_addr, + fmt_ms(&r.rtt), + r.ttl, + r.hop + ) + .trim() + .to_string(); + + let mut node = Tree::new(head); + if let Some(port) = &r.port_number { + node.push(Tree::new(format!("Port: {}", port))); + } + if let Some(state) = &r.port_status { + node.push(Tree::new(format!("State: {}", state.as_str()))); + } + replies.push(node); + }, + _ => { + let err_head = format!( + "#{} {}: {}", + r.seq, + r.probe_status.kind.name(), + r.probe_status.message, + ) + .trim() + .to_string(); + let node = Tree::new(err_head); + replies.push(node); + }, + } + } + root.push(replies); + } + + println!("{}", root); +} diff --git a/src/output/port.rs b/src/output/port.rs new file mode 100644 index 0000000..64c4144 --- /dev/null +++ b/src/output/port.rs @@ -0,0 +1,233 @@ +use std::{collections::BTreeMap, net::IpAddr, time::{Duration, SystemTime}}; + +use nex::packet::frame::Frame; +use serde::{Deserialize, Serialize}; +use termtree::Tree; +use crate::{endpoint::{EndpointResult, Port, PortResult, PortState, ServiceInfo, TransportProtocol}, output::{tree_label, ScanResult}, service::{probe::ServiceProbe, ServiceDetectionResult}}; + +/// Results of OS probing +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OsProbeResult { + pub endpoints: Vec, + pub probe_time: Duration, + pub fingerprints: Vec, +} + +impl OsProbeResult { + /// Construct a new, empty OsProbeResult. + pub fn new() -> Self { + Self { + endpoints: Vec::new(), + probe_time: Duration::new(0, 0), + fingerprints: Vec::new(), + } + } +} + +/// Comprehensive scan report combining various scan results. +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct ScanReport { + pub meta: ReportMeta, + /// Keep IP as key for merging + #[serde(default)] + pub endpoints: BTreeMap, + #[serde(default)] + pub stats: ReportStats, +} + +/// Metadata about the scan report +#[derive(Serialize, Deserialize, Debug)] +pub struct ReportMeta { + pub tool: String, // "nrev" + pub version: String, // env!("CARGO_PKG_VERSION") + pub started_at: SystemTime, + pub finished_at: Option, +} + +impl Default for ReportMeta { + fn default() -> Self { + Self { + tool: "nrev".into(), + version: env!("CARGO_PKG_VERSION").into(), + started_at: SystemTime::now(), + finished_at: None, + } + } +} + +/// Statistics about the scan +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct ReportStats { + pub hosts_total: usize, + pub ports_scanned: usize, + pub open_ports: usize, + pub duration_scan: Option, // PortScan + pub duration_service: Option, // ServiceDetect + pub duration_os: Option, // OS probe +} + +/// An attempt to probe a service on a port +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct PortProbeAttempt { + pub probe_id: ServiceProbe, + pub result: Result, +} + +impl ScanReport { + pub fn new() -> Self { Self::default() } + + /// Apply port scan results: merge endpoints, update stats + pub fn apply_port_scan(&mut self, ps: ScanResult) { + for ep in ps.endpoints { + // Upsert + self.endpoints + .entry(ep.ip) + .and_modify(|e| e.merge(ep.clone())) + .or_insert_with(|| ep); + } + self.stats.duration_scan = Some(ps.scan_time); + self.recompute_stats(); + } + + /// Apply service detection results: merge service info, update stats + pub fn apply_service_detection(&mut self, sd: ServiceDetectionResult) { + for r in sd.results { + let Some(ep) = self.endpoints.get_mut(&r.ip) else { + continue; + }; + // Upsert port result + let port_key = Port { number: r.port, transport: r.transport }; + let pr = ep.ports.entry(port_key).or_insert_with(|| PortResult { + port: port_key, + state: PortState::Open, + service: r.service_info.clone(), + rtt_ms: None, + }); + + pr.state = PortState::Open; + pr.port = port_key; + pr.service = select_better_service(pr.service.clone(), r.service_info); + } + self.stats.duration_service = Some(sd.scan_time); + self.recompute_stats(); + } + + /// Apply OS probe results: replace with the more confident one + pub fn apply_os_probe(&mut self, osr: OsProbeResult) { + for ep2 in osr.endpoints { + if let Some(ep) = self.endpoints.get_mut(&ep2.ip) { + ep.merge(ep2); + } else { + self.endpoints.insert(ep2.ip, ep2); + } + } + self.stats.duration_os = Some(osr.probe_time); + self.recompute_stats(); + } + + pub fn finish(&mut self) { + self.meta.finished_at = Some(SystemTime::now()); + let tcp_svc_db = crate::db::service::tcp_service_db(); + let udp_svc_db = crate::db::service::udp_service_db(); + // check service name. if service name is not in result, set it. + for ep in self.endpoints.values_mut() { + for (port, pr) in &mut ep.ports { + if pr.service.name.is_none() { + match port.transport { + TransportProtocol::Tcp => { + pr.service.name = tcp_svc_db.get_name(port.number).map(|s| s.to_string()); + } + TransportProtocol::Udp | TransportProtocol::Quic => { + pr.service.name = udp_svc_db.get_name(port.number).map(|s| s.to_string()); + } + } + } + } + } + } + pub fn as_vec(&self) -> Vec<&EndpointResult> { + self.endpoints.values().collect() + } + + fn recompute_stats(&mut self) { + self.stats.hosts_total = self.endpoints.len(); + let mut ports_scanned = 0usize; + let mut open_ports = 0usize; + for ep in self.endpoints.values() { + ports_scanned += ep.ports.len(); + open_ports += ep.ports.values().filter(|p| p.state == PortState::Open).count(); + } + self.stats.ports_scanned = ports_scanned; + self.stats.open_ports = open_ports; + } +} + +/// Choose the better ServiceInfo based on a simple scoring system. +fn select_better_service(cur: ServiceInfo, newv: ServiceInfo) -> ServiceInfo { + let old_score = score_service(&cur); + let new_score = score_service(&newv); + if new_score >= old_score { newv } else { cur } +} + +/// Score a ServiceInfo based on the presence of certain fields. +fn score_service(s: &ServiceInfo) -> usize { + let mut sc = 0; + if s.name.is_some() { sc += 1; } + if s.product.is_some() { sc += 1; } + if let Some(b) = &s.banner { + sc += 1; + // Check for HTTP 200 OK for additional points + if b.contains("HTTP") && b.contains("200 OK") { + sc += 1; + } + } + sc += s.cpes.len(); + sc +} + +/// Match and print OS detection results in a tree structure. +pub fn print_report_tree(rep: &ScanReport) { + let mut root = Tree::new(tree_label("Scan report(s)")); + for ep in rep.endpoints.values() { + let title = if let Some(hn) = &ep.hostname { + format!("{} ({})", ep.ip, hn) + } else { + format!("{}", ep.ip) + }; + let mut ep_root = Tree::new(title); + + // OS + if ep.os.family.is_some() || !ep.cpes.is_empty() { + let mut os_node = Tree::new(tree_label("os")); + if let Some(f) = &ep.os.family { os_node.push(Tree::new(tree_label(format!("family: {}", f)))); } + if let Some(v) = &ep.os.ttl_observed { os_node.push(Tree::new(tree_label(format!("TTL: {}", v)))); } + if !ep.cpes.is_empty() { + let mut cpe_node = Tree::new(tree_label("cpes")); + for c in &ep.cpes { cpe_node.push(Tree::new(c.clone())); } + os_node.push(cpe_node); + } + ep_root.push(os_node); + } + + // Ports + for (port, pr) in &ep.ports { + if pr.state != PortState::Open { + continue; + } + let mut pnode = Tree::new(tree_label(format!("{}/{}", port.number, port.transport.as_str().to_uppercase()))); + pnode.push(Tree::new(tree_label(format!("state: {:?}", pr.state)))); + if let Some(name) = &pr.service.name { pnode.push(Tree::new(tree_label(format!("service: {}", name)))); } + if let Some(b) = &pr.service.banner { pnode.push(Tree::new(tree_label(format!("banner: {}", b)))); } + if let Some(p) = &pr.service.product { pnode.push(Tree::new(tree_label(format!("product: {}", p)))); } + if !pr.service.cpes.is_empty() { + let mut c = Tree::new(tree_label("cpes")); + for cp in &pr.service.cpes { c.push(Tree::new(cp.clone())); } + pnode.push(c); + } + ep_root.push(pnode); + } + + root.push(ep_root); + } + println!("{}", root); +} diff --git a/src/output/progress.rs b/src/output/progress.rs new file mode 100644 index 0000000..09aa433 --- /dev/null +++ b/src/output/progress.rs @@ -0,0 +1,28 @@ +use indicatif::{ProgressState, ProgressStyle}; + +/// Get a progress bar style with a custom elapsed time formatter. +pub fn get_progress_style() -> ProgressStyle { + ProgressStyle::default_bar() + .template( + "{spinner:.green} {msg} [{elapsed_precise_subsec}] [{bar:40.cyan/blue}] {pos}/{len}", + ) + .unwrap() + .with_key("elapsed_precise_subsec", elapsed_precise_subsec) + .tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏", "✓"]) + .progress_chars("#>-") +} + +/// Custom formatter for elapsed time with millisecond precision. +fn elapsed_precise_subsec( + state: &ProgressState, + writer: &mut dyn std::fmt::Write, +) { + let elapsed = state.elapsed(); + let secs = elapsed.as_secs(); + let sub_ms = elapsed.subsec_millis(); + let hours = secs / 3600; + let mins = (secs % 3600) / 60; + let s = secs % 60; + // HH:MM:SS.mmm + let _ = write!(writer, "{:02}:{:02}:{:02}.{:03}", hours, mins, s, sub_ms); +} diff --git a/src/output/trace.rs b/src/output/trace.rs new file mode 100644 index 0000000..c8c3a1d --- /dev/null +++ b/src/output/trace.rs @@ -0,0 +1,83 @@ +use std::net::IpAddr; +use netdev::MacAddr; +use termtree::Tree; + +use crate::endpoint::{Host, NodeType}; +use crate::trace::TraceResult; +use crate::probe::ProbeStatusKind; + +/// Format a Duration as HH:MM:SS.mmm +fn fmt_dur(d: std::time::Duration) -> String { + // HH:MM:SS.mmm + let s = d.as_secs(); + let ms = d.subsec_millis(); + format!("{:02}:{:02}:{:02}.{:03}", s/3600, (s%3600)/60, s%60, ms) +} + +/// Format an IP address with an optional hostname. +fn fmt_ip_host(ip: IpAddr, host: &Option) -> String { + if let Some(h) = host { + format!("{} ({})", ip, h) + } else { + ip.to_string() + } +} + +/// Print the traceroute results in a tree structure. +pub fn print_trace_tree(tr: &TraceResult, target: Host) { + if tr.nodes.is_empty() { + println!("(no hops)"); + return; + } + + // Check if the target was reached + let reached = tr.nodes.iter().any(|n| n.ip_addr == target.ip && matches!(n.probe_status.kind, ProbeStatusKind::Done)); + let mut root = if reached { + Tree::new(format!("Traceroute to {} - reached ({} hops, elapsed {})", + fmt_ip_host(target.ip, &target.hostname), tr.nodes.len(), fmt_dur(tr.elapsed_time))) + } else { + Tree::new(format!("Traceroute to {} - not reached ({} hops, elapsed {})", + fmt_ip_host(target.ip, &target.hostname), tr.nodes.len(), fmt_dur(tr.elapsed_time))) + }; + + let mut nodes = tr.nodes.clone(); + nodes.sort_by(|a, b| a.seq.cmp(&b.seq)); + + for n in nodes { + match n.probe_status.kind { + ProbeStatusKind::Done => { + let mut hop_node = Tree::new(format!( + "#{} {}", + n.seq, + fmt_ip_host(n.ip_addr, &n.host_name) + )); + + if n.mac_addr != MacAddr::zero() && n.node_type == NodeType::Gateway { + hop_node.push(Tree::new(format!("MAC: {}", n.mac_addr))); + } + hop_node.push(Tree::new(format!("RTT: {:.3}ms", n.rtt.as_secs_f64()*1000.0))); + hop_node.push(Tree::new(format!("TTL: {}", n.ttl))); + hop_node.push(Tree::new(format!("HOP: {}", n.hop))); + hop_node.push(Tree::new(format!("Type: {}", n.node_type.name()))); + + root.push(hop_node); + } + ProbeStatusKind::Timeout => { + let hop_node = Tree::new(format!( + "#{} timed out", + n.seq + )); + root.push(hop_node); + } + _ => { + let hop_node = Tree::new(format!( + "#{} unknown", + n.seq + )); + root.push(hop_node); + } + } + } + + println!("{}", root); +} diff --git a/src/packet/arp.rs b/src/packet/arp.rs index 1741ce6..207a705 100644 --- a/src/packet/arp.rs +++ b/src/packet/arp.rs @@ -1,38 +1,58 @@ -use crate::packet::setting::PacketBuildSetting; +use netdev::Interface; use nex::net::mac::MacAddr; +use nex::packet::builder::arp::ArpPacketBuilder; +use nex::packet::builder::ethernet::EthernetPacketBuilder; use nex::packet::ethernet::EtherType; -use nex::util::packet_builder::arp::ArpPacketBuilder; -use nex::util::packet_builder::builder::PacketBuilder; -use nex::util::packet_builder::ethernet::EthernetPacketBuilder; -use std::net::IpAddr; +use nex::packet::packet::Packet; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// Build ARP packet -pub fn build_arp_packet(setting: PacketBuildSetting) -> Vec { - let mut packet_builder = PacketBuilder::new(); - // Ethernet Header - let ethernet_packet_builder = EthernetPacketBuilder { - src_mac: setting.src_mac, - dst_mac: MacAddr::broadcast(), - ether_type: EtherType::Arp, +pub fn build_arp_packet(interface: &Interface, dst_ip: IpAddr) -> Vec { + let src_mac = interface.mac_addr.unwrap_or(MacAddr::zero()); + let src_ipv4 = crate::interface::get_interface_ipv4(interface).unwrap_or(Ipv4Addr::UNSPECIFIED); + let src_global_ipv6 = + crate::interface::get_interface_global_ipv6(interface).unwrap_or(Ipv6Addr::UNSPECIFIED); + let src_local_ipv6 = + crate::interface::get_interface_local_ipv6(interface).unwrap_or(Ipv6Addr::UNSPECIFIED); + + let src_ip: IpAddr = match dst_ip { + IpAddr::V4(_) => { + IpAddr::V4(src_ipv4) + }, + IpAddr::V6(_) => { + if nex::net::ip::is_global_ip(&dst_ip) { + IpAddr::V6(src_global_ipv6) + } else { + IpAddr::V6(src_local_ipv6) + } + }, }; - packet_builder.set_ethernet(ethernet_packet_builder); - match setting.src_ip { + + match src_ip { IpAddr::V4(src_ipv4) => { - match setting.dst_ip { + match dst_ip { IpAddr::V4(dst_ipv4) => { // ARP Header - let arp_packet = ArpPacketBuilder { - src_mac: setting.src_mac, - dst_mac: MacAddr::broadcast(), - src_ip: src_ipv4, - dst_ip: dst_ipv4, - }; - packet_builder.set_arp(arp_packet); + let arp_builder = ArpPacketBuilder::new(src_mac, src_ipv4, dst_ipv4) + .operation(nex::packet::arp::ArpOperation::Request); + // Ethernet Header + let eth_builder = EthernetPacketBuilder::new() + .source(src_mac) + .destination(MacAddr::broadcast()) + .ethertype(EtherType::Arp); + + let packet = eth_builder.payload(arp_builder.build().to_bytes()).build(); + + return packet.to_bytes().to_vec(); + } + IpAddr::V6(_) => { + // ARP is not used with IPv6, return empty vector + return Vec::new(); } - IpAddr::V6(_) => {} } } - IpAddr::V6(_) => {} + IpAddr::V6(_) => { + return Vec::new(); // ARP is not used with IPv6 + } } - packet_builder.packet() } diff --git a/src/packet/frame.rs b/src/packet/frame.rs deleted file mode 100644 index a577322..0000000 --- a/src/packet/frame.rs +++ /dev/null @@ -1,60 +0,0 @@ -use nex::packet::arp::ArpHeader; -use nex::packet::ethernet::EthernetHeader; -use nex::packet::frame::Frame; -use nex::packet::icmp::IcmpHeader; -use nex::packet::icmpv6::Icmpv6Header; -use nex::packet::ipv4::Ipv4Header; -use nex::packet::ipv6::Ipv6Header; -use nex::packet::tcp::TcpHeader; -use nex::packet::udp::UdpHeader; -use serde::{Deserialize, Serialize}; - -/// Packet Frame. Contains all the possible packet types -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct PacketFrame { - pub ethernet_header: Option, - pub arp_header: Option, - pub ipv4_header: Option, - pub ipv6_header: Option, - pub icmp_header: Option, - pub icmpv6_header: Option, - pub tcp_header: Option, - pub udp_header: Option, - pub payload: Vec, -} - -impl PacketFrame { - /// Constructs a new PacketFrame - pub fn new() -> PacketFrame { - PacketFrame { - ethernet_header: None, - arp_header: None, - ipv4_header: None, - ipv6_header: None, - icmp_header: None, - icmpv6_header: None, - tcp_header: None, - udp_header: None, - payload: vec![], - } - } - pub fn from_nex_frame(frame: &Frame) -> PacketFrame { - let mut packet_frame = PacketFrame::new(); - if let Some(datalink) = &frame.datalink { - packet_frame.ethernet_header = datalink.ethernet.clone(); - packet_frame.arp_header = datalink.arp.clone(); - } - if let Some(ip) = &frame.ip { - packet_frame.ipv4_header = ip.ipv4.clone(); - packet_frame.ipv6_header = ip.ipv6.clone(); - packet_frame.icmp_header = ip.icmp.clone(); - packet_frame.icmpv6_header = ip.icmpv6.clone(); - } - if let Some(transport) = &frame.transport { - packet_frame.tcp_header = transport.tcp.clone(); - packet_frame.udp_header = transport.udp.clone(); - } - packet_frame.payload = frame.payload.clone(); - packet_frame - } -} diff --git a/src/packet/icmp.rs b/src/packet/icmp.rs index b3355f7..9b21f8d 100644 --- a/src/packet/icmp.rs +++ b/src/packet/icmp.rs @@ -1,249 +1,104 @@ +use bytes::Bytes; +use netdev::{Interface, MacAddr}; +use nex::packet::builder::ethernet::EthernetPacketBuilder; +use nex::packet::builder::icmp::IcmpPacketBuilder; +use nex::packet::builder::icmpv6::Icmpv6PacketBuilder; +use nex::packet::builder::ipv4::Ipv4PacketBuilder; +use nex::packet::builder::ipv6::Ipv6PacketBuilder; use nex::packet::ethernet::EtherType; +use nex::packet::icmp; use nex::packet::icmp::IcmpType; +use nex::packet::icmpv6; use nex::packet::icmpv6::Icmpv6Type; -use nex::packet::ip::IpNextLevelProtocol; -use nex::util::packet_builder::builder::PacketBuilder; -use nex::util::packet_builder::ethernet::EthernetPacketBuilder; -use nex::util::packet_builder::icmp::IcmpPacketBuilder; -use nex::util::packet_builder::icmpv6::Icmpv6PacketBuilder; -use nex::util::packet_builder::ipv4::Ipv4PacketBuilder; -use nex::util::packet_builder::ipv6::Ipv6PacketBuilder; -use std::net::IpAddr; - -use crate::fp::setting::FingerprintType; -use crate::packet::setting::PacketBuildSetting; +use nex::packet::ip::IpNextProtocol; +use nex::packet::ipv4::Ipv4Flags; +use nex::packet::packet::Packet; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// Build ICMP packet. Supports both ICMPv4 and ICMPv6 -pub fn build_icmp_packet(setting: PacketBuildSetting) -> Vec { - let mut packet_builder = PacketBuilder::new(); - - // Ethernet Header - let ethernet_packet_builder = EthernetPacketBuilder { - src_mac: setting.src_mac, - dst_mac: setting.dst_mac, - ether_type: match setting.dst_ip { - IpAddr::V4(_) => EtherType::Ipv4, - IpAddr::V6(_) => EtherType::Ipv6, - }, +pub fn build_icmp_packet(interface: &Interface, dst_ip: IpAddr, is_ip_packet: bool) -> Vec { + let src_mac = interface.mac_addr.unwrap_or(MacAddr::zero()); + let dst_mac = match &interface.gateway { + Some(gateway) => gateway.mac_addr, + None => MacAddr::zero(), }; - packet_builder.set_ethernet(ethernet_packet_builder); + let src_ipv4 = crate::interface::get_interface_ipv4(interface).unwrap_or(Ipv4Addr::UNSPECIFIED); + let src_global_ipv6 = + crate::interface::get_interface_global_ipv6(interface).unwrap_or(Ipv6Addr::UNSPECIFIED); + let src_local_ipv6 = + crate::interface::get_interface_local_ipv6(interface).unwrap_or(Ipv6Addr::UNSPECIFIED); - // IP Header - match setting.dst_ip { - IpAddr::V4(dst_ipv4) => match setting.src_ip { - IpAddr::V4(src_ipv4) => { - let mut ipv4_packet_builder = - Ipv4PacketBuilder::new(src_ipv4, dst_ipv4, IpNextLevelProtocol::Icmp); - ipv4_packet_builder.ttl = Some(setting.hop_limit); - packet_builder.set_ipv4(ipv4_packet_builder); - } - IpAddr::V6(_) => {} - }, - IpAddr::V6(dst_ipv6) => match setting.src_ip { - IpAddr::V4(_) => {} - IpAddr::V6(src_ipv4) => { - let mut ipv6_packet_builder = - Ipv6PacketBuilder::new(src_ipv4, dst_ipv6, IpNextLevelProtocol::Icmpv6); - ipv6_packet_builder.hop_limit = Some(setting.hop_limit); - packet_builder.set_ipv6(ipv6_packet_builder); + let src_ip: IpAddr = match dst_ip { + IpAddr::V4(_) => { + IpAddr::V4(src_ipv4) + }, + IpAddr::V6(_) => { + if nex::net::ip::is_global_ip(&dst_ip) { + IpAddr::V6(src_global_ipv6) + } else { + IpAddr::V6(src_local_ipv6) } }, - } - // ICMP Header - match setting.dst_ip { - IpAddr::V4(dst_ipv4) => match setting.src_ip { - IpAddr::V4(src_ipv4) => { - let mut icmp_packet_builder = IcmpPacketBuilder::new(src_ipv4, dst_ipv4); - icmp_packet_builder.icmp_type = IcmpType::EchoRequest; - packet_builder.set_icmp(icmp_packet_builder); - } - IpAddr::V6(_) => {} - }, - IpAddr::V6(dst_ipv6) => match setting.src_ip { - IpAddr::V4(_) => {} - IpAddr::V6(src_ipv6) => { - let mut icmpv6_packet_builder = Icmpv6PacketBuilder::new(src_ipv6, dst_ipv6); - icmpv6_packet_builder.icmpv6_type = Icmpv6Type::EchoRequest; - packet_builder.set_icmpv6(icmpv6_packet_builder); - } - }, - } - if setting.ip_packet { - packet_builder.ip_packet() - } else { - packet_builder.packet() - } -} - -/// Build ICMP probe packet. Supports both ICMPv4 and ICMPv6 -pub fn build_icmp_probe_packet( - setting: PacketBuildSetting, - probe_type: FingerprintType, -) -> Vec { - let mut packet_builder = PacketBuilder::new(); - - // Ethernet Header - let ethernet_packet_builder = EthernetPacketBuilder { - src_mac: setting.src_mac, - dst_mac: setting.dst_mac, - ether_type: match setting.dst_ip { - IpAddr::V4(_) => EtherType::Ipv4, - IpAddr::V6(_) => EtherType::Ipv6, - }, }; - packet_builder.set_ethernet(ethernet_packet_builder); - // IP Header - match setting.dst_ip { - IpAddr::V4(dst_ipv4) => match setting.src_ip { - IpAddr::V4(src_ipv4) => { - let mut ipv4_packet_builder = - Ipv4PacketBuilder::new(src_ipv4, dst_ipv4, IpNextLevelProtocol::Icmp); - ipv4_packet_builder.ttl = Some(setting.hop_limit); - packet_builder.set_ipv4(ipv4_packet_builder); - } - IpAddr::V6(_) => {} - }, - IpAddr::V6(dst_ipv6) => match setting.src_ip { - IpAddr::V4(_) => {} - IpAddr::V6(src_ipv4) => { - let mut ipv6_packet_builder = - Ipv6PacketBuilder::new(src_ipv4, dst_ipv6, IpNextLevelProtocol::Icmpv6); - ipv6_packet_builder.hop_limit = Some(setting.hop_limit); - packet_builder.set_ipv6(ipv6_packet_builder); - } - }, - } - // ICMP Header - match setting.dst_ip { - IpAddr::V4(dst_ipv4) => match setting.src_ip { - IpAddr::V4(src_ipv4) => { - let mut icmp_packet_builder = IcmpPacketBuilder::new(src_ipv4, dst_ipv4); - match probe_type { - FingerprintType::IcmpEcho => { - icmp_packet_builder.icmp_type = IcmpType::EchoRequest; - } - FingerprintType::IcmpTimestamp => { - icmp_packet_builder.icmp_type = IcmpType::TimestampRequest; - } - FingerprintType::IcmpAddressMask => { - icmp_packet_builder.icmp_type = IcmpType::AddressMaskRequest; - } - FingerprintType::IcmpInformation => { - icmp_packet_builder.icmp_type = IcmpType::InformationRequest; - } - _ => { - icmp_packet_builder.icmp_type = IcmpType::EchoRequest; - } - } - packet_builder.set_icmp(icmp_packet_builder); - } - IpAddr::V6(_) => {} - }, - IpAddr::V6(dst_ipv6) => match setting.src_ip { - IpAddr::V4(_) => {} - IpAddr::V6(src_ipv6) => { - let mut icmpv6_packet_builder = Icmpv6PacketBuilder::new(src_ipv6, dst_ipv6); - icmpv6_packet_builder.icmpv6_type = Icmpv6Type::EchoRequest; - packet_builder.set_icmpv6(icmpv6_packet_builder); - } - }, - } - if setting.ip_packet { - packet_builder.ip_packet() - } else { - packet_builder.packet() - } -} - -pub fn build_ip_next_icmp_packet(setting: PacketBuildSetting) -> Vec { - // ICMP Header - match setting.dst_ip { - IpAddr::V4(dst_ipv4) => match setting.src_ip { - IpAddr::V4(src_ipv4) => { - let mut icmp_packet_builder = IcmpPacketBuilder::new(src_ipv4, dst_ipv4); - icmp_packet_builder.icmp_type = IcmpType::EchoRequest; - icmp_packet_builder.build() - } - IpAddr::V6(_) => Vec::new(), - }, - IpAddr::V6(dst_ipv6) => match setting.src_ip { - IpAddr::V4(_) => Vec::new(), - IpAddr::V6(src_ipv6) => { - let mut icmpv6_packet_builder = Icmpv6PacketBuilder::new(src_ipv6, dst_ipv6); - icmpv6_packet_builder.icmpv6_type = Icmpv6Type::EchoRequest; - icmpv6_packet_builder.build() - } - }, - } -} + let icmp_packet: Bytes = match (src_ip, dst_ip) { + (IpAddr::V4(src), IpAddr::V4(dst)) => IcmpPacketBuilder::new(src, dst) + .icmp_type(IcmpType::EchoRequest) + .icmp_code(icmp::echo_request::IcmpCodes::NoCode) + .echo_fields(0x1234, 0x1) + .payload(Bytes::from_static(b"hello")) + .build() + .to_bytes(), + (IpAddr::V6(src), IpAddr::V6(dst)) => Icmpv6PacketBuilder::new(src, dst) + .icmpv6_type(Icmpv6Type::EchoRequest) + .icmpv6_code(icmpv6::echo_request::Icmpv6Codes::NoCode) + .echo_fields(0x1234, 0x1) + .payload(Bytes::from_static(b"hello")) + .build() + .to_bytes(), + _ => panic!("Source and destination IP version mismatch"), + }; -/// Build ICMP trace packet -pub fn build_icmp_trace_packet(setting: PacketBuildSetting, hop_limit: Option) -> Vec { - let mut packet_builder = PacketBuilder::new(); + let ip_packet = match (src_ip, dst_ip) { + (IpAddr::V4(src), IpAddr::V4(dst)) => Ipv4PacketBuilder::new() + .source(src) + .destination(dst) + .protocol(IpNextProtocol::Icmp) + .flags(Ipv4Flags::DontFragment) + .payload(icmp_packet) + .build() + .to_bytes(), + (IpAddr::V6(src), IpAddr::V6(dst)) => Ipv6PacketBuilder::new() + .source(src) + .destination(dst) + .next_header(IpNextProtocol::Icmpv6) + .payload(icmp_packet) + .build() + .to_bytes(), + _ => unreachable!(), + }; - // Ethernet Header - let ethernet_packet_builder = EthernetPacketBuilder { - src_mac: setting.src_mac, - dst_mac: setting.dst_mac, - ether_type: match setting.dst_ip { + let ethernet_packet = EthernetPacketBuilder::new() + .source(if is_ip_packet { + MacAddr::zero() + } else { + src_mac + }) + .destination(if is_ip_packet { + MacAddr::zero() + } else { + dst_mac + }) + .ethertype(match dst_ip { IpAddr::V4(_) => EtherType::Ipv4, IpAddr::V6(_) => EtherType::Ipv6, - }, - }; - packet_builder.set_ethernet(ethernet_packet_builder); + }) + .payload(ip_packet) + .build(); - // IP Header - match setting.dst_ip { - IpAddr::V4(dst_ipv4) => match setting.src_ip { - IpAddr::V4(src_ipv4) => { - let mut ipv4_packet_builder = - Ipv4PacketBuilder::new(src_ipv4, dst_ipv4, IpNextLevelProtocol::Icmp); - if let Some(hoplimit) = hop_limit { - ipv4_packet_builder.ttl = Some(hoplimit); - } else { - ipv4_packet_builder.ttl = Some(setting.hop_limit); - } - packet_builder.set_ipv4(ipv4_packet_builder); - } - IpAddr::V6(_) => {} - }, - IpAddr::V6(dst_ipv6) => match setting.src_ip { - IpAddr::V4(_) => {} - IpAddr::V6(src_ipv4) => { - let mut ipv6_packet_builder = - Ipv6PacketBuilder::new(src_ipv4, dst_ipv6, IpNextLevelProtocol::Icmpv6); - if let Some(hoplimit) = hop_limit { - ipv6_packet_builder.hop_limit = Some(hoplimit); - } else { - ipv6_packet_builder.hop_limit = Some(setting.hop_limit); - } - packet_builder.set_ipv6(ipv6_packet_builder); - } - }, - } - // ICMP Header - match setting.dst_ip { - IpAddr::V4(dst_ipv4) => match setting.src_ip { - IpAddr::V4(src_ipv4) => { - let mut icmp_packet_builder = IcmpPacketBuilder::new(src_ipv4, dst_ipv4); - icmp_packet_builder.icmp_type = IcmpType::EchoRequest; - packet_builder.set_icmp(icmp_packet_builder); - } - IpAddr::V6(_) => {} - }, - IpAddr::V6(dst_ipv6) => match setting.src_ip { - IpAddr::V4(_) => {} - IpAddr::V6(src_ipv6) => { - let mut icmpv6_packet_builder = Icmpv6PacketBuilder::new(src_ipv6, dst_ipv6); - icmpv6_packet_builder.icmpv6_type = Icmpv6Type::EchoRequest; - packet_builder.set_icmpv6(icmpv6_packet_builder); - } - }, - } - if setting.ip_packet { - packet_builder.ip_packet() + if is_ip_packet { + ethernet_packet.ip_packet().unwrap().to_vec() } else { - packet_builder.packet() + ethernet_packet.to_bytes().to_vec() } } diff --git a/src/packet/mod.rs b/src/packet/mod.rs index 16aa7c7..d0ff892 100644 --- a/src/packet/mod.rs +++ b/src/packet/mod.rs @@ -1,7 +1,5 @@ -pub mod arp; -pub mod frame; -pub mod icmp; -pub mod ndp; -pub mod setting; pub mod tcp; pub mod udp; +pub mod icmp; +pub mod arp; +pub mod ndp; diff --git a/src/packet/ndp.rs b/src/packet/ndp.rs index 67189d7..94574f1 100644 --- a/src/packet/ndp.rs +++ b/src/packet/ndp.rs @@ -1,45 +1,73 @@ -use crate::packet::setting::PacketBuildSetting; +use netdev::Interface; use nex::net::mac::MacAddr; +use nex::packet::builder::ethernet::EthernetPacketBuilder; +use nex::packet::builder::ipv6::Ipv6PacketBuilder; +use nex::packet::builder::ndp::NdpPacketBuilder; use nex::packet::ethernet::EtherType; -use nex::packet::ethernet::MAC_ADDR_LEN; -use nex::packet::icmpv6::ndp::{NDP_OPT_PACKET_LEN, NDP_SOL_PACKET_LEN}; -use nex::packet::ip::IpNextLevelProtocol; -use nex::util::packet_builder::builder::PacketBuilder; -use nex::util::packet_builder::ethernet::EthernetPacketBuilder; -use nex::util::packet_builder::ipv6::Ipv6PacketBuilder; -use nex::util::packet_builder::ndp::NdpPacketBuilder; -use std::net::IpAddr; +use nex::packet::ip::IpNextProtocol; +use nex::packet::packet::Packet; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +/// Compute multicast MAC address from solicited-node multicast IPv6 address +fn ipv6_multicast_mac(ipv6: &Ipv6Addr) -> MacAddr { + let segments = ipv6.segments(); + MacAddr::new( + 0x33, + 0x33, + ((segments[6] >> 8) & 0xff) as u8, + (segments[6] & 0xff) as u8, + ((segments[7] >> 8) & 0xff) as u8, + (segments[7] & 0xff) as u8, + ) +} /// Build NDP packet -pub fn build_ndp_packet(setting: PacketBuildSetting) -> Vec { - let mut packet_builder = PacketBuilder::new(); - let ethernet_packet_builder = EthernetPacketBuilder { - src_mac: setting.src_mac, - dst_mac: MacAddr::broadcast(), - ether_type: EtherType::Ipv6, - }; - packet_builder.set_ethernet(ethernet_packet_builder); +pub fn build_ndp_packet(interface: &Interface, dst_ip: IpAddr) -> Vec { + let src_mac = interface.mac_addr.unwrap_or(MacAddr::zero()); + let src_ipv4 = crate::interface::get_interface_ipv4(interface).unwrap_or(Ipv4Addr::UNSPECIFIED); + let src_global_ipv6 = + crate::interface::get_interface_global_ipv6(interface).unwrap_or(Ipv6Addr::UNSPECIFIED); + let src_local_ipv6 = + crate::interface::get_interface_local_ipv6(interface).unwrap_or(Ipv6Addr::UNSPECIFIED); - match setting.src_ip { - IpAddr::V4(_) => {} - IpAddr::V6(src_ipv6) => { - match setting.dst_ip { - IpAddr::V4(_) => {} - IpAddr::V6(dst_ipv6) => { - // IPv6 Header - let mut ipv6_packet_builder = - Ipv6PacketBuilder::new(src_ipv6, dst_ipv6, IpNextLevelProtocol::Icmpv6); - ipv6_packet_builder.payload_length = - Some((NDP_SOL_PACKET_LEN + NDP_OPT_PACKET_LEN + MAC_ADDR_LEN) as u16); - ipv6_packet_builder.hop_limit = Some(u8::MAX); - packet_builder.set_ipv6(ipv6_packet_builder); - // NDP Header - let ndp_packet_builder = - NdpPacketBuilder::new(setting.src_mac, src_ipv6, dst_ipv6); - packet_builder.set_ndp(ndp_packet_builder); - } + let src_ip: IpAddr = match dst_ip { + IpAddr::V4(_) => { + IpAddr::V4(src_ipv4) + }, + IpAddr::V6(_) => { + if nex::net::ip::is_global_ip(&dst_ip) { + IpAddr::V6(src_global_ipv6) + } else { + IpAddr::V6(src_local_ipv6) } + }, + }; + // Build NDP packet + //let ndp_payload_len = (NDP_SOL_PACKET_LEN + NDP_OPT_PACKET_LEN + MAC_ADDR_LEN) as u16; + match (src_ip, dst_ip) { + (IpAddr::V4(_), IpAddr::V4(_)) => { + panic!("NDP is not used with IPv4 addresses"); + } + (IpAddr::V6(src), IpAddr::V6(dst)) => { + let ipv6 = Ipv6PacketBuilder::new() + .source(src) + .destination(dst) + .next_header(IpNextProtocol::Icmpv6) + .hop_limit(255); + + let dst_mac = ipv6_multicast_mac(&dst); + + let ndp = NdpPacketBuilder::new(src_mac, src, dst); + + let ethernet = EthernetPacketBuilder::new() + .source(src_mac) + .destination(dst_mac) + .ethertype(EtherType::Ipv6) + .payload(ipv6.payload(ndp.build().to_bytes()).build().to_bytes()); + + let packet = ethernet.build().to_bytes(); + packet.to_vec() } + _ => panic!("Source and destination IP versions must match"), } - packet_builder.packet() } diff --git a/src/packet/setting.rs b/src/packet/setting.rs deleted file mode 100644 index f5957b6..0000000 --- a/src/packet/setting.rs +++ /dev/null @@ -1,170 +0,0 @@ -use crate::{ - config::DEFAULT_LOCAL_UDP_PORT, neighbor::setting::AddressResolveSetting, - ping::setting::PingSetting, trace::setting::TraceSetting, -}; -use netdev::mac::MacAddr; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - -#[derive(Clone, Debug)] -pub struct PacketBuildSetting { - pub src_mac: MacAddr, - pub dst_mac: MacAddr, - pub src_ip: IpAddr, - pub dst_ip: IpAddr, - pub src_port: u16, - pub dst_port: u16, - pub hop_limit: u8, - pub payload: Vec, - pub ip_packet: bool, -} - -impl PacketBuildSetting { - pub fn new() -> Self { - Self { - src_mac: MacAddr::zero(), - dst_mac: MacAddr::zero(), - src_ip: IpAddr::V4(Ipv4Addr::LOCALHOST), - dst_ip: IpAddr::V4(Ipv4Addr::LOCALHOST), - src_port: 0, - dst_port: 0, - hop_limit: 64, - payload: Vec::new(), - ip_packet: false, - } - } - pub fn from_ping_setting(ping_setting: &PingSetting) -> Self { - match crate::interface::get_interface_by_index(ping_setting.if_index) { - Some(interface) => { - let dst_mac = match &interface.gateway { - Some(gateway) => gateway.mac_addr, - None => MacAddr::zero(), - }; - let src_ip = match ping_setting.dst_ip { - IpAddr::V4(_) => crate::interface::get_interface_ipv4(&interface) - .unwrap_or(IpAddr::V4(Ipv4Addr::LOCALHOST)), - IpAddr::V6(ipv6_addr) => { - if nex::net::ip::is_global_ipv6(&ipv6_addr) { - crate::interface::get_interface_global_ipv6(&interface) - .unwrap_or(IpAddr::V6(Ipv6Addr::LOCALHOST)) - } else { - crate::interface::get_interface_local_ipv6(&interface) - .unwrap_or(IpAddr::V6(Ipv6Addr::LOCALHOST)) - } - } - }; - Self { - src_mac: interface.mac_addr.unwrap_or(MacAddr::zero()), - dst_mac: dst_mac, - src_ip: src_ip, - dst_ip: ping_setting.dst_ip, - src_port: DEFAULT_LOCAL_UDP_PORT, - dst_port: ping_setting.dst_port.unwrap_or(0), - hop_limit: ping_setting.hop_limit, - payload: Vec::new(), - ip_packet: interface.is_tun() || interface.is_loopback(), - } - } - None => Self { - src_mac: MacAddr::zero(), - dst_mac: MacAddr::zero(), - src_ip: ping_setting.dst_ip, - dst_ip: ping_setting.dst_ip, - src_port: 0, - dst_port: 0, - hop_limit: ping_setting.hop_limit, - payload: Vec::new(), - ip_packet: false, - }, - } - } - pub fn from_trace_setting(ping_setting: &TraceSetting, seq_ttl: u8) -> Self { - match crate::interface::get_interface_by_index(ping_setting.if_index) { - Some(interface) => { - let dst_mac = match &interface.gateway { - Some(gateway) => gateway.mac_addr, - None => MacAddr::zero(), - }; - let src_ip = match ping_setting.dst_ip { - IpAddr::V4(_) => crate::interface::get_interface_ipv4(&interface) - .unwrap_or(IpAddr::V4(Ipv4Addr::LOCALHOST)), - IpAddr::V6(ipv6_addr) => { - if nex::net::ip::is_global_ipv6(&ipv6_addr) { - crate::interface::get_interface_global_ipv6(&interface) - .unwrap_or(IpAddr::V6(Ipv6Addr::LOCALHOST)) - } else { - crate::interface::get_interface_local_ipv6(&interface) - .unwrap_or(IpAddr::V6(Ipv6Addr::LOCALHOST)) - } - } - }; - Self { - src_mac: interface.mac_addr.unwrap_or(MacAddr::zero()), - dst_mac: dst_mac, - src_ip: src_ip, - dst_ip: ping_setting.dst_ip, - src_port: DEFAULT_LOCAL_UDP_PORT, - dst_port: ping_setting.dst_port, - hop_limit: seq_ttl, - payload: Vec::new(), - ip_packet: interface.is_tun() || interface.is_loopback(), - } - } - None => Self { - src_mac: MacAddr::zero(), - dst_mac: MacAddr::zero(), - src_ip: ping_setting.dst_ip, - dst_ip: ping_setting.dst_ip, - src_port: 0, - dst_port: 0, - hop_limit: seq_ttl, - payload: Vec::new(), - ip_packet: false, - }, - } - } - pub fn from_address_resolve_settomg(resolve_setting: &AddressResolveSetting) -> Self { - match crate::interface::get_interface_by_index(resolve_setting.if_index) { - Some(interface) => { - let dst_mac = match &interface.gateway { - Some(gateway) => gateway.mac_addr, - None => MacAddr::zero(), - }; - let src_ip = match resolve_setting.dst_ip { - IpAddr::V4(_) => crate::interface::get_interface_ipv4(&interface) - .unwrap_or(IpAddr::V4(Ipv4Addr::LOCALHOST)), - IpAddr::V6(ipv6_addr) => { - if nex::net::ip::is_global_ipv6(&ipv6_addr) { - crate::interface::get_interface_global_ipv6(&interface) - .unwrap_or(IpAddr::V6(Ipv6Addr::LOCALHOST)) - } else { - crate::interface::get_interface_local_ipv6(&interface) - .unwrap_or(IpAddr::V6(Ipv6Addr::LOCALHOST)) - } - } - }; - Self { - src_mac: interface.mac_addr.unwrap_or(MacAddr::zero()), - dst_mac: dst_mac, - src_ip: src_ip, - dst_ip: resolve_setting.dst_ip, - src_port: DEFAULT_LOCAL_UDP_PORT, - dst_port: 0, - hop_limit: 64, - payload: Vec::new(), - ip_packet: interface.is_tun() || interface.is_loopback(), - } - } - None => Self { - src_mac: MacAddr::zero(), - dst_mac: MacAddr::zero(), - src_ip: resolve_setting.dst_ip, - dst_ip: resolve_setting.dst_ip, - src_port: 0, - dst_port: 0, - hop_limit: 64, - payload: Vec::new(), - ip_packet: false, - }, - } - } -} diff --git a/src/packet/tcp.rs b/src/packet/tcp.rs index c68c7a2..4b58613 100644 --- a/src/packet/tcp.rs +++ b/src/packet/tcp.rs @@ -1,140 +1,107 @@ -use crate::packet::setting::PacketBuildSetting; -use nex::packet::ethernet::EtherType; -use nex::packet::ip::IpNextLevelProtocol; -use nex::packet::tcp::{TcpFlags, TcpOption}; -use nex::util::packet_builder::{ - builder::PacketBuilder, ethernet::EthernetPacketBuilder, ipv4::Ipv4PacketBuilder, - ipv6::Ipv6PacketBuilder, tcp::TcpPacketBuilder, +use bytes::Bytes; +use netdev::{Interface, MacAddr}; +use nex::packet::builder::{ + ethernet::EthernetPacketBuilder, ipv4::Ipv4PacketBuilder, ipv6::Ipv6PacketBuilder, + tcp::TcpPacketBuilder, }; -use std::net::{IpAddr, SocketAddr}; +use nex::packet::ethernet::EtherType; +use nex::packet::ip::IpNextProtocol; +use nex::packet::ipv4::Ipv4Flags; +use nex::packet::packet::Packet; +use nex::packet::tcp::{TcpFlags, TcpOptionPacket}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +use crate::config::default::DEFAULT_LOCAL_TCP_PORT; /// Build TCP SYN packet with default options -pub fn build_tcp_syn_packet(setting: PacketBuildSetting) -> Vec { - let mut packet_builder = PacketBuilder::new(); - let ethernet_packet_builder = EthernetPacketBuilder { - src_mac: setting.src_mac, - dst_mac: setting.dst_mac, - ether_type: match setting.dst_ip { - IpAddr::V4(_) => EtherType::Ipv4, - IpAddr::V6(_) => EtherType::Ipv6, - }, +pub fn build_tcp_syn_packet( + interface: &Interface, + dst_ip: IpAddr, + dst_port: u16, + is_ip_packet: bool +) -> Vec { + let src_mac = interface.mac_addr.unwrap_or(MacAddr::zero()); + let dst_mac = match &interface.gateway { + Some(gateway) => gateway.mac_addr, + None => MacAddr::zero(), }; - packet_builder.set_ethernet(ethernet_packet_builder); - match setting.src_ip { - IpAddr::V4(src_ipv4) => match setting.dst_ip { - IpAddr::V4(dst_ipv4) => { - let mut ipv4_packet_builder = - Ipv4PacketBuilder::new(src_ipv4, dst_ipv4, IpNextLevelProtocol::Tcp); - ipv4_packet_builder.total_length = Some(64); - ipv4_packet_builder.ttl = Some(setting.hop_limit); - packet_builder.set_ipv4(ipv4_packet_builder); - } - IpAddr::V6(_) => {} + let src_ipv4 = crate::interface::get_interface_ipv4(interface).unwrap_or(Ipv4Addr::UNSPECIFIED); + let src_global_ipv6 = + crate::interface::get_interface_global_ipv6(interface).unwrap_or(Ipv6Addr::UNSPECIFIED); + let src_local_ipv6 = + crate::interface::get_interface_local_ipv6(interface).unwrap_or(Ipv6Addr::UNSPECIFIED); + + let src_ip: IpAddr = match dst_ip { + IpAddr::V4(_) => { + IpAddr::V4(src_ipv4) }, - IpAddr::V6(src_ipv6) => match setting.dst_ip { - IpAddr::V4(_) => {} - IpAddr::V6(dst_ipv6) => { - let mut ipv6_packet_builder = - Ipv6PacketBuilder::new(src_ipv6, dst_ipv6, IpNextLevelProtocol::Tcp); - ipv6_packet_builder.payload_length = Some(44); - ipv6_packet_builder.hop_limit = Some(setting.hop_limit); - packet_builder.set_ipv6(ipv6_packet_builder); + IpAddr::V6(_) => { + if nex::net::ip::is_global_ip(&dst_ip) { + IpAddr::V6(src_global_ipv6) + } else { + IpAddr::V6(src_local_ipv6) } }, - } - let mut tcp_packet_builder = TcpPacketBuilder::new( - SocketAddr::new(setting.src_ip, setting.src_port), - SocketAddr::new(setting.dst_ip, setting.dst_port), - ); - tcp_packet_builder.flags = TcpFlags::SYN; - tcp_packet_builder.window = 65535; - tcp_packet_builder.options = vec![ - TcpOption::mss(1460), - TcpOption::nop(), - TcpOption::wscale(6), - TcpOption::nop(), - TcpOption::nop(), - TcpOption::timestamp(u32::MAX, u32::MIN), - TcpOption::sack_perm(), - ]; - packet_builder.set_tcp(tcp_packet_builder); + }; - if setting.ip_packet { - packet_builder.ip_packet() - } else { - packet_builder.packet() - } -} + // Packet builder for TCP SYN + let tcp_packet = TcpPacketBuilder::new(src_ip, dst_ip) + .source(DEFAULT_LOCAL_TCP_PORT) + .destination(dst_port) + .flags(TcpFlags::SYN) + .window(65535) + .options(vec![ + TcpOptionPacket::mss(1460), + TcpOptionPacket::nop(), + TcpOptionPacket::wscale(6), + TcpOptionPacket::nop(), + TcpOptionPacket::nop(), + TcpOptionPacket::timestamp(u32::MAX, u32::MIN), + TcpOptionPacket::sack_perm(), + ]) + .build(); -/// Build TCP SYN packet with minimum options -pub fn build_tcp_syn_packet_min(setting: PacketBuildSetting) -> Vec { - let mut packet_builder = PacketBuilder::new(); - let ethernet_packet_builder = EthernetPacketBuilder { - src_mac: setting.src_mac, - dst_mac: setting.dst_mac, - ether_type: match setting.dst_ip { + let ip_packet = match (src_ip, dst_ip) { + (IpAddr::V4(src), IpAddr::V4(dst)) => Ipv4PacketBuilder::new() + .source(src) + .destination(dst) + .protocol(IpNextProtocol::Tcp) + .flags(Ipv4Flags::DontFragment) + .payload(tcp_packet.to_bytes()) + .build() + .to_bytes(), + (IpAddr::V6(src), IpAddr::V6(dst)) => Ipv6PacketBuilder::new() + .source(src) + .destination(dst) + .next_header(IpNextProtocol::Tcp) + .payload(tcp_packet.to_bytes()) + .build() + .to_bytes(), + _ => unreachable!(), + }; + + let ethernet_packet = EthernetPacketBuilder::new() + .source(if is_ip_packet { + MacAddr::zero() + } else { + src_mac + }) + .destination(if is_ip_packet { + MacAddr::zero() + } else { + dst_mac + }) + .ethertype(match dst_ip { IpAddr::V4(_) => EtherType::Ipv4, IpAddr::V6(_) => EtherType::Ipv6, - }, - }; - packet_builder.set_ethernet(ethernet_packet_builder); - match setting.src_ip { - IpAddr::V4(src_ipv4) => match setting.dst_ip { - IpAddr::V4(dst_ipv4) => { - let mut ipv4_packet_builder = - Ipv4PacketBuilder::new(src_ipv4, dst_ipv4, IpNextLevelProtocol::Tcp); - ipv4_packet_builder.ttl = Some(setting.hop_limit); - packet_builder.set_ipv4(ipv4_packet_builder); - } - IpAddr::V6(_) => {} - }, - IpAddr::V6(src_ipv6) => match setting.dst_ip { - IpAddr::V4(_) => {} - IpAddr::V6(dst_ipv6) => { - let mut ipv6_packet_builder = - Ipv6PacketBuilder::new(src_ipv6, dst_ipv6, IpNextLevelProtocol::Tcp); - ipv6_packet_builder.hop_limit = Some(setting.hop_limit); - packet_builder.set_ipv6(ipv6_packet_builder); - } - }, - } - let mut tcp_packet_builder = TcpPacketBuilder::new( - SocketAddr::new(setting.src_ip, setting.src_port), - SocketAddr::new(setting.dst_ip, setting.dst_port), - ); - tcp_packet_builder.flags = TcpFlags::SYN; - tcp_packet_builder.window = 65535; - tcp_packet_builder.options = vec![ - TcpOption::mss(1460), - TcpOption::sack_perm(), - TcpOption::nop(), - TcpOption::nop(), - TcpOption::wscale(7), - ]; - packet_builder.set_tcp(tcp_packet_builder); + }) + .payload(ip_packet) + .build(); - if setting.ip_packet { - packet_builder.ip_packet() + let packet: Bytes = if is_ip_packet { + ethernet_packet.ip_packet().unwrap() } else { - packet_builder.packet() - } -} - -pub fn build_ip_next_tcp_syn_packet(setting: PacketBuildSetting) -> Vec { - let mut tcp_packet_builder = TcpPacketBuilder::new( - SocketAddr::new(setting.src_ip, setting.src_port), - SocketAddr::new(setting.dst_ip, setting.dst_port), - ); - tcp_packet_builder.flags = TcpFlags::SYN; - tcp_packet_builder.window = 65535; - tcp_packet_builder.options = vec![ - TcpOption::mss(1460), - TcpOption::nop(), - TcpOption::wscale(6), - TcpOption::nop(), - TcpOption::nop(), - TcpOption::timestamp(u32::MAX, u32::MIN), - TcpOption::sack_perm(), - ]; - tcp_packet_builder.build() + ethernet_packet.to_bytes() + }; + packet.to_vec() } diff --git a/src/packet/udp.rs b/src/packet/udp.rs index 08ec3d0..7f2ae13 100644 --- a/src/packet/udp.rs +++ b/src/packet/udp.rs @@ -1,100 +1,174 @@ -use crate::packet::setting::PacketBuildSetting; -use nex::packet::ethernet::EtherType; -use nex::packet::ip::IpNextLevelProtocol; -use nex::util::packet_builder::{ - builder::PacketBuilder, ethernet::EthernetPacketBuilder, ipv4::Ipv4PacketBuilder, - ipv6::Ipv6PacketBuilder, udp::UdpPacketBuilder, +use bytes::Bytes; +use netdev::{Interface, MacAddr}; +use nex::packet::builder::{ + ethernet::EthernetPacketBuilder, ipv4::Ipv4PacketBuilder, ipv6::Ipv6PacketBuilder, + udp::UdpPacketBuilder, }; -use std::net::{IpAddr, SocketAddr}; +use nex::packet::ethernet::EtherType; +use nex::packet::ip::IpNextProtocol; +use nex::packet::ipv4::Ipv4Flags; +use nex::packet::packet::Packet; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +use crate::config::default::DEFAULT_LOCAL_UDP_PORT; +use crate::trace::TraceSetting; /// Build UDP packet -pub fn build_udp_packet(setting: PacketBuildSetting) -> Vec { - let mut packet_builder = PacketBuilder::new(); - - // Ethernet Header - let ethernet_packet_builder = EthernetPacketBuilder { - src_mac: setting.src_mac, - dst_mac: setting.dst_mac, - ether_type: match setting.dst_ip { - IpAddr::V4(_) => EtherType::Ipv4, - IpAddr::V6(_) => EtherType::Ipv6, - }, +pub fn build_udp_packet(interface: &Interface, dst_ip: IpAddr, dst_port: u16, is_ip_packet: bool) -> Vec { + let src_mac = interface.mac_addr.unwrap_or(MacAddr::zero()); + let dst_mac = match &interface.gateway { + Some(gateway) => gateway.mac_addr, + None => MacAddr::zero(), }; - packet_builder.set_ethernet(ethernet_packet_builder); - - // IP Header - match setting.dst_ip { - IpAddr::V4(dst_ipv4) => match setting.src_ip { - IpAddr::V4(src_ipv4) => { - let mut ipv4_packet_builder = - Ipv4PacketBuilder::new(src_ipv4, dst_ipv4, IpNextLevelProtocol::Udp); - ipv4_packet_builder.ttl = Some(setting.hop_limit); - packet_builder.set_ipv4(ipv4_packet_builder); - } - IpAddr::V6(_) => {} - }, - IpAddr::V6(dst_ipv6) => match setting.src_ip { - IpAddr::V4(_) => {} - IpAddr::V6(src_ipv4) => { - let mut ipv6_packet_builder = - Ipv6PacketBuilder::new(src_ipv4, dst_ipv6, IpNextLevelProtocol::Udp); - ipv6_packet_builder.hop_limit = Some(setting.hop_limit); - packet_builder.set_ipv6(ipv6_packet_builder); - } - }, - } - // UDP Header - match setting.dst_ip { - IpAddr::V4(dst_ipv4) => match setting.src_ip { - IpAddr::V4(src_ipv4) => { - let udp_packet_builder = UdpPacketBuilder::new( - SocketAddr::new(IpAddr::V4(src_ipv4), setting.src_port), - SocketAddr::new(IpAddr::V4(dst_ipv4), setting.dst_port), - ); - packet_builder.set_udp(udp_packet_builder); - } - IpAddr::V6(_) => {} + let src_ipv4 = crate::interface::get_interface_ipv4(interface).unwrap_or(Ipv4Addr::UNSPECIFIED); + let src_global_ipv6 = + crate::interface::get_interface_global_ipv6(interface).unwrap_or(Ipv6Addr::UNSPECIFIED); + let src_local_ipv6 = + crate::interface::get_interface_local_ipv6(interface).unwrap_or(Ipv6Addr::UNSPECIFIED); + + let src_ip: IpAddr = match dst_ip { + IpAddr::V4(_) => { + IpAddr::V4(src_ipv4) }, - IpAddr::V6(dst_ipv6) => match setting.src_ip { - IpAddr::V4(_) => {} - IpAddr::V6(src_ipv6) => { - let udp_packet_builder = UdpPacketBuilder::new( - SocketAddr::new(IpAddr::V6(src_ipv6), setting.src_port), - SocketAddr::new(IpAddr::V6(dst_ipv6), setting.dst_port), - ); - packet_builder.set_udp(udp_packet_builder); + IpAddr::V6(_) => { + if nex::net::ip::is_global_ip(&dst_ip) { + IpAddr::V6(src_global_ipv6) + } else { + IpAddr::V6(src_local_ipv6) } }, - } - if setting.ip_packet { - packet_builder.ip_packet() + }; + + let udp_packet = UdpPacketBuilder::new(src_ip, dst_ip) + .source(DEFAULT_LOCAL_UDP_PORT) + .destination(dst_port) + .build(); + + let ip_packet: Bytes = match (src_ip, dst_ip) { + (IpAddr::V4(src), IpAddr::V4(dst)) => Ipv4PacketBuilder::new() + .source(src) + .destination(dst) + .protocol(IpNextProtocol::Udp) + .flags(Ipv4Flags::DontFragment) + .payload(udp_packet.to_bytes()) + .build() + .to_bytes(), + (IpAddr::V6(src), IpAddr::V6(dst)) => Ipv6PacketBuilder::new() + .source(src) + .destination(dst) + .next_header(IpNextProtocol::Udp) + .payload(udp_packet.to_bytes()) + .build() + .to_bytes(), + _ => panic!("Source and destination IP version mismatch"), + }; + + let ethernet_packet = EthernetPacketBuilder::new() + .source(if is_ip_packet { + MacAddr::zero() + } else { + src_mac + }) + .destination(if is_ip_packet { + MacAddr::zero() + } else { + dst_mac + }) + .ethertype(match dst_ip { + IpAddr::V4(_) => EtherType::Ipv4, + IpAddr::V6(_) => EtherType::Ipv6, + }) + .payload(ip_packet) + .build(); + + let packet: Bytes = if is_ip_packet { + ethernet_packet.ip_packet().unwrap() } else { - packet_builder.packet() - } + ethernet_packet.to_bytes() + }; + + packet.to_vec() } -pub fn build_ip_next_udp_packet(setting: PacketBuildSetting) -> Vec { - // UDP Header - match setting.dst_ip { - IpAddr::V4(dst_ipv4) => match setting.src_ip { - IpAddr::V4(src_ipv4) => { - let udp_packet_builder = UdpPacketBuilder::new( - SocketAddr::new(IpAddr::V4(src_ipv4), setting.src_port), - SocketAddr::new(IpAddr::V4(dst_ipv4), setting.dst_port), - ); - udp_packet_builder.build() - } - IpAddr::V6(_) => Vec::new(), +/// Build UDP packet for traceroute with specific TTL +pub fn build_udp_trace_packet(interface: &Interface, trace_setting: &TraceSetting, seq_ttl: u8) -> Vec { + let src_mac = interface.mac_addr.unwrap_or(MacAddr::zero()); + let dst_mac = match &interface.gateway { + Some(gateway) => gateway.mac_addr, + None => MacAddr::zero(), + }; + let src_ipv4 = crate::interface::get_interface_ipv4(interface).unwrap_or(Ipv4Addr::UNSPECIFIED); + let src_global_ipv6 = + crate::interface::get_interface_global_ipv6(interface).unwrap_or(Ipv6Addr::UNSPECIFIED); + let src_local_ipv6 = + crate::interface::get_interface_local_ipv6(interface).unwrap_or(Ipv6Addr::UNSPECIFIED); + + let src_ip: IpAddr = match trace_setting.dst_ip { + IpAddr::V4(_) => { + IpAddr::V4(src_ipv4) }, - IpAddr::V6(dst_ipv6) => match setting.src_ip { - IpAddr::V4(_) => Vec::new(), - IpAddr::V6(src_ipv6) => { - let udp_packet_builder = UdpPacketBuilder::new( - SocketAddr::new(IpAddr::V6(src_ipv6), setting.src_port), - SocketAddr::new(IpAddr::V6(dst_ipv6), setting.dst_port), - ); - udp_packet_builder.build() + IpAddr::V6(_) => { + if nex::net::ip::is_global_ip(&trace_setting.dst_ip) { + IpAddr::V6(src_global_ipv6) + } else { + IpAddr::V6(src_local_ipv6) } }, - } + }; + + let dst_port = trace_setting.dst_port.unwrap_or(DEFAULT_LOCAL_UDP_PORT); + + let is_ip_packet = interface.is_tun() || interface.is_loopback(); + + let udp_packet = UdpPacketBuilder::new(src_ip, trace_setting.dst_ip) + .source(DEFAULT_LOCAL_UDP_PORT) + .destination(dst_port) + .build(); + + let ip_packet: Bytes = match (src_ip, trace_setting.dst_ip) { + (IpAddr::V4(src), IpAddr::V4(dst)) => Ipv4PacketBuilder::new() + .source(src) + .destination(dst) + .protocol(IpNextProtocol::Udp) + .flags(Ipv4Flags::DontFragment) + .ttl(seq_ttl) + .payload(udp_packet.to_bytes()) + .build() + .to_bytes(), + (IpAddr::V6(src), IpAddr::V6(dst)) => Ipv6PacketBuilder::new() + .source(src) + .destination(dst) + .next_header(IpNextProtocol::Udp) + .hop_limit(seq_ttl) + .payload(udp_packet.to_bytes()) + .build() + .to_bytes(), + _ => panic!("Source and destination IP version mismatch"), + }; + + let ethernet_packet = EthernetPacketBuilder::new() + .source(if is_ip_packet { + MacAddr::zero() + } else { + src_mac + }) + .destination(if is_ip_packet { + MacAddr::zero() + } else { + dst_mac + }) + .ethertype(match trace_setting.dst_ip { + IpAddr::V4(_) => EtherType::Ipv4, + IpAddr::V6(_) => EtherType::Ipv6, + }) + .payload(ip_packet) + .build(); + + let packet: Bytes = if is_ip_packet { + ethernet_packet.ip_packet().unwrap() + } else { + ethernet_packet.to_bytes() + }; + + packet.to_vec() } diff --git a/src/pcap/setting.rs b/src/pcap/setting.rs deleted file mode 100644 index 09152f6..0000000 --- a/src/pcap/setting.rs +++ /dev/null @@ -1,119 +0,0 @@ -use nex::net::interface::Interface; -use nex::packet::{ethernet::EtherType, ip::IpNextLevelProtocol}; -use serde::{Deserialize, Serialize}; -use std::collections::HashSet; -use std::net::IpAddr; -use std::time::Duration; - -use crate::interface; - -/// Packet capture options -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct PacketCaptureSetting { - /// Interface index - pub interface_index: u32, - /// Source IP addresses to filter. If empty, all source IP addresses will be captured - pub src_ips: HashSet, - /// Destination IP addresses to filter. If empty, all destination IP addresses will be captured - pub dst_ips: HashSet, - /// Source ports to filter. If empty, all source ports will be captured - pub src_ports: HashSet, - /// Destination ports to filter. If empty, all destination ports will be captured - pub dst_ports: HashSet, - /// Ether types to filter. If empty, all ether types will be captured - pub ether_types: HashSet, - /// IP protocols to filter. If empty, all IP protocols will be captured - pub ip_protocols: HashSet, - /// Capture duration limit - pub capture_timeout: Duration, - /// Read Timeout for read next packet (Linux, BPF only) - pub read_timeout: Duration, - /// Capture in promiscuous mode - pub promiscuous: bool, - /// Receive undefined packets - pub receive_undefined: bool, - /// Use TUN interface - pub tunnel: bool, - /// Use Loopback interface - pub loopback: bool, -} - -impl Default for PacketCaptureSetting { - fn default() -> Self { - let iface: Interface = netdev::get_default_interface().unwrap(); - Self { - interface_index: iface.index, - src_ips: HashSet::new(), - dst_ips: HashSet::new(), - src_ports: HashSet::new(), - dst_ports: HashSet::new(), - ether_types: HashSet::new(), - ip_protocols: HashSet::new(), - capture_timeout: Duration::MAX, - read_timeout: Duration::from_millis(200), - promiscuous: false, - receive_undefined: true, - tunnel: iface.is_tun(), - loopback: iface.is_loopback(), - } - } -} - -impl PacketCaptureSetting { - pub fn from_interface_index(if_index: u32) -> Option { - let iface = interface::get_interface_by_index(if_index)?; - let options = PacketCaptureSetting { - interface_index: iface.index, - src_ips: HashSet::new(), - dst_ips: HashSet::new(), - src_ports: HashSet::new(), - dst_ports: HashSet::new(), - ether_types: HashSet::new(), - ip_protocols: HashSet::new(), - capture_timeout: Duration::MAX, - read_timeout: Duration::from_millis(200), - promiscuous: false, - receive_undefined: true, - tunnel: iface.is_tun(), - loopback: iface.is_loopback(), - }; - Some(options) - } - pub fn from_interface_name(if_name: String) -> PacketCaptureSetting { - let iface = interface::get_interface_by_name(if_name).unwrap(); - let options: PacketCaptureSetting = PacketCaptureSetting { - interface_index: iface.index, - src_ips: HashSet::new(), - dst_ips: HashSet::new(), - src_ports: HashSet::new(), - dst_ports: HashSet::new(), - ether_types: HashSet::new(), - ip_protocols: HashSet::new(), - capture_timeout: Duration::MAX, - read_timeout: Duration::from_millis(200), - promiscuous: false, - receive_undefined: true, - tunnel: iface.is_tun(), - loopback: iface.is_loopback(), - }; - options - } - pub fn from_interface(iface: &Interface) -> PacketCaptureSetting { - let options = PacketCaptureSetting { - interface_index: iface.index, - src_ips: HashSet::new(), - dst_ips: HashSet::new(), - src_ports: HashSet::new(), - dst_ports: HashSet::new(), - ether_types: HashSet::new(), - ip_protocols: HashSet::new(), - capture_timeout: Duration::MAX, - read_timeout: Duration::from_millis(200), - promiscuous: false, - receive_undefined: true, - tunnel: iface.is_tun(), - loopback: iface.is_loopback(), - }; - options - } -} diff --git a/src/ping/mod.rs b/src/ping/mod.rs index d284651..1381f67 100644 --- a/src/ping/mod.rs +++ b/src/ping/mod.rs @@ -1,3 +1,69 @@ pub mod pinger; -pub mod result; pub mod setting; +pub mod result; +pub mod probe; + +use std::time::Duration; + +use anyhow::Result; +use netdev::Interface; + +use crate::{endpoint::Host, ping::{pinger::Pinger, setting::PingSetting}}; + +// Check reachability of the target and measure latency before probing +pub async fn initial_ping(interface: &Interface, dst_host: &Host, port: Option) -> Result { + match dst_host.hostname { + Some(ref name) => { + tracing::info!("Performing initial ping to {} ({})", name, dst_host.ip); + }, + None => { + tracing::info!("Performing initial ping to {}", dst_host.ip); + } + } + + // 1. Try ICMP ping + let icmp_setting: PingSetting = PingSetting::icmp_ping(&interface, dst_host.clone(), 1)?; + let pinger = Pinger::new(icmp_setting); + match pinger.run().await { + Ok(r) => { + if let Some(first) = r.first_response() { + return Ok(first.rtt); + } + }, + Err(e) => { + tracing::warn!("Initial ICMP ping failed: {}", e); + } + } + + // 2. Try UDP ping + let udp_setting: PingSetting = PingSetting::udp_ping(&interface, dst_host.clone(), 1)?; + let pinger = Pinger::new(udp_setting); + match pinger.run().await { + Ok(r) => { + if let Some(first) = r.first_response() { + return Ok(first.rtt); + } + }, + Err(e) => { + tracing::warn!("Initial UDP ping failed: {}", e); + } + } + + // 3. Try TCP ping + let target_port = port.unwrap_or(80); + let tcp_setting: PingSetting = PingSetting::tcp_ping(&interface, dst_host.clone(), target_port, 1)?; + let pinger = Pinger::new(tcp_setting); + match pinger.run().await { + Ok(r) => { + if let Some(first) = r.first_response() { + return Ok(first.rtt); + } + }, + Err(e) => { + tracing::warn!("Initial TCP ping failed: {}", e); + } + } + + anyhow::bail!("All initial ping methods failed"); + +} \ No newline at end of file diff --git a/src/ping/pinger.rs b/src/ping/pinger.rs index 1da20a3..e641405 100644 --- a/src/ping/pinger.rs +++ b/src/ping/pinger.rs @@ -1,20 +1,5 @@ -use super::result::{PingResult, PingStat}; -use super::setting::PingSetting; -use crate::host::{NodeType, PortStatus}; -use crate::packet::setting::PacketBuildSetting; -use crate::probe::{ProbeResult, ProbeStatus, ProbeStatusKind}; -use crate::protocol::Protocol; -use netdev::Interface; -use nex::datalink::{RawReceiver, RawSender}; -use nex::net::mac::MacAddr; -use nex::packet::frame::{Frame, ParseOption}; -use nex::packet::icmp::IcmpType; -use nex::packet::icmpv6::Icmpv6Type; -use nex::packet::tcp::TcpFlags; -use std::net::IpAddr; -use std::sync::mpsc::{channel, Receiver, Sender}; -use std::sync::{Arc, Mutex}; -use std::time::{Duration, Instant}; +use crate::{ping::{result::PingResult, setting::PingSetting}, protocol::Protocol}; +use anyhow::Result; /// Pinger structure. /// @@ -23,698 +8,22 @@ use std::time::{Duration, Instant}; pub struct Pinger { /// Probe Setting pub ping_setting: PingSetting, - /// Sender for progress messaging - tx: Arc>>, - /// Receiver for progress messaging - rx: Arc>>, } impl Pinger { - /// Create new Pinger instance with destination IP address - pub fn new(setting: PingSetting) -> Result { - // Check interface - if crate::interface::get_interface_by_index(setting.if_index).is_none() { - return Err(format!( - "Pinger::new: unable to get interface. index: {}", - setting.if_index - )); - } - let (tx, rx) = channel(); - let pinger = Pinger { - ping_setting: setting, - tx: Arc::new(Mutex::new(tx)), - rx: Arc::new(Mutex::new(rx)), - }; - return Ok(pinger); - } - /// Run ping - pub fn ping(&self) -> Result { - run_ping(&self.ping_setting, &self.tx) - } - /// Get progress receiver - pub fn get_progress_receiver(&self) -> Arc>> { - self.rx.clone() + /// Create a new Pinger instance. + pub fn new(ping_setting: PingSetting) -> Self { + Self { ping_setting } } -} - -fn run_ping( - setting: &PingSetting, - msg_tx: &Arc>>, -) -> Result { - let interface: Interface = match crate::interface::get_interface_by_index(setting.if_index) { - Some(interface) => interface, - None => { - return Err(format!( - "run_ping: unable to get interface by index {}", - setting.if_index - )) - } - }; - let config = nex::datalink::Config { - write_buffer_size: 4096, - read_buffer_size: 4096, - read_timeout: Some(setting.receive_timeout), - write_timeout: None, - channel_type: nex::datalink::ChannelType::Layer2, - bpf_fd_attempts: 1000, - linux_fanout: None, - promiscuous: false, - }; - // Create a channel to send/receive packet - let (mut tx, mut rx) = match nex::datalink::channel(&interface, config) { - Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), - Ok(_) => return Err("run_ping: unable to create channel".to_string()), - Err(e) => return Err(format!("run_ping: unable to create channel: {}", e)), - }; - match setting.protocol { - crate::protocol::Protocol::ICMP => { - let result = icmp_ping(&mut tx, &mut rx, setting, msg_tx); - return Ok(result); - } - crate::protocol::Protocol::TCP => { - let result = tcp_ping(&mut tx, &mut rx, setting, msg_tx); - return Ok(result); - } - crate::protocol::Protocol::UDP => { - let result = udp_ping(&mut tx, &mut rx, setting, msg_tx); - return Ok(result); - } - _ => { - return Err("run_ping: unsupported protocol".to_string()); + /// Run the ping based on the specified protocol and return the results. + pub async fn run(&self) -> Result { + match self.ping_setting.protocol { + Protocol::Icmp => super::probe::icmp::run_icmp_ping(&self.ping_setting).await, + Protocol::Udp => super::probe::udp::run_udp_ping(&self.ping_setting).await, + Protocol::Tcp => super::probe::tcp::run_tcp_ping(&self.ping_setting).await, + _ => { + Err(anyhow::anyhow!("Unsupported protocol")) + }, } } } - -pub fn icmp_ping( - tx: &mut Box, - rx: &mut Box, - setting: &PingSetting, - msg_tx: &Arc>>, -) -> PingResult { - let mut result = PingResult::new(); - result.protocol = Protocol::ICMP; - let mut parse_option: ParseOption = ParseOption::default(); - if setting.tunnel { - let payload_offset = if setting.loopback { 14 } else { 0 }; - parse_option.from_ip_packet = true; - parse_option.offset = payload_offset; - } - result.start_time = crate::sys::time::get_sysdate(); - let start_time = Instant::now(); - let mut responses: Vec = Vec::new(); - let packet_setting: PacketBuildSetting = PacketBuildSetting::from_ping_setting(setting); - let icmp_packet: Vec = crate::packet::icmp::build_icmp_packet(packet_setting.clone()); - for seq in 1..setting.count + 1 { - //let icmp_packet: Vec = crate::packet::icmp::build_icmp_packet(PacketBuildSetting::from_ping_setting(setting)); - let send_time = Instant::now(); - match tx.send(&icmp_packet) { - Some(_) => {} - None => {} - } - loop { - match rx.next() { - Ok(packet) => { - let recv_time: Duration = Instant::now().duration_since(send_time); - let frame: Frame = Frame::from_bytes(&packet, parse_option.clone()); - // Datalink - let mut mac_addr: MacAddr = MacAddr::zero(); - if let Some(datalink_layer) = &frame.datalink { - // Ethernet - if let Some(ethernet_header) = &datalink_layer.ethernet { - mac_addr = ethernet_header.source; - } - } - if let Some(ip_layer) = &frame.ip { - // IPv4 - if let Some(ipv4_header) = &ip_layer.ipv4 { - if IpAddr::V4(ipv4_header.source) != setting.dst_ip - || IpAddr::V4(ipv4_header.destination) != packet_setting.src_ip - { - continue; - } - // IPv4 ICMP - if let Some(icmp_header) = &ip_layer.icmp { - if icmp_header.icmp_type == IcmpType::EchoReply { - let probe_result: ProbeResult = ProbeResult { - seq: seq, - mac_addr: mac_addr, - ip_addr: setting.dst_ip, - host_name: setting.dst_hostname.clone(), - port_number: None, - port_status: None, - ttl: ipv4_header.ttl, - hop: crate::ip::guess_initial_ttl(ipv4_header.ttl) - - ipv4_header.ttl, - rtt: recv_time, - probe_status: ProbeStatus::new(), - protocol: Protocol::ICMP, - node_type: NodeType::Destination, - sent_packet_size: icmp_packet.len(), - received_packet_size: packet.len(), - }; - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - } - // IPv6 - if let Some(ipv6_header) = &ip_layer.ipv6 { - if IpAddr::V6(ipv6_header.source) != setting.dst_ip - || IpAddr::V6(ipv6_header.destination) != packet_setting.src_ip - { - continue; - } - // ICMPv6 - if let Some(icmpv6_header) = &ip_layer.icmpv6 { - if icmpv6_header.icmpv6_type == Icmpv6Type::EchoReply { - let probe_result: ProbeResult = ProbeResult { - seq: seq, - mac_addr: mac_addr, - ip_addr: setting.dst_ip, - host_name: setting.dst_hostname.clone(), - port_number: None, - port_status: None, - ttl: ipv6_header.hop_limit, - hop: crate::ip::guess_initial_ttl(ipv6_header.hop_limit) - - ipv6_header.hop_limit, - rtt: recv_time, - probe_status: ProbeStatus::new(), - protocol: Protocol::ICMP, - node_type: NodeType::Destination, - sent_packet_size: icmp_packet.len(), - received_packet_size: packet.len(), - }; - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - } - } - } - Err(_e) => { - let probe_result = ProbeResult::timeout( - seq, - setting.dst_ip, - setting.dst_hostname.clone(), - Protocol::ICMP, - icmp_packet.len(), - ); - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - let wait_time: Duration = Instant::now().duration_since(send_time); - if wait_time > setting.receive_timeout { - let probe_result = ProbeResult::timeout( - seq, - setting.dst_ip, - setting.dst_hostname.clone(), - Protocol::ICMP, - icmp_packet.len(), - ); - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - if seq < setting.count { - std::thread::sleep(setting.send_rate); - } - } - let probe_time = Instant::now().duration_since(start_time); - result.end_time = crate::sys::time::get_sysdate(); - result.elapsed_time = probe_time; - let received_count: usize = responses - .iter() - .filter(|r| r.probe_status.kind == ProbeStatusKind::Done) - .count(); - if received_count == 0 { - result.probe_status = ProbeStatus::with_error_message("No response".to_string()); - } else { - let ping_stat: PingStat = PingStat { - responses: responses.clone(), - probe_time: probe_time, - transmitted_count: setting.count as usize, - received_count: received_count, - min: responses - .iter() - .map(|r| r.rtt) - .min() - .unwrap_or(Duration::from_millis(0)), - avg: responses - .iter() - .fold(Duration::from_millis(0), |acc, r| acc + r.rtt) - / received_count as u32, - max: responses - .iter() - .map(|r| r.rtt) - .max() - .unwrap_or(Duration::from_millis(0)), - }; - result.stat = ping_stat; - result.probe_status = ProbeStatus::new(); - } - result -} - -pub fn tcp_ping( - tx: &mut Box, - rx: &mut Box, - setting: &PingSetting, - msg_tx: &Arc>>, -) -> PingResult { - let mut result = PingResult::new(); - result.protocol = Protocol::ICMP; - let mut parse_option: ParseOption = ParseOption::default(); - if setting.tunnel { - let payload_offset = if setting.loopback { 14 } else { 0 }; - parse_option.from_ip_packet = true; - parse_option.offset = payload_offset; - } - result.start_time = crate::sys::time::get_sysdate(); - let start_time = Instant::now(); - let mut responses: Vec = Vec::new(); - let packet_setting: PacketBuildSetting = PacketBuildSetting::from_ping_setting(setting); - let tcp_packet: Vec = crate::packet::tcp::build_tcp_syn_packet(packet_setting.clone()); - for seq in 1..setting.count + 1 { - //let tcp_packet: Vec = crate::packet::tcp::build_tcp_packet(setting.clone(), None); - let send_time = Instant::now(); - match tx.send(&tcp_packet) { - Some(_) => {} - None => {} - } - loop { - match rx.next() { - Ok(packet) => { - let recv_time: Duration = Instant::now().duration_since(send_time); - let frame: Frame = Frame::from_bytes(&packet, parse_option.clone()); - // Datalink - let mut mac_addr: MacAddr = MacAddr::zero(); - if let Some(datalink_layer) = &frame.datalink { - // Ethernet - if let Some(ethernet_header) = &datalink_layer.ethernet { - mac_addr = ethernet_header.source; - } - } - // So deep nested... but this is simplest way to check TCP packet safely. - if let Some(ip_layer) = &frame.ip { - if let Some(ipv4_header) = &ip_layer.ipv4 { - if IpAddr::V4(ipv4_header.source) != setting.dst_ip - || IpAddr::V4(ipv4_header.destination) != packet_setting.src_ip - { - continue; - } - } - if let Some(ipv6_header) = &ip_layer.ipv6 { - if IpAddr::V6(ipv6_header.source) != setting.dst_ip - || IpAddr::V6(ipv6_header.destination) != packet_setting.src_ip - { - continue; - } - } - if let Some(transport_layer) = &frame.transport { - if let Some(tcp_header) = &transport_layer.tcp { - if let Some(port) = setting.dst_port { - if tcp_header.source != port { - continue; - } - } - let mut probe_result: ProbeResult = ProbeResult { - seq: seq, - mac_addr: mac_addr, - ip_addr: setting.dst_ip, - host_name: setting.dst_hostname.clone(), - port_number: Some(tcp_header.source), - port_status: None, - ttl: 0, - hop: 0, - rtt: recv_time, - probe_status: ProbeStatus::new(), - protocol: Protocol::TCP, - node_type: NodeType::Destination, - sent_packet_size: tcp_packet.len(), - received_packet_size: packet.len(), - }; - if tcp_header.flags == TcpFlags::SYN | TcpFlags::ACK { - probe_result.port_status = Some(PortStatus::Open); - if let Some(ipv4) = &ip_layer.ipv4 { - if IpAddr::V4(ipv4.source) != setting.dst_ip { - continue; - } - probe_result.ttl = ipv4.ttl; - probe_result.hop = - crate::ip::guess_initial_ttl(ipv4.ttl) - ipv4.ttl; - } else if let Some(ipv6) = &ip_layer.ipv6 { - if IpAddr::V6(ipv6.source) != setting.dst_ip { - continue; - } - probe_result.ttl = ipv6.hop_limit; - probe_result.hop = - crate::ip::guess_initial_ttl(ipv6.hop_limit) - - ipv6.hop_limit; - } - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } else if tcp_header.flags == TcpFlags::RST | TcpFlags::ACK { - probe_result.port_status = Some(PortStatus::Closed); - if let Some(ipv4) = &ip_layer.ipv4 { - if IpAddr::V4(ipv4.source) != setting.dst_ip { - continue; - } - probe_result.ttl = ipv4.ttl; - probe_result.hop = - crate::ip::guess_initial_ttl(ipv4.ttl) - ipv4.ttl; - } else if let Some(ipv6) = &ip_layer.ipv6 { - if IpAddr::V6(ipv6.source) != setting.dst_ip { - continue; - } - probe_result.ttl = ipv6.hop_limit; - probe_result.hop = - crate::ip::guess_initial_ttl(ipv6.hop_limit) - - ipv6.hop_limit; - } - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - } - } - } - Err(_e) => { - let mut probe_result = ProbeResult::timeout( - seq, - setting.dst_ip, - setting.dst_hostname.clone(), - Protocol::TCP, - tcp_packet.len(), - ); - probe_result.port_number = setting.dst_port; - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - let wait_time: Duration = Instant::now().duration_since(send_time); - if wait_time > setting.receive_timeout { - let mut probe_result = ProbeResult::timeout( - seq, - setting.dst_ip, - setting.dst_hostname.clone(), - Protocol::TCP, - tcp_packet.len(), - ); - probe_result.port_number = setting.dst_port; - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - if seq < setting.count { - std::thread::sleep(setting.send_rate); - } - } - let probe_time = Instant::now().duration_since(start_time); - result.end_time = crate::sys::time::get_sysdate(); - result.elapsed_time = probe_time; - let received_count: usize = responses - .iter() - .filter(|r| r.probe_status.kind == ProbeStatusKind::Done) - .count(); - if received_count == 0 { - result.probe_status = ProbeStatus::with_error_message("No response".to_string()); - } else { - let ping_stat: PingStat = PingStat { - responses: responses.clone(), - probe_time: probe_time, - transmitted_count: setting.count as usize, - received_count: received_count, - min: responses - .iter() - .map(|r| r.rtt) - .min() - .unwrap_or(Duration::from_millis(0)), - avg: responses - .iter() - .fold(Duration::from_millis(0), |acc, r| acc + r.rtt) - / received_count as u32, - max: responses - .iter() - .map(|r| r.rtt) - .max() - .unwrap_or(Duration::from_millis(0)), - }; - result.stat = ping_stat; - result.probe_status = ProbeStatus::new(); - } - result -} - -pub fn udp_ping( - tx: &mut Box, - rx: &mut Box, - setting: &PingSetting, - msg_tx: &Arc>>, -) -> PingResult { - let mut result = PingResult::new(); - result.protocol = Protocol::UDP; - let mut parse_option: ParseOption = ParseOption::default(); - if setting.tunnel { - let payload_offset = if setting.loopback { 14 } else { 0 }; - parse_option.from_ip_packet = true; - parse_option.offset = payload_offset; - } - result.start_time = crate::sys::time::get_sysdate(); - let start_time = Instant::now(); - let mut responses: Vec = Vec::new(); - let packet_setting: PacketBuildSetting = PacketBuildSetting::from_ping_setting(setting); - let udp_packet: Vec = crate::packet::udp::build_udp_packet(packet_setting.clone()); - for seq in 1..setting.count + 1 { - //let udp_packet: Vec = crate::packet::udp::build_udp_packet(setting.clone(), None); - let send_time = Instant::now(); - match tx.send(&udp_packet) { - Some(_) => {} - None => {} - } - loop { - match rx.next() { - Ok(packet) => { - let recv_time: Duration = Instant::now().duration_since(send_time); - let frame: Frame = Frame::from_bytes(&packet, parse_option.clone()); - // Datalink - let mut mac_addr: MacAddr = MacAddr::zero(); - if let Some(datalink_layer) = &frame.datalink { - // Ethernet - if let Some(ethernet_header) = &datalink_layer.ethernet { - mac_addr = ethernet_header.source; - } - } - if let Some(ip_layer) = &frame.ip { - // IPv4 - if let Some(ipv4_header) = &ip_layer.ipv4 { - if IpAddr::V4(ipv4_header.source) != setting.dst_ip - || IpAddr::V4(ipv4_header.destination) != packet_setting.src_ip - { - continue; - } - // ICMP - if let Some(icmp_header) = &ip_layer.icmp { - if icmp_header.icmp_type == IcmpType::DestinationUnreachable { - let probe_result: ProbeResult = ProbeResult { - seq: seq, - mac_addr: mac_addr, - ip_addr: setting.dst_ip, - host_name: setting.dst_hostname.clone(), - port_number: setting.dst_port, - port_status: Some(PortStatus::Closed), - ttl: ipv4_header.ttl, - hop: crate::ip::guess_initial_ttl(ipv4_header.ttl) - - ipv4_header.ttl, - rtt: recv_time, - probe_status: ProbeStatus::new(), - protocol: Protocol::UDP, - node_type: NodeType::Destination, - sent_packet_size: udp_packet.len(), - received_packet_size: packet.len(), - }; - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - } - // IPv6 - if let Some(ipv6_header) = &ip_layer.ipv6 { - if IpAddr::V6(ipv6_header.destination) != packet_setting.src_ip { - continue; - } - // ICMPv6 - if let Some(icmpv6_header) = &ip_layer.icmpv6 { - if icmpv6_header.icmpv6_type == Icmpv6Type::DestinationUnreachable { - let probe_result: ProbeResult = ProbeResult { - seq: seq, - mac_addr: mac_addr, - ip_addr: setting.dst_ip, - host_name: setting.dst_hostname.clone(), - port_number: setting.dst_port, - port_status: Some(PortStatus::Closed), - ttl: ipv6_header.hop_limit, - hop: crate::ip::guess_initial_ttl(ipv6_header.hop_limit) - - ipv6_header.hop_limit, - rtt: recv_time, - probe_status: ProbeStatus::new(), - protocol: Protocol::UDP, - node_type: NodeType::Destination, - sent_packet_size: udp_packet.len(), - received_packet_size: packet.len(), - }; - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - } - } - } - Err(_e) => { - let probe_result = ProbeResult::timeout( - seq, - setting.dst_ip, - setting.dst_hostname.clone(), - Protocol::UDP, - udp_packet.len(), - ); - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - let wait_time: Duration = Instant::now().duration_since(send_time); - if wait_time > setting.receive_timeout { - let probe_result = ProbeResult::timeout( - seq, - setting.dst_ip, - setting.dst_hostname.clone(), - Protocol::UDP, - udp_packet.len(), - ); - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - if seq < setting.count { - std::thread::sleep(setting.send_rate); - } - } - let probe_time = Instant::now().duration_since(start_time); - result.end_time = crate::sys::time::get_sysdate(); - result.elapsed_time = probe_time; - let received_count: usize = responses - .iter() - .filter(|r| r.probe_status.kind == ProbeStatusKind::Done) - .count(); - if received_count == 0 { - result.probe_status = ProbeStatus::with_error_message("No response".to_string()); - } else { - let ping_stat: PingStat = PingStat { - responses: responses.clone(), - probe_time: probe_time, - transmitted_count: setting.count as usize, - received_count: received_count, - min: responses - .iter() - .map(|r| r.rtt) - .min() - .unwrap_or(Duration::from_millis(0)), - avg: responses - .iter() - .fold(Duration::from_millis(0), |acc, r| acc + r.rtt) - / received_count as u32, - max: responses - .iter() - .map(|r| r.rtt) - .max() - .unwrap_or(Duration::from_millis(0)), - }; - result.stat = ping_stat; - result.probe_status = ProbeStatus::new(); - } - result -} diff --git a/src/ping/probe/icmp.rs b/src/ping/probe/icmp.rs new file mode 100644 index 0000000..46bb46c --- /dev/null +++ b/src/ping/probe/icmp.rs @@ -0,0 +1,264 @@ +use std::collections::HashSet; +use std::net::IpAddr; +use std::time::{Duration, Instant}; +use futures::stream::StreamExt; +use futures::future::poll_fn; +use netdev::MacAddr; +use nex::packet::frame::{Frame, ParseOption}; +use nex::packet::icmp::IcmpType; +use nex::packet::icmpv6::Icmpv6Type; +use crate::endpoint::NodeType; +use crate::ping::result::PingStat; +use crate::probe::{ProbeStatus, ProbeStatusKind}; +use crate::{ping::{result::PingResult, setting::PingSetting}, probe::ProbeResult, protocol::Protocol}; +use anyhow::Result; +use nex::datalink::async_io::{async_channel, AsyncChannel}; +use tracing_indicatif::span_ext::IndicatifSpanExt; + +/// Run ICMP Ping and return the results. +pub async fn run_icmp_ping(setting: &PingSetting) -> Result { + let mut result = PingResult::new(); + result.protocol = Protocol::Icmp; + + let interface = match crate::interface::get_interface_by_index(setting.if_index) { + Some(interface) => interface, + None => return Err(anyhow::anyhow!("Interface not found")), + }; + let src_ip_set: HashSet = interface.ip_addrs().iter().map(|ip| ip.clone()).collect(); + // Create sender + let config = nex::datalink::Config { + write_buffer_size: 4096, + read_buffer_size: 4096, + read_timeout: Some(setting.receive_timeout), + write_timeout: None, + channel_type: nex::datalink::ChannelType::Layer2, + bpf_fd_attempts: 1000, + linux_fanout: None, + promiscuous: false, + }; + + let AsyncChannel::Ethernet(mut tx, mut rx) = async_channel(&interface, config)? + else { + unreachable!(); + }; + + let mut responses: Vec = Vec::new(); + + let mut parse_option: ParseOption = ParseOption::default(); + if interface.is_tun() || (cfg!(any(target_os = "macos", target_os = "ios")) && interface.is_loopback()) { + let payload_offset = if interface.is_loopback() { 14 } else { 0 }; + parse_option.from_ip_packet = true; + parse_option.offset = payload_offset; + } + + let header_span = tracing::info_span!("ping"); + header_span.pb_set_style(&crate::output::progress::get_progress_style()); + header_span.pb_set_message(&format!("ping ({})", setting.dst_ip)); + header_span.pb_set_length(setting.count as u64); + header_span.pb_set_position(0); + header_span.pb_start(); + + let start_time = Instant::now(); + let icmp_packet = crate::packet::icmp::build_icmp_packet(&interface, setting.dst_ip, false); + for seq in 1..setting.count + 1 { + let send_time = Instant::now(); + match poll_fn(|cx| tx.poll_send(cx, &icmp_packet)).await { + Ok(_) => { + }, + Err(e) => eprintln!("Failed to send packet: {}", e), + } + loop { + match tokio::time::timeout(setting.receive_timeout, rx.next()).await { + Ok(Some(Ok(packet))) => { + let rtt = send_time.elapsed(); + let frame = match Frame::from_buf(&packet, parse_option.clone()) { + Some(frame) => frame, + None => { + eprintln!("Failed to parse packet: {:?}", packet); + continue; + } + }; + let mut mac_addr: MacAddr = MacAddr::zero(); + if let Some(datalink_layer) = &frame.datalink { + // Ethernet + if let Some(ethernet_header) = &datalink_layer.ethernet { + mac_addr = ethernet_header.source; + } + } + if let Some(ip_layer) = &frame.ip { + // IPv4 + if let Some(ipv4_header) = &ip_layer.ipv4 { + if IpAddr::V4(ipv4_header.source) == setting.dst_ip + && src_ip_set.contains(&IpAddr::V4(ipv4_header.destination)) + { + // IPv4 ICMP + if let Some(icmp_header) = &ip_layer.icmp { + if icmp_header.icmp_type == IcmpType::EchoReply { + let probe_result: ProbeResult = ProbeResult { + seq: seq, + mac_addr: mac_addr, + ip_addr: setting.dst_ip, + host_name: setting.dst_hostname.clone(), + port_number: None, + port_status: None, + ttl: ipv4_header.ttl, + hop: crate::util::ip::initial_ttl(ipv4_header.ttl) + - ipv4_header.ttl, + rtt: rtt, + probe_status: ProbeStatus::new(), + protocol: Protocol::Icmp, + node_type: NodeType::Destination, + sent_packet_size: icmp_packet.len(), + received_packet_size: packet.len(), + }; + tracing::info!("Reply from {}, bytes={} RTT={:?} TTL={}", setting.dst_ip, packet.len(), rtt, ipv4_header.ttl); + responses.push(probe_result); + header_span.pb_inc(1); + break; + } + } + } + } + // IPv6 + if let Some(ipv6_header) = &ip_layer.ipv6 { + if IpAddr::V6(ipv6_header.source) == setting.dst_ip + && src_ip_set.contains(&IpAddr::V6(ipv6_header.destination)) + { + // ICMPv6 + if let Some(icmpv6_header) = &ip_layer.icmpv6 { + if icmpv6_header.icmpv6_type == Icmpv6Type::EchoReply { + let probe_result: ProbeResult = ProbeResult { + seq: seq, + mac_addr: mac_addr, + ip_addr: setting.dst_ip, + host_name: setting.dst_hostname.clone(), + port_number: None, + port_status: None, + ttl: ipv6_header.hop_limit, + hop: crate::util::ip::initial_ttl(ipv6_header.hop_limit) + - ipv6_header.hop_limit, + rtt: rtt, + probe_status: ProbeStatus::new(), + protocol: Protocol::Icmp, + node_type: NodeType::Destination, + sent_packet_size: icmp_packet.len(), + received_packet_size: packet.len(), + }; + tracing::info!("Reply from {}, bytes={} RTT={:?} TTL={}", setting.dst_ip, packet.len(), rtt, ipv6_header.hop_limit); + responses.push(probe_result); + header_span.pb_inc(1); + break; + } + } + } + } + } + }, + Ok(Some(Err(e))) => { + tracing::error!("Failed to receive packet: {}", e); + header_span.pb_inc(1); + break; + }, + Ok(None) => { + tracing::error!("Channel closed"); + header_span.pb_inc(1); + break; + }, + Err(_) => { + tracing::error!("Request timeout for seq {}", seq); + let probe_result = ProbeResult::timeout( + seq, + setting.dst_ip, + setting.dst_hostname.clone(), + Protocol::Icmp, + icmp_packet.len(), + ); + responses.push(probe_result); + + header_span.pb_inc(1); + break; + } + } + + let elapsed_time: Duration = send_time.elapsed(); + if elapsed_time > setting.receive_timeout { + tracing::error!("Request timeout for seq {}", seq); + let probe_result = ProbeResult::timeout( + seq, + setting.dst_ip, + setting.dst_hostname.clone(), + Protocol::Icmp, + icmp_packet.len(), + ); + responses.push(probe_result); + + header_span.pb_inc(1); + break; + } + } + if !setting.send_rate.is_zero() { + tokio::time::sleep(setting.send_rate).await; + } + } + + // Finish header span + drop(header_span); + + let elapsed_time = start_time.elapsed(); + let received_count: usize = responses + .iter() + .filter(|r| r.probe_status.kind == ProbeStatusKind::Done) + .count(); + + let min_opt = if received_count > 0 { + let min = responses + .iter() + .map(|r| r.rtt) + .min() + .unwrap_or(Duration::from_millis(0)); + Some(min) + } else { + None + }; + + let avg_opt = if received_count > 0 { + let total = responses + .iter() + .map(|r| r.rtt) + .fold(Duration::from_millis(0), |acc, rtt| acc + rtt); + Some(total / received_count as u32) + } else { + None + }; + + let max_opt = if received_count > 0 { + let max = responses + .iter() + .map(|r| r.rtt) + .max() + .unwrap_or(Duration::from_millis(0)); + Some(max) + } else { + None + }; + + let ping_stat: PingStat = PingStat { + responses: responses.clone(), + probe_time: elapsed_time, + transmitted_count: setting.count as usize, + received_count: received_count, + min: min_opt, + avg: avg_opt, + max: max_opt, + }; + + result.probe_status = ProbeStatus::new(); + result.elapsed_time = elapsed_time; + result.ip_addr = setting.dst_ip; + result.hostname = setting.dst_hostname.clone(); + result.port_number = setting.dst_port; + result.protocol = Protocol::Icmp; + result.stat = ping_stat; + + Ok(result) +} diff --git a/src/ping/probe/mod.rs b/src/ping/probe/mod.rs new file mode 100644 index 0000000..decb943 --- /dev/null +++ b/src/ping/probe/mod.rs @@ -0,0 +1,3 @@ +pub mod icmp; +pub mod tcp; +pub mod udp; diff --git a/src/ping/probe/tcp.rs b/src/ping/probe/tcp.rs new file mode 100644 index 0000000..903b6c9 --- /dev/null +++ b/src/ping/probe/tcp.rs @@ -0,0 +1,276 @@ +use std::collections::HashSet; +use std::net::IpAddr; +use std::time::{Duration, Instant}; +use futures::stream::StreamExt; +use futures::future::poll_fn; +use netdev::MacAddr; +use nex::packet::frame::{Frame, ParseOption}; +use nex::packet::tcp::TcpFlags; +use crate::endpoint::{NodeType, PortState}; +use crate::ping::result::PingStat; +use crate::probe::{ProbeStatus, ProbeStatusKind}; +use crate::{ping::{result::PingResult, setting::PingSetting}, probe::ProbeResult, protocol::Protocol}; +use anyhow::Result; +use nex::datalink::async_io::{async_channel, AsyncChannel}; +use tracing_indicatif::span_ext::IndicatifSpanExt; + +/// Run TCP Ping and return the results. +pub async fn run_tcp_ping(setting: &PingSetting) -> Result { + let mut result = PingResult::new(); + result.protocol = Protocol::Tcp; + let dst_port: u16 = setting.dst_port.unwrap_or(80); + + let interface = match crate::interface::get_interface_by_index(setting.if_index) { + Some(interface) => interface, + None => return Err(anyhow::anyhow!("Interface not found")), + }; + let src_ip_set: HashSet = interface.ip_addrs().iter().map(|ip| ip.clone()).collect(); + // Create sender + let config = nex::datalink::Config { + write_buffer_size: 4096, + read_buffer_size: 4096, + read_timeout: Some(setting.receive_timeout), + write_timeout: None, + channel_type: nex::datalink::ChannelType::Layer2, + bpf_fd_attempts: 1000, + linux_fanout: None, + promiscuous: false, + }; + + let AsyncChannel::Ethernet(mut tx, mut rx) = async_channel(&interface, config)? + else { + unreachable!(); + }; + + let mut responses: Vec = Vec::new(); + + let mut parse_option: ParseOption = ParseOption::default(); + if interface.is_tun() || (cfg!(any(target_os = "macos", target_os = "ios")) && interface.is_loopback()) { + let payload_offset = if interface.is_loopback() { 14 } else { 0 }; + parse_option.from_ip_packet = true; + parse_option.offset = payload_offset; + } + + let header_span = tracing::info_span!("ping"); + header_span.pb_set_style(&crate::output::progress::get_progress_style()); + header_span.pb_set_message(&format!("ping ({})", setting.dst_ip)); + header_span.pb_set_length(setting.count as u64); + header_span.pb_set_position(0); + header_span.pb_start(); + + let start_time = Instant::now(); + let tcp_packet = crate::packet::tcp::build_tcp_syn_packet(&interface, setting.dst_ip, dst_port, false); + for seq in 1..setting.count + 1 { + let send_time = Instant::now(); + match poll_fn(|cx| tx.poll_send(cx, &tcp_packet)).await { + Ok(_) => { + }, + Err(e) => eprintln!("Failed to send packet: {}", e), + } + loop { + match tokio::time::timeout(setting.receive_timeout, rx.next()).await { + Ok(Some(Ok(packet))) => { + let rtt = send_time.elapsed(); + let frame = match Frame::from_buf(&packet, parse_option.clone()) { + Some(frame) => frame, + None => { + eprintln!("Failed to parse packet: {:?}", packet); + continue; + } + }; + let mut mac_addr: MacAddr = MacAddr::zero(); + if let Some(datalink_layer) = &frame.datalink { + // Ethernet + if let Some(ethernet_header) = &datalink_layer.ethernet { + mac_addr = ethernet_header.source; + } + } + if let Some(ip_layer) = &frame.ip { + // IPv4 + if let Some(ipv4_header) = &ip_layer.ipv4 { + if IpAddr::V4(ipv4_header.source) == setting.dst_ip + && src_ip_set.contains(&IpAddr::V4(ipv4_header.destination)) + { + if let Some(transport) = &frame.transport { + if let Some(tcp) = &transport.tcp { + let port_state: PortState = if (tcp.flags & (TcpFlags::SYN | TcpFlags::ACK)) == (TcpFlags::SYN | TcpFlags::ACK) { + PortState::Open + } else if (tcp.flags & TcpFlags::RST) != 0 { + PortState::Closed + } else { + PortState::Filtered + }; + let probe_result: ProbeResult = ProbeResult { + seq: seq, + mac_addr: mac_addr, + ip_addr: setting.dst_ip, + host_name: setting.dst_hostname.clone(), + port_number: Some(dst_port), + port_status: Some(port_state), + ttl: ipv4_header.ttl, + hop: crate::util::ip::initial_ttl(ipv4_header.ttl) + - ipv4_header.ttl, + rtt: rtt, + probe_status: ProbeStatus::new(), + protocol: Protocol::Tcp, + node_type: NodeType::Destination, + sent_packet_size: tcp_packet.len(), + received_packet_size: packet.len(), + }; + tracing::info!("Reply from {}:{}, State={} bytes={} RTT={:?} TTL={}", setting.dst_ip, dst_port, port_state.as_str(), packet.len(), rtt, ipv4_header.ttl); + responses.push(probe_result); + header_span.pb_inc(1); + break; + } + } + } + } + // IPv6 + if let Some(ipv6_header) = &ip_layer.ipv6 { + if IpAddr::V6(ipv6_header.source) == setting.dst_ip + && src_ip_set.contains(&IpAddr::V6(ipv6_header.destination)) + { + if let Some(transport) = &frame.transport { + if let Some(tcp) = &transport.tcp { + let port_state: PortState = if (tcp.flags & (TcpFlags::SYN | TcpFlags::ACK)) == (TcpFlags::SYN | TcpFlags::ACK) { + PortState::Open + } else if (tcp.flags & TcpFlags::RST) != 0 { + PortState::Closed + } else { + PortState::Filtered + }; + let probe_result: ProbeResult = ProbeResult { + seq: seq, + mac_addr: mac_addr, + ip_addr: setting.dst_ip, + host_name: setting.dst_hostname.clone(), + port_number: Some(dst_port), + port_status: Some(port_state), + ttl: ipv6_header.hop_limit, + hop: crate::util::ip::initial_ttl(ipv6_header.hop_limit) + - ipv6_header.hop_limit, + rtt: rtt, + probe_status: ProbeStatus::new(), + protocol: Protocol::Tcp, + node_type: NodeType::Destination, + sent_packet_size: tcp_packet.len(), + received_packet_size: packet.len(), + }; + tracing::info!("Reply from {}:{}, State={} bytes={} RTT={:?} TTL={}", setting.dst_ip, dst_port, port_state.as_str(), packet.len(), rtt, ipv6_header.hop_limit); + responses.push(probe_result); + header_span.pb_inc(1); + break; + } + } + } + } + } + }, + Ok(Some(Err(e))) => { + tracing::error!("Failed to receive packet: {}", e); + header_span.pb_inc(1); + break; + }, + Ok(None) => { + tracing::error!("Channel closed"); + header_span.pb_inc(1); + break; + }, + Err(_) => { + tracing::error!("Request timeout for seq {}", seq); + let probe_result = ProbeResult::timeout( + seq, + setting.dst_ip, + setting.dst_hostname.clone(), + Protocol::Tcp, + tcp_packet.len(), + ); + responses.push(probe_result); + + header_span.pb_inc(1); + break; + } + } + + let elapsed_time: Duration = send_time.elapsed(); + if elapsed_time > setting.receive_timeout { + tracing::error!("Request timeout for seq {}", seq); + let probe_result = ProbeResult::timeout( + seq, + setting.dst_ip, + setting.dst_hostname.clone(), + Protocol::Tcp, + tcp_packet.len(), + ); + responses.push(probe_result); + + header_span.pb_inc(1); + break; + } + } + if !setting.send_rate.is_zero() { + tokio::time::sleep(setting.send_rate).await; + } + } + + // Finish header span + drop(header_span); + + let elapsed_time = start_time.elapsed(); + let received_count: usize = responses + .iter() + .filter(|r| r.probe_status.kind == ProbeStatusKind::Done) + .count(); + + let min_opt = if received_count > 0 { + let min = responses + .iter() + .map(|r| r.rtt) + .min() + .unwrap_or(Duration::from_millis(0)); + Some(min) + } else { + None + }; + + let avg_opt = if received_count > 0 { + let total = responses + .iter() + .map(|r| r.rtt) + .fold(Duration::from_millis(0), |acc, rtt| acc + rtt); + Some(total / received_count as u32) + } else { + None + }; + + let max_opt = if received_count > 0 { + let max = responses + .iter() + .map(|r| r.rtt) + .max() + .unwrap_or(Duration::from_millis(0)); + Some(max) + } else { + None + }; + + let ping_stat: PingStat = PingStat { + responses: responses.clone(), + probe_time: elapsed_time, + transmitted_count: setting.count as usize, + received_count: received_count, + min: min_opt, + avg: avg_opt, + max: max_opt, + }; + + result.probe_status = ProbeStatus::new(); + result.elapsed_time = elapsed_time; + result.ip_addr = setting.dst_ip; + result.hostname = setting.dst_hostname.clone(); + result.port_number = setting.dst_port; + result.protocol = Protocol::Tcp; + result.stat = ping_stat; + + Ok(result) +} diff --git a/src/ping/probe/udp.rs b/src/ping/probe/udp.rs new file mode 100644 index 0000000..dec7c5c --- /dev/null +++ b/src/ping/probe/udp.rs @@ -0,0 +1,265 @@ +use std::collections::HashSet; +use std::net::IpAddr; +use std::time::{Duration, Instant}; +use futures::stream::StreamExt; +use futures::future::poll_fn; +use netdev::MacAddr; +use nex::packet::frame::{Frame, ParseOption}; +use nex::packet::icmp::IcmpType; +use nex::packet::icmpv6::Icmpv6Type; +use crate::config::default::DEFAULT_BASE_TARGET_UDP_PORT; +use crate::endpoint::NodeType; +use crate::ping::result::PingStat; +use crate::probe::{ProbeStatus, ProbeStatusKind}; +use crate::{ping::{result::PingResult, setting::PingSetting}, probe::ProbeResult, protocol::Protocol}; +use anyhow::Result; +use nex::datalink::async_io::{async_channel, AsyncChannel}; +use tracing_indicatif::span_ext::IndicatifSpanExt; + +/// Run UDP Ping and return the results. +pub async fn run_udp_ping(setting: &PingSetting) -> Result { + let mut result = PingResult::new(); + result.protocol = Protocol::Udp; + + let interface = match crate::interface::get_interface_by_index(setting.if_index) { + Some(interface) => interface, + None => return Err(anyhow::anyhow!("Interface not found")), + }; + let src_ip_set: HashSet = interface.ip_addrs().iter().map(|ip| ip.clone()).collect(); + // Create sender + let config = nex::datalink::Config { + write_buffer_size: 4096, + read_buffer_size: 4096, + read_timeout: Some(setting.receive_timeout), + write_timeout: None, + channel_type: nex::datalink::ChannelType::Layer2, + bpf_fd_attempts: 1000, + linux_fanout: None, + promiscuous: false, + }; + + let AsyncChannel::Ethernet(mut tx, mut rx) = async_channel(&interface, config)? + else { + unreachable!(); + }; + + let mut responses: Vec = Vec::new(); + + let mut parse_option: ParseOption = ParseOption::default(); + if interface.is_tun() || (cfg!(any(target_os = "macos", target_os = "ios")) && interface.is_loopback()) { + let payload_offset = if interface.is_loopback() { 14 } else { 0 }; + parse_option.from_ip_packet = true; + parse_option.offset = payload_offset; + } + + let header_span = tracing::info_span!("ping"); + header_span.pb_set_style(&crate::output::progress::get_progress_style()); + header_span.pb_set_message(&format!("ping ({})", setting.dst_ip)); + header_span.pb_set_length(setting.count as u64); + header_span.pb_set_position(0); + header_span.pb_start(); + + let start_time = Instant::now(); + let udp_packet = crate::packet::udp::build_udp_packet(&interface, setting.dst_ip, DEFAULT_BASE_TARGET_UDP_PORT, false); + for seq in 1..setting.count + 1 { + let send_time = Instant::now(); + match poll_fn(|cx| tx.poll_send(cx, &udp_packet)).await { + Ok(_) => { + }, + Err(e) => eprintln!("Failed to send packet: {}", e), + } + loop { + match tokio::time::timeout(setting.receive_timeout, rx.next()).await { + Ok(Some(Ok(packet))) => { + let rtt = send_time.elapsed(); + let frame = match Frame::from_buf(&packet, parse_option.clone()) { + Some(frame) => frame, + None => { + eprintln!("Failed to parse packet: {:?}", packet); + continue; + } + }; + let mut mac_addr: MacAddr = MacAddr::zero(); + if let Some(datalink_layer) = &frame.datalink { + // Ethernet + if let Some(ethernet_header) = &datalink_layer.ethernet { + mac_addr = ethernet_header.source; + } + } + if let Some(ip_layer) = &frame.ip { + // IPv4 + if let Some(ipv4_header) = &ip_layer.ipv4 { + if IpAddr::V4(ipv4_header.source) == setting.dst_ip + && src_ip_set.contains(&IpAddr::V4(ipv4_header.destination)) + { + // IPv4 ICMP + if let Some(icmp_header) = &ip_layer.icmp { + if icmp_header.icmp_type == IcmpType::DestinationUnreachable { + let probe_result: ProbeResult = ProbeResult { + seq: seq, + mac_addr: mac_addr, + ip_addr: setting.dst_ip, + host_name: setting.dst_hostname.clone(), + port_number: None, + port_status: None, + ttl: ipv4_header.ttl, + hop: crate::util::ip::initial_ttl(ipv4_header.ttl) + - ipv4_header.ttl, + rtt: rtt, + probe_status: ProbeStatus::new(), + protocol: Protocol::Udp, + node_type: NodeType::Destination, + sent_packet_size: udp_packet.len(), + received_packet_size: packet.len(), + }; + tracing::info!("Reply from {}, bytes={} RTT={:?} TTL={}", setting.dst_ip, packet.len(), rtt, ipv4_header.ttl); + responses.push(probe_result); + header_span.pb_inc(1); + break; + } + } + } + } + // IPv6 + if let Some(ipv6_header) = &ip_layer.ipv6 { + if IpAddr::V6(ipv6_header.source) == setting.dst_ip + && src_ip_set.contains(&IpAddr::V6(ipv6_header.destination)) + { + // ICMPv6 + if let Some(icmpv6_header) = &ip_layer.icmpv6 { + if icmpv6_header.icmpv6_type == Icmpv6Type::DestinationUnreachable { + let probe_result: ProbeResult = ProbeResult { + seq: seq, + mac_addr: mac_addr, + ip_addr: setting.dst_ip, + host_name: setting.dst_hostname.clone(), + port_number: None, + port_status: None, + ttl: ipv6_header.hop_limit, + hop: crate::util::ip::initial_ttl(ipv6_header.hop_limit) + - ipv6_header.hop_limit, + rtt: rtt, + probe_status: ProbeStatus::new(), + protocol: Protocol::Udp, + node_type: NodeType::Destination, + sent_packet_size: udp_packet.len(), + received_packet_size: packet.len(), + }; + tracing::info!("Reply from {}, bytes={} RTT={:?} TTL={}", setting.dst_ip, packet.len(), rtt, ipv6_header.hop_limit); + responses.push(probe_result); + header_span.pb_inc(1); + break; + } + } + } + } + } + }, + Ok(Some(Err(e))) => { + tracing::error!("Failed to receive packet: {}", e); + header_span.pb_inc(1); + break; + }, + Ok(None) => { + tracing::error!("Channel closed"); + header_span.pb_inc(1); + break; + }, + Err(_) => { + tracing::error!("Request timeout for seq {}", seq); + let probe_result = ProbeResult::timeout( + seq, + setting.dst_ip, + setting.dst_hostname.clone(), + Protocol::Udp, + udp_packet.len(), + ); + responses.push(probe_result); + + header_span.pb_inc(1); + break; + } + } + + let elapsed_time: Duration = send_time.elapsed(); + if elapsed_time > setting.receive_timeout { + tracing::error!("Request timeout for seq {}", seq); + let probe_result = ProbeResult::timeout( + seq, + setting.dst_ip, + setting.dst_hostname.clone(), + Protocol::Udp, + udp_packet.len(), + ); + responses.push(probe_result); + + header_span.pb_inc(1); + break; + } + } + if !setting.send_rate.is_zero() { + tokio::time::sleep(setting.send_rate).await; + } + } + + // Finish header span + drop(header_span); + + let elapsed_time = start_time.elapsed(); + let received_count: usize = responses + .iter() + .filter(|r| r.probe_status.kind == ProbeStatusKind::Done) + .count(); + + let min_opt = if received_count > 0 { + let min = responses + .iter() + .map(|r| r.rtt) + .min() + .unwrap_or(Duration::from_millis(0)); + Some(min) + } else { + None + }; + + let avg_opt = if received_count > 0 { + let total = responses + .iter() + .map(|r| r.rtt) + .fold(Duration::from_millis(0), |acc, rtt| acc + rtt); + Some(total / received_count as u32) + } else { + None + }; + + let max_opt = if received_count > 0 { + let max = responses + .iter() + .map(|r| r.rtt) + .max() + .unwrap_or(Duration::from_millis(0)); + Some(max) + } else { + None + }; + + let ping_stat: PingStat = PingStat { + responses: responses.clone(), + probe_time: elapsed_time, + transmitted_count: setting.count as usize, + received_count: received_count, + min: min_opt, + avg: avg_opt, + max: max_opt, + }; + + result.probe_status = ProbeStatus::new(); + result.elapsed_time = elapsed_time; + result.ip_addr = setting.dst_ip; + result.hostname = setting.dst_hostname.clone(); + result.port_number = setting.dst_port; + result.protocol = Protocol::Udp; + result.stat = ping_stat; + + Ok(result) +} diff --git a/src/ping/result.rs b/src/ping/result.rs index 60b511a..563154d 100644 --- a/src/ping/result.rs +++ b/src/ping/result.rs @@ -1,8 +1,8 @@ -use crate::probe::{ProbeResult, ProbeStatus}; -use crate::protocol::Protocol; +use crate::{probe::{ProbeResult, ProbeStatus, ProbeStatusKind}, protocol::Protocol}; use serde::{Deserialize, Serialize}; -use std::time::Duration; +use std::{net::{IpAddr, Ipv4Addr}, time::Duration}; +/// Statistics of ping results #[derive(Serialize, Deserialize, Clone, Debug)] pub struct PingStat { /// Ping responses @@ -14,11 +14,11 @@ pub struct PingStat { /// Received packets pub received_count: usize, /// Minimum RTT - pub min: Duration, + pub min: Option, /// Avarage RTT - pub avg: Duration, + pub avg: Option, /// Maximum RTT - pub max: Duration, + pub max: Option, } impl PingStat { @@ -28,23 +28,22 @@ impl PingStat { probe_time: Duration::from_millis(0), transmitted_count: 0, received_count: 0, - min: Duration::from_millis(0), - avg: Duration::from_millis(0), - max: Duration::from_millis(0), + min: None, + avg: None, + max: None, } } } +/// Result of a ping operation #[derive(Serialize, Deserialize, Clone, Debug)] pub struct PingResult { pub stat: PingStat, pub probe_status: ProbeStatus, - /// start-time in RFC 3339 and ISO 8601 date and time string - pub start_time: String, - /// end-time in RFC 3339 and ISO 8601 date and time string - pub end_time: String, - /// Elapsed time pub elapsed_time: Duration, + pub ip_addr: IpAddr, + pub hostname: Option, + pub port_number: Option, pub protocol: Protocol, } @@ -53,40 +52,20 @@ impl PingResult { PingResult { stat: PingStat::new(), probe_status: ProbeStatus::new(), - start_time: String::new(), - end_time: String::new(), elapsed_time: Duration::from_millis(0), - protocol: Protocol::ICMP, + ip_addr: IpAddr::V4(Ipv4Addr::UNSPECIFIED), + hostname: None, + port_number: None, + protocol: Protocol::Icmp, } } -} - -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct TracerouteResult { - pub nodes: Vec, - pub probe_status: ProbeStatus, - /// start-time in RFC 3339 and ISO 8601 date and time string - pub start_time: String, - /// end-time in RFC 3339 and ISO 8601 date and time string - pub end_time: String, - /// Elapsed time - pub elapsed_time: Duration, - pub protocol: Protocol, -} - -impl TracerouteResult { - pub fn new() -> TracerouteResult { - TracerouteResult { - nodes: Vec::new(), - probe_status: ProbeStatus::new(), - start_time: String::new(), - end_time: String::new(), - elapsed_time: Duration::from_millis(0), - protocol: Protocol::UDP, - } + /// Return first successful response + pub fn first_response(&self) -> Option<&ProbeResult> { + self.stat.responses.iter().find(|r| r.probe_status.kind == ProbeStatusKind::Done) } } +/// Result of device resolution (ARP/NDP) #[derive(Serialize, Deserialize, Clone, Debug)] pub struct DeviceResolveResult { pub results: Vec, @@ -95,7 +74,6 @@ pub struct DeviceResolveResult { pub start_time: String, /// end-time in RFC 3339 and ISO 8601 date and time string pub end_time: String, - /// Elapsed time pub elapsed_time: Duration, pub protocol: Protocol, } @@ -108,7 +86,7 @@ impl DeviceResolveResult { start_time: String::new(), end_time: String::new(), elapsed_time: Duration::from_millis(0), - protocol: Protocol::ARP, + protocol: Protocol::Arp, } } } diff --git a/src/ping/setting.rs b/src/ping/setting.rs index 0ca1733..25b0e70 100644 --- a/src/ping/setting.rs +++ b/src/ping/setting.rs @@ -1,16 +1,17 @@ use std::net::Ipv4Addr; use std::{net::IpAddr, time::Duration}; - +use anyhow::Result; use netdev::Interface; use serde::{Deserialize, Serialize}; - -use crate::config::{DEFAULT_HOP_LIMIT, DEFAULT_PING_COUNT}; +use crate::config::default::{DEFAULT_BASE_TARGET_UDP_PORT, DEFAULT_HOP_LIMIT, DEFAULT_PING_COUNT}; +use crate::endpoint::Host; use crate::protocol::Protocol; +/// Settings for a ping operation #[derive(Deserialize, Serialize, Clone, Debug)] pub struct PingSetting { pub if_index: u32, - pub dst_hostname: String, + pub dst_hostname: Option, pub dst_ip: IpAddr, pub dst_port: Option, pub hop_limit: u8, @@ -27,11 +28,11 @@ impl Default for PingSetting { fn default() -> Self { Self { if_index: 0, - dst_hostname: "localhost".to_string(), + dst_hostname: None, dst_ip: IpAddr::V4(Ipv4Addr::LOCALHOST), dst_port: None, hop_limit: DEFAULT_HOP_LIMIT, - protocol: Protocol::ICMP, + protocol: Protocol::Icmp, count: DEFAULT_PING_COUNT, receive_timeout: Duration::from_secs(1), probe_timeout: Duration::from_secs(30), @@ -43,22 +44,23 @@ impl Default for PingSetting { } impl PingSetting { + /// Create a new ICMP ping setting pub fn icmp_ping( interface: &Interface, - dst_ip_addr: IpAddr, + dst_host: Host, count: u32, - ) -> Result { + ) -> Result { let use_tun = interface.is_tun(); let loopback = interface.is_loopback(); let setting = PingSetting { if_index: interface.index, - dst_ip: dst_ip_addr, - dst_hostname: dst_ip_addr.to_string(), + dst_ip: dst_host.ip, + dst_hostname: dst_host.hostname, dst_port: None, hop_limit: 64, count: count, - protocol: Protocol::ICMP, + protocol: Protocol::Icmp, receive_timeout: Duration::from_secs(1), probe_timeout: Duration::from_secs(30), send_rate: Duration::from_secs(1), @@ -67,23 +69,24 @@ impl PingSetting { }; Ok(setting) } + /// Create a new TCP ping setting pub fn tcp_ping( interface: &Interface, - dst_ip_addr: IpAddr, + dst_host: Host, dst_port: u16, count: u32, - ) -> Result { + ) -> Result { let use_tun = interface.is_tun(); let loopback = interface.is_loopback(); let setting = PingSetting { if_index: interface.index, - dst_ip: dst_ip_addr, - dst_hostname: dst_ip_addr.to_string(), + dst_ip: dst_host.ip, + dst_hostname: dst_host.hostname, dst_port: Some(dst_port), hop_limit: 64, count: count, - protocol: Protocol::TCP, + protocol: Protocol::Tcp, receive_timeout: Duration::from_secs(1), probe_timeout: Duration::from_secs(30), send_rate: Duration::from_secs(1), @@ -92,22 +95,23 @@ impl PingSetting { }; Ok(setting) } + /// Create a new UDP ping setting pub fn udp_ping( interface: &Interface, - dst_ip_addr: IpAddr, + dst_host: Host, count: u32, - ) -> Result { + ) -> Result { let use_tun = interface.is_tun(); let loopback = interface.is_loopback(); let setting: PingSetting = PingSetting { if_index: interface.index, - dst_ip: dst_ip_addr, - dst_hostname: dst_ip_addr.to_string(), - dst_port: Some(crate::config::DEFAULT_BASE_TARGET_UDP_PORT), + dst_ip: dst_host.ip, + dst_hostname: dst_host.hostname, + dst_port: Some(DEFAULT_BASE_TARGET_UDP_PORT), hop_limit: 64, count: count, - protocol: Protocol::UDP, + protocol: Protocol::Udp, receive_timeout: Duration::from_secs(1), probe_timeout: Duration::from_secs(30), send_rate: Duration::from_secs(1), diff --git a/src/probe.rs b/src/probe.rs index c2df529..5a9d377 100644 --- a/src/probe.rs +++ b/src/probe.rs @@ -1,10 +1,37 @@ -use crate::host::{NodeType, PortStatus}; +use crate::endpoint::{Endpoint, NodeType, PortState}; use crate::protocol::Protocol; use nex::net::mac::MacAddr; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use std::net::IpAddr; use std::time::Duration; +/// Settings for probe +#[derive(Debug, Clone)] +pub struct ProbeSetting { + pub target_endpoints: Vec, + pub if_index: u32, + pub host_concurrency: usize, + pub port_concurrency: usize, + pub task_timeout: Duration, + pub connect_timeout: Duration, + pub wait_time: Duration, + pub send_rate: Duration, +} + +impl ProbeSetting { + /// Get a map of IP addresses to hostnames for DNS resolution + pub fn get_dns_map(&self) -> HashMap { + let mut map = HashMap::new(); + for ep in &self.target_endpoints { + if let Some(hostname) = &ep.hostname { + map.insert(ep.ip, hostname.clone()); + } + } + map + } +} + /// Status of probe #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub enum ProbeStatusKind { @@ -17,6 +44,7 @@ pub enum ProbeStatusKind { } impl ProbeStatusKind { + /// Get the name of the status pub fn name(&self) -> String { match *self { ProbeStatusKind::Done => String::from("Done"), @@ -26,6 +54,7 @@ impl ProbeStatusKind { } } +/// Status of probe #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ProbeStatus { pub kind: ProbeStatusKind, @@ -33,18 +62,21 @@ pub struct ProbeStatus { } impl ProbeStatus { + /// Create a new ProbeStatus with Done kind pub fn new() -> ProbeStatus { ProbeStatus { kind: ProbeStatusKind::Done, message: String::new(), } } + /// Create a new ProbeStatus with Error kind and message pub fn with_error_message(message: String) -> ProbeStatus { ProbeStatus { kind: ProbeStatusKind::Error, message: message, } } + /// Create a new ProbeStatus with Timeout kind and message pub fn with_timeout_message(message: String) -> ProbeStatus { ProbeStatus { kind: ProbeStatusKind::Timeout, @@ -53,6 +85,7 @@ impl ProbeStatus { } } +/// Result of probe #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ProbeResult { /// Sequence number @@ -62,11 +95,11 @@ pub struct ProbeResult { /// IP address pub ip_addr: IpAddr, /// Host name - pub host_name: String, + pub host_name: Option, /// Port pub port_number: Option, /// Port Status - pub port_status: Option, + pub port_status: Option, /// Time To Live pub ttl: u8, /// Number of hops @@ -86,28 +119,30 @@ pub struct ProbeResult { } impl ProbeResult { + /// Create a new ProbeResult with default values pub fn new() -> ProbeResult { ProbeResult { seq: 0, mac_addr: MacAddr::zero(), ip_addr: IpAddr::V4(std::net::Ipv4Addr::LOCALHOST), - host_name: String::new(), + host_name: None, port_number: None, port_status: None, ttl: 0, hop: 0, rtt: Duration::from_millis(0), probe_status: ProbeStatus::new(), - protocol: Protocol::ICMP, + protocol: Protocol::Icmp, node_type: NodeType::Destination, sent_packet_size: 0, received_packet_size: 0, } } + /// Create a new ProbeResult with timeout status pub fn timeout( seq: u32, ip_addr: IpAddr, - host_name: String, + host_name: Option, protocol: Protocol, sent_packet_size: usize, ) -> ProbeResult { @@ -131,6 +166,7 @@ impl ProbeResult { received_packet_size: 0, } } + /// Create a new ProbeResult for trace timeout pub fn trace_timeout( seq: u32, protocol: Protocol, @@ -141,7 +177,7 @@ impl ProbeResult { seq: seq, mac_addr: MacAddr::zero(), ip_addr: IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED), - host_name: String::new(), + host_name: None, port_number: None, port_status: None, ttl: 0, diff --git a/src/protocol.rs b/src/protocol.rs new file mode 100644 index 0000000..44fb3c5 --- /dev/null +++ b/src/protocol.rs @@ -0,0 +1,27 @@ +use serde::{Deserialize, Serialize}; +use clap::ValueEnum; + +/// Supported protocols for probing +#[derive(Deserialize, Serialize, Copy, Clone, Debug, ValueEnum, Eq, PartialEq)] +pub enum Protocol { + Icmp, + Udp, + Tcp, + Quic, + Arp, + Ndp +} + +impl Protocol { + /// Get the protocol as a string + pub fn as_str(&self) -> &str { + match self { + Protocol::Icmp => "icmp", + Protocol::Udp => "udp", + Protocol::Tcp => "tcp", + Protocol::Quic => "quic", + Protocol::Arp => "arp", + Protocol::Ndp => "ndp", + } + } +} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs deleted file mode 100644 index 96c4af9..0000000 --- a/src/protocol/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)] -pub enum Protocol { - ARP, - NDP, - ICMP, - TCP, - UDP, -} - -impl Protocol { - pub fn from_str(s: &str) -> Option { - match s.to_lowercase().as_str() { - "arp" => Some(Protocol::ARP), - "ndp" => Some(Protocol::NDP), - "icmp" => Some(Protocol::ICMP), - "tcp" => Some(Protocol::TCP), - "udp" => Some(Protocol::UDP), - _ => None, - } - } - pub fn to_str(&self) -> &str { - match self { - Protocol::ARP => "ARP", - Protocol::NDP => "NDP", - Protocol::ICMP => "ICMP", - Protocol::TCP => "TCP", - Protocol::UDP => "UDP", - } - } -} diff --git a/src/scan/async_io.rs b/src/scan/async_io.rs deleted file mode 100644 index 84ac754..0000000 --- a/src/scan/async_io.rs +++ /dev/null @@ -1,490 +0,0 @@ -use futures::stream::{self, StreamExt}; -use netdev::Interface; -use nex::socket::{AsyncSocket, IpVersion, SocketOption, SocketType}; -use std::net::{IpAddr, SocketAddr}; -use std::sync::mpsc::{self, Sender}; -use std::sync::{Arc, Mutex}; -use std::time::Duration; - -use crate::host::{Host, Port, PortStatus}; - -use super::packet::{build_hostscan_ip_next_packet, build_portscan_ip_next_packet}; -use super::result::ScanResult; -use super::setting::{HostScanSetting, PortScanSetting}; - -use crate::config::PCAP_WAIT_TIME_MILLIS; -use crate::packet::frame::PacketFrame; -use crate::pcap::PacketCaptureOptions; -use nex::packet::ip::IpNextLevelProtocol; -use std::collections::HashSet; -use std::thread; - -use super::result::{parse_hostscan_result, parse_portscan_result, ScanStatus}; -use super::setting::{HostScanType, PortScanType}; - -pub(crate) async fn send_portscan_packets( - interface: &Interface, - socket: &AsyncSocket, - scan_setting: &PortScanSetting, - ptx: &Arc>>, -) { - let fut_host = stream::iter(scan_setting.targets.clone()).for_each_concurrent( - scan_setting.concurrency, - |dst| async move { - let fut_port = stream::iter(dst.get_ports()).for_each_concurrent( - scan_setting.concurrency, - |port| { - let target = dst.clone(); - let dst_socket_addr: SocketAddr = SocketAddr::new(target.ip_addr, port); - async move { - let packet_bytes: Vec = - build_portscan_ip_next_packet(&interface, target.ip_addr, port); - match socket.send_to(&packet_bytes, dst_socket_addr).await { - Ok(_) => {} - Err(_) => {} - } - match ptx.lock() { - Ok(lr) => match lr.send(dst_socket_addr) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - //thread::sleep(scan_setting.send_rate); - } - }, - ); - fut_port.await; - }, - ); - fut_host.await; -} - -pub(crate) async fn send_hostscan_packets( - interface: &Interface, - scan_setting: &HostScanSetting, - ptx: &Arc>>, -) { - let fut_host = stream::iter(scan_setting.targets.clone()).for_each_concurrent( - scan_setting.concurrency, - |dst| async move { - let socket: AsyncSocket = match scan_setting.scan_type { - HostScanType::IcmpPingScan => match dst.ip_addr { - IpAddr::V4(_) => { - let socket_option = SocketOption { - ip_version: IpVersion::V4, - socket_type: SocketType::Raw, - protocol: Some(IpNextLevelProtocol::Icmp), - non_blocking: true, - }; - AsyncSocket::new(socket_option).unwrap() - } - IpAddr::V6(_) => { - let socket_option = SocketOption { - ip_version: IpVersion::V6, - socket_type: SocketType::Raw, - protocol: Some(IpNextLevelProtocol::Icmpv6), - non_blocking: true, - }; - AsyncSocket::new(socket_option).unwrap() - } - }, - HostScanType::TcpPingScan => { - let socket_option = SocketOption { - ip_version: if dst.ip_addr.is_ipv4() { - IpVersion::V4 - } else { - IpVersion::V6 - }, - socket_type: SocketType::Raw, - protocol: Some(IpNextLevelProtocol::Tcp), - non_blocking: true, - }; - AsyncSocket::new(socket_option).unwrap() - } - HostScanType::UdpPingScan => { - let socket_option = SocketOption { - ip_version: if dst.ip_addr.is_ipv4() { - IpVersion::V4 - } else { - IpVersion::V6 - }, - socket_type: SocketType::Raw, - protocol: Some(IpNextLevelProtocol::Udp), - non_blocking: true, - }; - AsyncSocket::new(socket_option).unwrap() - } - }; - let dst_socket_addr: SocketAddr = SocketAddr::new(dst.ip_addr, 0); - let packet_bytes = - build_hostscan_ip_next_packet(&interface, &dst, &scan_setting.scan_type); - match socket.send_to(&packet_bytes, dst_socket_addr).await { - Ok(_) => {} - Err(_) => {} - } - match ptx.lock() { - Ok(lr) => match lr.send(dst) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - //thread::sleep(scan_setting.send_rate); - }, - ); - fut_host.await; -} - -pub async fn try_connect_ports( - target: Host, - concurrency: usize, - timeout: Duration, - ptx: &Arc>>, -) -> Host { - let (channel_tx, channel_rx) = mpsc::channel(); - let fut = stream::iter(target.get_ports()).for_each_concurrent(concurrency, |port| { - let channel_tx = channel_tx.clone(); - async move { - let socket_addr: SocketAddr = SocketAddr::new(target.ip_addr, port); - match AsyncSocket::new_with_async_connect_timeout(&socket_addr, timeout).await { - Ok(async_socket) => { - let _ = channel_tx.send(port); - match async_socket.shutdown(std::net::Shutdown::Both).await { - Ok(_) => {} - Err(_) => {} - } - } - Err(_) => {} - } - match ptx.lock() { - Ok(lr) => match lr.send(socket_addr) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - } - }); - fut.await; - drop(channel_tx); - let mut open_ports: Vec = vec![]; - loop { - match channel_rx.recv() { - Ok(port) => { - open_ports.push(Port { - number: port, - status: PortStatus::Open, - service_name: String::new(), - service_version: String::new(), - }); - } - Err(_) => { - break; - } - } - } - Host { - ip_addr: target.ip_addr, - hostname: target.hostname, - ports: open_ports, - mac_addr: target.mac_addr, - vendor_name: target.vendor_name, - os_family: String::new(), - ttl: target.ttl, - } -} - -pub fn run_connect_scan( - scan_setting: PortScanSetting, - ptx: &Arc>>, -) -> ScanResult { - let rt = tokio::runtime::Runtime::new().unwrap(); - let result = rt.block_on(async { - let start_time = std::time::Instant::now(); - let mut tasks = vec![]; - for target in scan_setting.targets { - let ptx = ptx.clone(); - tasks.push(tokio::spawn(async move { - let host = - try_connect_ports(target, scan_setting.concurrency, scan_setting.timeout, &ptx) - .await; - host - })); - } - let mut hosts: Vec = vec![]; - for task in tasks { - match task.await { - Ok(host) => { - hosts.push(host); - } - Err(e) => { - println!("error: {}", e); - } - } - } - let mut result = ScanResult::new(); - result.hosts = hosts; - result.scan_time = start_time.elapsed(); - result.scan_status = crate::scan::result::ScanStatus::Done; - result - }); - result -} - -pub(crate) async fn scan_hosts( - scan_setting: HostScanSetting, - ptx: &Arc>>, -) -> ScanResult { - let interface = match crate::interface::get_interface_by_index(scan_setting.if_index) { - Some(interface) => interface, - None => return ScanResult::new(), - }; - // Create sender - let config = nex::datalink::Config { - write_buffer_size: 4096, - read_buffer_size: 4096, - read_timeout: Some(scan_setting.wait_time), - write_timeout: None, - channel_type: nex::datalink::ChannelType::Layer2, - bpf_fd_attempts: 1000, - linux_fanout: None, - promiscuous: false, - }; - let (mut _tx, mut rx) = match nex::datalink::channel(&interface, config) { - Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), - Ok(_) => return ScanResult::error("Unhandled channel type".to_string()), - Err(e) => return ScanResult::error(format!("Failed to create channel: {}", e)), - }; - let mut capture_options: PacketCaptureOptions = PacketCaptureOptions { - interface_index: interface.index, - interface_name: interface.name.clone(), - src_ips: HashSet::new(), - dst_ips: HashSet::new(), - src_ports: HashSet::new(), - dst_ports: HashSet::new(), - ether_types: HashSet::new(), - ip_protocols: HashSet::new(), - capture_timeout: scan_setting.timeout, - read_timeout: scan_setting.wait_time, - promiscuous: false, - receive_undefined: false, - tunnel: interface.is_tun(), - loopback: interface.is_loopback(), - }; - for target in scan_setting.targets.clone() { - capture_options.src_ips.insert(target.ip_addr); - } - match scan_setting.scan_type { - HostScanType::IcmpPingScan => { - capture_options - .ip_protocols - .insert(IpNextLevelProtocol::Icmp); - capture_options - .ip_protocols - .insert(IpNextLevelProtocol::Icmpv6); - } - HostScanType::TcpPingScan => { - capture_options - .ip_protocols - .insert(IpNextLevelProtocol::Tcp); - for target in scan_setting.targets.clone() { - for port in target.get_ports() { - capture_options.src_ports.insert(port); - } - } - } - HostScanType::UdpPingScan => { - capture_options - .ip_protocols - .insert(IpNextLevelProtocol::Udp); - capture_options - .ip_protocols - .insert(IpNextLevelProtocol::Icmp); - capture_options - .ip_protocols - .insert(IpNextLevelProtocol::Icmpv6); - } - } - let stop: Arc> = Arc::new(Mutex::new(false)); - let stop_handle = Arc::clone(&stop); - let packets: Arc>> = Arc::new(Mutex::new(vec![])); - let receive_packets: Arc>> = Arc::clone(&packets); - // Spawn pcap thread - let pcap_handler = thread::spawn(move || { - let packets: Vec = - crate::pcap::start_capture(&mut rx, capture_options, &stop_handle); - match receive_packets.lock() { - Ok(mut receive_packets) => { - for p in packets { - receive_packets.push(p); - } - } - Err(e) => { - eprintln!("Failed to lock receive_packets: {}", e); - } - } - }); - - // Wait for listener to start (need fix for better way) - thread::sleep(Duration::from_millis(PCAP_WAIT_TIME_MILLIS)); - let start_time = std::time::Instant::now(); - // Send probe packets - send_hostscan_packets(&interface, &scan_setting, ptx).await; - thread::sleep(scan_setting.wait_time); - // Stop pcap - match stop.lock() { - Ok(mut stop) => { - *stop = true; - } - Err(e) => { - eprintln!("Failed to lock stop: {}", e); - } - } - // Wait for listener to stop - match pcap_handler.join() { - Ok(_) => {} - Err(e) => { - eprintln!("Failed to join pcap_handler: {:?}", e); - } - } - - let mut scan_result: ScanResult = ScanResult::new(); - match packets.lock() { - Ok(packets) => { - scan_result = parse_hostscan_result(packets.clone(), scan_setting); - } - Err(e) => { - eprintln!("Failed to lock packets: {}", e); - } - } - scan_result.scan_time = start_time.elapsed(); - scan_result.scan_status = ScanStatus::Done; - scan_result -} - -pub(crate) async fn scan_ports( - scan_setting: PortScanSetting, - ptx: &Arc>>, -) -> ScanResult { - let interface = match crate::interface::get_interface_by_index(scan_setting.if_index) { - Some(interface) => interface, - None => return ScanResult::new(), - }; - // Create sender - let config = nex::datalink::Config { - write_buffer_size: 4096, - read_buffer_size: 4096, - read_timeout: Some(scan_setting.wait_time), - write_timeout: None, - channel_type: nex::datalink::ChannelType::Layer2, - bpf_fd_attempts: 1000, - linux_fanout: None, - promiscuous: false, - }; - let (mut _tx, mut rx) = match nex::datalink::channel(&interface, config) { - Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), - Ok(_) => return ScanResult::error("Unhandled channel type".to_string()), - Err(e) => return ScanResult::error(format!("Failed to create channel: {}", e)), - }; - let socket_option = SocketOption { - ip_version: if scan_setting.targets.len() > 0 { - if scan_setting.targets[0].ip_addr.is_ipv4() { - IpVersion::V4 - } else { - IpVersion::V6 - } - } else { - IpVersion::V4 - }, - socket_type: SocketType::Raw, - protocol: Some(IpNextLevelProtocol::Tcp), - non_blocking: true, - }; - let socket: AsyncSocket = AsyncSocket::new(socket_option).unwrap(); - let mut capture_options: PacketCaptureOptions = PacketCaptureOptions { - interface_index: interface.index, - interface_name: interface.name.clone(), - src_ips: HashSet::new(), - dst_ips: HashSet::new(), - src_ports: HashSet::new(), - dst_ports: HashSet::new(), - ether_types: HashSet::new(), - ip_protocols: HashSet::new(), - capture_timeout: scan_setting.timeout, - read_timeout: scan_setting.wait_time, - promiscuous: false, - receive_undefined: false, - tunnel: interface.is_tun(), - loopback: interface.is_loopback(), - }; - for target in scan_setting.targets.clone() { - capture_options.src_ips.insert(target.ip_addr); - capture_options.src_ports.extend(target.get_ports()); - } - match scan_setting.scan_type { - PortScanType::TcpSynScan => { - capture_options - .ip_protocols - .insert(IpNextLevelProtocol::Tcp); - } - PortScanType::TcpConnectScan => { - capture_options - .ip_protocols - .insert(IpNextLevelProtocol::Tcp); - } - } - let stop: Arc> = Arc::new(Mutex::new(false)); - let stop_handle = Arc::clone(&stop); - let packets: Arc>> = Arc::new(Mutex::new(vec![])); - let receive_packets: Arc>> = Arc::clone(&packets); - // Spawn pcap thread - let pcap_handler = thread::spawn(move || { - let packets: Vec = - crate::pcap::start_capture(&mut rx, capture_options, &stop_handle); - match receive_packets.lock() { - Ok(mut receive_packets) => { - for p in packets { - receive_packets.push(p); - } - } - Err(e) => { - eprintln!("Failed to lock receive_packets: {}", e); - } - } - }); - // Wait for listener to start (need fix for better way) - thread::sleep(Duration::from_millis(PCAP_WAIT_TIME_MILLIS)); - let start_time = std::time::Instant::now(); - // Send probe packets - send_portscan_packets(&interface, &socket, &scan_setting, ptx).await; - thread::sleep(scan_setting.wait_time); - // Stop pcap - match stop.lock() { - Ok(mut stop) => { - *stop = true; - } - Err(e) => { - eprintln!("Failed to lock stop: {}", e); - } - } - // Wait for listener to stop - match pcap_handler.join() { - Ok(_) => {} - Err(e) => { - eprintln!("Failed to join pcap_handler: {:?}", e); - } - } - let mut scan_result: ScanResult = ScanResult::new(); - match packets.lock() { - Ok(packets) => { - scan_result = parse_portscan_result(packets.clone(), scan_setting); - } - Err(e) => { - eprintln!("Failed to lock packets: {}", e); - } - } - scan_result.scan_time = start_time.elapsed(); - scan_result.scan_status = ScanStatus::Done; - scan_result -} diff --git a/src/scan/blocking.rs b/src/scan/blocking.rs deleted file mode 100644 index 1490336..0000000 --- a/src/scan/blocking.rs +++ /dev/null @@ -1,352 +0,0 @@ -use crate::config::PCAP_WAIT_TIME_MILLIS; -use crate::host::Host; -use crate::packet::frame::PacketFrame; -use crate::pcap::PacketCaptureOptions; -use crate::scan::setting::{HostScanSetting, PortScanSetting}; -use netdev::Interface; -use nex::datalink::RawSender; -use nex::packet::ip::IpNextLevelProtocol; -use std::collections::HashSet; -use std::net::SocketAddr; -use std::sync::mpsc::Sender; -use std::sync::{Arc, Mutex}; -use std::thread; -use std::time::Duration; - -use super::packet::{build_hostscan_packet, build_portscan_packet}; -use super::result::{parse_hostscan_result, parse_portscan_result, ScanResult, ScanStatus}; -use super::setting::{HostScanType, PortScanType}; - -pub(crate) fn send_hostscan_packets( - tx: &mut Box, - interface: &Interface, - targets: Vec, - ptx: &Arc>>, - scan_type: HostScanType, -) { - // Acquire message sender lock - let ptx_lock = match ptx.lock() { - Ok(ptx) => ptx, - Err(e) => { - eprintln!("Failed to lock ptx: {}", e); - return; - } - }; - for target in targets { - let packet = build_hostscan_packet(&interface, &target, &scan_type, false); - match tx.send(&packet) { - Some(_) => { - // Notify packet sent - match ptx_lock.send(target) { - Ok(_) => {} - Err(e) => { - eprintln!("Failed to send message: {}", e); - } - } - } - None => { - eprintln!("Failed to send packet"); - } - } - } - // Drop message sender lock - drop(ptx_lock); -} - -pub(crate) fn send_portscan_packets( - tx: &mut Box, - interface: &Interface, - targets: Vec, - ptx: &Arc>>, - scan_type: PortScanType, -) { - // Acquire message sender lock - let ptx_lock = match ptx.lock() { - Ok(ptx) => ptx, - Err(e) => { - eprintln!("Failed to lock ptx: {}", e); - return; - } - }; - for target in targets { - match scan_type { - PortScanType::TcpSynScan => { - for port in target.ports { - let packet = - build_portscan_packet(&interface, target.ip_addr, port.number, false); - match tx.send(&packet) { - Some(_) => { - // Notify packet sent - match ptx_lock.send(SocketAddr::new(target.ip_addr, port.number)) { - Ok(_) => {} - Err(e) => { - eprintln!("Failed to send message: {}", e); - } - } - } - None => { - eprintln!("Failed to send packet"); - } - } - } - } - PortScanType::TcpConnectScan => { - // TODO - } - } - } - // Drop message sender lock - drop(ptx_lock); -} - -pub(crate) fn scan_hosts( - scan_setting: HostScanSetting, - ptx: &Arc>>, -) -> ScanResult { - let interface = match crate::interface::get_interface_by_index(scan_setting.if_index) { - Some(interface) => interface, - None => return ScanResult::new(), - }; - // Create sender - let config = nex::datalink::Config { - write_buffer_size: 4096, - read_buffer_size: 4096, - read_timeout: Some(scan_setting.wait_time), - write_timeout: None, - channel_type: nex::datalink::ChannelType::Layer2, - bpf_fd_attempts: 1000, - linux_fanout: None, - promiscuous: false, - }; - let (mut tx, mut rx) = match nex::datalink::channel(&interface, config) { - Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), - Ok(_) => return ScanResult::error("Unhandled channel type".to_string()), - Err(e) => return ScanResult::error(format!("Failed to create channel: {}", e)), - }; - let mut capture_options: PacketCaptureOptions = PacketCaptureOptions { - interface_index: interface.index, - interface_name: interface.name.clone(), - src_ips: HashSet::new(), - dst_ips: HashSet::new(), - src_ports: HashSet::new(), - dst_ports: HashSet::new(), - ether_types: HashSet::new(), - ip_protocols: HashSet::new(), - capture_timeout: scan_setting.timeout, - read_timeout: scan_setting.wait_time, - promiscuous: false, - receive_undefined: false, - tunnel: interface.is_tun(), - loopback: interface.is_loopback(), - }; - for target in scan_setting.targets.clone() { - capture_options.src_ips.insert(target.ip_addr); - } - match scan_setting.scan_type { - HostScanType::IcmpPingScan => { - capture_options - .ip_protocols - .insert(IpNextLevelProtocol::Icmp); - capture_options - .ip_protocols - .insert(IpNextLevelProtocol::Icmpv6); - } - HostScanType::TcpPingScan => { - capture_options - .ip_protocols - .insert(IpNextLevelProtocol::Tcp); - for target in scan_setting.targets.clone() { - for port in target.ports { - capture_options.src_ports.insert(port.number); - } - } - } - HostScanType::UdpPingScan => { - capture_options - .ip_protocols - .insert(IpNextLevelProtocol::Udp); - capture_options - .ip_protocols - .insert(IpNextLevelProtocol::Icmp); - capture_options - .ip_protocols - .insert(IpNextLevelProtocol::Icmpv6); - } - } - let stop: Arc> = Arc::new(Mutex::new(false)); - let stop_handle = Arc::clone(&stop); - let packets: Arc>> = Arc::new(Mutex::new(vec![])); - let receive_packets: Arc>> = Arc::clone(&packets); - // Spawn pcap thread - let pcap_handler = thread::spawn(move || { - let packets: Vec = - crate::pcap::start_capture(&mut rx, capture_options, &stop_handle); - match receive_packets.lock() { - Ok(mut receive_packets) => { - for p in packets { - receive_packets.push(p); - } - } - Err(e) => { - eprintln!("Failed to lock receive_packets: {}", e); - } - } - }); - // Wait for listener to start (need fix for better way) - thread::sleep(Duration::from_millis(PCAP_WAIT_TIME_MILLIS)); - let start_time = std::time::Instant::now(); - // Send probe packets - send_hostscan_packets( - &mut tx, - &interface, - scan_setting.targets.clone(), - ptx, - scan_setting.scan_type.clone(), - ); - thread::sleep(scan_setting.wait_time); - // Stop pcap - match stop.lock() { - Ok(mut stop) => { - *stop = true; - } - Err(e) => { - eprintln!("Failed to lock stop: {}", e); - } - } - // Wait for listener to stop - match pcap_handler.join() { - Ok(_) => {} - Err(e) => { - eprintln!("Failed to join pcap_handler: {:?}", e); - } - } - let mut scan_result: ScanResult = ScanResult::new(); - match packets.lock() { - Ok(packets) => { - scan_result = parse_hostscan_result(packets.clone(), scan_setting); - } - Err(e) => { - eprintln!("Failed to lock packets: {}", e); - } - } - scan_result.scan_time = start_time.elapsed(); - scan_result.scan_status = ScanStatus::Done; - scan_result -} - -pub(crate) fn scan_ports( - scan_setting: PortScanSetting, - ptx: &Arc>>, -) -> ScanResult { - let interface = match crate::interface::get_interface_by_index(scan_setting.if_index) { - Some(interface) => interface, - None => return ScanResult::new(), - }; - // Create sender - let config = nex::datalink::Config { - write_buffer_size: 4096, - read_buffer_size: 4096, - read_timeout: Some(scan_setting.wait_time), - write_timeout: None, - channel_type: nex::datalink::ChannelType::Layer2, - bpf_fd_attempts: 1000, - linux_fanout: None, - promiscuous: false, - }; - let (mut tx, mut rx) = match nex::datalink::channel(&interface, config) { - Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), - Ok(_) => return ScanResult::error("Unhandled channel type".to_string()), - Err(e) => return ScanResult::error(format!("Failed to create channel: {}", e)), - }; - let mut capture_options: PacketCaptureOptions = PacketCaptureOptions { - interface_index: interface.index, - interface_name: interface.name.clone(), - src_ips: HashSet::new(), - dst_ips: HashSet::new(), - src_ports: HashSet::new(), - dst_ports: HashSet::new(), - ether_types: HashSet::new(), - ip_protocols: HashSet::new(), - capture_timeout: scan_setting.timeout, - read_timeout: scan_setting.wait_time, - promiscuous: false, - receive_undefined: false, - tunnel: interface.is_tun(), - loopback: interface.is_loopback(), - }; - for target in scan_setting.targets.clone() { - capture_options.src_ips.insert(target.ip_addr); - capture_options.src_ports.extend(target.get_ports()); - } - match scan_setting.scan_type { - PortScanType::TcpSynScan => { - capture_options - .ip_protocols - .insert(IpNextLevelProtocol::Tcp); - } - PortScanType::TcpConnectScan => { - capture_options - .ip_protocols - .insert(IpNextLevelProtocol::Tcp); - } - } - let stop: Arc> = Arc::new(Mutex::new(false)); - let stop_handle = Arc::clone(&stop); - let packets: Arc>> = Arc::new(Mutex::new(vec![])); - let receive_packets: Arc>> = Arc::clone(&packets); - // Spawn pcap thread - let pcap_handler = thread::spawn(move || { - let packets: Vec = - crate::pcap::start_capture(&mut rx, capture_options, &stop_handle); - match receive_packets.lock() { - Ok(mut receive_packets) => { - for p in packets { - receive_packets.push(p); - } - } - Err(e) => { - eprintln!("Failed to lock receive_packets: {}", e); - } - } - }); - // Wait for listener to start (need fix for better way) - thread::sleep(Duration::from_millis(PCAP_WAIT_TIME_MILLIS)); - let start_time = std::time::Instant::now(); - // Send probe packets - send_portscan_packets( - &mut tx, - &interface, - scan_setting.targets.clone(), - ptx, - scan_setting.scan_type.clone(), - ); - thread::sleep(scan_setting.wait_time); - // Stop pcap - match stop.lock() { - Ok(mut stop) => { - *stop = true; - } - Err(e) => { - eprintln!("Failed to lock stop: {}", e); - } - } - // Wait for listener to stop - match pcap_handler.join() { - Ok(_) => {} - Err(e) => { - eprintln!("Failed to join pcap_handler: {:?}", e); - } - } - let mut scan_result: ScanResult = ScanResult::new(); - match packets.lock() { - Ok(packets) => { - scan_result = parse_portscan_result(packets.clone(), scan_setting); - } - Err(e) => { - eprintln!("Failed to lock packets: {}", e); - } - } - scan_result.scan_time = start_time.elapsed(); - scan_result.scan_status = ScanStatus::Done; - scan_result -} diff --git a/src/scan/mod.rs b/src/scan/mod.rs index c722db6..ded2588 100644 --- a/src/scan/mod.rs +++ b/src/scan/mod.rs @@ -1,8 +1,52 @@ -pub mod async_io; -pub mod blocking; -pub mod packet; -pub mod payload; -pub mod result; -pub mod scanner; -pub mod service; -pub mod setting; +use anyhow::Result; + +use crate::{cli::{HostScanProto, PortScanMethod}, endpoint::TransportProtocol, output::ScanResult, probe::ProbeSetting}; + +pub mod probe; + +/// A port scanner that can perform scans using different methods and transport protocols. +pub struct PortScanner { + pub settings: ProbeSetting, + pub scan_method: PortScanMethod, + pub transport: TransportProtocol, +} + +impl PortScanner { + /// Create a new PortScanner instance. + pub fn new(settings: ProbeSetting, transport: TransportProtocol, scan_method: PortScanMethod) -> Self { + Self { + settings, + scan_method, + transport, + } + } + /// Run the port scan based on the specified transport protocol and method. + pub async fn run(&self) -> Result { + match self.transport { + TransportProtocol::Tcp => probe::tcp::run_port_scan(self.settings.clone(), self.scan_method).await, + TransportProtocol::Quic => probe::quic::run_port_scan(self.settings.clone(), self.scan_method).await, + _ => anyhow::bail!("Unsupported transport protocol: {:?}", self.transport), + } + } +} + +/// A host scanner that can perform scans using different protocols. +pub struct HostScanner { + pub settings: ProbeSetting, + pub protocol: HostScanProto, +} + +impl HostScanner { + /// Create a new HostScanner instance. + pub fn new(settings: ProbeSetting, protocol: HostScanProto) -> Self { + Self { settings, protocol } + } + /// Run the host scan based on the specified protocol. + pub async fn run(&self) -> Result { + match self.protocol { + HostScanProto::Icmp => probe::icmp::run_host_scan(self.settings.clone()).await, + HostScanProto::Udp => probe::udp::run_host_scan(self.settings.clone()).await, + HostScanProto::Tcp => probe::tcp::run_host_scan(self.settings.clone()).await, + } + } +} diff --git a/src/scan/packet.rs b/src/scan/packet.rs deleted file mode 100644 index 4a54a47..0000000 --- a/src/scan/packet.rs +++ /dev/null @@ -1,203 +0,0 @@ -use super::setting::HostScanType; -use crate::config::{DEFAULT_HOP_LIMIT, DEFAULT_LOCAL_TCP_PORT, DEFAULT_LOCAL_UDP_PORT}; -use crate::host::Host; -use crate::packet::setting::PacketBuildSetting; -use netdev::Interface; -use nex::net::ip::is_global_ipv6; -use std::net::IpAddr; - -pub(crate) fn build_hostscan_packet( - interface: &Interface, - target_host: &Host, - scan_type: &HostScanType, - ip_packet: bool, -) -> Vec { - let mut build_setting = PacketBuildSetting::new(); - if let Some(mac_addr) = &interface.mac_addr { - build_setting.src_mac = *mac_addr; - } - if let Some(gateway) = &interface.gateway { - build_setting.dst_mac = gateway.mac_addr; - } - match target_host.ip_addr { - IpAddr::V4(ipv4_addr) => { - interface.ipv4.iter().for_each(|ipv4| { - build_setting.src_ip = IpAddr::V4(ipv4.addr()); - }); - build_setting.dst_ip = IpAddr::V4(ipv4_addr); - } - IpAddr::V6(ipv6_addr) => { - if is_global_ipv6(&ipv6_addr) { - interface.ipv6.iter().for_each(|ipv6| { - if is_global_ipv6(&ipv6.addr()) { - build_setting.src_ip = IpAddr::V6(ipv6.addr()); - } - }); - } else { - interface.ipv6.iter().for_each(|ipv6| { - build_setting.src_ip = IpAddr::V6(ipv6.addr()); - }); - } - build_setting.dst_ip = IpAddr::V6(ipv6_addr); - } - } - if target_host.ports.len() > 0 { - build_setting.dst_port = target_host.ports[0].number; - } - build_setting.hop_limit = DEFAULT_HOP_LIMIT; - if ip_packet || interface.is_tun() || interface.is_loopback() { - build_setting.ip_packet = true; - } - match scan_type { - HostScanType::IcmpPingScan => crate::packet::icmp::build_icmp_packet(build_setting), - HostScanType::TcpPingScan => { - build_setting.src_port = DEFAULT_LOCAL_TCP_PORT; - crate::packet::tcp::build_tcp_syn_packet(build_setting) - } - HostScanType::UdpPingScan => { - build_setting.src_port = DEFAULT_LOCAL_UDP_PORT; - crate::packet::udp::build_udp_packet(build_setting) - } - } -} - -pub(crate) fn build_hostscan_ip_next_packet( - interface: &Interface, - target_host: &Host, - scan_type: &HostScanType, -) -> Vec { - let mut build_setting = PacketBuildSetting::new(); - if let Some(mac_addr) = &interface.mac_addr { - build_setting.src_mac = *mac_addr; - } - if let Some(gateway) = &interface.gateway { - build_setting.dst_mac = gateway.mac_addr; - } - match target_host.ip_addr { - IpAddr::V4(ipv4_addr) => { - interface.ipv4.iter().for_each(|ipv4| { - build_setting.src_ip = IpAddr::V4(ipv4.addr()); - }); - build_setting.dst_ip = IpAddr::V4(ipv4_addr); - } - IpAddr::V6(ipv6_addr) => { - if is_global_ipv6(&ipv6_addr) { - interface.ipv6.iter().for_each(|ipv6| { - if is_global_ipv6(&ipv6.addr()) { - build_setting.src_ip = IpAddr::V6(ipv6.addr()); - } - }); - } else { - interface.ipv6.iter().for_each(|ipv6| { - build_setting.src_ip = IpAddr::V6(ipv6.addr()); - }); - } - build_setting.dst_ip = IpAddr::V6(ipv6_addr); - } - } - if target_host.ports.len() > 0 { - build_setting.dst_port = target_host.ports[0].number; - } - build_setting.hop_limit = DEFAULT_HOP_LIMIT; - if interface.is_tun() || interface.is_loopback() { - build_setting.ip_packet = true; - } - match scan_type { - HostScanType::IcmpPingScan => crate::packet::icmp::build_ip_next_icmp_packet(build_setting), - HostScanType::TcpPingScan => { - build_setting.src_port = DEFAULT_LOCAL_TCP_PORT; - crate::packet::tcp::build_ip_next_tcp_syn_packet(build_setting) - } - HostScanType::UdpPingScan => { - build_setting.src_port = DEFAULT_LOCAL_UDP_PORT; - crate::packet::udp::build_ip_next_udp_packet(build_setting) - } - } -} - -pub(crate) fn build_portscan_packet( - interface: &Interface, - target_ip_addr: IpAddr, - target_port: u16, - ip_packet: bool, -) -> Vec { - let mut build_setting = PacketBuildSetting::new(); - if let Some(mac_addr) = &interface.mac_addr { - build_setting.src_mac = *mac_addr; - } - if let Some(gateway) = &interface.gateway { - build_setting.dst_mac = gateway.mac_addr; - } - match target_ip_addr { - IpAddr::V4(ipv4_addr) => { - interface.ipv4.iter().for_each(|ipv4| { - build_setting.src_ip = IpAddr::V4(ipv4.addr()); - }); - build_setting.dst_ip = IpAddr::V4(ipv4_addr); - } - IpAddr::V6(ipv6_addr) => { - if is_global_ipv6(&ipv6_addr) { - interface.ipv6.iter().for_each(|ipv6| { - if is_global_ipv6(&ipv6.addr()) { - build_setting.src_ip = IpAddr::V6(ipv6.addr()); - } - }); - } else { - interface.ipv6.iter().for_each(|ipv6| { - build_setting.src_ip = IpAddr::V6(ipv6.addr()); - }); - } - build_setting.dst_ip = IpAddr::V6(ipv6_addr); - } - } - build_setting.dst_port = target_port; - build_setting.hop_limit = DEFAULT_HOP_LIMIT; - if ip_packet || interface.is_tun() || interface.is_loopback() { - build_setting.ip_packet = true; - } - build_setting.src_port = DEFAULT_LOCAL_TCP_PORT; - crate::packet::tcp::build_tcp_syn_packet(build_setting) -} - -pub(crate) fn build_portscan_ip_next_packet( - interface: &Interface, - target_ip_addr: IpAddr, - target_port: u16, -) -> Vec { - let mut build_setting = PacketBuildSetting::new(); - if let Some(mac_addr) = &interface.mac_addr { - build_setting.src_mac = *mac_addr; - } - if let Some(gateway) = &interface.gateway { - build_setting.dst_mac = gateway.mac_addr; - } - match target_ip_addr { - IpAddr::V4(ipv4_addr) => { - interface.ipv4.iter().for_each(|ipv4| { - build_setting.src_ip = IpAddr::V4(ipv4.addr()); - }); - build_setting.dst_ip = IpAddr::V4(ipv4_addr); - } - IpAddr::V6(ipv6_addr) => { - if is_global_ipv6(&ipv6_addr) { - interface.ipv6.iter().for_each(|ipv6| { - if is_global_ipv6(&ipv6.addr()) { - build_setting.src_ip = IpAddr::V6(ipv6.addr()); - } - }); - } else { - interface.ipv6.iter().for_each(|ipv6| { - build_setting.src_ip = IpAddr::V6(ipv6.addr()); - }); - } - build_setting.dst_ip = IpAddr::V6(ipv6_addr); - } - } - build_setting.dst_port = target_port; - build_setting.hop_limit = DEFAULT_HOP_LIMIT; - if interface.is_tun() || interface.is_loopback() { - build_setting.ip_packet = true; - } - build_setting.src_port = DEFAULT_LOCAL_TCP_PORT; - crate::packet::tcp::build_ip_next_tcp_syn_packet(build_setting) -} diff --git a/src/scan/payload.rs b/src/scan/payload.rs deleted file mode 100644 index 5e11a2d..0000000 --- a/src/scan/payload.rs +++ /dev/null @@ -1,198 +0,0 @@ -/// Payloads for service detection -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum PayloadType { - /// No payload. Just open TCP connection and read response. - Null, - /// HTTP request - Http, - /// HTTPS request - Https, - /// Common payload. Write payload and read response. - Common, - /// Common payload for TLS. Write payload and read response with TLS. - CommonTls, -} - -/// Payload information for service detection -#[derive(Clone, Debug, PartialEq)] -pub struct PayloadInfo { - pub payload: Vec, - pub payload_type: PayloadType, -} - -/// Payload builder for service detection -#[derive(Clone, Debug)] -pub struct PayloadBuilder { - payload_info: PayloadInfo, -} - -impl PayloadBuilder { - /// Create new PayloadBuilder - pub fn new() -> Self { - PayloadBuilder { - payload_info: PayloadInfo { - payload: vec![], - payload_type: PayloadType::Common, - }, - } - } - /// Create new PayloadBuilder for TLS - pub fn new_tls() -> Self { - PayloadBuilder { - payload_info: PayloadInfo { - payload: vec![], - payload_type: PayloadType::CommonTls, - }, - } - } - /// Add byte to payload - pub fn add_byte(&mut self, byte: u8) -> &mut Self { - self.payload_info.payload.push(byte); - self - } - /// Add bytes to payload - pub fn add_bytes(&mut self, bytes: &[u8]) -> &mut Self { - self.payload_info.payload.extend_from_slice(bytes); - self - } - /// Add bytes (from string) to payload - pub fn add_str(&mut self, s: &str) -> &mut Self { - self.payload_info.payload.extend_from_slice(s.as_bytes()); - self - } - /// Enable/Diable TLS - pub fn set_tls(&mut self, tls_enabled: bool) -> &mut Self { - if tls_enabled { - self.payload_info.payload_type = PayloadType::CommonTls; - } else { - self.payload_info.payload_type = PayloadType::Common; - } - self - } - /// Return payload as Vec - pub fn bytes(self) -> Vec { - self.payload_info.payload - } - /// Return payload as PayloadInfo - pub fn payload(self) -> PayloadInfo { - self.payload_info - } - /* pub fn null() -> PayloadInfo { - PayloadInfo { - payload: vec![0x00], - payload_type: PayloadType::Null, - } - } */ - /// Create a new PayloadInfo with a generic line - pub fn generic_line() -> PayloadInfo { - PayloadInfo { - payload: "\r\n\r\n".as_bytes().to_vec(), - payload_type: PayloadType::Common, - } - } - /// Create a new PayloadInfo with a generic line for TLS - pub fn generic_line_tls() -> PayloadInfo { - PayloadInfo { - payload: "\r\n\r\n".as_bytes().to_vec(), - payload_type: PayloadType::CommonTls, - } - } - /// Create a new PayloadInfo with a hello message - pub fn hello() -> PayloadInfo { - PayloadInfo { - payload: "EHLO\r\n".as_bytes().to_vec(), - payload_type: PayloadType::Common, - } - } - /// Create a new PayloadInfo with a hello message for TLS - pub fn hello_tls() -> PayloadInfo { - PayloadInfo { - payload: "EHLO\r\n".as_bytes().to_vec(), - payload_type: PayloadType::CommonTls, - } - } - /// Create a new PayloadInfo with a HTTP head request - pub fn http_head() -> PayloadInfo { - PayloadInfo { - payload: "HEAD / HTTP/1.0\r\n\r\n".as_bytes().to_vec(), - payload_type: PayloadType::Http, - } - } - /// Create a new PayloadInfo with a HTTPS head request - pub fn https_head(hostname: &str) -> PayloadInfo { - let req: String = format!( - "HEAD / HTTP/1.1\r\nHost: {}\r\nConnection: close\r\nAccept-Encoding: identity\r\n\r\n", - hostname - ); - PayloadInfo { - payload: req.into_bytes(), - payload_type: PayloadType::Https, - } - } - /// Create a new PayloadInfo with a HTTP get request - pub fn http_get(path: &str) -> PayloadInfo { - let req = format!("GET {} HTTP/1.1\r\nHost: example.com\r\n\r\n", path); - PayloadInfo { - payload: req.into_bytes(), - payload_type: PayloadType::Http, - } - } - /// Create a new PayloadInfo with a HTTPS get request - pub fn https_get(path: &str, hostname: &str) -> PayloadInfo { - let req = format!( - "GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\nAccept-Encoding: identity\r\n\r\n", - path, hostname - ); - PayloadInfo { - payload: req.into_bytes(), - payload_type: PayloadType::Https, - } - } - /* pub fn ftp_user(username: &str) -> PayloadInfo { - let req = format!("USER {}\r\n", username); - PayloadInfo { - payload: req.into_bytes(), - payload_type: PayloadType::Common, - } - } */ - /* pub fn smtp_ehlo() -> PayloadInfo { - PayloadInfo { - payload: "EHLO example.com\r\n".as_bytes().to_vec(), - payload_type: PayloadType::Common, - } - } */ - /* pub fn tls_1_1_session_request() -> PayloadInfo { - PayloadInfo { - payload: vec![ - 0x16, 0x03, 0x02, // Content Type: Handshake (22), Version: TLS 1.1 (0x0302) - 0x00, 0x01, 0xfc, 0x01, 0x00, 0x00, 0xf8, // Length: 1 byte, Handshake Type: Session Request (0x00) - ], - payload_type: PayloadType::CommonTls, - } - } */ - /* pub fn tls_1_2_session_request() -> PayloadInfo { - PayloadInfo { - payload: vec![ - 0x16, 0x03, 0x03, // Content Type: Handshake (22), Version: TLS 1.2 (0x0303) - 0x00, 0x01, 0xfc, 0x01, 0x00, 0x00, 0xf8, // Length: 1 byte, Handshake Type: Session Request (0x00) - ], - payload_type: PayloadType::CommonTls, - } - } */ - /* pub fn tls_1_3_session_request() -> PayloadInfo { - PayloadInfo { - payload: vec![ - 0x16, 0x03, 0x04, // Content Type: Handshake (22), Version: TLS 1.3 (0x0304) - 0x00, 0x01, 0xfc, 0x01, 0x00, 0x00, 0xf8, // Length: 1 byte, Handshake Type: Session Request (0x00) - ], - payload_type: PayloadType::CommonTls, - } - } */ - /* pub fn ssh_public_key_request(username: &str) -> PayloadInfo { - let payload = format!("{}\0ssh-connection\0\0\0\0\0\0\0\0\0\0\0\0\0\0", username); - PayloadInfo { - payload: payload.as_bytes().to_vec(), - payload_type: PayloadType::Common, - } - } */ -} diff --git a/src/scan/probe/icmp.rs b/src/scan/probe/icmp.rs new file mode 100644 index 0000000..3372e3b --- /dev/null +++ b/src/scan/probe/icmp.rs @@ -0,0 +1,207 @@ +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +use futures::future::poll_fn; +use netdev::{Interface, MacAddr}; +use nex::datalink::async_io::{async_channel, AsyncChannel, AsyncRawSender}; +use nex::packet::frame::Frame; +use nex::packet::ip::IpNextProtocol; +use tracing_indicatif::span_ext::IndicatifSpanExt; +use anyhow::Result; +use crate::{output::ScanResult, scan::ProbeSetting}; +use crate::capture::pcap::PacketCaptureOptions; +use crate::endpoint::{EndpointResult, OsGuess}; + +/// Send ICMP Echo Request packets to the specified target endpoints +pub async fn send_hostscan_packets( + tx: &mut Box, + interface: &Interface, + scan_setting: &ProbeSetting, +) { + let header_span = tracing::info_span!("icmp_host_scan"); + header_span.pb_set_style(&crate::output::progress::get_progress_style()); + header_span.pb_set_message("HostScan"); + header_span.pb_set_length(scan_setting.target_endpoints.len() as u64); + header_span.pb_set_position(0); + header_span.pb_start(); + + for target in &scan_setting.target_endpoints { + let packet = crate::packet::icmp::build_icmp_packet(&interface, target.ip, false); + // Send a packet using poll_fn. + match poll_fn(|cx| tx.poll_send(cx, &packet)).await { + Ok(_) => { + if !scan_setting.send_rate.is_zero() { + tokio::time::sleep(scan_setting.send_rate).await; + } + }, + Err(e) => eprintln!("Failed to send packet: {}", e), + } + header_span.pb_inc(1); + } + drop(header_span); +} + +/// Run host scan using ICMP Echo Request packets and return the results. +pub async fn run_host_scan(setting: ProbeSetting) -> Result { + let interface = match crate::interface::get_interface_by_index(setting.if_index) { + Some(interface) => interface, + None => return Err(anyhow::anyhow!("Interface not found")), + }; + // Create sender + let config = nex::datalink::Config { + write_buffer_size: 4096, + read_buffer_size: 4096, + read_timeout: Some(setting.wait_time), + write_timeout: None, + channel_type: nex::datalink::ChannelType::Layer2, + bpf_fd_attempts: 1000, + linux_fanout: None, + promiscuous: false, + }; + + let AsyncChannel::Ethernet(mut tx, mut rx) = async_channel(&interface, config)? + else { + unreachable!(); + }; + + let mut capture_options: PacketCaptureOptions = PacketCaptureOptions { + interface_index: interface.index, + interface_name: interface.name.clone(), + src_ips: HashSet::new(), + dst_ips: HashSet::new(), + src_ports: HashSet::new(), + dst_ports: HashSet::new(), + ether_types: HashSet::new(), + ip_protocols: HashSet::new(), + capture_timeout: setting.task_timeout, + read_timeout: setting.wait_time, + promiscuous: false, + receive_undefined: false, + tunnel: interface.is_tun(), + loopback: interface.is_loopback(), + }; + for endpoint in &setting.target_endpoints { + capture_options.src_ips.insert(endpoint.ip); + } + capture_options.ip_protocols.insert(IpNextProtocol::Icmp); + capture_options.ip_protocols.insert(IpNextProtocol::Icmpv6); + + + let (ready_tx, ready_rx) = tokio::sync::oneshot::channel(); + let (stop_tx, mut stop_rx) = tokio::sync::oneshot::channel(); + + let capture_handle: tokio::task::JoinHandle<_> = tokio::spawn(async move { + crate::capture::pcap::start_capture( + &mut rx, + capture_options, + ready_tx, + &mut stop_rx, + ) + .await + }); + + // Wait for listener to start + let _ = ready_rx; + let start_time = std::time::Instant::now(); + // Send probe packets + send_hostscan_packets(&mut tx, &interface, &setting).await; + tokio::time::sleep(setting.wait_time).await; + // Stop pcap + let _ = stop_tx.send(()); + let frames = capture_handle.await.unwrap(); + let dns_map = setting.get_dns_map(); + let mut result = parse_hostscan_result(frames, &interface, &dns_map); + result.scan_time = start_time.elapsed(); + Ok(result) +} + +/// Parse captured packets and extract scan results. +fn parse_hostscan_result( + packets: Vec, + iface: &Interface, + dns_map: &HashMap, +) -> ScanResult { + let oui_db = crate::db::oui::oui_db(); + let if_ipv4_set: HashSet = iface.ipv4_addrs().into_iter().collect(); + let if_ipv6_set: HashSet = iface.ipv6_addrs().into_iter().collect(); + let mut result: ScanResult = ScanResult::new(); + let mut endpoint_map: HashMap = HashMap::new(); + for p in packets { + if p.ip.is_none() { + continue; + } + let mut mac_addr: MacAddr; + if let Some(datalink) = &p.datalink { + if let Some(ethernet_frame) = &datalink.ethernet { + if ethernet_frame.destination != iface.mac_addr.unwrap_or(MacAddr::zero()) { + continue; + } + mac_addr = ethernet_frame.source; + } else { + mac_addr = MacAddr::zero(); + } + } else { + mac_addr = MacAddr::zero(); + } + let ip_addr: IpAddr; + let ttl: u8; + if let Some(ip) = &p.ip { + // Expect ICMP or ICMPv6 Port Unreachable + if ip.icmp.is_none() && ip.icmpv6.is_none() { + continue; + } + if let Some(ipv4_packet) = &ip.ipv4 { + if if_ipv4_set.contains(&ipv4_packet.source) { + mac_addr = iface.mac_addr.unwrap_or(MacAddr::zero()); + ttl = crate::util::ip::initial_ttl(ipv4_packet.ttl); + }else{ + ttl = ipv4_packet.ttl; + } + ip_addr = IpAddr::V4(ipv4_packet.source); + } else if let Some(ipv6_packet) = &ip.ipv6 { + if if_ipv6_set.contains(&ipv6_packet.source) { + mac_addr = iface.mac_addr.unwrap_or(MacAddr::zero()); + ttl = crate::util::ip::initial_ttl(ipv6_packet.hop_limit); + }else { + ttl = ipv6_packet.hop_limit; + } + ip_addr = IpAddr::V6(ipv6_packet.source); + } else { + continue; + } + } else { + continue; + } + + let vendor_name_opt: Option; + if let Some(oui) = oui_db.lookup_mac(&mac_addr) { + if let Some(vendor_detail) = &oui.vendor_detail { + vendor_name_opt = Some(vendor_detail.clone()); + } else { + vendor_name_opt = Some(oui.vendor.clone()); + } + } else { + vendor_name_opt = None; + } + + endpoint_map + .entry(ip_addr) + .or_insert(EndpointResult { + ip: ip_addr, + hostname: dns_map.get(&ip_addr).cloned(), + ports: BTreeMap::new(), + mac_addr: Some(mac_addr), + vendor_name: vendor_name_opt, + os: OsGuess::default().with_ttl_observed(ttl), + tags: Vec::new(), + cpes: Vec::new(), + }); + + result.fingerprints.push(p.clone()); + + } + for (_ip, endpoint) in endpoint_map { + result.endpoints.push(endpoint); + } + result +} diff --git a/src/scan/probe/mod.rs b/src/scan/probe/mod.rs new file mode 100644 index 0000000..5a350cd --- /dev/null +++ b/src/scan/probe/mod.rs @@ -0,0 +1,4 @@ +pub mod tcp; +pub mod udp; +pub mod quic; +pub mod icmp; diff --git a/src/scan/probe/quic.rs b/src/scan/probe/quic.rs new file mode 100644 index 0000000..5a00471 --- /dev/null +++ b/src/scan/probe/quic.rs @@ -0,0 +1,165 @@ +use std::{collections::BTreeMap, time::Duration}; + +use anyhow::Result; +use futures::StreamExt; +use tokio::sync::mpsc; +use tracing_indicatif::span_ext::IndicatifSpanExt; +use crate::{cli::PortScanMethod, endpoint::{EndpointResult, OsGuess, Port, PortResult, PortState, ServiceInfo, TransportProtocol}, output::ScanResult, scan::ProbeSetting, service::probe::quic::quic_client_config}; + +/// Try to connect to the given ports on the target endpoint using QUIC protocol. +/// Concurrency specifies the number of concurrent connection attempts. +pub async fn try_connect_ports( + target: crate::endpoint::Endpoint, + concurrency: usize, + timeout: Duration, +) -> Result { + let alpn: [&[u8]; 8] = [ + b"h3".as_slice(), + b"h3-34".as_slice(), b"h3-33".as_slice(), b"h3-32".as_slice(), b"h3-31".as_slice(), b"h3-30".as_slice(), b"h3-29".as_slice(), + b"hq-29".as_slice(), + ]; + let (ch_tx, mut ch_rx) = mpsc::unbounded_channel::(); + let header_span = tracing::info_span!("quic_connect_scan"); + header_span.pb_set_style(&crate::output::progress::get_progress_style()); + header_span.pb_set_message(&format!("QUIC PortScan ({})", target.ip)); + header_span.pb_set_length(target.ports.len() as u64); + header_span.pb_set_position(0); + header_span.pb_start(); + + let span_rx = header_span.clone(); + let recv_task = tokio::spawn(async move { + let mut open_ports: BTreeMap = BTreeMap::new(); + while let Some(port_result) = ch_rx.recv().await { + open_ports.insert(port_result.port.clone(), port_result); + // Update progress bar + span_rx.pb_inc(1); + } + open_ports + }); + + let hostname = target.hostname.clone().unwrap_or_else(|| target.ip.to_string()); + let prod = futures::stream::iter(target.socket_addrs(TransportProtocol::Quic)).for_each_concurrent(concurrency, move |socket_addr| { + let ch_tx = ch_tx.clone(); + let hostname = hostname.clone(); + let client_cfg = quic_client_config(true, &alpn).unwrap(); + + async move { + let mut endpoint = match quinn::Endpoint::client((if target.ip.is_ipv6() { "[::]:0" } else { "0.0.0.0:0" }).parse().unwrap()) { + Ok(ep) => ep, + Err(_) => return, + }; + endpoint.set_default_client_config(client_cfg.clone()); + let connect_fut = match endpoint.connect(socket_addr, hostname.as_str()) { + Ok(connecting) => { + connecting + } + Err(e) => { + tracing::error!("Failed to connect to {}: {}", socket_addr, e); + return; + } + }; + let mut port_result = PortResult { + port: Port::new(socket_addr.port(), TransportProtocol::Udp), + state: PortState::Closed, + service: ServiceInfo::default(), + rtt_ms: None, + }; + match tokio::time::timeout(timeout, connect_fut).await { + Ok(quinn_conn) => { + match quinn_conn { + Ok(conn) => { + // Connection succeeded + port_result.state = PortState::Open; + conn.close(0u32.into(), b"Connection closed by client"); + } + Err(e) => { + match e { + quinn::ConnectionError::VersionMismatch + | quinn::ConnectionError::TransportError(_) + | quinn::ConnectionError::ConnectionClosed(_) + | quinn::ConnectionError::ApplicationClosed(_) + | quinn::ConnectionError::Reset => { + // Error, but QUIC service is still running + // So we classify it as open + port_result.state = PortState::Open; + }, + _ => {}, + } + } + } + } + Err(e) => { + // Timeout + tracing::error!("Failed to connect to {}: {}", socket_addr, e); + } + } + let _ = ch_tx.send(port_result); + } + }); + + let prod_task = tokio::spawn(prod); + let (results_res, _prod_res) = tokio::join!(recv_task, prod_task); + let open_ports = results_res?; + // Finish header span + drop(header_span); + + let ep = EndpointResult { + ip: target.ip, + hostname: target.hostname, + ports: open_ports, + mac_addr: target.mac_addr, + vendor_name: None, + os: OsGuess::default(), + tags: target.tags, + cpes: Vec::new(), + }; + Ok(ep) +} + +/// Run a QUIC connect scan based on the provided probe settings. +pub async fn run_connect_scan( + setting: ProbeSetting, +) -> Result { + let start_time = std::time::Instant::now(); + let mut tasks = vec![]; + for target in setting.target_endpoints { + tasks.push(tokio::spawn(async move { + let host = try_connect_ports( + target, + setting.port_concurrency, + setting.connect_timeout, + ) + .await; + host + })); + } + let mut endpoints: Vec = vec![]; + for task in tasks { + match task.await { + Ok(endpoint) => { + match endpoint { + Ok(ep) => endpoints.push(ep), + Err(e) => { + tracing::error!("Failed to connect to endpoint: {}", e); + } + } + } + Err(e) => { + tracing::error!("Failed to join task: {}", e); + } + } + } + let mut result = ScanResult::new(); + result.endpoints = endpoints; + result.scan_time = start_time.elapsed(); + result.fingerprints = Vec::new(); + Ok(result) +} + +/// Run a QUIC port scan using the specified probe settings and method. +pub async fn run_port_scan( + setting: ProbeSetting, + _method: PortScanMethod, +) -> Result { + run_connect_scan(setting).await +} diff --git a/src/scan/probe/tcp.rs b/src/scan/probe/tcp.rs new file mode 100644 index 0000000..f50085d --- /dev/null +++ b/src/scan/probe/tcp.rs @@ -0,0 +1,609 @@ +use futures::stream::{self, StreamExt}; +use futures::future::poll_fn; +use netdev::{Interface, MacAddr}; +use nex::datalink::async_io::{async_channel, AsyncChannel, AsyncRawSender}; +use nex::packet::frame::Frame; +use nex::packet::ip::IpNextProtocol; +use nex::packet::tcp::TcpFlags; +use nex::socket::tcp::{AsyncTcpSocket, TcpConfig}; +use tracing_indicatif::span_ext::IndicatifSpanExt; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::time::Duration; +use tokio::io::AsyncWriteExt; +use std::collections::{BTreeMap, HashMap, HashSet}; +use anyhow::Result; +use tokio::sync::mpsc; + +use crate::capture::pcap::PacketCaptureOptions; +use crate::cli::{PortScanMethod}; +use crate::endpoint::{Endpoint, EndpointResult, OsGuess, Port, PortResult, PortState, ServiceInfo, TransportProtocol}; +use crate::output::ScanResult; +use crate::probe::ProbeSetting; + +/// Try to connect to the given ports on the target endpoint using TCP protocol. +/// Concurrency specifies the number of concurrent connection attempts. +pub async fn try_connect_ports( + target: Endpoint, + concurrency: usize, + timeout: Duration, +) -> Result { + let (ch_tx, mut ch_rx) = mpsc::unbounded_channel::(); + let header_span = tracing::info_span!("tcp_connect_scan"); + header_span.pb_set_style(&crate::output::progress::get_progress_style()); + header_span.pb_set_message(&format!("TCP PortScan ({})", target.ip)); + header_span.pb_set_length(target.ports.len() as u64); + header_span.pb_set_position(0); + header_span.pb_start(); + + let span_rx = header_span.clone(); + let recv_task = tokio::spawn(async move { + let mut open_ports: BTreeMap = BTreeMap::new(); + while let Some(port_result) = ch_rx.recv().await { + open_ports.insert(port_result.port.clone(), port_result); + // Update progress bar + span_rx.pb_inc(1); + } + open_ports + }); + + let prod = stream::iter(target.socket_addrs(TransportProtocol::Tcp)).for_each_concurrent(concurrency, move |socket_addr| { + let ch_tx = ch_tx.clone(); + async move { + let cfg = if socket_addr.is_ipv4() { + TcpConfig::v4_stream() + } else { + TcpConfig::v6_stream() + }; + let socket = AsyncTcpSocket::from_config(&cfg).unwrap(); + let mut port_result = PortResult { + port: Port::new(socket_addr.port(), TransportProtocol::Tcp), + state: PortState::Closed, + service: ServiceInfo::default(), + rtt_ms: None, + }; + match socket.connect_timeout(socket_addr, timeout).await { + Ok(mut stream) => { + port_result.state = PortState::Open; + match stream.shutdown().await { + Ok(_) => {} + Err(_) => {} + } + } + Err(_) => {} + } + let _ = ch_tx.send(port_result); + } + }); + let prod_task = tokio::spawn(prod); + let (results_res, _prod_res) = tokio::join!(recv_task, prod_task); + let open_ports = results_res?; + // Finish header span + drop(header_span); + Ok(EndpointResult { + ip: target.ip, + hostname: target.hostname, + ports: open_ports, + mac_addr: target.mac_addr, + vendor_name: None, + os: OsGuess::default(), + tags: target.tags, + cpes: Vec::new(), + }) +} + +/// Run a TCP connect scan based on the provided probe settings. +pub async fn run_connect_scan( + setting: ProbeSetting, +) -> Result { + let start_time = std::time::Instant::now(); + let mut tasks = vec![]; + for target in setting.target_endpoints { + tasks.push(tokio::spawn(async move { + let host = try_connect_ports( + target, + setting.port_concurrency, + setting.connect_timeout, + ) + .await; + host + })); + } + let mut endpoints: Vec = vec![]; + for task in tasks { + match task.await { + Ok(endpoint_result) => { + match endpoint_result { + Ok(endpoint) => { + endpoints.push(endpoint); + } + Err(e) => { + tracing::error!("error: {}", e); + } + } + } + Err(e) => { + tracing::error!("error: {}", e); + } + } + } + let mut result = ScanResult::new(); + result.endpoints = endpoints; + result.scan_time = start_time.elapsed(); + result.fingerprints = Vec::new(); + Ok(result) +} + +/// Send TCP SYN packets for port scanning. +pub async fn send_portscan_packets( + tx: &mut Box, + interface: &Interface, + scan_setting: &ProbeSetting, +) { + let mut sent: usize = 0; + for target in &scan_setting.target_endpoints { + let header_span = tracing::info_span!("tcp_syn_scan"); + header_span.pb_set_style(&crate::output::progress::get_progress_style()); + header_span.pb_set_message(&format!("PortScan ({})", target.ip)); + header_span.pb_set_length(target.ports.len() as u64); + header_span.pb_set_position(0); + header_span.pb_start(); + + for port in &target.ports { + let packet = + crate::packet::tcp::build_tcp_syn_packet(&interface, target.ip, port.number, false); + + // Send a packet using poll_fn. + match poll_fn(|cx| tx.poll_send(cx, &packet)).await { + Ok(_) => { + if !scan_setting.send_rate.is_zero() && sent < 64 { + tokio::time::sleep(scan_setting.send_rate).await; + } + sent += 1; + } + Err(e) => eprintln!("Failed to send packet: {}", e), + } + header_span.pb_inc(1); + } + drop(header_span); + } +} + +/// Send TCP SYN packets for host scanning. +pub async fn send_hostscan_packets( + tx: &mut Box, + interface: &Interface, + scan_setting: &ProbeSetting, +) { + let header_span = tracing::info_span!("tcp_syn_host_scan"); + header_span.pb_set_style(&crate::output::progress::get_progress_style()); + header_span.pb_set_message("HostScan"); + header_span.pb_set_length(scan_setting.target_endpoints.len() as u64); + header_span.pb_set_position(0); + header_span.pb_start(); + + for target in &scan_setting.target_endpoints { + for port in &target.ports { + let packet = + crate::packet::tcp::build_tcp_syn_packet(&interface, target.ip, port.number, false); + + // Send a packet using poll_fn. + match poll_fn(|cx| tx.poll_send(cx, &packet)).await { + Ok(_) => { + if !scan_setting.send_rate.is_zero() { + tokio::time::sleep(scan_setting.send_rate).await; + } + } + Err(e) => eprintln!("Failed to send packet: {}", e), + } + } + header_span.pb_inc(1); + } + drop(header_span); +} + +/// Run a TCP SYN scan based on the provided probe settings. +pub async fn run_syn_scan( + setting: ProbeSetting, +) -> Result { + let interface = match crate::interface::get_interface_by_index(setting.if_index) { + Some(interface) => interface, + None => return Err(anyhow::anyhow!("Interface not found")), + }; + // Create sender + let config = nex::datalink::Config { + write_buffer_size: 4096, + read_buffer_size: 4096, + read_timeout: Some(setting.wait_time), + write_timeout: None, + channel_type: nex::datalink::ChannelType::Layer2, + bpf_fd_attempts: 1000, + linux_fanout: None, + promiscuous: false, + }; + + let AsyncChannel::Ethernet(mut tx, mut rx) = async_channel(&interface, config)? + else { + unreachable!(); + }; + + let mut capture_options: PacketCaptureOptions = PacketCaptureOptions { + interface_index: interface.index, + interface_name: interface.name.clone(), + src_ips: HashSet::new(), + dst_ips: HashSet::new(), + src_ports: HashSet::new(), + dst_ports: HashSet::new(), + ether_types: HashSet::new(), + ip_protocols: HashSet::new(), + capture_timeout: setting.task_timeout, + read_timeout: setting.wait_time, + promiscuous: false, + receive_undefined: false, + tunnel: interface.is_tun(), + loopback: interface.is_loopback(), + }; + for endpoint in setting.target_endpoints.clone() { + capture_options.src_ips.insert(endpoint.ip); + capture_options.src_ports.extend(endpoint.ports.iter().map(|p| p.number)); + } + capture_options.ip_protocols.insert(IpNextProtocol::Tcp); + + let (ready_tx, ready_rx) = tokio::sync::oneshot::channel(); + let (stop_tx, mut stop_rx) = tokio::sync::oneshot::channel(); + + let capture_handle: tokio::task::JoinHandle<_> = tokio::spawn(async move { + crate::capture::pcap::start_capture( + &mut rx, + capture_options, + ready_tx, + &mut stop_rx, + ) + .await + }); + + // Wait for listener to start + let _ = ready_rx; + let start_time = std::time::Instant::now(); + // Send probe packets + send_portscan_packets(&mut tx, &interface, &setting).await; + tokio::time::sleep(setting.wait_time).await; + // Stop pcap + let _ = stop_tx.send(()); + let frames = capture_handle.await.unwrap(); + let dns_map = setting.get_dns_map(); + let mut result = parse_portscan_result(frames, &interface, &dns_map); + result.scan_time = start_time.elapsed(); + Ok(result) +} + +/// Run a TCP port scan using the specified probe settings and method. +pub async fn run_port_scan( + setting: ProbeSetting, + method: PortScanMethod +) -> Result { + match method { + PortScanMethod::Connect => { + return run_connect_scan(setting).await; + } + PortScanMethod::Syn => { + return run_syn_scan(setting).await; + } + } +} + +/// Run a TCP host scan based on the provided probe settings. +pub async fn run_host_scan(setting: ProbeSetting) -> Result { + let interface = match crate::interface::get_interface_by_index(setting.if_index) { + Some(interface) => interface, + None => return Err(anyhow::anyhow!("Interface not found")), + }; + // Create sender + let config = nex::datalink::Config { + write_buffer_size: 4096, + read_buffer_size: 4096, + read_timeout: Some(setting.wait_time), + write_timeout: None, + channel_type: nex::datalink::ChannelType::Layer2, + bpf_fd_attempts: 1000, + linux_fanout: None, + promiscuous: false, + }; + + let AsyncChannel::Ethernet(mut tx, mut rx) = async_channel(&interface, config)? + else { + unreachable!(); + }; + + let mut capture_options: PacketCaptureOptions = PacketCaptureOptions { + interface_index: interface.index, + interface_name: interface.name.clone(), + src_ips: HashSet::new(), + dst_ips: HashSet::new(), + src_ports: HashSet::new(), + dst_ports: HashSet::new(), + ether_types: HashSet::new(), + ip_protocols: HashSet::new(), + capture_timeout: setting.task_timeout, + read_timeout: setting.wait_time, + promiscuous: false, + receive_undefined: false, + tunnel: interface.is_tun(), + loopback: interface.is_loopback(), + }; + for endpoint in &setting.target_endpoints { + capture_options.src_ips.insert(endpoint.ip); + capture_options.src_ports.extend(endpoint.ports.iter().map(|p| p.number)); + } + capture_options.ip_protocols.insert(IpNextProtocol::Tcp); + + let (ready_tx, ready_rx) = tokio::sync::oneshot::channel(); + let (stop_tx, mut stop_rx) = tokio::sync::oneshot::channel(); + + let capture_handle: tokio::task::JoinHandle<_> = tokio::spawn(async move { + crate::capture::pcap::start_capture( + &mut rx, + capture_options, + ready_tx, + &mut stop_rx, + ) + .await + }); + + // Wait for listener to start + let _ = ready_rx; + let start_time = std::time::Instant::now(); + // Send probe packets + send_hostscan_packets(&mut tx, &interface, &setting).await; + tokio::time::sleep(setting.wait_time).await; + // Stop pcap + let _ = stop_tx.send(()); + let frames = capture_handle.await.unwrap(); + let dns_map = setting.get_dns_map(); + let mut result = parse_hostscan_result(frames, &interface, &dns_map); + result.scan_time = start_time.elapsed(); + Ok(result) +} + +/// Parse port scan results from captured packets. +fn parse_portscan_result( + packets: Vec, + iface: &Interface, + dns_map: &HashMap, +) -> ScanResult { + let mut result: ScanResult = ScanResult::new(); + let mut socket_set: HashSet = HashSet::new(); + let mut endpoint_map: HashMap = HashMap::new(); + for p in packets { + if p.ip.is_none() || p.transport.is_none() { + continue; + } + let mac_addr: MacAddr; + if let Some(datalink) = &p.datalink { + if let Some(ethernet_frame) = &datalink.ethernet { + if ethernet_frame.destination != iface.mac_addr.unwrap_or(MacAddr::zero()) { + continue; + } + mac_addr = ethernet_frame.source; + } else { + mac_addr = MacAddr::zero(); + } + } else { + mac_addr = MacAddr::zero(); + } + let ip_addr: IpAddr; + let ttl: u8; + let port: PortResult; + if let Some(ip) = &p.ip { + if let Some(ipv4_packet) = &ip.ipv4 { + ip_addr = IpAddr::V4(ipv4_packet.source); + ttl = ipv4_packet.ttl; + } else if let Some(ipv6_packet) = &ip.ipv6 { + ip_addr = IpAddr::V6(ipv6_packet.source); + ttl = ipv6_packet.hop_limit; + } else { + continue; + } + } else { + continue; + } + if let Some(transport) = &p.transport { + if let Some(tcp_packet) = &transport.tcp { + if socket_set.contains(&SocketAddr::new(ip_addr,tcp_packet.source)) { + continue; + } + let f = tcp_packet.flags; + if (f & TcpFlags::RST) != 0 { + port = PortResult { + port: Port::new(tcp_packet.source, TransportProtocol::Tcp), + state: PortState::Closed, + service: ServiceInfo::default(), + rtt_ms: None, + }; + } else if (f & (TcpFlags::SYN | TcpFlags::ACK)) == (TcpFlags::SYN | TcpFlags::ACK) { + port = PortResult { + port: Port::new(tcp_packet.source, TransportProtocol::Tcp), + state: PortState::Open, + service: ServiceInfo::default(), + rtt_ms: None, + }; + } else { + continue; + } + } else { + continue; + } + } else { + continue; + } + endpoint_map + .entry(ip_addr) + .or_insert(EndpointResult { + ip: ip_addr, + hostname: dns_map.get(&ip_addr).cloned(), + ports: BTreeMap::new(), + mac_addr: Some(mac_addr), + vendor_name: None, + os: OsGuess::default().with_ttl_observed(ttl), + tags: Vec::new(), + cpes: Vec::new(), + }) + .ports + .insert(port.port.clone(), port.clone()); + + result.fingerprints.push(p.clone()); + socket_set.insert(SocketAddr::new(ip_addr, port.port.number)); + + } + for (ip, endpoint) in endpoint_map { + let mut ep = EndpointResult::new(ip); + ep.hostname = endpoint.hostname; + ep.mac_addr = endpoint.mac_addr; + ep.vendor_name = endpoint.vendor_name; + ep.os = endpoint.os; + ep.tags = endpoint.tags; + for (_port, port_result) in endpoint.ports { + ep.upsert_port(port_result); + } + result.endpoints.push(ep); + } + result +} + +/// Parse host scan results from captured packets. +fn parse_hostscan_result( + packets: Vec, + iface: &Interface, + dns_map: &HashMap, +) -> ScanResult { + let oui_db = crate::db::oui::oui_db(); + let if_ipv4_set: HashSet = iface.ipv4_addrs().into_iter().collect(); + let if_ipv6_set: HashSet = iface.ipv6_addrs().into_iter().collect(); + let mut result: ScanResult = ScanResult::new(); + let mut socket_set: HashSet = HashSet::new(); + let mut endpoint_map: HashMap = HashMap::new(); + for p in packets { + if p.ip.is_none() || p.transport.is_none() { + continue; + } + let mut mac_addr: MacAddr; + if let Some(datalink) = &p.datalink { + if let Some(ethernet_frame) = &datalink.ethernet { + if ethernet_frame.destination != iface.mac_addr.unwrap_or(MacAddr::zero()) { + continue; + } + mac_addr = ethernet_frame.source; + } else { + mac_addr = MacAddr::zero(); + } + } else { + mac_addr = MacAddr::zero(); + } + let ip_addr: IpAddr; + let ttl: u8; + let port: PortResult; + if let Some(ip) = &p.ip { + if let Some(ipv4_packet) = &ip.ipv4 { + if if_ipv4_set.contains(&ipv4_packet.source) { + mac_addr = iface.mac_addr.unwrap_or(MacAddr::zero()); + ttl = crate::util::ip::initial_ttl(ipv4_packet.ttl); + }else{ + ttl = ipv4_packet.ttl; + } + ip_addr = IpAddr::V4(ipv4_packet.source); + } else if let Some(ipv6_packet) = &ip.ipv6 { + if if_ipv6_set.contains(&ipv6_packet.source) { + mac_addr = iface.mac_addr.unwrap_or(MacAddr::zero()); + ttl = crate::util::ip::initial_ttl(ipv6_packet.hop_limit); + }else { + ttl = ipv6_packet.hop_limit; + } + ip_addr = IpAddr::V6(ipv6_packet.source); + } else { + continue; + } + } else { + continue; + } + if let Some(transport) = &p.transport { + if let Some(tcp_packet) = &transport.tcp { + if socket_set.contains(&SocketAddr::new(ip_addr,tcp_packet.source)) { + continue; + } + let f = tcp_packet.flags; + if (f & (TcpFlags::SYN | TcpFlags::ACK)) == (TcpFlags::SYN | TcpFlags::ACK) { + port = PortResult { + port: Port::new(tcp_packet.source, TransportProtocol::Tcp), + state: PortState::Open, + service: ServiceInfo::default(), + rtt_ms: None, + }; + } else { + continue; + } + } else { + continue; + } + } else { + continue; + } + + let vendor_name_opt: Option; + if let Some(oui) = oui_db.lookup_mac(&mac_addr) { + if let Some(vendor_detail) = &oui.vendor_detail { + vendor_name_opt = Some(vendor_detail.clone()); + } else { + vendor_name_opt = Some(oui.vendor.clone()); + } + } else { + vendor_name_opt = None; + } + + let mut os_guess = OsGuess::default().with_ttl_observed(ttl); + let mut cpes: Vec = Vec::new(); + + match crate::os::match_tcpip_signatures(&p) { + Some(os_match) => { + os_guess.family = Some(os_match.family); + os_guess.confidence = Some(os_match.confidence as f32); + os_guess.ttl_observed = Some(ttl); + cpes = os_match.cpes; + } + None => { + tracing::debug!("No matching OS found"); + } + } + + endpoint_map + .entry(ip_addr) + .or_insert(EndpointResult { + ip: ip_addr, + hostname: dns_map.get(&ip_addr).cloned(), + ports: BTreeMap::new(), + mac_addr: Some(mac_addr), + vendor_name: vendor_name_opt, + os: os_guess, + tags: Vec::new(), + cpes: cpes, + }) + .ports + .insert(port.port.clone(), port.clone()); + + result.fingerprints.push(p.clone()); + socket_set.insert(SocketAddr::new(ip_addr, port.port.number)); + + } + for (ip, endpoint) in endpoint_map { + let mut ep = EndpointResult::new(ip); + ep.hostname = endpoint.hostname; + ep.mac_addr = endpoint.mac_addr; + ep.vendor_name = endpoint.vendor_name; + ep.os = endpoint.os; + ep.cpes = endpoint.cpes; + ep.tags = endpoint.tags; + for (_port, port_result) in endpoint.ports { + ep.upsert_port(port_result); + } + result.endpoints.push(ep); + } + result +} diff --git a/src/scan/probe/udp.rs b/src/scan/probe/udp.rs new file mode 100644 index 0000000..4235f1f --- /dev/null +++ b/src/scan/probe/udp.rs @@ -0,0 +1,207 @@ +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +use futures::future::poll_fn; +use netdev::{Interface, MacAddr}; +use nex::datalink::async_io::{async_channel, AsyncChannel, AsyncRawSender}; +use nex::packet::frame::Frame; +use nex::packet::ip::IpNextProtocol; +use tracing_indicatif::span_ext::IndicatifSpanExt; +use anyhow::Result; +use crate::{config::default::DEFAULT_BASE_TARGET_UDP_PORT, output::ScanResult, probe::ProbeSetting}; +use crate::capture::pcap::PacketCaptureOptions; +use crate::endpoint::{EndpointResult, OsGuess}; + +/// Send UDP packets for host scanning. +pub async fn send_hostscan_packets( + tx: &mut Box, + interface: &Interface, + scan_setting: &ProbeSetting, +) { + let header_span = tracing::info_span!("udp_host_scan"); + header_span.pb_set_style(&crate::output::progress::get_progress_style()); + header_span.pb_set_message("HostScan"); + header_span.pb_set_length(scan_setting.target_endpoints.len() as u64); + header_span.pb_set_position(0); + header_span.pb_start(); + + for target in &scan_setting.target_endpoints { + let packet = crate::packet::udp::build_udp_packet(&interface, target.ip, DEFAULT_BASE_TARGET_UDP_PORT, false); + // Send a packet using poll_fn. + match poll_fn(|cx| tx.poll_send(cx, &packet)).await { + Ok(_) => { + if !scan_setting.send_rate.is_zero() { + tokio::time::sleep(scan_setting.send_rate).await; + } + }, + Err(e) => eprintln!("Failed to send packet: {}", e), + } + header_span.pb_inc(1); + } + drop(header_span); +} + +/// Run a UDP host scan based on the provided probe settings. +pub async fn run_host_scan(setting: ProbeSetting) -> Result { + let interface = match crate::interface::get_interface_by_index(setting.if_index) { + Some(interface) => interface, + None => return Err(anyhow::anyhow!("Interface not found")), + }; + // Create sender + let config = nex::datalink::Config { + write_buffer_size: 4096, + read_buffer_size: 4096, + read_timeout: Some(setting.wait_time), + write_timeout: None, + channel_type: nex::datalink::ChannelType::Layer2, + bpf_fd_attempts: 1000, + linux_fanout: None, + promiscuous: false, + }; + + let AsyncChannel::Ethernet(mut tx, mut rx) = async_channel(&interface, config)? + else { + unreachable!(); + }; + + let mut capture_options: PacketCaptureOptions = PacketCaptureOptions { + interface_index: interface.index, + interface_name: interface.name.clone(), + src_ips: HashSet::new(), + dst_ips: HashSet::new(), + src_ports: HashSet::new(), + dst_ports: HashSet::new(), + ether_types: HashSet::new(), + ip_protocols: HashSet::new(), + capture_timeout: setting.task_timeout, + read_timeout: setting.wait_time, + promiscuous: false, + receive_undefined: false, + tunnel: interface.is_tun(), + loopback: interface.is_loopback(), + }; + for endpoint in &setting.target_endpoints { + capture_options.src_ips.insert(endpoint.ip); + } + capture_options.ip_protocols.insert(IpNextProtocol::Udp); + capture_options.ip_protocols.insert(IpNextProtocol::Icmp); + capture_options.ip_protocols.insert(IpNextProtocol::Icmpv6); + + let (ready_tx, ready_rx) = tokio::sync::oneshot::channel(); + let (stop_tx, mut stop_rx) = tokio::sync::oneshot::channel(); + + let capture_handle: tokio::task::JoinHandle<_> = tokio::spawn(async move { + crate::capture::pcap::start_capture( + &mut rx, + capture_options, + ready_tx, + &mut stop_rx, + ) + .await + }); + + // Wait for listener to start + let _ = ready_rx; + let start_time = std::time::Instant::now(); + // Send probe packets + send_hostscan_packets(&mut tx, &interface, &setting).await; + tokio::time::sleep(setting.wait_time).await; + // Stop pcap + let _ = stop_tx.send(()); + let frames = capture_handle.await.unwrap(); + let dns_map = setting.get_dns_map(); + let mut result = parse_hostscan_result(frames, &interface, &dns_map); + result.scan_time = start_time.elapsed(); + Ok(result) +} + +/// Parse host scan results from captured packets. +fn parse_hostscan_result( + packets: Vec, + iface: &Interface, + dns_map: &HashMap, +) -> ScanResult { + let oui_db = crate::db::oui::oui_db(); + let if_ipv4_set: HashSet = iface.ipv4_addrs().into_iter().collect(); + let if_ipv6_set: HashSet = iface.ipv6_addrs().into_iter().collect(); + let mut result: ScanResult = ScanResult::new(); + let mut endpoint_map: HashMap = HashMap::new(); + for p in packets { + if p.ip.is_none() { + continue; + } + let mut mac_addr: MacAddr; + if let Some(datalink) = &p.datalink { + if let Some(ethernet_frame) = &datalink.ethernet { + if ethernet_frame.destination != iface.mac_addr.unwrap_or(MacAddr::zero()) { + continue; + } + mac_addr = ethernet_frame.source; + } else { + mac_addr = MacAddr::zero(); + } + } else { + mac_addr = MacAddr::zero(); + } + let ip_addr: IpAddr; + let ttl: u8; + if let Some(ip) = &p.ip { + // Expect ICMP or ICMPv6 Port Unreachable + if ip.icmp.is_none() && ip.icmpv6.is_none() { + continue; + } + if let Some(ipv4_packet) = &ip.ipv4 { + if if_ipv4_set.contains(&ipv4_packet.source) { + mac_addr = iface.mac_addr.unwrap_or(MacAddr::zero()); + ttl = crate::util::ip::initial_ttl(ipv4_packet.ttl); + }else{ + ttl = ipv4_packet.ttl; + } + ip_addr = IpAddr::V4(ipv4_packet.source); + } else if let Some(ipv6_packet) = &ip.ipv6 { + if if_ipv6_set.contains(&ipv6_packet.source) { + mac_addr = iface.mac_addr.unwrap_or(MacAddr::zero()); + ttl = crate::util::ip::initial_ttl(ipv6_packet.hop_limit); + }else { + ttl = ipv6_packet.hop_limit; + } + ip_addr = IpAddr::V6(ipv6_packet.source); + } else { + continue; + } + } else { + continue; + } + + let vendor_name_opt: Option; + if let Some(oui) = oui_db.lookup_mac(&mac_addr) { + if let Some(vendor_detail) = &oui.vendor_detail { + vendor_name_opt = Some(vendor_detail.clone()); + } else { + vendor_name_opt = Some(oui.vendor.clone()); + } + } else { + vendor_name_opt = None; + } + + endpoint_map + .entry(ip_addr) + .or_insert(EndpointResult { + ip: ip_addr, + hostname: dns_map.get(&ip_addr).cloned(), + ports: BTreeMap::new(), + mac_addr: Some(mac_addr), + vendor_name: vendor_name_opt, + os: OsGuess::default().with_ttl_observed(ttl), + tags: Vec::new(), + cpes: Vec::new(), + }); + + result.fingerprints.push(p.clone()); + + } + for (_ip, endpoint) in endpoint_map { + result.endpoints.push(endpoint); + } + result +} diff --git a/src/scan/result.rs b/src/scan/result.rs deleted file mode 100644 index a093539..0000000 --- a/src/scan/result.rs +++ /dev/null @@ -1,397 +0,0 @@ -use netdev::mac::MacAddr; -use netdev::Interface; -use nex::packet::tcp::TcpFlags; - -use crate::host::{Host, Port, PortStatus}; -use crate::packet::frame::PacketFrame; -use std::collections::HashSet; -use std::net::{IpAddr, SocketAddr}; -use std::time::Duration; - -use super::setting::{HostScanSetting, HostScanType, PortScanSetting}; - -use serde::{Deserialize, Serialize}; - -/// Status of scan task -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub enum ScanStatus { - Done, - Timeout, - Error(String), -} - -/// Result of scan -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ScanResult { - /// List of scanned Host info and their respective ports - pub hosts: Vec, - /// Time taken to scan - pub scan_time: Duration, - /// Status of the scan task - pub scan_status: ScanStatus, - /// Captured packet fingerprints - pub fingerprints: Vec, -} - -impl ScanResult { - pub fn new() -> ScanResult { - ScanResult { - hosts: vec![], - scan_time: Duration::from_millis(0), - scan_status: ScanStatus::Done, - fingerprints: vec![], - } - } - pub fn error(message: String) -> ScanResult { - ScanResult { - hosts: vec![], - scan_time: Duration::from_millis(0), - scan_status: ScanStatus::Error(message), - fingerprints: vec![], - } - } - /// Returns IP addresses from the scan result - pub fn get_hosts(&self) -> Vec { - let mut hosts: Vec = vec![]; - for host in self.hosts.clone() { - hosts.push(host.ip_addr); - } - hosts - } - /// Get open ports of the specified IP address from the scan results - pub fn get_open_port_numbers(&self, ip_addr: IpAddr) -> Vec { - let mut open_ports: Vec = vec![]; - self.hosts.iter().for_each(|host_info| { - if host_info.ip_addr == ip_addr { - host_info - .ports - .iter() - .for_each(|port_info| match port_info.status { - PortStatus::Open => { - open_ports.push(port_info.number); - } - _ => {} - }); - } - }); - open_ports - } - /// Get open port fingerprint - pub fn get_syn_ack_fingerprint(&self, ip_addr: IpAddr, port: u16) -> Option { - for fingerprint in self.fingerprints.iter() { - if let Some(ipv4_packet) = &fingerprint.ipv4_header { - if ipv4_packet.source == ip_addr { - if let Some(tcp_packet) = &fingerprint.tcp_header { - if tcp_packet.source == port - && tcp_packet.flags == TcpFlags::SYN | TcpFlags::ACK - { - return Some(fingerprint.clone()); - } - } - } - } else if let Some(ipv6_packet) = &fingerprint.ipv6_header { - if ipv6_packet.source == ip_addr { - if let Some(tcp_packet) = &fingerprint.tcp_header { - if tcp_packet.source == port - && tcp_packet.flags == TcpFlags::SYN | TcpFlags::ACK - { - return Some(fingerprint.clone()); - } - } - } - } - } - None - } - pub fn get_host(&self, ip_addr: IpAddr) -> Option { - for host in self.hosts.iter() { - if host.ip_addr == ip_addr { - return Some(host.clone()); - } - } - None - } - pub fn sort_hosts(&mut self) { - self.hosts.sort_by(|a, b| a.ip_addr.cmp(&b.ip_addr)); - } - pub fn sort_ports(&mut self) { - for host in self.hosts.iter_mut() { - host.ports.sort_by(|a, b| a.number.cmp(&b.number)); - } - } -} - -/// Result of a service probe -#[derive(Clone, Debug, PartialEq)] -pub struct ServiceProbeResult { - pub port: u16, - pub service_name: String, - pub service_detail: Option, - pub response: Vec, - pub error: Option, -} - -impl ServiceProbeResult { - /// Create a new successful probe result - pub fn new(port: u16, service_name: String, response: Vec) -> Self { - ServiceProbeResult { - port, - service_name, - service_detail: None, - response, - error: None, - } - } - - /// Create a new probe result with an error - pub fn with_error(port: u16, service_name: String, error: ServiceProbeError) -> Self { - ServiceProbeResult { - port, - service_name, - service_detail: None, - response: Vec::new(), - error: Some(error), - } - } - - /// Check if the result contains an error - pub fn has_error(&self) -> bool { - self.error.is_some() - } - - /// Get a reference to the contained error, if any - pub fn error(&self) -> Option<&ServiceProbeError> { - self.error.as_ref() - } - - /// Extract the error, consuming the result - pub fn into_error(self) -> Option { - self.error - } -} - -#[derive(Clone, Debug, PartialEq)] -pub enum ServiceProbeError { - ConnectionError(String), - WriteError(String), - ReadError(String), - TlsError(String), - CustomError(String), -} - -pub(crate) fn parse_hostscan_result( - packets: Vec, - scan_setting: HostScanSetting, -) -> ScanResult { - let mut result: ScanResult = ScanResult::new(); - let iface: Interface = match crate::interface::get_interface_by_index(scan_setting.if_index) { - Some(iface) => iface, - None => return ScanResult::error("Interface not found".to_string()), - }; - let iface_ips: HashSet = crate::interface::get_local_ips(scan_setting.if_index); - for p in packets { - let mac_addr: MacAddr; - if let Some(ethernet_frame) = &p.ethernet_header { - if ethernet_frame.destination != iface.mac_addr.unwrap_or(MacAddr::zero()) { - continue; - } - mac_addr = ethernet_frame.source; - } else { - mac_addr = MacAddr::zero(); - } - let mut ports: Vec = vec![]; - match scan_setting.scan_type { - HostScanType::IcmpPingScan => { - if p.icmp_header.is_none() && p.icmpv6_header.is_none() { - continue; - } - } - HostScanType::TcpPingScan => { - if p.tcp_header.is_none() { - continue; - } - if let Some(tcp_packet) = &p.tcp_header { - if tcp_packet.flags == TcpFlags::SYN | TcpFlags::ACK { - let port_info: Port = Port { - number: tcp_packet.source, - status: PortStatus::Open, - service_name: String::new(), - service_version: String::new(), - }; - ports.push(port_info); - } else if tcp_packet.flags == TcpFlags::RST | TcpFlags::ACK { - let port_info: Port = Port { - number: tcp_packet.source, - status: PortStatus::Closed, - service_name: String::new(), - service_version: String::new(), - }; - ports.push(port_info); - } else { - continue; - } - } else { - continue; - } - } - HostScanType::UdpPingScan => { - if p.icmp_header.is_none() && p.icmp_header.is_none() { - continue; - } - } - } - let host_info: Host = if let Some(ipv4_packet) = &p.ipv4_header { - Host { - ip_addr: IpAddr::V4(ipv4_packet.source), - hostname: scan_setting - .dns_map - .get(&IpAddr::V4(ipv4_packet.source)) - .unwrap_or(&String::new()) - .clone(), - ports: ports, - mac_addr: if iface_ips.contains(&IpAddr::V4(ipv4_packet.source)) { - iface.mac_addr.unwrap_or(MacAddr::zero()) - } else { - mac_addr - }, - vendor_name: String::new(), - os_family: String::new(), - ttl: ipv4_packet.ttl, - } - } else if let Some(ipv6_packet) = &p.ipv6_header { - Host { - ip_addr: IpAddr::V6(ipv6_packet.source), - hostname: scan_setting - .dns_map - .get(&IpAddr::V6(ipv6_packet.source)) - .unwrap_or(&String::new()) - .clone(), - ports: ports, - mac_addr: if iface_ips.contains(&IpAddr::V6(ipv6_packet.source)) { - iface.mac_addr.unwrap_or(MacAddr::zero()) - } else { - mac_addr - }, - vendor_name: String::new(), - os_family: String::new(), - ttl: ipv6_packet.hop_limit, - } - } else { - continue; - }; - if !result.hosts.contains(&host_info) { - result.hosts.push(host_info); - result.fingerprints.push(p.clone()); - } - } - return result; -} - -pub(crate) fn parse_portscan_result( - packets: Vec, - scan_setting: PortScanSetting, -) -> ScanResult { - let mut result: ScanResult = ScanResult::new(); - let mut socket_set: HashSet = HashSet::new(); - let iface: Interface = match crate::interface::get_interface_by_index(scan_setting.if_index) { - Some(iface) => iface, - None => return ScanResult::error("Interface not found".to_string()), - }; - for p in packets { - if p.ipv4_header.is_none() && p.ipv6_header.is_none() { - continue; - } - let mac_addr: MacAddr; - if let Some(ethernet_frame) = &p.ethernet_header { - if ethernet_frame.destination != iface.mac_addr.unwrap_or(MacAddr::zero()) { - continue; - } - mac_addr = ethernet_frame.source; - } else { - mac_addr = MacAddr::zero(); - } - let ip_addr: IpAddr = { - if let Some(ipv4_packet) = &p.ipv4_header { - if let Some(tcp_packet) = &p.tcp_header { - if socket_set.contains(&SocketAddr::new( - IpAddr::V4(ipv4_packet.source), - tcp_packet.source, - )) { - continue; - } - } else { - continue; - } - IpAddr::V4(ipv4_packet.source) - } else if let Some(ipv6_packet) = &p.ipv6_header { - if let Some(tcp_packet) = &p.tcp_header { - if socket_set.contains(&SocketAddr::new( - IpAddr::V6(ipv6_packet.source), - tcp_packet.source, - )) { - continue; - } - } else { - continue; - } - IpAddr::V6(ipv6_packet.source) - } else { - continue; - } - }; - let ttl = if let Some(ipv4_packet) = &p.ipv4_header { - ipv4_packet.ttl - } else if let Some(ipv6_packet) = &p.ipv6_header { - ipv6_packet.hop_limit - } else { - 0 - }; - let port_info: Port = if let Some(tcp_packet) = &p.tcp_header { - if tcp_packet.flags == TcpFlags::SYN | TcpFlags::ACK { - Port { - number: tcp_packet.source, - status: PortStatus::Open, - service_name: String::new(), - service_version: String::new(), - } - } else if tcp_packet.flags == TcpFlags::RST | TcpFlags::ACK { - Port { - number: tcp_packet.source, - status: PortStatus::Closed, - service_name: String::new(), - service_version: String::new(), - } - } else { - continue; - } - } else { - continue; - }; - let mut exists: bool = false; - for host in result.hosts.iter_mut() { - if host.ip_addr == ip_addr { - host.ports.push(port_info.clone()); - exists = true; - } - } - if !exists { - let host_info: Host = Host { - ip_addr: ip_addr, - hostname: scan_setting - .dns_map - .get(&ip_addr) - .unwrap_or(&String::new()) - .clone(), - ports: vec![port_info.clone()], - mac_addr: mac_addr, - vendor_name: String::new(), - os_family: String::new(), - ttl: ttl, - }; - result.hosts.push(host_info); - } - result.fingerprints.push(p.clone()); - socket_set.insert(SocketAddr::new(ip_addr, port_info.number)); - } - result -} diff --git a/src/scan/scanner.rs b/src/scan/scanner.rs deleted file mode 100644 index 473f9ab..0000000 --- a/src/scan/scanner.rs +++ /dev/null @@ -1,122 +0,0 @@ -use crate::host::Host; -use crate::scan::setting::{HostScanSetting, PortScanSetting}; -use std::collections::HashMap; -use std::net::SocketAddr; -use std::sync::mpsc::{channel, Receiver, Sender}; -use std::sync::{Arc, Mutex}; - -use super::async_io; -use super::blocking; -use super::result::{ScanResult, ServiceProbeResult}; -use super::setting::ServiceProbeSetting; - -/// Host Scanner -#[derive(Clone, Debug)] -pub struct HostScanner { - /// Scan Setting - pub scan_setting: HostScanSetting, - /// Sender for progress messaging - pub tx: Arc>>, - /// Receiver for progress messaging - pub rx: Arc>>, -} - -impl HostScanner { - /// Create new HostScanner - pub fn new(scan_setting: HostScanSetting) -> Self { - let (tx, rx) = channel(); - Self { - scan_setting, - tx: Arc::new(Mutex::new(tx)), - rx: Arc::new(Mutex::new(rx)), - } - } - /// Get progress receiver - pub fn get_progress_receiver(&self) -> Arc>> { - self.rx.clone() - } - // Scan hosts - pub fn scan(&self) -> ScanResult { - if self.scan_setting.async_scan { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(async_io::scan_hosts(self.scan_setting.clone(), &self.tx)) - } else { - blocking::scan_hosts(self.scan_setting.clone(), &self.tx) - } - } -} - -/// Port Scanner -#[derive(Clone, Debug)] -pub struct PortScanner { - /// Scan Setting - pub scan_setting: PortScanSetting, - /// Sender for progress messaging - pub tx: Arc>>, - /// Receiver for progress messaging - pub rx: Arc>>, -} - -impl PortScanner { - /// Create new PortScanner - pub fn new(scan_setting: PortScanSetting) -> Self { - let (tx, rx) = channel(); - Self { - scan_setting, - tx: Arc::new(Mutex::new(tx)), - rx: Arc::new(Mutex::new(rx)), - } - } - /// Get progress receiver - pub fn get_progress_receiver(&self) -> Arc>> { - self.rx.clone() - } - /// Scan ports - pub fn scan(&self) -> ScanResult { - match self.scan_setting.scan_type { - crate::scan::setting::PortScanType::TcpSynScan => { - if self.scan_setting.async_scan { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(async_io::scan_ports(self.scan_setting.clone(), &self.tx)) - } else { - blocking::scan_ports(self.scan_setting.clone(), &self.tx) - } - } - crate::scan::setting::PortScanType::TcpConnectScan => { - async_io::run_connect_scan(self.scan_setting.clone(), &self.tx) - } - } - } -} - -/// Struct for service detection -#[derive(Clone, Debug)] -pub struct ServiceDetector { - /// Probe setting for service detection - pub setting: ServiceProbeSetting, - /// Sender for progress messaging - pub tx: Arc>>, - /// Receiver for progress messaging - pub rx: Arc>>, -} - -impl ServiceDetector { - /// Create new ServiceDetector - pub fn new(setting: ServiceProbeSetting) -> Self { - let (tx, rx) = channel(); - Self { - setting, - tx: Arc::new(Mutex::new(tx)), - rx: Arc::new(Mutex::new(rx)), - } - } - /// Get progress receiver - pub fn get_progress_receiver(&self) -> Arc>> { - self.rx.clone() - } - /// Run service detection - pub fn run(&self) -> HashMap { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(super::service::run_service_probe(&self.setting, &self.tx)) - } -} diff --git a/src/scan/service.rs b/src/scan/service.rs deleted file mode 100644 index 1fc2cdb..0000000 --- a/src/scan/service.rs +++ /dev/null @@ -1,428 +0,0 @@ -use super::payload::{PayloadInfo, PayloadType}; -use super::result::{ServiceProbeError, ServiceProbeResult}; -use super::setting::ServiceProbeSetting; -use crate::db::tcp_service::PORT_SERVICE_MAP; -use futures::stream::{self, StreamExt}; -use std::collections::HashMap; -use std::net::{IpAddr, SocketAddr}; -use std::sync::mpsc::Sender; -use std::sync::{Arc, Mutex}; -use std::time::Duration; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use tokio::net::TcpStream; -use tokio_rustls::TlsConnector; - -/// Parse HTTP header and return server name -/// -/// The server name possibly contains version number. -fn parse_http_header(res_bytes: &Vec) -> Option { - let res_string: String = res_bytes.iter().map(|&c| c as char).collect(); - let header_fields: Vec<&str> = res_string.split("\r\n").collect(); - if header_fields.len() == 1 { - if res_string.contains("Server:") { - return Some(res_string); - } else { - return None; - } - } - for field in header_fields { - if field.contains("Server:") { - let server_info: String = field.trim().to_string(); - return Some(server_info); - } - } - None -} - -/// Read to end and return response as Vec -/// This ignore io::Error on read_to_end because it is expected when reading response. -/// If no response is received, and io::Error is occurred, return Err. -async fn read_response_timeout( - tcp_stream: &mut TcpStream, - timeout_duration: Duration, -) -> std::io::Result> { - let mut response = Vec::new(); - let mut buf = [0u8; 1024]; - - loop { - match tokio::time::timeout(timeout_duration, tcp_stream.read(&mut buf)).await { - Ok(Ok(0)) => break, - Ok(Ok(n)) => { - response.extend_from_slice(&buf[..n]); - break; - } - Ok(Err(e)) => return Err(e), - Err(_) => break, - } - } - - if response.is_empty() { - Err(std::io::Error::new( - std::io::ErrorKind::TimedOut, - "No response", - )) - } else { - Ok(response) - } -} - -fn set_read_timeout(tcp_stream: TcpStream, timeout: Duration) -> std::io::Result { - // Convert to std::net::TcpStream - let std_tcp_stream = tcp_stream.into_std()?; - // Set read timeout - std_tcp_stream.set_read_timeout(Some(timeout))?; - // Convert back to tokio TcpStream - let tokio_tcp_stream = TcpStream::from_std(std_tcp_stream)?; - Ok(tokio_tcp_stream) -} - -async fn probe_port( - ip_addr: IpAddr, - hostname: String, - port: u16, - payload_info: Option, - timeout: Duration, -) -> ServiceProbeResult { - let service_name: String = match PORT_SERVICE_MAP.get(&port) { - Some(name) => name.to_string(), - None => String::new(), - }; - let socket_addr: SocketAddr = SocketAddr::new(ip_addr, port); - let tcp_stream = match tokio::time::timeout(timeout, TcpStream::connect(socket_addr)).await { - Ok(connect_result) => match connect_result { - Ok(tcp_stream) => tcp_stream, - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ConnectionError(e.to_string()), - ) - } - }, - Err(elapsed) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ConnectionError(elapsed.to_string()), - ) - } - }; - // Set read timeout - let mut tcp_stream = match set_read_timeout(tcp_stream, timeout) { - Ok(tcp_stream) => tcp_stream, - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ConnectionError(e.to_string()), - ) - } - }; - if let Some(payload) = payload_info { - match payload.payload_type { - PayloadType::Http => match tcp_stream.write_all(&payload.payload).await { - Ok(_) => { - match tcp_stream.flush().await { - Ok(_) => {} - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::WriteError(e.to_string()), - ) - } - } - match read_response_timeout(&mut tcp_stream, timeout).await { - Ok(res) => { - let mut result = - ServiceProbeResult::new(port, service_name, res.clone()); - result.service_detail = parse_http_header(&res); - return result; - } - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ReadError(e.to_string()), - ) - } - } - } - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::WriteError(e.to_string()), - ) - } - }, - PayloadType::Https => { - let native_certs = crate::tls::cert::get_native_certs().unwrap(); - let config = rustls::ClientConfig::builder() - .with_root_certificates(native_certs) - .with_no_client_auth(); - let tls_connector = TlsConnector::from(Arc::new(config)); - let name = match rustls_pki_types::ServerName::try_from(hostname) { - Ok(name) => name, - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ConnectionError(e.to_string()), - ) - } - }; - let mut tls_stream = - match tokio::time::timeout(timeout, tls_connector.connect(name, tcp_stream)) - .await - { - Ok(connect_result) => match connect_result { - Ok(tls_stream) => tls_stream, - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ConnectionError(e.to_string()), - ) - } - }, - Err(elapsed) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ConnectionError(elapsed.to_string()), - ) - } - }; - match tls_stream.write_all(&payload.payload).await { - Ok(_) => { - match tls_stream.flush().await { - Ok(_) => {} - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::WriteError(e.to_string()), - ) - } - } - let mut buf: Vec = Vec::new(); - match tls_stream.read_to_end(&mut buf).await { - Ok(_) => { - let mut result = - ServiceProbeResult::new(port, service_name, buf.clone()); - result.service_detail = parse_http_header(&buf); - return result; - } - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ReadError(e.to_string()), - ) - } - } - } - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::WriteError(e.to_string()), - ) - } - } - } - PayloadType::Common => match tcp_stream.write_all(&payload.payload).await { - Ok(_) => { - match tcp_stream.flush().await { - Ok(_) => {} - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::WriteError(e.to_string()), - ) - } - } - match read_response_timeout(&mut tcp_stream, timeout).await { - Ok(res) => { - let mut result = - ServiceProbeResult::new(port, service_name, res.clone()); - result.service_detail = Some( - String::from_utf8(res) - .unwrap_or(String::new()) - .replace("\r\n", ""), - ); - return result; - } - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ReadError(e.to_string()), - ) - } - } - } - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::WriteError(e.to_string()), - ) - } - }, - PayloadType::CommonTls => { - let native_certs = crate::tls::cert::get_native_certs().unwrap(); - let config = rustls::ClientConfig::builder() - .with_root_certificates(native_certs) - .with_no_client_auth(); - let tls_connector = TlsConnector::from(Arc::new(config)); - let name = match rustls_pki_types::ServerName::try_from(hostname) { - Ok(name) => name, - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ConnectionError(e.to_string()), - ) - } - }; - let mut tls_stream = - match tokio::time::timeout(timeout, tls_connector.connect(name, tcp_stream)) - .await - { - Ok(connect_result) => match connect_result { - Ok(tls_stream) => tls_stream, - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ConnectionError(e.to_string()), - ) - } - }, - Err(elapsed) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ConnectionError(elapsed.to_string()), - ) - } - }; - match tls_stream.write_all(&payload.payload).await { - Ok(_) => { - match tls_stream.flush().await { - Ok(_) => {} - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::WriteError(e.to_string()), - ) - } - } - let mut buf: Vec = Vec::new(); - match tls_stream.read_to_end(&mut buf).await { - Ok(_) => { - let mut result = - ServiceProbeResult::new(port, service_name, buf.clone()); - result.service_detail = Some( - String::from_utf8(buf).unwrap_or(String::new()).to_string(), - ); - return result; - } - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ReadError(e.to_string()), - ) - } - } - } - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::WriteError(e.to_string()), - ) - } - } - } - PayloadType::Null => match read_response_timeout(&mut tcp_stream, timeout).await { - Ok(res) => { - let mut result = ServiceProbeResult::new(port, service_name, res.clone()); - result.service_detail = Some( - String::from_utf8(res) - .unwrap_or(String::new()) - .replace("\r\n", ""), - ); - return result; - } - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ReadError(e.to_string()), - ) - } - }, - } - } else { - match read_response_timeout(&mut tcp_stream, timeout).await { - Ok(res) => { - let mut result = ServiceProbeResult::new(port, service_name, res.clone()); - result.service_detail = Some( - String::from_utf8(res) - .unwrap_or(String::new()) - .replace("\r\n", ""), - ); - return result; - } - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ReadError(e.to_string()), - ) - } - } - } -} - -pub async fn run_service_probe( - setting: &ServiceProbeSetting, - ptx: &Arc>>, -) -> HashMap { - let service_map: Arc>> = - Arc::new(Mutex::new(HashMap::new())); - let fut_port = - stream::iter(setting.clone().ports).for_each_concurrent(setting.concurrent_limit, |port| { - let c_service_map: Arc>> = - Arc::clone(&service_map); - async move { - let ip_addr = setting.ip_addr; - let hostname = setting.hostname.clone(); - let probe_result: ServiceProbeResult = probe_port( - ip_addr, - hostname, - port, - setting.payload_map.get(&port).cloned(), - setting.read_timeout, - ) - .await; - c_service_map.lock().unwrap().insert(port, probe_result); - match ptx.lock() { - Ok(lr) => match lr.send(SocketAddr::new(ip_addr, port)) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - } - }); - fut_port.await; - let result_map: HashMap = service_map.lock().unwrap().clone(); - result_map -} diff --git a/src/scan/setting.rs b/src/scan/setting.rs deleted file mode 100644 index 75a07b7..0000000 --- a/src/scan/setting.rs +++ /dev/null @@ -1,376 +0,0 @@ -use crate::host::Host; -use crate::protocol::Protocol; -use crate::scan::payload::PayloadBuilder; -use rand::seq::SliceRandom; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::net::{IpAddr, Ipv4Addr}; -use std::time::Duration; - -use crate::config::DEFAULT_PORTS_CONCURRENCY; - -use super::payload::PayloadInfo; - -/* /// Scan Type -#[derive(Deserialize, Serialize, Clone, Debug)] -pub enum ScanType { - /// Port scan type. - PortScan(PortScanType), - /// Host scan type. - HostScan(HostScanType), -} - */ - -/// Port Scan Type -#[derive(Deserialize, Serialize, Clone, Debug)] -pub enum PortScanType { - /// Default fast port scan type. - /// - /// Send TCP packet with SYN flag to the target ports and check response. - TcpSynScan, - /// Attempt TCP connection and check port status. - /// - /// Slow but can be run without administrator privileges. - TcpConnectScan, -} - -impl PortScanType { - pub fn from_str(scan_type: &str) -> PortScanType { - match scan_type { - "SYN" | "TCP-SYN" | "TCP_SYN" => PortScanType::TcpSynScan, - "CONNECT" | "TCP-CONNECT" | "TCP_CONNECT" => PortScanType::TcpConnectScan, - _ => PortScanType::TcpSynScan, - } - } - pub fn to_str(&self) -> &str { - match self { - PortScanType::TcpSynScan => "TCP-SYN", - PortScanType::TcpConnectScan => "TCP-CONNECT", - } - } -} - -#[derive(Deserialize, Serialize, Clone, Debug)] -pub struct PortScanSetting { - pub if_index: u32, - pub targets: Vec, - pub protocol: Protocol, - pub scan_type: PortScanType, - pub concurrency: usize, - pub timeout: Duration, - pub wait_time: Duration, - pub send_rate: Duration, - pub randomize: bool, - pub minimize_packet: bool, - pub dns_map: HashMap, - pub async_scan: bool, -} - -impl Default for PortScanSetting { - fn default() -> Self { - Self { - if_index: 0, - targets: Vec::new(), - protocol: Protocol::TCP, - scan_type: PortScanType::TcpSynScan, - concurrency: DEFAULT_PORTS_CONCURRENCY, - timeout: Duration::from_secs(30), - wait_time: Duration::from_secs(200), - send_rate: Duration::from_millis(0), - randomize: true, - minimize_packet: false, - dns_map: HashMap::new(), - async_scan: false, - } - } -} - -impl PortScanSetting { - // support builder pattern for all fields - pub fn set_if_index(mut self, if_index: u32) -> Self { - self.if_index = if_index; - self - } - pub fn add_target(mut self, target: Host) -> Self { - self.targets.push(target); - self - } - pub fn set_targets(mut self, targets: Vec) -> Self { - self.targets = targets; - self - } - pub fn set_protocol(mut self, protocol: Protocol) -> Self { - self.protocol = protocol; - self - } - pub fn set_scan_type(mut self, scan_type: PortScanType) -> Self { - self.scan_type = scan_type; - self - } - pub fn set_concurrency(mut self, concurrency: usize) -> Self { - self.concurrency = concurrency; - self - } - pub fn set_timeout(mut self, timeout: Duration) -> Self { - self.timeout = timeout; - self - } - pub fn set_wait_time(mut self, wait_time: Duration) -> Self { - self.wait_time = wait_time; - self - } - pub fn set_send_rate(mut self, send_rate: Duration) -> Self { - self.send_rate = send_rate; - self - } - pub fn set_randomize(mut self, randomize: bool) -> Self { - self.randomize = randomize; - self - } - pub fn set_minimize_packet(mut self, minimize_packet: bool) -> Self { - self.minimize_packet = minimize_packet; - self - } - pub fn set_dns_map(mut self, dns_map: HashMap) -> Self { - self.dns_map = dns_map; - self - } - pub fn set_async_scan(mut self, async_scan: bool) -> Self { - self.async_scan = async_scan; - self - } - pub fn randomize_hosts(&mut self) { - let mut rng = rand::thread_rng(); - self.targets.shuffle(&mut rng); - } - pub fn randomize_ports(&mut self) { - for target in &mut self.targets { - target.ports.shuffle(&mut rand::thread_rng()); - } - } -} - -/// Host Scan Type -#[derive(Deserialize, Serialize, Clone, Debug)] -pub enum HostScanType { - /// Default host scan type. - /// - /// Send ICMP echo request and check response. - IcmpPingScan, - /// Perform host scan for a specific service. - /// - /// Send TCP packets with SYN flag to a specific port and check response. - TcpPingScan, - /// Send UDP packets to a probably closed port and check response. - /// This expects ICMP port unreachable message. - UdpPingScan, -} - -impl HostScanType { - pub fn from_str(scan_type: &str) -> HostScanType { - match scan_type { - "ICMP" | "ICMP-PING" | "ICMP_PING" => HostScanType::IcmpPingScan, - "TCP" | "TCP-PING" | "TCP_PING" => HostScanType::TcpPingScan, - "UDP" | "UDP-PING" | "UDP_PING" => HostScanType::UdpPingScan, - _ => HostScanType::IcmpPingScan, - } - } - pub fn to_str(&self) -> &str { - match self { - HostScanType::IcmpPingScan => "ICMP-PING", - HostScanType::TcpPingScan => "TCP-PING", - HostScanType::UdpPingScan => "UDP-PING", - } - } -} - -#[derive(Deserialize, Serialize, Clone, Debug)] -pub struct HostScanSetting { - pub if_index: u32, - pub targets: Vec, - pub protocol: Protocol, - pub scan_type: HostScanType, - pub concurrency: usize, - pub timeout: Duration, - pub wait_time: Duration, - pub send_rate: Duration, - pub randomize: bool, - pub minimize_packet: bool, - pub dns_map: HashMap, - pub async_scan: bool, -} - -impl Default for HostScanSetting { - fn default() -> Self { - Self { - if_index: 0, - targets: Vec::new(), - protocol: Protocol::ICMP, - scan_type: HostScanType::IcmpPingScan, - concurrency: DEFAULT_PORTS_CONCURRENCY, - timeout: Duration::from_secs(30), - wait_time: Duration::from_secs(200), - send_rate: Duration::from_millis(0), - randomize: true, - minimize_packet: false, - dns_map: HashMap::new(), - async_scan: false, - } - } -} - -impl HostScanSetting { - // support builder pattern for all fields - pub fn set_if_index(mut self, if_index: u32) -> Self { - self.if_index = if_index; - self - } - pub fn set_targets(mut self, targets: Vec) -> Self { - self.targets = targets; - self - } - pub fn set_protocol(mut self, protocol: Protocol) -> Self { - self.protocol = protocol; - self - } - pub fn set_scan_type(mut self, scan_type: HostScanType) -> Self { - self.scan_type = scan_type; - self - } - pub fn set_concurrency(mut self, concurrency: usize) -> Self { - self.concurrency = concurrency; - self - } - pub fn set_timeout(mut self, timeout: Duration) -> Self { - self.timeout = timeout; - self - } - pub fn set_wait_time(mut self, wait_time: Duration) -> Self { - self.wait_time = wait_time; - self - } - pub fn set_send_rate(mut self, send_rate: Duration) -> Self { - self.send_rate = send_rate; - self - } - pub fn set_randomize(mut self, randomize: bool) -> Self { - self.randomize = randomize; - self - } - pub fn set_minimize_packet(mut self, minimize_packet: bool) -> Self { - self.minimize_packet = minimize_packet; - self - } - pub fn set_dns_map(mut self, dns_map: HashMap) -> Self { - self.dns_map = dns_map; - self - } - pub fn add_target(mut self, target: Host) -> Self { - self.targets.push(target); - self - } - pub fn set_async_scan(mut self, async_scan: bool) -> Self { - self.async_scan = async_scan; - self - } - pub fn randomize_hosts(&mut self) { - let mut rng = rand::thread_rng(); - self.targets.shuffle(&mut rng); - } - pub fn randomize_ports(&mut self) { - for target in &mut self.targets { - target.ports.shuffle(&mut rand::thread_rng()); - } - } -} - -/// Probe setting for service detection -#[derive(Clone, Debug)] -pub struct ServiceProbeSetting { - /// Destination IP address - pub ip_addr: IpAddr, - /// Destination Host Name - pub hostname: String, - /// Target ports for service detection - pub ports: Vec, - /// TCP connect (open) timeout - pub connect_timeout: Duration, - /// TCP read timeout - pub read_timeout: Duration, - /// SSL/TLS certificate validation when detecting HTTPS services. - /// - /// Default value is false, which means validation is enabled. - pub accept_invalid_certs: bool, - /// Payloads for specified ports. - /// - /// If not set, default null probe will be used. (No payload, just open TCP connection and read response) - pub payload_map: HashMap, - /// Concurrent connection limit for service detection - pub concurrent_limit: usize, -} - -impl ServiceProbeSetting { - /// Create new ProbeSetting - pub fn new() -> ServiceProbeSetting { - ServiceProbeSetting { - ip_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), - hostname: String::new(), - ports: vec![], - connect_timeout: Duration::from_millis(200), - read_timeout: Duration::from_secs(5), - accept_invalid_certs: false, - payload_map: HashMap::new(), - concurrent_limit: 10, - } - } - pub fn default(ip_addr: IpAddr, hostname: String, ports: Vec) -> ServiceProbeSetting { - let mut payload_map: HashMap = HashMap::new(); - let http_head = PayloadBuilder::http_head(); - let https_head = PayloadBuilder::https_head(&hostname); - payload_map.insert(80, http_head.clone()); - payload_map.insert(443, https_head.clone()); - payload_map.insert(8080, http_head); - payload_map.insert(8443, https_head); - ServiceProbeSetting { - ip_addr: ip_addr, - hostname: hostname, - ports: ports, - connect_timeout: Duration::from_secs(1), - read_timeout: Duration::from_secs(5), - accept_invalid_certs: false, - payload_map: payload_map, - concurrent_limit: 10, - } - } - /// Set Destination IP address - pub fn with_ip_addr(&mut self, ip_addr: IpAddr) -> &mut Self { - self.ip_addr = ip_addr; - self - } - /// Set Destination Host Name. If IP address is not set, it will be resolved from the hostname. - pub fn with_hostname(&mut self, hostname: String) -> &mut Self { - self.hostname = hostname; - if self.ip_addr == IpAddr::V4(Ipv4Addr::LOCALHOST) - || self.ip_addr == IpAddr::V4(Ipv4Addr::UNSPECIFIED) - || self.ip_addr == IpAddr::V6(std::net::Ipv6Addr::LOCALHOST) - || self.ip_addr == IpAddr::V6(std::net::Ipv6Addr::UNSPECIFIED) - { - if let Some(ip_addr) = crate::dns::lookup_host_name(&self.hostname) { - self.ip_addr = ip_addr; - } - } - self - } - /// Add target port - pub fn add_port(&mut self, port: u16) { - self.ports.push(port); - } - /// Set connect (open) timeout in milliseconds - pub fn set_connect_timeout_millis(&mut self, connect_timeout_millis: u64) { - self.connect_timeout = Duration::from_millis(connect_timeout_millis); - } - /// Set TCP read timeout in milliseconds - pub fn set_read_timeout_millis(&mut self, read_timeout_millis: u64) { - self.read_timeout = Duration::from_millis(read_timeout_millis); - } -} diff --git a/src/service/mod.rs b/src/service/mod.rs new file mode 100644 index 0000000..82c4dea --- /dev/null +++ b/src/service/mod.rs @@ -0,0 +1,281 @@ +use std::time::Duration; +use regex::{Regex, RegexBuilder}; +use anyhow::{Result, bail}; +use futures::stream::{self, StreamExt}; +use tokio::{io::{AsyncRead, AsyncReadExt}, net::TcpStream, time::{timeout, Instant}}; +use tokio::sync::mpsc; +use tracing_indicatif::span_ext::IndicatifSpanExt; + +use crate::{endpoint::Endpoint, service::probe::{PortProbe, PortProbeResult, ProbeContext, ServiceProbe}}; + +pub mod probe; +mod payload; + +/// Configuration for service probing +#[derive(Clone,Debug)] +pub struct ServiceProbeConfig { + pub timeout: Duration, + pub max_concurrency: usize, + pub max_read_size: usize, + pub sni: bool, + pub skip_cert_verify: bool, +} + +/// Result of service detection on multiple endpoints +pub struct ServiceDetectionResult { + pub results: Vec, + pub scan_time: Duration, +} + +/// Service detector that runs probes against endpoints +pub struct ServiceDetector { + pub config: ServiceProbeConfig, +} + +impl ServiceDetector { + /// Create a new ServiceDetector with the given configuration + pub fn new(config: ServiceProbeConfig) -> Self { + ServiceDetector { + config + } + } + /// Detect services on the given endpoint using configured probes + pub async fn detect_services(config: ServiceProbeConfig, endpoint: Endpoint) -> Result> { + let port_probe_db = crate::db::service::port_probe_db(); + let service_probe_db = crate::db::service::service_probe_db(); + let (ch_tx, mut ch_rx) = mpsc::unbounded_channel::>>(); + + let header_span = tracing::info_span!("detect_services"); + header_span.pb_set_style(&crate::output::progress::get_progress_style()); + header_span.pb_set_message(&format!("Service Probe ({})", endpoint.ip)); + //header_span.pb_set_finish_message("Service Probe(task)(completed)"); + header_span.pb_set_length(endpoint.ports.len() as u64); + header_span.pb_set_position(0); + header_span.pb_start(); + //let header_span_enter = header_span.enter(); + + let mut results = Vec::new(); + let span_rx = header_span.clone(); + let recv_task = tokio::spawn(async move { + while let Some(port_results) = ch_rx.recv().await { + for res in port_results { + match res { + Ok(r) => results.push(r), + Err(e) => tracing::error!("Probe failed: {}", e), + } + } + // Update progress bar + span_rx.pb_inc(1); + } + results + }); + + let ports = endpoint.ports.clone(); + let prod = stream::iter(ports).for_each_concurrent(config.max_concurrency, move |port| { + let tx = ch_tx.clone(); + let endpoint = endpoint.clone(); + let port_probe_db = port_probe_db.clone(); + let service_probe_db = service_probe_db.clone(); + async move { + // Perform service detection for each endpoint + let mut results: Vec> = Vec::new(); + if let Some(probes) = port_probe_db.get(&port) { + for probe in probes { + let probe_payload = match service_probe_db.get(&probe) { + Some(payload) => payload, + None => { + results.push(Err(anyhow::anyhow!("No payload for probe {:?}", probe))); + continue; + } + }; + let port_probe: PortProbe = PortProbe { + probe_id: probe.clone(), + probe_name: probe_payload.id.clone(), + port: port.number, + transport: port.transport, + payload: probe_payload.payload.clone(), + payload_encoding: probe_payload.payload_encoding, + }; + let ctx = ProbeContext { + ip: endpoint.ip, + hostname: endpoint.hostname.clone(), + probe: port_probe, + timeout: config.timeout, + max_read_size: config.max_read_size, + sni: config.sni, + skip_cert_verify: config.skip_cert_verify, + }; + + let r = match probe { + ServiceProbe::TcpHTTPGet | ServiceProbe::TcpHTTPSGet | ServiceProbe::TcpHTTPOptions => { + probe::http::HttpProbe::run(ctx).await + }, + ServiceProbe::TcpTlsSession => { + probe::tls::TlsProbe::run(ctx).await + }, + ServiceProbe::TcpGenericLines | ServiceProbe::TcpHelp => { + probe::generic::GenericProbe::run(ctx).await + }, + ServiceProbe::UdpDNSVersionBindReq | ServiceProbe::TcpDNSVersionBindReq => { + probe::dns::DnsProbe::run(ctx).await + }, + ServiceProbe::UdpQuic => { + probe::quic::QuicProbe::run(ctx).await + }, + _ => { + probe::null::NullProbe::run(ctx).await + } + }; + results.push(r); + } + } else { + let ctx = ProbeContext { + ip: endpoint.ip, + hostname: endpoint.hostname.clone(), + probe: PortProbe::null_probe(port.number, port.transport), + timeout: config.timeout, + max_read_size: config.max_read_size, + sni: config.sni, + skip_cert_verify: config.skip_cert_verify, + }; + results.push(probe::null::NullProbe::run(ctx).await); + } + let _ = tx.send(results); + } + }); + + let prod_task = tokio::spawn(prod); + let (results_res, _prod_res) = tokio::join!(recv_task, prod_task); + let results = results_res?; + // Finish header span + drop(header_span); + Ok(results) + } + + pub async fn run_service_detection(&self, targets: Vec) -> Result { + let start_time = Instant::now(); + let mut tasks = vec![]; + for endpoint in targets { + let endpoint = endpoint.clone(); + let conf = self.config.clone(); + tasks.push(tokio::spawn(async move { + let probe_results = Self::detect_services( + conf, + endpoint + ) + .await; + probe_results + })); + } + let mut results: Vec = Vec::new(); + for task in tasks { + if let Ok(r) = task.await { + match r { + Ok(mut result) => { + // Merge results + results.append(&mut result); + }, + Err(e) => { + tracing::error!("Service detection failed: {}", e); + } + } + } + } + Ok(ServiceDetectionResult { + results, + scan_time: start_time.elapsed(), + }) + } +} + +pub fn set_read_timeout(tcp_stream: TcpStream, timeout: Duration) -> std::io::Result { + // Convert to std::net::TcpStream + let std_tcp_stream = tcp_stream.into_std()?; + // Set read timeout + std_tcp_stream.set_read_timeout(Some(timeout))?; + // Convert back to tokio TcpStream + let tokio_tcp_stream = TcpStream::from_std(std_tcp_stream)?; + Ok(tokio_tcp_stream) +} + +pub async fn read_timeout( + reader: &mut S, + idle_timeout: Duration, + total_timeout: Duration, + max_bytes: usize, +) -> Result> +where + S: AsyncRead + Unpin, +{ + let start = Instant::now(); + let mut buf = [0u8; 4096]; + let mut out = Vec::new(); + + loop { + // Check total timeout + let elapsed = start.elapsed(); + if elapsed >= total_timeout { + break; + } + let remaining_total = total_timeout - elapsed; + let wait = idle_timeout.min(remaining_total); + + match timeout(wait, reader.read(&mut buf)).await { + // Closed by peer + Ok(Ok(0)) => break, + // Data read + Ok(Ok(n)) => { + if out.len() > max_bytes { + bail!("response exceeded max_bytes ({} > {})", out.len(), max_bytes); + } + out.extend_from_slice(&buf[..n]); + + continue; + } + // Read error + Ok(Err(e)) => bail!("error reading response: {e}"), + // Idle timeout (no data received) + Err(_elapsed) => break, + } + } + + if out.is_empty() { + bail!("no response within time limits"); + } + Ok(out) +} + +// Build a regex with given pattern and flags +fn build_regex(pat: &str, flags: &str) -> anyhow::Result { + let mut b = RegexBuilder::new(pat); + b.case_insensitive(flags.contains('i')).dot_matches_new_line(flags.contains('s')); + //b.multi_line(true); + Ok(b.build()?) +} + +/// Build a regex for HTTP headers (multi-line, case-insensitive, dot matches new line) +fn build_http_regex(pat: &str) -> anyhow::Result { + Ok(RegexBuilder::new(pat) + .multi_line(true) + .case_insensitive(true) + .dot_matches_new_line(true) + .build()?) +} + +/// Expand CPE templates with regex capture groups +fn expand_cpe_templates(cpe_list: &[String], caps: ®ex::Captures) -> Vec { + let mut out = Vec::with_capacity(cpe_list.len()); + for t in cpe_list { + let mut s = t.clone(); + // Replace $1, $2, ... $99 with corresponding capture groups + for i in (1..=99).rev() { + let needle = format!("${}", i); + if s.contains(&needle) { + let repl = caps.get(i).map(|m| m.as_str()).unwrap_or(""); + s = s.replace(&needle, repl); + } + } + out.push(s); + } + out +} diff --git a/src/service/payload.rs b/src/service/payload.rs new file mode 100644 index 0000000..39e139a --- /dev/null +++ b/src/service/payload.rs @@ -0,0 +1,48 @@ +use anyhow::Result; +use base64::{engine::general_purpose, Engine as _}; +use crate::service::probe::{PayloadEncoding, PortProbe}; + +/// Context for building payloads +#[derive(Default, Clone)] +pub struct PayloadContext<'a> { + pub hostname: Option<&'a str>, + pub path: Option<&'a str>, +} + +/// Payload builder for service detection +pub struct PayloadBuilder { + pub probe: PortProbe, +} + +impl PayloadBuilder { + pub fn new(probe: PortProbe) -> Self { + PayloadBuilder { probe } + } + + /// Decode payload bytes (raw/base64). Returns error on decode failure. + pub fn payload(&self, ctx: PayloadContext) -> Result> { + match self.probe.payload_encoding { + PayloadEncoding::Raw => { + let mut s = self.probe.payload.clone(); + + if s.contains("$HOST") { + let host = ctx.hostname.ok_or_else(|| { + anyhow::anyhow!("probe {} requires hostname (found $HOST in payload)", self.probe.probe_id.as_str()) + })?; + s = s.replace("$HOST", host); + } + if s.contains("$PATH") { + let path = ctx.path.unwrap_or("/"); + s = s.replace("$PATH", path); + } + + Ok(s.into_bytes()) + } + PayloadEncoding::Base64 => { + general_purpose::STANDARD + .decode(&self.probe.payload) + .map_err(|e| anyhow::anyhow!("base64 decode failed for {}: {}", self.probe.probe_id.as_str(), e)) + } + } + } +} diff --git a/src/service/probe/dns.rs b/src/service/probe/dns.rs new file mode 100644 index 0000000..a987cab --- /dev/null +++ b/src/service/probe/dns.rs @@ -0,0 +1,253 @@ +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; + +use anyhow::Result; +use crate::{endpoint::ServiceInfo, service::{build_regex, expand_cpe_templates, probe::{PortProbeResult, ProbeContext, ServiceProbe}}}; + +use hickory_proto::{ + op::{Message, MessageType, OpCode, Query}, + rr::{DNSClass, Name, RecordType}, + serialize::binary::{BinEncodable, BinEncoder}, +}; +use tokio::{io::{AsyncReadExt, AsyncWriteExt}, net::{TcpStream, UdpSocket}}; + +/// Build a DNS query message for "version.bind" TXT record in CHAOS class. +fn build_version_bind_query() -> anyhow::Result> { + let mut msg = Message::new(); + msg.set_id(fastrand::u16(..)); + msg.set_message_type(MessageType::Query); + msg.set_op_code(OpCode::Query); + msg.set_recursion_desired(false); + + let name = Name::from_ascii("version.bind.")?; + let mut q = Query::query(name, RecordType::TXT); + // CHAOS class for version.bind + q.set_query_class(DNSClass::CH); + msg.add_query(q); + + let mut bytes = Vec::with_capacity(64); + let mut enc = BinEncoder::new(&mut bytes); + msg.emit(&mut enc)?; + Ok(bytes) +} + +/// Perform a DNS version.bind query over UDP. +async fn run_dns_version_bind_udp(addr: std::net::SocketAddr, idle: std::time::Duration, _total: std::time::Duration, max_bytes: usize) +-> anyhow::Result<(String, bool)> { + let q = build_version_bind_query()?; + let local = if addr.is_ipv6() { + SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 0) + } else { + SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0) + }; + let sock = UdpSocket::bind(local).await?; + sock.connect(addr).await?; + sock.send(&q).await?; + + // Receive response + let mut buf = vec![0u8; max_bytes.min(4096)]; + let n = tokio::time::timeout(idle, sock.recv(&mut buf)).await??; + buf.truncate(n); + + let msg = Message::from_vec(&buf)?; + let truncated = msg.truncated(); + + // Extract TXT record + let mut txt = String::new(); + for ans in msg.answers() { + if ans.record_type() == RecordType::TXT && ans.name().to_ascii().eq_ignore_ascii_case("version.bind.") { + if ans.dns_class() == DNSClass::CH { + if let hickory_proto::rr::RData::TXT(t) = ans.data() { + let joined = t.txt_data().iter().map(|b| String::from_utf8_lossy(b).to_string()) + .collect::>().join(""); + txt = joined; + break; + } + } + } + } + + if txt.is_empty() { + anyhow::bail!("no TXT answer for version.bind"); + } + Ok((txt, truncated)) +} + +/// Perform a DNS version.bind query over TCP. +async fn run_dns_version_bind_tcp(addr: std::net::SocketAddr, idle: std::time::Duration, total: std::time::Duration, max_bytes: usize) +-> anyhow::Result { + let mut stream = tokio::time::timeout(total, TcpStream::connect(addr)).await??; + + let q = build_version_bind_query()?; + let mut framed = Vec::with_capacity(q.len() + 2); + framed.extend_from_slice(&(q.len() as u16).to_be_bytes()); + framed.extend_from_slice(&q); + stream.write_all(&framed).await?; + stream.flush().await?; + + // Read the first 2 bytes for length + let mut lenbuf = [0u8; 2]; + tokio::time::timeout(idle, stream.read_exact(&mut lenbuf)).await??; + let want = u16::from_be_bytes(lenbuf) as usize; + if want > max_bytes { anyhow::bail!("dns/tcp response exceeds max_bytes"); } + + let mut buf = vec![0u8; want]; + tokio::time::timeout(idle, stream.read_exact(&mut buf)).await??; + + let msg = hickory_proto::op::Message::from_vec(&buf)?; + // Extract TXT record + for ans in msg.answers() { + if ans.record_type() == RecordType::TXT && ans.name().to_ascii().eq_ignore_ascii_case("version.bind.") { + if ans.dns_class() == DNSClass::CH { + if let hickory_proto::rr::RData::TXT(t) = ans.data() { + let joined = t.txt_data().iter().map(|b| String::from_utf8_lossy(b).to_string()) + .collect::>().join(""); + if !joined.is_empty() { return Ok(joined); } + } + } + } + } + anyhow::bail!("no TXT answer for version.bind over TCP"); +} + +/// Match response text against known service signatures. +/// (service, cpes) +fn match_response_signatures( + probe_id: &str, + text: &str, +) -> anyhow::Result)>> { + let sigdb = crate::db::service::response_signatures_db(); + let mut best_service: String = String::new(); + let mut cpes: Vec = Vec::new(); + for sig in sigdb { + if !sig.probe_id.eq_ignore_ascii_case(probe_id) { + continue; + } + + let re = match build_regex(&sig.regex, "") { + Ok(r) => r, + Err(_) => build_regex(&sig.regex, "i")?, + }; + if let Some(caps) = re.captures(text) { + if best_service.is_empty() && !sig.service.is_empty() { + best_service = sig.service.clone(); + } + let cs = expand_cpe_templates(&sig.cpe, &caps); + if !cs.is_empty() { + cpes.extend(cs); + // Generic CPEs can have multiple candidates, so it's okay to continue collecting + } + } + } + if best_service.is_empty() && cpes.is_empty() { + Ok(None) + } else { + Ok(Some((best_service, cpes))) + } +} + +/// A DNS probe that performs version.bind queries to identify DNS services. +pub struct DnsProbe; + +impl DnsProbe { + /// Run the DNS probe with the given context. + pub async fn run(ctx: ProbeContext) -> Result { + let addr = std::net::SocketAddr::new(ctx.ip, ctx.probe.port); + // Try UDP first + if matches!(ctx.probe.probe_id, ServiceProbe::UdpDNSVersionBindReq | ServiceProbe::TcpDNSVersionBindReq) { + tracing::debug!("DNS Version Bind Probe (UDP): {}:{}", ctx.ip, ctx.probe.port); + match run_dns_version_bind_udp(addr, ctx.timeout, ctx.timeout, ctx.max_read_size).await { + Ok((txt, truncated)) => { + let mut svc = ServiceInfo::default(); + let udp_svc_db = crate::db::service::udp_service_db(); + svc.name = udp_svc_db.get_name(ctx.probe.port).map(|s| s.to_string()); + svc.banner = Some(txt.clone()); + svc.raw = Some(txt.clone()); + // Match (using UDP-side) + let hits = match_response_signatures( + "udp:dns_version_bind_req", &txt + )?; + if let Some((best_service, cpes)) = hits { + svc.name = Some(best_service); + svc.cpes = cpes; + } + // If truncated, try TCP as well + if truncated { + if let Ok(txt2) = run_dns_version_bind_tcp(addr, ctx.timeout, ctx.timeout, ctx.max_read_size).await { + svc.raw = Some(txt2.clone()); + let hits2 = match_response_signatures( + "tcp:dns_version_bind_req", &txt2 + )?; + if let Some((best_service, cpes)) = hits2 { + svc.name = Some(best_service); + svc.cpes = cpes; + } + } + } + let probe_result: PortProbeResult = PortProbeResult { + ip: ctx.ip, + hostname: ctx.hostname, + port: ctx.probe.port, + transport: ctx.probe.transport, + probe_id: ctx.probe.probe_id, + service_info: svc, + }; + return Ok(probe_result); + } + Err(e) => { + tracing::debug!("DNS Version Bind Probe (UDP) failed: {}", e); + tracing::debug!("Attempting DNS Version Bind Probe (TCP): {}:{}", ctx.ip, ctx.probe.port); + let mut svc = ServiceInfo::default(); + let tcp_svc_db = crate::db::service::tcp_service_db(); + svc.name = tcp_svc_db.get_name(ctx.probe.port).map(|s| s.to_string()); + // If UDP failed, try TCP + if let Ok(txt) = run_dns_version_bind_tcp(addr, ctx.timeout, ctx.timeout, ctx.max_read_size).await { + svc.banner = Some(txt.clone()); + svc.raw = Some(txt.clone()); + let hits = match_response_signatures( + "tcp:dns_version_bind_req", &txt + )?; + if let Some((best_service, cpes)) = hits { + svc.name = Some(best_service); + svc.cpes = cpes; + } + } + let probe_result: PortProbeResult = PortProbeResult { + ip: ctx.ip, + hostname: ctx.hostname, + port: ctx.probe.port, + transport: ctx.probe.transport, + probe_id: ctx.probe.probe_id, + service_info: svc, + }; + return Ok(probe_result); + } + } + } + + // If UDP not selected or failed, and TCP is selected + if matches!(ctx.probe.probe_id, ServiceProbe::TcpDNSVersionBindReq) { + tracing::debug!("DNS Version Bind Probe (TCP): {}:{}", ctx.ip, ctx.probe.port); + let txt = run_dns_version_bind_tcp(addr, ctx.timeout, ctx.timeout, ctx.max_read_size).await?; + let mut svc = ServiceInfo::default(); + let tcp_svc_db = crate::db::service::tcp_service_db(); + svc.name = tcp_svc_db.get_name(ctx.probe.port).map(|s| s.to_string()); + svc.banner = Some(txt.clone()); + svc.raw = Some(txt.clone()); + let hits = match_response_signatures("tcp:dns_version_bind_req", &txt)?; + if let Some((best_service, cpes)) = hits { + svc.name = Some(best_service); + svc.cpes = cpes; + } + let probe_result: PortProbeResult = PortProbeResult { + ip: ctx.ip, + hostname: ctx.hostname, + port: ctx.probe.port, + transport: ctx.probe.transport, + probe_id: ctx.probe.probe_id, + service_info: svc, + }; + return Ok(probe_result); + } + anyhow::bail!("unsupported probe for dns version.bind") + } +} diff --git a/src/service/probe/generic.rs b/src/service/probe/generic.rs new file mode 100644 index 0000000..221169f --- /dev/null +++ b/src/service/probe/generic.rs @@ -0,0 +1,122 @@ +use std::net::SocketAddr; +use anyhow::Result; +use tokio::{io::{AsyncWriteExt}, net::TcpStream, time::timeout}; + +use crate::{ + endpoint::ServiceInfo, + service::{ + build_regex, expand_cpe_templates, payload::{PayloadBuilder, PayloadContext}, probe::{PortProbeResult, ProbeContext}, read_timeout + }, +}; + +#[derive(Debug, Default, Clone)] +struct BannerLite { + first_line: Option, + raw_text: String, +} + +/// First line (terminated by \r\n or \n) is treated as banner. +fn parse_banner(bytes: &[u8], max_preview: usize) -> BannerLite { + let raw = String::from_utf8_lossy(bytes); + let mut out = BannerLite::default(); + out.raw_text = if raw.len() > max_preview { + raw[..max_preview].to_string() + } else { + raw.to_string() + }; + let first = out.raw_text.split(|c| c == '\n').next().unwrap_or("").trim_end_matches('\r'); + if !first.is_empty() { + out.first_line = Some(first.to_string()); + } + out +} + +/// Match response text against known service signatures. +/// (service, cpes) +fn match_signatures( + probe_id: &str, + text: &str, +) -> anyhow::Result)>> { + let sigdb = crate::db::service::response_signatures_db(); + let mut best_service: String = String::new(); + let mut cpes: Vec = Vec::new(); + for sig in sigdb { + if !sig.probe_id.eq_ignore_ascii_case(probe_id) { + continue; + } + + let re = match build_regex(&sig.regex, "") { + Ok(r) => r, + Err(_) => build_regex(&sig.regex, "i")?, + }; + if let Some(caps) = re.captures(text) { + if best_service.is_empty() && !sig.service.is_empty() { + best_service = sig.service.clone(); + } + let cs = expand_cpe_templates(&sig.cpe, &caps); + if !cs.is_empty() { + cpes.extend(cs); + // Generic CPEs can have multiple candidates, so it's okay to continue collecting + } + } + } + Ok(Some((best_service, cpes))) +} + +/// A generic probe that connects to a TCP port, optionally sends a payload, and reads the response. +pub struct GenericProbe; + +impl GenericProbe { + /// Run the generic probe with the given context. + pub async fn run(ctx: ProbeContext) -> Result { + tracing::debug!("Generic Probe: {}:{} - Connecting", ctx.ip, ctx.probe.port); + let addr: SocketAddr = SocketAddr::new(ctx.ip, ctx.probe.port); + let mut stream = timeout(ctx.timeout, TcpStream::connect(addr)).await??; + + tracing::debug!("Generic Probe: {}:{} - Connected", ctx.ip, ctx.probe.port); + + // If payload is present, send it + let payload = PayloadBuilder::new(ctx.probe.clone()) + .payload(PayloadContext::default()) + .unwrap_or_default(); + if !payload.is_empty() { + timeout(ctx.timeout, stream.write_all(&payload)).await??; + stream.flush().await?; + } + + // Apply idle/total timeout + max byte limit for reading + let idle = ctx.timeout; + let total = ctx.timeout; + tracing::debug!("Generic Probe: {}:{} - Reading response(timeout: {})", ctx.ip, ctx.probe.port, total.as_millis()); + let bytes = read_timeout(&mut stream, idle, total, ctx.max_read_size).await?; + + // Extract banner + let banner = parse_banner(&bytes, 64 * 1024); + + tracing::debug!("Generic Probe: {}:{} - Banner: {:?}", ctx.ip, ctx.probe.port, banner.first_line); + + // Match signatures + let hit = match_signatures(ctx.probe.probe_id.as_str(), &banner.raw_text)?; + + // Build result + let mut svc = ServiceInfo::default(); + if let Some((service_name, cpes)) = hit { + svc.name = Some(service_name); + if !cpes.is_empty() { + svc.cpes = cpes; + } + } + // If name is still empty, keep banner + svc.banner = banner.first_line.clone(); + svc.raw = Some(banner.raw_text); + let probe_result: PortProbeResult = PortProbeResult { + ip: ctx.ip, + hostname: ctx.hostname, + port: ctx.probe.port, + transport: ctx.probe.transport, + probe_id: ctx.probe.probe_id, + service_info: svc, + }; + Ok(probe_result) + } +} diff --git a/src/service/probe/http.rs b/src/service/probe/http.rs new file mode 100644 index 0000000..361289a --- /dev/null +++ b/src/service/probe/http.rs @@ -0,0 +1,267 @@ +use std::{collections::HashMap, net::SocketAddr}; + +use anyhow::Result; +use rustls_pki_types::ServerName; +use tokio::{io::{AsyncWriteExt}, net::TcpStream, time::timeout}; +use tokio_rustls::{TlsConnector, rustls::{ClientConfig, RootCertStore}}; +use std::sync::Arc; + +use crate::{endpoint::ServiceInfo, service::{build_http_regex, expand_cpe_templates, payload::{PayloadBuilder, PayloadContext}, probe::{PortProbeResult, ProbeContext, ServiceProbe}, read_timeout}}; +use super::tls::SkipServerVerification; + +/// A lightweight representation of an HTTP response for analysis. +#[derive(Debug, Default, Clone)] +pub struct HttpResponseLite { + pub status_line: Option, + pub status_code: Option, + pub headers: HashMap, + pub header_text: String, + pub body: Vec, + pub raw_text: String, +} + +/// Parse raw bytes into an HttpResponseLite structure. +fn parse_http_response(bytes: &[u8], body_limit: usize) -> HttpResponseLite { + const HDR_MAX: usize = 64 * 1024; + let mut res = HttpResponseLite::default(); + + // Find the end of headers in the byte array (prefer \r\n\r\n, else try \n\n) + let hdr_end = bytes + .windows(4) + .position(|w| w == b"\r\n\r\n") + .map(|p| p + 4) + .or_else(|| bytes.windows(2).position(|w| w == b"\n\n").map(|p| p + 2)) + .unwrap_or_else(|| bytes.len().min(HDR_MAX)); + + let header_bytes = &bytes[..hdr_end.min(bytes.len())]; + let body_bytes = if hdr_end < bytes.len() { &bytes[hdr_end..] } else { &[][..] }; + + // Convert header bytes to a lossy UTF-8 string + let header_text = String::from_utf8_lossy(header_bytes); + res.header_text = header_text.to_string(); + + // Parse status line and headers into the structure. Try \r\n first, then \n if not found. + let mut lines = header_text.split("\r\n"); + if header_text.find("\r\n").is_none() { + lines = header_text.split("\n"); + } + + if let Some(first) = lines.next() { + let line = first.trim().to_string(); + res.status_line = Some(line.clone()); + if let Some(code) = line.split_whitespace().nth(1).and_then(|s| s.parse::().ok()) { + res.status_code = Some(code); + } + } + for line in lines { + if let Some((k, v)) = line.split_once(':') { + res.headers.insert(k.trim().to_ascii_lowercase(), v.trim().to_string()); + } + } + + // Limit body size to body_limit + let take = body_bytes.len().min(body_limit); + res.body.extend_from_slice(&body_bytes[..take]); + + // Construct raw_text as "headers + CRLFCRLF + first N bytes of body" + // for easier and safer regex matching later + let mut raw = header_text.into_owned(); + raw.push_str("\r\n\r\n"); + raw.push_str(&String::from_utf8_lossy(&body_bytes[..take])); + res.raw_text = raw; + + res +} + +/// Match HTTP response against known service signatures. +/// Returns matched CPEs if any. +fn match_http_signatures( + service_keys: &[&str], + _probe_id: &str, + http_res: &HttpResponseLite, +) -> anyhow::Result> { + let sigdb = crate::db::service::response_signatures_db(); + let mut hits = Vec::new(); + + 'outer: for sig in sigdb { + if !service_keys.iter().any(|k| sig.service.eq_ignore_ascii_case(k)) { + continue; + } + + /* if !sig.probe_id.is_empty() && !sig.probe_id.eq_ignore_ascii_case(probe_id) { + continue; + } */ + + let re = build_http_regex(&sig.regex)?; + + if let Some(caps) = re.captures(&http_res.header_text) { + let cpes = expand_cpe_templates(&sig.cpe, &caps); + if !cpes.is_empty() { + hits.extend(cpes); + break 'outer; + } + } + } + Ok(hits) +} + +/// An HTTP probe that can send HTTP/HTTPS requests and analyze responses. +pub struct HttpProbe; + +impl HttpProbe { + /// Run the HTTP probe with the given context. + pub async fn run(ctx: ProbeContext) -> Result { + let addr: SocketAddr = SocketAddr::new(ctx.ip, ctx.probe.port); + let hostname = ctx.hostname.clone().unwrap_or_else(|| ctx.ip.to_string()); + let mut tcp_stream = timeout(ctx.timeout, TcpStream::connect(addr)).await??; + let payload_builder = PayloadBuilder::new(ctx.probe.clone()); + let tcp_svc_db = crate::db::service::tcp_service_db(); + match ctx.probe.probe_id { + ServiceProbe::TcpHTTPGet => { + tracing::debug!("HTTP Probe: {}:{} - Sending HTTP GET", ctx.ip, ctx.probe.port); + let payload: Vec = payload_builder.payload(PayloadContext::default())?; + timeout(ctx.timeout, tcp_stream.write_all(&payload)).await??; + tcp_stream.flush().await?; + let res: Vec = read_timeout(&mut tcp_stream, ctx.timeout, ctx.timeout, ctx.max_read_size).await?; + let http_res = parse_http_response(&res, 64 * 1024); + tracing::debug!("HTTP Probe: {}:{} - Header: {:?}", ctx.ip, ctx.probe.port, http_res.header_text); + let mut svc = ServiceInfo::default(); + svc.name = tcp_svc_db.get_name(ctx.probe.port).map(|s| s.to_string()); + svc.banner = http_res.status_line.clone(); + svc.product = http_res.headers.get("server").cloned(); + svc.raw = Some(http_res.raw_text.clone()); + + tracing::debug!("HTTP Probe: {}:{} - Banner: {:?}, Server {:?}", ctx.ip, ctx.probe.port, svc.banner, svc.product); + + // Match signatures + let cpes = match_http_signatures( + &["http"], + "tcp:http_get", + &http_res, + )?; + if !cpes.is_empty() { + svc.cpes = cpes; + } + let probe_result: PortProbeResult = PortProbeResult { + ip: ctx.ip, + hostname: ctx.hostname, + port: ctx.probe.port, + transport: ctx.probe.transport, + probe_id: ctx.probe.probe_id, + service_info: svc, + }; + tracing::debug!("HTTP Probe Result: {:?}", probe_result); + return Ok(probe_result); + }, + ServiceProbe::TcpHTTPSGet => { + tracing::debug!("HTTP Probe: {}:{} - Sending HTTPS GET", ctx.ip, ctx.probe.port); + let payload_ctx = PayloadContext { + hostname: ctx.hostname.as_deref(), + path: Some("/".into()), + }; + let payload: Vec = payload_builder.payload(payload_ctx)?; + + // rustls config + let mut roots = RootCertStore::empty(); + for cert in rustls_native_certs::load_native_certs()? { let _ = roots.add(cert); } + let mut config = ClientConfig::builder() + .with_root_certificates(roots) + .with_no_client_auth(); + + // Set ALPN protocols + //config.alpn_protocols = vec!["h2".into(), "http/1.1".into()]; + config.alpn_protocols = vec!["http/1.1".into()]; + + if ctx.skip_cert_verify { + config.dangerous().set_certificate_verifier(SkipServerVerification::new()); + } + + let connector = TlsConnector::from(Arc::new(config)); + let sni_name = if ctx.sni { + ServerName::try_from(hostname)? + } else { + ServerName::try_from("localhost")? + }; + + let mut tls_stream = timeout(ctx.timeout, connector.connect(sni_name, tcp_stream)).await??; + // server connection + let conn = tls_stream.get_ref().1; + + let mut svc = ServiceInfo::default(); + svc.name = tcp_svc_db.get_name(ctx.probe.port).map(|s| s.to_string()); + + svc.tls_info = super::tls::extract_tls_info(&ctx, &conn); + + tls_stream.write_all(&payload).await?; + tls_stream.flush().await?; + let res: Vec = read_timeout(&mut tls_stream, ctx.timeout, ctx.timeout, ctx.max_read_size).await?; + let http_res = parse_http_response(&res, 64 * 1024); + tracing::debug!("HTTP Probe: {}:{} - Header: {:?}", ctx.ip, ctx.probe.port, http_res.header_text); + svc.banner = http_res.status_line.clone(); + svc.product = http_res.headers.get("server").cloned(); + svc.raw = Some(http_res.raw_text.clone()); + + tracing::debug!("HTTPS Probe: {}:{} - Banner: {:?}, Server {:?}", ctx.ip, ctx.probe.port, svc.banner, svc.product); + tracing::debug!("RAW: {:?}", svc.raw); + + // Match signatures + let cpes = match_http_signatures( + &["http"], + "tcp:https_get", + &http_res, + )?; + if !cpes.is_empty() { + svc.cpes = cpes; + } + let probe_result: PortProbeResult = PortProbeResult { + ip: ctx.ip, + hostname: ctx.hostname, + port: ctx.probe.port, + transport: ctx.probe.transport, + probe_id: ctx.probe.probe_id, + service_info: svc, + }; + tracing::debug!("HTTP Probe Result: {:?}", probe_result); + return Ok(probe_result); + }, + ServiceProbe::TcpHTTPOptions => { + tracing::debug!("HTTP Probe: {}:{} - Sending HTTP OPTIONS", ctx.ip, ctx.probe.port); + let payload: Vec = payload_builder.payload(PayloadContext::default())?; + timeout(ctx.timeout, tcp_stream.write_all(&payload)).await??; + tcp_stream.flush().await?; + let res: Vec = read_timeout(&mut tcp_stream, ctx.timeout, ctx.timeout, ctx.max_read_size).await?; + let http_res = parse_http_response(&res, 64 * 1024); + tracing::debug!("HTTP Probe: {}:{} - Header: {:?}", ctx.ip, ctx.probe.port, http_res.header_text); + let mut svc = ServiceInfo::default(); + svc.name = tcp_svc_db.get_name(ctx.probe.port).map(|s| s.to_string()); + svc.banner = http_res.status_line.clone(); + svc.product = http_res.headers.get("server").cloned(); + svc.raw = Some(http_res.raw_text.clone()); + + tracing::debug!("HTTP Probe: {}:{} - Banner: {:?}, Server {:?}", ctx.ip, ctx.probe.port, svc.banner, svc.product); + + // Match signatures + let cpes = match_http_signatures( + &["http"], + "tcp:http_options", + &http_res, + )?; + if !cpes.is_empty() { + svc.cpes = cpes; + } + let probe_result: PortProbeResult = PortProbeResult { + ip: ctx.ip, + hostname: ctx.hostname, + port: ctx.probe.port, + transport: ctx.probe.transport, + probe_id: ctx.probe.probe_id, + service_info: svc, + }; + tracing::debug!("HTTP Probe Result: {:?}", probe_result); + return Ok(probe_result); + } + _ => {}, + } + Err(anyhow::anyhow!("Failed to probe HTTP service at {}", addr)) + } +} diff --git a/src/service/probe/mod.rs b/src/service/probe/mod.rs new file mode 100644 index 0000000..05e75af --- /dev/null +++ b/src/service/probe/mod.rs @@ -0,0 +1,217 @@ +pub mod null; +pub mod generic; +pub mod http; +pub mod tls; +pub mod dns; +pub mod quic; + +use std::{collections::BTreeMap, net::IpAddr, time::Duration}; + +use serde::{Deserialize, Serialize}; + +use crate::{endpoint::{ServiceInfo, TransportProtocol}}; + +/// Metadata for the database +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Meta { + pub name: String, + pub version: String, +} + +/// Supported service probes +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] +pub enum ServiceProbe { + TcpNull, + TcpGenericLines, + TcpHTTPGet, + TcpHTTPSGet, + TcpHTTPOptions, + TcpDNSVersionBindReq, + TcpHelp, + TcpTlsSession, + UdpDNSVersionBindReq, + UdpQuic, +} + +impl ServiceProbe { + /// Convert the ServiceProbe enum to its string representation. + pub fn as_str(&self) -> &str { + match self { + ServiceProbe::TcpNull => "tcp:null", + ServiceProbe::TcpGenericLines => "tcp:generic_lines", + ServiceProbe::TcpHTTPGet => "tcp:http_get", + ServiceProbe::TcpHTTPSGet => "tcp:https_get", + ServiceProbe::TcpHTTPOptions => "tcp:http_options", + ServiceProbe::TcpDNSVersionBindReq => "tcp:dns_version_bind_req", + ServiceProbe::TcpHelp => "tcp:help", + ServiceProbe::TcpTlsSession => "tcp:tls_session", + ServiceProbe::UdpDNSVersionBindReq => "udp:dns_version_bind_req", + ServiceProbe::UdpQuic => "udp:quic", + } + } + /// Create a ServiceProbe enum from its string representation. + pub fn from_str(s: &str) -> Option { + match s { + "tcp:null" => Some(ServiceProbe::TcpNull), + "tcp:generic_lines" => Some(ServiceProbe::TcpGenericLines), + "tcp:http_get" => Some(ServiceProbe::TcpHTTPGet), + "tcp:https_get" => Some(ServiceProbe::TcpHTTPSGet), + "tcp:http_options" => Some(ServiceProbe::TcpHTTPOptions), + "tcp:dns_version_bind_req" => Some(ServiceProbe::TcpDNSVersionBindReq), + "tcp:help" => Some(ServiceProbe::TcpHelp), + "tcp:tls_session" => Some(ServiceProbe::TcpTlsSession), + "udp:dns_version_bind_req" => Some(ServiceProbe::UdpDNSVersionBindReq), + "udp:quic" => Some(ServiceProbe::UdpQuic), + _ => None, + } + } + /// Get the transport protocol associated with the ServiceProbe. + pub fn transport(&self) -> TransportProtocol { + match self { + ServiceProbe::TcpNull | ServiceProbe::TcpGenericLines | ServiceProbe::TcpHTTPGet + | ServiceProbe::TcpHTTPSGet | ServiceProbe::TcpHTTPOptions + | ServiceProbe::TcpDNSVersionBindReq | ServiceProbe::TcpHelp + | ServiceProbe::TcpTlsSession => TransportProtocol::Tcp, + ServiceProbe::UdpDNSVersionBindReq | ServiceProbe::UdpQuic => TransportProtocol::Udp, + } + } +} + +/// Encoding type for probe payloads +#[derive(Serialize, Deserialize, Clone, Copy, Debug)] +#[serde(rename_all = "lowercase")] +pub enum PayloadEncoding { + Raw, + Base64, +} + +/// Database mapping ports to associated probes +#[derive(Serialize, Deserialize)] +pub struct PortProbeDb { + pub meta: Meta, + // port -> probe_id[] + pub map: BTreeMap>, +} + +impl PortProbeDb { + /// Create a new empty PortProbeDb + pub fn new() -> Self { + PortProbeDb { + meta: Meta { + name: "Port Probe Database".into(), + version: "1.0".into(), + }, + map: BTreeMap::new(), + } + } +} + +/// Definition of a probe payload +#[derive(Serialize, Deserialize, Clone)] +pub struct ProbePayload { + pub id: String, + pub protocol: TransportProtocol, + pub name: String, + pub payload: String, + pub payload_encoding: PayloadEncoding, + pub wait_ms: Option, + pub ports: Vec, +} + +/// Database of probe payloads +#[derive(Serialize, Deserialize)] +pub struct ProbePayloadDb { + pub meta: Meta, + pub probes: Vec, +} + +impl ProbePayloadDb { + /// Create a new empty ProbePayloadDb + pub fn new() -> Self { + ProbePayloadDb { + meta: Meta { + name: "Probe Payload Database".into(), + version: "1.0".into(), + }, + probes: Vec::new(), + } + } +} + +/// Definition of a response signature for service identification +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ResponseSignature { + pub probe_id: String, + pub service: String, + pub regex: String, + pub regex_literal_tokens: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub cpe: Vec, +} + +/// Database of response signatures +#[derive(Serialize, Deserialize)] +pub struct ResponseSignaturesDb { + pub meta: Meta, + pub signatures: Vec, +} + +impl ResponseSignaturesDb { + /// Create a new empty SignaturesDb + pub fn new() -> Self { + ResponseSignaturesDb { + meta: Meta { + name: "Signatures Database".into(), + version: "1.0".into(), + }, + signatures: Vec::new(), + } + } +} + +/// Definition of a port probe +#[derive(Debug, Clone)] +pub struct PortProbe { + pub probe_id: ServiceProbe, + pub probe_name: String, + pub port: u16, + pub transport: TransportProtocol, + pub payload: String, // Raw or Base64 + pub payload_encoding: PayloadEncoding, +} + +impl PortProbe { + pub fn null_probe(port: u16, transport: TransportProtocol) -> Self { + PortProbe { + probe_id: ServiceProbe::TcpNull, + probe_name: "tcp:null".into(), + port, + transport, + payload: String::new(), + payload_encoding: PayloadEncoding::Raw, + } + } +} + +/// Context for running a probe against a target +#[derive(Debug, Clone)] +pub struct ProbeContext { + pub ip: IpAddr, + pub hostname: Option, + pub probe: PortProbe, + pub timeout: Duration, + pub max_read_size: usize, + pub sni: bool, + pub skip_cert_verify: bool, +} + +/// Result of running a probe against a target +#[derive(Debug, Clone)] +pub struct PortProbeResult { + pub ip: IpAddr, + pub hostname: Option, + pub port: u16, + pub transport: TransportProtocol, + pub probe_id: ServiceProbe, + pub service_info: ServiceInfo, +} diff --git a/src/service/probe/null.rs b/src/service/probe/null.rs new file mode 100644 index 0000000..8b5aaae --- /dev/null +++ b/src/service/probe/null.rs @@ -0,0 +1,144 @@ +use std::net::SocketAddr; +use anyhow::{bail, Result}; +use tokio::{io::{AsyncWriteExt}, net::TcpStream, time::timeout}; + +use crate::{ + endpoint::ServiceInfo, + service::{ + build_regex, expand_cpe_templates, payload::{PayloadBuilder, PayloadContext}, probe::{PortProbeResult, ProbeContext, ServiceProbe}, read_timeout + }, +}; + +/// Lightweight representation of a service banner +#[derive(Debug, Default, Clone)] +struct BannerLite { + first_line: Option, + raw_text: String, +} + +/// Determine if a string looks like a text line +fn looks_like_text_line(s: &str) -> bool { + let t = s.trim(); + if t.is_empty() { + return false; + } + // ASCII printable characters and whitespace ratio + let printable = t.chars().filter(|&c| c.is_ascii_graphic() || c.is_ascii_whitespace()).count(); + let ratio = printable as f32 / t.len() as f32; + ratio >= 0.6 +} + +/// Extract banner: +/// 1. If the first line looks like text, use it +/// 2. If not, use the second line if it exists +/// 3. If neither exists, use the first line (raw) +fn parse_banner(bytes: &[u8], max_preview: usize) -> BannerLite { + let raw = String::from_utf8_lossy(bytes); + let mut out = BannerLite::default(); + + out.raw_text = if raw.len() > max_preview { + raw[..max_preview].to_string() + } else { + raw.to_string() + }; + + let mut lines = out.raw_text.split(|c| c == '\n').map(|l| l.trim_end_matches('\r')); + + let first = lines.next().unwrap_or(""); + let second = lines.next().unwrap_or(""); + + if looks_like_text_line(first) { + out.first_line = Some(first.to_string()); + } else if !second.is_empty() { + out.first_line = Some(second.to_string()); + } else if !first.is_empty() { + out.first_line = Some(first.to_string()); + } + out +} + +/// Match response text against known service signatures for tcp:NULL probes. +/// (service, cpes) +fn match_null_signatures( + probe_id: &str, + text: &str, +) -> anyhow::Result)>> { + let sigdb = crate::db::service::response_signatures_db(); + for sig in sigdb { + if !sig.probe_id.eq_ignore_ascii_case(probe_id) { + continue; + } + let re = match build_regex(&sig.regex, "") { + Ok(r) => r, + Err(_) => build_regex(&sig.regex, "i")?, + }; + if let Some(caps) = re.captures(text) { + let cpes = expand_cpe_templates(&sig.cpe, &caps); + return Ok(Some((sig.service.clone(), cpes))); + } + } + Ok(None) +} + +/// Probe implementation for tcp:null (no payload) +pub struct NullProbe; + +impl NullProbe { + pub async fn run(ctx: ProbeContext) -> Result { + // Pre-check + if ctx.probe.probe_id != ServiceProbe::TcpNull { + bail!("NullProbe invoked with non-tcp:null probe_id: {:?}", ctx.probe.probe_id); + } + + tracing::debug!("Null Probe: {}:{} - Connecting", ctx.ip, ctx.probe.port); + let addr: SocketAddr = SocketAddr::new(ctx.ip, ctx.probe.port); + let mut stream = timeout(ctx.timeout, TcpStream::connect(addr)).await??; + + tracing::debug!("Null Probe: {}:{} - Connected", ctx.ip, ctx.probe.port); + + // if payload is present, send it (should not happen for tcp:null, but just in case) + let payload = PayloadBuilder::new(ctx.probe.clone()) + .payload(PayloadContext::default()) + .unwrap_or_default(); + if !payload.is_empty() { + timeout(ctx.timeout, stream.write_all(&payload)).await??; + stream.flush().await?; + } + + // Apply idle/total timeout and max byte limit for reading + let idle = ctx.timeout; + let total = ctx.timeout; + tracing::debug!("Null Probe: {}:{} - Reading response(timeout: {})", ctx.ip, ctx.probe.port, total.as_millis()); + let bytes = read_timeout(&mut stream, idle, total, ctx.max_read_size).await?; + + // Parse banner from response + let banner = parse_banner(&bytes, 64 * 1024); + + tracing::debug!("TCP NULL Probe: {}:{} - Banner: {:?}", ctx.ip, ctx.probe.port, banner.first_line); + + // Match signatures (tcp:NULL) + let hit = match_null_signatures( "tcp:NULL", &banner.raw_text)?; + + // Construct service info + let mut svc = ServiceInfo::default(); + let tcp_svc_db = crate::db::service::tcp_service_db(); + svc.name = tcp_svc_db.get_name(ctx.probe.port).map(|s| s.to_string()); + if let Some((_service_name, cpes)) = hit { + if !cpes.is_empty() { + svc.cpes = cpes; + } + } + // Even if name is still unknown, keep the banner + svc.banner = banner.first_line.clone(); + svc.raw = Some(banner.raw_text); + let probe_result: PortProbeResult = PortProbeResult { + ip: ctx.ip, + hostname: ctx.hostname, + port: ctx.probe.port, + transport: ctx.probe.transport, + probe_id: ctx.probe.probe_id, + service_info: svc, + }; + Ok(probe_result) + } +} diff --git a/src/service/probe/quic.rs b/src/service/probe/quic.rs new file mode 100644 index 0000000..2eca02a --- /dev/null +++ b/src/service/probe/quic.rs @@ -0,0 +1,217 @@ +use anyhow::Result; +use bytes::{Buf, BytesMut}; +use x509_parser::prelude::FromDer; +use std::{net::SocketAddr, sync::Arc}; +use quinn::{ClientConfig, Endpoint}; +use rustls::{ClientConfig as RustlsClientConfig, RootCertStore}; +use http::{Request, Method}; + +use crate::{ + endpoint::{ServiceInfo, TlsInfo}, + service::{ + probe::{ProbeContext, PortProbeResult, tls::SkipServerVerification}, + }, +}; + +/// Create a QUIC client configuration with optional certificate verification skipping and ALPN protocols. +pub fn quic_client_config(skip_verify: bool, alpn: &[&[u8]]) -> Result { + let mut roots = RootCertStore::empty(); + for cert in rustls_native_certs::load_native_certs()? { + let _ = roots.add(cert); + } + let mut tls = RustlsClientConfig::builder() + .with_root_certificates(roots) + .with_no_client_auth(); + if skip_verify { + tls.dangerous().set_certificate_verifier(SkipServerVerification::new()); + } + tls.enable_early_data = true; + tls.alpn_protocols = alpn.iter().map(|p| p.to_vec()).collect(); + let client_conf = quinn::crypto::rustls::QuicClientConfig::try_from(tls)?; + Ok(ClientConfig::new(Arc::new(client_conf))) +} + +/// Probe implementation for UDP QUIC (with optional HTTP/3) +pub struct QuicProbe; + +impl QuicProbe { + pub async fn run(ctx: ProbeContext) -> Result { + let addr = SocketAddr::new(ctx.ip, ctx.probe.port); + let hostname = ctx.hostname.clone().unwrap_or_else(|| ctx.ip.to_string()); + + // Set ALPN protocols + let alpn = [ + b"h3".as_slice(), + b"h3-34".as_slice(), b"h3-33".as_slice(), b"h3-32".as_slice(), b"h3-31".as_slice(), b"h3-30".as_slice(), b"h3-29".as_slice(), + b"hq-29".as_slice(), + ]; + let client_cfg = quic_client_config(ctx.skip_cert_verify, &alpn)?; + let mut endpoint = Endpoint::client((if ctx.ip.is_ipv6() { "[::]:0" } else { "0.0.0.0:0" }).parse().unwrap())?; + endpoint.set_default_client_config(client_cfg); + + // Connect to the server (SNI is hostname or "localhost") + let server_name = if ctx.sni { hostname.as_str() } else { "localhost" }; + let connect_fut = endpoint.connect(addr, server_name)?; + let quinn_conn = tokio::time::timeout(ctx.timeout, connect_fut).await??; + + // QUIC connection is established. Get ALPN + let alpn_proto = match quinn_conn.handshake_data() { + Some(data) => { + match data.downcast::() { + Ok(hd) => { + match hd.protocol { + Some(ref p) => Some(String::from_utf8_lossy(p).to_string()), + None => None, + } + }, + Err(_) => None, + } + }, + None => None, + }; + + let cert_der_bytes: Option> = quinn_conn + .peer_identity() + .and_then(|any| any.downcast_ref::>().cloned()) + .and_then(|vec_der| vec_der.into_iter().next()) + .map(|der| der.to_vec()); + + // Construct TlsInfo + let mut tls_info = TlsInfo::default(); + tls_info.alpn = alpn_proto.clone(); + // Fixed to TLS 1.3 for QUIC + tls_info.version = Some("TLSv1_3".into()); + if let Some(bytes) = cert_der_bytes.as_deref() { + if let Ok((_, x509)) = x509_parser::prelude::X509Certificate::from_der(bytes) { + tls_info.subject = x509.subject() + .iter_common_name() + .next() + .and_then(|cn| cn.as_str().ok()) + .map(|s| s.to_string()); + tls_info.issuer = x509.issuer() + .iter_common_name() + .next() + .and_then(|cn| cn.as_str().ok()) + .map(|s| s.to_string()); + let mut sans = Vec::new(); + for ext in x509.extensions() { + if let x509_parser::extensions::ParsedExtension::SubjectAlternativeName(san) = ext.parsed_extension() { + for name in san.general_names.iter() { + sans.push(name.to_string()); + } + } + } + tls_info.san_list = sans; + tls_info.not_before = Some(x509.validity().not_before.to_string()); + tls_info.not_after = Some(x509.validity().not_after.to_string()); + tls_info.serial_hex = Some(x509.raw_serial_as_string()); + tls_info.sig_algorithm = Some(crate::db::tls::oid_sig_name( + x509.signature_algorithm.oid().to_id_string().as_str(), + )); + tls_info.pubkey_algorithm = Some(crate::db::tls::oid_pubkey_name( + x509.public_key().algorithm.oid().to_id_string().as_str(), + )); + } + } + + // If ALPN indicates HTTP/3, perform a simple GET request + if let Some(alpn_s) = &alpn_proto { + if alpn_s.starts_with("h3") { + // HTTP/3 client initialization + tracing::debug!("HTTP/3 Probe: {}:{} - Connecting", ctx.ip, ctx.probe.port); + let h3_quinn_conn = h3_quinn::Connection::new(quinn_conn); + let (mut driver, mut send_request) = h3::client::new(h3_quinn_conn).await?; + let drive = async move { + return Err::<(), h3::error::ConnectionError>(futures::future::poll_fn(|cx| driver.poll_close(cx)).await); + }; + + let request = async move { + let mut svc = ServiceInfo::default(); + // Simple GET request + let req = Request::builder() + .method(Method::GET) + .uri("https://".to_string() + server_name + "/") + .header("Host", server_name) + .header("User-Agent", "nrev/0.1 (probe)") + .body(()) + .unwrap(); + + tracing::debug!("HTTP/3 Probe: {}:{} - Sending request", ctx.ip, ctx.probe.port); + // Send request + let mut stream = send_request.send_request(req).await?; + //let mut stream = tokio::time::timeout(ctx.timeout, send_request.send_request(req)).await??; + stream.finish().await?; + + // Receive response (headers) + tracing::debug!("HTTP/3 Probe: {}:{} - Receiving response", ctx.ip, ctx.probe.port); + let res = stream.recv_response().await?; + // Extract status and Server headers + let udp_svc_db = crate::db::service::udp_service_db(); + svc.name = udp_svc_db.get_name(ctx.probe.port).map(|s| s.to_string()); + svc.banner = Some(format!("HTTP/3 {}", res.status())); + svc.quic_version = Some("1".into()); + if let Some(val) = res.headers().get("server") { + if let Ok(s) = val.to_str() { + svc.product = Some(s.to_string()); + } + } + + // receiving potential response body + let mut read = 0usize; + let max_body = 8 * 1024; + let mut body_bytes = BytesMut::new(); + while let Some(chunk) = stream.recv_data().await? { + let bytes: &[u8] = chunk.chunk(); + body_bytes.extend_from_slice(bytes); + read += bytes.len(); + if read >= max_body { break; } + } + + svc.raw = Some(format!("alpn=h3; status={}", res.status())); + + Ok::(svc) + }; + + let (req_res, _drive_res) = tokio::join!(request, drive); + match req_res { + Ok(mut svc) => { + tracing::debug!("HTTP/3 Probe: {}:{} - Request succeeded", ctx.ip, ctx.probe.port); + tracing::debug!("HTTP/3 Probe Result: {:?}", svc); + svc.tls_info = Some(tls_info.clone()); + let probe_result = PortProbeResult { + ip: ctx.ip, + hostname: ctx.hostname, + port: ctx.probe.port, + transport: ctx.probe.transport, + probe_id: ctx.probe.probe_id, + service_info: svc, + }; + return Ok(probe_result); + }, + Err(e) => { + tracing::error!("HTTP/3 Probe: {}:{} - Request failed: {}", ctx.ip, ctx.probe.port, e); + } + } + } + } + + let mut svc = ServiceInfo::default(); + svc.name = Some("quic".into()); + svc.quic_version = Some("1".into()); + svc.tls_info = Some(tls_info); + + let probe_result = PortProbeResult { + ip: ctx.ip, + hostname: ctx.hostname, + port: ctx.probe.port, + transport: ctx.probe.transport, + probe_id: ctx.probe.probe_id, + service_info: svc, + }; + + // Wait for the connection to be closed + endpoint.wait_idle().await; + + Ok(probe_result) + } +} diff --git a/src/service/probe/tls.rs b/src/service/probe/tls.rs new file mode 100644 index 0000000..850b4ff --- /dev/null +++ b/src/service/probe/tls.rs @@ -0,0 +1,181 @@ +use anyhow::Result; +use rustls::client::danger::ServerCertVerifier; +use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; +use rustls::ClientConnection; +use tokio::{net::TcpStream, time::timeout}; +use tokio_rustls::{TlsConnector, rustls::{ClientConfig, RootCertStore}}; +use std::sync::Arc; +use x509_parser::prelude::{FromDer, ParsedExtension, X509Certificate}; +use std::net::SocketAddr; +use crate::endpoint::TlsInfo; +use crate::{endpoint::ServiceInfo}; +use crate::service::probe::{PortProbeResult, ProbeContext}; + +/// Dummy certificate verifier that treats any certificate as valid. +/// NOTE, such verification is vulnerable to MITM attacks, but convenient for testing. +#[derive(Debug)] +pub struct SkipServerVerification(Arc); + +impl SkipServerVerification { + pub fn new() -> Arc { + Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider()))) + } +} + +impl ServerCertVerifier for SkipServerVerification { + fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + _ocsp: &[u8], + _now: UnixTime, + ) -> Result { + Ok(rustls::client::danger::ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + rustls::crypto::verify_tls12_signature( + message, + cert, + dss, + &self.0.signature_verification_algorithms, + ) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + rustls::crypto::verify_tls13_signature( + message, + cert, + dss, + &self.0.signature_verification_algorithms, + ) + } + + fn supported_verify_schemes(&self) -> Vec { + self.0.signature_verification_algorithms.supported_schemes() + } +} + +/// Extract TLS info from a ClientConnection +pub fn extract_tls_info(probe_ctx: &ProbeContext, client_conn: &ClientConnection) -> Option { + let mut tls_info = TlsInfo::default(); + if let Some(version) = client_conn.protocol_version() { + if let Some(version) = version.as_str() { + tls_info.version = Some(version.to_string()); + } + } + if let Some(cs) = client_conn.negotiated_cipher_suite() { + if let Some(cs) = cs.suite().as_str() { + tls_info.cipher_suite = Some(cs.to_string()); + } + } + if let Some(alpn) = client_conn.alpn_protocol() { + tls_info.alpn = Some(String::from_utf8_lossy(alpn).to_string()); + } + + if let Some(cert) = client_conn.peer_certificates().and_then(|v| v.first()).cloned() { + tls_info.sni = probe_ctx.hostname.clone(); + match X509Certificate::from_der(&cert) { + Ok((_, x509)) => { + // Subject + let subject = x509.subject().iter_common_name() + .next() + .and_then(|cn| cn.as_str().ok()) + .map(|s| s.to_string()); + + // Issuer + let issuer = x509.issuer().iter_common_name() + .next() + .and_then(|cn| cn.as_str().ok()) + .map(|s| s.to_string()); + + // SAN (Subject Alternative Name) + let mut san_list = Vec::new(); + for ext in x509.extensions() { + if let ParsedExtension::SubjectAlternativeName(san) = ext.parsed_extension() { + for name in san.general_names.iter() { + san_list.push(name.to_string()); + } + } + } + + tls_info.subject = subject; + tls_info.issuer = issuer; + tls_info.san_list = san_list; + tls_info.not_before = Some(x509.validity().not_before.to_string()); + tls_info.not_after = Some(x509.validity().not_after.to_string()); + tls_info.serial_hex = Some(x509.raw_serial_as_string()); + let sig_alg_name = crate::db::tls::oid_sig_name(x509.signature_algorithm.oid().to_id_string().as_str()); + tls_info.sig_algorithm = Some(sig_alg_name); + let pubkey_alg_name = crate::db::tls::oid_pubkey_name(x509.public_key().algorithm.oid().to_id_string().as_str()); + tls_info.pubkey_algorithm = Some(pubkey_alg_name); + + } + Err(e) => { + tracing::warn!("Failed to parse certificate: {}", e); + } + } + } + Some(tls_info) +} + +/// Probe implementation for tcp:tls +pub struct TlsProbe; + +impl TlsProbe { + /// Run the TLS probe with the given context. + pub async fn run(ctx: ProbeContext) -> Result { + let addr: SocketAddr = SocketAddr::new(ctx.ip, ctx.probe.port); + let hostname = ctx.hostname.clone().unwrap_or_else(|| ctx.ip.to_string()); + let tcp_stream = timeout(ctx.timeout, TcpStream::connect(addr)).await??; + + // rustls config + let mut roots = RootCertStore::empty(); + for cert in rustls_native_certs::load_native_certs()? { let _ = roots.add(cert); } + let mut config = ClientConfig::builder() + .with_root_certificates(roots) + .with_no_client_auth(); + + if ctx.skip_cert_verify { + config.dangerous().set_certificate_verifier(SkipServerVerification::new()); + } + + let connector = TlsConnector::from(Arc::new(config)); + let sni_name = if ctx.sni { + ServerName::try_from(hostname)? + } else { + ServerName::try_from("localhost")? + }; + + let tls_stream = timeout(ctx.timeout, connector.connect(sni_name, tcp_stream)).await??; + let conn = tls_stream.get_ref().1; // server connection + + let mut svc = ServiceInfo::default(); + let tcp_svc_db = crate::db::service::tcp_service_db(); + svc.name = tcp_svc_db.get_name(ctx.probe.port).map(|s| s.to_string()); + + svc.tls_info = crate::service::probe::tls::extract_tls_info(&ctx, &conn); + + let probe_result: PortProbeResult = PortProbeResult { + ip: ctx.ip, + hostname: ctx.hostname, + port: ctx.probe.port, + transport: ctx.probe.transport, + probe_id: ctx.probe.probe_id, + service_info: svc, + }; + tracing::debug!("TLS Probe Result: {:?}", probe_result); + return Ok(probe_result); + } +} diff --git a/src/sys/id.rs b/src/sys/id.rs deleted file mode 100644 index eb03ee8..0000000 --- a/src/sys/id.rs +++ /dev/null @@ -1,11 +0,0 @@ -use uuid::Uuid; - -pub fn get_probe_id() -> String { - let id = Uuid::new_v4(); - id.to_string().replace("-", "") -} - -pub fn get_host_id(hostname: String) -> String { - let id = Uuid::new_v5(&Uuid::NAMESPACE_DNS, hostname.as_bytes()); - id.to_string().replace("-", "") -} diff --git a/src/sys/mod.rs b/src/sys/mod.rs deleted file mode 100644 index b8bab1c..0000000 --- a/src/sys/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod id; -pub mod os; -pub mod time; diff --git a/src/sys/os.rs b/src/sys/os.rs deleted file mode 100644 index 58f4b76..0000000 --- a/src/sys/os.rs +++ /dev/null @@ -1,14 +0,0 @@ -#[cfg(target_os = "windows")] -pub fn get_os_type() -> String { - "windows".to_owned() -} - -#[cfg(target_os = "linux")] -pub fn get_os_type() -> String { - "linux".to_owned() -} - -#[cfg(target_os = "macos")] -pub fn get_os_type() -> String { - "macos".to_owned() -} diff --git a/src/sys/time.rs b/src/sys/time.rs deleted file mode 100644 index 59f31fa..0000000 --- a/src/sys/time.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::time::Duration; - -pub fn get_sysdate() -> String { - let now = chrono::Local::now(); - now.to_rfc3339() -} - -pub fn get_systime() -> String { - let now = chrono::Local::now(); - now.format("%H:%M:%S").to_string() -} - -pub fn ceil_duration_millis(duration: Duration) -> Duration { - let millis = duration.as_millis(); - if millis % 1000 == 0 { - duration - } else { - Duration::from_millis(millis as u64 + 1) - } -} diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 0000000..13e258b --- /dev/null +++ b/src/time.rs @@ -0,0 +1,22 @@ +use tracing_subscriber::fmt::time::FormatTime; +use std::fmt; +use chrono::Local; + +/// DateTime format for logging that includes date, time, and timezone (YYYY-MM-DD HH:MM:SS.mmmmmm+00:00) +/// Same as `ChronoLocal::rfc_3339()` but with a custom format +pub struct LocalDateTime; + +impl FormatTime for LocalDateTime { + fn format_time(&self, w: &mut tracing_subscriber::fmt::format::Writer<'_>) -> fmt::Result { + write!(w, "{}", Local::now().format("%Y-%m-%d %H:%M:%S%.6f%:z")) + } +} + +/// Time format for logging that only includes the time (HH:MM:SS.mmmmmm+00:00) +pub struct LocalTimeOnly; + +impl FormatTime for LocalTimeOnly { + fn format_time(&self, w: &mut tracing_subscriber::fmt::format::Writer<'_>) -> fmt::Result { + write!(w, "{}", Local::now().format("%H:%M:%S%.6f%:z")) + } +} diff --git a/src/tls/cert.rs b/src/tls/cert.rs deleted file mode 100644 index 1a50fd9..0000000 --- a/src/tls/cert.rs +++ /dev/null @@ -1,90 +0,0 @@ -use anyhow::Result; -use rustls::client::danger::ServerCertVerifier; -use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; -use std::sync::Arc; -use std::{fs, io, path::Path}; - -/// Get the native certificates from the system. return rustls::RootCertStore -pub(crate) fn get_native_certs() -> io::Result { - let mut root_store = rustls::RootCertStore::empty(); - match rustls_native_certs::load_native_certs() { - Ok(certs) => { - for cert in certs { - match root_store.add(cert) { - Ok(_) => {} - Err(_) => {} - } - } - Ok(root_store) - } - Err(e) => return Err(e), - } -} - -/// Load certificate chain from a file -#[allow(dead_code)] -pub(crate) fn load_certs(cert_path: &Path) -> Result>> { - let cert_chain = fs::read(cert_path)?; - let cert_chain = if cert_path.extension().map_or(false, |x| x == "der") { - vec![CertificateDer::from(cert_chain)] - } else { - rustls_pemfile::certs(&mut &*cert_chain).collect::, _>>()? - }; - Ok(cert_chain) -} - -/// Dummy certificate verifier that treats any certificate as valid. -/// NOTE, such verification is vulnerable to MITM attacks, but convenient for testing. -#[derive(Debug)] -pub struct SkipServerVerification(Arc); - -impl SkipServerVerification { - pub fn new() -> Arc { - Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider()))) - } -} - -impl ServerCertVerifier for SkipServerVerification { - fn verify_server_cert( - &self, - _end_entity: &CertificateDer<'_>, - _intermediates: &[CertificateDer<'_>], - _server_name: &ServerName<'_>, - _ocsp: &[u8], - _now: UnixTime, - ) -> Result { - Ok(rustls::client::danger::ServerCertVerified::assertion()) - } - - fn verify_tls12_signature( - &self, - message: &[u8], - cert: &CertificateDer<'_>, - dss: &rustls::DigitallySignedStruct, - ) -> Result { - rustls::crypto::verify_tls12_signature( - message, - cert, - dss, - &self.0.signature_verification_algorithms, - ) - } - - fn verify_tls13_signature( - &self, - message: &[u8], - cert: &CertificateDer<'_>, - dss: &rustls::DigitallySignedStruct, - ) -> Result { - rustls::crypto::verify_tls13_signature( - message, - cert, - dss, - &self.0.signature_verification_algorithms, - ) - } - - fn supported_verify_schemes(&self) -> Vec { - self.0.signature_verification_algorithms.supported_schemes() - } -} diff --git a/src/tls/key.rs b/src/tls/key.rs deleted file mode 100644 index 32f65f1..0000000 --- a/src/tls/key.rs +++ /dev/null @@ -1,16 +0,0 @@ -use anyhow::anyhow; -use anyhow::Result; -use rustls::pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer}; -use std::{fs, path::Path}; - -/// Load private key from a file -#[allow(dead_code)] -pub(crate) fn load_key(key_path: &Path) -> Result> { - let key = fs::read(key_path)?; - let key = if key_path.extension().map_or(false, |x| x == "der") { - PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key)) - } else { - rustls_pemfile::private_key(&mut &*key)?.ok_or_else(|| anyhow!("no keys found"))? - }; - Ok(key) -} diff --git a/src/tls/mod.rs b/src/tls/mod.rs deleted file mode 100644 index cb3a5d7..0000000 --- a/src/tls/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod cert; -pub mod key; diff --git a/src/trace/mod.rs b/src/trace/mod.rs index f3d0b8d..8d5bd96 100644 --- a/src/trace/mod.rs +++ b/src/trace/mod.rs @@ -1,2 +1,116 @@ -pub mod setting; -pub mod tracer; +pub mod probe; + +use std::net::Ipv4Addr; +use std::{net::IpAddr, time::Duration}; + +use netdev::Interface; +use serde::{Deserialize, Serialize}; +use anyhow::Result; + +use crate::config::default::{DEFAULT_BASE_TARGET_UDP_PORT, DEFAULT_HOP_LIMIT}; +use crate::endpoint::Host; +use crate::probe::{ProbeResult, ProbeStatus}; +use crate::protocol::Protocol; + +/// Settings for traceroute operations. +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct TraceSetting { + pub if_index: u32, + pub dst_hostname: Option, + pub dst_ip: IpAddr, + pub dst_port: Option, + pub hop_limit: u8, + pub protocol: Protocol, + pub receive_timeout: Duration, + pub probe_timeout: Duration, + pub send_rate: Duration, + pub tunnel: bool, + pub loopback: bool, +} + +impl Default for TraceSetting { + fn default() -> Self { + Self { + if_index: 0, + dst_hostname: None, + dst_ip: IpAddr::V4(Ipv4Addr::LOCALHOST), + dst_port: Some(DEFAULT_BASE_TARGET_UDP_PORT), + hop_limit: DEFAULT_HOP_LIMIT, + protocol: Protocol::Udp, + receive_timeout: Duration::from_secs(1), + probe_timeout: Duration::from_secs(30), + send_rate: Duration::from_secs(1), + tunnel: false, + loopback: false, + } + } +} + +impl TraceSetting { + /// Create a UDP trace setting from the given interface and destination host. + pub fn udp_trace(interface: &Interface, dst_host: &Host) -> Result { + let use_tun = interface.is_tun(); + let loopback = interface.is_loopback(); + + let setting = TraceSetting { + if_index: interface.index, + dst_ip: dst_host.ip, + dst_hostname: dst_host.hostname.clone(), + dst_port: Some(DEFAULT_BASE_TARGET_UDP_PORT), + hop_limit: 64, + protocol: Protocol::Udp, + receive_timeout: Duration::from_secs(1), + probe_timeout: Duration::from_secs(30), + send_rate: Duration::from_secs(1), + tunnel: use_tun, + loopback: loopback, + }; + Ok(setting) + } +} + +/// Result of a traceroute operation. +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct TraceResult { + pub nodes: Vec, + pub probe_status: ProbeStatus, + pub elapsed_time: Duration, + pub protocol: Protocol, +} + +impl TraceResult { + /// Create a new empty TraceResult. + pub fn new() -> TraceResult { + TraceResult { + nodes: Vec::new(), + probe_status: ProbeStatus::new(), + elapsed_time: Duration::from_millis(0), + protocol: Protocol::Udp, + } + } +} + +/// Tracer structure. +/// +/// Supports UDP Traceroute. +#[derive(Clone, Debug)] +pub struct Tracer { + /// Probe Setting + pub setting: TraceSetting, +} + +impl Tracer { + /// Create a new Tracer instance. + pub fn new(setting: TraceSetting) -> Self { + Self { setting } + } + /// Run the traceroute based on the specified protocol. + pub async fn run(&self) -> Result { + match self.setting.protocol { + Protocol::Udp => probe::udp::run_udp_trace(&self.setting).await, + _ => { + Err(anyhow::anyhow!("Unsupported protocol")) + }, + } + } +} diff --git a/src/trace/probe/mod.rs b/src/trace/probe/mod.rs new file mode 100644 index 0000000..7e5aaa1 --- /dev/null +++ b/src/trace/probe/mod.rs @@ -0,0 +1 @@ +pub mod udp; diff --git a/src/trace/probe/udp.rs b/src/trace/probe/udp.rs new file mode 100644 index 0000000..ce4909c --- /dev/null +++ b/src/trace/probe/udp.rs @@ -0,0 +1,274 @@ +use anyhow::Result; +use crate::trace::{TraceResult, TraceSetting}; +use std::net::IpAddr; +use std::time::{Duration, Instant}; +use futures::stream::StreamExt; +use futures::future::poll_fn; +use netdev::MacAddr; +use nex::packet::frame::{Frame, ParseOption}; +use nex::packet::icmp::IcmpType; +use nex::packet::icmpv6::Icmpv6Type; +use crate::endpoint::NodeType; +use crate::probe::ProbeStatus; +use crate::{probe::ProbeResult, protocol::Protocol}; +use nex::datalink::async_io::{async_channel, AsyncChannel}; +use tracing_indicatif::span_ext::IndicatifSpanExt; + +/// Run a UDP traceroute based on the provided trace settings. +pub async fn run_udp_trace(setting: &TraceSetting) -> Result { + let mut result = TraceResult::new(); + result.protocol = Protocol::Udp; + + let interface = match crate::interface::get_interface_by_index(setting.if_index) { + Some(interface) => interface, + None => return Err(anyhow::anyhow!("Interface not found")), + }; + // Create sender + let config = nex::datalink::Config { + write_buffer_size: 4096, + read_buffer_size: 4096, + read_timeout: Some(setting.receive_timeout), + write_timeout: None, + channel_type: nex::datalink::ChannelType::Layer2, + bpf_fd_attempts: 1000, + linux_fanout: None, + promiscuous: false, + }; + + let AsyncChannel::Ethernet(mut tx, mut rx) = async_channel(&interface, config)? + else { + unreachable!(); + }; + + let mut responses: Vec = Vec::new(); + + let mut parse_option: ParseOption = ParseOption::default(); + if interface.is_tun() || (cfg!(any(target_os = "macos", target_os = "ios")) && interface.is_loopback()) { + let payload_offset = if interface.is_loopback() { 14 } else { 0 }; + parse_option.from_ip_packet = true; + parse_option.offset = payload_offset; + } + + let header_span = tracing::info_span!("trace"); + header_span.pb_set_style(&crate::output::progress::get_progress_style()); + header_span.pb_set_message(&format!("trace ({})", setting.dst_ip)); + header_span.pb_set_length(setting.hop_limit as u64); + header_span.pb_set_position(0); + header_span.pb_start(); + + let mut dst_reached: bool = false; + let start_time = Instant::now(); + for seq_ttl in 1..setting.hop_limit { + let udp_packet = crate::packet::udp::build_udp_trace_packet(&interface, &setting, seq_ttl); + let send_time = Instant::now(); + match poll_fn(|cx| tx.poll_send(cx, &udp_packet)).await { + Ok(_) => { + }, + Err(e) => eprintln!("Failed to send packet: {}", e), + } + loop { + match tokio::time::timeout(setting.receive_timeout, rx.next()).await { + Ok(Some(Ok(packet))) => { + let rtt = send_time.elapsed(); + let frame = match Frame::from_buf(&packet, parse_option.clone()) { + Some(frame) => frame, + None => { + eprintln!("Failed to parse packet: {:?}", packet); + continue; + } + }; + let mut mac_addr: MacAddr = MacAddr::zero(); + if let Some(datalink_layer) = &frame.datalink { + // Ethernet + if let Some(ethernet_header) = &datalink_layer.ethernet { + mac_addr = ethernet_header.source; + } + } + if let Some(ip_layer) = &frame.ip { + // IPv4 + if let Some(ipv4_header) = &ip_layer.ipv4 { + // IPv4 ICMP + if let Some(icmp_header) = &ip_layer.icmp { + match icmp_header.icmp_type { + IcmpType::TimeExceeded => { + let probe_result: ProbeResult = ProbeResult { + seq: seq_ttl as u32, + mac_addr: mac_addr, + ip_addr: IpAddr::V4(ipv4_header.source), + host_name: None, + port_number: None, + port_status: None, + ttl: ipv4_header.ttl, + hop: crate::util::ip::initial_ttl(ipv4_header.ttl) + - ipv4_header.ttl, + rtt: rtt, + probe_status: ProbeStatus::new(), + protocol: Protocol::Udp, + node_type: if seq_ttl == 1 { + NodeType::Gateway + } else { + NodeType::Hop + }, + sent_packet_size: udp_packet.len(), + received_packet_size: packet.len(), + }; + tracing::info!("#{} Reply from {}, RTT={:?} TTL={} Type={}", seq_ttl, ipv4_header.source, rtt, ipv4_header.ttl, probe_result.node_type.as_str()); + responses.push(probe_result); + header_span.pb_inc(1); + break; + } + IcmpType::DestinationUnreachable => { + if IpAddr::V4(ipv4_header.source) == setting.dst_ip { + let probe_result: ProbeResult = ProbeResult { + seq: seq_ttl as u32, + mac_addr: mac_addr, + ip_addr: setting.dst_ip, + host_name: setting.dst_hostname.clone(), + port_number: None, + port_status: None, + ttl: ipv4_header.ttl, + hop: crate::util::ip::initial_ttl(ipv4_header.ttl) + - ipv4_header.ttl, + rtt: rtt, + probe_status: ProbeStatus::new(), + protocol: Protocol::Udp, + node_type: NodeType::Destination, + sent_packet_size: udp_packet.len(), + received_packet_size: packet.len(), + }; + tracing::info!("#{} Reply from {}, RTT={:?} TTL={} Type={}", seq_ttl, ipv4_header.source, rtt, ipv4_header.ttl, probe_result.node_type.as_str()); + responses.push(probe_result); + header_span.pb_inc(1); + dst_reached = true; + break; + } + } + _ => {} + } + } + } + // IPv6 + if let Some(ipv6_header) = &ip_layer.ipv6 { + // ICMPv6 + if let Some(icmpv6_header) = &ip_layer.icmpv6 { + match icmpv6_header.icmpv6_type { + Icmpv6Type::TimeExceeded => { + let probe_result: ProbeResult = ProbeResult { + seq: seq_ttl as u32, + mac_addr: mac_addr, + ip_addr: IpAddr::V6(ipv6_header.source), + host_name: None, + port_number: None, + port_status: None, + ttl: ipv6_header.hop_limit, + hop: crate::util::ip::initial_ttl(ipv6_header.hop_limit) + - ipv6_header.hop_limit, + rtt: rtt, + probe_status: ProbeStatus::new(), + protocol: Protocol::Udp, + node_type: if seq_ttl == 1 { + NodeType::Gateway + } else { + NodeType::Hop + }, + sent_packet_size: udp_packet.len(), + received_packet_size: packet.len(), + }; + tracing::info!("#{} Reply from {}, RTT={:?} TTL={} Type={}", seq_ttl, ipv6_header.source, rtt, ipv6_header.hop_limit, probe_result.node_type.as_str()); + responses.push(probe_result); + header_span.pb_inc(1); + break; + }, + Icmpv6Type::DestinationUnreachable => { + if IpAddr::V6(ipv6_header.source) == setting.dst_ip { + let probe_result: ProbeResult = ProbeResult { + seq: seq_ttl as u32, + mac_addr: mac_addr, + ip_addr: setting.dst_ip, + host_name: setting.dst_hostname.clone(), + port_number: None, + port_status: None, + ttl: ipv6_header.hop_limit, + hop: crate::util::ip::initial_ttl(ipv6_header.hop_limit) + - ipv6_header.hop_limit, + rtt: rtt, + probe_status: ProbeStatus::new(), + protocol: Protocol::Udp, + node_type: NodeType::Destination, + sent_packet_size: udp_packet.len(), + received_packet_size: packet.len(), + }; + tracing::info!("#{} Reply from {}, RTT={:?} TTL={} Type={}", seq_ttl, ipv6_header.source, rtt, ipv6_header.hop_limit, probe_result.node_type.as_str()); + responses.push(probe_result); + header_span.pb_inc(1); + dst_reached = true; + break; + } + } + _ => {} + } + } + } + } + }, + Ok(Some(Err(e))) => { + tracing::error!("Failed to receive packet: {}", e); + header_span.pb_inc(1); + break; + }, + Ok(None) => { + tracing::error!("Channel closed"); + header_span.pb_inc(1); + break; + }, + Err(_) => { + tracing::error!("Request timeout for seq {}", seq_ttl as u32); + let probe_result = ProbeResult::timeout( + seq_ttl as u32, + setting.dst_ip, + setting.dst_hostname.clone(), + Protocol::Udp, + udp_packet.len(), + ); + responses.push(probe_result); + + header_span.pb_inc(1); + break; + } + } + + let elapsed_time: Duration = send_time.elapsed(); + if elapsed_time > setting.receive_timeout { + tracing::error!("Request timeout for seq {}", seq_ttl as u32); + let probe_result = ProbeResult::timeout( + seq_ttl as u32, + setting.dst_ip, + setting.dst_hostname.clone(), + Protocol::Udp, + udp_packet.len(), + ); + responses.push(probe_result); + + header_span.pb_inc(1); + break; + } + } + if dst_reached { + break; + } + if !setting.send_rate.is_zero() && seq_ttl < setting.hop_limit { + tokio::time::sleep(setting.send_rate).await; + } + } + + // Finish header span + drop(header_span); + + let elapsed_time = start_time.elapsed(); + result.probe_status = ProbeStatus::new(); + result.elapsed_time = elapsed_time; + result.nodes = responses; + result.protocol = Protocol::Udp; + + Ok(result) +} diff --git a/src/trace/setting.rs b/src/trace/setting.rs deleted file mode 100644 index 1c1c54e..0000000 --- a/src/trace/setting.rs +++ /dev/null @@ -1,63 +0,0 @@ -use std::net::Ipv4Addr; -use std::{net::IpAddr, time::Duration}; - -use netdev::Interface; -use serde::{Deserialize, Serialize}; - -use crate::config::{DEFAULT_BASE_TARGET_UDP_PORT, DEFAULT_HOP_LIMIT}; -use crate::protocol::Protocol; - -#[derive(Deserialize, Serialize, Clone, Debug)] -pub struct TraceSetting { - pub if_index: u32, - pub dst_hostname: String, - pub dst_ip: IpAddr, - pub dst_port: u16, - pub hop_limit: u8, - pub protocol: Protocol, - pub receive_timeout: Duration, - pub probe_timeout: Duration, - pub send_rate: Duration, - pub tunnel: bool, - pub loopback: bool, -} - -impl Default for TraceSetting { - fn default() -> Self { - Self { - if_index: 0, - dst_hostname: "localhost".to_string(), - dst_ip: IpAddr::V4(Ipv4Addr::LOCALHOST), - dst_port: DEFAULT_BASE_TARGET_UDP_PORT, - hop_limit: DEFAULT_HOP_LIMIT, - protocol: Protocol::UDP, - receive_timeout: Duration::from_secs(1), - probe_timeout: Duration::from_secs(30), - send_rate: Duration::from_secs(1), - tunnel: false, - loopback: false, - } - } -} - -impl TraceSetting { - pub fn udp_trace(interface: &Interface, dst_ip_addr: IpAddr) -> Result { - let use_tun = interface.is_tun(); - let loopback = interface.is_loopback(); - - let setting = TraceSetting { - if_index: interface.index, - dst_ip: dst_ip_addr, - dst_hostname: dst_ip_addr.to_string(), - dst_port: DEFAULT_BASE_TARGET_UDP_PORT, - hop_limit: 64, - protocol: Protocol::UDP, - receive_timeout: Duration::from_secs(1), - probe_timeout: Duration::from_secs(30), - send_rate: Duration::from_secs(1), - tunnel: use_tun, - loopback: loopback, - }; - Ok(setting) - } -} diff --git a/src/trace/tracer.rs b/src/trace/tracer.rs deleted file mode 100644 index 5b9db44..0000000 --- a/src/trace/tracer.rs +++ /dev/null @@ -1,344 +0,0 @@ -use crate::host::{NodeType, PortStatus}; -use crate::packet::setting::PacketBuildSetting; -use crate::ping::result::TracerouteResult; -use crate::probe::{ProbeResult, ProbeStatus}; -use crate::protocol::Protocol; -use netdev::Interface; -use nex::datalink::{RawReceiver, RawSender}; -use nex::net::mac::MacAddr; -use nex::packet::frame::{Frame, ParseOption}; -use nex::packet::icmp::IcmpType; -use nex::packet::icmpv6::Icmpv6Type; -use std::net::IpAddr; -use std::sync::mpsc::{channel, Receiver, Sender}; -use std::sync::{Arc, Mutex}; -use std::time::{Duration, Instant}; - -use super::setting::TraceSetting; - -/// Tracer structure. -/// -/// Supports UDP Traceroute. -#[derive(Clone, Debug)] -pub struct Tracer { - /// Probe Setting - pub probe_setting: TraceSetting, - /// Sender for progress messaging - tx: Arc>>, - /// Receiver for progress messaging - rx: Arc>>, -} - -impl Tracer { - /// Create new Tracer instance with setting - pub fn new(setting: TraceSetting) -> Result { - // Check interface - if crate::interface::get_interface_by_index(setting.if_index).is_none() { - return Err(format!( - "Tracer::new: unable to get interface. index: {}", - setting.if_index - )); - } - let (tx, rx) = channel(); - let tracer = Tracer { - probe_setting: setting, - tx: Arc::new(Mutex::new(tx)), - rx: Arc::new(Mutex::new(rx)), - }; - return Ok(tracer); - } - /// Run traceroute - pub fn trace(&self) -> Result { - run_traceroute(&self.probe_setting, &self.tx) - } - /// Get progress receiver - pub fn get_progress_receiver(&self) -> Arc>> { - self.rx.clone() - } -} - -fn run_traceroute( - setting: &TraceSetting, - msg_tx: &Arc>>, -) -> Result { - let interface: Interface = match crate::interface::get_interface_by_index(setting.if_index) { - Some(interface) => interface, - None => { - return Err(format!( - "run_traceroute: unable to get interface by index {}", - setting.if_index - )) - } - }; - let config = nex::datalink::Config { - write_buffer_size: 4096, - read_buffer_size: 4096, - read_timeout: Some(setting.receive_timeout), - write_timeout: None, - channel_type: nex::datalink::ChannelType::Layer2, - bpf_fd_attempts: 1000, - linux_fanout: None, - promiscuous: false, - }; - // Create a channel to send/receive packet - let (mut tx, mut rx) = match nex::datalink::channel(&interface, config) { - Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), - Ok(_) => return Err("run_traceroute: unable to create channel".to_string()), - Err(e) => return Err(format!("run_traceroute: unable to create channel: {}", e)), - }; - match setting.protocol { - crate::protocol::Protocol::ICMP => Err("ICMP traceroute is not supported".to_string()), - crate::protocol::Protocol::TCP => Err("TCP traceroute is not supported".to_string()), - crate::protocol::Protocol::UDP => { - let result = udp_trace(&mut tx, &mut rx, setting, msg_tx); - return Ok(result); - } - _ => { - return Err("run_ping: unsupported protocol".to_string()); - } - } -} - -pub fn udp_trace( - tx: &mut Box, - rx: &mut Box, - setting: &TraceSetting, - msg_tx: &Arc>>, -) -> TracerouteResult { - let mut result = TracerouteResult::new(); - result.protocol = Protocol::UDP; - let mut parse_option: ParseOption = ParseOption::default(); - if setting.tunnel { - let payload_offset = if setting.loopback { 14 } else { 0 }; - parse_option.from_ip_packet = true; - parse_option.offset = payload_offset; - } - result.start_time = crate::sys::time::get_sysdate(); - let start_time = Instant::now(); - let mut responses: Vec = Vec::new(); - let mut dst_reached: bool = false; - for seq_ttl in 1..setting.hop_limit { - let packet_setting: PacketBuildSetting = - PacketBuildSetting::from_trace_setting(setting, seq_ttl); - let udp_packet: Vec = crate::packet::udp::build_udp_packet(packet_setting.clone()); - //let udp_packet: Vec = crate::packet::udp::build_udp_packet(setting.clone(), Some(seq_ttl)); - let send_time = Instant::now(); - match tx.send(&udp_packet) { - Some(_) => {} - None => {} - } - loop { - match rx.next() { - Ok(packet) => { - let recv_time: Duration = Instant::now().duration_since(send_time); - let frame: Frame = Frame::from_bytes(&packet, parse_option.clone()); - // Datalink - let mut mac_addr: MacAddr = MacAddr::zero(); - if let Some(datalink_layer) = &frame.datalink { - // Ethernet - if let Some(ethernet_header) = &datalink_layer.ethernet { - mac_addr = ethernet_header.source; - } - } - if let Some(ip_layer) = &frame.ip { - // IPv4 - if let Some(ipv4_header) = &ip_layer.ipv4 { - if IpAddr::V4(ipv4_header.destination) != packet_setting.src_ip { - continue; - } - // ICMP - if let Some(icmp_header) = &ip_layer.icmp { - match icmp_header.icmp_type { - IcmpType::TimeExceeded => { - let probe_result: ProbeResult = ProbeResult { - seq: seq_ttl as u32, - mac_addr: mac_addr, - ip_addr: IpAddr::V4(ipv4_header.source), - host_name: ipv4_header.source.to_string(), - port_number: None, - port_status: None, - ttl: ipv4_header.ttl, - hop: crate::ip::guess_initial_ttl(ipv4_header.ttl) - - ipv4_header.ttl, - rtt: recv_time, - probe_status: ProbeStatus::new(), - protocol: Protocol::UDP, - node_type: if seq_ttl == 1 { - NodeType::DefaultGateway - } else { - NodeType::Relay - }, - sent_packet_size: udp_packet.len(), - received_packet_size: packet.len(), - }; - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - IcmpType::DestinationUnreachable => { - let probe_result: ProbeResult = ProbeResult { - seq: seq_ttl as u32, - mac_addr: mac_addr, - ip_addr: IpAddr::V4(ipv4_header.source), - host_name: ipv4_header.source.to_string(), - port_number: Some(setting.dst_port), - port_status: Some(PortStatus::Closed), - ttl: ipv4_header.ttl, - hop: crate::ip::guess_initial_ttl(ipv4_header.ttl) - - ipv4_header.ttl, - rtt: recv_time, - probe_status: ProbeStatus::new(), - protocol: Protocol::UDP, - node_type: NodeType::Destination, - sent_packet_size: udp_packet.len(), - received_packet_size: packet.len(), - }; - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - dst_reached = true; - break; - } - _ => {} - } - } - } - // IPv6 - if let Some(ipv6_header) = &ip_layer.ipv6 { - if IpAddr::V6(ipv6_header.destination) != packet_setting.src_ip { - continue; - } - // ICMPv6 - if let Some(icmpv6_header) = &ip_layer.icmpv6 { - match icmpv6_header.icmpv6_type { - Icmpv6Type::TimeExceeded => { - let probe_result: ProbeResult = ProbeResult { - seq: seq_ttl as u32, - mac_addr: mac_addr, - ip_addr: IpAddr::V6(ipv6_header.source), - host_name: ipv6_header.source.to_string(), - port_number: None, - port_status: None, - ttl: ipv6_header.hop_limit, - hop: crate::ip::guess_initial_ttl( - ipv6_header.hop_limit, - ) - ipv6_header.hop_limit, - rtt: recv_time, - probe_status: ProbeStatus::new(), - protocol: Protocol::UDP, - node_type: if seq_ttl == 1 { - NodeType::DefaultGateway - } else { - NodeType::Relay - }, - sent_packet_size: udp_packet.len(), - received_packet_size: packet.len(), - }; - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - Icmpv6Type::DestinationUnreachable => { - let probe_result: ProbeResult = ProbeResult { - seq: seq_ttl as u32, - mac_addr: mac_addr, - ip_addr: IpAddr::V6(ipv6_header.source), - host_name: ipv6_header.source.to_string(), - port_number: Some(setting.dst_port), - port_status: Some(PortStatus::Closed), - ttl: ipv6_header.hop_limit, - hop: crate::ip::guess_initial_ttl( - ipv6_header.hop_limit, - ) - ipv6_header.hop_limit, - rtt: recv_time, - probe_status: ProbeStatus::new(), - protocol: Protocol::UDP, - node_type: NodeType::Destination, - sent_packet_size: udp_packet.len(), - received_packet_size: packet.len(), - }; - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - dst_reached = true; - break; - } - _ => {} - } - } - } - } - } - Err(_e) => { - let probe_result = ProbeResult::trace_timeout( - seq_ttl as u32, - Protocol::UDP, - udp_packet.len(), - NodeType::Relay, - ); - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - let wait_time: Duration = Instant::now().duration_since(send_time); - if wait_time > setting.receive_timeout { - let probe_result = ProbeResult::trace_timeout( - seq_ttl as u32, - Protocol::UDP, - udp_packet.len(), - NodeType::Relay, - ); - responses.push(probe_result.clone()); - match msg_tx.lock() { - Ok(lr) => match lr.send(probe_result) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - break; - } - } - if dst_reached { - break; - } - if seq_ttl < setting.hop_limit { - std::thread::sleep(setting.send_rate); - } - } - let probe_time = Instant::now().duration_since(start_time); - result.end_time = crate::sys::time::get_sysdate(); - result.elapsed_time = probe_time; - result.nodes = responses; - result.probe_status = ProbeStatus::new(); - result -} diff --git a/src/util/ip.rs b/src/util/ip.rs new file mode 100644 index 0000000..0eaec74 --- /dev/null +++ b/src/util/ip.rs @@ -0,0 +1,39 @@ +use std::net::IpAddr; + +use netdev::Interface; + +/// Initial TTL class based on TTL(IPv4) or Hop Limit(IPv6) +pub fn initial_ttl(ttl: u8) -> u8 { + match ttl { + 0..=64 => 64, + 65..=128 => 128, + _ => 255, + } +} + +/// Get the next hop IP address for a target IP address based on the interface's routing information. +pub fn next_hop_ip(iface: &Interface, target: IpAddr) -> Option { + match target { + IpAddr::V4(dst) => { + // Check if the target IP is in the same network as the interface + if let Some(_) = iface.ipv4.iter().find(|ipnet| ipnet.contains(&dst)) { + return Some(IpAddr::V4(dst)); + } + // off-link, return the default gateway (IPv4) + match &iface.gateway { + Some(gw) => gw.ipv4.iter().next().map(|ip| IpAddr::V4(*ip)), + None => None, + } + } + IpAddr::V6(dst) => { + if let Some(_) = iface.ipv6.iter().find(|ipnet| ipnet.contains(&dst)) { + return Some(IpAddr::V6(dst)); + } + // off-link, return the default gateway (IPv6) + match &iface.gateway { + Some(gw) => gw.ipv6.iter().next().map(|ip| IpAddr::V6(*ip)), + None => None, + } + } + } +} diff --git a/src/util/json.rs b/src/util/json.rs new file mode 100644 index 0000000..47de179 --- /dev/null +++ b/src/util/json.rs @@ -0,0 +1,29 @@ +use std::fs::File; +use std::io::Write; +use std::path::Path; +use anyhow::Result; +use serde::Serialize; + +/// JSON output style +pub enum JsonStyle { + /// Compact one-line JSON + Compact, + /// Pretty printed (indented) JSON + Pretty, +} + +/// Save any serializable data to a JSON file. +pub fn save_json_output(data: &T, out_path: &Path, style: JsonStyle) -> Result<()> { + // Serialize depending on style + let json = match style { + JsonStyle::Compact => serde_json::to_string(data)?, + JsonStyle::Pretty => serde_json::to_string_pretty(data)?, + }; + + // Write to file (create or truncate) + let mut file = File::create(out_path)?; + file.write_all(json.as_bytes())?; + file.flush()?; // ensure it's written + + Ok(()) +} diff --git a/src/util/mod.rs b/src/util/mod.rs index 42d1925..1d2daf6 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,2 +1,2 @@ -pub mod setting; -pub mod tree; +pub mod ip; +pub mod json; diff --git a/src/util/setting.rs b/src/util/setting.rs deleted file mode 100644 index ae34269..0000000 --- a/src/util/setting.rs +++ /dev/null @@ -1,26 +0,0 @@ -use std::time::Duration; - -const DEFAULT_WAIT_TIME_FACTOR: f64 = 2.0; -const MINIMUM_WAIT_TIME: Duration = Duration::from_millis(50); - -pub fn caluculate_wait_time(rtt: Duration) -> Duration { - if rtt < MINIMUM_WAIT_TIME { - return MINIMUM_WAIT_TIME; - } - - let num_cores = num_cpus::get_physical(); - let num_threads = num_cpus::get(); - - let factor = if num_cores <= 2 || num_threads <= 4 { - // If the number of cores is less than or equal to 2 - // or the number of threads is less than or equal to 4 - // , increase the factor - DEFAULT_WAIT_TIME_FACTOR * 2.0 - } else { - // Otherwise, the factor is the default value - DEFAULT_WAIT_TIME_FACTOR - }; - - let wait_time = rtt.as_secs_f64() * factor; - Duration::from_secs_f64(wait_time) -} diff --git a/src/util/tree.rs b/src/util/tree.rs deleted file mode 100644 index 0558c4a..0000000 --- a/src/util/tree.rs +++ /dev/null @@ -1,16 +0,0 @@ -pub fn node_label(label: &str, value: Option<&str>, delimiter: Option<&str>) -> String { - match value { - Some(value) => { - let delimiter = match delimiter { - Some(delimiter) => delimiter, - None => ":", - }; - //Tree::new(format!("{}{} {}", label, delimiter, value)) - format!("{}{} {}", label, delimiter, value) - } - None => { - //Tree::new(label.to_string()) - label.to_string() - } - } -}