├── src ├── lib.md ├── db │ ├── nmap-os-db.zip │ └── nmap-service-probes.zip ├── flood │ ├── udp6.rs │ ├── udp.rs │ ├── icmp.rs │ ├── icmpv6.rs │ ├── tcp.rs │ └── tcp6.rs ├── scan │ ├── arp.rs │ ├── ndp_ns.rs │ ├── udp6.rs │ └── udp.rs ├── os │ └── rr.rs ├── error.rs ├── ping │ ├── icmpv6.rs │ └── icmp.rs ├── utils.rs ├── trace │ ├── icmp.rs │ ├── icmpv6.rs │ ├── tcp6.rs │ ├── tcp.rs │ ├── udp.rs │ └── udp6.rs ├── vs │ └── vscan.rs └── trace.rs ├── imgs ├── u1_bug.png └── pistol_arch.png ├── .cargo └── config.toml ├── tests ├── ping_test.sh ├── linux_routetable.txt ├── unix_routetable.txt └── windows_routetable.txt ├── scripts ├── label.json ├── scale.py ├── mean.py ├── variance.py ├── w.py ├── cpe.py ├── re_match_test.py └── test.py ├── .gitignore ├── .github └── workflows │ └── rust.yml ├── .vscode └── launch.json ├── LICENSE-MIT ├── Cargo.toml └── LICENSE-APACHE /src/lib.md: -------------------------------------------------------------------------------- 1 | A Rust Library about Cybersecurity. -------------------------------------------------------------------------------- /imgs/u1_bug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rikonaka/pistol-rs/HEAD/imgs/u1_bug.png -------------------------------------------------------------------------------- /imgs/pistol_arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rikonaka/pistol-rs/HEAD/imgs/pistol_arch.png -------------------------------------------------------------------------------- /src/db/nmap-os-db.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rikonaka/pistol-rs/HEAD/src/db/nmap-os-db.zip -------------------------------------------------------------------------------- /src/db/nmap-service-probes.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rikonaka/pistol-rs/HEAD/src/db/nmap-service-probes.zip -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.x86_64-unknown-linux-gnu] 2 | runner = 'sudo -E' 3 | 4 | [target.x86_64-unknown-freebsd] 5 | runner = 'sudo -E' 6 | 7 | [target.aarch64-unknown-linux-gnu] 8 | runner = 'sudo -E' -------------------------------------------------------------------------------- /tests/ping_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TARGET_IP="192.168.5.133" 4 | 5 | echo "Script PID: $$" 6 | 7 | while true; do 8 | ping -c 1 $TARGET_IP > /dev/null 9 | 10 | FD_COUNT=$(lsof -p $$ | wc -l) 11 | echo "Opened fd: $FD_COUNT" 12 | 13 | sleep 1 14 | done 15 | -------------------------------------------------------------------------------- /scripts/label.json: -------------------------------------------------------------------------------- 1 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # npcap lib 17 | *.lib 18 | 19 | # My libc raw socket test 20 | *.pcapng 21 | *.pcap -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | os: [ubuntu-latest, macos-latest, windows-latest] 18 | arch: [x86_64, aarch64] 19 | steps: 20 | - name: Checkout code 21 | uses: actions/checkout@v3 22 | 23 | - name: Build project 24 | run: cargo build --release 25 | 26 | # only build and not test due to env limits 27 | # - name: Run tests 28 | # run: cargo test 29 | 30 | # - name: Test doc 31 | # run: cargo test --doc 32 | -------------------------------------------------------------------------------- /scripts/scale.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def main(): 5 | filename = 'scale.txt' 6 | result = [] 7 | with open(filename, 'r') as fp: 8 | for l in fp.readlines(): 9 | lsplit = [x.replace('{', '') 10 | .replace('}', '') 11 | .replace('/*', '') 12 | .replace('*/', '') 13 | .strip() for x in l.split(',')] 14 | a = float(lsplit[0]) 15 | b = float(lsplit[1]) 16 | name = lsplit[2] 17 | tmp = {} 18 | tmp['name'] = name 19 | tmp['value'] = [a, b] 20 | result.append(tmp) 21 | 22 | output_filename = 'scale.json' 23 | with open(output_filename, 'w') as fp: 24 | json.dump(result, fp) 25 | 26 | 27 | main() 28 | -------------------------------------------------------------------------------- /scripts/mean.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def main(): 5 | filename = 'mean.txt' 6 | result = [] 7 | with open(filename, 'r') as fp: 8 | name = '' 9 | for l in fp.readlines(): 10 | if '*' in l: 11 | name = l.replace('/*', '').replace('*/', '').strip() 12 | else: 13 | value = [x.strip().replace( 14 | '{', '').replace('}', '') for x in l.split(',')] 15 | value = [float(x) for x in value if len(x) > 0] 16 | tmp = {} 17 | tmp['name'] = name 18 | tmp['value'] = value 19 | result.append(tmp) 20 | 21 | # print(ret) 22 | output_filename = 'mean.json' 23 | with open(output_filename, 'w') as fp: 24 | json.dump(result, fp) 25 | 26 | 27 | main() 28 | -------------------------------------------------------------------------------- /scripts/variance.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def main(): 5 | filename = 'variance.txt' 6 | result = [] 7 | with open(filename, 'r') as fp: 8 | name = '' 9 | for l in fp.readlines(): 10 | if '*' in l: 11 | name = l.replace('/*', '').replace('*/', '').strip() 12 | else: 13 | value = [x.strip().replace( 14 | '{', '').replace('}', '') for x in l.split(',')] 15 | tmp = {} 16 | value = [float(x) for x in value if len(x) > 0] 17 | # print(len(value)) 18 | tmp['name'] = name 19 | tmp['value'] = value 20 | result.append(tmp) 21 | 22 | # print(ret) 23 | output_filename = 'variance.json' 24 | with open(output_filename, 'w') as fp: 25 | json.dump(result, fp) 26 | 27 | 28 | main() 29 | -------------------------------------------------------------------------------- /scripts/w.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def main(): 5 | filename = 'w.txt' 6 | result = [] 7 | sum = 0 8 | with open(filename, 'r') as fp: 9 | name = '' 10 | for l in fp.readlines(): 11 | if '*' in l: 12 | name = l.replace('/*', '').replace('*/', '').strip() 13 | else: 14 | value = [x.strip().replace( 15 | '{', '').replace('}', '') for x in l.split(',')] 16 | value = [float(x) for x in value if len(x) > 0] 17 | sum += len(value) 18 | tmp = {} 19 | tmp['name'] = name 20 | tmp['value'] = value 21 | result.append(tmp) 22 | 23 | # print(ret) 24 | print(sum) 25 | output_filename = 'w.json' 26 | with open(output_filename, 'w') as fp: 27 | json.dump(result, fp) 28 | 29 | 30 | main() 31 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug unit tests in library 'pistol'", 11 | "cargo": { 12 | "args": [ 13 | "test", 14 | "--no-run", 15 | "--lib", 16 | "--package=pistol" 17 | ], 18 | "filter": { 19 | "name": "pistol", 20 | "kind": "lib" 21 | } 22 | }, 23 | "args": [], 24 | "cwd": "${workspaceFolder}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 RikoNaka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pistol" 3 | version = "5.0.0" 4 | edition = "2024" 5 | license = "MIT OR Apache-2.0" 6 | description = "A Rust Library about Cybersecurity" 7 | homepage = "https://github.com/rikonaka/pistol-rs" 8 | documentation = "https://docs.rs/pistol" 9 | repository = "https://github.com/rikonaka/pistol-rs" 10 | readme = "README.md" 11 | keywords = ["penetration-test", "cybersecurity", "nmap"] 12 | 13 | [dependencies] 14 | num_cpus = "^1" 15 | rand = "^0" 16 | subnetwork = "^0" 17 | threadpool = "^1" 18 | chrono = "^0" 19 | gcdx = "^0" 20 | crc32fast = "^1" 21 | hex = "^0" 22 | serde = { version = "^1", features = ["derive"] } 23 | serde_json = "^1" 24 | dns-lookup = "^3" 25 | fancy-regex = "^0" 26 | pnet = { version = "^0", features = ["serde"] } 27 | prettytable-rs = "^0" 28 | regex = "^1" 29 | thiserror = "^2" 30 | zip = "^4" 31 | escape-bytes = "^0" 32 | tracing = "^0" 33 | tracing-subscriber = "^0" 34 | pcapture = { version = "^0", features = ["pcapng", "libpcap"] } 35 | 36 | [features] 37 | # default = ["scan"] 38 | # default = ["ping"] 39 | # default = ["flood"] 40 | # default = ["os"] 41 | # default = ["vs"] 42 | # default = ["trace"] 43 | default = ["scan", "ping", "flood", "os", "vs", "trace"] 44 | scan = [] 45 | ping = [] 46 | flood = [] 47 | os = [] 48 | vs = [] 49 | trace = [] 50 | -------------------------------------------------------------------------------- /scripts/cpe.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def main(): 5 | filename = 'cpe.txt' 6 | result = [] 7 | name_num = 0 8 | with open(filename, 'r') as fp: 9 | name = '' 10 | osclass_list = [] 11 | cpe_list = [] 12 | for l in fp.readlines(): 13 | if 'match.OS_name' in l: 14 | if len(name) > 0: 15 | tmp = {} 16 | tmp['name'] = name 17 | tmp['osclass'] = osclass_list 18 | tmp['cpe'] = cpe_list 19 | result.append(tmp) 20 | osclass_list = [] 21 | cpe_list = [] 22 | 23 | name_num += 1 24 | name = l.split('"')[-2] 25 | # print(name) 26 | 27 | # if name_num == 2: 28 | # break 29 | 30 | elif 'OS_Classification osclass' in l: 31 | osclass_split = l.replace(',', '').split('"') 32 | osclass_split.pop(0) 33 | osclass_split.pop(-1) 34 | osclass = [] 35 | for o in osclass_split: 36 | if len(o.strip()) > 0: 37 | osclass.append(o.strip()) 38 | #print(osclass_list) 39 | osclass_list.append(osclass) 40 | elif 'osclass.cpe.push_back' in l: 41 | cpe_split = l.split('"') 42 | cpe = cpe_split[1] 43 | #print(cpe_list) 44 | cpe_list.append(cpe) 45 | # break 46 | 47 | tmp = {} 48 | tmp['name'] = name 49 | tmp['osclass'] = osclass_list 50 | tmp['cpe'] = cpe_list 51 | result.append(tmp) 52 | 53 | # print(ret) 54 | output_filename = 'cpe.json' 55 | with open(output_filename, 'w') as fp: 56 | json.dump(result, fp) 57 | 58 | 59 | main() 60 | -------------------------------------------------------------------------------- /scripts/re_match_test.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | test_str = "SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.7" 5 | pattern = '^SSH-([\d.]+)-OpenSSH_([\w._-]+)[ -]{1,2}Ubuntu[ -_]([^\r\n]+)\r?\n' 6 | prog = re.compile(pattern) 7 | result = prog.match(test_str) 8 | print(result) 9 | 10 | 11 | test_str = "HTTP/1.1 200 OK\r\nDate: Sun, 28 Apr 2024 13:42:24 GMT\r\nServer: Apache/2.4.52 (Ubuntu)\r\n" 12 | pattern = "^HTTP/1\.[01] \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Apache[/ ](\d[-.\w]+) ([^\r\n]+)/s" 13 | pattern = "^HTTP/1\.[01] \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Apache[/ ](\d[.\w-]+)\s*\r?\n/s" 14 | pattern = "^HTTP/1\.[01] \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Apache +\(([^\r\n\)]+)\)\r\n/s" 15 | prog = re.compile(pattern) 16 | result = prog.match(test_str) 17 | print(result) 18 | 19 | # test_str = 'HTTP/1.1 200 OK\r\nDate: Sun, 28 Apr 2024 13:42:24 GMT\r\nServer: Apache/2.4.52 (Ubuntu)\r\n' 20 | # test_str = 'HTTP/1.1 200 OK\r\nDate: Sun, 28 Apr 2024 13:42:24 GMT\r\nServer: Apache/2.4.52 (Ubuntu)\r\nLast-Modified: Sun, 28 Apr 2024 04:52:36 GMT\r\nETag: "29af-61720e7d531b3"\r\nAccept-Ranges: bytes\r\nContent-Length: 10671\r\nVary: Accept-Encoding\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n' 21 | # 22 | # pattern = '^HTTP/1\.[01] (.*?400)\d\d\d.*\r\nDate: .*\r\nServer: Apache ([^\r\n]+)\r\n' 23 | # prog = re.compile(pattern) 24 | # result = prog.match(test_str) 25 | # print(result) 26 | # 27 | # pattern = '^HTTP/1\.[01] \d\d\d.*\r\nDate: .*\r\nServer: Apache ((?:[\w_]+/[\w._-]+ ?)+)\r\n' 28 | # prog = re.compile(pattern) 29 | # result = prog.match(test_str) 30 | # print(result) 31 | # 32 | # pattern = '^HTTP/1\.[01] \d\d\d (?:[^\r\n]*\r\n(.*?\r\n))*?Server: Apache +\(([^\r\n\)]+)\)\r\n' 33 | # prog = re.compile(pattern) 34 | # result = prog.match(test_str) 35 | # print(result) 36 | # 37 | # pattern = '^HTTP/1\.[01] \d\d\d.*\r\nDate: .*\r\nServer: Apache' 38 | # prog = re.compile(pattern) 39 | # result = prog.match(test_str) 40 | # print(result) 41 | -------------------------------------------------------------------------------- /src/flood/udp6.rs: -------------------------------------------------------------------------------- 1 | use pnet::packet::ip::IpNextHeaderProtocols; 2 | use pnet::packet::ipv6::MutableIpv6Packet; 3 | use pnet::packet::udp::MutableUdpPacket; 4 | use pnet::packet::udp::ipv6_checksum; 5 | use std::net::Ipv6Addr; 6 | use std::panic::Location; 7 | 8 | use crate::error::PistolError; 9 | use crate::layer::IPV6_HEADER_SIZE; 10 | use crate::layer::UDP_HEADER_SIZE; 11 | use crate::layer::layer3_ipv6_send; 12 | 13 | const UDP_DATA_SIZE: usize = 0; 14 | const TTL: u8 = 255; 15 | 16 | pub fn send_udp_flood_packet( 17 | dst_ipv6: Ipv6Addr, 18 | dst_port: u16, 19 | src_ipv6: Ipv6Addr, 20 | src_port: u16, 21 | max_same_packet: usize, 22 | ) -> Result { 23 | // ipv6 header 24 | let mut ipv6_buff = [0u8; IPV6_HEADER_SIZE + UDP_HEADER_SIZE + UDP_DATA_SIZE]; 25 | let mut ipv6_header = match MutableIpv6Packet::new(&mut ipv6_buff) { 26 | Some(p) => p, 27 | None => { 28 | return Err(PistolError::BuildPacketError { 29 | location: format!("{}", Location::caller()), 30 | }); 31 | } 32 | }; 33 | ipv6_header.set_version(6); 34 | // In all cases, the IPv6 flow label is 0x12345, on platforms that allow us to set it. 35 | // On platforms that do not (which includes non-Linux Unix platforms when not using Ethernet to send), the flow label will be 0. 36 | ipv6_header.set_flow_label(0x12345); 37 | let payload_length = UDP_HEADER_SIZE + UDP_DATA_SIZE; 38 | ipv6_header.set_payload_length(payload_length as u16); 39 | ipv6_header.set_next_header(IpNextHeaderProtocols::Udp); 40 | ipv6_header.set_hop_limit(TTL); 41 | ipv6_header.set_source(src_ipv6); 42 | ipv6_header.set_destination(dst_ipv6); 43 | 44 | // udp header 45 | let mut udp_header = match MutableUdpPacket::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) { 46 | Some(p) => p, 47 | None => { 48 | return Err(PistolError::BuildPacketError { 49 | location: format!("{}", Location::caller()), 50 | }); 51 | } 52 | }; 53 | udp_header.set_source(src_port); 54 | udp_header.set_destination(dst_port); 55 | udp_header.set_length((UDP_HEADER_SIZE + UDP_DATA_SIZE) as u16); 56 | let checksum = ipv6_checksum(&udp_header.to_immutable(), &src_ipv6, &dst_ipv6); 57 | udp_header.set_checksum(checksum); 58 | let timeout = None; 59 | 60 | for _ in 0..max_same_packet { 61 | let _ret = layer3_ipv6_send(dst_ipv6, src_ipv6, &ipv6_buff, vec![], timeout, false)?; 62 | } 63 | Ok(ipv6_buff.len() * max_same_packet) 64 | } 65 | -------------------------------------------------------------------------------- /src/flood/udp.rs: -------------------------------------------------------------------------------- 1 | use pnet::packet::ip::IpNextHeaderProtocols; 2 | use pnet::packet::ipv4; 3 | use pnet::packet::ipv4::Ipv4Flags; 4 | use pnet::packet::ipv4::MutableIpv4Packet; 5 | use pnet::packet::udp::MutableUdpPacket; 6 | use pnet::packet::udp::ipv4_checksum; 7 | use rand::Rng; 8 | use std::net::Ipv4Addr; 9 | use std::panic::Location; 10 | 11 | use crate::error::PistolError; 12 | use crate::layer::IPV4_HEADER_SIZE; 13 | use crate::layer::UDP_HEADER_SIZE; 14 | use crate::layer::layer3_ipv4_send; 15 | 16 | const UDP_DATA_SIZE: usize = 0; 17 | const TTL: u8 = 64; 18 | 19 | pub fn send_udp_flood_packet( 20 | dst_ipv4: Ipv4Addr, 21 | dst_port: u16, 22 | src_ipv4: Ipv4Addr, 23 | src_port: u16, 24 | max_same_packet: usize, 25 | ) -> Result { 26 | let mut rng = rand::rng(); 27 | // ip header 28 | let mut ip_buff = [0u8; IPV4_HEADER_SIZE + UDP_HEADER_SIZE + UDP_DATA_SIZE]; 29 | let mut ip_header = match MutableIpv4Packet::new(&mut ip_buff) { 30 | Some(p) => p, 31 | None => { 32 | return Err(PistolError::BuildPacketError { 33 | location: format!("{}", Location::caller()), 34 | }); 35 | } 36 | }; 37 | ip_header.set_version(4); 38 | ip_header.set_header_length(5); 39 | ip_header.set_total_length((IPV4_HEADER_SIZE + UDP_HEADER_SIZE + UDP_DATA_SIZE) as u16); 40 | let id = rng.random(); 41 | ip_header.set_identification(id); 42 | ip_header.set_flags(Ipv4Flags::DontFragment); 43 | ip_header.set_ttl(TTL); 44 | ip_header.set_next_level_protocol(IpNextHeaderProtocols::Udp); 45 | ip_header.set_source(src_ipv4); 46 | ip_header.set_destination(dst_ipv4); 47 | let c = ipv4::checksum(&ip_header.to_immutable()); 48 | ip_header.set_checksum(c); 49 | 50 | // udp header 51 | let mut udp_header = match MutableUdpPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 52 | Some(p) => p, 53 | None => { 54 | return Err(PistolError::BuildPacketError { 55 | location: format!("{}", Location::caller()), 56 | }); 57 | } 58 | }; 59 | udp_header.set_source(src_port); 60 | udp_header.set_destination(dst_port); 61 | udp_header.set_length((UDP_HEADER_SIZE + UDP_DATA_SIZE) as u16); 62 | // udp_header.set_payload(&vec![b'a'; 10]); // test 63 | let checksum = ipv4_checksum(&udp_header.to_immutable(), &src_ipv4, &dst_ipv4); 64 | udp_header.set_checksum(checksum); 65 | let timeout = None; 66 | 67 | for _ in 0..max_same_packet { 68 | let _ret = layer3_ipv4_send(dst_ipv4, src_ipv4, &ip_buff, vec![], timeout, false)?; 69 | } 70 | Ok(ip_buff.len() * max_same_packet) 71 | } 72 | -------------------------------------------------------------------------------- /src/scan/arp.rs: -------------------------------------------------------------------------------- 1 | use pnet::datalink::MacAddr; 2 | use pnet::datalink::NetworkInterface; 3 | use pnet::packet::Packet; 4 | use pnet::packet::arp::ArpHardwareTypes; 5 | use pnet::packet::arp::ArpOperations; 6 | use pnet::packet::arp::ArpPacket; 7 | use pnet::packet::arp::MutableArpPacket; 8 | use pnet::packet::ethernet::EtherTypes; 9 | use pnet::packet::ethernet::EthernetPacket; 10 | use std::net::Ipv4Addr; 11 | use std::panic::Location; 12 | use std::time::Duration; 13 | // use tracing::debug; 14 | 15 | use crate::error::PistolError; 16 | use crate::layer::ARP_HEADER_SIZE; 17 | use crate::layer::Layer2; 18 | use crate::layer::Layer2Filter; 19 | use crate::layer::Layer3Filter; 20 | use crate::layer::PacketFilter; 21 | 22 | fn get_mac_from_arp_response(ethernet_packet: &[u8]) -> Option { 23 | if ethernet_packet.len() > 0 { 24 | match EthernetPacket::new(ethernet_packet) { 25 | Some(re) => match re.get_ethertype() { 26 | EtherTypes::Arp => match ArpPacket::new(re.payload()) { 27 | Some(arp) => return Some(arp.get_sender_hw_addr()), 28 | None => (), 29 | }, 30 | _ => (), 31 | }, 32 | None => (), 33 | } 34 | } 35 | None 36 | } 37 | 38 | pub fn send_arp_scan_packet( 39 | dst_ipv4: Ipv4Addr, 40 | dst_mac: MacAddr, 41 | src_ipv4: Ipv4Addr, 42 | src_mac: MacAddr, 43 | interface: NetworkInterface, 44 | timeout: Option, 45 | ) -> Result<(Option, Duration), PistolError> { 46 | let mut arp_buffer = [0u8; ARP_HEADER_SIZE]; 47 | let mut arp_packet = match MutableArpPacket::new(&mut arp_buffer) { 48 | Some(p) => p, 49 | None => { 50 | return Err(PistolError::BuildPacketError { 51 | location: format!("{}", Location::caller()), 52 | }); 53 | } 54 | }; 55 | 56 | arp_packet.set_hardware_type(ArpHardwareTypes::Ethernet); 57 | arp_packet.set_protocol_type(EtherTypes::Ipv4); 58 | arp_packet.set_hw_addr_len(6); 59 | arp_packet.set_proto_addr_len(4); 60 | arp_packet.set_operation(ArpOperations::Request); 61 | arp_packet.set_sender_hw_addr(src_mac); 62 | arp_packet.set_sender_proto_addr(src_ipv4); 63 | arp_packet.set_target_hw_addr(MacAddr::zero()); 64 | arp_packet.set_target_proto_addr(dst_ipv4); 65 | 66 | let ether_type = EtherTypes::Arp; 67 | let layer2 = Layer2Filter { 68 | name: "arp scan layer2", 69 | src_mac: None, 70 | dst_mac: Some(src_mac), 71 | ether_type: Some(ether_type), 72 | }; 73 | let layer3 = Layer3Filter { 74 | name: "arp scan layer3", 75 | layer2: Some(layer2), 76 | src_addr: Some(dst_ipv4.into()), 77 | dst_addr: Some(src_ipv4.into()), 78 | }; 79 | let filters = vec![PacketFilter::Layer3Filter(layer3)]; 80 | 81 | let layer2 = Layer2::new(dst_mac, interface, ether_type, filters, timeout, true); 82 | let (ret, rtt) = layer2.send_recv(&arp_buffer)?; 83 | // debug!("{} get ret from internet", dst_ipv4); 84 | let mac = get_mac_from_arp_response(&ret); 85 | // debug!("{}: {:?}", dst_ipv4, mac); 86 | Ok((mac, rtt)) 87 | } 88 | -------------------------------------------------------------------------------- /tests/linux_routetable.txt: -------------------------------------------------------------------------------- 1 | default via 192.168.5.2 dev ens33 onlink 2 | 192.168.5.0/24 dev ens33 proto kernel scope link src 192.168.5.66 3 | fe80::/64 dev ens33 proto kernel metric 256 pref medium 4 | +++ 5 | default via 192.168.7.1 dev wlan0 proto dhcp src 192.168.7.100 metric 600 6 | 10.8.0.0/24 dev tun0 proto kernel scope link src 10.8.0.199 7 | 10.179.141.128/26 dev eth0 proto kernel scope link src 10.179.141.188 metric 100 8 | 10.179.201.0/24 via 10.179.141.129 dev eth0 proto static metric 100 9 | 10.179.202.0/24 via 10.179.141.129 dev eth0 proto static metric 100 10 | 10.179.203.0/24 via 10.179.141.129 dev eth0 proto static metric 100 11 | 10.179.204.0/24 via 10.179.141.129 dev eth0 proto static metric 100 12 | 10.179.205.0/24 via 10.179.141.129 dev eth0 proto static metric 100 13 | 10.179.206.0/24 via 10.179.141.129 dev eth0 proto static metric 100 14 | 10.179.207.0/24 via 10.179.141.129 dev eth0 proto static metric 100 15 | 10.179.208.0/24 via 10.179.141.129 dev eth0 proto static metric 100 16 | 10.179.209.0/24 via 10.179.141.129 dev eth0 proto static metric 100 17 | 10.179.210.0/24 via 10.179.141.129 dev eth0 proto static metric 100 18 | 10.179.211.0/24 via 10.179.141.129 dev eth0 proto static metric 100 19 | 10.179.212.0/24 via 10.179.141.129 dev eth0 proto static metric 100 20 | 10.179.213.0/24 via 10.179.141.129 dev eth0 proto static metric 100 21 | 10.179.214.0/24 via 10.179.141.129 dev eth0 proto static metric 100 22 | 10.179.215.0/24 via 10.179.141.129 dev eth0 proto static metric 100 23 | 10.179.225.0/24 via 10.179.141.129 dev eth0 proto static metric 100 24 | 10.179.252.0/24 via 10.179.141.129 dev eth0 proto static metric 100 25 | 192.168.7.0/24 dev wlan0 proto kernel scope link src 192.168.7.100 metric 600 26 | +++ 27 | default via 192.168.1.1 dev eth0 28 | 10.0.0.0/8 dev eth0 proto kernel scope link src 10.0.0.2 29 | 192.168.1.0/24 dev eth0 proto dhcp scope link src 192.168.1.2 30 | 192.168.100.0/24 via 192.168.1.1 dev eth0 31 | 192.168.200.0/24 dev eth1 proto static scope link 32 | 172.16.0.0/12 dev eth2 proto static scope link 33 | 169.254.0.0/16 dev eth0 scope link metric 100 34 | 192.168.2.0/24 dev wlan0 proto static scope link 35 | +++ 36 | default via fe80::1 dev eth0 37 | 2001:db8::/32 dev eth0 proto kernel metric 256 38 | fe80::/10 dev eth0 proto kernel metric 256 39 | fd00:1234::/64 dev eth1 proto static metric 1024 40 | fe80::/64 dev wlan0 proto static metric 256 41 | +++ 42 | default via 192.168.1.1 dev eth0 proto dhcp metric 100 43 | 10.0.0.0/8 dev eth0 proto static scope link metric 100 44 | 172.16.0.0/12 via 192.168.1.1 dev eth0 metric 200 45 | 192.168.1.0/24 dev eth0 proto dhcp scope link metric 100 46 | +++ 47 | default via fe80::1 dev eth0 metric 1024 48 | 2001:db8::/32 dev eth0 proto static metric 256 49 | fe80::/10 dev eth0 proto kernel metric 1024 50 | fd00:1234::/64 dev eth1 proto static metric 100 51 | +++ 52 | default via 192.168.1.1 dev eth0 53 | default via 192.168.2.1 dev eth1 54 | 192.168.0.0/16 dev eth0 proto kernel scope link src 192.168.0.10 55 | 10.0.0.0/8 via 192.168.1.1 dev eth0 56 | 192.168.1.0/24 dev eth0 proto static scope link 57 | +++ 58 | default via 192.168.1.1 dev eth0 proto dhcp metric 100 59 | 192.168.1.0/24 dev eth0 proto dhcp scope link metric 100 60 | 172.16.0.0/12 dev eth2 proto static scope link metric 200 61 | 192.168.200.0/24 via 192.168.1.1 dev eth0 -------------------------------------------------------------------------------- /src/flood/icmp.rs: -------------------------------------------------------------------------------- 1 | use chrono::Utc; 2 | use pnet::packet::icmp; 3 | use pnet::packet::icmp::IcmpCode; 4 | use pnet::packet::icmp::IcmpType; 5 | use pnet::packet::icmp::MutableIcmpPacket; 6 | use pnet::packet::icmp::echo_request::MutableEchoRequestPacket; 7 | use pnet::packet::ip::IpNextHeaderProtocols; 8 | use pnet::packet::ipv4; 9 | use pnet::packet::ipv4::Ipv4Flags; 10 | use pnet::packet::ipv4::MutableIpv4Packet; 11 | use rand::Rng; 12 | use std::net::Ipv4Addr; 13 | use std::panic::Location; 14 | 15 | use crate::error::PistolError; 16 | use crate::layer::ICMP_HEADER_SIZE; 17 | use crate::layer::IPV4_HEADER_SIZE; 18 | use crate::layer::layer3_ipv4_send; 19 | 20 | const TTL: u8 = 64; 21 | pub fn send_icmp_flood_packet( 22 | dst_ipv4: Ipv4Addr, 23 | _: u16, // unified interface 24 | src_ipv4: Ipv4Addr, 25 | _: u16, // unified interface 26 | max_same_packet: usize, 27 | ) -> Result { 28 | const ICMP_DATA_SIZE: usize = 16; 29 | let mut rng = rand::rng(); 30 | // ip header 31 | let mut ip_buff = [0u8; IPV4_HEADER_SIZE + ICMP_HEADER_SIZE + ICMP_DATA_SIZE]; 32 | let mut ip_header = match MutableIpv4Packet::new(&mut ip_buff) { 33 | Some(p) => p, 34 | None => { 35 | return Err(PistolError::BuildPacketError { 36 | location: format!("{}", Location::caller()), 37 | }); 38 | } 39 | }; 40 | ip_header.set_version(4); 41 | ip_header.set_header_length(5); 42 | ip_header.set_source(src_ipv4); 43 | ip_header.set_destination(dst_ipv4); 44 | ip_header.set_total_length((IPV4_HEADER_SIZE + ICMP_HEADER_SIZE + ICMP_DATA_SIZE) as u16); 45 | let id = rng.random(); 46 | ip_header.set_identification(id); 47 | ip_header.set_flags(Ipv4Flags::DontFragment); 48 | ip_header.set_ttl(TTL); 49 | ip_header.set_next_level_protocol(IpNextHeaderProtocols::Icmp); 50 | let c = ipv4::checksum(&ip_header.to_immutable()); 51 | ip_header.set_checksum(c); 52 | 53 | let mut icmp_header = match MutableEchoRequestPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 54 | Some(p) => p, 55 | None => { 56 | return Err(PistolError::BuildPacketError { 57 | location: format!("{}", Location::caller()), 58 | }); 59 | } 60 | }; 61 | icmp_header.set_icmp_type(IcmpType(8)); 62 | icmp_header.set_icmp_code(IcmpCode(0)); 63 | icmp_header.set_sequence_number(1); 64 | // icmp_header.set_identifier(2); 65 | icmp_header.set_identifier(rng.random()); 66 | let mut tv_sec = Utc::now().timestamp().to_be_bytes(); 67 | tv_sec.reverse(); // Big-Endian 68 | let mut tv_usec = Utc::now().timestamp_subsec_millis().to_be_bytes(); 69 | tv_usec.reverse(); // Big-Endian 70 | let mut timestamp = Vec::new(); 71 | timestamp.extend(tv_sec); 72 | timestamp.extend(tv_usec); 73 | // println!("{:?}", timestamp); 74 | icmp_header.set_payload(×tamp); 75 | 76 | let mut icmp_header = match MutableIcmpPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 77 | Some(p) => p, 78 | None => { 79 | return Err(PistolError::BuildPacketError { 80 | location: format!("{}", Location::caller()), 81 | }); 82 | } 83 | }; 84 | let checksum = icmp::checksum(&icmp_header.to_immutable()); 85 | icmp_header.set_checksum(checksum); 86 | let timeout = None; // not wait the result 87 | 88 | for _ in 0..max_same_packet { 89 | let _ret = layer3_ipv4_send(dst_ipv4, src_ipv4, &ip_buff, vec![], timeout, false)?; 90 | } 91 | 92 | Ok(ip_buff.len() * max_same_packet) 93 | } 94 | -------------------------------------------------------------------------------- /src/flood/icmpv6.rs: -------------------------------------------------------------------------------- 1 | use chrono::Utc; 2 | use pnet::packet::icmpv6; 3 | use pnet::packet::icmpv6::Icmpv6Code; 4 | use pnet::packet::icmpv6::Icmpv6Type; 5 | use pnet::packet::icmpv6::MutableIcmpv6Packet; 6 | use pnet::packet::icmpv6::echo_request::MutableEchoRequestPacket; 7 | use pnet::packet::ip::IpNextHeaderProtocols; 8 | use pnet::packet::ipv6::MutableIpv6Packet; 9 | use rand::Rng; 10 | use std::net::Ipv6Addr; 11 | use std::panic::Location; 12 | 13 | use crate::error::PistolError; 14 | use crate::layer::ICMPV6_ER_HEADER_SIZE; 15 | use crate::layer::IPV6_HEADER_SIZE; 16 | use crate::layer::layer3_ipv6_send; 17 | 18 | const TTL: u8 = 255; 19 | 20 | pub fn send_icmpv6_flood_packet( 21 | dst_ipv6: Ipv6Addr, 22 | _: u16, // unified interface 23 | src_ipv6: Ipv6Addr, 24 | _: u16, // unified interface 25 | max_same_packet: usize, 26 | ) -> Result { 27 | const ICMPV6_DATA_SIZE: usize = 16; 28 | let mut rng = rand::rng(); 29 | // ipv6 header 30 | let mut ipv6_buff = [0u8; IPV6_HEADER_SIZE + ICMPV6_ER_HEADER_SIZE + ICMPV6_DATA_SIZE]; 31 | let mut ipv6_header = match MutableIpv6Packet::new(&mut ipv6_buff) { 32 | Some(p) => p, 33 | None => { 34 | return Err(PistolError::BuildPacketError { 35 | location: format!("{}", Location::caller()), 36 | }); 37 | } 38 | }; 39 | ipv6_header.set_version(6); 40 | // In all cases, the IPv6 flow label is 0x12345, on platforms that allow us to set it. 41 | // On platforms that do not (which includes non-Linux Unix platforms when not using Ethernet to send), the flow label will be 0. 42 | ipv6_header.set_flow_label(0x12345); 43 | let payload_length = ICMPV6_ER_HEADER_SIZE + ICMPV6_DATA_SIZE; 44 | ipv6_header.set_payload_length(payload_length as u16); 45 | ipv6_header.set_next_header(IpNextHeaderProtocols::Icmpv6); 46 | ipv6_header.set_hop_limit(TTL); 47 | ipv6_header.set_source(src_ipv6); 48 | ipv6_header.set_destination(dst_ipv6); 49 | 50 | let mut icmpv6_header = match MutableEchoRequestPacket::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) 51 | { 52 | Some(p) => p, 53 | None => { 54 | return Err(PistolError::BuildPacketError { 55 | location: format!("{}", Location::caller()), 56 | }); 57 | } 58 | }; 59 | icmpv6_header.set_icmpv6_type(Icmpv6Type(128)); 60 | icmpv6_header.set_icmpv6_code(Icmpv6Code(0)); 61 | icmpv6_header.set_sequence_number(1); 62 | icmpv6_header.set_identifier(rng.random()); 63 | let mut tv_sec = Utc::now().timestamp().to_be_bytes(); 64 | tv_sec.reverse(); // Big-Endian 65 | let mut tv_usec = Utc::now().timestamp_subsec_millis().to_be_bytes(); 66 | tv_usec.reverse(); // Big-Endian 67 | let mut timestamp = Vec::new(); 68 | timestamp.extend(tv_sec); 69 | timestamp.extend(tv_usec); 70 | // println!("{:?}", timestamp); 71 | icmpv6_header.set_payload(×tamp); 72 | 73 | let mut icmp_header = match MutableIcmpv6Packet::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) { 74 | Some(p) => p, 75 | None => { 76 | return Err(PistolError::BuildPacketError { 77 | location: format!("{}", Location::caller()), 78 | }); 79 | } 80 | }; 81 | let checksum = icmpv6::checksum(&icmp_header.to_immutable(), &src_ipv6, &dst_ipv6); 82 | icmp_header.set_checksum(checksum); 83 | let timeout = None; 84 | 85 | for _ in 0..max_same_packet { 86 | let _ret = layer3_ipv6_send(dst_ipv6, src_ipv6, &ipv6_buff, vec![], timeout, false)?; 87 | } 88 | Ok(ipv6_buff.len() * max_same_packet) 89 | } 90 | -------------------------------------------------------------------------------- /src/os/rr.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | // Each request corresponds to a response, all layer3 packet 4 | #[derive(Debug, Clone)] 5 | pub struct RequestResponse { 6 | // pub name: String, 7 | pub request: Vec, // layer3 8 | pub response: Vec, // layer3, if no response: response.len() == 0 9 | } 10 | 11 | impl RequestResponse { 12 | pub fn empty() -> RequestResponse { 13 | RequestResponse { 14 | request: Vec::new(), 15 | response: Vec::new(), 16 | } 17 | } 18 | } 19 | 20 | #[derive(Debug, Clone)] 21 | pub struct SEQRR { 22 | pub seq1: RequestResponse, 23 | pub seq2: RequestResponse, 24 | pub seq3: RequestResponse, 25 | pub seq4: RequestResponse, 26 | pub seq5: RequestResponse, 27 | pub seq6: RequestResponse, 28 | pub elapsed: f64, 29 | } 30 | 31 | #[derive(Debug, Clone)] 32 | pub struct IERR { 33 | pub ie1: RequestResponse, 34 | pub ie2: RequestResponse, 35 | } 36 | 37 | #[derive(Debug, Clone)] 38 | pub struct ECNRR { 39 | pub ecn: RequestResponse, 40 | } 41 | 42 | #[derive(Debug, Clone)] 43 | pub struct TXRR { 44 | pub t2: RequestResponse, 45 | pub t3: RequestResponse, 46 | pub t4: RequestResponse, 47 | pub t5: RequestResponse, 48 | pub t6: RequestResponse, 49 | pub t7: RequestResponse, 50 | } 51 | 52 | #[derive(Debug, Clone)] 53 | pub struct U1RR { 54 | pub u1: RequestResponse, 55 | } 56 | 57 | #[derive(Debug, Clone)] 58 | pub struct AllPacketRR { 59 | pub seq: SEQRR, 60 | pub ie: IERR, 61 | pub ecn: ECNRR, 62 | pub tx: TXRR, 63 | pub u1: U1RR, 64 | } 65 | 66 | impl AllPacketRR { 67 | pub fn build(&mut self) {} 68 | } 69 | 70 | #[derive(Debug, Clone)] 71 | pub struct NXRR6 { 72 | pub ni: RequestResponse, 73 | pub ns: RequestResponse, 74 | pub sti: Duration, 75 | pub rti: Duration, 76 | pub sts: Duration, 77 | pub rts: Duration, 78 | } 79 | 80 | #[derive(Debug, Clone)] 81 | pub struct TECNRR6 { 82 | pub tecn: RequestResponse, 83 | pub st: Duration, 84 | pub rt: Duration, 85 | } 86 | 87 | #[derive(Debug, Clone)] 88 | pub struct SEQRR6 { 89 | pub seq1: RequestResponse, 90 | pub seq2: RequestResponse, 91 | pub seq3: RequestResponse, 92 | pub seq4: RequestResponse, 93 | pub seq5: RequestResponse, 94 | pub seq6: RequestResponse, 95 | pub elapsed: f64, 96 | pub st1: Duration, 97 | pub rt1: Duration, 98 | pub st2: Duration, 99 | pub rt2: Duration, 100 | pub st3: Duration, 101 | pub rt3: Duration, 102 | pub st4: Duration, 103 | pub rt4: Duration, 104 | pub st5: Duration, 105 | pub rt5: Duration, 106 | pub st6: Duration, 107 | pub rt6: Duration, 108 | } 109 | 110 | #[derive(Debug, Clone)] 111 | pub struct IERR6 { 112 | pub ie1: RequestResponse, 113 | pub ie2: RequestResponse, 114 | pub st1: Duration, 115 | pub rt1: Duration, 116 | pub st2: Duration, 117 | pub rt2: Duration, 118 | } 119 | 120 | #[derive(Debug, Clone)] 121 | pub struct U1RR6 { 122 | pub u1: RequestResponse, 123 | pub st: Duration, 124 | pub rt: Duration, 125 | } 126 | 127 | #[derive(Debug, Clone)] 128 | pub struct TXRR6 { 129 | pub t2: RequestResponse, 130 | pub t3: RequestResponse, 131 | pub t4: RequestResponse, 132 | pub t5: RequestResponse, 133 | pub t6: RequestResponse, 134 | pub t7: RequestResponse, 135 | pub st2: Duration, 136 | pub rt2: Duration, 137 | pub st3: Duration, 138 | pub rt3: Duration, 139 | pub st4: Duration, 140 | pub rt4: Duration, 141 | pub st5: Duration, 142 | pub rt5: Duration, 143 | pub st6: Duration, 144 | pub rt6: Duration, 145 | pub st7: Duration, 146 | pub rt7: Duration, 147 | } 148 | 149 | #[derive(Debug, Clone)] 150 | pub struct AllPacketRR6 { 151 | pub seq: SEQRR6, 152 | pub ie: IERR6, 153 | pub nx: NXRR6, 154 | pub u1: U1RR6, 155 | pub tecn: TECNRR6, 156 | pub tx: TXRR6, 157 | } 158 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::net::{IpAddr, Ipv4Addr}; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug)] 5 | pub enum PistolError { 6 | /* OS DETECT ERROR */ 7 | #[error("calculation of diff vec failed, the input vec length is not enough")] 8 | CalcDiffFailed, 9 | #[error("build {probe_name} ipv4 packet failed")] 10 | BuildIpv4PacketFailed { probe_name: String }, 11 | #[error("build {probe_name} ipv6 packet failed")] 12 | BuildIpv6PacketFailed { probe_name: String }, 13 | #[error("build {probe_name} icmp packet failed")] 14 | BuildIcmpPacketFailed { probe_name: String }, 15 | #[error("build {probe_name} icmpv6 packet failed")] 16 | BuildIcmpv6PacketFailed { probe_name: String }, 17 | #[error("build {probe_name} tcp packet failed")] 18 | BuildTcpPacketFailed { probe_name: String }, 19 | #[error("build udp packet failed")] 20 | BuildUdpPacketFailed { probe_name: String }, 21 | #[error("calculation of isr failed")] 22 | CalcISRFailed, 23 | #[error("calculation of ss failed")] 24 | CalcSSFailed, 25 | #[error("icmp length is not enough")] 26 | CalcUNFailed, 27 | #[error("not enough port value for os detect")] 28 | OSDetectPortsNotEnough, 29 | #[error("os detect results is null")] 30 | OSDetectResultsNullError, 31 | #[error("tsval value length is not enough")] 32 | TsValIsNull, 33 | #[error("system time error")] 34 | SystemTimeError(#[from] std::time::SystemTimeError), 35 | #[error("os db parser error: {name}-{line}")] 36 | OSDBParseError { name: String, line: String }, 37 | #[error("service probes parser error: {name}-{line}")] 38 | ServiceProbesParseError { name: String, line: String }, 39 | #[error("service probes protocol unknown: {protocol}")] 40 | ServiceProbesProtocolUnknown { protocol: String }, 41 | #[error("zip error")] 42 | ZipError(#[from] zip::result::ZipError), 43 | #[error("zip file empty")] 44 | ZipEmptyError, 45 | 46 | /* PING ERROR */ 47 | #[error("The target {target} does not support this detection method {method}")] 48 | PingDetectionMethodError { target: IpAddr, method: String }, 49 | 50 | /* SCAN ERROR */ 51 | #[error( 52 | "idle scan zombie {zombie_ipv4} port {zombie_port} cannot be used because IP ID sequence class is: all zeros, try another proxy" 53 | )] 54 | IdleScanAllZeroError { 55 | zombie_ipv4: Ipv4Addr, 56 | zombie_port: u16, 57 | }, 58 | #[error("serde json error")] 59 | SerdeJsonError(#[from] serde_json::Error), 60 | 61 | /* SERVICE DETECT ERROR */ 62 | #[error("parse int error")] 63 | ParseIntError(#[from] std::num::ParseIntError), 64 | #[error("fancy_regex error")] 65 | FancyRegexError(#[from] fancy_regex::Error), 66 | #[error("no match found")] 67 | NoMatchFound, 68 | #[error("can not unescape string [{s}]: {e}")] 69 | CanNotUnescapeString { s: String, e: String }, 70 | 71 | /* LAYERS ERROR */ 72 | #[error("create datalink channel failed")] 73 | CreateDatalinkChannelFailed, 74 | #[error("can not found the target mac address, please make sure the target is alive")] 75 | CanNotFoundMacAddress, 76 | #[error("can not found the route's mac address")] 77 | CanNotFoundRouteMacAddress, 78 | #[error("can not found the interface")] 79 | CanNotFoundInterface, 80 | #[error("can not found the source address, please set the source address maunal")] 81 | CanNotFoundSourceAddress, 82 | #[error("can not found router address")] 83 | CanNotFoundRouterAddress, 84 | #[error("build packet error occurret at [{location}]")] 85 | BuildPacketError { location: String }, 86 | 87 | /* ROUTE ERROR */ 88 | #[error("subnetwork error")] 89 | RegexError(#[from] regex::Error), 90 | 91 | /* HOP ERROR */ 92 | #[error( 93 | "the destination address {dst_addr} and source address {src_addr} protocols do not match" 94 | )] 95 | AddressProtocolError { dst_addr: IpAddr, src_addr: IpAddr }, 96 | 97 | /* OTHER ERROR */ 98 | #[error("std error")] 99 | IOError(#[from] std::io::Error), 100 | #[error("subnetwork error")] 101 | SubnetworkError(#[from] subnetwork::SubnetworkError), 102 | #[error("hex error")] 103 | FromHexError(#[from] hex::FromHexError), 104 | #[error("pcapture error")] 105 | PcaptureError(#[from] pcapture::error::PcaptureError), 106 | #[error("try lock {var_name} failed: {e}")] 107 | LockVarFailed { var_name: String, e: String }, 108 | #[error("tracing error")] 109 | SetGlobalDefaultError(#[from] tracing::subscriber::SetGlobalDefaultError), 110 | #[error("input {v} is too loog to convert to u32")] 111 | InputTooLoog { v: String }, 112 | } 113 | -------------------------------------------------------------------------------- /src/ping/icmpv6.rs: -------------------------------------------------------------------------------- 1 | use chrono::Utc; 2 | use pnet::packet::Packet; 3 | use pnet::packet::icmpv6; 4 | use pnet::packet::icmpv6::Icmpv6Code; 5 | use pnet::packet::icmpv6::Icmpv6Packet; 6 | use pnet::packet::icmpv6::Icmpv6Types; 7 | use pnet::packet::icmpv6::MutableIcmpv6Packet; 8 | use pnet::packet::icmpv6::echo_request::MutableEchoRequestPacket; 9 | use pnet::packet::ip::IpNextHeaderProtocols; 10 | use pnet::packet::ipv6::Ipv6Packet; 11 | use pnet::packet::ipv6::MutableIpv6Packet; 12 | use rand::Rng; 13 | use std::net::Ipv6Addr; 14 | use std::panic::Location; 15 | use std::time::Duration; 16 | 17 | use crate::error::PistolError; 18 | use crate::layer::ICMPV6_ER_HEADER_SIZE; 19 | use crate::layer::IPV6_HEADER_SIZE; 20 | use crate::layer::Layer3Filter; 21 | use crate::layer::Layer4FilterIcmpv6; 22 | use crate::layer::PacketFilter; 23 | use crate::layer::layer3_ipv6_send; 24 | use crate::ping::PingStatus; 25 | use crate::scan::DataRecvStatus; 26 | 27 | const TTL: u8 = 255; 28 | 29 | pub fn send_icmpv6_ping_packet( 30 | dst_ipv6: Ipv6Addr, 31 | src_ipv6: Ipv6Addr, 32 | timeout: Option, 33 | ) -> Result<(PingStatus, DataRecvStatus, Duration), PistolError> { 34 | const ICMPV6_DATA_SIZE: usize = 16; 35 | let mut rng = rand::rng(); 36 | // ipv6 header 37 | let mut ipv6_buff = [0u8; IPV6_HEADER_SIZE + ICMPV6_ER_HEADER_SIZE + ICMPV6_DATA_SIZE]; 38 | let mut ipv6_header = match MutableIpv6Packet::new(&mut ipv6_buff) { 39 | Some(p) => p, 40 | None => { 41 | return Err(PistolError::BuildPacketError { 42 | location: format!("{}", Location::caller()), 43 | }); 44 | } 45 | }; 46 | ipv6_header.set_version(6); 47 | // In all cases, the IPv6 flow label is 0x12345, on platforms that allow us to set it. 48 | // On platforms that do not (which includes non-Linux Unix platforms when not using Ethernet to send), the flow label will be 0. 49 | ipv6_header.set_flow_label(0x12345); 50 | let payload_length = ICMPV6_ER_HEADER_SIZE + ICMPV6_DATA_SIZE; 51 | ipv6_header.set_payload_length(payload_length as u16); 52 | ipv6_header.set_next_header(IpNextHeaderProtocols::Icmpv6); 53 | ipv6_header.set_hop_limit(TTL); 54 | ipv6_header.set_source(src_ipv6); 55 | ipv6_header.set_destination(dst_ipv6); 56 | 57 | let mut icmpv6_header = match MutableEchoRequestPacket::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) 58 | { 59 | Some(p) => p, 60 | None => { 61 | return Err(PistolError::BuildPacketError { 62 | location: format!("{}", Location::caller()), 63 | }); 64 | } 65 | }; 66 | icmpv6_header.set_icmpv6_type(Icmpv6Types::EchoRequest); 67 | icmpv6_header.set_icmpv6_code(Icmpv6Code(0)); 68 | icmpv6_header.set_identifier(rng.random()); 69 | icmpv6_header.set_sequence_number(1); 70 | let mut tv_sec = Utc::now().timestamp().to_be_bytes(); 71 | tv_sec.reverse(); // Big-Endian 72 | let mut tv_usec = Utc::now().timestamp_subsec_millis().to_be_bytes(); 73 | tv_usec.reverse(); // Big-Endian 74 | let mut timestamp = Vec::new(); 75 | timestamp.extend(tv_sec); 76 | timestamp.extend(tv_usec); 77 | // println!("{:?}", timestamp); 78 | icmpv6_header.set_payload(×tamp); 79 | 80 | let mut icmp_header = match MutableIcmpv6Packet::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) { 81 | Some(p) => p, 82 | None => { 83 | return Err(PistolError::BuildPacketError { 84 | location: format!("{}", Location::caller()), 85 | }); 86 | } 87 | }; 88 | let checksum = icmpv6::checksum(&icmp_header.to_immutable(), &src_ipv6, &dst_ipv6); 89 | icmp_header.set_checksum(checksum); 90 | 91 | let layer3 = Layer3Filter { 92 | name: "ping6 layer3", 93 | layer2: None, 94 | src_addr: Some(dst_ipv6.into()), 95 | dst_addr: Some(src_ipv6.into()), 96 | }; 97 | // match all icmpv6 reply 98 | let layer4_icmpv6 = Layer4FilterIcmpv6 { 99 | name: "ping6 icmpv6", 100 | layer3: Some(layer3), 101 | icmpv6_type: None, 102 | icmpv6_code: None, 103 | payload: None, 104 | }; 105 | let layer_match = PacketFilter::Layer4FilterIcmpv6(layer4_icmpv6); 106 | 107 | let (ret, rtt) = layer3_ipv6_send( 108 | dst_ipv6, 109 | src_ipv6, 110 | &ipv6_buff, 111 | vec![layer_match], 112 | timeout, 113 | true, 114 | )?; 115 | 116 | match Ipv6Packet::new(&ret) { 117 | Some(ipv6_packet) => match ipv6_packet.get_next_header() { 118 | IpNextHeaderProtocols::Icmpv6 => match Icmpv6Packet::new(ipv6_packet.payload()) { 119 | Some(icmpv6_packet) => { 120 | let icmpv6_type = icmpv6_packet.get_icmpv6_type(); 121 | if icmpv6_type == Icmpv6Types::DestinationUnreachable { 122 | return Ok((PingStatus::Down, DataRecvStatus::Yes, rtt)); 123 | } else if icmpv6_type == Icmpv6Types::EchoReply { 124 | return Ok((PingStatus::Up, DataRecvStatus::Yes, rtt)); 125 | } 126 | } 127 | None => (), 128 | }, 129 | _ => (), 130 | }, 131 | None => (), 132 | } 133 | // no response received (even after retransmissions) 134 | Ok((PingStatus::Down, DataRecvStatus::No, rtt)) 135 | } 136 | -------------------------------------------------------------------------------- /src/scan/ndp_ns.rs: -------------------------------------------------------------------------------- 1 | use pnet::datalink::MacAddr; 2 | use pnet::datalink::NetworkInterface; 3 | use pnet::packet::Packet; 4 | use pnet::packet::ethernet::EtherTypes; 5 | use pnet::packet::ethernet::EthernetPacket; 6 | use pnet::packet::icmpv6; 7 | use pnet::packet::icmpv6::Icmpv6Code; 8 | use pnet::packet::icmpv6::Icmpv6Type; 9 | use pnet::packet::icmpv6::Icmpv6Types; 10 | use pnet::packet::icmpv6::MutableIcmpv6Packet; 11 | use pnet::packet::icmpv6::ndp::MutableNeighborSolicitPacket; 12 | use pnet::packet::icmpv6::ndp::NdpOption; 13 | use pnet::packet::icmpv6::ndp::NdpOptionTypes; 14 | use pnet::packet::icmpv6::ndp::NeighborAdvertPacket; 15 | use pnet::packet::ip::IpNextHeaderProtocols; 16 | use pnet::packet::ipv6::Ipv6Packet; 17 | use pnet::packet::ipv6::MutableIpv6Packet; 18 | use std::net::Ipv6Addr; 19 | use std::panic::Location; 20 | use std::time::Duration; 21 | use subnetwork::Ipv6AddrExt; 22 | 23 | use crate::error::PistolError; 24 | use crate::layer::ICMPV6_NS_HEADER_SIZE; 25 | use crate::layer::IPV6_HEADER_SIZE; 26 | use crate::layer::Layer2; 27 | use crate::layer::Layer3Filter; 28 | use crate::layer::Layer4FilterIcmpv6; 29 | use crate::layer::PacketFilter; 30 | use crate::layer::multicast_mac; 31 | 32 | fn get_mac_from_ndp_ns(ethernet_packet: &[u8]) -> Option { 33 | if ethernet_packet.len() == 0 { 34 | return None; 35 | } 36 | // return mac address from ndp 37 | let ethernet_packet = match EthernetPacket::new(ethernet_packet) { 38 | Some(p) => p, 39 | None => return None, 40 | }; 41 | let ipv6_packet = match Ipv6Packet::new(ethernet_packet.payload()) { 42 | Some(p) => p, 43 | None => return None, 44 | }; 45 | let icmpv6_packet = match NeighborAdvertPacket::new(ipv6_packet.payload()) { 46 | Some(p) => p, 47 | None => return None, 48 | }; 49 | for o in icmpv6_packet.get_options() { 50 | let mac = MacAddr::new( 51 | o.data[0], o.data[1], o.data[2], o.data[3], o.data[4], o.data[5], 52 | ); 53 | // println!("{:?}", mac); 54 | return Some(mac); 55 | } 56 | None 57 | } 58 | 59 | pub fn send_ndp_ns_scan_packet( 60 | dst_ipv6: Ipv6Addr, 61 | src_ipv6: Ipv6Addr, 62 | src_mac: MacAddr, 63 | interface: NetworkInterface, 64 | timeout: Option, 65 | ) -> Result<(Option, Duration), PistolError> { 66 | // same as arp in ipv4 67 | // ipv6 68 | let mut ipv6_buff = [0u8; IPV6_HEADER_SIZE + ICMPV6_NS_HEADER_SIZE]; 69 | let mut ipv6_header = match MutableIpv6Packet::new(&mut ipv6_buff) { 70 | Some(p) => p, 71 | None => { 72 | return Err(PistolError::BuildPacketError { 73 | location: format!("{}", Location::caller()), 74 | }); 75 | } 76 | }; 77 | ipv6_header.set_version(6); 78 | ipv6_header.set_traffic_class(0); 79 | ipv6_header.set_flow_label(0); 80 | ipv6_header.set_payload_length(ICMPV6_NS_HEADER_SIZE as u16); 81 | ipv6_header.set_next_header(IpNextHeaderProtocols::Icmpv6); 82 | ipv6_header.set_hop_limit(255); 83 | ipv6_header.set_source(src_ipv6); 84 | let ipv6_ext: Ipv6AddrExt = dst_ipv6.into(); 85 | let dst_multicast = ipv6_ext.link_multicast(); 86 | ipv6_header.set_destination(dst_multicast); 87 | 88 | // icmpv6 89 | let mut icmpv6_header = 90 | match MutableNeighborSolicitPacket::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) { 91 | Some(p) => p, 92 | None => { 93 | return Err(PistolError::BuildPacketError { 94 | location: format!("{}", Location::caller()), 95 | }); 96 | } 97 | }; 98 | // Neighbor Solicitation 99 | icmpv6_header.set_icmpv6_type(Icmpv6Type(135)); 100 | icmpv6_header.set_icmpv6_code(Icmpv6Code(0)); 101 | icmpv6_header.set_reserved(0); 102 | icmpv6_header.set_target_addr(dst_ipv6); 103 | let ndp_option = NdpOption { 104 | option_type: NdpOptionTypes::SourceLLAddr, 105 | length: 1, 106 | data: src_mac.octets().to_vec(), 107 | }; 108 | icmpv6_header.set_options(&vec![ndp_option]); 109 | 110 | let mut icmpv6_header = match MutableIcmpv6Packet::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) { 111 | Some(p) => p, 112 | None => { 113 | return Err(PistolError::BuildPacketError { 114 | location: format!("{}", Location::caller()), 115 | }); 116 | } 117 | }; 118 | let checksum = icmpv6::checksum(&icmpv6_header.to_immutable(), &src_ipv6, &dst_multicast); 119 | icmpv6_header.set_checksum(checksum); 120 | 121 | let layer3 = Layer3Filter { 122 | name: "ndp_ns scan layer3", 123 | layer2: None, 124 | src_addr: Some(dst_ipv6.into()), 125 | dst_addr: Some(src_ipv6.into()), 126 | }; 127 | let layer4_icmpv6 = Layer4FilterIcmpv6 { 128 | name: "ndp_ns scan icmpv6", 129 | layer3: Some(layer3), 130 | icmpv6_type: Some(Icmpv6Types::NeighborAdvert), 131 | icmpv6_code: None, 132 | payload: None, 133 | }; 134 | let filters = vec![PacketFilter::Layer4FilterIcmpv6(layer4_icmpv6.clone())]; 135 | let ether_type = EtherTypes::Ipv6; 136 | let dst_mac = multicast_mac(dst_ipv6); 137 | 138 | let layer2 = Layer2::new(dst_mac, interface, ether_type, filters, timeout, true); 139 | let (ret, rtt) = layer2.send_recv(&ipv6_buff)?; 140 | 141 | let mac = get_mac_from_ndp_ns(&ret); 142 | Ok((mac, rtt)) 143 | } 144 | 145 | #[cfg(feature = "scan")] 146 | #[cfg(test)] 147 | mod tests { 148 | use super::*; 149 | #[test] 150 | fn test_localtion() { 151 | let l = format!("{}", Location::caller()); 152 | println!("{}", l); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use num_cpus; 2 | use pnet::datalink::MacAddr; 3 | use rand::Rng; 4 | use std::net::IpAddr; 5 | use std::net::Ipv4Addr; 6 | use std::net::Ipv6Addr; 7 | use std::time::Duration; 8 | use threadpool::ThreadPool; 9 | use tracing::debug; 10 | use tracing::warn; 11 | 12 | use crate::DEFAULT_TIMEOUT; 13 | use crate::SYSTEM_NET_CACHE; 14 | use crate::error::PistolError; 15 | 16 | const MAX_THREADS: usize = 40960; 17 | 18 | pub fn time_sec_to_string(cost: Duration) -> String { 19 | if cost.as_secs_f64() > 1.0 { 20 | format!("{:.3}s", cost.as_secs_f64()) 21 | } else { 22 | format!("{:.3} ms", cost.as_secs_f64() * 1000.0) 23 | } 24 | } 25 | 26 | pub fn num_threads_check(threads: usize) -> usize { 27 | let mut threads = threads; 28 | if threads > MAX_THREADS { 29 | warn!( 30 | "system try to create too many threads (current threads num: {}, fixed threads num: {}))", 31 | threads, MAX_THREADS 32 | ); 33 | threads = MAX_THREADS; 34 | } 35 | debug!("the number of create threads: {}", threads); 36 | threads 37 | } 38 | 39 | pub fn neigh_cache_update(addr: IpAddr, mac: MacAddr) -> Result<(), PistolError> { 40 | // release the lock when leaving the function 41 | let mut snc = match SYSTEM_NET_CACHE.lock() { 42 | Ok(snc) => snc, 43 | Err(e) => { 44 | return Err(PistolError::LockVarFailed { 45 | var_name: String::from("SYSTEM_NET_CACHE"), 46 | e: e.to_string(), 47 | }); 48 | } 49 | }; 50 | let _ = snc.update_neighbor_cache(addr, mac); 51 | debug!("update neighbor cache finish: {:?}", (*snc)); 52 | Ok(()) 53 | } 54 | 55 | /// Returns the random port 56 | pub fn random_port() -> u16 { 57 | let mut rng = rand::rng(); 58 | rng.random_range(10000..=65535) 59 | } 60 | 61 | /// Returns the random ipv4 addr 62 | pub fn random_ipv4_addr() -> Ipv4Addr { 63 | let mut rng = rand::rng(); 64 | let x0 = rng.random_range(0..=255); 65 | let x1 = rng.random_range(0..=255); 66 | let x2 = rng.random_range(0..=255); 67 | let x3 = rng.random_range(0..=255); 68 | Ipv4Addr::new(x0, x1, x2, x3) 69 | } 70 | 71 | /// Returns the random ipv6 addr 72 | pub fn random_ipv6_addr() -> Ipv6Addr { 73 | let mut rng = rand::rng(); 74 | let x0 = rng.random_range(0..=65535); 75 | let x1 = rng.random_range(0..=65535); 76 | let x2 = rng.random_range(0..=65535); 77 | let x3 = rng.random_range(0..=65535); 78 | let x4 = rng.random_range(0..=65535); 79 | let x5 = rng.random_range(0..=65535); 80 | let x6 = rng.random_range(0..=65535); 81 | let x7 = rng.random_range(0..=65535); 82 | Ipv6Addr::new(x0, x1, x2, x3, x4, x5, x6, x7) 83 | } 84 | 85 | /// Returns the random port with range 86 | pub fn random_port_range(start: u16, end: u16) -> u16 { 87 | let mut rng = rand::rng(); 88 | rng.random_range(start..=end) 89 | } 90 | 91 | /// Returns the number of CPUs in the machine 92 | pub fn get_cpu_num() -> usize { 93 | num_cpus::get() 94 | } 95 | 96 | pub fn get_threads_pool(threads: usize) -> ThreadPool { 97 | let pool = if threads > 0 { 98 | ThreadPool::new(threads) 99 | } else { 100 | let cpus = get_cpu_num(); 101 | ThreadPool::new(cpus) 102 | }; 103 | pool 104 | } 105 | 106 | pub fn get_default_timeout() -> Duration { 107 | Duration::from_secs_f32(DEFAULT_TIMEOUT) 108 | } 109 | 110 | pub struct PistolHex { 111 | pub hex: String, // hex => dec 112 | } 113 | 114 | impl PistolHex { 115 | pub fn new_hex(hex_str: &str) -> PistolHex { 116 | let hex_str_len = hex_str.len(); 117 | let after_fix = if hex_str_len % 2 == 1 { 118 | format!("0{}", hex_str) 119 | } else { 120 | hex_str.to_string() 121 | }; 122 | PistolHex { hex: after_fix } 123 | } 124 | pub fn be_vec_to_u32(input: &[u8]) -> Result { 125 | if input.len() <= 4 { 126 | let padding_size = 4 - input.len(); 127 | let mut after_padding = vec![0; padding_size]; 128 | for &i in input { 129 | after_padding.push(i as u32); 130 | } 131 | let a = after_padding[0] << 24; 132 | let b = after_padding[1] << 16; 133 | let c = after_padding[2] << 8; 134 | let d = after_padding[3]; 135 | Ok(a + b + c + d) 136 | } else { 137 | let v = format!("{:?}", input); 138 | Err(PistolError::InputTooLoog { v }) 139 | } 140 | } 141 | pub fn decode_as_u32(&self) -> Result { 142 | match hex::decode(&self.hex) { 143 | Ok(d) => Self::be_vec_to_u32(&d), 144 | Err(e) => Err(e.into()), 145 | } 146 | } 147 | } 148 | 149 | #[cfg(test)] 150 | mod tests { 151 | use super::*; 152 | use pnet::datalink::interfaces; 153 | #[test] 154 | fn test_convert() { 155 | let v: Vec = vec![1, 1, 1, 1]; 156 | let r = PistolHex::be_vec_to_u32(&v).unwrap(); 157 | assert_eq!(r, 16843009); 158 | 159 | let s = "51E80C"; 160 | let h = PistolHex::new_hex(s); 161 | let r = h.decode_as_u32().unwrap(); 162 | assert_eq!(r, 5367820); 163 | 164 | let s = "1C"; 165 | let h = PistolHex::new_hex(s); 166 | let r = h.decode_as_u32().unwrap(); 167 | assert_eq!(r, 28); 168 | 169 | let s = "A"; 170 | let h = PistolHex::new_hex(s); 171 | let r = h.decode_as_u32().unwrap(); 172 | assert_eq!(r, 10); 173 | } 174 | #[test] 175 | fn test_get_cpus() { 176 | let cpus = get_cpu_num(); 177 | println!("{}", cpus); 178 | } 179 | #[test] 180 | fn interface_loopback() { 181 | for interface in interfaces() { 182 | if interface.is_loopback() { 183 | println!("{} is loopback interface", interface); 184 | } 185 | } 186 | } 187 | #[test] 188 | fn interface_list() { 189 | for interface in interfaces() { 190 | println!("list interface: {}, {:?}", interface.name, interface.ips); 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/trace/icmp.rs: -------------------------------------------------------------------------------- 1 | use pnet::packet::Packet; 2 | use pnet::packet::icmp; 3 | use pnet::packet::icmp::IcmpCode; 4 | use pnet::packet::icmp::IcmpPacket; 5 | use pnet::packet::icmp::IcmpTypes; 6 | use pnet::packet::icmp::MutableIcmpPacket; 7 | use pnet::packet::icmp::echo_request::MutableEchoRequestPacket; 8 | use pnet::packet::ip::IpNextHeaderProtocols; 9 | use pnet::packet::ipv4; 10 | use pnet::packet::ipv4::Ipv4Flags; 11 | use pnet::packet::ipv4::Ipv4Packet; 12 | use pnet::packet::ipv4::MutableIpv4Packet; 13 | use std::panic::Location; 14 | 15 | use std::net::Ipv4Addr; 16 | use std::time::Duration; 17 | 18 | use crate::error::PistolError; 19 | use crate::layer::ICMP_HEADER_SIZE; 20 | use crate::layer::IPV4_HEADER_SIZE; 21 | use crate::layer::Layer3Filter; 22 | use crate::layer::Layer4FilterIcmp; 23 | use crate::layer::PacketFilter; 24 | use crate::layer::PayloadMatch; 25 | use crate::layer::PayloadMatchIcmp; 26 | use crate::layer::PayloadMatchIp; 27 | use crate::layer::layer3_ipv4_send; 28 | use crate::trace::HopStatus; 29 | 30 | pub fn send_icmp_trace_packet( 31 | dst_ipv4: Ipv4Addr, 32 | src_ipv4: Ipv4Addr, 33 | ip_id: u16, 34 | ttl: u8, 35 | icmp_id: u16, 36 | seq: u16, 37 | timeout: Option, 38 | ) -> Result<(HopStatus, Duration), PistolError> { 39 | const ICMP_DATA_SIZE: usize = 32; 40 | // ip header 41 | let mut ip_buff = [0u8; IPV4_HEADER_SIZE + ICMP_HEADER_SIZE + ICMP_DATA_SIZE]; 42 | let mut ip_header = match MutableIpv4Packet::new(&mut ip_buff) { 43 | Some(p) => p, 44 | None => { 45 | return Err(PistolError::BuildPacketError { 46 | location: format!("{}", Location::caller()), 47 | }); 48 | } 49 | }; 50 | ip_header.set_version(4); 51 | ip_header.set_header_length(5); 52 | ip_header.set_source(src_ipv4); 53 | ip_header.set_destination(dst_ipv4); 54 | ip_header.set_total_length((IPV4_HEADER_SIZE + ICMP_HEADER_SIZE + ICMP_DATA_SIZE) as u16); 55 | ip_header.set_identification(ip_id); 56 | ip_header.set_flags(Ipv4Flags::DontFragment); 57 | ip_header.set_ttl(ttl); 58 | ip_header.set_next_level_protocol(IpNextHeaderProtocols::Icmp); 59 | let c = ipv4::checksum(&ip_header.to_immutable()); 60 | ip_header.set_checksum(c); 61 | 62 | let mut icmp_header = match MutableEchoRequestPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 63 | Some(p) => p, 64 | None => { 65 | return Err(PistolError::BuildPacketError { 66 | location: format!("{}", Location::caller()), 67 | }); 68 | } 69 | }; 70 | icmp_header.set_icmp_type(IcmpTypes::EchoRequest); // echo 71 | icmp_header.set_icmp_code(IcmpCode(0)); 72 | icmp_header.set_identifier(icmp_id); 73 | icmp_header.set_sequence_number(seq); 74 | let icmp_payload = "HIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefg"; 75 | assert_eq!(icmp_payload.len(), ICMP_DATA_SIZE); 76 | icmp_header.set_payload(&icmp_payload.as_bytes()); 77 | 78 | let mut icmp_header = match MutableIcmpPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 79 | Some(p) => p, 80 | None => { 81 | return Err(PistolError::BuildPacketError { 82 | location: format!("{}", Location::caller()), 83 | }); 84 | } 85 | }; 86 | let checksum = icmp::checksum(&icmp_header.to_immutable()); 87 | icmp_header.set_checksum(checksum); 88 | 89 | // time exceeded packet 90 | let layer3 = Layer3Filter { 91 | name: "icmp trace time exceeded layer3", 92 | layer2: None, 93 | src_addr: None, // usually this is the address of the router, not the address of the target machine. 94 | dst_addr: Some(src_ipv4.into()), 95 | }; 96 | let payload_ip = PayloadMatchIp { 97 | src_addr: Some(src_ipv4.into()), 98 | dst_addr: Some(dst_ipv4.into()), 99 | }; 100 | let payload_icmp = PayloadMatchIcmp { 101 | layer3: Some(payload_ip), 102 | icmp_type: Some(IcmpTypes::EchoRequest), 103 | icmp_code: None, 104 | }; 105 | let payload = PayloadMatch::PayloadMatchIcmp(payload_icmp); 106 | let layer4_icmp = Layer4FilterIcmp { 107 | name: "icmp trace time exceeded icmp", 108 | layer3: Some(layer3), 109 | icmp_type: Some(IcmpTypes::TimeExceeded), 110 | icmp_code: None, 111 | payload: Some(payload), 112 | }; 113 | let layer_match_icmp_time_exceeded = PacketFilter::Layer4FilterIcmp(layer4_icmp); 114 | 115 | // icmp reply 116 | let layer3 = Layer3Filter { 117 | name: "icmp trace reply layer3", 118 | layer2: None, 119 | src_addr: Some(dst_ipv4.into()), 120 | dst_addr: Some(src_ipv4.into()), 121 | }; 122 | let layer4_icmp = Layer4FilterIcmp { 123 | name: "icmp trace reply icmp", 124 | layer3: Some(layer3), 125 | icmp_type: Some(IcmpTypes::EchoReply), 126 | icmp_code: None, 127 | payload: None, 128 | }; 129 | let layer_match_icmp_reply = PacketFilter::Layer4FilterIcmp(layer4_icmp); 130 | 131 | let (ret, rtt) = layer3_ipv4_send( 132 | dst_ipv4, 133 | src_ipv4, 134 | &ip_buff, 135 | vec![layer_match_icmp_time_exceeded, layer_match_icmp_reply], 136 | timeout, 137 | true, 138 | )?; 139 | match Ipv4Packet::new(&ret) { 140 | Some(ipv4_packet) => match ipv4_packet.get_next_level_protocol() { 141 | IpNextHeaderProtocols::Icmp => match IcmpPacket::new(ipv4_packet.payload()) { 142 | Some(icmp_packet) => { 143 | let icmp_type = icmp_packet.get_icmp_type(); 144 | let ret_ip = ipv4_packet.get_source(); 145 | if icmp_type == IcmpTypes::TimeExceeded { 146 | return Ok((HopStatus::TimeExceeded(ret_ip.into()), rtt)); 147 | } else if icmp_type == IcmpTypes::EchoReply { 148 | return Ok((HopStatus::RecvReply(ret_ip.into()), rtt)); 149 | } 150 | } 151 | None => (), 152 | }, 153 | _ => (), 154 | }, 155 | None => (), 156 | } 157 | Ok((HopStatus::NoResponse, rtt)) 158 | } 159 | -------------------------------------------------------------------------------- /src/scan/udp6.rs: -------------------------------------------------------------------------------- 1 | use pnet::packet::Packet; 2 | use pnet::packet::icmpv6::Icmpv6Code; 3 | use pnet::packet::icmpv6::Icmpv6Packet; 4 | use pnet::packet::icmpv6::Icmpv6Types; 5 | use pnet::packet::ip::IpNextHeaderProtocols; 6 | use pnet::packet::ipv6::Ipv6Packet; 7 | use pnet::packet::ipv6::MutableIpv6Packet; 8 | use pnet::packet::udp::MutableUdpPacket; 9 | use pnet::packet::udp::ipv6_checksum; 10 | use std::net::Ipv6Addr; 11 | use std::panic::Location; 12 | use std::time::Duration; 13 | 14 | use crate::error::PistolError; 15 | use crate::layer::IPV6_HEADER_SIZE; 16 | use crate::layer::Layer3Filter; 17 | use crate::layer::Layer4FilterIcmpv6; 18 | use crate::layer::Layer4FilterTcpUdp; 19 | use crate::layer::PacketFilter; 20 | use crate::layer::PayloadMatch; 21 | use crate::layer::PayloadMatchIp; 22 | use crate::layer::PayloadMatchTcpUdp; 23 | use crate::layer::UDP_HEADER_SIZE; 24 | use crate::layer::layer3_ipv6_send; 25 | use crate::scan::DataRecvStatus; 26 | use crate::scan::PortStatus; 27 | 28 | const UDP_DATA_SIZE: usize = 0; 29 | const TTL: u8 = 255; 30 | 31 | pub fn send_udp_scan_packet( 32 | dst_ipv6: Ipv6Addr, 33 | dst_port: u16, 34 | src_ipv6: Ipv6Addr, 35 | src_port: u16, 36 | timeout: Option, 37 | ) -> Result<(PortStatus, DataRecvStatus, Duration), PistolError> { 38 | // ipv6 header 39 | let mut ipv6_buff = [0u8; IPV6_HEADER_SIZE + UDP_HEADER_SIZE + UDP_DATA_SIZE]; 40 | let mut ipv6_header = match MutableIpv6Packet::new(&mut ipv6_buff) { 41 | Some(p) => p, 42 | None => { 43 | return Err(PistolError::BuildPacketError { 44 | location: format!("{}", Location::caller()), 45 | }); 46 | } 47 | }; 48 | ipv6_header.set_version(6); 49 | // In all cases, the IPv6 flow label is 0x12345, on platforms that allow us to set it. 50 | // On platforms that do not (which includes non-Linux Unix platforms when not using Ethernet to send), the flow label will be 0. 51 | ipv6_header.set_flow_label(0x12345); 52 | let payload_length = UDP_HEADER_SIZE + UDP_DATA_SIZE; 53 | ipv6_header.set_payload_length(payload_length as u16); 54 | ipv6_header.set_next_header(IpNextHeaderProtocols::Udp); 55 | ipv6_header.set_hop_limit(TTL); 56 | ipv6_header.set_source(src_ipv6); 57 | ipv6_header.set_destination(dst_ipv6); 58 | 59 | // udp header 60 | let mut udp_header = match MutableUdpPacket::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) { 61 | Some(p) => p, 62 | None => { 63 | return Err(PistolError::BuildPacketError { 64 | location: format!("{}", Location::caller()), 65 | }); 66 | } 67 | }; 68 | udp_header.set_source(src_port); 69 | udp_header.set_destination(dst_port); 70 | udp_header.set_length((UDP_HEADER_SIZE + UDP_DATA_SIZE) as u16); 71 | let checksum = ipv6_checksum(&udp_header.to_immutable(), &src_ipv6, &dst_ipv6); 72 | udp_header.set_checksum(checksum); 73 | 74 | let layer3 = Layer3Filter { 75 | name: "udp6 scan layer3", 76 | layer2: None, 77 | src_addr: Some(dst_ipv6.into()), 78 | dst_addr: Some(src_ipv6.into()), 79 | }; 80 | let layer4_tcp_udp = Layer4FilterTcpUdp { 81 | name: "udp6 scan tcp_udp", 82 | layer3: Some(layer3), 83 | src_port: Some(dst_port), 84 | dst_port: Some(src_port), 85 | }; 86 | // set the icmp payload matchs 87 | let payload_ip = PayloadMatchIp { 88 | src_addr: Some(src_ipv6.into()), 89 | dst_addr: Some(dst_ipv6.into()), 90 | }; 91 | let payload_tcp_udp = PayloadMatchTcpUdp { 92 | layer3: Some(payload_ip), 93 | src_port: Some(src_port), 94 | dst_port: Some(dst_port), 95 | }; 96 | let payload = PayloadMatch::PayloadMatchTcpUdp(payload_tcp_udp); 97 | let layer4_icmpv6 = Layer4FilterIcmpv6 { 98 | name: "udp6 scan icmpv6", 99 | layer3: Some(layer3), 100 | icmpv6_type: None, 101 | icmpv6_code: None, 102 | payload: Some(payload), 103 | }; 104 | let layer_match_1 = PacketFilter::Layer4FilterTcpUdp(layer4_tcp_udp); 105 | let layer_match_2 = PacketFilter::Layer4FilterIcmpv6(layer4_icmpv6); 106 | 107 | let codes_1 = vec![ 108 | Icmpv6Code(4), // port unreachable 109 | ]; 110 | let codes_2 = vec![ 111 | Icmpv6Code(1), // communication with destination administratively prohibited 112 | Icmpv6Code(3), // address unreachable 113 | ]; 114 | 115 | let (ret, rtt) = layer3_ipv6_send( 116 | dst_ipv6, 117 | src_ipv6, 118 | &ipv6_buff, 119 | vec![layer_match_1, layer_match_2], 120 | timeout, 121 | true, 122 | )?; 123 | match Ipv6Packet::new(&ret) { 124 | Some(ipv6_packet) => { 125 | match ipv6_packet.get_next_header() { 126 | IpNextHeaderProtocols::Udp => { 127 | // any udp response from target port (unusual) 128 | return Ok((PortStatus::Open, DataRecvStatus::Yes, rtt)); 129 | } 130 | IpNextHeaderProtocols::Icmpv6 => { 131 | match Icmpv6Packet::new(ipv6_packet.payload()) { 132 | Some(icmpv6_packet) => { 133 | let icmpv6_type = icmpv6_packet.get_icmpv6_type(); 134 | let icmpv6_code = icmpv6_packet.get_icmpv6_code(); 135 | if icmpv6_type == Icmpv6Types::DestinationUnreachable { 136 | if codes_1.contains(&icmpv6_code) { 137 | // icmpv6 port unreachable error (type 1, code 4) 138 | return Ok((PortStatus::Closed, DataRecvStatus::Yes, rtt)); 139 | } else if codes_2.contains(&icmpv6_code) { 140 | return Ok((PortStatus::Filtered, DataRecvStatus::Yes, rtt)); 141 | } 142 | } 143 | } 144 | None => (), 145 | } 146 | } 147 | _ => (), 148 | } 149 | } 150 | None => (), 151 | } 152 | // no response received (even after retransmissions) 153 | Ok((PortStatus::OpenOrFiltered, DataRecvStatus::No, rtt)) 154 | } 155 | -------------------------------------------------------------------------------- /src/trace/icmpv6.rs: -------------------------------------------------------------------------------- 1 | use pnet::packet::Packet; 2 | use pnet::packet::icmpv6; 3 | use pnet::packet::icmpv6::Icmpv6Code; 4 | use pnet::packet::icmpv6::Icmpv6Packet; 5 | use pnet::packet::icmpv6::Icmpv6Types; 6 | use pnet::packet::icmpv6::MutableIcmpv6Packet; 7 | use pnet::packet::icmpv6::echo_request::MutableEchoRequestPacket; 8 | use pnet::packet::ip::IpNextHeaderProtocols; 9 | use pnet::packet::ipv6::Ipv6Packet; 10 | use pnet::packet::ipv6::MutableIpv6Packet; 11 | use std::net::Ipv6Addr; 12 | use std::panic::Location; 13 | use std::time::Duration; 14 | 15 | use crate::error::PistolError; 16 | use crate::layer::ICMPV6_ER_HEADER_SIZE; 17 | use crate::layer::IPV6_HEADER_SIZE; 18 | use crate::layer::Layer3Filter; 19 | use crate::layer::Layer4FilterIcmpv6; 20 | use crate::layer::PacketFilter; 21 | use crate::layer::PayloadMatch; 22 | use crate::layer::PayloadMatchIcmpv6; 23 | use crate::layer::PayloadMatchIp; 24 | use crate::layer::layer3_ipv6_send; 25 | use crate::trace::HopStatus; 26 | 27 | pub fn send_icmpv6_trace_packet( 28 | dst_ipv6: Ipv6Addr, 29 | src_ipv6: Ipv6Addr, 30 | hop_limit: u8, 31 | icmpv6_id: u16, 32 | seq: u16, 33 | timeout: Option, 34 | ) -> Result<(HopStatus, Duration), PistolError> { 35 | const ICMPV6_DATA_SIZE: usize = 32; 36 | // ipv6 header 37 | let mut ipv6_buff = [0u8; IPV6_HEADER_SIZE + ICMPV6_ER_HEADER_SIZE + ICMPV6_DATA_SIZE]; 38 | let mut ipv6_header = match MutableIpv6Packet::new(&mut ipv6_buff) { 39 | Some(p) => p, 40 | None => { 41 | return Err(PistolError::BuildPacketError { 42 | location: format!("{}", Location::caller()), 43 | }); 44 | } 45 | }; 46 | ipv6_header.set_version(6); 47 | // In all cases, the IPv6 flow label is 0x12345, on platforms that allow us to set it. 48 | // On platforms that do not (which includes non-Linux Unix platforms when not using Ethernet to send), the flow label will be 0. 49 | ipv6_header.set_flow_label(0x12345); 50 | let payload_length = ICMPV6_ER_HEADER_SIZE + ICMPV6_DATA_SIZE; 51 | ipv6_header.set_payload_length(payload_length as u16); 52 | ipv6_header.set_next_header(IpNextHeaderProtocols::Icmpv6); 53 | ipv6_header.set_hop_limit(hop_limit); 54 | ipv6_header.set_source(src_ipv6); 55 | ipv6_header.set_destination(dst_ipv6); 56 | 57 | let mut icmpv6_header = match MutableEchoRequestPacket::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) 58 | { 59 | Some(p) => p, 60 | None => { 61 | return Err(PistolError::BuildPacketError { 62 | location: format!("{}", Location::caller()), 63 | }); 64 | } 65 | }; 66 | icmpv6_header.set_icmpv6_type(Icmpv6Types::EchoRequest); 67 | icmpv6_header.set_icmpv6_code(Icmpv6Code(0)); 68 | icmpv6_header.set_identifier(icmpv6_id); 69 | icmpv6_header.set_sequence_number(seq); 70 | let icmpv6_payload = "HIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefg"; 71 | assert_eq!(icmpv6_payload.len(), ICMPV6_DATA_SIZE); 72 | icmpv6_header.set_payload(&icmpv6_payload.as_bytes()); 73 | 74 | let mut icmp_header = match MutableIcmpv6Packet::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) { 75 | Some(p) => p, 76 | None => { 77 | return Err(PistolError::BuildPacketError { 78 | location: format!("{}", Location::caller()), 79 | }); 80 | } 81 | }; 82 | let checksum = icmpv6::checksum(&icmp_header.to_immutable(), &src_ipv6, &dst_ipv6); 83 | icmp_header.set_checksum(checksum); 84 | 85 | // time exceeded packet 86 | let layer3 = Layer3Filter { 87 | name: "icmpv6 trace time exceeded layer3", 88 | layer2: None, 89 | src_addr: None, // usually this is the address of the router, not the address of the target machine. 90 | dst_addr: Some(src_ipv6.into()), 91 | }; 92 | let payload_ip = PayloadMatchIp { 93 | src_addr: Some(src_ipv6.into()), 94 | dst_addr: Some(dst_ipv6.into()), 95 | }; 96 | let payload_icmp = PayloadMatchIcmpv6 { 97 | layer3: Some(payload_ip), 98 | icmpv6_type: Some(Icmpv6Types::EchoRequest), 99 | icmpv6_code: None, 100 | }; 101 | let payload = PayloadMatch::PayloadMatchIcmpv6(payload_icmp); 102 | let layer4_icmp = Layer4FilterIcmpv6 { 103 | name: "icmpv6 trace time exceeded icmpv6", 104 | layer3: Some(layer3), 105 | icmpv6_type: Some(Icmpv6Types::TimeExceeded), 106 | icmpv6_code: None, 107 | payload: Some(payload), 108 | }; 109 | let layer_match_icmp_time_exceeded = PacketFilter::Layer4FilterIcmpv6(layer4_icmp); 110 | 111 | // icmp reply 112 | let layer3 = Layer3Filter { 113 | name: "icmpv6 trace reply layer3", 114 | layer2: None, 115 | src_addr: Some(dst_ipv6.into()), 116 | dst_addr: Some(src_ipv6.into()), 117 | }; 118 | let layer4_icmpv6 = Layer4FilterIcmpv6 { 119 | name: "icmpv6 trace reply icmpv6", 120 | layer3: Some(layer3), 121 | icmpv6_type: None, 122 | icmpv6_code: None, 123 | payload: None, 124 | }; 125 | let layer_match_icmp_reply = PacketFilter::Layer4FilterIcmpv6(layer4_icmpv6); 126 | 127 | let (ret, rtt) = layer3_ipv6_send( 128 | dst_ipv6, 129 | src_ipv6, 130 | &ipv6_buff, 131 | vec![layer_match_icmp_time_exceeded, layer_match_icmp_reply], 132 | timeout, 133 | true, 134 | )?; 135 | 136 | match Ipv6Packet::new(&ret) { 137 | Some(ipv6_packet) => match ipv6_packet.get_next_header() { 138 | IpNextHeaderProtocols::Icmpv6 => match Icmpv6Packet::new(ipv6_packet.payload()) { 139 | Some(icmpv6_packet) => { 140 | let icmp_type = icmpv6_packet.get_icmpv6_type(); 141 | let ret_ip = ipv6_packet.get_source(); 142 | if icmp_type == Icmpv6Types::TimeExceeded { 143 | return Ok((HopStatus::TimeExceeded(ret_ip.into()), rtt)); 144 | } else if icmp_type == Icmpv6Types::EchoReply { 145 | return Ok((HopStatus::RecvReply(ret_ip.into()), rtt)); 146 | } 147 | } 148 | None => (), 149 | }, 150 | _ => (), 151 | }, 152 | None => (), 153 | } 154 | Ok((HopStatus::NoResponse, rtt)) 155 | } 156 | -------------------------------------------------------------------------------- /src/trace/tcp6.rs: -------------------------------------------------------------------------------- 1 | use pnet::packet::Packet; 2 | use pnet::packet::icmpv6::Icmpv6Packet; 3 | use pnet::packet::icmpv6::Icmpv6Types; 4 | use pnet::packet::ip::IpNextHeaderProtocols; 5 | use pnet::packet::ipv6::Ipv6Packet; 6 | use pnet::packet::ipv6::MutableIpv6Packet; 7 | use pnet::packet::tcp::MutableTcpPacket; 8 | use pnet::packet::tcp::TcpFlags; 9 | use pnet::packet::tcp::TcpOption; 10 | use pnet::packet::tcp::ipv6_checksum; 11 | use rand::Rng; 12 | use std::net::Ipv6Addr; 13 | use std::panic::Location; 14 | use std::time::Duration; 15 | 16 | use crate::error::PistolError; 17 | use crate::layer::IPV6_HEADER_SIZE; 18 | use crate::layer::Layer3Filter; 19 | use crate::layer::Layer4FilterIcmpv6; 20 | use crate::layer::Layer4FilterTcpUdp; 21 | use crate::layer::PacketFilter; 22 | use crate::layer::PayloadMatch; 23 | use crate::layer::PayloadMatchIp; 24 | use crate::layer::PayloadMatchTcpUdp; 25 | use crate::layer::TCP_HEADER_SIZE; 26 | use crate::layer::layer3_ipv6_send; 27 | use crate::trace::HopStatus; 28 | 29 | const TCP_DATA_SIZE: usize = 0; 30 | // TCP options size 31 | const NOP_SIZE: usize = 1; 32 | const MSS_SIZE: usize = 4; 33 | const WSCALE_SIZE: usize = 3; 34 | const TIMESTAMP_SIZE: usize = 10; 35 | const SACK_PERM_SIZE: usize = 2; 36 | 37 | pub fn send_syn_trace_packet( 38 | dst_ipv6: Ipv6Addr, 39 | dst_port: u16, 40 | src_ipv6: Ipv6Addr, 41 | src_port: u16, 42 | hop_limit: u8, 43 | timeout: Option, 44 | ) -> Result<(HopStatus, Duration), PistolError> { 45 | const TCP_OPTIONS_SIZE: usize = 46 | MSS_SIZE + SACK_PERM_SIZE + TIMESTAMP_SIZE + NOP_SIZE + WSCALE_SIZE; 47 | 48 | let mut rng = rand::rng(); 49 | // ipv6 header 50 | let mut ipv6_buff = 51 | [0u8; IPV6_HEADER_SIZE + TCP_HEADER_SIZE + TCP_OPTIONS_SIZE + TCP_DATA_SIZE]; 52 | let mut ipv6_header = match MutableIpv6Packet::new(&mut ipv6_buff) { 53 | Some(p) => p, 54 | None => { 55 | return Err(PistolError::BuildPacketError { 56 | location: format!("{}", Location::caller()), 57 | }); 58 | } 59 | }; 60 | ipv6_header.set_version(6); 61 | // In all cases, the IPv6 flow label is 0x12345, on platforms that allow us to set it. 62 | // On platforms that do not (which includes non-Linux Unix platforms when not using Ethernet to send), the flow label will be 0. 63 | ipv6_header.set_flow_label(0x12345); 64 | let payload_length = TCP_HEADER_SIZE + TCP_OPTIONS_SIZE + TCP_DATA_SIZE; 65 | ipv6_header.set_payload_length(payload_length as u16); 66 | ipv6_header.set_next_header(IpNextHeaderProtocols::Tcp); 67 | ipv6_header.set_hop_limit(hop_limit); 68 | ipv6_header.set_source(src_ipv6); 69 | ipv6_header.set_destination(dst_ipv6); 70 | 71 | // tcp header 72 | let mut tcp_header = match MutableTcpPacket::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) { 73 | Some(p) => p, 74 | None => { 75 | return Err(PistolError::BuildPacketError { 76 | location: format!("{}", Location::caller()), 77 | }); 78 | } 79 | }; 80 | tcp_header.set_source(src_port); 81 | tcp_header.set_destination(dst_port); 82 | tcp_header.set_sequence(rng.random()); 83 | tcp_header.set_acknowledgement(0); 84 | tcp_header.set_reserved(0); 85 | tcp_header.set_flags(TcpFlags::SYN); 86 | tcp_header.set_urgent_ptr(0); 87 | tcp_header.set_window(5840); 88 | tcp_header.set_data_offset(10); 89 | tcp_header.set_options(&vec![ 90 | TcpOption::mss(1460), 91 | TcpOption::sack_perm(), 92 | TcpOption::timestamp(2017864790, 0x0), 93 | TcpOption::nop(), 94 | TcpOption::wscale(2), 95 | ]); 96 | let checksum = ipv6_checksum(&tcp_header.to_immutable(), &src_ipv6, &dst_ipv6); 97 | tcp_header.set_checksum(checksum); 98 | 99 | // time exceeded packet 100 | let layer3 = Layer3Filter { 101 | name: "tcp6 trace time exceeded layer3", 102 | layer2: None, 103 | src_addr: None, // usually this is the address of the router, not the address of the target machine. 104 | dst_addr: Some(src_ipv6.into()), 105 | }; 106 | let payload_ip = PayloadMatchIp { 107 | src_addr: Some(src_ipv6.into()), 108 | dst_addr: Some(dst_ipv6.into()), 109 | }; 110 | let payload_tcp_udp = PayloadMatchTcpUdp { 111 | layer3: Some(payload_ip), 112 | src_port: Some(src_port), 113 | dst_port: Some(dst_port), 114 | }; 115 | let payload = PayloadMatch::PayloadMatchTcpUdp(payload_tcp_udp); 116 | let layer4_icmpv6 = Layer4FilterIcmpv6 { 117 | name: "tcp6 trace time exceeded icmpv6", 118 | layer3: Some(layer3), 119 | icmpv6_type: Some(Icmpv6Types::TimeExceeded), 120 | icmpv6_code: None, 121 | payload: Some(payload), 122 | }; 123 | let layer_match_icmpv6_time_exceeded = PacketFilter::Layer4FilterIcmpv6(layer4_icmpv6); 124 | 125 | // tcp syn, ack or rst packet 126 | let layer3 = Layer3Filter { 127 | name: "tcp6 trace reply layer3", 128 | layer2: None, 129 | src_addr: Some(dst_ipv6.into()), 130 | dst_addr: Some(src_ipv6.into()), 131 | }; 132 | let layer4_tcp_udp = Layer4FilterTcpUdp { 133 | name: "tcp6 trace reply tcp_udp", 134 | layer3: Some(layer3), 135 | src_port: Some(dst_port), 136 | dst_port: Some(src_port), 137 | }; 138 | let layer_match_tcp = PacketFilter::Layer4FilterTcpUdp(layer4_tcp_udp); 139 | 140 | let (ret, rtt) = layer3_ipv6_send( 141 | dst_ipv6, 142 | src_ipv6, 143 | &ipv6_buff, 144 | vec![layer_match_icmpv6_time_exceeded, layer_match_tcp], 145 | timeout, 146 | true, 147 | )?; 148 | 149 | match Ipv6Packet::new(&ret) { 150 | Some(ipv6_packet) => match ipv6_packet.get_next_header() { 151 | IpNextHeaderProtocols::Tcp => { 152 | let ret_ip = ipv6_packet.get_source(); 153 | return Ok((HopStatus::RecvReply(ret_ip.into()), rtt)); 154 | } 155 | IpNextHeaderProtocols::Icmpv6 => match Icmpv6Packet::new(ipv6_packet.payload()) { 156 | Some(icmpv6_packet) => { 157 | let icmpv6_type = icmpv6_packet.get_icmpv6_type(); 158 | let ret_ip = ipv6_packet.get_source(); 159 | if icmpv6_type == Icmpv6Types::TimeExceeded { 160 | return Ok((HopStatus::TimeExceeded(ret_ip.into()), rtt)); 161 | } 162 | } 163 | None => (), 164 | }, 165 | _ => (), 166 | }, 167 | None => (), 168 | } 169 | Ok((HopStatus::NoResponse, rtt)) 170 | } 171 | -------------------------------------------------------------------------------- /src/trace/tcp.rs: -------------------------------------------------------------------------------- 1 | use pnet::packet::Packet; 2 | use pnet::packet::icmp::IcmpPacket; 3 | use pnet::packet::icmp::IcmpTypes; 4 | use pnet::packet::ip::IpNextHeaderProtocols; 5 | use pnet::packet::ipv4; 6 | use pnet::packet::ipv4::Ipv4Flags; 7 | use pnet::packet::ipv4::Ipv4Packet; 8 | use pnet::packet::ipv4::MutableIpv4Packet; 9 | use pnet::packet::tcp; 10 | use pnet::packet::tcp::MutableTcpPacket; 11 | use pnet::packet::tcp::TcpFlags; 12 | use pnet::packet::tcp::TcpOption; 13 | use rand::Rng; 14 | use std::net::Ipv4Addr; 15 | use std::panic::Location; 16 | use std::time::Duration; 17 | 18 | use crate::error::PistolError; 19 | use crate::layer::IPV4_HEADER_SIZE; 20 | use crate::layer::Layer3Filter; 21 | use crate::layer::Layer4FilterIcmp; 22 | use crate::layer::Layer4FilterTcpUdp; 23 | use crate::layer::PacketFilter; 24 | use crate::layer::PayloadMatch; 25 | use crate::layer::PayloadMatchIp; 26 | use crate::layer::PayloadMatchTcpUdp; 27 | use crate::layer::TCP_HEADER_SIZE; 28 | use crate::layer::layer3_ipv4_send; 29 | use crate::trace::HopStatus; 30 | 31 | const TCP_DATA_SIZE: usize = 0; 32 | // TCP options size 33 | const NOP_SIZE: usize = 1; 34 | const MSS_SIZE: usize = 4; 35 | const WSCALE_SIZE: usize = 3; 36 | const TIMESTAMP_SIZE: usize = 10; 37 | const SACK_PERM_SIZE: usize = 2; 38 | 39 | pub fn send_syn_trace_packet( 40 | dst_ipv4: Ipv4Addr, 41 | dst_port: u16, 42 | src_ipv4: Ipv4Addr, 43 | src_port: u16, 44 | ip_id: u16, 45 | ttl: u8, 46 | timeout: Option, 47 | ) -> Result<(HopStatus, Duration), PistolError> { 48 | const TCP_OPTIONS_SIZE: usize = 49 | MSS_SIZE + SACK_PERM_SIZE + TIMESTAMP_SIZE + NOP_SIZE + WSCALE_SIZE; 50 | 51 | let mut rng = rand::rng(); 52 | // ip header 53 | let mut ip_buff = [0u8; IPV4_HEADER_SIZE + TCP_HEADER_SIZE + TCP_OPTIONS_SIZE + TCP_DATA_SIZE]; 54 | let mut ip_header = match MutableIpv4Packet::new(&mut ip_buff) { 55 | Some(p) => p, 56 | None => { 57 | return Err(PistolError::BuildPacketError { 58 | location: format!("{}", Location::caller()), 59 | }); 60 | } 61 | }; 62 | ip_header.set_version(4); 63 | ip_header.set_header_length(5); 64 | ip_header.set_source(src_ipv4); 65 | ip_header.set_destination(dst_ipv4); 66 | let total_length = IPV4_HEADER_SIZE + TCP_HEADER_SIZE + TCP_OPTIONS_SIZE + TCP_DATA_SIZE; 67 | ip_header.set_total_length(total_length as u16); 68 | ip_header.set_identification(ip_id); 69 | ip_header.set_flags(Ipv4Flags::DontFragment); 70 | ip_header.set_ttl(ttl); 71 | ip_header.set_next_level_protocol(IpNextHeaderProtocols::Tcp); 72 | let c = ipv4::checksum(&ip_header.to_immutable()); 73 | ip_header.set_checksum(c); 74 | 75 | // tcp header 76 | let mut tcp_header = match MutableTcpPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 77 | Some(p) => p, 78 | None => { 79 | return Err(PistolError::BuildPacketError { 80 | location: format!("{}", Location::caller()), 81 | }); 82 | } 83 | }; 84 | tcp_header.set_source(src_port); 85 | tcp_header.set_destination(dst_port); 86 | tcp_header.set_sequence(rng.random()); 87 | tcp_header.set_acknowledgement(0); 88 | tcp_header.set_reserved(0); 89 | tcp_header.set_flags(TcpFlags::SYN); 90 | tcp_header.set_urgent_ptr(0); 91 | tcp_header.set_window(5840); 92 | tcp_header.set_data_offset(10); 93 | tcp_header.set_options(&vec![ 94 | TcpOption::mss(1460), 95 | TcpOption::sack_perm(), 96 | TcpOption::timestamp(2025080516, 0x0), 97 | TcpOption::nop(), 98 | TcpOption::wscale(2), 99 | ]); 100 | let checksum = tcp::ipv4_checksum(&tcp_header.to_immutable(), &src_ipv4, &dst_ipv4); 101 | tcp_header.set_checksum(checksum); 102 | 103 | // time exceeded packet 104 | let layer3 = Layer3Filter { 105 | name: "tcp trace time exceeded layer3", 106 | layer2: None, 107 | src_addr: None, // usually this is the address of the router, not the address of the target machine. 108 | dst_addr: Some(src_ipv4.into()), 109 | }; 110 | let payload_ip = PayloadMatchIp { 111 | src_addr: Some(src_ipv4.into()), 112 | dst_addr: Some(dst_ipv4.into()), 113 | }; 114 | let payload_tcp_udp = PayloadMatchTcpUdp { 115 | layer3: Some(payload_ip), 116 | src_port: Some(src_port), 117 | dst_port: Some(dst_port), 118 | }; 119 | let payload = PayloadMatch::PayloadMatchTcpUdp(payload_tcp_udp); 120 | let layer4_icmp = Layer4FilterIcmp { 121 | name: "tcp trace time exceeded icmp", 122 | layer3: Some(layer3), 123 | icmp_type: Some(IcmpTypes::TimeExceeded), 124 | icmp_code: None, 125 | payload: Some(payload), 126 | }; 127 | let layer_match_icmp_time_exceeded = PacketFilter::Layer4FilterIcmp(layer4_icmp); 128 | 129 | // tcp syn, ack or rst packet 130 | let layer3 = Layer3Filter { 131 | name: "tcp trace reply layer3", 132 | layer2: None, 133 | src_addr: Some(dst_ipv4.into()), 134 | dst_addr: Some(src_ipv4.into()), 135 | }; 136 | let layer4_tcp_udp = Layer4FilterTcpUdp { 137 | name: "tcp trace reply tcp_udp", 138 | layer3: Some(layer3), 139 | src_port: Some(dst_port), 140 | dst_port: Some(src_port), 141 | }; 142 | let layer_match_tcp = PacketFilter::Layer4FilterTcpUdp(layer4_tcp_udp); 143 | 144 | let (ret, rtt) = layer3_ipv4_send( 145 | dst_ipv4, 146 | src_ipv4, 147 | &ip_buff, 148 | vec![layer_match_icmp_time_exceeded, layer_match_tcp], 149 | timeout, 150 | true, 151 | )?; 152 | 153 | match Ipv4Packet::new(&ret) { 154 | Some(ipv4_packet) => { 155 | let protocol = ipv4_packet.get_next_level_protocol(); 156 | match protocol { 157 | IpNextHeaderProtocols::Tcp => { 158 | let ret_ip = ipv4_packet.get_source(); 159 | return Ok((HopStatus::RecvReply(ret_ip.into()), rtt)); 160 | } 161 | IpNextHeaderProtocols::Icmp => match IcmpPacket::new(ipv4_packet.payload()) { 162 | Some(icmp_packet) => { 163 | let icmp_type = icmp_packet.get_icmp_type(); 164 | let ret_ip = ipv4_packet.get_source(); 165 | if icmp_type == IcmpTypes::TimeExceeded { 166 | return Ok((HopStatus::TimeExceeded(ret_ip.into()), rtt)); 167 | } 168 | } 169 | None => (), 170 | }, 171 | _ => (), 172 | } 173 | } 174 | None => (), 175 | } 176 | Ok((HopStatus::NoResponse, rtt)) 177 | } 178 | -------------------------------------------------------------------------------- /src/scan/udp.rs: -------------------------------------------------------------------------------- 1 | use pnet::packet::Packet; 2 | use pnet::packet::icmp::IcmpPacket; 3 | use pnet::packet::icmp::IcmpTypes; 4 | use pnet::packet::icmp::destination_unreachable; 5 | use pnet::packet::ip::IpNextHeaderProtocols; 6 | use pnet::packet::ipv4; 7 | use pnet::packet::ipv4::Ipv4Flags; 8 | use pnet::packet::ipv4::Ipv4Packet; 9 | use pnet::packet::ipv4::MutableIpv4Packet; 10 | use pnet::packet::udp::MutableUdpPacket; 11 | use pnet::packet::udp::ipv4_checksum; 12 | use rand::Rng; 13 | use std::net::Ipv4Addr; 14 | use std::panic::Location; 15 | use std::time::Duration; 16 | 17 | use crate::error::PistolError; 18 | use crate::layer::IPV4_HEADER_SIZE; 19 | use crate::layer::Layer3Filter; 20 | use crate::layer::Layer4FilterIcmp; 21 | use crate::layer::Layer4FilterTcpUdp; 22 | use crate::layer::PacketFilter; 23 | use crate::layer::PayloadMatch; 24 | use crate::layer::PayloadMatchIp; 25 | use crate::layer::PayloadMatchTcpUdp; 26 | use crate::layer::UDP_HEADER_SIZE; 27 | use crate::layer::layer3_ipv4_send; 28 | use crate::scan::DataRecvStatus; 29 | use crate::scan::PortStatus; 30 | 31 | const UDP_DATA_SIZE: usize = 0; 32 | const TTL: u8 = 64; 33 | 34 | pub fn send_udp_scan_packet( 35 | dst_ipv4: Ipv4Addr, 36 | dst_port: u16, 37 | src_ipv4: Ipv4Addr, 38 | src_port: u16, 39 | timeout: Option, 40 | ) -> Result<(PortStatus, DataRecvStatus, Duration), PistolError> { 41 | let mut rng = rand::rng(); 42 | // ip header 43 | let mut ip_buff = [0u8; IPV4_HEADER_SIZE + UDP_HEADER_SIZE + UDP_DATA_SIZE]; 44 | let mut ip_header = match MutableIpv4Packet::new(&mut ip_buff) { 45 | Some(p) => p, 46 | None => { 47 | return Err(PistolError::BuildPacketError { 48 | location: format!("{}", Location::caller()), 49 | }); 50 | } 51 | }; 52 | ip_header.set_version(4); 53 | ip_header.set_header_length(5); 54 | ip_header.set_total_length((IPV4_HEADER_SIZE + UDP_HEADER_SIZE + UDP_DATA_SIZE) as u16); 55 | let id = rng.random(); 56 | ip_header.set_identification(id); 57 | ip_header.set_flags(Ipv4Flags::DontFragment); 58 | ip_header.set_ttl(TTL); 59 | ip_header.set_next_level_protocol(IpNextHeaderProtocols::Udp); 60 | ip_header.set_source(src_ipv4); 61 | ip_header.set_destination(dst_ipv4); 62 | let c = ipv4::checksum(&ip_header.to_immutable()); 63 | ip_header.set_checksum(c); 64 | 65 | // udp header 66 | let mut udp_header = match MutableUdpPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 67 | Some(p) => p, 68 | None => { 69 | return Err(PistolError::BuildPacketError { 70 | location: format!("{}", Location::caller()), 71 | }); 72 | } 73 | }; 74 | udp_header.set_source(src_port); 75 | udp_header.set_destination(dst_port); 76 | udp_header.set_length((UDP_HEADER_SIZE + UDP_DATA_SIZE) as u16); 77 | // udp_header.set_payload(b"1234567890"); // udp test 78 | let checksum = ipv4_checksum(&udp_header.to_immutable(), &src_ipv4, &dst_ipv4); 79 | udp_header.set_checksum(checksum); 80 | 81 | let layer3 = Layer3Filter { 82 | name: "udp scan layer3", 83 | layer2: None, 84 | src_addr: Some(dst_ipv4.into()), 85 | dst_addr: Some(src_ipv4.into()), 86 | }; 87 | let layer4_tcp_udp = Layer4FilterTcpUdp { 88 | name: "udp scan tcp_udp", 89 | layer3: Some(layer3), 90 | src_port: Some(dst_port), 91 | dst_port: Some(src_port), 92 | }; 93 | // set the icmp payload matchs 94 | let payload_ip = PayloadMatchIp { 95 | src_addr: Some(src_ipv4.into()), 96 | dst_addr: Some(dst_ipv4.into()), 97 | }; 98 | let payload_tcp_udp = PayloadMatchTcpUdp { 99 | layer3: Some(payload_ip), 100 | src_port: Some(src_port), 101 | dst_port: Some(dst_port), 102 | }; 103 | let payload = PayloadMatch::PayloadMatchTcpUdp(payload_tcp_udp); 104 | let layer4_icmp = Layer4FilterIcmp { 105 | name: "udp scan icmp", 106 | layer3: Some(layer3), 107 | icmp_type: None, 108 | icmp_code: None, 109 | payload: Some(payload), 110 | }; 111 | let layer_match_1 = PacketFilter::Layer4FilterTcpUdp(layer4_tcp_udp); 112 | let layer_match_2 = PacketFilter::Layer4FilterIcmp(layer4_icmp); 113 | 114 | let codes_1 = vec![ 115 | destination_unreachable::IcmpCodes::DestinationPortUnreachable, // 3 116 | ]; 117 | let codes_2 = vec![ 118 | destination_unreachable::IcmpCodes::DestinationHostUnreachable, // 1 119 | destination_unreachable::IcmpCodes::DestinationProtocolUnreachable, // 2 120 | destination_unreachable::IcmpCodes::NetworkAdministrativelyProhibited, // 9 121 | destination_unreachable::IcmpCodes::HostAdministrativelyProhibited, // 10 122 | destination_unreachable::IcmpCodes::CommunicationAdministrativelyProhibited, // 13 123 | ]; 124 | 125 | let (ret, rtt) = layer3_ipv4_send( 126 | dst_ipv4, 127 | src_ipv4, 128 | &ip_buff, 129 | vec![layer_match_1, layer_match_2], 130 | timeout, 131 | true, 132 | )?; 133 | match Ipv4Packet::new(&ret) { 134 | Some(ipv4_packet) => { 135 | match ipv4_packet.get_next_level_protocol() { 136 | IpNextHeaderProtocols::Udp => { 137 | // any udp response from target port (unusual) 138 | return Ok((PortStatus::Open, DataRecvStatus::Yes, rtt)); 139 | } 140 | IpNextHeaderProtocols::Icmp => { 141 | match IcmpPacket::new(ipv4_packet.payload()) { 142 | Some(icmp_packet) => { 143 | let icmp_type = icmp_packet.get_icmp_type(); 144 | let icmp_code = icmp_packet.get_icmp_code(); 145 | if icmp_type == IcmpTypes::DestinationUnreachable { 146 | if codes_1.contains(&icmp_code) { 147 | // icmp port unreachable error (type 3, code 3) 148 | return Ok((PortStatus::Closed, DataRecvStatus::Yes, rtt)); 149 | } else if codes_2.contains(&icmp_code) { 150 | // other icmp unreachable errors (type 3, code 1, 2, 9, 10, or 13) 151 | return Ok((PortStatus::Filtered, DataRecvStatus::Yes, rtt)); 152 | } 153 | } 154 | } 155 | None => (), 156 | } 157 | } 158 | _ => (), 159 | } 160 | } 161 | None => (), 162 | } 163 | // no response received (even after retransmissions) 164 | Ok((PortStatus::OpenOrFiltered, DataRecvStatus::No, rtt)) 165 | } 166 | -------------------------------------------------------------------------------- /src/trace/udp.rs: -------------------------------------------------------------------------------- 1 | use pnet::packet::Packet; 2 | use pnet::packet::icmp::IcmpCode; 3 | use pnet::packet::icmp::IcmpPacket; 4 | use pnet::packet::icmp::IcmpTypes; 5 | use pnet::packet::ip::IpNextHeaderProtocols; 6 | use pnet::packet::ipv4; 7 | use pnet::packet::ipv4::Ipv4Flags; 8 | use pnet::packet::ipv4::Ipv4Packet; 9 | use pnet::packet::ipv4::MutableIpv4Packet; 10 | use pnet::packet::udp::MutableUdpPacket; 11 | use pnet::packet::udp::ipv4_checksum; 12 | use std::net::Ipv4Addr; 13 | use std::panic::Location; 14 | use std::time::Duration; 15 | 16 | use crate::error::PistolError; 17 | use crate::layer::IPV4_HEADER_SIZE; 18 | use crate::layer::Layer3Filter; 19 | use crate::layer::Layer4FilterIcmp; 20 | use crate::layer::Layer4FilterTcpUdp; 21 | use crate::layer::PacketFilter; 22 | use crate::layer::PayloadMatch; 23 | use crate::layer::PayloadMatchIp; 24 | use crate::layer::PayloadMatchTcpUdp; 25 | use crate::layer::UDP_HEADER_SIZE; 26 | use crate::layer::layer3_ipv4_send; 27 | use crate::trace::HopStatus; 28 | use crate::trace::UDP_DATA_SIZE; 29 | 30 | pub fn send_udp_trace_packet( 31 | dst_ipv4: Ipv4Addr, 32 | dst_port: u16, 33 | src_ipv4: Ipv4Addr, 34 | src_port: u16, 35 | ip_id: u16, 36 | ttl: u8, 37 | timeout: Option, 38 | ) -> Result<(HopStatus, Duration), PistolError> { 39 | // ip header 40 | let mut ip_buff = [0u8; IPV4_HEADER_SIZE + UDP_HEADER_SIZE + UDP_DATA_SIZE]; 41 | let mut ip_header = match MutableIpv4Packet::new(&mut ip_buff) { 42 | Some(p) => p, 43 | None => { 44 | return Err(PistolError::BuildPacketError { 45 | location: format!("{}", Location::caller()), 46 | }); 47 | } 48 | }; 49 | ip_header.set_version(4); 50 | ip_header.set_header_length(5); 51 | ip_header.set_total_length((IPV4_HEADER_SIZE + UDP_HEADER_SIZE + UDP_DATA_SIZE) as u16); 52 | ip_header.set_identification(ip_id); 53 | ip_header.set_flags(Ipv4Flags::DontFragment); 54 | ip_header.set_ttl(ttl); 55 | ip_header.set_next_level_protocol(IpNextHeaderProtocols::Udp); 56 | ip_header.set_source(src_ipv4); 57 | ip_header.set_destination(dst_ipv4); 58 | let c = ipv4::checksum(&ip_header.to_immutable()); 59 | ip_header.set_checksum(c); 60 | 61 | // udp header 62 | let mut udp_header = match MutableUdpPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 63 | Some(p) => p, 64 | None => { 65 | return Err(PistolError::BuildPacketError { 66 | location: format!("{}", Location::caller()), 67 | }); 68 | } 69 | }; 70 | udp_header.set_source(src_port); 71 | udp_header.set_destination(dst_port); 72 | udp_header.set_length((UDP_HEADER_SIZE + UDP_DATA_SIZE) as u16); 73 | let udp_payload = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"; 74 | assert_eq!(udp_payload.len(), UDP_DATA_SIZE); 75 | udp_header.set_payload(&udp_payload.as_bytes()); 76 | let checksum = ipv4_checksum(&udp_header.to_immutable(), &src_ipv4, &dst_ipv4); 77 | udp_header.set_checksum(checksum); 78 | 79 | // generally speaking, the target random UDP port will not return any data. 80 | let layer3 = Layer3Filter { 81 | name: "udp trace reply layer3 1", 82 | layer2: None, 83 | src_addr: None, // usually this is the address of the router, not the address of the target machine. 84 | dst_addr: Some(src_ipv4.into()), 85 | }; 86 | // set the icmp payload matchs 87 | let payload_ip = PayloadMatchIp { 88 | src_addr: Some(src_ipv4.into()), 89 | dst_addr: Some(dst_ipv4.into()), 90 | }; 91 | let payload_tcp_udp = PayloadMatchTcpUdp { 92 | layer3: Some(payload_ip), 93 | src_port: Some(src_port), 94 | dst_port: Some(dst_port), 95 | }; 96 | let payload = PayloadMatch::PayloadMatchTcpUdp(payload_tcp_udp); 97 | let layer4_icmp = Layer4FilterIcmp { 98 | name: "udp trace icmp 1", 99 | layer3: Some(layer3), 100 | icmp_type: Some(IcmpTypes::TimeExceeded), 101 | icmp_code: None, 102 | payload: Some(payload), 103 | }; 104 | let layer_match_icmp_time_exceeded = PacketFilter::Layer4FilterIcmp(layer4_icmp); 105 | 106 | // finally, the UDP packet arrives at the target machine. 107 | let layer3 = Layer3Filter { 108 | name: "udp trace layer3 2", 109 | layer2: None, 110 | src_addr: Some(dst_ipv4.into()), 111 | dst_addr: Some(src_ipv4.into()), 112 | }; 113 | let layer4_icmp = Layer4FilterIcmp { 114 | name: "udp trace icmp 2", 115 | layer3: Some(layer3), 116 | icmp_type: Some(IcmpTypes::DestinationUnreachable), 117 | icmp_code: Some(IcmpCode(3)), 118 | payload: None, 119 | }; 120 | let layer_match_icmp_port_unreachable = PacketFilter::Layer4FilterIcmp(layer4_icmp); 121 | 122 | // there is a small chance that the target's UDP port will be open. 123 | let layer3 = Layer3Filter { 124 | name: "udp trace layer3 3", 125 | layer2: None, 126 | src_addr: Some(dst_ipv4.into()), 127 | dst_addr: Some(src_ipv4.into()), 128 | }; 129 | let layer4 = Layer4FilterTcpUdp { 130 | name: "udp trace tcp_udp", 131 | layer3: Some(layer3), 132 | src_port: Some(dst_port), 133 | dst_port: Some(src_port), 134 | }; 135 | let layer_match_udp = PacketFilter::Layer4FilterTcpUdp(layer4); 136 | 137 | let (ret, rtt) = layer3_ipv4_send( 138 | dst_ipv4, 139 | src_ipv4, 140 | &ip_buff, 141 | vec![ 142 | layer_match_icmp_time_exceeded, 143 | layer_match_icmp_port_unreachable, 144 | layer_match_udp, 145 | ], 146 | timeout, 147 | true, 148 | )?; 149 | match Ipv4Packet::new(&ret) { 150 | Some(ipv4_packet) => match ipv4_packet.get_next_level_protocol() { 151 | IpNextHeaderProtocols::Udp => { 152 | let ret_ip = ipv4_packet.get_source(); 153 | return Ok((HopStatus::RecvReply(ret_ip.into()), rtt)); 154 | } 155 | IpNextHeaderProtocols::Icmp => match IcmpPacket::new(ipv4_packet.payload()) { 156 | Some(icmp_packet) => { 157 | let icmp_type = icmp_packet.get_icmp_type(); 158 | let icmp_code = icmp_packet.get_icmp_code(); 159 | let ret_ip = ipv4_packet.get_source(); 160 | if icmp_type == IcmpTypes::TimeExceeded { 161 | return Ok((HopStatus::TimeExceeded(ret_ip.into()), rtt)); 162 | } else if icmp_type == IcmpTypes::DestinationUnreachable { 163 | if icmp_code == IcmpCode(3) { 164 | // icmp type 3, code 3 (port unreachable) 165 | return Ok((HopStatus::Unreachable(ret_ip.into()), rtt)); 166 | } 167 | } 168 | } 169 | None => (), 170 | }, 171 | _ => (), 172 | }, 173 | None => (), 174 | } 175 | Ok((HopStatus::NoResponse, rtt)) 176 | } 177 | -------------------------------------------------------------------------------- /src/trace/udp6.rs: -------------------------------------------------------------------------------- 1 | use pnet::packet::Packet; 2 | use pnet::packet::icmpv6::Icmpv6Code; 3 | use pnet::packet::icmpv6::Icmpv6Packet; 4 | use pnet::packet::icmpv6::Icmpv6Types; 5 | use pnet::packet::ip::IpNextHeaderProtocols; 6 | use pnet::packet::ipv6::Ipv6Packet; 7 | use pnet::packet::ipv6::MutableIpv6Packet; 8 | use pnet::packet::udp::MutableUdpPacket; 9 | use pnet::packet::udp::ipv6_checksum; 10 | use std::net::Ipv6Addr; 11 | use std::panic::Location; 12 | use std::time::Duration; 13 | 14 | use crate::error::PistolError; 15 | use crate::layer::IPV6_HEADER_SIZE; 16 | use crate::layer::Layer3Filter; 17 | use crate::layer::Layer4FilterIcmpv6; 18 | use crate::layer::Layer4FilterTcpUdp; 19 | use crate::layer::PacketFilter; 20 | use crate::layer::PayloadMatch; 21 | use crate::layer::PayloadMatchIp; 22 | use crate::layer::PayloadMatchTcpUdp; 23 | use crate::layer::UDP_HEADER_SIZE; 24 | use crate::layer::layer3_ipv6_send; 25 | use crate::trace::HopStatus; 26 | use crate::trace::UDP_DATA_SIZE; 27 | 28 | pub fn send_udp_trace_packet( 29 | dst_ipv6: Ipv6Addr, 30 | dst_port: u16, 31 | src_ipv6: Ipv6Addr, 32 | src_port: u16, 33 | hop_limit: u8, 34 | timeout: Option, 35 | ) -> Result<(HopStatus, Duration), PistolError> { 36 | // ipv6 header 37 | let mut ipv6_buff = [0u8; IPV6_HEADER_SIZE + UDP_HEADER_SIZE + UDP_DATA_SIZE]; 38 | let mut ipv6_header = match MutableIpv6Packet::new(&mut ipv6_buff) { 39 | Some(p) => p, 40 | None => { 41 | return Err(PistolError::BuildPacketError { 42 | location: format!("{}", Location::caller()), 43 | }); 44 | } 45 | }; 46 | ipv6_header.set_version(6); 47 | // In all cases, the IPv6 flow label is 0x12345, on platforms that allow us to set it. 48 | // On platforms that do not (which includes non-Linux Unix platforms when not using Ethernet to send), the flow label will be 0. 49 | ipv6_header.set_flow_label(0x12345); 50 | let payload_length = UDP_HEADER_SIZE + UDP_DATA_SIZE; 51 | ipv6_header.set_payload_length(payload_length as u16); 52 | ipv6_header.set_next_header(IpNextHeaderProtocols::Udp); 53 | ipv6_header.set_hop_limit(hop_limit); 54 | ipv6_header.set_source(src_ipv6); 55 | ipv6_header.set_destination(dst_ipv6); 56 | 57 | // udp header 58 | let mut udp_header = match MutableUdpPacket::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) { 59 | Some(p) => p, 60 | None => { 61 | return Err(PistolError::BuildPacketError { 62 | location: format!("{}", Location::caller()), 63 | }); 64 | } 65 | }; 66 | udp_header.set_source(src_port); 67 | udp_header.set_destination(dst_port); 68 | udp_header.set_length((UDP_HEADER_SIZE + UDP_DATA_SIZE) as u16); 69 | let udp_payload = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"; 70 | assert_eq!(udp_payload.len(), UDP_DATA_SIZE); 71 | udp_header.set_payload(&udp_payload.as_bytes()); 72 | let checksum = ipv6_checksum(&udp_header.to_immutable(), &src_ipv6, &dst_ipv6); 73 | udp_header.set_checksum(checksum); 74 | 75 | // generally speaking, the target random UDP port will not return any data. 76 | let layer3 = Layer3Filter { 77 | name: "udp6 trace layer3 1", 78 | layer2: None, 79 | src_addr: None, // usually this is the address of the router, not the address of the target machine. 80 | dst_addr: Some(src_ipv6.into()), 81 | }; 82 | let payload_ip = PayloadMatchIp { 83 | src_addr: Some(src_ipv6.into()), 84 | dst_addr: Some(dst_ipv6.into()), 85 | }; 86 | let payload_tcp_udp = PayloadMatchTcpUdp { 87 | layer3: Some(payload_ip), 88 | src_port: Some(src_port), 89 | dst_port: Some(dst_port), 90 | }; 91 | let payload = PayloadMatch::PayloadMatchTcpUdp(payload_tcp_udp); 92 | let layer4_icmpv6 = Layer4FilterIcmpv6 { 93 | name: "udp6 trace icmpv6 1", 94 | layer3: Some(layer3), 95 | icmpv6_type: Some(Icmpv6Types::TimeExceeded), 96 | icmpv6_code: None, 97 | payload: Some(payload), 98 | }; 99 | let layer_match_icmp_time_exceeded = PacketFilter::Layer4FilterIcmpv6(layer4_icmpv6); 100 | 101 | // finally, the UDP packet arrives at the target machine. 102 | let layer3 = Layer3Filter { 103 | name: "udp6 trace layer3 2", 104 | layer2: None, 105 | src_addr: Some(dst_ipv6.into()), 106 | dst_addr: Some(src_ipv6.into()), 107 | }; 108 | let layer4_icmpv6 = Layer4FilterIcmpv6 { 109 | name: "udp6 trace icmpv6", 110 | layer3: Some(layer3), 111 | icmpv6_type: Some(Icmpv6Types::DestinationUnreachable), 112 | icmpv6_code: Some(Icmpv6Code(4)), // port unreachable 113 | payload: None, 114 | }; 115 | let layer_match_icmp_port_unreachable = PacketFilter::Layer4FilterIcmpv6(layer4_icmpv6); 116 | 117 | // there is a small chance that the target's UDP port will be open. 118 | let layer3 = Layer3Filter { 119 | name: "udp6 trace layer3 3", 120 | layer2: None, 121 | src_addr: Some(dst_ipv6.into()), 122 | dst_addr: Some(src_ipv6.into()), 123 | }; 124 | let layer4 = Layer4FilterTcpUdp { 125 | name: "udp6 trace tcp_udp", 126 | layer3: Some(layer3), 127 | src_port: Some(dst_port), 128 | dst_port: Some(src_port), 129 | }; 130 | let layer_match_udp = PacketFilter::Layer4FilterTcpUdp(layer4); 131 | 132 | let (ret, rtt) = layer3_ipv6_send( 133 | dst_ipv6, 134 | src_ipv6, 135 | &ipv6_buff, 136 | vec![ 137 | layer_match_icmp_time_exceeded, 138 | layer_match_icmp_port_unreachable, 139 | layer_match_udp, 140 | ], 141 | timeout, 142 | true, 143 | )?; 144 | match Ipv6Packet::new(&ret) { 145 | Some(ipv6_packet) => { 146 | match ipv6_packet.get_next_header() { 147 | IpNextHeaderProtocols::Udp => { 148 | // any udp response from target port (unusual) 149 | let ret_ip = ipv6_packet.get_source(); 150 | return Ok((HopStatus::RecvReply(ret_ip.into()), rtt)); 151 | } 152 | IpNextHeaderProtocols::Icmpv6 => match Icmpv6Packet::new(ipv6_packet.payload()) { 153 | Some(icmpv6_packet) => { 154 | let icmpv6_type = icmpv6_packet.get_icmpv6_type(); 155 | let icmpv6_code = icmpv6_packet.get_icmpv6_code(); 156 | let ret_ip = ipv6_packet.get_source(); 157 | if icmpv6_type == Icmpv6Types::TimeExceeded { 158 | return Ok((HopStatus::TimeExceeded(ret_ip.into()), rtt)); 159 | } else if icmpv6_type == Icmpv6Types::DestinationUnreachable { 160 | if icmpv6_code == Icmpv6Code(4) { 161 | return Ok((HopStatus::Unreachable(ret_ip.into()), rtt)); 162 | } 163 | } 164 | } 165 | None => (), 166 | }, 167 | _ => (), 168 | } 169 | } 170 | None => (), 171 | } 172 | Ok((HopStatus::NoResponse, rtt)) 173 | } 174 | -------------------------------------------------------------------------------- /src/flood/tcp.rs: -------------------------------------------------------------------------------- 1 | use pnet::packet::ip::IpNextHeaderProtocols; 2 | use pnet::packet::ipv4; 3 | use pnet::packet::ipv4::Ipv4Flags; 4 | use pnet::packet::ipv4::MutableIpv4Packet; 5 | use pnet::packet::tcp::MutableTcpPacket; 6 | use pnet::packet::tcp::TcpFlags; 7 | use pnet::packet::tcp::ipv4_checksum; 8 | use rand::Rng; 9 | use std::net::Ipv4Addr; 10 | use std::panic::Location; 11 | 12 | use crate::error::PistolError; 13 | use crate::layer::IPV4_HEADER_SIZE; 14 | use crate::layer::TCP_HEADER_SIZE; 15 | use crate::layer::layer3_ipv4_send; 16 | 17 | const TCP_DATA_SIZE: usize = 0; 18 | const TTL: u8 = 64; 19 | 20 | pub fn send_syn_flood_packet( 21 | dst_ipv4: Ipv4Addr, 22 | dst_port: u16, 23 | src_ipv4: Ipv4Addr, 24 | src_port: u16, 25 | max_same_packet: usize, 26 | ) -> Result { 27 | let mut rng = rand::rng(); 28 | // ip header 29 | let mut ip_buff = [0u8; IPV4_HEADER_SIZE + TCP_HEADER_SIZE + TCP_DATA_SIZE]; 30 | let mut ip_header = match MutableIpv4Packet::new(&mut ip_buff) { 31 | Some(p) => p, 32 | None => { 33 | return Err(PistolError::BuildPacketError { 34 | location: format!("{}", Location::caller()), 35 | }); 36 | } 37 | }; 38 | ip_header.set_version(4); 39 | ip_header.set_header_length(5); 40 | ip_header.set_source(src_ipv4); 41 | ip_header.set_destination(dst_ipv4); 42 | ip_header.set_total_length((IPV4_HEADER_SIZE + TCP_HEADER_SIZE + TCP_DATA_SIZE) as u16); 43 | let id = rng.random(); 44 | ip_header.set_identification(id); 45 | ip_header.set_flags(Ipv4Flags::DontFragment); 46 | ip_header.set_ttl(TTL); 47 | ip_header.set_next_level_protocol(IpNextHeaderProtocols::Tcp); 48 | let c = ipv4::checksum(&ip_header.to_immutable()); 49 | ip_header.set_checksum(c); 50 | 51 | // tcp header 52 | let mut tcp_header = match MutableTcpPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 53 | Some(p) => p, 54 | None => { 55 | return Err(PistolError::BuildPacketError { 56 | location: format!("{}", Location::caller()), 57 | }); 58 | } 59 | }; 60 | tcp_header.set_source(src_port); 61 | tcp_header.set_destination(dst_port); 62 | tcp_header.set_sequence(rng.random()); 63 | tcp_header.set_acknowledgement(rng.random()); 64 | tcp_header.set_reserved(0); 65 | tcp_header.set_flags(TcpFlags::SYN); 66 | tcp_header.set_urgent_ptr(0); 67 | tcp_header.set_window(1024); 68 | tcp_header.set_data_offset(5); 69 | let checksum = ipv4_checksum(&tcp_header.to_immutable(), &src_ipv4, &dst_ipv4); 70 | tcp_header.set_checksum(checksum); 71 | let timeout = None; 72 | 73 | for _ in 0..max_same_packet { 74 | let _ret = layer3_ipv4_send(dst_ipv4, src_ipv4, &ip_buff, vec![], timeout, false)?; 75 | } 76 | Ok(ip_buff.len() * max_same_packet) 77 | } 78 | 79 | pub fn send_ack_flood_packet( 80 | dst_ipv4: Ipv4Addr, 81 | dst_port: u16, 82 | src_ipv4: Ipv4Addr, 83 | src_port: u16, 84 | max_same_packet: usize, 85 | ) -> Result { 86 | let mut rng = rand::rng(); 87 | // ip header 88 | let mut ip_buff = [0u8; IPV4_HEADER_SIZE + TCP_HEADER_SIZE + TCP_DATA_SIZE]; 89 | let mut ip_header = match MutableIpv4Packet::new(&mut ip_buff) { 90 | Some(p) => p, 91 | None => { 92 | return Err(PistolError::BuildPacketError { 93 | location: format!("{}", Location::caller()), 94 | }); 95 | } 96 | }; 97 | ip_header.set_version(4); 98 | ip_header.set_header_length(5); 99 | ip_header.set_source(src_ipv4); 100 | ip_header.set_destination(dst_ipv4); 101 | ip_header.set_total_length((IPV4_HEADER_SIZE + TCP_HEADER_SIZE + TCP_DATA_SIZE) as u16); 102 | let id = rng.random(); 103 | ip_header.set_identification(id); 104 | ip_header.set_flags(Ipv4Flags::DontFragment); 105 | ip_header.set_ttl(TTL); 106 | ip_header.set_next_level_protocol(IpNextHeaderProtocols::Tcp); 107 | let c = ipv4::checksum(&ip_header.to_immutable()); 108 | ip_header.set_checksum(c); 109 | 110 | // tcp header 111 | let mut tcp_header = match MutableTcpPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 112 | Some(p) => p, 113 | None => { 114 | return Err(PistolError::BuildPacketError { 115 | location: format!("{}", Location::caller()), 116 | }); 117 | } 118 | }; 119 | tcp_header.set_source(src_port); 120 | tcp_header.set_destination(dst_port); 121 | tcp_header.set_sequence(rng.random()); 122 | tcp_header.set_acknowledgement(rng.random()); 123 | tcp_header.set_reserved(0); 124 | tcp_header.set_flags(TcpFlags::ACK); 125 | tcp_header.set_urgent_ptr(0); 126 | tcp_header.set_window(1024); 127 | tcp_header.set_data_offset(5); 128 | let checksum = ipv4_checksum(&tcp_header.to_immutable(), &src_ipv4, &dst_ipv4); 129 | tcp_header.set_checksum(checksum); 130 | let timeout = None; 131 | 132 | for _ in 0..max_same_packet { 133 | let _ret = layer3_ipv4_send(dst_ipv4, src_ipv4, &ip_buff, vec![], timeout, false)?; 134 | } 135 | Ok(ip_buff.len() * max_same_packet) 136 | } 137 | 138 | pub fn send_ack_psh_flood_packet( 139 | dst_ipv4: Ipv4Addr, 140 | dst_port: u16, 141 | src_ipv4: Ipv4Addr, 142 | src_port: u16, 143 | max_same_packet: usize, 144 | ) -> Result { 145 | let mut rng = rand::rng(); 146 | // ip header 147 | let mut ip_buff = [0u8; IPV4_HEADER_SIZE + TCP_HEADER_SIZE + TCP_DATA_SIZE]; 148 | let mut ip_header = match MutableIpv4Packet::new(&mut ip_buff) { 149 | Some(p) => p, 150 | None => { 151 | return Err(PistolError::BuildPacketError { 152 | location: format!("{}", Location::caller()), 153 | }); 154 | } 155 | }; 156 | ip_header.set_version(4); 157 | ip_header.set_header_length(5); 158 | ip_header.set_source(src_ipv4); 159 | ip_header.set_destination(dst_ipv4); 160 | ip_header.set_total_length((IPV4_HEADER_SIZE + TCP_HEADER_SIZE + TCP_DATA_SIZE) as u16); 161 | let id = rng.random(); 162 | ip_header.set_identification(id); 163 | ip_header.set_flags(Ipv4Flags::DontFragment); 164 | ip_header.set_ttl(TTL); 165 | ip_header.set_next_level_protocol(IpNextHeaderProtocols::Tcp); 166 | let c = ipv4::checksum(&ip_header.to_immutable()); 167 | ip_header.set_checksum(c); 168 | 169 | // tcp header 170 | let mut tcp_header = match MutableTcpPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 171 | Some(p) => p, 172 | None => { 173 | return Err(PistolError::BuildPacketError { 174 | location: format!("{}", Location::caller()), 175 | }); 176 | } 177 | }; 178 | tcp_header.set_source(src_port); 179 | tcp_header.set_destination(dst_port); 180 | tcp_header.set_sequence(rng.random()); 181 | tcp_header.set_acknowledgement(rng.random()); 182 | tcp_header.set_reserved(0); 183 | tcp_header.set_flags(TcpFlags::ACK | TcpFlags::PSH); 184 | tcp_header.set_urgent_ptr(0); 185 | tcp_header.set_window(1024); 186 | tcp_header.set_data_offset(5); 187 | let checksum = ipv4_checksum(&tcp_header.to_immutable(), &src_ipv4, &dst_ipv4); 188 | tcp_header.set_checksum(checksum); 189 | let timeout = None; 190 | 191 | for _ in 0..max_same_packet { 192 | let _ret = layer3_ipv4_send(dst_ipv4, src_ipv4, &ip_buff, vec![], timeout, false)?; 193 | } 194 | Ok(ip_buff.len() * max_same_packet) 195 | } 196 | -------------------------------------------------------------------------------- /src/flood/tcp6.rs: -------------------------------------------------------------------------------- 1 | use pnet::packet::ip::IpNextHeaderProtocols; 2 | use pnet::packet::ipv6::MutableIpv6Packet; 3 | use pnet::packet::tcp::MutableTcpPacket; 4 | use pnet::packet::tcp::TcpFlags; 5 | use pnet::packet::tcp::ipv6_checksum; 6 | use rand::Rng; 7 | use std::net::Ipv6Addr; 8 | use std::panic::Location; 9 | 10 | use crate::error::PistolError; 11 | use crate::layer::IPV6_HEADER_SIZE; 12 | use crate::layer::TCP_HEADER_SIZE; 13 | use crate::layer::layer3_ipv6_send; 14 | 15 | const TCP_DATA_SIZE: usize = 0; 16 | const TTL: u8 = 255; 17 | 18 | pub fn send_syn_flood_packet( 19 | dst_ipv6: Ipv6Addr, 20 | dst_port: u16, 21 | src_ipv6: Ipv6Addr, 22 | src_port: u16, 23 | max_same_packet: usize, 24 | ) -> Result { 25 | let mut rng = rand::rng(); 26 | // ipv6 header 27 | let mut ipv6_buff = [0u8; IPV6_HEADER_SIZE + TCP_HEADER_SIZE + TCP_DATA_SIZE]; 28 | let mut ipv6_header = match MutableIpv6Packet::new(&mut ipv6_buff) { 29 | Some(p) => p, 30 | None => { 31 | return Err(PistolError::BuildPacketError { 32 | location: format!("{}", Location::caller()), 33 | }); 34 | } 35 | }; 36 | ipv6_header.set_version(6); 37 | // In all cases, the IPv6 flow label is 0x12345, on platforms that allow us to set it. 38 | // On platforms that do not (which includes non-Linux Unix platforms when not using Ethernet to send), the flow label will be 0. 39 | ipv6_header.set_flow_label(0x12345); 40 | let payload_length = TCP_HEADER_SIZE + TCP_DATA_SIZE; 41 | ipv6_header.set_payload_length(payload_length as u16); 42 | ipv6_header.set_next_header(IpNextHeaderProtocols::Tcp); 43 | ipv6_header.set_hop_limit(TTL); 44 | ipv6_header.set_source(src_ipv6); 45 | ipv6_header.set_destination(dst_ipv6); 46 | 47 | // tcp header 48 | let mut tcp_header = match MutableTcpPacket::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) { 49 | Some(p) => p, 50 | None => { 51 | return Err(PistolError::BuildPacketError { 52 | location: format!("{}", Location::caller()), 53 | }); 54 | } 55 | }; 56 | tcp_header.set_source(src_port); 57 | tcp_header.set_destination(dst_port); 58 | tcp_header.set_sequence(rng.random()); 59 | tcp_header.set_acknowledgement(rng.random()); 60 | tcp_header.set_reserved(0); 61 | tcp_header.set_flags(TcpFlags::SYN); 62 | tcp_header.set_urgent_ptr(0); 63 | tcp_header.set_window(1024); 64 | tcp_header.set_data_offset(5); 65 | let checksum = ipv6_checksum(&tcp_header.to_immutable(), &src_ipv6, &dst_ipv6); 66 | tcp_header.set_checksum(checksum); 67 | let timeout = None; 68 | 69 | for _ in 0..max_same_packet { 70 | let _ret = layer3_ipv6_send(dst_ipv6, src_ipv6, &ipv6_buff, vec![], timeout, false)?; 71 | } 72 | Ok(ipv6_buff.len() * max_same_packet) 73 | } 74 | 75 | pub fn send_ack_flood_packet( 76 | dst_ipv6: Ipv6Addr, 77 | dst_port: u16, 78 | src_ipv6: Ipv6Addr, 79 | src_port: u16, 80 | max_same_packet: usize, 81 | ) -> Result { 82 | let mut rng = rand::rng(); 83 | // ipv6 header 84 | let mut ipv6_buff = [0u8; IPV6_HEADER_SIZE + TCP_HEADER_SIZE + TCP_DATA_SIZE]; 85 | let mut ipv6_header = match MutableIpv6Packet::new(&mut ipv6_buff) { 86 | Some(p) => p, 87 | None => { 88 | return Err(PistolError::BuildPacketError { 89 | location: format!("{}", Location::caller()), 90 | }); 91 | } 92 | }; 93 | ipv6_header.set_version(6); 94 | // In all cases, the IPv6 flow label is 0x12345, on platforms that allow us to set it. 95 | // On platforms that do not (which includes non-Linux Unix platforms when not using Ethernet to send), the flow label will be 0. 96 | ipv6_header.set_flow_label(0x12345); 97 | let payload_length = TCP_HEADER_SIZE + TCP_DATA_SIZE; 98 | ipv6_header.set_payload_length(payload_length as u16); 99 | ipv6_header.set_next_header(IpNextHeaderProtocols::Tcp); 100 | ipv6_header.set_hop_limit(TTL); 101 | ipv6_header.set_source(src_ipv6); 102 | ipv6_header.set_destination(dst_ipv6); 103 | 104 | // tcp header 105 | let mut tcp_header = match MutableTcpPacket::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) { 106 | Some(p) => p, 107 | None => { 108 | return Err(PistolError::BuildPacketError { 109 | location: format!("{}", Location::caller()), 110 | }); 111 | } 112 | }; 113 | tcp_header.set_source(src_port); 114 | tcp_header.set_destination(dst_port); 115 | tcp_header.set_sequence(rng.random()); 116 | tcp_header.set_acknowledgement(rng.random()); 117 | tcp_header.set_reserved(0); 118 | tcp_header.set_flags(TcpFlags::ACK); 119 | tcp_header.set_urgent_ptr(0); 120 | tcp_header.set_window(1024); 121 | tcp_header.set_data_offset(5); 122 | let checksum = ipv6_checksum(&tcp_header.to_immutable(), &src_ipv6, &dst_ipv6); 123 | tcp_header.set_checksum(checksum); 124 | let timeout = None; 125 | 126 | for _ in 0..max_same_packet { 127 | let _ret = layer3_ipv6_send(dst_ipv6, src_ipv6, &ipv6_buff, vec![], timeout, false)?; 128 | } 129 | Ok(ipv6_buff.len() * max_same_packet) 130 | } 131 | 132 | pub fn send_ack_psh_flood_packet( 133 | dst_ipv6: Ipv6Addr, 134 | dst_port: u16, 135 | src_ipv6: Ipv6Addr, 136 | src_port: u16, 137 | max_same_packet: usize, 138 | ) -> Result { 139 | let mut rng = rand::rng(); 140 | // ipv6 header 141 | let mut ipv6_buff = [0u8; IPV6_HEADER_SIZE + TCP_HEADER_SIZE + TCP_DATA_SIZE]; 142 | let mut ipv6_header = match MutableIpv6Packet::new(&mut ipv6_buff) { 143 | Some(p) => p, 144 | None => { 145 | return Err(PistolError::BuildPacketError { 146 | location: format!("{}", Location::caller()), 147 | }); 148 | } 149 | }; 150 | ipv6_header.set_version(6); 151 | // In all cases, the IPv6 flow label is 0x12345, on platforms that allow us to set it. 152 | // On platforms that do not (which includes non-Linux Unix platforms when not using Ethernet to send), the flow label will be 0. 153 | ipv6_header.set_flow_label(0x12345); 154 | let payload_length = TCP_HEADER_SIZE + TCP_DATA_SIZE; 155 | ipv6_header.set_payload_length(payload_length as u16); 156 | ipv6_header.set_next_header(IpNextHeaderProtocols::Tcp); 157 | ipv6_header.set_hop_limit(TTL); 158 | ipv6_header.set_source(src_ipv6); 159 | ipv6_header.set_destination(dst_ipv6); 160 | 161 | // tcp header 162 | let mut tcp_header = match MutableTcpPacket::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) { 163 | Some(p) => p, 164 | None => { 165 | return Err(PistolError::BuildPacketError { 166 | location: format!("{}", Location::caller()), 167 | }); 168 | } 169 | }; 170 | tcp_header.set_source(src_port); 171 | tcp_header.set_destination(dst_port); 172 | tcp_header.set_sequence(rng.random()); 173 | tcp_header.set_acknowledgement(rng.random()); 174 | tcp_header.set_reserved(0); 175 | tcp_header.set_flags(TcpFlags::ACK | TcpFlags::PSH); 176 | tcp_header.set_urgent_ptr(0); 177 | tcp_header.set_window(1024); 178 | tcp_header.set_data_offset(5); 179 | let checksum = ipv6_checksum(&tcp_header.to_immutable(), &src_ipv6, &dst_ipv6); 180 | tcp_header.set_checksum(checksum); 181 | let timeout = None; 182 | 183 | for _ in 0..max_same_packet { 184 | let _ret = layer3_ipv6_send(dst_ipv6, src_ipv6, &ipv6_buff, vec![], timeout, false)?; 185 | } 186 | Ok(ipv6_buff.len() * max_same_packet) 187 | } 188 | -------------------------------------------------------------------------------- /scripts/test.py: -------------------------------------------------------------------------------- 1 | l1 = [40.0, 0.0, 64.0, 40.0, 0.0, 64.0, 40.0, 0.0, 64.0, 40.0, 0.0, 64.0, 40.0, 0.0, 64.0, 36.0, 0.0, 64.0, 128.0, 0.0, 64.0, 88.0, 0.0, 64.0, 24.0, 0.0, 255.0, 356.0, 0.0, 64.0, 32.0, 0.0, 64.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 20.0, 0.0, 64.0, 20.0, 0.0, 64.0, 20.0, 0.0, 64.0, 20.0, 0.0, 64.0, 28976198658.3, 64260.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 4.0, 8.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 4.0, 2.0, 10.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1440.0, 1.0, 7.0, 44.6, 64260.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 4.0, 8.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 4.0, 2.0, 10.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1440.0, 1.0, 7.0, 44.6, 64260.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 1.0, 1.0, 8.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 4.0, 1.0, 1.0, 10.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1440.0, -1.0, 7.0, 44.6, 64260.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 4.0, 8.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 4.0, 2.0, 10.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1440.0, 1.0, 7.0, 44.6, 64260.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 4.0, 8.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 4.0, 2.0, 10.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1440.0, 1.0, 7.0, 44.6, 64260.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 4.0, 8.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 4.0, 2.0, 10.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1440.0, 1.0, -1.0, 44.6, 64800.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 1.0, 1.0, 4.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 4.0, 1.0, 1.0, 2.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1440.0, 1.0, 7.0, 45.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 129.0, 9.0, 4.0, 1.0, 136.0, 0.0] 2 | 3 | l2 = [40.0, 0.0, 64.0, 40.0, 0.0, 64.0, 40.0, 0.0, 64.0, 40.0, 0.0, 64.0, 40.0, 0.0, 64.0, 36.0, 0.0, 64.0, 128.0, 0.0, 64.0, 88.0, 0.0, 64.0, 24.0, 0.0, 255.0, 356.0, 0.0, 64.0, 32.0, 0.0, 64.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 20.0, 0.0, 64.0, 20.0, 0.0, 64.0, 20.0, 0.0, 64.0, 20.0, 0.0, 64.0, 19619020458.833717, 64260.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 4.0, 8.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 4.0, 2.0, 10.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1440.0, 1.0, 7.0, 44.625, 64260.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 4.0, 8.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 4.0, 2.0, 10.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1440.0, 1.0, 7.0, 44.625, 64260.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 1.0, 1.0, 8.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 4.0, 1.0, 1.0, 10.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1440.0, -1.0, 7.0, 44.625, 64260.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 4.0, 8.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 4.0, 2.0, 10.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1440.0, 1.0, 7.0, 44.625, 64260.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 4.0, 8.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 4.0, 2.0, 10.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1440.0, 1.0, 7.0, 44.625, 64260.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 4.0, 8.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 4.0, 2.0, 10.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1440.0, 1.0, -1.0, 44.625, 64800.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 1.0, 1.0, 4.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 4.0, 1.0, 1.0, 2.0, 1.0, 3.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1440.0, 1.0, 7.0, 45.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 129.0, 9.0, 4.0, 1.0, 136.0, 0.0] 4 | 5 | for i, (a, b) in enumerate(zip(l1, l2)): 6 | if a != b: 7 | print('i: {} | {} - {}'.format(i + 1, a, b)) -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /tests/unix_routetable.txt: -------------------------------------------------------------------------------- 1 | Routing tables 2 | 3 | Internet: 4 | Destination Gateway Flags Netif Expire 5 | default 192.168.50.1 UGScg en0 6 | 127 127.0.0.1 UCS lo0 7 | 127.0.0.1 127.0.0.1 UH lo0 8 | 169.254 link#12 UCS en0 ! 9 | 169.254.169.254 link#12 UHLSW en0 ! 10 | 192.168.50 link#12 UCS en0 ! 11 | 192.168.50.1/32 link#12 UCS en0 ! 12 | 192.168.50.1 58:11:22:5d:1b:0 UHLWIir en0 1198 13 | 192.168.50.24 e2:ad:7f:c5:cd:fb UHLWI en0 378 14 | 192.168.50.35 80:60:b7:de:72:51 UHLWIi en0 1122 15 | 192.168.50.61 ee:c0:db:b4:73:dc UHLWI en0 142 16 | 192.168.50.108/32 link#12 UCS en0 ! 17 | 192.168.50.115 fe:c:d0:a:53:3f UHLWI en0 1151 18 | 192.168.50.165 ca:b5:38:f9:21:9a UHLWIi en0 1125 19 | 192.168.50.179 f8:4f:ad:b3:5a:4c UHLWIi en0 1195 20 | 192.168.50.201 5a:c5:db:ff:13:4d UHLWI en0 317 21 | 192.168.50.255 ff:ff:ff:ff:ff:ff UHLWbI en0 ! 22 | 224.0.0/4 link#12 UmCS en0 ! 23 | 224.0.0.251 1:0:5e:0:0:fb UHmLWI en0 24 | 239.255.255.250 1:0:5e:7f:ff:fa UHmLWI en0 25 | 255.255.255.255/32 link#12 UCS en0 ! 26 | 27 | Internet6: 28 | Destination Gateway Flags Netif Expire 29 | default fe80::%utun0 UGcIg utun0 30 | default fe80::%utun1 UGcIg utun1 31 | default fe80::%utun2 UGcIg utun2 32 | default fe80::%utun3 UGcIg utun3 33 | ::1 ::1 UHL lo0 34 | fe80::%lo0/64 fe80::1%lo0 UcI lo0 35 | fe80::1%lo0 link#1 UHLI lo0 36 | fe80::%ap1/64 link#11 UCI ap1 37 | fe80::a494:37ff:fedd:b48f%ap1 a6:94:37:dd:b4:8f UHLI lo0 38 | fe80::%en0/64 link#12 UCI en0 39 | fe80::8a:4628:b188:5a9%en0 2:b9:20:cd:7b:cd UHLWI en0 40 | fe80::85b:abc8:4ad9:6ccb%en0 ca:b5:38:f9:21:9a UHLWIi en0 41 | fe80::1432:d2fe:d3a:8a0a%en0 84:94:37:dd:b4:8f UHLI lo0 42 | fe80::fa4f:adff:feb3:5a4c%en0 f8:4f:ad:b3:5a:4c UHLWIi en0 43 | fe80::a00c:1bff:fe0d:6a61%awdl0 a2:c:1b:d:6a:61 UHLI lo0 44 | fe80::a00c:1bff:fe0d:6a61%llw0 a2:c:1b:d:6a:61 UHLI lo0 45 | fe80::%utun0/64 fe80::27a5:7220:507b:b2b1%utun0 UcI utun0 46 | fe80::27a5:7220:507b:b2b1%utun0 link#15 UHLI lo0 47 | fe80::%utun1/64 fe80::9a55:83bb:1893:c3e3%utun1 UcI utun1 48 | fe80::9a55:83bb:1893:c3e3%utun1 link#16 UHLI lo0 49 | fe80::%utun2/64 fe80::ce81:b1c:bd2c:69e%utun2 UcI utun2 50 | fe80::ce81:b1c:bd2c:69e%utun2 link#17 UHLI lo0 51 | fe80::%utun3/64 fe80::f918:d594:abb5:de0e%utun3 UcI utun3 52 | fe80::f918:d594:abb5:de0e%utun3 link#18 UHLI lo0 53 | ff00::/8 ::1 UmCI lo0 54 | ff00::/8 link#11 UmCI ap1 55 | ff00::/8 link#12 UmCI en0 56 | ff00::/8 link#13 UmCI awdl0 57 | ff00::/8 link#14 UmCI llw0 58 | ff00::/8 fe80::27a5:7220:507b:b2b1%utun0 UmCI utun0 59 | ff00::/8 fe80::9a55:83bb:1893:c3e3%utun1 UmCI utun1 60 | ff00::/8 fe80::ce81:b1c:bd2c:69e%utun2 UmCI utun2 61 | ff00::/8 fe80::f918:d594:abb5:de0e%utun3 UmCI utun3 62 | ff01::%lo0/32 ::1 UmCI lo0 63 | ff01::%ap1/32 link#11 UmCI ap1 64 | ff01::%en0/32 link#12 UmCI en0 65 | ff01::%utun0/32 fe80::27a5:7220:507b:b2b1%utun0 UmCI utun0 66 | ff01::%utun1/32 fe80::9a55:83bb:1893:c3e3%utun1 UmCI utun1 67 | ff01::%utun2/32 fe80::ce81:b1c:bd2c:69e%utun2 UmCI utun2 68 | ff01::%utun3/32 fe80::f918:d594:abb5:de0e%utun3 UmCI utun3 69 | ff02::%lo0/32 ::1 UmCI lo0 70 | ff02::%ap1/32 link#11 UmCI ap1 71 | ff02::%en0/32 link#12 UmCI en0 72 | ff02::%utun0/32 fe80::27a5:7220:507b:b2b1%utun0 UmCI utun0 73 | ff02::%utun1/32 fe80::9a55:83bb:1893:c3e3%utun1 UmCI utun1 74 | ff02::%utun2/32 fe80::ce81:b1c:bd2c:69e%utun2 UmCI utun2 75 | ff02::%utun3/32 fe80::f918:d594:abb5:de0e%utun3 UmCI utun3 76 | +++ 77 | Routing tables 78 | 79 | Internet: 80 | Destination Gateway Flags Refs Use Netif Expire 81 | default 192.168.1.1 UGSc 100 2000 en0 82 | 10.0.0.0/8 link#4 UCS 100 1500 en1 83 | 192.168.1.0/24 link#1 U 1 1000 en0 84 | 192.168.100.0/24 192.168.1.1 UGSc 100 500 en0 85 | 169.254.0.0/16 link#1 U 0 200 en0 86 | 172.16.0.0/12 192.168.1.1 UGSc 50 400 en0 87 | +++ 88 | Routing tables 89 | 90 | Internet: 91 | Destination Gateway Flags Refs Use Netif Expire 92 | default 192.168.1.1 UGSc 100 2000 en0 93 | default 192.168.2.1 UGSc 50 1000 en1 94 | 10.0.0.0/8 link#4 UCS 100 1500 en1 95 | 192.168.1.0/24 link#1 U 1 1000 en0 96 | 192.168.100.0/24 192.168.1.1 UGSc 100 500 en0 97 | +++ 98 | Routing tables 99 | 100 | Internet6: 101 | Destination Gateway Flags Netif Expire 102 | ::/0 fe80::1 UGSc en0 103 | 2001:db8::/32 link#4 UCS en1 104 | fe80::/10 link#1 U en0 105 | fd00:abcd::/64 2001:db8::1 UGSc en0 106 | fe80::/64 link#4 U en1 107 | +++ 108 | Routing tables 109 | 110 | Internet: 111 | Destination Gateway Flags Refs Use Netif Expire 112 | default 192.168.1.1 UGSc 100 2000 en0 113 | 10.0.0.0/8 link#4 UCS 100 1500 en1 114 | 192.168.1.0/24 link#1 U 1 1000 en0 115 | 172.16.0.0/12 192.168.1.1 UGSc 50 400 en0 116 | +++ 117 | Routing tables 118 | 119 | Internet: 120 | Destination Gateway Flags Refs Use Netif Expire 121 | default 192.168.1.1 UGSc 100 2000 en0 122 | 10.0.0.0/8 link#4 UCS 100 1500 en1 123 | 192.168.1.0/24 link#1 U 1 1000 en0 124 | 169.254.0.0/16 link#1 U 0 200 en0 125 | +++ 126 | Routing tables 127 | 128 | Internet: 129 | Destination Gateway Flags Refs Use Netif Expire 130 | default 192.168.1.1 UGSc 100 2000 en0 131 | 10.0.0.0/8 link#4 UCS 100 1500 en1 132 | 192.168.1.0/24 link#1 U 1 1000 en0 133 | 192.168.100.0/24 192.168.1.1 UGSc 100 500 en0 134 | 169.254.0.0/16 link#1 U 0 200 en0 135 | 172.16.0.0/12 192.168.1.1 UGSc 50 400 en0 136 | 137 | Internet6: 138 | Destination Gateway Flags Netif Expire 139 | ::/0 fe80::1 UGSc en0 140 | 2001:db8::/32 link#4 UCS en1 141 | fe80::/10 link#1 U en0 142 | fd00:abcd::/64 2001:db8::1 UGSc en0 143 | fe80::/64 link#4 U en1 144 | +++ 145 | Routing tables 146 | 147 | Internet: 148 | Destination Gateway Flags Refs Use Netif Expire 149 | default 192.168.1.1 UGSc 100 2000 en0 150 | 10.0.0.0/8 link#4 UCS 100 1500 en1 151 | 192.168.1.0/24 link#1 U 1 1000 en0 152 | 172.16.0.0/12 192.168.1.1 UGSc 50 400 en0 153 | 154 | Internet6: 155 | Destination Gateway Flags Netif Expire 156 | ::/0 fe80::1 UGSc en0 157 | 2001:db8::/32 link#4 UCS en1 158 | fe80::/10 link#1 U en0 159 | fd00:abcd::/64 2001:db8::1 UGSc en0 160 | fe80::/64 link#4 U en1 161 | +++ 162 | Routing tables 163 | 164 | Internet: 165 | Destination Gateway Flags Refs Use Netif Expire 166 | default 192.168.1.1 UGSc 100 2000 en0 167 | 10.0.0.0/8 link#4 UCS 100 1500 en1 168 | 192.168.1.0/24 link#1 U 1 1000 en0 169 | 192.168.100.0/24 192.168.1.1 UGSc 100 500 en0 170 | 172.16.0.0/12 192.168.1.1 UGSc 50 400 en0 171 | 172 | Internet6: 173 | Destination Gateway Flags Netif Expire 174 | ::/0 fe80::1 UGSc en0 175 | 2001:db8::/32 link#4 UCS en1 176 | fe80::/10 link#1 U en0 177 | fd00:abcd::/64 2001:db8::1 UGSc en0 178 | fe80::/64 link#4 U en1 -------------------------------------------------------------------------------- /src/vs/vscan.rs: -------------------------------------------------------------------------------- 1 | use std::io::Read; 2 | use std::io::Write; 3 | use std::net::IpAddr; 4 | use std::net::Ipv4Addr; 5 | use std::net::Ipv6Addr; 6 | use std::net::Shutdown; 7 | use std::net::SocketAddr; 8 | use std::net::TcpStream; 9 | use std::net::UdpSocket; 10 | use std::time::Duration; 11 | use tracing::debug; 12 | use tracing::error; 13 | use tracing::warn; 14 | 15 | use crate::error::PistolError; 16 | use crate::utils::random_port; 17 | use crate::vs::dbparser::Match; 18 | use crate::vs::dbparser::ProbeProtocol; 19 | use crate::vs::dbparser::ServiceProbe; 20 | use crate::vs::dbparser::SoftMatch; 21 | 22 | const TCP_BUFF_SIZE: usize = 40960; 23 | const UDP_BUFF_SIZE: usize = 40960; 24 | 25 | fn vs_probe_data_to_string(input: &[u8]) -> String { 26 | let mut ret = String::new(); 27 | for &i in input { 28 | match i { 29 | 0 => { 30 | ret += "\\0"; 31 | } 32 | 9 => { 33 | ret += "\t"; 34 | } 35 | 10 => { 36 | ret += "\n"; 37 | } 38 | 13 => { 39 | ret += "\r"; 40 | } 41 | 32..=126 => { 42 | let s = (i as char).to_string(); 43 | ret += &s; 44 | } 45 | _ => { 46 | let s = format!("\\x{:02x}", i); 47 | ret += &s; 48 | } 49 | } 50 | } 51 | ret 52 | } 53 | 54 | #[derive(Debug, Clone)] 55 | pub enum MatchX { 56 | Match(Match), 57 | SoftMatch(SoftMatch), 58 | } 59 | 60 | fn tcp_null_probe( 61 | stream: &mut TcpStream, 62 | service_probes: &[ServiceProbe], 63 | ) -> Result, PistolError> { 64 | let mut total_recv_buff = Vec::new(); 65 | loop { 66 | let mut recv_buff = [0u8; TCP_BUFF_SIZE]; 67 | match stream.read(&mut recv_buff) { 68 | Ok(n) => { 69 | debug!("tcp null probe n: {}", n); 70 | if n == 0 { 71 | break; 72 | } else { 73 | total_recv_buff.extend(recv_buff); 74 | } 75 | } 76 | Err(e) => { 77 | warn!("tcp null probe stream read failed: {}", e); 78 | break; 79 | } 80 | }; 81 | } 82 | 83 | let mut ret = Vec::new(); 84 | debug!("null probe recv buff len: {}", total_recv_buff.len()); 85 | if total_recv_buff.len() > 0 { 86 | let recv_str = vs_probe_data_to_string(&total_recv_buff); 87 | for sp in service_probes { 88 | if sp.probe.probename == "NULL" { 89 | match sp.check(&recv_str) { 90 | Some(mx) => ret.push(mx), 91 | None => (), 92 | } 93 | } 94 | } 95 | } 96 | Ok(ret) 97 | } 98 | 99 | fn tcp_continue_probe( 100 | stream: &mut TcpStream, 101 | dst_port: u16, 102 | only_tcp_recommended: bool, 103 | intensity: usize, 104 | service_probes: &[ServiceProbe], 105 | ) -> Result, PistolError> { 106 | fn run_probe(stream: &mut TcpStream, sp: &ServiceProbe) -> Result, PistolError> { 107 | let probestring = &sp.probe.probestring; 108 | stream.write(probestring)?; 109 | let mut total_recv_buff = Vec::new(); 110 | loop { 111 | let mut recv_buff = [0u8; TCP_BUFF_SIZE]; 112 | match stream.read(&mut recv_buff) { 113 | Ok(n) => { 114 | debug!("tcp continue probe n: {}", n); 115 | if n == 0 { 116 | break; 117 | } else { 118 | total_recv_buff.extend(recv_buff); 119 | } 120 | } 121 | Err(e) => { 122 | error!("tcp continue probe stream read failed: {}", e); 123 | break; 124 | } 125 | }; 126 | } 127 | 128 | debug!( 129 | "tcp continue probe recv buff len: {}", 130 | total_recv_buff.len() 131 | ); 132 | if total_recv_buff.len() > 0 { 133 | let recv_str = vs_probe_data_to_string(&total_recv_buff); 134 | let mut ret = Vec::new(); 135 | match sp.check(&recv_str) { 136 | Some(mx) => ret.push(mx), 137 | None => (), 138 | } 139 | Ok(ret) 140 | } else { 141 | Ok(vec![]) 142 | } 143 | } 144 | 145 | let mut ret = Vec::new(); 146 | // TCP connections continue here if the NULL probe described above fails or soft-matches. 147 | for sp in service_probes { 148 | if sp.probe.probename != "NULL" 149 | && sp.probe.probeprotocol == ProbeProtocol::Tcp 150 | && intensity >= sp.rarity 151 | { 152 | // Since the reality is that most ports are used by the service they are registered to in nmap-services, 153 | // every probe has a list of port numbers that are considered to be most effective. 154 | if only_tcp_recommended { 155 | if sp.ports.len() > 0 && sp.ports.contains(&dst_port) { 156 | let r = run_probe(stream, &sp)?; 157 | ret.extend(r); 158 | } 159 | } else { 160 | let r = run_probe(stream, &sp)?; 161 | ret.extend(r); 162 | } 163 | } 164 | } 165 | 166 | Ok(ret) 167 | } 168 | 169 | fn udp_probe( 170 | dst_addr: IpAddr, 171 | dst_port: u16, 172 | only_udp_recommended: bool, 173 | intensity: usize, 174 | service_probes: &[ServiceProbe], 175 | timeout: Duration, 176 | ) -> Result, PistolError> { 177 | fn run_probe(socket: &UdpSocket, sp: &ServiceProbe) -> Result, PistolError> { 178 | let mut ret = Vec::new(); 179 | let probestring = &sp.probe.probestring; 180 | socket.send(probestring)?; 181 | 182 | let mut total_recv_buff = Vec::new(); 183 | loop { 184 | let mut recv_buff = [0u8; UDP_BUFF_SIZE]; 185 | match socket.recv(&mut recv_buff) { 186 | Ok(n) => { 187 | debug!("udp probe n: {}", n); 188 | if n == 0 { 189 | break; 190 | } else { 191 | total_recv_buff.extend(recv_buff); 192 | } 193 | } 194 | Err(e) => { 195 | error!("udp probe recv failed: {}", e); 196 | break; 197 | } 198 | } 199 | } 200 | 201 | if total_recv_buff.len() > 0 { 202 | let recv_str = vs_probe_data_to_string(&total_recv_buff); 203 | match sp.check(&recv_str) { 204 | Some(mx) => ret.push(mx), 205 | None => (), 206 | } 207 | } 208 | Ok(ret) 209 | } 210 | 211 | let random_port = random_port(); 212 | let src_addr = match dst_addr { 213 | IpAddr::V4(_) => { 214 | let addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); 215 | SocketAddr::new(addr, random_port) 216 | } 217 | IpAddr::V6(_) => { 218 | let addr = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); 219 | SocketAddr::new(addr, random_port) 220 | } 221 | }; 222 | 223 | let dst_addr = SocketAddr::new(dst_addr, dst_port); 224 | let socket = UdpSocket::bind(src_addr)?; 225 | // let timeout = Duration::from_secs(1); // both 1 sec 226 | socket.set_read_timeout(Some(timeout))?; 227 | socket.set_write_timeout(Some(timeout))?; 228 | socket.connect(dst_addr)?; 229 | 230 | let mut ret = Vec::new(); 231 | for sp in service_probes { 232 | if sp.probe.probename != "NULL" 233 | && sp.probe.probeprotocol == ProbeProtocol::Udp 234 | && intensity >= sp.rarity 235 | { 236 | // Since the reality is that most ports are used by the service they are registered to in nmap-services, 237 | // every probe has a list of port numbers that are considered to be most effective. 238 | if only_udp_recommended { 239 | if sp.ports.contains(&dst_port) { 240 | let r = run_probe(&socket, sp)?; 241 | ret.extend(r); 242 | } 243 | } else { 244 | let r = run_probe(&socket, sp)?; 245 | ret.extend(r); 246 | } 247 | } 248 | } 249 | 250 | Ok(ret) 251 | } 252 | 253 | pub fn vs_scan_thread( 254 | dst_addr: IpAddr, 255 | dst_port: u16, 256 | only_null_probe: bool, 257 | only_tcp_recommended: bool, 258 | only_udp_recommended: bool, 259 | intensity: usize, 260 | service_probes: Vec, 261 | timeout: Duration, 262 | ) -> Result, PistolError> { 263 | // If the port is TCP, Nmap starts by connecting to it. 264 | let tcp_dst_addr = SocketAddr::new(dst_addr, dst_port); 265 | match TcpStream::connect_timeout(&tcp_dst_addr, timeout) { 266 | Ok(mut stream) => { 267 | // stream.set_nonblocking(false)?; 268 | // Once the TCP connection is made, Nmap listens for roughly five seconds. 269 | let five_seconds = Duration::from_secs(5); 270 | stream.set_read_timeout(Some(five_seconds))?; 271 | stream.set_nodelay(true).expect("set stream nodelay failed"); 272 | stream 273 | .set_nonblocking(false) 274 | .expect("set noblocking failed"); 275 | 276 | // If the connection succeeds and the port had been in the open|filtered state, it is changed to open (ignore this step). 277 | debug!("send null probe"); 278 | let null_probe_ret = tcp_null_probe(&mut stream, &service_probes)?; 279 | if null_probe_ret.len() > 0 { 280 | debug!("null probe work, exit"); 281 | match stream.shutdown(Shutdown::Both) { 282 | Ok(_) => (), 283 | Err(e) => error!("shutdown tcp stream failed: {}", e), 284 | } 285 | Ok(null_probe_ret) 286 | } else { 287 | stream.set_read_timeout(Some(timeout))?; 288 | stream.set_write_timeout(Some(timeout))?; 289 | if !only_null_probe { 290 | // Start TCP continue probe. 291 | debug!("send tcp continue probe"); 292 | let tcp_ret = tcp_continue_probe( 293 | &mut stream, 294 | dst_port, 295 | only_tcp_recommended, 296 | intensity, 297 | &service_probes, 298 | )?; 299 | if tcp_ret.len() > 0 { 300 | debug!("tcp continue probe work, exit"); 301 | match stream.shutdown(Shutdown::Both) { 302 | Ok(_) => (), 303 | Err(e) => error!("shutdown tcp stream failed: {}", e), 304 | } 305 | Ok(tcp_ret) 306 | } else { 307 | // This point is where Nmap starts for UDP probes, 308 | // and TCP connections continue here if the NULL probe described above fails or soft-matches. 309 | debug!("send udp probe"); 310 | let udp_ret = udp_probe( 311 | dst_addr, 312 | dst_port, 313 | only_udp_recommended, 314 | intensity, 315 | &service_probes, 316 | timeout, 317 | )?; 318 | match stream.shutdown(Shutdown::Both) { 319 | Ok(_) => (), 320 | Err(e) => error!("shutdown tcp stream failed: {}", e), 321 | } 322 | Ok(udp_ret) 323 | } 324 | } else { 325 | match stream.shutdown(Shutdown::Both) { 326 | Ok(_) => (), 327 | Err(e) => error!("shutdown tcp stream failed: {}", e), 328 | } 329 | Ok(vec![]) 330 | } 331 | } 332 | } 333 | Err(e) => { 334 | error!("connect to dst failed: {}", e); 335 | Ok(vec![]) // ignore closed port here 336 | } 337 | } 338 | } 339 | 340 | #[cfg(test)] 341 | mod tests { 342 | use super::*; 343 | use fancy_regex::Regex as FancyRegex; 344 | use regex::bytes::Regex; 345 | #[test] 346 | fn test_vs_probe_to_string() { 347 | let regex = FancyRegex::new(r"\\0\\0\\xae\\xae").unwrap(); 348 | let data = vec![0, 0, 0xae, 0xae]; 349 | let data_str = vs_probe_data_to_string(&data); 350 | let ret = regex.is_match(&data_str).unwrap(); 351 | println!("{}", data_str); 352 | println!("{}", ret); 353 | // let data_str = String::from_utf8_lossy(&data); 354 | // let ret = regex.is_match(&data_str).unwrap(); 355 | // println!("{}", data_str); 356 | // println!("{}", ret); 357 | } 358 | #[test] 359 | fn test_xx() { 360 | let data: &[u8] = &[72, 101, 108, 108, 111, 0, 87, 111, 114, 108, 100]; // "Hello\0World" 361 | let regex = Regex::new(r"\x00").unwrap(); 362 | 363 | if regex.is_match(data) { 364 | println!("XXX"); 365 | } else { 366 | } 367 | } 368 | } 369 | -------------------------------------------------------------------------------- /src/trace.rs: -------------------------------------------------------------------------------- 1 | /// Code obtained by analyzing the traffic generated by the Linux `traceroute` command. 2 | #[cfg(any(feature = "trace", feature = "os"))] 3 | use rand::Rng; 4 | #[cfg(any(feature = "trace", feature = "os"))] 5 | use std::net::IpAddr; 6 | #[cfg(any(feature = "trace", feature = "os"))] 7 | use std::time::Duration; 8 | #[cfg(any(feature = "trace", feature = "os"))] 9 | use tracing::debug; 10 | 11 | #[cfg(any(feature = "trace", feature = "os"))] 12 | use crate::error::PistolError; 13 | #[cfg(feature = "trace")] 14 | use crate::utils::random_port_range; 15 | 16 | #[cfg(any(feature = "trace", feature = "os"))] 17 | pub mod icmp; 18 | #[cfg(any(feature = "trace", feature = "os"))] 19 | pub mod icmpv6; 20 | #[cfg(any(feature = "trace", feature = "os"))] 21 | pub mod tcp; 22 | #[cfg(any(feature = "trace", feature = "os"))] 23 | pub mod tcp6; 24 | #[cfg(any(feature = "trace", feature = "os"))] 25 | pub mod udp; 26 | #[cfg(any(feature = "trace", feature = "os"))] 27 | pub mod udp6; 28 | 29 | #[cfg(any(feature = "trace", feature = "os"))] 30 | const UDP_DATA_SIZE: usize = 32; 31 | #[cfg(feature = "trace")] 32 | const START_PORT: u16 = 33434; 33 | 34 | #[cfg(any(feature = "trace", feature = "os"))] 35 | #[derive(Debug, Clone, Copy, PartialEq)] 36 | pub enum HopStatus { 37 | TimeExceeded(IpAddr), 38 | NoResponse, 39 | Unreachable(IpAddr), 40 | RecvReply(IpAddr), 41 | } 42 | 43 | #[cfg(feature = "trace")] 44 | pub fn syn_trace( 45 | dst_addr: IpAddr, 46 | dst_port: Option, // default is 80 47 | src_addr: IpAddr, 48 | timeout: Option, 49 | ) -> Result { 50 | let dst_port = match dst_port { 51 | Some(p) => p, 52 | None => 80, 53 | }; 54 | match dst_addr { 55 | IpAddr::V4(dst_ipv4) => { 56 | if let IpAddr::V4(src_ipv4) = src_addr { 57 | let mut rng = rand::rng(); 58 | let mut ip_id = rng.random(); 59 | // ensure that this u16 does not overflow 60 | if ip_id > u16::MAX - 30 { 61 | ip_id -= 30 62 | } 63 | let mut last_response_ttl = 0; 64 | for ttl in 1..=30 { 65 | let random_src_port = random_port_range(1000, 65535); 66 | // let random_src_port = 61234; // debug use 67 | let (hop_status, rtt) = tcp::send_syn_trace_packet( 68 | dst_ipv4, 69 | dst_port, 70 | src_ipv4, 71 | random_src_port, 72 | ip_id, 73 | ttl, 74 | timeout, 75 | )?; 76 | ip_id += 1; 77 | match hop_status { 78 | HopStatus::TimeExceeded(_) => { 79 | last_response_ttl = ttl; 80 | } 81 | HopStatus::RecvReply(_) => { 82 | // tcp rst packet, means packet arrive the target machine 83 | debug!("ttl: {}, {:?} - {:.2}s", ttl, hop_status, rtt.as_secs_f64()); 84 | return Ok(ttl); 85 | } 86 | _ => (), 87 | } 88 | } 89 | debug!("last ttl: {}", last_response_ttl); 90 | Ok(last_response_ttl) 91 | } else { 92 | return Err(PistolError::AddressProtocolError { dst_addr, src_addr }); 93 | } 94 | } 95 | IpAddr::V6(dst_ipv6) => { 96 | if let IpAddr::V6(src_ipv6) = src_addr { 97 | let mut last_response_hop_limit = 0; 98 | for hop_limit in 1..=30 { 99 | let random_src_port = random_port_range(1000, 65535); 100 | let (hop_status, rtt) = tcp6::send_syn_trace_packet( 101 | dst_ipv6, 102 | dst_port, 103 | src_ipv6, 104 | random_src_port, 105 | hop_limit, 106 | timeout, 107 | )?; 108 | 109 | match hop_status { 110 | HopStatus::TimeExceeded(_) => { 111 | last_response_hop_limit = hop_limit; 112 | } 113 | HopStatus::RecvReply(_) => { 114 | debug!( 115 | "hop_limit: {}, {:?} - {:.2}s", 116 | hop_limit, 117 | hop_status, 118 | rtt.as_secs_f64() 119 | ); 120 | // tcp rst packet, means packet arrive the target machine 121 | return Ok(hop_limit); 122 | } 123 | _ => (), 124 | } 125 | } 126 | debug!("last hop limit: {}", last_response_hop_limit); 127 | Ok(last_response_hop_limit) 128 | } else { 129 | return Err(PistolError::AddressProtocolError { dst_addr, src_addr }); 130 | } 131 | } 132 | } 133 | } 134 | 135 | #[cfg(any(feature = "trace", feature = "os"))] 136 | pub fn icmp_trace( 137 | dst_addr: IpAddr, 138 | src_addr: IpAddr, 139 | timeout: Option, 140 | ) -> Result { 141 | let mut rng = rand::rng(); 142 | let icmp_id = rng.random(); 143 | match dst_addr { 144 | IpAddr::V4(dst_ipv4) => { 145 | if let IpAddr::V4(src_ipv4) = src_addr { 146 | let mut ip_id = rng.random(); 147 | if ip_id > u16::MAX - 30 { 148 | ip_id -= 30; 149 | } 150 | let mut last_response_ttl = 0; 151 | for ttl in 1..=30 { 152 | let (hop_status, rtt) = icmp::send_icmp_trace_packet( 153 | dst_ipv4, src_ipv4, ip_id, ttl, icmp_id, ttl as u16, timeout, 154 | )?; 155 | ip_id += 1; 156 | match hop_status { 157 | HopStatus::TimeExceeded(_) => last_response_ttl = ttl, 158 | // recv echo reply, means packet arrive the target machine 159 | HopStatus::RecvReply(_) => { 160 | debug!("ttl: {}, {:?} - {:.2}s", ttl, hop_status, rtt.as_secs_f64()); 161 | return Ok(ttl); 162 | } 163 | _ => (), 164 | } 165 | } 166 | debug!("last ttl: {}", last_response_ttl); 167 | Ok(last_response_ttl) 168 | } else { 169 | return Err(PistolError::AddressProtocolError { dst_addr, src_addr }); 170 | } 171 | } 172 | IpAddr::V6(dst_ipv6) => { 173 | if let IpAddr::V6(src_ipv6) = src_addr { 174 | let mut last_response_hop_limit = 0; 175 | for hop_limit in 1..=30 { 176 | let (hop_status, rtt) = icmpv6::send_icmpv6_trace_packet( 177 | dst_ipv6, 178 | src_ipv6, 179 | hop_limit, 180 | icmp_id, 181 | hop_limit as u16, 182 | timeout, 183 | )?; 184 | 185 | match hop_status { 186 | HopStatus::TimeExceeded(_) => last_response_hop_limit = hop_limit, 187 | // recv echo reply, means packet arrive the target machine 188 | HopStatus::RecvReply(_) => { 189 | debug!( 190 | "hop_limit: {}, {:?} - {:.2}s", 191 | hop_limit, 192 | hop_status, 193 | rtt.as_secs_f64() 194 | ); 195 | return Ok(hop_limit); 196 | } 197 | _ => (), 198 | } 199 | } 200 | debug!("last hop limit: {}", last_response_hop_limit); 201 | Ok(last_response_hop_limit) 202 | } else { 203 | return Err(PistolError::AddressProtocolError { dst_addr, src_addr }); 204 | } 205 | } 206 | } 207 | } 208 | 209 | #[cfg(feature = "trace")] 210 | pub fn udp_trace( 211 | dst_addr: IpAddr, 212 | src_addr: IpAddr, 213 | timeout: Option, 214 | ) -> Result { 215 | match dst_addr { 216 | IpAddr::V4(dst_ipv4) => { 217 | if let IpAddr::V4(src_ipv4) = src_addr { 218 | let mut rng = rand::rng(); 219 | let mut ip_id = rng.random(); 220 | if ip_id > u16::MAX - 30 { 221 | ip_id -= 30; 222 | } 223 | let mut last_response_ttl = 0; 224 | for ttl in 1..=30 { 225 | let random_src_port = random_port_range(1000, 65535); 226 | let dst_port = START_PORT + (ttl - 1) as u16; 227 | let (hop_status, rtt) = udp::send_udp_trace_packet( 228 | dst_ipv4, 229 | dst_port, 230 | src_ipv4, 231 | random_src_port, 232 | ip_id, 233 | ttl, 234 | timeout, 235 | )?; 236 | ip_id += 1; 237 | match hop_status { 238 | HopStatus::TimeExceeded(_) => last_response_ttl = ttl, 239 | // icmpunreachable, means packet arrive the target machine 240 | HopStatus::RecvReply(_) | HopStatus::Unreachable(_) => { 241 | debug!("ttl: {}, {:?} - {:.2}s", ttl, hop_status, rtt.as_secs_f64()); 242 | return Ok(ttl); 243 | } 244 | _ => (), 245 | } 246 | } 247 | debug!("last ttl: {}", last_response_ttl); 248 | Ok(last_response_ttl) 249 | } else { 250 | return Err(PistolError::AddressProtocolError { dst_addr, src_addr }); 251 | } 252 | } 253 | IpAddr::V6(dst_ipv6) => { 254 | if let IpAddr::V6(src_ipv6) = src_addr { 255 | let mut last_response_hop_limit = 0; 256 | for hop_limit in 1..=30 { 257 | let random_src_port = random_port_range(1000, 65535); 258 | let dst_port = START_PORT + (hop_limit - 1) as u16; 259 | let (hop_status, rtt) = udp6::send_udp_trace_packet( 260 | dst_ipv6, 261 | dst_port, 262 | src_ipv6, 263 | random_src_port, 264 | hop_limit, 265 | timeout, 266 | )?; 267 | 268 | match hop_status { 269 | HopStatus::TimeExceeded(_) => last_response_hop_limit = hop_limit, 270 | // icmpunreachable, means packet arrive the target machine 271 | HopStatus::RecvReply(_) | HopStatus::Unreachable(_) => { 272 | debug!( 273 | "hop limit: {}, {:?} - {:.2}s", 274 | hop_limit, 275 | hop_status, 276 | rtt.as_secs_f64() 277 | ); 278 | return Ok(hop_limit); 279 | } 280 | _ => (), 281 | } 282 | } 283 | debug!("last hop limit: {}", last_response_hop_limit); 284 | Ok(last_response_hop_limit) 285 | } else { 286 | return Err(PistolError::AddressProtocolError { dst_addr, src_addr }); 287 | } 288 | } 289 | } 290 | } 291 | 292 | #[cfg(feature = "trace")] 293 | #[cfg(test)] 294 | mod tests { 295 | use super::*; 296 | use std::net::Ipv4Addr; 297 | use std::net::Ipv6Addr; 298 | use std::str::FromStr; 299 | #[test] 300 | fn test_get_hops_syn() { 301 | // let dst_ipv4 = Ipv4Addr::new(192, 168, 1, 3); 302 | // let dst_ipv4 = Ipv4Addr::new(192, 168, 5, 5); 303 | let dst_ipv4 = Ipv4Addr::new(182, 61, 244, 181); 304 | let src_ipv4 = Ipv4Addr::new(192, 168, 5, 3); 305 | let timeout = Some(Duration::from_secs_f64(5.0)); 306 | let hops = syn_trace(dst_ipv4.into(), None, src_ipv4.into(), timeout).unwrap(); 307 | println!("{}", hops); 308 | } 309 | #[test] 310 | fn test_get_hops_icmp() { 311 | // let dst_ipv4 = Ipv4Addr::new(192, 168, 1, 3); 312 | // let dst_ipv4 = Ipv4Addr::new(192, 168, 5, 5); 313 | let dst_ipv4 = Ipv4Addr::new(182, 61, 244, 181); 314 | let src_ipv4 = Ipv4Addr::new(192, 168, 5, 3); 315 | let timeout = Some(Duration::from_secs_f64(5.0)); 316 | let hops = icmp_trace(dst_ipv4.into(), src_ipv4.into(), timeout).unwrap(); 317 | println!("{}", hops); 318 | } 319 | #[test] 320 | fn test_get_hops_udp() { 321 | // let dst_ipv4 = Ipv4Addr::new(192, 168, 1, 3); 322 | let dst_ipv4 = Ipv4Addr::new(182, 61, 244, 181); 323 | let src_ipv4 = Ipv4Addr::new(192, 168, 5, 3); 324 | let timeout = Some(Duration::from_secs_f64(1.0)); 325 | let hops = udp_trace(dst_ipv4.into(), src_ipv4.into(), timeout).unwrap(); 326 | println!("{}", hops); 327 | } 328 | #[test] 329 | fn test_get_hops_udp6() { 330 | let src_ipv6 = Ipv6Addr::from_str("fe80::20c:29ff:fe5b:bd5c").unwrap(); 331 | let dst_ipv6 = Ipv6Addr::from_str("fe80::20c:29ff:fe2c:9e4").unwrap(); 332 | let timeout = Some(Duration::from_secs_f64(5.0)); 333 | let hops = udp_trace(dst_ipv6.into(), src_ipv6.into(), timeout).unwrap(); 334 | println!("{}", hops); 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /tests/windows_routetable.txt: -------------------------------------------------------------------------------- 1 | ifIndex DestinationPrefix NextHop RouteMetric ifMetric PolicyStore 2 | ------- ----------------- ------- ----------- -------- ----------- 3 | 15 255.255.255.255/32 0.0.0.0 256 25 ActiveStore 4 | 21 255.255.255.255/32 0.0.0.0 256 25 ActiveStore 5 | 11 255.255.255.255/32 0.0.0.0 256 35 ActiveStore 6 | 14 255.255.255.255/32 0.0.0.0 256 35 ActiveStore 7 | 30 255.255.255.255/32 0.0.0.0 256 35 ActiveStore 8 | 8 255.255.255.255/32 0.0.0.0 256 25 ActiveStore 9 | 20 255.255.255.255/32 0.0.0.0 256 65 ActiveStore 10 | 6 255.255.255.255/32 0.0.0.0 256 1 ActiveStore 11 | 10 255.255.255.255/32 0.0.0.0 256 25 ActiveStore 12 | 1 255.255.255.255/32 0.0.0.0 256 75 ActiveStore 13 | 15 224.0.0.0/4 0.0.0.0 256 25 ActiveStore 14 | 21 224.0.0.0/4 0.0.0.0 256 25 ActiveStore 15 | 11 224.0.0.0/4 0.0.0.0 256 35 ActiveStore 16 | 14 224.0.0.0/4 0.0.0.0 256 35 ActiveStore 17 | 30 224.0.0.0/4 0.0.0.0 256 35 ActiveStore 18 | 8 224.0.0.0/4 0.0.0.0 256 25 ActiveStore 19 | 20 224.0.0.0/4 0.0.0.0 256 65 ActiveStore 20 | 6 224.0.0.0/4 0.0.0.0 256 1 ActiveStore 21 | 10 224.0.0.0/4 0.0.0.0 256 25 ActiveStore 22 | 1 224.0.0.0/4 0.0.0.0 256 75 ActiveStore 23 | 14 192.168.23.255/32 0.0.0.0 256 35 ActiveStore 24 | 14 192.168.23.1/32 0.0.0.0 256 35 ActiveStore 25 | 14 192.168.23.0/24 0.0.0.0 256 35 ActiveStore 26 | 11 192.168.5.255/32 0.0.0.0 256 35 ActiveStore 27 | 11 192.168.5.1/32 0.0.0.0 256 35 ActiveStore 28 | 11 192.168.5.0/24 0.0.0.0 256 35 ActiveStore 29 | 30 192.168.1.255/32 0.0.0.0 256 35 ActiveStore 30 | 30 192.168.1.233/32 0.0.0.0 256 35 ActiveStore 31 | 30 192.168.1.0/24 0.0.0.0 256 35 ActiveStore 32 | 1 127.255.255.255/32 0.0.0.0 256 75 ActiveStore 33 | 1 127.0.0.1/32 0.0.0.0 256 75 ActiveStore 34 | 1 127.0.0.0/8 0.0.0.0 256 75 ActiveStore 35 | 30 10.100.45.255/32 0.0.0.0 256 35 ActiveStore 36 | 30 10.100.45.144/32 0.0.0.0 256 35 ActiveStore 37 | 30 10.100.45.0/24 0.0.0.0 256 35 ActiveStore 38 | 30 0.0.0.0/0 10.100.45.1 256 35 ActiveStore 39 | 15 ff00::/8 :: 256 25 ActiveStore 40 | 21 ff00::/8 :: 256 25 ActiveStore 41 | 11 ff00::/8 :: 256 35 ActiveStore 42 | 14 ff00::/8 :: 256 35 ActiveStore 43 | 30 ff00::/8 :: 256 35 ActiveStore 44 | 8 ff00::/8 :: 256 25 ActiveStore 45 | 20 ff00::/8 :: 256 65 ActiveStore 46 | 6 ff00::/8 :: 256 25 ActiveStore 47 | 10 ff00::/8 :: 256 25 ActiveStore 48 | 1 ff00::/8 :: 256 75 ActiveStore 49 | 8 fe80::e091:e6d2:91d3:6034/128 :: 256 25 ActiveStore 50 | 10 fe80::9e11:94e7:8ef7:a454/128 :: 256 25 ActiveStore 51 | 20 fe80::93d7:e6d3:3955:b9c7/128 :: 256 65 ActiveStore 52 | 15 fe80::8b9a:6195:689d:fc41/128 :: 256 25 ActiveStore 53 | 6 fe80::618c:eec0:e30b:994f/128 :: 256 25 ActiveStore 54 | 11 fe80::52d4:9e2c:4d51:d664/128 :: 256 35 ActiveStore 55 | 30 fe80::374f:624b:6e66:1062/128 :: 256 35 ActiveStore 56 | 21 fe80::1eb2:aff8:47c5:9b03/128 :: 256 25 ActiveStore 57 | 14 fe80::19d3:7bea:9c12:d930/128 :: 256 35 ActiveStore 58 | 15 fe80::/64 :: 256 25 ActiveStore 59 | 21 fe80::/64 :: 256 25 ActiveStore 60 | 11 fe80::/64 :: 256 35 ActiveStore 61 | 14 fe80::/64 :: 256 35 ActiveStore 62 | 30 fe80::/64 :: 256 35 ActiveStore 63 | 8 fe80::/64 :: 256 25 ActiveStore 64 | 20 fe80::/64 :: 256 65 ActiveStore 65 | 6 fe80::/64 :: 256 25 ActiveStore 66 | 10 fe80::/64 :: 256 25 ActiveStore 67 | 1 ::1/128 :: 256 75 ActiveStore 68 | +++ 69 | ifIndex DestinationPrefix NextHop RouteMetric 70 | ------- ----------------- ------- ------------ 71 | 3 0.0.0.0/0 192.168.1.1 30 72 | 4 10.0.0.0/8 On-link 10 73 | 4 192.168.1.0/24 On-link 10 74 | 4 192.168.100.0/24 192.168.1.1 20 75 | 4 192.168.200.0/24 On-link 10 76 | 3 169.254.0.0/16 On-link 10 77 | 4 172.16.0.0/12 192.168.1.1 50 78 | +++ 79 | ifIndex DestinationPrefix NextHop RouteMetric InterfaceAlias 80 | ------- ----------------- ------- ------------ --------------- 81 | 3 0.0.0.0/0 192.168.1.1 30 Ethernet 82 | 4 10.0.0.0/8 On-link 10 Wi-Fi 83 | 4 192.168.1.0/24 On-link 10 Ethernet 84 | 4 192.168.100.0/24 192.168.1.1 20 Ethernet 85 | 4 192.168.200.0/24 On-link 10 Ethernet 86 | 3 169.254.0.0/16 On-link 10 Ethernet 87 | 4 172.16.0.0/12 192.168.1.1 50 Wi-Fi 88 | +++ 89 | ifIndex DestinationPrefix NextHop RouteMetric RouteSource 90 | ------- ----------------- ------- ------------ ------------ 91 | 3 0.0.0.0/0 192.168.1.1 30 Dhcp 92 | 4 10.0.0.0/8 On-link 10 Kernel 93 | 4 192.168.1.0/24 On-link 10 Kernel 94 | 4 192.168.100.0/24 192.168.1.1 20 Static 95 | 4 192.168.200.0/24 On-link 10 Static 96 | 3 169.254.0.0/16 On-link 10 Kernel 97 | 4 172.16.0.0/12 192.168.1.1 50 Static 98 | +++ 99 | ifIndex DestinationPrefix NextHop RouteMetric RouteSource 100 | ------- ----------------- ------- ------------ ------------ 101 | 3 ::/0 fe80::1 30 Dhcp 102 | 4 2001:db8::/32 On-link 10 Kernel 103 | 4 2001:db8:abcd::/64 2001:db8::1 20 Static 104 | 4 fe80::/10 On-link 10 Kernel 105 | 3 fe80::/64 On-link 10 Dhcp 106 | 4 fd00:abcd::/64 2001:db8::1 50 Static 107 | +++ 108 | ifIndex DestinationPrefix NextHop RouteMetric RouteSource 109 | ------- ----------------- ------- ------------ ------------ 110 | 3 0.0.0.0/0 192.168.1.1 30 Dhcp 111 | 4 0.0.0.0/0 192.168.2.1 20 Static 112 | 4 10.0.0.0/8 On-link 10 Kernel 113 | 3 192.168.1.0/24 On-link 10 Kernel 114 | +++ 115 | ifIndex DestinationPrefix NextHop RouteMetric InterfaceAlias 116 | ------- ----------------- ------- ------------ --------------- 117 | 3 0.0.0.0/0 192.168.1.1 30 Ethernet 118 | 4 10.0.0.0/8 On-link 10 Wi-Fi 119 | 4 192.168.1.0/24 On-link 10 Ethernet 120 | 4 192.168.100.0/24 192.168.1.1 20 Ethernet 121 | 4 172.16.0.0/12 192.168.2.1 50 Wi-Fi 122 | 3 169.254.0.0/16 On-link 10 Ethernet 123 | +++ 124 | ifIndex DestinationPrefix NextHop RouteMetric InterfaceAlias 125 | ------- ----------------- ------- ------------ --------------- 126 | 3 0.0.0.0/0 192.168.1.1 30 Ethernet 127 | 4 10.0.0.0/8 On-link 10 Wi-Fi 128 | 4 192.168.1.0/24 On-link 10 Ethernet 129 | 4 172.16.0.0/12 192.168.2.1 50 Wi-Fi 130 | 4 2001:db8::/32 On-link 10 Ethernet 131 | +++ 132 | ifIndex DestinationPrefix NextHop RouteMetric RouteType LinkState 133 | ------- ----------------- ------- ------------ --------- ---------- 134 | 3 0.0.0.0/0 192.168.1.1 30 Direct Up 135 | 4 10.0.0.0/8 On-link 10 Direct Up 136 | 4 192.168.1.0/24 On-link 10 Direct Up 137 | 4 172.16.0.0/12 192.168.1.1 50 Static Down -------------------------------------------------------------------------------- /src/ping/icmp.rs: -------------------------------------------------------------------------------- 1 | use chrono::Utc; 2 | use pnet::packet::Packet; 3 | use pnet::packet::icmp; 4 | use pnet::packet::icmp::IcmpCode; 5 | use pnet::packet::icmp::IcmpPacket; 6 | use pnet::packet::icmp::IcmpTypes; 7 | use pnet::packet::icmp::MutableIcmpPacket; 8 | use pnet::packet::icmp::destination_unreachable; 9 | use pnet::packet::icmp::echo_request::MutableEchoRequestPacket; 10 | use pnet::packet::ip::IpNextHeaderProtocols; 11 | use pnet::packet::ipv4; 12 | use pnet::packet::ipv4::Ipv4Flags; 13 | use pnet::packet::ipv4::Ipv4Packet; 14 | use pnet::packet::ipv4::MutableIpv4Packet; 15 | use rand::Rng; 16 | use std::panic::Location; 17 | 18 | use std::net::Ipv4Addr; 19 | use std::time::Duration; 20 | 21 | use crate::error::PistolError; 22 | use crate::layer::ICMP_HEADER_SIZE; 23 | use crate::layer::IPV4_HEADER_SIZE; 24 | use crate::layer::Layer3Filter; 25 | use crate::layer::Layer4FilterIcmp; 26 | use crate::layer::PacketFilter; 27 | use crate::layer::layer3_ipv4_send; 28 | use crate::ping::PingStatus; 29 | use crate::scan::DataRecvStatus; 30 | 31 | const TTL: u8 = 64; 32 | 33 | pub fn send_icmp_echo_packet( 34 | dst_ipv4: Ipv4Addr, 35 | src_ipv4: Ipv4Addr, 36 | timeout: Option, 37 | ) -> Result<(PingStatus, DataRecvStatus, Duration), PistolError> { 38 | const ICMP_DATA_SIZE: usize = 16; 39 | let mut rng = rand::rng(); 40 | // ip header 41 | let mut ip_buff = [0u8; IPV4_HEADER_SIZE + ICMP_HEADER_SIZE + ICMP_DATA_SIZE]; 42 | let mut ip_header = match MutableIpv4Packet::new(&mut ip_buff) { 43 | Some(p) => p, 44 | None => { 45 | return Err(PistolError::BuildPacketError { 46 | location: format!("{}", Location::caller()), 47 | }); 48 | } 49 | }; 50 | ip_header.set_version(4); 51 | ip_header.set_header_length(5); 52 | ip_header.set_source(src_ipv4); 53 | ip_header.set_destination(dst_ipv4); 54 | ip_header.set_total_length((IPV4_HEADER_SIZE + ICMP_HEADER_SIZE + ICMP_DATA_SIZE) as u16); 55 | let id = rng.random(); 56 | ip_header.set_identification(id); 57 | ip_header.set_flags(Ipv4Flags::DontFragment); 58 | ip_header.set_ttl(TTL); 59 | ip_header.set_next_level_protocol(IpNextHeaderProtocols::Icmp); 60 | let c = ipv4::checksum(&ip_header.to_immutable()); 61 | ip_header.set_checksum(c); 62 | 63 | let mut icmp_header = match MutableEchoRequestPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 64 | Some(p) => p, 65 | None => { 66 | return Err(PistolError::BuildPacketError { 67 | location: format!("{}", Location::caller()), 68 | }); 69 | } 70 | }; 71 | icmp_header.set_icmp_type(IcmpTypes::EchoRequest); // echo 72 | icmp_header.set_icmp_code(IcmpCode(0)); 73 | icmp_header.set_identifier(rng.random()); 74 | icmp_header.set_sequence_number(1); 75 | let mut tv_sec = Utc::now().timestamp().to_be_bytes(); 76 | tv_sec.reverse(); // Big-Endian 77 | let mut tv_usec = Utc::now().timestamp_subsec_millis().to_be_bytes(); 78 | tv_usec.reverse(); // Big-Endian 79 | let mut timestamp = Vec::new(); 80 | timestamp.extend(tv_sec); 81 | timestamp.extend(tv_usec); 82 | icmp_header.set_payload(×tamp); 83 | 84 | let mut icmp_header = match MutableIcmpPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 85 | Some(p) => p, 86 | None => { 87 | return Err(PistolError::BuildPacketError { 88 | location: format!("{}", Location::caller()), 89 | }); 90 | } 91 | }; 92 | let checksum = icmp::checksum(&icmp_header.to_immutable()); 93 | icmp_header.set_checksum(checksum); 94 | 95 | let codes_1 = vec![ 96 | destination_unreachable::IcmpCodes::DestinationProtocolUnreachable, // 2 97 | destination_unreachable::IcmpCodes::DestinationHostUnreachable, // 1 98 | destination_unreachable::IcmpCodes::DestinationPortUnreachable, // 3 99 | destination_unreachable::IcmpCodes::NetworkAdministrativelyProhibited, // 9 100 | destination_unreachable::IcmpCodes::HostAdministrativelyProhibited, // 10 101 | destination_unreachable::IcmpCodes::CommunicationAdministrativelyProhibited, // 13 102 | ]; 103 | 104 | let layer3 = Layer3Filter { 105 | name: "ping echo layer3", 106 | layer2: None, 107 | src_addr: Some(dst_ipv4.into()), 108 | dst_addr: Some(src_ipv4.into()), 109 | }; 110 | // match all icmp reply 111 | let layer4_icmp = Layer4FilterIcmp { 112 | name: "ping echo icmp", 113 | layer3: Some(layer3), 114 | icmp_type: None, 115 | icmp_code: None, 116 | payload: None, 117 | }; 118 | let layer_match = PacketFilter::Layer4FilterIcmp(layer4_icmp); 119 | 120 | let (ret, rtt) = layer3_ipv4_send( 121 | dst_ipv4, 122 | src_ipv4, 123 | &ip_buff, 124 | vec![layer_match], 125 | timeout, 126 | true, 127 | )?; 128 | match Ipv4Packet::new(&ret) { 129 | Some(ipv4_packet) => match ipv4_packet.get_next_level_protocol() { 130 | IpNextHeaderProtocols::Icmp => match IcmpPacket::new(ipv4_packet.payload()) { 131 | Some(icmp_packet) => { 132 | let icmp_type = icmp_packet.get_icmp_type(); 133 | let icmp_code = icmp_packet.get_icmp_code(); 134 | if icmp_type == IcmpTypes::DestinationUnreachable { 135 | if codes_1.contains(&icmp_code) { 136 | return Ok((PingStatus::Down, DataRecvStatus::Yes, rtt)); 137 | } 138 | } else if icmp_type == IcmpTypes::EchoReply { 139 | return Ok((PingStatus::Up, DataRecvStatus::Yes, rtt)); 140 | } 141 | } 142 | None => (), 143 | }, 144 | _ => (), 145 | }, 146 | None => (), 147 | } 148 | // no response received (even after retransmissions) 149 | Ok((PingStatus::Down, DataRecvStatus::No, rtt)) 150 | } 151 | 152 | pub fn send_icmp_timestamp_packet( 153 | dst_ipv4: Ipv4Addr, 154 | src_ipv4: Ipv4Addr, 155 | timeout: Option, 156 | ) -> Result<(PingStatus, DataRecvStatus, Duration), PistolError> { 157 | const ICMP_DATA_SIZE: usize = 12; 158 | let mut rng = rand::rng(); 159 | // ip header 160 | let mut ip_buff = [0u8; IPV4_HEADER_SIZE + ICMP_HEADER_SIZE + ICMP_DATA_SIZE]; 161 | let mut ip_header = match MutableIpv4Packet::new(&mut ip_buff) { 162 | Some(p) => p, 163 | None => { 164 | return Err(PistolError::BuildPacketError { 165 | location: format!("{}", Location::caller()), 166 | }); 167 | } 168 | }; 169 | ip_header.set_version(4); 170 | ip_header.set_header_length(5); 171 | ip_header.set_source(src_ipv4); 172 | ip_header.set_destination(dst_ipv4); 173 | ip_header.set_total_length((IPV4_HEADER_SIZE + ICMP_HEADER_SIZE + ICMP_DATA_SIZE) as u16); 174 | let id = rng.random(); 175 | ip_header.set_identification(id); 176 | ip_header.set_flags(Ipv4Flags::DontFragment); 177 | ip_header.set_ttl(TTL); 178 | ip_header.set_next_level_protocol(IpNextHeaderProtocols::Icmp); 179 | let c = ipv4::checksum(&ip_header.to_immutable()); 180 | ip_header.set_checksum(c); 181 | 182 | // the timestamp package and echo request package are similar, so the structure will not be changed. 183 | let mut icmp_header = match MutableEchoRequestPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 184 | Some(p) => p, 185 | None => { 186 | return Err(PistolError::BuildPacketError { 187 | location: format!("{}", Location::caller()), 188 | }); 189 | } 190 | }; 191 | icmp_header.set_icmp_type(IcmpTypes::Timestamp); // timestamp 192 | icmp_header.set_icmp_code(IcmpCode(0)); 193 | icmp_header.set_identifier(rng.random()); 194 | icmp_header.set_sequence_number(0); 195 | let timestamp_payload = [0u8; ICMP_DATA_SIZE]; 196 | icmp_header.set_payload(×tamp_payload); 197 | 198 | let mut icmp_header = match MutableIcmpPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 199 | Some(p) => p, 200 | None => { 201 | return Err(PistolError::BuildPacketError { 202 | location: format!("{}", Location::caller()), 203 | }); 204 | } 205 | }; 206 | let checksum = icmp::checksum(&icmp_header.to_immutable()); 207 | icmp_header.set_checksum(checksum); 208 | 209 | let layer3 = Layer3Filter { 210 | name: "ping timestamp layer3", 211 | layer2: None, 212 | src_addr: Some(dst_ipv4.into()), 213 | dst_addr: Some(src_ipv4.into()), 214 | }; 215 | // match all icmp reply 216 | let layer4_icmp = Layer4FilterIcmp { 217 | name: "ping timestamp icmp", 218 | layer3: Some(layer3), 219 | icmp_type: None, 220 | icmp_code: None, 221 | payload: None, 222 | }; 223 | let layer_match = PacketFilter::Layer4FilterIcmp(layer4_icmp); 224 | 225 | let (ret, rtt) = layer3_ipv4_send( 226 | dst_ipv4, 227 | src_ipv4, 228 | &ip_buff, 229 | vec![layer_match], 230 | timeout, 231 | true, 232 | )?; 233 | match Ipv4Packet::new(&ret) { 234 | Some(ipv4_packet) => match ipv4_packet.get_next_level_protocol() { 235 | IpNextHeaderProtocols::Icmp => match IcmpPacket::new(ipv4_packet.payload()) { 236 | Some(icmp_packet) => { 237 | let icmp_type = icmp_packet.get_icmp_type(); 238 | if icmp_type == IcmpTypes::DestinationUnreachable { 239 | return Ok((PingStatus::Down, DataRecvStatus::Yes, rtt)); 240 | } else if icmp_type == IcmpTypes::TimestampReply { 241 | return Ok((PingStatus::Up, DataRecvStatus::Yes, rtt)); 242 | } 243 | } 244 | None => (), 245 | }, 246 | _ => (), 247 | }, 248 | None => (), 249 | } 250 | // no response received (even after retransmissions) 251 | Ok((PingStatus::Down, DataRecvStatus::No, rtt)) 252 | } 253 | 254 | pub fn send_icmp_address_mask_packet( 255 | dst_ipv4: Ipv4Addr, 256 | src_ipv4: Ipv4Addr, 257 | timeout: Option, 258 | ) -> Result<(PingStatus, DataRecvStatus, Duration), PistolError> { 259 | const ICMP_DATA_SIZE: usize = 4; 260 | let mut rng = rand::rng(); 261 | // ip header 262 | let mut ip_buff = [0u8; IPV4_HEADER_SIZE + ICMP_HEADER_SIZE + ICMP_DATA_SIZE]; 263 | let mut ip_header = match MutableIpv4Packet::new(&mut ip_buff) { 264 | Some(p) => p, 265 | None => { 266 | return Err(PistolError::BuildPacketError { 267 | location: format!("{}", Location::caller()), 268 | }); 269 | } 270 | }; 271 | ip_header.set_version(4); 272 | ip_header.set_header_length(5); 273 | ip_header.set_source(src_ipv4); 274 | ip_header.set_destination(dst_ipv4); 275 | ip_header.set_total_length((IPV4_HEADER_SIZE + ICMP_HEADER_SIZE + ICMP_DATA_SIZE) as u16); 276 | let id = rng.random(); 277 | ip_header.set_identification(id); 278 | ip_header.set_flags(Ipv4Flags::DontFragment); 279 | ip_header.set_ttl(TTL); 280 | ip_header.set_next_level_protocol(IpNextHeaderProtocols::Icmp); 281 | let c = ipv4::checksum(&ip_header.to_immutable()); 282 | ip_header.set_checksum(c); 283 | 284 | // the address mask package and echo request package are similar, so the structure will not be changed. 285 | let mut icmp_header = match MutableEchoRequestPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 286 | Some(p) => p, 287 | None => { 288 | return Err(PistolError::BuildPacketError { 289 | location: format!("{}", Location::caller()), 290 | }); 291 | } 292 | }; 293 | icmp_header.set_icmp_type(IcmpTypes::AddressMaskRequest); // address mask 294 | icmp_header.set_icmp_code(IcmpCode(0)); 295 | icmp_header.set_identifier(rng.random()); 296 | icmp_header.set_sequence_number(0); 297 | let timestamp_payload = [0u8; ICMP_DATA_SIZE]; 298 | icmp_header.set_payload(×tamp_payload); 299 | 300 | let mut icmp_header = match MutableIcmpPacket::new(&mut ip_buff[IPV4_HEADER_SIZE..]) { 301 | Some(p) => p, 302 | None => { 303 | return Err(PistolError::BuildPacketError { 304 | location: format!("{}", Location::caller()), 305 | }); 306 | } 307 | }; 308 | let checksum = icmp::checksum(&icmp_header.to_immutable()); 309 | icmp_header.set_checksum(checksum); 310 | 311 | let codes_1 = vec![ 312 | destination_unreachable::IcmpCodes::DestinationProtocolUnreachable, // 2 313 | destination_unreachable::IcmpCodes::DestinationHostUnreachable, // 1 314 | destination_unreachable::IcmpCodes::DestinationPortUnreachable, // 3 315 | destination_unreachable::IcmpCodes::NetworkAdministrativelyProhibited, // 9 316 | destination_unreachable::IcmpCodes::HostAdministrativelyProhibited, // 10 317 | destination_unreachable::IcmpCodes::CommunicationAdministrativelyProhibited, // 13 318 | ]; 319 | 320 | let layer3 = Layer3Filter { 321 | name: "ping address mask layer3", 322 | layer2: None, 323 | src_addr: Some(dst_ipv4.into()), 324 | dst_addr: Some(src_ipv4.into()), 325 | }; 326 | // match all icmp reply 327 | let layer4_icmp = Layer4FilterIcmp { 328 | name: "ping address mask icmp", 329 | layer3: Some(layer3), 330 | icmp_type: None, 331 | icmp_code: None, 332 | payload: None, 333 | }; 334 | let layer_match = PacketFilter::Layer4FilterIcmp(layer4_icmp); 335 | 336 | let (ret, rtt) = layer3_ipv4_send( 337 | dst_ipv4, 338 | src_ipv4, 339 | &ip_buff, 340 | vec![layer_match], 341 | timeout, 342 | true, 343 | )?; 344 | match Ipv4Packet::new(&ret) { 345 | Some(ipv4_packet) => { 346 | match ipv4_packet.get_next_level_protocol() { 347 | IpNextHeaderProtocols::Icmp => { 348 | match IcmpPacket::new(ipv4_packet.payload()) { 349 | Some(icmp_packet) => { 350 | let icmp_type = icmp_packet.get_icmp_type(); 351 | let icmp_code = icmp_packet.get_icmp_code(); 352 | if icmp_type == IcmpTypes::DestinationUnreachable { 353 | if codes_1.contains(&icmp_code) { 354 | // icmp protocol unreachable error (type 3, code 2) 355 | return Ok((PingStatus::Down, DataRecvStatus::Yes, rtt)); 356 | } 357 | } else if icmp_type == IcmpTypes::AddressMaskReply { 358 | return Ok((PingStatus::Up, DataRecvStatus::Yes, rtt)); 359 | } 360 | } 361 | None => (), 362 | } 363 | } 364 | _ => (), 365 | } 366 | } 367 | None => (), 368 | } 369 | // no response received (even after retransmissions) 370 | Ok((PingStatus::Down, DataRecvStatus::No, rtt)) 371 | } 372 | --------------------------------------------------------------------------------