Repository navigation
Expand file tree
/
Copy pathbuild.rs
More file actions
214 lines (178 loc) · 7.5 KB
/
Copy pathbuild.rs
File metadata and controls
214 lines (178 loc) · 7.5 KB
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
use anyhow::Result;
use std::{env, fs::File, path::PathBuf};
fn main() -> Result<()> {
generate_assets()?;
setup_cross_compilation_libs();
// A stock Npcap install keeps its DLLs under System32\Npcap, outside the
// default loader search path. Delay-loading lets main() add that directory
// before Windows resolves the imports.
setup_windows_npcap_delay_load();
#[cfg(target_os = "windows")]
download_windows_npcap_sdk()?;
embed_windows_manifest();
println!("cargo:rerun-if-changed=src/cli.rs");
Ok(())
}
/// Embed the UTF-8 active-code-page application manifest on MSVC Windows
/// targets, the only Windows toolchain releases ship. Two plain linker
/// flags, no build dependency; see the manifest file for why it exists.
/// The GNU toolchain would need a compiled resource object instead and is
/// deliberately left as-is (no manifest, today's behavior).
fn embed_windows_manifest() {
let target = env::var("TARGET").unwrap_or_default();
if !target.contains("windows-msvc") {
return;
}
let manifest = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap_or_default())
.join("resources/packaging/windows/rustnet.exe.manifest");
println!("cargo:rerun-if-changed={}", manifest.display());
println!("cargo:rustc-link-arg-bins=/MANIFEST:EMBED");
println!(
"cargo:rustc-link-arg-bins=/MANIFESTINPUT:{}",
manifest.display()
);
}
include!("src/cli.rs");
fn setup_cross_compilation_libs() {
let target = env::var("TARGET").unwrap_or_default();
let host = env::var("HOST").unwrap_or_default();
// Only apply hard-coded multiarch lib paths when actually cross-compiling.
// On native builds (e.g. Homebrew on Linux arm64) these paths would shadow
// package-manager-provided libraries and break linkage.
if host == target {
return;
}
match target.as_str() {
"aarch64-unknown-linux-gnu" => {
println!("cargo:rustc-link-search=native=/usr/lib/aarch64-linux-gnu");
println!("cargo:rustc-link-lib=elf");
println!("cargo:rustc-link-lib=z");
}
"armv7-unknown-linux-gnueabihf" => {
println!("cargo:rustc-link-search=native=/usr/lib/arm-linux-gnueabihf");
println!("cargo:rustc-link-lib=elf");
println!("cargo:rustc-link-lib=z");
}
"x86_64-unknown-freebsd" => {
// FreeBSD uses libpcap from base system (in /usr/lib)
// When cross-compiling, the sysroot should provide these
println!("cargo:rustc-link-lib=pcap");
}
_ => {
// For other targets, including native builds, let pkg-config handle it
}
}
}
fn setup_windows_npcap_delay_load() {
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default();
if target_os == "windows" && target_env == "msvc" {
println!("cargo:rustc-link-arg=/DELAYLOAD:Packet.dll");
println!("cargo:rustc-link-arg=/DELAYLOAD:wpcap.dll");
println!("cargo:rustc-link-arg=/DEFAULTLIB:delayimp.lib");
}
}
fn generate_assets() -> Result<()> {
use clap::ValueEnum;
use clap_complete::Shell;
use clap_mangen::Man;
let mut cmd = build_cli();
let asset_dir: PathBuf = env::var_os("RUSTNET_ASSET_DIR")
.or_else(|| env::var_os("OUT_DIR"))
.ok_or_else(|| anyhow::anyhow!("OUT_DIR is unset"))?
.into();
for &shell in Shell::value_variants() {
clap_complete::generate_to(shell, &mut cmd, "rustnet", &asset_dir)?;
}
let mut manpage_out = File::create(asset_dir.join("rustnet.1"))?;
let manpage = Man::new(cmd);
manpage.render(&mut manpage_out)?;
Ok(())
}
#[cfg(target_os = "windows")]
fn download_windows_npcap_sdk() -> Result<()> {
use sha2::{Digest, Sha256};
use std::{
fs,
io::{self, Write},
};
println!("cargo:rerun-if-changed=build.rs");
const NPCAP_SDK: &str = "npcap-sdk-1.15.zip";
const NPCAP_SDK_SHA256: &str =
"52c7b9fb4abee3ad9fe739bb545c3efe77b731c8e127122bdf328eafdae3ed4f";
let npcap_sdk_download_url = format!("https://npcap.com/dist/{NPCAP_SDK}");
// The tcpdump project's byte-identical SDK mirror avoids making Windows
// builds depend on npcap.com's TLS availability. Pin the mirror revision;
// both sources must still pass the same checksum below.
let npcap_sdk_mirror_url = format!(
"https://raw.githubusercontent.com/the-tcpdump-group/tcpdump-htdocs/\
f5712b53248430a38938b045b692a9611344d9c8/depends/{NPCAP_SDK}"
);
let cache_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?).join("target");
let npcap_sdk_cache_path = cache_dir.join(NPCAP_SDK);
let npcap_zip = match fs::read(&npcap_sdk_cache_path) {
Ok(zip_data) => {
eprintln!("Found cached npcap SDK");
verify_npcap_checksum(&zip_data)?;
zip_data
}
Err(_) => {
eprintln!("Downloading npcap SDK");
let mut zip_data = vec![];
if let Err(error) = http_req::request::get(npcap_sdk_download_url, &mut zip_data) {
eprintln!("Npcap SDK download failed ({error}); trying the pinned tcpdump mirror");
// A failed request may have written a partial response.
zip_data.clear();
http_req::request::get(npcap_sdk_mirror_url, &mut zip_data)?;
}
verify_npcap_checksum(&zip_data)?;
fs::create_dir_all(cache_dir)?;
let mut cache = fs::File::create(npcap_sdk_cache_path)?;
cache.write_all(&zip_data)?;
zip_data
}
};
fn verify_npcap_checksum(data: &[u8]) -> Result<()> {
let hash = Sha256::digest(data);
let actual = hash.iter().map(|b| format!("{b:02x}")).collect::<String>();
if actual != NPCAP_SDK_SHA256 {
anyhow::bail!(
"Npcap SDK checksum mismatch!\n Expected: {}\n Actual: {}\n\
The downloaded file may be corrupted or tampered with.",
NPCAP_SDK_SHA256,
actual
);
}
eprintln!("Npcap SDK checksum verified: {actual}");
Ok(())
}
let target = env::var("TARGET").unwrap_or_else(|_| "unknown".to_string());
let (packet_lib_path, wpcap_lib_path) = if target.contains("aarch64") {
("Lib/ARM64/Packet.lib", "Lib/ARM64/wpcap.lib")
} else if target.contains("x86_64") {
("Lib/x64/Packet.lib", "Lib/x64/wpcap.lib")
} else if target.contains("i686") || target.contains("i586") {
("Lib/Packet.lib", "Lib/wpcap.lib")
} else {
panic!("Unsupported target: {}", target)
};
let mut archive = zip::ZipArchive::new(io::Cursor::new(npcap_zip))?;
let mut packet_lib = archive.by_name(packet_lib_path)?;
let lib_dir = PathBuf::from(env::var("OUT_DIR")?).join("npcap_sdk");
fs::create_dir_all(&lib_dir)?;
let packet_lib_dest = lib_dir.join("Packet.lib");
let mut packet_file = fs::File::create(packet_lib_dest)?;
io::copy(&mut packet_lib, &mut packet_file)?;
drop(packet_lib);
let mut wpcap_lib = archive.by_name(wpcap_lib_path)?;
let wpcap_lib_dest = lib_dir.join("wpcap.lib");
let mut wpcap_file = fs::File::create(wpcap_lib_dest)?;
io::copy(&mut wpcap_lib, &mut wpcap_file)?;
println!(
"cargo:rustc-link-search=native={}",
lib_dir
.to_str()
.ok_or_else(|| anyhow::anyhow!("{lib_dir:?} is not valid UTF-8"))?
);
Ok(())
}