Skip to content

Commit e17dcf4

Browse files
authored
Merge pull request #6 from thewh1teagle/feat/windows-support
feat: add windows support
2 parents afcd1ed + 92713f8 commit e17dcf4

File tree

6 files changed

+47
-21
lines changed

6 files changed

+47
-21
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
/target
22
/builds
33
**/*.rs.bk
4+
*.lib

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,16 @@ The features below will be shipped in the next releases of the project.
224224
- Change verbose options (for debug, network details, quiet mode, ...)
225225
- Avoid packet copy in userspace for faster scans (BPF filtering)
226226

227+
## Building
228+
#### Linux / Mac
229+
Simply run `cargo build`
230+
231+
#### Windows
232+
See [github.com/libpnet/libpnet#windows](https://github.com/libpnet/libpnet#windows)
233+
In additional for what they described there,
234+
for linking `Packet.lib` you can just place it in the root of this project.
235+
236+
227237
## Contributing
228238

229239
Feel free to suggest an improvement, report a bug, or ask something: https://github.com/saluki/arp-scan-rs/issues

src/args.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,15 @@ pub fn build_args() -> Command {
6161
])
6262
.help("Scan profile - a preset of ARP scan options")
6363
)
64+
.arg(
65+
Arg::new("index").long("index")
66+
.value_name("INTERFACE_INDEX")
67+
.help("Network interface index (defaults to first 'up' interface with IPv4)")
68+
)
6469
.arg(
6570
Arg::new("interface").short('i').long("interface")
6671
.value_name("INTERFACE_NAME")
67-
.help("Network interface (defaults to first 'up' interface with IPv4)")
72+
.help("Network interface name (defaults to first 'up' interface with IPv4)")
6873
)
6974
.arg(
7075
Arg::new("network").short('n').long("network")
@@ -207,6 +212,7 @@ pub enum ScanTiming {
207212
pub struct ScanOptions {
208213
pub profile: ProfileType,
209214
pub interface_name: Option<String>,
215+
pub interface_index: Option<u32>,
210216
pub network_range: Option<Vec<ipnetwork::IpNetwork>>,
211217
pub timeout_ms: u64,
212218
pub resolve_hostname: bool,
@@ -330,6 +336,9 @@ impl ScanOptions {
330336
};
331337

332338
let interface_name = matches.get_one::<String>("interface").cloned();
339+
let interface_index_str = matches.get_one::<String>("index").cloned();
340+
let interface_index = interface_index_str.unwrap_or_default().parse::<u32>().ok();
341+
333342

334343
let file_option = matches.get_one::<String>("file");
335344
let network_option = matches.get_one::<String>("network");
@@ -528,6 +537,7 @@ impl ScanOptions {
528537
Arc::new(ScanOptions {
529538
profile,
530539
interface_name,
540+
interface_index,
531541
network_range,
532542
timeout_ms,
533543
resolve_hostname,

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ fn main() {
4545
process::exit(0);
4646
}
4747

48-
if !utils::is_root_user() {
48+
if !cfg!(windows) && !utils::is_root_user() {
4949
eprintln!("Should run this binary as root or use --help for options");
5050
process::exit(1);
5151
}

src/network.rs

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -69,29 +69,23 @@ pub struct TargetDetails {
6969
* specific network on a network interfaces.
7070
*/
7171
pub fn compute_network_configuration<'a>(interfaces: &'a [NetworkInterface], scan_options: &'a Arc<ScanOptions>) -> (&'a NetworkInterface, Vec<&'a IpNetwork>) {
72-
73-
let interface_name = match &scan_options.interface_name {
74-
Some(name) => String::from(name),
75-
None => {
76-
77-
let name = utils::select_default_interface(interfaces).map(|interface| interface.name);
78-
79-
match name {
80-
Some(name) => name,
81-
None => {
72+
let selected_interface = match (&scan_options.interface_name, &scan_options.interface_index) {
73+
(Some(interface_name), _) => {
74+
find_interface_by_name(interfaces, interface_name)
75+
},
76+
(None, Some(interface_index)) => {
77+
find_interface_by_index(interfaces, *interface_index)
78+
},
79+
_ => {
8280
eprintln!("Could not find a default network interface");
8381
eprintln!("Use 'arp scan -l' to list available interfaces");
8482
process::exit(1);
8583
}
86-
}
87-
}
8884
};
8985

90-
let selected_interface: &NetworkInterface = interfaces.iter()
91-
.find(|interface| { interface.name == interface_name && interface.is_up() && !interface.is_loopback() })
92-
.unwrap_or_else(|| {
93-
eprintln!("Could not find interface with name {}", interface_name);
94-
eprintln!("Make sure the interface is up, not loopback and has a valid IPv4");
86+
let selected_interface = selected_interface.unwrap_or_else(|| {
87+
eprintln!("Could not find the specified interface");
88+
eprintln!("Make sure the interface is up, not loopback, and has a valid IPv4");
9589
process::exit(1);
9690
});
9791

@@ -103,6 +97,17 @@ pub fn compute_network_configuration<'a>(interfaces: &'a [NetworkInterface], sca
10397
(selected_interface, ip_networks)
10498
}
10599

100+
fn find_interface_by_name<'a>(interfaces: &'a [NetworkInterface], interface_name: &str) -> Option<&'a NetworkInterface> {
101+
interfaces.iter()
102+
.find(|interface| interface.name == interface_name && (cfg!(windows) || interface.is_up()) && !interface.is_loopback())
103+
}
104+
105+
fn find_interface_by_index<'a>(interfaces: &'a [NetworkInterface], interface_index: u32) -> Option<&'a NetworkInterface> {
106+
interfaces.iter()
107+
.find(|interface| interface.index == interface_index && (cfg!(windows) || interface.is_up()) && !interface.is_loopback())
108+
}
109+
110+
106111
/**
107112
* Based on the network size and given scan options, this function performs an
108113
* estimation of the scan impact (timing, bandwidth, ...). Keep in mind that

src/utils.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ pub fn show_interfaces(interfaces: &[NetworkInterface]) {
3030

3131
println!();
3232
for interface in interfaces.iter() {
33-
3433
let up_text = match interface.is_up() {
3534
true => format!("{} UP", Green.paint("✔")),
3635
false => format!("{} DOWN", Red.paint("✖"))
@@ -44,7 +43,8 @@ pub fn show_interfaces(interfaces: &[NetworkInterface]) {
4443
None => "".to_string()
4544
};
4645

47-
println!("{: <20} {: <18} {: <20} {}", interface.name, up_text, mac_text, first_ip);
46+
let index_text = format!("{} ", interface.index);
47+
println!("{: <3} {: <20} {: <18} {: <20} {}", index_text ,interface.name, up_text, mac_text, first_ip);
4848

4949
interface_count += 1;
5050
if interface.is_up() && !interface.is_loopback() && !interface.ips.is_empty() {

0 commit comments

Comments
 (0)