from itertools import repeat import logging from pcaputil import header_conversation_iter import client_proto from client import (SpiceLinkHeader, SpiceLinkMess, SpiceLinkReply, SpiceDataHeader) logger = logging.getLogger('pcapspice') def is_single_packet_data(payload): return len(payload) == SpiceDataHeader(payload).e.size + SpiceDataHeader.size # guesses = {} def guess_channel_iter(): channel = None optional_channels = set(client_proto.all_channels) seen_headers = [] while True: src, dst, data_header = yield channel key = (min(src, dst), max(src, dst)) if guesses.has_key(key): optional_channels = set([guesses[key]]) seen_headers.append((src, dst, data_header)) optional_channels = optional_channels & client_proto.possible_channels( src == min(src, dst), data_header) logger.debug(str(optional_channels)) if len(optional_channels) == 1: channel = list(optional_channels)[0] guesses[key] = channel if len(optional_channels) == 0: import pdb; pdb.set_trace() def ident(x, **kw): return x def channel_spice_message_iter(src, dst, guesser): """ coroutine, accepts (src,dst,header,data) packets and yields maybe parsed spice results. maybe X - returns None or X """ server_port, client_port = min(src, dst), max(src, dst) serial = {} channel = None result = None while True: src, dst, (data_header, message) = yield result result = None bad_header = False if serial.has_key(src): if data_header.e.serial != serial[src] + 1: logger.debug("bad serial, replacing for %s->%s (%s!=%s+1)" % ( src, dst, data_header.e.serial, serial[src])) bad_header = True serial[src] = data_header.e.serial if data_header.e.type not in client_proto.valid_message_ids: logger.error("bad message type %s in %s->%s" % (data_header.e.type, src, dst)) bad_header = True if bad_header: continue channel = guesser.send((src, dst, data_header)) if channel is not None: # read as many messages as it takes to get result = client_proto.parse( channel_type=channel, is_client=src == client_port, header=data_header, data=message) class ChannelGuesser(object): def __init__(self): self.iter = None self.channel = None def send(self, (src, dst, payload)): if self.channel: return self.channel if self.iter is None: self.iter = guess_channel_iter() self.iter.next() self.channel = self.iter.send((src, dst, payload)) return self.channel def on_client_link_message(self, p, **kw): self._client_message = SpiceLinkMess(p) self.channel = self._client_message.e.channel_type logger.info("STARTED channel %s" % self.channel) def on_server_link_message(self, p, **kw): self._server_message = SpiceLinkReply(p) def spice_iter(packet_iter): guessers = {} def make_start_iter(src, dst): is_server = src < dst key = (min(src, dst), max(src, dst)) guesser = guessers[key] = guessers.get(key, ChannelGuesser()) logger.debug('%s %s **** make_start_iter %s -> %s' %( guesser, is_server, src, dst)) link_messages = [] yield (SpiceLinkHeader.size, SpiceLinkHeader.set_proto, lambda h: h.e.size, (guesser.on_server_link_message if is_server else guesser.on_client_link_message)) logger.debug('%s %s 2' % (guesser, is_server)) yield 128 if src > dst else 4, ident, lambda h: 0, ident for i, data in enumerate(make_iter(src, dst)): logger.debug('%s %s 2+%s' % (guesser, is_server, i)) yield data def make_iter(src, dst): key = (min(src, dst), max(src, dst)) guesser = guessers[key] = guessers.get(key, ChannelGuesser()) message_iter = channel_spice_message_iter(src, dst, guesser) message_iter.next() return repeat( (SpiceDataHeader.size, SpiceDataHeader.verify, lambda h: h.e.size, lambda pkt, src, dst, header: message_iter.send((src, dst, (header, pkt))))) def filter_result(r): return (r.msg is not None and r.msg.data != None and hasattr(r.msg.data, 'result_name')) return header_conversation_iter( packet_iter, server_start=make_start_iter, client_start=make_start_iter, server=make_iter, client=make_iter, filter_result=filter_result)