Skip to content

Commit a346cd0

Browse files
committed
improve ForwardOpen handling and tighten SendUnitData checks
1 parent 496bb80 commit a346cd0

1 file changed

Lines changed: 54 additions & 14 deletions

File tree

src/client/connected.rs

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,39 @@ impl ConnectedMessaging for EthernetIpClient {
2323
self.send_rr_data(cip).await?
2424
};
2525

26-
if res.len() < 10 {
26+
// Minimum: service (0), reserved (1), general status (2), ext count (3)
27+
if res.len() < 4 {
2728
return Err(ForwardOpenError::Other(
2829
"ForwardOpen response too short".into(),
2930
));
3031
}
3132

32-
let status = res[2];
33-
if status != 0 {
33+
let general = res[2];
34+
let ext_words = res[3] as usize;
35+
let ext_bytes = 4 + ext_words * 2;
36+
37+
if res.len() < ext_bytes + 4 {
38+
return Err(ForwardOpenError::Other(
39+
"ForwardOpen response missing connection ID".into(),
40+
));
41+
}
42+
43+
if general != 0 {
3444
let ext = decode_extended_status(&res);
3545

3646
if !ext.is_empty() {
3747
return Err(map_extended_status(&ext));
3848
}
39-
40-
return Err(ForwardOpenError::GeneralStatus(status));
49+
return Err(ForwardOpenError::GeneralStatus(general));
4150
}
4251

43-
let conn_id = u32::from_le_bytes([res[6], res[7], res[8], res[9]]);
52+
let conn_id = u32::from_le_bytes([
53+
res[ext_bytes],
54+
res[ext_bytes + 1],
55+
res[ext_bytes + 2],
56+
res[ext_bytes + 3],
57+
]);
58+
4459
self.connection_id = Some(conn_id);
4560
self.sequence = 1;
4661
self.connected = true;
@@ -76,19 +91,34 @@ impl ConnectedMessaging for EthernetIpClient {
7691
self.send_rr_data(cip).await?
7792
};
7893

79-
if res.len() < 10 {
94+
if res.len() < 4 {
8095
return Err(io::Error::other("LargeForwardOpen response too short"));
8196
}
8297

83-
let status = res[2];
84-
if status != 0 {
98+
let general = res[2];
99+
let ext_words = res[3] as usize;
100+
let ext_bytes = 4 + ext_words * 2;
101+
102+
if res.len() < ext_bytes + 4 {
103+
return Err(io::Error::other(
104+
"LargeForwardOpen response missing connection ID",
105+
));
106+
}
107+
108+
if general != 0 {
85109
return Err(io::Error::other(format!(
86110
"LargeForwardOpen failed: 0x{:02X}",
87-
status
111+
general
88112
)));
89113
}
90114

91-
let conn_id = u32::from_le_bytes([res[6], res[7], res[8], res[9]]);
115+
let conn_id = u32::from_le_bytes([
116+
res[ext_bytes],
117+
res[ext_bytes + 1],
118+
res[ext_bytes + 2],
119+
res[ext_bytes + 3],
120+
]);
121+
92122
self.connection_id = Some(conn_id);
93123
self.sequence = 1;
94124
self.connected = true;
@@ -127,7 +157,6 @@ impl ConnectedMessaging for EthernetIpClient {
127157

128158
match res {
129159
Ok(_) => return Ok(()),
130-
131160
Err(e) => {
132161
let msg = e.to_string();
133162

@@ -153,9 +182,14 @@ impl ConnectedMessaging for EthernetIpClient {
153182
.ok_or_else(|| io::Error::other("No active ForwardOpen connection"))?;
154183

155184
let seq = self.sequence;
156-
self.sequence = self.sequence.wrapping_add(1);
185+
// Avoid sequence 0 on wrap
186+
self.sequence = if self.sequence == u16::MAX {
187+
1
188+
} else {
189+
self.sequence.wrapping_add(1)
190+
};
157191

158-
let mut rr = Vec::new();
192+
let mut rr = Vec::with_capacity(4 + 2 + cip.len());
159193
rr.extend_from_slice(&conn_id.to_le_bytes());
160194
rr.extend_from_slice(&seq.to_le_bytes());
161195
rr.extend_from_slice(&cip);
@@ -174,6 +208,12 @@ impl ConnectedMessaging for EthernetIpClient {
174208
let h = EncapsulationHeader::from_bytes(&h_buf)
175209
.ok_or_else(|| io::Error::other("Bad encapsulation header"))?;
176210

211+
if h.command != COMMAND_SEND_UNIT_DATA {
212+
return Err(io::Error::other(
213+
"Unexpected encapsulation command in response",
214+
));
215+
}
216+
177217
let mut d = vec![0u8; h.length as usize];
178218
self.stream.read_exact(&mut d).await?;
179219

0 commit comments

Comments
 (0)