asnfuzzgen/parser/asn/
oid.rs

1//! Parsing functionality related to Object Identifier
2
3use std::collections::HashMap;
4
5use lazy_static::lazy_static;
6
7use crate::error::Error;
8use crate::tokenizer::Token;
9
10use crate::parser::utils::{expect_one_of_tokens, expect_token, expect_tokens};
11
12use super::structs::oid::{OIDComponent, ObjectIdentifier};
13
14lazy_static! {
15    pub(crate) static ref WELL_KNOWN_OID_NAMES: HashMap<&'static str, u32> = {
16        let mut m = HashMap::new();
17        m.insert("iso", 1);
18        m.insert("itu-t", 0);
19        m.insert("ccitt", 0);
20        m.insert("joint-iso-itu-t", 2);
21        m.insert("joint-iso-ccitt", 2);
22        // FIXME: We use the itu-t identified-organization (iso standard-orgainization is 3)
23        m.insert("identified-organization", 4);
24        m.insert("network-operator", 3);
25        m.insert("administration", 2);
26        m.insert("question", 1);
27        m
28    };
29}
30
31// Parses a named OID component
32//
33// Parses named OID components of the form `iso` or `iso(1)`
34fn parse_named_oid_component(tokens: &[Token]) -> Result<(OIDComponent, usize), Error> {
35    if !expect_token(tokens, Token::is_value_reference)? {
36        return Err(unexpected_token!("'IDENTIFIER'", tokens[0]));
37    }
38    let name_token = &tokens[0];
39    let name = &name_token.text;
40    let (number, consumed) = match expect_tokens(
41        &tokens[1..],
42        &[
43            &[Token::is_round_begin],
44            &[Token::is_numeric],
45            &[Token::is_round_end],
46        ],
47    ) {
48        Ok(success) => {
49            if success {
50                let number_token = &tokens[2];
51                let number = number_token
52                    .text
53                    .parse::<u32>()
54                    .map_err(|_| invalid_token!(number_token))?;
55                (number, 4)
56            } else {
57                let number = WELL_KNOWN_OID_NAMES.get(name.as_str());
58                if number.is_none() {
59                    return Err(unknown_oid_name!(name_token));
60                }
61                (*number.unwrap(), 1)
62            }
63        }
64        Err(_) => {
65            let number = WELL_KNOWN_OID_NAMES.get(name.as_str());
66            if number.is_none() {
67                return Err(unknown_oid_name!(name_token));
68            }
69            (*number.unwrap(), 1)
70        }
71    };
72
73    Ok((OIDComponent::new(Some(name.clone()), number), consumed))
74}
75
76// Wrapper for Parsing an OID Component
77//
78// Parses Either Numbered or Named/Numbered OID components
79fn parse_oid_component(tokens: &[Token]) -> Result<(OIDComponent, usize), Error> {
80    let consumed = 0;
81
82    if expect_one_of_tokens(
83        &tokens[consumed..],
84        &[Token::is_identifier, Token::is_numeric],
85    )? {
86        let first = &tokens[0];
87        if first.is_identifier() {
88            parse_named_oid_component(tokens)
89        } else {
90            let number = first
91                .text
92                .parse::<u32>()
93                .map_err(|_| invalid_token!(first))?;
94            Ok((OIDComponent::new(None, number), 1))
95        }
96    } else {
97        Err(unexpected_token!(
98            "Expected 'identifier' or 'number'",
99            tokens[0]
100        ))
101    }
102}
103
104// This is required by 'resolver' to resolve object identifier values (which requires the value
105// which is basically just a string of the form '{ iso ... }' to be parsed (and resolved) there.
106// Hence this module is `pub(crate)`.
107pub(crate) fn parse_object_identifier(
108    tokens: &[Token],
109) -> Result<(ObjectIdentifier, usize), Error> {
110    let mut consumed = 0;
111
112    if !expect_token(&tokens[consumed..], Token::is_curly_begin)? {
113        return Err(unexpected_token!("{", tokens[consumed]));
114    }
115    consumed += 1;
116
117    let mut components = vec![];
118    while !expect_token(&tokens[consumed..], Token::is_curly_end)? {
119        let (component, component_consumed) = parse_oid_component(&tokens[consumed..])?;
120        components.push(component);
121        consumed += component_consumed;
122    }
123    consumed += 1;
124
125    // FIXME: OID with empty components is an Error?
126
127    Ok((ObjectIdentifier::new(components), consumed))
128}
129
130#[cfg(test)]
131mod tests {
132
133    use super::*;
134    use crate::tokenizer::tokenize;
135
136    struct OIDTestCase<'testcase> {
137        input: &'testcase str,
138        success: bool,
139        consumed: usize,
140        parse_component_only: bool,
141        components_count: usize,
142    }
143
144    #[test]
145    fn object_identifier_cases() {
146        let test_cases = vec![
147            OIDTestCase {
148                input: " iso ",
149                success: true,
150                consumed: 1,
151                parse_component_only: true,
152                components_count: 0,
153            },
154            OIDTestCase {
155                input: " foo ",
156                success: false,
157                consumed: 1,
158                parse_component_only: true,
159                components_count: 0,
160            },
161            OIDTestCase {
162                input: " something(3) ",
163                success: true,
164                consumed: 4,
165                parse_component_only: true,
166                components_count: 0,
167            },
168            OIDTestCase {
169                input: " something(-3) ",
170                success: false,
171                consumed: 4,
172                parse_component_only: true,
173                components_count: 0,
174            },
175            OIDTestCase {
176                input: " iso() ",
177                success: true,
178                consumed: 1,
179                parse_component_only: true,
180                components_count: 0,
181            },
182            OIDTestCase {
183                input: "{ iso() }",
184                success: false,
185                consumed: 4,
186                parse_component_only: false,
187                components_count: 0,
188            },
189            OIDTestCase {
190                input: "{ iso(1 }",
191                success: false,
192                consumed: 4,
193                parse_component_only: false,
194                components_count: 0,
195            },
196            OIDTestCase {
197                input: " { iso something(3) }",
198                success: true,
199                consumed: 7,
200                parse_component_only: false,
201                components_count: 2,
202            },
203            OIDTestCase {
204                input: "{ iso }",
205                success: true,
206                consumed: 3,
207                parse_component_only: false,
208                components_count: 1,
209            },
210        ];
211
212        for tc in test_cases {
213            let reader = std::io::BufReader::new(std::io::Cursor::new(tc.input));
214            let tokens = tokenize(reader);
215            assert!(tokens.is_ok());
216
217            let tokens = tokens.unwrap();
218            if tc.parse_component_only {
219                let oidcomp = parse_oid_component(&tokens);
220                assert_eq!(oidcomp.is_ok(), tc.success, "{:#?}", tc.input);
221                if tc.success {
222                    let (_oidcomp, consumed) = oidcomp.unwrap();
223                    assert_eq!(consumed, tc.consumed, "{:#?}", tc.input);
224                }
225            } else {
226                let oid = parse_object_identifier(&tokens);
227                assert_eq!(oid.is_ok(), tc.success, "{:#?}", tc.input);
228                if tc.success {
229                    let (oid, consumed) = oid.unwrap();
230                    assert_eq!(oid.len(), tc.components_count);
231                    assert_eq!(consumed, tc.consumed);
232                }
233            }
234        }
235    }
236}