1+ use crate :: types:: { MacPrefix , NmapMatch , NmapProbe , NmapService , ServicePattern } ;
2+ use anyhow:: Result ;
13use regex:: Regex ;
4+ use std:: collections:: HashMap ;
25use std:: fs;
36use std:: path:: Path ;
4- use anyhow:: Result ;
5- use std:: collections:: HashMap ;
6- use crate :: types:: { ServicePattern , NmapService , NmapProbe , NmapMatch , MacPrefix } ;
77
88lazy_static:: lazy_static! {
99 static ref MAC_PREFIXES : HashMap <String , String > = {
@@ -28,15 +28,15 @@ lazy_static::lazy_static! {
2828
2929 static ref SERVICE_PATTERNS : Vec <ServicePattern > = {
3030 let mut patterns = Vec :: with_capacity( 1000 ) ;
31-
31+
3232 patterns. extend( get_ssh_patterns( ) ) ;
3333 patterns. extend( get_http_patterns( ) ) ;
3434 patterns. extend( get_smtp_patterns( ) ) ;
3535 patterns. extend( get_imap_patterns( ) ) ;
3636 patterns. extend( get_ftp_patterns( ) ) ;
3737 patterns. extend( get_mysql_patterns( ) ) ;
3838 patterns. extend( get_redis_patterns( ) ) ;
39-
39+
4040 if let Ok ( probes) = load_nmap_probes( "src/assets/nmap-service-probes" ) {
4141 for probe in probes {
4242 for nmap_match in probe. matches {
@@ -77,108 +77,99 @@ pub fn get_services_by_port(port: u16) -> Option<&'static Vec<NmapService>> {
7777}
7878
7979pub fn get_ssh_patterns ( ) -> Vec < ServicePattern > {
80- vec ! [
81- ServicePattern {
82- name: "SSH" . to_string( ) ,
83- regex: Regex :: new( r"^SSH-\d\.\d" ) . unwrap( ) ,
84- probe: "SSH-2.0-OpenSSH_8.2p1\r \n " . to_string( ) ,
85- version_regex: Some ( Regex :: new( r"SSH-(\d\.\d)" ) . unwrap( ) ) ,
86- product_regex: Some ( Regex :: new( r"OpenSSH_([^\r\n]+)" ) . unwrap( ) ) ,
87- os_regex: Some ( Regex :: new( r"OpenSSH.*?([^\r\n]+)" ) . unwrap( ) ) ,
88- extra_info_regex: None ,
89- cpe_regex: None ,
90- } ,
91- ]
80+ vec ! [ ServicePattern {
81+ name: "SSH" . to_string( ) ,
82+ regex: Regex :: new( r"^SSH-\d\.\d" ) . unwrap( ) ,
83+ probe: "SSH-2.0-OpenSSH_8.2p1\r \n " . to_string( ) ,
84+ version_regex: Some ( Regex :: new( r"SSH-(\d\.\d)" ) . unwrap( ) ) ,
85+ product_regex: Some ( Regex :: new( r"OpenSSH_([^\r\n]+)" ) . unwrap( ) ) ,
86+ os_regex: Some ( Regex :: new( r"OpenSSH.*?([^\r\n]+)" ) . unwrap( ) ) ,
87+ extra_info_regex: None ,
88+ cpe_regex: None ,
89+ } ]
9290}
9391
9492pub fn get_http_patterns ( ) -> Vec < ServicePattern > {
95- vec ! [
96- ServicePattern {
97- name: "HTTP" . to_string( ) ,
98- regex: Regex :: new( r"^HTTP/\d\.\d" ) . unwrap( ) ,
99- probe: "HEAD / HTTP/1.1\r \n Host: localhost\r \n \r \n " . to_string( ) ,
100- version_regex: Some ( Regex :: new( r"HTTP/(\d\.\d)" ) . unwrap( ) ) ,
101- product_regex: Some ( Regex :: new( r"Server: ([^\r\n]+)" ) . unwrap( ) ) ,
102- os_regex: None ,
103- extra_info_regex: None ,
104- cpe_regex: None ,
105- } ,
106- ]
93+ vec ! [ ServicePattern {
94+ name: "HTTP" . to_string( ) ,
95+ regex: Regex :: new( r"^HTTP/\d\.\d" ) . unwrap( ) ,
96+ probe: "HEAD / HTTP/1.1\r \n Host: localhost\r \n \r \n " . to_string( ) ,
97+ version_regex: Some ( Regex :: new( r"HTTP/(\d\.\d)" ) . unwrap( ) ) ,
98+ product_regex: Some ( Regex :: new( r"Server: ([^\r\n]+)" ) . unwrap( ) ) ,
99+ os_regex: None ,
100+ extra_info_regex: None ,
101+ cpe_regex: None ,
102+ } ]
107103}
108104
109105pub fn get_ftp_patterns ( ) -> Vec < ServicePattern > {
110- vec ! [
111- ServicePattern {
112- name: "FTP" . to_string( ) ,
113- regex: Regex :: new( r"^220" ) . unwrap( ) ,
114- probe: "USER anonymous\r \n " . to_string( ) ,
115- version_regex: Some ( Regex :: new( r"220 ([^\r\n]+)" ) . unwrap( ) ) ,
116- product_regex: None ,
117- os_regex: None ,
118- extra_info_regex: None ,
119- cpe_regex: None ,
120- } ,
121- ]
106+ vec ! [ ServicePattern {
107+ name: "FTP" . to_string( ) ,
108+ regex: Regex :: new( r"^220" ) . unwrap( ) ,
109+ probe: "USER anonymous\r \n " . to_string( ) ,
110+ version_regex: Some ( Regex :: new( r"220 ([^\r\n]+)" ) . unwrap( ) ) ,
111+ product_regex: None ,
112+ os_regex: None ,
113+ extra_info_regex: None ,
114+ cpe_regex: None ,
115+ } ]
122116}
123117
124118pub fn get_mysql_patterns ( ) -> Vec < ServicePattern > {
125- vec ! [
126- ServicePattern {
127- name: "MySQL" . to_string( ) ,
128- regex: Regex :: new( r"^\x00" ) . unwrap( ) ,
129- probe: "\x4a \x00 \x00 \x00 \x0a \x35 \x2e \x35 \x2e \x35 " . to_string( ) ,
130- version_regex: Some ( Regex :: new( r"(\d+\.\d+\.\d+)" ) . unwrap( ) ) ,
131- product_regex: None ,
132- os_regex: None ,
133- extra_info_regex: None ,
134- cpe_regex: None ,
135- } ,
136- ]
119+ vec ! [ ServicePattern {
120+ name: "MySQL" . to_string( ) ,
121+ regex: Regex :: new( r"^\x00" ) . unwrap( ) ,
122+ probe: "\x4a \x00 \x00 \x00 \x0a \x35 \x2e \x35 \x2e \x35 " . to_string( ) ,
123+ version_regex: Some ( Regex :: new( r"(\d+\.\d+\.\d+)" ) . unwrap( ) ) ,
124+ product_regex: None ,
125+ os_regex: None ,
126+ extra_info_regex: None ,
127+ cpe_regex: None ,
128+ } ]
137129}
138130
139131pub fn get_smtp_patterns ( ) -> Vec < ServicePattern > {
140- vec ! [
141- ServicePattern {
142- name: "SMTP" . to_string( ) ,
143- regex: Regex :: new( r"^220.*ESMTP|^220.*SMTP|^220.*OpenSMTPD|^220.*Postfix|^220.*Sendmail|^220.*Microsoft" ) . unwrap( ) ,
144- probe: "EHLO localhost\r \n " . to_string( ) ,
145- version_regex: Some ( Regex :: new( r"220.*?([^\r\n]+)" ) . unwrap( ) ) ,
146- product_regex: Some ( Regex :: new( r"220.*?(OpenSMTPD|Postfix|Sendmail|Microsoft|ESMTP)" ) . unwrap( ) ) ,
147- os_regex: None ,
148- extra_info_regex: None ,
149- cpe_regex: None ,
150- } ,
151- ]
132+ vec ! [ ServicePattern {
133+ name: "SMTP" . to_string( ) ,
134+ regex: Regex :: new(
135+ r"^220.*ESMTP|^220.*SMTP|^220.*OpenSMTPD|^220.*Postfix|^220.*Sendmail|^220.*Microsoft" ,
136+ )
137+ . unwrap( ) ,
138+ probe: "EHLO localhost\r \n " . to_string( ) ,
139+ version_regex: Some ( Regex :: new( r"220.*?([^\r\n]+)" ) . unwrap( ) ) ,
140+ product_regex: Some (
141+ Regex :: new( r"220.*?(OpenSMTPD|Postfix|Sendmail|Microsoft|ESMTP)" ) . unwrap( ) ,
142+ ) ,
143+ os_regex: None ,
144+ extra_info_regex: None ,
145+ cpe_regex: None ,
146+ } ]
152147}
153148
154149pub fn get_imap_patterns ( ) -> Vec < ServicePattern > {
155- vec ! [
156- ServicePattern {
157- name: "IMAP" . to_string( ) ,
158- regex: Regex :: new( r"^\* OK.*IMAP|^\* OK.*Dovecot|^\* OK.*Cyrus" ) . unwrap( ) ,
159- probe: "A001 CAPABILITY\r \n " . to_string( ) ,
160- version_regex: Some ( Regex :: new( r"IMAP(\d+[^\s]*)" ) . unwrap( ) ) ,
161- product_regex: Some ( Regex :: new( r"(Dovecot|Cyrus)" ) . unwrap( ) ) ,
162- os_regex: None ,
163- extra_info_regex: None ,
164- cpe_regex: None ,
165- } ,
166- ]
150+ vec ! [ ServicePattern {
151+ name: "IMAP" . to_string( ) ,
152+ regex: Regex :: new( r"^\* OK.*IMAP|^\* OK.*Dovecot|^\* OK.*Cyrus" ) . unwrap( ) ,
153+ probe: "A001 CAPABILITY\r \n " . to_string( ) ,
154+ version_regex: Some ( Regex :: new( r"IMAP(\d+[^\s]*)" ) . unwrap( ) ) ,
155+ product_regex: Some ( Regex :: new( r"(Dovecot|Cyrus)" ) . unwrap( ) ) ,
156+ os_regex: None ,
157+ extra_info_regex: None ,
158+ cpe_regex: None ,
159+ } ]
167160}
168161
169162pub fn get_redis_patterns ( ) -> Vec < ServicePattern > {
170- vec ! [
171- ServicePattern {
172- name: "Redis" . to_string( ) ,
173- regex: Regex :: new( r"^\+PONG|^\+OK|^\$-|^\*[0-9]|^:[\d-]+|^ERR|^redis_version" ) . unwrap( ) ,
174- probe: "PING\r \n " . to_string( ) ,
175- version_regex: Some ( Regex :: new( r"redis_version:(\d+\.\d+\.\d+)" ) . unwrap( ) ) ,
176- product_regex: None ,
177- os_regex: None ,
178- extra_info_regex: None ,
179- cpe_regex: None ,
180- } ,
181- ]
163+ vec ! [ ServicePattern {
164+ name: "Redis" . to_string( ) ,
165+ regex: Regex :: new( r"^\+PONG|^\+OK|^\$-|^\*[0-9]|^:[\d-]+|^ERR|^redis_version" ) . unwrap( ) ,
166+ probe: "PING\r \n " . to_string( ) ,
167+ version_regex: Some ( Regex :: new( r"redis_version:(\d+\.\d+\.\d+)" ) . unwrap( ) ) ,
168+ product_regex: None ,
169+ os_regex: None ,
170+ extra_info_regex: None ,
171+ cpe_regex: None ,
172+ } ]
182173}
183174
184175pub fn load_nmap_services ( file_path : & str ) -> Result < Vec < NmapService > > {
@@ -198,7 +189,12 @@ pub fn load_nmap_services(file_path: &str) -> Result<Vec<NmapService>> {
198189 let parts: Vec < & str > = line. split_whitespace ( ) . collect ( ) ;
199190 if parts. len ( ) >= 3 {
200191 let name = parts[ 0 ] . to_string ( ) ;
201- let port = parts[ 1 ] . split ( '/' ) . next ( ) . unwrap_or ( "0" ) . parse :: < u16 > ( ) . unwrap_or ( 0 ) ;
192+ let port = parts[ 1 ]
193+ . split ( '/' )
194+ . next ( )
195+ . unwrap_or ( "0" )
196+ . parse :: < u16 > ( )
197+ . unwrap_or ( 0 ) ;
202198 let protocol = parts[ 1 ] . split ( '/' ) . nth ( 1 ) . unwrap_or ( "tcp" ) . to_string ( ) ;
203199 services. push ( NmapService {
204200 name,
@@ -248,7 +244,7 @@ pub fn load_nmap_probes(file_path: &str) -> Result<Vec<NmapProbe>> {
248244 if parts. len ( ) >= 3 {
249245 let service = parts[ 1 ] . to_string ( ) ;
250246 let pattern = parts[ 2 ] . trim_matches ( |c| c == 'm' || c == '|' ) . to_string ( ) ;
251-
247+
252248 if pattern. contains ( "**" ) || pattern. contains ( "\\ " ) || pattern. contains ( "^" ) {
253249 continue ;
254250 }
@@ -286,13 +282,23 @@ pub fn load_nmap_probes(file_path: &str) -> Result<Vec<NmapProbe>> {
286282 }
287283 } else if line. starts_with ( "totalwaitms " ) {
288284 if let Some ( probe) = & mut current_probe {
289- if let Ok ( ms) = line. split_whitespace ( ) . nth ( 1 ) . unwrap_or ( "6000" ) . parse :: < u64 > ( ) {
285+ if let Ok ( ms) = line
286+ . split_whitespace ( )
287+ . nth ( 1 )
288+ . unwrap_or ( "6000" )
289+ . parse :: < u64 > ( )
290+ {
290291 probe. total_wait_ms = ms;
291292 }
292293 }
293294 } else if line. starts_with ( "tcpwrappedms " ) {
294295 if let Some ( probe) = & mut current_probe {
295- if let Ok ( ms) = line. split_whitespace ( ) . nth ( 1 ) . unwrap_or ( "3000" ) . parse :: < u64 > ( ) {
296+ if let Ok ( ms) = line
297+ . split_whitespace ( )
298+ . nth ( 1 )
299+ . unwrap_or ( "3000" )
300+ . parse :: < u64 > ( )
301+ {
296302 probe. tcp_wrapped_ms = ms;
297303 }
298304 }
@@ -325,12 +331,9 @@ pub fn load_mac_prefixes(file_path: &str) -> Result<Vec<MacPrefix>> {
325331 let prefix = parts[ 0 ] . to_string ( ) ;
326332 let vendor = parts[ 1 ..] . join ( " " ) ;
327333
328- prefixes. push ( MacPrefix {
329- prefix,
330- vendor,
331- } ) ;
334+ prefixes. push ( MacPrefix { prefix, vendor } ) ;
332335 }
333336 }
334337
335338 Ok ( prefixes)
336- }
339+ }
0 commit comments