from optparse import OptionParser from operator import itemgetter import os import sys import struct import socket import string import gzip import re import time import os import math # check if we are running the right version required_version = 2 if sys.version_info[0] != required_version: print >> sys.stderr, "Error: this script requires Python version", required_version, "but you are currently running", str(sys.version_info[0]) + "." + str(sys.version_info[1]) sys.exit(1) def is_glasnost_strchr(c): return c >= 32 and c <= 126 and c != ord('"') def encode_glasnost_binary(byte, rep): if rep > 1: return "repbyte(%d,%d)" % (byte, rep) else: return "byte(%d)" % byte def encode_glasnost_ascii(str): return "string(\"%s\")" % str def encode_glasnost(bytes): """Encodes a byte-string into glasnost format with string(), byte() and repbyte()""" res = list() str = None byte, rep = None, 0 for b in bytes: if is_glasnost_strchr(b): # ASCII printable character. If we had bytes in the buffer, flush them. # Continue (or start a new) string(...) section with this character if byte is not None: res.append(encode_glasnost_binary(byte, rep)) byte, rep = None, 0 if str is None: str = "" str += chr(b) else: # ASCII non-printable character. If we had an ascii string in the buffer, # flush it. For repeated bytes, keep counting. Else start a new # byte(..) section. if str is not None: res.append(encode_glasnost_ascii(str)) str = None if byte == b: rep += 1 else: if byte is not None: res.append(encode_glasnost_binary(byte, rep)) byte, rep = b, 1 # Flush any string(...) or byte(...) section in buffer if str is not None: res.append(encode_glasnost_ascii(str)) if byte is not None: res.append(encode_glasnost_binary(byte, rep)) return " ".join(res) def emit(test, time, agent, cmd, str="", payload_size=0): test.append({ "time":time, "agent":agent, "command":"%s %s" % (cmd, str), "payload":payload_size}) def parseSize(size_as_string): def match_error(): print >> sys.stderr print >> sys.stderr, "Could not understand size:", size_as_string, "(valid syntax examples: 10000b or 10kb or 0.01mb)" print >> sys.stderr m = re.match(r"(\d+(?:\.\d+)?)\s*(b|mb|kb|gb)", size_as_string, re.I) if m is None: match_error() return None else: size = float(m.group(1)) if m.group(2) is not None: scale = string.upper(m.group(2)) if scale == "KB": size *= 1e3 elif scale == "MB": size *= 1e6 elif scale == "GB": size *= 1e9 return int(size) def print_test(test, options, fileobj, already_written_chars, filename): """write a test (list of statements) to fileobj, using the given options. already_written_chars is equal to the number of bytes already written to the file (e.g. because of a preamble). Returns the number of additional bytes written by print_test()""" last_action = dict() written_chars = 0 for statement in test: statement_str = "" statement_str += insert_spacing(options.spacing) if last_action.has_key(statement['agent']) and options.delays: diff = statement['time'] - last_action[statement['agent']] if diff > 0.0: (fp, ip) = math.modf(diff) statement_str += "%s pause(%d,%d)\n" % (statement['agent'], int(math.floor(ip)), int(math.floor(fp * 1e06))) statement_str += insert_spacing(options.spacing) if statement['agent']: statement_str += "# %s sends %d bytes\n%s %s\n" % (statement['agent'], statement['payload'], statement['agent'], statement['command']) else: statement_str += "%s\n" % (statement['command']) if len(statement_str) + written_chars + already_written_chars > options.max_size: print >> sys.stderr, filename, "truncated after", written_chars + already_written_chars, "bytes\n" return (written_chars, True) print >> fileobj, statement_str, written_chars += len(statement_str) if statement['agent']: last_action[statement['agent']] = statement['time'] return (written_chars, False) def insert_spacing(space_count): s = 0 spaces = ""; while s < space_count: spaces += "\n" s += 1 return spaces def __printable__(x): if ord(x) > 32 and ord(x) < 126: return chr(ord(x)) else: return '.' def hexdump(s): """ returns a string with hexdump -C style output of a bytestream """ bytes = ["%.2x" % ord(x) for x in s] chars = [__printable__(x) for x in s] outstr = "" for i in xrange(0, len(bytes)/16): outstr += ' %s' % string.join(bytes[i*16:(i+1)*16],' ') outstr += ' ' outstr += '%s\n' % string.join(chars[i*16:(i+1)*16], '') outstr += ' %s' % string.join(bytes[(i+1)*16:],' ') outstr += ' ' outstr += '%s' % string.join(chars[(i+1)*16:],'') return outstr def decodecHDLCHeader(s): d = {} d['address'], d['control'] = s[0], s[1] d['protocol'] = struct.unpack(">H", s[2:4])[0] d['data'] = s[4:] return d def decodePPPHeader(s): d = {} if struct.unpack("BB", s[0:2]) == (0xff, 0x03): # HDLC-framed d['protocol'] = struct.unpack(">H", s[2:4])[0] d['data'] = s[4:] else: # No framing d['protocol'] = struct.unpack(">H", s[0:2])[0] d['data'] = s[2:] return d def decodeEtherHeader(s): """ Takes in a raw byte string and returns a dictionary with the following keys: destMAC srcMAC etherType data """ d = {} d['destMAC'] = s[:6] d['srcMAC'] = s[6:12] d['etherType'] = struct.unpack(">H", s[12:14])[0] d['data'] = s[14:] return d def decodeUDPHeader(s): """ Takes in a raw byte string of an IP packet payload and returns a dictionary with the following keys: srcPort dstPort len checksum data """ d = {} d['srcPort'] = struct.unpack(">H", s[:2])[0] d['dstPort'] = struct.unpack(">H", s[2:4])[0] d['len'] = struct.unpack(">H", s[4:6])[0] d['checksum'] =struct.unpack('>H', s[6:8])[0] d['data'] = s[8:] return d def decodeTCPHeader(s): """ Takes in a raw byte string of an IP packet payload and returns a dictionary with the following keys: srcPort dstPort seqNum sckNum dataOffset flags window checksum urg options [MAYBE] data """ d = {} d['srcPort'] = struct.unpack(">H", s[:2])[0] d['dstPort'] = struct.unpack(">H", s[2:4])[0] d['seqNum'] = struct.unpack(">I", s[4:8])[0] d['ackNum'] = struct.unpack(">I", s[8:12])[0] d['dataOffset'] = (struct.unpack("B", s[12])[0] & 0xF0) >> 4 d['flags'] = struct.unpack("B", s[13])[0] & 0x3F d['window'] = struct.unpack(">H", s[14:16])[0] d['checksum'] = struct.unpack(">H", s[16:18])[0] d['urg'] = struct.unpack(">H", s[18:20])[0] if d['dataOffset'] > 5: d['options'] = s[20:(4*d['dataOffset'])] else: d['options'] = None d['data'] = s[(4*d['dataOffset']):] return d def decodeIPHeader(s): """ Takes in a raw byte string of an IP packet and returns a dictionary with the following keys: version header_len tos total_len id flags fragment_offset ttl protocol checksum srcAddr dstAddr options [MAYBE] data """ # from pylibpcap... d = {} d['version'] = (ord(s[0]) & 0xf0) >> 4 d['header_len'] = ord(s[0]) & 0x0f d['tos'] = ord(s[1]) d['total_len'] = socket.ntohs(struct.unpack('H',s[2:4])[0]) d['id'] = socket.ntohs(struct.unpack('H',s[4:6])[0]) d['flags'] = (ord(s[6]) & 0xe0) >> 5 d['fragment_offset'] = socket.ntohs(struct.unpack('H',s[6:8])[0] & 0x1f) d['ttl'] = ord(s[8]) d['protocol'] = ord(s[9]) d['checksum'] = socket.ntohs(struct.unpack('H',s[10:12])[0]) d['srcAddr'] = socket.inet_ntoa(s[12:16]) d['dstAddr'] = socket.inet_ntoa(s[16:20]) if d['header_len'] > 5: d['options'] = s[20:(4*(d['header_len']))] else: d['options'] = None d['data'] = s[(4*(d['header_len'])):d['total_len']] return d class pcaputils: """ pcaputils(path) will open the capture file at path and allow packet parsing with next() or auto() """ def __init__(self, path): self.endian = None self.f = None self.path = None self.verMaj = None self.verMin = None self.tZone = None self.accuracy = None self.snaplen = None self.netType = None self.gHeaderFmt = "%sHHiIII" self.gHeaderLen = 20 self.pHeaderFmt = "%sIIII" self.pHeaderLen = 16 self.handlers = [] self.DLT_EN10MB = 1 self.DLT_PPP = 9 self.C_HDLC = 104 self.RAW_IP = 101 self.__open__(path) def __open__(self, path): self.path = path if path != "-": if re.match(".*\.gz$", path): self.f = gzip.open(path, "rb") else: self.f = file(path, "rb") else: self.f = sys.stdin magicNumber = self.f.read(4) if magicNumber == "\xa1\xb2\xc3\xd4": self.endian = ">" elif magicNumber == "\xd4\xc3\xb2\xa1": self.endian = "<" else: raise Exception("unknown magic number: %s" % repr(magicNumber)) self.gHeaderFmt = self.gHeaderFmt % self.endian self.pHeaderFmt = self.pHeaderFmt % self.endian globalHeader = self.f.read(self.gHeaderLen) self.verMaj, self.verMin, self.tZone, \ self.accuracy, self.snaplen, self.netType = struct.unpack(self.gHeaderFmt, globalHeader) return True def next(self): """ trim the next packet off the capture and return it returns a tuple: (timestampSeconds, timestampUSeconds, packetData) """ # TODO: return pythonic time format, incl timezones. packetHeader = self.f.read(self.pHeaderLen) while packetHeader: tsSec, tsUSec, capLen, realLen = struct.unpack(self.pHeaderFmt, packetHeader) packetData = self.f.read(capLen) yield (tsSec, tsUSec, packetData) packetHeader = self.f.read(self.pHeaderLen) def subscribe(self, expr, fn): """ subscribe handler fn to packets matching expr """ code = compile(expr, "[pcap subscriptions]", "eval") self.handlers.append((expr, fn)) def auto(self): """ will loop through the packet capture, parse the ethernet, ip, tcp and/or udp headers. will compare the current packet to each subscription, and call the handler function on a match. handler functions must take four arguments: handler(timestampSeconds, timestampUSeconds, headerDict, fullPacketData) subscriptions match on header values, using dictionaries named 'ether', 'ip', 'tcp' and/or 'udp' and the key values from the pcaputils.decode* functions. """ packetHeader = self.f.read(self.pHeaderLen) while packetHeader: ip = None ether = None udp = None tcp = None tsSec, tsUSec, capLen, realLen = struct.unpack(self.pHeaderFmt, packetHeader) packetData = self.f.read(capLen) ipdata = None if self.netType == self.DLT_EN10MB: ether = decodeEtherHeader(packetData) if ether['etherType'] == 0x0800: ipdata = ether['data'] elif self.netType == self.DLT_PPP: ppp = decodePPPHeader(packetData) if ppp['protocol'] == 0x0021: ipdata = ppp['data'] elif self.netType == self.C_HDLC: chdlc = decodecHDLCHeader(packetData) if chdlc['protocol'] == 0x0800: ipdata = chdlc['data'] elif self.netType == self.RAW_IP: ipdata = packetData else: sys.exit("Unsupported link layer type: " + str(self.netType)) if ipdata is not None: if len(ipdata) < 20: pass #print >> sys.stderr, "IP packet too short, skipping (", len(ipdata), "bytes)" else: ip = decodeIPHeader(ipdata) if ip['protocol'] == socket.IPPROTO_UDP: if len(ipdata) < 28: pass #print >> sys.stderr, "UDP packet too short, skipping (", len(ipdata), "bytes)" else: udp= decodeUDPHeader(ip['data']) elif ip['protocol'] == socket.IPPROTO_TCP: if len(ipdata) < 40: pass #print >> sys.stderr, "TCP packet too short, skipping (", len(ipdata), "bytes)" else: tcp = decodeTCPHeader(ip['data']) else: pass #print >> sys.stderr, "Found non-TCP, non-UDP packet" else: print >> sys.stderr, "Found non-IP packet" globals = {'ether': ether, 'ip': ip, 'tcp': tcp, 'udp': udp} for code,fn in self.handlers: try: result = eval(code, globals) except TypeError: result = False if result: fn(tsSec, tsUSec, packetData, globals) packetHeader = self.f.read(self.pHeaderLen) # TCP flags SYN = 0x02 FIN = 0X01 RST = 0x04 ACK = 0x10 # dictionary containing information on connections, idnexed by conenction identifier cn = dict() def print_packet(proto, packetData = None): """print packet fields, used for debugging""" res = "%s:%s -> %s:%s\n" % (proto['ip']['srcAddr'], proto['tcp']['srcPort'], proto['ip']['dstAddr'], proto['tcp']['dstPort']) if packetData: res += "TOTAL_DATA: %d\n" % (len(packetData)) res += "TCP_DATA: %d\n" % (len(proto['tcp']['data'])) res += "IP_DATA: %d\n" % (len(proto['ip']['data'])) if proto['ip']['options']: res += "IP_OPTIONS: %d\n" % (len(proto['ip']['options'])) if proto['tcp']['options']: res += "TCP_OPTIONS: %d\n" % (len(proto['tcp']['options'])) res += "SEQN: %d\n" % (proto['tcp']['seqNum']) if proto['tcp']['flags'] & ACK: res += "ACKN: %d\n" % (proto['tcp']['ackNum']) if proto['tcp']['flags'] & SYN: res += " SYN" if proto['tcp']['flags'] & FIN: res += " FIN" if proto['tcp']['flags'] & RST: res += " RST" res += " IPHLEN = " + str(4*proto['ip']['header_len']) res += " TCPDATAOFFSET = " + str(4*proto['tcp']['dataOffset']) return res def print_conn(conn): return "%s:%s <-> %s:%s" % (conn['client_ip'], conn['client_port'], conn['server_ip'], conn['server_port']) def print_conn_single_word(conn): return "client-%s-%s-server-%s-%s" % (conn['client_ip'], conn['client_port'], conn['server_ip'], conn['server_port']) def wasClosed(conn): """check if a connection was closed (either by FIN exchange, or with RST)""" return (conn['client_fin_sent'] and conn['server_fin_sent'] and conn['client_fin_acked'] and conn['server_fin_acked']) or conn['reset'] def wasOpened(conn): """check if a connection was opened with SYN exchange""" return conn['client_syn_sent'] and conn['server_syn_sent'] and conn['client_syn_acked'] and conn['server_syn_acked'] def make_conn_id(proto): """build a unique string identifier for the connection that is used to uniquely identify each connection in the dictionaries""" srcip = proto['ip']['srcAddr'] srcport = proto['tcp']['srcPort'] dstip = proto['ip']['dstAddr'] dstport = proto['tcp']['dstPort'] if (srcip < dstip): return srcip + ":" + str(srcport) + ":" + dstip + ":" + str(dstport) else: return dstip + ":" + str(dstport) + ":" + srcip + ":" + str(srcport) def tcp_packet_handler(tsS, tsUS, data, proto): """this is called whenever a TCP packet is found in the trace tsS = timestamp (seconds) tsUS = timestamp (microseconds) data = whole packet data proto = structure containing the fields of the protocols found in the packet. For our purposes, we care about IP and TCP """ global options # build a decent timestamp tstamp = float(tsS) + (float(tsUS) / 1e06) # bitch if not tcp if not proto['tcp']: sys.exit("I was expecting a TCP packet, found a non-TCP packet") # bitch if not enough bytes were captured ip_data_len_from_header = proto['ip']['total_len'] - (4*proto['ip']['header_len']) if ip_data_len_from_header != len(proto['ip']['data']): sys.exit("Wrong number of bytes in IP packet: according to IP header I should have " + str(ip_data_len_from_header) + ", but found " + str(len(proto['ip']['data'])) + " (Are you sure you used the \"-s 0\" option when collecting the trace?)") conn_id = make_conn_id(proto) # add the new connections to the cn dictionary, if it's not already there if not cn.has_key(conn_id): client_ip = proto['ip']['srcAddr'] client_port = proto['tcp']['srcPort'] server_ip = proto['ip']['dstAddr'] server_port = proto['tcp']['dstPort'] if options.client_host and options.client_host != client_ip: return if options.server_host and options.server_host != server_ip: return if options.server_port and options.server_port != server_port: return if options.client_port and options.client_port != client_port: return if options.host and options.host not in (client_ip, server_ip): return if options.port and options.port not in (client_port, server_port): return # these fields are used to keep track of the connection state cn[conn_id] = { 'start':tstamp, 'client_ip':client_ip, 'client_port':client_port, 'server_ip':server_ip, 'server_port':server_port, 'client_bytes_sent':0, 'server_bytes_sent':0, 'client_last_seq':None, 'server_last_seq':None, 'client_last_acked':None, 'server_last_acked':None, 'client_syn_sent':False, 'client_syn_acked':False, 'server_syn_sent':False, 'server_syn_acked':False, 'client_fin_sent':False, 'client_fin_acked':False, 'server_fin_sent':False, 'server_fin_acked':False, 'reset':False, 'test':list() } print >> sys.stderr, "Found new connection:", print_conn(cn[conn_id]) # if the connetion had already been closed, this packet should be ignored if wasClosed(cn[conn_id]): print >> sys.stderr, "ignoring packet from closed connection (", print_conn(cn[conn_id]), ")" return # handle SYN packets if proto['tcp']['flags'] & SYN: if proto['ip']['srcAddr'] == cn[conn_id]['client_ip']: if cn[conn_id]['client_syn_sent']: # duplucat syn pass cn[conn_id]['client_syn_sent'] = True cn[conn_id]['client_last_seq'] = proto['tcp']['seqNum'] else: if cn[conn_id]['server_syn_sent']: # duplucat syn pass cn[conn_id]['server_syn_sent'] = True cn[conn_id]['server_last_seq'] = proto['tcp']['seqNum'] # handle ACK packets if proto['tcp']['flags'] & ACK: # connection wasn't open yet, this must be an ACK for a SYN if not wasOpened(cn[conn_id]): if proto['ip']['srcAddr'] == cn[conn_id]['client_ip'] and cn[conn_id]['server_syn_sent']: if proto['tcp']['ackNum'] == cn[conn_id]['server_last_seq'] + 1: cn[conn_id]['server_syn_acked'] = True else: # must have missed a SYN packet, ignore pass #sys.exit("SYN sequence number mismatch in connection " + print_conn_id(conn_id) + "\n" + print_conn(cn[conn_id]) + "\n" + print_packet(proto)) elif proto['ip']['dstAddr'] == cn[conn_id]['client_ip'] and cn[conn_id]['client_syn_sent']: if proto['tcp']['ackNum'] == cn[conn_id]['client_last_seq'] + 1: cn[conn_id]['client_syn_acked'] = True else: # must have missed a SYN packet, ignore pass #sys.exit("SYN sequence number mismatch in connection " + print_conn_id(conn_id) + "\n" + print_conn(cn[conn_id]) + "\n" + print_packet(proto)) else: if proto['ip']['srcAddr'] == cn[conn_id]['client_ip']: # increase the last acked field according to what we saw in the packet # the following test is necessary because sometimes TCP receiver "pre ack" by # acknowledging packets that haven't been sent yet to inflate the cognestion window # we want to avoid this if proto['tcp']['ackNum'] > cn[conn_id]['server_last_seq'] + 1: # ignore pre-acking proto['tcp']['ackNum'] = cn[conn_id]['server_last_seq'] + 1 # update lasta acked field for the connection endpoint cn[conn_id]['server_last_acked'] = max(proto['tcp']['ackNum'], cn[conn_id]['server_last_acked']) # handle ACK for FIN packets if cn[conn_id]['server_fin_sent'] and cn[conn_id]['server_last_seq'] + 1 == proto['tcp']['ackNum']: cn[conn_id]['server_fin_acked'] = True else: # same as above, symmetrical for the other connectione end if proto['tcp']['ackNum'] > cn[conn_id]['client_last_seq'] + 1: # ignore pre-acking proto['tcp']['ackNum'] = cn[conn_id]['client_last_seq'] + 1 cn[conn_id]['client_last_acked'] = max(proto['tcp']['ackNum'], cn[conn_id]['client_last_acked']) if cn[conn_id]['client_fin_sent'] and cn[conn_id]['client_last_seq'] + 1 == proto['tcp']['ackNum']: cn[conn_id]['client_fin_acked'] = True # handle packets with payload if len(proto['tcp']['data']) > 0: # if connection wasn't opened, ignore if not wasOpened(cn[conn_id]): # print >> sys.stderr, "ignoring data packet for connection for which we did not see a SYN exchange! (", print_conn_id(conn_id), print_conn(cn[conn_id]), print_packet(proto, data), ")" #print >> sys.stderr, "ignoring data packet for connection for which we did not see a SYN exchange!" return if proto['ip']['srcAddr'] == cn[conn_id]['client_ip']: if proto['tcp']['seqNum'] > cn[conn_id]['client_last_seq']: # packet is not a retransmission # update the sequence number field cn[conn_id]['client_last_seq'] += len(proto['tcp']['data']) cn[conn_id]['client_bytes_sent'] += len(proto['tcp']['data']) # convert data string to array of bytes, because this is what encode_glasnost expects bytes_array = struct.unpack(str(len(proto['tcp']['data'])) + "B", proto['tcp']['data']) # add the payload to the glasnost test corresponding to this connection emit(cn[conn_id]['test'], tstamp, "client", "send", encode_glasnost(bytes_array), len(bytes_array)) else: # retransmitted segment, ignore pass else: if proto['tcp']['seqNum'] > cn[conn_id]['server_last_seq']: # new data cn[conn_id]['server_last_seq'] += len(proto['tcp']['data']) cn[conn_id]['server_bytes_sent'] += len(proto['tcp']['data']) # convert data string to array of byte bytes_array = struct.unpack(str(len(proto['tcp']['data'])) + "B", proto['tcp']['data']) emit(cn[conn_id]['test'], tstamp, "server", "send", encode_glasnost(bytes_array), len(bytes_array)) else: # retransmitted segment pass # handle FIN packets if wasOpened(cn[conn_id]): if proto['tcp']['flags'] & FIN: if proto['ip']['srcAddr'] == cn[conn_id]['client_ip'] and not cn[conn_id]['client_fin_sent']: cn[conn_id]['client_fin_sent'] = True cn[conn_id]['client_last_seq'] += 1 else: cn[conn_id]['server_fin_sent'] = True cn[conn_id]['server_last_seq'] += 1 # handle RST packets if proto['tcp']['flags'] & RST: cn[conn_id]['reset'] = True def make_preamble_with_ports(tracename, protoname, port1, port2): if tracename == "-": tracename = "standard input" return "[protocol:%s port:%d,%d]\n\n# generated by %s on %s\n# from the pcap file: \"%s\"\n" % (protoname, port1, port2, sys.argv[0], time.asctime(time.localtime()), tracename) def make_preamble(tracename, protoname): if tracename == "-": tracename = "standard input" return "[protocol:%s]\n\n# generated by %s on %s\n# from the pcap file: \"%s\"\n" % (protoname, sys.argv[0], time.asctime(time.localtime()), tracename) def main(args): global options usage = "%prog [options] " parser = OptionParser(usage=usage) parser.add_option("-d", "--include-delays", dest="delays", action="store_true", help="whether to generate pause commands based on time delays in the trace [default: %default]") parser.add_option("-s", "--spacing-lines", dest="spacing", action="store", type="int", default=1, help="number of empty lines to insert between subsequent commands [default: %default]") parser.add_option("-p", "--proto", dest="proto", action="store", type="string", default=None, help="protocol name [default: name of pcap file]") parser.add_option("-l", "--limit-test-size", dest="max_test_size", action="store", type="string", default="5MB", help="truncate test size to this value [default: %default]") parser.add_option("--client-host", dest="client_host", default=None, action="store", type="string", help="if provided, only consider connections with this host as client") parser.add_option("--server-host", dest="server_host", default=None, action="store", type="string", help="if provided, only consider connections with this host as server") parser.add_option("--concatenate-tests", dest="concatenate_tests", action="store_true", help="concatenate all tests to standard output, don't create any files") parser.add_option("--client-port", dest="client_port", default=None, action="store", type="int", help="if provided, only consider connections with this client port number") parser.add_option("--server-port", dest="server_port", default=None, action="store", type="int", help="if provided, only consider connections with this server port number") parser.add_option("--port", dest="port", default=None, action="store", type="int", help="if provided, only consider connections involving this port number") parser.add_option("--host", dest="host", default=None, action="store", type="string", help="if provided, only consider connections involving this host") (options, args) = parser.parse_args() if len(args) != 1: parser.print_help() return 1 pcap_file = args[0] if not options.proto: if pcap_file != "-": options.proto = os.path.basename(pcap_file) else: print >> sys.stderr, "Error: since I am reading the trace from standard input, you need to specify a protocol name with the \"-p\" option (add \"-p \" to the command line)" print >> sys.stderr return 1 options.max_size = parseSize(options.max_test_size) if options.max_size is None: parser.print_help() return 1 if options.server_host: options.server_host = socket.gethostbyname(options.server_host) if options.client_host: options.client_host = socket.gethostbyname(options.client_host) if options.host: options.host = socket.gethostbyname(options.host) pcap = pcaputils(pcap_file) # this tells the pcap parser to call tcp_packet_handler whenever it encounters a TCP packet in the trace # the parser is in the file pcap_parser.py pcap.subscribe("tcp", tcp_packet_handler) pcap.auto() print >> sys.stderr, "\nFound", len(cn.keys()), "connections:\n" corr_opened = 0 corr_closed = 0 with_payload = 0 for conn_id in cn.keys(): if wasOpened(cn[conn_id]): corr_opened += 1 if len(cn[conn_id]['test']) > 0: with_payload += 1 if wasClosed(cn[conn_id]): corr_closed += 1 #print >> sys.stderr, "Time:", cn[conn_id]['start'],"Connection:", print_conn_id(conn_id), print_conn(cn[conn_id]) print >> sys.stderr, "\n%d connections were correctly opened\n%d connections were correctly closed\n%d connections were correclty opened and transferred some data, tests have been created for them\n" % (corr_opened, corr_closed, with_payload) def get_test_time(conn): return conn['start'] # print tests for each conenction sorted by initial timestamp cn_list = cn.values(); cn_list.sort(key=get_test_time) if len(cn_list) == 0: print >> sys.stderr, "no output produced!" conn_count = 1 written_chars = 0 if options.concatenate_tests: preamble = make_preamble(pcap_file, options.proto) print >> sys.stdout, preamble, written_chars += len(preamble) for (conn) in cn_list: if not wasOpened(conn): continue if len(conn['test']) == 0: # discard connection for which no data was ever transferred (e.g. only SYN exchange) continue filename = "standard output" preamble = "" fileobj = sys.stdout if not options.concatenate_tests: protoname = options.proto + "-" + print_conn_single_word(conn) filename = protoname + ".gtest" if os.access(filename, os.F_OK): print >> sys.stderr, "Error: file " + filename + " already exists! Please remove first" return 1 # start new file fileobj = open(filename, "w") written_chars = 0 preamble = make_preamble_with_ports(pcap_file, protoname, conn['client_port'], conn['server_port']) preamble += "\n# connection %d\n# clientIP %s clientPort %s\n# serverIP %s serverPort %s\n# captured on %s timestamp: %d\n# client sent %d bytes\n# server sent %d bytes\n\n" % (conn_count, conn['client_ip'], conn['client_port'], conn['server_ip'], conn['server_port'], time.ctime(conn['start']), conn['start'], conn['client_bytes_sent'], conn['server_bytes_sent']) print >> fileobj, preamble, written_chars += len(preamble) (test_chars, truncated) = print_test(conn['test'], options, fileobj, written_chars, filename) if options.concatenate_tests and truncated: # output file has been truncated, stop writing to it break written_chars += test_chars conn_count += 1 if not options.concatenate_tests: fileobj.close() if __name__ == "__main__": sys.exit(main(sys.argv))