diff --git a/configs.toml b/configs.toml index a2cf360..c47fd14 100644 --- a/configs.toml +++ b/configs.toml @@ -11,6 +11,9 @@ worker_threads=1 # base api url /{base_url}/routes base_url="backend" +# ipinfo.io API key, If the api-key works, the priority is with the api, if not with the offline database +ipinfo_api_key="" + # set directory of speedtest web front to server load on `/`. use empty retrun 404 speed_test_dir="./assets" # Write without suffix separator diff --git a/src/config/mod.rs b/src/config/mod.rs index 58dabba..5eda21c 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -16,6 +16,7 @@ pub struct ServerConfig { pub listen_port : i32, pub worker_threads: Value, pub base_url : String, + pub ipinfo_api_key : String, pub stats_password : String, pub speed_test_dir : String, pub database_type : String, @@ -36,6 +37,7 @@ impl Default for ServerConfig { listen_port: 8080, worker_threads: Value::from(1), base_url: "backend".to_string(), + ipinfo_api_key: "".to_string(), stats_password: "".to_string(), speed_test_dir: "".to_string(), database_type: "none".to_string(), diff --git a/src/ip/ip_info.rs b/src/ip/ip_info.rs index 3c79bec..2286c7e 100644 --- a/src/ip/ip_info.rs +++ b/src/ip/ip_info.rs @@ -2,7 +2,8 @@ use std::net::{IpAddr, Ipv4Addr}; use log::warn; use serde::{Deserialize, Serialize}; use serde_json::json; -use crate::ip::IPInfo; +use crate::config::SERVER_CONFIG; +use crate::http::http_client::HttpClient; use crate::ip::mmdb::mmdb_reader::MMDBReader; use crate::ip::mmdb::mmdb_record::MMDBResult; @@ -66,15 +67,22 @@ impl IPInfo { return json_string.to_string() } - //get isp info from db or api - if let Some(isp_info) = get_isp_info_from_db(raw_ip) { - ip_info_model.processedString = format!("{} - {}, {}",raw_ip,isp_info.as_name,isp_info.country_name); - ip_info_model.rawIspInfo.ip = raw_ip.to_string(); - ip_info_model.rawIspInfo.country = isp_info.country; - ip_info_model.rawIspInfo.organization = format!("{} {}",isp_info.asn,isp_info.as_name); - let json_string = json!(ip_info_model); - json_string.to_string() - } else { + //get isp info from api + if let Some(isp_info) = Self::get_isp_info_from_api(raw_ip).await { + ip_info_model.processedString = isp_info; + let json_string = json!(ip_info_model); + return json_string.to_string() + } + + //get isp info from db + if let Some(isp_info) = Self::get_isp_info_from_db(raw_ip) { + ip_info_model.processedString = format!("{} - {}, {}",raw_ip,isp_info.as_name,isp_info.country_name); + ip_info_model.rawIspInfo.ip = raw_ip.to_string(); + ip_info_model.rawIspInfo.country = isp_info.country; + ip_info_model.rawIspInfo.organization = format!("{} {}",isp_info.asn,isp_info.as_name); + let json_string = json!(ip_info_model); + json_string.to_string() + } else { //failed to get isp and send only ip ip_info_model.processedString = raw_ip.to_string(); @@ -105,13 +113,45 @@ impl IPInfo { None } -fn get_isp_info_from_db(ip : &str) -> Option { - if let Some(mut ipdb_reader) = MMDBReader::from("country_asn.mmdb") { - return ipdb_reader.lookup(ip) + fn get_isp_info_from_db(ip : &str) -> Option { + if let Some(mut ipdb_reader) = MMDBReader::from("country_asn.mmdb") { + return ipdb_reader.lookup(ip) + } + warn!("Unable to open country asn database file"); + None + } + + async fn get_isp_info_from_api(ip : &str) -> Option { + let config = SERVER_CONFIG.get().unwrap(); + let ip_info_token = config.ipinfo_api_key.clone(); + if ip_info_token.is_empty() { + return None + } + let mut client = HttpClient::open("https://ipinfo.io").await; + let request = format!( + "GET /{}/json?token={} HTTP/1.1\r\n\ + Host: ipinfo.io\r\n\r\n", + ip, + ip_info_token + ); + if let Some(res_body) = client.send_request_json(request.as_bytes()).await { + let isp = if let Some(org) = res_body.get("org") { + Some(org.as_str().unwrap()) + } else { + let asn_name = &res_body["asn"]["name"]; + if !asn_name.is_null() { + Some(asn_name.as_str().unwrap()) + } else { + None + } + }; + isp.as_ref()?; + let output = format!("{} - {}, {}",ip,isp.unwrap(),res_body.get("country").unwrap().as_str().unwrap()); + Some(output) + } else { + None + } } - warn!("Unable to open country asn database file"); - None -} fn is_private_ipv4(ip: &str) -> bool { if let Ok(ip_addr) = ip.parse::() {