mirror of
https://codeberg.org/slatian/service.echoip-slatecave.git
synced 2025-07-16 14:03:28 +02:00
Compare commits
30 Commits
v1.2.2
...
8d055682b6
Author | SHA1 | Date | |
---|---|---|---|
8d055682b6 | |||
ff8d86ff1d | |||
ce7632d443 | |||
cf82db3e87 | |||
fecbe68c7a | |||
2e5a2408b4 | |||
3b4e6eba4b | |||
708fb9c0b3 | |||
0d711648a8 | |||
1863af50f8 | |||
52d2834e98 | |||
da391003e4 | |||
7e58423269 | |||
2657aae847 | |||
13cb85ac5a | |||
1a973e09a0 | |||
f799927f90 | |||
8695f0026f | |||
3b552dba8a | |||
1ce60d8291 | |||
b5097b5a03 | |||
610842abac | |||
35c71aba64 | |||
d79d949d65 | |||
b3f94b0d90 | |||
96207f3960 | |||
cd7a7fbe05 | |||
aaecdb84bb | |||
b08c98376c | |||
51877fc4c3 |
1538
Cargo.lock
generated
1538
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
27
Cargo.toml
27
Cargo.toml
@ -1,31 +1,30 @@
|
||||
[package]
|
||||
name = "echoip-slatecave"
|
||||
version = "1.2.2"
|
||||
version = "1.5.2"
|
||||
edition = "2021"
|
||||
authors = ["Slatian <baschdel@disroot.org>"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
lib-humus = { version="0.2", features=["axum-view+cookie"], git="https://codeberg.org/slatian/lib-humus.git" }
|
||||
lib-humus = { version="0.2", features=["axum-view+cookie"] }
|
||||
|
||||
axum = { version = "0.7", features = ["macros"] }
|
||||
axum-extra = { version = "0.9", features = ["cookie", "typed-header"] }
|
||||
axum-client-ip = "0.5"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
governor = "0.6"
|
||||
idna = "0.4"
|
||||
lazy_static = "1.4.0"
|
||||
axum-client-ip = "0.6"
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
governor = "0.8"
|
||||
idna = "1.0"
|
||||
parking_lot = "0.12"
|
||||
regex = "1.10"
|
||||
regex = "1.11"
|
||||
serde = { version = "1", features = ["derive","rc"] }
|
||||
tokio = { version = "1", features = ["macros","signal"] }
|
||||
tera = "1"
|
||||
toml = "0.8"
|
||||
tower = "0.4"
|
||||
tower-http = { version = "0.5", features = ["fs"] }
|
||||
trust-dns-proto = "0.23"
|
||||
trust-dns-resolver = { version = "0.23", features = ["dns-over-rustls","dns-over-https","dns-over-quic"] }
|
||||
maxminddb = "0.23"
|
||||
tower = "0.5"
|
||||
tower-http = { version = "0.6", features = ["fs"] }
|
||||
hickory-proto = "0.24"
|
||||
hickory-resolver = { version = "0.24", features = ["dns-over-rustls","dns-over-https","dns-over-quic","native-certs"] }
|
||||
maxminddb = "0.24"
|
||||
mime = "0.3"
|
||||
http = "1.0"
|
||||
http = "1.2"
|
||||
|
21
README.md
21
README.md
@ -1,9 +1,20 @@
|
||||
# echoip-slatecave
|
||||
|
||||
This is a service inspired by ifconfig.co, but built from scratch with a more useful feature set (in rust !!1!).
|
||||
This is a service inspired by ifconfig.co, but built from scratch with a more useful feature set. Currently live on [echoip.slatecave.net](https://echoip.slatecave.net).
|
||||
|
||||
It is Licensed under the AGPL-v3 license.
|
||||
|
||||
## Maintainence Mode
|
||||
|
||||
This project is in maintanance mode.
|
||||
|
||||
This means the following will happen:
|
||||
* Dependency updates
|
||||
* Bugfixes
|
||||
* Small quality of life improvements
|
||||
|
||||
But no active feature development by Slatian.
|
||||
|
||||
## Building
|
||||
|
||||
Simply run `cargo build` after cloning. The binary should be called `target/debug/echoip-slatecave`.
|
||||
@ -63,10 +74,16 @@ In addition to that the following fields are accessible from inside the template
|
||||
|
||||
The templates are covered by the AGPL as well, please share them with your users if you modified them.
|
||||
|
||||
### GeoLite2 database
|
||||
### Geolocation databases
|
||||
|
||||
For geolocation to work you need a MaxMind format database, for full functionality you need the GeoLite2-ASN and GeoLite2-City databses. Unfortunately you have to sign up with [MaxMind](https://maxmind.com) to obtain them. Once you have a license key there is a helper script in [contrib/maxmind-download.sh](contrib/maxmind-download.sh) that helps you with keeping the databse updated.
|
||||
|
||||
As an alternative to MaxMind there is also [DB-IP who offer their free databases without a login](https://db-ip.com/db/lite.php). You want the City and ASN databases in mmdb format.
|
||||
|
||||
**Don't forget to set the atttribution in the template configuration appropriately.**
|
||||
|
||||
See the file `templates/extra.toml`.
|
||||
|
||||
Since v1.0 echoip-slatecave reloads the databses when it rececieves a `USR1` signal.
|
||||
|
||||
## Security Considerations
|
||||
|
@ -1,5 +1,7 @@
|
||||
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::collections::HashMap;
|
||||
@ -84,12 +86,12 @@ impl Into<Protocol> for DnsProtocol {
|
||||
}
|
||||
|
||||
impl DnsResolverConfig {
|
||||
pub fn to_trust_resolver_config(
|
||||
pub fn to_hickory_resolver_config(
|
||||
&self
|
||||
) -> trust_dns_resolver::config::ResolverConfig {
|
||||
let mut resolver = trust_dns_resolver::config::ResolverConfig::new();
|
||||
) -> HickoryResolverConfig {
|
||||
let mut resolver = HickoryResolverConfig::new();
|
||||
for server in &self.servers {
|
||||
resolver.add_name_server(trust_dns_resolver::config::NameServerConfig{
|
||||
resolver.add_name_server(NameServerConfig{
|
||||
socket_addr: *server,
|
||||
protocol: self.protocol.clone().into(),
|
||||
tls_dns_name: self.tls_dns_name.clone().map(|s| s.to_string()),
|
||||
|
@ -6,7 +6,7 @@ use std::num::NonZeroU32;
|
||||
|
||||
mod dns;
|
||||
|
||||
pub use crate::config::dns::{DnsConfig, DnsProtocol, DnsResolverConfig};
|
||||
pub use crate::config::dns::{DnsConfig, DnsResolverConfig};
|
||||
|
||||
#[derive(Deserialize, Default, Clone)]
|
||||
pub struct EchoIpServiceConfig {
|
||||
@ -23,7 +23,6 @@ pub struct ServerConfig {
|
||||
pub ip_header: SecureClientIpSource,
|
||||
|
||||
pub allow_private_ip_lookup: bool,
|
||||
pub static_location: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
@ -53,7 +52,6 @@ impl Default for ServerConfig {
|
||||
listen_on: "127.0.0.1:3000".parse().unwrap(),
|
||||
ip_header: SecureClientIpSource::ConnectInfo,
|
||||
allow_private_ip_lookup: false,
|
||||
static_location: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
62
src/main.rs
62
src/main.rs
@ -17,13 +17,12 @@ use axum_client_ip::SecureClientIp;
|
||||
use axum_extra::headers;
|
||||
use axum_extra::TypedHeader;
|
||||
use clap::Parser;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize,Serialize};
|
||||
use tower::ServiceBuilder;
|
||||
use tower_http::services::ServeDir;
|
||||
use trust_dns_resolver::Name;
|
||||
use trust_dns_resolver::TokioAsyncResolver;
|
||||
use hickory_resolver::Name;
|
||||
use hickory_resolver::TokioAsyncResolver;
|
||||
|
||||
use tokio::signal::unix::{
|
||||
signal,
|
||||
@ -33,7 +32,9 @@ use tokio::task;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::net::IpAddr;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use lib_humus::TemplateEngineLoader;
|
||||
use lib_humus::read_toml_from_file;
|
||||
@ -67,6 +68,7 @@ pub struct SettingsQuery {
|
||||
format: Option<ResponseFormat>,
|
||||
lang: Option<String>,
|
||||
dns: Option<String>,
|
||||
dns_self_lookup: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
@ -82,6 +84,7 @@ pub struct IpResult {
|
||||
location: Option<LocationResult>,
|
||||
ip_info: AddressInfo,
|
||||
used_dns_resolver: Option<Arc<str>>,
|
||||
reverse_dns_disabled_for_privacy: bool,
|
||||
}
|
||||
|
||||
// We need this one to hide the partial lookup field when irelevant
|
||||
@ -119,7 +122,7 @@ struct CliArgs {
|
||||
#[arg(short, long)]
|
||||
config: Option<String>,
|
||||
#[arg(short, long)]
|
||||
listen_on: Option<String>,
|
||||
listen_on: Option<SocketAddr>,
|
||||
#[arg(short, long)]
|
||||
template_location: Option<String>,
|
||||
#[arg(short,long)]
|
||||
@ -227,7 +230,7 @@ async fn main() {
|
||||
for (key, resolver_config) in &config.dns.resolver {
|
||||
println!("Initalizing {} resolver ...", key);
|
||||
let resolver = TokioAsyncResolver::tokio(
|
||||
resolver_config.to_trust_resolver_config(),
|
||||
resolver_config.to_hickory_resolver_config(),
|
||||
Default::default()
|
||||
);
|
||||
dns_resolver_map.insert(key.clone(), resolver);
|
||||
@ -241,7 +244,7 @@ async fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
let listen_on = config.server.listen_on;
|
||||
let listen_on = cli_args.listen_on.unwrap_or(config.server.listen_on);
|
||||
let ip_header = config.server.ip_header.clone();
|
||||
|
||||
// Initialize shared state
|
||||
@ -357,6 +360,7 @@ async fn settings_query_middleware(
|
||||
lang: query.lang.unwrap_or("en".to_string()),
|
||||
available_dns_resolvers: derived_config.dns_resolver_selectables,
|
||||
dns_resolver_id: dns_resolver_id,
|
||||
dns_disable_self_lookup: !query.dns_self_lookup.unwrap_or(false),
|
||||
});
|
||||
next.run(req).await
|
||||
}
|
||||
@ -400,7 +404,7 @@ async fn handle_default_route(
|
||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||
Extension(settings): Extension<QuerySettings>,
|
||||
user_agent_header: Option<TypedHeader<headers::UserAgent>>,
|
||||
SecureClientIp(address): SecureClientIp
|
||||
SecureClientIp(client_ip): SecureClientIp
|
||||
) -> Response {
|
||||
|
||||
let state = Arc::clone(&arc_state);
|
||||
@ -411,12 +415,20 @@ async fn handle_default_route(
|
||||
search_query,
|
||||
false,
|
||||
settings,
|
||||
state
|
||||
state,
|
||||
&client_ip
|
||||
).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 {
|
||||
Some(TypedHeader(user_agent)) => Some(user_agent.to_string()),
|
||||
@ -432,22 +444,20 @@ async fn handle_default_route(
|
||||
)
|
||||
}
|
||||
|
||||
static ASN_REGEX: LazyLock<Regex> = LazyLock::new(|| { Regex::new(r"^[Aa][Ss][Nn]?\s*(\d{1,7})$").unwrap() });
|
||||
static VIA_REGEX: LazyLock<Regex> = LazyLock::new(|| { Regex::new(r"[Vv][Ii][Aa]\s+(\S+)").unwrap() });
|
||||
|
||||
async fn handle_search_request(
|
||||
search_query: String,
|
||||
this_should_have_been_an_ip: bool,
|
||||
settings: QuerySettings,
|
||||
arc_state: Arc<ServiceSharedState>,
|
||||
client_ip: &IpAddr,
|
||||
) -> Response {
|
||||
|
||||
let mut search_query = search_query.trim().to_string();
|
||||
let mut settings = settings;
|
||||
|
||||
lazy_static!{
|
||||
static ref ASN_REGEX: Regex = Regex::new(r"^[Aa][Ss][Nn]?\s*(\d{1,7})$").unwrap();
|
||||
static ref VIA_REGEX: Regex = Regex::new(r"[Vv][Ii][Aa]\s+(\S+)").unwrap();
|
||||
}
|
||||
|
||||
//If someone asked for an asn, give an asn answer
|
||||
if let Some(asn_cap) = ASN_REGEX.captures(&search_query) {
|
||||
if let Some(asn) = asn_cap.get(1).map_or(None, |m| m.as_str().parse::<u32>().ok()) {
|
||||
@ -474,7 +484,7 @@ async fn handle_search_request(
|
||||
|
||||
// Try to interpret as an IP-Address
|
||||
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
|
||||
@ -522,11 +532,12 @@ async fn handle_ip_route_with_path(
|
||||
Extension(settings): Extension<QuerySettings>,
|
||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||
extract::Path(query): extract::Path<String>,
|
||||
SecureClientIp(client_ip): SecureClientIp
|
||||
) -> Response {
|
||||
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 {
|
||||
return handle_search_request(query, true, settings, arc_state).await;
|
||||
return handle_search_request(query, true, settings, arc_state, &client_ip).await;
|
||||
}
|
||||
}
|
||||
|
||||
@ -534,6 +545,7 @@ async fn handle_ip_request(
|
||||
address: IpAddr,
|
||||
settings: QuerySettings,
|
||||
arc_state: Arc<ServiceSharedState>,
|
||||
client_ip: &IpAddr,
|
||||
) -> Response {
|
||||
|
||||
let state = Arc::clone(&arc_state);
|
||||
@ -541,6 +553,8 @@ async fn handle_ip_request(
|
||||
&address,
|
||||
&settings.lang,
|
||||
&settings.dns_resolver_id,
|
||||
settings.dns_disable_self_lookup,
|
||||
client_ip,
|
||||
&state).await;
|
||||
|
||||
state.templating_engine.render_view(
|
||||
@ -553,9 +567,19 @@ async fn get_ip_result(
|
||||
address: &IpAddr,
|
||||
lang: &String,
|
||||
dns_resolver_name: &Arc<str>,
|
||||
dns_disable_self_lookup: bool,
|
||||
client_ip: &IpAddr,
|
||||
state: &ServiceSharedState,
|
||||
) -> 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);
|
||||
|
||||
if !(ip_info.scope == AddressScope::Global || ip_info.scope == AddressScope::Shared) || ip_info.cast != AddressCast::Unicast {
|
||||
@ -567,6 +591,7 @@ async fn get_ip_result(
|
||||
location: None,
|
||||
ip_info: ip_info,
|
||||
used_dns_resolver: None,
|
||||
reverse_dns_disabled_for_privacy: reverse_dns_disabled_for_privacy,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -574,7 +599,7 @@ async fn get_ip_result(
|
||||
// do reverse lookup
|
||||
let mut hostname: Option<String> = 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) {
|
||||
hostname = simple_dns::reverse_lookup(&dns_resolver, &address).await;
|
||||
used_dns_resolver = Some(dns_resolver_name.clone());
|
||||
@ -605,6 +630,7 @@ async fn get_ip_result(
|
||||
location: location_result,
|
||||
ip_info: ip_info,
|
||||
used_dns_resolver: used_dns_resolver,
|
||||
reverse_dns_disabled_for_privacy: reverse_dns_disabled_for_privacy,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ pub struct QuerySettings {
|
||||
pub lang: String,
|
||||
pub available_dns_resolvers: Vec<Selectable>,
|
||||
pub dns_resolver_id: Arc<str>,
|
||||
pub dns_disable_self_lookup: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
|
@ -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;
|
||||
use trust_dns_proto::rr::{
|
||||
//! This module wraps the hickory_resolver library
|
||||
//! 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,
|
||||
record_type::RecordType,
|
||||
};
|
||||
use trust_dns_resolver::{
|
||||
use hickory_resolver::{
|
||||
error::ResolveError,
|
||||
error::ResolveErrorKind,
|
||||
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){
|
||||
match record {
|
||||
RData::AAAA(address) => opush(&mut result.aaaa, std::net::IpAddr::V6(address.0)),
|
||||
RData::AAAA(aaaa) => opush(&mut result.aaaa, std::net::IpAddr::V6(aaaa.0)),
|
||||
RData::ANAME(aname) => opush(&mut result.aname, aname.to_string()),
|
||||
RData::A(address) => opush(&mut result.a, std::net::IpAddr::V4(address.0)),
|
||||
RData::A(a) => opush(&mut result.a, std::net::IpAddr::V4(a.0)),
|
||||
RData::CAA(caa) => opush(&mut result.caa, caa.to_string()),
|
||||
RData::CNAME(cname) => opush(&mut result.cname, cname.to_string()),
|
||||
RData::MX(mx) => opush(&mut result.mx, MxRecord{
|
||||
|
@ -5,6 +5,9 @@
|
||||
<meta charset="utf-8">
|
||||
<title>{% block title %}{{ extra[view].title | default(value="…") }}{% endblock %} | {{extra.site_name|default(value="echoip")}}</title>
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||
<meta name="color-scheme" content="echoip-slatecave <https://codeberg.org/slatian/service.echoip-slatecave>">
|
||||
{% block robots_meta %}
|
||||
{% endblock robots_meta %}
|
||||
<!-- Open-Graph -->
|
||||
{% block metadata %}
|
||||
<meta name="description" property="og:description" content="{% block description %}{{ extra[view].description | default(value="One of the best echoip services") | escape_xml }}{% endblock %}" />
|
||||
@ -24,7 +27,11 @@
|
||||
<body>
|
||||
<header>
|
||||
<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 }}">
|
||||
<input type="search" name="query" autocomplete="on" maxlength="260"
|
||||
title="Search for an IP-Adress, Domain-Name, or ASN."
|
||||
|
@ -2,6 +2,8 @@
|
||||
{% import "helpers.html" as helper %}
|
||||
{% import "links.html" as links %}
|
||||
|
||||
{% block robots_meta %}<meta name="robots" content="noindex,nofollow">{% endblock %}
|
||||
|
||||
{% block title %}dig {{ data.query }}{% endblock %}
|
||||
{% block og_title %}dig {{ data.query }}{% endblock %}
|
||||
{% block h1 %}dig <code>{{ helper::breadcrumb_domain(extra=extra, name=data.query) }}</code> <small>via <a href="{{extra.base_url}}/dns_resolver/{{data.result.used_dns_resolver}}">{{data.result.used_dns_resolver}}</a></small>{% endblock %}
|
||||
|
@ -8,15 +8,26 @@ base_url="http://localhost:3000"
|
||||
stylesheet = "/style.css"
|
||||
|
||||
# URL to and mimetype of your favicon
|
||||
# favicon = ""
|
||||
# favicon_mimetype = "image/png"
|
||||
favicon = "/icon_64.png"
|
||||
favicon_mimetype = "image/png"
|
||||
# favicon_mimetype = "image/svg+xml"
|
||||
# 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
|
||||
# If you have not configured them, comment them out, the button will stay hidden
|
||||
v4_url="http://v4.localhost:3000/"
|
||||
v6_url="http://v6.localhost:3000/"
|
||||
|
||||
# Geolocation Attribution for MaxMind
|
||||
#geo_attribution_html="The Geolocation and ASN information is provided by the GeoLite2 database created by <a href='https://www.maxmind.com/'>MaxMind</a>."
|
||||
|
||||
# Geolocation Attribution for DB-IP
|
||||
#geo_attribution_html="The Geolocation and ASN information is provided by <a href='https://db-ip.com/'>DB-IP</a>."
|
||||
|
||||
|
||||
[404]
|
||||
# configure the 404 page, this is available for other pages too!
|
||||
# Use the template name as the section name.
|
||||
|
@ -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>
|
||||
{% endmacro dig %}
|
||||
|
||||
{% macro ip(extra, ip, text=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>
|
||||
{% 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 }}{% if with_self_lookup %}?dns_self_lookup=true{% endif %}"><code>{% if text %}{{ text }}{% else %}{{ ip }}{% endif %}</code></a>
|
||||
{% endmacro dig %}
|
||||
|
||||
{% macro breadcrumb_domain(extra, name) %}
|
||||
|
@ -1,6 +1,8 @@
|
||||
{% extends "ip.html" %}
|
||||
{% import "helpers.html" as helper %}
|
||||
|
||||
{% block robots_meta %}{# Allow indexing for landing page #}{% endblock %}
|
||||
|
||||
{% block title %}Your IP: {{ data.result.address }}{% endblock %}
|
||||
{% block og_title %}What is my IP-Address?{% endblock %}
|
||||
{% block h1 %}Your IPv{% if data.result.ip_info.is_v6_address %}6{% else %}4{% endif %}: <code>{{ data.result.address }}</code>{% endblock %}
|
||||
|
@ -2,6 +2,8 @@
|
||||
{% import "helpers.html" as helper %}
|
||||
{% import "links.html" as links %}
|
||||
|
||||
{% block robots_meta %}<meta name="robots" content="noindex,nofollow">{% endblock %}
|
||||
|
||||
{% block title %}{{ data.result.address }}{% endblock %}
|
||||
{% block og_title %}Lookup {{ data.result.address }}{% endblock %}
|
||||
{% block h1 %}Lookup <code>{{ data.result.address }}</code>{% endblock %}
|
||||
@ -18,6 +20,9 @@
|
||||
{% if r.hostname %}
|
||||
<dt>Hostname</dt>
|
||||
<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 %}
|
||||
{% if r.asn %}
|
||||
<dt><abbr="Autonomous System Number">ASN</abbr></dt>
|
||||
@ -31,43 +36,44 @@
|
||||
{% if r.location %}
|
||||
<section>
|
||||
<h2>Geolocation</h2>
|
||||
<dl>
|
||||
{{ helper::place_dl(place=r.location.continent, label="Continent") }}
|
||||
{{ helper::place_dl(place=r.location.country, label="Country") }}
|
||||
{% if r.location.country.iso_code | default(value="") != r.location.registered_country.iso_code | default(value="") %}
|
||||
{{ helper::place_dl(place=r.location.registered_country, label="Registered in") }}
|
||||
{% endif %}
|
||||
{% if r.location.country.iso_code | default(value="") != r.location.represented_country.iso_code | default(value="")%}
|
||||
{{ helper::place_dl(place=r.location.represented_country, label="Represents") }}
|
||||
{% endif %}
|
||||
{% if r.location.subdivisions %}
|
||||
{% for sd in r.location.subdivisions %}
|
||||
{{ helper::place_dl(place=sd, label="Subdivision", iso_code_prefix=r.location.country.iso_code|default(value="")) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{{ helper::place_dl(place=r.location.city, label="City") }}
|
||||
{% if r.location.postal_code %}
|
||||
<dt>Postal Code</dt>
|
||||
<dd>{{r.location.postal_code}}</dd>
|
||||
{% endif %}
|
||||
{% if r.location.time_zone %}
|
||||
<dt>Timezone</dt>
|
||||
<dd>{{r.location.time_zone}}</dd>
|
||||
{% endif %}
|
||||
{% if r.location.accuracy %}
|
||||
<dt>Accuracy</dt>
|
||||
<dd>~{{r.location.accuracy}}km</dd>
|
||||
{% endif %}
|
||||
{% if r.location.coordinates %}
|
||||
<dt>Coordinates</dt>
|
||||
<dd><a target="_blank" href="{{ links::map_link(lat=r.location.coordinates.lat, lon=r.location.coordinates.lon)}}">lat: {{r.location.coordinates.lat}}, lon: {{r.location.coordinates.lon}}</a></dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
<!--We have to put that there to comply with maxminds licensing-->
|
||||
<p><small>
|
||||
The GeoIP and ASN information is provided by the GeoLite2 database created by
|
||||
<a target="_blank" href="https://www.maxmind.com">MaxMind</a>.
|
||||
</small></p>
|
||||
{% if extra.geo_attribution_html %}
|
||||
<dl>
|
||||
{{ helper::place_dl(place=r.location.continent, label="Continent") }}
|
||||
{{ helper::place_dl(place=r.location.country, label="Country") }}
|
||||
{% if r.location.country.iso_code | default(value="") != r.location.registered_country.iso_code | default(value="") %}
|
||||
{{ helper::place_dl(place=r.location.registered_country, label="Registered in") }}
|
||||
{% endif %}
|
||||
{% if r.location.country.iso_code | default(value="") != r.location.represented_country.iso_code | default(value="")%}
|
||||
{{ helper::place_dl(place=r.location.represented_country, label="Represents") }}
|
||||
{% endif %}
|
||||
{% if r.location.subdivisions %}
|
||||
{% for sd in r.location.subdivisions %}
|
||||
{{ helper::place_dl(place=sd, label="Subdivision", iso_code_prefix=r.location.country.iso_code|default(value="")) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{{ helper::place_dl(place=r.location.city, label="City") }}
|
||||
{% if r.location.postal_code %}
|
||||
<dt>Postal Code</dt>
|
||||
<dd>{{r.location.postal_code}}</dd>
|
||||
{% endif %}
|
||||
{% if r.location.time_zone %}
|
||||
<dt>Timezone</dt>
|
||||
<dd>{{r.location.time_zone}}</dd>
|
||||
{% endif %}
|
||||
{% if r.location.accuracy %}
|
||||
<dt>Accuracy</dt>
|
||||
<dd>~{{r.location.accuracy}}km</dd>
|
||||
{% endif %}
|
||||
{% if r.location.coordinates %}
|
||||
<dt>Coordinates</dt>
|
||||
<dd><a target="_blank" href="{{ links::map_link(lat=r.location.coordinates.lat, lon=r.location.coordinates.lon)}}">lat: {{r.location.coordinates.lat}}, lon: {{r.location.coordinates.lon}}</a></dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
<p><small>{{extra.geo_attribution_html | safe}}</small></p>
|
||||
{% else %}
|
||||
<p><strong style="font-size: 2em">Please configure the <code>geo_attribution_html</code> key in the template extra configuration!</strong></p>
|
||||
<p>The geolocation information will then become visible.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endif %}
|
||||
{% block extra_content %}{% endblock %}
|
||||
|
@ -13,12 +13,19 @@
|
||||
* Type of Address: {{ helper::ip_info(ip_info=r.ip_info) }}
|
||||
{% if r.hostname -%}
|
||||
* Hostname: {{ r.hostname }}
|
||||
{%- elif r.reverse_dns_disabled_for_privacy %}
|
||||
* Hostname: Lookup disabled by default
|
||||
{%- endif %}
|
||||
{% if r.asn -%}
|
||||
* ASN: AS{{ r.asn.asn }}
|
||||
* AS Name: {{r.asn.name}}
|
||||
{%- 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 %}
|
||||
|
||||
## Geolocation
|
||||
@ -52,7 +59,7 @@ lat: {{r.location.coordinates.lat}}, lon: {{r.location.coordinates.lon}}
|
||||
=> {{ links::map_link(lat=r.location.coordinates.lat, lon=r.location.coordinates.lon)}}
|
||||
{%- endif %}
|
||||
|
||||
The GeoIP and ASN information is provided by the GeoLite2 database created by MaxMind.
|
||||
{{ extra.geo_attribution_html | default(value="Please configure the geo_attribution_html key in the template extra configuration.") | striptags }}
|
||||
{% endif -%}
|
||||
|
||||
{%- block extra_content %}{% endblock -%}
|
||||
|
@ -29,11 +29,11 @@
|
||||
{% macro asn_links(asn, org_name="") %}
|
||||
<p>Look up <code>AS{{asn}}</code>{% if org_name%} ({{org_name}}){% endif %} …</p>
|
||||
<ul class="link-list">
|
||||
<li><a target="_blank" href="https://bgp.he.net/AS{{asn | urlencode_strict}}">… on Hurricane Electric BGP Toolkit</a></li>
|
||||
<li><a target="_blank" href="https://radar.qrator.net/as{{asn | urlencode_strict}}">… 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 | urlencode_strict}}">… on search.censys.io <small>(10 query's per day, wants an account)</small></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://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://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 | urlencode_strict }}%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>
|
||||
{% endmacro asn_links %}
|
||||
|
||||
|
50
templates/static/icon.svg
Normal file
50
templates/static/icon.svg
Normal 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 |
BIN
templates/static/icon_128.png
Normal file
BIN
templates/static/icon_128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
BIN
templates/static/icon_32.png
Normal file
BIN
templates/static/icon_32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 950 B |
BIN
templates/static/icon_64.png
Normal file
BIN
templates/static/icon_64.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
@ -599,3 +599,10 @@ form.search {
|
||||
background: var(--button-bg);
|
||||
}
|
||||
|
||||
/* Custom icon style for sitename*/
|
||||
|
||||
.sitename > img {
|
||||
height: 1.2em;
|
||||
padding: 0 0.3ch;
|
||||
margin-bottom: -.2em;
|
||||
}
|
||||
|
Reference in New Issue
Block a user