mirror of
https://codeberg.org/slatian/service.echoip-slatecave.git
synced 2025-07-17 14:33:27 +02:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
6f898a6280 | |||
59bad8def4 | |||
f01f6a886d | |||
34e5e8f95c | |||
5c322f2611 | |||
8f0b4c5883 | |||
c3a039be16 | |||
03685b1bde | |||
d5c5d30d32 | |||
38be0c05d0 | |||
e81ce74a2f |
26
.forgejo/workflows/cargo_audit.yaml
Normal file
26
.forgejo/workflows/cargo_audit.yaml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: Cargo Audit
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'Cargo.toml'
|
||||||
|
- 'Cargo.lock'
|
||||||
|
schedule:
|
||||||
|
# Frinday evening 18:20
|
||||||
|
- cron: 20 18 * * 5
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
audit:
|
||||||
|
name: Audit
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: codeberg.org/unobtanium/unobtanium-ci-builder:latest
|
||||||
|
steps:
|
||||||
|
- uses: https://codeberg.org/slatian/shell-ci-checkout@v1
|
||||||
|
- run: cargo audit
|
||||||
|
|
1224
Cargo.lock
generated
1224
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
12
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "echoip-slatecave"
|
name = "echoip-slatecave"
|
||||||
version = "1.6.0"
|
version = "1.7.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Slatian <baschdel@disroot.org>"]
|
authors = ["Slatian <baschdel@disroot.org>"]
|
||||||
|
|
||||||
@ -14,13 +14,13 @@ axum-extra = { version = "0.10", features = ["cookie", "typed-header"] }
|
|||||||
axum = { version = "0.8", features = ["macros"] }
|
axum = { version = "0.8", features = ["macros"] }
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
governor = "0.8"
|
governor = "0.10"
|
||||||
hickory-proto = "0.24"
|
hickory-proto = "0.25"
|
||||||
hickory-resolver = { version = "0.24", features = ["dns-over-rustls","dns-over-https","dns-over-quic","native-certs"] }
|
hickory-resolver = { version = "0.25", features = ["tls-ring","https-ring","quic-ring","rustls-platform-verifier","system-config"] }
|
||||||
http = "1.2"
|
http = "1.3"
|
||||||
idna = "1.0"
|
idna = "1.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
maxminddb = "0.24"
|
maxminddb = "0.26"
|
||||||
mime = "0.3"
|
mime = "0.3"
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
regex = "1.11"
|
regex = "1.11"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use serde::{Deserialize,Serialize};
|
use serde::{Deserialize,Serialize};
|
||||||
use hickory_resolver::config::Protocol;
|
use hickory_proto::xfer::Protocol;
|
||||||
use hickory_resolver::config::ResolverConfig as HickoryResolverConfig;
|
use hickory_resolver::config::ResolverConfig as HickoryResolverConfig;
|
||||||
use hickory_resolver::config::NameServerConfig;
|
use hickory_resolver::config::NameServerConfig;
|
||||||
|
|
||||||
@ -43,6 +43,7 @@ pub struct DnsResolverConfig {
|
|||||||
pub servers: Vec<SocketAddr>,
|
pub servers: Vec<SocketAddr>,
|
||||||
pub protocol: DnsProtocol,
|
pub protocol: DnsProtocol,
|
||||||
pub tls_dns_name: Option<Arc<str>>,
|
pub tls_dns_name: Option<Arc<str>>,
|
||||||
|
pub http_endpoint: Option<Arc<str>>,
|
||||||
#[serde(skip_serializing)] //Don't leak our bind address to the outside
|
#[serde(skip_serializing)] //Don't leak our bind address to the outside
|
||||||
pub bind_address: Option<SocketAddr>,
|
pub bind_address: Option<SocketAddr>,
|
||||||
#[serde(default="default_true", alias="trust_nx_responses")]
|
#[serde(default="default_true", alias="trust_nx_responses")]
|
||||||
@ -95,8 +96,8 @@ impl DnsResolverConfig {
|
|||||||
socket_addr: *server,
|
socket_addr: *server,
|
||||||
protocol: self.protocol.clone().into(),
|
protocol: self.protocol.clone().into(),
|
||||||
tls_dns_name: self.tls_dns_name.clone().map(|s| s.to_string()),
|
tls_dns_name: self.tls_dns_name.clone().map(|s| s.to_string()),
|
||||||
|
http_endpoint: self.http_endpoint.as_deref().map(ToString::to_string),
|
||||||
trust_negative_responses: self.trust_negative_responses,
|
trust_negative_responses: self.trust_negative_responses,
|
||||||
tls_config: None,
|
|
||||||
bind_addr: self.bind_address,
|
bind_addr: self.bind_address,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ use serde::Deserialize;
|
|||||||
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
mod dns;
|
mod dns;
|
||||||
|
|
||||||
@ -28,8 +29,8 @@ pub struct ServerConfig {
|
|||||||
|
|
||||||
#[derive(Deserialize, Clone)]
|
#[derive(Deserialize, Clone)]
|
||||||
pub struct GeoIpConfig {
|
pub struct GeoIpConfig {
|
||||||
pub asn_database: Option<String>,
|
pub asn_database: Option<PathBuf>,
|
||||||
pub location_database: Option<String>,
|
pub location_database: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Clone)]
|
#[derive(Deserialize, Clone)]
|
||||||
@ -59,8 +60,8 @@ impl Default for ServerConfig {
|
|||||||
impl Default for GeoIpConfig {
|
impl Default for GeoIpConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
GeoIpConfig {
|
GeoIpConfig {
|
||||||
asn_database: Some("mmdb/GeoLite2-ASN.mmdb".to_string()),
|
asn_database: Some("mmdb/GeoLite2-ASN.mmdb".into()),
|
||||||
location_database: Some("mmdb/GeoLite2-City.mmdb".to_string()),
|
location_database: Some("mmdb/GeoLite2-City.mmdb".into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
src/geoip.rs
20
src/geoip.rs
@ -6,12 +6,12 @@
|
|||||||
use log::{debug,info,warn,error};
|
use log::{debug,info,warn,error};
|
||||||
use maxminddb::geoip2;
|
use maxminddb::geoip2;
|
||||||
|
|
||||||
use maxminddb::MaxMindDBError::AddressNotFoundError;
|
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/* Datatypes */
|
/* Datatypes */
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ pub struct AsnResult {
|
|||||||
pub struct MMDBCarrier {
|
pub struct MMDBCarrier {
|
||||||
pub mmdb: RwLock<Option<maxminddb::Reader<Vec<u8>>>>,
|
pub mmdb: RwLock<Option<maxminddb::Reader<Vec<u8>>>>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub path: Option<String>,
|
pub path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait QueryLocation {
|
pub trait QueryLocation {
|
||||||
@ -130,13 +130,13 @@ impl QueryAsn for MMDBCarrier {
|
|||||||
match &*mmdb {
|
match &*mmdb {
|
||||||
Some(mmdb) => {
|
Some(mmdb) => {
|
||||||
match mmdb.lookup::<geoip2::Asn>(*address) {
|
match mmdb.lookup::<geoip2::Asn>(*address) {
|
||||||
Ok(res) => {
|
Ok(Some(res)) => {
|
||||||
Some(AsnResult {
|
Some(AsnResult {
|
||||||
asn: res.autonomous_system_number,
|
asn: res.autonomous_system_number,
|
||||||
name: res.autonomous_system_organization.map(ToString::to_string),
|
name: res.autonomous_system_organization.map(ToString::to_string),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Err(AddressNotFoundError(_)) => {
|
Ok(None) => {
|
||||||
// Log to the debug channel.
|
// Log to the debug channel.
|
||||||
// This isn't severe, and shouldn't be logged in production.
|
// This isn't severe, and shouldn't be logged in production.
|
||||||
debug!("ASN not found in database for {address}.");
|
debug!("ASN not found in database for {address}.");
|
||||||
@ -159,7 +159,7 @@ impl QueryLocation for MMDBCarrier {
|
|||||||
match &*mmdb {
|
match &*mmdb {
|
||||||
Some(mmdb) => {
|
Some(mmdb) => {
|
||||||
match mmdb.lookup::<geoip2::City>(*address) {
|
match mmdb.lookup::<geoip2::City>(*address) {
|
||||||
Ok(res) => {
|
Ok(Some(res)) => {
|
||||||
Some(LocationResult {
|
Some(LocationResult {
|
||||||
continent:
|
continent:
|
||||||
res.continent.map(|c| geoip2_continent_to_named_location(c, languages)),
|
res.continent.map(|c| geoip2_continent_to_named_location(c, languages)),
|
||||||
@ -210,7 +210,7 @@ impl QueryLocation for MMDBCarrier {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Err(AddressNotFoundError(_)) => {
|
Ok(None) => {
|
||||||
// Log to the debug channel.
|
// Log to the debug channel.
|
||||||
// This isn't severe, and shouldn't be logged in production.
|
// This isn't severe, and shouldn't be logged in production.
|
||||||
debug!("IP location not found in database for {address}");
|
debug!("IP location not found in database for {address}");
|
||||||
@ -228,7 +228,7 @@ impl QueryLocation for MMDBCarrier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MMDBCarrier {
|
impl MMDBCarrier {
|
||||||
pub fn new(name: String, path: Option<String>) -> MMDBCarrier {
|
pub fn new(name: String, path: Option<PathBuf>) -> MMDBCarrier {
|
||||||
MMDBCarrier {
|
MMDBCarrier {
|
||||||
mmdb: RwLock::new(None),
|
mmdb: RwLock::new(None),
|
||||||
name: name,
|
name: name,
|
||||||
@ -236,14 +236,14 @@ impl MMDBCarrier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reload_database(&self) -> Result<(),maxminddb::MaxMindDBError> {
|
pub fn reload_database(&self) -> Result<(),maxminddb::MaxMindDbError> {
|
||||||
match &self.path {
|
match &self.path {
|
||||||
Some(path) => self.load_database_from_path(Path::new(&path)),
|
Some(path) => self.load_database_from_path(path),
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_database_from_path(&self, path: &Path) -> Result<(),maxminddb::MaxMindDBError> {
|
pub fn load_database_from_path(&self, path: &Path) -> Result<(),maxminddb::MaxMindDbError> {
|
||||||
let mut mmdb = self.mmdb.write();
|
let mut mmdb = self.mmdb.write();
|
||||||
info!("Loading {} from '{}' ...", &self.name, path.display());
|
info!("Loading {} from '{}' ...", &self.name, path.display());
|
||||||
match maxminddb::Reader::open_readfile(path) {
|
match maxminddb::Reader::open_readfile(path) {
|
||||||
|
@ -28,6 +28,7 @@ pub enum AddressScope {
|
|||||||
Loopback,
|
Loopback,
|
||||||
Reserved,
|
Reserved,
|
||||||
Documentation,
|
Documentation,
|
||||||
|
Nat64,
|
||||||
#[default]
|
#[default]
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
@ -78,6 +79,9 @@ impl AddressInfo {
|
|||||||
// Test for the documentation address 2001:db8::/32
|
// Test for the documentation address 2001:db8::/32
|
||||||
} else if segments[0]==0x2001 && segments[1]==0x0db8 && segments[2]==0 && segments[3]==0 {
|
} else if segments[0]==0x2001 && segments[1]==0x0db8 && segments[2]==0 && segments[3]==0 {
|
||||||
address_scope = AddressScope::Documentation;
|
address_scope = AddressScope::Documentation;
|
||||||
|
// Test for NAT64 address 64:ff9b::/96
|
||||||
|
} else if segments[0]==0x64 && segments[1]==0xff9b {
|
||||||
|
address_scope = AddressScope::Nat64;
|
||||||
// Test for multicase scope
|
// Test for multicase scope
|
||||||
} else if addr.is_multicast() {
|
} else if addr.is_multicast() {
|
||||||
address_cast = AddressCast::Multicast;
|
address_cast = AddressCast::Multicast;
|
||||||
|
86
src/main.rs
86
src/main.rs
@ -22,9 +22,10 @@ use axum_extra::headers;
|
|||||||
use axum_extra::TypedHeader;
|
use axum_extra::TypedHeader;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use env_logger::Env;
|
use env_logger::Env;
|
||||||
use hickory_resolver::Name;
|
use hickory_resolver::{name_server::TokioConnectionProvider, system_conf::read_system_conf, Name, ResolveError, Resolver};
|
||||||
use hickory_resolver::TokioAsyncResolver;
|
use hickory_resolver::TokioResolver;
|
||||||
use log::{info,warn,error};
|
use log::{info,warn,error};
|
||||||
|
use nat64::resolve_nat64_address;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::{Deserialize,Serialize};
|
use serde::{Deserialize,Serialize};
|
||||||
use tower_http::services::ServeDir;
|
use tower_http::services::ServeDir;
|
||||||
@ -39,6 +40,7 @@ use tokio::task;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
@ -50,6 +52,7 @@ mod config;
|
|||||||
mod geoip;
|
mod geoip;
|
||||||
mod idna;
|
mod idna;
|
||||||
mod ipinfo;
|
mod ipinfo;
|
||||||
|
mod nat64;
|
||||||
mod ratelimit;
|
mod ratelimit;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod simple_dns;
|
mod simple_dns;
|
||||||
@ -82,8 +85,28 @@ pub struct SearchQuery {
|
|||||||
query: Option<String>,
|
query: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Enumerates possible mapping strategies
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
#[serde(rename_all="snake_case")]
|
||||||
|
pub enum IpMappingStrategy {
|
||||||
|
/// See: https://en.wikipedia.org/wiki/NAT64
|
||||||
|
Nat64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Clone)]
|
||||||
|
pub struct IpMapping {
|
||||||
|
strategy: IpMappingStrategy,
|
||||||
|
from_address: IpAddr,
|
||||||
|
to_address: IpAddr,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Clone)]
|
#[derive(Serialize, Clone)]
|
||||||
pub struct IpResult {
|
pub struct IpResult {
|
||||||
|
/// When the mapping is set the queried for address
|
||||||
|
/// was automtically replaced with the mapped to address.
|
||||||
|
mapping: Option<IpMapping>,
|
||||||
|
/// The address that was queried for or the mapping resulted in.
|
||||||
address: IpAddr,
|
address: IpAddr,
|
||||||
hostname: Option<String>,
|
hostname: Option<String>,
|
||||||
asn: Option<AsnResult>,
|
asn: Option<AsnResult>,
|
||||||
@ -108,7 +131,7 @@ pub struct DigResult {
|
|||||||
|
|
||||||
struct ServiceSharedState {
|
struct ServiceSharedState {
|
||||||
templating_engine: TemplatingEngine,
|
templating_engine: TemplatingEngine,
|
||||||
dns_resolvers: HashMap<Arc<str>,TokioAsyncResolver>,
|
dns_resolvers: HashMap<Arc<str>,TokioResolver>,
|
||||||
dns_resolver_aliases: HashMap<Arc<str>,Arc<str>>,
|
dns_resolver_aliases: HashMap<Arc<str>,Arc<str>>,
|
||||||
asn_db: geoip::MMDBCarrier,
|
asn_db: geoip::MMDBCarrier,
|
||||||
location_db: geoip::MMDBCarrier,
|
location_db: geoip::MMDBCarrier,
|
||||||
@ -126,7 +149,7 @@ struct DerivedConfiguration {
|
|||||||
#[command(author, version, long_about="A web service that tells you your ip-address and more …")]
|
#[command(author, version, long_about="A web service that tells you your ip-address and more …")]
|
||||||
struct CliArgs {
|
struct CliArgs {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
config: Option<String>,
|
config: Option<PathBuf>,
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
listen_on: Option<SocketAddr>,
|
listen_on: Option<SocketAddr>,
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
@ -134,7 +157,7 @@ struct CliArgs {
|
|||||||
#[arg(short,long)]
|
#[arg(short,long)]
|
||||||
extra_config: Option<String>,
|
extra_config: Option<String>,
|
||||||
#[arg(short,long)]
|
#[arg(short,long)]
|
||||||
static_location: Option<String>,
|
static_location: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_domain_hidden_list(domain: &str, hidden_list: &Vec<String>) -> bool {
|
fn match_domain_hidden_list(domain: &str, hidden_list: &Vec<String>) -> bool {
|
||||||
@ -215,21 +238,24 @@ async fn main() {
|
|||||||
info!("Initalizing dns resolvers ...");
|
info!("Initalizing dns resolvers ...");
|
||||||
|
|
||||||
let mut dns_resolver_selectables = Vec::<Selectable>::new();
|
let mut dns_resolver_selectables = Vec::<Selectable>::new();
|
||||||
let mut dns_resolver_map: HashMap<Arc<str>,TokioAsyncResolver> = HashMap::new();
|
let mut dns_resolver_map: HashMap<Arc<str>,TokioResolver> = HashMap::new();
|
||||||
let mut dns_resolver_aliases: HashMap<Arc<str>,Arc<str>> = HashMap::new();
|
let mut dns_resolver_aliases: HashMap<Arc<str>,Arc<str>> = HashMap::new();
|
||||||
|
|
||||||
if config.dns.enable_system_resolver {
|
if config.dns.enable_system_resolver {
|
||||||
info!("Initalizing System resolver ...");
|
info!("Initalizing System resolver ...");
|
||||||
let res = TokioAsyncResolver::tokio_from_system_conf();
|
match initalize_system_resolver() {
|
||||||
let resolver = match res {
|
Ok(resolver) => {
|
||||||
Ok(resolver) => resolver,
|
info!("System resolver successfully Initalized.");
|
||||||
|
dns_resolver_map.insert(
|
||||||
|
config.dns.system_resolver_id.clone(),
|
||||||
|
resolver
|
||||||
|
);
|
||||||
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
info!("Error while setting up dns resolver: {e}");
|
error!("Problem setting up system resolver: {e}");
|
||||||
::std::process::exit(1);
|
::std::process::exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
dns_resolver_map.insert(config.dns.system_resolver_id.clone(), resolver);
|
|
||||||
dns_resolver_selectables.push(Selectable {
|
dns_resolver_selectables.push(Selectable {
|
||||||
id: config.dns.system_resolver_id.clone(),
|
id: config.dns.system_resolver_id.clone(),
|
||||||
name: config.dns.system_resolver_name.clone(),
|
name: config.dns.system_resolver_name.clone(),
|
||||||
@ -239,10 +265,10 @@ async fn main() {
|
|||||||
|
|
||||||
for (key, resolver_config) in &config.dns.resolver {
|
for (key, resolver_config) in &config.dns.resolver {
|
||||||
info!("Initalizing {} resolver ...", key);
|
info!("Initalizing {} resolver ...", key);
|
||||||
let resolver = TokioAsyncResolver::tokio(
|
let resolver = TokioResolver::builder_with_config(
|
||||||
resolver_config.to_hickory_resolver_config(),
|
resolver_config.to_hickory_resolver_config(),
|
||||||
Default::default()
|
Default::default()
|
||||||
);
|
).build();
|
||||||
dns_resolver_map.insert(key.clone(), resolver);
|
dns_resolver_map.insert(key.clone(), resolver);
|
||||||
dns_resolver_selectables.push(Selectable {
|
dns_resolver_selectables.push(Selectable {
|
||||||
id: key.clone(),
|
id: key.clone(),
|
||||||
@ -331,6 +357,17 @@ async fn main() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn initalize_system_resolver() -> Result<TokioResolver, ResolveError> {
|
||||||
|
let (system_conf, system_options) = read_system_conf()?;
|
||||||
|
let mut builder = Resolver::builder_with_config(
|
||||||
|
system_conf,
|
||||||
|
TokioConnectionProvider::default()
|
||||||
|
);
|
||||||
|
*builder.options_mut() = system_options;
|
||||||
|
|
||||||
|
return Ok(builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn settings_query_middleware(
|
async fn settings_query_middleware(
|
||||||
Query(query): Query<SettingsQuery>,
|
Query(query): Query<SettingsQuery>,
|
||||||
@ -583,6 +620,19 @@ async fn handle_ip_request(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_ip_mapping(address: &IpAddr) -> Option<IpMapping> {
|
||||||
|
if let IpAddr::V6(v6_address) = address {
|
||||||
|
if let Some(nat64_result) = resolve_nat64_address(*v6_address) {
|
||||||
|
return Some(IpMapping {
|
||||||
|
from_address: *address,
|
||||||
|
to_address: IpAddr::V4(nat64_result),
|
||||||
|
strategy: IpMappingStrategy::Nat64,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_ip_result(
|
async fn get_ip_result(
|
||||||
address: &IpAddr,
|
address: &IpAddr,
|
||||||
lang: &str,
|
lang: &str,
|
||||||
@ -592,10 +642,14 @@ async fn get_ip_result(
|
|||||||
state: &ServiceSharedState,
|
state: &ServiceSharedState,
|
||||||
) -> IpResult {
|
) -> IpResult {
|
||||||
|
|
||||||
|
let mapping = get_ip_mapping(address);
|
||||||
|
let original_address = address;
|
||||||
|
let address = &mapping.clone().map_or(*original_address, |m| m.to_address);
|
||||||
|
|
||||||
let mut reverse_dns_disabled_for_privacy = false;
|
let mut reverse_dns_disabled_for_privacy = false;
|
||||||
|
|
||||||
if state.config.dns.allow_reverse_lookup &&
|
if state.config.dns.allow_reverse_lookup &&
|
||||||
address == client_ip &&
|
(address == client_ip || original_address == client_ip) &&
|
||||||
dns_disable_self_lookup
|
dns_disable_self_lookup
|
||||||
{
|
{
|
||||||
reverse_dns_disabled_for_privacy = true;
|
reverse_dns_disabled_for_privacy = true;
|
||||||
@ -608,6 +662,7 @@ async fn get_ip_result(
|
|||||||
// The address falls into a private range and lookup of private addresses is not allowed.
|
// The address falls into a private range and lookup of private addresses is not allowed.
|
||||||
if (!state.config.server.allow_private_ip_lookup) && (ip_info.scope == AddressScope::Private || ip_info.scope == AddressScope::LinkLocal) {
|
if (!state.config.server.allow_private_ip_lookup) && (ip_info.scope == AddressScope::Private || ip_info.scope == AddressScope::LinkLocal) {
|
||||||
return IpResult {
|
return IpResult {
|
||||||
|
mapping: mapping,
|
||||||
address: *address,
|
address: *address,
|
||||||
hostname: None,
|
hostname: None,
|
||||||
asn: None,
|
asn: None,
|
||||||
@ -646,6 +701,7 @@ async fn get_ip_result(
|
|||||||
}
|
}
|
||||||
|
|
||||||
IpResult{
|
IpResult{
|
||||||
|
mapping: mapping,
|
||||||
address: *address,
|
address: *address,
|
||||||
hostname: hostname,
|
hostname: hostname,
|
||||||
asn: asn_result,
|
asn: asn_result,
|
||||||
|
23
src/nat64.rs
Normal file
23
src/nat64.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
use std::net::Ipv4Addr;
|
||||||
|
use std::net::Ipv6Addr;
|
||||||
|
|
||||||
|
/// Resolves a NAT64 address if it is in the range of 64:ff9b::/96
|
||||||
|
pub fn resolve_nat64_address(from: Ipv6Addr) -> Option<Ipv4Addr> {
|
||||||
|
if is_nat64_address(&from) {
|
||||||
|
let segments = from.segments();
|
||||||
|
return Some(Ipv4Addr::new(
|
||||||
|
((segments[6] & 0xff00) >> 8) as u8,
|
||||||
|
(segments[6] & 0x00ff) as u8,
|
||||||
|
((segments[7] & 0xff00) >> 8) as u8,
|
||||||
|
(segments[7] & 0x00ff) as u8,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_nat64_address(address: &Ipv6Addr) -> bool {
|
||||||
|
let segments = address.segments();
|
||||||
|
segments[0]==0x64 && segments[1]==0xff9b
|
||||||
|
}
|
||||||
|
|
@ -10,12 +10,13 @@ use hickory_proto::rr::{
|
|||||||
RData,
|
RData,
|
||||||
record_type::RecordType,
|
record_type::RecordType,
|
||||||
};
|
};
|
||||||
|
use hickory_proto::ProtoErrorKind;
|
||||||
use hickory_resolver::{
|
use hickory_resolver::{
|
||||||
error::ResolveError,
|
|
||||||
error::ResolveErrorKind,
|
|
||||||
lookup::Lookup,
|
lookup::Lookup,
|
||||||
Name,
|
Name,
|
||||||
TokioAsyncResolver,
|
ResolveError,
|
||||||
|
ResolveErrorKind,
|
||||||
|
TokioResolver,
|
||||||
};
|
};
|
||||||
use log::{warn,error};
|
use log::{warn,error};
|
||||||
|
|
||||||
@ -42,6 +43,7 @@ pub struct DnsLookupResult {
|
|||||||
pub dns_error: bool,
|
pub dns_error: bool,
|
||||||
pub nxdomain: bool,
|
pub nxdomain: bool,
|
||||||
pub timeout: bool,
|
pub timeout: bool,
|
||||||
|
pub too_busy: bool,
|
||||||
pub invalid_name: bool,
|
pub invalid_name: bool,
|
||||||
pub unkown_resolver: bool,
|
pub unkown_resolver: bool,
|
||||||
}
|
}
|
||||||
@ -74,7 +76,7 @@ pub struct SrvRecord {
|
|||||||
/* Lookup Functions*/
|
/* Lookup Functions*/
|
||||||
|
|
||||||
pub async fn reverse_lookup(
|
pub async fn reverse_lookup(
|
||||||
resolver: &TokioAsyncResolver,
|
resolver: &TokioResolver,
|
||||||
address: &IpAddr,
|
address: &IpAddr,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
let revese_res = resolver.reverse_lookup(*address);
|
let revese_res = resolver.reverse_lookup(*address);
|
||||||
@ -88,8 +90,16 @@ pub async fn reverse_lookup(
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
let kind = e.kind();
|
let kind = e.kind();
|
||||||
match kind {
|
match kind {
|
||||||
ResolveErrorKind::NoRecordsFound { .. } => {
|
ResolveErrorKind::Proto(protocol_error) => {
|
||||||
//Ignore, that just happens …
|
match protocol_error.kind() {
|
||||||
|
ProtoErrorKind::NoRecordsFound { .. } => {
|
||||||
|
//Ignore, that just happens …
|
||||||
|
// TODO: Add NSec when adding support for dnssec
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
error!("Reverse lookup on {address} failed with protocol error: {protocol_error}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
error!("Reverse lookup on {address} failed: {kind}");
|
error!("Reverse lookup on {address} failed: {kind}");
|
||||||
@ -179,9 +189,7 @@ pub fn integrate_lookup_result(dig_result: &mut DnsLookupResult, lookup_result:
|
|||||||
let name = lookup.query().name();
|
let name = lookup.query().name();
|
||||||
for record in lookup.record_iter() {
|
for record in lookup.record_iter() {
|
||||||
if name == record.name() {
|
if name == record.name() {
|
||||||
if let Some(data) = record.data() {
|
add_record_to_lookup_result(dig_result, record.data());
|
||||||
add_record_to_lookup_result(dig_result, data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//TODO: handle additional responses
|
//TODO: handle additional responses
|
||||||
}
|
}
|
||||||
@ -189,27 +197,35 @@ pub fn integrate_lookup_result(dig_result: &mut DnsLookupResult, lookup_result:
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
match e.kind() {
|
match e.kind() {
|
||||||
ResolveErrorKind::Message(..) |
|
ResolveErrorKind::Message(..) |
|
||||||
ResolveErrorKind::Msg(..) |
|
ResolveErrorKind::Msg(..) => {
|
||||||
ResolveErrorKind::NoConnections |
|
error!("There was an error message while doing a DNS Lookup: {e}");
|
||||||
ResolveErrorKind::Io(..) |
|
|
||||||
ResolveErrorKind::Proto(..) => {
|
|
||||||
dig_result.other_error = true;
|
|
||||||
error!("There was an error while doing a DNS Lookup: {e}");
|
|
||||||
},
|
|
||||||
ResolveErrorKind::Timeout => {
|
|
||||||
dig_result.timeout = true;
|
|
||||||
warn!("There was a timeout while doing a DNS Lookup.");
|
|
||||||
},
|
|
||||||
ResolveErrorKind::NoRecordsFound{response_code, ..} => {
|
|
||||||
match response_code {
|
|
||||||
ResponseCode::NXDomain => dig_result.nxdomain = true,
|
|
||||||
ResponseCode::NoError => {},
|
|
||||||
_ => {
|
|
||||||
error!("The DNS Server returned an error while doing a DNS Lookup: {response_code}");
|
|
||||||
dig_result.dns_error = true;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
ResolveErrorKind::Proto(protocol_error) => {
|
||||||
|
match protocol_error.kind() {
|
||||||
|
ProtoErrorKind::Busy => {
|
||||||
|
dig_result.too_busy = true;
|
||||||
|
warn!("A resource was too busy for doing a DNS Lookup.");
|
||||||
|
},
|
||||||
|
ProtoErrorKind::Timeout => {
|
||||||
|
dig_result.timeout = true;
|
||||||
|
warn!("There was a timeout while doing a DNS Lookup.");
|
||||||
|
},
|
||||||
|
ProtoErrorKind::NoRecordsFound { response_code, .. } => {
|
||||||
|
match response_code {
|
||||||
|
ResponseCode::NXDomain => dig_result.nxdomain = true,
|
||||||
|
ResponseCode::NoError => {},
|
||||||
|
_ => {
|
||||||
|
error!("The DNS Server returned an error while doing a DNS Lookup: {response_code}");
|
||||||
|
dig_result.dns_error = true;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
dig_result.other_error = true;
|
||||||
|
error!("There was an error while doing a DNS Lookup: {protocol_error}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => { /*Ignore for now*/ },
|
_ => { /*Ignore for now*/ },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -220,7 +236,7 @@ pub fn integrate_lookup_result(dig_result: &mut DnsLookupResult, lookup_result:
|
|||||||
// If do_full_lookup is false only the A and AAAA (CNAMEs planned for the future)
|
// If do_full_lookup is false only the A and AAAA (CNAMEs planned for the future)
|
||||||
// records will be fetched.
|
// records will be fetched.
|
||||||
pub async fn lookup(
|
pub async fn lookup(
|
||||||
resolver: &TokioAsyncResolver,
|
resolver: &TokioResolver,
|
||||||
name: &Name,
|
name: &Name,
|
||||||
do_full_lookup: bool,
|
do_full_lookup: bool,
|
||||||
) -> DnsLookupResult {
|
) -> DnsLookupResult {
|
||||||
|
@ -12,6 +12,12 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% set r = data.result %}
|
{% set r = data.result %}
|
||||||
|
{% if r.mapping %}
|
||||||
|
<section>
|
||||||
|
<h2>{{ r.mapping.strategy | title }} Mapping</h2>
|
||||||
|
<p>The address <code>{{ r.mapping.from_address }}</code> was automatically translated to <code>{{ r.mapping.to_address }}</code> using {{ r.mapping.strategy | title }}.</p>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
<section>
|
<section>
|
||||||
<h2>Network Information</h2>
|
<h2>Network Information</h2>
|
||||||
<dl>
|
<dl>
|
||||||
|
@ -8,6 +8,13 @@
|
|||||||
{% set r = data.result -%}
|
{% set r = data.result -%}
|
||||||
# {% block title %}Lookup {{ data.result.address }}{% endblock %}
|
# {% block title %}Lookup {{ data.result.address }}{% endblock %}
|
||||||
|
|
||||||
|
{%- if r.mapping %}
|
||||||
|
|
||||||
|
## {{ r.mapping.strategy | title }} Mapping
|
||||||
|
|
||||||
|
The address {{ r.mapping.from_address }} was automatically translated to {{ r.mapping.to_address }} using {{ r.mapping.strategy | title }}.
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
## Network information
|
## Network information
|
||||||
|
|
||||||
* Type of Address: {{ helper::ip_info(ip_info=r.ip_info) }}
|
* Type of Address: {{ helper::ip_info(ip_info=r.ip_info) }}
|
||||||
|
@ -6,10 +6,13 @@
|
|||||||
<li><a target="_blank" href="https://client.rdap.org/?type=ip&object={{ address }}">… on client.rdap.org <small>(a modern whois, make sure to allow xhr to 3rd parties)</small></a></li>
|
<li><a target="_blank" href="https://client.rdap.org/?type=ip&object={{ address }}">… on client.rdap.org <small>(a modern whois, make sure to allow xhr to 3rd parties)</small></a></li>
|
||||||
<li><a target="_blank" href="https://www.shodan.io/host/{{ address }}">… on shodan.io <small>(limited queries per day, wants an account)</small></a></li>
|
<li><a target="_blank" href="https://www.shodan.io/host/{{ address }}">… on shodan.io <small>(limited queries per day, wants an account)</small></a></li>
|
||||||
<li><a target="_blank" href="https://search.censys.io/search?resource=hosts&sort=RELEVANCE&per_page=25&virtual_hosts=EXCLUDE&q=ip%3D{{ address }}">… on search.censys.io <small>(10 query per day, wants an account)</small></a></li>
|
<li><a target="_blank" href="https://search.censys.io/search?resource=hosts&sort=RELEVANCE&per_page=25&virtual_hosts=EXCLUDE&q=ip%3D{{ address }}">… on search.censys.io <small>(10 query per day, wants an account)</small></a></li>
|
||||||
|
<li><a target="_blank" href="https://www.abuseipdb.com/check/{{ address }}">… an AbuseIPDB.com</a></li>
|
||||||
|
<li><a target="_blank" href="https://app.crowdsec.net/cti/{{ address }}">… on CrowdSec.net CTI <small>(10 query's per day, wants an account)</small></a></li>
|
||||||
{% if not address is matching(":") %}
|
{% if not address is matching(":") %}
|
||||||
{# v4 only #}
|
{# v4 only #}
|
||||||
<li><a target="_blank" href="https://www.virustotal.com/gui/ip-address/{{ address }}">… on virustotal.com</a></li>
|
<li><a target="_blank" href="https://www.virustotal.com/gui/ip-address/{{ address }}">… on virustotal.com</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<li><a target="_blank" href="https://check.spamhaus.org/results?query={{ address }}">… on spamhaus.org</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
{% endmacro ip_address_links %}
|
{% endmacro ip_address_links %}
|
||||||
|
|
||||||
@ -23,6 +26,7 @@
|
|||||||
<li><a target="_blank" href="https://internet.nl/site/{{ name | urlencode_strict }}">… on the Internet.nl Website test</a></li>
|
<li><a target="_blank" href="https://internet.nl/site/{{ name | urlencode_strict }}">… on the Internet.nl Website test</a></li>
|
||||||
<li><a target="_blank" href="https://client.rdap.org/?type=domain&object={{ name | urlencode_strict }}">… on client.rdap.org <small>(a modern whois, make sure to allow xhr to 3rd parties)</small></a></li>
|
<li><a target="_blank" href="https://client.rdap.org/?type=domain&object={{ name | urlencode_strict }}">… on client.rdap.org <small>(a modern whois, make sure to allow xhr to 3rd parties)</small></a></li>
|
||||||
<li><a target="_blank" href="https://crt.sh/?Identity={{ name | urlencode_strict }}&match==">… on crt.sh <small>(Certificate Transparancy Monitor)</small></a></li>
|
<li><a target="_blank" href="https://crt.sh/?Identity={{ name | urlencode_strict }}&match==">… on crt.sh <small>(Certificate Transparancy Monitor)</small></a></li>
|
||||||
|
<li><a target="_blank" href="https://check.spamhaus.org/results?query={{ name | urlencode_strict }}">… on spamhaus.org</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
{% endmacro domain_name_links %}
|
{% endmacro domain_name_links %}
|
||||||
|
|
||||||
@ -32,6 +36,8 @@
|
|||||||
<li><a target="_blank" href="https://bgp.he.net/AS{{asn}}">… on Hurricane Electric BGP Toolkit</a></li>
|
<li><a target="_blank" href="https://bgp.he.net/AS{{asn}}">… on Hurricane Electric BGP Toolkit</a></li>
|
||||||
<li><a target="_blank" href="https://radar.qrator.net/as{{asn}}">… on radar.qrator.net (BGP Tool)</a></li>
|
<li><a target="_blank" href="https://radar.qrator.net/as{{asn}}">… on radar.qrator.net (BGP Tool)</a></li>
|
||||||
<li><a target="_blank" href="https://search.censys.io/search?resource=hosts&sort=RELEVANCE&per_page=25&virtual_hosts=EXCLUDE&q=autonomous_system.asn%3D{{asn}}">… on search.censys.io <small>(10 query's per day, wants an account)</small></a></li>
|
<li><a target="_blank" href="https://search.censys.io/search?resource=hosts&sort=RELEVANCE&per_page=25&virtual_hosts=EXCLUDE&q=autonomous_system.asn%3D{{asn}}">… on search.censys.io <small>(10 query's per day, wants an account)</small></a></li>
|
||||||
|
<li><a target="_blank" href="https://app.crowdsec.net/cti?q=as_num%3A{{ asn }}&page=1">… on CrowdSec.net <small>(30 queries per week, wants an account)</small></a></li>
|
||||||
|
<li><a target="_blank" href="https://check.spamhaus.org/results?query=AS{{ asn }}">… on spamhaus.org</a></li>
|
||||||
<li><a target="_blank" href="https://client.rdap.org/?type=autnum&object={{ asn }}">… on client.rdap.org <small>(a modern whois, make sure to allow xhr to 3rd parties)</small></a></li>
|
<li><a target="_blank" href="https://client.rdap.org/?type=autnum&object={{ asn }}">… on client.rdap.org <small>(a modern whois, make sure to allow xhr to 3rd parties)</small></a></li>
|
||||||
<li><a target="_blank" href="https://query.wikidata.org/#%23Select%20Wikipedia%20articles%20that%20belong%20to%20a%20given%20asn%0ASELECT%20DISTINCT%20%3Fitem%20%3Fwebsite%20%3FitemLabel%20%3FitemDescription%20%3Flang%20%3Farticle%20WHERE%20%7B%0A%20%20VALUES%20%3Fasn%20%7B%0A%20%20%20%20%22{{ asn }}%22%0A%20%20%7D%0A%20%20%3Fasn%20%5Ewdt%3AP3797%20%3Fitem.%0A%20%20OPTIONAL%20%7B%20%3Fitem%20wdt%3AP856%20%3Fwebsite.%20%7D%0A%20%20OPTIONAL%20%7B%0A%20%20%20%20%3Fitem%20%5Eschema%3Aabout%20%3Farticle.%0A%20%20%20%20%3Farticle%20schema%3AisPartOf%20_%3Ab64.%0A%20%20%20%20_%3Ab64%20wikibase%3AwikiGroup%20%22wikipedia%22.%0A%20%20%20%20%3Farticle%20schema%3AinLanguage%20%3Flang%3B%0A%20%20%20%20%20%20schema%3Aname%20%3Farticlename.%0A%20%20%20%20FILTER(((%3Flang%20%3D%20%22%5BAUTO_LANGUAGE%5D%22)%20%7C%7C%20(%3Flang%20%3D%20%22en%22))%20%7C%7C%20(%3Flang%20%3D%20%22de%22))%0A%20%20%7D%0A%20%20SERVICE%20wikibase%3Alabel%20%7B%0A%20%20%20%20bd%3AserviceParam%20wikibase%3Alanguage%20%22%5BAUTO_LANGUAGE%5D%2Cen%22.%0A%20%20%20%20%3Fitem%20rdfs%3Alabel%20%3FitemLabel%3B%0A%20%20%20%20%20%20schema%3Adescription%20%3FitemDescription.%0A%20%20%7D%0A%7D%0AORDER%20BY%20(UCASE(%3FitemLabel))">… on Wikidata and Wikipedia <small>(Press the run button in the sidebar to get results)</small></a></li>
|
<li><a target="_blank" href="https://query.wikidata.org/#%23Select%20Wikipedia%20articles%20that%20belong%20to%20a%20given%20asn%0ASELECT%20DISTINCT%20%3Fitem%20%3Fwebsite%20%3FitemLabel%20%3FitemDescription%20%3Flang%20%3Farticle%20WHERE%20%7B%0A%20%20VALUES%20%3Fasn%20%7B%0A%20%20%20%20%22{{ asn }}%22%0A%20%20%7D%0A%20%20%3Fasn%20%5Ewdt%3AP3797%20%3Fitem.%0A%20%20OPTIONAL%20%7B%20%3Fitem%20wdt%3AP856%20%3Fwebsite.%20%7D%0A%20%20OPTIONAL%20%7B%0A%20%20%20%20%3Fitem%20%5Eschema%3Aabout%20%3Farticle.%0A%20%20%20%20%3Farticle%20schema%3AisPartOf%20_%3Ab64.%0A%20%20%20%20_%3Ab64%20wikibase%3AwikiGroup%20%22wikipedia%22.%0A%20%20%20%20%3Farticle%20schema%3AinLanguage%20%3Flang%3B%0A%20%20%20%20%20%20schema%3Aname%20%3Farticlename.%0A%20%20%20%20FILTER(((%3Flang%20%3D%20%22%5BAUTO_LANGUAGE%5D%22)%20%7C%7C%20(%3Flang%20%3D%20%22en%22))%20%7C%7C%20(%3Flang%20%3D%20%22de%22))%0A%20%20%7D%0A%20%20SERVICE%20wikibase%3Alabel%20%7B%0A%20%20%20%20bd%3AserviceParam%20wikibase%3Alanguage%20%22%5BAUTO_LANGUAGE%5D%2Cen%22.%0A%20%20%20%20%3Fitem%20rdfs%3Alabel%20%3FitemLabel%3B%0A%20%20%20%20%20%20schema%3Adescription%20%3FitemDescription.%0A%20%20%7D%0A%7D%0AORDER%20BY%20(UCASE(%3FitemLabel))">… on Wikidata and Wikipedia <small>(Press the run button in the sidebar to get results)</small></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
Reference in New Issue
Block a user