# Copyright (c) 2007, Neville-Neil Consulting
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# Neither the name of Neville-Neil Consulting nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# File: packet_stream.py
#
# Author: George V. Neville-Neil
#
# Description: This module implements operations on streams of packets
# for the packet debugger. The user interacts with streams in order
# to read, write and manipulate packets.
import pcs
import pcap
class InitError(Exception):
def __init__(self, message):
self.message = message
def __str__(self):
return repr(self.message)
class Stream(object):
"""A class which represents a stream of packets for PDB
"""
def __init__(self, options, filename=None, interface=None):
"""Initialize a packet stream.
A packet stream can be read from a pcap file or it can be
taken live from an interface. Either or, but not both, must
be supplied. A pcap dump file is read in completely once it
is opened and is then available to the system."""
# Set up all known data in a common place
self.breakpoints = [] # List of integer breakpoints
self.file = None # The file we are working with.
self.filter = "" # Pcap style filter
self.packets = [] # The list of packets
self.position = -1 # Where we are in the list of packets
self.type = None # What type of stream this is XXX
self.lock = None # A lock for use in threading XXX
self.layer = 0 # The ISO layer we're working with.
if filename == None and interface == None:
raise InitError, "Must supply a file or an interface"
self.options = options
if (interface != None):
try:
self.outfile = pcs.PcapConnector(interface)
except:
raise InitError, "Cannot open interface %s, you may need to be root." % interface
if (filename != None):
try:
self.file = pcs.PcapConnector(filename)
except:
raise InitError, "Cannot open pcap file %s" % filename
done = False
packet = None
while not done:
try:
packet = self.file.readpkt()
except:
done = True
self.packets.append(packet)
self.file.close()
self.position = 0
if (self.file.dlink == pcap.DLT_EN10MB):
self.layer = 2
else: # Default case
self.layer = 3 # Network and above
def __str__(self):
"""give a human readable version of a Stream"""
retval = ""
retval += "Breakpoints %s\n" % self.breakpoints
retval += "File %s\n" % self.file.file.name
retval += "Filter %s\n" % self.file.file.filter
retval += "Number of packets: %d\n" % len(self.packets)
retval += "Current Position: %d\n" % self.position
retval += "Type\n"
retval += "Layer: %d\n" % self.layer
if self.file.dlink == pcap.DLT_NULL:
datalink = "Localhost"
elif self.file.dlink == pcap.DLT_EN10MB:
datalink = "Ethernet"
else:
datalink = "Unknown"
retval += "Datalink: %s\tOffset: %d" % (datalink, self.file.dloff)
return retval
def __repr__(self):
return "<pdb.Stream bp, file, filter, numpkts %d, position %d, type, layer %d>" % (len(self.packets), self.position, self.layer)
def run(self, index, ignore = -1):
"""Run the packet stream from the index. If ignore is set the ignore the breakpoint at that numbered index"""
if ((self.file.dlink != self.outfile.dlink) and (self.outfile.dlink != pcap.DLT_NULL)):
print "Input stream and output interface must agree."
print "Input stream dlink %d, output interface dlink %d" % (self.file.dlink, self.outfile.dlink)
print "run stopped"
return
self.position = index
for packet in self.packets[index:len(self.packets)]:
if ((self.position in self.breakpoints) and
(self.position != ignore)):
print "Breakpoint at packet %d" % self.position
self.display(self.position, packet)
return
packet = self.map(packet, self.outfile)
try:
written = self.outfile.write(packet.bytes, len(packet.bytes))
except:
print "Writing packet %d failed, aborting." % self.position
return
self.position += 1
def send(self, packet):
"""Send a single packet"""
packet = self.map(packet, self.outfile)
try:
written = self.outfile.write(packet.bytes, len(packet.bytes))
except:
print "Sending packet failed."
return
def map(self, packet, device):
"""Take a packet and make it transmittable on the device passed in."""
if (self.file.dloff == device.dloff):
return packet.chain()
def list(self):
"""List the packets in the stream."""
if (self.position + self.options.list_length) > len(self.packets):
end = len(self.packets)
else:
end = self.position + self.options.list_length
index = self.position
for packet in self.packets[self.position:end]:
self.display(index, packet)
print
index+=1
def display(self, index, packet):
"""Method for displaying packets."""
if packet == None:
print "%d: Unknown Packet"
return
if (self.options.layer == -1):
print "%d: %s" % (index, packet.println())
while True:
try:
packet = packet.data
except:
return
if (packet == None):
return
print " %s" % packet.println()
else:
if self.options.layer < self.layer:
return
if self.options.layer == self.layer:
print "%d: %s" % (index, packet.println())
return
skip = self.options.layer - self.layer
while skip > 0:
try:
packet = packet.data
except:
return
skip-= 1
if (packet == None):
return
print "%d: %s" % (index, packet.println())
def update(self, packet):
"Interactively update a packet."
pass
def next(self, jump=1):
"""Move to the next packet"""
if ((self.position + jump) > len(self.packets)):
print "Distance %d is too far" % jump
return
self.position += jump
self.display(self.position, self.packets[self.position])
def prev(self, jump=1):
"""Move to the previous packet."""
if (self.position <= 0):
print "Distance %d is too far" % jump
return
if ((self.position - jump) < 0):
print "Distance %d is too far" % jump
return
self.position -= jump
self.display(self.position, self.packets[self.position])
def add_break(self, arg):
"""Add a breakpoint."""
if (arg < 0 or arg > len(self.packets)):
print "Cannot set breakpoint at packet %d" % arg
return
if (arg in self.breakpoints):
print "Breakpoint already set at %d" % arg
return
self.breakpoints.append(arg)
self.breakpoints.sort()
def del_break(self, arg):
if (arg < 0 or arg > len(self.packets)):
print "No breakpoint set at packet %d" % arg
return
self.breakpoints.remove(arg)
def clear_break(self):
self.breakpoints = []
syntax highlighted by Code2HTML, v. 0.9.1