###############################################################################
#                                                                             #
#   Copyright 2005 University of Cambridge Computer Laboratory.               #
#                                                                             #
#   This file is part of Nprobe.                                              #
#                                                                             #
#   Nprobe is free software; you can redistribute it and/or modify            #
#   it under the terms of the GNU General Public License as published by      #
#   the Free Software Foundation; either version 2 of the License, or         #
#   (at your option) any later version.                                       #
#                                                                             #
#   Nprobe is distributed in the hope that it will be useful,                 #
#   but WITHOUT ANY WARRANTY; without even the implied warranty of            #
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             #
#   GNU General Public License for more details.                              #
#                                                                             #
#   You should have received a copy of the GNU General Public License         #
#   along with Nprobe; if not, write to the Free Software                     #
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA #
#                                                                             #
###############################################################################


from string import rjust
from nprobe import intoa_string
from nprobe import TCP_CLIENT_SEEN, TCP_SERVER_SEEN
from nprobe import TCP_SERV_SYN, TCP_CLI_SYN, TCP_SERV_HTTP, SERVER, CLIENT, \
     TRANS_VAL, TRANS_DUMMY_UNSYNCH, TRANS_DUMMY_ERR, TRANS_ERR, \
     TRANS_INCOMPLETE, TRANS_FINISHED
from nprobe import TSP_SYN, TSP_DUP_SYN, TSP_FIN, TSP_ACKSYN, TSP_RST, \
     CT_NON_OBJECT
from  np_longutil import ul2l, ull2l, Longstring
from print_col import cprint, whoops, inform, F_GREEN, F_WHITE
from np_TCP import Trans_Tms_t, Trans_Tms_T, TCP_Machine, TCPNoModel, TCPModelPkts, TCPModelNoTrans

##############################################################################
##############################################################################

#
# Help for TCP
#

TH_SYN = 0x2
TH_RST = 0x4
TH_FIN = 0x1
TH_ACK = 0x10
TH_URG = 0x20
TH_PUSH = 0x08
        

#############################################################################
#############################################################################

L_RTMT_THRESH = 1500000
#
# Values for connection delay flags
#

D_DUPCSYN = 0x1
D_DUPFREQ = 0x2
D_CRETRANS = 0x4
D_NOT_CONNECTED = 0x8
D_REQNOTSEEN = 0x10
D_REPNOTSEEN = 0x20
D_SLOSS = 0x40
D_REPBDLY = 0x80
D_REPSDLY = 0x100
D_REPSLRTMT = 0x200
D_SFINDLY = 0x400
D_INTREE = 0x1000

D_BIGDEL = D_DUPCSYN | D_DUPFREQ | D_NOT_CONNECTED | D_REQNOTSEEN | D_REPNOTSEEN | D_REPBDLY

############################################################################
#############################################################################

#
# Represents a TCP connection
#

class TCPConn:

    def __init__(self, tconn, hdrlist, logfun=None, trace=0):
        
        self.trace = trace
        trace = 1 and trace

        if logfun:
            self.logfun = logfun
        else:
            logfun = self.logfun = self.f_null

        self.modelled = 0
	#self.do_state = do_state
	self.Class = 'TCPConn'
	self.id = tconn.get_conn_id()
	self.server = tconn.dhost()
	self.serverport = tconn.dport()
	self.client = tconn.shost()
        self.persist = 0
        self.intree = 0
        
        #self.obrec = FileRec(self.Class)

	open = self.abstart = ull2l(tconn.open())
        #print 'tcp #%d open at %d' % (self.id, open)
	close = self.abclose = ull2l(tconn.close())

        #if open == close:
            #close = self.abclose = hdrlist[-1].abtm
            
        self.dur = close - open
	# following are all relative to open time
	self.relclisyn = ul2l(tconn.clisyn())
	#print 'clisyn %s' % (ulstring(self.clisyn))
	self.relservsyn = ul2l(tconn.servsyn())
	self.relcliacksyn = ul2l(tconn.cliacksyn())
	self.relservacksyn = ul2l(tconn.servacksyn())
	self.relclifin = ul2l(tconn.clifin())
	self.relservfin = ul2l(tconn.servfin())
	self.relclirst = ul2l(tconn.clirst())
	self.relservrst = ul2l(tconn.servrst())

	self.relsfdata = ul2l(tconn.servfirstdata())
	self.relsldata = ul2l(tconn.servlastdata())
	self.relcfdata = ul2l(tconn.clifirstdata())
	self.relcldata = ul2l(tconn.clilastdata())

	self.slowseq = ul2l(tconn.slowseq())
	self.clowseq = ul2l(tconn.clowseq())
	self.slowack = ul2l(tconn.slowack())
	self.clowack = ul2l(tconn.clowack())

	smss = tconn.get_smss()
        if not smss:
            smss = 536
        self.smss = smss
	cmss = tconn.get_cmss()
        if not cmss:
            cmss = 536
        self.cmss = cmss

 	if open == 0 or close == 0:
            str = 'TCPConn #%d Zero open/close time:' % (self.id)
            whoops(str)
	    tconn.printself(0)
            logfun(str)
 	    #raw_input('anything to continue...\n')
	    

	if self.relclisyn < 0 or self.relservsyn < 0 or self.relcliacksyn < 0 \
	   or self.relservacksyn < 0 or self.relclifin < 0 or self.relservfin < 0 \
	   or self.relclirst < 0 or self.relservrst < 0:
            str = 'TCPConn #%d Negative relative time time:' % (self.id)
            whoops(str)
	    tconn.printself(0)
            logfun(str)
 	    #raw_input('anything to continue...\n')
	    
	if open > close:
            str = 'TCPConn #%d Connection opens after closing:' % (self.id)
            whoops(str)
	    tconn.printself(0)
            logfun(str)
 	    #raw_input('anything to continue...\n')

        # Some connection stuff
        



	# HTTP connection specific
	if tconn.flow_inner.serv_type == TCP_SERV_HTTP:
            self.translist = []
	    self.http_status = tconn.http_status()
	    self.http_verstr = tconn.http_vers_str()[:]
	    self.pers = tconn.http_persistent()
	    self.ntrans = tconn.http_ntrans() # all
            self.ncgood = 0 # good requests
            self.nsgood = 0 # do responses
	else:
	    self.http_status = 0
	    self.http_verstr = ''
	    self.pers = 0
	    
	self.flags = tconn.get_flags()
        if self.flags & TCP_CLIENT_SEEN: 
            self.cflags = tconn.get_cflags()
            self.cbytes = tconn.tot_client_octs()
            self.cpkts = tconn.tot_client_pkts()
            self.cdpkts = tconn.tot_client_dpkts()
            self.closs = tconn.c_rtmts()
            if self.closs:
                str = 'TCPConn #%d Client rtmts:' % (self.id)
                logfun(str)
        else:
            self.cflags = 0x00000000
            self.cbytes = 0
            self.cpkts = 0
            self.cdpkts = 0
            self.closs = 0
            
        if self.flags & TCP_SERVER_SEEN:
            self.sflags = tconn.get_sflags()
            self.sbytes = tconn.tot_server_octs()
            self.spkts = tconn.tot_server_pkts()
            self.sdpkts = tconn.tot_server_dpkts()
            self.sloss = tconn.s_rtmts()
            if self.sloss:
                str = 'TCPConn #%d Server rtmts:' % (self.id)
                logfun(str)
                
        else:
            self.sflags = 0x00000000
            self.sbytes = 0
            self.spkts = 0
            self.sdpkts = 0
            self.sloss = 0
            
        if self.cdpkts == 0:
            str1 = 'TCPConn #%d No client data packets:' % (self.id)
            str2 = 'WebClient #%s No client data packets:' % (intoa_string(self.client))
            if trace:
                inform(str1)
            logfun(str1)
            logfun(str2)
            
        if self.sdpkts == 0:
            str1 = 'TCPConn #%d No server data packets:' % (self.id)
            str2 = 'WebClient #%s No server data packets:' % (intoa_string(self.server))
            if trace:
                inform(str1)
            logfun(str1)
            logfun(str2)
            
	self.pktlist = hdrlist
	#self.pktlist = []
	self.objects = []

	self.noloss = tconn.no_rtmts_or_gaps()
        if self.sloss:
            self.soop = tconn.n_s_ooo_pkts()
            self.srtmtp = tconn.n_s_dup_pkts()
        
        #del(tconn)
	# TMP for debugging
	#self.tconn = tconn

##         # XXX TMP XXX
##         str = 'TCPConn #%d TCP Connection:' % (self.id)
##         logfun(str)

  ##   def __del__(self):
##         print 'Freeing TCPConn'

#############################################################################

    def f_null(self, arg):

        pass


#############################################################################

    #
    # Adjust all time offsets to common base 
    #

    def adjust_tm_offsets(self, base):
        
        #
        # **idempotent**
        #
	# self.abstart = absolute open time
	# absolute -> relative to start
	open = self.open = self.abstart - base
	#print 'conn open %s' % (tsLongstring(open))
	self.close = self.abclose - base
	# relative to open -> relative to start
	self.clisyn = open + self.relclisyn
	self.servsyn = open + self.relservsyn
	self.cliacksyn = open + self.relcliacksyn
	self.servacksyn = open + self.relservacksyn
	self.clifin = open + self.relclifin
	self.servfin = open + self.relservfin
	self.clirst = open + self.relclirst
	self.servrst = open + self.relservrst
        self.cldata = open + self.relcldata
        self.sldata = open + self.relsldata
        self.cfdata = open + self.relcfdata
        self.sfdata = open + self.relsfdata
	self.spktlist = [] # tmp
	self.cpktlist = [] # tmp

	slast = clast = None

	for pkt in self.pktlist:
            pkt.tm = pkt.abtm -base

            if pkt.dir == SERVER:
                self.spktlist.append(pkt)
                if slast != None:
                    slast.nxt = pkt
                slast = pkt  
            elif pkt.dir == CLIENT:
                self.cpktlist.append(pkt)
                if clast != None:
                    clast.nxt = pkt
                clast = pkt
            else:
                print 'Goof - unknown way pkt % d' % (pkt.indx)
                #self.printself_with_pkts()
		    #sys.exit(1)

	#print
        if slast:
            slast.nxt = slast
        if clast:
            clast.nxt = clast


#############################################################################

    def make_clusters(self):

        trace = 0 and self.trace
        
## 	i=0
## 	for p in self.pktlist:
## 	    p.indx = i
## 	    i = i+1

	#sdur = int(self.sldata-self.sfdata)
	sdur = self.sldata-self.sfdata
	if sdur == 0:
	    sdur = 1
	cdur = int(self.cldata-self.cfdata)
	if cdur == 0:
	    cdur = 1
	#print 'sdur = %.3f cdur = %.3f' % (sdur/1000.0, cdur/1000.0)
	try:
	    sthresh = (sdur/self.sdpkts)/2
	except ZeroDivisionError:
          ##   str = 'TCPConn #%d S Zero data packets:' % (self.id)
##             if trace:
##                 inform(str)
##             self.logfun(str)
	    sthresh = 1
	try:
	    cthresh = (cdur/self.cdpkts)/2
	except ZeroDivisionError:
          ##   str = 'TCPConn #%d C Zero data packets:' % (self.id)
##             if trace:
##                 inform(str)
##             self.logfun(str)
	    cthresh = 1
	    
	#print 'spkts = %d cpkts = %d' % (self.sdpkts, self.cdpkts)
	#print 'sthresh = %.3f cthresh = %.3f ' % (sthresh/1000.0, cthresh/1000.0)

        cclus = []
        sclus = []
        self.sdbw = []
        self.cdbw = []
	for way, pl, clul, thresh, bwl in [
            (SERVER, self.spktlist, sclus, sthresh, self.sdbw),
            (CLIENT, self.cpktlist, cclus, cthresh, self.cdbw)
            ]:
            if not pl:
                continue
	    clu = 0
	    lt = pl[0].tm
            cl = []
	    for p in pl:
		if p.len or p.flags & (TH_SYN | TH_FIN):
		    if p.tm-lt > thresh:
                        clul.append(cl)
                        cl = []
			clu = clu+1
			p.clu = clu
                        cl.append(p)
		    else:
			p.clu = clu
                        cl.append(p)
		    lt = p.tm
		else:
		    p.clu=999
            clul.append(cl)
		#print 'pkt %d next %d clu %d' % (p.indx, p.nxt.indx, p.clu)

            for cl in clul:
                if len(cl) > 1:
                    octs = 0
                    for p in cl[:-1]:
                        octs += p.len + 20 + 20 + 18
                    per = cl[-1].tm - cl[0].tm
                    bw = float(octs)/per
                    bwl.append(bw)

            if self.trace:
                if way == SERVER:
                    w = 'Server'
                else:
                    w = 'Client'
                print self.id, w,'clusters ['
                for cl in clul:
                    print '[',
                    octs = 0
                    for p in cl:
                        octs += p.len + 20 + 20 + 18
                        print '(', p.indx, p.clu, p.tm/1000, ')',
                    octs -= cl[-1].len + 20 + 20 + 18
                    print ']',
                    if len(cl) > 1:
                        print octs, '/', cl[-1].tm - cl[0].tm, '=', float(octs)/(cl[-1].tm - cl[0].tm)
                    else:
                        print
                print ']'
		

#############################################################################
		
    def calc_delays(self, cliid, imps, adict):

        #
        # find delays associated with this connection
        #

        def report(s, n, d):
            str = 'WebClient #%s %s - clients: TCPConn #%d %d %d' \
                  % (intoa_string(cliid), s, self.id, n, d)
            self.logfun(str)
            if self.trace:
                whoops(str)
            str = 'TCPConn #%d %s - conns: Client #%s %d %d' % ( self.id,
                                            s, intoa_string(cliid), n, d)
            self.logfun(str)
            if self.trace:
                whoops(str)

        delays = []
        cflags = self.cflags
        sflags = self.sflags
        connected = sflags & TSP_SYN
        gotreq = self.cdpkts
        gotrep = self.sdpkts
        ncsyns = 1
        csyndelay = 0
        nfirstreqr = 0
        firstreqdelay = 0
        nreqr = 0
        reqdelay =0
        nfpkts = 0
        dflags = 0
        nspkts = 0
        nsertmts = 0
        nsrtmts = 0
        repbdelay = 0
        repsdelay = 0
        spkts = []
        cgoodacks = 0
        sFIN = 0
        sRTTest = 0
        nslrtmts = 0
        slrtmtdelay = 0

        if (cflags & TSP_DUP_SYN) or self.closs or self.sloss:
            ncsyns = 0
            chiseq = 0
            lastreqtm = 0L
            lastspkt = 0L
            gotreq = 0
            gotrep = 0
            sfirst = cfirst = 1
            for p in self.pktlist:
                if p.dir == SERVER:
                    seq = p.seq
                    len = p.len
                    end = seq + len
                    if sfirst:
                        shiseq = stopseq = end
                        sfirst = 0
                    if p.flags & TH_SYN:
                        end = seq + 1
                        shiseq = ssynseq = seq
                        shiseq += 1
                        stopseq = shiseq
                        spkts.append([seq, seq+1, 0, [p.tm], p.indx])
                        #print  p.indx, 'on plist', seq-ssynseq, seq+1-ssynseq, p.tm
                        #print 'SYN chiseq', chiseq
                    elif len:
                        gotrep = 1
                        end = seq+len
                        #print seq, len, end
                        if seq == shiseq or seq == stopseq:
                            shiseq += len
                            lastspkt = p.tm
                            nspkts += 1
                            #print 'ok'
                            spkts.append([seq, end, 0, [p.tm], p.indx])
                            #print  p.indx, 'on plist', seq-ssynseq, end-ssynseq, p.tm
                        #elif seq > chiseq:
                        elif end <= stopseq:
                            if sFIN and end >= sFIN and p.tm - sFINtm > L_RTMT_THRESH:
                                sfindly = p.tm - sFINtm
                                #print p.indx, 'REPLAST', 'dly=', sfindly/1000
                                dflags |= D_SFINDLY
                                if self.translist:
                                    try:
                                        self.translist[0].delrepend = self.translist[0].repend + sfindly
                                    except AttributeError:
                                        print self.translist[0]
                                        raise
                            else:
                                for pp in spkts:
                                    if pp[0] <= seq < pp[1]:
                                        pp[2] = 1
                                        pp[3].append(p.tm)
                                    #print  p.indx, 'set to revisit', seq-ssynseq, end-ssynseq, '(->', pp[4], pp[0]-ssynseq, pp[1]-ssynseq, pp[3], ')'
                            sdelay = p.tm - lastspkt
                    if p.flags & TH_FIN:
                        if not sFIN:
                            sFINtm = p.tm
                        sFIN = end
                        #print p.indx, 'sFIN', sFIN-ssynseq
                    stopseq = max(end, stopseq)
                        
                    #print 'topseq=', stopseq-ssynseq, 'hiseq=', shiseq-ssynseq
                else:
                    seq = p.seq
                    len = p.len
                    if cfirst:
                        chiseq = ctopseq = end = seq+len
                        lastreqtm = p.tm
                    if p.flags & TH_ACK:
                        ack = p.ack
                        sacki = 0
                        bads = 0
                        for pp in spkts:
                            if ack < pp[1]:
                                break
                            #print p.indx, 'ack', ack-ssynseq, 'saw', pp[4],
                            if not pp[2]:
                                if not bads:
                                    cgoodacks += 1
                                    #print 'goodack', cgoodacks
                                #else:
                                    #print
                            else:
                                bads += 1
                                if cgoodacks < 3:
                                    cgoodacks = 0
                                lastbad = sacki
                                #print 'nullack'
                            sacki += 1
                        if bads:
                            lb = spkts[lastbad]
                            sdelay = lb[3][-1] - lb[3][0]
                            if sdelay < L_RTMT_THRESH:
                                sRTTest = 1
                            if sRTTest:
                                if sdelay < L_RTMT_THRESH:   
                                    nsrtmts += 1
                                    repsdelay += sdelay
                                    dflags |= D_REPSDLY
                                    # print p.indx, 'smalldelay', lb[3][-1]/1000, lb[3][0]/1000, sdelay/1000, lb[4]
                                else:   
                                    nslrtmts += 1
                                    slrtmtdelay += sdelay
                                    dflags |= D_REPSLRTMT
                                    # print p.indx, 'long retransmit', lb[3][-1]/1000, lb[3][0]/1000, sdelay/1000, lb[4]
                            else:
                                nsertmts += 1
                                repbdelay += sdelay
                                dflags |= D_REPBDLY
                                #print p.indx, 'bigdelay', lb[3][-1]/1000, lb[3][0]/1000, sdelay/1000, lb[4]
                            
                        del spkts[0:sacki]
                        #print 'del->', sacki
                        
                    if p.flags & TH_SYN:
                        ncsyns += 1
                        if ncsyns > 1:
                            dflags |= D_DUPCSYN
                            csyndelay = p.abtm - self.abstart
                        chiseq = csynseq = seq
                        chiseq += 1
                        ctopseq = chiseq
                        #print 'SYN chiseq', chiseq
                    elif len:
                        gotreq = 1
                        end = seq+len
                        #print seq, len, end
                        ctopseq = max(end, ctopseq)
                        if seq == csynseq + 1: # first request
                            if end <= chiseq:
                                nfirstreqr += 1
                                firstreqdelay += p.tm - lastreqtm
                                dflags |= D_DUPFREQ
                                #print 'D_DUPFREQ'
                            chiseq = end
                            lastreqtm = p.tm
                        else:
                            #print 'follower', 
                            nfpkts += 1
                            if seq == chiseq or seq == ctopseq:
                                chiseq += len
                                lastreqtm = p.tm
                                #print 'ok'
                            #elif seq > chiseq:
                            elif end <= ctopseq:
                                nreqr += 1
                                reqdelay += p.tm - lastreqtm
                                dflags |= D_CRETRANS
                                #print 'rtmt'
            #raw_input('...')

        if self.sloss:
            dflags |= D_SLOSS


        if not gotreq:
            dflags |= D_REQNOTSEEN
        if not gotrep:
            dflags |= D_REPNOTSEEN


        if connected:
            if nfirstreqr:
                delays.append(nfirstreqr)
                delays.append(int(firstreqdelay/1000))
                if firstreqdelay/1000 < 1000:
                    # first req rtmtd in less than 1s
                    report('Fast request retransmit', nfirstreqr, int(firstreqdelay/1000))
                if gotrep:
                    report('Duplicate requests', nfirstreqr, int(firstreqdelay/1000))
                else:
                    report('Duplicate requests - no server response', nfirstreqr, int(firstreqdelay/1000))
            elif not gotreq:
                report('No request', 1, int(self.dur/1000))
                delays.append(1)
                delays.append(int(self.dur/1000))
            elif not gotrep:
                report('No server response', 1, int(self.dur/1000))
            if nreqr:
                delays.append(nreqr)
                delays.append(int(reqdelay/1000))
                report('Request pkt retransmit', nreqr, int(reqdelay/1000))
        else:
            dflags |= D_NOT_CONNECTED
            csyndelay = self.dur


        if dflags & D_DUPCSYN:
            delays.append(ncsyns-1)
            delays.append(int(csyndelay/1000))
            if connected:
                report('Duplicate CliSYN', ncsyns-1, int(csyndelay/1000))
            else:
                report('Duplicate CliSYN, connection refused', ncsyns-1, int(csyndelay/1000))
        elif not connected:
            report ('Connection refused', 0, int(csyndelay/1000))
            delays.append(0)
            delays.append(int(csyndelay/1000))

        if (not gotrep) and gotreq:
            delays.append(1)
            delays.append(int(self.dur/1000))

##         if dflags & D_SLOSS:
##             report('Server pkt loss')
##             #self.calc_pkt_loss(imps, adict)
##             delays.append(self.soop)
##             delays.append(self.srtmtp)

        if dflags & D_REPSLRTMT:
            report('Server long rtmt',  nslrtmts, int(slrtmtdelay/1000))
            delays.append(nslrtmts)
            delays.append(int(slrtmtdelay/1000))

        if dflags & D_SFINDLY:
            report('Server fin delay', 1, int(sfindly/1000))
            delays.append(nsrtmts)
            delays.append(int(sfindly/1000))

        if dflags & D_REPSDLY:
            report('Server later loss', nsrtmts, int(repsdelay/1000))
            delays.append(nsrtmts)
            delays.append(int(repsdelay/1000))

        if dflags & D_REPBDLY:
            report('Server early loss', nsertmts, int(repbdelay/1000))
            delays.append(nsertmts)
            delays.append(int(repbdelay/1000))

        if dflags:
            delays.append(dflags)
            self.delays = delays
            self.dly = dly = (self.open/1000000.0, nfpkts-nreqr, self.cdpkts,
                              self.sdpkts, delays)
        else:
            delays = self.delays = None
            self.dly = dly = (self.open/1000000.0, self.cdpkts-1,
                              self.cdpkts, self.sdpkts)

        return (gotreq, dflags)

#############################################################################

    def calc_pkt_loss(self, imps, adict):

        t = self.model(imps, adict)
        if not t:
            str = 'TCPConn #%d Couldn\'t model: ' % (self.id)
            #raw_input('...')
            self.logfun(str)
    

#############################################################################
		
    def get_delays(self, d_or_n):

        #
        # Return None if no delays on the connection else number or duration
        # of delays (depending on value of d_or_n) as:
        # [0: flags,
        #  1: not connected,
        #  2: not connected after dup client SYNs,
        #  3: connected after dup client SYNs,
        #  4: connected - no req,
        #  5:  duplicate first requests,
        #  6: retrans packets on any subsequent requests,
        #  7: no reply,
        #  8: server early retransmits,
        #  9: server later retransmits,
        # 10: server fin delays,
        # 11: server long retransmits
        # ]
        #


        dels = self.delays
        if not dels:
            return None
        flags = dels[-1]
        if not (flags & D_BIGDEL):
            return None

        #print '%d %x' % (self.id, dels[-1]), 
        #print dels
        dl = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        di = d_or_n # 1 yields delay in ms, 0 yields number of occurences
        si = d_or_n -2
        dl[0] = flags
        try:
            if flags & D_DUPFREQ:
                dl[5] = dels[di]
                di += 2
            elif flags & D_REQNOTSEEN and not (flags & D_NOT_CONNECTED):
                dl[4] = dels[di]
                di += 2   
            if flags & D_CRETRANS:
                dl[6] = dels[di]
                di += 2
            if flags & D_DUPCSYN:
                if flags & D_NOT_CONNECTED:
                    dl[2] = dels[di]
                else:
                    dl[3] = dels[di]
                di += 2
            elif flags & D_NOT_CONNECTED:
                dl[1] = dels[di]
                di += 2
            if flags & D_REPNOTSEEN and not (flags & D_REQNOTSEEN):
                dl[7] = dels[di]
            if flags & D_REPBDLY:
                dl[8] = dels[si]
                si -= 2
            if flags & D_REPSDLY:
                dl[9] = dels[si]
                si -= 2
            if flags & D_SFINDLY:
                dl[10] = dels[si]
                si -= 2
            if flags & D_REPSLRTMT:
                dl[11] = dels[si]
        except IndexError:
            str = 'TCPConn #%d Get_delays error: flags%x di%d len%d' % (self.id, flags, di, len(dels))
            whoops(str)

            self.logfun(str)

        return dl
            
#############################################################################

    def get_ttms(self):

        def patch_tend():

            raw_input('...')

            tl = self.translist
            ti = 1
            tr = tl[ti]
            rqs = tr.reqstart

            for p in self.sdpkts:
                tm = p.tm
                if rqs < tm:
                    tr.repend = lastm
                    ti += 1
                    if ti == len(tl):
                        break
                    tr = tl[ti]
                    rqs = tr.reqstart
                lastm = tm
                    

        
	#
	# Get any info we have on usage of the connection
	#
        i = 0
        if self.http_status and self.tlist:
	    self.ttms = []
            for i in range(len(self.tlist)):
                self.ttms.append(Trans_Tms_t(self.tlist[i], i, self.open))
            self.uagent = self.tlist[0].get_uagent()
            self.sagent = self.tlist[0].get_server()
            if i > 0:
                str = 'TCPConn #%d Multiple transactions:' % (self.id)
                self.logfun(str)
            else:
                str = 'TCPConn #%d Single transaction:' % (self.id)
                self.logfun(str)
        elif self.http_status and self.translist:
	    self.ttms = []
            for i in range(len(self.translist)):
                self.ttms.append(Trans_Tms_T(self.translist[i], i))
            self.uagent = self.translist[0].uagent
            self.sagent = self.translist[0].server
            if i > 0:
                patch_tend()
                str = 'TCPConn #%d Multiple transactions:' % (self.id)
                self.logfun(str)
            else:
                str = 'TCPConn #%d Single transaction:' % (self.id)
                self.logfun(str)
	else:
	    self.ttms = [None]
	    str = 'TCPConn #%d NO TRANSACTIONS:' % (self.id)
	    if self.trace: 
		whoops(str)
	    self.logfun(str)
	    #self.valid = 0
	    self.uagent = None
	    self.sagent = None
            raise TCPModelNoTrans


#############################################################################
		
    def model(self, imps, adict):

        self.make_clusters()
        self.calc_delays(self.client, imps, adict)
        t = None
        try:
            self.get_ttms()
            try:
                t = TCP_Machine(self, imps, self.logfun,
                                trace=self.trace)
                try:
                    imps.set_start()
                    simp, cimp = t.do_pkts()
                    self.modelled = 1
                    #stats.add_TCPStats(t)
                    str = 'TCPConn #%d Modelled: ' % (self.id)
                    self.logfun(str)
                except TCPNoModel:
                    # can't model this one
                    cimp = simp = t = None
                    str = 'TCPConn #%d Can\'t model: ' % (self.id)
                    whoops(str)
                    self.logfun(str)
                
            except TCPModelPkts:
                cimp = simp = t = None
                print 'INSUFFICIENT PACKETS'
            except:
                #something really bad
                str = 'TCPConn #%d Total failure: ' % (self.id)
                #raw_input('...')
                whoops(str)
                self.logfun(str)
                cimp = simp = t = None
                raise
        except TCPModelNoTrans:
            cimp = simp = t = None
            print 'NO TRANS'
        

        if adict:
            adict.add(SERVER, self.server, self.sagent, cimp)
            adict.add(CLIENT, self.client, self.uagent, simp)

        if simp and simp.iw_fact == 0:
            str = 'TCPConn #%d Zero server IWF: ' % (self.id)
            self.logfun(str)

        if cimp and cimp.iw_fact == 0:
            str = 'TCPConn #%d Zero client IWF: ' % (self.id)
            self.logfun(str)

        self.cimp = cimp
        self.simp = simp
        self.model = t
        
        return t

#############################################################################
		
    def print_pktlist(self):


	for p in self.pktlist:

	    #way = ord(p.way)
	    way = p.dir
	    #flags = ord(p.flags)
	    flags = p.flags
	    if flags & TH_ACK:
		ackstr = 'ack%s' % (Longstring(p.ack, width=10))
	    else:
		ackstr = ''
	    if way == SERVER:
		dir = '<'
		col = F_WHITE
	    elif way == CLIENT:
		dir = '>'
		col = F_GREEN
            else:
                dir = 'X'
                col = F_RED

	    flagstr = ''
	    if flags & TH_SYN:
		flagstr = flagstr+'S'
	    if flags & TH_ACK:
		flagstr = flagstr+'A'
	    if flags & TH_FIN:
		flagstr = flagstr+'F'
	    if flags & TH_RST:
		flagstr = flagstr+'R'
	    if flags & TH_PUSH:
		flagstr = flagstr+'P'
	    if flags & TH_URG:
		flagstr = flagstr+'U'

	    tstr = rjust('%.3f' % (p.tm/1000.0), 9)

	    str = '%3d %s %s %s +%4d %s w:%5d %-5s' % (p.indx, tstr, dir, 
					  Longstring(p.seq, width=10),
					  p.len, ackstr,
					  p.window, flagstr)

	    #cprint(col, str)
	    print str
		    


#############################################################################

    def printself(self, fnm=None):

        #print self.FileRec.recsets
	tconn = self.FileRec.build_tconn(self.FileRec.recsets)[0]
        if fnm:
            tconn.printself_tofile(fnm, 0)
        else:
            tconn.printself(0)

#############################################################################

    def printself_with_pkts(self):

	tconn = self.FileRec.build_tconn(self.FileRec.recsets)[0]
	tconn.printself(0)
	self.print_pktlist()

#############################################################################

    def printself_with_trans(self, fnm=None):
	
	tconn, hlist, translist = self.FileRec.build_tconn(self.FileRec.recsets)

        if not fnm:
            tconn.printself(0)
            for t in translist:
                t.printself(tconn)
        else:
            tconn.printself_tofile(fnm, 0)
            i = 0
            for t in translist:
                t.printself_tofile(i, tconn, fnm, 'a')
                i += 1

#############################################################################
#############################################################################

#
# Represents a TCP pkt
#

class TCPHdr:

    #
    # hdrs is the tcp_hdrs object containing the buffer of dumped tcp_dumphdr
    # structs and the abs time to which time stamps are relative
    # indx is in to the hdr buffer
    #
    def __init__(self, hdrs, indx, atm, i):

        self.indx = i
        self.seq = ul2l(hdrs.get_seq(indx))
        self.ack = ul2l(hdrs.get_ack(indx))
        self.abtm = atm + hdrs.get_rtm(indx)
        fw = hdrs.get_flags_way(indx)
        self.flags = fw >> 8
        self.dir = fw & 0xff
        wl = hdrs.get_win_len(indx)
        self.window = ul2l(wl >> 16)
        self.len = wl & 0xffff
        #self.trig = 0
        #self.delay = 0
        

#############################################################################
#############################################################################
