1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
19#[repr(u8)]
20pub enum StatusCode {
21 Endpointalreadyinstalled = 2,
23 Rootcertificatenotfound = 3,
25 Clientcertificatenotfound = 4,
27 Endpointinuse = 5,
29 Invalidtime = 6,
31}
32
33impl StatusCode {
34 pub fn from_u8(value: u8) -> Option<Self> {
36 match value {
37 2 => Some(StatusCode::Endpointalreadyinstalled),
38 3 => Some(StatusCode::Rootcertificatenotfound),
39 4 => Some(StatusCode::Clientcertificatenotfound),
40 5 => Some(StatusCode::Endpointinuse),
41 6 => Some(StatusCode::Invalidtime),
42 _ => None,
43 }
44 }
45
46 pub fn to_u8(self) -> u8 {
48 self as u8
49 }
50}
51
52impl From<StatusCode> for u8 {
53 fn from(val: StatusCode) -> Self {
54 val as u8
55 }
56}
57
58#[derive(Debug, serde::Serialize)]
61pub struct TLSEndpoint {
62 pub endpoint_id: Option<u8>,
63 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
64 pub hostname: Option<Vec<u8>>,
65 pub port: Option<u16>,
66 pub caid: Option<u8>,
67 pub ccdid: Option<u8>,
68 pub reference_count: Option<u8>,
69}
70
71pub fn encode_provision_endpoint(hostname: Vec<u8>, port: u16, caid: u8, ccdid: Option<u8>, endpoint_id: Option<u8>) -> anyhow::Result<Vec<u8>> {
75 let tlv = tlv::TlvItemEnc {
76 tag: 0,
77 value: tlv::TlvItemValueEnc::StructInvisible(vec![
78 (0, tlv::TlvItemValueEnc::OctetString(hostname)).into(),
79 (1, tlv::TlvItemValueEnc::UInt16(port)).into(),
80 (2, tlv::TlvItemValueEnc::UInt8(caid)).into(),
81 (3, tlv::TlvItemValueEnc::UInt8(ccdid.unwrap_or(0))).into(),
82 (4, tlv::TlvItemValueEnc::UInt8(endpoint_id.unwrap_or(0))).into(),
83 ]),
84 };
85 Ok(tlv.encode()?)
86}
87
88pub fn encode_find_endpoint(endpoint_id: u8) -> anyhow::Result<Vec<u8>> {
90 let tlv = tlv::TlvItemEnc {
91 tag: 0,
92 value: tlv::TlvItemValueEnc::StructInvisible(vec![
93 (0, tlv::TlvItemValueEnc::UInt8(endpoint_id)).into(),
94 ]),
95 };
96 Ok(tlv.encode()?)
97}
98
99pub fn encode_remove_endpoint(endpoint_id: u8) -> anyhow::Result<Vec<u8>> {
101 let tlv = tlv::TlvItemEnc {
102 tag: 0,
103 value: tlv::TlvItemValueEnc::StructInvisible(vec![
104 (0, tlv::TlvItemValueEnc::UInt8(endpoint_id)).into(),
105 ]),
106 };
107 Ok(tlv.encode()?)
108}
109
110pub fn decode_max_provisioned(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
114 if let tlv::TlvItemValue::Int(v) = inp {
115 Ok(*v as u8)
116 } else {
117 Err(anyhow::anyhow!("Expected UInt8"))
118 }
119}
120
121pub fn decode_provisioned_endpoints(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TLSEndpoint>> {
123 let mut res = Vec::new();
124 if let tlv::TlvItemValue::List(v) = inp {
125 for item in v {
126 res.push(TLSEndpoint {
127 endpoint_id: item.get_int(&[0]).map(|v| v as u8),
128 hostname: item.get_octet_string_owned(&[1]),
129 port: item.get_int(&[2]).map(|v| v as u16),
130 caid: item.get_int(&[3]).map(|v| v as u8),
131 ccdid: item.get_int(&[4]).map(|v| v as u8),
132 reference_count: item.get_int(&[5]).map(|v| v as u8),
133 });
134 }
135 }
136 Ok(res)
137}
138
139
140pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
152 if cluster_id != 0x0802 {
154 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0802, got {}\"}}", cluster_id);
155 }
156
157 match attribute_id {
158 0x0000 => {
159 match decode_max_provisioned(tlv_value) {
160 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
161 Err(e) => format!("{{\"error\": \"{}\"}}", e),
162 }
163 }
164 0x0001 => {
165 match decode_provisioned_endpoints(tlv_value) {
166 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
167 Err(e) => format!("{{\"error\": \"{}\"}}", e),
168 }
169 }
170 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
171 }
172}
173
174pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
179 vec![
180 (0x0000, "MaxProvisioned"),
181 (0x0001, "ProvisionedEndpoints"),
182 ]
183}
184
185pub fn get_command_list() -> Vec<(u32, &'static str)> {
188 vec![
189 (0x00, "ProvisionEndpoint"),
190 (0x02, "FindEndpoint"),
191 (0x04, "RemoveEndpoint"),
192 ]
193}
194
195pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
196 match cmd_id {
197 0x00 => Some("ProvisionEndpoint"),
198 0x02 => Some("FindEndpoint"),
199 0x04 => Some("RemoveEndpoint"),
200 _ => None,
201 }
202}
203
204pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
205 match cmd_id {
206 0x00 => Some(vec![
207 crate::clusters::codec::CommandField { tag: 0, name: "hostname", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
208 crate::clusters::codec::CommandField { tag: 1, name: "port", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
209 crate::clusters::codec::CommandField { tag: 2, name: "caid", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
210 crate::clusters::codec::CommandField { tag: 3, name: "ccdid", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: true },
211 crate::clusters::codec::CommandField { tag: 4, name: "endpoint_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: true },
212 ]),
213 0x02 => Some(vec![
214 crate::clusters::codec::CommandField { tag: 0, name: "endpoint_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
215 ]),
216 0x04 => Some(vec![
217 crate::clusters::codec::CommandField { tag: 0, name: "endpoint_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
218 ]),
219 _ => None,
220 }
221}
222
223pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
224 match cmd_id {
225 0x00 => {
226 let hostname = crate::clusters::codec::json_util::get_octstr(args, "hostname")?;
227 let port = crate::clusters::codec::json_util::get_u16(args, "port")?;
228 let caid = crate::clusters::codec::json_util::get_u8(args, "caid")?;
229 let ccdid = crate::clusters::codec::json_util::get_opt_u8(args, "ccdid")?;
230 let endpoint_id = crate::clusters::codec::json_util::get_opt_u8(args, "endpoint_id")?;
231 encode_provision_endpoint(hostname, port, caid, ccdid, endpoint_id)
232 }
233 0x02 => {
234 let endpoint_id = crate::clusters::codec::json_util::get_u8(args, "endpoint_id")?;
235 encode_find_endpoint(endpoint_id)
236 }
237 0x04 => {
238 let endpoint_id = crate::clusters::codec::json_util::get_u8(args, "endpoint_id")?;
239 encode_remove_endpoint(endpoint_id)
240 }
241 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
242 }
243}
244
245#[derive(Debug, serde::Serialize)]
246pub struct ProvisionEndpointResponse {
247 pub endpoint_id: Option<u8>,
248}
249
250#[derive(Debug, serde::Serialize)]
251pub struct FindEndpointResponse {
252 pub endpoint: Option<TLSEndpoint>,
253}
254
255pub fn decode_provision_endpoint_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ProvisionEndpointResponse> {
259 if let tlv::TlvItemValue::List(_fields) = inp {
260 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
261 Ok(ProvisionEndpointResponse {
262 endpoint_id: item.get_int(&[0]).map(|v| v as u8),
263 })
264 } else {
265 Err(anyhow::anyhow!("Expected struct fields"))
266 }
267}
268
269pub fn decode_find_endpoint_response(inp: &tlv::TlvItemValue) -> anyhow::Result<FindEndpointResponse> {
271 if let tlv::TlvItemValue::List(_fields) = inp {
272 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
273 Ok(FindEndpointResponse {
274 endpoint: {
275 if let Some(nested_tlv) = item.get(&[0]) {
276 if let tlv::TlvItemValue::List(_) = nested_tlv {
277 let nested_item = tlv::TlvItem { tag: 0, value: nested_tlv.clone() };
278 Some(TLSEndpoint {
279 endpoint_id: nested_item.get_int(&[0]).map(|v| v as u8),
280 hostname: nested_item.get_octet_string_owned(&[1]),
281 port: nested_item.get_int(&[2]).map(|v| v as u16),
282 caid: nested_item.get_int(&[3]).map(|v| v as u8),
283 ccdid: nested_item.get_int(&[4]).map(|v| v as u8),
284 reference_count: nested_item.get_int(&[5]).map(|v| v as u8),
285 })
286 } else {
287 None
288 }
289 } else {
290 None
291 }
292 },
293 })
294 } else {
295 Err(anyhow::anyhow!("Expected struct fields"))
296 }
297}
298
299pub async fn provision_endpoint(conn: &crate::controller::Connection, endpoint: u16, hostname: Vec<u8>, port: u16, caid: u8, ccdid: Option<u8>, endpoint_id: Option<u8>) -> anyhow::Result<ProvisionEndpointResponse> {
303 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TLS_CLIENT_MANAGEMENT, crate::clusters::defs::CLUSTER_TLS_CLIENT_MANAGEMENT_CMD_ID_PROVISIONENDPOINT, &encode_provision_endpoint(hostname, port, caid, ccdid, endpoint_id)?).await?;
304 decode_provision_endpoint_response(&tlv)
305}
306
307pub async fn find_endpoint(conn: &crate::controller::Connection, endpoint: u16, endpoint_id: u8) -> anyhow::Result<FindEndpointResponse> {
309 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TLS_CLIENT_MANAGEMENT, crate::clusters::defs::CLUSTER_TLS_CLIENT_MANAGEMENT_CMD_ID_FINDENDPOINT, &encode_find_endpoint(endpoint_id)?).await?;
310 decode_find_endpoint_response(&tlv)
311}
312
313pub async fn remove_endpoint(conn: &crate::controller::Connection, endpoint: u16, endpoint_id: u8) -> anyhow::Result<()> {
315 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_TLS_CLIENT_MANAGEMENT, crate::clusters::defs::CLUSTER_TLS_CLIENT_MANAGEMENT_CMD_ID_REMOVEENDPOINT, &encode_remove_endpoint(endpoint_id)?).await?;
316 Ok(())
317}
318
319pub async fn read_max_provisioned(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
321 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TLS_CLIENT_MANAGEMENT, crate::clusters::defs::CLUSTER_TLS_CLIENT_MANAGEMENT_ATTR_ID_MAXPROVISIONED).await?;
322 decode_max_provisioned(&tlv)
323}
324
325pub async fn read_provisioned_endpoints(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<TLSEndpoint>> {
327 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_TLS_CLIENT_MANAGEMENT, crate::clusters::defs::CLUSTER_TLS_CLIENT_MANAGEMENT_ATTR_ID_PROVISIONEDENDPOINTS).await?;
328 decode_provisioned_endpoints(&tlv)
329}
330