#!/usr/bin/env python

# tcpsplit
# Mark Allman (mallman@icir.org)

# Copyright (c) 2004--2025 International Computer Science Institute
# 
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# 
# The names and trademarks of copyright holders may not be used in
# advertising or publicity pertaining to the software without specific
# prior permission. Title to copyright in this software and any
# associated documentation will at all times remain with the copyright
# holders.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import os
import re
import sys

DEBUG = False

cnns = {}
trace_pkts = 0
base_total = None
sub_total = 0
min_sub = None
max_sub = None
weird_pkts = 0

class Conn:
    def __init__ (self,ep):
        self.endpoint = ep
        self.traces = {}

    def Update (self,pkt,tname):
        try:
            self.traces [tname] += 1
        except:
            self.traces [tname] = 1

    def Check (self):
        if len (self.traces) != 2:
            sys.stderr.write ("connection in multiple traces:\n")
            sys.stderr.write ("  %s\n" % self.endpoint)
            sys.stderr.write ("  %s\n" % self.traces.keys ())
            return (False)
        return (True)

    ## This is just for debugging.
    def Dump (self):
        for i in self.traces:
            sys.stdout.write ("C %s file %s\n" % (self.endpoint,i))

def Usage ():
    sys.stderr.write ("%s original_trace sub_traces\n" % sys.argv [0])
    sys.exit (1)
    
def ProcessPacket (pkt,tname):
    global cnns, trace_pkts
    
    if pkt == None:
        return
    m = re.search ("length.*\d+\)\s+(\S+)\.(\d+) > (\S+)\.(\d+): Flags",pkt)
    if m == None:
        sys.stderr.write ("cannot grok packet:\n")
        sys.stderr.write ("%s\n" % pkt)
        sys.exit (1)
    trace_pkts += 1
    if m.group (1) < m.group (3):
        ep = "%s:%s<->%s:%s" % \
            (m.group (1),m.group (2),m.group (3),m.group (4))
    else:
        ep = "%s:%s<->%s:%s" % \
            (m.group (3),m.group (4),m.group (1),m.group (2))
    if ep not in cnns:
        cnns [ep] = Conn (ep)
    cnns [ep].Update (pkt,tname)
    

def ProcessTrace (tname):
    global base_total, sub_total, trace_pkts, min_sub, max_sub, weird_pkts
    
    sys.stdout.write ("## processing ... %s" % tname)
    sys.stdout.flush ()
    pkt = None
    for i in os.popen ("tcpdump -vvvv -tt -S -nn -r %s 2>/dev/null" % tname):
        i = i.rstrip ()
        if pkt == None:
            pkt = i
            continue 
        if re.search ("^\s", i) != None:
            pkt += i
            continue
        ProcessPacket (pkt,tname)
        pkt = i
    if pkt != None:
        ProcessPacket (pkt,tname)
    if base_total == None:
        base_total = trace_pkts
        min_sub = trace_pkts + 1
        max_sub = 0
    else:
        if re.search ("weird",tname) == None:
            sub_total += trace_pkts
            min_sub = min (min_sub,trace_pkts)
            max_sub = max (max_sub,trace_pkts)
        else:
            weird_pkts += trace_pkts
    sys.stdout.write (" ... %d pkts read\n" % trace_pkts)
    trace_pkts = 0

print len (sys.argv)
sys.exit (1)

if len (sys.argv) < 3:
    Usage ()
    
for i in sys.argv [1:]:
    ProcessTrace (i)

all_good = True
for i in cnns:
    if not cnns [i].Check ():
        all_good = False

sys.stdout.write ("##\n")
sys.stdout.write ("total pkts in base trace:      %d\n" % base_total)
sys.stdout.write ("total pkts in sub-traces:      %d\n" % sub_total)
sys.stdout.write ("  minimum pkts in a sub-trace: %d\n" % min_sub)
sys.stdout.write ("  maximum pkts in a sub-trace: %d\n" % max_sub)
sys.stdout.write ("pkts in weird sub-trace:       %d\n" % weird_pkts)
sys.stdout.write ("all conns in only 1 sub-trace: %s\n" % all_good)

if DEBUG:
    sys.stdout.write ("##\n")
    for i in cnns:
        cnns [i].Dump ()
