opcua_types/
node_id.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2022 Adam Lock
4
5//! Contains the implementation of `NodeId` and `ExpandedNodeId`.
6
7use std::{
8    self,
9    convert::TryFrom,
10    fmt,
11    io::{Read, Write},
12    str::FromStr,
13    sync::atomic::{AtomicUsize, Ordering},
14    u16, u32,
15};
16
17use crate::{
18    byte_string::ByteString,
19    encoding::*,
20    guid::Guid,
21    node_ids::{ObjectId, ReferenceTypeId},
22    status_codes::StatusCode,
23    string::*,
24};
25
26/// The kind of identifier, numeric, string, guid or byte
27#[derive(Eq, PartialEq, Clone, Debug, Hash, Serialize, Deserialize)]
28pub enum Identifier {
29    Numeric(u32),
30    String(UAString),
31    Guid(Guid),
32    ByteString(ByteString),
33}
34
35impl fmt::Display for Identifier {
36    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37        match self {
38            Identifier::Numeric(v) => write!(f, "i={}", *v),
39            Identifier::String(v) => write!(f, "s={}", v),
40            Identifier::Guid(v) => write!(f, "g={:?}", v),
41            Identifier::ByteString(v) => write!(f, "b={}", v.as_base64()),
42        }
43    }
44}
45
46impl FromStr for Identifier {
47    type Err = ();
48
49    fn from_str(s: &str) -> Result<Self, Self::Err> {
50        if s.len() < 2 {
51            Err(())
52        } else {
53            let k = &s[..2];
54            let v = &s[2..];
55            match k {
56                "i=" => v.parse::<u32>().map(|v| v.into()).map_err(|_| ()),
57                "s=" => Ok(UAString::from(v).into()),
58                "g=" => Guid::from_str(v).map(|v| v.into()).map_err(|_| ()),
59                "b=" => ByteString::from_base64(v).map(|v| v.into()).ok_or(()),
60                _ => Err(()),
61            }
62        }
63    }
64}
65
66impl From<i32> for Identifier {
67    fn from(v: i32) -> Self {
68        Identifier::Numeric(v as u32)
69    }
70}
71
72impl From<u32> for Identifier {
73    fn from(v: u32) -> Self {
74        Identifier::Numeric(v as u32)
75    }
76}
77
78impl<'a> From<&'a str> for Identifier {
79    fn from(v: &'a str) -> Self {
80        Identifier::from(UAString::from(v))
81    }
82}
83
84impl From<&String> for Identifier {
85    fn from(v: &String) -> Self {
86        Identifier::from(UAString::from(v))
87    }
88}
89
90impl From<String> for Identifier {
91    fn from(v: String) -> Self {
92        Identifier::from(UAString::from(v))
93    }
94}
95
96impl From<UAString> for Identifier {
97    fn from(v: UAString) -> Self {
98        Identifier::String(v)
99    }
100}
101
102impl From<Guid> for Identifier {
103    fn from(v: Guid) -> Self {
104        Identifier::Guid(v)
105    }
106}
107
108impl From<ByteString> for Identifier {
109    fn from(v: ByteString) -> Self {
110        Identifier::ByteString(v)
111    }
112}
113
114#[derive(Debug)]
115pub struct NodeIdError;
116
117impl fmt::Display for NodeIdError {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        write!(f, "NodeIdError")
120    }
121}
122
123impl std::error::Error for NodeIdError {}
124
125/// An identifier for a node in the address space of an OPC UA Server.
126#[derive(PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)]
127pub struct NodeId {
128    /// The index for a namespace
129    pub namespace: u16,
130    /// The identifier for the node in the address space
131    pub identifier: Identifier,
132}
133
134impl fmt::Display for NodeId {
135    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136        if self.namespace != 0 {
137            write!(f, "ns={};{}", self.namespace, self.identifier)
138        } else {
139            write!(f, "{}", self.identifier)
140        }
141    }
142}
143
144impl BinaryEncoder<NodeId> for NodeId {
145    fn byte_len(&self) -> usize {
146        // Type determines the byte code
147        let size: usize = match self.identifier {
148            Identifier::Numeric(value) => {
149                if self.namespace == 0 && value <= 255 {
150                    2
151                } else if self.namespace <= 255 && value <= 65535 {
152                    4
153                } else {
154                    7
155                }
156            }
157            Identifier::String(ref value) => 3 + value.byte_len(),
158            Identifier::Guid(ref value) => 3 + value.byte_len(),
159            Identifier::ByteString(ref value) => 3 + value.byte_len(),
160        };
161        size
162    }
163
164    fn encode<S: Write>(&self, stream: &mut S) -> EncodingResult<usize> {
165        let mut size: usize = 0;
166        // Type determines the byte code
167        match &self.identifier {
168            Identifier::Numeric(value) => {
169                if self.namespace == 0 && *value <= 255 {
170                    // node id fits into 2 bytes when the namespace is 0 and the value <= 255
171                    size += write_u8(stream, 0x0)?;
172                    size += write_u8(stream, *value as u8)?;
173                } else if self.namespace <= 255 && *value <= 65535 {
174                    // node id fits into 4 bytes when namespace <= 255 and value <= 65535
175                    size += write_u8(stream, 0x1)?;
176                    size += write_u8(stream, self.namespace as u8)?;
177                    size += write_u16(stream, *value as u16)?;
178                } else {
179                    // full node id
180                    size += write_u8(stream, 0x2)?;
181                    size += write_u16(stream, self.namespace)?;
182                    size += write_u32(stream, *value)?;
183                }
184            }
185            Identifier::String(value) => {
186                size += write_u8(stream, 0x3)?;
187                size += write_u16(stream, self.namespace)?;
188                size += value.encode(stream)?;
189            }
190            Identifier::Guid(value) => {
191                size += write_u8(stream, 0x4)?;
192                size += write_u16(stream, self.namespace)?;
193                size += value.encode(stream)?;
194            }
195            Identifier::ByteString(value) => {
196                size += write_u8(stream, 0x5)?;
197                size += write_u16(stream, self.namespace)?;
198                size += value.encode(stream)?;
199            }
200        }
201        assert_eq!(size, self.byte_len());
202        Ok(size)
203    }
204
205    fn decode<S: Read>(stream: &mut S, decoding_options: &DecodingOptions) -> EncodingResult<Self> {
206        let identifier = read_u8(stream)?;
207        let node_id = match identifier {
208            0x0 => {
209                let namespace = 0;
210                let value = read_u8(stream)?;
211                NodeId::new(namespace, u32::from(value))
212            }
213            0x1 => {
214                let namespace = read_u8(stream)?;
215                let value = read_u16(stream)?;
216                NodeId::new(u16::from(namespace), u32::from(value))
217            }
218            0x2 => {
219                let namespace = read_u16(stream)?;
220                let value = read_u32(stream)?;
221                NodeId::new(namespace, value)
222            }
223            0x3 => {
224                let namespace = read_u16(stream)?;
225                let value = UAString::decode(stream, decoding_options)?;
226                NodeId::new(namespace, value)
227            }
228            0x4 => {
229                let namespace = read_u16(stream)?;
230                let value = Guid::decode(stream, decoding_options)?;
231                NodeId::new(namespace, value)
232            }
233            0x5 => {
234                let namespace = read_u16(stream)?;
235                let value = ByteString::decode(stream, decoding_options)?;
236                NodeId::new(namespace, value)
237            }
238            _ => {
239                error!("Unrecognized node id type {}", identifier);
240                return Err(StatusCode::BadDecodingError);
241            }
242        };
243        Ok(node_id)
244    }
245}
246
247impl FromStr for NodeId {
248    type Err = StatusCode;
249    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
250        use regex::Regex;
251
252        // Parses a node from a string using the format specified in 5.3.1.10 part 6
253        //
254        // ns=<namespaceindex>;<type>=<value>
255        //
256        // Where type:
257        //   i = NUMERIC
258        //   s = STRING
259        //   g = GUID
260        //   b = OPAQUE (ByteString)
261        //
262        // If namespace == 0, the ns=0; will be omitted
263
264        lazy_static! {
265            // Contains capture groups "ns" and "t" for namespace and type respectively
266            static ref RE: Regex = Regex::new(r"^(ns=(?P<ns>[0-9]+);)?(?P<t>[isgb]=.+)$").unwrap();
267        }
268
269        let captures = RE.captures(s).ok_or(StatusCode::BadNodeIdInvalid)?;
270
271        // Check namespace (optional)
272        let namespace = if let Some(ns) = captures.name("ns") {
273            ns.as_str()
274                .parse::<u16>()
275                .map_err(|_| StatusCode::BadNodeIdInvalid)?
276        } else {
277            0
278        };
279
280        // Type identifier
281        let t = captures.name("t").unwrap();
282        Identifier::from_str(t.as_str())
283            .map(|t| NodeId::new(namespace, t))
284            .map_err(|_| StatusCode::BadNodeIdInvalid)
285    }
286}
287
288impl From<&NodeId> for NodeId {
289    fn from(v: &NodeId) -> Self {
290        v.clone()
291    }
292}
293
294impl Into<String> for NodeId {
295    fn into(self) -> String {
296        self.to_string()
297    }
298}
299
300impl<'a> From<(u16, &'a str)> for NodeId {
301    fn from(v: (u16, &'a str)) -> Self {
302        Self::new(v.0, UAString::from(v.1))
303    }
304}
305
306impl From<(u16, UAString)> for NodeId {
307    fn from(v: (u16, UAString)) -> Self {
308        Self::new(v.0, v.1)
309    }
310}
311
312impl From<(u16, u32)> for NodeId {
313    fn from(v: (u16, u32)) -> Self {
314        Self::new(v.0, v.1)
315    }
316}
317
318impl From<(u16, Guid)> for NodeId {
319    fn from(v: (u16, Guid)) -> Self {
320        Self::new(v.0, v.1)
321    }
322}
323
324impl From<(u16, ByteString)> for NodeId {
325    fn from(v: (u16, ByteString)) -> Self {
326        Self::new(v.0, v.1)
327    }
328}
329
330static NEXT_NODE_ID_NUMERIC: AtomicUsize = AtomicUsize::new(0);
331
332impl Default for NodeId {
333    fn default() -> Self {
334        NodeId::null()
335    }
336}
337
338impl NodeId {
339    // Constructs a new NodeId from anything that can be turned into Identifier
340    // u32, Guid, ByteString or String
341    pub fn new<T>(namespace: u16, value: T) -> NodeId
342    where
343        T: 'static + Into<Identifier>,
344    {
345        NodeId {
346            namespace,
347            identifier: value.into(),
348        }
349    }
350
351    /// Returns the node id for the root folder.
352    pub fn root_folder_id() -> NodeId {
353        ObjectId::RootFolder.into()
354    }
355
356    /// Returns the node id for the objects folder.
357    pub fn objects_folder_id() -> NodeId {
358        ObjectId::ObjectsFolder.into()
359    }
360
361    /// Returns the node id for the types folder.
362    pub fn types_folder_id() -> NodeId {
363        ObjectId::TypesFolder.into()
364    }
365
366    /// Returns the node id for the views folder.
367    pub fn views_folder_id() -> NodeId {
368        ObjectId::ViewsFolder.into()
369    }
370
371    /// Test if the node id is null, i.e. 0 namespace and 0 identifier
372    pub fn is_null(&self) -> bool {
373        self.namespace == 0 && self.identifier == Identifier::Numeric(0)
374    }
375
376    /// Returns a null node id
377    pub fn null() -> NodeId {
378        NodeId::new(0, 0u32)
379    }
380
381    // Creates a numeric node id with an id incrementing up from 1000
382    pub fn next_numeric(namespace: u16) -> NodeId {
383        NodeId::new(
384            namespace,
385            NEXT_NODE_ID_NUMERIC.fetch_add(1, Ordering::SeqCst) as u32,
386        )
387    }
388
389    /// Extracts an ObjectId from a node id, providing the node id holds an object id
390    pub fn as_object_id(&self) -> std::result::Result<ObjectId, NodeIdError> {
391        match self.identifier {
392            Identifier::Numeric(id) if self.namespace == 0 => {
393                ObjectId::try_from(id).map_err(|_| NodeIdError)
394            }
395            _ => Err(NodeIdError),
396        }
397    }
398
399    pub fn as_reference_type_id(&self) -> std::result::Result<ReferenceTypeId, NodeIdError> {
400        // TODO this function should not exist - filter code should work with non ns 0 reference
401        // types
402        if self.is_null() {
403            Err(NodeIdError)
404        } else {
405            match self.identifier {
406                Identifier::Numeric(id) if self.namespace == 0 => {
407                    ReferenceTypeId::try_from(id).map_err(|_| NodeIdError)
408                }
409                _ => Err(NodeIdError),
410            }
411        }
412    }
413
414    /// Test if the node id is numeric
415    pub fn is_numeric(&self) -> bool {
416        matches!(self.identifier, Identifier::Numeric(_))
417    }
418
419    /// Test if the node id is a string
420    pub fn is_string(&self) -> bool {
421        matches!(self.identifier, Identifier::String(_))
422    }
423
424    /// Test if the node id is a guid
425    pub fn is_guid(&self) -> bool {
426        matches!(self.identifier, Identifier::Guid(_))
427    }
428
429    /// Test if the node id us a byte string
430    pub fn is_byte_string(&self) -> bool {
431        matches!(self.identifier, Identifier::ByteString(_))
432    }
433}
434
435/// A NodeId that allows the namespace URI to be specified instead of an index.
436#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
437pub struct ExpandedNodeId {
438    pub node_id: NodeId,
439    pub namespace_uri: UAString,
440    pub server_index: u32,
441}
442
443impl BinaryEncoder<ExpandedNodeId> for ExpandedNodeId {
444    fn byte_len(&self) -> usize {
445        let mut size = self.node_id.byte_len();
446        if !self.namespace_uri.is_null() {
447            size += self.namespace_uri.byte_len();
448        }
449        if self.server_index != 0 {
450            size += self.server_index.byte_len();
451        }
452        size
453    }
454
455    fn encode<S: Write>(&self, stream: &mut S) -> EncodingResult<usize> {
456        let mut size: usize = 0;
457
458        let mut data_encoding = 0;
459        if !self.namespace_uri.is_null() {
460            data_encoding |= 0x80;
461        }
462        if self.server_index != 0 {
463            data_encoding |= 0x40;
464        }
465
466        // Type determines the byte code
467        match &self.node_id.identifier {
468            Identifier::Numeric(value) => {
469                if self.node_id.namespace == 0 && *value <= 255 {
470                    // node id fits into 2 bytes when the namespace is 0 and the value <= 255
471                    size += write_u8(stream, data_encoding)?;
472                    size += write_u8(stream, *value as u8)?;
473                } else if self.node_id.namespace <= 255 && *value <= 65535 {
474                    // node id fits into 4 bytes when namespace <= 255 and value <= 65535
475                    size += write_u8(stream, data_encoding | 0x1)?;
476                    size += write_u8(stream, self.node_id.namespace as u8)?;
477                    size += write_u16(stream, *value as u16)?;
478                } else {
479                    // full node id
480                    size += write_u8(stream, data_encoding | 0x2)?;
481                    size += write_u16(stream, self.node_id.namespace)?;
482                    size += write_u32(stream, *value)?;
483                }
484            }
485            Identifier::String(value) => {
486                size += write_u8(stream, data_encoding | 0x3)?;
487                size += write_u16(stream, self.node_id.namespace)?;
488                size += value.encode(stream)?;
489            }
490            Identifier::Guid(value) => {
491                size += write_u8(stream, data_encoding | 0x4)?;
492                size += write_u16(stream, self.node_id.namespace)?;
493                size += value.encode(stream)?;
494            }
495            Identifier::ByteString(ref value) => {
496                size += write_u8(stream, data_encoding | 0x5)?;
497                size += write_u16(stream, self.node_id.namespace)?;
498                size += value.encode(stream)?;
499            }
500        }
501        if !self.namespace_uri.is_null() {
502            size += self.namespace_uri.encode(stream)?;
503        }
504        if self.server_index != 0 {
505            size += self.server_index.encode(stream)?;
506        }
507        assert_eq!(size, self.byte_len());
508        Ok(size)
509    }
510
511    fn decode<S: Read>(stream: &mut S, decoding_options: &DecodingOptions) -> EncodingResult<Self> {
512        let data_encoding = read_u8(stream)?;
513        let identifier = data_encoding & 0x0f;
514        let node_id = match identifier {
515            0x0 => {
516                let value = read_u8(stream)?;
517                NodeId::new(0, u32::from(value))
518            }
519            0x1 => {
520                let namespace = read_u8(stream)?;
521                let value = read_u16(stream)?;
522                NodeId::new(u16::from(namespace), u32::from(value))
523            }
524            0x2 => {
525                let namespace = read_u16(stream)?;
526                let value = read_u32(stream)?;
527                NodeId::new(namespace, value)
528            }
529            0x3 => {
530                let namespace = read_u16(stream)?;
531                let value = UAString::decode(stream, decoding_options)?;
532                NodeId::new(namespace, value)
533            }
534            0x4 => {
535                let namespace = read_u16(stream)?;
536                let value = Guid::decode(stream, decoding_options)?;
537                NodeId::new(namespace, value)
538            }
539            0x5 => {
540                let namespace = read_u16(stream)?;
541                let value = ByteString::decode(stream, decoding_options)?;
542                NodeId::new(namespace, value)
543            }
544            _ => {
545                error!("Unrecognized expanded node id type {}", identifier);
546                return Err(StatusCode::BadDecodingError);
547            }
548        };
549
550        // Optional stuff
551        let namespace_uri = if data_encoding & 0x80 != 0 {
552            UAString::decode(stream, decoding_options)?
553        } else {
554            UAString::null()
555        };
556        let server_index = if data_encoding & 0x40 != 0 {
557            u32::decode(stream, decoding_options)?
558        } else {
559            0
560        };
561
562        Ok(ExpandedNodeId {
563            node_id,
564            namespace_uri,
565            server_index,
566        })
567    }
568}
569
570impl<'a> Into<ExpandedNodeId> for &'a NodeId {
571    fn into(self) -> ExpandedNodeId {
572        self.clone().into()
573    }
574}
575
576impl From<NodeId> for ExpandedNodeId {
577    fn from(v: NodeId) -> Self {
578        ExpandedNodeId {
579            node_id: v,
580            namespace_uri: UAString::null(),
581            server_index: 0,
582        }
583    }
584}
585
586impl fmt::Display for ExpandedNodeId {
587    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
588        // Formatted depending on the namespace uri being empty or not.
589        if self.namespace_uri.is_empty() {
590            // svr=<serverindex>;ns=<namespaceindex>;<type>=<value>
591            write!(f, "svr={};{}", self.server_index, self.node_id)
592        } else {
593            // The % and ; chars have to be escaped out in the uri
594            let namespace_uri = String::from(self.namespace_uri.as_ref())
595                .replace('%', "%25")
596                .replace(';', "%3b");
597            // svr=<serverindex>;nsu=<uri>;<type>=<value>
598            write!(
599                f,
600                "svr={};nsu={};{}",
601                self.server_index, namespace_uri, self.node_id.identifier
602            )
603        }
604    }
605}
606
607impl FromStr for ExpandedNodeId {
608    type Err = StatusCode;
609    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
610        use regex::Regex;
611
612        // Parses a node from a string using the format specified in 5.3.1.11 part 6
613        //
614        // svr=<serverindex>;ns=<namespaceindex>;<type>=<value>
615        // or
616        // svr=<serverindex>;nsu=<uri>;<type>=<value>
617
618        lazy_static! {
619            // Contains capture groups "svr", either "ns" or "nsu" and then "t" for type
620            static ref RE: Regex = Regex::new(r"^svr=(?P<svr>[0-9]+);(ns=(?P<ns>[0-9]+)|nsu=(?P<nsu>[^;]+));(?P<t>[isgb]=.+)$").unwrap();
621        }
622
623        let captures = RE.captures(s).ok_or(StatusCode::BadNodeIdInvalid)?;
624
625        // Server index
626        let server_index = captures
627            .name("svr")
628            .ok_or(StatusCode::BadNodeIdInvalid)
629            .and_then(|server_index| {
630                server_index
631                    .as_str()
632                    .parse::<u32>()
633                    .map_err(|_| StatusCode::BadNodeIdInvalid)
634            })?;
635
636        // Check for namespace uri
637        let namespace_uri = if let Some(nsu) = captures.name("nsu") {
638            // The % and ; chars need to be unescaped
639            let nsu = String::from(nsu.as_str())
640                .replace("%3b", ";")
641                .replace("%25", "%");
642            UAString::from(nsu)
643        } else {
644            UAString::null()
645        };
646
647        let namespace = if let Some(ns) = captures.name("ns") {
648            ns.as_str()
649                .parse::<u16>()
650                .map_err(|_| StatusCode::BadNodeIdInvalid)?
651        } else {
652            0
653        };
654
655        // Type identifier
656        let t = captures.name("t").unwrap();
657        Identifier::from_str(t.as_str())
658            .map(|t| ExpandedNodeId {
659                server_index,
660                namespace_uri,
661                node_id: NodeId::new(namespace, t),
662            })
663            .map_err(|_| StatusCode::BadNodeIdInvalid)
664    }
665}
666
667impl ExpandedNodeId {
668    /// Creates an expanded node id from a node id
669    pub fn new<T>(value: T) -> ExpandedNodeId
670    where
671        T: 'static + Into<ExpandedNodeId>,
672    {
673        value.into()
674    }
675
676    pub fn null() -> ExpandedNodeId {
677        Self::new(NodeId::null())
678    }
679
680    pub fn is_null(&self) -> bool {
681        self.node_id.is_null()
682    }
683}