Skip to content

Commit 8d1a5d6

Browse files
authored
Merge pull request #5 from wiseaidev/rnd-src-ip-patch
fix source ip randomization
2 parents ea34fc9 + 434f5f9 commit 8d1a5d6

File tree

3 files changed

+240
-165
lines changed

3 files changed

+240
-165
lines changed

src/ip.rs

Lines changed: 99 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,95 @@
1+
use crate::tcp::TcpHeader;
2+
use crate::utils::calculate_ip_checksum;
3+
use std::net::Ipv4Addr;
4+
use std::str::FromStr;
5+
16
/// Represents the IP header structure with its fields.
7+
///
8+
/// This struct follows the IP header format.
9+
/// Reference: https://github.com/wiseaidev/dark-web-rust/tree/main/chapter-1#13-the-ip-header-struct
10+
#[repr(C, packed)]
211
#[derive(Debug)]
312
pub struct IpHeader {
413
/// Version and Internet Header Length (IHL) combined field.
514
pub version_ihl: u8,
15+
/// Type of Service (TOS) field.
16+
pub tos: u8,
17+
/// Length field.
18+
pub len: u16,
19+
/// Identification field.
20+
pub id: u16,
21+
/// Fragment Offset field.
22+
pub offset: u16,
623
/// Time To Live (TTL) field.
724
pub ttl: u8,
8-
/// Source IP address field.
9-
pub source_ip: u32,
10-
/// Destination IP address field.
11-
pub dest_ip: u32,
1225
/// Protocol field.
1326
pub protocol: u8,
14-
/// Length field.
15-
pub len: u16,
27+
/// Header Checksum field.
28+
pub sum: u16,
29+
/// Source IP address field.
30+
pub src: u32,
31+
/// Destination IP address field.
32+
pub dst: u32,
1633
}
1734
/// Implements methods for the IpHeader struct.
1835
impl IpHeader {
36+
/// Constructs an IP header with the given source IP and computes the length and checksum.
37+
///
38+
/// # Arguments
39+
///
40+
/// * `source_ip` - The source IP address.
41+
/// * `dest_ip` - The target ip.
42+
///
43+
/// # Returns
44+
///
45+
/// (`IpHeader`): The IP header with calculated length and checksum.
46+
///
47+
/// # Examples
48+
///
49+
/// ```
50+
/// use rping::utils::{generate_random_ip, calculate_tcp_checksum};
51+
/// use rping::ip::IpHeader;
52+
///
53+
/// let source_ip = generate_random_ip();
54+
/// let ip_header = IpHeader::new(source_ip, "192.168.1.10");
55+
/// // Ensure that relevant fields have been initialized properly.
56+
/// assert_eq!(ip_header.version_ihl, (4 << 4) | 5);
57+
/// assert_eq!(ip_header.protocol, 6);
58+
/// ```
59+
pub fn new(source_ip: u32, dest_ip: &str) -> Self {
60+
let mut ip_header = Self {
61+
version_ihl: 69,
62+
tos: 0,
63+
len: 0,
64+
id: 0,
65+
offset: 0,
66+
ttl: 50,
67+
protocol: 6,
68+
sum: 0,
69+
src: source_ip.to_be(),
70+
dst: Ipv4Addr::from_str(dest_ip).unwrap().into(),
71+
};
72+
73+
// Convert destination IP to big-endian
74+
ip_header.dst = ip_header.dst.to_be();
75+
76+
// Calculate the total length (IP header + TCP header)
77+
let total_length =
78+
(std::mem::size_of::<IpHeader>() + std::mem::size_of::<TcpHeader>()) as u16;
79+
80+
// Set the total length in the IP header
81+
ip_header.len = total_length.to_be();
82+
83+
// Calculate the checksum without TCP header
84+
ip_header.sum = calculate_ip_checksum(&ip_header);
85+
86+
// Convert length and checksum to network byte order (big-endian)
87+
ip_header.len = ip_header.len.to_be();
88+
ip_header.sum = ip_header.sum.to_be();
89+
90+
ip_header
91+
}
92+
1993
/// Returns a byte slice representing the binary data of the IpHeader.
2094
///
2195
/// # Examples
@@ -24,14 +98,21 @@ impl IpHeader {
2498
///
2599
/// let ip_header = IpHeader {
26100
/// version_ihl: 0x45,
101+
/// tos: 0,
102+
/// len: 20,
103+
/// id: 0,
104+
/// offset: 0,
27105
/// ttl: 64,
28-
/// source_ip: 0xC0A80001, // 192.168.0.1
29-
/// dest_ip: 0xC0A80002, // 192.168.0.2
30106
/// protocol: 6,
31-
/// len: 0,
107+
/// sum: 127,
108+
/// src: 0xC0A80001, // 192.168.0.1
109+
/// dst: 0xC0A80002, // 192.168.0.2
32110
/// };
33111
///
34-
/// assert_eq!(ip_header.as_bytes(), &[1, 0, 168, 192, 2, 0, 168, 192, 0, 0, 69, 64, 6, 127, 0, 0]);
112+
/// assert_eq!(
113+
/// ip_header.as_bytes(),
114+
/// &[69, 0, 20, 0, 0, 0, 0, 0, 64, 6, 127, 0, 1, 0, 168, 192, 2, 0, 168, 192]
115+
/// );
35116
/// ```
36117
/// Returns a byte slice representing the binary data of the IpHeader.
37118
pub fn as_bytes(&self) -> &[u8] {
@@ -52,16 +133,20 @@ mod tests {
52133
fn test_ip_header_as_bytes() {
53134
let ip_header = IpHeader {
54135
version_ihl: 0x45,
136+
tos: 0,
137+
len: 20,
138+
id: 0,
139+
offset: 0,
55140
ttl: 64,
56-
source_ip: 0xC0A80001, // 192.168.0.1
57-
dest_ip: 0xC0A80002, // 192.168.0.2
58141
protocol: 6,
59-
len: 0,
142+
sum: 0,
143+
src: 0xC0A80001, // 192.168.0.1
144+
dst: 0xC0A80002, // 192.168.0.2
60145
};
61146

62147
assert_eq!(
63148
ip_header.as_bytes(),
64-
&[1, 0, 168, 192, 2, 0, 168, 192, 0, 0, 69, 64, 6, 0, 0, 0]
149+
&[69, 0, 20, 0, 0, 0, 0, 0, 64, 6, 0, 0, 1, 0, 168, 192, 2, 0, 168, 192]
65150
);
66151
}
67152
}

src/tcp.rs

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,63 @@
1+
use rand::Rng;
2+
use std::time::SystemTime;
3+
14
/// Represents the TCP header structure with its fields.
5+
///
6+
/// This struct follows the TCP header format.
7+
/// Reference: https://github.com/wiseaidev/dark-web-rust/tree/main/chapter-1#16-decoding-tcp-packets
8+
#[repr(C, packed)]
29
#[derive(Clone)]
310
pub struct TcpHeader {
411
/// Source Port field.
512
pub sport: u16,
613
/// Destination Port field.
714
pub dport: u16,
815
/// Sequence Number field.
9-
pub seq: u32,
16+
pub seq: u16,
1017
/// Acknowledgment Number field.
11-
pub ack: u32,
12-
/// Data Offset (offset of the data in the TCP header) and Reserved (always zero) combined field.
13-
pub offx2: u8,
14-
/// Flags field.
15-
pub flags: u8,
18+
pub ack: u16,
19+
/// Data Offset (offset of the data in the TCP header), Reserved (always zero), and flags combined field.
20+
pub off_reserved_flags: u16,
1621
/// Window Size field.
1722
pub win: u16,
1823
/// Checksum field.
1924
pub sum: u16,
2025
/// Urgent Pointer field.
2126
pub urp: u16,
27+
/// Options field.
28+
pub opt: u16,
29+
/// Padding field.
30+
pub pad: u16,
2231
}
2332

2433
/// Implements methods for the TcpHeader struct.
2534
impl TcpHeader {
35+
/// Creates a new TCP header with default values.
36+
pub fn new(dest_port: u16) -> Self {
37+
let mut rng = rand::thread_rng();
38+
let data_offset = 20; // 5 words (20 bytes)
39+
let reserved = 0;
40+
let flags = 2; // 2 is the flag value for SYN
41+
let off_reserved_flags = (data_offset << 15) | (reserved << 12) | (flags << 7);
42+
43+
TcpHeader {
44+
sport: (SystemTime::now()
45+
.duration_since(SystemTime::UNIX_EPOCH)
46+
.unwrap()
47+
.as_millis()
48+
% u16::MAX as u128) as u16,
49+
dport: dest_port.to_be(),
50+
seq: rng.gen::<u16>(),
51+
ack: rng.gen::<u16>(),
52+
off_reserved_flags,
53+
win: rng.gen::<u16>(),
54+
sum: rng.gen::<u16>(),
55+
urp: rng.gen::<u16>(),
56+
opt: rng.gen::<u16>(),
57+
pad: rng.gen::<u16>(),
58+
}
59+
}
60+
2661
/// Returns a byte slice representing the binary data of the TcpHeader.
2762
///
2863
/// # Examples
@@ -34,14 +69,15 @@ impl TcpHeader {
3469
/// dport: 80,
3570
/// seq: 12345,
3671
/// ack: 0,
37-
/// offx2: 0x50,
38-
/// flags: 0x02,
72+
/// off_reserved_flags: 0x5000,
3973
/// win: 1024,
4074
/// sum: 0,
4175
/// urp: 0,
76+
/// opt: 0,
77+
/// pad: 0,
4278
/// };
4379
///
44-
/// assert_eq!(tcp_header.as_bytes(), &[57, 48, 0, 0, 0, 0, 0, 0, 144, 31, 80, 0, 0, 4, 0, 0, 0, 0, 80, 2]);
80+
/// assert_eq!(tcp_header.as_bytes(), &[144, 31, 80, 0, 57, 48, 0, 0, 0, 80, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0]);
4581
/// ```
4682
/// Returns a byte slice representing the binary data of the TcpHeader.
4783
pub fn as_bytes(&self) -> &[u8] {
@@ -65,16 +101,17 @@ mod tests {
65101
dport: 80,
66102
seq: 12345,
67103
ack: 0,
68-
offx2: 0x50,
69-
flags: 0x02,
104+
off_reserved_flags: 0x0202,
70105
win: 1024,
71106
sum: 0,
72107
urp: 0,
108+
opt: 0,
109+
pad: 0,
73110
};
74111

75112
assert_eq!(
76113
tcp_header.as_bytes(),
77-
&[57, 48, 0, 0, 0, 0, 0, 0, 144, 31, 80, 0, 0, 4, 0, 0, 0, 0, 80, 2]
114+
&[144, 31, 80, 0, 57, 48, 0, 0, 2, 2, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0]
78115
);
79116
}
80117
}

0 commit comments

Comments
 (0)