diff options
-rw-r--r-- | dev-requirements.txt | 3 | ||||
-rw-r--r-- | fabfile.py | 28 | ||||
-rw-r--r-- | paramiko/__init__.py | 4 | ||||
-rw-r--r-- | paramiko/packet.py | 1 | ||||
-rw-r--r-- | paramiko/proxy.py | 33 | ||||
-rw-r--r-- | paramiko/transport.py | 17 | ||||
-rw-r--r-- | setup.py | 2 | ||||
-rw-r--r-- | sites/www/changelog.rst | 31 |
8 files changed, 99 insertions, 20 deletions
diff --git a/dev-requirements.txt b/dev-requirements.txt index 331e38c6..72172183 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,3 +1,6 @@ +# Older junk +tox>=1.4,<1.5 +epydoc>=3.0,<3.1 # For newer tasks like building Sphinx docs. # NOTE: Requires Python >=2.6 invoke>=0.7.0 @@ -1,8 +1,10 @@ -from fabric.api import task, sudo, env +from fabric.api import task, sudo, env, local, hosts from fabric.contrib.project import rsync_project +from fabric.contrib.console import confirm @task +@hosts("paramiko.org") def upload_docs(): target = "/var/www/paramiko.org" staging = "/tmp/paramiko_docs" @@ -11,3 +13,27 @@ def upload_docs(): sudo("rm -rf %s/*" % target) rsync_project(local_dir='docs/', remote_dir=staging, delete=True) sudo("cp -R %s/* %s/" % (staging, target)) + +@task +def build_docs(): + local("epydoc --no-private -o docs/ paramiko") + +@task +def clean(): + local("rm -rf build dist docs") + local("rm -f MANIFEST *.log demos/*.log") + local("rm -f paramiko/*.pyc") + local("rm -f test.log") + local("rm -rf paramiko.egg-info") + +@task +def test(): + local("python ./test.py") + +@task +def release(): + confirm("Only hit Enter if you remembered to update the version!") + confirm("Also, did you remember to tag your release?") + build_docs() + local("python setup.py sdist register upload") + upload_docs() diff --git a/paramiko/__init__.py b/paramiko/__init__.py index 7f60b1fa..eba9f608 100644 --- a/paramiko/__init__.py +++ b/paramiko/__init__.py @@ -46,6 +46,8 @@ Paramiko is written entirely in python (no C or platform-dependent code) and is released under the GNU Lesser General Public License (LGPL). Website: U{https://github.com/paramiko/paramiko/} + +Mailing list: U{paramiko@librelist.com<mailto:paramiko@librelist.com>} """ import sys @@ -55,7 +57,7 @@ if sys.version_info < (2, 5): __author__ = "Jeff Forcier <jeff@bitprophet.org>" -__version__ = "1.10.6" +__version__ = "1.11.4" __version_info__ = tuple([ int(d) for d in __version__.split(".") ]) __license__ = "GNU Lesser General Public License (LGPL)" diff --git a/paramiko/packet.py b/paramiko/packet.py index dce4383a..af807956 100644 --- a/paramiko/packet.py +++ b/paramiko/packet.py @@ -152,7 +152,6 @@ class Packetizer (object): def close(self): self.__closed = True - self.__socket.close() def set_hexdump(self, hexdump): self.__dump_packets = hexdump diff --git a/paramiko/proxy.py b/paramiko/proxy.py index 218b76e2..3f5c8bb2 100644 --- a/paramiko/proxy.py +++ b/paramiko/proxy.py @@ -20,10 +20,13 @@ L{ProxyCommand}. """ +from datetime import datetime import os from shlex import split as shlsplit import signal from subprocess import Popen, PIPE +from select import select +import socket from paramiko.ssh_exception import ProxyCommandFailure @@ -48,6 +51,8 @@ class ProxyCommand(object): """ self.cmd = shlsplit(command_line) self.process = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) + self.timeout = None + self.buffer = [] def send(self, content): """ @@ -64,7 +69,7 @@ class ProxyCommand(object): # died and we can't proceed. The best option here is to # raise an exception informing the user that the informed # ProxyCommand is not working. - raise BadProxyCommand(' '.join(self.cmd), e.strerror) + raise ProxyCommandFailure(' '.join(self.cmd), e.strerror) return len(content) def recv(self, size): @@ -78,14 +83,30 @@ class ProxyCommand(object): @rtype: int """ try: - return os.read(self.process.stdout.fileno(), size) + start = datetime.now() + while len(self.buffer) < size: + if self.timeout is not None: + elapsed = (datetime.now() - start).microseconds + timeout = self.timeout * 1000 * 1000 # to microseconds + if elapsed >= timeout: + raise socket.timeout() + r, w, x = select([self.process.stdout], [], [], 0.0) + if r and r[0] == self.process.stdout: + b = os.read(self.process.stdout.fileno(), 1) + # Store in class-level buffer for persistence across + # timeouts; this makes us act more like a real socket + # (where timeouts don't actually drop data.) + self.buffer.append(b) + result = ''.join(self.buffer) + self.buffer = [] + return result + except socket.timeout: + raise # socket.timeout is a subclass of IOError except IOError, e: - raise BadProxyCommand(' '.join(self.cmd), e.strerror) + raise ProxyCommandFailure(' '.join(self.cmd), e.strerror) def close(self): os.kill(self.process.pid, signal.SIGTERM) def settimeout(self, timeout): - # Timeouts are meaningless for this implementation, but are part of the - # spec, so must be present. - pass + self.timeout = timeout diff --git a/paramiko/transport.py b/paramiko/transport.py index 6c42cc27..990c9d3c 100644 --- a/paramiko/transport.py +++ b/paramiko/transport.py @@ -400,7 +400,6 @@ class Transport (threading.Thread): @since: 1.5.3 """ - self.sock.close() self.close() def get_security_options(self): @@ -614,11 +613,10 @@ class Transport (threading.Thread): """ if not self.active: return - self.active = False - self.packetizer.close() - self.join() + self.stop_thread() for chan in self._channels.values(): chan._unlink() + self.sock.close() def get_remote_server_key(self): """ @@ -1391,6 +1389,8 @@ class Transport (threading.Thread): def stop_thread(self): self.active = False self.packetizer.close() + while self.isAlive(): + self.join(10) ### internals... @@ -1539,10 +1539,6 @@ class Transport (threading.Thread): # containers. Random.atfork() - # Hold reference to 'sys' so we can test sys.modules to detect - # interpreter shutdown. - self.sys = sys - # active=True occurs before the thread is launched, to avoid a race _active_threads.append(self) if self.server_mode: @@ -1612,7 +1608,10 @@ class Transport (threading.Thread): self.saved_exception = e except socket.error, e: if type(e.args) is tuple: - emsg = '%s (%d)' % (e.args[1], e.args[0]) + if e.args: + emsg = '%s (%d)' % (e.args[1], e.args[0]) + else: # empty tuple, e.g. socket.timeout + emsg = str(e) or repr(e) else: emsg = e.args self._log(ERROR, 'Socket exception: ' + emsg) @@ -52,7 +52,7 @@ if sys.platform == 'darwin': setup(name = "paramiko", - version = "1.10.6", + version = "1.11.4", description = "SSH2 protocol library", author = "Jeff Forcier", author_email = "jeff@bitprophet.org", diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst index fa58c5f6..9d41cb22 100644 --- a/sites/www/changelog.rst +++ b/sites/www/changelog.rst @@ -6,18 +6,31 @@ Changelog protect against `timing-based attacks <http://codahale.com/a-lesson-in-timing-attacks/>`_. Thanks to Alex Gaynor for the patch. +* :release:`1.11.4 <2014-02-14>` * :release:`1.10.6 <2014-02-14>` +* :bug:`252` (`Fabric #1020 <https://github.com/fabric/fabric/issues/1020>`_) + Enhanced the implementation of ``ProxyCommand`` to avoid a deadlock/hang + condition that frequently occurs at ``Transport`` shutdown time. Thanks to + Mateusz Kobos, Matthijs van der Vleuten and Guillaume Zitta for the original + reports and to Marius Gedminas for helping test nontrivial use cases. +* :bug:`268` Fix some missed renames of ``ProxyCommand`` related error classes. + Thanks to Marius Gedminas for catch & patch. * :bug:`34` (PR :issue:`35`) Fix SFTP prefetching incompatibility with some SFTP servers regarding request/response ordering. Thanks to Richard - Kettlewell for catch & patch. + Kettlewell. * :bug:`193` (and its attentant PRs :issue:`230` & :issue:`253`) Fix SSH agent problems present on Windows. Thanks to David Hobbs for initial report and to Aarni Koskela & Olle Lundberg for the patches. +* :release:`1.11.3 <2014-01-08>` * :release:`1.10.5 <2014-01-08>` * :bug:`176` Fix AttributeError bugs in known_hosts file (re)loading. Thanks to Nathan Scowcroft for the patch & Martin Blumenstingl for the initial test case. +* :release:`1.11.2 <2013-09-27>` * :release:`1.10.4 <2013-09-27>` +* :bug:`156 (1.11+)` Fix potential deadlock condition when using Channel + objects as sockets (e.g. when using SSH gatewaying). Thanks to Steven Noonan + and Frank Arnold for catch & patch. * :bug:`179` Fix a missing variable causing errors when an ssh_config file has a non-default AddressFamily set. Thanks to Ed Marshall & Tomaz Muraus for catch & patch. @@ -25,6 +38,7 @@ Changelog Buchanan for catch & Dave Foster for patch. * :bug:`199` Typo fix in the license header cross-project. Thanks to Armin Ronacher for catch & patch. +* :release:`1.11.1 <2013-09-20>` * :release:`1.10.3 <2013-09-20>` * :bug:`162` Clean up HMAC module import to avoid deadlocks in certain uses of SSHClient. Thanks to Gernot Hillier for the catch & suggested fix. @@ -32,7 +46,22 @@ Changelog Thanks to Jonathan Halcrow for catch & patch. * :bug:`168` Update config handling to properly handle multiple 'localforward' and 'remoteforward' keys. Thanks to Emre Yılmaz for the patch. +* :release:`1.11.0 <2013-07-26>` * :release:`1.10.2 <2013-07-26>` +* :bug:`98 major` On Windows, when interacting with the PuTTY PAgeant, Paramiko + now creates the shared memory map with explicit Security Attributes of the + user, which is the same technique employed by the canonical PuTTY library to + avoid permissions issues when Paramiko is running under a different UAC + context than the PuTTY Ageant process. Thanks to Jason R. Coombs for the + patch. +* :support:`100` Remove use of PyWin32 in ``win_pageant`` module. Module was + already dependent on ctypes for constructing appropriate structures and had + ctypes implementations of all functionality. Thanks to Jason R. Coombs for + the patch. +* :bug:`87 major` Ensure updates to ``known_hosts`` files account for any + updates to said files after Paramiko initially read them. (Includes related + fix to guard against duplicate entries during subsequent ``known_hosts`` + loads.) Thanks to ``@sunweaver`` for the contribution. * :bug:`153` (also :issue:`67`) Warn on parse failure when reading known_hosts file. Thanks to ``@glasserc`` for patch. * :bug:`146` Indentation fixes for readability. Thanks to Abhinav Upadhyay for |