1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#![deny(warnings)]
extern crate resolve;
use std::io::Error;
use resolve::resolve_host;
static HTTPBL_HOST : &'static str = "dnsbl.httpbl.org";
#[derive(Debug, Clone, PartialEq)]
pub enum VisitorClass {
SearchEngine{name: String},
Suspicious,
Harvester,
CommentSpammer,
SuspiciousHarvester,
SuspiciousCommentSpammer,
SuspiciousHarvesterCommentSpammer,
NotClassified,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Rating {
NotClassified,
Low,
Medium,
High,
Dangerous
}
#[derive(Debug)]
pub struct Visitor {
pub class: VisitorClass,
pub threat_rating: Rating,
pub last_activity: u16,
}
#[derive(Debug)]
pub enum HoneypotBlacklistError {
InvalidApiKey,
Std(Error),
}
impl From<Error> for HoneypotBlacklistError {
fn from(err: Error) -> HoneypotBlacklistError {
HoneypotBlacklistError::Std(err)
}
}
pub struct HoneypotBlacklist {
key: String,
}
impl HoneypotBlacklist {
pub fn new(key: String) -> HoneypotBlacklist {
HoneypotBlacklist {
key: key,
}
}
pub fn lookup(&self, ip: String) -> Result<Visitor, HoneypotBlacklistError> {
let reversed_ip = ip.rsplit('.').collect::<Vec<&str>>().join(".");
let query = format!("{}.{}.{}", self.key, reversed_ip, HTTPBL_HOST);
match resolve_host(query.as_ref()) {
Ok(mut addrs) => {
let addr: String = format!("{}", addrs.next().unwrap());
let visitor: Vec<_> = addr.split('.').collect();
Ok(Visitor {
class: self.get_visitor_class(visitor[3], visitor[2]),
threat_rating: self.get_threat_rating(visitor[2]),
last_activity: self.get_last_activity(visitor[1]),
})
}
Err(e) => Err(HoneypotBlacklistError::Std(e))
}
}
fn get_visitor_class(&self, class: &str, engine: &str) -> VisitorClass {
match class {
"0" => VisitorClass::SearchEngine { name: self.get_search_engine(engine) },
"1" => VisitorClass::Suspicious,
"2" => VisitorClass::Harvester,
"3" => VisitorClass::CommentSpammer,
"4" => VisitorClass::SuspiciousHarvester,
"5" => VisitorClass::SuspiciousCommentSpammer,
"6" => VisitorClass::SuspiciousHarvesterCommentSpammer,
_ => VisitorClass::NotClassified,
}
}
fn get_threat_rating(&self, value: &str) -> Rating {
let rating: u16 = value.parse().unwrap();
match rating {
1 ... 25 => Rating::Low,
26 ... 50 => Rating::Medium,
51 ... 75 => Rating::High,
76 ... 255 => Rating::Dangerous,
_ => Rating::NotClassified
}
}
fn get_last_activity(&self, value: &str) -> u16 {
let last_activity: u16 = value.parse().unwrap();
last_activity
}
fn get_search_engine(&self, engine_code: &str) -> String {
match engine_code {
"0" => "Undocumented".into(),
"1" => "AltaVista".into(),
"2" => "Ask".into(),
"3" => "Baidu".into(),
"4" => "Excite".into(),
"5" => "Google".into(),
"6" => "Looksmart".into(),
"7" => "Lycos".into(),
"8" => "MSN".into(),
"9" => "Yahoo".into(),
"10" => "Cuil".into(),
"11" => "InfoSeek".into(),
"12" => "Miscellaneous".into(),
_ => "NotFound".into()
}
}
}