1use ipnet::IpNet;
18use regex::Regex;
19use std::{
20 collections::HashSet,
21 error::Error,
22 fmt::{self, Debug, Display, Formatter},
23 net::{IpAddr, Ipv4Addr, Ipv6Addr},
24 str::FromStr,
25 sync::Arc,
26 time::Duration,
27};
28use syslog::Facility;
29use viadkim::{
30 crypto::{HashAlgorithm, SigningKey},
31 header::{FieldName, HeaderFieldError},
32 signature::{Canonicalization, CanonicalizationAlgorithm, DomainName, Selector},
33 signer,
34};
35
36#[derive(Clone, Debug, Default)]
40pub struct ConfigOverrides {
41 pub signing_config: PartialSigningConfig,
42 pub verification_config: PartialVerificationConfig,
43}
44
45impl ConfigOverrides {
46 pub fn merge(&mut self, other: &Self) {
47 self.signing_config.merge(&other.signing_config);
48 self.verification_config.merge(&other.verification_config);
49 }
50}
51
52#[derive(Clone, Debug, PartialEq)]
53pub struct SigningConfig {
54 pub ascii_only_signatures: bool,
55 pub canonicalization: Canonicalization,
56 pub copy_headers: bool,
57 pub default_signed_headers: Vec<SignedFieldName>, pub default_unsigned_headers: Vec<SignedFieldName>, pub expiration: Expiration,
60 pub hash_algorithm: HashAlgorithm,
61 pub limit_body_length: bool,
62 pub oversign_headers: OversignedHeaders,
63 pub request_reports: bool,
64 pub sign_headers: SignedHeaders,
65}
66
67impl SigningConfig {
68 fn check_invariants(&self) -> Result<(), Box<dyn Error>> {
69 match (&self.oversign_headers, &self.sign_headers) {
70 (
71 OversignedHeaders::Pick(oversigned_names),
72 s @ (SignedHeaders::Pick(names) | SignedHeaders::PickWithDefault(names)),
73 ) => {
74 let mut all_signed: HashSet<_> = names.iter().collect();
75 if matches!(s, SignedHeaders::PickWithDefault(_)) {
76 all_signed.extend(self.default_signed_headers.iter());
77 }
78 for h in oversigned_names {
79 if !all_signed.contains(h) {
80 return Err("cannot oversign header not included for signing".into());
81 }
82 }
83 }
84 (OversignedHeaders::Pick(oversigned_names), SignedHeaders::All) => {
85 for h in oversigned_names {
86 if self.default_unsigned_headers.contains(h) {
87 return Err("cannot oversign header expressly excluded from signing".into());
88 }
89 }
90 }
91 (OversignedHeaders::Extended, SignedHeaders::All) => {
92 for h in &self.default_signed_headers {
93 if self.default_unsigned_headers.contains(h) {
94 return Err("cannot oversign header expressly excluded from signing".into());
95 }
96 }
97 }
98 _ => {}
99 }
100 Ok(())
101 }
102
103 pub fn merged_with(&self, overrides: &PartialSigningConfig) -> Result<Self, Box<dyn Error>> {
104 let mut config = self.clone();
105
106 if let Some(value) = overrides.ascii_only_signatures {
107 config.ascii_only_signatures = value;
108 }
109 if let Some(value) = overrides.canonicalization {
110 config.canonicalization = value;
111 }
112 if let Some(value) = overrides.copy_headers {
113 config.copy_headers = value;
114 }
115 if let Some(value) = &overrides.default_signed_headers {
116 config.default_signed_headers = value.as_ref().clone();
117 }
118 if let Some(value) = &overrides.default_unsigned_headers {
119 config.default_unsigned_headers = value.as_ref().clone();
120 }
121 if let Some(value) = overrides.expiration {
122 config.expiration = value;
123 }
124 if let Some(value) = overrides.hash_algorithm {
125 config.hash_algorithm = value;
126 }
127 if let Some(value) = overrides.limit_body_length {
128 config.limit_body_length = value;
129 }
130 if let Some(value) = &overrides.oversign_headers {
131 config.oversign_headers = value.as_ref().clone();
132 }
133 if let Some(value) = overrides.request_reports {
134 config.request_reports = value;
135 }
136 if let Some(value) = &overrides.sign_headers {
137 config.sign_headers = value.as_ref().clone();
138 }
139
140 config.check_invariants()?;
141
142 Ok(config)
143 }
144}
145
146impl Default for SigningConfig {
147 fn default() -> Self {
148 use CanonicalizationAlgorithm::*;
149 Self {
150 ascii_only_signatures: false,
151 canonicalization: Canonicalization::from((Relaxed, Simple)),
152 copy_headers: false,
153 default_signed_headers: signer::default_signed_headers().into_iter()
154 .map(SignedFieldName)
155 .collect(),
156 default_unsigned_headers: signer::default_unsigned_headers().into_iter()
157 .map(SignedFieldName)
158 .collect(),
159 expiration: Expiration::After(Duration::from_secs(60 * 60 * 24 * 5)), hash_algorithm: HashAlgorithm::Sha256,
161 limit_body_length: false,
162 oversign_headers: OversignedHeaders::Pick(Default::default()),
163 request_reports: false,
164 sign_headers: SignedHeaders::PickWithDefault(Default::default()),
165 }
166 }
167}
168
169#[derive(Clone, Debug, Default, PartialEq)]
170pub struct PartialSigningConfig {
171 pub ascii_only_signatures: Option<bool>,
172 pub canonicalization: Option<Canonicalization>,
173 pub copy_headers: Option<bool>,
174 pub default_signed_headers: Option<Arc<Vec<SignedFieldName>>>,
175 pub default_unsigned_headers: Option<Arc<Vec<SignedFieldName>>>,
176 pub expiration: Option<Expiration>,
177 pub hash_algorithm: Option<HashAlgorithm>,
178 pub limit_body_length: Option<bool>,
179 pub oversign_headers: Option<Arc<OversignedHeaders>>,
180 pub request_reports: Option<bool>,
181 pub sign_headers: Option<Arc<SignedHeaders>>,
182}
183
184impl PartialSigningConfig {
185 pub fn merged_with(&self, overrides: &Self) -> Self {
186 Self {
187 ascii_only_signatures: overrides.ascii_only_signatures.or(self.ascii_only_signatures),
188 canonicalization: overrides.canonicalization.or(self.canonicalization),
189 copy_headers: overrides.copy_headers.or(self.copy_headers),
190 default_signed_headers: overrides.default_signed_headers.as_ref()
191 .or(self.default_signed_headers.as_ref())
192 .cloned(),
193 default_unsigned_headers: overrides.default_unsigned_headers.as_ref()
194 .or(self.default_unsigned_headers.as_ref())
195 .cloned(),
196 expiration: overrides.expiration.or(self.expiration),
197 hash_algorithm: overrides.hash_algorithm.or(self.hash_algorithm),
198 limit_body_length: overrides.limit_body_length.or(self.limit_body_length),
199 oversign_headers: overrides.oversign_headers.as_ref()
200 .or(self.oversign_headers.as_ref())
201 .cloned(),
202 request_reports: overrides.request_reports.or(self.request_reports),
203 sign_headers: overrides.sign_headers.as_ref()
204 .or(self.sign_headers.as_ref())
205 .cloned(),
206 }
207 }
208
209 pub fn merge(&mut self, other: &Self) {
210 if let Some(value) = other.ascii_only_signatures {
211 self.ascii_only_signatures = Some(value);
212 }
213 if let Some(value) = other.canonicalization {
214 self.canonicalization = Some(value);
215 }
216 if let Some(value) = other.copy_headers {
217 self.copy_headers = Some(value);
218 }
219 if let Some(value) = &other.default_signed_headers {
220 self.default_signed_headers = Some(value.clone());
221 }
222 if let Some(value) = &other.default_unsigned_headers {
223 self.default_unsigned_headers = Some(value.clone());
224 }
225 if let Some(value) = other.expiration {
226 self.expiration = Some(value);
227 }
228 if let Some(value) = other.hash_algorithm {
229 self.hash_algorithm = Some(value);
230 }
231 if let Some(value) = other.limit_body_length {
232 self.limit_body_length = Some(value);
233 }
234 if let Some(value) = &other.oversign_headers {
235 self.oversign_headers = Some(value.clone());
236 }
237 if let Some(value) = other.request_reports {
238 self.request_reports = Some(value);
239 }
240 if let Some(value) = &other.sign_headers {
241 self.sign_headers = Some(value.clone());
242 }
243 }
244
245 pub fn into_signing_config(self) -> Result<SigningConfig, Box<dyn Error>> {
246 let mut config = SigningConfig::default();
247
248 if let Some(value) = self.ascii_only_signatures {
249 config.ascii_only_signatures = value;
250 }
251 if let Some(value) = self.canonicalization {
252 config.canonicalization = value;
253 }
254 if let Some(value) = self.copy_headers {
255 config.copy_headers = value;
256 }
257 if let Some(value) = self.default_signed_headers {
258 config.default_signed_headers = unwrap_arc(value);
259 }
260 if let Some(value) = self.default_unsigned_headers {
261 config.default_unsigned_headers = unwrap_arc(value);
262 }
263 if let Some(value) = self.expiration {
264 config.expiration = value;
265 }
266 if let Some(value) = self.hash_algorithm {
267 config.hash_algorithm = value;
268 }
269 if let Some(value) = self.limit_body_length {
270 config.limit_body_length = value;
271 }
272 if let Some(value) = self.oversign_headers {
273 config.oversign_headers = unwrap_arc(value);
274 }
275 if let Some(value) = self.request_reports {
276 config.request_reports = value;
277 }
278 if let Some(value) = self.sign_headers {
279 config.sign_headers = unwrap_arc(value);
280 }
281
282 config.check_invariants()?;
283
284 Ok(config)
285 }
286}
287
288#[derive(Clone, Debug, PartialEq)]
289pub struct VerificationConfig {
290 pub allow_expired: bool,
291 pub allow_sha1: bool,
292 pub allow_timestamp_in_future: bool,
293 pub forbid_unsigned_content: bool,
294 pub max_signatures_to_verify: usize,
295 pub min_rsa_key_bits: usize,
296 pub reject_failures: RejectFailures,
297 pub required_signed_headers: Vec<SignedFieldNameWithQualifier>,
298 pub time_tolerance: Duration,
299}
300
301impl VerificationConfig {
302 pub fn merged_with(&self, overrides: &PartialVerificationConfig) -> Self {
303 let mut config = self.clone();
304
305 if let Some(value) = overrides.allow_expired {
306 config.allow_expired = value;
307 }
308 if let Some(value) = overrides.allow_sha1 {
309 config.allow_sha1 = value;
310 }
311 if let Some(value) = overrides.allow_timestamp_in_future {
312 config.allow_timestamp_in_future = value;
313 }
314 if let Some(value) = overrides.forbid_unsigned_content {
315 config.forbid_unsigned_content = value;
316 }
317 if let Some(value) = overrides.max_signatures_to_verify {
318 config.max_signatures_to_verify = value;
319 }
320 if let Some(value) = overrides.min_rsa_key_bits {
321 config.min_rsa_key_bits = value;
322 }
323 if let Some(value) = &overrides.reject_failures {
324 config.reject_failures = value.as_ref().clone();
325 }
326 if let Some(value) = &overrides.required_signed_headers {
327 config.required_signed_headers = value.as_ref().clone();
328 }
329 if let Some(value) = overrides.time_tolerance {
330 config.time_tolerance = value;
331 }
332
333 config
334 }
335}
336
337impl Default for VerificationConfig {
338 fn default() -> Self {
339 Self {
340 allow_expired: false,
341 allow_sha1: false,
342 allow_timestamp_in_future: false,
343 forbid_unsigned_content: false,
344 max_signatures_to_verify: 10,
345 min_rsa_key_bits: 1024,
346 reject_failures: Default::default(),
347 required_signed_headers: vec![SignedFieldNameWithQualifier::Asterisk(
348 SignedFieldName::new("From").unwrap(),
349 )],
350 time_tolerance: Duration::from_secs(5 * 60),
351 }
352 }
353}
354
355#[derive(Clone, Debug, Default, PartialEq)]
356pub struct PartialVerificationConfig {
357 pub allow_expired: Option<bool>,
358 pub allow_sha1: Option<bool>,
359 pub allow_timestamp_in_future: Option<bool>,
360 pub forbid_unsigned_content: Option<bool>,
361 pub max_signatures_to_verify: Option<usize>,
362 pub min_rsa_key_bits: Option<usize>,
363 pub reject_failures: Option<Arc<RejectFailures>>,
364 pub required_signed_headers: Option<Arc<Vec<SignedFieldNameWithQualifier>>>,
365 pub time_tolerance: Option<Duration>,
366}
367
368impl PartialVerificationConfig {
369 pub fn merged_with(&self, overrides: &Self) -> Self {
370 Self {
371 allow_expired: overrides.allow_expired.or(self.allow_expired),
372 allow_sha1: overrides.allow_sha1.or(self.allow_sha1),
373 allow_timestamp_in_future: overrides.allow_timestamp_in_future.or(self.allow_timestamp_in_future),
374 forbid_unsigned_content: overrides.forbid_unsigned_content.or(self.forbid_unsigned_content),
375 max_signatures_to_verify: overrides.max_signatures_to_verify.or(self.max_signatures_to_verify),
376 min_rsa_key_bits: overrides.min_rsa_key_bits.or(self.min_rsa_key_bits),
377 reject_failures: overrides.reject_failures.as_ref()
378 .or(self.reject_failures.as_ref())
379 .cloned(),
380 required_signed_headers: overrides.required_signed_headers.as_ref()
381 .or(self.required_signed_headers.as_ref())
382 .cloned(),
383 time_tolerance: overrides.time_tolerance.or(self.time_tolerance),
384 }
385 }
386
387 pub fn merge(&mut self, other: &Self) {
388 if let Some(value) = other.allow_expired {
389 self.allow_expired = Some(value);
390 }
391 if let Some(value) = other.allow_sha1 {
392 self.allow_sha1 = Some(value);
393 }
394 if let Some(value) = other.allow_timestamp_in_future {
395 self.allow_timestamp_in_future = Some(value);
396 }
397 if let Some(value) = other.forbid_unsigned_content {
398 self.forbid_unsigned_content = Some(value);
399 }
400 if let Some(value) = other.max_signatures_to_verify {
401 self.max_signatures_to_verify = Some(value);
402 }
403 if let Some(value) = other.min_rsa_key_bits {
404 self.min_rsa_key_bits = Some(value);
405 }
406 if let Some(value) = &other.reject_failures {
407 self.reject_failures = Some(value.clone());
408 }
409 if let Some(value) = &other.required_signed_headers {
410 self.required_signed_headers = Some(value.clone());
411 }
412 if let Some(value) = other.time_tolerance {
413 self.time_tolerance = Some(value);
414 }
415 }
416
417 pub fn into_verification_config(self) -> VerificationConfig {
418 let mut config = VerificationConfig::default();
419
420 if let Some(value) = self.allow_expired {
421 config.allow_expired = value;
422 }
423 if let Some(value) = self.allow_sha1 {
424 config.allow_sha1 = value;
425 }
426 if let Some(value) = self.allow_timestamp_in_future {
427 config.allow_timestamp_in_future = value;
428 }
429 if let Some(value) = self.forbid_unsigned_content {
430 config.forbid_unsigned_content = value;
431 }
432 if let Some(value) = self.max_signatures_to_verify {
433 config.max_signatures_to_verify = value;
434 }
435 if let Some(value) = self.min_rsa_key_bits {
436 config.min_rsa_key_bits = value;
437 }
438 if let Some(value) = self.reject_failures {
439 config.reject_failures = unwrap_arc(value);
440 }
441 if let Some(value) = self.required_signed_headers {
442 config.required_signed_headers = unwrap_arc(value);
443 }
444 if let Some(value) = self.time_tolerance {
445 config.time_tolerance = value;
446 }
447
448 config
449 }
450}
451
452fn unwrap_arc<T: Clone>(arc: Arc<T>) -> T {
453 Arc::try_unwrap(arc).unwrap_or_else(|a| a.as_ref().clone())
454}
455
456#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
457pub struct ParseLogDestinationError;
458
459impl Error for ParseLogDestinationError {}
460
461impl Display for ParseLogDestinationError {
462 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
463 write!(f, "failed to parse log destination")
464 }
465}
466
467#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
468pub enum LogDestination {
469 #[default]
470 Syslog,
471 Stderr,
472}
473
474impl FromStr for LogDestination {
475 type Err = ParseLogDestinationError;
476
477 fn from_str(s: &str) -> Result<Self, Self::Err> {
478 match s {
479 "syslog" => Ok(Self::Syslog),
480 "stderr" => Ok(Self::Stderr),
481 _ => Err(ParseLogDestinationError),
482 }
483 }
484}
485
486#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
487pub struct ParseLogLevelError;
488
489impl Error for ParseLogLevelError {}
490
491impl Display for ParseLogLevelError {
492 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
493 write!(f, "failed to parse log level")
494 }
495}
496
497#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
498pub enum LogLevel {
499 Error,
500 Warn,
501 #[default]
502 Info,
503 Debug,
504}
505
506impl FromStr for LogLevel {
507 type Err = ParseLogLevelError;
508
509 fn from_str(s: &str) -> Result<Self, Self::Err> {
510 match s {
511 "error" => Ok(Self::Error),
512 "warn" => Ok(Self::Warn),
513 "info" => Ok(Self::Info),
514 "debug" => Ok(Self::Debug),
515 _ => Err(ParseLogLevelError),
516 }
517 }
518}
519
520#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
522pub struct ParseSyslogFacilityError;
523
524impl Error for ParseSyslogFacilityError {}
525
526impl Display for ParseSyslogFacilityError {
527 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
528 write!(f, "failed to parse syslog facility")
529 }
530}
531
532#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
534pub enum SyslogFacility {
535 Auth,
536 Authpriv,
537 Cron,
538 Daemon,
539 Ftp,
540 Kern,
541 Local0,
542 Local1,
543 Local2,
544 Local3,
545 Local4,
546 Local5,
547 Local6,
548 Local7,
549 Lpr,
550 #[default]
551 Mail,
552 News,
553 Syslog,
554 User,
555 Uucp,
556}
557
558impl FromStr for SyslogFacility {
559 type Err = ParseSyslogFacilityError;
560
561 fn from_str(s: &str) -> Result<Self, Self::Err> {
562 match s {
563 "auth" => Ok(Self::Auth),
564 "authpriv" => Ok(Self::Authpriv),
565 "cron" => Ok(Self::Cron),
566 "daemon" => Ok(Self::Daemon),
567 "ftp" => Ok(Self::Ftp),
568 "kern" => Ok(Self::Kern),
569 "local0" => Ok(Self::Local0),
570 "local1" => Ok(Self::Local1),
571 "local2" => Ok(Self::Local2),
572 "local3" => Ok(Self::Local3),
573 "local4" => Ok(Self::Local4),
574 "local5" => Ok(Self::Local5),
575 "local6" => Ok(Self::Local6),
576 "local7" => Ok(Self::Local7),
577 "lpr" => Ok(Self::Lpr),
578 "mail" => Ok(Self::Mail),
579 "news" => Ok(Self::News),
580 "syslog" => Ok(Self::Syslog),
581 "user" => Ok(Self::User),
582 "uucp" => Ok(Self::Uucp),
583 _ => Err(ParseSyslogFacilityError),
584 }
585 }
586}
587
588impl From<SyslogFacility> for Facility {
589 fn from(syslog_facility: SyslogFacility) -> Self {
590 match syslog_facility {
591 SyslogFacility::Auth => Self::LOG_AUTH,
592 SyslogFacility::Authpriv => Self::LOG_AUTHPRIV,
593 SyslogFacility::Cron => Self::LOG_CRON,
594 SyslogFacility::Daemon => Self::LOG_DAEMON,
595 SyslogFacility::Ftp => Self::LOG_FTP,
596 SyslogFacility::Kern => Self::LOG_KERN,
597 SyslogFacility::Local0 => Self::LOG_LOCAL0,
598 SyslogFacility::Local1 => Self::LOG_LOCAL1,
599 SyslogFacility::Local2 => Self::LOG_LOCAL2,
600 SyslogFacility::Local3 => Self::LOG_LOCAL3,
601 SyslogFacility::Local4 => Self::LOG_LOCAL4,
602 SyslogFacility::Local5 => Self::LOG_LOCAL5,
603 SyslogFacility::Local6 => Self::LOG_LOCAL6,
604 SyslogFacility::Local7 => Self::LOG_LOCAL7,
605 SyslogFacility::Lpr => Self::LOG_LPR,
606 SyslogFacility::Mail => Self::LOG_MAIL,
607 SyslogFacility::News => Self::LOG_NEWS,
608 SyslogFacility::Syslog => Self::LOG_SYSLOG,
609 SyslogFacility::User => Self::LOG_USER,
610 SyslogFacility::Uucp => Self::LOG_UUCP,
611 }
612 }
613}
614
615#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
616pub struct ParseSocketError;
617
618impl Error for ParseSocketError {}
619
620impl Display for ParseSocketError {
621 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
622 write!(f, "failed to parse socket")
623 }
624}
625
626#[derive(Clone, Debug, Eq, Hash, PartialEq)]
627pub enum Socket {
628 Inet(String),
629 Unix(String),
630}
631
632impl Display for Socket {
633 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
634 match self {
635 Self::Inet(s) => write!(f, "inet:{s}"),
636 Self::Unix(s) => write!(f, "unix:{s}"),
637 }
638 }
639}
640
641impl FromStr for Socket {
642 type Err = ParseSocketError;
643
644 fn from_str(s: &str) -> Result<Self, Self::Err> {
645 if let Some(s) = s.strip_prefix("inet:") {
646 Ok(Self::Inet(s.into()))
647 } else if let Some(s) = s.strip_prefix("unix:") {
648 Ok(Self::Unix(s.into()))
649 } else {
650 Err(ParseSocketError)
651 }
652 }
653}
654
655#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
656pub enum OpMode {
657 Sign,
658 Verify,
659 #[default]
660 Auto,
661}
662
663impl FromStr for OpMode {
664 type Err = &'static str;
665
666 fn from_str(s: &str) -> Result<Self, Self::Err> {
667 match s {
668 "sign" => Ok(Self::Sign),
669 "verify" => Ok(Self::Verify),
670 "auto" => Ok(Self::Auto),
671 _ => Err("unknown mode"),
672 }
673 }
674}
675
676#[derive(Clone, Debug, Eq, PartialEq)]
677pub struct TrustedNetworks {
678 pub trust_loopback: bool,
679 pub networks: HashSet<IpNet>,
680}
681
682impl TrustedNetworks {
683 pub fn contains(&self, addr: IpAddr) -> bool {
684 (self.trust_loopback && addr.is_loopback())
685 || self.networks.iter().any(|n| n.contains(&addr))
686 }
687
688 pub fn contains_loopback(&self) -> bool {
689 self.trust_loopback
692 || self.networks.iter().any(|n| {
693 n.contains(&IpAddr::from(Ipv4Addr::LOCALHOST))
694 || n.contains(&IpAddr::from(Ipv6Addr::LOCALHOST))
695 })
696 }
697}
698
699impl Default for TrustedNetworks {
700 fn default() -> Self {
701 Self {
702 trust_loopback: true,
703 networks: Default::default(),
704 }
705 }
706}
707
708#[derive(Clone, Eq, Hash, PartialEq)]
710pub struct SignedFieldName(FieldName);
711
712impl SignedFieldName {
713 pub fn new(value: impl Into<Box<str>>) -> Result<Self, HeaderFieldError> {
714 let name = FieldName::new(value)?;
715 if name.as_ref().contains(';') {
716 return Err(HeaderFieldError);
717 }
718 Ok(Self(name))
719 }
720}
721
722impl AsRef<FieldName> for SignedFieldName {
723 fn as_ref(&self) -> &FieldName {
724 &self.0
725 }
726}
727
728impl fmt::Debug for SignedFieldName {
729 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
730 write!(f, "{:?}", self.0)
731 }
732}
733
734#[derive(Clone, Debug, PartialEq)]
735pub enum SignedFieldNameWithQualifier {
736 Bare(SignedFieldName),
737 Plus(SignedFieldName),
738 Asterisk(SignedFieldName),
739}
740
741#[derive(Clone, Debug, PartialEq)]
742pub enum SignedHeaders {
743 Pick(Vec<SignedFieldName>), PickWithDefault(Vec<SignedFieldName>), All,
746}
747
748#[derive(Clone, Debug, PartialEq)]
749pub enum OversignedHeaders {
750 Pick(Vec<SignedFieldName>),
751 Signed,
752 Extended,
753}
754
755#[derive(Clone, Copy, Debug, PartialEq)]
756pub enum Expiration {
757 Never,
758 After(Duration), }
760
761#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
762pub enum RejectFailure {
763 Missing,
764 NoPass,
765 AuthorMismatch,
766}
767
768#[derive(Clone, Debug, Default, PartialEq)]
769pub struct RejectFailures(pub HashSet<RejectFailure>);
770
771#[derive(Clone, Debug, Default)]
772pub struct SigningSenders {
773 pub entries: Vec<SigningSender>,
774}
775
776#[derive(Clone, Debug)]
777pub struct SigningSender {
778 pub sender_expr: Regex,
779 pub domain: DomainExpr,
780 pub selector: Selector,
781 pub key: Arc<SigningKey>,
782 pub signing_config: Option<PartialSigningConfig>,
783}
784
785#[derive(Clone, Debug)]
786pub enum DomainExpr {
787 Domain(DomainName),
788 SenderDomain,
789 Identity(IdentityExpr),
790}
791
792#[derive(Clone, Debug)]
793pub struct IdentityExpr {
794 pub local_part: Option<LocalPartExpr>,
795 pub domain_part: IdentityDomainExpr,
796}
797
798#[derive(Clone, Debug)]
799pub enum LocalPartExpr {
800 LocalPart(String),
801 SenderLocalPart,
802}
803
804#[derive(Clone, Debug)]
805pub enum IdentityDomainExpr {
806 Domain(DomainName),
807 SenderDomain,
808 SplitDomain {
809 d_domain: DomainName,
810 i_domain: DomainName,
811 },
812}
813
814#[derive(Clone, Debug, Default)]
815pub struct ConnectionOverrides {
816 pub entries: Vec<NetworkOverride>,
817}
818
819#[derive(Clone, Debug)]
820pub struct NetworkOverride {
821 pub net: IpNet,
822 pub config: ConfigOverrides,
823}
824
825#[derive(Clone, Debug, Default)]
826pub struct RecipientOverrides {
827 pub entries: Vec<MailAddrOverride>,
828}
829
830#[derive(Clone, Debug)]
831pub struct MailAddrOverride {
832 pub expr: Regex,
833 pub config: ConfigOverrides,
834}
835
836#[cfg(test)]
837mod tests {
838 use super::*;
839 use ipnet::Ipv4Net;
840
841 #[test]
842 fn trusted_networks_contains_ip() {
843 let net = Ipv4Net::new([43, 5, 0, 0].into(), 16).unwrap();
844 let trusted_networks = TrustedNetworks {
845 networks: HashSet::from([net.into()]),
846 ..Default::default()
847 };
848
849 assert!(trusted_networks.contains(IpAddr::from([43, 5, 117, 8])));
850 assert!(trusted_networks.contains(IpAddr::from(Ipv6Addr::LOCALHOST)));
851 }
852
853 #[test]
854 fn trusted_networks_contains_loopback() {
855 let trusted_networks = TrustedNetworks::default();
856
857 assert!(trusted_networks.contains_loopback());
858
859 let net = Ipv4Net::new([127, 0, 0, 1].into(), 8).unwrap();
860 let trusted_networks = TrustedNetworks {
861 trust_loopback: false,
862 networks: HashSet::from([net.into()]),
863 };
864
865 assert!(trusted_networks.contains_loopback());
866 }
867}