root/pm/trunk/umit/pm/manager/auditmanager.py @ 5553

Revision 5553, 29.8 kB (checked in by nopper, 3 years ago)

Dropping inj:: cfields and fixing run_dissectors.

Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# Copyright (C) 2009 Adriano Monteiro Marques
4#
5# Author: Francesco Piccinno <stack.box@gmail.com>
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
21"""
22Audit manager module
23"""
24
25import sys
26import os.path
27
28from xml.sax import handler, make_parser
29from xml.sax.saxutils import XMLGenerator
30from xml.sax.xmlreader import AttributesImpl
31
32from umit.pm.core.i18n import _
33from umit.pm.core.logger import log
34from umit.pm.core.bus import ServiceBus
35from umit.pm.core.auditutils import AuditOperation
36from umit.pm.manager.sessionmanager import ConnectionManager
37from umit.pm.core.atoms import Singleton, defaultdict, generate_traceback
38from umit.pm.core.const import PM_TYPE_STR, PM_TYPE_INT, PM_TYPE_INSTANCE,\
39                               PM_HOME
40from umit.pm.core.netconst import *
41
42###############################################################################
43# Decorators
44###############################################################################
45
46def coroutine(func):
47    def start(*args,**kwargs):
48        cr = func(*args,**kwargs)
49        cr.next()
50        return cr
51    return start
52
53###############################################################################
54# Configurations
55###############################################################################
56
57class ConfigurationsLoader(handler.ContentHandler):
58    def __init__(self):
59        self.data = ''
60        self.parse_pass = -1
61
62        self.opt_id = None
63        self.opt_desc = None
64        self.configuration = None
65        self.opt_dict = {}
66
67        self.trans = {
68            'bool'  : lambda x: x == '1',
69            'int'   : int,
70            'float' : float,
71            'str'   : str,
72        }
73
74    def startElement(self, name, attrs):
75        if name == 'configurations' and self.parse_pass == -1:
76            self.parse_pass = 0
77        elif name == 'configuration' and self.parse_pass == 0:
78            self.parse_pass = 1
79            name = attrs.get('name')
80
81            if name:
82                self.configuration = Configuration(name)
83
84        elif name in ('bool', 'str', 'int', 'float') and self.parse_pass == 1:
85            self.opt_id = attrs.get('id')
86            self.opt_desc = attrs.get('description') or None
87
88            self.parse_pass = 2
89            self.data = ''
90
91    def characters(self, ch):
92        if self.parse_pass == 2:
93            self.data += ch
94
95    def endElement(self, name):
96        if name == 'configurations' and self.parse_pass == 0:
97            self.parse_pass = -1
98        elif name == 'configuration' and self.parse_pass == 1:
99            if self.configuration:
100                self.opt_dict[self.configuration.get_name()] = \
101                    self.configuration
102                self.configuration = None
103
104            self.parse_pass = 0
105        elif name in ('bool', 'str', 'int', 'float') and self.parse_pass == 2:
106            try:
107                if self.configuration:
108                    self.configuration.update({self.opt_id : [
109                        self.trans[name](self.data),
110                        self.opt_desc]}
111                    )
112            finally:
113                self.data = ''
114                self.opt_id = None
115                self.opt_desc = None
116                self.parse_pass = 1
117
118class ConfigurationsWriter(object):
119    def startElement(self, names, attrs):
120        self.depth_idx += 1
121        self.writer.characters('  ' * self.depth_idx)
122        self.writer.startElement(names, attrs)
123
124    def endElement(self, name):
125        self.writer.endElement(name)
126        self.writer.characters('\n')
127        self.depth_idx -= 1
128
129    def __init__(self, fname, options):
130        # The commented code here is to enable write to file options with
131        # value same as the default. IMHO it's useless so I've commented it
132
133        #from PM.Gui.Plugins.Engine import PluginEngine
134
135        #orig_dict = {}
136
137        #for plug in PluginEngine().available_plugins:
138        #    if plug.audit_type == -1:
139        #        continue
140
141        #    for conf_name, conf_dict in plug.configurations:
142        #        orig_dict[conf_name] = conf_dict
143
144        output = open(fname, 'w')
145        self.depth_idx = -1
146        self.writer = XMLGenerator(output, 'utf-8')
147        self.writer.startDocument()
148
149        self.startElement('configurations', {}),
150        self.writer.characters('\n')
151
152        items = options.keys()
153        items.sort()
154
155        trans = {
156            bool  : 'bool',
157            int   : 'int',
158            float : 'float',
159            str   : 'str'
160        }
161
162        for key in items:
163            self.startElement('configuration', AttributesImpl({'name' : key}))
164            self.writer.characters('\n')
165
166            opts = options[key].items()
167            opts.sort()
168
169            for opt_id, (opt_val, opt_desc) in opts:
170                #if key in orig_dict and opt_id in orig_dict[key] and \
171                #   orig_dict[key][opt_id][0] == opt_val:
172                #    continue
173
174                try:
175                    self.startElement(trans[type(opt_val)],
176                                      AttributesImpl({
177                                          'id' : opt_id,
178                                          'description' : opt_desc
179                                      }))
180
181                    if isinstance(opt_val, bool):
182                        self.writer.characters(opt_val and '1' or '0')
183                    else:
184                        self.writer.characters(str(opt_val))
185
186                    self.endElement(trans[type(opt_val)])
187                except:
188                    continue
189
190            self.writer.characters('  ' * self.depth_idx)
191            self.endElement('configuration')
192
193        self.endElement('configurations')
194        self.writer.endDocument()
195        output.close()
196
197class Configuration(object):
198    def __init__(self, name, odict=None):
199        """
200        @param name a string representing the Configuration
201        @param odict options dictionary {'key' : [value, 'description' or None]}
202        """
203        self._name = name
204
205        if not odict:
206            self._dict = {}
207        else:
208            self._dict = odict
209
210    def __getitem__(self, x):
211        return self._dict[x][0]
212
213    def __setitem__(self, x, value):
214        tup = self._dict[x]
215
216        if isinstance(value, type(tup[0])):
217            self._dict[x] = (value, tup[1])
218        else:
219            raise Exception('value has different type')
220
221    def get_name(self): return self._name
222    def get_option(self, x):
223        """
224        @return a tuple (opt_value, opt_desc)
225        """
226        return self._dict[x]
227
228    def get_description(self, x):
229        return self._dict[x][1]
230
231    def keys(self): return self._dict.keys()
232    def items(self): return self._dict.items()
233    def update(self, new_dict): self._dict.update(new_dict)
234    def revupdate(self, new_dict):
235        new_dict.update(self._dict)
236        self._dict = new_dict
237
238    def __repr__(self):
239        return 'Conf: %s -> %s' % (self._name, self._dict)
240
241    name = property(get_name)
242
243###############################################################################
244# Implementation
245###############################################################################
246
247class AuditManager(Singleton):
248    """
249    This is a singleton classes that is used to track decoders/dissectors etc.
250    It's a singleton class that is used to dispatch packets.
251    """
252    def __init__(self):
253        self._output = None
254        # It seems that specifying {} * n doesn't create a new object
255        # but instead only create a new pointer to the same object.
256        # Here we need separated dict so we should declare them all
257        self._decoders = ({}, {}, {}, {}, {}, {}, {}, {})
258        self._injectors = ({}, {}, {}, {}, {}, {}, {}, {})
259        self._hooks = {
260            'pm::received'    : [],
261            'pm::handled'     : [],
262            'pm::decoded'     : [],
263            'pm::filter'      : [],
264            'pm::pre-forward' : [],
265            'pm::dispatcher'  : [],
266        }
267        self._configurations = {}
268
269        self.load_configurations()
270
271        self._global_conf = self.register_configuration('global', {
272            'debug' : [False, 'Turn out debugging'],
273        })
274
275        self._global_cfields = self.register_configuration('global.cfields', {
276            'username' : [PM_TYPE_STR, 'Account username'],
277            'password' : [PM_TYPE_STR, 'Account password'],
278            'banner'   : [PM_TYPE_STR, 'Service banner'],
279
280            'good_checksum' : [PM_TYPE_STR, 'Hex string representation of the '
281                               'good checksum for the packet. Set if the packet'
282                               ' has a wrong checksum'],
283            'reassembled_payload' : [PM_TYPE_STR, 'Used by audits that can '
284                                     'treassemble fragments of packets'],
285        })
286
287        self.add_decoder(APP_LAYER, PL_DEFAULT, self.__run_dissectors)
288
289    def __run_dissectors(self, mpkt):
290        if mpkt.flags & MPKT_DONT_DISSECT:
291            return
292
293        # TODO: Here we should add a callback to unset MPKT_IGNORE
294        # if the packet is directed to TARGET1 or TARGET2
295
296        self.run_hook_point('pm::handled', mpkt)
297
298        if mpkt.flags & MPKT_IGNORE:
299            return
300
301        if mpkt.l4_proto == NL_TYPE_TCP:
302            ret = APP_LAYER_TCP
303        elif mpkt.l4_proto == NL_TYPE_UDP:
304            ret = APP_LAYER_UDP
305        else:
306            ret = None
307
308        if ret is not None:
309            self.run_decoder(ret, mpkt.l4_src, mpkt)
310            self.run_decoder(ret, mpkt.l4_dst, mpkt)
311
312        self.run_hook_point('pm::decoded', mpkt)
313
314        mtu = mpkt.context and mpkt.context.get_mtu() or 1500
315        max_len = mtu - (mpkt.l2_len + mpkt.l3_len + mpkt.l4_len)
316
317        #log.debug('Max length for the payload is %d' % max_len)
318
319        if mpkt.data_len > max_len:
320            mpkt.inject = mpkt.data[max_len:]
321            mpkt.inject_len = mpkt.data_len - max_len
322            mpkt.inj_delta -= mpkt.data_len - max_len
323            mpkt.data_len = max_len
324
325        self.run_hook_point('pm::filter', mpkt)
326
327    # Configurations stuff
328
329    def load_configurations(self):
330        log.debug('Loading configurations from audits-conf.xml')
331
332        try:
333            handler = ConfigurationsLoader()
334            parser = make_parser()
335            parser.setContentHandler(handler)
336            parser.parse(os.path.join(PM_HOME, 'audits-conf.xml'))
337
338            self._configurations.update(handler.opt_dict)
339        except Exception, err:
340            log.warning('Error while loading audits-conf.xml. ' \
341                        'Using default options')
342
343    def write_configurations(self):
344        log.debug('Writing configurations to audits-conf.xml')
345
346        writer = ConfigurationsWriter(os.path.join(PM_HOME, 'audits-conf.xml'),
347                                      self._configurations)
348
349    def register_configuration(self, conf_name, conf_dict):
350        """
351        Register a configuration
352        @param conf_name a str for configuration root element
353        @param conf_dict a dictionary
354        @see Configuration()
355        """
356
357        if conf_name not in self._configurations:
358            conf = Configuration(conf_name, conf_dict)
359            self._configurations[conf_name] = conf
360
361            log.debug('Configuration %s registered.' % conf_name)
362        else:
363            conf = self._configurations[conf_name]
364            conf.revupdate(conf_dict)
365
366            log.debug('Configuration %s updated.' % conf_name)
367
368        return conf
369
370    def get_configuration(self, conf_name):
371        return self._configurations[conf_name]
372
373    def user_msg(self, msg, severity=5, facility=None):
374        """
375        @param msg the message to show to the user
376        @param severity 0 for emerg
377                        1 for alert
378                        2 for crit
379                        3 for err
380                        4 for warning
381                        5 for notice
382                        6 for info
383                        7 for debug
384                        8 for none
385        @param facility a str representing a facility
386        """
387        trans = ('emerg', 'alert', 'crit', 'err', 'warn', 'notice', 'info',
388                 'debug', 'none')
389
390        if facility:
391            out = '%s.%s %s' % (facility, trans[severity], msg)
392        else:
393            out = '%s %s' % (trans[severity], msg)
394
395        if self._global_conf['debug']:
396            print out
397        else:
398            if not self._output:
399                import umit.pm.gui.core.app
400                tab = umit.pm.gui.core.app.PMApp().\
401                      main_window.get_tab('StatusTab')
402                self._output = tab.status
403            self._output.info(out)
404
405    ############################################################################
406    # General hooks
407    ############################################################################
408
409    def register_hook_point(self, name):
410        """
411        Register a hook point
412        @param name the hook point name
413        @return bool True if registered correctly
414        """
415        if name in self._hooks:
416            return False
417
418        self._hooks[name] = []
419        return True
420
421    def deregister_hook_point(self, name):
422        try:
423            del self._hooks[name]
424            return True
425        except:
426            return False
427
428    def add_to_hook_point(self, name, callback, to=-1):
429        try:
430            if to < 0:
431                self._hooks[name].append(callback)
432            else:
433                self._hooks[name].insert(to, callback)
434            return True
435        except:
436            return False
437
438    def remove_from_hook_point(self, name, callback):
439        try:
440            self._hooks[name].remove(callback)
441            return True
442        except:
443            return False
444
445    def run_hook_point(self, name, *args, **kwargs):
446        idx = 0
447        log.debug('Starting hook cascade for %s' % name)
448        while name in self._hooks and idx < len(self._hooks[name]):
449            callback = self._hooks[name][idx]
450            log.debug('Callback %d is %s' % (idx, callback))
451            callback(*args, **kwargs)
452            idx += 1
453
454    ############################################################################
455    # Injectors
456    ############################################################################
457
458    def add_injector(self, level, type, injector):
459        """
460        Add a injector for the given level
461        @param level the level where the injector works on
462        @param type the type of injector
463        @param injector a callable object
464        """
465        log.debug("Registering injector %s for level %s with type %s" % \
466                  (injector, level, type))
467        self._injectors[level][type] = injector
468
469    def remove_injector(self, level, type, injector):
470        """
471        Remove a injector for the given level
472        @param level the level where the injector works on
473        @param type the type of injector
474        @param injector a callable object
475        """
476
477        assert self._injectors[level][type], injector
478        del self._injectors[level][type]
479        return True
480
481    def get_injector(self, level, type):
482        try:
483            return self._injectors[level][type]
484        except:
485            return None
486
487    ############################################################################
488    # Decoders stuff
489    ############################################################################
490
491    def add_decoder(self, level, type, decoder):
492        """
493        Add a decoder for the given level
494        @param level the level where the decoder works on
495        @param type the type of decoder
496        @param decoder a callable object
497        """
498        log.debug("Registering dissector %s for level %s with type %s" % \
499                  (decoder, level, type))
500        self._decoders[level][type] = (decoder, [], [])
501
502    def remove_decoder(self, level, type, decoder, force=True):
503        """
504        Remove a decoder for the given level
505        @param level the level where the decoder works on
506        @param type the type of decoder
507        @param decoder a callable object
508        @param force if force is True and post or pre hooks are set
509               remove anyway
510        """
511
512        tup = self._decoders[level][type]
513
514        if any(tup[1:]) and not force:
515                return False
516
517        del self._decoders[level][type]
518        return True
519
520    def add_decoder_hook(self, level, type, decoder_hook, post=0):
521        if type not in self._decoders[level]:
522            self._decoders[level][type] = (None, [], [])
523
524        self._decoders[level][type][post + 1].append(decoder_hook)
525
526    def remove_decoder_hook(self, level, type, decoder_hook, post=0):
527        if type not in self._decoders[level]:
528            return False
529
530        self._decoders[level][type][post + 1].remove(decoder_hook)
531        return True
532
533    def get_decoder(self, level, type):
534        try:
535            return self._decoders[level][type]
536        except:
537            #log.debug("No decoder registered for level %s type %s" % (level,
538            #                                                          type))
539            return None, None, None
540
541    def run_decoder(self, level, type, metapkt):
542        ret = None
543        while level is not None and type is not None:
544            decoder, pre, post = self.get_decoder(level, type)
545
546
547            if not decoder and not pre and not post:
548                return
549
550            #log.debug("Running decoder %s" % decoder)
551
552            for pre_hook in pre:
553                pre_hook(metapkt)
554
555            if decoder:
556                ret = decoder(metapkt)
557
558            for post_hook in post:
559                post_hook(metapkt)
560
561            if decoder and isinstance(ret, tuple):
562                # Infinite loop over there :)
563                level, type = ret
564            else:
565                return ret
566
567    def add_dissector(self, layer, port, dissector):
568        """
569        Add a dissector to the chain
570        @param layer APP_LAYER_TCP or APP_LAYER_UDP
571        @param port the remote port
572        @param dissector a callable
573        """
574        assert layer in (APP_LAYER_TCP, APP_LAYER_UDP)
575        # The dissector is only a special case of a decoder.
576        self.add_decoder(layer, port, dissector)
577
578    def remove_dissector(self, layer, port, dissector):
579        assert layer in (APP_LAYER_TCP, APP_LAYER_UDP)
580        # The dissector is only a special case of a decoder.
581        self.remove_decoder(layer, port, dissector)
582
583    # Properties
584
585    def get_global_conf(self): return self._global_conf
586
587    global_conf = property(get_global_conf)
588
589class AuditDispatcher(object):
590    def __init__(self, datalink=IL_TYPE_ETH, context=None):
591        """
592        Create an audit manager to use in conjunction with a PacketProducer
593        that feeds the instance with feed() method @see AuditManager.feed.
594
595        @param datalink the datalink to be used. As default we use IL_TYPE_ETH.
596                        For more information on that @see pcap_datalink manpage
597        @param context an AuditContext or None
598        """
599
600        self._datalink = datalink
601        self._context = context
602        self._conn_manager = ConnectionManager()
603        self._main_decoder = AuditManager().get_decoder(LINK_LAYER,
604                                                        self._datalink)
605
606    def feed(self, mpkt, *args):
607        """
608        General purpose procedure.
609        Will be used the main_decoder created in the constructor. So if you need
610        to have another main_dissector you could change it with the correct
611        property.
612
613        @param metapkt a MetaPacket object or None
614        """
615        if not mpkt:# or not self._main_decoder:
616            return
617
618        manager = AuditManager()
619        manager.run_hook_point('pm::received', mpkt)
620
621        if not self._context:
622            manager.run_decoder(LINK_LAYER, self.datalink, mpkt)
623            return
624
625        # Same code of run_decoder.
626        # Only executed when there's a context so we can have more granularity
627        # over various callbacks
628
629        level = LINK_LAYER
630        type = self.datalink
631        mpkt.context = self._context
632
633        while level is not None and type is not None:
634            decoder, pre, post = manager.get_decoder(level, type)
635
636            if not decoder and not pre and not post:
637                break
638
639            #log.debug("Running decoder %s" % decoder)
640
641            for pre_hook in pre:
642                pre_hook(mpkt)
643
644            if decoder:
645                ret = decoder(mpkt)
646
647            for post_hook in post:
648                post_hook(mpkt)
649
650            if decoder and isinstance(ret, tuple):
651                # Infinite loop over there :)
652                level, type = ret
653            else:
654                break
655
656        if not mpkt.flags & MPKT_FORWARDED:
657            self._conn_manager.parse(mpkt)
658
659            if mpkt.flags & MPKT_FORWARDABLE:
660                manager.run_hook_point('pm::pre-forward', mpkt)
661                self._context.forward(mpkt)
662
663        mpkt.context = None
664        mpkt.data = ''
665
666
667    def get_main_decoder(self): return self._main_decoder
668    def set_main_decoder(self, dec): self._main_decoder = dec
669
670    def get_datalink(self): return self._datalink
671    def get_connection_manager(self): return self._conn_manager
672
673    main_decoder = property(get_main_decoder, set_main_decoder)
674    datalink = property(get_datalink)
675
676###############################################################################
677# Plugin related classes
678###############################################################################
679
680class AuditPlugin(object):
681    def register_hooks(self): pass
682    def register_decoders(self): pass
683    def register_dissectors(self): pass
684    def register_filters(self): pass
685
686class PassiveAudit(AuditPlugin):
687    # TODO: passive related methods goes here
688    pass
689
690# TODO: the code related to gtk and PMApp should be moved outside this module
691class ActiveAudit(AuditPlugin):
692    """
693    ActiveAudits could require user inputs.
694    The dialog to introduce inputs is created automatically by PacketManipulator
695    starting by the inputs element contained in Manifest.xml file.
696
697    On load the plugin could user add_menu_entry() function to create a menu
698    entry under Audits menu in MainWindow. If the user will click on that
699    entry PM will create an input dialog and if the user press OK the inputs
700    will be passed as dict in execute_audit() callback that the plugin should
701    overload.
702
703    About executing the audit you could use an Operation if the audit should
704    run in a continuous mode or is long job.
705
706    If your plugin doesn't need any type of inputs you could set __inputs__ to
707    an empty tuple ().
708
709    __inputs__ = (
710      ('gateway', ('127.0.0.1', 'The Gateway IP address or hostname')),
711      ('port', (80, 'The HTTP port')),
712    )
713    """
714
715    __inputs__ = ()
716
717    def remove_menu_entry(self, item):
718        import umit.pm.gui.core.app
719
720        return umit.pm.gui.core.app.PMApp().main_window.deregister_audit_item(
721            item
722        )
723
724    def remove_mitm_attack(self, item):
725        import umit.pm.gui.core.app
726
727        return umit.pm.gui.core.app.PMApp().main_window.\
728               deregister_audit_mitm_item(item)
729
730    def add_menu_entry(self, name, lbl, tooltip, stock):
731        """
732        Add a MenuEntry to the MainWindow of PM.
733        @param name the name for the gtk.Action
734        @param label the label to use
735        @param tooltip a tooltip for the menuitem
736        @param stock a stock-id to use
737        """
738
739        log.debug('Creating a new menu entry with \'%s\' as label' % lbl)
740
741        import umit.pm.gui.core.app
742
743        return umit.pm.gui.core.app.PMApp().main_window.register_audit_item(
744            name, lbl, tooltip, stock, self.on_input_request
745        )
746
747    def add_mitm_attack(self, name, lbl, tooltip, stock):
748        log.debug('Registering new MITM menu entry for \'%s\' attack' % lbl)
749
750        import umit.pm.gui.core.app
751
752        return umit.pm.gui.core.app.PMApp().main_window.\
753               register_audit_mitm_item(name, lbl, tooltip, stock,
754                                        self.on_input_request)
755
756    def execute_audit(self, audit_sess, input_dct=None):
757        """
758        Overload me.
759        @param audit_sess an AuditSession object.
760        @param input_dict a dict containing audit inputs.
761        @return a bool with True if the audit is executed or False
762        """
763        raise Exception('This method must be overloaded.')
764
765    def on_input_request(self, action):
766        import gtk
767        import umit.pm.gui.core.app
768
769        if not self.__inputs__:
770            audit_sess = ServiceBus().call('pm.sessions',
771                                           'get_current_session')
772
773            self.__start_audit(audit_sess, {})
774            return
775
776        dialog = gtk.Dialog(_('Inputs for %s - PacketManipulator') % \
777                            self.__class__.__name__,
778                            umit.pm.gui.core.app.PMApp().main_window,
779                            gtk.DIALOG_DESTROY_WITH_PARENT,
780                            (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT,
781                             gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
782
783        tbl = gtk.Table(2, 1, False)
784
785        tbl.set_border_width(4)
786        tbl.set_col_spacings(4)
787        tbl.set_row_spacings(4)
788
789        dialog.vbox.pack_start(tbl)
790
791        idx = 0
792
793        for txt, (opt_val, desc) in self.__inputs__:
794            lbl = gtk.Label('')
795            lbl.set_alignment(.0, .5)
796            lbl.set_markup('<b>%s:</b>' % txt.capitalize())
797
798            if isinstance(opt_val, bool):
799                widget = gtk.ToggleButton('')
800                widget.set_active(opt_val)
801
802                widget.get_child().set_text(widget.get_active() \
803                                            and _('Enabled') \
804                                            or _('Disabled'))
805
806                widget.connect('toggled', lambda w: w.get_child().set_text( \
807                    w.get_active() and _('Enabled') or _('Disabled')))
808
809            elif isinstance(opt_val, str):
810                widget = gtk.Entry()
811                widget.set_text(opt_val)
812
813            elif isinstance(opt_val, int):
814                widget = gtk.SpinButton(gtk.Adjustment(opt_val, -sys.maxint,
815                                                       sys.maxint, 1, 10),
816                                        digits=0)
817
818            elif isinstance(opt_val, float):
819                widget = gtk.SpinButton(gtk.Adjustment(opt_val, -sys.maxint,
820                                                       sys.maxint, 1, 10),
821                                        digits=4)
822
823            lbl.props.has_tooltip = True
824            widget.props.has_tooltip = True
825
826            lbl.set_tooltip_markup(desc)
827            widget.set_tooltip_markup(desc)
828            widget.set_name(txt)
829
830            tbl.attach(lbl, 0, 1, idx, idx + 1, gtk.FILL, gtk.FILL)
831            tbl.attach(widget, 1, 2, idx, idx + 1, yoptions=gtk.FILL)
832            idx += 1
833
834        tbl.show_all()
835        dialog.connect('response', self.__on_dialog_response)
836        dialog.show()
837
838    def __start_audit(self, audit_sess, inp_dict):
839        ret = self.execute_audit(audit_sess, inp_dict)
840
841        if isinstance(ret, AuditOperation):
842            log.debug('Nice. This audit implements AuditOperation')
843            audit_sess.audit_page.tree.append_operation(ret)
844
845    def __on_dialog_response(self, dialog, rid):
846        import gtk
847        import umit.pm.gui.core.app
848
849        if rid != gtk.RESPONSE_ACCEPT:
850            dialog.hide()
851            dialog.destroy()
852            return
853
854        table = dialog.vbox.get_children()[0]
855
856        assert isinstance(table, gtk.Table)
857
858        inp_dict = {}
859
860        for widget in table:
861            if isinstance(widget, gtk.Label):
862                continue
863
864            if isinstance(widget, gtk.SpinButton):
865                if widget.get_digits() == 0:
866                    value = widget.get_value_as_int()
867                else:
868                    value = widget.get_value()
869
870            elif isinstance(widget, gtk.ToggleButton):
871                value = widget.get_active()
872            else:
873                value = widget.get_text()
874
875            inp_dict[widget.get_name()] = value
876
877        dialog.hide()
878        dialog.destroy()
879
880        audit_sess = ServiceBus().call('pm.sessions', 'get_current_session')
881
882        self.__start_audit(audit_sess, inp_dict)
883
884###############################################################################
885# Testing classes
886###############################################################################
887
888class AuditTester(object):
889    """
890    Simple test class to test your decoders/dissectors.
891    Simple use:
892        test = AuditTester('my-dump.pcap')
893        test.manager.add_decoder(...)
894        test.start() # Threaded
895        test.join()
896    """
897    def __init__(self, pcapfile, datalink=IL_TYPE_ETH):
898        """
899        Launch an audit manager against a pcap file using the selected backend
900        """
901        import umit.pm.backend
902
903        self.dispatcher = AuditDispatcher(datalink)
904        self.ctx = umit.pm.backend.SniffContext(None, capfile=pcapfile, capmethod=1,
905                                                callback=self.dispatcher.feed,
906                                                audits=False)
907
908    def start(self):
909        log.debug('Starting context for test')
910        self.ctx.start()
911
912    def join(self):
913        log.debug('Waiting for test thread termination')
914        # We have to use that for the moment since join in SniffContext is dummy
915        self.ctx.thread.join()
Note: See TracBrowser for help on using the browser.