1use native_tls::TlsConnector;
2use rayon::prelude::*;
3use std::collections::HashMap;
4use std::fmt;
5use std::io::{prelude::*, BufReader, BufWriter};
6use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream};
7use std::sync::{Arc, Mutex};
8use std::time::Duration;
9
10#[derive(Clone, Debug)]
60pub struct ServiceDetector {
61 pub dst_ip: IpAddr,
63 pub dst_name: String,
65 pub open_ports: Vec<u16>,
67 pub connect_timeout: Duration,
69 pub read_timeout: Duration,
71 pub accept_invalid_certs: bool,
75}
76
77impl ServiceDetector {
78 pub fn new() -> ServiceDetector {
80 ServiceDetector {
81 dst_ip: IpAddr::V4(Ipv4Addr::LOCALHOST),
82 dst_name: String::new(),
83 open_ports: vec![],
84 connect_timeout: Duration::from_millis(200),
85 read_timeout: Duration::from_secs(5),
86 accept_invalid_certs: false,
87 }
88 }
89 pub fn set_dst_ip(&mut self, dst_ip: IpAddr) {
91 self.dst_ip = dst_ip;
92 }
93 pub fn set_dst_name(&mut self, host_name: IpAddr) {
95 self.dst_ip = host_name;
96 }
97 pub fn set_open_ports(&mut self, open_ports: Vec<u16>) {
99 self.open_ports = open_ports;
100 }
101 pub fn add_open_port(&mut self, open_port: u16) {
103 self.open_ports.push(open_port);
104 }
105 pub fn set_connect_timeout(&mut self, connect_timeout: Duration) {
107 self.connect_timeout = connect_timeout;
108 }
109 pub fn set_read_timeout(&mut self, read_timeout: Duration) {
111 self.read_timeout = read_timeout;
112 }
113 pub fn set_accept_invalid_certs(&mut self, accept_invalid_certs: bool) {
115 self.accept_invalid_certs = accept_invalid_certs;
116 }
117 pub fn detect(&self, port_db: Option<PortDatabase>) -> HashMap<u16, String> {
121 detect_service(self, port_db.unwrap_or(PortDatabase::default()))
122 }
123 pub fn scan(&self, port_db: Option<PortDatabase>) -> ScanServiceResult {
125 let ports = self.detect(port_db).drain().collect::<Vec<(u16, String)>>();
126 ScanServiceResult {
127 dst_ip: self.dst_ip,
128 dst_name: self.dst_name.clone(),
129 ports,
130 }
131 }
132}
133
134fn detect_service(setting: &ServiceDetector, port_db: PortDatabase) -> HashMap<u16, String> {
135 let service_map: Arc<Mutex<HashMap<u16, String>>> = Arc::new(Mutex::new(HashMap::new()));
136 setting.clone().open_ports.into_par_iter().for_each(|port| {
137 let sock_addr: SocketAddr = SocketAddr::new(setting.dst_ip, port);
138 match TcpStream::connect_timeout(&sock_addr, setting.connect_timeout) {
139 Ok(stream) => {
140 stream
141 .set_read_timeout(Some(setting.read_timeout))
142 .expect("Failed to set read timeout.");
143 let mut reader = BufReader::new(&stream);
144 let mut writer = BufWriter::new(&stream);
145 let msg: String = if port_db.http_ports.contains(&port) {
146 write_head_request(&mut writer, setting.dst_ip.to_string());
147 let header = read_response(&mut reader);
148 parse_header(header)
149 } else if port_db.https_ports.contains(&port) {
150 let header = head_request_secure(
151 setting.dst_name.clone(),
152 port,
153 setting.accept_invalid_certs,
154 );
155 parse_header(header)
156 } else {
157 read_response(&mut reader).replace("\r\n", "")
158 };
159 service_map.lock().unwrap().insert(port, msg);
160 }
161 Err(e) => {
162 service_map.lock().unwrap().insert(port, e.to_string());
163 }
164 }
165 });
166 let result_map: HashMap<u16, String> = service_map.lock().unwrap().clone();
167 result_map
168}
169
170fn read_response(reader: &mut BufReader<&TcpStream>) -> String {
171 let mut msg = String::new();
172 match reader.read_to_string(&mut msg) {
173 Ok(_) => {}
174 Err(_) => {}
175 }
176 msg
177}
178
179fn parse_header(response_header: String) -> String {
180 let header_fields: Vec<&str> = response_header.split("\r\n").collect();
181 if header_fields.len() == 1 {
182 return response_header;
183 }
184 for field in header_fields {
185 if field.contains("Server:") {
186 return field.trim().to_string();
187 }
188 }
189 String::new()
190}
191
192fn write_head_request(writer: &mut BufWriter<&TcpStream>, _ip_addr: String) {
193 let msg = format!("HEAD / HTTP/1.0\r\n\r\n");
194 match writer.write(msg.as_bytes()) {
195 Ok(_) => {}
196 Err(_) => {}
197 }
198 writer.flush().unwrap();
199}
200
201fn head_request_secure(host_name: String, port: u16, accept_invalid_certs: bool) -> String {
202 if host_name.is_empty() {
203 return String::from("Error: Invalid host name");
204 }
205 let sock_addr: String = format!("{}:{}", host_name, port);
206 let connector = if accept_invalid_certs {
207 match TlsConnector::builder()
208 .danger_accept_invalid_certs(true)
209 .build()
210 {
211 Ok(c) => c,
212 Err(e) => return format!("Error: {}", e.to_string()),
213 }
214 } else {
215 match TlsConnector::new() {
216 Ok(c) => c,
217 Err(e) => return format!("Error: {}", e.to_string()),
218 }
219 };
220 let stream = match TcpStream::connect(sock_addr.clone()) {
221 Ok(s) => s,
222 Err(e) => return format!("Error: {}", e.to_string()),
223 };
224 match stream.set_read_timeout(Some(Duration::from_secs(10))) {
225 Ok(_) => {}
226 Err(e) => return format!("Error: {}", e.to_string()),
227 }
228 let mut stream = match connector.connect(host_name.as_str(), stream) {
229 Ok(s) => s,
230 Err(e) => return format!("Error: {}", e.to_string()),
231 };
232 let msg = format!("HEAD / HTTP/1.0\r\n\r\n");
233 match stream.write(msg.as_bytes()) {
234 Ok(_) => {}
235 Err(e) => return format!("Error: {}", e.to_string()),
236 }
237 let mut res = vec![];
238 match stream.read_to_end(&mut res) {
239 Ok(_) => {
240 let result = String::from_utf8_lossy(&res);
241 return result.to_string();
242 }
243 Err(e) => return format!("Error: {}", e.to_string()),
244 };
245}
246#[doc(hidden)]
250#[derive(Clone, Debug)]
251pub struct PortDatabase {
252 pub http_ports: Vec<u16>,
253 pub https_ports: Vec<u16>,
254}
255
256impl PortDatabase {
257 pub fn new() -> PortDatabase {
258 PortDatabase {
259 http_ports: vec![],
260 https_ports: vec![],
261 }
262 }
263 pub fn default() -> PortDatabase {
264 PortDatabase {
265 http_ports: vec![80, 8080],
266 https_ports: vec![443, 8443],
267 }
268 }
269}
270
271#[doc(hidden)]
273#[derive(Debug, Clone)]
274pub struct ScanServiceResult {
275 pub dst_ip: IpAddr,
276 pub dst_name: String,
277 pub ports: Vec<(u16, String)>,
278}
279impl fmt::Display for ScanServiceResult {
280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 write!(
285 f,
286 "[{} {} [ {:?} ]]",
287 self.dst_ip, self.dst_name, self.ports
288 )
289 }
290}