1use crate::server::ics_support::number;
3use anyhow::{bail, ensure, Context, Result};
4use serde_json::{json, Value};
5pub const MAX_FRAME: usize = 480;
6pub const DEFAULT_DEVICE_ID: u32 = 1234;
7pub fn wrap(a: &[u8]) -> Vec<u8> {
8 let mut b = vec![0x81, 0x0a, 0, 0, 1, 0];
9 b.extend(a);
10 let n = b.len() as u16;
11 b[2..4].copy_from_slice(&n.to_be_bytes());
12 b
13}
14pub fn unwrap(b: &[u8]) -> Result<&[u8]> {
15 ensure!(
16 b.len() >= 7
17 && b.len() <= MAX_FRAME
18 && b[0] == 0x81
19 && matches!(b[1], 10 | 11)
20 && u16::from_be_bytes([b[2], b[3]]) as usize == b.len(),
21 "BVLC framing"
22 );
23 ensure!(
24 b[4] == 1 && matches!(b[5], 0 | 4),
25 "only local BACnet/IP NPDU supported"
26 );
27 Ok(&b[6..])
28}
29fn uint(n: u64) -> Vec<u8> {
30 let b = n.to_be_bytes();
31 b[b.iter().position(|v| *v != 0).unwrap_or(7)..].to_vec()
32}
33fn tag(n: u8, context: bool, data: &[u8]) -> Result<Vec<u8>> {
34 ensure!(n < 15 && data.len() <= 255, "tag bound");
35 let mut b = vec![
36 (n << 4) | if context { 8 } else { 0 } | if data.len() <= 4 { data.len() as u8 } else { 5 },
37 ];
38 if data.len() > 4 {
39 b.push(data.len() as u8);
40 }
41 b.extend(data);
42 Ok(b)
43}
44fn context(n: u8, value: u64) -> Result<Vec<u8>> {
45 tag(n, true, &uint(value))
46}
47struct Reader<'a> {
48 b: &'a [u8],
49 p: usize,
50}
51impl<'a> Reader<'a> {
52 fn value(&mut self, n: u8, ctx: bool) -> Result<&'a [u8]> {
53 ensure!(self.p < self.b.len(), "missing tag");
54 let t = self.b[self.p];
55 self.p += 1;
56 ensure!(
57 t >> 4 == n && (t & 8 != 0) == ctx && t & 7 <= 5,
58 "unexpected tag"
59 );
60 let len = if t & 7 == 5 {
61 ensure!(self.p < self.b.len(), "missing extended length");
62 let l = self.b[self.p] as usize;
63 self.p += 1;
64 l
65 } else {
66 (t & 7) as usize
67 };
68 ensure!(self.p + len <= self.b.len(), "truncated tag");
69 let b = &self.b[self.p..self.p + len];
70 self.p += len;
71 Ok(b)
72 }
73 fn num(&mut self, n: u8, ctx: bool) -> Result<u64> {
74 let b = self.value(n, ctx)?;
75 ensure!(!b.is_empty() && b.len() <= 4, "integer width");
76 Ok(b.iter().fold(0, |v, b| (v << 8) | *b as u64))
77 }
78 fn marker(&mut self, b: u8) -> Result<()> {
79 ensure!(self.b.get(self.p) == Some(&b), "opening/closing tag");
80 self.p += 1;
81 Ok(())
82 }
83}
84pub fn encode_value(v: &Value) -> Result<Vec<u8>> {
85 match v["value_type"].as_str() {
86 Some("null") => Ok(vec![0]),
87 Some("boolean") => Ok(vec![
88 0x10 | u8::from(v["value"].as_bool().context("boolean")?),
89 ]),
90 Some("unsigned") => tag(2, false, &uint(number(v, "value", u32::MAX as u64)?)),
91 Some("enumerated") => tag(9, false, &uint(number(v, "value", u32::MAX as u64)?)),
92 Some("signed") => {
93 let n = v["value"].as_i64().context("signed integer")?;
94 ensure!(i32::try_from(n).is_ok(), "signed range");
95 tag(3, false, &(n as i32).to_be_bytes())
96 }
97 Some("real") => {
98 let n = v["value"].as_f64().context("real")?;
99 ensure!(n.is_finite() && (n as f32).is_finite(), "real range");
100 tag(4, false, &(n as f32).to_be_bytes())
101 }
102 Some("string") => {
103 let s = v["value"].as_str().context("string")?;
104 ensure!(s.len() <= 200, "string bound");
105 let mut b = vec![0];
106 b.extend(s.as_bytes());
107 tag(7, false, &b)
108 }
109 Some("object_identifier") => {
110 let t = number(&v["value"], "object_type", 1023)?;
111 let i = number(&v["value"], "instance", 4194303)?;
112 tag(12, false, &(((t << 22) | i) as u32).to_be_bytes())
113 }
114 _ => bail!("unsupported application value"),
115 }
116}
117fn decode_value(r: &mut Reader) -> Result<Value> {
118 let t = *r.b.get(r.p).context("missing application tag")?;
119 ensure!(t & 8 == 0, "expected application value");
120 if t >> 4 == 1 {
121 ensure!(t & 7 <= 1, "boolean tag");
122 r.p += 1;
123 return Ok(json!({"value_type":"boolean","value":t&1!=0}));
124 }
125 let b = r.value(t >> 4, false)?;
126 let n = || -> Result<u32> {
127 ensure!(!b.is_empty() && b.len() <= 4, "integer width");
128 Ok(b.iter().fold(0, |v, b| (v << 8) | *b as u32))
129 };
130 let (kind, value) = match t >> 4 {
131 0 => {
132 ensure!(b.is_empty(), "null length");
133 ("null", Value::Null)
134 }
135 2 => ("unsigned", json!(n()?)),
136 9 => ("enumerated", json!(n()?)),
137 3 => {
138 let mut out = if b.first().is_some_and(|n| n & 128 != 0) {
139 [255; 4]
140 } else {
141 [0; 4]
142 };
143 ensure!(!b.is_empty() && b.len() <= 4, "signed width");
144 out[4 - b.len()..].copy_from_slice(b);
145 ("signed", json!(i32::from_be_bytes(out)))
146 }
147 4 => {
148 ensure!(b.len() == 4, "real width");
149 let n = f32::from_be_bytes(b.try_into()?);
150 ensure!(n.is_finite(), "nonfinite real");
151 ("real", json!(n))
152 }
153 7 => {
154 ensure!(b.first() == Some(&0), "UTF8 character set required");
155 ("string", json!(std::str::from_utf8(&b[1..])?))
156 }
157 12 => {
158 ensure!(b.len() == 4, "object id width");
159 let v = n()?;
160 (
161 "object_identifier",
162 json!({"object_type":v>>22,"instance":v&4194303}),
163 )
164 }
165 _ => bail!("unsupported application tag"),
166 };
167 Ok(json!({"value_type":kind,"value":value}))
168}
169fn fields(r: &mut Reader) -> Result<Value> {
170 let o = r.num(0, true)? as u32;
171 let property = r.num(1, true)?;
172 let mut v = json!({"object_type":o>>22,"instance":o&4194303,"property":property});
173 if r.b.get(r.p).is_some_and(|v| v >> 4 == 2) {
174 v["array_index"] = json!(r.num(2, true)?);
175 }
176 Ok(v)
177}
178fn encode_fields(v: &Value) -> Result<Vec<u8>> {
179 let t = number(v, "object_type", 1023)?;
180 let i = number(v, "instance", 4194303)?;
181 let mut b = tag(0, true, &(((t << 22) | i) as u32).to_be_bytes())?;
182 b.extend(context(1, number(v, "property", 4194303)?)?);
183 if v.get("array_index").is_some() {
184 b.extend(context(2, number(v, "array_index", u32::MAX as u64)?)?);
185 }
186 Ok(b)
187}
188pub fn receive(a: &[u8], device: u32) -> Result<(Vec<u8>, Option<Value>)> {
189 ensure!(!a.is_empty(), "APDU empty");
190 if a[0] == 0x10 {
191 ensure!(a.len() >= 2, "unconfirmed service");
192 if a[1] != 8 {
193 return Ok((vec![], None));
194 }
195 let mut r = Reader { b: &a[2..], p: 0 };
196 if !r.b.is_empty() {
197 let low = r.num(0, true)?;
198 let high = r.num(1, true)?;
199 ensure!(r.p == r.b.len() && low <= high, "WhoIs range");
200 if !(low..=high).contains(&(device as u64)) {
201 return Ok((vec![], None));
202 }
203 }
204 let mut b = vec![0x10, 0];
205 b.extend(tag(12, false, &((8u32 << 22) | device).to_be_bytes())?);
206 b.extend(tag(2, false, &uint(MAX_FRAME as u64))?);
207 b.extend(tag(9, false, &[3])?);
208 b.extend(tag(2, false, &[0])?);
209 return Ok((wrap(&b), None));
210 }
211 ensure!(a[0] >> 4 == 0 && a.len() >= 4, "confirmed request framing");
212 let invoke = a[2];
213 if a[0] & 8 != 0 {
214 return Ok((wrap(&[0x71, invoke, 4]), None));
215 }
216 let service = a[3];
217 if !matches!(service, 12 | 15) {
218 return Ok((wrap(&[0x60, invoke, 9]), None));
219 }
220 let mut r = Reader { b: &a[4..], p: 0 };
221 let mut v = fields(&mut r)?;
222 v["operation"] = json!(if service == 12 { "read" } else { "write" });
223 v["invoke_id"] = json!(invoke);
224 v["service"] = json!(service);
225 if service == 15 {
226 r.marker(0x3e)?;
227 let value = decode_value(&mut r)?;
228 r.marker(0x3f)?;
229 v["value_type"] = value["value_type"].clone();
230 v["value"] = value["value"].clone();
231 if r.p < r.b.len() {
232 v["priority"] = json!(r.num(4, true)?);
233 ensure!(
234 v["priority"]
235 .as_u64()
236 .is_some_and(|n| (1..=16).contains(&n)),
237 "priority range"
238 );
239 }
240 }
241 ensure!(r.p == r.b.len(), "trailing request data");
242 Ok((vec![], Some(v)))
243}
244pub fn answer(r: &Value, a: Option<&Value>) -> Result<Vec<u8>> {
245 let invoke = number(r, "invoke_id", 255)? as u8;
246 let service = number(r, "service", 255)? as u8;
247 let Some(a) = a.filter(|a| a["type"] == "bacnet_reply") else {
248 return error(invoke, service, 2, 40);
249 };
250 if a.get("error_code").is_some() {
251 return error(
252 invoke,
253 service,
254 number(a, "error_class", 7)?,
255 number(a, "error_code", 65535)?,
256 );
257 }
258 if service == 15 {
259 if a["accepted"] == true {
260 return Ok(wrap(&[0x20, invoke, service]));
261 }
262 return error(invoke, service, 2, 40);
263 }
264 let mut b = vec![0x30, invoke, service];
265 b.extend(encode_fields(r)?);
266 b.push(0x3e);
267 b.extend(encode_value(a)?);
268 b.push(0x3f);
269 ensure!(b.len() + 6 <= MAX_FRAME, "response bound");
270 Ok(wrap(&b))
271}
272fn error(invoke: u8, service: u8, class: u64, code: u64) -> Result<Vec<u8>> {
273 let mut b = vec![0x50, invoke, service];
274 b.extend(tag(9, false, &uint(class))?);
275 b.extend(tag(9, false, &uint(code))?);
276 Ok(wrap(&b))
277}
278pub fn validate(v: &Value) -> Result<()> {
279 match v["type"].as_str() {
280 Some("bacnet_discover") => {}
281 Some("bacnet_read") => {
282 encode_fields(v)?;
283 }
284 Some("bacnet_write") => {
285 encode_fields(v)?;
286 encode_value(v)?;
287 if v.get("priority").is_some() {
288 ensure!(
289 (1..=16).contains(&number(v, "priority", 16)?),
290 "priority range"
291 );
292 }
293 }
294 _ => bail!("unknown BACnet action"),
295 };
296 Ok(())
297}
298pub fn request(v: &Value, invoke: u8) -> Result<Vec<u8>> {
299 validate(v)?;
300 if v["type"] == "bacnet_discover" {
301 return Ok(wrap(&[0x10, 8]));
302 }
303 let service = if v["type"] == "bacnet_read" { 12 } else { 15 };
304 let mut b = vec![0, 5, invoke, service];
305 b.extend(encode_fields(v)?);
306 if service == 15 {
307 b.push(0x3e);
308 b.extend(encode_value(v)?);
309 b.push(0x3f);
310 if v.get("priority").is_some() {
311 b.extend(context(4, number(v, "priority", 16)?)?);
312 }
313 }
314 Ok(wrap(&b))
315}
316pub fn response(a: &[u8], v: &Value, invoke: u8) -> Result<Value> {
317 if v["type"] == "bacnet_discover" {
318 ensure!(a.len() >= 2 && a[..2] == [0x10, 0], "expected IAm");
319 let mut r = Reader { b: &a[2..], p: 0 };
320 let o = r.num(12, false)?;
321 ensure!(o >> 22 == 8, "IAm device id");
322 let max = r.num(2, false)?;
323 let seg = r.num(9, false)?;
324 let vendor = r.num(2, false)?;
325 ensure!(r.p == r.b.len(), "IAm trailing data");
326 return Ok(
327 json!({"success":true,"device_id":o&4194303,"max_apdu":max,"segmentation":seg,"vendor_id":vendor}),
328 );
329 }
330 ensure!(a.len() >= 3 && a[1] == invoke, "invoke correlation");
331 let service = if v["type"] == "bacnet_read" { 12 } else { 15 };
332 match a[0] >> 4 {
333 2 => {
334 ensure!(
335 a.len() == 3 && a[2] == service && service == 15,
336 "simple ack mismatch"
337 );
338 Ok(json!({"success":true}))
339 }
340 3 => {
341 ensure!(
342 a[0] & 8 == 0 && a[2] == 12 && service == 12,
343 "complex ack mismatch"
344 );
345 let mut r = Reader { b: &a[3..], p: 0 };
346 let f = fields(&mut r)?;
347 for k in ["object_type", "instance", "property", "array_index"] {
348 ensure!(f[k] == v[k], "property correlation");
349 }
350 r.marker(0x3e)?;
351 let mut out = decode_value(&mut r)?;
352 r.marker(0x3f)?;
353 ensure!(r.p == r.b.len(), "ack trailing data");
354 out["success"] = json!(true);
355 Ok(out)
356 }
357 5 => {
358 ensure!(a[2] == service, "error service correlation");
359 let mut r = Reader { b: &a[3..], p: 0 };
360 let class = r.num(9, false)?;
361 let code = r.num(9, false)?;
362 ensure!(r.p == r.b.len(), "error trailing data");
363 Ok(json!({"success":false,"error_class":class,"error_code":code}))
364 }
365 6 | 7 => Ok(json!({"success":false,"reason":a[2]})),
366 _ => bail!("unexpected APDU"),
367 }
368}