root/branch/ggpolo/umitDB/Search.py @ 1355

Revision 1355, 12.2 kB (checked in by ggpolo, 6 years ago)

Added About window for Network Inventory. Searches on database for changes now too.

Line 
1# Copyright (C) 2007 Insecure.Com LLC.
2#
3# Author:  Guilherme Polo <ggpolo@gmail.com>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18# USA
19
20from umitDB.Utils import debug
21from umitDB.Connection import ConnectDB
22from umitDB.Retrieve import CompositeRetrieve
23
24from umitCore.I18N import _
25
26change_text = (_("change"), _("changes"))
27port_text = (_("port"), _("ports"), _("service"))
28comparison = ("<", ">", "==", ">=", "<=", "!=")
29
30def perform_comparison(ports, compare, decider):
31    """
32    Compare ports against decider using especified comparation.
33    """
34    decider = int(decider)
35   
36    # ports are numbered in ascendent order.
37    for port in ports:
38        if compare == '>':
39            if decider <= port[0]:
40                return True
41        if compare == '<':
42            if decider < port[0]:
43                return False
44            elif decider > port[0]:
45                return True
46        if compare == '!=':
47            if port[0] == decider:
48                return False
49        if compare == '==':
50            if port[0] == decider:
51                return True
52        if compare == '>=':
53            if port[0] >= decider:
54                return True
55        if compare == '<=':
56            if decider < port[0]:
57                if decider == port[0]:
58                    return True
59                return False
60            return True
61           
62    if compare == '!=':
63        return True
64   
65    return False
66
67
68class SearchDB(ConnectDB, CompositeRetrieve):
69    """
70    Performs search on database.
71    """
72    search_meths = { "ports": "port_search",
73                     "changes": "changes_search"
74                     }
75       
76   
77    def __init__(self, db):
78        """
79        Expects an umit database.
80        """
81        ConnectDB.__init__(self, db)
82        CompositeRetrieve.__init__(self, self.conn, self.cursor)
83   
84   
85    def search(self, host_id, query):
86        """
87        Convenience method. Performs searches for host_id.
88        """
89        likely_category = query.split()[0]
90        meth = None
91       
92        if likely_category in port_text:
93            meth = self.search_meths["ports"]
94        elif likely_category in change_text:
95            meth = self.search_meths["changes"]
96
97        if meth:
98            res = getattr(self, meth)(host_id, query)
99            if res:
100                return res
101           
102        if self.search_for_hostname_for_host_from_db(host_id, query):
103            return _("Hostname")
104       
105        if self.search_for_osmatch_for_host_from_db(host_id, query):
106            return _("OS Match")
107           
108        if self.search_for_osclasses_for_host_from_db(host_id, query):
109            return _("OS Classes")
110       
111        if self.search_for_mac_for_host_from_db(host_id, query):
112            return _("MAC")
113       
114        if self.search_for_fingerprint_for_host_from_db(host_id, query):
115            return _("Fingerprint")
116       
117        # if I'm still here, no results were returned. I will try to do a
118        # port search then. Situation where this may happen:
119        # - Person searching doesnt know about port search syntax, so it does
120        #   a search like "mysql". The correct way would be: "service mysql".
121        res = self.port_search(host_id, port_text[0] + " " + query)
122        if res:
123            return res
124       
125       
126    def port_search(self, host_id, query):
127        """
128        Search for ports for host_id.
129        """
130        debug("Searching under Ports for %s for host_id %d.." % (query, 
131                                                                 host_id))
132        # a port search query is expected to be something like:
133        #  <port_text> <comparison OR Nothing> <something>
134       
135        query = query.split()
136        if len(query) < 2 or (not query[0] in port_text):
137            # bad syntax for port search
138            return None
139       
140        looking_for = query[1]
141        compare = None
142        if len(query) == 3:
143            # assuming second item is a comparion item
144            if query[1] in comparison:
145                # valid comparison especified
146                compare = query[1]
147           
148            looking_for = query[2]
149       
150       
151        results = ''
152        portnumber = False
153       
154        try:
155            int(looking_for)
156            portnumber = True
157        except ValueError:
158            portnumber = False
159            # will perform porttext search
160       
161        if portnumber:
162            # search for port with number in looking_for
163            if compare:
164                self.cursor.execute("SELECT port.portid FROM port \
165                           JOIN _host_port ON (_host_port.fk_port=port.pk) \
166                           WHERE _host_port.fk_host=?", (host_id, ))
167                search = self.cursor.fetchall()
168                search = perform_comparison(search, compare, looking_for)
169
170            else:
171
172                self.cursor.execute("SELECT port.portid FROM port \
173                           JOIN _host_port ON (_host_port.fk_port=port.pk) \
174                           WHERE _host_port.fk_host=? AND \
175                           port.portid LIKE ?", (host_id, '%'+looking_for+'%'))
176                search = self.cursor.fetchall()
177
178            if search:
179                results = _("Port number")
180        else:
181            # search for some text
182            pdata = self.get_portid_and_fks_for_host_from_db(host_id)
183            for pd in pdata:
184                res = self._search_port_text_for_pdata(pd[1], looking_for)
185                if res:
186                    results = res
187                    break
188       
189        return results
190   
191   
192    def _search_port_text_for_pdata(self, service_info_id, query):
193        """
194        """
195        bquery = '%' + query + '%'
196       
197        # Im supposing service name is more likely to be queried than
198        # service info. Also, there is just one field to compare against query
199        # in service_name.
200        service_name = self.cursor.execute("SELECT name FROM service_name \
201                        JOIN service_info ON \
202                        (service_info.fk_service_name=service_name.pk) WHERE \
203                        service_info.pk=? AND name LIKE ?", (service_info_id,
204                                                             bquery)).fetchall()
205        if service_name:
206            return _("Service name")
207       
208        service_info = self.cursor.execute("SELECT product, version, extrainfo \
209                        FROM service_info WHERE service_info.pk=? AND \
210                        (product LIKE ? OR version LIKE ? or \
211                        extrainfo LIKE ?)", (service_info_id, bquery, bquery,
212                                             bquery)).fetchall()
213        if service_info:
214            return _("Service info")
215   
216   
217    def changes_search(self, host_id, query):
218        """
219        Search in _inventory_changes for something like query for host_id.
220        """
221        debug("Searching under Inventory changes for %s for \
222host_id %d.." % (query, host_id))
223       
224        query = query.split()
225        if len(query) < 2 or (not query[0] in change_text):
226            # bad syntax for changes search
227            return None
228       
229        address_id = self.get_address_pk_for_host_from_db(host_id)
230       
231        looking_for = query[1:]
232
233        bquery = '%' + ' '.join(looking_for) + '%'
234       
235        self.cursor.execute("SELECT pk FROM _inventory_changes \
236                           WHERE fk_address=? AND short_description LIKE ?", 
237                           (address_id, bquery))
238        results = self.cursor.fetchall()
239       
240        if results:
241            return _("Changes")
242       
243   
244    def search_for_hostname_for_host_from_db(self, host_id, query):
245        """
246        Search for hostnames like query for host_id.
247        """
248        debug("Searching for hostname %s for host_id %d" % (query, host_id))
249       
250        hostname = self.cursor.execute("SELECT hostname.name FROM hostname \
251                        JOIN _host_hostname ON \
252                        (_host_hostname.fk_hostname=hostname.pk) WHERE \
253                        _host_hostname.fk_host=? AND hostname.name \
254                        LIKE ?", (host_id, '%' + query + '%')).fetchall()
255       
256        if hostname:
257            return True
258       
259                   
260    def search_for_osmatch_for_host_from_db(self, host_id, query):
261        """
262        Search for osmatch like query for host_id.
263        """
264        debug("Searching under OS Match for %s for host_id %d.." % (query, 
265                                                                    host_id))
266       
267        match = self.cursor.execute("SELECT name FROM osmatch WHERE \
268                                     fk_host=? AND name LIKE ?", 
269                                     (host_id, '%' + query + '%')).fetchall()
270        if match:
271            return True
272       
273   
274   
275    def search_for_osclasses_for_host_from_db(self, host_id, query):
276        """
277        Search for osclasses like query for host_id.
278        """
279        debug("Searching under OS Classes for %s for hots_id %d" % (query, 
280                                                                    host_id))
281       
282        bquery = '%' + query + '%'
283       
284        classes = self.cursor.execute("SELECT osclass.accuracy, osgen.gen, \
285                  osfamily.family, osvendor.vendor, ostype.type FROM osclass \
286                  JOIN osgen ON (osclass.fk_osgen = osgen.pk) \
287                  JOIN osfamily ON (osclass.fk_osfamily = osfamily.pk) \
288                  JOIN osvendor ON (osclass.fk_osvendor = osvendor.pk) \
289                  JOIN ostype ON (osclass.fk_ostype = ostype.pk) \
290                  WHERE osclass.fk_host = ? AND (osgen.gen LIKE ? OR \
291                  osfamily.family LIKE ? OR osvendor.vendor LIKE ? OR \
292                  ostype.type LIKE ?)", (host_id, bquery, bquery, 
293                                         bquery, bquery)).fetchall()
294       
295        if classes:
296            return True
297   
298   
299    def search_for_mac_for_host_from_db(self, host_id, query):
300        """
301        Search for MAC like query for host_id.
302        """
303        debug("Searching MAC address like %s for host_id %d" % (query, host_id))
304       
305        fk_address = self.cursor.execute("SELECT fk_address FROM _host_address \
306                                          WHERE fk_host = ?", 
307                                          (host_id, )).fetchall()
308       
309        mac = None
310        for fk in fk_address:
311            mac = self.cursor.execute("SELECT address FROM address WHERE \
312                                       type = 'mac' AND pk = ? AND address \
313                                       LIKE ?", (fk[0], 
314                                                 '%' + query + '%')).fetchone()
315            if mac:
316                return True
317   
318   
319    def search_for_fingerprint_for_host_from_db(self, host_id, query):
320        """
321        Search for query in fingerprint table for host_id.
322        """
323        debug("Searching for Fingerprint info like %s for \
324host_id %d" % (query, host_id))
325       
326        bquery = '%' + query + '%'
327        self.cursor.execute("SELECT tcp_sequence_class, \
328                             tcp_ts_sequence_class, ip_id_sequence_class\
329                             FROM fingerprint_info WHERE fk_host=? AND \
330                             (tcp_sequence_class LIKE ? OR \
331                             tcp_ts_sequence_class LIKE ? OR \
332                             ip_id_sequence_class LIKE ?)", (host_id,
333                                                             bquery, bquery,
334                                                             bquery))
335        fp = self.cursor.fetchall()
336        if fp:
337            return True
338   
Note: See TracBrowser for help on using the browser.