27 Commits
v1.2.0 ... v1.4

Author SHA1 Message Date
7e58423269 Update dependencies 2024-04-21 00:38:30 +02:00
2657aae847 Template fix 2024-04-21 00:26:15 +02:00
13cb85ac5a Added an opt-in to looking up own IP-Address 2024-04-21 00:00:04 +02:00
1a973e09a0 cargo update 2024-04-20 21:47:06 +02:00
f799927f90 Cargo update 2024-03-17 22:02:20 +01:00
8695f0026f lib-humus is now on crates.io 2024-02-11 14:15:13 +01:00
3b552dba8a Downgrade clap to 4.4.18 to support "older" rust versions 2024-02-11 12:38:33 +01:00
1ce60d8291 to_trust_resolver_config() -> to_hickory_resolver_config() 2024-02-11 11:52:33 +01:00
b5097b5a03 cargo update 2024-02-11 11:51:23 +01:00
610842abac Remove unused import 2024-02-11 11:51:08 +01:00
35c71aba64 Use absolute path for icons 2023-12-29 02:51:12 +01:00
d79d949d65 Use the more efficient icon 2023-12-29 02:49:04 +01:00
b3f94b0d90 cargo update 2023-12-29 02:41:33 +01:00
96207f3960 Added a way to display the icon as part of the sitename 2023-12-29 02:37:22 +01:00
cd7a7fbe05 Added a favicon 2023-12-29 02:26:32 +01:00
aaecdb84bb Release 1.2.4 hickory 2023-12-10 18:35:16 +01:00
b08c98376c Update trust_dns to hickory
It doesn't work yet because of:
https://github.com/hickory-dns/hickory-dns/issues/2108
2023-12-10 18:34:20 +01:00
51877fc4c3 1.2.3 quickfix don't encode as numbers (they are nubers internally so that is safe) 2023-12-10 10:43:19 +01:00
396bbdb348 Release 1.2.2 2023-12-10 10:18:31 +01:00
a582c74d18 urlencode queries to external services
and add crt.sh
2023-12-10 10:16:43 +01:00
e8a21ac95f Release 1.2.1 2023-12-09 23:26:12 +01:00
d706e7c614 Update to axum 0.7 2023-12-09 23:21:19 +01:00
0bffa0fd96 Update smaller dependencies 2023-12-09 12:01:00 +01:00
fb0ce1dc0b Update trust_dns to 23.2 2023-12-09 11:54:07 +01:00
a67631fa9b cargo update 2023-12-09 11:17:33 +01:00
636e10f786 Adapted to the new syncronous interface of the HumusEngine 2023-10-30 17:44:33 +01:00
0076db531a cargo update 2023-10-30 17:41:37 +01:00
20 changed files with 803 additions and 671 deletions

1205
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +1,31 @@
[package] [package]
name = "echoip-slatecave" name = "echoip-slatecave"
version = "1.2.0" version = "1.2.4"
edition = "2021" edition = "2021"
authors = ["Slatian <baschdel@disroot.org>"] authors = ["Slatian <baschdel@disroot.org>"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
lib-humus = { version="0.1", features=["axum-view+cookie"], git="https://codeberg.org/slatian/lib-humus.git" } lib-humus = { version="0.2", features=["axum-view+cookie"] }
axum = { version = "0.6", features = ["macros", "headers"] } axum = { version = "0.7", features = ["macros"] }
axum-extra = { version = "0.7", features = ["cookie"] } axum-extra = { version = "0.9", features = ["cookie", "typed-header"] }
axum-client-ip = "0.4" axum-client-ip = "0.6"
clap = { version = "4", features = ["derive"] } clap = { version = "4.5", features = ["derive"] }
governor = "0.5" governor = "0.6"
idna = "0.3" idna = "0.5"
lazy_static = "1.4.0" lazy_static = "1.4.0"
parking_lot = "0.12" parking_lot = "0.12"
regex = "1.7" regex = "1.10"
serde = { version = "1", features = ["derive","rc"] } serde = { version = "1", features = ["derive","rc"] }
tokio = { version = "1", features = ["macros","signal"] } tokio = { version = "1", features = ["macros","signal"] }
tera = "1" tera = "1"
toml = "0.7" toml = "0.8"
tower = "0.4" tower = "0.4"
tower-http = { version = "0.4", features = ["fs"] } tower-http = { version = "0.5", features = ["fs"] }
trust-dns-proto = "0.22" hickory-proto = "0.24"
trust-dns-resolver = { version = "0.22", features = ["dns-over-rustls","dns-over-https","dns-over-quic"] } hickory-resolver = { version = "0.24", features = ["dns-over-rustls","dns-over-https","dns-over-quic","native-certs"] }
maxminddb = "0.23" maxminddb = "0.24"
mime = "0.3" mime = "0.3"
http = "1.1"

View File

@ -1,5 +1,7 @@
use serde::{Deserialize,Serialize}; use serde::{Deserialize,Serialize};
use trust_dns_resolver::config::Protocol; use hickory_resolver::config::Protocol;
use hickory_resolver::config::ResolverConfig as HickoryResolverConfig;
use hickory_resolver::config::NameServerConfig;
use std::sync::Arc; use std::sync::Arc;
use std::collections::HashMap; use std::collections::HashMap;
@ -43,8 +45,8 @@ pub struct DnsResolverConfig {
pub tls_dns_name: Option<Arc<str>>, pub tls_dns_name: 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")] #[serde(default="default_true", alias="trust_nx_responses")]
pub trust_nx_responses: bool, pub trust_negative_responses: bool,
} }
fn zero() -> i32 { fn zero() -> i32 {
@ -84,16 +86,16 @@ impl Into<Protocol> for DnsProtocol {
} }
impl DnsResolverConfig { impl DnsResolverConfig {
pub fn to_trust_resolver_config( pub fn to_hickory_resolver_config(
&self &self
) -> trust_dns_resolver::config::ResolverConfig { ) -> HickoryResolverConfig {
let mut resolver = trust_dns_resolver::config::ResolverConfig::new(); let mut resolver = HickoryResolverConfig::new();
for server in &self.servers { for server in &self.servers {
resolver.add_name_server(trust_dns_resolver::config::NameServerConfig{ resolver.add_name_server(NameServerConfig{
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()),
trust_nx_responses: self.trust_nx_responses, trust_negative_responses: self.trust_negative_responses,
tls_config: None, tls_config: None,
bind_addr: self.bind_address, bind_addr: self.bind_address,
}); });

View File

@ -6,7 +6,7 @@ use std::num::NonZeroU32;
mod dns; mod dns;
pub use crate::config::dns::{DnsConfig, DnsProtocol, DnsResolverConfig}; pub use crate::config::dns::{DnsConfig, DnsResolverConfig};
#[derive(Deserialize, Default, Clone)] #[derive(Deserialize, Default, Clone)]
pub struct EchoIpServiceConfig { pub struct EchoIpServiceConfig {

View File

@ -1,28 +1,29 @@
use axum::{ use axum::{
body::Body,
extract::{ extract::{
self, self,
Query, Query,
State, State,
Extension, Extension,
}, },
headers,
http::Request,
handler::Handler, handler::Handler,
http::Request,
middleware::{self, Next}, middleware::{self, Next},
response::Response, response::Response,
Router, Router,
routing::get, routing::get,
TypedHeader,
}; };
use axum_client_ip::SecureClientIp; use axum_client_ip::SecureClientIp;
use axum_extra::headers;
use axum_extra::TypedHeader;
use clap::Parser; use clap::Parser;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use regex::Regex; use regex::Regex;
use serde::{Deserialize,Serialize}; use serde::{Deserialize,Serialize};
use tower::ServiceBuilder; use tower::ServiceBuilder;
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
use trust_dns_resolver::Name; use hickory_resolver::Name;
use trust_dns_resolver::TokioAsyncResolver; use hickory_resolver::TokioAsyncResolver;
use tokio::signal::unix::{ use tokio::signal::unix::{
signal, signal,
@ -66,6 +67,7 @@ pub struct SettingsQuery {
format: Option<ResponseFormat>, format: Option<ResponseFormat>,
lang: Option<String>, lang: Option<String>,
dns: Option<String>, dns: Option<String>,
dns_self_lookup: Option<bool>,
} }
#[derive(Deserialize, Serialize, Clone)] #[derive(Deserialize, Serialize, Clone)]
@ -81,6 +83,7 @@ pub struct IpResult {
location: Option<LocationResult>, location: Option<LocationResult>,
ip_info: AddressInfo, ip_info: AddressInfo,
used_dns_resolver: Option<Arc<str>>, used_dns_resolver: Option<Arc<str>>,
reverse_dns_disabled_for_privacy: bool,
} }
// We need this one to hide the partial lookup field when irelevant // We need this one to hide the partial lookup field when irelevant
@ -226,9 +229,9 @@ async fn main() {
for (key, resolver_config) in &config.dns.resolver { for (key, resolver_config) in &config.dns.resolver {
println!("Initalizing {} resolver ...", key); println!("Initalizing {} resolver ...", key);
let resolver = TokioAsyncResolver::tokio( let resolver = TokioAsyncResolver::tokio(
resolver_config.to_trust_resolver_config(), resolver_config.to_hickory_resolver_config(),
Default::default() Default::default()
).unwrap(); );
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(),
@ -311,21 +314,21 @@ async fn main() {
println!("Starting Server on {} ...",listen_on); println!("Starting Server on {} ...",listen_on);
axum::Server::bind(&listen_on) let listener = tokio::net::TcpListener::bind(&listen_on).await.unwrap();
.serve(app.into_make_service_with_connect_info::<std::net::SocketAddr>()) axum::serve(listener, app.into_make_service_with_connect_info::<std::net::SocketAddr>())
.await .await
.unwrap(); .unwrap();
} }
async fn settings_query_middleware<B>( async fn settings_query_middleware(
Query(query): Query<SettingsQuery>, Query(query): Query<SettingsQuery>,
Extension(config): Extension<config::EchoIpServiceConfig>, Extension(config): Extension<config::EchoIpServiceConfig>,
Extension(derived_config): Extension<DerivedConfiguration>, Extension(derived_config): Extension<DerivedConfiguration>,
cookie_header: Option<TypedHeader<headers::Cookie>>, cookie_header: Option<TypedHeader<headers::Cookie>>,
user_agent_header: Option<TypedHeader<headers::UserAgent>>, user_agent_header: Option<TypedHeader<headers::UserAgent>>,
mut req: Request<B>, mut req: Request<Body>,
next: Next<B> next: Next
) -> Response { ) -> Response {
let mut format = query.format; let mut format = query.format;
let mut dns_resolver_id = derived_config.default_resolver; let mut dns_resolver_id = derived_config.default_resolver;
@ -356,6 +359,7 @@ async fn settings_query_middleware<B>(
lang: query.lang.unwrap_or("en".to_string()), lang: query.lang.unwrap_or("en".to_string()),
available_dns_resolvers: derived_config.dns_resolver_selectables, available_dns_resolvers: derived_config.dns_resolver_selectables,
dns_resolver_id: dns_resolver_id, dns_resolver_id: dns_resolver_id,
dns_disable_self_lookup: !query.dns_self_lookup.unwrap_or(false),
}); });
next.run(req).await next.run(req).await
} }
@ -369,7 +373,7 @@ async fn not_found_handler(
state.templating_engine.render_view( state.templating_engine.render_view(
&settings, &settings,
View::NotFound, View::NotFound,
).await )
} }
async fn hello_world_handler( async fn hello_world_handler(
@ -382,9 +386,9 @@ async fn hello_world_handler(
&settings, &settings,
View::Message{ View::Message{
title: "Hey There!".to_string(), title: "Hey There!".to_string(),
message: "You,You are an awesome Creature!".to_string() message: "You are an awesome Creature!".to_string()
}, },
).await )
} }
@ -399,7 +403,7 @@ async fn handle_default_route(
State(arc_state): State<Arc<ServiceSharedState>>, State(arc_state): State<Arc<ServiceSharedState>>,
Extension(settings): Extension<QuerySettings>, Extension(settings): Extension<QuerySettings>,
user_agent_header: Option<TypedHeader<headers::UserAgent>>, user_agent_header: Option<TypedHeader<headers::UserAgent>>,
SecureClientIp(address): SecureClientIp SecureClientIp(client_ip): SecureClientIp
) -> Response { ) -> Response {
let state = Arc::clone(&arc_state); let state = Arc::clone(&arc_state);
@ -410,12 +414,20 @@ async fn handle_default_route(
search_query, search_query,
false, false,
settings, settings,
state state,
&client_ip
).await; ).await;
} }
} }
let result = get_ip_result(&address, &settings.lang, &"default".into(), &state).await; let result = get_ip_result(
&client_ip,
&settings.lang,
&settings.dns_resolver_id,
settings.dns_disable_self_lookup,
&client_ip,
&state,
).await;
let user_agent: Option<String> = match user_agent_header { let user_agent: Option<String> = match user_agent_header {
Some(TypedHeader(user_agent)) => Some(user_agent.to_string()), Some(TypedHeader(user_agent)) => Some(user_agent.to_string()),
@ -428,7 +440,7 @@ async fn handle_default_route(
result: result, result: result,
user_agent: user_agent, user_agent: user_agent,
} }
).await )
} }
@ -437,6 +449,7 @@ async fn handle_search_request(
this_should_have_been_an_ip: bool, this_should_have_been_an_ip: bool,
settings: QuerySettings, settings: QuerySettings,
arc_state: Arc<ServiceSharedState>, arc_state: Arc<ServiceSharedState>,
client_ip: &IpAddr,
) -> Response { ) -> Response {
let mut search_query = search_query.trim().to_string(); let mut search_query = search_query.trim().to_string();
@ -455,7 +468,7 @@ async fn handle_search_request(
return state.templating_engine.render_view( return state.templating_engine.render_view(
&settings, &settings,
View::Asn{asn: asn}, View::Asn{asn: asn},
).await )
} }
} }
@ -473,7 +486,7 @@ async fn handle_search_request(
// Try to interpret as an IP-Address // Try to interpret as an IP-Address
if let Ok(address) = search_query.parse() { if let Ok(address) = search_query.parse() {
return handle_ip_request(address, settings, arc_state).await; return handle_ip_request(address, settings, arc_state, client_ip).await;
} }
// Fall back to treating it as a hostname // Fall back to treating it as a hostname
@ -494,7 +507,7 @@ async fn handle_dns_resolver_route(
state.templating_engine.render_view( state.templating_engine.render_view(
&settings, &settings,
View::DnsResolverList, View::DnsResolverList,
).await )
} }
@ -508,12 +521,12 @@ async fn handle_dns_resolver_route_with_path(
state.templating_engine.render_view( state.templating_engine.render_view(
&settings, &settings,
View::DnsResolver{ config: resolver.clone() }, View::DnsResolver{ config: resolver.clone() },
).await )
} else { } else {
state.templating_engine.render_view( state.templating_engine.render_view(
&settings, &settings,
View::NotFound, View::NotFound,
).await )
} }
} }
@ -521,11 +534,12 @@ async fn handle_ip_route_with_path(
Extension(settings): Extension<QuerySettings>, Extension(settings): Extension<QuerySettings>,
State(arc_state): State<Arc<ServiceSharedState>>, State(arc_state): State<Arc<ServiceSharedState>>,
extract::Path(query): extract::Path<String>, extract::Path(query): extract::Path<String>,
SecureClientIp(client_ip): SecureClientIp
) -> Response { ) -> Response {
if let Ok(address) = query.parse() { if let Ok(address) = query.parse() {
return handle_ip_request(address, settings, arc_state).await return handle_ip_request(address, settings, arc_state, &client_ip).await
} else { } else {
return handle_search_request(query, true, settings, arc_state).await; return handle_search_request(query, true, settings, arc_state, &client_ip).await;
} }
} }
@ -533,6 +547,7 @@ async fn handle_ip_request(
address: IpAddr, address: IpAddr,
settings: QuerySettings, settings: QuerySettings,
arc_state: Arc<ServiceSharedState>, arc_state: Arc<ServiceSharedState>,
client_ip: &IpAddr,
) -> Response { ) -> Response {
let state = Arc::clone(&arc_state); let state = Arc::clone(&arc_state);
@ -540,21 +555,33 @@ async fn handle_ip_request(
&address, &address,
&settings.lang, &settings.lang,
&settings.dns_resolver_id, &settings.dns_resolver_id,
settings.dns_disable_self_lookup,
client_ip,
&state).await; &state).await;
state.templating_engine.render_view( state.templating_engine.render_view(
&settings, &settings,
View::Ip{result: result} View::Ip{result: result}
).await )
} }
async fn get_ip_result( async fn get_ip_result(
address: &IpAddr, address: &IpAddr,
lang: &String, lang: &String,
dns_resolver_name: &Arc<str>, dns_resolver_name: &Arc<str>,
dns_disable_self_lookup: bool,
client_ip: &IpAddr,
state: &ServiceSharedState, state: &ServiceSharedState,
) -> IpResult { ) -> IpResult {
let mut reverse_dns_disabled_for_privacy = false;
if state.config.dns.allow_reverse_lookup {
if address == client_ip && dns_disable_self_lookup {
reverse_dns_disabled_for_privacy = true;
}
}
let ip_info = AddressInfo::new(&address); let ip_info = AddressInfo::new(&address);
if !(ip_info.scope == AddressScope::Global || ip_info.scope == AddressScope::Shared) || ip_info.cast != AddressCast::Unicast { if !(ip_info.scope == AddressScope::Global || ip_info.scope == AddressScope::Shared) || ip_info.cast != AddressCast::Unicast {
@ -566,6 +593,7 @@ async fn get_ip_result(
location: None, location: None,
ip_info: ip_info, ip_info: ip_info,
used_dns_resolver: None, used_dns_resolver: None,
reverse_dns_disabled_for_privacy: reverse_dns_disabled_for_privacy,
} }
} }
} }
@ -573,7 +601,7 @@ async fn get_ip_result(
// do reverse lookup // do reverse lookup
let mut hostname: Option<String> = None; let mut hostname: Option<String> = None;
let mut used_dns_resolver: Option<Arc<str>> = None; let mut used_dns_resolver: Option<Arc<str>> = None;
if state.config.dns.allow_reverse_lookup { if state.config.dns.allow_reverse_lookup && !reverse_dns_disabled_for_privacy {
if let Some(dns_resolver) = &state.dns_resolvers.get(dns_resolver_name) { if let Some(dns_resolver) = &state.dns_resolvers.get(dns_resolver_name) {
hostname = simple_dns::reverse_lookup(&dns_resolver, &address).await; hostname = simple_dns::reverse_lookup(&dns_resolver, &address).await;
used_dns_resolver = Some(dns_resolver_name.clone()); used_dns_resolver = Some(dns_resolver_name.clone());
@ -604,6 +632,7 @@ async fn get_ip_result(
location: location_result, location: location_result,
ip_info: ip_info, ip_info: ip_info,
used_dns_resolver: used_dns_resolver, used_dns_resolver: used_dns_resolver,
reverse_dns_disabled_for_privacy: reverse_dns_disabled_for_privacy,
} }
} }
@ -634,7 +663,7 @@ async fn handle_dig_request(
state.templating_engine.render_view( state.templating_engine.render_view(
&settings, &settings,
View::Dig{ query: dig_query, result: dig_result} View::Dig{ query: dig_query, result: dig_result}
).await )
} }

View File

@ -1,5 +1,6 @@
use axum_client_ip::SecureClientIp; use axum_client_ip::SecureClientIp;
use axum::{ use axum::{
body::Body,
extract::Extension, extract::Extension,
http::{ http::{
Request, Request,
@ -40,11 +41,11 @@ pub fn build_rate_limiting_state(
Extension(arc_limiter) Extension(arc_limiter)
} }
pub async fn rate_limit_middleware<B>( pub async fn rate_limit_middleware(
SecureClientIp(address): SecureClientIp, SecureClientIp(address): SecureClientIp,
Extension(arc_limiter): Extension<Arc<SimpleRateLimiter<IpAddr>>>, Extension(arc_limiter): Extension<Arc<SimpleRateLimiter<IpAddr>>>,
req: Request<B>, req: Request<Body>,
next: Next<B> next: Next
) -> Response { ) -> Response {
let limiter = Arc::clone(&arc_limiter); let limiter = Arc::clone(&arc_limiter);

View File

@ -17,6 +17,7 @@ pub struct QuerySettings {
pub lang: String, pub lang: String,
pub available_dns_resolvers: Vec<Selectable>, pub available_dns_resolvers: Vec<Selectable>,
pub dns_resolver_id: Arc<str>, pub dns_resolver_id: Arc<str>,
pub dns_disable_self_lookup: bool,
} }
#[derive(Deserialize, Serialize, Clone)] #[derive(Deserialize, Serialize, Clone)]

View File

@ -1,17 +1,16 @@
/*
* This module wraps the trust_dns_resolver library
* to generate results thaat are ready for serializing
* or templating.
* It does not aim to be reusable for any other purpose,
* the trust_dns_resolver library already does that.
*/
use trust_dns_proto::op::response_code::ResponseCode; //! This module wraps the hickory_resolver library
use trust_dns_proto::rr::{ //! to generate results thaat are ready for serializing
//! or templating.
//! It does not aim to be reusable for any other purpose,
//! the hickory_resolver library already does that.
use hickory_proto::op::response_code::ResponseCode;
use hickory_proto::rr::{
RData, RData,
record_type::RecordType, record_type::RecordType,
}; };
use trust_dns_resolver::{ use hickory_resolver::{
error::ResolveError, error::ResolveError,
error::ResolveErrorKind, error::ResolveErrorKind,
lookup::Lookup, lookup::Lookup,
@ -121,9 +120,9 @@ pub fn set_default_if_none<T>(opt_vec: &mut Option<Vec<T>>) {
pub fn add_record_to_lookup_result(result: &mut DnsLookupResult, record: &RData){ pub fn add_record_to_lookup_result(result: &mut DnsLookupResult, record: &RData){
match record { match record {
RData::AAAA(address) => opush(&mut result.aaaa, std::net::IpAddr::V6(*address)), RData::AAAA(aaaa) => opush(&mut result.aaaa, std::net::IpAddr::V6(aaaa.0)),
RData::ANAME(aname) => opush(&mut result.aname, aname.to_string()), RData::ANAME(aname) => opush(&mut result.aname, aname.to_string()),
RData::A(address) => opush(&mut result.a, std::net::IpAddr::V4(*address)), RData::A(a) => opush(&mut result.a, std::net::IpAddr::V4(a.0)),
RData::CAA(caa) => opush(&mut result.caa, caa.to_string()), RData::CAA(caa) => opush(&mut result.caa, caa.to_string()),
RData::CNAME(cname) => opush(&mut result.cname, cname.to_string()), RData::CNAME(cname) => opush(&mut result.cname, cname.to_string()),
RData::MX(mx) => opush(&mut result.mx, MxRecord{ RData::MX(mx) => opush(&mut result.mx, MxRecord{

View File

@ -51,10 +51,10 @@ impl HumusView<QuerySettings, ResponseFormat> for View {
fn get_cookie_header(&self, settings: &QuerySettings) -> Option<String> { fn get_cookie_header(&self, settings: &QuerySettings) -> Option<String> {
Some( Some(
Cookie::build("dns_resolver",settings.dns_resolver_id.to_string()) Cookie::build(Cookie::new("dns_resolver",settings.dns_resolver_id.to_string()))
.path("/") .path("/")
.same_site(cookie::SameSite::Strict) .same_site(cookie::SameSite::Strict)
.finish() .build()
.to_string() .to_string()
) )
} }

View File

@ -24,7 +24,11 @@
<body> <body>
<header> <header>
<nav> <nav>
<a href="{{ extra.base_url }}" class="sitename">{{extra.site_name|default(value="echoip")}}</a> <a href="{{ extra.base_url }}" class="sitename">
{%- if extra.display_icon -%}
<img src="{{extra.display_icon}}" alt="">
{%- endif -%}
{{extra.site_name|default(value="echoip")}}</a>
<form class="search" method="GET" action="{{ extra.base_url }}"> <form class="search" method="GET" action="{{ extra.base_url }}">
<input type="search" name="query" autocomplete="on" maxlength="260" <input type="search" name="query" autocomplete="on" maxlength="260"
title="Search for an IP-Adress, Domain-Name, or ASN." title="Search for an IP-Adress, Domain-Name, or ASN."

View File

@ -8,10 +8,14 @@ base_url="http://localhost:3000"
stylesheet = "/style.css" stylesheet = "/style.css"
# URL to and mimetype of your favicon # URL to and mimetype of your favicon
# favicon = "" favicon = "/icon_64.png"
# favicon_mimetype = "image/png" favicon_mimetype = "image/png"
# favicon_mimetype = "image/svg+xml"
# favicon_mimetype = "image/jpeg" # favicon_mimetype = "image/jpeg"
# Icon to display next to the title
display_icon = "/icon_64.png"
# URLs to look up v4 and v6 addresses explicitly # URLs to look up v4 and v6 addresses explicitly
# If you have not configured them, comment them out, the button will stay hidden # If you have not configured them, comment them out, the button will stay hidden
v4_url="http://v4.localhost:3000/" v4_url="http://v4.localhost:3000/"

View File

@ -17,8 +17,8 @@
<a href="{{ self::dig_link(extra=extra, name=name) }}">{% if prefix %}{{ prefix }} {% endif %}{% if fqdn or name=="." %}{{ name }}{% else %}{{ name | trim_end_matches(pat=".") }}{% endif %}</a> <a href="{{ self::dig_link(extra=extra, name=name) }}">{% if prefix %}{{ prefix }} {% endif %}{% if fqdn or name=="." %}{{ name }}{% else %}{{ name | trim_end_matches(pat=".") }}{% endif %}</a>
{% endmacro dig %} {% endmacro dig %}
{% macro ip(extra, ip, text=false) %} {% macro ip(extra, ip, text=false, with_self_lookup=false) %}
<a href="{{ extra.base_url }}/ip/{{ ip | urlencode_strict | replace(from="%2e", to=".") | replace(from="%3a", to=":") | safe }}"><code>{% if text %}{{ text }}{% else %}{{ ip }}{% endif %}</code></a> <a href="{{ extra.base_url }}/ip/{{ ip | urlencode_strict | replace(from="%2e", to=".") | replace(from="%3a", to=":") | safe }}{% if with_self_lookup %}?dns_self_lookup=true{% endif %}"><code>{% if text %}{{ text }}{% else %}{{ ip }}{% endif %}</code></a>
{% endmacro dig %} {% endmacro dig %}
{% macro breadcrumb_domain(extra, name) %} {% macro breadcrumb_domain(extra, name) %}

View File

@ -18,6 +18,9 @@
{% if r.hostname %} {% if r.hostname %}
<dt>Hostname</dt> <dt>Hostname</dt>
<dd>{{ helper::dig(extra=extra, name=r.hostname) }}</dd> <dd>{{ helper::dig(extra=extra, name=r.hostname) }}</dd>
{% elif r.reverse_dns_disabled_for_privacy %}
<dt>Hostname</dt>
<dd>Lookup disabled by default: {{ helper::ip(ip=r.address, extra=extra, text="enable", with_self_lookup=true)}}</dd>
{% endif %} {% endif %}
{% if r.asn %} {% if r.asn %}
<dt><abbr="Autonomous System Number">ASN</abbr></dt> <dt><abbr="Autonomous System Number">ASN</abbr></dt>

View File

@ -13,12 +13,19 @@
* Type of Address: {{ helper::ip_info(ip_info=r.ip_info) }} * Type of Address: {{ helper::ip_info(ip_info=r.ip_info) }}
{% if r.hostname -%} {% if r.hostname -%}
* Hostname: {{ r.hostname }} * Hostname: {{ r.hostname }}
{%- elif r.reverse_dns_disabled_for_privacy %}
* Hostname: Lookup disabled by default
{%- endif %} {%- endif %}
{% if r.asn -%} {% if r.asn -%}
* ASN: AS{{ r.asn.asn }} * ASN: AS{{ r.asn.asn }}
* AS Name: {{r.asn.name}} * AS Name: {{r.asn.name}}
{%- endif -%} {%- endif -%}
{%- if r.reverse_dns_disabled_for_privacy %}
=> /ip/{{ data.result.address }}?dns_self_lookup=true Do a reverse DNS lookup
{% endif %}
{%- if r.location %} {%- if r.location %}
## Geolocation ## Geolocation

View File

@ -16,12 +16,13 @@
{% macro domain_name_links(name) %} {% macro domain_name_links(name) %}
<p>Look up <code>{{name}}</code></p> <p>Look up <code>{{name}}</code></p>
<ul class="link-list"> <ul class="link-list">
<li><a target="_blank" href="https://www.shodan.io/domain/{{ name }}">… on shodan.io <small>(limited query's per day, wants an account)</small></a></li> <li><a target="_blank" href="https://www.shodan.io/domain/{{ name | urlencode_strict }}">… on shodan.io <small>(limited 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={{ name }}">… 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={{ name | urlencode_strict }}">… on search.censys.io <small>(10 query's per day, wants an account)</small></a></li>
<li><a target="_blank" href="https://www.virustotal.com/gui/domain/{{ name }}">… on virustotal.com</a></li> <li><a target="_blank" href="https://www.virustotal.com/gui/domain/{{ name | urlencode_strict }}">… on virustotal.com</a></li>
<li><a target="_blank" href="https://observatory.mozilla.org/analyze/{{ name }}">… on the Mozilla Observatory (http and tls checks)</a></li> <li><a target="_blank" href="https://observatory.mozilla.org/analyze/{{ name | urlencode_strict }}">… on the Mozilla Observatory (http and tls checks)</a></li>
<li><a target="_blank" href="https://internet.nl/site/{{ name }}">… 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 }}">… 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>
</ul> </ul>
{% endmacro domain_name_links %} {% endmacro domain_name_links %}

50
templates/static/icon.svg Normal file
View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="48"
height="48"
viewBox="0 0 48 48"
version="1.1"
id="svg1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<linearGradient
id="linearGradient8">
<stop
style="stop-color:#fb9a00;stop-opacity:1;"
offset="0"
id="stop8" />
<stop
style="stop-color:#884f00;stop-opacity:1;"
offset="0.49966338"
id="stop10" />
<stop
style="stop-color:#be8700;stop-opacity:1;"
offset="1"
id="stop9" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient8"
id="linearGradient9"
x1="10.202637"
y1="35.241699"
x2="39.21582"
y2="12.833984"
gradientUnits="userSpaceOnUse" />
</defs>
<g
id="layer1">
<path
id="path2"
style="fill:url(#linearGradient9);fill-opacity:1;stroke-width:3.15427;stroke-linejoin:round;paint-order:stroke markers fill"
d="m 2,7 v 33.767595 l 1.586,0.0021 L 8.299716,45.41681 12.826,40.767584 H 46 V 7 Z" />
<path
id="rect1"
style="fill:#111111;stroke-width:3;stroke-linejoin:round;paint-order:stroke markers fill"
d="M 3 8 L 3 40 L 4.0019531 40 L 4 40.001953 L 8.2792969 44.205078 L 12.412109 40 L 45 40 L 45 8 L 3 8 z M 35.671875 11.712891 L 39.357422 11.712891 L 39.357422 36.287109 L 35.671875 36.287109 L 35.671875 17.033203 L 31.494141 21.363281 L 28.839844 18.804688 C 31.107109 16.462871 35.671875 11.712891 35.671875 11.712891 z M 8.6425781 21.542969 L 12.328125 21.542969 L 12.328125 25.228516 L 8.6425781 25.228516 L 8.6425781 21.542969 z M 20.927734 21.542969 L 24.615234 21.542969 L 24.615234 25.228516 L 20.927734 25.228516 L 20.927734 21.542969 z M 8.6425781 32.599609 L 12.328125 32.599609 L 12.328125 36.287109 L 8.6425781 36.287109 L 8.6425781 32.599609 z M 20.927734 32.599609 L 24.615234 32.599609 L 24.615234 36.287109 L 20.927734 36.287109 L 20.927734 32.599609 z " />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 950 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -599,3 +599,10 @@ form.search {
background: var(--button-bg); background: var(--button-bg);
} }
/* Custom icon style for sitename*/
.sitename > img {
height: 1.2em;
padding: 0 0.3ch;
margin-bottom: -.2em;
}