jbardin / scp.py

scp module for paramiko
Other
535 stars 137 forks source link

Fixed a hang in scp._recv_file that happens when underlying paramiko … #142

Closed re995 closed 4 years ago

re995 commented 4 years ago

When _recv_file is called and underlying paramiko channel is closed (for example because of ssh server crash), the transfer hangs forever because paramiko's chan.recv function returns empty string when the channel is closed (this behaviour is also documented in paramiko Channel.recv function docs:

def recv(self, nbytes):
    """
    Receive data from the channel.  The return value is a string
    representing the data received.  The maximum amount of data to be
    received at once is specified by ``nbytes``.  If a string of
    length zero is returned, the channel stream has closed.
    :param int nbytes: maximum number of bytes to read.
    :return: received data, as a ``str``/``bytes``.
    :raises socket.timeout:
        if no data is ready before the timeout set by `settimeout`.
    """
    try:
        out = self.in_buffer.read(nbytes, self.timeout)
    except PipeTimeout:
        raise socket.timeout()

Note: if a string of length zero is returned, the channel stream has closed

scp._recv_file has the following while loop:

        while pos < size:
            # we have to make sure we don't read the final byte
            if size - pos <= buff_size:
                buff_size = size - pos
            file_hdl.write(chan.recv(buff_size))
            pos = file_hdl.tell()
            if self._progress:
                self._progress(path, size, pos, self.peername)

Which gets an empty string from chan.recv(buff_size) call, writes nothing to file_hdl and therefore pos is never updated.