add IP and AS informations

This commit is contained in:
Miraty 2024-04-20 20:09:48 +02:00
parent 0fcc49cf26
commit 376a34ddd5
12 changed files with 204 additions and 7 deletions

48
Cargo.lock generated
View File

@ -59,6 +59,19 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
[[package]]
name = "asn-db2"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06cdaeaa434cbef439220bb84896ce0fd36b4452f0325f0a8e5cd21a08d98901"
dependencies = [
"csv",
"ipnet",
"serde",
"thiserror",
"tracing",
]
[[package]]
name = "async-stream"
version = "0.3.5"
@ -307,6 +320,27 @@ dependencies = [
"typenum",
]
[[package]]
name = "csv"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
dependencies = [
"csv-core",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70"
dependencies = [
"memchr",
]
[[package]]
name = "deranged"
version = "0.3.11"
@ -408,6 +442,18 @@ dependencies = [
"syn",
]
[[package]]
name = "dns-lookup"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5766087c2235fec47fafa4cfecc81e494ee679d0fd4a59887ea0919bfb0e4fc"
dependencies = [
"cfg-if",
"libc",
"socket2",
"windows-sys 0.48.0",
]
[[package]]
name = "either"
version = "1.11.0"
@ -1872,7 +1918,9 @@ dependencies = [
name = "services"
version = "0.1.0"
dependencies = [
"asn-db2",
"diesel",
"dns-lookup",
"fluent-templates",
"reqwest",
"rocket",

View File

@ -8,6 +8,8 @@ name = "updater"
path = "src/updater.rs"
[dependencies]
asn-db2 = { version = "0.2", default-features = false }
dns-lookup = { version = "2.0", default-features = false }
rocket = { version = "0.5", default-features = false }
rocket-accept-language = { version = "0.8", default-features = false }
serde = { version = "1.0", default-features = false }

View File

@ -19,5 +19,12 @@ table-ipv6 = { -ipv6 }
table-ipv4 = { -ipv4 }
table-ipv6-availability = { -ipv6 } availability
table-ipv4-availability = { -ipv4 } availability
table-asn = IP info
dl-ipinfo =
.subnet = Subnet
.asn = <abbr title="Autonomous System Number">ASN</title>
.as-owner = <abbr title="Autonomous System">AS</title> owner
.country = Country
field-url = Service URL

View File

@ -17,7 +17,14 @@ table-server = Serveur
table-time = Moment
table-ipv6 = { -ipv6 }
table-ipv4 = { -ipv4 }
table-ipv6-availability = Disponibilité { -ipv6 }
table-ipv4-availability = Disponibilité { -ipv4 }
table-ipv6-availability = Dispo { -ipv6 }
table-ipv4-availability = Dispo { -ipv4 }
table-asn = Infos IP
dl-ipinfo =
.subnet = Sous-réseau
.asn = <abbr title="Autonomous System Number" lang="en">ASN</title>
.as-owner = Propriétaire de l'<abbr title="Autonomous System" lang="en">AS</title>
.country = Pays
field-url = URL du service

View File

@ -0,0 +1,2 @@
ALTER TABLE "services" DROP "address_ipv6";
ALTER TABLE "services" DROP "address_ipv4";

View File

@ -0,0 +1,2 @@
ALTER TABLE "services" ADD COLUMN "address_ipv6" TEXT NOT NULL DEFAULT "";
ALTER TABLE "services" ADD COLUMN "address_ipv4" TEXT NOT NULL DEFAULT "";

View File

@ -81,6 +81,8 @@ pub async fn check(url: &str, ipv: Option<u8>) -> Result<Services, String> {
ipv4: "".to_string(),
availability_ipv6: "".to_string(),
availability_ipv4: "".to_string(),
address_ipv6: "".to_string(),
address_ipv4: "".to_string(),
})
}
Err(e) => Err(e.to_string()),

View File

@ -7,6 +7,7 @@ use services::{establish_connection, models::*, Software, DATABASE_URL};
use std::fs::File;
use asn_db2::{Ipv4Database, Ipv6Database};
use diesel::prelude::*;
use fluent_templates::FluentLoader;
use rocket::{
@ -18,7 +19,9 @@ use rocket::{
Request, Response, State,
};
use rocket_accept_language::{language, AcceptLanguage, LanguageIdentifier};
use serde::Serialize;
use serde_json::json;
use std::io::BufReader;
use tera::{Context, Tera, Value};
use unic_langid::{langid, subtags::Language};
@ -67,13 +70,30 @@ fn rocket() -> _ {
let mut tera = Tera::new("templates/*.html.tera").unwrap();
tera.register_function("fluent", FluentLoader::new(&*LOCALES));
let db6 = Ipv6Database::from_reader(BufReader::new(
File::open("ip2asn-v6.tsv").expect("unable to open ip2asn-v6.tsv"),
))
.unwrap();
let db4 = Ipv4Database::from_reader(BufReader::new(
File::open("ip2asn-v4.tsv").expect("unable to open ip2asn-v4.tsv"),
))
.unwrap();
rocket::build()
.manage(tera)
.manage(db6)
.manage(db4)
.attach(Shield::new().enable(Referrer::NoReferrer))
.attach(HttpHeaders)
.mount(
"/",
routes![list_services, list_scans, add_service_get, add_service_post, dl],
routes![
list_services,
list_scans,
add_service_get,
add_service_post,
dl
],
)
}
@ -82,9 +102,30 @@ fn dl() -> File {
File::open(DATABASE_URL).unwrap()
}
#[derive(Serialize, Debug)]
struct IpInfo {
ip: String,
subnet: String,
asn: String,
country: String,
as_owner: String,
}
#[derive(Serialize, Debug)]
struct TemplateServices {
url: String,
software: String,
server: String,
ipv6: String,
ipv4: String,
availability_ipv6: String,
availability_ipv4: String,
ip_info: Vec<IpInfo>,
}
#[get("/?<software>")]
fn list_services(
tera: &State<Tera>,
db6: &State<Ipv6Database>,
db4: &State<Ipv4Database>,
al: &AcceptLanguage,
software: Option<Strict<Software>>,
) -> RawHtml<String> {
@ -93,6 +134,55 @@ fn list_services(
request =
request.filter(services::schema::services::software.eq(s.to_string().to_lowercase()));
}
let services = request
.limit(300)
.select(Services::as_select())
.load(&mut establish_connection())
.unwrap();
let mut templates: Vec<TemplateServices> = vec![];
for service in &services {
let mut ip_info = vec![];
service
.address_ipv6
.split(',')
.filter(|ip| !ip.is_empty())
.for_each(|ip| {
let info = db6.lookup(ip.parse().unwrap()).unwrap();
ip_info.push(IpInfo {
ip: ip.to_string(),
subnet: info.subnet.to_string(),
asn: info.as_number.to_string(),
country: info.country.to_string(),
as_owner: info.owner.to_string(),
})
});
service
.address_ipv4
.split(',')
.filter(|ip| !ip.is_empty())
.for_each(|ip| {
let info = db4.lookup(ip.parse().unwrap()).unwrap();
ip_info.push(IpInfo {
ip: ip.to_string(),
subnet: info.subnet.to_string(),
asn: info.as_number.to_string(),
country: info.country.to_string(),
as_owner: info.owner.to_string(),
})
});
templates.push(TemplateServices {
url: service.url.to_string(),
software: service.software.to_string(),
server: service.server.to_string(),
ipv6: service.ipv6.to_string(),
ipv4: service.ipv4.to_string(),
availability_ipv6: service.availability_ipv6.to_string(),
availability_ipv4: service.availability_ipv4.to_string(),
ip_info,
})
}
RawHtml(
tera.render(
@ -100,10 +190,7 @@ fn list_services(
&gen_context(
al,
json!({
"services": &request.limit(300)
.select(Services::as_select())
.load(&mut establish_connection())
.unwrap(),
"services": &templates,
}),
),
)
@ -193,6 +280,8 @@ async fn add_service_post(
ipv4: "".to_string(),
availability_ipv6: "".to_string(),
availability_ipv4: "".to_string(),
address_ipv6: "".to_string(),
address_ipv4: "".to_string(),
})
.execute(&mut establish_connection())
.unwrap();

View File

@ -12,6 +12,8 @@ pub struct Services {
pub ipv4: String,
pub availability_ipv6: String,
pub availability_ipv4: String,
pub address_ipv6: String,
pub address_ipv4: String,
}
#[derive(Queryable, Selectable, Insertable, Serialize, Debug)]

View File

@ -19,6 +19,8 @@ diesel::table! {
ipv4 -> Text,
availability_ipv6 -> Text,
availability_ipv4 -> Text,
address_ipv6 -> Text,
address_ipv4 -> Text,
}
}

View File

@ -12,6 +12,8 @@ use diesel::ExpressionMethods;
use diesel::QueryDsl;
use diesel::RunQueryDsl;
use diesel::SelectableHelper;
use dns_lookup::lookup_host;
use std::net::IpAddr;
#[tokio::main]
async fn main() {
@ -65,8 +67,24 @@ async fn main() {
let scans_nb = installation_scans.len();
let ips: Vec<std::net::IpAddr> =
match lookup_host(reqwest::Url::parse(&service.url).unwrap().domain().unwrap()) {
Ok(i) => i,
Err(_) => vec![],
};
let service_new = check(&service.url, None).await.unwrap_or(service);
let mut addr_ipv6 = vec![];
let mut addr_ipv4 = vec![];
for ip in ips {
match ip {
IpAddr::V6(ip) => addr_ipv6.push(ip.to_string()),
IpAddr::V4(ip) => addr_ipv4.push(ip.to_string()),
};
}
diesel::update(services.find(&service_new.url))
.set((
url.eq(&service_new.url),
@ -78,6 +96,8 @@ async fn main() {
.eq(((ipv6_successes as f32 / scans_nb as f32 * 100.0) as u8).to_string()),
availability_ipv4
.eq(((ipv4_successes as f32 / scans_nb as f32 * 100.0) as u8).to_string()),
address_ipv6.eq(&addr_ipv6.join(",")),
address_ipv4.eq(&addr_ipv4.join(",")),
))
.execute(connection)
.unwrap();

View File

@ -16,6 +16,7 @@
<th>{{ fluent(key="table-ipv4", lang=ln) }}</th>
<th>{{ fluent(key="table-ipv6-availability", lang=ln) }}</th>
<th>{{ fluent(key="table-ipv4-availability", lang=ln) }}</th>
<th>{{ fluent(key="table-asn", lang=ln) }}</th>
</tr>
</thead>
<tbody>
@ -28,6 +29,19 @@
<td>{% if service.ipv4 == "ok" %}✓{% else %}—{% endif %}</td>
<td>{% if service.ipv6 == "" %}—{% else %}{{ service.availability_ipv6 }} %{% endif %}</td>
<td>{% if service.ipv4 == "" %}—{% else %}{{ service.availability_ipv4 }} %{% endif %}</td>
<td>
{% for ip_info in service.ip_info %}
<details>
<summary>{{ ip_info.ip }}</summary>
<dl>
<dt>{{ fluent(key="dl-ipinfo.subnet", lang=ln) }}</dt><dd>{{ ip_info.subnet }}<dd>
<dt>{{ fluent(key="dl-ipinfo.asn", lang=ln) }}</dt><dd>{{ ip_info.asn }}</dd>
<dt>{{ fluent(key="dl-ipinfo.as-owner", lang=ln) }}</dt><dd>{{ ip_info.as_owner }}<dd>
<dt>{{ fluent(key="dl-ipinfo.country", lang=ln) }}</dt><dd>{{ ip_info.country }}<dd>
</dl>
</details>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>