commit d6a73bad105ece35794ad3709f17d1740275554c Author: Isis Lovecruft isis@torproject.org Date: Tue Nov 20 14:02:23 2012 +0000
Cleaned up report generation and results/logging display. --- nettests/bridge_reachability/tcpsyn.py | 137 +++++++++++++++----------------- 1 files changed, 64 insertions(+), 73 deletions(-)
diff --git a/nettests/bridge_reachability/tcpsyn.py b/nettests/bridge_reachability/tcpsyn.py index bc79a93..e261a83 100644 --- a/nettests/bridge_reachability/tcpsyn.py +++ b/nettests/bridge_reachability/tcpsyn.py @@ -21,10 +21,10 @@ from twisted.python import usage from twisted.internet import reactor, defer, address from ooni import nettest from ooni.utils import net, log +from ooni.utils.otime import timestamp
try: from scapy.all import TCP, IP - from scapy.all import sr1 from ooni.utils import txscapy except: log.msg("This test requires scapy, see www.secdev.org/projects/scapy") @@ -32,11 +32,16 @@ except:
class UsageOptions(usage.Options): """Options for TCPSynTest.""" - optParameters = [['dst', 'd', None, 'Host IP to ping'], - ['port', 'p', None, 'Host port'], - ['count', 'c', 3, 'Number of SYN packets to send', int], - ['interface', 'i', None, 'Network interface to use'], - ['verbose', 'v', False, 'Show hexdump of responses']] + optParameters = [ + ['dst', 'd', None, 'Host IP to ping'], + ['port', 'p', None, 'Host port'], + ['count', 'c', 3, 'Number of SYN packets to send', int], + ['interface', 'i', None, 'Network interface to use'], + ['hexdump', 'x', False, 'Show hexdump of responses'], + ['pdf', 'y', False, + 'Create pdf of visual representation of packet conversations'], + ['cerealize', 'z', False, + 'Cerealize scapy objects for further scripting']]
class TCPSynTest(nettest.NetTestCase): """ @@ -63,7 +68,6 @@ class TCPSynTest(nettest.NetTestCase): for key, value in self.localOptions.items(): log.debug("setting self.%s = %s" % (key, value)) setattr(self, key, value) - if not self.interface: try: iface = net.getDefaultIface() @@ -75,14 +79,27 @@ class TCPSynTest(nettest.NetTestCase): log.msg("Using system default interface: %s" % iface) self.interface = iface
+ if self.cerealize: + if True: + raise NotImplemented("need handler for type(dictproxy)...") + else: + from Cerealize import cerealizer + self.cheerios = Cerealize.cerealizer() + mind = ['scapy.layers.inet.IP', + 'scapy.base_classes.Packet_metaclass', + 'scapy.plist.SndRcvList'] + for spoon in mind: + __import__(spoon) + self.cheerios.register(spoon) + def addToDestinations(self, addr, port): - try: - dst, dport = net.checkIPandPort(addr, port) - if not dst in self.destinations.keys(): - self.destinations[dst] = {'dst': dst, 'dport': dport} - return (dst, dport) - except Exception, ex: - log.exception(ex) + dst, dport = net.checkIPandPort(addr, port) + if not dst in self.destinations.keys(): + self.destinations[dst] = {'dst': dst, 'dport': dport} + else: + ## XXX implement multiple port or portrange options + log.msg("Multiple port scanning not yet implemented.") + return (dst, dport)
def inputProcessor(self, input_file=None): """ @@ -91,11 +108,11 @@ class TCPSynTest(nettest.NetTestCase): """ try: ## get the commandline input, if there is one: - if self.localOptions['dst'] is not None and self.localOptions['port'] is not None: + if self.localOptions['dst'] is not None \ + and self.localOptions['port'] is not None: log.debug("processing commandline destination input") yield self.addToDestinations(self.localOptions['dst'], self.localOptions['port']) - ## get the inputs from inputFile: if input_file and os.path.isfile(input_file): log.debug("processing input file %s" % input_file) @@ -104,7 +121,7 @@ class TCPSynTest(nettest.NetTestCase): if line.startswith('#'): continue one = line.strip() - raw_ip, raw_port = one.rsplit(':', 1) ## XXX not ipv6 safe! + raw_ip, raw_port = one.rsplit(':', 1) ## XXX not ipv6 safe! yield self.addToDestinations(raw_ip, raw_port) except Exception, ex: log.exception(ex) @@ -120,86 +137,59 @@ class TCPSynTest(nettest.NetTestCase): packets.append(pkt) return packets
- def sort_nicely(packets): - """Print the summary of each packet in a list.""" - return [pkt.summary() for pkt in packets] - - def tcp_flags(responses): - """Print summary of hosts which responded with a SYN/ACK.""" - for response in responses: - layer = response.getlayer('TCP') if response.haslayer('TCP') else None - yield layer.sprintf("{TCP:%TCP.flags%}") if layer else None - - def received_syn(responses, flags): - yield responses.filter( - lambda x: (x for x in responses if str(flags) in ['S','SA'])) - def process_packets(packet_list): + """xxx""" results, unanswered = packet_list
- log.debug("RESULTS ARE: %s" % results) - log.debug("UNANSWERED: %s" % unanswered) - - for (q, re) in results: + if self.pdf: + pdf_name = self.name +'_'+ timestamp() + try: + results.pdfdump(pdf_name) + except Exception, ex: + log.exception(ex) + else: + log.msg("Visual packet conversation saved to %s.pdf" + % pdf_name) + + for (q, r) in results: request_data = {'summary': q.summary(), 'command': q.command(), - 'object': export_object(q), 'hash': q.hashret(), 'display': q.display(), 'sent_time': q.time} response_data = {'summary': r.summary(), 'command': r.command(), - 'object': export_object(r) 'hash': r.hashret(), 'src': r['IP'].src, 'flags': r['IP'].flags, 'display': r.display(), 'recv_time': r.time, 'delay': r.time - q.time} - if self.verbose: - request_data['hexdump'] = q.hexdump() - response_data['hexdump'] = r.hexdump() - + if self.hexdump: + request_data.update('hexdump', q.hexdump()) + response_data.update('hexdump', r.hexdump()) + if self.cerealize: + pass result_data = (request_data, response_data)
- flags = tcp_flags(response) for dest, data in self.destinations.items(): - if data['dst'] == response.src: - if not 'response' in data: - log.msg("%s" % request.summary()) - log.msg("%s" % response.summary()) - data['response'] = [response.summary()] + if data['dst'] == response_data['src']: + if not 'received_response' in data: + if self.hexdump: + log.msg("%s" % request.hexdump()) + log.msg("%s" % response.hexdump()) + else: + log.msg("\n %s\n ==> %s" % (q.summary(), + r.summary())) + data['result'] = [result_data, ] + data['received_response'] = True data['reachable'] = True else: - data['response'].append(response.summary()) - if self.verbose: - log.msg("%s" % request.summary()) - log.msg("%s" % response.hexdump()) - - for unans in unanswered: - process_unanswered(unans) - - #try: - # response.make_table( - # lambda x:( - # (x.src for x in received_syn(response)), - # (x.dport for x in request), - # (x for x in tcp.flags(response)) ) - # ) - #except Exception, ex: - # log.exception(ex) + data['result'].append(result_data)
def process_unanswered(unanswer): """Callback function to process unanswered packets.""" - log.msg("unanswered packets:\n%s" - % sort_nicely(unanswer)) - self.report['unanswered'] = sort_nicely(unanswer) - - for dest, data in self.destinations.items(): - if not 'response' in data: - log.msg("No reply from %s." % dest) - data['response'] = None - data['reachable'] = False + #log.debug("%s" % str(unanswer)) return unanswer
(addr, port) = self.input @@ -209,6 +199,7 @@ class TCPSynTest(nettest.NetTestCase):
d = txscapy.sr(packets, iface=self.interface) d.addCallbacks(process_packets, log.exception) + d.addCallbacks(process_unanswered, log.exception) self.report['destinations'] = self.destinations return d