Jor*_*man 8 sockets ip icmp rust
我很难理解为什么我的程序中在网络上发送 ICMP 回显请求的部分会出现此错误。最重要的是,我可以通过让套接字处理 IP 标头来使其工作,但是当我设置 IP_HDRINCL 选项并为其提供有效的 IP 标头时,它会返回 EINVAL 错误:
initialize
using interface en0 with ip 192.168.1.126 and mac a4:83:e7:43:40:81.
Input start ip/scan range: 192.168.1.1
[45, 0, 0, 1c, 20, 1, 40, 0, 40, 1, 97, 10, c0, a8, 1, 7e, c0, a8, 1, 1, 8, 0, 22, 2a, 97, 3e, 3e, 97]
[69, 0, 0, 28, 32, 1, 64, 0, 64, 1, 151, 16, 192, 168, 1, 126, 192, 168, 1, 1, 8, 0, 34, 42, 151, 62, 62, 151]
Err(Os { code: 22, kind: InvalidInput, message: "Invalid argument" })
Run Code Online (Sandbox Code Playgroud)
为了帮助直观地了解正在发生的情况,我在发送数据时将发送的数据转储放在那里。第一个转储是十六进制格式的数据,第二个转储是相同的,但是十进制格式。
这是打开套接字并创建/发送/接收数据包的代码:
(对可能大量的代码表示歉意,尽管我应该确保提供足够的信息:))
let response = net_tools::interface::Interface::detect();
if response.is_err() {
panic! {response};
}
let active_interface = response.unwrap();
println!(
"using interface {} with ip {} and mac {}.",
active_interface.get_name(),
active_interface.get_ip_as_struct(),
active_interface.get_mac()
);
let scan_targets = net_tools::TaregtAddresses::retrieve_from_user();
let sock = net_tools::socket_tools::Socket::new_v4().expect("Failed to open socket");
let mut i = 0;
for ipv4addr in scan_targets {
if i == 1 {
break;
}
let mut d = packet_crafter::Packet::new(
active_interface.clone(),
vec![Protocols::IP, Protocols::ICMP],
// vec![Protocols::ICMP],
);
d.ip_header.set_next_protocol(Protocols::ICMP);
d.ip_header.set_dst_ip(Some(ipv4addr.octets()));
// d.ip_header.set_tos(0b11000100);
d.finalize_headers(); // calculates checksums etc
let data = d.build();
println!("{:x?}", data);
println!("{:?}", data);
let response = sock.sendto(data.as_slice(), SocketAddrV4::new(ipv4addr, 21));
println!("{:?}", response);
i += 1;
}
println!("All data sent, receiving response:");
let mut a = [0u8; 56];
let received_data = sock.recv(&mut a);
println!("{:?}", received_data);
let v = a.to_vec();
println!("{:?}", v);
Run Code Online (Sandbox Code Playgroud)
这就是我打开套接字并在其上发送数据的方式,请注意,我在打开它时调用了setsockopt并将IP_HDRINCL设置为1:
pub fn new_v4() -> crate::io::Result<Self> {
unsafe {
let fam = libc::AF_INET; // Set domain family to ipv4
if cfg!(target_os = "linux") {
match cvt(libc::socket(fam, libc::SOCK_RAW | SOCK_CLOEXEC, 1)) {
Ok(fd) => return Ok(Self(Fd::new(fd))),
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {}
Err(e) => return Err(e),
}
}
let _fd = cvt(libc::socket(fam, libc::SOCK_RAW, 1))?; // 1 = setting next protocol as ICMP
let _fd = Fd::new(_fd);
_fd.set_cloexec()?;
let socket = Self(_fd);
if cfg!(target_vendor = "apple") {
let payload = &1u32 as *const u32 as *const libc::c_void;
cvt(libc::setsockopt(
*socket.as_inner(),
libc::SOL_SOCKET,
SO_NOSIGPIPE,
payload,
size_of::<libc::c_int>() as libc::socklen_t,
))?;
}
let payload = &1u32 as *const u32 as *const libc::c_void;
cvt(libc::setsockopt(
*socket.as_inner(),
libc::IPPROTO_IP,
libc::IP_HDRINCL,
payload,
size_of::<libc::c_int>() as libc::socklen_t,
))?;
Ok(socket)
}
}
pub fn sendto(&self, buf: &[u8], addr: SocketAddrV4) -> crate::io::Result<usize> {
unsafe {
let (sa, l) = sock_addr_into_raw(addr);
let n = cvt({
libc::sendto(
*self.as_inner(),
buf.as_ptr() as *const libc::c_void,
cmp::min(buf.len(), super::max_len()),
MSG_NOSIGNAL,
&sa as *const _,
l,
)
})?;
Ok(n as usize)
}
}
Run Code Online (Sandbox Code Playgroud)
这是转换socketAddr类型的函数:
pub fn sock_addr_into_raw(s: SocketAddrV4) -> (libc::sockaddr, libc::socklen_t) {
unsafe {
let mut storage = mem::MaybeUninit::<libc::sockaddr>::uninit();
let len = mem::size_of::<SocketAddrV4>();
copy_nonoverlapping(
&s as *const _ as *const libc::sockaddr as *const _ as *const u8,
&mut storage as *mut _ as *mut u8,
len,
);
(storage.assume_init(), len as _)
}
}
Run Code Online (Sandbox Code Playgroud)
回到我的观点,正如您从第一段代码中看到的那样,我转储的数据包数据中的 IP 标头是一个有效的 IP 标头,后面跟着一个有效的 ICMP 标头,据我所知,我有一直在改变周围的事情以确保它是有效的,所以如果不是的话请纠正我。但无论如何,我知道传入的所有其他参数sendto都是有效的,因为当我将 IP_HDRINCL 套接字选项设置为 0 并仅发送 ICMP 数据时,它就可以工作了...
initialize
using interface en0 with ip 192.168.1.126 and mac a4:83:e7:43:40:81.
Input start ip/scan range: 192.168.1.1
[8, 0, 8a, 92, 3f, 2e, 2e, 3f]
[8, 0, 138, 146, 63, 46, 46, 63]
Ok(8)
All data sent, receiving response:
Ok(28)
[69, 0, 8, 0, 224, 108, 0, 0, 254, 1, 88, 164, 192, 168, 1, 1, 192, 168, 1, 126, 0, 0, 146, 146, 63, 46, 46, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Run Code Online (Sandbox Code Playgroud)
我什至尝试过诸如当 IP_HDRINCL 为 1 时在数据包中发送以太网 II 标头之类的操作,但这仍然给出相同的 OSError。有谁知道为什么我自己发送IP头会破坏它?
(顺便说一句,我在 Mac OS Mojave 10.14.6 上运行它)