Merge pull request #56 from wuruilong01/master
[prads.git] / tools / prads-reporter
blob10f8df4de803ebffbfcdeb92df12880bb22c5c03
1 #!/usr/bin/python
2 #
3 # This script will generate a formatted report based on the data
4 # produced by Passive Real-time Asset Detection System (PRADS).
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 # TODO: DNS lookups
21 # TODO: ARP (mac, date)
22 # TODO: ICMP
23 # TODO: SERVICE/CLIENT port/service/application
24 # TODO: guess_os
26 # TODO: check flux
28 import re
29 import socket
30 import pycares
31 import select
33 version = '0.5'
34 date = '2015-05-20'
36 assetlogfile = 'prads-asset.log'
38 def wait_channel(channel):
39 while True:
40 read_fds, write_fds = channel.getsock()
41 if not read_fds and not write_fds:
42 break
43 timeout = channel.timeout()
44 if not timeout:
45 channel.process_fd(pycares.ARES_SOCKET_BAD, pycares.ARES_SOCKET_BAD)
46 continue
47 rlist, wlist, xlist = select.select(read_fds, write_fds, [], timeout)
48 for fd in rlist:
49 channel.process_fd(fd, pycares.ARES_SOCKET_BAD)
50 for fd in wlist:
51 channel.process_fd(pycares.ARES_SOCKET_BAD, fd)
53 def lookup_dns(address):
54 #try:
55 ai = socket.getaddrinfo(address, None)
56 if ai is not None:
57 return ai[0][-1][0]
58 #except:
59 # print("address %s error: %s" % address, )
61 def lookup_reversedns(address):
62 try:
63 ha = socket.gethostbyaddr(address)
64 if ha is not None:
65 return ha[0]
66 except socket.herror:
67 pass
69 def lookup_dns(channel, address, reverse=False, ip6=False):
70 def cb(results, error):
71 print("Should add this to queue: ")
72 print(results)
73 print(error)
74 if ip6:
75 if reverse:
76 channel.gethostbyaddr6(address, cb)
77 else:
78 channel.gethostbyname(address, socket.AF_INET6, cb)
79 else:
80 if reverse:
81 channel.gethostbyaddr(address, cb)
82 else:
83 channel.gethostbyname(address, socket.AF_INET, cb)
86 def do_lookups():
87 def cb(result, error):
88 print(result)
89 print(error)
90 channel = pycares.Channel()
91 channel.gethostbyname('google.com', socket.AF_INET, cb)
92 channel.query('google.com', pycares.QUERY_TYPE_A, cb)
93 channel.query('sip2sip.info', pycares.QUERY_TYPE_SOA, cb)
94 wait_channel(channel)
95 print("Done!")
97 def match_serviceline(line):
98 m = validpartial.match(line)
99 if m is None:
100 return None
101 return m.groups()
103 def guess_os(asset):
104 return "unknown", "unknown", 0, 0, 1
106 if __name__ == '__main__':
107 do_lookups()
108 exit(0)
110 # asset,vlan,port,proto,service,[service-info],distance,discovered
111 validline = re.compile('^([\w\.:]+),([\d]{1,4}),([\d]{1,5}),([\d]{1,3}),(\S+?),\[(.*)\],([\d]{1,3}),(\d{10})')
112 validpartial = re.compile('^\[(.*)\],([\d]{1,3}),(\d{10})')
114 fpinfo = re.compile('^SYN')
115 #Invalid match on S20:58:1:60:M1452,S,T,N,W7:.:unknown:unknown:link:pppoe (DSL):uptime:184hrs
116 validsyn = re.compile(':[\d]{2,4}:\d:.*:.*:.*:(\w+):(.*):link')
118 validservice = re.compile('^(\w+):(.*)$')
120 # asset = (
121 # <IP Address> => {
122 # ARP => [ $mac, $discovered, $vendor ],
123 # ICMP => ICMP,
124 # TCP => [ $port, $service, $app, $discovered ]
125 # },
128 # m = liner.match("85.105.159.158,0,33372,6,SYN,[S4:53:1:60:M1452,S,T,N,W1:.:Linux:2.6, seldom 2.4 (older, 2):link:pppoe (DSL):uptime:154hrs],11,1429563367")
131 asset = {}
133 with open(assetlogfile) as file:
134 for line in file:
135 l = line.split(',')
136 ip, vlan, sport, proto, service = l[0:5]
137 s_info, distance, discovered = '','',''
138 if proto == 6:
139 proto = 'TCP'
140 elif proto == 17:
141 proto = 'UDP'
143 os, details = '',''
144 if fpinfo.match(service):
145 smatch = match_serviceline(','.join(l[5:]))
146 s_info, distance, discovered = smatch
147 fp = s_info.split(':')
148 os,details = fp[6], fp[7]
149 if service == 'SERVER' or service == 'CLIENT':
150 ms = validservice.match(s_info)
151 if ms is not None:
152 service_nfo = ms.group(1)
153 #ARP (info, discovered, vendor) ICMP,
154 if ip not in asset:
155 asset[ip] = {}
156 if service not in asset[ip]:
157 asset[ip][service] = [ (proto, sport, service, s_info, discovered, distance, os, details )]
158 else:
159 asset[ip][service].append( (proto, sport, service, s_info, discovered, distance, os, details) )
161 # now iterate over all assets and dump them
162 for ip in sorted(asset.keys()):
163 host = lookup_reversedns(ip)
164 if host is not None:
165 print("DNS: %s" % (host))
166 fwdip = lookup_dns(host)
167 else:
168 fwdip = None
169 if fwdip is None:
170 print(" (dnswall: no such domain)")
171 elif fwdip != ip:
172 print(" (%s)" % fwdip)
173 revhost = lookup_reversedns(fwdip)
174 if revhost != host:
175 print("[%s]" % revhost)
177 # and now the prads data..
178 os, desc, confidence, timestamp, flux = guess_os(asset[ip])
179 #print("OS: %s %s (%s%%) %s" % (os,desc,confidence,flux))