use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::net::IpAddr;
use validator::{Validate, ValidationError, ValidationErrors, ValidationErrorsKind};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
pub struct ResolvedTarget {
pub ip: String,
pub port: u16,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
#[serde(untagged)]
pub enum Target {
Ip { ip: String, port: u16 },
Domain { domain: String, port: u16 },
Node { node: String, port: u16 },
}
impl Validate for Target {
fn validate(&self) -> Result<(), ValidationErrors> {
let mut errors = ValidationErrors::new();
match self {
Self::Ip { ip, port } => {
if ip.parse::<IpAddr>().is_err() {
errors.add("ip", ValidationError::new("ip"));
}
if *port == 0 {
let mut err = ValidationError::new("range");
err.message = Some("port must be greater than 0".into());
errors.add("port", err);
}
}
Self::Domain { domain, port } => {
if domain.is_empty() || domain.len() > 253 {
errors.add("domain", ValidationError::new("hostname"));
}
if *port == 0 {
let mut err = ValidationError::new("range");
err.message = Some("port must be greater than 0".into());
errors.add("port", err);
}
}
Self::Node { port, .. } => {
if *port == 0 {
let mut err = ValidationError::new("range");
err.message = Some("port must be greater than 0".into());
errors.add("port", err);
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum DetectMethod {
Magic,
Prefix,
Regex,
Fallback,
}
#[derive(Serialize, Deserialize, Debug, Clone, Validate, PartialEq, Eq)]
pub struct Detect {
pub method: DetectMethod,
#[validate(length(min = 1))]
pub pattern: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum Strategy {
Random,
Serial,
Fastest,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Forward {
pub strategy: Strategy,
#[serde(default)]
pub targets: Vec<Target>,
#[serde(default)]
pub fallbacks: Vec<Target>,
}
impl Validate for Forward {
fn validate(&self) -> Result<(), ValidationErrors> {
let mut errors = ValidationErrors::new();
if self.targets.is_empty() {
let mut err = ValidationError::new("length");
err.message = Some("must have at least one target".into());
errors.add("targets", err);
}
for (i, target) in self.targets.iter().enumerate() {
let path = format!("targets[{i}]");
let target_errors_list = super::validator::validate_target(target, &path);
for flow_err in target_errors_list {
let mut err = ValidationError::new("feature_disabled");
err.message = Some(flow_err.message.into());
errors.add("targets", err);
}
if let Err(target_errors) = target.validate() {
for (field, kind) in target_errors.errors() {
if let ValidationErrorsKind::Field(field_errors) = kind {
for error in field_errors {
let mut err = error.clone();
let old_msg = err.message.clone().unwrap_or_else(|| Cow::from("invalid"));
err.message = Some(format!("[index {i}] {field}: {old_msg}").into());
errors.add("targets", err);
}
}
}
}
}
for (i, target) in self.fallbacks.iter().enumerate() {
let path = format!("fallbacks[{i}]");
let target_errors_list = super::validator::validate_target(target, &path);
for flow_err in target_errors_list {
let mut err = ValidationError::new("feature_disabled");
err.message = Some(flow_err.message.into());
errors.add("fallbacks", err);
}
if let Err(target_errors) = target.validate() {
for (field, kind) in target_errors.errors() {
if let ValidationErrorsKind::Field(field_errors) = kind {
for error in field_errors {
let mut err = error.clone();
let old_msg = err.message.clone().unwrap_or_else(|| Cow::from("invalid"));
err.message = Some(format!("[index {i}] {field}: {old_msg}").into());
errors.add("fallbacks", err);
}
}
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
}
lazy_static::lazy_static! {
pub(super) static ref NAME_REGEX: regex::Regex = regex::Regex::new(r"^[a-z0-9_-]+$").expect("Failed to compile NAME_REGEX");
}