use crate::error::RegentError;
use crate::hosts::managed_host::InternalApiCallOutcome;
use crate::hosts::managed_host::{AssessCompliance, ReachCompliance, Timeout};
use crate::hosts::properties::HostProperties;
use crate::secrets::SecretProvidersPool;
use crate::state::Check;
use crate::state::attribute::HostHandler;
use crate::state::attribute::Privilege;
use crate::state::compliance::AttributeComplianceAssessment;
use serde::{Deserialize, Serialize};
use std::time::Duration;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
#[serde(rename_all = "PascalCase")]
pub enum DnsExpectedState {
#[serde(rename_all = "PascalCase")]
CheckResponseAndServer {
dns_name: String,
response: String,
server: String,
},
#[serde(rename_all = "PascalCase")]
CheckResponse {
dns_name: String,
response: String,
},
#[serde(rename_all = "PascalCase")]
CheckServer {
dns_name: String,
server: String,
},
#[serde(rename_all = "PascalCase")]
SimpleCheck {
dns_name: String,
},
}
impl DnsExpectedState {
pub fn check_response_and_server(
dns_name: &str,
response: &str,
server: &str,
) -> DnsExpectedState {
DnsExpectedState::CheckResponseAndServer {
dns_name: dns_name.to_string(),
response: response.to_string(),
server: server.to_string(),
}
}
pub fn check_response(dns_name: &str, response: &str) -> DnsExpectedState {
DnsExpectedState::CheckResponse {
dns_name: dns_name.to_string(),
response: response.to_string(),
}
}
pub fn check_server(dns_name: &str, server: &str) -> DnsExpectedState {
DnsExpectedState::CheckServer {
dns_name: dns_name.to_string(),
server: server.to_string(),
}
}
pub fn simple_check(dns_name: &str) -> DnsExpectedState {
DnsExpectedState::SimpleCheck {
dns_name: dns_name.to_string(),
}
}
}
impl Check for DnsExpectedState {
fn check(&self) -> Result<(), RegentError> {
Ok(())
}
fn check_host_compatibility(
&self,
_host_properties: &HostProperties,
) -> Result<(), RegentError> {
Ok(())
}
}
impl Timeout for DnsExpectedState {
fn default_timeout(&self) -> Duration {
Duration::from_secs(5)
}
}
impl<Handler: HostHandler> AssessCompliance<Handler> for DnsExpectedState {
async fn assess_compliance(
&self,
host_handler: &mut Handler,
_host_properties: &Option<HostProperties>,
_privilege: &Privilege,
_optional_secret_provider: &Option<SecretProvidersPool>,
) -> Result<AttributeComplianceAssessment, RegentError> {
if let Err(details) = host_handler
.is_this_command_available("dig", &Privilege::None)
.await
{
return Err(RegentError::FailedDryRunEvaluation(format!(
"command dig no available on this host : {:?}",
details
)));
}
let (dns_name, server, expected_response) = match &self {
DnsExpectedState::CheckResponseAndServer {
dns_name,
response,
server,
} => (dns_name, Some(server.to_string()), Some(response.to_string())),
DnsExpectedState::CheckResponse { dns_name, response } => (dns_name, None, Some(response.to_string())),
DnsExpectedState::CheckServer { dns_name, server } => {
(dns_name, Some(server.to_string()), None)
}
DnsExpectedState::SimpleCheck { dns_name } => (dns_name, None, None),
};
let responses: Vec<String> = match host_handler
.run_command(&final_dns_query(dns_name, server), &Privilege::None)
.await
{
Ok(command_result) => {
if command_result.return_code == 0 {
command_result
.stdout
.lines()
.map(|line| line.to_string())
.collect()
} else {
return Err(RegentError::FailedDryRunEvaluation(format!(
"Failed dig command: {:?}",
command_result
)));
}
}
Err(details) => {
return Err(RegentError::FailedDryRunEvaluation(format!(
"Unable to run dig command: {:?}",
details
)));
}
};
if responses.is_empty() {
return Ok(AttributeComplianceAssessment::NonCompliantFatal(
"Name doesnt\'t resolve".to_string()
));
} else {
match expected_response {
Some(expected_response) => {
if responses.contains(&expected_response) {
return Ok(AttributeComplianceAssessment::Compliant);
} else {
return Ok(AttributeComplianceAssessment::NonCompliantFatal(
format!("Name resolves but expected response not found among results ({:?})", responses)
))
}
}
None => {
return Ok(AttributeComplianceAssessment::Compliant);
}
}
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DnsApiCall {}
impl DnsApiCall {
pub fn display(&self) -> String {
return format!("Should not have been called");
}
}
impl Check for DnsApiCall {
fn check(&self) -> Result<(), RegentError> {
Err(RegentError::InternalLogicError(
"(check) DnsApiCall should not have been called as we cannot remediate automatically a wrong DNS configuration".to_string()
))
}
fn check_host_compatibility(
&self,
_host_properties: &HostProperties,
) -> Result<(), RegentError> {
Err(RegentError::InternalLogicError(
"(check_host_compatibility) DnsApiCall should not have been called as we cannot remediate automatically a wrong DNS configuration".to_string()
))
}
}
impl<Handler: HostHandler> ReachCompliance<Handler> for DnsApiCall {
async fn call(
&self,
_host_handler: &mut Handler,
_host_properties: &Option<HostProperties>,
_optional_secret_provider: &Option<SecretProvidersPool>,
) -> Result<InternalApiCallOutcome, RegentError> {
Err(RegentError::InternalLogicError(
"(call) DnsApiCall should not have been called as we cannot remediate automatically a wrong DNS configuration".to_string()
))
}
}
fn final_dns_query(dns_name: &str, server: Option<String>) -> String {
match server {
Some(server_address) => {
format!("dig @{server_address} +short {dns_name}")
}
None => {
format!("dig +short {dns_name}")
}
}
}