Пример #1
0
    def createchan(self, pkt, x, y):
        # Older clients first report version here
        self.version=pkt.p2

        name=str(pkt.body).strip('\0')
        pv = self.server.GetPV(name)

        if pv is None:
            # PV does not exist
            log.debug("Can't create channel for non-existant PV %s",name)
            fail = CAmessage(cmd=26, p1=pkt.p1)
            self.send(fail.pack())
            return

        chan=Channel(self.next_sid, pkt.p1, self.server, self, pv)
        self.channels[chan.sid]=chan
        dtype, maxcount = pv.info(chan)

        ok = CAmessage(cmd=18, dtype=dtype, count=maxcount,
                       p1=pkt.p1, p2=chan.sid)

        rights = CAmessage(cmd=22, p1=pkt.p1, p2=chan.rights)

        self.send(ok.pack()+rights.pack())
        
        self.next_sid=self.next_sid+1
        while self.next_sid in self.channels:
            self.next_sid=self.next_sid+1
Пример #2
0
    def test_two(self):
        """Unpack two packets from the same buffer
        """
        msg1='\x00\x01\x00\x08\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07' \
             'hello w\x00'
        msg2 = '\x00\x02\x00\x00\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07'
        msg3 = 'testing'
        buf = msg1 + msg2 + msg3

        exp1 = CAmessage(cmd=1,
                         size=8,
                         dtype=2,
                         count=3,
                         p1=0x40005,
                         p2=0x60007,
                         body='hello w\x00')
        exp2 = CAmessage(cmd=2,
                         size=0,
                         dtype=2,
                         count=3,
                         p1=0x40005,
                         p2=0x60007)

        pkt1, rem = CAmessage.unpack(buf)

        self.assertEqual(pkt1, exp1)
        self.assertEqual(rem, buffer(msg2 + msg3))

        pkt2, rem = CAmessage.unpack(rem)
        self.assertEqual(pkt2, exp2)

        self.assertEqual(rem, buffer(msg3))
Пример #3
0
    def connectionMade(self):
        self.peer = self.transport.getPeer()
        log.debug('Open %s', self)

        user = padString(self.client.user)
        host = padString(self.client.host)

        msg = CAmessage(cmd=0, dtype=self.prio, count=defs.CA_VERSION).pack()
        msg += CAmessage(cmd=20, size=len(user), body=user).pack()
        msg += CAmessage(cmd=21, size=len(host), body=host).pack()

        self.transport.write(msg)

        self.readyWait = reactor.callLater(0.1, self.circuitReady)
Пример #4
0
    def test_tcplookup(self):
        client=StubClient()
        client.tst=self

        name1=padString('test1')
        name2=padString('anotherpv')
        user=padString('hello')
        host=padString('world')

        sfact=self.sfact=CAExpectFactory()
        sfact.tst=self
            
        self.serv=reactor.listenTCP(0, sfact, interface='127.0.0.1')
        target=('127.0.0.1', self.serv.getHost().port)

        sfact.program= \
            [('send',CAmessage(dtype=0, count=12)),
             ('recv',CAmessage(dtype=0, count=CA_VERSION)),
             ('recv',CAmessage(cmd=20, size=len(user), body=user)),
             ('recv',CAmessage(cmd=21, size=len(host), body=host)),
             ('recv',CAmessage(cmd=6, size=len(name1),
                               dtype=5, count=CA_VERSION,
                               p1=0, p2=0, body=name1)),
             ('send',CAmessage(cmd=6, size=8, dtype=target[1],
                               p1=0xffffffff, p2=0,
                               body=searchbody.pack(12))),
             ('recv',CAmessage(cmd=6, size=len(name2),
                               dtype=5, count=CA_VERSION,
                               p1=1, p2=1, body=name2)),
             ('send',CAmessage(cmd=6, size=8, dtype=target[1],
                               p1=0xffffffff, p2=1,
                               body=searchbody.pack(12))),
            ]
        
        self.cfact=CACircuitFactory(client)

        conf=Config(Config.empty)
        conf.nameservs=[target]
        
        resolv=self.resolv=Resolver(conf=conf, tcpfactory=self.cfact)
        

        d=resolv.lookup('test1')

        @d.addCallback
        def result(srv):
            self.assertEqual(srv, target)
            
            self.assertEqual(len(self.sfact.program),2)
            
            return resolv.lookup('anotherpv')

        @d.addCallback
        def result(srv):
            self.assertEqual(srv, target)
            
            self.assertEqual(len(self.sfact.program),0)

        return d
Пример #5
0
    def _chanOk(self, chan):
        self.__D = None
        if chan is None:
            # channel has shutdown
            self.close()
            return

        assert self._chan is chan

        ver = chan._circ.version

        self.ioid = chan._circ.pendingActions.add(self)

        dbf = self.dbf
        if dbf is None:
            dbf, _ = dbr_to_dbf(chan.dbr)
        dbr = dbf_to_dbr(dbf, self._meta)

        # use dynamic array length whenever possible
        cnt = self.count if ver < 13 else 0
        if cnt is None or cnt > chan.maxcount:
            cnt = chan.maxcount

        msg = CAmessage(cmd=15,
                        dtype=dbr,
                        count=cnt,
                        p1=chan.sid,
                        p2=self.ioid).pack()
        chan._circ.send(msg)

        d = self.__D = self._chan.whenDis
        d.addCallback(self._circuitLost)

        return chan
Пример #6
0
        def done(_):
            # send channel create fail
            self.chan.dispatch(CAmessage(cmd=26), circ)
            self.assertTrue(self.chan._d is None)

            self.assertEqual(self.chan.state, self.chan.S_init)
            self.chan.close()
            circ.close()
Пример #7
0
 def clearchan(self, pkt, x, y):
     chan=self.channels.get(pkt.p1)
     if not chan:
         log.warning('Attempt to clean non-existent channel')
         return
     
     chan.close()
     ok = CAmessage(cmd=12, p1=pkt.p1, p2=pkt.p2)
     self.send(ok.pack())
Пример #8
0
    def test_udplookup(self):
        name=padString('test1')

        serv=CAExpectDatagramProtocol(self, [], halt=False)

        up=self.up=reactor.listenUDP(0, serv, interface='127.0.0.1')

        addr=up.getHost()
        addr=addr.host, addr.port

        conf=Config(Config.empty)
        conf.addrs=[addr]
        
        resolv=self.resolv=Resolver(conf=conf)

        serv.dest='127.0.0.1', resolv._udp.getHost().port

        # name search
        # respond after second request
        serv.program= \
            [('recv',CAmessage(dtype=0, count=CA_VERSION)),
             ('recv',CAmessage(cmd=6, size=len(name),
                               dtype=5, count=CA_VERSION,
                               p1=0, p2=0, body=name)),
             ('recv',CAmessage(dtype=0, count=CA_VERSION)),
             ('recv',CAmessage(cmd=6, size=len(name),
                               dtype=5, count=CA_VERSION,
                               p1=0, p2=0, body=name)),
             ('send',CAmessage(cmd=6, size=8, dtype=addr[1],
                               p1=0xffffffff, p2=0,
                               body=searchbody.pack(11))),
            ]
        

        d=resolv.lookup('test1')

        @d.addCallback
        def result(srv):
            self.assertEqual(srv, addr)
            
            self.assertEqual(len(serv.program),0)


        return d
Пример #9
0
    def post(self, mask):
        """Send monitor update if mask matches
        
        Data is read from the PV with the type meta-data
        requested by the client
        """
        if (self.mask & mask) == 0:
            return
        try:
            count = self.count
            if count == 0 and self.channel.circuit.version >= 13:
                # when a client requests a dcount it must get the exact
                # count.  Additional space is zeros.
                # with version 13 a request for zero data get the
                # current native size
                count = self.channel.pv.count
            count = min(count, self.channel.pv.maxcount)

            data, count = self.channel.pv.get(self.channel, self.dbr, count)

            if self.count != 0 and count < self.count:
                # Zero pad data
                dbf, _ = dbr_to_dbf(self.dbr)
                pad = dbf_element_size(dbf) * (self.count - count)
                data = padString(data + '\0' * pad)
                count = self.count

            pkt = CAmessage(cmd=1,
                            size=len(data),
                            dtype=self.dbr,
                            count=count,
                            p1=ECA_NORMAL,
                            p2=self.ioid,
                            body=data)
            log.debug('post to %s', self.channel.circuit.peer)
        except CAError, e:
            log.exception('Post failed')
            pkt = CAmessage(cmd=1,
                            size=0,
                            dtype=self.dbr,
                            count=0,
                            p1=e.code,
                            p2=self.ioid)
Пример #10
0
    def monitordel(self, pkt, peer, circuit):
        if pkt.p2 not in self.monitors:
            raise CAError('Attempt to cancel non-existant monitor',
                          ECA_BADCHID)

        mon = self.monitors.pop(pkt.p2)
        log.debug('Del %s', mon)

        pkt = CAmessage(cmd=1, dtype=mon.dbr, p1=self.circuit.cid, p2=mon.ioid)
        self.circuit.send(pkt.pack())
Пример #11
0
    def test_tcpabort(self):
        """Abort a TCP persistent circuit
        """
        client=StubClient()
        client.tst=self

        name1=padString('test1')
        user=padString('hello')
        host=padString('world')

        sfact=self.sfact=CAExpectFactory()
        sfact.tst=self
            
        self.serv=reactor.listenTCP(0, sfact, interface='127.0.0.1')
        target=('127.0.0.1', self.serv.getHost().port)

        sfact.program= \
            [('send',CAmessage(dtype=0, count=12)),
             ('recv',CAmessage(dtype=0, count=CA_VERSION)),
             ('recv',CAmessage(cmd=20, size=len(user), body=user)),
             ('recv',CAmessage(cmd=21, size=len(host), body=host)),
            ]+[('recv',CAmessage(cmd=6, size=len(name1),
                               dtype=5, count=CA_VERSION,
                               p1=0, p2=0, body=name1))]*6
        
        self.cfact=CACircuitFactory(client)

        conf=Config(Config.empty)
        conf.nameservs=[target]
        
        resolv=Resolver(conf=conf, tcpfactory=self.cfact)
        

        d=resolv.lookup('test1')

        @d.addCallback
        def result(srv):
            self.assertTrue(srv is None)

        d2=deferLater(reactor, 0.5, resolv.close)

        return gatherResults([d,d2])
Пример #12
0
    def regreq(self, peer):
        node = RepeaterNode(self, peer)
        p = Port(0, node, 'localhost')
        p.startListening()
        p.connect(peer[0], peer[1])

        self.clients.add(node)
        log.debug('Repeater add %s', peer)

        rep = CAmessage(cmd=17, p2=INADDR_LOOPBACK)

        node.transport.write(rep.pack())
Пример #13
0
    def _chanOk(self, chan):
        self.__D = None
        if chan is None:
            self.close()
            # channel has shutdown
            return
        assert self._chan is chan

        self.ioid = chan._circ.pendingActions.add(self)

        dbf = self.dbf
        if dbf is None:
            dbf, _ = dbr_to_dbf(chan.dbr)
        dbr = dbf_to_dbr(dbf, self._meta)

        meta = self.meta
        if meta is None:
            meta = caMeta(dbf)

        data = self._data
        cnt = len(data)
        if cnt > chan.maxcount:
            cnt = chan.maxcount
            data = data[:chan.maxcount]

        data, cnt = tostring(data, meta, dbr, cnt)

        log.debug('Set %s to %s', chan, data)

        cmd = 19 if self._wait else 4

        msg = CAmessage(cmd=cmd,
                        size=len(data),
                        dtype=dbr,
                        count=cnt,
                        p1=chan.sid,
                        p2=self.ioid,
                        body=data).pack()
        chan._circ.send(msg)

        if not self._wait:
            log.debug('Send put request (no wait) %s', self._chan.name)
            # do completion here
            self.ioid = None
            self.done = True
            self._comp.callback(ECA_NORMAL)

        else:
            log.debug('Send put request (wait) %s', self._chan.name)
            d = self.__D = self._chan.whenDis
            d.addCallback(self._circuitLost)

        return chan
Пример #14
0
    def setUp(self):
        client = StubClient()

        user = padString('hello')
        host = padString('world')

        self.program = \
            [('send',CAmessage(dtype=0, count=13)),
             ('recv',CAmessage(dtype=0, count=CA_VERSION)),
             ('recv',CAmessage(cmd=20, size=len(user), body=user)),
             ('recv',CAmessage(cmd=21, size=len(host), body=host)),
            ]

        sfact = CAExpectFactory()
        sfact.tst = self
        sfact.program = self.program + self.program

        self.serv = reactor.listenTCP(0, sfact, interface='127.0.0.1')
        self.target = ('127.0.0.1', self.serv.getHost().port)

        self.cfact = CACircuitFactory(client)
Пример #15
0
    def __init__(self, name, id, manager):
        self.name, self.id, self.manager = name, id, manager

        self.d = Deferred()

        self.wait = 0.04
        self.T = None
        self.Skip = set()

        nbody = padString(name)
        self.udp = CAmessage(cmd=0, count=CA_VERSION).pack()
        self.tcp = CAmessage(cmd=6,
                             size=len(nbody),
                             dtype=5,
                             count=CA_VERSION,
                             p1=id,
                             p2=id,
                             body=nbody).pack()

        self.udp = self.udp + self.tcp

        self.lookup()
Пример #16
0
    def test_normal(self):
        data = [
            ('\x00' * 16, CAmessage()),
            ('\x00\x01\x00\x00\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07',
             CAmessage(cmd=1, size=0, dtype=2, count=3, p1=0x40005,
                       p2=0x60007)),
            ('\x00\x01\x00\x08\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07'
             + 'hello w\x00',
             CAmessage(cmd=1,
                       size=8,
                       dtype=2,
                       count=3,
                       p1=0x40005,
                       p2=0x60007,
                       body='hello w\x00')),
        ]

        for raw, cook in data:
            raw2 = cook.pack()
            self.assertEqual(raw, raw2)
            cook2, rem = CAmessage.unpack(raw)
            self.assertEqual(cook, cook2)
            self.assertEqual(len(rem), 0)
Пример #17
0
    def dropchan(self, channel):
        """Remove a channel from this circuit
        """
        assert channel in self.channels

        self.channels.pop(channel.cid)

        msg = CAmessage(cmd=12, p1=channel.sid, p2=channel.cid).pack()
        self.transport.write(msg)

        if len(self.channels) != 0:
            return

        self.loseConnection()
Пример #18
0
    def test_handshakeV12(self):
        """Handshake with a v11 server.
        
        Server sends version on connection
        to facilitate name server on TCP
        """

        user = padString('hello')
        host = padString('world')

        self.sfact.program= \
            [('send',CAmessage(dtype=0, count=12)),
             ('recv',CAmessage(dtype=0, count=CA_VERSION)),
             ('recv',CAmessage(cmd=20, size=len(user), body=user)),
             ('recv',CAmessage(cmd=21, size=len(host), body=host)),
            ]
        # since client gets notification before program
        # completes have server do shutdown
        self.sfact.halt = True

        d = self.cfact.requestCircuit(self.target)

        @d.addCallback
        def postCondition(circ):
            self.assertTrue(circ is not None)
            # we get notification when the first packet is processed
            # the next three may have been received
            self.assertTrue(len(self.sfact.program) <= 3)
            self.assertEqual(circ.version, 12)

            return circ.transport.connector.whenDis

        @d.addCallback
        def done(circ):
            self.assertEqual(self.sfact.program, [])

        return d
Пример #19
0
    def addchan(self, channel):
        """Add a channel to this circuit
        """
        assert channel not in self.channels

        channel.cid = self.channels.add(channel)

        name = padString(channel.name)
        msg = CAmessage(cmd=18,
                        size=len(name),
                        p1=channel.cid,
                        p2=defs.CA_VERSION,
                        body=name).pack()

        self.transport.write(msg)
Пример #20
0
    def test_handshakeV11(self):
        """Handshake with a v11 server.
        
        Server sends version after authentication
        """

        user = padString('hello')
        host = padString('world')

        self.sfact.program= \
            [('recv',CAmessage(dtype=0, count=CA_VERSION)),
             ('recv',CAmessage(cmd=20, size=len(user), body=user)),
             ('recv',CAmessage(cmd=21, size=len(host), body=host)),
            ]

        d = self.cfact.requestCircuit(self.target)

        @d.addCallback
        def postCondition(circ):
            self.assertTrue(circ is not None)
            self.assertEqual(self.sfact.program, [])
            self.assertEqual(circ.version, 11)

        return d
Пример #21
0
    def connectionMade(self):
        self.peer=self.transport.getPeer()
        self.tcpport=self.transport.getHost().port

        # before 3.14.12 servers didn't send version until client authenticated
        # from 3.14.12 clients attempting to do TCP name resolution don't authenticate
        # but expect a version message immediately
        pkt=CAmessage(cmd=0, dtype=self.prio, count=defs.CA_VERSION)
        self.send(pkt.pack())
        log.debug('connection from %s',self.peer)
        log.debug('Create %s',self)

        self.server.circuits.add(self)

        self.__D=DeferredManager()
        self.__C.callback(self)
Пример #22
0
    def close(self, connected=False):
        """Called when the client closes the channel
        """
        log.debug('Destroy %s', self)
        self.pv.disconnect(self)

        if self.__D is not None:
            self.__D.addErrback(lambda e: e.trap(CancelledError))
            self.__D.cancel()
            self.__D = None

        if connected:
            pkt = CAmessage(cmd=27, p1=self.cid)
            self.circuit.send(pkt.pack())

        self.circuit.dropchan(self)
Пример #23
0
    def sendBeacon(self):

        for dest, srv, sock in self.becdests:
            # Note that broadcast beacons include to full address
            # and do not depend on the repeater to determine them.
            # TODO: Is this correct?  What about unicast.
            b = CAmessage(cmd=13, dtype=srv.port,
                          p1=self.beaconID,
                          p2=addr2int(srv.host)).pack()

            try:
                sock.write(b, (dest, self.cport))
            except socket.error, e:
                #TODO: Why is this raising EINVAL for some bcast?
                #print repr(b), (intr, self.cport)
                pass
Пример #24
0
    def nameres(self, pkt, endpoint, peer):
        name=str(pkt.body).strip('\0')
        log.info('%s is looking for %s',str(peer),name)
        ret = self.Lookup(name)
        if isinstance(ret, tuple) and pkt.count<11:
            # redirect not supported by older clients
            return

        elif ret and not isinstance(ret, tuple):
            ret = (0xffffffff, endpoint.tcpport)

        if ret:
            ack=CAmessage(cmd=6, size=8, dtype=ret[1],
                          p1=ret[0], p2=pkt.p2,
                          body=packSearchBody(CA_VERSION))

            endpoint.sendto(ack.pack(), peer)
Пример #25
0
    def _chanOk(self, chan):
        self.__D=None
        if chan is None:
            self.close()
            # channel shutdown
            return
        assert self._chan is chan
        
        ver=chan._circ.version

        self.subid=chan._circ.subscriptions.add(self)

        dbf=self.dbf
        if dbf is None:
            self.dbf,_=dbr_to_dbf(chan.dbr)
        dbr=dbf_to_dbr(self.dbf, self._meta)

        if self.dbf_conv is None:
            self.meta=caMeta(self.dbf)
        else:
            self.meta=caMeta(dbf_conv)

        # use dynamic array length whenever possible
        cnt=self.count if ver<13 else 0
        if cnt is None or cnt>chan.maxcount:
            cnt=chan.maxcount

        b=monitormask.pack(self.mask)

        msg=CAmessage(cmd=1, size=len(b),
                      dtype=dbr, count=cnt,
                      p1=chan.sid, p2=self.subid,
                      body=b).pack()
        chan._circ.send(msg)
        log.debug('Start monitor %s (%d)',chan.name,self.mask)

        d=self.__D=self._chan.whenDis
        d.addCallback(self._circuitLost)

        return chan
Пример #26
0
    def close(self):
        """Cancel request
        """
        if self.subid is None:
            return

        if self._chan.sid is not None:
            log.debug('Cancel %s (%d)',self._chan.name,self.mask)
            msg=CAmessage(cmd=2, p1=self._chan.sid,
                                p2=self.subid).pack()
            self._chan._circ.send(msg)

        if self.__D is not None and hasattr(self.__D, 'cancel'):
            self.__D.addErrback(lambda e:e.trap(CancelledError))
            self.__D.cancel()
            self.__D=None

        # any updates in the queue will be lost
        if self._chan._circ is not None:
            self._chan._circ.subscriptions.remove(self)
        self.subid=None

        self._updates(None, 0, ECA_DISCONN)
Пример #27
0
    def test_opencircuit(self):
        name = padString('testpv')
        user = padString('testuser')
        host = padString('testhost')

        namelookup = CAExpectDatagramProtocol(self, [], halt=False)

        udp = self.udp = reactor.listenUDP(0,
                                           namelookup,
                                           interface='127.0.0.1')

        sfact = self.sfact = CAExpectFactory()
        sfact.tst = self

        tcp = self.tcp = reactor.listenTCP(0, sfact, interface='127.0.0.1')

        udptarget = '127.0.0.1', udp.getHost().port
        tcptarget = '127.0.0.1', tcp.getHost().port

        # name search
        # respond after second request
        namelookup.program= \
            [('recv',CAmessage(dtype=0, count=CA_VERSION)),
             ('recv',CAmessage(cmd=6, size=len(name),
                               dtype=5, count=CA_VERSION,
                               p1=0, p2=0, body=name)),
             ('recv',CAmessage(dtype=0, count=CA_VERSION)),
             ('recv',CAmessage(cmd=6, size=len(name),
                               dtype=5, count=CA_VERSION,
                               p1=0, p2=0, body=name)),
             ('send',CAmessage(cmd=6, size=8, dtype=tcptarget[1],
                               p1=0xffffffff, p2=0,
                               body=searchbody.pack(11))),
            ]
        #namelookup.debug=True

        sfact.program= \
            [('send',CAmessage(dtype=0, count=12)),
             ('recv',CAmessage(dtype=0, count=CA_VERSION)),
             ('recv',CAmessage(cmd=20, size=len(user), body=user)),
             ('recv',CAmessage(cmd=21, size=len(host), body=host)),
             ('recv',CAmessage(cmd=100)),
            ]

        conf = Config(Config.empty)
        conf.addrs = [udptarget]

        cli = self.cli = CAClient(conf, user='******', host='testhost')

        namelookup.dest = '127.0.0.1', cli.resolv._udp.getHost().port

        d = cli.lookup('testpv')

        @d.addCallback
        @inlineCallbacks
        def findAndConnect(srv):
            self.assertEqual(srv, tcptarget)

            circ = yield cli.openCircuit(srv)

            self.assertTrue(circ is not None)

            self.assertEqual(circ.version, 12)

        return d
Пример #28
0
    def test_reconn(self):
        """connect, disconnect, and reconnect
        """

        self.chan = CAClientChannel('somepv', self.cli)
        #self.check=ref(self.chan) #TODO: find the reference leak!

        cb = Counter()

        def conCB(chan, status):
            if status:
                cb.c += 5
            else:
                cb.c -= 1

        self.chan.status.add(conCB)

        circ = MockCircuit()
        circ.transport.connector.doCon(circ)

        self.cli._L.callback(('localhost', 42))
        self.cli._O.callback(circ)

        # wait until channel calls circ.addchan(chan)
        chan = yield circ.reqAttach

        self.assertEqual(chan, self.chan)
        self.assertEqual(chan.state, chan.S_attach)

        self.assertTrue(chan._d is not None)

        # fall out of circ.addchan(chan)
        # before sending packets
        yield deferLater(reactor, 0, lambda: None)

        # send rights
        self.chan.dispatch(CAmessage(cmd=22, p2=3), circ)
        # send channel create ok
        self.chan.dispatch(CAmessage(cmd=18, dtype=6, count=10, p2=52), circ)

        self.assertTrue(chan._d is None)

        # verify decode
        self.assertEqual(chan.rights, 3)
        self.assertEqual(chan.sid, 52)
        self.assertEqual((chan.dbr, chan.maxcount), (6, 10))

        self.assertEqual(self.chan.state, self.chan.S_connect)

        # wait for channel to signal connected
        chan = yield self.chan.whenCon

        # check that status callback was fired
        self.assertEqual(cb.c, 5)

        # prepare to drop circuit
        circ2 = MockCircuit()
        circ2.transport.connector.doCon(circ2)

        self.cli._L = succeed(('localhost', 43))
        self.cli._O = succeed(circ2)

        circ.transport.connector.doLost(circ)
        circ.close()

        # wait for channel to signal disconnected
        chan = yield self.chan.whenDis

        self.assertEqual(chan.state, chan.S_init)
        # verify decode
        self.assertEqual(chan.rights, 0)
        self.assertEqual(chan.sid, None)
        self.assertEqual((chan.dbr, chan.maxcount), (None, 0))

        # check the status cb was notified of dcnt
        self.assertEqual(cb.c, 4)

        # wait until channel reattaches
        chan = yield circ2.reqAttach
        self.assertEqual(self.chan, chan)
        self.assertEqual(chan._circ, circ2)

        self.assertEqual(chan.state, chan.S_attach)

        self.assertTrue(chan._d is not None)

        # fall out of circ.addchan(chan)
        yield deferLater(reactor, 0, lambda: None)

        # send rights
        self.chan.dispatch(CAmessage(cmd=22, p2=2), circ2)
        # send channel create ok
        self.chan.dispatch(CAmessage(cmd=18, dtype=5, count=11, p2=53), circ2)

        # verify decode
        self.assertEqual(chan.rights, 2)
        self.assertEqual(chan.sid, 53)
        self.assertEqual((chan.dbr, chan.maxcount), (5, 11))

        self.assertEqual(self.chan.state, self.chan.S_connect)

        # wait for channel to signal connected
        chan = yield self.chan.whenCon

        # check that status callback was fired
        self.assertEqual(cb.c, 9)

        self.chan.close()
        circ2.close()

        returnValue(None)