mesh_node/
common.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use mesh_derive::Protobuf;
5use std::fmt;
6use std::fmt::Debug;
7use std::fmt::Display;
8use std::str::FromStr;
9
10/// A unique ID.
11#[derive(Copy, Clone, PartialEq, Eq, Hash, Protobuf)]
12pub struct Uuid(pub [u8; 16]);
13
14impl Uuid {
15    fn new() -> Self {
16        // Generate a cryptographically random ID so that a malicious peer
17        // cannot guess a port ID.
18        let mut id = Self([0; 16]);
19        getrandom::fill(&mut id.0[..]).expect("rng failure");
20        id
21    }
22}
23
24impl Display for Uuid {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        write!(f, "{:x}", u128::from_be_bytes(self.0))
27    }
28}
29
30impl Debug for Uuid {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        Display::fmt(self, f)
33    }
34}
35
36#[derive(Debug)]
37pub struct ParseUuidError;
38
39impl FromStr for Uuid {
40    type Err = ParseUuidError;
41
42    fn from_str(s: &str) -> Result<Self, Self::Err> {
43        if s.is_empty() || s.as_bytes()[0] == b'+' {
44            return Err(ParseUuidError);
45        }
46        u128::from_str_radix(s, 16)
47            .map(|n| Self(n.to_be_bytes()))
48            .map_err(|_| ParseUuidError)
49    }
50}
51
52#[cfg(debug_assertions)]
53mod debug {
54    //! In debug builds, conditionally return linear node and port IDs instead
55    //! of random ones, based on the contents of an environment variable. This
56    //! breaks some of the mesh security guarantees, so it is never safe for
57    //! production use, but it simplifies mesh debugging.
58
59    use super::Uuid;
60    use std::sync::Once;
61    use std::sync::atomic::AtomicBool;
62    use std::sync::atomic::AtomicU64;
63    use std::sync::atomic::Ordering;
64
65    static CHECK_ONCE: Once = Once::new();
66    static USE_LINEAR_IDS: AtomicBool = AtomicBool::new(false);
67
68    pub struct DebugUuidSource(AtomicU64);
69
70    impl DebugUuidSource {
71        pub const fn new() -> Self {
72            Self(AtomicU64::new(1))
73        }
74
75        pub fn next(&self) -> Option<Uuid> {
76            CHECK_ONCE.call_once(|| {
77                if std::env::var_os("__MESH_UNSAFE_DEBUG_IDS__").is_some_and(|x| !x.is_empty()) {
78                    tracing::error!("using unsafe debugging mesh IDs--this mesh could be compromised by external callers");
79                    USE_LINEAR_IDS.store(true, Ordering::Relaxed);
80                }
81            });
82
83            if !USE_LINEAR_IDS.load(Ordering::Relaxed) {
84                return None;
85            }
86
87            Some(Uuid(
88                u128::from(self.0.fetch_add(1, Ordering::Relaxed)).to_be_bytes(),
89            ))
90        }
91    }
92}
93
94/// A node ID.
95#[derive(Copy, Clone, PartialEq, Eq, Hash, Protobuf)]
96pub struct NodeId(pub Uuid);
97
98impl NodeId {
99    pub const ZERO: Self = Self(Uuid([0; 16]));
100
101    pub fn new() -> Self {
102        #[cfg(debug_assertions)]
103        {
104            static SOURCE: debug::DebugUuidSource = debug::DebugUuidSource::new();
105            if let Some(id) = SOURCE.next() {
106                return Self(id);
107            }
108        }
109        Self(Uuid::new())
110    }
111}
112
113impl Debug for NodeId {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        write!(f, "N-{:?}", &self.0)
116    }
117}
118
119/// A port ID.
120#[derive(Copy, Clone, PartialEq, Eq, Hash, Protobuf)]
121pub struct PortId(pub Uuid);
122
123impl PortId {
124    pub fn new() -> Self {
125        #[cfg(debug_assertions)]
126        {
127            static SOURCE: debug::DebugUuidSource = debug::DebugUuidSource::new();
128            if let Some(id) = SOURCE.next() {
129                return Self(id);
130            }
131        }
132        Self(Uuid::new())
133    }
134}
135
136impl Debug for PortId {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        write!(f, "P-{:?}", &self.0)
139    }
140}
141
142/// A port address.
143#[derive(Copy, Clone, PartialEq, Eq, Protobuf)]
144pub struct Address {
145    pub node: NodeId,
146    pub port: PortId,
147}
148
149impl Address {
150    pub fn new(node: NodeId, port: PortId) -> Self {
151        Self { node, port }
152    }
153}
154
155impl Debug for Address {
156    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157        write!(f, "{:?}.{:?}", &self.node, &self.port)
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::Uuid;
164
165    #[test]
166    fn test_uuid() {
167        Uuid::new();
168    }
169}