예제 #1
0
 def setUp(self):
     self.clock = Clock()
     self.temp_dir = FilePath(tempfile.mkdtemp()).asBytesMode()
     self.target = self.temp_dir.child(b'foo')
     with self.target.open('wb') as temp_fd:
         temp_fd.write(self.test_data)
     self.reader = DelayedReader(self.target, _clock=self.clock, delay=2)
     self.transport = FakeTransport(hostAddress=('127.0.0.1', self.port))
     self.rs = ReadSession(self.reader, _clock=self.clock)
     self.rs.transport = self.transport
     self.rs.startProtocol()
예제 #2
0
 def setUp(self):
     self.clock = Clock()
     self.tmp_dir_path = tempfile.mkdtemp()
     self.target = FilePath(self.tmp_dir_path).child('foo')
     with self.target.open('wb') as temp_fd:
         temp_fd.write(self.test_data)
     self.reader = DelayedReader(self.target, _clock=self.clock, delay=2)
     self.transport = FakeTransport(hostAddress=('127.0.0.1', self.port))
     self.rs = ReadSession(self.reader, _clock=self.clock)
     self.rs.transport = self.transport
     self.rs.startProtocol()
예제 #3
0
class RemoteOriginReadSession(TFTPBootstrap):
    """Bootstraps a L{ReadSession}, that was started remotely, - we've received
    a RRQ.

    """

    timeout = (1, 3, 7)

    def __init__(self, remote, reader, options=None, _clock=None):
        TFTPBootstrap.__init__(self, remote, reader, options, _clock)
        self.session = ReadSession(reader, self._clock)

    def startProtocol(self):
        """Start sending an OACK datagram if we were initialized with options
        or start the L{ReadSession} immediately.

        """
        self.transport.connect(*self.remote)
        if self.options:
            self.resultant_options = self.processOptions(self.options)
            bytes = OACKDatagram(self.resultant_options).to_wire()
            self.timeout_watchdog = SequentialCall.run(
                self.timeout[:-1],
                callable=self.transport.write,
                callable_args=[bytes],
                on_timeout=lambda: self._clock.callLater(self.timeout[-1], self.timedOut),
                run_now=True,
                _clock=self._clock,
            )
        else:
            self.session.transport = self.transport
            self.session.startProtocol()
            return self.session.nextBlock()

    def _datagramReceived(self, datagram):
        if datagram.opcode == OP_ACK and datagram.blocknum == 0:
            return self.tftp_ACK(datagram)
        elif self.session.started:
            return self.session.datagramReceived(datagram)

    def tftp_ACK(self, datagram):
        """Handle incoming ACK datagram. Hand over control to the underlying
        L{ReadSession}.

        @param datagram: ACK datagram
        @type datagram: L{ACKDatagram}

        """
        if self.timeout_watchdog is not None:
            self.timeout_watchdog.cancel()
        if not self.session.started:
            self.applyOptions(self.session, self.resultant_options)
            self.session.transport = self.transport
            self.session.startProtocol()
            return self.session.nextBlock()
예제 #4
0
class LocalOriginReadSession(TFTPBootstrap):
    """Bootstraps a L{ReadSession}, that was originated locally, - we've requested
    a write to a remote server.

    """
    def __init__(self, remote, reader, options=None, _clock=None):
        TFTPBootstrap.__init__(self, remote, reader, options, _clock)
        self.session = ReadSession(reader, self._clock)

    def startProtocol(self):
        """Connect the transport and start the L{timeout_watchdog}"""
        self.transport.connect(*self.remote)
        if self.timeout_watchdog is not None:
            self.timeout_watchdog.start()

    def _datagramReceived(self, datagram):
        if datagram.opcode == OP_OACK:
            return self.tftp_OACK(datagram)
        elif (datagram.opcode == OP_ACK and datagram.blocknum == 0
              and not self.session.started):
            self.session.transport = self.transport
            self.session.startProtocol()
            if self.timeout_watchdog is not None and self.timeout_watchdog.active(
            ):
                self.timeout_watchdog.cancel()
            return self.session.nextBlock()
        elif self.session.started:
            return self.session.datagramReceived(datagram)

    def tftp_OACK(self, datagram):
        """Handle incoming OACK datagram, process and apply options and hand over
        control to the underlying L{ReadSession}.

        @param datagram: OACK datagram
        @type datagram: L{OACKDatagram}

        """
        if not self.session.started:
            self.resultant_options = self.processOptions(datagram.options)
            if self.timeout_watchdog is not None and self.timeout_watchdog.active(
            ):
                self.timeout_watchdog.cancel()
            self.applyOptions(self.session, self.resultant_options)
            self.session.transport = self.transport
            self.session.startProtocol()
            return self.session.nextBlock()
        else:
            log.msg("Duplicate OACK received, ignored")
예제 #5
0
class LocalOriginReadSession(TFTPBootstrap):
    """Bootstraps a L{ReadSession}, that was originated locally, - we've requested
    a write to a remote server.

    """
    def __init__(self, remote, reader, options=None, _clock=None):
        TFTPBootstrap.__init__(self, remote, reader, options, _clock)
        self.session = ReadSession(reader, self._clock)

    def startProtocol(self):
        """Connect the transport and start the L{timeout_watchdog}"""
        self.transport.connect(*self.remote)
        if self.timeout_watchdog is not None:
            self.timeout_watchdog.start()

    def _datagramReceived(self, datagram):
        if datagram.opcode == OP_OACK:
            return self.tftp_OACK(datagram)
        elif (datagram.opcode == OP_ACK and datagram.blocknum == 0
                    and not self.session.started):
            self.session.transport = self.transport
            self.session.startProtocol()
            if self.timeout_watchdog is not None and self.timeout_watchdog.active():
                self.timeout_watchdog.cancel()
            return self.session.nextBlock()
        elif self.session.started:
            return self.session.datagramReceived(datagram)

    def tftp_OACK(self, datagram):
        """Handle incoming OACK datagram, process and apply options and hand over
        control to the underlying L{ReadSession}.

        @param datagram: OACK datagram
        @type datagram: L{OACKDatagram}

        """
        if not self.session.started:
            self.resultant_options = self.processOptions(datagram.options)
            if self.timeout_watchdog is not None and self.timeout_watchdog.active():
                self.timeout_watchdog.cancel()
            self.applyOptions(self.session, self.resultant_options)
            self.session.transport = self.transport
            self.session.startProtocol()
            return self.session.nextBlock()
        else:
            log.msg("Duplicate OACK received, ignored")
예제 #6
0
class ReadSessions(unittest.TestCase):
    test_data = b"""line1
line2
anotherline"""
    port = 65466

    def setUp(self):
        self.clock = Clock()
        self.temp_dir = FilePath(tempfile.mkdtemp()).asBytesMode()
        self.target = self.temp_dir.child(b'foo')
        with self.target.open('wb') as temp_fd:
            temp_fd.write(self.test_data)
        self.reader = DelayedReader(self.target, _clock=self.clock, delay=2)
        self.transport = FakeTransport(hostAddress=('127.0.0.1', self.port))
        self.rs = ReadSession(self.reader, _clock=self.clock)
        self.rs.transport = self.transport
        self.rs.startProtocol()

    @inlineCallbacks
    def test_ERROR(self):
        err_dgram = ERRORDatagram.from_code(ERR_NOT_DEFINED, b'no reason')
        yield self.rs.datagramReceived(err_dgram)
        self.assertFalse(self.transport.value())
        self.assertTrue(self.transport.disconnecting)

    @inlineCallbacks
    def test_ACK_invalid_blocknum(self):
        ack_datagram = ACKDatagram(3)
        yield self.rs.datagramReceived(ack_datagram)
        self.assertFalse(self.transport.disconnecting)
        err_dgram = TFTPDatagramFactory(*split_opcode(self.transport.value()))
        self.assertTrue(isinstance(err_dgram, ERRORDatagram))
        self.addCleanup(self.rs.cancel)

    @inlineCallbacks
    def test_ACK_stale_blocknum(self):
        self.rs.blocknum = 2
        ack_datagram = ACKDatagram(1)
        yield self.rs.datagramReceived(ack_datagram)
        self.assertFalse(self.transport.disconnecting)
        self.assertFalse(
            self.transport.value(),
            "Stale ACK datagram, we should not write anything back")
        self.addCleanup(self.rs.cancel)

    def test_ACK(self):
        self.rs.block_size = 5
        self.rs.blocknum = 1
        ack_datagram = ACKDatagram(1)
        d = self.rs.datagramReceived(ack_datagram)

        def cb(ign):
            self.clock.advance(0.1)
            self.assertFalse(self.transport.disconnecting)
            data_datagram = TFTPDatagramFactory(
                *split_opcode(self.transport.value()))
            self.assertEqual(data_datagram.data, b'line1')
            self.assertFalse(
                self.rs.completed,
                "Got enough bytes from the reader, there is no reason to stop")

        d.addCallback(cb)
        self.clock.advance(2.5)
        self.addCleanup(self.rs.cancel)
        return d

    def test_ACK_finished(self):
        self.rs.block_size = 512
        self.rs.blocknum = 1

        # Send a terminating datagram
        ack_datagram = ACKDatagram(1)
        d = self.rs.datagramReceived(ack_datagram)

        def cb(ign):
            self.clock.advance(0.1)
            ack_datagram = ACKDatagram(2)
            # This datagram doesn't trigger any sends
            self.rs.datagramReceived(ack_datagram)

            self.assertEqual(self.transport.value(),
                             DATADatagram(2, self.test_data).to_wire())
            self.assertTrue(
                self.rs.completed,
                "Data length is less, than blocksize, time to stop")

        self.addCleanup(self.rs.cancel)
        d.addCallback(cb)
        self.clock.advance(3)
        return d

    def test_ACK_backoff(self):
        self.rs.block_size = 5
        self.rs.blocknum = 1

        ack_datagram = ACKDatagram(1)
        d = self.rs.datagramReceived(ack_datagram)

        def cb(ign):

            self.clock.pump((1, ) * 4)
            # Sent two times - initial send and a retransmit after first timeout
            self.assertEqual(self.transport.value(),
                             DATADatagram(2, self.test_data[:5]).to_wire() * 2)

            # Sent three times - initial send and two retransmits
            self.clock.pump((1, ) * 5)
            self.assertEqual(self.transport.value(),
                             DATADatagram(2, self.test_data[:5]).to_wire() * 3)

            # Sent still three times - initial send, two retransmits and the last wait
            self.clock.pump((1, ) * 10)
            self.assertEqual(self.transport.value(),
                             DATADatagram(2, self.test_data[:5]).to_wire() * 3)

            self.assertTrue(self.transport.disconnecting)

        d.addCallback(cb)
        self.clock.advance(2.5)
        return d

    @inlineCallbacks
    def test_failed_read(self):
        self.reader.finish()
        self.rs.reader = FailingReader()
        self.rs.blocknum = 1
        ack_datagram = ACKDatagram(1)
        yield self.rs.datagramReceived(ack_datagram)
        self.flushLoggedErrors()
        self.clock.advance(0.1)
        err_datagram = TFTPDatagramFactory(
            *split_opcode(self.transport.value()))
        self.assertTrue(isinstance(err_datagram, ERRORDatagram))
        self.assertTrue(self.transport.disconnecting)

    def test_rollover(self):
        self.rs.block_size = len(self.test_data)
        self.rs.blocknum = 65536
        self.rs.dataFromReader(self.test_data)
        self.assertEqual(self.rs.blocknum, 0)
        self.addCleanup(self.rs.cancel)

    def tearDown(self):
        self.temp_dir.remove()
예제 #7
0
 def __init__(self, remote, reader, options=None, _clock=None):
     TFTPBootstrap.__init__(self, remote, reader, options, _clock)
     self.session = ReadSession(reader, self._clock)
예제 #8
0
class RemoteOriginReadSession(TFTPBootstrap):
    """Bootstraps a L{ReadSession}, that was started remotely, - we've received
    a RRQ.

    """
    timeout = (1, 3, 7)

    def __init__(self, remote, reader, options=None, _clock=None):
        TFTPBootstrap.__init__(self, remote, reader, options, _clock)
        self.session = ReadSession(reader, self._clock)

    def option_tsize(self, val):
        """Process tsize option.

        If tsize is zero, get the size of the file to be read so that it can
        be returned in the OACK datagram.

        @see: L{TFTPBootstrap.option_tsize}

        """
        val = TFTPBootstrap.option_tsize(self, val)
        if val == b"0":
            val = self.session.reader.size
            if val is not None:
                val = intToBytes(val)
        return val

    def startProtocol(self):
        """Start sending an OACK datagram if we were initialized with options
        or start the L{ReadSession} immediately.

        """
        self.transport.connect(*self.remote)
        if self.options:
            self.resultant_options = self.processOptions(self.options)
            bytes = OACKDatagram(self.resultant_options).to_wire()
            self.timeout_watchdog = SequentialCall.run(
                self.timeout[:-1],
                callable=self.transport.write,
                callable_args=[
                    bytes,
                ],
                on_timeout=lambda: self._clock.callLater(
                    self.timeout[-1], self.timedOut),
                run_now=True,
                _clock=self._clock)
        else:
            self.session.transport = self.transport
            self.session.startProtocol()
            return self.session.nextBlock()

    def _datagramReceived(self, datagram):
        if datagram.opcode == OP_ACK and datagram.blocknum == 0 and self.session.started is False:
            return self.tftp_ACK(datagram)
        elif self.session.started:
            return self.session.datagramReceived(datagram)

    def tftp_ACK(self, datagram):
        """Handle incoming ACK datagram. Hand over control to the underlying
        L{ReadSession}.

        @param datagram: ACK datagram
        @type datagram: L{ACKDatagram}

        """
        if self.timeout_watchdog is not None:
            self.timeout_watchdog.cancel()
        if not self.session.started:
            self.applyOptions(self.session, self.resultant_options)
            self.session.transport = self.transport
            self.session.startProtocol()
            return self.session.nextBlock()
예제 #9
0
 def __init__(self, remote, reader, options=None, _clock=None):
     TFTPBootstrap.__init__(self, remote, reader, options, _clock)
     self.session = ReadSession(reader, self._clock)
예제 #10
0
class ReadSessions(unittest.TestCase):
    test_data = """line1
line2
anotherline"""
    port = 65466

    def setUp(self):
        self.clock = Clock()
        self.tmp_dir_path = tempfile.mkdtemp()
        self.target = FilePath(self.tmp_dir_path).child('foo')
        with self.target.open('wb') as temp_fd:
            temp_fd.write(self.test_data)
        self.reader = DelayedReader(self.target, _clock=self.clock, delay=2)
        self.transport = FakeTransport(hostAddress=('127.0.0.1', self.port))
        self.rs = ReadSession(self.reader, _clock=self.clock)
        self.rs.transport = self.transport
        self.rs.startProtocol()

    @inlineCallbacks
    def test_ERROR(self):
        err_dgram = ERRORDatagram.from_code(ERR_NOT_DEFINED, 'no reason')
        yield self.rs.datagramReceived(err_dgram)
        self.failIf(self.transport.value())
        self.failUnless(self.transport.disconnecting)

    @inlineCallbacks
    def test_ACK_invalid_blocknum(self):
        ack_datagram = ACKDatagram(3)
        yield self.rs.datagramReceived(ack_datagram)
        self.failIf(self.transport.disconnecting)
        err_dgram = TFTPDatagramFactory(*split_opcode(self.transport.value()))
        self.assert_(isinstance(err_dgram, ERRORDatagram))
        self.addCleanup(self.rs.cancel)

    @inlineCallbacks
    def test_ACK_stale_blocknum(self):
        self.rs.blocknum = 2
        ack_datagram = ACKDatagram(1)
        yield self.rs.datagramReceived(ack_datagram)
        self.failIf(self.transport.disconnecting)
        self.failIf(self.transport.value(),
                    "Stale ACK datagram, we should not write anything back")
        self.addCleanup(self.rs.cancel)

    def test_ACK(self):
        self.rs.block_size = 5
        self.rs.blocknum = 1
        ack_datagram = ACKDatagram(1)
        d = self.rs.datagramReceived(ack_datagram)
        def cb(ign):
            self.clock.advance(0.1)
            self.failIf(self.transport.disconnecting)
            data_datagram = TFTPDatagramFactory(*split_opcode(self.transport.value()))
            self.assertEqual(data_datagram.data, 'line1')
            self.failIf(self.rs.completed,
                        "Got enough bytes from the reader, there is no reason to stop")
        d.addCallback(cb)
        self.clock.advance(2.5)
        self.addCleanup(self.rs.cancel)
        return d

    def test_ACK_finished(self):
        self.rs.block_size = 512
        self.rs.blocknum = 1

        # Send a terminating datagram
        ack_datagram = ACKDatagram(1)
        d = self.rs.datagramReceived(ack_datagram)
        def cb(ign):
            self.clock.advance(0.1)
            ack_datagram = ACKDatagram(2)
            # This datagram doesn't trigger any sends
            self.rs.datagramReceived(ack_datagram)

            self.assertEqual(self.transport.value(), DATADatagram(2, self.test_data).to_wire())
            self.failUnless(self.rs.completed,
                        "Data length is less, than blocksize, time to stop")
        self.addCleanup(self.rs.cancel)
        d.addCallback(cb)
        self.clock.advance(3)
        return d

    def test_ACK_backoff(self):
        self.rs.block_size = 5
        self.rs.blocknum = 1

        ack_datagram = ACKDatagram(1)
        d = self.rs.datagramReceived(ack_datagram)
        def cb(ign):

            self.clock.pump((1,)*4)
            # Sent two times - initial send and a retransmit after first timeout
            self.assertEqual(self.transport.value(),
                             DATADatagram(2, self.test_data[:5]).to_wire()*2)

            # Sent three times - initial send and two retransmits
            self.clock.pump((1,)*5)
            self.assertEqual(self.transport.value(),
                             DATADatagram(2, self.test_data[:5]).to_wire()*3)

            # Sent still three times - initial send, two retransmits and the last wait
            self.clock.pump((1,)*10)
            self.assertEqual(self.transport.value(),
                             DATADatagram(2, self.test_data[:5]).to_wire()*3)

            self.failUnless(self.transport.disconnecting)
        d.addCallback(cb)
        self.clock.advance(2.5)
        return d

    @inlineCallbacks
    def test_failed_read(self):
        self.reader.finish()
        self.rs.reader = FailingReader()
        self.rs.blocknum = 1
        ack_datagram = ACKDatagram(1)
        yield self.rs.datagramReceived(ack_datagram)
        self.flushLoggedErrors()
        self.clock.advance(0.1)
        err_datagram = TFTPDatagramFactory(*split_opcode(self.transport.value()))
        self.failUnless(isinstance(err_datagram, ERRORDatagram))
        self.failUnless(self.transport.disconnecting)

    def tearDown(self):
        shutil.rmtree(self.tmp_dir_path)
예제 #11
0
class RemoteOriginReadSession(TFTPBootstrap):
    """Bootstraps a L{ReadSession}, that was started remotely, - we've received
    a RRQ.

    """
    timeout = (1, 3, 7)

    def __init__(self, remote, reader, options=None, _clock=None):
        TFTPBootstrap.__init__(self, remote, reader, options, _clock)
        self.session = ReadSession(reader, self._clock)

    def option_tsize(self, val):
        """Process tsize option.

        If tsize is zero, get the size of the file to be read so that it can
        be returned in the OACK datagram.

        @see: L{TFTPBootstrap.option_tsize}

        """
        val = TFTPBootstrap.option_tsize(self, val)
        if val == b"0":
            val = self.session.reader.size
            if val is not None:
                val = intToBytes(val)
        return val

    def startProtocol(self):
        """Start sending an OACK datagram if we were initialized with options
        or start the L{ReadSession} immediately.

        """
        self.transport.connect(*self.remote)
        if self.options:
            self.resultant_options = self.processOptions(self.options)
            bytes = OACKDatagram(self.resultant_options).to_wire()
            self.timeout_watchdog = timedCaller(
                chain((0,), self.timeout), partial(self.transport.write, bytes),
                self.timedOut, clock=self._clock)
        else:
            self.session.transport = self.transport
            self.session.startProtocol()
            return self.session.nextBlock()

    def _datagramReceived(self, datagram):
        if datagram.opcode == OP_ACK and datagram.blocknum == 0 and self.session.started is False:
            return self.tftp_ACK(datagram)
        elif self.session.started:
            return self.session.datagramReceived(datagram)

    def tftp_ACK(self, datagram):
        """Handle incoming ACK datagram. Hand over control to the underlying
        L{ReadSession}.

        @param datagram: ACK datagram
        @type datagram: L{ACKDatagram}

        """
        self.timeout_watchdog.cancel()
        if not self.session.started:
            self.applyOptions(self.session, self.resultant_options)
            self.session.transport = self.transport
            self.session.startProtocol()
            return self.session.nextBlock()