2016-10-26 13:11:19 +02:00
|
|
|
use asns::*;
|
2016-11-12 21:10:36 +01:00
|
|
|
use horrorshow::prelude::*;
|
2017-08-12 14:32:05 +02:00
|
|
|
use iron::{typemap, BeforeMiddleware};
|
2017-09-12 15:32:12 +02:00
|
|
|
use iron::headers::{Accept, CacheControl, CacheDirective, Vary};
|
2016-10-26 13:11:19 +02:00
|
|
|
use iron::mime::*;
|
|
|
|
use iron::modifiers::Header;
|
|
|
|
use iron::prelude::*;
|
|
|
|
use iron::status;
|
|
|
|
use router::Router;
|
|
|
|
use serde_json;
|
|
|
|
use std::net::IpAddr;
|
|
|
|
use std::str::FromStr;
|
2016-10-27 10:45:56 +02:00
|
|
|
use std::sync::{Arc, RwLock};
|
2017-09-12 15:32:12 +02:00
|
|
|
use unicase::UniCase;
|
2016-10-26 13:11:19 +02:00
|
|
|
|
|
|
|
const TTL: u32 = 86400;
|
|
|
|
|
|
|
|
struct ASNsMiddleware {
|
2016-10-27 16:37:07 +02:00
|
|
|
asns_arc: Arc<RwLock<Arc<ASNs>>>,
|
2016-10-26 13:11:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl typemap::Key for ASNsMiddleware {
|
|
|
|
type Value = Arc<ASNs>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ASNsMiddleware {
|
2016-10-27 16:37:07 +02:00
|
|
|
fn new(asns_arc: Arc<RwLock<Arc<ASNs>>>) -> ASNsMiddleware {
|
2016-10-27 10:45:56 +02:00
|
|
|
ASNsMiddleware { asns_arc: asns_arc }
|
2016-10-26 13:11:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BeforeMiddleware for ASNsMiddleware {
|
|
|
|
fn before(&self, req: &mut Request) -> IronResult<()> {
|
2017-04-27 22:27:21 +02:00
|
|
|
req.extensions
|
|
|
|
.insert::<ASNsMiddleware>(self.asns_arc.read().unwrap().clone());
|
2016-10-26 13:11:19 +02:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-12 21:10:36 +01:00
|
|
|
enum OutputType {
|
|
|
|
Json,
|
|
|
|
Html,
|
|
|
|
}
|
|
|
|
|
2016-10-26 13:11:19 +02:00
|
|
|
pub struct WebService;
|
|
|
|
|
|
|
|
impl WebService {
|
|
|
|
fn index(_: &mut Request) -> IronResult<Response> {
|
2017-08-12 14:32:05 +02:00
|
|
|
Ok(Response::with((
|
|
|
|
status::Ok,
|
|
|
|
Mime(
|
|
|
|
TopLevel::Text,
|
|
|
|
SubLevel::Plain,
|
|
|
|
vec![(Attr::Charset, Value::Utf8)],
|
|
|
|
),
|
|
|
|
Header(CacheControl(
|
|
|
|
vec![CacheDirective::Public, CacheDirective::MaxAge(TTL)],
|
|
|
|
)),
|
|
|
|
"See https://iptoasn.com",
|
|
|
|
)))
|
2016-10-26 13:11:19 +02:00
|
|
|
}
|
|
|
|
|
2016-11-12 21:10:36 +01:00
|
|
|
fn accept_type(req: &Request) -> OutputType {
|
|
|
|
let mut output_type = OutputType::Json;
|
|
|
|
if let Some(header_accept) = req.headers.get::<Accept>() {
|
|
|
|
for header in header_accept.iter() {
|
|
|
|
match header.item {
|
|
|
|
Mime(TopLevel::Text, SubLevel::Html, _) => {
|
|
|
|
output_type = OutputType::Html;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Mime(_, SubLevel::Json, _) => {
|
|
|
|
output_type = OutputType::Json;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
output_type
|
|
|
|
}
|
|
|
|
|
2017-08-12 14:32:05 +02:00
|
|
|
fn output_json(
|
|
|
|
map: serde_json::Map<String, serde_json::value::Value>,
|
|
|
|
cache_header: Header<CacheControl>,
|
2017-09-12 15:32:12 +02:00
|
|
|
vary_header: Header<Vary>,
|
2017-08-12 14:32:05 +02:00
|
|
|
) -> IronResult<Response> {
|
2016-11-12 21:10:36 +01:00
|
|
|
let json = serde_json::to_string(&map).unwrap();
|
2017-08-12 14:32:05 +02:00
|
|
|
let mime_json = Mime(
|
|
|
|
TopLevel::Application,
|
|
|
|
SubLevel::Json,
|
|
|
|
vec![(Attr::Charset, Value::Utf8)],
|
|
|
|
);
|
2017-09-12 15:32:12 +02:00
|
|
|
Ok(Response::with(
|
|
|
|
(status::Ok, mime_json, cache_header, vary_header, json),
|
|
|
|
))
|
2016-11-12 21:10:36 +01:00
|
|
|
}
|
|
|
|
|
2017-08-12 14:32:05 +02:00
|
|
|
fn output_html(
|
|
|
|
map: serde_json::Map<String, serde_json::value::Value>,
|
|
|
|
cache_header: Header<CacheControl>,
|
2017-09-12 15:32:12 +02:00
|
|
|
vary_header: Header<Vary>,
|
2017-08-12 14:32:05 +02:00
|
|
|
) -> IronResult<Response> {
|
|
|
|
let mime_html = Mime(
|
|
|
|
TopLevel::Text,
|
|
|
|
SubLevel::Html,
|
|
|
|
vec![(Attr::Charset, Value::Utf8)],
|
|
|
|
);
|
2016-11-12 21:10:36 +01:00
|
|
|
let html = html!{
|
|
|
|
head {
|
|
|
|
title { : "iptoasn lookup" }
|
2016-11-14 12:28:15 +01:00
|
|
|
meta(name="viewport", content="width=device-widthinitial-scale=1");
|
|
|
|
link(rel="stylesheet", href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css", integrity="sha384-AysaV+vQoT3kOAXZkl02PThvDr8HYKPZhNT5h/CXfBThSRXQ6jW5DO2ekP5ViFdi", crossorigin="anonymous");
|
2016-11-12 21:10:36 +01:00
|
|
|
style {
|
|
|
|
: "body { margin: 1em 4em }"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
body(class="container-fluid") {
|
|
|
|
header {
|
2017-07-27 17:47:01 +02:00
|
|
|
h1 { : format_args!("Information for IP address: {}", map.get("ip").unwrap().as_str().unwrap()) }
|
2016-11-12 21:10:36 +01:00
|
|
|
}
|
|
|
|
table {
|
|
|
|
tr {
|
|
|
|
th { : "Announced" }
|
2016-11-14 12:28:15 +01:00
|
|
|
td { : format_args!("{}", if map.get("announced")
|
|
|
|
.unwrap().as_bool().unwrap() { "Yes" } else { "No" }) }
|
2016-11-12 21:10:36 +01:00
|
|
|
}
|
|
|
|
@ if map.get("announced").unwrap().as_bool().unwrap() == true {
|
|
|
|
tr {
|
|
|
|
th { : "First IP" }
|
|
|
|
td { : format_args!("{}", map.get("first_ip")
|
2016-11-12 23:44:47 +01:00
|
|
|
.unwrap().as_str().unwrap()) }
|
2016-11-12 21:10:36 +01:00
|
|
|
}
|
|
|
|
tr {
|
|
|
|
th { : "Last IP" }
|
|
|
|
td { : format_args!("{}", map.get("last_ip")
|
2016-11-12 23:44:47 +01:00
|
|
|
.unwrap().as_str().unwrap()) }
|
2016-11-12 21:10:36 +01:00
|
|
|
}
|
|
|
|
tr {
|
|
|
|
th { : "AS Number" }
|
|
|
|
td { : format_args!("{}", map.get("as_number")
|
2016-11-12 23:44:47 +01:00
|
|
|
.unwrap().as_u64().unwrap()) }
|
2016-11-12 21:10:36 +01:00
|
|
|
}
|
|
|
|
tr {
|
|
|
|
th { : "AS Country code" }
|
|
|
|
td { : format_args!("{}", map.get("as_country_code")
|
2016-11-12 23:44:47 +01:00
|
|
|
.unwrap().as_str().unwrap()) }
|
2016-11-12 21:10:36 +01:00
|
|
|
}
|
|
|
|
tr {
|
|
|
|
th { : "AS Description" }
|
|
|
|
td { : format_args!("{}", map.get("as_description")
|
2016-11-12 23:44:47 +01:00
|
|
|
.unwrap().as_str().unwrap()) }
|
2016-11-12 21:10:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.into_string()
|
|
|
|
.unwrap();
|
2016-11-14 12:28:15 +01:00
|
|
|
let html = format!("<!DOCTYPE html>\n<html>{}</html>", html);
|
2017-09-12 15:32:12 +02:00
|
|
|
Ok(Response::with(
|
|
|
|
(status::Ok, mime_html, cache_header, vary_header, html),
|
|
|
|
))
|
2016-11-12 21:10:36 +01:00
|
|
|
}
|
|
|
|
|
2017-08-12 14:32:05 +02:00
|
|
|
fn output(
|
|
|
|
output_type: OutputType,
|
|
|
|
map: serde_json::Map<String, serde_json::value::Value>,
|
|
|
|
cache_header: Header<CacheControl>,
|
2017-09-12 15:32:12 +02:00
|
|
|
vary_header: Header<Vary>,
|
2017-08-12 14:32:05 +02:00
|
|
|
) -> IronResult<Response> {
|
2016-11-12 21:10:36 +01:00
|
|
|
match output_type {
|
2017-09-12 15:32:12 +02:00
|
|
|
OutputType::Json => Self::output_json(map, cache_header, vary_header),
|
|
|
|
_ => Self::output_html(map, cache_header, vary_header),
|
2016-11-12 21:10:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-26 13:11:19 +02:00
|
|
|
fn ip_lookup(req: &mut Request) -> IronResult<Response> {
|
2017-08-12 14:32:05 +02:00
|
|
|
let mime_text = Mime(
|
|
|
|
TopLevel::Text,
|
|
|
|
SubLevel::Plain,
|
|
|
|
vec![(Attr::Charset, Value::Utf8)],
|
|
|
|
);
|
|
|
|
let cache_header = Header(CacheControl(
|
|
|
|
vec![CacheDirective::Public, CacheDirective::MaxAge(TTL)],
|
|
|
|
));
|
2017-09-12 15:32:12 +02:00
|
|
|
let vary_header = Header(Vary::Items(vec![
|
|
|
|
UniCase::from_str("accept-encoding").unwrap(),
|
|
|
|
UniCase::from_str("accept").unwrap(),
|
|
|
|
]));
|
2016-10-26 13:11:19 +02:00
|
|
|
let ip_str = match req.extensions.get::<Router>().unwrap().find("ip") {
|
|
|
|
None => {
|
2017-08-12 14:32:05 +02:00
|
|
|
let response = Response::with((
|
|
|
|
status::BadRequest,
|
|
|
|
mime_text,
|
|
|
|
cache_header,
|
|
|
|
"Missing IP address",
|
|
|
|
));
|
2016-10-26 13:11:19 +02:00
|
|
|
return Ok(response);
|
|
|
|
}
|
|
|
|
Some(ip_str) => ip_str,
|
|
|
|
};
|
|
|
|
let ip = match IpAddr::from_str(ip_str) {
|
|
|
|
Err(_) => {
|
2017-08-12 14:32:05 +02:00
|
|
|
return Ok(Response::with((
|
|
|
|
status::BadRequest,
|
|
|
|
mime_text,
|
|
|
|
cache_header,
|
|
|
|
"Invalid IP address",
|
|
|
|
)));
|
2016-10-26 13:11:19 +02:00
|
|
|
}
|
|
|
|
Ok(ip) => ip,
|
|
|
|
};
|
|
|
|
let asns = req.extensions.get::<ASNsMiddleware>().unwrap();
|
2017-08-12 14:14:59 +02:00
|
|
|
let mut map = serde_json::Map::new();
|
2017-08-12 14:32:05 +02:00
|
|
|
map.insert(
|
|
|
|
"ip".to_string(),
|
|
|
|
serde_json::value::Value::String(ip_str.to_string()),
|
|
|
|
);
|
2016-10-26 13:11:19 +02:00
|
|
|
let found = match asns.lookup_by_ip(ip) {
|
|
|
|
None => {
|
2017-08-12 14:32:05 +02:00
|
|
|
map.insert(
|
|
|
|
"announced".to_string(),
|
|
|
|
serde_json::value::Value::Bool(false),
|
|
|
|
);
|
2017-09-12 15:32:12 +02:00
|
|
|
return Self::output(Self::accept_type(&req), map, cache_header, vary_header);
|
2016-10-26 13:11:19 +02:00
|
|
|
}
|
|
|
|
Some(found) => found,
|
|
|
|
};
|
2017-08-12 14:32:05 +02:00
|
|
|
map.insert(
|
|
|
|
"announced".to_string(),
|
|
|
|
serde_json::value::Value::Bool(true),
|
|
|
|
);
|
|
|
|
map.insert(
|
|
|
|
"first_ip".to_string(),
|
|
|
|
serde_json::value::Value::String(found.first_ip.to_string()),
|
|
|
|
);
|
|
|
|
map.insert(
|
|
|
|
"last_ip".to_string(),
|
|
|
|
serde_json::value::Value::String(found.last_ip.to_string()),
|
|
|
|
);
|
|
|
|
map.insert(
|
|
|
|
"as_number".to_string(),
|
|
|
|
serde_json::value::Value::Number(serde_json::Number::from(found.number)),
|
|
|
|
);
|
|
|
|
map.insert(
|
|
|
|
"as_country_code".to_string(),
|
|
|
|
serde_json::value::Value::String(found.country.clone()),
|
|
|
|
);
|
|
|
|
map.insert(
|
|
|
|
"as_description".to_string(),
|
|
|
|
serde_json::value::Value::String(found.description.clone()),
|
|
|
|
);
|
2017-09-12 15:32:12 +02:00
|
|
|
Self::output(Self::accept_type(&req), map, cache_header, vary_header)
|
2016-10-26 13:11:19 +02:00
|
|
|
}
|
|
|
|
|
2016-10-27 16:37:07 +02:00
|
|
|
pub fn start(asns_arc: Arc<RwLock<Arc<ASNs>>>, listen_addr: &str) {
|
2016-10-26 13:11:19 +02:00
|
|
|
let router = router!(index: get "/" => Self::index,
|
|
|
|
ip_lookup: get "/v1/as/ip/:ip" => Self::ip_lookup);
|
|
|
|
let mut chain = Chain::new(router);
|
2016-10-27 10:45:56 +02:00
|
|
|
let asns_middleware = ASNsMiddleware::new(asns_arc);
|
2016-10-26 13:11:19 +02:00
|
|
|
chain.link_before(asns_middleware);
|
|
|
|
warn!("webservice ready");
|
|
|
|
Iron::new(chain).http(listen_addr).unwrap();
|
|
|
|
}
|
|
|
|
}
|