Skip to main content

netget/server/h323/
per.rs

1//! ALIGNED PER (ITU-T X.691), driven by the schema tables in `schema.rs`.
2//!
3//! H.225.0 and H.245 are big ASN.1 modules — the parts NetGet speaks reach about seven hundred
4//! types — and every message's root fields have to be understood to reach the next one: PER has
5//! no tags, so a field nobody can decode makes everything after it unreadable. Rather than hand
6//! code a walker per message, `scripts/h323_asn1_schema.py` turns the ITU modules into a table
7//! of [`Ty`] values and this module interprets it in both directions:
8//!
9//! - [`decode`] reads one complete encoding into JSON: a SEQUENCE is an object of its present
10//!   fields, a CHOICE a one-key object naming the alternative, an ENUMERATED its name, an OCTET
11//!   STRING lowercase hex, a BIT STRING a string of `0`/`1`, an OBJECT IDENTIFIER its dotted
12//!   form, NULL `null`.
13//! - [`encode`] writes the same JSON back. Unknown keys are refused rather than ignored, so a
14//!   misspelt field is an error and not a silently missing one.
15//!
16//! # Bounds
17//!
18//! Every read is checked against the input, and every count or length the peer declares is
19//! checked against its constraint *and* against what is actually left before anything is
20//! allocated for it. Nesting is bounded by [`MAX_PER_DEPTH`] — H.225.0's `GenericData` and
21//! H.245's `GenericParameter` are recursive, and without the bound a few hundred bytes of
22//! nested `compound`/`nested` parameters would overflow the stack — and the number of values in
23//! one decode by [`MAX_PER_NODES`], because a SEQUENCE OF NULL costs no bits per element.
24//! Fragmented lengths (≥ 16384, X.691 11.9.3.8) are refused: nothing NetGet speaks comes near
25//! one. A normally-small number above 63 (an extension index or bitmap that long) is refused.
26//!
27//! Types the generator could not express (`SIGNED{}`, `ENCRYPTED{}`, `HASHED{}`,
28//! `GeneralString`) are [`Ty::Opaque`]: an extension addition of that type is stepped over by
29//! its open-type length; in a root position the decode stops with an error naming it.
30
31use serde_json::{json, Map, Value};
32use std::fmt;
33
34/// Deepest nesting of types in one decode or encode. Real H.225.0 messages nest about fifteen
35/// deep (Setup → UU-PDU → Setup-UUIE → EndpointType → GatewayInfo → SupportedProtocols → …).
36pub const MAX_PER_DEPTH: u32 = 40;
37/// Most values one decode may produce.
38pub const MAX_PER_NODES: usize = 20_000;
39/// The largest unfragmented length (X.691 11.9.3.8).
40pub const MAX_UNFRAGMENTED: usize = 16_383;
41
42#[derive(Debug, Clone, Copy)]
43pub struct Range {
44    pub lb: Option<i64>,
45    pub ub: Option<i64>,
46    pub ext: bool,
47}
48
49#[derive(Debug, Clone, Copy)]
50pub struct Size {
51    pub lb: u32,
52    pub ub: Option<u32>,
53    pub ext: bool,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum CharKind {
58    Ia5,
59    Bmp,
60    Numeric,
61    Printable,
62    /// GeneralString: not a known-multiplier type, so PER writes an unconstrained length in
63    /// octets and the octets themselves (X.691 30.6). NetGet reads them as Latin-1.
64    General,
65}
66
67#[derive(Debug)]
68pub struct Field {
69    pub name: &'static str,
70    pub ty: u16,
71    pub optional: bool,
72}
73
74#[derive(Debug)]
75pub enum Ty {
76    Null,
77    Boolean,
78    Oid,
79    Int(Range),
80    Enum {
81        root: &'static [&'static str],
82        ext: Option<&'static [&'static str]>,
83    },
84    Octets(Size),
85    Bits(Size),
86    Chars {
87        kind: CharKind,
88        size: Size,
89        alphabet: Option<&'static str>,
90    },
91    Seq {
92        root: &'static [Field],
93        ext: Option<&'static [Field]>,
94    },
95    Choice {
96        root: &'static [Field],
97        ext: Option<&'static [Field]>,
98    },
99    SeqOf {
100        size: Size,
101        item: u16,
102    },
103    Alias(u16),
104    Opaque(&'static str),
105}
106
107/// A decode or encode refusal, naming the field path. Nothing in it reaches a peer.
108#[derive(Debug, Clone, PartialEq, Eq)]
109pub struct PerError(pub String);
110
111impl fmt::Display for PerError {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        f.write_str(&self.0)
114    }
115}
116
117impl std::error::Error for PerError {}
118
119pub type PerResult<T> = Result<T, PerError>;
120
121fn ty(index: u16) -> &'static Ty {
122    &super::schema::TYPES[index as usize]
123}
124
125/// Bits needed to encode `range` distinct values (`range >= 1`).
126fn bits_for_range(range: u64) -> u32 {
127    if range <= 1 {
128        0
129    } else {
130        64 - (range - 1).leading_zeros()
131    }
132}
133
134fn octets_for(value: u64) -> usize {
135    let bits = 64 - value.leading_zeros() as usize;
136    bits.div_ceil(8).max(1)
137}
138
139/// Characters of a known-multiplier string type's effective alphabet, sorted by value
140/// (X.680's canonical order), and the bits per character in the ALIGNED variant.
141fn alphabet(kind: CharKind, from: Option<&'static str>) -> (Vec<u32>, u32) {
142    let mut chars: Vec<u32> = match (from, kind) {
143        (Some(a), _) => a.chars().map(|c| c as u32).collect(),
144        (None, CharKind::Numeric) => " 0123456789".chars().map(|c| c as u32).collect(),
145        (None, CharKind::Printable) => {
146            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '()+,-./:=?"
147                .chars()
148                .map(|c| c as u32)
149                .collect()
150        }
151        (None, CharKind::Ia5) => (0..128).collect(),
152        (None, CharKind::Bmp) => Vec::new(),
153        (None, CharKind::General) => (0..256).collect(),
154    };
155    chars.sort_unstable();
156    chars.dedup();
157    if kind == CharKind::Bmp && from.is_none() {
158        return (chars, 16);
159    }
160    if kind == CharKind::General {
161        return (chars, 8);
162    }
163    let n = chars.len() as u64;
164    let bits = bits_for_range(n);
165    // X.691 30.5.2: in the ALIGNED variant b is rounded up to a power of two.
166    let b = match bits {
167        0..=1 => bits,
168        2 => 2,
169        3..=4 => 4,
170        5..=8 => 8,
171        _ => 16,
172    };
173    (chars, b)
174}
175
176// ============================================================================
177// Bit reader / writer
178// ============================================================================
179
180struct Reader<'a> {
181    data: &'a [u8],
182    pos: usize,
183}
184
185impl<'a> Reader<'a> {
186    fn remaining_bits(&self) -> usize {
187        self.data.len() * 8 - self.pos
188    }
189
190    fn align(&mut self) {
191        self.pos = self.pos.div_ceil(8) * 8;
192        if self.pos > self.data.len() * 8 {
193            self.pos = self.data.len() * 8;
194        }
195    }
196
197    fn bits(&mut self, n: u32, what: &str) -> PerResult<u64> {
198        if n > 64 {
199            return err(format!("{what}: {n}-bit field is wider than 64"));
200        }
201        if self.remaining_bits() < n as usize {
202            return err(format!(
203                "{what}: needs {n} bits, {} remain",
204                self.remaining_bits()
205            ));
206        }
207        let mut value = 0u64;
208        for _ in 0..n {
209            let byte = self.data[self.pos / 8];
210            let bit = (byte >> (7 - (self.pos % 8))) & 1;
211            value = (value << 1) | bit as u64;
212            self.pos += 1;
213        }
214        Ok(value)
215    }
216
217    fn octets(&mut self, n: usize, what: &str) -> PerResult<&'a [u8]> {
218        self.align();
219        let start = self.pos / 8;
220        if self.data.len() - start < n {
221            return err(format!(
222                "{what}: declares {n} octets, {} remain",
223                self.data.len() - start
224            ));
225        }
226        self.pos += n * 8;
227        Ok(&self.data[start..start + n])
228    }
229
230    fn octets_unaligned(&mut self, n: usize, what: &str) -> PerResult<Vec<u8>> {
231        if self.remaining_bits() < n * 8 {
232            return err(format!("{what}: declares {n} octets past the end"));
233        }
234        (0..n).map(|_| Ok(self.bits(8, what)? as u8)).collect()
235    }
236
237    /// Constrained whole number in `0..range` (X.691 11.5.7, ALIGNED).
238    fn whole(&mut self, range: u64, what: &str) -> PerResult<u64> {
239        match range {
240            0 | 1 => Ok(0),
241            r if r <= 255 => self.bits(bits_for_range(r), what),
242            256 => {
243                self.align();
244                self.bits(8, what)
245            }
246            r if r <= 65_536 => {
247                self.align();
248                self.bits(16, what)
249            }
250            r => {
251                let max_octets = octets_for(r - 1) as u64;
252                let len = self.bits(bits_for_range(max_octets), what)? + 1;
253                let raw = self.octets(len as usize, what)?;
254                Ok(raw.iter().fold(0u64, |acc, b| (acc << 8) | *b as u64))
255            }
256        }
257    }
258
259    fn general_length(&mut self, what: &str) -> PerResult<usize> {
260        self.align();
261        let first = self.bits(8, what)? as usize;
262        if first & 0x80 == 0 {
263            return Ok(first);
264        }
265        if first & 0xC0 == 0x80 {
266            let second = self.bits(8, what)? as usize;
267            return Ok(((first & 0x3F) << 8) | second);
268        }
269        err(format!(
270            "{what}: fragmented length (≥16384) is not accepted"
271        ))
272    }
273
274    fn normally_small(&mut self, what: &str) -> PerResult<u64> {
275        if self.bits(1, what)? == 1 {
276            return err(format!("{what}: normally-small value above 63"));
277        }
278        self.bits(6, what)
279    }
280
281    /// A length or count under `size`; `extended` tells whether the extension bit was set.
282    fn length(&mut self, size: Size, what: &str) -> PerResult<usize> {
283        if size.ext && self.bits(1, what)? == 1 {
284            return self.general_length(what);
285        }
286        let n = match size.ub {
287            Some(ub) if ub == size.lb => ub as usize,
288            Some(ub) if ub < 65_536 => {
289                size.lb as usize + self.whole((ub - size.lb) as u64 + 1, what)? as usize
290            }
291            _ => self.general_length(what)?,
292        };
293        if n < size.lb as usize || size.ub.is_some_and(|ub| n > ub as usize) {
294            return err(format!(
295                "{what}: size {n} outside {}..{:?}",
296                size.lb, size.ub
297            ));
298        }
299        Ok(n)
300    }
301
302    fn open_type(&mut self, what: &str) -> PerResult<&'a [u8]> {
303        let len = self.general_length(what)?;
304        self.octets(len, what)
305    }
306}
307
308#[derive(Default)]
309struct Writer {
310    data: Vec<u8>,
311    bit_len: usize,
312}
313
314impl Writer {
315    fn bits(&mut self, value: u64, n: u32) {
316        for i in (0..n).rev() {
317            let bit = ((value >> i) & 1) as u8;
318            if self.bit_len.is_multiple_of(8) {
319                self.data.push(0);
320            }
321            if bit == 1 {
322                let last = self.data.len() - 1;
323                self.data[last] |= 1 << (7 - (self.bit_len % 8));
324            }
325            self.bit_len += 1;
326        }
327    }
328
329    fn align(&mut self) {
330        self.bit_len = self.bit_len.div_ceil(8) * 8;
331    }
332
333    fn octets(&mut self, bytes: &[u8]) {
334        self.align();
335        self.data.extend_from_slice(bytes);
336        self.bit_len += bytes.len() * 8;
337    }
338
339    fn octets_unaligned(&mut self, bytes: &[u8]) {
340        for b in bytes {
341            self.bits(*b as u64, 8);
342        }
343    }
344
345    fn whole(&mut self, v: u64, range: u64) {
346        match range {
347            0 | 1 => {}
348            r if r <= 255 => self.bits(v, bits_for_range(r)),
349            256 => {
350                self.align();
351                self.bits(v, 8);
352            }
353            r if r <= 65_536 => {
354                self.align();
355                self.bits(v, 16);
356            }
357            r => {
358                let max_octets = octets_for(r - 1) as u64;
359                let len = octets_for(v);
360                self.bits(len as u64 - 1, bits_for_range(max_octets));
361                self.align();
362                for i in (0..len).rev() {
363                    self.bits((v >> (8 * i)) & 0xFF, 8);
364                }
365            }
366        }
367    }
368
369    fn general_length(&mut self, n: usize, what: &str) -> PerResult<()> {
370        self.align();
371        if n < 128 {
372            self.bits(n as u64, 8);
373        } else if n <= MAX_UNFRAGMENTED {
374            self.bits(0x8000 | n as u64, 16);
375        } else {
376            return err(format!("{what}: {n} needs a fragmented length"));
377        }
378        Ok(())
379    }
380
381    fn normally_small(&mut self, v: u64, what: &str) -> PerResult<()> {
382        if v > 63 {
383            return err(format!("{what}: {v} is not a normally-small number"));
384        }
385        self.bits(0, 1);
386        self.bits(v, 6);
387        Ok(())
388    }
389
390    fn length(&mut self, n: usize, size: Size, what: &str) -> PerResult<()> {
391        let in_root = n >= size.lb as usize && size.ub.is_none_or(|ub| n <= ub as usize);
392        if !in_root {
393            return err(format!(
394                "{what}: size {n} outside {}..{:?}",
395                size.lb, size.ub
396            ));
397        }
398        if size.ext {
399            self.bits(0, 1);
400        }
401        match size.ub {
402            Some(ub) if ub == size.lb => {}
403            Some(ub) if ub < 65_536 => {
404                self.whole((n - size.lb as usize) as u64, (ub - size.lb) as u64 + 1)
405            }
406            _ => self.general_length(n, what)?,
407        }
408        Ok(())
409    }
410
411    fn open_type(&mut self, inner: &[u8], what: &str) -> PerResult<()> {
412        self.general_length(inner.len(), what)?;
413        self.octets(inner);
414        Ok(())
415    }
416
417    fn finish(mut self) -> Vec<u8> {
418        if self.data.is_empty() {
419            self.data.push(0);
420        }
421        self.data
422    }
423}
424
425fn err<T>(msg: impl Into<String>) -> PerResult<T> {
426    Err(PerError(msg.into()))
427}
428
429// ============================================================================
430// Decode
431// ============================================================================
432
433/// Decode one complete encoding of schema type `root`.
434pub fn decode(root: u16, data: &[u8]) -> PerResult<Value> {
435    let mut nodes = 0usize;
436    decode_at(root, data, 0, &mut nodes)
437}
438
439fn decode_at(root: u16, data: &[u8], depth: u32, nodes: &mut usize) -> PerResult<Value> {
440    let mut d = Decoder {
441        r: Reader { data, pos: 0 },
442        nodes,
443    };
444    d.value(root, "", depth)
445}
446
447struct Decoder<'a, 'n> {
448    r: Reader<'a>,
449    nodes: &'n mut usize,
450}
451
452impl Decoder<'_, '_> {
453    fn value(&mut self, index: u16, path: &str, depth: u32) -> PerResult<Value> {
454        if depth > MAX_PER_DEPTH {
455            return err(format!("{path}: nested deeper than {MAX_PER_DEPTH}"));
456        }
457        *self.nodes += 1;
458        if *self.nodes > MAX_PER_NODES {
459            return err(format!("{path}: more than {MAX_PER_NODES} values"));
460        }
461        let what = if path.is_empty() {
462            super::schema::NAMES
463                .get(index as usize)
464                .copied()
465                .unwrap_or("value")
466        } else {
467            path
468        };
469        match ty(index) {
470            Ty::Alias(target) => self.value(*target, path, depth + 1),
471            Ty::Null => Ok(Value::Null),
472            Ty::Boolean => Ok(Value::Bool(self.r.bits(1, what)? == 1)),
473            Ty::Int(range) => self.integer(*range, what),
474            Ty::Enum { root, ext } => {
475                if ext.is_some() && self.r.bits(1, what)? == 1 {
476                    let i = self.r.normally_small(what)? as usize;
477                    return Ok(match ext.and_then(|e| e.get(i)) {
478                        Some(name) => json!(name),
479                        None => json!(format!("_unknown_{i}")),
480                    });
481                }
482                let i = self.r.whole(root.len() as u64, what)? as usize;
483                root.get(i)
484                    .map(|n| json!(n))
485                    .ok_or_else(|| PerError(format!("{what}: enumeration index {i}")))
486            }
487            Ty::Octets(size) => {
488                let n = self.r.length(*size, what)?;
489                let fixed = !size.ext && size.ub == Some(size.lb);
490                let raw = if n == 0 {
491                    Vec::new()
492                } else if fixed && n <= 2 {
493                    self.r.octets_unaligned(n, what)?
494                } else {
495                    self.r.octets(n, what)?.to_vec()
496                };
497                Ok(json!(hex::encode(raw)))
498            }
499            Ty::Bits(size) => {
500                let n = self.r.length(*size, what)?;
501                if n > self.r.remaining_bits() {
502                    return err(format!("{what}: declares {n} bits past the end"));
503                }
504                let fixed = !size.ext && size.ub == Some(size.lb);
505                if n > 0 && !(fixed && n <= 16) {
506                    self.r.align();
507                }
508                let mut s = String::with_capacity(n);
509                for _ in 0..n {
510                    s.push(if self.r.bits(1, what)? == 1 { '1' } else { '0' });
511                }
512                Ok(json!(s))
513            }
514            Ty::Chars {
515                kind,
516                size,
517                alphabet: from,
518            } => self.chars(*kind, *size, *from, what),
519            Ty::Oid => {
520                let len = self.r.general_length(what)?;
521                let raw = self.r.octets(len, what)?;
522                Ok(json!(oid_to_string(raw).ok_or_else(|| PerError(
523                    format!("{what}: malformed OBJECT IDENTIFIER")
524                ))?))
525            }
526            Ty::Seq { root, ext } => self.sequence(root, *ext, path, depth),
527            Ty::Choice { root, ext } => {
528                if ext.is_some() && self.r.bits(1, what)? == 1 {
529                    let i = self.r.normally_small(what)? as usize;
530                    let inner = self.r.open_type(what)?;
531                    return match ext.and_then(|e| e.get(i)) {
532                        Some(f) => {
533                            let sub = join(path, f.name);
534                            let v = self.open(f.ty, inner, &sub, depth)?;
535                            Ok(json!({ f.name: v }))
536                        }
537                        None => Ok(json!({ "_unknown_extension": i })),
538                    };
539                }
540                let i = self.r.whole(root.len() as u64, what)? as usize;
541                let f = root
542                    .get(i)
543                    .ok_or_else(|| PerError(format!("{what}: alternative {i}")))?;
544                let sub = join(path, f.name);
545                let v = self.value(f.ty, &sub, depth + 1)?;
546                Ok(json!({ f.name: v }))
547            }
548            Ty::SeqOf { size, item } => {
549                let n = self.r.length(*size, what)?;
550                if n > MAX_PER_NODES {
551                    return err(format!("{what}: {n} elements"));
552                }
553                let mut out = Vec::with_capacity(n.min(64));
554                for i in 0..n {
555                    let sub = format!("{path}[{i}]");
556                    out.push(self.value(*item, &sub, depth + 1)?);
557                }
558                Ok(Value::Array(out))
559            }
560            Ty::Opaque(why) => err(format!("{what}: {why} is outside NetGet's PER subset")),
561        }
562    }
563
564    fn open(&mut self, index: u16, inner: &[u8], path: &str, depth: u32) -> PerResult<Value> {
565        let mut d = Decoder {
566            r: Reader {
567                data: inner,
568                pos: 0,
569            },
570            nodes: self.nodes,
571        };
572        d.value(index, path, depth + 1)
573    }
574
575    fn sequence(
576        &mut self,
577        root: &'static [Field],
578        ext: Option<&'static [Field]>,
579        path: &str,
580        depth: u32,
581    ) -> PerResult<Value> {
582        let what = if path.is_empty() { "sequence" } else { path };
583        let extended = ext.is_some() && self.r.bits(1, what)? == 1;
584        let optional = root.iter().filter(|f| f.optional).count();
585        let mut present = Vec::with_capacity(optional);
586        for _ in 0..optional {
587            present.push(self.r.bits(1, what)? == 1);
588        }
589        let mut obj = Map::new();
590        let mut opt = present.into_iter();
591        for f in root {
592            if f.optional && !opt.next().unwrap_or(false) {
593                continue;
594            }
595            let sub = join(path, f.name);
596            let v = self.value(f.ty, &sub, depth + 1)?;
597            obj.insert(f.name.to_string(), v);
598        }
599        if extended {
600            let n = self.r.normally_small(what)? as usize + 1;
601            let mut bitmap = Vec::with_capacity(n);
602            for _ in 0..n {
603                bitmap.push(self.r.bits(1, what)? == 1);
604            }
605            let additions = ext.unwrap_or(&[]);
606            for (i, on) in bitmap.into_iter().enumerate() {
607                if !on {
608                    continue;
609                }
610                let inner = self.r.open_type(what)?;
611                let Some(f) = additions.get(i) else {
612                    continue;
613                };
614                if let Ty::Opaque(_) = resolve(f.ty) {
615                    // Stepped over by its length: H.235 SIGNED{}/ENCRYPTED{} tokens and the like.
616                    obj.insert(f.name.to_string(), json!({"_opaque_octets": inner.len()}));
617                    continue;
618                }
619                let sub = join(path, f.name);
620                let v = self.open(f.ty, inner, &sub, depth)?;
621                obj.insert(f.name.to_string(), v);
622            }
623        }
624        Ok(Value::Object(obj))
625    }
626
627    fn integer(&mut self, range: Range, what: &str) -> PerResult<Value> {
628        if range.ext && self.r.bits(1, what)? == 1 {
629            return self.unconstrained(what);
630        }
631        match (range.lb, range.ub) {
632            (Some(lb), Some(ub)) => {
633                let span = (ub - lb) as u64 + 1;
634                let v = self.r.whole(span, what)?;
635                let v = lb + v as i64;
636                if v > ub {
637                    return err(format!("{what}: {v} is above {ub}"));
638                }
639                Ok(json!(v))
640            }
641            (Some(lb), None) => {
642                let len = self.r.general_length(what)?;
643                if len == 0 || len > 8 {
644                    return err(format!("{what}: {len}-octet integer"));
645                }
646                let raw = self.r.octets(len, what)?;
647                let v = raw.iter().fold(0u64, |acc, b| (acc << 8) | *b as u64);
648                i64::try_from(v)
649                    .ok()
650                    .and_then(|v| lb.checked_add(v))
651                    .map(|n| json!(n))
652                    .ok_or_else(|| PerError(format!("{what}: {v} above {lb} does not fit")))
653            }
654            _ => self.unconstrained(what),
655        }
656    }
657
658    fn unconstrained(&mut self, what: &str) -> PerResult<Value> {
659        let len = self.r.general_length(what)?;
660        if len == 0 || len > 8 {
661            return err(format!("{what}: {len}-octet integer"));
662        }
663        let raw = self.r.octets(len, what)?;
664        let mut v: i64 = if raw[0] & 0x80 != 0 { -1 } else { 0 };
665        for b in raw {
666            v = (v << 8) | *b as i64;
667        }
668        Ok(json!(v))
669    }
670
671    fn chars(
672        &mut self,
673        kind: CharKind,
674        size: Size,
675        from: Option<&'static str>,
676        what: &str,
677    ) -> PerResult<Value> {
678        let (set, b) = alphabet(kind, from);
679        let n = self.r.length(size, what)?;
680        if n * b as usize > self.r.remaining_bits() {
681            return err(format!("{what}: {n} characters past the end"));
682        }
683        let fixed = !size.ext && size.ub == Some(size.lb);
684        let aligned = n > 0
685            && match size.ub {
686                Some(ub) if fixed => ub as usize * b as usize > 16,
687                Some(ub) => ub as usize * b as usize > 16,
688                None => true,
689            };
690        if aligned {
691            self.r.align();
692        }
693        let remap = set.last().is_some_and(|max| *max as u64 > (1u64 << b) - 1);
694        let mut s = String::with_capacity(n);
695        for _ in 0..n {
696            let raw = self.r.bits(b, what)? as u32;
697            let code = if remap {
698                *set.get(raw as usize)
699                    .ok_or_else(|| PerError(format!("{what}: character index {raw}")))?
700            } else {
701                raw
702            };
703            if !set.is_empty() && !remap && set.binary_search(&code).is_err() {
704                return err(format!("{what}: character {code:#x} is not permitted"));
705            }
706            s.push(char::from_u32(code).unwrap_or('\u{fffd}'));
707        }
708        Ok(json!(s))
709    }
710}
711
712fn join(path: &str, name: &str) -> String {
713    if path.is_empty() {
714        name.to_string()
715    } else {
716        format!("{path}.{name}")
717    }
718}
719
720/// Follow aliases to the type that is not one. Aliases in the schema never form a cycle (the
721/// generator only aliases a name to a different name), but the walk is bounded anyway.
722fn resolve(mut index: u16) -> &'static Ty {
723    for _ in 0..16 {
724        match ty(index) {
725            Ty::Alias(t) => index = *t,
726            other => return other,
727        }
728    }
729    ty(index)
730}
731
732pub fn oid_to_string(raw: &[u8]) -> Option<String> {
733    if raw.is_empty() || raw.len() > 64 {
734        return None;
735    }
736    let mut arcs: Vec<u64> = Vec::new();
737    let mut acc: u64 = 0;
738    let mut in_arc = false;
739    for b in raw {
740        if acc > (u64::MAX >> 8) {
741            return None;
742        }
743        acc = (acc << 7) | (*b & 0x7F) as u64;
744        in_arc = true;
745        if b & 0x80 == 0 {
746            if arcs.is_empty() {
747                let first = (acc / 40).min(2);
748                arcs.push(first);
749                arcs.push(acc - first * 40);
750            } else {
751                arcs.push(acc);
752            }
753            acc = 0;
754            in_arc = false;
755        }
756    }
757    if in_arc {
758        return None;
759    }
760    Some(
761        arcs.iter()
762            .map(|a| a.to_string())
763            .collect::<Vec<_>>()
764            .join("."),
765    )
766}
767
768pub fn oid_from_string(s: &str) -> Option<Vec<u8>> {
769    let arcs: Vec<u64> = s
770        .split('.')
771        .map(|a| a.parse().ok())
772        .collect::<Option<Vec<_>>>()?;
773    if arcs.len() < 2 || arcs[0] > 2 || (arcs[0] < 2 && arcs[1] >= 40) || arcs.len() > 32 {
774        return None;
775    }
776    let mut out = Vec::new();
777    let mut push = |mut v: u64| {
778        let mut tmp = vec![(v & 0x7F) as u8];
779        v >>= 7;
780        while v > 0 {
781            tmp.push(((v & 0x7F) as u8) | 0x80);
782            v >>= 7;
783        }
784        tmp.reverse();
785        out.extend(tmp);
786    };
787    push(arcs[0] * 40 + arcs[1]);
788    for a in &arcs[2..] {
789        push(*a);
790    }
791    Some(out)
792}
793
794// ============================================================================
795// Encode
796// ============================================================================
797
798/// Encode `value` as schema type `root`: one complete encoding.
799pub fn encode(root: u16, value: &Value) -> PerResult<Vec<u8>> {
800    let mut w = Writer::default();
801    put(&mut w, root, value, "", 0)?;
802    Ok(w.finish())
803}
804
805fn put(w: &mut Writer, index: u16, v: &Value, path: &str, depth: u32) -> PerResult<()> {
806    if depth > MAX_PER_DEPTH {
807        return err(format!("{path}: nested deeper than {MAX_PER_DEPTH}"));
808    }
809    let what = if path.is_empty() {
810        super::schema::NAMES
811            .get(index as usize)
812            .copied()
813            .unwrap_or("value")
814    } else {
815        path
816    };
817    match ty(index) {
818        Ty::Alias(t) => put(w, *t, v, path, depth + 1),
819        Ty::Null => {
820            if !v.is_null() {
821                return err(format!("{what}: NULL takes null"));
822            }
823            Ok(())
824        }
825        Ty::Boolean => {
826            let b = v
827                .as_bool()
828                .ok_or_else(|| PerError(format!("{what}: expected a boolean")))?;
829            w.bits(b as u64, 1);
830            Ok(())
831        }
832        Ty::Int(range) => put_integer(w, *range, v, what),
833        Ty::Enum { root, ext } => {
834            let name = v
835                .as_str()
836                .ok_or_else(|| PerError(format!("{what}: expected an enumeration name")))?;
837            if let Some(i) = root.iter().position(|n| *n == name) {
838                if ext.is_some() {
839                    w.bits(0, 1);
840                }
841                w.whole(i as u64, root.len() as u64);
842                return Ok(());
843            }
844            if let Some(i) = ext.and_then(|e| e.iter().position(|n| *n == name)) {
845                w.bits(1, 1);
846                return w.normally_small(i as u64, what);
847            }
848            err(format!("{what}: {name:?} is not one of {root:?}"))
849        }
850        Ty::Octets(size) => {
851            let raw = v
852                .as_str()
853                .and_then(|s| hex::decode(s).ok())
854                .ok_or_else(|| PerError(format!("{what}: expected hex octets")))?;
855            w.length(raw.len(), *size, what)?;
856            let fixed = !size.ext && size.ub == Some(size.lb);
857            if raw.is_empty() {
858            } else if fixed && raw.len() <= 2 {
859                w.octets_unaligned(&raw);
860            } else {
861                w.octets(&raw);
862            }
863            Ok(())
864        }
865        Ty::Bits(size) => {
866            let s = v
867                .as_str()
868                .filter(|s| s.chars().all(|c| c == '0' || c == '1'))
869                .ok_or_else(|| PerError(format!("{what}: expected a string of 0 and 1")))?;
870            w.length(s.len(), *size, what)?;
871            let fixed = !size.ext && size.ub == Some(size.lb);
872            if !s.is_empty() && !(fixed && s.len() <= 16) {
873                w.align();
874            }
875            for c in s.chars() {
876                w.bits((c == '1') as u64, 1);
877            }
878            Ok(())
879        }
880        Ty::Chars {
881            kind,
882            size,
883            alphabet: from,
884        } => {
885            let s = v
886                .as_str()
887                .ok_or_else(|| PerError(format!("{what}: expected a string")))?;
888            let (set, b) = alphabet(*kind, *from);
889            let codes: Vec<u32> = s.chars().map(|c| c as u32).collect();
890            for c in &codes {
891                let ok = if set.is_empty() {
892                    *c <= 0xFFFF && !(0xD800..0xE000).contains(c)
893                } else {
894                    set.binary_search(c).is_ok()
895                };
896                if !ok {
897                    return err(format!(
898                        "{what}: {:?} is not permitted in this string",
899                        char::from_u32(*c).unwrap_or('?')
900                    ));
901                }
902            }
903            let n = codes.len();
904            w.length(n, *size, what)?;
905            let fixed = !size.ext && size.ub == Some(size.lb);
906            let aligned = n > 0
907                && match size.ub {
908                    Some(ub) if fixed => ub as usize * b as usize > 16,
909                    Some(ub) => ub as usize * b as usize > 16,
910                    None => true,
911                };
912            if aligned {
913                w.align();
914            }
915            let remap = set.last().is_some_and(|max| *max as u64 > (1u64 << b) - 1);
916            for c in codes {
917                let code = if remap {
918                    set.binary_search(&c).unwrap_or(0) as u64
919                } else {
920                    c as u64
921                };
922                w.bits(code, b);
923            }
924            Ok(())
925        }
926        Ty::Oid => {
927            let raw = v
928                .as_str()
929                .and_then(oid_from_string)
930                .ok_or_else(|| PerError(format!("{what}: expected a dotted OBJECT IDENTIFIER")))?;
931            w.general_length(raw.len(), what)?;
932            w.octets(&raw);
933            Ok(())
934        }
935        Ty::Seq { root, ext } => {
936            let obj = v
937                .as_object()
938                .ok_or_else(|| PerError(format!("{what}: expected an object")))?;
939            for key in obj.keys() {
940                let known = root.iter().any(|f| f.name == key)
941                    || ext.is_some_and(|e| e.iter().any(|f| f.name == key));
942                if !known {
943                    return err(format!("{what}: unknown field {key:?}"));
944                }
945            }
946            let additions: Vec<&Field> = ext
947                .map(|e| e.iter().filter(|f| obj.contains_key(f.name)).collect())
948                .unwrap_or_default();
949            if ext.is_some() {
950                w.bits(!additions.is_empty() as u64, 1);
951            }
952            for f in root.iter().filter(|f| f.optional) {
953                w.bits(obj.contains_key(f.name) as u64, 1);
954            }
955            for f in root.iter() {
956                match obj.get(f.name) {
957                    Some(fv) => put(w, f.ty, fv, &join(path, f.name), depth + 1)?,
958                    None if f.optional => {}
959                    None => return err(format!("{what}: missing field {:?}", f.name)),
960                }
961            }
962            if let (Some(all), false) = (ext, additions.is_empty()) {
963                // The bitmap runs to the last addition present (X.691 19.8 allows any length
964                // covering the present ones; a receiver of an older version ignores the rest).
965                let last = all
966                    .iter()
967                    .rposition(|f| obj.contains_key(f.name))
968                    .unwrap_or(0);
969                w.normally_small(last as u64, what)?;
970                for f in &all[..=last] {
971                    w.bits(obj.contains_key(f.name) as u64, 1);
972                }
973                for f in &all[..=last] {
974                    if let Some(fv) = obj.get(f.name) {
975                        let mut inner = Writer::default();
976                        put(&mut inner, f.ty, fv, &join(path, f.name), depth + 1)?;
977                        w.open_type(&inner.finish(), what)?;
978                    }
979                }
980            }
981            Ok(())
982        }
983        Ty::Choice { root, ext } => {
984            let obj = v
985                .as_object()
986                .filter(|o| o.len() == 1)
987                .ok_or_else(|| PerError(format!("{what}: a CHOICE is an object with one key")))?;
988            let (key, fv) = obj.iter().next().expect("one key");
989            if let Some(i) = root.iter().position(|f| f.name == key) {
990                if ext.is_some() {
991                    w.bits(0, 1);
992                }
993                w.whole(i as u64, root.len() as u64);
994                return put(w, root[i].ty, fv, &join(path, key), depth + 1);
995            }
996            if let Some(i) = ext.and_then(|e| e.iter().position(|f| f.name == key)) {
997                let f = &ext.expect("checked")[i];
998                w.bits(1, 1);
999                w.normally_small(i as u64, what)?;
1000                let mut inner = Writer::default();
1001                put(&mut inner, f.ty, fv, &join(path, key), depth + 1)?;
1002                return w.open_type(&inner.finish(), what);
1003            }
1004            err(format!("{what}: {key:?} is not an alternative"))
1005        }
1006        Ty::SeqOf { size, item } => {
1007            let arr = v
1008                .as_array()
1009                .ok_or_else(|| PerError(format!("{what}: expected an array")))?;
1010            w.length(arr.len(), *size, what)?;
1011            for (i, e) in arr.iter().enumerate() {
1012                put(w, *item, e, &format!("{path}[{i}]"), depth + 1)?;
1013            }
1014            Ok(())
1015        }
1016        Ty::Opaque(why) => err(format!("{what}: NetGet cannot encode {why}")),
1017    }
1018}
1019
1020fn put_integer(w: &mut Writer, range: Range, v: &Value, what: &str) -> PerResult<()> {
1021    let n = v
1022        .as_i64()
1023        .ok_or_else(|| PerError(format!("{what}: expected an integer")))?;
1024    let in_root = range.lb.is_none_or(|lb| n >= lb) && range.ub.is_none_or(|ub| n <= ub);
1025    if !in_root {
1026        if !range.ext {
1027            return err(format!(
1028                "{what}: {n} outside {:?}..{:?}",
1029                range.lb, range.ub
1030            ));
1031        }
1032        w.bits(1, 1);
1033        return put_unconstrained(w, n, what);
1034    }
1035    if range.ext {
1036        w.bits(0, 1);
1037    }
1038    match (range.lb, range.ub) {
1039        (Some(lb), Some(ub)) => {
1040            w.whole((n - lb) as u64, (ub - lb) as u64 + 1);
1041            Ok(())
1042        }
1043        (Some(lb), None) => {
1044            let v = (n - lb) as u64;
1045            let len = octets_for(v);
1046            w.general_length(len, what)?;
1047            let bytes = v.to_be_bytes();
1048            w.octets(&bytes[8 - len..]);
1049            Ok(())
1050        }
1051        _ => put_unconstrained(w, n, what),
1052    }
1053}
1054
1055fn put_unconstrained(w: &mut Writer, n: i64, what: &str) -> PerResult<()> {
1056    let bytes = n.to_be_bytes();
1057    let mut start = 0;
1058    while start < 7 {
1059        let redundant = (bytes[start] == 0x00 && bytes[start + 1] & 0x80 == 0)
1060            || (bytes[start] == 0xFF && bytes[start + 1] & 0x80 != 0);
1061        if !redundant {
1062            break;
1063        }
1064        start += 1;
1065    }
1066    w.general_length(8 - start, what)?;
1067    w.octets(&bytes[start..]);
1068    Ok(())
1069}