Skip to main content

brooks_lib/verifying/original/
verify.rs

1// brooks, Copyright 2026, Will Hawkins
2//
3// This file is part of brooks.
4
5// This file is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <https://www.gnu.org/licenses/>.
17
18//! All Metadata Information except for what is defined in Processing Stages.
19
20use crate::{
21    cdni::{
22        gmd::spec::TypedGenericMetadata,
23        original::spec::{SourceMetadata, TypedSourceMetadata},
24    },
25    verifying::verify::{
26        AnalysisResult, CdniVerificationKey, MetadataAnalysisContext, MetadataVerificationError,
27        Verifier,
28    },
29};
30use std::{
31    fmt::{Debug, Display},
32    net::SocketAddr,
33    str::FromStr,
34};
35
36#[derive(Debug, Clone, Default)]
37pub struct SourceVerificationKey {
38    pub sources: Vec<(Vec<SocketAddr>, SourceProtocol)>,
39}
40
41#[derive(Debug, Clone, Default)]
42pub enum SourceProtocol {
43    #[default]
44    Http11,
45}
46
47impl Display for SourceProtocol {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        match self {
50            SourceProtocol::Http11 => write!(f, "Http1/1"),
51        }
52    }
53}
54
55impl FromStr for SourceProtocol {
56    type Err = MetadataVerificationError;
57
58    fn from_str(s: &str) -> Result<Self, Self::Err> {
59        if s == "http/1.1" {
60            return Ok(SourceProtocol::Http11);
61        }
62        Err(MetadataVerificationError::JsonError(format!(
63            "{} is not a valid protocol for Source MI",
64            s
65        )))
66    }
67}
68
69fn analyze_source_metadata(
70    to_verify: TypedSourceMetadata<()>,
71    input: MetadataAnalysisContext,
72) -> AnalysisResult<MetadataAnalysisContext, MetadataVerificationError> {
73    let mut sources: Vec<(Vec<SocketAddr>, SourceProtocol)> = vec![];
74
75    for s in &to_verify.value.sources {
76        let typed_protocol: SourceProtocol = s.protocol.parse()?;
77
78        let mut typed_endpoints: Vec<SocketAddr> = vec![];
79        for ep in &s.endpoints {
80            typed_endpoints.push(ep.parse().map_err(|_| {
81                MetadataVerificationError::JsonError(format!(
82                    "{ep} is not a valid IP/Port combination"
83                ))
84            })?);
85        }
86
87        sources.push((typed_endpoints, typed_protocol));
88    }
89
90    let res = TypedSourceMetadata {
91        tpe: TypedSourceMetadata::<()>::typed_cdni_metadata_name(),
92        value: SourceMetadata::<SourceVerificationKey> {
93            sources: to_verify.value.sources,
94            aug: SourceVerificationKey { sources },
95        },
96    };
97
98    Ok((input, CdniVerificationKey::Source(Box::new(res))))
99}
100#[derive(Debug)]
101pub struct SourceMetadataAnalyzer {}
102
103impl Verifier<MetadataAnalysisContext, MetadataVerificationError> for SourceMetadataAnalyzer {
104    fn analyze(
105        &self,
106        v: &TypedGenericMetadata<()>,
107        input: MetadataAnalysisContext,
108    ) -> AnalysisResult<MetadataAnalysisContext, MetadataVerificationError> {
109        let typed_source = serde_json::from_value::<TypedSourceMetadata<()>>(
110            serde_json::to_value(v)
111                .map_err(|e| MetadataVerificationError::JsonError(e.to_string()))?,
112        )
113        .map_err(|e| MetadataVerificationError::JsonError(e.to_string()))?;
114
115        analyze_source_metadata(typed_source, input)
116    }
117
118    fn type_name(&self, candidate: &str) -> bool {
119        candidate == TypedSourceMetadata::<()>::typed_cdni_metadata_name()
120    }
121}
122
123#[cfg(test)]
124mod test_verify_source_metadata {
125    use std::{assert_matches, path::Path};
126
127    use crate::{
128        cdni::original::spec::{SourceMetadata, TypedSourceMetadata},
129        tests::read_test_file,
130        verifying::{
131            original::verify::analyze_source_metadata,
132            verify::{CdniVerificationKey, MetadataAnalysisContext},
133        },
134    };
135
136    #[test]
137    fn test_verify_8006_example() {
138        let json = read_test_file(Path::new(
139            "./src/cdni/tests/source/modified-8006-example.json",
140        ));
141        let cop_metadata = serde_json::from_str::<TypedSourceMetadata<()>>(&json)
142            .expect("Could not parse JSON test file");
143        assert_eq!(
144            cop_metadata.tpe,
145            TypedSourceMetadata::<()>::typed_cdni_metadata_name()
146        );
147
148        let (_, result) = analyze_source_metadata(cop_metadata, MetadataAnalysisContext::default())
149            .expect("Could not verify valid CDNI Source Metadata");
150
151        let result = match result {
152            CdniVerificationKey::Source(source) => source,
153            _ => panic!("Incorrect CDNI Verification Key type returned by analyze_source_metadata"),
154        };
155
156        assert_matches!(
157            *result,
158            TypedSourceMetadata {
159                tpe: _,
160                value: SourceMetadata { sources, aug: _ }
161            }
162            if sources.len() == 2 &&
163                sources[0].endpoints.len() == 2 &&
164                sources[1].endpoints.len() == 1
165        )
166    }
167}