summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--.travis.yml17
-rw-r--r--MANIFEST.in2
-rw-r--r--README.rst5
-rwxr-xr-xdemos/demo.py85
-rwxr-xr-xdemos/demo_keygen.py130
-rw-r--r--demos/demo_server.py88
-rw-r--r--demos/demo_sftp.py76
-rw-r--r--demos/demo_simple.py50
-rw-r--r--demos/forward.py176
-rw-r--r--demos/interactive.py19
-rwxr-xr-xdemos/rforward.py147
-rw-r--r--dev-requirements.txt33
-rw-r--r--paramiko/__init__.py121
-rw-r--r--paramiko/_version.py4
-rw-r--r--paramiko/_winapi.py154
-rw-r--r--paramiko/agent.py57
-rw-r--r--paramiko/auth_handler.py335
-rw-r--r--paramiko/ber.py17
-rw-r--r--paramiko/buffered_pipe.py9
-rw-r--r--paramiko/channel.py166
-rw-r--r--paramiko/client.py177
-rw-r--r--paramiko/common.py165
-rw-r--r--paramiko/compress.py6
-rw-r--r--paramiko/config.py134
-rw-r--r--paramiko/dsskey.py89
-rw-r--r--paramiko/ecdsakey.py69
-rw-r--r--paramiko/ed25519key.py19
-rw-r--r--paramiko/file.py76
-rw-r--r--paramiko/hostkeys.py59
-rw-r--r--paramiko/kex_ecdh_nist.py22
-rw-r--r--paramiko/kex_gex.py77
-rw-r--r--paramiko/kex_group1.py31
-rw-r--r--paramiko/kex_group14.py6
-rw-r--r--paramiko/kex_gss.py216
-rw-r--r--paramiko/message.py24
-rw-r--r--paramiko/packet.py133
-rw-r--r--paramiko/pipe.py19
-rw-r--r--paramiko/pkey.py112
-rw-r--r--paramiko/primes.py31
-rw-r--r--paramiko/proxy.py21
-rw-r--r--paramiko/py3compat.py79
-rw-r--r--paramiko/rsakey.py42
-rw-r--r--paramiko/server.py54
-rw-r--r--paramiko/sftp.py148
-rw-r--r--paramiko/sftp_attr.py74
-rw-r--r--paramiko/sftp_client.py179
-rw-r--r--paramiko/sftp_file.py90
-rw-r--r--paramiko/sftp_handle.py11
-rw-r--r--paramiko/sftp_server.py161
-rw-r--r--paramiko/sftp_si.py9
-rw-r--r--paramiko/ssh_exception.py56
-rw-r--r--paramiko/ssh_gss.py116
-rw-r--r--paramiko/transport.py1065
-rw-r--r--paramiko/util.py38
-rw-r--r--paramiko/win_pageant.py34
-rw-r--r--setup.cfg11
-rw-r--r--setup.py55
-rw-r--r--setup_helper.py48
-rw-r--r--sites/docs/conf.py13
-rw-r--r--sites/shared_conf.py39
-rw-r--r--sites/www/changelog.rst23
-rw-r--r--sites/www/conf.py16
-rw-r--r--sites/www/index.rst8
-rw-r--r--sites/www/installing.rst4
-rw-r--r--tasks.py141
-rwxr-xr-xtest.py196
-rw-r--r--tests/__init__.py37
-rw-r--r--tests/conftest.py103
-rw-r--r--tests/loop.py14
-rw-r--r--tests/stub_sftp.py59
-rw-r--r--tests/test_auth.py125
-rw-r--r--tests/test_buffered_pipe.py28
-rw-r--r--tests/test_client.py362
-rw-r--r--[-rwxr-xr-x]tests/test_file.py141
-rw-r--r--tests/test_gssapi.py75
-rw-r--r--tests/test_hostkeys.py80
-rw-r--r--tests/test_kex.py319
-rw-r--r--tests/test_kex_gss.py51
-rw-r--r--tests/test_message.py40
-rw-r--r--tests/test_packetizer.py39
-rw-r--r--tests/test_pkey.py262
-rw-r--r--[-rwxr-xr-x]tests/test_sftp.py813
-rw-r--r--tests/test_sftp_big.py293
-rw-r--r--tests/test_ssh_exception.py21
-rw-r--r--tests/test_ssh_gss.py79
-rw-r--r--tests/test_transport.py331
-rw-r--r--tests/test_util.py384
-rw-r--r--tests/util.py29
89 files changed, 5444 insertions, 4130 deletions
diff --git a/.gitignore b/.gitignore
index 44b45974..4345d86c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,9 +3,9 @@ build/
dist/
.tox/
paramiko.egg-info/
-test.log
docs/
demos/*.log
!sites/docs
_build
.coverage
+.cache
diff --git a/.travis.yml b/.travis.yml
index 2819eb20..16d33b76 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,9 +4,7 @@ cache:
directories:
- $HOME/.cache/pip
python:
- - "2.6"
- "2.7"
- - "3.3"
- "3.4"
- "3.5"
- "3.6"
@@ -15,22 +13,25 @@ python:
matrix:
allow_failures:
- python: "3.7-dev"
- - python: "pypy-5.6.0"
install:
- # Ensure modern pip/etc on Python 3.3 workers (not sure WTF, but, eh)
+ # Ensure modern pip/etc to avoid some issues w/ older worker environs
- pip install pip==9.0.1 setuptools==36.6.0
# Self-install for setup.py-driven deps
- pip install -e .
# Dev (doc/test running) requirements
+ # TODO: use pipenv + whatever contexty-type stuff it has
- pip install codecov # For codecov specifically
- pip install -r dev-requirements.txt
script:
- # Main tests, w/ coverage!
- - inv test --coverage
+ # Fast syntax check failures for more rapid feedback to submitters
+ # (Travis-oriented metatask that version checks Python, installs, runs.)
+ - inv travis.blacken
+ # I have this in my git pre-push hook, but contributors probably don't
+ - flake8
+ # All (including slow) tests, w/ coverage!
+ - inv coverage
# Ensure documentation builds, both sites, maxxed nitpicking
- inv sites
- # flake8 is now possible!
- - flake8
notifications:
irc:
channels: "irc.freenode.org#paramiko"
diff --git a/MANIFEST.in b/MANIFEST.in
index c13eb177..62239689 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,4 @@
-include LICENSE test.py setup_helper.py
+include LICENSE setup_helper.py
recursive-include docs *
recursive-include tests *.py *.key *.pub
recursive-include demos *.py *.key user_rsa_key user_rsa_key.pub
diff --git a/README.rst b/README.rst
index 399dceb9..6e49bd68 100644
--- a/README.rst
+++ b/README.rst
@@ -22,7 +22,7 @@ What
----
"Paramiko" is a combination of the Esperanto words for "paranoid" and
-"friend". It's a module for Python 2.6+/3.3+ that implements the SSH2 protocol
+"friend". It's a module for Python 2.7/3.4+ that implements the SSH2 protocol
for secure (encrypted and authenticated) connections to remote machines. Unlike
SSL (aka TLS), SSH2 protocol does not require hierarchical certificates signed
by a powerful central authority. You may know SSH2 as the protocol that
@@ -132,6 +132,7 @@ doc/ folder.
There are also unit tests here::
- $ python ./test.py
+ $ pip install -r dev-requirements.txt
+ $ pytest
Which will verify that most of the core components are working correctly.
diff --git a/demos/demo.py b/demos/demo.py
index fff61784..c9b0a5f5 100755
--- a/demos/demo.py
+++ b/demos/demo.py
@@ -31,6 +31,7 @@ import traceback
from paramiko.py3compat import input
import paramiko
+
try:
import interactive
except ImportError:
@@ -42,71 +43,73 @@ def agent_auth(transport, username):
Attempt to authenticate to the given transport using any of the private
keys available from an SSH agent.
"""
-
+
agent = paramiko.Agent()
agent_keys = agent.get_keys()
if len(agent_keys) == 0:
return
-
+
for key in agent_keys:
- print('Trying ssh-agent key %s' % hexlify(key.get_fingerprint()))
+ print("Trying ssh-agent key %s" % hexlify(key.get_fingerprint()))
try:
transport.auth_publickey(username, key)
- print('... success!')
+ print("... success!")
return
except paramiko.SSHException:
- print('... nope.')
+ print("... nope.")
def manual_auth(username, hostname):
- default_auth = 'p'
- auth = input('Auth by (p)assword, (r)sa key, or (d)ss key? [%s] ' % default_auth)
+ default_auth = "p"
+ auth = input(
+ "Auth by (p)assword, (r)sa key, or (d)ss key? [%s] " % default_auth
+ )
if len(auth) == 0:
auth = default_auth
- if auth == 'r':
- default_path = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa')
- path = input('RSA key [%s]: ' % default_path)
+ if auth == "r":
+ default_path = os.path.join(os.environ["HOME"], ".ssh", "id_rsa")
+ path = input("RSA key [%s]: " % default_path)
if len(path) == 0:
path = default_path
try:
key = paramiko.RSAKey.from_private_key_file(path)
except paramiko.PasswordRequiredException:
- password = getpass.getpass('RSA key password: ')
+ password = getpass.getpass("RSA key password: ")
key = paramiko.RSAKey.from_private_key_file(path, password)
t.auth_publickey(username, key)
- elif auth == 'd':
- default_path = os.path.join(os.environ['HOME'], '.ssh', 'id_dsa')
- path = input('DSS key [%s]: ' % default_path)
+ elif auth == "d":
+ default_path = os.path.join(os.environ["HOME"], ".ssh", "id_dsa")
+ path = input("DSS key [%s]: " % default_path)
if len(path) == 0:
path = default_path
try:
key = paramiko.DSSKey.from_private_key_file(path)
except paramiko.PasswordRequiredException:
- password = getpass.getpass('DSS key password: ')
+ password = getpass.getpass("DSS key password: ")
key = paramiko.DSSKey.from_private_key_file(path, password)
t.auth_publickey(username, key)
else:
- pw = getpass.getpass('Password for %s@%s: ' % (username, hostname))
+ pw = getpass.getpass("Password for %s@%s: " % (username, hostname))
t.auth_password(username, pw)
# setup logging
-paramiko.util.log_to_file('demo.log')
+paramiko.util.log_to_file("demo.log")
-username = ''
+username = ""
if len(sys.argv) > 1:
hostname = sys.argv[1]
- if hostname.find('@') >= 0:
- username, hostname = hostname.split('@')
+ if hostname.find("@") >= 0:
+ username, hostname = hostname.split("@")
else:
- hostname = input('Hostname: ')
+ hostname = input("Hostname: ")
if len(hostname) == 0:
- print('*** Hostname required.')
+ print("*** Hostname required.")
sys.exit(1)
port = 22
-if hostname.find(':') >= 0:
- hostname, portstr = hostname.split(':')
+if hostname.find(":") >= 0:
+ hostname, portstr = hostname.split(":")
port = int(portstr)
# now connect
@@ -114,7 +117,7 @@ try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((hostname, port))
except Exception as e:
- print('*** Connect failed: ' + str(e))
+ print("*** Connect failed: " + str(e))
traceback.print_exc()
sys.exit(1)
@@ -123,34 +126,38 @@ try:
try:
t.start_client()
except paramiko.SSHException:
- print('*** SSH negotiation failed.')
+ print("*** SSH negotiation failed.")
sys.exit(1)
try:
- keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
+ keys = paramiko.util.load_host_keys(
+ os.path.expanduser("~/.ssh/known_hosts")
+ )
except IOError:
try:
- keys = paramiko.util.load_host_keys(os.path.expanduser('~/ssh/known_hosts'))
+ keys = paramiko.util.load_host_keys(
+ os.path.expanduser("~/ssh/known_hosts")
+ )
except IOError:
- print('*** Unable to open host keys file')
+ print("*** Unable to open host keys file")
keys = {}
# check server's host key -- this is important.
key = t.get_remote_server_key()
if hostname not in keys:
- print('*** WARNING: Unknown host key!')
+ print("*** WARNING: Unknown host key!")
elif key.get_name() not in keys[hostname]:
- print('*** WARNING: Unknown host key!')
+ print("*** WARNING: Unknown host key!")
elif keys[hostname][key.get_name()] != key:
- print('*** WARNING: Host key has changed!!!')
+ print("*** WARNING: Host key has changed!!!")
sys.exit(1)
else:
- print('*** Host key OK.')
+ print("*** Host key OK.")
# get username
- if username == '':
+ if username == "":
default_username = getpass.getuser()
- username = input('Username [%s]: ' % default_username)
+ username = input("Username [%s]: " % default_username)
if len(username) == 0:
username = default_username
@@ -158,25 +165,23 @@ try:
if not t.is_authenticated():
manual_auth(username, hostname)
if not t.is_authenticated():
- print('*** Authentication failed. :(')
+ print("*** Authentication failed. :(")
t.close()
sys.exit(1)
chan = t.open_session()
chan.get_pty()
chan.invoke_shell()
- print('*** Here we go!\n')
+ print("*** Here we go!\n")
interactive.interactive_shell(chan)
chan.close()
t.close()
except Exception as e:
- print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e))
+ print("*** Caught exception: " + str(e.__class__) + ": " + str(e))
traceback.print_exc()
try:
t.close()
except:
pass
sys.exit(1)
-
-
diff --git a/demos/demo_keygen.py b/demos/demo_keygen.py
index 860ee4e9..6a80272d 100755
--- a/demos/demo_keygen.py
+++ b/demos/demo_keygen.py
@@ -28,62 +28,97 @@ from paramiko import RSAKey
from paramiko.ssh_exception import SSHException
from paramiko.py3compat import u
-usage="""
+usage = """
%prog [-v] [-b bits] -t type [-N new_passphrase] [-f output_keyfile]"""
default_values = {
"ktype": "dsa",
"bits": 1024,
"filename": "output",
- "comment": ""
+ "comment": "",
}
-key_dispatch_table = {
- 'dsa': DSSKey,
- 'rsa': RSAKey,
-}
+key_dispatch_table = {"dsa": DSSKey, "rsa": RSAKey}
+
def progress(arg=None):
if not arg:
- sys.stdout.write('0%\x08\x08\x08 ')
+ sys.stdout.write("0%\x08\x08\x08 ")
sys.stdout.flush()
- elif arg[0] == 'p':
- sys.stdout.write('25%\x08\x08\x08\x08 ')
+ elif arg[0] == "p":
+ sys.stdout.write("25%\x08\x08\x08\x08 ")
sys.stdout.flush()
- elif arg[0] == 'h':
- sys.stdout.write('50%\x08\x08\x08\x08 ')
+ elif arg[0] == "h":
+ sys.stdout.write("50%\x08\x08\x08\x08 ")
sys.stdout.flush()
- elif arg[0] == 'x':
- sys.stdout.write('75%\x08\x08\x08\x08 ')
+ elif arg[0] == "x":
+ sys.stdout.write("75%\x08\x08\x08\x08 ")
sys.stdout.flush()
-if __name__ == '__main__':
- phrase=None
- pfunc=None
+if __name__ == "__main__":
+
+ phrase = None
+ pfunc = None
parser = OptionParser(usage=usage)
- parser.add_option("-t", "--type", type="string", dest="ktype",
+ parser.add_option(
+ "-t",
+ "--type",
+ type="string",
+ dest="ktype",
help="Specify type of key to create (dsa or rsa)",
- metavar="ktype", default=default_values["ktype"])
- parser.add_option("-b", "--bits", type="int", dest="bits",
- help="Number of bits in the key to create", metavar="bits",
- default=default_values["bits"])
- parser.add_option("-N", "--new-passphrase", dest="newphrase",
- help="Provide new passphrase", metavar="phrase")
- parser.add_option("-P", "--old-passphrase", dest="oldphrase",
- help="Provide old passphrase", metavar="phrase")
- parser.add_option("-f", "--filename", type="string", dest="filename",
- help="Filename of the key file", metavar="filename",
- default=default_values["filename"])
- parser.add_option("-q", "--quiet", default=False, action="store_false",
- help="Quiet")
- parser.add_option("-v", "--verbose", default=False, action="store_true",
- help="Verbose")
- parser.add_option("-C", "--comment", type="string", dest="comment",
- help="Provide a new comment", metavar="comment",
- default=default_values["comment"])
+ metavar="ktype",
+ default=default_values["ktype"],
+ )
+ parser.add_option(
+ "-b",
+ "--bits",
+ type="int",
+ dest="bits",
+ help="Number of bits in the key to create",
+ metavar="bits",
+ default=default_values["bits"],
+ )
+ parser.add_option(
+ "-N",
+ "--new-passphrase",
+ dest="newphrase",
+ help="Provide new passphrase",
+ metavar="phrase",
+ )
+ parser.add_option(
+ "-P",
+ "--old-passphrase",
+ dest="oldphrase",
+ help="Provide old passphrase",
+ metavar="phrase",
+ )
+ parser.add_option(
+ "-f",
+ "--filename",
+ type="string",
+ dest="filename",
+ help="Filename of the key file",
+ metavar="filename",
+ default=default_values["filename"],
+ )
+ parser.add_option(
+ "-q", "--quiet", default=False, action="store_false", help="Quiet"
+ )
+ parser.add_option(
+ "-v", "--verbose", default=False, action="store_true", help="Verbose"
+ )
+ parser.add_option(
+ "-C",
+ "--comment",
+ type="string",
+ dest="comment",
+ help="Provide a new comment",
+ metavar="comment",
+ default=default_values["comment"],
+ )
(options, args) = parser.parse_args()
@@ -95,18 +130,23 @@ if __name__ == '__main__':
globals()[o] = getattr(options, o, default_values[o.lower()])
if options.newphrase:
- phrase = getattr(options, 'newphrase')
+ phrase = getattr(options, "newphrase")
if options.verbose:
pfunc = progress
- sys.stdout.write("Generating priv/pub %s %d bits key pair (%s/%s.pub)..." % (ktype, bits, filename, filename))
+ sys.stdout.write(
+ "Generating priv/pub %s %d bits key pair (%s/%s.pub)..."
+ % (ktype, bits, filename, filename)
+ )
sys.stdout.flush()
- if ktype == 'dsa' and bits > 1024:
+ if ktype == "dsa" and bits > 1024:
raise SSHException("DSA Keys must be 1024 bits")
if ktype not in key_dispatch_table:
- raise SSHException("Unknown %s algorithm to generate keys pair" % ktype)
+ raise SSHException(
+ "Unknown %s algorithm to generate keys pair" % ktype
+ )
# generating private key
prv = key_dispatch_table[ktype].generate(bits=bits, progress_func=pfunc)
@@ -114,7 +154,7 @@ if __name__ == '__main__':
# generating public key
pub = key_dispatch_table[ktype](filename=filename, password=phrase)
- with open("%s.pub" % filename, 'w') as f:
+ with open("%s.pub" % filename, "w") as f:
f.write("%s %s" % (pub.get_name(), pub.get_base64()))
if options.comment:
f.write(" %s" % comment)
@@ -123,4 +163,12 @@ if __name__ == '__main__':
print("done.")
hash = u(hexlify(pub.get_fingerprint()))
- print("Fingerprint: %d %s %s.pub (%s)" % (bits, ":".join([ hash[i:2+i] for i in range(0, len(hash), 2)]), filename, ktype.upper()))
+ print(
+ "Fingerprint: %d %s %s.pub (%s)"
+ % (
+ bits,
+ ":".join([hash[i : 2 + i] for i in range(0, len(hash), 2)]),
+ filename,
+ ktype.upper(),
+ )
+ )
diff --git a/demos/demo_server.py b/demos/demo_server.py
index 3a7ec854..313e5fb2 100644
--- a/demos/demo_server.py
+++ b/demos/demo_server.py
@@ -31,45 +31,47 @@ from paramiko.py3compat import b, u, decodebytes
# setup logging
-paramiko.util.log_to_file('demo_server.log')
+paramiko.util.log_to_file("demo_server.log")
-host_key = paramiko.RSAKey(filename='test_rsa.key')
-#host_key = paramiko.DSSKey(filename='test_dss.key')
+host_key = paramiko.RSAKey(filename="test_rsa.key")
+# host_key = paramiko.DSSKey(filename='test_dss.key')
-print('Read key: ' + u(hexlify(host_key.get_fingerprint())))
+print("Read key: " + u(hexlify(host_key.get_fingerprint())))
-class Server (paramiko.ServerInterface):
+class Server(paramiko.ServerInterface):
# 'data' is the output of base64.b64encode(key)
# (using the "user_rsa_key" files)
- data = (b'AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hp'
- b'fAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMC'
- b'KDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iT'
- b'UWT10hcuO4Ks8=')
+ data = (
+ b"AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hp"
+ b"fAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMC"
+ b"KDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iT"
+ b"UWT10hcuO4Ks8="
+ )
good_pub_key = paramiko.RSAKey(data=decodebytes(data))
def __init__(self):
self.event = threading.Event()
def check_channel_request(self, kind, chanid):
- if kind == 'session':
+ if kind == "session":
return paramiko.OPEN_SUCCEEDED
return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
def check_auth_password(self, username, password):
- if (username == 'robey') and (password == 'foo'):
+ if (username == "robey") and (password == "foo"):
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
def check_auth_publickey(self, username, key):
- print('Auth attempt with key: ' + u(hexlify(key.get_fingerprint())))
- if (username == 'robey') and (key == self.good_pub_key):
+ print("Auth attempt with key: " + u(hexlify(key.get_fingerprint())))
+ if (username == "robey") and (key == self.good_pub_key):
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
-
- def check_auth_gssapi_with_mic(self, username,
- gss_authenticated=paramiko.AUTH_FAILED,
- cc_file=None):
+
+ def check_auth_gssapi_with_mic(
+ self, username, gss_authenticated=paramiko.AUTH_FAILED, cc_file=None
+ ):
"""
.. note::
We are just checking in `AuthHandler` that the given user is a
@@ -88,9 +90,9 @@ class Server (paramiko.ServerInterface):
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
- def check_auth_gssapi_keyex(self, username,
- gss_authenticated=paramiko.AUTH_FAILED,
- cc_file=None):
+ def check_auth_gssapi_keyex(
+ self, username, gss_authenticated=paramiko.AUTH_FAILED, cc_file=None
+ ):
if gss_authenticated == paramiko.AUTH_SUCCESSFUL:
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
@@ -99,14 +101,15 @@ class Server (paramiko.ServerInterface):
return True
def get_allowed_auths(self, username):
- return 'gssapi-keyex,gssapi-with-mic,password,publickey'
+ return "gssapi-keyex,gssapi-with-mic,password,publickey"
def check_channel_shell_request(self, channel):
self.event.set()
return True
- def check_channel_pty_request(self, channel, term, width, height, pixelwidth,
- pixelheight, modes):
+ def check_channel_pty_request(
+ self, channel, term, width, height, pixelwidth, pixelheight, modes
+ ):
return True
@@ -116,22 +119,22 @@ DoGSSAPIKeyExchange = True
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- sock.bind(('', 2200))
+ sock.bind(("", 2200))
except Exception as e:
- print('*** Bind failed: ' + str(e))
+ print("*** Bind failed: " + str(e))
traceback.print_exc()
sys.exit(1)
try:
sock.listen(100)
- print('Listening for connection ...')
+ print("Listening for connection ...")
client, addr = sock.accept()
except Exception as e:
- print('*** Listen/accept failed: ' + str(e))
+ print("*** Listen/accept failed: " + str(e))
traceback.print_exc()
sys.exit(1)
-print('Got a connection!')
+print("Got a connection!")
try:
t = paramiko.Transport(client, gss_kex=DoGSSAPIKeyExchange)
@@ -139,43 +142,44 @@ try:
try:
t.load_server_moduli()
except:
- print('(Failed to load moduli -- gex will be unsupported.)')
+ print("(Failed to load moduli -- gex will be unsupported.)")
raise
t.add_server_key(host_key)
server = Server()
try:
t.start_server(server=server)
except paramiko.SSHException:
- print('*** SSH negotiation failed.')
+ print("*** SSH negotiation failed.")
sys.exit(1)
# wait for auth
chan = t.accept(20)
if chan is None:
- print('*** No channel.')
+ print("*** No channel.")
sys.exit(1)
- print('Authenticated!')
+ print("Authenticated!")
server.event.wait(10)
if not server.event.is_set():
- print('*** Client never asked for a shell.')
+ print("*** Client never asked for a shell.")
sys.exit(1)
- chan.send('\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n')
- chan.send('We are on fire all the time! Hooray! Candy corn for everyone!\r\n')
- chan.send('Happy birthday to Robot Dave!\r\n\r\n')
- chan.send('Username: ')
- f = chan.makefile('rU')
- username = f.readline().strip('\r\n')
- chan.send('\r\nI don\'t like you, ' + username + '.\r\n')
+ chan.send("\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n")
+ chan.send(
+ "We are on fire all the time! Hooray! Candy corn for everyone!\r\n"
+ )
+ chan.send("Happy birthday to Robot Dave!\r\n\r\n")
+ chan.send("Username: ")
+ f = chan.makefile("rU")
+ username = f.readline().strip("\r\n")
+ chan.send("\r\nI don't like you, " + username + ".\r\n")
chan.close()
except Exception as e:
- print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e))
+ print("*** Caught exception: " + str(e.__class__) + ": " + str(e))
traceback.print_exc()
try:
t.close()
except:
pass
sys.exit(1)
-
diff --git a/demos/demo_sftp.py b/demos/demo_sftp.py
index 2cb44701..7f6a002e 100644
--- a/demos/demo_sftp.py
+++ b/demos/demo_sftp.py
@@ -32,38 +32,38 @@ from paramiko.py3compat import input
# setup logging
-paramiko.util.log_to_file('demo_sftp.log')
+paramiko.util.log_to_file("demo_sftp.log")
# Paramiko client configuration
-UseGSSAPI = True # enable GSS-API / SSPI authentication
+UseGSSAPI = True # enable GSS-API / SSPI authentication
DoGSSAPIKeyExchange = True
Port = 22
# get hostname
-username = ''
+username = ""
if len(sys.argv) > 1:
hostname = sys.argv[1]
- if hostname.find('@') >= 0:
- username, hostname = hostname.split('@')
+ if hostname.find("@") >= 0:
+ username, hostname = hostname.split("@")
else:
- hostname = input('Hostname: ')
+ hostname = input("Hostname: ")
if len(hostname) == 0:
- print('*** Hostname required.')
+ print("*** Hostname required.")
sys.exit(1)
-if hostname.find(':') >= 0:
- hostname, portstr = hostname.split(':')
+if hostname.find(":") >= 0:
+ hostname, portstr = hostname.split(":")
Port = int(portstr)
# get username
-if username == '':
+if username == "":
default_username = getpass.getuser()
- username = input('Username [%s]: ' % default_username)
+ username = input("Username [%s]: " % default_username)
if len(username) == 0:
username = default_username
if not UseGSSAPI:
- password = getpass.getpass('Password for %s@%s: ' % (username, hostname))
+ password = getpass.getpass("Password for %s@%s: " % (username, hostname))
else:
password = None
@@ -72,59 +72,69 @@ else:
hostkeytype = None
hostkey = None
try:
- host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
+ host_keys = paramiko.util.load_host_keys(
+ os.path.expanduser("~/.ssh/known_hosts")
+ )
except IOError:
try:
# try ~/ssh/ too, because windows can't have a folder named ~/.ssh/
- host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/ssh/known_hosts'))
+ host_keys = paramiko.util.load_host_keys(
+ os.path.expanduser("~/ssh/known_hosts")
+ )
except IOError:
- print('*** Unable to open host keys file')
+ print("*** Unable to open host keys file")
host_keys = {}
if hostname in host_keys:
hostkeytype = host_keys[hostname].keys()[0]
hostkey = host_keys[hostname][hostkeytype]
- print('Using host key of type %s' % hostkeytype)
+ print("Using host key of type %s" % hostkeytype)
# now, connect and use paramiko Transport to negotiate SSH2 across the connection
try:
t = paramiko.Transport((hostname, Port))
- t.connect(hostkey, username, password, gss_host=socket.getfqdn(hostname),
- gss_auth=UseGSSAPI, gss_kex=DoGSSAPIKeyExchange)
+ t.connect(
+ hostkey,
+ username,
+ password,
+ gss_host=socket.getfqdn(hostname),
+ gss_auth=UseGSSAPI,
+ gss_kex=DoGSSAPIKeyExchange,
+ )
sftp = paramiko.SFTPClient.from_transport(t)
# dirlist on remote host
- dirlist = sftp.listdir('.')
+ dirlist = sftp.listdir(".")
print("Dirlist: %s" % dirlist)
# copy this demo onto the server
try:
sftp.mkdir("demo_sftp_folder")
except IOError:
- print('(assuming demo_sftp_folder/ already exists)')
- with sftp.open('demo_sftp_folder/README', 'w') as f:
- f.write('This was created by demo_sftp.py.\n')
- with open('demo_sftp.py', 'r') as f:
+ print("(assuming demo_sftp_folder/ already exists)")
+ with sftp.open("demo_sftp_folder/README", "w") as f:
+ f.write("This was created by demo_sftp.py.\n")
+ with open("demo_sftp.py", "r") as f:
data = f.read()
- sftp.open('demo_sftp_folder/demo_sftp.py', 'w').write(data)
- print('created demo_sftp_folder/ on the server')
-
+ sftp.open("demo_sftp_folder/demo_sftp.py", "w").write(data)
+ print("created demo_sftp_folder/ on the server")
+
# copy the README back here
- with sftp.open('demo_sftp_folder/README', 'r') as f:
+ with sftp.open("demo_sftp_folder/README", "r") as f:
data = f.read()
- with open('README_demo_sftp', 'w') as f:
+ with open("README_demo_sftp", "w") as f:
f.write(data)
- print('copied README back here')
-
+ print("copied README back here")
+
# BETTER: use the get() and put() methods
- sftp.put('demo_sftp.py', 'demo_sftp_folder/demo_sftp.py')
- sftp.get('demo_sftp_folder/README', 'README_demo_sftp')
+ sftp.put("demo_sftp.py", "demo_sftp_folder/demo_sftp.py")
+ sftp.get("demo_sftp_folder/README", "README_demo_sftp")
t.close()
except Exception as e:
- print('*** Caught exception: %s: %s' % (e.__class__, e))
+ print("*** Caught exception: %s: %s" % (e.__class__, e))
traceback.print_exc()
try:
t.close()
diff --git a/demos/demo_simple.py b/demos/demo_simple.py
index 9def57f8..5dd4f6c1 100644
--- a/demos/demo_simple.py
+++ b/demos/demo_simple.py
@@ -28,6 +28,7 @@ import traceback
from paramiko.py3compat import input
import paramiko
+
try:
import interactive
except ImportError:
@@ -35,39 +36,43 @@ except ImportError:
# setup logging
-paramiko.util.log_to_file('demo_simple.log')
+paramiko.util.log_to_file("demo_simple.log")
# Paramiko client configuration
-UseGSSAPI = paramiko.GSS_AUTH_AVAILABLE # enable "gssapi-with-mic" authentication, if supported by your python installation
-DoGSSAPIKeyExchange = paramiko.GSS_AUTH_AVAILABLE # enable "gssapi-kex" key exchange, if supported by your python installation
+UseGSSAPI = (
+ paramiko.GSS_AUTH_AVAILABLE
+) # enable "gssapi-with-mic" authentication, if supported by your python installation
+DoGSSAPIKeyExchange = (
+ paramiko.GSS_AUTH_AVAILABLE
+) # enable "gssapi-kex" key exchange, if supported by your python installation
# UseGSSAPI = False
# DoGSSAPIKeyExchange = False
port = 22
# get hostname
-username = ''
+username = ""
if len(sys.argv) > 1:
hostname = sys.argv[1]
- if hostname.find('@') >= 0:
- username, hostname = hostname.split('@')
+ if hostname.find("@") >= 0:
+ username, hostname = hostname.split("@")
else:
- hostname = input('Hostname: ')
+ hostname = input("Hostname: ")
if len(hostname) == 0:
- print('*** Hostname required.')
+ print("*** Hostname required.")
sys.exit(1)
-if hostname.find(':') >= 0:
- hostname, portstr = hostname.split(':')
+if hostname.find(":") >= 0:
+ hostname, portstr = hostname.split(":")
port = int(portstr)
# get username
-if username == '':
+if username == "":
default_username = getpass.getuser()
- username = input('Username [%s]: ' % default_username)
+ username = input("Username [%s]: " % default_username)
if len(username) == 0:
username = default_username
if not UseGSSAPI and not DoGSSAPIKeyExchange:
- password = getpass.getpass('Password for %s@%s: ' % (username, hostname))
+ password = getpass.getpass("Password for %s@%s: " % (username, hostname))
# now, connect and use paramiko Client to negotiate SSH2 across the connection
@@ -75,27 +80,34 @@ try:
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.WarningPolicy())
- print('*** Connecting...')
+ print("*** Connecting...")
if not UseGSSAPI and not DoGSSAPIKeyExchange:
client.connect(hostname, port, username, password)
else:
try:
- client.connect(hostname, port, username, gss_auth=UseGSSAPI,
- gss_kex=DoGSSAPIKeyExchange)
+ client.connect(
+ hostname,
+ port,
+ username,
+ gss_auth=UseGSSAPI,
+ gss_kex=DoGSSAPIKeyExchange,
+ )
except Exception:
# traceback.print_exc()
- password = getpass.getpass('Password for %s@%s: ' % (username, hostname))
+ password = getpass.getpass(
+ "Password for %s@%s: " % (username, hostname)
+ )
client.connect(hostname, port, username, password)
chan = client.invoke_shell()
print(repr(client.get_transport()))
- print('*** Here we go!\n')
+ print("*** Here we go!\n")
interactive.interactive_shell(chan)
chan.close()
client.close()
except Exception as e:
- print('*** Caught exception: %s: %s' % (e.__class__, e))
+ print("*** Caught exception: %s: %s" % (e.__class__, e))
traceback.print_exc()
try:
client.close()
diff --git a/demos/forward.py b/demos/forward.py
index 96e1700d..98757911 100644
--- a/demos/forward.py
+++ b/demos/forward.py
@@ -30,6 +30,7 @@ import getpass
import os
import socket
import select
+
try:
import SocketServer
except ImportError:
@@ -46,30 +47,41 @@ DEFAULT_PORT = 4000
g_verbose = True
-class ForwardServer (SocketServer.ThreadingTCPServer):
+class ForwardServer(SocketServer.ThreadingTCPServer):
daemon_threads = True
allow_reuse_address = True
-
-class Handler (SocketServer.BaseRequestHandler):
+
+class Handler(SocketServer.BaseRequestHandler):
def handle(self):
try:
- chan = self.ssh_transport.open_channel('direct-tcpip',
- (self.chain_host, self.chain_port),
- self.request.getpeername())
+ chan = self.ssh_transport.open_channel(
+ "direct-tcpip",
+ (self.chain_host, self.chain_port),
+ self.request.getpeername(),
+ )
except Exception as e:
- verbose('Incoming request to %s:%d failed: %s' % (self.chain_host,
- self.chain_port,
- repr(e)))
+ verbose(
+ "Incoming request to %s:%d failed: %s"
+ % (self.chain_host, self.chain_port, repr(e))
+ )
return
if chan is None:
- verbose('Incoming request to %s:%d was rejected by the SSH server.' %
- (self.chain_host, self.chain_port))
+ verbose(
+ "Incoming request to %s:%d was rejected by the SSH server."
+ % (self.chain_host, self.chain_port)
+ )
return
- verbose('Connected! Tunnel open %r -> %r -> %r' % (self.request.getpeername(),
- chan.getpeername(), (self.chain_host, self.chain_port)))
+ verbose(
+ "Connected! Tunnel open %r -> %r -> %r"
+ % (
+ self.request.getpeername(),
+ chan.getpeername(),
+ (self.chain_host, self.chain_port),
+ )
+ )
while True:
r, w, x = select.select([self.request, chan], [], [])
if self.request in r:
@@ -82,22 +94,23 @@ class Handler (SocketServer.BaseRequestHandler):
if len(data) == 0:
break
self.request.send(data)
-
+
peername = self.request.getpeername()
chan.close()
self.request.close()
- verbose('Tunnel closed from %r' % (peername,))
+ verbose("Tunnel closed from %r" % (peername,))
def forward_tunnel(local_port, remote_host, remote_port, transport):
# this is a little convoluted, but lets me configure things for the Handler
# object. (SocketServer doesn't give Handlers any way to access the outer
# server normally.)
- class SubHander (Handler):
+ class SubHander(Handler):
chain_host = remote_host
chain_port = remote_port
ssh_transport = transport
- ForwardServer(('', local_port), SubHander).serve_forever()
+
+ ForwardServer(("", local_port), SubHander).serve_forever()
def verbose(s):
@@ -114,40 +127,88 @@ the SSH server. This is similar to the openssh -L option.
def get_host_port(spec, default_port):
"parse 'hostname:22' into a host and port, with the port optional"
- args = (spec.split(':', 1) + [default_port])[:2]
+ args = (spec.split(":", 1) + [default_port])[:2]
args[1] = int(args[1])
return args[0], args[1]
def parse_options():
global g_verbose
-
- parser = OptionParser(usage='usage: %prog [options] <ssh-server>[:<server-port>]',
- version='%prog 1.0', description=HELP)
- parser.add_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
- help='squelch all informational output')
- parser.add_option('-p', '--local-port', action='store', type='int', dest='port',
- default=DEFAULT_PORT,
- help='local port to forward (default: %d)' % DEFAULT_PORT)
- parser.add_option('-u', '--user', action='store', type='string', dest='user',
- default=getpass.getuser(),
- help='username for SSH authentication (default: %s)' % getpass.getuser())
- parser.add_option('-K', '--key', action='store', type='string', dest='keyfile',
- default=None,
- help='private key file to use for SSH authentication')
- parser.add_option('', '--no-key', action='store_false', dest='look_for_keys', default=True,
- help='don\'t look for or use a private key file')
- parser.add_option('-P', '--password', action='store_true', dest='readpass', default=False,
- help='read password (for key or password auth) from stdin')
- parser.add_option('-r', '--remote', action='store', type='string', dest='remote', default=None, metavar='host:port',
- help='remote host and port to forward to')
+
+ parser = OptionParser(
+ usage="usage: %prog [options] <ssh-server>[:<server-port>]",
+ version="%prog 1.0",
+ description=HELP,
+ )
+ parser.add_option(
+ "-q",
+ "--quiet",
+ action="store_false",
+ dest="verbose",
+ default=True,
+ help="squelch all informational output",
+ )
+ parser.add_option(
+ "-p",
+ "--local-port",
+ action="store",
+ type="int",
+ dest="port",
+ default=DEFAULT_PORT,
+ help="local port to forward (default: %d)" % DEFAULT_PORT,
+ )
+ parser.add_option(
+ "-u",
+ "--user",
+ action="store",
+ type="string",
+ dest="user",
+ default=getpass.getuser(),
+ help="username for SSH authentication (default: %s)"
+ % getpass.getuser(),
+ )
+ parser.add_option(
+ "-K",
+ "--key",
+ action="store",
+ type="string",
+ dest="keyfile",
+ default=None,
+ help="private key file to use for SSH authentication",
+ )
+ parser.add_option(
+ "",
+ "--no-key",
+ action="store_false",
+ dest="look_for_keys",
+ default=True,
+ help="don't look for or use a private key file",
+ )
+ parser.add_option(
+ "-P",
+ "--password",
+ action="store_true",
+ dest="readpass",
+ default=False,
+ help="read password (for key or password auth) from stdin",
+ )
+ parser.add_option(
+ "-r",
+ "--remote",
+ action="store",
+ type="string",
+ dest="remote",
+ default=None,
+ metavar="host:port",
+ help="remote host and port to forward to",
+ )
options, args = parser.parse_args()
if len(args) != 1:
- parser.error('Incorrect number of arguments.')
+ parser.error("Incorrect number of arguments.")
if options.remote is None:
- parser.error('Remote address required (-r).')
-
+ parser.error("Remote address required (-r).")
+
g_verbose = options.verbose
server_host, server_port = get_host_port(args[0], SSH_PORT)
remote_host, remote_port = get_host_port(options.remote, SSH_PORT)
@@ -156,31 +217,42 @@ def parse_options():
def main():
options, server, remote = parse_options()
-
+
password = None
if options.readpass:
- password = getpass.getpass('Enter SSH password: ')
-
+ password = getpass.getpass("Enter SSH password: ")
+
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.WarningPolicy())
- verbose('Connecting to ssh host %s:%d ...' % (server[0], server[1]))
+ verbose("Connecting to ssh host %s:%d ..." % (server[0], server[1]))
try:
- client.connect(server[0], server[1], username=options.user, key_filename=options.keyfile,
- look_for_keys=options.look_for_keys, password=password)
+ client.connect(
+ server[0],
+ server[1],
+ username=options.user,
+ key_filename=options.keyfile,
+ look_for_keys=options.look_for_keys,
+ password=password,
+ )
except Exception as e:
- print('*** Failed to connect to %s:%d: %r' % (server[0], server[1], e))
+ print("*** Failed to connect to %s:%d: %r" % (server[0], server[1], e))
sys.exit(1)
- verbose('Now forwarding port %d to %s:%d ...' % (options.port, remote[0], remote[1]))
+ verbose(
+ "Now forwarding port %d to %s:%d ..."
+ % (options.port, remote[0], remote[1])
+ )
try:
- forward_tunnel(options.port, remote[0], remote[1], client.get_transport())
+ forward_tunnel(
+ options.port, remote[0], remote[1], client.get_transport()
+ )
except KeyboardInterrupt:
- print('C-c: Port forwarding stopped.')
+ print("C-c: Port forwarding stopped.")
sys.exit(0)
-if __name__ == '__main__':
+if __name__ == "__main__":
main()
diff --git a/demos/interactive.py b/demos/interactive.py
index 7138cd6c..037787c4 100644
--- a/demos/interactive.py
+++ b/demos/interactive.py
@@ -25,6 +25,7 @@ from paramiko.py3compat import u
try:
import termios
import tty
+
has_termios = True
except ImportError:
has_termios = False
@@ -39,7 +40,7 @@ def interactive_shell(chan):
def posix_shell(chan):
import select
-
+
oldtty = termios.tcgetattr(sys.stdin)
try:
tty.setraw(sys.stdin.fileno())
@@ -52,7 +53,7 @@ def posix_shell(chan):
try:
x = u(chan.recv(1024))
if len(x) == 0:
- sys.stdout.write('\r\n*** EOF\r\n')
+ sys.stdout.write("\r\n*** EOF\r\n")
break
sys.stdout.write(x)
sys.stdout.flush()
@@ -67,26 +68,28 @@ def posix_shell(chan):
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
-
+
# thanks to Mike Looijmans for this code
def windows_shell(chan):
import threading
- sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n")
-
+ sys.stdout.write(
+ "Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n"
+ )
+
def writeall(sock):
while True:
data = sock.recv(256)
if not data:
- sys.stdout.write('\r\n*** EOF ***\r\n\r\n')
+ sys.stdout.write("\r\n*** EOF ***\r\n\r\n")
sys.stdout.flush()
break
sys.stdout.write(data)
sys.stdout.flush()
-
+
writer = threading.Thread(target=writeall, args=(chan,))
writer.start()
-
+
try:
while True:
d = sys.stdin.read(1)
diff --git a/demos/rforward.py b/demos/rforward.py
index ae70670c..a2e8a776 100755
--- a/demos/rforward.py
+++ b/demos/rforward.py
@@ -47,11 +47,13 @@ def handler(chan, host, port):
try:
sock.connect((host, port))
except Exception as e:
- verbose('Forwarding request to %s:%d failed: %r' % (host, port, e))
+ verbose("Forwarding request to %s:%d failed: %r" % (host, port, e))
return
-
- verbose('Connected! Tunnel open %r -> %r -> %r' % (chan.origin_addr,
- chan.getpeername(), (host, port)))
+
+ verbose(
+ "Connected! Tunnel open %r -> %r -> %r"
+ % (chan.origin_addr, chan.getpeername(), (host, port))
+ )
while True:
r, w, x = select.select([sock, chan], [], [])
if sock in r:
@@ -66,16 +68,18 @@ def handler(chan, host, port):
sock.send(data)
chan.close()
sock.close()
- verbose('Tunnel closed from %r' % (chan.origin_addr,))
+ verbose("Tunnel closed from %r" % (chan.origin_addr,))
def reverse_forward_tunnel(server_port, remote_host, remote_port, transport):
- transport.request_port_forward('', server_port)
+ transport.request_port_forward("", server_port)
while True:
chan = transport.accept(1000)
if chan is None:
continue
- thr = threading.Thread(target=handler, args=(chan, remote_host, remote_port))
+ thr = threading.Thread(
+ target=handler, args=(chan, remote_host, remote_port)
+ )
thr.setDaemon(True)
thr.start()
@@ -95,40 +99,88 @@ network. This is similar to the openssh -R option.
def get_host_port(spec, default_port):
"parse 'hostname:22' into a host and port, with the port optional"
- args = (spec.split(':', 1) + [default_port])[:2]
+ args = (spec.split(":", 1) + [default_port])[:2]
args[1] = int(args[1])
return args[0], args[1]
def parse_options():
global g_verbose
-
- parser = OptionParser(usage='usage: %prog [options] <ssh-server>[:<server-port>]',
- version='%prog 1.0', description=HELP)
- parser.add_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
- help='squelch all informational output')
- parser.add_option('-p', '--remote-port', action='store', type='int', dest='port',
- default=DEFAULT_PORT,
- help='port on server to forward (default: %d)' % DEFAULT_PORT)
- parser.add_option('-u', '--user', action='store', type='string', dest='user',
- default=getpass.getuser(),
- help='username for SSH authentication (default: %s)' % getpass.getuser())
- parser.add_option('-K', '--key', action='store', type='string', dest='keyfile',
- default=None,
- help='private key file to use for SSH authentication')
- parser.add_option('', '--no-key', action='store_false', dest='look_for_keys', default=True,
- help='don\'t look for or use a private key file')
- parser.add_option('-P', '--password', action='store_true', dest='readpass', default=False,
- help='read password (for key or password auth) from stdin')
- parser.add_option('-r', '--remote', action='store', type='string', dest='remote', default=None, metavar='host:port',
- help='remote host and port to forward to')
+
+ parser = OptionParser(
+ usage="usage: %prog [options] <ssh-server>[:<server-port>]",
+ version="%prog 1.0",
+ description=HELP,
+ )
+ parser.add_option(
+ "-q",
+ "--quiet",
+ action="store_false",
+ dest="verbose",
+ default=True,
+ help="squelch all informational output",
+ )
+ parser.add_option(
+ "-p",
+ "--remote-port",
+ action="store",
+ type="int",
+ dest="port",
+ default=DEFAULT_PORT,
+ help="port on server to forward (default: %d)" % DEFAULT_PORT,
+ )
+ parser.add_option(
+ "-u",
+ "--user",
+ action="store",
+ type="string",
+ dest="user",
+ default=getpass.getuser(),
+ help="username for SSH authentication (default: %s)"
+ % getpass.getuser(),
+ )
+ parser.add_option(
+ "-K",
+ "--key",
+ action="store",
+ type="string",
+ dest="keyfile",
+ default=None,
+ help="private key file to use for SSH authentication",
+ )
+ parser.add_option(
+ "",
+ "--no-key",
+ action="store_false",
+ dest="look_for_keys",
+ default=True,
+ help="don't look for or use a private key file",
+ )
+ parser.add_option(
+ "-P",
+ "--password",
+ action="store_true",
+ dest="readpass",
+ default=False,
+ help="read password (for key or password auth) from stdin",
+ )
+ parser.add_option(
+ "-r",
+ "--remote",
+ action="store",
+ type="string",
+ dest="remote",
+ default=None,
+ metavar="host:port",
+ help="remote host and port to forward to",
+ )
options, args = parser.parse_args()
if len(args) != 1:
- parser.error('Incorrect number of arguments.')
+ parser.error("Incorrect number of arguments.")
if options.remote is None:
- parser.error('Remote address required (-r).')
-
+ parser.error("Remote address required (-r).")
+
g_verbose = options.verbose
server_host, server_port = get_host_port(args[0], SSH_PORT)
remote_host, remote_port = get_host_port(options.remote, SSH_PORT)
@@ -137,31 +189,42 @@ def parse_options():
def main():
options, server, remote = parse_options()
-
+
password = None
if options.readpass:
- password = getpass.getpass('Enter SSH password: ')
-
+ password = getpass.getpass("Enter SSH password: ")
+
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.WarningPolicy())
- verbose('Connecting to ssh host %s:%d ...' % (server[0], server[1]))
+ verbose("Connecting to ssh host %s:%d ..." % (server[0], server[1]))
try:
- client.connect(server[0], server[1], username=options.user, key_filename=options.keyfile,
- look_for_keys=options.look_for_keys, password=password)
+ client.connect(
+ server[0],
+ server[1],
+ username=options.user,
+ key_filename=options.keyfile,
+ look_for_keys=options.look_for_keys,
+ password=password,
+ )
except Exception as e:
- print('*** Failed to connect to %s:%d: %r' % (server[0], server[1], e))
+ print("*** Failed to connect to %s:%d: %r" % (server[0], server[1], e))
sys.exit(1)
- verbose('Now forwarding remote port %d to %s:%d ...' % (options.port, remote[0], remote[1]))
+ verbose(
+ "Now forwarding remote port %d to %s:%d ..."
+ % (options.port, remote[0], remote[1])
+ )
try:
- reverse_forward_tunnel(options.port, remote[0], remote[1], client.get_transport())
+ reverse_forward_tunnel(
+ options.port, remote[0], remote[1], client.get_transport()
+ )
except KeyboardInterrupt:
- print('C-c: Port forwarding stopped.')
+ print("C-c: Port forwarding stopped.")
sys.exit(0)
-if __name__ == '__main__':
+if __name__ == "__main__":
main()
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 698510e0..71f85faf 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -1,12 +1,23 @@
-# Older junk
-tox>=1.4,<1.5
-# For newer tasks like building Sphinx docs.
-invoke>=0.13,<=0.21.0
-invocations>=0.13,<=0.20.0
-sphinx>=1.1.3,<1.5
-alabaster>=0.7.5,<2.0
-releases>=1.1.0,<1.4.0
-semantic_version<3.0
+# Invocations for common project tasks
+invoke>=1.0,<2.0
+invocations>=1.2.0,<2.0
+# NOTE: pytest-relaxed currently only works with pytest >=3, <3.3
+pytest>=3.2,<3.3
+pytest-relaxed==1.1.4
+# pytest-xdist for test dir watching and the inv guard task
+pytest-xdist>=1.22,<2.0
+# Linting!
+flake8==2.4.0
+# Coverage!
+coverage==3.7.1
+codecov==1.6.3
+# Documentation tools
+sphinx>=1.4,<1.7
+alabaster>=0.7,<2.0
+releases>=1.5,<2.0
+# Release tools
+semantic_version>=2.4,<2.5
wheel==0.24
-twine>=1.11.0
-flake8==2.6.2
+twine==1.11.0
+
+
diff --git a/paramiko/__init__.py b/paramiko/__init__.py
index bdc91d51..f77a2bcc 100644
--- a/paramiko/__init__.py
+++ b/paramiko/__init__.py
@@ -19,26 +19,24 @@
# flake8: noqa
import sys
from paramiko._version import __version__, __version_info__
-
-if sys.version_info < (2, 6):
- raise RuntimeError('You need Python 2.6+ for this module.')
-
-
-__author__ = "Jeff Forcier <jeff@bitprophet.org>"
-__license__ = "GNU Lesser General Public License (LGPL)"
-
-
from paramiko.transport import SecurityOptions, Transport
from paramiko.client import (
- SSHClient, MissingHostKeyPolicy, AutoAddPolicy, RejectPolicy,
+ SSHClient,
+ MissingHostKeyPolicy,
+ AutoAddPolicy,
+ RejectPolicy,
WarningPolicy,
)
from paramiko.auth_handler import AuthHandler
from paramiko.ssh_gss import GSSAuth, GSS_AUTH_AVAILABLE, GSS_EXCEPTIONS
from paramiko.channel import Channel, ChannelFile
from paramiko.ssh_exception import (
- SSHException, PasswordRequiredException, BadAuthenticationType,
- ChannelException, BadHostKeyException, AuthenticationException,
+ SSHException,
+ PasswordRequiredException,
+ BadAuthenticationType,
+ ChannelException,
+ BadHostKeyException,
+ AuthenticationException,
ProxyCommandFailure,
)
from paramiko.server import ServerInterface, SubsystemHandler, InteractiveQuery
@@ -63,55 +61,70 @@ from paramiko.config import SSHConfig
from paramiko.proxy import ProxyCommand
from paramiko.common import (
- AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED, OPEN_SUCCEEDED,
- OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, OPEN_FAILED_CONNECT_FAILED,
- OPEN_FAILED_UNKNOWN_CHANNEL_TYPE, OPEN_FAILED_RESOURCE_SHORTAGE,
+ AUTH_SUCCESSFUL,
+ AUTH_PARTIALLY_SUCCESSFUL,
+ AUTH_FAILED,
+ OPEN_SUCCEEDED,
+ OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED,
+ OPEN_FAILED_CONNECT_FAILED,
+ OPEN_FAILED_UNKNOWN_CHANNEL_TYPE,
+ OPEN_FAILED_RESOURCE_SHORTAGE,
)
from paramiko.sftp import (
- SFTP_OK, SFTP_EOF, SFTP_NO_SUCH_FILE, SFTP_PERMISSION_DENIED, SFTP_FAILURE,
- SFTP_BAD_MESSAGE, SFTP_NO_CONNECTION, SFTP_CONNECTION_LOST,
+ SFTP_OK,
+ SFTP_EOF,
+ SFTP_NO_SUCH_FILE,
+ SFTP_PERMISSION_DENIED,
+ SFTP_FAILURE,
+ SFTP_BAD_MESSAGE,
+ SFTP_NO_CONNECTION,
+ SFTP_CONNECTION_LOST,
SFTP_OP_UNSUPPORTED,
)
from paramiko.common import io_sleep
+
+__author__ = "Jeff Forcier <jeff@bitprophet.org>"
+__license__ = "GNU Lesser General Public License (LGPL)"
+
__all__ = [
- 'Transport',
- 'SSHClient',
- 'MissingHostKeyPolicy',
- 'AutoAddPolicy',
- 'RejectPolicy',
- 'WarningPolicy',
- 'SecurityOptions',
- 'SubsystemHandler',
- 'Channel',
- 'PKey',
- 'RSAKey',
- 'DSSKey',
- 'Message',
- 'SSHException',
- 'AuthenticationException',
- 'PasswordRequiredException',
- 'BadAuthenticationType',
- 'ChannelException',
- 'BadHostKeyException',
- 'ProxyCommand',
- 'ProxyCommandFailure',
- 'SFTP',
- 'SFTPFile',
- 'SFTPHandle',
- 'SFTPClient',
- 'SFTPServer',
- 'SFTPError',
- 'SFTPAttributes',
- 'SFTPServerInterface',
- 'ServerInterface',
- 'BufferedFile',
- 'Agent',
- 'AgentKey',
- 'HostKeys',
- 'SSHConfig',
- 'util',
- 'io_sleep',
+ "Agent",
+ "AgentKey",
+ "AuthenticationException",
+ "AutoAddPolicy",
+ "BadAuthenticationType",
+ "BadHostKeyException",
+ "BufferedFile",
+ "Channel",
+ "ChannelException",
+ "DSSKey",
+ "HostKeys",
+ "Message",
+ "MissingHostKeyPolicy",
+ "PKey",
+ "PasswordRequiredException",
+ "ProxyCommand",
+ "ProxyCommandFailure",
+ "RSAKey",
+ "RejectPolicy",
+ "SFTP",
+ "SFTPAttributes",
+ "SFTPClient",
+ "SFTPError",
+ "SFTPFile",
+ "SFTPHandle",
+ "SFTPServer",
+ "SFTPServerInterface",
+ "SSHClient",
+ "SSHConfig",
+ "SSHException",
+ "SecurityOptions",
+ "ServerInterface",
+ "SubsystemHandler",
+ "Transport",
+ "WarningPolicy",
+ "io_sleep",
+ "util",
]
diff --git a/paramiko/_version.py b/paramiko/_version.py
index aee80ab4..95e86b6a 100644
--- a/paramiko/_version.py
+++ b/paramiko/_version.py
@@ -1,2 +1,2 @@
-__version_info__ = (2, 3, 2)
-__version__ = '.'.join(map(str, __version_info__))
+__version_info__ = (2, 4, 1)
+__version__ = ".".join(map(str, __version_info__))
diff --git a/paramiko/_winapi.py b/paramiko/_winapi.py
index a13d7e87..ebcc678a 100644
--- a/paramiko/_winapi.py
+++ b/paramiko/_winapi.py
@@ -15,6 +15,7 @@ from paramiko.py3compat import u, builtins
######################
# jaraco.windows.error
+
def format_system_message(errno):
"""
Call FormatMessage with a system error number to retrieve
@@ -77,7 +78,7 @@ class WindowsError(builtins.WindowsError):
return self.message
def __repr__(self):
- return '{self.__class__.__name__}({self.winerror})'.format(**vars())
+ return "{self.__class__.__name__}({self.winerror})".format(**vars())
def handle_nonzero_success(result):
@@ -95,15 +96,15 @@ GlobalAlloc.argtypes = ctypes.wintypes.UINT, ctypes.c_size_t
GlobalAlloc.restype = ctypes.wintypes.HANDLE
GlobalLock = ctypes.windll.kernel32.GlobalLock
-GlobalLock.argtypes = ctypes.wintypes.HGLOBAL,
+GlobalLock.argtypes = (ctypes.wintypes.HGLOBAL,)
GlobalLock.restype = ctypes.wintypes.LPVOID
GlobalUnlock = ctypes.windll.kernel32.GlobalUnlock
-GlobalUnlock.argtypes = ctypes.wintypes.HGLOBAL,
+GlobalUnlock.argtypes = (ctypes.wintypes.HGLOBAL,)
GlobalUnlock.restype = ctypes.wintypes.BOOL
GlobalSize = ctypes.windll.kernel32.GlobalSize
-GlobalSize.argtypes = ctypes.wintypes.HGLOBAL,
+GlobalSize.argtypes = (ctypes.wintypes.HGLOBAL,)
GlobalSize.restype = ctypes.c_size_t
CreateFileMapping = ctypes.windll.kernel32.CreateFileMappingW
@@ -121,16 +122,12 @@ MapViewOfFile = ctypes.windll.kernel32.MapViewOfFile
MapViewOfFile.restype = ctypes.wintypes.HANDLE
UnmapViewOfFile = ctypes.windll.kernel32.UnmapViewOfFile
-UnmapViewOfFile.argtypes = ctypes.wintypes.HANDLE,
+UnmapViewOfFile.argtypes = (ctypes.wintypes.HANDLE,)
RtlMoveMemory = ctypes.windll.kernel32.RtlMoveMemory
-RtlMoveMemory.argtypes = (
- ctypes.c_void_p,
- ctypes.c_void_p,
- ctypes.c_size_t,
-)
+RtlMoveMemory.argtypes = (ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t)
-ctypes.windll.kernel32.LocalFree.argtypes = ctypes.wintypes.HLOCAL,
+ctypes.windll.kernel32.LocalFree.argtypes = (ctypes.wintypes.HLOCAL,)
#####################
# jaraco.windows.mmap
@@ -140,6 +137,7 @@ class MemoryMap(object):
"""
A memory map object which can have security attributes overridden.
"""
+
def __init__(self, name, length, security_attributes=None):
self.name = name
self.length = length
@@ -149,14 +147,20 @@ class MemoryMap(object):
def __enter__(self):
p_SA = (
ctypes.byref(self.security_attributes)
- if self.security_attributes else None
+ if self.security_attributes
+ else None
)
INVALID_HANDLE_VALUE = -1
PAGE_READWRITE = 0x4
FILE_MAP_WRITE = 0x2
filemap = ctypes.windll.kernel32.CreateFileMappingW(
- INVALID_HANDLE_VALUE, p_SA, PAGE_READWRITE, 0, self.length,
- u(self.name))
+ INVALID_HANDLE_VALUE,
+ p_SA,
+ PAGE_READWRITE,
+ 0,
+ self.length,
+ u(self.name),
+ )
handle_nonzero_success(filemap)
if filemap == INVALID_HANDLE_VALUE:
raise Exception("Failed to create file mapping")
@@ -220,41 +224,45 @@ POLICY_LOOKUP_NAMES = 0x00000800
POLICY_NOTIFICATION = 0x00001000
POLICY_ALL_ACCESS = (
- STANDARD_RIGHTS_REQUIRED |
- POLICY_VIEW_LOCAL_INFORMATION |
- POLICY_VIEW_AUDIT_INFORMATION |
- POLICY_GET_PRIVATE_INFORMATION |
- POLICY_TRUST_ADMIN |
- POLICY_CREATE_ACCOUNT |
- POLICY_CREATE_SECRET |
- POLICY_CREATE_PRIVILEGE |
- POLICY_SET_DEFAULT_QUOTA_LIMITS |
- POLICY_SET_AUDIT_REQUIREMENTS |
- POLICY_AUDIT_LOG_ADMIN |
- POLICY_SERVER_ADMIN |
- POLICY_LOOKUP_NAMES)
+ STANDARD_RIGHTS_REQUIRED
+ | POLICY_VIEW_LOCAL_INFORMATION
+ | POLICY_VIEW_AUDIT_INFORMATION
+ | POLICY_GET_PRIVATE_INFORMATION
+ | POLICY_TRUST_ADMIN
+ | POLICY_CREATE_ACCOUNT
+ | POLICY_CREATE_SECRET
+ | POLICY_CREATE_PRIVILEGE
+ | POLICY_SET_DEFAULT_QUOTA_LIMITS
+ | POLICY_SET_AUDIT_REQUIREMENTS
+ | POLICY_AUDIT_LOG_ADMIN
+ | POLICY_SERVER_ADMIN
+ | POLICY_LOOKUP_NAMES
+)
POLICY_READ = (
- STANDARD_RIGHTS_READ |
- POLICY_VIEW_AUDIT_INFORMATION |
- POLICY_GET_PRIVATE_INFORMATION)
+ STANDARD_RIGHTS_READ
+ | POLICY_VIEW_AUDIT_INFORMATION
+ | POLICY_GET_PRIVATE_INFORMATION
+)
POLICY_WRITE = (
- STANDARD_RIGHTS_WRITE |
- POLICY_TRUST_ADMIN |
- POLICY_CREATE_ACCOUNT |
- POLICY_CREATE_SECRET |
- POLICY_CREATE_PRIVILEGE |
- POLICY_SET_DEFAULT_QUOTA_LIMITS |
- POLICY_SET_AUDIT_REQUIREMENTS |
- POLICY_AUDIT_LOG_ADMIN |
- POLICY_SERVER_ADMIN)
+ STANDARD_RIGHTS_WRITE
+ | POLICY_TRUST_ADMIN
+ | POLICY_CREATE_ACCOUNT
+ | POLICY_CREATE_SECRET
+ | POLICY_CREATE_PRIVILEGE
+ | POLICY_SET_DEFAULT_QUOTA_LIMITS
+ | POLICY_SET_AUDIT_REQUIREMENTS
+ | POLICY_AUDIT_LOG_ADMIN
+ | POLICY_SERVER_ADMIN
+)
POLICY_EXECUTE = (
- STANDARD_RIGHTS_EXECUTE |
- POLICY_VIEW_LOCAL_INFORMATION |
- POLICY_LOOKUP_NAMES)
+ STANDARD_RIGHTS_EXECUTE
+ | POLICY_VIEW_LOCAL_INFORMATION
+ | POLICY_LOOKUP_NAMES
+)
class TokenAccess:
@@ -268,8 +276,8 @@ class TokenInformationClass:
class TOKEN_USER(ctypes.Structure):
num = 1
_fields_ = [
- ('SID', ctypes.c_void_p),
- ('ATTRIBUTES', ctypes.wintypes.DWORD),
+ ("SID", ctypes.c_void_p),
+ ("ATTRIBUTES", ctypes.wintypes.DWORD),
]
@@ -290,13 +298,13 @@ class SECURITY_DESCRIPTOR(ctypes.Structure):
REVISION = 1
_fields_ = [
- ('Revision', ctypes.c_ubyte),
- ('Sbz1', ctypes.c_ubyte),
- ('Control', SECURITY_DESCRIPTOR_CONTROL),
- ('Owner', ctypes.c_void_p),
- ('Group', ctypes.c_void_p),
- ('Sacl', ctypes.c_void_p),
- ('Dacl', ctypes.c_void_p),
+ ("Revision", ctypes.c_ubyte),
+ ("Sbz1", ctypes.c_ubyte),
+ ("Control", SECURITY_DESCRIPTOR_CONTROL),
+ ("Owner", ctypes.c_void_p),
+ ("Group", ctypes.c_void_p),
+ ("Sacl", ctypes.c_void_p),
+ ("Dacl", ctypes.c_void_p),
]
@@ -309,9 +317,9 @@ class SECURITY_ATTRIBUTES(ctypes.Structure):
} SECURITY_ATTRIBUTES;
"""
_fields_ = [
- ('nLength', ctypes.wintypes.DWORD),
- ('lpSecurityDescriptor', ctypes.c_void_p),
- ('bInheritHandle', ctypes.wintypes.BOOL),
+ ("nLength", ctypes.wintypes.DWORD),
+ ("lpSecurityDescriptor", ctypes.c_void_p),
+ ("bInheritHandle", ctypes.wintypes.BOOL),
]
def __init__(self, *args, **kwargs):
@@ -343,21 +351,30 @@ def GetTokenInformation(token, information_class):
Given a token, get the token information for it.
"""
data_size = ctypes.wintypes.DWORD()
- ctypes.windll.advapi32.GetTokenInformation(token, information_class.num,
- 0, 0, ctypes.byref(data_size))
+ ctypes.windll.advapi32.GetTokenInformation(
+ token, information_class.num, 0, 0, ctypes.byref(data_size)
+ )
data = ctypes.create_string_buffer(data_size.value)
- handle_nonzero_success(ctypes.windll.advapi32.GetTokenInformation(token,
- information_class.num,
- ctypes.byref(data), ctypes.sizeof(data),
- ctypes.byref(data_size)))
+ handle_nonzero_success(
+ ctypes.windll.advapi32.GetTokenInformation(
+ token,
+ information_class.num,
+ ctypes.byref(data),
+ ctypes.sizeof(data),
+ ctypes.byref(data_size),
+ )
+ )
return ctypes.cast(data, ctypes.POINTER(TOKEN_USER)).contents
def OpenProcessToken(proc_handle, access):
result = ctypes.wintypes.HANDLE()
proc_handle = ctypes.wintypes.HANDLE(proc_handle)
- handle_nonzero_success(ctypes.windll.advapi32.OpenProcessToken(
- proc_handle, access, ctypes.byref(result)))
+ handle_nonzero_success(
+ ctypes.windll.advapi32.OpenProcessToken(
+ proc_handle, access, ctypes.byref(result)
+ )
+ )
return result
@@ -366,8 +383,7 @@ def get_current_user():
Return a TOKEN_USER for the owner of this process.
"""
process = OpenProcessToken(
- ctypes.windll.kernel32.GetCurrentProcess(),
- TokenAccess.TOKEN_QUERY,
+ ctypes.windll.kernel32.GetCurrentProcess(), TokenAccess.TOKEN_QUERY
)
return GetTokenInformation(process, TOKEN_USER)
@@ -389,8 +405,10 @@ def get_security_attributes_for_user(user=None):
SA.descriptor = SD
SA.bInheritHandle = 1
- ctypes.windll.advapi32.InitializeSecurityDescriptor(ctypes.byref(SD),
- SECURITY_DESCRIPTOR.REVISION)
- ctypes.windll.advapi32.SetSecurityDescriptorOwner(ctypes.byref(SD),
- user.SID, 0)
+ ctypes.windll.advapi32.InitializeSecurityDescriptor(
+ ctypes.byref(SD), SECURITY_DESCRIPTOR.REVISION
+ )
+ ctypes.windll.advapi32.SetSecurityDescriptorOwner(
+ ctypes.byref(SD), user.SID, 0
+ )
return SA
diff --git a/paramiko/agent.py b/paramiko/agent.py
index 7a4dde21..62a271d5 100644
--- a/paramiko/agent.py
+++ b/paramiko/agent.py
@@ -43,8 +43,8 @@ cSSH2_AGENTC_SIGN_REQUEST = byte_chr(13)
SSH2_AGENT_SIGN_RESPONSE = 14
-
class AgentSSH(object):
+
def __init__(self):
self._conn = None
self._keys = ()
@@ -65,7 +65,7 @@ class AgentSSH(object):
self._conn = conn
ptype, result = self._send_message(cSSH2_AGENTC_REQUEST_IDENTITIES)
if ptype != SSH2_AGENT_IDENTITIES_ANSWER:
- raise SSHException('could not get keys from ssh-agent')
+ raise SSHException("could not get keys from ssh-agent")
keys = []
for i in range(result.get_int()):
keys.append(AgentKey(self, result.get_binary()))
@@ -80,19 +80,19 @@ class AgentSSH(object):
def _send_message(self, msg):
msg = asbytes(msg)
- self._conn.send(struct.pack('>I', len(msg)) + msg)
+ self._conn.send(struct.pack(">I", len(msg)) + msg)
l = self._read_all(4)
- msg = Message(self._read_all(struct.unpack('>I', l)[0]))
+ msg = Message(self._read_all(struct.unpack(">I", l)[0]))
return ord(msg.get_byte()), msg
def _read_all(self, wanted):
result = self._conn.recv(wanted)
while len(result) < wanted:
if len(result) == 0:
- raise SSHException('lost ssh-agent')
+ raise SSHException("lost ssh-agent")
extra = self._conn.recv(wanted - len(result))
if len(extra) == 0:
- raise SSHException('lost ssh-agent')
+ raise SSHException("lost ssh-agent")
result += extra
return result
@@ -101,6 +101,7 @@ class AgentProxyThread(threading.Thread):
"""
Class in charge of communication between two channels.
"""
+
def __init__(self, agent):
threading.Thread.__init__(self, target=self.run)
self._agent = agent
@@ -115,12 +116,9 @@ class AgentProxyThread(threading.Thread):
# The address should be an IP address as a string? or None
self.__addr = addr
self._agent.connect()
- if (
- not isinstance(self._agent, int) and
- (
- self._agent._conn is None or
- not hasattr(self._agent._conn, 'fileno')
- )
+ if not isinstance(self._agent, int) and (
+ self._agent._conn is None
+ or not hasattr(self._agent._conn, "fileno")
):
raise AuthenticationException("Unable to connect to SSH agent")
self._communicate()
@@ -130,6 +128,7 @@ class AgentProxyThread(threading.Thread):
def _communicate(self):
import fcntl
+
oldflags = fcntl.fcntl(self.__inr, fcntl.F_GETFL)
fcntl.fcntl(self.__inr, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
while not self._exit:
@@ -162,6 +161,7 @@ class AgentLocalProxy(AgentProxyThread):
Class to be used when wanting to ask a local SSH Agent being
asked from a remote fake agent (so use a unix socket for ex.)
"""
+
def __init__(self, agent):
AgentProxyThread.__init__(self, agent)
@@ -185,6 +185,7 @@ class AgentRemoteProxy(AgentProxyThread):
"""
Class to be used when wanting to ask a remote SSH Agent
"""
+
def __init__(self, agent, chan):
AgentProxyThread.__init__(self, agent)
self.__chan = chan
@@ -205,6 +206,7 @@ class AgentClientProxy(object):
the remote fake agent and the local agent
#. Communication occurs ...
"""
+
def __init__(self, chanRemote):
self._conn = None
self.__chanR = chanRemote
@@ -218,16 +220,18 @@ class AgentClientProxy(object):
"""
Method automatically called by ``AgentProxyThread.run``.
"""
- if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
+ if ("SSH_AUTH_SOCK" in os.environ) and (sys.platform != "win32"):
conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
retry_on_signal(
- lambda: conn.connect(os.environ['SSH_AUTH_SOCK']))
+ lambda: conn.connect(os.environ["SSH_AUTH_SOCK"])
+ )
except:
# probably a dangling env var: the ssh agent is gone
return
- elif sys.platform == 'win32':
+ elif sys.platform == "win32":
import paramiko.win_pageant as win_pageant
+
if win_pageant.can_talk_to_agent():
conn = win_pageant.PageantConnection()
else:
@@ -255,12 +259,13 @@ class AgentServerProxy(AgentSSH):
:raises: `.SSHException` -- mostly if we lost the agent
"""
+
def __init__(self, t):
AgentSSH.__init__(self)
self.__t = t
- self._dir = tempfile.mkdtemp('sshproxy')
+ self._dir = tempfile.mkdtemp("sshproxy")
os.chmod(self._dir, stat.S_IRWXU)
- self._file = self._dir + '/sshproxy.ssh'
+ self._file = self._dir + "/sshproxy.ssh"
self.thread = AgentLocalProxy(self)
self.thread.start()
@@ -270,8 +275,8 @@ class AgentServerProxy(AgentSSH):
def connect(self):
conn_sock = self.__t.open_forward_agent_channel()
if conn_sock is None:
- raise SSHException('lost ssh-agent')
- conn_sock.set_name('auth-agent')
+ raise SSHException("lost ssh-agent")
+ conn_sock.set_name("auth-agent")
self._connect(conn_sock)
def close(self):
@@ -292,7 +297,7 @@ class AgentServerProxy(AgentSSH):
:return:
a dict containing the ``SSH_AUTH_SOCK`` environnement variables
"""
- return {'SSH_AUTH_SOCK': self._get_filename()}
+ return {"SSH_AUTH_SOCK": self._get_filename()}
def _get_filename(self):
return self._file
@@ -319,6 +324,7 @@ class AgentRequestHandler(object):
# the remote end.
session.exec_command("git clone https://my.git.repository/")
"""
+
def __init__(self, chanClient):
self._conn = None
self.__chanC = chanClient
@@ -350,18 +356,20 @@ class Agent(AgentSSH):
:raises: `.SSHException` --
if an SSH agent is found, but speaks an incompatible protocol
"""
+
def __init__(self):
AgentSSH.__init__(self)
- if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
+ if ("SSH_AUTH_SOCK" in os.environ) and (sys.platform != "win32"):
conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
- conn.connect(os.environ['SSH_AUTH_SOCK'])
+ conn.connect(os.environ["SSH_AUTH_SOCK"])
except:
# probably a dangling env var: the ssh agent is gone
return
- elif sys.platform == 'win32':
+ elif sys.platform == "win32":
from . import win_pageant
+
if win_pageant.can_talk_to_agent():
conn = win_pageant.PageantConnection()
else:
@@ -384,6 +392,7 @@ class AgentKey(PKey):
authenticating to a remote server (signing). Most other key operations
work as expected.
"""
+
def __init__(self, agent, blob):
self.agent = agent
self.blob = blob
@@ -407,5 +416,5 @@ class AgentKey(PKey):
msg.add_int(0)
ptype, result = self.agent._send_message(msg)
if ptype != SSH2_AGENT_SIGN_RESPONSE:
- raise SSHException('key cannot be used for signing')
+ raise SSHException("key cannot be used for signing")
return result.get_binary()
diff --git a/paramiko/auth_handler.py b/paramiko/auth_handler.py
index d2dab3d8..3f0456e5 100644
--- a/paramiko/auth_handler.py
+++ b/paramiko/auth_handler.py
@@ -24,31 +24,55 @@ import weakref
import time
from paramiko.common import (
- cMSG_SERVICE_REQUEST, cMSG_DISCONNECT, DISCONNECT_SERVICE_NOT_AVAILABLE,
- DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, cMSG_USERAUTH_REQUEST,
- cMSG_SERVICE_ACCEPT, DEBUG, AUTH_SUCCESSFUL, INFO, cMSG_USERAUTH_SUCCESS,
- cMSG_USERAUTH_FAILURE, AUTH_PARTIALLY_SUCCESSFUL,
- cMSG_USERAUTH_INFO_REQUEST, WARNING, AUTH_FAILED, cMSG_USERAUTH_PK_OK,
- cMSG_USERAUTH_INFO_RESPONSE, MSG_SERVICE_REQUEST, MSG_SERVICE_ACCEPT,
- MSG_USERAUTH_REQUEST, MSG_USERAUTH_SUCCESS, MSG_USERAUTH_FAILURE,
- MSG_USERAUTH_BANNER, MSG_USERAUTH_INFO_REQUEST, MSG_USERAUTH_INFO_RESPONSE,
- cMSG_USERAUTH_GSSAPI_RESPONSE, cMSG_USERAUTH_GSSAPI_TOKEN,
- cMSG_USERAUTH_GSSAPI_MIC, MSG_USERAUTH_GSSAPI_RESPONSE,
- MSG_USERAUTH_GSSAPI_TOKEN, MSG_USERAUTH_GSSAPI_ERROR,
- MSG_USERAUTH_GSSAPI_ERRTOK, MSG_USERAUTH_GSSAPI_MIC, MSG_NAMES,
- cMSG_USERAUTH_BANNER
+ cMSG_SERVICE_REQUEST,
+ cMSG_DISCONNECT,
+ DISCONNECT_SERVICE_NOT_AVAILABLE,
+ DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
+ cMSG_USERAUTH_REQUEST,
+ cMSG_SERVICE_ACCEPT,
+ DEBUG,
+ AUTH_SUCCESSFUL,
+ INFO,
+ cMSG_USERAUTH_SUCCESS,
+ cMSG_USERAUTH_FAILURE,
+ AUTH_PARTIALLY_SUCCESSFUL,
+ cMSG_USERAUTH_INFO_REQUEST,
+ WARNING,
+ AUTH_FAILED,
+ cMSG_USERAUTH_PK_OK,
+ cMSG_USERAUTH_INFO_RESPONSE,
+ MSG_SERVICE_REQUEST,
+ MSG_SERVICE_ACCEPT,
+ MSG_USERAUTH_REQUEST,
+ MSG_USERAUTH_SUCCESS,
+ MSG_USERAUTH_FAILURE,
+ MSG_USERAUTH_BANNER,
+ MSG_USERAUTH_INFO_REQUEST,
+ MSG_USERAUTH_INFO_RESPONSE,
+ cMSG_USERAUTH_GSSAPI_RESPONSE,
+ cMSG_USERAUTH_GSSAPI_TOKEN,
+ cMSG_USERAUTH_GSSAPI_MIC,
+ MSG_USERAUTH_GSSAPI_RESPONSE,
+ MSG_USERAUTH_GSSAPI_TOKEN,
+ MSG_USERAUTH_GSSAPI_ERROR,
+ MSG_USERAUTH_GSSAPI_ERRTOK,
+ MSG_USERAUTH_GSSAPI_MIC,
+ MSG_NAMES,
+ cMSG_USERAUTH_BANNER,
)
from paramiko.message import Message
from paramiko.py3compat import bytestring
from paramiko.ssh_exception import (
- SSHException, AuthenticationException, BadAuthenticationType,
+ SSHException,
+ AuthenticationException,
+ BadAuthenticationType,
PartialAuthentication,
)
from paramiko.server import InteractiveQuery
from paramiko.ssh_gss import GSSAuth, GSS_EXCEPTIONS
-class AuthHandler (object):
+class AuthHandler(object):
"""
Internal class to handle the mechanics of authentication.
"""
@@ -58,7 +82,7 @@ class AuthHandler (object):
self.username = None
self.authenticated = False
self.auth_event = None
- self.auth_method = ''
+ self.auth_method = ""
self.banner = None
self.password = None
self.private_key = None
@@ -71,6 +95,9 @@ class AuthHandler (object):
self.gss_host = None
self.gss_deleg_creds = True
+ def _log(self, *args):
+ return self.transport._log(*args)
+
def is_authenticated(self):
return self.authenticated
@@ -84,7 +111,7 @@ class AuthHandler (object):
self.transport.lock.acquire()
try:
self.auth_event = event
- self.auth_method = 'none'
+ self.auth_method = "none"
self.username = username
self._request_auth()
finally:
@@ -94,7 +121,7 @@ class AuthHandler (object):
self.transport.lock.acquire()
try:
self.auth_event = event
- self.auth_method = 'publickey'
+ self.auth_method = "publickey"
self.username = username
self.private_key = key
self._request_auth()
@@ -105,21 +132,21 @@ class AuthHandler (object):
self.transport.lock.acquire()
try:
self.auth_event = event
- self.auth_method = 'password'
+ self.auth_method = "password"
self.username = username
self.password = password
self._request_auth()
finally:
self.transport.lock.release()
- def auth_interactive(self, username, handler, event, submethods=''):
+ def auth_interactive(self, username, handler, event, submethods=""):
"""
response_list = handler(title, instructions, prompt_list)
"""
self.transport.lock.acquire()
try:
self.auth_event = event
- self.auth_method = 'keyboard-interactive'
+ self.auth_method = "keyboard-interactive"
self.username = username
self.interactive_handler = handler
self.submethods = submethods
@@ -131,7 +158,7 @@ class AuthHandler (object):
self.transport.lock.acquire()
try:
self.auth_event = event
- self.auth_method = 'gssapi-with-mic'
+ self.auth_method = "gssapi-with-mic"
self.username = username
self.gss_host = gss_host
self.gss_deleg_creds = gss_deleg_creds
@@ -143,7 +170,7 @@ class AuthHandler (object):
self.transport.lock.acquire()
try:
self.auth_event = event
- self.auth_method = 'gssapi-keyex'
+ self.auth_method = "gssapi-keyex"
self.username = username
self._request_auth()
finally:
@@ -158,15 +185,15 @@ class AuthHandler (object):
def _request_auth(self):
m = Message()
m.add_byte(cMSG_SERVICE_REQUEST)
- m.add_string('ssh-userauth')
+ m.add_string("ssh-userauth")
self.transport._send_message(m)
def _disconnect_service_not_available(self):
m = Message()
m.add_byte(cMSG_DISCONNECT)
m.add_int(DISCONNECT_SERVICE_NOT_AVAILABLE)
- m.add_string('Service not available')
- m.add_string('en')
+ m.add_string("Service not available")
+ m.add_string("en")
self.transport._send_message(m)
self.transport.close()
@@ -174,8 +201,8 @@ class AuthHandler (object):
m = Message()
m.add_byte(cMSG_DISCONNECT)
m.add_int(DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE)
- m.add_string('No more auth methods available')
- m.add_string('en')
+ m.add_string("No more auth methods available")
+ m.add_string("en")
self.transport._send_message(m)
self.transport.close()
@@ -185,7 +212,7 @@ class AuthHandler (object):
m.add_byte(cMSG_USERAUTH_REQUEST)
m.add_string(username)
m.add_string(service)
- m.add_string('publickey')
+ m.add_string("publickey")
m.add_boolean(True)
# Use certificate contents, if available, plain pubkey otherwise
if key.public_blob:
@@ -205,17 +232,17 @@ class AuthHandler (object):
if not self.transport.is_active():
e = self.transport.get_exception()
if (e is None) or issubclass(e.__class__, EOFError):
- e = AuthenticationException('Authentication failed.')
+ e = AuthenticationException("Authentication failed.")
raise e
if event.is_set():
break
if max_ts is not None and max_ts <= time.time():
- raise AuthenticationException('Authentication timeout.')
+ raise AuthenticationException("Authentication timeout.")
if not self.is_authenticated():
e = self.transport.get_exception()
if e is None:
- e = AuthenticationException('Authentication failed.')
+ e = AuthenticationException("Authentication failed.")
# this is horrible. Python Exception isn't yet descended from
# object, so type(e) won't work. :(
if issubclass(e.__class__, PartialAuthentication):
@@ -225,7 +252,7 @@ class AuthHandler (object):
def _parse_service_request(self, m):
service = m.get_text()
- if self.transport.server_mode and (service == 'ssh-userauth'):
+ if self.transport.server_mode and (service == "ssh-userauth"):
# accepted
m = Message()
m.add_byte(cMSG_SERVICE_ACCEPT)
@@ -244,18 +271,18 @@ class AuthHandler (object):
def _parse_service_accept(self, m):
service = m.get_text()
- if service == 'ssh-userauth':
- self.transport._log(DEBUG, 'userauth is OK')
+ if service == "ssh-userauth":
+ self._log(DEBUG, "userauth is OK")
m = Message()
m.add_byte(cMSG_USERAUTH_REQUEST)
m.add_string(self.username)
- m.add_string('ssh-connection')
+ m.add_string("ssh-connection")
m.add_string(self.auth_method)
- if self.auth_method == 'password':
+ if self.auth_method == "password":
m.add_boolean(False)
password = bytestring(self.password)
m.add_string(password)
- elif self.auth_method == 'publickey':
+ elif self.auth_method == "publickey":
m.add_boolean(True)
# Use certificate contents, if available, plain pubkey
# otherwise
@@ -266,11 +293,12 @@ class AuthHandler (object):
m.add_string(self.private_key.get_name())
m.add_string(self.private_key)
blob = self._get_session_blob(
- self.private_key, 'ssh-connection', self.username)
+ self.private_key, "ssh-connection", self.username
+ )
sig = self.private_key.sign_ssh_data(blob)
m.add_string(sig)
- elif self.auth_method == 'keyboard-interactive':
- m.add_string('')
+ elif self.auth_method == "keyboard-interactive":
+ m.add_string("")
m.add_string(self.submethods)
elif self.auth_method == "gssapi-with-mic":
sshgss = GSSAuth(self.auth_method, self.gss_deleg_creds)
@@ -289,10 +317,11 @@ class AuthHandler (object):
m = Message()
m.add_byte(cMSG_USERAUTH_GSSAPI_TOKEN)
try:
- m.add_string(sshgss.ssh_init_sec_context(
- self.gss_host,
- mech,
- self.username,))
+ m.add_string(
+ sshgss.ssh_init_sec_context(
+ self.gss_host, mech, self.username
+ )
+ )
except GSS_EXCEPTIONS as e:
return self._handle_local_gss_failure(e)
self.transport._send_message(m)
@@ -305,7 +334,8 @@ class AuthHandler (object):
self.gss_host,
mech,
self.username,
- srv_token)
+ srv_token,
+ )
except GSS_EXCEPTIONS as e:
return self._handle_local_gss_failure(e)
# After this step the GSSAPI should not return any
@@ -320,7 +350,8 @@ class AuthHandler (object):
self.transport.send_message(m)
else:
raise SSHException(
- "Received Package: %s" % MSG_NAMES[ptype])
+ "Received Package: {}".format(MSG_NAMES[ptype])
+ )
m = Message()
m.add_byte(cMSG_USERAUTH_GSSAPI_MIC)
# send the MIC to the server
@@ -336,48 +367,55 @@ class AuthHandler (object):
min_status = m.get_int()
err_msg = m.get_string()
m.get_string() # Lang tag - discarded
- raise SSHException("GSS-API Error:\nMajor Status: %s\n\
- Minor Status: %s\ \nError Message:\
- %s\n") % (str(maj_status),
- str(min_status),
- err_msg)
+ raise SSHException(
+ """GSS-API Error:
+Major Status: {}
+Minor Status: {}
+Error Message: {}
+""".format(
+ maj_status, min_status, err_msg
+ )
+ )
elif ptype == MSG_USERAUTH_FAILURE:
self._parse_userauth_failure(m)
return
else:
raise SSHException(
- "Received Package: %s" % MSG_NAMES[ptype])
+ "Received Package: {}".format(MSG_NAMES[ptype])
+ )
elif (
- self.auth_method == 'gssapi-keyex' and
- self.transport.gss_kex_used
+ self.auth_method == "gssapi-keyex"
+ and self.transport.gss_kex_used
):
kexgss = self.transport.kexgss_ctxt
kexgss.set_username(self.username)
mic_token = kexgss.ssh_get_mic(self.transport.session_id)
m.add_string(mic_token)
- elif self.auth_method == 'none':
+ elif self.auth_method == "none":
pass
else:
raise SSHException(
- 'Unknown auth method "%s"' % self.auth_method)
+ 'Unknown auth method "{}"'.format(self.auth_method)
+ )
self.transport._send_message(m)
else:
- self.transport._log(
- DEBUG,
- 'Service request "%s" accepted (?)' % service)
+ self._log(
+ DEBUG, 'Service request "{}" accepted (?)'.format(service)
+ )
def _send_auth_result(self, username, method, result):
# okay, send result
m = Message()
if result == AUTH_SUCCESSFUL:
- self.transport._log(INFO, 'Auth granted (%s).' % method)
+ self._log(INFO, "Auth granted ({}).".format(method))
m.add_byte(cMSG_USERAUTH_SUCCESS)
self.authenticated = True
else:
- self.transport._log(INFO, 'Auth rejected (%s).' % method)
+ self._log(INFO, "Auth rejected ({}).".format(method))
m.add_byte(cMSG_USERAUTH_FAILURE)
m.add_string(
- self.transport.server_object.get_allowed_auths(username))
+ self.transport.server_object.get_allowed_auths(username)
+ )
if result == AUTH_PARTIALLY_SUCCESSFUL:
m.add_boolean(True)
else:
@@ -407,7 +445,7 @@ class AuthHandler (object):
# er, uh... what?
m = Message()
m.add_byte(cMSG_USERAUTH_FAILURE)
- m.add_string('none')
+ m.add_string("none")
m.add_boolean(False)
self.transport._send_message(m)
return
@@ -417,18 +455,21 @@ class AuthHandler (object):
username = m.get_text()
service = m.get_text()
method = m.get_text()
- self.transport._log(
+ self._log(
DEBUG,
- 'Auth request (type=%s) service=%s, username=%s' % (
- method, service, username))
- if service != 'ssh-connection':
+ "Auth request (type={}) service={}, username={}".format(
+ method, service, username
+ ),
+ )
+ if service != "ssh-connection":
self._disconnect_service_not_available()
return
- if ((self.auth_username is not None) and
- (self.auth_username != username)):
- self.transport._log(
+ if (self.auth_username is not None) and (
+ self.auth_username != username
+ ):
+ self._log(
WARNING,
- 'Auth rejected because the client attempted to change username in mid-flight' # noqa
+ "Auth rejected because the client attempted to change username in mid-flight", # noqa
)
self._disconnect_no_more_auth()
return
@@ -436,13 +477,13 @@ class AuthHandler (object):
# check if GSS-API authentication is enabled
gss_auth = self.transport.server_object.enable_auth_gssapi()
- if method == 'none':
+ if method == "none":
result = self.transport.server_object.check_auth_none(username)
- elif method == 'password':
+ elif method == "password":
changereq = m.get_boolean()
password = m.get_binary()
try:
- password = password.decode('UTF-8')
+ password = password.decode("UTF-8")
except UnicodeError:
# some clients/servers expect non-utf-8 passwords!
# in this case, just return the raw byte string.
@@ -451,39 +492,39 @@ class AuthHandler (object):
# always treated as failure, since we don't support changing
# passwords, but collect the list of valid auth types from
# the callback anyway
- self.transport._log(
- DEBUG,
- 'Auth request to change passwords (rejected)')
+ self._log(DEBUG, "Auth request to change passwords (rejected)")
newpassword = m.get_binary()
try:
- newpassword = newpassword.decode('UTF-8', 'replace')
+ newpassword = newpassword.decode("UTF-8", "replace")
except UnicodeError:
pass
result = AUTH_FAILED
else:
result = self.transport.server_object.check_auth_password(
- username, password)
- elif method == 'publickey':
+ username, password
+ )
+ elif method == "publickey":
sig_attached = m.get_boolean()
keytype = m.get_text()
keyblob = m.get_binary()
try:
key = self.transport._key_info[keytype](Message(keyblob))
except SSHException as e:
- self.transport._log(
- INFO,
- 'Auth rejected: public key: %s' % str(e))
+ self._log(INFO, "Auth rejected: public key: {}".format(str(e)))
key = None
except Exception as e:
- msg = 'Auth rejected: unsupported or mangled public key ({0}: {1})' # noqa
- self.transport._log(INFO, msg.format(e.__class__.__name__, e))
+ msg = (
+ "Auth rejected: unsupported or mangled public key ({}: {})"
+ ) # noqa
+ self._log(INFO, msg.format(e.__class__.__name__, e))
key = None
if key is None:
self._disconnect_no_more_auth()
return
# first check if this key is okay... if not, we can skip the verify
result = self.transport.server_object.check_auth_publickey(
- username, key)
+ username, key
+ )
if result != AUTH_FAILED:
# key is okay, verify it
if not sig_attached:
@@ -498,14 +539,13 @@ class AuthHandler (object):
sig = Message(m.get_binary())
blob = self._get_session_blob(key, service, username)
if not key.verify_ssh_sig(blob, sig):
- self.transport._log(
- INFO,
- 'Auth rejected: invalid signature')
+ self._log(INFO, "Auth rejected: invalid signature")
result = AUTH_FAILED
- elif method == 'keyboard-interactive':
+ elif method == "keyboard-interactive":
submethods = m.get_string()
result = self.transport.server_object.check_auth_interactive(
- username, submethods)
+ username, submethods
+ )
if isinstance(result, InteractiveQuery):
# make interactive query instead of response
self._interactive_query(result)
@@ -519,17 +559,19 @@ class AuthHandler (object):
# We can't accept more than one OID, so if the SSH client sends
# more than one, disconnect.
if mechs > 1:
- self.transport._log(
+ self._log(
INFO,
- 'Disconnect: Received more than one GSS-API OID mechanism')
+ "Disconnect: Received more than one GSS-API OID mechanism",
+ )
self._disconnect_no_more_auth()
desired_mech = m.get_string()
mech_ok = sshgss.ssh_check_mech(desired_mech)
# if we don't support the mechanism, disconnect.
if not mech_ok:
- self.transport._log(
+ self._log(
INFO,
- 'Disconnect: Received an invalid GSS-API OID mechanism')
+ "Disconnect: Received an invalid GSS-API OID mechanism",
+ )
self._disconnect_no_more_auth()
# send the Kerberos V5 GSSAPI OID to the client
supported_mech = sshgss.ssh_gss_oids("server")
@@ -538,11 +580,14 @@ class AuthHandler (object):
m = Message()
m.add_byte(cMSG_USERAUTH_GSSAPI_RESPONSE)
m.add_bytes(supported_mech)
- self.transport.auth_handler = GssapiWithMicAuthHandler(self,
- sshgss)
- self.transport._expected_packet = (MSG_USERAUTH_GSSAPI_TOKEN,
- MSG_USERAUTH_REQUEST,
- MSG_SERVICE_REQUEST)
+ self.transport.auth_handler = GssapiWithMicAuthHandler(
+ self, sshgss
+ )
+ self.transport._expected_packet = (
+ MSG_USERAUTH_GSSAPI_TOKEN,
+ MSG_USERAUTH_REQUEST,
+ MSG_SERVICE_REQUEST,
+ )
self.transport._send_message(m)
return
elif method == "gssapi-keyex" and gss_auth:
@@ -553,25 +598,26 @@ class AuthHandler (object):
result = AUTH_FAILED
self._send_auth_result(username, method, result)
try:
- sshgss.ssh_check_mic(mic_token,
- self.transport.session_id,
- self.auth_username)
+ sshgss.ssh_check_mic(
+ mic_token, self.transport.session_id, self.auth_username
+ )
except Exception:
result = AUTH_FAILED
self._send_auth_result(username, method, result)
raise
result = AUTH_SUCCESSFUL
self.transport.server_object.check_auth_gssapi_keyex(
- username, result)
+ username, result
+ )
else:
result = self.transport.server_object.check_auth_none(username)
# okay, send result
self._send_auth_result(username, method, result)
def _parse_userauth_success(self, m):
- self.transport._log(
- INFO,
- 'Authentication (%s) successful!' % self.auth_method)
+ self._log(
+ INFO, "Authentication ({}) successful!".format(self.auth_method)
+ )
self.authenticated = True
self.transport._auth_trigger()
if self.auth_event is not None:
@@ -581,22 +627,24 @@ class AuthHandler (object):
authlist = m.get_list()
partial = m.get_boolean()
if partial:
- self.transport._log(INFO, 'Authentication continues...')
- self.transport._log(DEBUG, 'Methods: ' + str(authlist))
+ self._log(INFO, "Authentication continues...")
+ self._log(DEBUG, "Methods: " + str(authlist))
self.transport.saved_exception = PartialAuthentication(authlist)
elif self.auth_method not in authlist:
- self.transport._log(
- DEBUG,
- 'Authentication type (%s) not permitted.' % self.auth_method)
- self.transport._log(
- DEBUG,
- 'Allowed methods: ' + str(authlist))
+ for msg in (
+ "Authentication type ({}) not permitted.".format(
+ self.auth_method
+ ),
+ "Allowed methods: {}".format(authlist),
+ ):
+ self._log(DEBUG, msg)
self.transport.saved_exception = BadAuthenticationType(
- 'Bad authentication type', authlist)
+ "Bad authentication type", authlist
+ )
else:
- self.transport._log(
- INFO,
- 'Authentication (%s) failed.' % self.auth_method)
+ self._log(
+ INFO, "Authentication ({}) failed.".format(self.auth_method)
+ )
self.authenticated = False
self.username = None
if self.auth_event is not None:
@@ -605,12 +653,12 @@ class AuthHandler (object):
def _parse_userauth_banner(self, m):
banner = m.get_string()
self.banner = banner
- self.transport._log(INFO, 'Auth banner: %s' % banner)
+ self._log(INFO, "Auth banner: {}".format(banner))
# who cares.
def _parse_userauth_info_request(self, m):
- if self.auth_method != 'keyboard-interactive':
- raise SSHException('Illegal info request from server')
+ if self.auth_method != "keyboard-interactive":
+ raise SSHException("Illegal info request from server")
title = m.get_text()
instructions = m.get_text()
m.get_binary() # lang
@@ -619,7 +667,8 @@ class AuthHandler (object):
for i in range(prompts):
prompt_list.append((m.get_text(), m.get_boolean()))
response_list = self.interactive_handler(
- title, instructions, prompt_list)
+ title, instructions, prompt_list
+ )
m = Message()
m.add_byte(cMSG_USERAUTH_INFO_RESPONSE)
@@ -630,25 +679,26 @@ class AuthHandler (object):
def _parse_userauth_info_response(self, m):
if not self.transport.server_mode:
- raise SSHException('Illegal info response from server')
+ raise SSHException("Illegal info response from server")
n = m.get_int()
responses = []
for i in range(n):
responses.append(m.get_text())
result = self.transport.server_object.check_auth_interactive_response(
- responses)
+ responses
+ )
if isinstance(result, InteractiveQuery):
# make interactive query instead of response
self._interactive_query(result)
return
self._send_auth_result(
- self.auth_username, 'keyboard-interactive', result)
+ self.auth_username, "keyboard-interactive", result
+ )
def _handle_local_gss_failure(self, e):
self.transport.saved_exception = e
- self.transport._log(DEBUG, "GSSAPI failure: %s" % str(e))
- self.transport._log(INFO, 'Authentication (%s) failed.' %
- self.auth_method)
+ self._log(DEBUG, "GSSAPI failure: {}".format(e))
+ self._log(INFO, "Authentication ({}) failed.".format(self.auth_method))
self.authenticated = False
self.username = None
if self.auth_event is not None:
@@ -709,9 +759,9 @@ class GssapiWithMicAuthHandler(object):
# context.
sshgss = self.sshgss
try:
- token = sshgss.ssh_accept_sec_context(self.gss_host,
- client_token,
- self.auth_username)
+ token = sshgss.ssh_accept_sec_context(
+ self.gss_host, client_token, self.auth_username
+ )
except Exception as e:
self.transport.saved_exception = e
result = AUTH_FAILED
@@ -722,9 +772,11 @@ class GssapiWithMicAuthHandler(object):
m = Message()
m.add_byte(cMSG_USERAUTH_GSSAPI_TOKEN)
m.add_string(token)
- self.transport._expected_packet = (MSG_USERAUTH_GSSAPI_TOKEN,
- MSG_USERAUTH_GSSAPI_MIC,
- MSG_USERAUTH_REQUEST)
+ self.transport._expected_packet = (
+ MSG_USERAUTH_GSSAPI_TOKEN,
+ MSG_USERAUTH_GSSAPI_MIC,
+ MSG_USERAUTH_REQUEST,
+ )
self.transport._send_message(m)
def _parse_userauth_gssapi_mic(self, m):
@@ -733,9 +785,9 @@ class GssapiWithMicAuthHandler(object):
username = self.auth_username
self._restore_delegate_auth_handler()
try:
- sshgss.ssh_check_mic(mic_token,
- self.transport.session_id,
- username)
+ sshgss.ssh_check_mic(
+ mic_token, self.transport.session_id, username
+ )
except Exception as e:
self.transport.saved_exception = e
result = AUTH_FAILED
@@ -745,8 +797,9 @@ class GssapiWithMicAuthHandler(object):
# The OpenSSH server is able to create a TGT with the delegated
# client credentials, but this is not supported by GSS-API.
result = AUTH_SUCCESSFUL
- self.transport.server_object.check_auth_gssapi_with_mic(username,
- result)
+ self.transport.server_object.check_auth_gssapi_with_mic(
+ username, result
+ )
# okay, send result
self._send_auth_result(username, self.method, result)
diff --git a/paramiko/ber.py b/paramiko/ber.py
index 7725f944..92d7121e 100644
--- a/paramiko/ber.py
+++ b/paramiko/ber.py
@@ -21,7 +21,7 @@ from paramiko.py3compat import b, byte_ord, byte_chr, long
import paramiko.util as util
-class BERException (Exception):
+class BERException(Exception):
pass
@@ -41,7 +41,7 @@ class BER(object):
return self.asbytes()
def __repr__(self):
- return 'BER(\'' + repr(self.content) + '\')'
+ return "BER('" + repr(self.content) + "')"
def decode(self):
return self.decode_next()
@@ -72,12 +72,13 @@ class BER(object):
if self.idx + t > len(self.content):
return None
size = util.inflate_long(
- self.content[self.idx: self.idx + t], True)
+ self.content[self.idx : self.idx + t], True
+ )
self.idx += t
if self.idx + size > len(self.content):
# can't fit
return None
- data = self.content[self.idx: self.idx + size]
+ data = self.content[self.idx : self.idx + size]
self.idx += size
# now switch on id
if ident == 0x30:
@@ -88,8 +89,8 @@ class BER(object):
return util.inflate_long(data)
else:
# 1: boolean (00 false, otherwise true)
- raise BERException(
- 'Unknown ber encoding type %d (robey is lazy)' % ident)
+ msg = "Unknown ber encoding type {:d} (robey is lazy)"
+ raise BERException(msg.format(ident))
@staticmethod
def decode_sequence(data):
@@ -125,7 +126,9 @@ class BER(object):
elif (type(x) is list) or (type(x) is tuple):
self.encode_tlv(0x30, self.encode_sequence(x))
else:
- raise BERException('Unknown type for encoding: %s' % repr(type(x)))
+ raise BERException(
+ "Unknown type for encoding: {!r}".format(type(x))
+ )
@staticmethod
def encode_sequence(data):
diff --git a/paramiko/buffered_pipe.py b/paramiko/buffered_pipe.py
index d9f5149d..48f5aae5 100644
--- a/paramiko/buffered_pipe.py
+++ b/paramiko/buffered_pipe.py
@@ -28,14 +28,14 @@ import time
from paramiko.py3compat import PY2, b
-class PipeTimeout (IOError):
+class PipeTimeout(IOError):
"""
Indicates that a timeout was reached on a read from a `.BufferedPipe`.
"""
pass
-class BufferedPipe (object):
+class BufferedPipe(object):
"""
A buffer that obeys normal read (with timeout) & close semantics for a
file or socket, but is fed data from another thread. This is used by
@@ -46,16 +46,19 @@ class BufferedPipe (object):
self._lock = threading.Lock()
self._cv = threading.Condition(self._lock)
self._event = None
- self._buffer = array.array('B')
+ self._buffer = array.array("B")
self._closed = False
if PY2:
+
def _buffer_frombytes(self, data):
self._buffer.fromstring(data)
def _buffer_tobytes(self, limit=None):
return self._buffer[:limit].tostring()
+
else:
+
def _buffer_frombytes(self, data):
self._buffer.frombytes(data)
diff --git a/paramiko/channel.py b/paramiko/channel.py
index c6016a0e..00f86d6e 100644
--- a/paramiko/channel.py
+++ b/paramiko/channel.py
@@ -25,14 +25,22 @@ import os
import socket
import time
import threading
+
# TODO: switch as much of py3compat.py to 'six' as possible, then use six.wraps
from functools import wraps
from paramiko import util
from paramiko.common import (
- cMSG_CHANNEL_REQUEST, cMSG_CHANNEL_WINDOW_ADJUST, cMSG_CHANNEL_DATA,
- cMSG_CHANNEL_EXTENDED_DATA, DEBUG, ERROR, cMSG_CHANNEL_SUCCESS,
- cMSG_CHANNEL_FAILURE, cMSG_CHANNEL_EOF, cMSG_CHANNEL_CLOSE,
+ cMSG_CHANNEL_REQUEST,
+ cMSG_CHANNEL_WINDOW_ADJUST,
+ cMSG_CHANNEL_DATA,
+ cMSG_CHANNEL_EXTENDED_DATA,
+ DEBUG,
+ ERROR,
+ cMSG_CHANNEL_SUCCESS,
+ cMSG_CHANNEL_FAILURE,
+ cMSG_CHANNEL_EOF,
+ cMSG_CHANNEL_CLOSE,
)
from paramiko.message import Message
from paramiko.py3compat import bytes_types
@@ -51,20 +59,22 @@ def open_only(func):
`.SSHException` -- If the wrapped method is called on an unopened
`.Channel`.
"""
+
@wraps(func)
def _check(self, *args, **kwds):
if (
- self.closed or
- self.eof_received or
- self.eof_sent or
- not self.active
+ self.closed
+ or self.eof_received
+ or self.eof_sent
+ or not self.active
):
- raise SSHException('Channel is not open')
+ raise SSHException("Channel is not open")
return func(self, *args, **kwds)
+
return _check
-class Channel (ClosingContextManager):
+class Channel(ClosingContextManager):
"""
A secure tunnel across an SSH `.Transport`. A Channel is meant to behave
like a socket, and has an API that should be indistinguishable from the
@@ -117,7 +127,7 @@ class Channel (ClosingContextManager):
self.in_window_sofar = 0
self.status_event = threading.Event()
self._name = str(chanid)
- self.logger = util.get_logger('paramiko.transport')
+ self.logger = util.get_logger("paramiko.transport")
self._pipe = None
self.event = threading.Event()
self.event_ready = False
@@ -135,24 +145,30 @@ class Channel (ClosingContextManager):
"""
Return a string representation of this object, for debugging.
"""
- out = '<paramiko.Channel %d' % self.chanid
+ out = "<paramiko.Channel {}".format(self.chanid)
if self.closed:
- out += ' (closed)'
+ out += " (closed)"
elif self.active:
if self.eof_received:
- out += ' (EOF received)'
+ out += " (EOF received)"
if self.eof_sent:
- out += ' (EOF sent)'
- out += ' (open) window=%d' % self.out_window_size
+ out += " (EOF sent)"
+ out += " (open) window={}".format(self.out_window_size)
if len(self.in_buffer) > 0:
- out += ' in-buffer=%d' % (len(self.in_buffer),)
- out += ' -> ' + repr(self.transport)
- out += '>'
+ out += " in-buffer={}".format(len(self.in_buffer))
+ out += " -> " + repr(self.transport)
+ out += ">"
return out
@open_only
- def get_pty(self, term='vt100', width=80, height=24, width_pixels=0,
- height_pixels=0):
+ def get_pty(
+ self,
+ term="vt100",
+ width=80,
+ height=24,
+ width_pixels=0,
+ height_pixels=0,
+ ):
"""
Request a pseudo-terminal from the server. This is usually used right
after creating a client channel, to ask the server to provide some
@@ -174,7 +190,7 @@ class Channel (ClosingContextManager):
m = Message()
m.add_byte(cMSG_CHANNEL_REQUEST)
m.add_int(self.remote_chanid)
- m.add_string('pty-req')
+ m.add_string("pty-req")
m.add_boolean(True)
m.add_string(term)
m.add_int(width)
@@ -207,7 +223,7 @@ class Channel (ClosingContextManager):
m = Message()
m.add_byte(cMSG_CHANNEL_REQUEST)
m.add_int(self.remote_chanid)
- m.add_string('shell')
+ m.add_string("shell")
m.add_boolean(True)
self._event_pending()
self.transport._send_user_message(m)
@@ -233,7 +249,7 @@ class Channel (ClosingContextManager):
m = Message()
m.add_byte(cMSG_CHANNEL_REQUEST)
m.add_int(self.remote_chanid)
- m.add_string('exec')
+ m.add_string("exec")
m.add_boolean(True)
m.add_string(command)
self._event_pending()
@@ -259,7 +275,7 @@ class Channel (ClosingContextManager):
m = Message()
m.add_byte(cMSG_CHANNEL_REQUEST)
m.add_int(self.remote_chanid)
- m.add_string('subsystem')
+ m.add_string("subsystem")
m.add_boolean(True)
m.add_string(subsystem)
self._event_pending()
@@ -284,7 +300,7 @@ class Channel (ClosingContextManager):
m = Message()
m.add_byte(cMSG_CHANNEL_REQUEST)
m.add_int(self.remote_chanid)
- m.add_string('window-change')
+ m.add_string("window-change")
m.add_boolean(False)
m.add_int(width)
m.add_int(height)
@@ -315,7 +331,7 @@ class Channel (ClosingContextManager):
try:
self.set_environment_variable(name, value)
except SSHException as e:
- err = "Failed to set environment variable \"{0}\"."
+ err = 'Failed to set environment variable "{}".'
raise SSHException(err.format(name), e)
@open_only
@@ -339,7 +355,7 @@ class Channel (ClosingContextManager):
m = Message()
m.add_byte(cMSG_CHANNEL_REQUEST)
m.add_int(self.remote_chanid)
- m.add_string('env')
+ m.add_string("env")
m.add_boolean(False)
m.add_string(name)
m.add_string(value)
@@ -403,19 +419,19 @@ class Channel (ClosingContextManager):
m = Message()
m.add_byte(cMSG_CHANNEL_REQUEST)
m.add_int(self.remote_chanid)
- m.add_string('exit-status')
+ m.add_string("exit-status")
m.add_boolean(False)
m.add_int(status)
self.transport._send_user_message(m)
@open_only
def request_x11(
- self,
- screen_number=0,
- auth_protocol=None,
- auth_cookie=None,
- single_connection=False,
- handler=None
+ self,
+ screen_number=0,
+ auth_protocol=None,
+ auth_cookie=None,
+ single_connection=False,
+ handler=None,
):
"""
Request an x11 session on this channel. If the server allows it,
@@ -456,14 +472,14 @@ class Channel (ClosingContextManager):
:return: the auth_cookie used
"""
if auth_protocol is None:
- auth_protocol = 'MIT-MAGIC-COOKIE-1'
+ auth_protocol = "MIT-MAGIC-COOKIE-1"
if auth_cookie is None:
auth_cookie = binascii.hexlify(os.urandom(16))
m = Message()
m.add_byte(cMSG_CHANNEL_REQUEST)
m.add_int(self.remote_chanid)
- m.add_string('x11-req')
+ m.add_string("x11-req")
m.add_boolean(True)
m.add_boolean(single_connection)
m.add_string(auth_protocol)
@@ -493,7 +509,7 @@ class Channel (ClosingContextManager):
m = Message()
m.add_byte(cMSG_CHANNEL_REQUEST)
m.add_int(self.remote_chanid)
- m.add_string('auth-agent-req@openssh.com')
+ m.add_string("auth-agent-req@openssh.com")
m.add_boolean(False)
self.transport._send_user_message(m)
self.transport._set_forward_agent_handler(handler)
@@ -808,7 +824,6 @@ class Channel (ClosingContextManager):
m.add_int(1)
return self._send(s, m)
-
def sendall(self, s):
"""
Send data to the channel, without allowing partial results. Unlike
@@ -976,7 +991,7 @@ class Channel (ClosingContextManager):
# a window update
self.in_window_threshold = window_size // 10
self.in_window_sofar = 0
- self._log(DEBUG, 'Max packet in: %d bytes' % max_packet_size)
+ self._log(DEBUG, "Max packet in: {} bytes".format(max_packet_size))
def _set_remote_channel(self, chanid, window_size, max_packet_size):
self.remote_chanid = chanid
@@ -985,10 +1000,12 @@ class Channel (ClosingContextManager):
max_packet_size
)
self.active = 1
- self._log(DEBUG, 'Max packet out: %d bytes' % self.out_max_packet_size)
+ self._log(
+ DEBUG, "Max packet out: {} bytes".format(self.out_max_packet_size)
+ )
def _request_success(self, m):
- self._log(DEBUG, 'Sesch channel %d request ok' % self.chanid)
+ self._log(DEBUG, "Sesch channel {} request ok".format(self.chanid))
self.event_ready = True
self.event.set()
return
@@ -1016,8 +1033,7 @@ class Channel (ClosingContextManager):
s = m.get_binary()
if code != 1:
self._log(
- ERROR,
- 'unknown extended_data type %d; discarding' % code
+ ERROR, "unknown extended_data type {}; discarding".format(code)
)
return
if self.combine_stderr:
@@ -1030,7 +1046,7 @@ class Channel (ClosingContextManager):
self.lock.acquire()
try:
if self.ultra_debug:
- self._log(DEBUG, 'window up %d' % nbytes)
+ self._log(DEBUG, "window up {}".format(nbytes))
self.out_window_size += nbytes
self.out_buffer_cv.notifyAll()
finally:
@@ -1041,14 +1057,14 @@ class Channel (ClosingContextManager):
want_reply = m.get_boolean()
server = self.transport.server_object
ok = False
- if key == 'exit-status':
+ if key == "exit-status":
self.exit_status = m.get_int()
self.status_event.set()
ok = True
- elif key == 'xon-xoff':
+ elif key == "xon-xoff":
# ignore
ok = True
- elif key == 'pty-req':
+ elif key == "pty-req":
term = m.get_string()
width = m.get_int()
height = m.get_int()
@@ -1059,39 +1075,33 @@ class Channel (ClosingContextManager):
ok = False
else:
ok = server.check_channel_pty_request(
- self,
- term,
- width,
- height,
- pixelwidth,
- pixelheight,
- modes
+ self, term, width, height, pixelwidth, pixelheight, modes
)
- elif key == 'shell':
+ elif key == "shell":
if server is None:
ok = False
else:
ok = server.check_channel_shell_request(self)
- elif key == 'env':
+ elif key == "env":
name = m.get_string()
value = m.get_string()
if server is None:
ok = False
else:
ok = server.check_channel_env_request(self, name, value)
- elif key == 'exec':
+ elif key == "exec":
cmd = m.get_string()
if server is None:
ok = False
else:
ok = server.check_channel_exec_request(self, cmd)
- elif key == 'subsystem':
+ elif key == "subsystem":
name = m.get_text()
if server is None:
ok = False
else:
ok = server.check_channel_subsystem_request(self, name)
- elif key == 'window-change':
+ elif key == "window-change":
width = m.get_int()
height = m.get_int()
pixelwidth = m.get_int()
@@ -1100,8 +1110,9 @@ class Channel (ClosingContextManager):
ok = False
else:
ok = server.check_channel_window_change_request(
- self, width, height, pixelwidth, pixelheight)
- elif key == 'x11-req':
+ self, width, height, pixelwidth, pixelheight
+ )
+ elif key == "x11-req":
single_connection = m.get_boolean()
auth_proto = m.get_text()
auth_cookie = m.get_binary()
@@ -1114,15 +1125,15 @@ class Channel (ClosingContextManager):
single_connection,
auth_proto,
auth_cookie,
- screen_number
+ screen_number,
)
- elif key == 'auth-agent-req@openssh.com':
+ elif key == "auth-agent-req@openssh.com":
if server is None:
ok = False
else:
ok = server.check_channel_forward_agent_request(self)
else:
- self._log(DEBUG, 'Unhandled channel request "%s"' % key)
+ self._log(DEBUG, 'Unhandled channel request "{}"'.format(key))
ok = False
if want_reply:
m = Message()
@@ -1144,7 +1155,7 @@ class Channel (ClosingContextManager):
self._pipe.set_forever()
finally:
self.lock.release()
- self._log(DEBUG, 'EOF received (%s)', self._name)
+ self._log(DEBUG, "EOF received ({})".format(self._name))
def _handle_close(self, m):
self.lock.acquire()
@@ -1166,7 +1177,7 @@ class Channel (ClosingContextManager):
if self.closed:
# this doesn't seem useful, but it is the documented behavior
# of Socket
- raise socket.error('Socket is closed')
+ raise socket.error("Socket is closed")
size = self._wait_for_send_window(size)
if size == 0:
# eof or similar
@@ -1193,7 +1204,7 @@ class Channel (ClosingContextManager):
return
e = self.transport.get_exception()
if e is None:
- e = SSHException('Channel closed.')
+ e = SSHException("Channel closed.")
raise e
def _set_closed(self):
@@ -1216,7 +1227,7 @@ class Channel (ClosingContextManager):
m.add_byte(cMSG_CHANNEL_EOF)
m.add_int(self.remote_chanid)
self.eof_sent = True
- self._log(DEBUG, 'EOF sent (%s)', self._name)
+ self._log(DEBUG, "EOF sent ({})".format(self._name))
return m
def _close_internal(self):
@@ -1250,12 +1261,14 @@ class Channel (ClosingContextManager):
if self.closed or self.eof_received or not self.active:
return 0
if self.ultra_debug:
- self._log(DEBUG, 'addwindow %d' % n)
+ self._log(DEBUG, "addwindow {}".format(n))
self.in_window_sofar += n
if self.in_window_sofar <= self.in_window_threshold:
return 0
if self.ultra_debug:
- self._log(DEBUG, 'addwindow send %d' % self.in_window_sofar)
+ self._log(
+ DEBUG, "addwindow send {}".format(self.in_window_sofar)
+ )
out = self.in_window_sofar
self.in_window_sofar = 0
return out
@@ -1298,11 +1311,11 @@ class Channel (ClosingContextManager):
size = self.out_max_packet_size - 64
self.out_window_size -= size
if self.ultra_debug:
- self._log(DEBUG, 'window down to %d' % self.out_window_size)
+ self._log(DEBUG, "window down to {}".format(self.out_window_size))
return size
-class ChannelFile (BufferedFile):
+class ChannelFile(BufferedFile):
"""
A file-like wrapper around `.Channel`. A ChannelFile is created by calling
`Channel.makefile`.
@@ -1315,7 +1328,7 @@ class ChannelFile (BufferedFile):
flush the buffer.
"""
- def __init__(self, channel, mode='r', bufsize=-1):
+ def __init__(self, channel, mode="r", bufsize=-1):
self.channel = channel
BufferedFile.__init__(self)
self._set_mode(mode, bufsize)
@@ -1324,7 +1337,7 @@ class ChannelFile (BufferedFile):
"""
Returns a string representation of this object, for debugging.
"""
- return '<paramiko.ChannelFile from ' + repr(self.channel) + '>'
+ return "<paramiko.ChannelFile from " + repr(self.channel) + ">"
def _read(self, size):
return self.channel.recv(size)
@@ -1334,8 +1347,9 @@ class ChannelFile (BufferedFile):
return len(data)
-class ChannelStderrFile (ChannelFile):
- def __init__(self, channel, mode='r', bufsize=-1):
+class ChannelStderrFile(ChannelFile):
+
+ def __init__(self, channel, mode="r", bufsize=-1):
ChannelFile.__init__(self, channel, mode, bufsize)
def _read(self, size):
diff --git a/paramiko/client.py b/paramiko/client.py
index 75d295ea..2538d582 100644
--- a/paramiko/client.py
+++ b/paramiko/client.py
@@ -38,13 +38,15 @@ from paramiko.hostkeys import HostKeys
from paramiko.py3compat import string_types
from paramiko.rsakey import RSAKey
from paramiko.ssh_exception import (
- SSHException, BadHostKeyException, NoValidConnectionsError
+ SSHException,
+ BadHostKeyException,
+ NoValidConnectionsError,
)
from paramiko.transport import Transport
from paramiko.util import retry_on_signal, ClosingContextManager
-class SSHClient (ClosingContextManager):
+class SSHClient(ClosingContextManager):
"""
A high-level representation of a session with an SSH server. This class
wraps `.Transport`, `.Channel`, and `.SFTPClient` to take care of most
@@ -97,7 +99,7 @@ class SSHClient (ClosingContextManager):
"""
if filename is None:
# try the user's .ssh key file, and mask exceptions
- filename = os.path.expanduser('~/.ssh/known_hosts')
+ filename = os.path.expanduser("~/.ssh/known_hosts")
try:
self._system_host_keys.load(filename)
except IOError:
@@ -140,11 +142,14 @@ class SSHClient (ClosingContextManager):
if self._host_keys_filename is not None:
self.load_host_keys(self._host_keys_filename)
- with open(filename, 'w') as f:
+ with open(filename, "w") as f:
for hostname, keys in self._host_keys.items():
for keytype, key in keys.items():
- f.write('%s %s %s\n' % (
- hostname, keytype, key.get_base64()))
+ f.write(
+ "{} {} {}\n".format(
+ hostname, keytype, key.get_base64()
+ )
+ )
def get_host_keys(self):
"""
@@ -196,7 +201,8 @@ class SSHClient (ClosingContextManager):
"""
guess = True
addrinfos = socket.getaddrinfo(
- hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
+ hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM
+ )
for (family, socktype, proto, canonname, sockaddr) in addrinfos:
if socktype == socket.SOCK_STREAM:
yield family, sockaddr
@@ -229,6 +235,7 @@ class SSHClient (ClosingContextManager):
banner_timeout=None,
auth_timeout=None,
gss_trust_dns=True,
+ passphrase=None,
):
"""
Connect to an SSH server and authenticate to it. The server's host key
@@ -269,7 +276,10 @@ class SSHClient (ClosingContextManager):
the username to authenticate as (defaults to the current local
username)
:param str password:
- a password to use for authentication or for unlocking a private key
+ Used for password authentication; is also used for private key
+ decryption if ``passphrase`` is not given.
+ :param str passphrase:
+ Used for decrypting private keys.
:param .PKey pkey: an optional private key to use for authentication
:param str key_filename:
the filename, or list of filenames, of optional private key(s)
@@ -315,6 +325,8 @@ class SSHClient (ClosingContextManager):
``gss_deleg_creds`` and ``gss_host`` arguments.
.. versionchanged:: 2.3
Added the ``gss_trust_dns`` argument.
+ .. versionchanged:: 2.4
+ Added the ``passphrase`` argument.
"""
if not sock:
errors = {}
@@ -370,7 +382,7 @@ class SSHClient (ClosingContextManager):
if port == SSH_PORT:
server_hostkey_name = hostname
else:
- server_hostkey_name = "[%s]:%d" % (hostname, port)
+ server_hostkey_name = "[{}]:{}".format(hostname, port)
our_server_keys = None
our_server_keys = self._system_host_keys.get(server_hostkey_name)
@@ -412,8 +424,17 @@ class SSHClient (ClosingContextManager):
key_filenames = key_filename
self._auth(
- username, password, pkey, key_filenames, allow_agent,
- look_for_keys, gss_auth, gss_kex, gss_deleg_creds, t.gss_host,
+ username,
+ password,
+ pkey,
+ key_filenames,
+ allow_agent,
+ look_for_keys,
+ gss_auth,
+ gss_kex,
+ gss_deleg_creds,
+ t.gss_host,
+ passphrase,
)
def close(self):
@@ -476,13 +497,20 @@ class SSHClient (ClosingContextManager):
if environment:
chan.update_environment(environment)
chan.exec_command(command)
- stdin = chan.makefile('wb', bufsize)
- stdout = chan.makefile('r', bufsize)
- stderr = chan.makefile_stderr('r', bufsize)
+ stdin = chan.makefile("wb", bufsize)
+ stdout = chan.makefile("r", bufsize)
+ stderr = chan.makefile_stderr("r", bufsize)
return stdin, stdout, stderr
- def invoke_shell(self, term='vt100', width=80, height=24, width_pixels=0,
- height_pixels=0, environment=None):
+ def invoke_shell(
+ self,
+ term="vt100",
+ width=80,
+ height=24,
+ width_pixels=0,
+ height_pixels=0,
+ environment=None,
+ ):
"""
Start an interactive shell session on the SSH server. A new `.Channel`
is opened and connected to a pseudo-terminal using the requested
@@ -531,10 +559,10 @@ class SSHClient (ClosingContextManager):
- Otherwise, the filename is assumed to be a private key, and the
matching public cert will be loaded if it exists.
"""
- cert_suffix = '-cert.pub'
+ cert_suffix = "-cert.pub"
# Assume privkey, not cert, by default
if filename.endswith(cert_suffix):
- key_path = filename[:-len(cert_suffix)]
+ key_path = filename[: -len(cert_suffix)]
cert_path = filename
else:
key_path = filename
@@ -544,18 +572,30 @@ class SSHClient (ClosingContextManager):
# TODO: change this to 'Loading' instead of 'Trying' sometime; probably
# when #387 is released, since this is a critical log message users are
# likely testing/filtering for (bah.)
- msg = "Trying discovered key {0} in {1}".format(
- hexlify(key.get_fingerprint()), key_path,
+ msg = "Trying discovered key {} in {}".format(
+ hexlify(key.get_fingerprint()), key_path
)
self._log(DEBUG, msg)
# Attempt to load cert if it exists.
if os.path.isfile(cert_path):
key.load_certificate(cert_path)
- self._log(DEBUG, "Adding public certificate {0}".format(cert_path))
+ self._log(DEBUG, "Adding public certificate {}".format(cert_path))
return key
- def _auth(self, username, password, pkey, key_filenames, allow_agent,
- look_for_keys, gss_auth, gss_kex, gss_deleg_creds, gss_host):
+ def _auth(
+ self,
+ username,
+ password,
+ pkey,
+ key_filenames,
+ allow_agent,
+ look_for_keys,
+ gss_auth,
+ gss_kex,
+ gss_deleg_creds,
+ gss_host,
+ passphrase,
+ ):
"""
Try, in order:
@@ -565,13 +605,16 @@ class SSHClient (ClosingContextManager):
(if allowed).
- Plain username/password auth, if a password was given.
- (The password might be needed to unlock a private key, or for
- two-factor authentication [for which it is required].)
+ (The password might be needed to unlock a private key [if 'passphrase'
+ isn't also given], or for two-factor authentication [for which it is
+ required].)
"""
saved_exception = None
two_factor = False
allowed_types = set()
- two_factor_types = set(['keyboard-interactive', 'password'])
+ two_factor_types = {"keyboard-interactive", "password"}
+ if passphrase is None and password is not None:
+ passphrase = password
# If GSS-API support and GSS-PI Key Exchange was performed, we attempt
# authentication with gssapi-keyex.
@@ -589,7 +632,7 @@ class SSHClient (ClosingContextManager):
if gss_auth:
try:
return self._transport.auth_gssapi_with_mic(
- username, gss_host, gss_deleg_creds,
+ username, gss_host, gss_deleg_creds
)
except Exception as e:
saved_exception = e
@@ -598,10 +641,14 @@ class SSHClient (ClosingContextManager):
try:
self._log(
DEBUG,
- 'Trying SSH key %s' % hexlify(pkey.get_fingerprint()))
+ "Trying SSH key {}".format(
+ hexlify(pkey.get_fingerprint())
+ ),
+ )
allowed_types = set(
- self._transport.auth_publickey(username, pkey))
- two_factor = (allowed_types & two_factor_types)
+ self._transport.auth_publickey(username, pkey)
+ )
+ two_factor = allowed_types & two_factor_types
if not two_factor:
return
except SSHException as e:
@@ -612,11 +659,12 @@ class SSHClient (ClosingContextManager):
for pkey_class in (RSAKey, DSSKey, ECDSAKey, Ed25519Key):
try:
key = self._key_from_filepath(
- key_filename, pkey_class, password,
+ key_filename, pkey_class, passphrase
)
allowed_types = set(
- self._transport.auth_publickey(username, key))
- two_factor = (allowed_types & two_factor_types)
+ self._transport.auth_publickey(username, key)
+ )
+ two_factor = allowed_types & two_factor_types
if not two_factor:
return
break
@@ -629,15 +677,14 @@ class SSHClient (ClosingContextManager):
for key in self._agent.get_keys():
try:
- self._log(
- DEBUG,
- 'Trying SSH agent key %s' % hexlify(
- key.get_fingerprint()))
+ id_ = hexlify(key.get_fingerprint())
+ self._log(DEBUG, "Trying SSH agent key {}".format(id_))
# for 2-factor auth a successfully auth'd key password
# will return an allowed 2fac auth method
allowed_types = set(
- self._transport.auth_publickey(username, key))
- two_factor = (allowed_types & two_factor_types)
+ self._transport.auth_publickey(username, key)
+ )
+ two_factor = allowed_types & two_factor_types
if not two_factor:
return
break
@@ -656,13 +703,13 @@ class SSHClient (ClosingContextManager):
# ~/ssh/ is for windows
for directory in [".ssh", "ssh"]:
full_path = os.path.expanduser(
- "~/%s/id_%s" % (directory, name)
+ "~/{}/id_{}".format(directory, name)
)
if os.path.isfile(full_path):
# TODO: only do this append if below did not run
keyfiles.append((keytype, full_path))
- if os.path.isfile(full_path + '-cert.pub'):
- keyfiles.append((keytype, full_path + '-cert.pub'))
+ if os.path.isfile(full_path + "-cert.pub"):
+ keyfiles.append((keytype, full_path + "-cert.pub"))
if not look_for_keys:
keyfiles = []
@@ -670,13 +717,14 @@ class SSHClient (ClosingContextManager):
for pkey_class, filename in keyfiles:
try:
key = self._key_from_filepath(
- filename, pkey_class, password,
+ filename, pkey_class, passphrase
)
# for 2-factor auth a successfully auth'd key will result
# in ['password']
allowed_types = set(
- self._transport.auth_publickey(username, key))
- two_factor = (allowed_types & two_factor_types)
+ self._transport.auth_publickey(username, key)
+ )
+ two_factor = allowed_types & two_factor_types
if not two_factor:
return
break
@@ -699,13 +747,13 @@ class SSHClient (ClosingContextManager):
# if we got an auth-failed exception earlier, re-raise it
if saved_exception is not None:
raise saved_exception
- raise SSHException('No authentication methods available')
+ raise SSHException("No authentication methods available")
def _log(self, level, msg):
self._transport._log(level, msg)
-class MissingHostKeyPolicy (object):
+class MissingHostKeyPolicy(object):
"""
Interface for defining the policy that `.SSHClient` should use when the
SSH server's hostname is not in either the system host keys or the
@@ -726,7 +774,7 @@ class MissingHostKeyPolicy (object):
pass
-class AutoAddPolicy (MissingHostKeyPolicy):
+class AutoAddPolicy(MissingHostKeyPolicy):
"""
Policy for automatically adding the hostname and new host key to the
local `.HostKeys` object, and saving it. This is used by `.SSHClient`.
@@ -736,28 +784,41 @@ class AutoAddPolicy (MissingHostKeyPolicy):
client._host_keys.add(hostname, key.get_name(), key)
if client._host_keys_filename is not None:
client.save_host_keys(client._host_keys_filename)
- client._log(DEBUG, 'Adding %s host key for %s: %s' %
- (key.get_name(), hostname, hexlify(key.get_fingerprint())))
+ client._log(
+ DEBUG,
+ "Adding {} host key for {}: {}".format(
+ key.get_name(), hostname, hexlify(key.get_fingerprint())
+ ),
+ )
-class RejectPolicy (MissingHostKeyPolicy):
+class RejectPolicy(MissingHostKeyPolicy):
"""
Policy for automatically rejecting the unknown hostname & key. This is
used by `.SSHClient`.
"""
def missing_host_key(self, client, hostname, key):
- client._log(DEBUG, 'Rejecting %s host key for %s: %s' %
- (key.get_name(), hostname, hexlify(key.get_fingerprint())))
- raise SSHException('Server %r not found in known_hosts' % hostname)
+ client._log(
+ DEBUG,
+ "Rejecting {} host key for {}: {}".format(
+ key.get_name(), hostname, hexlify(key.get_fingerprint())
+ ),
+ )
+ raise SSHException(
+ "Server {!r} not found in known_hosts".format(hostname)
+ )
-class WarningPolicy (MissingHostKeyPolicy):
+class WarningPolicy(MissingHostKeyPolicy):
"""
Policy for logging a Python-style warning for an unknown host key, but
accepting it. This is used by `.SSHClient`.
"""
+
def missing_host_key(self, client, hostname, key):
- warnings.warn('Unknown %s host key for %s: %s' %
- (key.get_name(), hostname, hexlify(
- key.get_fingerprint())))
+ warnings.warn(
+ "Unknown {} host key for {}: {}".format(
+ key.get_name(), hostname, hexlify(key.get_fingerprint())
+ )
+ )
diff --git a/paramiko/common.py b/paramiko/common.py
index 11c4121d..87d3dcf6 100644
--- a/paramiko/common.py
+++ b/paramiko/common.py
@@ -22,22 +22,45 @@ Common constants and global variables.
import logging
from paramiko.py3compat import byte_chr, PY2, bytes_types, text_type, long
-MSG_DISCONNECT, MSG_IGNORE, MSG_UNIMPLEMENTED, MSG_DEBUG, \
- MSG_SERVICE_REQUEST, MSG_SERVICE_ACCEPT = range(1, 7)
-MSG_KEXINIT, MSG_NEWKEYS = range(20, 22)
-MSG_USERAUTH_REQUEST, MSG_USERAUTH_FAILURE, MSG_USERAUTH_SUCCESS, \
- MSG_USERAUTH_BANNER = range(50, 54)
+(
+ MSG_DISCONNECT,
+ MSG_IGNORE,
+ MSG_UNIMPLEMENTED,
+ MSG_DEBUG,
+ MSG_SERVICE_REQUEST,
+ MSG_SERVICE_ACCEPT,
+) = range(1, 7)
+(MSG_KEXINIT, MSG_NEWKEYS) = range(20, 22)
+(
+ MSG_USERAUTH_REQUEST,
+ MSG_USERAUTH_FAILURE,
+ MSG_USERAUTH_SUCCESS,
+ MSG_USERAUTH_BANNER,
+) = range(50, 54)
MSG_USERAUTH_PK_OK = 60
-MSG_USERAUTH_INFO_REQUEST, MSG_USERAUTH_INFO_RESPONSE = range(60, 62)
-MSG_USERAUTH_GSSAPI_RESPONSE, MSG_USERAUTH_GSSAPI_TOKEN = range(60, 62)
-MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, MSG_USERAUTH_GSSAPI_ERROR,\
- MSG_USERAUTH_GSSAPI_ERRTOK, MSG_USERAUTH_GSSAPI_MIC = range(63, 67)
+(MSG_USERAUTH_INFO_REQUEST, MSG_USERAUTH_INFO_RESPONSE) = range(60, 62)
+(MSG_USERAUTH_GSSAPI_RESPONSE, MSG_USERAUTH_GSSAPI_TOKEN) = range(60, 62)
+(
+ MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
+ MSG_USERAUTH_GSSAPI_ERROR,
+ MSG_USERAUTH_GSSAPI_ERRTOK,
+ MSG_USERAUTH_GSSAPI_MIC,
+) = range(63, 67)
HIGHEST_USERAUTH_MESSAGE_ID = 79
-MSG_GLOBAL_REQUEST, MSG_REQUEST_SUCCESS, MSG_REQUEST_FAILURE = range(80, 83)
-MSG_CHANNEL_OPEN, MSG_CHANNEL_OPEN_SUCCESS, MSG_CHANNEL_OPEN_FAILURE, \
- MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_DATA, MSG_CHANNEL_EXTENDED_DATA, \
- MSG_CHANNEL_EOF, MSG_CHANNEL_CLOSE, MSG_CHANNEL_REQUEST, \
- MSG_CHANNEL_SUCCESS, MSG_CHANNEL_FAILURE = range(90, 101)
+(MSG_GLOBAL_REQUEST, MSG_REQUEST_SUCCESS, MSG_REQUEST_FAILURE) = range(80, 83)
+(
+ MSG_CHANNEL_OPEN,
+ MSG_CHANNEL_OPEN_SUCCESS,
+ MSG_CHANNEL_OPEN_FAILURE,
+ MSG_CHANNEL_WINDOW_ADJUST,
+ MSG_CHANNEL_DATA,
+ MSG_CHANNEL_EXTENDED_DATA,
+ MSG_CHANNEL_EOF,
+ MSG_CHANNEL_CLOSE,
+ MSG_CHANNEL_REQUEST,
+ MSG_CHANNEL_SUCCESS,
+ MSG_CHANNEL_FAILURE,
+) = range(90, 101)
cMSG_DISCONNECT = byte_chr(MSG_DISCONNECT)
cMSG_IGNORE = byte_chr(MSG_IGNORE)
@@ -56,8 +79,9 @@ cMSG_USERAUTH_INFO_REQUEST = byte_chr(MSG_USERAUTH_INFO_REQUEST)
cMSG_USERAUTH_INFO_RESPONSE = byte_chr(MSG_USERAUTH_INFO_RESPONSE)
cMSG_USERAUTH_GSSAPI_RESPONSE = byte_chr(MSG_USERAUTH_GSSAPI_RESPONSE)
cMSG_USERAUTH_GSSAPI_TOKEN = byte_chr(MSG_USERAUTH_GSSAPI_TOKEN)
-cMSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE = \
- byte_chr(MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE)
+cMSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE = byte_chr(
+ MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE
+)
cMSG_USERAUTH_GSSAPI_ERROR = byte_chr(MSG_USERAUTH_GSSAPI_ERROR)
cMSG_USERAUTH_GSSAPI_ERRTOK = byte_chr(MSG_USERAUTH_GSSAPI_ERRTOK)
cMSG_USERAUTH_GSSAPI_MIC = byte_chr(MSG_USERAUTH_GSSAPI_MIC)
@@ -78,47 +102,47 @@ cMSG_CHANNEL_FAILURE = byte_chr(MSG_CHANNEL_FAILURE)
# for debugging:
MSG_NAMES = {
- MSG_DISCONNECT: 'disconnect',
- MSG_IGNORE: 'ignore',
- MSG_UNIMPLEMENTED: 'unimplemented',
- MSG_DEBUG: 'debug',
- MSG_SERVICE_REQUEST: 'service-request',
- MSG_SERVICE_ACCEPT: 'service-accept',
- MSG_KEXINIT: 'kexinit',
- MSG_NEWKEYS: 'newkeys',
- 30: 'kex30',
- 31: 'kex31',
- 32: 'kex32',
- 33: 'kex33',
- 34: 'kex34',
- 40: 'kex40',
- 41: 'kex41',
- MSG_USERAUTH_REQUEST: 'userauth-request',
- MSG_USERAUTH_FAILURE: 'userauth-failure',
- MSG_USERAUTH_SUCCESS: 'userauth-success',
- MSG_USERAUTH_BANNER: 'userauth--banner',
- MSG_USERAUTH_PK_OK: 'userauth-60(pk-ok/info-request)',
- MSG_USERAUTH_INFO_RESPONSE: 'userauth-info-response',
- MSG_GLOBAL_REQUEST: 'global-request',
- MSG_REQUEST_SUCCESS: 'request-success',
- MSG_REQUEST_FAILURE: 'request-failure',
- MSG_CHANNEL_OPEN: 'channel-open',
- MSG_CHANNEL_OPEN_SUCCESS: 'channel-open-success',
- MSG_CHANNEL_OPEN_FAILURE: 'channel-open-failure',
- MSG_CHANNEL_WINDOW_ADJUST: 'channel-window-adjust',
- MSG_CHANNEL_DATA: 'channel-data',
- MSG_CHANNEL_EXTENDED_DATA: 'channel-extended-data',
- MSG_CHANNEL_EOF: 'channel-eof',
- MSG_CHANNEL_CLOSE: 'channel-close',
- MSG_CHANNEL_REQUEST: 'channel-request',
- MSG_CHANNEL_SUCCESS: 'channel-success',
- MSG_CHANNEL_FAILURE: 'channel-failure',
- MSG_USERAUTH_GSSAPI_RESPONSE: 'userauth-gssapi-response',
- MSG_USERAUTH_GSSAPI_TOKEN: 'userauth-gssapi-token',
- MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE: 'userauth-gssapi-exchange-complete',
- MSG_USERAUTH_GSSAPI_ERROR: 'userauth-gssapi-error',
- MSG_USERAUTH_GSSAPI_ERRTOK: 'userauth-gssapi-error-token',
- MSG_USERAUTH_GSSAPI_MIC: 'userauth-gssapi-mic'
+ MSG_DISCONNECT: "disconnect",
+ MSG_IGNORE: "ignore",
+ MSG_UNIMPLEMENTED: "unimplemented",
+ MSG_DEBUG: "debug",
+ MSG_SERVICE_REQUEST: "service-request",
+ MSG_SERVICE_ACCEPT: "service-accept",
+ MSG_KEXINIT: "kexinit",
+ MSG_NEWKEYS: "newkeys",
+ 30: "kex30",
+ 31: "kex31",
+ 32: "kex32",
+ 33: "kex33",
+ 34: "kex34",
+ 40: "kex40",
+ 41: "kex41",
+ MSG_USERAUTH_REQUEST: "userauth-request",
+ MSG_USERAUTH_FAILURE: "userauth-failure",
+ MSG_USERAUTH_SUCCESS: "userauth-success",
+ MSG_USERAUTH_BANNER: "userauth--banner",
+ MSG_USERAUTH_PK_OK: "userauth-60(pk-ok/info-request)",
+ MSG_USERAUTH_INFO_RESPONSE: "userauth-info-response",
+ MSG_GLOBAL_REQUEST: "global-request",
+ MSG_REQUEST_SUCCESS: "request-success",
+ MSG_REQUEST_FAILURE: "request-failure",
+ MSG_CHANNEL_OPEN: "channel-open",
+ MSG_CHANNEL_OPEN_SUCCESS: "channel-open-success",
+ MSG_CHANNEL_OPEN_FAILURE: "channel-open-failure",
+ MSG_CHANNEL_WINDOW_ADJUST: "channel-window-adjust",
+ MSG_CHANNEL_DATA: "channel-data",
+ MSG_CHANNEL_EXTENDED_DATA: "channel-extended-data",
+ MSG_CHANNEL_EOF: "channel-eof",
+ MSG_CHANNEL_CLOSE: "channel-close",
+ MSG_CHANNEL_REQUEST: "channel-request",
+ MSG_CHANNEL_SUCCESS: "channel-success",
+ MSG_CHANNEL_FAILURE: "channel-failure",
+ MSG_USERAUTH_GSSAPI_RESPONSE: "userauth-gssapi-response",
+ MSG_USERAUTH_GSSAPI_TOKEN: "userauth-gssapi-token",
+ MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE: "userauth-gssapi-exchange-complete",
+ MSG_USERAUTH_GSSAPI_ERROR: "userauth-gssapi-error",
+ MSG_USERAUTH_GSSAPI_ERRTOK: "userauth-gssapi-error-token",
+ MSG_USERAUTH_GSSAPI_MIC: "userauth-gssapi-mic",
}
@@ -127,23 +151,28 @@ AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED = range(3)
# channel request failed reasons:
-(OPEN_SUCCEEDED,
- OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED,
- OPEN_FAILED_CONNECT_FAILED,
- OPEN_FAILED_UNKNOWN_CHANNEL_TYPE,
- OPEN_FAILED_RESOURCE_SHORTAGE) = range(0, 5)
+(
+ OPEN_SUCCEEDED,
+ OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED,
+ OPEN_FAILED_CONNECT_FAILED,
+ OPEN_FAILED_UNKNOWN_CHANNEL_TYPE,
+ OPEN_FAILED_RESOURCE_SHORTAGE,
+) = range(0, 5)
CONNECTION_FAILED_CODE = {
- 1: 'Administratively prohibited',
- 2: 'Connect failed',
- 3: 'Unknown channel type',
- 4: 'Resource shortage'
+ 1: "Administratively prohibited",
+ 2: "Connect failed",
+ 3: "Unknown channel type",
+ 4: "Resource shortage",
}
-DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \
- DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 7, 13, 14
+(
+ DISCONNECT_SERVICE_NOT_AVAILABLE,
+ DISCONNECT_AUTH_CANCELLED_BY_USER,
+ DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
+) = (7, 13, 14)
zero_byte = byte_chr(0)
one_byte = byte_chr(1)
diff --git a/paramiko/compress.py b/paramiko/compress.py
index 5073109c..cea2b308 100644
--- a/paramiko/compress.py
+++ b/paramiko/compress.py
@@ -23,7 +23,8 @@ Compression implementations for a Transport.
import zlib
-class ZlibCompressor (object):
+class ZlibCompressor(object):
+
def __init__(self):
# Use the default level of zlib compression
self.z = zlib.compressobj()
@@ -32,7 +33,8 @@ class ZlibCompressor (object):
return self.z.compress(data) + self.z.flush(zlib.Z_FULL_FLUSH)
-class ZlibDecompressor (object):
+class ZlibDecompressor(object):
+
def __init__(self):
self.z = zlib.decompressobj()
diff --git a/paramiko/config.py b/paramiko/config.py
index 073abb36..21c9dab8 100644
--- a/paramiko/config.py
+++ b/paramiko/config.py
@@ -30,7 +30,7 @@ import socket
SSH_PORT = 22
-class SSHConfig (object):
+class SSHConfig(object):
"""
Representation of config information as stored in the format used by
OpenSSH. Queries can be made via `lookup`. The format is described in
@@ -41,7 +41,7 @@ class SSHConfig (object):
.. versionadded:: 1.6
"""
- SETTINGS_REGEX = re.compile(r'(\w+)(?:\s*=\s*|\s+)(.+)')
+ SETTINGS_REGEX = re.compile(r"(\w+)(?:\s*=\s*|\s+)(.+)")
def __init__(self):
"""
@@ -55,31 +55,28 @@ class SSHConfig (object):
:param file_obj: a file-like object to read the config file from
"""
- host = {"host": ['*'], "config": {}}
+ host = {"host": ["*"], "config": {}}
for line in file_obj:
# Strip any leading or trailing whitespace from the line.
# Refer to https://github.com/paramiko/paramiko/issues/499
line = line.strip()
- if not line or line.startswith('#'):
+ if not line or line.startswith("#"):
continue
match = re.match(self.SETTINGS_REGEX, line)
if not match:
- raise Exception("Unparsable line %s" % line)
+ raise Exception("Unparsable line {}".format(line))
key = match.group(1).lower()
value = match.group(2)
- if key == 'host':
+ if key == "host":
self._config.append(host)
- host = {
- 'host': self._get_hosts(value),
- 'config': {}
- }
- elif key == 'proxycommand' and value.lower() == 'none':
+ host = {"host": self._get_hosts(value), "config": {}}
+ elif key == "proxycommand" and value.lower() == "none":
# Store 'none' as None; prior to 3.x, it will get stripped out
# at the end (for compatibility with issue #415). After 3.x, it
# will simply not get stripped, leaving a nice explicit marker.
- host['config'][key] = None
+ host["config"][key] = None
else:
if value.startswith('"') and value.endswith('"'):
value = value[1:-1]
@@ -87,13 +84,13 @@ class SSHConfig (object):
# identityfile, localforward, remoteforward keys are special
# cases, since they are allowed to be specified multiple times
# and they should be tried in order of specification.
- if key in ['identityfile', 'localforward', 'remoteforward']:
- if key in host['config']:
- host['config'][key].append(value)
+ if key in ["identityfile", "localforward", "remoteforward"]:
+ if key in host["config"]:
+ host["config"][key].append(value)
else:
- host['config'][key] = [value]
- elif key not in host['config']:
- host['config'][key] = value
+ host["config"][key] = [value]
+ elif key not in host["config"]:
+ host["config"][key] = value
self._config.append(host)
def lookup(self, hostname):
@@ -117,25 +114,26 @@ class SSHConfig (object):
:param str hostname: the hostname to lookup
"""
matches = [
- config for config in self._config
- if self._allowed(config['host'], hostname)
+ config
+ for config in self._config
+ if self._allowed(config["host"], hostname)
]
ret = {}
for match in matches:
- for key, value in match['config'].items():
+ for key, value in match["config"].items():
if key not in ret:
# Create a copy of the original value,
# else it will reference the original list
# in self._config and update that value too
# when the extend() is being called.
ret[key] = value[:] if value is not None else value
- elif key == 'identityfile':
+ elif key == "identityfile":
ret[key].extend(value)
ret = self._expand_variables(ret, hostname)
# TODO: remove in 3.x re #670
- if 'proxycommand' in ret and ret['proxycommand'] is None:
- del ret['proxycommand']
+ if "proxycommand" in ret and ret["proxycommand"] is None:
+ del ret["proxycommand"]
return ret
def get_hostnames(self):
@@ -145,13 +143,13 @@ class SSHConfig (object):
"""
hosts = set()
for entry in self._config:
- hosts.update(entry['host'])
+ hosts.update(entry["host"])
return hosts
def _allowed(self, hosts, hostname):
match = False
for host in hosts:
- if host.startswith('!') and fnmatch.fnmatch(hostname, host[1:]):
+ if host.startswith("!") and fnmatch.fnmatch(hostname, host[1:]):
return False
elif fnmatch.fnmatch(hostname, host):
match = True
@@ -169,52 +167,50 @@ class SSHConfig (object):
:param str hostname: the hostname that the config belongs to
"""
- if 'hostname' in config:
- config['hostname'] = config['hostname'].replace('%h', hostname)
+ if "hostname" in config:
+ config["hostname"] = config["hostname"].replace("%h", hostname)
else:
- config['hostname'] = hostname
+ config["hostname"] = hostname
- if 'port' in config:
- port = config['port']
+ if "port" in config:
+ port = config["port"]
else:
port = SSH_PORT
- user = os.getenv('USER')
- if 'user' in config:
- remoteuser = config['user']
+ user = os.getenv("USER")
+ if "user" in config:
+ remoteuser = config["user"]
else:
remoteuser = user
- host = socket.gethostname().split('.')[0]
+ host = socket.gethostname().split(".")[0]
fqdn = LazyFqdn(config, host)
- homedir = os.path.expanduser('~')
- replacements = {'controlpath':
- [
- ('%h', config['hostname']),
- ('%l', fqdn),
- ('%L', host),
- ('%n', hostname),
- ('%p', port),
- ('%r', remoteuser),
- ('%u', user)
- ],
- 'identityfile':
- [
- ('~', homedir),
- ('%d', homedir),
- ('%h', config['hostname']),
- ('%l', fqdn),
- ('%u', user),
- ('%r', remoteuser)
- ],
- 'proxycommand':
- [
- ('~', homedir),
- ('%h', config['hostname']),
- ('%p', port),
- ('%r', remoteuser)
- ]
- }
+ homedir = os.path.expanduser("~")
+ replacements = {
+ "controlpath": [
+ ("%h", config["hostname"]),
+ ("%l", fqdn),
+ ("%L", host),
+ ("%n", hostname),
+ ("%p", port),
+ ("%r", remoteuser),
+ ("%u", user),
+ ],
+ "identityfile": [
+ ("~", homedir),
+ ("%d", homedir),
+ ("%h", config["hostname"]),
+ ("%l", fqdn),
+ ("%u", user),
+ ("%r", remoteuser),
+ ],
+ "proxycommand": [
+ ("~", homedir),
+ ("%h", config["hostname"]),
+ ("%p", port),
+ ("%r", remoteuser),
+ ],
+ }
for k in config:
if config[k] is None:
@@ -239,7 +235,7 @@ class SSHConfig (object):
try:
return shlex.split(host)
except ValueError:
- raise Exception("Unparsable host %s" % host)
+ raise Exception("Unparsable host {}".format(host))
class LazyFqdn(object):
@@ -265,11 +261,11 @@ class LazyFqdn(object):
# Handle specific option
fqdn = None
- address_family = self.config.get('addressfamily', 'any').lower()
- if address_family != 'any':
+ address_family = self.config.get("addressfamily", "any").lower()
+ if address_family != "any":
try:
family = socket.AF_INET6
- if address_family == 'inet':
+ if address_family == "inet":
socket.AF_INET
results = socket.getaddrinfo(
self.host,
@@ -277,11 +273,11 @@ class LazyFqdn(object):
family,
socket.SOCK_DGRAM,
socket.IPPROTO_IP,
- socket.AI_CANONNAME
+ socket.AI_CANONNAME,
)
for res in results:
af, socktype, proto, canonname, sa = res
- if canonname and '.' in canonname:
+ if canonname and "." in canonname:
fqdn = canonname
break
# giaerror -> socket.getaddrinfo() can't resolve self.host
diff --git a/paramiko/dsskey.py b/paramiko/dsskey.py
index ac1d4c2e..ec358ee2 100644
--- a/paramiko/dsskey.py
+++ b/paramiko/dsskey.py
@@ -25,7 +25,8 @@ from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dsa
from cryptography.hazmat.primitives.asymmetric.utils import (
- decode_dss_signature, encode_dss_signature
+ decode_dss_signature,
+ encode_dss_signature,
)
from paramiko import util
@@ -42,8 +43,15 @@ class DSSKey(PKey):
data.
"""
- def __init__(self, msg=None, data=None, filename=None, password=None,
- vals=None, file_obj=None):
+ def __init__(
+ self,
+ msg=None,
+ data=None,
+ filename=None,
+ password=None,
+ vals=None,
+ file_obj=None,
+ ):
self.p = None
self.q = None
self.g = None
@@ -63,8 +71,8 @@ class DSSKey(PKey):
else:
self._check_type_and_load_cert(
msg=msg,
- key_type='ssh-dss',
- cert_type='ssh-dss-cert-v01@openssh.com',
+ key_type="ssh-dss",
+ cert_type="ssh-dss-cert-v01@openssh.com",
)
self.p = msg.get_mpint()
self.q = msg.get_mpint()
@@ -74,7 +82,7 @@ class DSSKey(PKey):
def asbytes(self):
m = Message()
- m.add_string('ssh-dss')
+ m.add_string("ssh-dss")
m.add_mpint(self.p)
m.add_mpint(self.q)
m.add_mpint(self.g)
@@ -88,7 +96,7 @@ class DSSKey(PKey):
return hash((self.get_name(), self.p, self.q, self.g, self.y))
def get_name(self):
- return 'ssh-dss'
+ return "ssh-dss"
def get_bits(self):
return self.size
@@ -102,17 +110,15 @@ class DSSKey(PKey):
public_numbers=dsa.DSAPublicNumbers(
y=self.y,
parameter_numbers=dsa.DSAParameterNumbers(
- p=self.p,
- q=self.q,
- g=self.g
- )
- )
+ p=self.p, q=self.q, g=self.g
+ ),
+ ),
).private_key(backend=default_backend())
sig = key.sign(data, hashes.SHA1())
r, s = decode_dss_signature(sig)
m = Message()
- m.add_string('ssh-dss')
+ m.add_string("ssh-dss")
# apparently, in rare cases, r or s may be shorter than 20 bytes!
rstr = util.deflate_long(r, 0)
sstr = util.deflate_long(s, 0)
@@ -129,7 +135,7 @@ class DSSKey(PKey):
sig = msg.asbytes()
else:
kind = msg.get_text()
- if kind != 'ssh-dss':
+ if kind != "ssh-dss":
return 0
sig = msg.get_binary()
@@ -142,10 +148,8 @@ class DSSKey(PKey):
key = dsa.DSAPublicNumbers(
y=self.y,
parameter_numbers=dsa.DSAParameterNumbers(
- p=self.p,
- q=self.q,
- g=self.g
- )
+ p=self.p, q=self.q, g=self.g
+ ),
).public_key(backend=default_backend())
try:
key.verify(signature, data, hashes.SHA1())
@@ -160,18 +164,16 @@ class DSSKey(PKey):
public_numbers=dsa.DSAPublicNumbers(
y=self.y,
parameter_numbers=dsa.DSAParameterNumbers(
- p=self.p,
- q=self.q,
- g=self.g
- )
- )
+ p=self.p, q=self.q, g=self.g
+ ),
+ ),
).private_key(backend=default_backend())
self._write_private_key_file(
filename,
key,
serialization.PrivateFormat.TraditionalOpenSSL,
- password=password
+ password=password,
)
def write_private_key(self, file_obj, password=None):
@@ -180,18 +182,16 @@ class DSSKey(PKey):
public_numbers=dsa.DSAPublicNumbers(
y=self.y,
parameter_numbers=dsa.DSAParameterNumbers(
- p=self.p,
- q=self.q,
- g=self.g
- )
- )
+ p=self.p, q=self.q, g=self.g
+ ),
+ ),
).private_key(backend=default_backend())
self._write_private_key(
file_obj,
key,
serialization.PrivateFormat.TraditionalOpenSSL,
- password=password
+ password=password,
)
@staticmethod
@@ -207,23 +207,25 @@ class DSSKey(PKey):
numbers = dsa.generate_private_key(
bits, backend=default_backend()
).private_numbers()
- key = DSSKey(vals=(
- numbers.public_numbers.parameter_numbers.p,
- numbers.public_numbers.parameter_numbers.q,
- numbers.public_numbers.parameter_numbers.g,
- numbers.public_numbers.y
- ))
+ key = DSSKey(
+ vals=(
+ numbers.public_numbers.parameter_numbers.p,
+ numbers.public_numbers.parameter_numbers.q,
+ numbers.public_numbers.parameter_numbers.g,
+ numbers.public_numbers.y,
+ )
+ )
key.x = numbers.x
return key
# ...internals...
def _from_private_key_file(self, filename, password):
- data = self._read_private_key_file('DSA', filename, password)
+ data = self._read_private_key_file("DSA", filename, password)
self._decode_key(data)
def _from_private_key(self, file_obj, password):
- data = self._read_private_key('DSA', file_obj, password)
+ data = self._read_private_key("DSA", file_obj, password)
self._decode_key(data)
def _decode_key(self, data):
@@ -232,14 +234,11 @@ class DSSKey(PKey):
try:
keylist = BER(data).decode()
except BERException as e:
- raise SSHException('Unable to parse key file: ' + str(e))
- if (
- type(keylist) is not list or
- len(keylist) < 6 or
- keylist[0] != 0
- ):
+ raise SSHException("Unable to parse key file: " + str(e))
+ if type(keylist) is not list or len(keylist) < 6 or keylist[0] != 0:
raise SSHException(
- 'not a valid DSA private key file (bad ber encoding)')
+ "not a valid DSA private key file (bad ber encoding)"
+ )
self.p = keylist[1]
self.q = keylist[2]
self.g = keylist[3]
diff --git a/paramiko/ecdsakey.py b/paramiko/ecdsakey.py
index 1bb5676f..b73a969e 100644
--- a/paramiko/ecdsakey.py
+++ b/paramiko/ecdsakey.py
@@ -25,7 +25,8 @@ from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import (
- decode_dss_signature, encode_dss_signature
+ decode_dss_signature,
+ encode_dss_signature,
)
from paramiko.common import four_byte
@@ -43,6 +44,7 @@ class _ECDSACurve(object):
the proper hash function. Also grabs the proper curve from the 'ecdsa'
package.
"""
+
def __init__(self, curve_class, nist_name):
self.nist_name = nist_name
self.key_length = curve_class.key_size
@@ -67,6 +69,7 @@ class _ECDSACurveSet(object):
format identifier. The two ways in which ECDSAKey needs to be able to look
up curves.
"""
+
def __init__(self, ecdsa_curves):
self.ecdsa_curves = ecdsa_curves
@@ -95,14 +98,24 @@ class ECDSAKey(PKey):
data.
"""
- _ECDSA_CURVES = _ECDSACurveSet([
- _ECDSACurve(ec.SECP256R1, 'nistp256'),
- _ECDSACurve(ec.SECP384R1, 'nistp384'),
- _ECDSACurve(ec.SECP521R1, 'nistp521'),
- ])
-
- def __init__(self, msg=None, data=None, filename=None, password=None,
- vals=None, file_obj=None, validate_point=True):
+ _ECDSA_CURVES = _ECDSACurveSet(
+ [
+ _ECDSACurve(ec.SECP256R1, "nistp256"),
+ _ECDSACurve(ec.SECP384R1, "nistp384"),
+ _ECDSACurve(ec.SECP521R1, "nistp521"),
+ ]
+ )
+
+ def __init__(
+ self,
+ msg=None,
+ data=None,
+ filename=None,
+ password=None,
+ vals=None,
+ file_obj=None,
+ validate_point=True,
+ ):
self.verifying_key = None
self.signing_key = None
self.public_blob = None
@@ -126,25 +139,24 @@ class ECDSAKey(PKey):
# identifier, so strip out any cert business. (NOTE: could push
# that into _ECDSACurveSet.get_by_key_format_identifier(), but it
# feels more correct to do it here?)
- suffix = '-cert-v01@openssh.com'
+ suffix = "-cert-v01@openssh.com"
if key_type.endswith(suffix):
- key_type = key_type[:-len(suffix)]
+ key_type = key_type[: -len(suffix)]
self.ecdsa_curve = self._ECDSA_CURVES.get_by_key_format_identifier(
key_type
)
key_types = self._ECDSA_CURVES.get_key_format_identifier_list()
cert_types = [
- '{0}-cert-v01@openssh.com'.format(x)
- for x in key_types
+ "{}-cert-v01@openssh.com".format(x) for x in key_types
]
self._check_type_and_load_cert(
- msg=msg,
- key_type=key_types,
- cert_type=cert_types,
+ msg=msg, key_type=key_types, cert_type=cert_types
)
curvename = msg.get_text()
if curvename != self.ecdsa_curve.nist_name:
- raise SSHException("Can't handle curve of type %s" % curvename)
+ raise SSHException(
+ "Can't handle curve of type {}".format(curvename)
+ )
pointinfo = msg.get_binary()
try:
@@ -170,10 +182,10 @@ class ECDSAKey(PKey):
key_size_bytes = (key.curve.key_size + 7) // 8
x_bytes = deflate_long(numbers.x, add_sign_padding=False)
- x_bytes = b'\x00' * (key_size_bytes - len(x_bytes)) + x_bytes
+ x_bytes = b"\x00" * (key_size_bytes - len(x_bytes)) + x_bytes
y_bytes = deflate_long(numbers.y, add_sign_padding=False)
- y_bytes = b'\x00' * (key_size_bytes - len(y_bytes)) + y_bytes
+ y_bytes = b"\x00" * (key_size_bytes - len(y_bytes)) + y_bytes
point_str = four_byte + x_bytes + y_bytes
m.add_string(point_str)
@@ -183,8 +195,13 @@ class ECDSAKey(PKey):
return self.asbytes()
def __hash__(self):
- return hash((self.get_name(), self.verifying_key.public_numbers().x,
- self.verifying_key.public_numbers().y))
+ return hash(
+ (
+ self.get_name(),
+ self.verifying_key.public_numbers().x,
+ self.verifying_key.public_numbers().y,
+ )
+ )
def get_name(self):
return self.ecdsa_curve.key_format_identifier
@@ -226,7 +243,7 @@ class ECDSAKey(PKey):
filename,
self.signing_key,
serialization.PrivateFormat.TraditionalOpenSSL,
- password=password
+ password=password,
)
def write_private_key(self, file_obj, password=None):
@@ -234,7 +251,7 @@ class ECDSAKey(PKey):
file_obj,
self.signing_key,
serialization.PrivateFormat.TraditionalOpenSSL,
- password=password
+ password=password,
)
@classmethod
@@ -249,7 +266,7 @@ class ECDSAKey(PKey):
if bits is not None:
curve = cls._ECDSA_CURVES.get_by_key_length(bits)
if curve is None:
- raise ValueError("Unsupported key length: %d" % bits)
+ raise ValueError("Unsupported key length: {:d}".format(bits))
curve = curve.curve_class()
private_key = ec.generate_private_key(curve, backend=default_backend())
@@ -258,11 +275,11 @@ class ECDSAKey(PKey):
# ...internals...
def _from_private_key_file(self, filename, password):
- data = self._read_private_key_file('EC', filename, password)
+ data = self._read_private_key_file("EC", filename, password)
self._decode_key(data)
def _from_private_key(self, file_obj, password):
- data = self._read_private_key('EC', file_obj, password)
+ data = self._read_private_key("EC", file_obj, password)
self._decode_key(data)
def _decode_key(self, data):
diff --git a/paramiko/ed25519key.py b/paramiko/ed25519key.py
index 8ad71d08..68ada224 100644
--- a/paramiko/ed25519key.py
+++ b/paramiko/ed25519key.py
@@ -56,8 +56,10 @@ class Ed25519Key(PKey):
.. versionchanged:: 2.3
Added a ``file_obj`` parameter to match other key classes.
"""
- def __init__(self, msg=None, data=None, filename=None, password=None,
- file_obj=None):
+
+ def __init__(
+ self, msg=None, data=None, filename=None, password=None, file_obj=None
+ ):
self.public_blob = None
verifying_key = signing_key = None
if msg is None and data is not None:
@@ -86,6 +88,7 @@ class Ed25519Key(PKey):
def _parse_signing_key_data(self, data, password):
from paramiko.transport import Transport
+
# We may eventually want this to be usable for other key types, as
# OpenSSH moves to it, but for now this is just for Ed25519 keys.
# This format is described here:
@@ -142,9 +145,9 @@ class Ed25519Key(PKey):
ignore_few_rounds=True,
)
decryptor = Cipher(
- cipher["class"](key[:cipher["key-size"]]),
- cipher["mode"](key[cipher["key-size"]:]),
- backend=default_backend()
+ cipher["class"](key[: cipher["key-size"]]),
+ cipher["mode"](key[cipher["key-size"] :]),
+ backend=default_backend(),
).decryptor()
private_data = (
decryptor.update(private_ciphertext) + decryptor.finalize()
@@ -166,8 +169,10 @@ class Ed25519Key(PKey):
signing_key = nacl.signing.SigningKey(key_data[:32])
# Verify that all the public keys are the same...
assert (
- signing_key.verify_key.encode() == public == public_keys[i] ==
- key_data[32:]
+ signing_key.verify_key.encode()
+ == public
+ == public_keys[i]
+ == key_data[32:]
)
signing_keys.append(signing_key)
# Comment, ignore.
diff --git a/paramiko/file.py b/paramiko/file.py
index a1bdafbe..9e9f6eb8 100644
--- a/paramiko/file.py
+++ b/paramiko/file.py
@@ -16,14 +16,18 @@
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
from paramiko.common import (
- linefeed_byte_value, crlf, cr_byte, linefeed_byte, cr_byte_value,
+ linefeed_byte_value,
+ crlf,
+ cr_byte,
+ linefeed_byte,
+ cr_byte_value,
)
from paramiko.py3compat import BytesIO, PY2, u, bytes_types, text_type
from paramiko.util import ClosingContextManager
-class BufferedFile (ClosingContextManager):
+class BufferedFile(ClosingContextManager):
"""
Reusable base class to implement Python-style file buffering around a
simpler stream.
@@ -70,7 +74,7 @@ class BufferedFile (ClosingContextManager):
:raises: ``ValueError`` -- if the file is closed.
"""
if self._closed:
- raise ValueError('I/O operation on closed file')
+ raise ValueError("I/O operation on closed file")
return self
def close(self):
@@ -90,6 +94,7 @@ class BufferedFile (ClosingContextManager):
return
if PY2:
+
def next(self):
"""
Returns the next line from the input, or raises
@@ -104,7 +109,9 @@ class BufferedFile (ClosingContextManager):
if not line:
raise StopIteration
return line
+
else:
+
def __next__(self):
"""
Returns the next line from the input, or raises ``StopIteration``
@@ -159,7 +166,7 @@ class BufferedFile (ClosingContextManager):
The number of bytes read.
"""
data = self.read(len(buff))
- buff[:len(data)] = data
+ buff[: len(data)] = data
return len(data)
def read(self, size=None):
@@ -180,9 +187,9 @@ class BufferedFile (ClosingContextManager):
encountered immediately
"""
if self._closed:
- raise IOError('File is closed')
+ raise IOError("File is closed")
if not (self._flags & self.FLAG_READ):
- raise IOError('File is not open for reading')
+ raise IOError("File is not open for reading")
if (size is None) or (size < 0):
# go for broke
result = self._rbuffer
@@ -245,16 +252,16 @@ class BufferedFile (ClosingContextManager):
"""
# it's almost silly how complex this function is.
if self._closed:
- raise IOError('File is closed')
+ raise IOError("File is closed")
if not (self._flags & self.FLAG_READ):
- raise IOError('File not open for reading')
+ raise IOError("File not open for reading")
line = self._rbuffer
truncated = False
while True:
if (
- self._at_trailing_cr and
- self._flags & self.FLAG_UNIVERSAL_NEWLINE and
- len(line) > 0
+ self._at_trailing_cr
+ and self._flags & self.FLAG_UNIVERSAL_NEWLINE
+ and len(line) > 0
):
# edge case: the newline may be '\r\n' and we may have read
# only the first '\r' last time.
@@ -276,12 +283,8 @@ class BufferedFile (ClosingContextManager):
n = size - len(line)
else:
n = self._bufsize
- if (
- linefeed_byte in line or
- (
- self._flags & self.FLAG_UNIVERSAL_NEWLINE and
- cr_byte in line
- )
+ if linefeed_byte in line or (
+ self._flags & self.FLAG_UNIVERSAL_NEWLINE and cr_byte in line
):
break
try:
@@ -306,9 +309,9 @@ class BufferedFile (ClosingContextManager):
return line if self._flags & self.FLAG_BINARY else u(line)
xpos = pos + 1
if (
- line[pos] == cr_byte_value and
- xpos < len(line) and
- line[xpos] == linefeed_byte_value
+ line[pos] == cr_byte_value
+ and xpos < len(line)
+ and line[xpos] == linefeed_byte_value
):
xpos += 1
# if the string was truncated, _rbuffer needs to have the string after
@@ -338,7 +341,7 @@ class BufferedFile (ClosingContextManager):
after rounding up to an internal buffer size) are read.
:param int sizehint: desired maximum number of bytes to read.
- :returns: `list` of lines read from the file.
+ :returns: list of lines read from the file.
"""
lines = []
byte_count = 0
@@ -370,7 +373,7 @@ class BufferedFile (ClosingContextManager):
:raises: ``IOError`` -- if the file doesn't support random access.
"""
- raise IOError('File does not support seeking.')
+ raise IOError("File does not support seeking.")
def tell(self):
"""
@@ -393,11 +396,11 @@ class BufferedFile (ClosingContextManager):
"""
if isinstance(data, text_type):
# Accept text and encode as utf-8 for compatibility only.
- data = data.encode('utf-8')
+ data = data.encode("utf-8")
if self._closed:
- raise IOError('File is closed')
+ raise IOError("File is closed")
if not (self._flags & self.FLAG_WRITE):
- raise IOError('File not open for writing')
+ raise IOError("File not open for writing")
if not (self._flags & self.FLAG_BUFFERED):
self._write_all(data)
return
@@ -408,9 +411,9 @@ class BufferedFile (ClosingContextManager):
if last_newline_pos >= 0:
wbuf = self._wbuffer.getvalue()
last_newline_pos += len(wbuf) - len(data)
- self._write_all(wbuf[:last_newline_pos + 1])
+ self._write_all(wbuf[: last_newline_pos + 1])
self._wbuffer = BytesIO()
- self._wbuffer.write(wbuf[last_newline_pos + 1:])
+ self._wbuffer.write(wbuf[last_newline_pos + 1 :])
return
# even if we're line buffering, if the buffer has grown past the
# buffer size, force a flush.
@@ -457,7 +460,7 @@ class BufferedFile (ClosingContextManager):
(subclass override)
Write data into the stream.
"""
- raise IOError('write not implemented')
+ raise IOError("write not implemented")
def _get_size(self):
"""
@@ -472,7 +475,7 @@ class BufferedFile (ClosingContextManager):
# ...internals...
- def _set_mode(self, mode='r', bufsize=-1):
+ def _set_mode(self, mode="r", bufsize=-1):
"""
Subclasses call this method to initialize the BufferedFile.
"""
@@ -495,17 +498,17 @@ class BufferedFile (ClosingContextManager):
# unbuffered
self._flags &= ~(self.FLAG_BUFFERED | self.FLAG_LINE_BUFFERED)
- if ('r' in mode) or ('+' in mode):
+ if ("r" in mode) or ("+" in mode):
self._flags |= self.FLAG_READ
- if ('w' in mode) or ('+' in mode):
+ if ("w" in mode) or ("+" in mode):
self._flags |= self.FLAG_WRITE
- if 'a' in mode:
+ if "a" in mode:
self._flags |= self.FLAG_WRITE | self.FLAG_APPEND
self._size = self._get_size()
self._pos = self._realpos = self._size
- if 'b' in mode:
+ if "b" in mode:
self._flags |= self.FLAG_BINARY
- if 'U' in mode:
+ if "U" in mode:
self._flags |= self.FLAG_UNIVERSAL_NEWLINE
# built-in file objects have this attribute to store which kinds of
# line terminations they've seen:
@@ -534,9 +537,8 @@ class BufferedFile (ClosingContextManager):
return
if self.newlines is None:
self.newlines = newline
- elif (
- self.newlines != newline and
- isinstance(self.newlines, bytes_types)
+ elif self.newlines != newline and isinstance(
+ self.newlines, bytes_types
):
self.newlines = (self.newlines, newline)
elif newline not in self.newlines:
diff --git a/paramiko/hostkeys.py b/paramiko/hostkeys.py
index d023b33d..c4d611db 100644
--- a/paramiko/hostkeys.py
+++ b/paramiko/hostkeys.py
@@ -34,7 +34,7 @@ from paramiko.ed25519key import Ed25519Key
from paramiko.ssh_exception import SSHException
-class HostKeys (MutableMapping):
+class HostKeys(MutableMapping):
"""
Representation of an OpenSSH-style "known hosts" file. Host keys can be
read from one or more files, and then individual hosts can be looked up to
@@ -88,10 +88,10 @@ class HostKeys (MutableMapping):
:raises: ``IOError`` -- if there was an error reading the file
"""
- with open(filename, 'r') as f:
+ with open(filename, "r") as f:
for lineno, line in enumerate(f, 1):
line = line.strip()
- if (len(line) == 0) or (line[0] == '#'):
+ if (len(line) == 0) or (line[0] == "#"):
continue
try:
e = HostKeyEntry.from_line(line, lineno)
@@ -118,7 +118,7 @@ class HostKeys (MutableMapping):
.. versionadded:: 1.6.1
"""
- with open(filename, 'w') as f:
+ with open(filename, "w") as f:
for e in self._entries:
line = e.to_line()
if line:
@@ -134,7 +134,9 @@ class HostKeys (MutableMapping):
:return: dict of `str` -> `.PKey` keys associated with this host
(or ``None``)
"""
- class SubDict (MutableMapping):
+
+ class SubDict(MutableMapping):
+
def __init__(self, hostname, entries, hostkeys):
self._hostname = hostname
self._entries = entries
@@ -176,7 +178,8 @@ class HostKeys (MutableMapping):
def keys(self):
return [
- e.key.get_name() for e in self._entries
+ e.key.get_name()
+ for e in self._entries
if e.key is not None
]
@@ -196,10 +199,10 @@ class HostKeys (MutableMapping):
"""
for h in entry.hostnames:
if (
- h == hostname or
- h.startswith('|1|') and
- not hostname.startswith('|1|') and
- constant_time_bytes_eq(self.hash_host(hostname, h), h)
+ h == hostname
+ or h.startswith("|1|")
+ and not hostname.startswith("|1|")
+ and constant_time_bytes_eq(self.hash_host(hostname, h), h)
):
return True
return False
@@ -295,16 +298,17 @@ class HostKeys (MutableMapping):
if salt is None:
salt = os.urandom(sha1().digest_size)
else:
- if salt.startswith('|1|'):
- salt = salt.split('|')[2]
+ if salt.startswith("|1|"):
+ salt = salt.split("|")[2]
salt = decodebytes(b(salt))
assert len(salt) == sha1().digest_size
hmac = HMAC(salt, b(hostname), sha1).digest()
- hostkey = '|1|%s|%s' % (u(encodebytes(salt)), u(encodebytes(hmac)))
- return hostkey.replace('\n', '')
+ hostkey = "|1|{}|{}".format(u(encodebytes(salt)), u(encodebytes(hmac)))
+ return hostkey.replace("\n", "")
class InvalidHostKey(Exception):
+
def __init__(self, line, exc):
self.line = line
self.exc = exc
@@ -334,32 +338,32 @@ class HostKeyEntry:
:param str line: a line from an OpenSSH known_hosts file
"""
- log = get_logger('paramiko.hostkeys')
- fields = line.split(' ')
+ log = get_logger("paramiko.hostkeys")
+ fields = line.split(" ")
if len(fields) < 3:
# Bad number of fields
- log.info("Not enough fields found in known_hosts in line %s (%r)" %
- (lineno, line))
+ msg = "Not enough fields found in known_hosts in line {} ({!r})"
+ log.info(msg.format(lineno, line))
return None
fields = fields[:3]
names, keytype, key = fields
- names = names.split(',')
+ names = names.split(",")
# Decide what kind of key we're looking at and create an object
# to hold it accordingly.
try:
key = b(key)
- if keytype == 'ssh-rsa':
+ if keytype == "ssh-rsa":
key = RSAKey(data=decodebytes(key))
- elif keytype == 'ssh-dss':
+ elif keytype == "ssh-dss":
key = DSSKey(data=decodebytes(key))
elif keytype in ECDSAKey.supported_key_format_identifiers():
key = ECDSAKey(data=decodebytes(key), validate_point=False)
- elif keytype == 'ssh-ed25519':
+ elif keytype == "ssh-ed25519":
key = Ed25519Key(data=decodebytes(key))
else:
- log.info("Unable to handle key of type %s" % (keytype,))
+ log.info("Unable to handle key of type {}".format(keytype))
return None
except binascii.Error as e:
@@ -374,11 +378,12 @@ class HostKeyEntry:
included.
"""
if self.valid:
- return '%s %s %s\n' % (
- ','.join(self.hostnames),
+ return "{} {} {}\n".format(
+ ",".join(self.hostnames),
self.key.get_name(),
- self.key.get_base64())
+ self.key.get_base64(),
+ )
return None
def __repr__(self):
- return '<HostKeyEntry %r: %r>' % (self.hostnames, self.key)
+ return "<HostKeyEntry {!r}: {!r}>".format(self.hostnames, self.key)
diff --git a/paramiko/kex_ecdh_nist.py b/paramiko/kex_ecdh_nist.py
index 702a872d..1d87442a 100644
--- a/paramiko/kex_ecdh_nist.py
+++ b/paramiko/kex_ecdh_nist.py
@@ -15,7 +15,7 @@ _MSG_KEXECDH_INIT, _MSG_KEXECDH_REPLY = range(30, 32)
c_MSG_KEXECDH_INIT, c_MSG_KEXECDH_REPLY = [byte_chr(c) for c in range(30, 32)]
-class KexNistp256():
+class KexNistp256:
name = "ecdh-sha2-nistp256"
hash_algo = sha256
@@ -45,7 +45,9 @@ class KexNistp256():
return self._parse_kexecdh_init(m)
elif not self.transport.server_mode and (ptype == _MSG_KEXECDH_REPLY):
return self._parse_kexecdh_reply(m)
- raise SSHException('KexECDH asked to handle packet type %d' % ptype)
+ raise SSHException(
+ "KexECDH asked to handle packet type {:d}".format(ptype)
+ )
def _generate_key_pair(self):
self.P = ec.generate_private_key(self.curve, default_backend())
@@ -64,8 +66,12 @@ class KexNistp256():
K = long(hexlify(K), 16)
# compute exchange hash
hm = Message()
- hm.add(self.transport.remote_version, self.transport.local_version,
- self.transport.remote_kex_init, self.transport.local_kex_init)
+ hm.add(
+ self.transport.remote_version,
+ self.transport.local_version,
+ self.transport.remote_kex_init,
+ self.transport.local_kex_init,
+ )
hm.add_string(K_S)
hm.add_string(Q_C_bytes)
# SEC1: V2.0 2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion
@@ -94,8 +100,12 @@ class KexNistp256():
K = long(hexlify(K), 16)
# compute exchange hash and verify signature
hm = Message()
- hm.add(self.transport.local_version, self.transport.remote_version,
- self.transport.local_kex_init, self.transport.remote_kex_init)
+ hm.add(
+ self.transport.local_version,
+ self.transport.remote_version,
+ self.transport.local_kex_init,
+ self.transport.remote_kex_init,
+ )
hm.add_string(K_S)
# SEC1: V2.0 2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion
hm.add_string(self.Q_C.public_numbers().encode_point())
diff --git a/paramiko/kex_gex.py b/paramiko/kex_gex.py
index ba45da18..fb8f01fd 100644
--- a/paramiko/kex_gex.py
+++ b/paramiko/kex_gex.py
@@ -32,17 +32,26 @@ from paramiko.py3compat import byte_chr, byte_ord, byte_mask
from paramiko.ssh_exception import SSHException
-_MSG_KEXDH_GEX_REQUEST_OLD, _MSG_KEXDH_GEX_GROUP, _MSG_KEXDH_GEX_INIT, \
- _MSG_KEXDH_GEX_REPLY, _MSG_KEXDH_GEX_REQUEST = range(30, 35)
+(
+ _MSG_KEXDH_GEX_REQUEST_OLD,
+ _MSG_KEXDH_GEX_GROUP,
+ _MSG_KEXDH_GEX_INIT,
+ _MSG_KEXDH_GEX_REPLY,
+ _MSG_KEXDH_GEX_REQUEST,
+) = range(30, 35)
-c_MSG_KEXDH_GEX_REQUEST_OLD, c_MSG_KEXDH_GEX_GROUP, c_MSG_KEXDH_GEX_INIT, \
- c_MSG_KEXDH_GEX_REPLY, c_MSG_KEXDH_GEX_REQUEST = \
- [byte_chr(c) for c in range(30, 35)]
+(
+ c_MSG_KEXDH_GEX_REQUEST_OLD,
+ c_MSG_KEXDH_GEX_GROUP,
+ c_MSG_KEXDH_GEX_INIT,
+ c_MSG_KEXDH_GEX_REPLY,
+ c_MSG_KEXDH_GEX_REQUEST,
+) = [byte_chr(c) for c in range(30, 35)]
-class KexGex (object):
+class KexGex(object):
- name = 'diffie-hellman-group-exchange-sha1'
+ name = "diffie-hellman-group-exchange-sha1"
min_bits = 1024
max_bits = 8192
preferred_bits = 2048
@@ -61,7 +70,8 @@ class KexGex (object):
def start_kex(self, _test_old_style=False):
if self.transport.server_mode:
self.transport._expect_packet(
- _MSG_KEXDH_GEX_REQUEST, _MSG_KEXDH_GEX_REQUEST_OLD)
+ _MSG_KEXDH_GEX_REQUEST, _MSG_KEXDH_GEX_REQUEST_OLD
+ )
return
# request a bit range: we accept (min_bits) to (max_bits), but prefer
# (preferred_bits). according to the spec, we shouldn't pull the
@@ -91,8 +101,8 @@ class KexGex (object):
return self._parse_kexdh_gex_reply(m)
elif ptype == _MSG_KEXDH_GEX_REQUEST_OLD:
return self._parse_kexdh_gex_request_old(m)
- raise SSHException(
- 'KexGex %s asked to handle packet type %d' % self.name, ptype)
+ msg = "KexGex {} asked to handle packet type {:d}"
+ raise SSHException(msg.format(self.name, ptype))
# ...internals...
@@ -137,12 +147,13 @@ class KexGex (object):
# generate prime
pack = self.transport._get_modulus_pack()
if pack is None:
- raise SSHException(
- 'Can\'t do server-side gex with no modulus pack')
+ raise SSHException("Can't do server-side gex with no modulus pack")
self.transport._log(
DEBUG,
- 'Picking p (%d <= %d <= %d bits)' % (
- minbits, preferredbits, maxbits))
+ "Picking p ({} <= {} <= {} bits)".format(
+ minbits, preferredbits, maxbits
+ ),
+ )
self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits)
m = Message()
m.add_byte(c_MSG_KEXDH_GEX_GROUP)
@@ -163,12 +174,13 @@ class KexGex (object):
# generate prime
pack = self.transport._get_modulus_pack()
if pack is None:
- raise SSHException(
- 'Can\'t do server-side gex with no modulus pack')
+ raise SSHException("Can't do server-side gex with no modulus pack")
self.transport._log(
- DEBUG, 'Picking p (~ %d bits)' % (self.preferred_bits,))
+ DEBUG, "Picking p (~ {} bits)".format(self.preferred_bits)
+ )
self.g, self.p = pack.get_modulus(
- self.min_bits, self.preferred_bits, self.max_bits)
+ self.min_bits, self.preferred_bits, self.max_bits
+ )
m = Message()
m.add_byte(c_MSG_KEXDH_GEX_GROUP)
m.add_mpint(self.p)
@@ -184,9 +196,10 @@ class KexGex (object):
bitlen = util.bit_length(self.p)
if (bitlen < 1024) or (bitlen > 8192):
raise SSHException(
- 'Server-generated gex p (don\'t ask) is out of range '
- '(%d bits)' % bitlen)
- self.transport._log(DEBUG, 'Got server p (%d bits)' % bitlen)
+ "Server-generated gex p (don't ask) is out of range "
+ "({} bits)".format(bitlen)
+ )
+ self.transport._log(DEBUG, "Got server p ({} bits)".format(bitlen))
self._generate_x()
# now compute e = g^x mod p
self.e = pow(self.g, self.x, self.p)
@@ -207,9 +220,13 @@ class KexGex (object):
# okay, build up the hash H of
# (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa
hm = Message()
- hm.add(self.transport.remote_version, self.transport.local_version,
- self.transport.remote_kex_init, self.transport.local_kex_init,
- key)
+ hm.add(
+ self.transport.remote_version,
+ self.transport.local_version,
+ self.transport.remote_kex_init,
+ self.transport.local_kex_init,
+ key,
+ )
if not self.old_style:
hm.add_int(self.min_bits)
hm.add_int(self.preferred_bits)
@@ -243,9 +260,13 @@ class KexGex (object):
# okay, build up the hash H of
# (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa
hm = Message()
- hm.add(self.transport.local_version, self.transport.remote_version,
- self.transport.local_kex_init, self.transport.remote_kex_init,
- host_key)
+ hm.add(
+ self.transport.local_version,
+ self.transport.remote_version,
+ self.transport.local_kex_init,
+ self.transport.remote_kex_init,
+ host_key,
+ )
if not self.old_style:
hm.add_int(self.min_bits)
hm.add_int(self.preferred_bits)
@@ -262,5 +283,5 @@ class KexGex (object):
class KexGexSHA256(KexGex):
- name = 'diffie-hellman-group-exchange-sha256'
+ name = "diffie-hellman-group-exchange-sha256"
hash_algo = sha256
diff --git a/paramiko/kex_group1.py b/paramiko/kex_group1.py
index e8f042b1..66b7bb20 100644
--- a/paramiko/kex_group1.py
+++ b/paramiko/kex_group1.py
@@ -41,10 +41,12 @@ b0000000000000000 = zero_byte * 8
class KexGroup1(object):
# draft-ietf-secsh-transport-09.txt, page 17
- P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF # noqa
+ P = (
+ 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF # noqa
+ )
G = 2
- name = 'diffie-hellman-group1-sha1'
+ name = "diffie-hellman-group1-sha1"
hash_algo = sha1
def __init__(self, transport):
@@ -73,7 +75,8 @@ class KexGroup1(object):
return self._parse_kexdh_init(m)
elif not self.transport.server_mode and (ptype == _MSG_KEXDH_REPLY):
return self._parse_kexdh_reply(m)
- raise SSHException('KexGroup1 asked to handle packet type %d' % ptype)
+ msg = "KexGroup1 asked to handle packet type {:d}"
+ raise SSHException(msg.format(ptype))
# ...internals...
@@ -87,8 +90,10 @@ class KexGroup1(object):
while 1:
x_bytes = os.urandom(128)
x_bytes = byte_mask(x_bytes[0], 0x7f) + x_bytes[1:]
- if (x_bytes[:8] != b7fffffffffffffff and
- x_bytes[:8] != b0000000000000000):
+ if (
+ x_bytes[:8] != b7fffffffffffffff
+ and x_bytes[:8] != b0000000000000000
+ ):
break
self.x = util.inflate_long(x_bytes)
@@ -103,8 +108,12 @@ class KexGroup1(object):
# okay, build up the hash H of
# (V_C || V_S || I_C || I_S || K_S || e || f || K)
hm = Message()
- hm.add(self.transport.local_version, self.transport.remote_version,
- self.transport.local_kex_init, self.transport.remote_kex_init)
+ hm.add(
+ self.transport.local_version,
+ self.transport.remote_version,
+ self.transport.local_kex_init,
+ self.transport.remote_kex_init,
+ )
hm.add_string(host_key)
hm.add_mpint(self.e)
hm.add_mpint(self.f)
@@ -123,8 +132,12 @@ class KexGroup1(object):
# okay, build up the hash H of
# (V_C || V_S || I_C || I_S || K_S || e || f || K)
hm = Message()
- hm.add(self.transport.remote_version, self.transport.local_version,
- self.transport.remote_kex_init, self.transport.local_kex_init)
+ hm.add(
+ self.transport.remote_version,
+ self.transport.local_version,
+ self.transport.remote_kex_init,
+ self.transport.local_kex_init,
+ )
hm.add_string(key)
hm.add_mpint(self.e)
hm.add_mpint(self.f)
diff --git a/paramiko/kex_group14.py b/paramiko/kex_group14.py
index 22955e34..29af2408 100644
--- a/paramiko/kex_group14.py
+++ b/paramiko/kex_group14.py
@@ -28,8 +28,10 @@ from hashlib import sha1
class KexGroup14(KexGroup1):
# http://tools.ietf.org/html/rfc3526#section-3
- P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF # noqa
+ P = (
+ 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF # noqa
+ )
G = 2
- name = 'diffie-hellman-group14-sha1'
+ name = "diffie-hellman-group14-sha1"
hash_algo = sha1
diff --git a/paramiko/kex_gss.py b/paramiko/kex_gss.py
index a2ea9fca..1510ff9c 100644
--- a/paramiko/kex_gss.py
+++ b/paramiko/kex_gss.py
@@ -47,14 +47,22 @@ from paramiko.py3compat import byte_chr, byte_mask, byte_ord
from paramiko.ssh_exception import SSHException
-MSG_KEXGSS_INIT, MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_HOSTKEY,\
- MSG_KEXGSS_ERROR = range(30, 35)
-MSG_KEXGSS_GROUPREQ, MSG_KEXGSS_GROUP = range(40, 42)
-c_MSG_KEXGSS_INIT, c_MSG_KEXGSS_CONTINUE, c_MSG_KEXGSS_COMPLETE,\
- c_MSG_KEXGSS_HOSTKEY, c_MSG_KEXGSS_ERROR = [
- byte_chr(c) for c in range(30, 35)
- ]
-c_MSG_KEXGSS_GROUPREQ, c_MSG_KEXGSS_GROUP = [
+(
+ MSG_KEXGSS_INIT,
+ MSG_KEXGSS_CONTINUE,
+ MSG_KEXGSS_COMPLETE,
+ MSG_KEXGSS_HOSTKEY,
+ MSG_KEXGSS_ERROR,
+) = range(30, 35)
+(MSG_KEXGSS_GROUPREQ, MSG_KEXGSS_GROUP) = range(40, 42)
+(
+ c_MSG_KEXGSS_INIT,
+ c_MSG_KEXGSS_CONTINUE,
+ c_MSG_KEXGSS_COMPLETE,
+ c_MSG_KEXGSS_HOSTKEY,
+ c_MSG_KEXGSS_ERROR,
+) = [byte_chr(c) for c in range(30, 35)]
+(c_MSG_KEXGSS_GROUPREQ, c_MSG_KEXGSS_GROUP) = [
byte_chr(c) for c in range(40, 42)
]
@@ -65,7 +73,9 @@ class KexGSSGroup1(object):
4462 Section 2 <https://tools.ietf.org/html/rfc4462.html#section-2>`_
"""
# draft-ietf-secsh-transport-09.txt, page 17
- P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF # noqa
+ P = (
+ 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF # noqa
+ )
G = 2
b7fffffffffffffff = byte_chr(0x7f) + max_byte * 7 # noqa
b0000000000000000 = zero_byte * 8 # noqa
@@ -98,10 +108,12 @@ class KexGSSGroup1(object):
m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host))
m.add_mpint(self.e)
self.transport._send_message(m)
- self.transport._expect_packet(MSG_KEXGSS_HOSTKEY,
- MSG_KEXGSS_CONTINUE,
- MSG_KEXGSS_COMPLETE,
- MSG_KEXGSS_ERROR)
+ self.transport._expect_packet(
+ MSG_KEXGSS_HOSTKEY,
+ MSG_KEXGSS_CONTINUE,
+ MSG_KEXGSS_COMPLETE,
+ MSG_KEXGSS_ERROR,
+ )
def parse_next(self, ptype, m):
"""
@@ -120,8 +132,8 @@ class KexGSSGroup1(object):
return self._parse_kexgss_complete(m)
elif ptype == MSG_KEXGSS_ERROR:
return self._parse_kexgss_error(m)
- raise SSHException('GSS KexGroup1 asked to handle packet type %d'
- % ptype)
+ msg = "GSS KexGroup1 asked to handle packet type {:d}"
+ raise SSHException(msg.format(ptype))
# ## internals...
@@ -152,8 +164,7 @@ class KexGSSGroup1(object):
self.transport.host_key = host_key
sig = m.get_string()
self.transport._verify_key(host_key, sig)
- self.transport._expect_packet(MSG_KEXGSS_CONTINUE,
- MSG_KEXGSS_COMPLETE)
+ self.transport._expect_packet(MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE)
def _parse_kexgss_continue(self, m):
"""
@@ -166,13 +177,14 @@ class KexGSSGroup1(object):
srv_token = m.get_string()
m = Message()
m.add_byte(c_MSG_KEXGSS_CONTINUE)
- m.add_string(self.kexgss.ssh_init_sec_context(
- target=self.gss_host, recv_token=srv_token))
+ m.add_string(
+ self.kexgss.ssh_init_sec_context(
+ target=self.gss_host, recv_token=srv_token
+ )
+ )
self.transport.send_message(m)
self.transport._expect_packet(
- MSG_KEXGSS_CONTINUE,
- MSG_KEXGSS_COMPLETE,
- MSG_KEXGSS_ERROR
+ MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR
)
else:
pass
@@ -200,8 +212,12 @@ class KexGSSGroup1(object):
# okay, build up the hash H of
# (V_C || V_S || I_C || I_S || K_S || e || f || K)
hm = Message()
- hm.add(self.transport.local_version, self.transport.remote_version,
- self.transport.local_kex_init, self.transport.remote_kex_init)
+ hm.add(
+ self.transport.local_version,
+ self.transport.remote_version,
+ self.transport.local_kex_init,
+ self.transport.remote_kex_init,
+ )
hm.add_string(self.transport.host_key.__str__())
hm.add_mpint(self.e)
hm.add_mpint(self.f)
@@ -209,8 +225,9 @@ class KexGSSGroup1(object):
H = sha1(str(hm)).digest()
self.transport._set_K_H(K, H)
if srv_token is not None:
- self.kexgss.ssh_init_sec_context(target=self.gss_host,
- recv_token=srv_token)
+ self.kexgss.ssh_init_sec_context(
+ target=self.gss_host, recv_token=srv_token
+ )
self.kexgss.ssh_check_mic(mic_token, H)
else:
self.kexgss.ssh_check_mic(mic_token, H)
@@ -234,20 +251,26 @@ class KexGSSGroup1(object):
# okay, build up the hash H of
# (V_C || V_S || I_C || I_S || K_S || e || f || K)
hm = Message()
- hm.add(self.transport.remote_version, self.transport.local_version,
- self.transport.remote_kex_init, self.transport.local_kex_init)
+ hm.add(
+ self.transport.remote_version,
+ self.transport.local_version,
+ self.transport.remote_kex_init,
+ self.transport.local_kex_init,
+ )
hm.add_string(key)
hm.add_mpint(self.e)
hm.add_mpint(self.f)
hm.add_mpint(K)
H = sha1(hm.asbytes()).digest()
self.transport._set_K_H(K, H)
- srv_token = self.kexgss.ssh_accept_sec_context(self.gss_host,
- client_token)
+ srv_token = self.kexgss.ssh_accept_sec_context(
+ self.gss_host, client_token
+ )
m = Message()
if self.kexgss._gss_srv_ctxt_status:
- mic_token = self.kexgss.ssh_get_mic(self.transport.session_id,
- gss_kex=True)
+ mic_token = self.kexgss.ssh_get_mic(
+ self.transport.session_id, gss_kex=True
+ )
m.add_byte(c_MSG_KEXGSS_COMPLETE)
m.add_mpint(self.f)
m.add_string(mic_token)
@@ -263,9 +286,9 @@ class KexGSSGroup1(object):
m.add_byte(c_MSG_KEXGSS_CONTINUE)
m.add_string(srv_token)
self.transport._send_message(m)
- self.transport._expect_packet(MSG_KEXGSS_CONTINUE,
- MSG_KEXGSS_COMPLETE,
- MSG_KEXGSS_ERROR)
+ self.transport._expect_packet(
+ MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR
+ )
def _parse_kexgss_error(self, m):
"""
@@ -281,11 +304,16 @@ class KexGSSGroup1(object):
maj_status = m.get_int()
min_status = m.get_int()
err_msg = m.get_string()
- m.get_string() # we don't care about the language!
- raise SSHException("GSS-API Error:\nMajor Status: %s\nMinor Status: %s\
- \nError Message: %s\n") % (str(maj_status),
- str(min_status),
- err_msg)
+ m.get_string() # we don't care about the language!
+ raise SSHException(
+ """GSS-API Error:
+Major Status: {}
+Minor Status: {}
+Error Message: {}
+""".format(
+ maj_status, min_status, err_msg
+ )
+ )
class KexGSSGroup14(KexGSSGroup1):
@@ -294,7 +322,9 @@ class KexGSSGroup14(KexGSSGroup1):
in `RFC 4462 Section 2
<https://tools.ietf.org/html/rfc4462.html#section-2>`_
"""
- P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF # noqa
+ P = (
+ 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF # noqa
+ )
G = 2
NAME = "gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g=="
@@ -361,7 +391,8 @@ class KexGSSGex(object):
return self._parse_kexgss_complete(m)
elif ptype == MSG_KEXGSS_ERROR:
return self._parse_kexgss_error(m)
- raise SSHException('KexGex asked to handle packet type %d' % ptype)
+ msg = "KexGex asked to handle packet type {:d}"
+ raise SSHException(msg.format(ptype))
# ## internals...
@@ -412,12 +443,13 @@ class KexGSSGex(object):
# generate prime
pack = self.transport._get_modulus_pack()
if pack is None:
- raise SSHException(
- 'Can\'t do server-side gex with no modulus pack')
+ raise SSHException("Can't do server-side gex with no modulus pack")
self.transport._log(
DEBUG, # noqa
- 'Picking p (%d <= %d <= %d bits)' % (
- minbits, preferredbits, maxbits))
+ "Picking p ({} <= {} <= {} bits)".format(
+ minbits, preferredbits, maxbits
+ ),
+ )
self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits)
m = Message()
m.add_byte(c_MSG_KEXGSS_GROUP)
@@ -438,9 +470,12 @@ class KexGSSGex(object):
bitlen = util.bit_length(self.p)
if (bitlen < 1024) or (bitlen > 8192):
raise SSHException(
- 'Server-generated gex p (don\'t ask) is out of range '
- '(%d bits)' % bitlen)
- self.transport._log(DEBUG, 'Got server p (%d bits)' % bitlen) # noqa
+ "Server-generated gex p (don't ask) is out of range "
+ "({} bits)".format(bitlen)
+ )
+ self.transport._log(
+ DEBUG, "Got server p ({} bits)".format(bitlen)
+ ) # noqa
self._generate_x()
# now compute e = g^x mod p
self.e = pow(self.g, self.x, self.p)
@@ -449,10 +484,12 @@ class KexGSSGex(object):
m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host))
m.add_mpint(self.e)
self.transport._send_message(m)
- self.transport._expect_packet(MSG_KEXGSS_HOSTKEY,
- MSG_KEXGSS_CONTINUE,
- MSG_KEXGSS_COMPLETE,
- MSG_KEXGSS_ERROR)
+ self.transport._expect_packet(
+ MSG_KEXGSS_HOSTKEY,
+ MSG_KEXGSS_CONTINUE,
+ MSG_KEXGSS_COMPLETE,
+ MSG_KEXGSS_ERROR,
+ )
def _parse_kexgss_gex_init(self, m):
"""
@@ -472,9 +509,13 @@ class KexGSSGex(object):
# okay, build up the hash H of
# (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa
hm = Message()
- hm.add(self.transport.remote_version, self.transport.local_version,
- self.transport.remote_kex_init, self.transport.local_kex_init,
- key)
+ hm.add(
+ self.transport.remote_version,
+ self.transport.local_version,
+ self.transport.remote_kex_init,
+ self.transport.local_kex_init,
+ key,
+ )
hm.add_int(self.min_bits)
hm.add_int(self.preferred_bits)
hm.add_int(self.max_bits)
@@ -485,12 +526,14 @@ class KexGSSGex(object):
hm.add_mpint(K)
H = sha1(hm.asbytes()).digest()
self.transport._set_K_H(K, H)
- srv_token = self.kexgss.ssh_accept_sec_context(self.gss_host,
- client_token)
+ srv_token = self.kexgss.ssh_accept_sec_context(
+ self.gss_host, client_token
+ )
m = Message()
if self.kexgss._gss_srv_ctxt_status:
- mic_token = self.kexgss.ssh_get_mic(self.transport.session_id,
- gss_kex=True)
+ mic_token = self.kexgss.ssh_get_mic(
+ self.transport.session_id, gss_kex=True
+ )
m.add_byte(c_MSG_KEXGSS_COMPLETE)
m.add_mpint(self.f)
m.add_string(mic_token)
@@ -506,9 +549,9 @@ class KexGSSGex(object):
m.add_byte(c_MSG_KEXGSS_CONTINUE)
m.add_string(srv_token)
self.transport._send_message(m)
- self.transport._expect_packet(MSG_KEXGSS_CONTINUE,
- MSG_KEXGSS_COMPLETE,
- MSG_KEXGSS_ERROR)
+ self.transport._expect_packet(
+ MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR
+ )
def _parse_kexgss_hostkey(self, m):
"""
@@ -521,8 +564,7 @@ class KexGSSGex(object):
self.transport.host_key = host_key
sig = m.get_string()
self.transport._verify_key(host_key, sig)
- self.transport._expect_packet(MSG_KEXGSS_CONTINUE,
- MSG_KEXGSS_COMPLETE)
+ self.transport._expect_packet(MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE)
def _parse_kexgss_continue(self, m):
"""
@@ -534,12 +576,15 @@ class KexGSSGex(object):
srv_token = m.get_string()
m = Message()
m.add_byte(c_MSG_KEXGSS_CONTINUE)
- m.add_string(self.kexgss.ssh_init_sec_context(target=self.gss_host,
- recv_token=srv_token))
+ m.add_string(
+ self.kexgss.ssh_init_sec_context(
+ target=self.gss_host, recv_token=srv_token
+ )
+ )
self.transport.send_message(m)
- self.transport._expect_packet(MSG_KEXGSS_CONTINUE,
- MSG_KEXGSS_COMPLETE,
- MSG_KEXGSS_ERROR)
+ self.transport._expect_packet(
+ MSG_KEXGSS_CONTINUE, MSG_KEXGSS_COMPLETE, MSG_KEXGSS_ERROR
+ )
else:
pass
@@ -564,9 +609,13 @@ class KexGSSGex(object):
# okay, build up the hash H of
# (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K) # noqa
hm = Message()
- hm.add(self.transport.local_version, self.transport.remote_version,
- self.transport.local_kex_init, self.transport.remote_kex_init,
- self.transport.host_key.__str__())
+ hm.add(
+ self.transport.local_version,
+ self.transport.remote_version,
+ self.transport.local_kex_init,
+ self.transport.remote_kex_init,
+ self.transport.host_key.__str__(),
+ )
if not self.old_style:
hm.add_int(self.min_bits)
hm.add_int(self.preferred_bits)
@@ -580,8 +629,9 @@ class KexGSSGex(object):
H = sha1(hm.asbytes()).digest()
self.transport._set_K_H(K, H)
if srv_token is not None:
- self.kexgss.ssh_init_sec_context(target=self.gss_host,
- recv_token=srv_token)
+ self.kexgss.ssh_init_sec_context(
+ target=self.gss_host, recv_token=srv_token
+ )
self.kexgss.ssh_check_mic(mic_token, H)
else:
self.kexgss.ssh_check_mic(mic_token, H)
@@ -602,11 +652,16 @@ class KexGSSGex(object):
maj_status = m.get_int()
min_status = m.get_int()
err_msg = m.get_string()
- m.get_string() # we don't care about the language (lang_tag)!
- raise SSHException("GSS-API Error:\nMajor Status: %s\nMinor Status: %s\
- \nError Message: %s\n") % (str(maj_status),
- str(min_status),
- err_msg)
+ m.get_string() # we don't care about the language (lang_tag)!
+ raise SSHException(
+ """GSS-API Error:
+Major Status: {}
+Minor Status: {}
+Error Message: {}
+""".format(
+ maj_status, min_status, err_msg
+ )
+ )
class NullHostKey(object):
@@ -615,6 +670,7 @@ class NullHostKey(object):
in `RFC 4462 Section 5
<https://tools.ietf.org/html/rfc4462.html#section-5>`_
"""
+
def __init__(self):
self.key = ""
diff --git a/paramiko/message.py b/paramiko/message.py
index f8ed6170..dead3508 100644
--- a/paramiko/message.py
+++ b/paramiko/message.py
@@ -27,7 +27,7 @@ from paramiko.common import zero_byte, max_byte, one_byte, asbytes
from paramiko.py3compat import long, BytesIO, u, integer_types
-class Message (object):
+class Message(object):
"""
An SSH2 message is a stream of bytes that encodes some combination of
strings, integers, bools, and infinite-precision integers (known in Python
@@ -63,7 +63,7 @@ class Message (object):
"""
Returns a string representation of this object, for debugging.
"""
- return 'paramiko.Message(' + repr(self.packet.getvalue()) + ')'
+ return "paramiko.Message(" + repr(self.packet.getvalue()) + ")"
def asbytes(self):
"""
@@ -139,13 +139,13 @@ class Message (object):
if byte == max_byte:
return util.inflate_long(self.get_binary())
byte += self.get_bytes(3)
- return struct.unpack('>I', byte)[0]
+ return struct.unpack(">I", byte)[0]
def get_int(self):
"""
Fetch an int from the stream.
"""
- return struct.unpack('>I', self.get_bytes(4))[0]
+ return struct.unpack(">I", self.get_bytes(4))[0]
def get_int64(self):
"""
@@ -153,7 +153,7 @@ class Message (object):
:return: a 64-bit unsigned integer (`long`).
"""
- return struct.unpack('>Q', self.get_bytes(8))[0]
+ return struct.unpack(">Q", self.get_bytes(8))[0]
def get_mpint(self):
"""
@@ -187,11 +187,11 @@ class Message (object):
def get_list(self):
"""
- Fetch a `list` of `strings <str>` from the stream.
+ Fetch a list of `strings <str>` from the stream.
These are trivially encoded as comma-separated values in a string.
"""
- return self.get_text().split(',')
+ return self.get_text().split(",")
def add_bytes(self, b):
"""
@@ -229,7 +229,7 @@ class Message (object):
:param int n: integer to add
"""
- self.packet.write(struct.pack('>I', n))
+ self.packet.write(struct.pack(">I", n))
return self
def add_adaptive_int(self, n):
@@ -242,7 +242,7 @@ class Message (object):
self.packet.write(max_byte)
self.add_string(util.deflate_long(n))
else:
- self.packet.write(struct.pack('>I', n))
+ self.packet.write(struct.pack(">I", n))
return self
def add_int64(self, n):
@@ -251,7 +251,7 @@ class Message (object):
:param long n: long int to add
"""
- self.packet.write(struct.pack('>Q', n))
+ self.packet.write(struct.pack(">Q", n))
return self
def add_mpint(self, z):
@@ -281,9 +281,9 @@ class Message (object):
a single string of values separated by commas. (Yes, really, that's
how SSH2 does it.)
- :param list l: list of strings to add
+ :param l: list of strings to add
"""
- self.add_string(','.join(l))
+ self.add_string(",".join(l))
return self
def _add(self, i):
diff --git a/paramiko/packet.py b/paramiko/packet.py
index 95a26c6e..d324fc35 100644
--- a/paramiko/packet.py
+++ b/paramiko/packet.py
@@ -30,7 +30,12 @@ from hmac import HMAC
from paramiko import util
from paramiko.common import (
- linefeed_byte, cr_byte_value, asbytes, MSG_NAMES, DEBUG, xffffffff,
+ linefeed_byte,
+ cr_byte_value,
+ asbytes,
+ MSG_NAMES,
+ DEBUG,
+ xffffffff,
zero_byte,
)
from paramiko.py3compat import u, byte_ord
@@ -42,7 +47,7 @@ def compute_hmac(key, message, digest_class):
return HMAC(key, message, digest_class).digest()
-class NeedRekeyException (Exception):
+class NeedRekeyException(Exception):
"""
Exception indicating a rekey is needed.
"""
@@ -56,7 +61,7 @@ def first_arg(e):
return arg
-class Packetizer (object):
+class Packetizer(object):
"""
Implementation of the base SSH packet protocol.
"""
@@ -128,8 +133,15 @@ class Packetizer (object):
"""
self.__logger = log
- def set_outbound_cipher(self, block_engine, block_size, mac_engine,
- mac_size, mac_key, sdctr=False):
+ def set_outbound_cipher(
+ self,
+ block_engine,
+ block_size,
+ mac_engine,
+ mac_size,
+ mac_key,
+ sdctr=False,
+ ):
"""
Switch outbound data cipher.
"""
@@ -149,7 +161,8 @@ class Packetizer (object):
self.__need_rekey = False
def set_inbound_cipher(
- self, block_engine, block_size, mac_engine, mac_size, mac_key):
+ self, block_engine, block_size, mac_engine, mac_size, mac_key
+ ):
"""
Switch inbound data cipher.
"""
@@ -352,7 +365,7 @@ class Packetizer (object):
while linefeed_byte not in buf:
buf += self._read_timeout(timeout)
n = buf.index(linefeed_byte)
- self.__remainder = buf[n + 1:]
+ self.__remainder = buf[n + 1 :]
buf = buf[:n]
if (len(buf) > 0) and (buf[-1] == cr_byte_value):
buf = buf[:-1]
@@ -368,7 +381,7 @@ class Packetizer (object):
if cmd in MSG_NAMES:
cmd_name = MSG_NAMES[cmd]
else:
- cmd_name = '$%x' % cmd
+ cmd_name = "${:x}".format(cmd)
orig_len = len(data)
self.__write_lock.acquire()
try:
@@ -378,34 +391,38 @@ class Packetizer (object):
if self.__dump_packets:
self._log(
DEBUG,
- 'Write packet <%s>, length %d' % (cmd_name, orig_len))
- self._log(DEBUG, util.format_binary(packet, 'OUT: '))
+ "Write packet <{}>, length {}".format(cmd_name, orig_len),
+ )
+ self._log(DEBUG, util.format_binary(packet, "OUT: "))
if self.__block_engine_out is not None:
out = self.__block_engine_out.update(packet)
else:
out = packet
# + mac
if self.__block_engine_out is not None:
- payload = struct.pack(
- '>I', self.__sequence_number_out) + packet
+ payload = (
+ struct.pack(">I", self.__sequence_number_out) + packet
+ )
out += compute_hmac(
- self.__mac_key_out,
- payload,
- self.__mac_engine_out)[:self.__mac_size_out]
- self.__sequence_number_out = \
- (self.__sequence_number_out + 1) & xffffffff
+ self.__mac_key_out, payload, self.__mac_engine_out
+ )[: self.__mac_size_out]
+ self.__sequence_number_out = (
+ self.__sequence_number_out + 1
+ ) & xffffffff
self.write_all(out)
self.__sent_bytes += len(out)
self.__sent_packets += 1
sent_too_much = (
- self.__sent_packets >= self.REKEY_PACKETS or
- self.__sent_bytes >= self.REKEY_BYTES
+ self.__sent_packets >= self.REKEY_PACKETS
+ or self.__sent_bytes >= self.REKEY_BYTES
)
if sent_too_much and not self.__need_rekey:
# only ask once for rekeying
- self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes sent)' %
- (self.__sent_packets, self.__sent_bytes))
+ msg = "Rekeying (hit {} packets, {} bytes sent)"
+ self._log(
+ DEBUG, msg.format(self.__sent_packets, self.__sent_bytes)
+ )
self.__received_bytes_overflow = 0
self.__received_packets_overflow = 0
self._trigger_rekey()
@@ -424,39 +441,43 @@ class Packetizer (object):
if self.__block_engine_in is not None:
header = self.__block_engine_in.update(header)
if self.__dump_packets:
- self._log(DEBUG, util.format_binary(header, 'IN: '))
- packet_size = struct.unpack('>I', header[:4])[0]
+ self._log(DEBUG, util.format_binary(header, "IN: "))
+ packet_size = struct.unpack(">I", header[:4])[0]
# leftover contains decrypted bytes from the first block (after the
# length field)
leftover = header[4:]
if (packet_size - len(leftover)) % self.__block_size_in != 0:
- raise SSHException('Invalid packet blocking')
+ raise SSHException("Invalid packet blocking")
buf = self.read_all(packet_size + self.__mac_size_in - len(leftover))
- packet = buf[:packet_size - len(leftover)]
- post_packet = buf[packet_size - len(leftover):]
+ packet = buf[: packet_size - len(leftover)]
+ post_packet = buf[packet_size - len(leftover) :]
if self.__block_engine_in is not None:
packet = self.__block_engine_in.update(packet)
if self.__dump_packets:
- self._log(DEBUG, util.format_binary(packet, 'IN: '))
+ self._log(DEBUG, util.format_binary(packet, "IN: "))
packet = leftover + packet
if self.__mac_size_in > 0:
- mac = post_packet[:self.__mac_size_in]
- mac_payload = struct.pack(
- '>II', self.__sequence_number_in, packet_size) + packet
+ mac = post_packet[: self.__mac_size_in]
+ mac_payload = (
+ struct.pack(">II", self.__sequence_number_in, packet_size)
+ + packet
+ )
my_mac = compute_hmac(
- self.__mac_key_in,
- mac_payload,
- self.__mac_engine_in)[:self.__mac_size_in]
+ self.__mac_key_in, mac_payload, self.__mac_engine_in
+ )[: self.__mac_size_in]
if not util.constant_time_bytes_eq(my_mac, mac):
- raise SSHException('Mismatched MAC')
+ raise SSHException("Mismatched MAC")
padding = byte_ord(packet[0])
- payload = packet[1:packet_size - padding]
+ payload = packet[1 : packet_size - padding]
if self.__dump_packets:
self._log(
DEBUG,
- 'Got payload (%d bytes, %d padding)' % (packet_size, padding))
+ "Got payload ({} bytes, {} padding)".format(
+ packet_size, padding
+ ),
+ )
if self.__compress_engine_in is not None:
payload = self.__compress_engine_in(payload)
@@ -474,17 +495,24 @@ class Packetizer (object):
# dropping the connection
self.__received_bytes_overflow += raw_packet_size
self.__received_packets_overflow += 1
- if (self.__received_packets_overflow >=
- self.REKEY_PACKETS_OVERFLOW_MAX) or \
- (self.__received_bytes_overflow >=
- self.REKEY_BYTES_OVERFLOW_MAX):
+ if (
+ self.__received_packets_overflow
+ >= self.REKEY_PACKETS_OVERFLOW_MAX
+ ) or (
+ self.__received_bytes_overflow >= self.REKEY_BYTES_OVERFLOW_MAX
+ ):
raise SSHException(
- 'Remote transport is ignoring rekey requests')
- elif (self.__received_packets >= self.REKEY_PACKETS) or \
- (self.__received_bytes >= self.REKEY_BYTES):
+ "Remote transport is ignoring rekey requests"
+ )
+ elif (self.__received_packets >= self.REKEY_PACKETS) or (
+ self.__received_bytes >= self.REKEY_BYTES
+ ):
# only ask once for rekeying
- self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes received)' %
- (self.__received_packets, self.__received_bytes))
+ err = "Rekeying (hit {} packets, {} bytes received)"
+ self._log(
+ DEBUG,
+ err.format(self.__received_packets, self.__received_bytes),
+ )
self.__received_bytes_overflow = 0
self.__received_packets_overflow = 0
self._trigger_rekey()
@@ -493,11 +521,12 @@ class Packetizer (object):
if cmd in MSG_NAMES:
cmd_name = MSG_NAMES[cmd]
else:
- cmd_name = '$%x' % cmd
+ cmd_name = "${:x}".format(cmd)
if self.__dump_packets:
self._log(
DEBUG,
- 'Read packet <%s>, length %d' % (cmd_name, len(payload)))
+ "Read packet <{}>, length {}".format(cmd_name, len(payload)),
+ )
return cmd, msg
# ...protected...
@@ -513,9 +542,9 @@ class Packetizer (object):
def _check_keepalive(self):
if (
- not self.__keepalive_interval or
- not self.__block_engine_out or
- self.__need_rekey
+ not self.__keepalive_interval
+ or not self.__block_engine_out
+ or self.__need_rekey
):
# wait till we're encrypting, and not in the middle of rekeying
return
@@ -550,13 +579,13 @@ class Packetizer (object):
# pad up at least 4 bytes, to nearest block-size (usually 8)
bsize = self.__block_size_out
padding = 3 + bsize - ((len(payload) + 8) % bsize)
- packet = struct.pack('>IB', len(payload) + padding + 1, padding)
+ packet = struct.pack(">IB", len(payload) + padding + 1, padding)
packet += payload
if self.__sdctr_out or self.__block_engine_out is None:
# cute trick i caught openssh doing: if we're not encrypting or
# SDCTR mode (RFC4344),
# don't waste random bytes for the padding
- packet += (zero_byte * padding)
+ packet += zero_byte * padding
else:
packet += os.urandom(padding)
return packet
diff --git a/paramiko/pipe.py b/paramiko/pipe.py
index 6ca37703..e88a5e44 100644
--- a/paramiko/pipe.py
+++ b/paramiko/pipe.py
@@ -31,14 +31,15 @@ import socket
def make_pipe():
- if sys.platform[:3] != 'win':
+ if sys.platform[:3] != "win":
p = PosixPipe()
else:
p = WindowsPipe()
return p
-class PosixPipe (object):
+class PosixPipe(object):
+
def __init__(self):
self._rfd, self._wfd = os.pipe()
self._set = False
@@ -64,26 +65,27 @@ class PosixPipe (object):
if self._set or self._closed:
return
self._set = True
- os.write(self._wfd, b'*')
+ os.write(self._wfd, b"*")
def set_forever(self):
self._forever = True
self.set()
-class WindowsPipe (object):
+class WindowsPipe(object):
"""
On Windows, only an OS-level "WinSock" may be used in select(), but reads
and writes must be to the actual socket object.
"""
+
def __init__(self):
serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- serv.bind(('127.0.0.1', 0))
+ serv.bind(("127.0.0.1", 0))
serv.listen(1)
# need to save sockets in _rsock/_wsock so they don't get closed
self._rsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self._rsock.connect(('127.0.0.1', serv.getsockname()[1]))
+ self._rsock.connect(("127.0.0.1", serv.getsockname()[1]))
self._wsock, addr = serv.accept()
serv.close()
@@ -110,14 +112,15 @@ class WindowsPipe (object):
if self._set or self._closed:
return
self._set = True
- self._wsock.send(b'*')
+ self._wsock.send(b"*")
def set_forever(self):
self._forever = True
self.set()
-class OrPipe (object):
+class OrPipe(object):
+
def __init__(self, pipe):
self._set = False
self._partner = None
diff --git a/paramiko/pkey.py b/paramiko/pkey.py
index 67723be2..fa014800 100644
--- a/paramiko/pkey.py
+++ b/paramiko/pkey.py
@@ -43,23 +43,23 @@ class PKey(object):
# known encryption types for private key files:
_CIPHER_TABLE = {
- 'AES-128-CBC': {
- 'cipher': algorithms.AES,
- 'keysize': 16,
- 'blocksize': 16,
- 'mode': modes.CBC
+ "AES-128-CBC": {
+ "cipher": algorithms.AES,
+ "keysize": 16,
+ "blocksize": 16,
+ "mode": modes.CBC,
},
- 'AES-256-CBC': {
- 'cipher': algorithms.AES,
- 'keysize': 32,
- 'blocksize': 16,
- 'mode': modes.CBC
+ "AES-256-CBC": {
+ "cipher": algorithms.AES,
+ "keysize": 32,
+ "blocksize": 16,
+ "mode": modes.CBC,
},
- 'DES-EDE3-CBC': {
- 'cipher': algorithms.TripleDES,
- 'keysize': 24,
- 'blocksize': 8,
- 'mode': modes.CBC
+ "DES-EDE3-CBC": {
+ "cipher": algorithms.TripleDES,
+ "keysize": 24,
+ "blocksize": 8,
+ "mode": modes.CBC,
},
}
@@ -107,7 +107,7 @@ class PKey(object):
hs = hash(self)
ho = hash(other)
if hs != ho:
- return cmp(hs, ho) # noqa
+ return cmp(hs, ho) # noqa
return cmp(self.asbytes(), other.asbytes()) # noqa
def __eq__(self, other):
@@ -121,7 +121,7 @@ class PKey(object):
name of this private key type, in SSH terminology, as a `str` (for
example, ``"ssh-rsa"``).
"""
- return ''
+ return ""
def get_bits(self):
"""
@@ -158,7 +158,7 @@ class PKey(object):
:return: a base64 `string <str>` containing the public part of the key.
"""
- return u(encodebytes(self.asbytes())).replace('\n', '')
+ return u(encodebytes(self.asbytes())).replace("\n", "")
def sign_ssh_data(self, data):
"""
@@ -239,7 +239,7 @@ class PKey(object):
:raises: ``IOError`` -- if there was an error writing the file
:raises: `.SSHException` -- if the key is invalid
"""
- raise Exception('Not implemented in PKey')
+ raise Exception("Not implemented in PKey")
def write_private_key(self, file_obj, password=None):
"""
@@ -252,7 +252,7 @@ class PKey(object):
:raises: ``IOError`` -- if there was an error writing to the file
:raises: `.SSHException` -- if the key is invalid
"""
- raise Exception('Not implemented in PKey')
+ raise Exception("Not implemented in PKey")
def _read_private_key_file(self, tag, filename, password=None):
"""
@@ -275,58 +275,61 @@ class PKey(object):
encrypted, and ``password`` is ``None``.
:raises: `.SSHException` -- if the key file is invalid.
"""
- with open(filename, 'r') as f:
+ with open(filename, "r") as f:
data = self._read_private_key(tag, f, password)
return data
def _read_private_key(self, tag, f, password=None):
lines = f.readlines()
start = 0
- beginning_of_key = '-----BEGIN ' + tag + ' PRIVATE KEY-----'
+ beginning_of_key = "-----BEGIN " + tag + " PRIVATE KEY-----"
while start < len(lines) and lines[start].strip() != beginning_of_key:
start += 1
if start >= len(lines):
- raise SSHException('not a valid ' + tag + ' private key file')
+ raise SSHException("not a valid " + tag + " private key file")
# parse any headers first
headers = {}
start += 1
while start < len(lines):
- l = lines[start].split(': ')
+ l = lines[start].split(": ")
if len(l) == 1:
break
headers[l[0].lower()] = l[1].strip()
start += 1
# find end
end = start
- ending_of_key = '-----END ' + tag + ' PRIVATE KEY-----'
+ ending_of_key = "-----END " + tag + " PRIVATE KEY-----"
while end < len(lines) and lines[end].strip() != ending_of_key:
end += 1
# if we trudged to the end of the file, just try to cope.
try:
- data = decodebytes(b(''.join(lines[start:end])))
+ data = decodebytes(b("".join(lines[start:end])))
except base64.binascii.Error as e:
- raise SSHException('base64 decoding error: ' + str(e))
- if 'proc-type' not in headers:
+ raise SSHException("base64 decoding error: " + str(e))
+ if "proc-type" not in headers:
# unencryped: done
return data
# encrypted keyfile: will need a password
- if headers['proc-type'] != '4,ENCRYPTED':
+ proc_type = headers["proc-type"]
+ if proc_type != "4,ENCRYPTED":
raise SSHException(
- 'Unknown private key structure "%s"' % headers['proc-type'])
+ 'Unknown private key structure "{}"'.format(proc_type)
+ )
try:
- encryption_type, saltstr = headers['dek-info'].split(',')
+ encryption_type, saltstr = headers["dek-info"].split(",")
except:
raise SSHException("Can't parse DEK-info in private key file")
if encryption_type not in self._CIPHER_TABLE:
raise SSHException(
- 'Unknown private key cipher "%s"' % encryption_type)
+ 'Unknown private key cipher "{}"'.format(encryption_type)
+ )
# if no password was passed in,
# raise an exception pointing out that we need one
if password is None:
- raise PasswordRequiredException('Private key file is encrypted')
- cipher = self._CIPHER_TABLE[encryption_type]['cipher']
- keysize = self._CIPHER_TABLE[encryption_type]['keysize']
- mode = self._CIPHER_TABLE[encryption_type]['mode']
+ raise PasswordRequiredException("Private key file is encrypted")
+ cipher = self._CIPHER_TABLE[encryption_type]["cipher"]
+ keysize = self._CIPHER_TABLE[encryption_type]["keysize"]
+ mode = self._CIPHER_TABLE[encryption_type]["mode"]
salt = unhexlify(b(saltstr))
key = util.generate_key_bytes(md5, salt, password, keysize)
decryptor = Cipher(
@@ -349,7 +352,7 @@ class PKey(object):
:raises: ``IOError`` -- if there was an error writing the file.
"""
- with open(filename, 'w') as f:
+ with open(filename, "w") as f:
os.chmod(filename, o600)
self._write_private_key(f, key, format, password=password)
@@ -359,11 +362,11 @@ class PKey(object):
else:
encryption = serialization.BestAvailableEncryption(b(password))
- f.write(key.private_bytes(
- serialization.Encoding.PEM,
- format,
- encryption
- ).decode())
+ f.write(
+ key.private_bytes(
+ serialization.Encoding.PEM, format, encryption
+ ).decode()
+ )
def _check_type_and_load_cert(self, msg, key_type, cert_type):
"""
@@ -386,7 +389,7 @@ class PKey(object):
cert_types = [cert_types]
# Can't do much with no message, that should've been handled elsewhere
if msg is None:
- raise SSHException('Key object may not be empty')
+ raise SSHException("Key object may not be empty")
# First field is always key type, in either kind of object. (make sure
# we rewind before grabbing it - sometimes caller had to do their own
# introspection first!)
@@ -409,7 +412,7 @@ class PKey(object):
# (requires going back into per-type subclasses.)
msg.get_string()
else:
- err = 'Invalid key (class: {0}, data type: {1}'
+ err = "Invalid key (class: {}, data type: {}"
raise SSHException(err.format(self.__class__.__name__, type_))
def load_certificate(self, value):
@@ -432,14 +435,14 @@ class PKey(object):
successfully.
"""
if isinstance(value, Message):
- constructor = 'from_message'
+ constructor = "from_message"
elif os.path.isfile(value):
- constructor = 'from_file'
+ constructor = "from_file"
else:
- constructor = 'from_string'
+ constructor = "from_string"
blob = getattr(PublicBlob, constructor)(value)
if not blob.key_type.startswith(self.get_name()):
- err = "PublicBlob type {0} incompatible with key type {1}"
+ err = "PublicBlob type {} incompatible with key type {}"
raise ValueError(err.format(blob.key_type, self.get_name()))
self.public_blob = blob
@@ -462,6 +465,7 @@ class PublicBlob(object):
`from_message` for useful instantiation, the main constructor is
basically "I should be using ``attrs`` for this."
"""
+
def __init__(self, type_, blob, comment=None):
"""
Create a new public blob of given type and contents.
@@ -490,7 +494,7 @@ class PublicBlob(object):
"""
fields = string.split(None, 2)
if len(fields) < 2:
- msg = "Not enough fields for public blob: {0}"
+ msg = "Not enough fields for public blob: {}"
raise ValueError(msg.format(fields))
key_type = fields[0]
key_blob = decodebytes(b(fields[1]))
@@ -503,8 +507,10 @@ class PublicBlob(object):
m = Message(key_blob)
blob_type = m.get_text()
if blob_type != key_type:
- msg = "Invalid PublicBlob contents: key type={0!r}, but blob type={1!r}" # noqa
- raise ValueError(msg.format(key_type, blob_type))
+ deets = "key type={!r}, but blob type={!r}".format(
+ key_type, blob_type
+ )
+ raise ValueError("Invalid PublicBlob contents: {}".format(deets))
# All good? All good.
return cls(type_=key_type, blob=key_blob, comment=comment)
@@ -520,9 +526,9 @@ class PublicBlob(object):
return cls(type_=type_, blob=message.asbytes())
def __str__(self):
- ret = '{0} public key/certificate'.format(self.key_type)
+ ret = "{} public key/certificate".format(self.key_type)
if self.comment:
- ret += "- {0}".format(self.comment)
+ ret += "- {}".format(self.comment)
return ret
def __eq__(self, other):
diff --git a/paramiko/primes.py b/paramiko/primes.py
index 65617914..8dff7683 100644
--- a/paramiko/primes.py
+++ b/paramiko/primes.py
@@ -49,7 +49,7 @@ def _roll_random(n):
return num
-class ModulusPack (object):
+class ModulusPack(object):
"""
convenience object for holding the contents of the /etc/ssh/moduli file,
on systems that have such a file.
@@ -61,8 +61,15 @@ class ModulusPack (object):
self.discarded = []
def _parse_modulus(self, line):
- timestamp, mod_type, tests, tries, size, generator, modulus = \
- line.split()
+ (
+ timestamp,
+ mod_type,
+ tests,
+ tries,
+ size,
+ generator,
+ modulus,
+ ) = line.split()
mod_type = int(mod_type)
tests = int(tests)
tries = int(tries)
@@ -75,12 +82,13 @@ class ModulusPack (object):
# test 4 (more than just a small-prime sieve)
# tries < 100 if test & 4 (at least 100 tries of miller-rabin)
if (
- mod_type < 2 or
- tests < 4 or
- (tests & 4 and tests < 8 and tries < 100)
+ mod_type < 2
+ or tests < 4
+ or (tests & 4 and tests < 8 and tries < 100)
):
self.discarded.append(
- (modulus, 'does not meet basic requirements'))
+ (modulus, "does not meet basic requirements")
+ )
return
if generator == 0:
generator = 2
@@ -91,7 +99,8 @@ class ModulusPack (object):
bl = util.bit_length(modulus)
if (bl != size) and (bl != size + 1):
self.discarded.append(
- (modulus, 'incorrectly reported bit length %d' % size))
+ (modulus, "incorrectly reported bit length {}".format(size))
+ )
return
if bl not in self.pack:
self.pack[bl] = []
@@ -102,10 +111,10 @@ class ModulusPack (object):
:raises IOError: passed from any file operations that fail.
"""
self.pack = {}
- with open(filename, 'r') as f:
+ with open(filename, "r") as f:
for line in f:
line = line.strip()
- if (len(line) == 0) or (line[0] == '#'):
+ if (len(line) == 0) or (line[0] == "#"):
continue
try:
self._parse_modulus(line)
@@ -115,7 +124,7 @@ class ModulusPack (object):
def get_modulus(self, min, prefer, max):
bitsizes = sorted(self.pack.keys())
if len(bitsizes) == 0:
- raise SSHException('no moduli available')
+ raise SSHException("no moduli available")
good = -1
# find nearest bitsize >= preferred
for b in bitsizes:
diff --git a/paramiko/proxy.py b/paramiko/proxy.py
index c4ec627c..444c47b6 100644
--- a/paramiko/proxy.py
+++ b/paramiko/proxy.py
@@ -39,6 +39,7 @@ class ProxyCommand(ClosingContextManager):
Instances of this class may be used as context managers.
"""
+
def __init__(self, command_line):
"""
Create a new CommandProxy instance. The instance created by this
@@ -50,9 +51,11 @@ class ProxyCommand(ClosingContextManager):
# NOTE: subprocess import done lazily so platforms without it (e.g.
# GAE) can still import us during overall Paramiko load.
from subprocess import Popen, PIPE
+
self.cmd = shlsplit(command_line)
- self.process = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE,
- bufsize=0)
+ self.process = Popen(
+ self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=0
+ )
self.timeout = None
def send(self, content):
@@ -69,7 +72,7 @@ class ProxyCommand(ClosingContextManager):
# 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 ProxyCommandFailure(' '.join(self.cmd), e.strerror)
+ raise ProxyCommandFailure(" ".join(self.cmd), e.strerror)
return len(content)
def recv(self, size):
@@ -81,21 +84,21 @@ class ProxyCommand(ClosingContextManager):
:return: the string of bytes read, which may be shorter than requested
"""
try:
- buffer = b''
+ buffer = b""
start = time.time()
while len(buffer) < size:
select_timeout = None
if self.timeout is not None:
- elapsed = (time.time() - start)
+ elapsed = time.time() - start
if elapsed >= self.timeout:
raise socket.timeout()
select_timeout = self.timeout - elapsed
- r, w, x = select(
- [self.process.stdout], [], [], select_timeout)
+ r, w, x = select([self.process.stdout], [], [], select_timeout)
if r and r[0] == self.process.stdout:
buffer += os.read(
- self.process.stdout.fileno(), size - len(buffer))
+ self.process.stdout.fileno(), size - len(buffer)
+ )
return buffer
except socket.timeout:
if buffer:
@@ -103,7 +106,7 @@ class ProxyCommand(ClosingContextManager):
return buffer
raise # socket.timeout is a subclass of IOError
except IOError as e:
- raise ProxyCommandFailure(' '.join(self.cmd), e.strerror)
+ raise ProxyCommandFailure(" ".join(self.cmd), e.strerror)
def close(self):
os.kill(self.process.pid, signal.SIGTERM)
diff --git a/paramiko/py3compat.py b/paramiko/py3compat.py
index 6703ace8..795b9b0e 100644
--- a/paramiko/py3compat.py
+++ b/paramiko/py3compat.py
@@ -1,11 +1,31 @@
import sys
import base64
-__all__ = ['PY2', 'string_types', 'integer_types', 'text_type', 'bytes_types',
- 'bytes', 'long', 'input', 'decodebytes', 'encodebytes',
- 'bytestring', 'byte_ord', 'byte_chr', 'byte_mask', 'b', 'u', 'b2s',
- 'StringIO', 'BytesIO', 'is_callable', 'MAXSIZE',
- 'next', 'builtins']
+__all__ = [
+ "PY2",
+ "string_types",
+ "integer_types",
+ "text_type",
+ "bytes_types",
+ "bytes",
+ "long",
+ "input",
+ "decodebytes",
+ "encodebytes",
+ "bytestring",
+ "byte_ord",
+ "byte_chr",
+ "byte_mask",
+ "b",
+ "u",
+ "b2s",
+ "StringIO",
+ "BytesIO",
+ "is_callable",
+ "MAXSIZE",
+ "next",
+ "builtins",
+]
PY2 = sys.version_info[0] < 3
@@ -22,22 +42,18 @@ if PY2:
import __builtin__ as builtins
-
def bytestring(s): # NOQA
if isinstance(s, unicode): # NOQA
- return s.encode('utf-8')
+ return s.encode("utf-8")
return s
-
byte_ord = ord # NOQA
byte_chr = chr # NOQA
-
def byte_mask(c, mask):
return chr(ord(c) & mask)
-
- def b(s, encoding='utf8'): # NOQA
+ def b(s, encoding="utf8"): # NOQA
"""cast unicode or bytes to bytes"""
if isinstance(s, str):
return s
@@ -46,10 +62,9 @@ if PY2:
elif isinstance(s, buffer): # NOQA
return s
else:
- raise TypeError("Expected unicode or bytes, got %r" % s)
-
+ raise TypeError("Expected unicode or bytes, got {!r}".format(s))
- def u(s, encoding='utf8'): # NOQA
+ def u(s, encoding="utf8"): # NOQA
"""cast bytes or unicode to unicode"""
if isinstance(s, str):
return s.decode(encoding)
@@ -58,55 +73,54 @@ if PY2:
elif isinstance(s, buffer): # NOQA
return s.decode(encoding)
else:
- raise TypeError("Expected unicode or bytes, got %r" % s)
-
+ raise TypeError("Expected unicode or bytes, got {!r}".format(s))
def b2s(s):
return s
-
import cStringIO
+
StringIO = cStringIO.StringIO
BytesIO = StringIO
-
def is_callable(c): # NOQA
return callable(c)
-
def get_next(c): # NOQA
return c.next
-
def next(c):
return c.next()
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
class X(object):
+
def __len__(self):
return 1 << 31
-
try:
len(X())
except OverflowError:
# 32-bit
- MAXSIZE = int((1 << 31) - 1) # NOQA
+ MAXSIZE = int((1 << 31) - 1) # NOQA
else:
# 64-bit
- MAXSIZE = int((1 << 63) - 1) # NOQA
+ MAXSIZE = int((1 << 63) - 1) # NOQA
del X
else:
import collections
import struct
import builtins
+
string_types = str
text_type = str
bytes = bytes
bytes_types = bytes
integer_types = int
+
class long(int):
pass
+
input = input
decodebytes = base64.decodebytes
encodebytes = base64.encodebytes
@@ -122,36 +136,37 @@ else:
def byte_chr(c):
assert isinstance(c, int)
- return struct.pack('B', c)
+ return struct.pack("B", c)
def byte_mask(c, mask):
assert isinstance(c, int)
- return struct.pack('B', c & mask)
+ return struct.pack("B", c & mask)
- def b(s, encoding='utf8'):
+ def b(s, encoding="utf8"):
"""cast unicode or bytes to bytes"""
if isinstance(s, bytes):
return s
elif isinstance(s, str):
return s.encode(encoding)
else:
- raise TypeError("Expected unicode or bytes, got %r" % s)
+ raise TypeError("Expected unicode or bytes, got {!r}".format(s))
- def u(s, encoding='utf8'):
+ def u(s, encoding="utf8"):
"""cast bytes or unicode to unicode"""
if isinstance(s, bytes):
return s.decode(encoding)
elif isinstance(s, str):
return s
else:
- raise TypeError("Expected unicode or bytes, got %r" % s)
+ raise TypeError("Expected unicode or bytes, got {!r}".format(s))
def b2s(s):
return s.decode() if isinstance(s, bytes) else s
import io
- StringIO = io.StringIO # NOQA
- BytesIO = io.BytesIO # NOQA
+
+ StringIO = io.StringIO # NOQA
+ BytesIO = io.BytesIO # NOQA
def is_callable(c):
return isinstance(c, collections.Callable)
@@ -161,4 +176,4 @@ else:
next = next
- MAXSIZE = sys.maxsize # NOQA
+ MAXSIZE = sys.maxsize # NOQA
diff --git a/paramiko/rsakey.py b/paramiko/rsakey.py
index 8dfcfb01..442bfe1f 100644
--- a/paramiko/rsakey.py
+++ b/paramiko/rsakey.py
@@ -37,8 +37,15 @@ class RSAKey(PKey):
data.
"""
- def __init__(self, msg=None, data=None, filename=None, password=None,
- key=None, file_obj=None):
+ def __init__(
+ self,
+ msg=None,
+ data=None,
+ filename=None,
+ password=None,
+ key=None,
+ file_obj=None,
+ ):
self.key = None
self.public_blob = None
if file_obj is not None:
@@ -54,8 +61,8 @@ class RSAKey(PKey):
else:
self._check_type_and_load_cert(
msg=msg,
- key_type='ssh-rsa',
- cert_type='ssh-rsa-cert-v01@openssh.com',
+ key_type="ssh-rsa",
+ cert_type="ssh-rsa-cert-v01@openssh.com",
)
self.key = rsa.RSAPublicNumbers(
e=msg.get_mpint(), n=msg.get_mpint()
@@ -74,7 +81,7 @@ class RSAKey(PKey):
def asbytes(self):
m = Message()
- m.add_string('ssh-rsa')
+ m.add_string("ssh-rsa")
m.add_mpint(self.public_numbers.e)
m.add_mpint(self.public_numbers.n)
return m.asbytes()
@@ -89,14 +96,15 @@ class RSAKey(PKey):
# tries stuffing it into ASCII for whatever godforsaken reason
return self.asbytes()
else:
- return self.asbytes().decode('utf8', errors='ignore')
+ return self.asbytes().decode("utf8", errors="ignore")
def __hash__(self):
- return hash((self.get_name(), self.public_numbers.e,
- self.public_numbers.n))
+ return hash(
+ (self.get_name(), self.public_numbers.e, self.public_numbers.n)
+ )
def get_name(self):
- return 'ssh-rsa'
+ return "ssh-rsa"
def get_bits(self):
return self.size
@@ -106,18 +114,16 @@ class RSAKey(PKey):
def sign_ssh_data(self, data):
sig = self.key.sign(
- data,
- padding=padding.PKCS1v15(),
- algorithm=hashes.SHA1(),
+ data, padding=padding.PKCS1v15(), algorithm=hashes.SHA1()
)
m = Message()
- m.add_string('ssh-rsa')
+ m.add_string("ssh-rsa")
m.add_string(sig)
return m
def verify_ssh_sig(self, data, msg):
- if msg.get_text() != 'ssh-rsa':
+ if msg.get_text() != "ssh-rsa":
return False
key = self.key
if isinstance(key, rsa.RSAPrivateKey):
@@ -137,7 +143,7 @@ class RSAKey(PKey):
filename,
self.key,
serialization.PrivateFormat.TraditionalOpenSSL,
- password=password
+ password=password,
)
def write_private_key(self, file_obj, password=None):
@@ -145,7 +151,7 @@ class RSAKey(PKey):
file_obj,
self.key,
serialization.PrivateFormat.TraditionalOpenSSL,
- password=password
+ password=password,
)
@staticmethod
@@ -166,11 +172,11 @@ class RSAKey(PKey):
# ...internals...
def _from_private_key_file(self, filename, password):
- data = self._read_private_key_file('RSA', filename, password)
+ data = self._read_private_key_file("RSA", filename, password)
self._decode_key(data)
def _from_private_key(self, file_obj, password):
- data = self._read_private_key('RSA', file_obj, password)
+ data = self._read_private_key("RSA", file_obj, password)
self._decode_key(data)
def _decode_key(self, data):
diff --git a/paramiko/server.py b/paramiko/server.py
index c96126e9..2fe9cc19 100644
--- a/paramiko/server.py
+++ b/paramiko/server.py
@@ -23,13 +23,16 @@
import threading
from paramiko import util
from paramiko.common import (
- DEBUG, ERROR, OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, AUTH_FAILED,
+ DEBUG,
+ ERROR,
+ OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED,
+ AUTH_FAILED,
AUTH_SUCCESSFUL,
)
from paramiko.py3compat import string_types
-class ServerInterface (object):
+class ServerInterface(object):
"""
This class defines an interface for controlling the behavior of Paramiko
in server mode.
@@ -99,7 +102,7 @@ class ServerInterface (object):
:param str username: the username requesting authentication.
:return: a comma-separated `str` of authentication types
"""
- return 'password'
+ return "password"
def check_auth_none(self, username):
"""
@@ -223,7 +226,7 @@ class ServerInterface (object):
The default implementation always returns ``AUTH_FAILED``.
- :param list responses: list of `str` responses from the client
+ :param responses: list of `str` responses from the client
:return:
``AUTH_FAILED`` if the authentication fails; ``AUTH_SUCCESSFUL`` if
it succeeds; ``AUTH_PARTIALLY_SUCCESSFUL`` if the interactive auth
@@ -233,9 +236,9 @@ class ServerInterface (object):
"""
return AUTH_FAILED
- def check_auth_gssapi_with_mic(self, username,
- gss_authenticated=AUTH_FAILED,
- cc_file=None):
+ def check_auth_gssapi_with_mic(
+ self, username, gss_authenticated=AUTH_FAILED, cc_file=None
+ ):
"""
Authenticate the given user to the server if he is a valid krb5
principal.
@@ -263,9 +266,9 @@ class ServerInterface (object):
return AUTH_SUCCESSFUL
return AUTH_FAILED
- def check_auth_gssapi_keyex(self, username,
- gss_authenticated=AUTH_FAILED,
- cc_file=None):
+ def check_auth_gssapi_keyex(
+ self, username, gss_authenticated=AUTH_FAILED, cc_file=None
+ ):
"""
Authenticate the given user to the server if he is a valid krb5
principal and GSS-API Key Exchange was performed.
@@ -372,8 +375,8 @@ class ServerInterface (object):
# ...Channel requests...
def check_channel_pty_request(
- self, channel, term, width, height, pixelwidth, pixelheight,
- modes):
+ self, channel, term, width, height, pixelwidth, pixelheight, modes
+ ):
"""
Determine if a pseudo-terminal of the given dimensions (usually
requested for shell access) can be provided on the given channel.
@@ -460,7 +463,8 @@ class ServerInterface (object):
return True
def check_channel_window_change_request(
- self, channel, width, height, pixelwidth, pixelheight):
+ self, channel, width, height, pixelwidth, pixelheight
+ ):
"""
Determine if the pseudo-terminal on the given channel can be resized.
This only makes sense if a pty was previously allocated on it.
@@ -479,8 +483,13 @@ class ServerInterface (object):
return False
def check_channel_x11_request(
- self, channel, single_connection, auth_protocol, auth_cookie,
- screen_number):
+ self,
+ channel,
+ single_connection,
+ auth_protocol,
+ auth_cookie,
+ screen_number,
+ ):
"""
Determine if the client will be provided with an X11 session. If this
method returns ``True``, X11 applications should be routed through new
@@ -584,12 +593,13 @@ class ServerInterface (object):
"""
return (None, None)
-class InteractiveQuery (object):
+
+class InteractiveQuery(object):
"""
A query (set of prompts) for a user during interactive authentication.
"""
- def __init__(self, name='', instructions='', *prompts):
+ def __init__(self, name="", instructions="", *prompts):
"""
Create a new interactive query to send to the client. The name and
instructions are optional, but are generally displayed to the end
@@ -623,7 +633,7 @@ class InteractiveQuery (object):
self.prompts.append((prompt, echo))
-class SubsystemHandler (threading.Thread):
+class SubsystemHandler(threading.Thread):
"""
Handler for a subsytem in server mode. If you create a subclass of this
class and pass it to `.Transport.set_subsystem_handler`, an object of this
@@ -637,6 +647,7 @@ class SubsystemHandler (threading.Thread):
``MP3Handler`` will be created, and `start_subsystem` will be called on
it from a new thread.
"""
+
def __init__(self, channel, name, server):
"""
Create a new handler for a channel. This is used by `.ServerInterface`
@@ -667,14 +678,15 @@ class SubsystemHandler (threading.Thread):
def _run(self):
try:
self.__transport._log(
- DEBUG, 'Starting handler for subsystem %s' % self.__name)
+ DEBUG, "Starting handler for subsystem {}".format(self.__name)
+ )
self.start_subsystem(self.__name, self.__transport, self.__channel)
except Exception as e:
self.__transport._log(
ERROR,
- 'Exception in subsystem handler for "{0}": {1}'.format(
+ 'Exception in subsystem handler for "{}": {}'.format(
self.__name, e
- )
+ ),
)
self.__transport._log(ERROR, util.tb_strings())
try:
diff --git a/paramiko/sftp.py b/paramiko/sftp.py
index e6786d10..6aa4ce44 100644
--- a/paramiko/sftp.py
+++ b/paramiko/sftp.py
@@ -26,27 +26,54 @@ from paramiko.message import Message
from paramiko.py3compat import byte_chr, byte_ord
-CMD_INIT, CMD_VERSION, CMD_OPEN, CMD_CLOSE, CMD_READ, CMD_WRITE, CMD_LSTAT, \
- CMD_FSTAT, CMD_SETSTAT, CMD_FSETSTAT, CMD_OPENDIR, CMD_READDIR, \
- CMD_REMOVE, CMD_MKDIR, CMD_RMDIR, CMD_REALPATH, CMD_STAT, CMD_RENAME, \
- CMD_READLINK, CMD_SYMLINK = range(1, 21)
-CMD_STATUS, CMD_HANDLE, CMD_DATA, CMD_NAME, CMD_ATTRS = range(101, 106)
-CMD_EXTENDED, CMD_EXTENDED_REPLY = range(200, 202)
+(
+ CMD_INIT,
+ CMD_VERSION,
+ CMD_OPEN,
+ CMD_CLOSE,
+ CMD_READ,
+ CMD_WRITE,
+ CMD_LSTAT,
+ CMD_FSTAT,
+ CMD_SETSTAT,
+ CMD_FSETSTAT,
+ CMD_OPENDIR,
+ CMD_READDIR,
+ CMD_REMOVE,
+ CMD_MKDIR,
+ CMD_RMDIR,
+ CMD_REALPATH,
+ CMD_STAT,
+ CMD_RENAME,
+ CMD_READLINK,
+ CMD_SYMLINK,
+) = range(1, 21)
+(CMD_STATUS, CMD_HANDLE, CMD_DATA, CMD_NAME, CMD_ATTRS) = range(101, 106)
+(CMD_EXTENDED, CMD_EXTENDED_REPLY) = range(200, 202)
SFTP_OK = 0
-SFTP_EOF, SFTP_NO_SUCH_FILE, SFTP_PERMISSION_DENIED, SFTP_FAILURE, \
- SFTP_BAD_MESSAGE, SFTP_NO_CONNECTION, SFTP_CONNECTION_LOST, \
- SFTP_OP_UNSUPPORTED = range(1, 9)
-
-SFTP_DESC = ['Success',
- 'End of file',
- 'No such file',
- 'Permission denied',
- 'Failure',
- 'Bad message',
- 'No connection',
- 'Connection lost',
- 'Operation unsupported']
+(
+ SFTP_EOF,
+ SFTP_NO_SUCH_FILE,
+ SFTP_PERMISSION_DENIED,
+ SFTP_FAILURE,
+ SFTP_BAD_MESSAGE,
+ SFTP_NO_CONNECTION,
+ SFTP_CONNECTION_LOST,
+ SFTP_OP_UNSUPPORTED,
+) = range(1, 9)
+
+SFTP_DESC = [
+ "Success",
+ "End of file",
+ "No such file",
+ "Permission denied",
+ "Failure",
+ "Bad message",
+ "No connection",
+ "Connection lost",
+ "Operation unsupported",
+]
SFTP_FLAG_READ = 0x1
SFTP_FLAG_WRITE = 0x2
@@ -60,54 +87,55 @@ _VERSION = 3
# for debugging
CMD_NAMES = {
- CMD_INIT: 'init',
- CMD_VERSION: 'version',
- CMD_OPEN: 'open',
- CMD_CLOSE: 'close',
- CMD_READ: 'read',
- CMD_WRITE: 'write',
- CMD_LSTAT: 'lstat',
- CMD_FSTAT: 'fstat',
- CMD_SETSTAT: 'setstat',
- CMD_FSETSTAT: 'fsetstat',
- CMD_OPENDIR: 'opendir',
- CMD_READDIR: 'readdir',
- CMD_REMOVE: 'remove',
- CMD_MKDIR: 'mkdir',
- CMD_RMDIR: 'rmdir',
- CMD_REALPATH: 'realpath',
- CMD_STAT: 'stat',
- CMD_RENAME: 'rename',
- CMD_READLINK: 'readlink',
- CMD_SYMLINK: 'symlink',
- CMD_STATUS: 'status',
- CMD_HANDLE: 'handle',
- CMD_DATA: 'data',
- CMD_NAME: 'name',
- CMD_ATTRS: 'attrs',
- CMD_EXTENDED: 'extended',
- CMD_EXTENDED_REPLY: 'extended_reply'
+ CMD_INIT: "init",
+ CMD_VERSION: "version",
+ CMD_OPEN: "open",
+ CMD_CLOSE: "close",
+ CMD_READ: "read",
+ CMD_WRITE: "write",
+ CMD_LSTAT: "lstat",
+ CMD_FSTAT: "fstat",
+ CMD_SETSTAT: "setstat",
+ CMD_FSETSTAT: "fsetstat",
+ CMD_OPENDIR: "opendir",
+ CMD_READDIR: "readdir",
+ CMD_REMOVE: "remove",
+ CMD_MKDIR: "mkdir",
+ CMD_RMDIR: "rmdir",
+ CMD_REALPATH: "realpath",
+ CMD_STAT: "stat",
+ CMD_RENAME: "rename",
+ CMD_READLINK: "readlink",
+ CMD_SYMLINK: "symlink",
+ CMD_STATUS: "status",
+ CMD_HANDLE: "handle",
+ CMD_DATA: "data",
+ CMD_NAME: "name",
+ CMD_ATTRS: "attrs",
+ CMD_EXTENDED: "extended",
+ CMD_EXTENDED_REPLY: "extended_reply",
}
-class SFTPError (Exception):
+class SFTPError(Exception):
pass
-class BaseSFTP (object):
+class BaseSFTP(object):
+
def __init__(self):
- self.logger = util.get_logger('paramiko.sftp')
+ self.logger = util.get_logger("paramiko.sftp")
self.sock = None
self.ultra_debug = False
# ...internals...
def _send_version(self):
- self._send_packet(CMD_INIT, struct.pack('>I', _VERSION))
+ self._send_packet(CMD_INIT, struct.pack(">I", _VERSION))
t, data = self._read_packet()
if t != CMD_VERSION:
- raise SFTPError('Incompatible sftp protocol')
- version = struct.unpack('>I', data[:4])[0]
+ raise SFTPError("Incompatible sftp protocol")
+ version = struct.unpack(">I", data[:4])[0]
# if version != _VERSION:
# raise SFTPError('Incompatible sftp protocol')
return version
@@ -117,10 +145,10 @@ class BaseSFTP (object):
# client finishes sending INIT.
t, data = self._read_packet()
if t != CMD_INIT:
- raise SFTPError('Incompatible sftp protocol')
- version = struct.unpack('>I', data[:4])[0]
+ raise SFTPError("Incompatible sftp protocol")
+ version = struct.unpack(">I", data[:4])[0]
# advertise that we support "check-file"
- extension_pairs = ['check-file', 'md5,sha1']
+ extension_pairs = ["check-file", "md5,sha1"]
msg = Message()
msg.add_int(_VERSION)
msg.add(*extension_pairs)
@@ -165,9 +193,9 @@ class BaseSFTP (object):
def _send_packet(self, t, packet):
packet = asbytes(packet)
- out = struct.pack('>I', len(packet) + 1) + byte_chr(t) + packet
+ out = struct.pack(">I", len(packet) + 1) + byte_chr(t) + packet
if self.ultra_debug:
- self._log(DEBUG, util.format_binary(out, 'OUT: '))
+ self._log(DEBUG, util.format_binary(out, "OUT: "))
self._write_all(out)
def _read_packet(self):
@@ -175,11 +203,11 @@ class BaseSFTP (object):
# most sftp servers won't accept packets larger than about 32k, so
# anything with the high byte set (> 16MB) is just garbage.
if byte_ord(x[0]):
- raise SFTPError('Garbage packet received')
- size = struct.unpack('>I', x)[0]
+ raise SFTPError("Garbage packet received")
+ size = struct.unpack(">I", x)[0]
data = self._read_all(size)
if self.ultra_debug:
- self._log(DEBUG, util.format_binary(data, 'IN: '))
+ self._log(DEBUG, util.format_binary(data, "IN: "))
if size > 0:
t = byte_ord(data[0])
return t, data[1:]
diff --git a/paramiko/sftp_attr.py b/paramiko/sftp_attr.py
index 5597948a..f16ac746 100644
--- a/paramiko/sftp_attr.py
+++ b/paramiko/sftp_attr.py
@@ -22,7 +22,7 @@ from paramiko.common import x80000000, o700, o70, xffffffff
from paramiko.py3compat import long, b
-class SFTPAttributes (object):
+class SFTPAttributes(object):
"""
Representation of the attributes of a file (or proxied file) for SFTP in
client or server mode. It attemps to mirror the object returned by
@@ -82,7 +82,7 @@ class SFTPAttributes (object):
return attr
def __repr__(self):
- return '<SFTPAttributes: %s>' % self._debug_str()
+ return "<SFTPAttributes: {}>".format(self._debug_str())
# ...internals...
@classmethod
@@ -144,29 +144,29 @@ class SFTPAttributes (object):
return
def _debug_str(self):
- out = '[ '
+ out = "[ "
if self.st_size is not None:
- out += 'size=%d ' % self.st_size
+ out += "size={} ".format(self.st_size)
if (self.st_uid is not None) and (self.st_gid is not None):
- out += 'uid=%d gid=%d ' % (self.st_uid, self.st_gid)
+ out += "uid={} gid={} ".format(self.st_uid, self.st_gid)
if self.st_mode is not None:
- out += 'mode=' + oct(self.st_mode) + ' '
+ out += "mode=" + oct(self.st_mode) + " "
if (self.st_atime is not None) and (self.st_mtime is not None):
- out += 'atime=%d mtime=%d ' % (self.st_atime, self.st_mtime)
+ out += "atime={} mtime={} ".format(self.st_atime, self.st_mtime)
for k, v in self.attr.items():
- out += '"%s"=%r ' % (str(k), v)
- out += ']'
+ out += '"{}"={!r} '.format(str(k), v)
+ out += "]"
return out
@staticmethod
def _rwx(n, suid, sticky=False):
if suid:
suid = 2
- out = '-r'[n >> 2] + '-w'[(n >> 1) & 1]
+ out = "-r"[n >> 2] + "-w"[(n >> 1) & 1]
if sticky:
- out += '-xTt'[suid + (n & 1)]
+ out += "-xTt"[suid + (n & 1)]
else:
- out += '-xSs'[suid + (n & 1)]
+ out += "-xSs"[suid + (n & 1)]
return out
def __str__(self):
@@ -174,42 +174,47 @@ class SFTPAttributes (object):
if self.st_mode is not None:
kind = stat.S_IFMT(self.st_mode)
if kind == stat.S_IFIFO:
- ks = 'p'
+ ks = "p"
elif kind == stat.S_IFCHR:
- ks = 'c'
+ ks = "c"
elif kind == stat.S_IFDIR:
- ks = 'd'
+ ks = "d"
elif kind == stat.S_IFBLK:
- ks = 'b'
+ ks = "b"
elif kind == stat.S_IFREG:
- ks = '-'
+ ks = "-"
elif kind == stat.S_IFLNK:
- ks = 'l'
+ ks = "l"
elif kind == stat.S_IFSOCK:
- ks = 's'
+ ks = "s"
else:
- ks = '?'
+ ks = "?"
ks += self._rwx(
- (self.st_mode & o700) >> 6, self.st_mode & stat.S_ISUID)
+ (self.st_mode & o700) >> 6, self.st_mode & stat.S_ISUID
+ )
ks += self._rwx(
- (self.st_mode & o70) >> 3, self.st_mode & stat.S_ISGID)
+ (self.st_mode & o70) >> 3, self.st_mode & stat.S_ISGID
+ )
ks += self._rwx(
- self.st_mode & 7, self.st_mode & stat.S_ISVTX, True)
+ self.st_mode & 7, self.st_mode & stat.S_ISVTX, True
+ )
else:
- ks = '?---------'
+ ks = "?---------"
# compute display date
if (self.st_mtime is None) or (self.st_mtime == xffffffff):
# shouldn't really happen
- datestr = '(unknown date)'
+ datestr = "(unknown date)"
else:
if abs(time.time() - self.st_mtime) > 15552000:
# (15552000 = 6 months)
datestr = time.strftime(
- '%d %b %Y', time.localtime(self.st_mtime))
+ "%d %b %Y", time.localtime(self.st_mtime)
+ )
else:
datestr = time.strftime(
- '%d %b %H:%M', time.localtime(self.st_mtime))
- filename = getattr(self, 'filename', '?')
+ "%d %b %H:%M", time.localtime(self.st_mtime)
+ )
+ filename = getattr(self, "filename", "?")
# not all servers support uid/gid
uid = self.st_uid
@@ -222,8 +227,17 @@ class SFTPAttributes (object):
if size is None:
size = 0
- return '%s 1 %-8d %-8d %8d %-12s %s' % (
- ks, uid, gid, size, datestr, filename)
+ # TODO: not sure this actually worked as expected beforehand, leaving
+ # it untouched for the time being, re: .format() upgrade, until someone
+ # has time to doublecheck
+ return "%s 1 %-8d %-8d %8d %-12s %s" % (
+ ks,
+ uid,
+ gid,
+ size,
+ datestr,
+ filename,
+ )
def asbytes(self):
return b(str(self))
diff --git a/paramiko/sftp_client.py b/paramiko/sftp_client.py
index 14b8b58a..f6d59d54 100644
--- a/paramiko/sftp_client.py
+++ b/paramiko/sftp_client.py
@@ -30,12 +30,37 @@ from paramiko.message import Message
from paramiko.common import INFO, DEBUG, o777
from paramiko.py3compat import bytestring, b, u, long
from paramiko.sftp import (
- BaseSFTP, CMD_OPENDIR, CMD_HANDLE, SFTPError, CMD_READDIR, CMD_NAME,
- CMD_CLOSE, SFTP_FLAG_READ, SFTP_FLAG_WRITE, SFTP_FLAG_CREATE,
- SFTP_FLAG_TRUNC, SFTP_FLAG_APPEND, SFTP_FLAG_EXCL, CMD_OPEN, CMD_REMOVE,
- CMD_RENAME, CMD_MKDIR, CMD_RMDIR, CMD_STAT, CMD_ATTRS, CMD_LSTAT,
- CMD_SYMLINK, CMD_SETSTAT, CMD_READLINK, CMD_REALPATH, CMD_STATUS,
- CMD_EXTENDED, SFTP_OK, SFTP_EOF, SFTP_NO_SUCH_FILE, SFTP_PERMISSION_DENIED,
+ BaseSFTP,
+ CMD_OPENDIR,
+ CMD_HANDLE,
+ SFTPError,
+ CMD_READDIR,
+ CMD_NAME,
+ CMD_CLOSE,
+ SFTP_FLAG_READ,
+ SFTP_FLAG_WRITE,
+ SFTP_FLAG_CREATE,
+ SFTP_FLAG_TRUNC,
+ SFTP_FLAG_APPEND,
+ SFTP_FLAG_EXCL,
+ CMD_OPEN,
+ CMD_REMOVE,
+ CMD_RENAME,
+ CMD_MKDIR,
+ CMD_RMDIR,
+ CMD_STAT,
+ CMD_ATTRS,
+ CMD_LSTAT,
+ CMD_SYMLINK,
+ CMD_SETSTAT,
+ CMD_READLINK,
+ CMD_REALPATH,
+ CMD_STATUS,
+ CMD_EXTENDED,
+ SFTP_OK,
+ SFTP_EOF,
+ SFTP_NO_SUCH_FILE,
+ SFTP_PERMISSION_DENIED,
)
from paramiko.sftp_attr import SFTPAttributes
@@ -51,15 +76,15 @@ def _to_unicode(s):
probably doesn't know the filename's encoding.
"""
try:
- return s.encode('ascii')
+ return s.encode("ascii")
except (UnicodeError, AttributeError):
try:
- return s.decode('utf-8')
+ return s.decode("utf-8")
except UnicodeError:
return s
-b_slash = b'/'
+b_slash = b"/"
class SFTPClient(BaseSFTP, ClosingContextManager):
@@ -71,6 +96,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
Instances of this class may be used as context managers.
"""
+
def __init__(self, sock):
"""
Create an SFTP client from an existing `.Channel`. The channel
@@ -97,15 +123,19 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
# override default logger
transport = self.sock.get_transport()
self.logger = util.get_logger(
- transport.get_log_channel() + '.sftp')
+ transport.get_log_channel() + ".sftp"
+ )
self.ultra_debug = transport.get_hexdump()
try:
server_version = self._send_version()
except EOFError:
- raise SSHException('EOF during negotiation')
+ raise SSHException("EOF during negotiation")
self._log(
INFO,
- 'Opened sftp connection (server version %d)' % server_version)
+ "Opened sftp connection (server version {})".format(
+ server_version
+ ),
+ )
@classmethod
def from_transport(cls, t, window_size=None, max_packet_size=None):
@@ -131,11 +161,12 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
.. versionchanged:: 1.15
Added the ``window_size`` and ``max_packet_size`` arguments.
"""
- chan = t.open_session(window_size=window_size,
- max_packet_size=max_packet_size)
+ chan = t.open_session(
+ window_size=window_size, max_packet_size=max_packet_size
+ )
if chan is None:
return None
- chan.invoke_subsystem('sftp')
+ chan.invoke_subsystem("sftp")
return cls(chan)
def _log(self, level, msg, *args):
@@ -143,12 +174,16 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
for m in msg:
self._log(level, m, *args)
else:
+ # NOTE: these bits MUST continue using %-style format junk because
+ # logging.Logger.log() explicitly requires it. Grump.
# escape '%' in msg (they could come from file or directory names)
# before logging
- msg = msg.replace('%', '%%')
+ msg = msg.replace("%", "%%")
super(SFTPClient, self)._log(
level,
- "[chan %s] " + msg, *([self.sock.get_name()] + list(args)))
+ "[chan %s] " + msg,
+ *([self.sock.get_name()] + list(args))
+ )
def close(self):
"""
@@ -156,7 +191,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
.. versionadded:: 1.4
"""
- self._log(INFO, 'sftp session closed.')
+ self._log(INFO, "sftp session closed.")
self.sock.close()
def get_channel(self):
@@ -168,7 +203,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
"""
return self.sock
- def listdir(self, path='.'):
+ def listdir(self, path="."):
"""
Return a list containing the names of the entries in the given
``path``.
@@ -182,7 +217,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
"""
return [f.filename for f in self.listdir_attr(path)]
- def listdir_attr(self, path='.'):
+ def listdir_attr(self, path="."):
"""
Return a list containing `.SFTPAttributes` objects corresponding to
files in the given ``path``. The list is in arbitrary order. It does
@@ -200,10 +235,10 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
.. versionadded:: 1.2
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'listdir(%r)' % path)
+ self._log(DEBUG, "listdir({!r})".format(path))
t, msg = self._request(CMD_OPENDIR, path)
if t != CMD_HANDLE:
- raise SFTPError('Expected handle')
+ raise SFTPError("Expected handle")
handle = msg.get_binary()
filelist = []
while True:
@@ -213,18 +248,18 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
# done with handle
break
if t != CMD_NAME:
- raise SFTPError('Expected name response')
+ raise SFTPError("Expected name response")
count = msg.get_int()
for i in range(count):
filename = msg.get_text()
longname = msg.get_text()
attr = SFTPAttributes._from_msg(msg, filename, longname)
- if (filename != '.') and (filename != '..'):
+ if (filename != ".") and (filename != ".."):
filelist.append(attr)
self._request(CMD_CLOSE, handle)
return filelist
- def listdir_iter(self, path='.', read_aheads=50):
+ def listdir_iter(self, path=".", read_aheads=50):
"""
Generator version of `.listdir_attr`.
@@ -239,11 +274,11 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
.. versionadded:: 1.15
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'listdir(%r)' % path)
+ self._log(DEBUG, "listdir({!r})".format(path))
t, msg = self._request(CMD_OPENDIR, path)
if t != CMD_HANDLE:
- raise SFTPError('Expected handle')
+ raise SFTPError("Expected handle")
handle = msg.get_string()
@@ -258,7 +293,6 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
num = self._async_request(type(None), CMD_READDIR, handle)
nums.append(num)
-
# For each of our sent requests
# Read and parse the corresponding packets
# If we're at the end of our queued requests, then fire off
@@ -277,8 +311,9 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
filename = msg.get_text()
longname = msg.get_text()
attr = SFTPAttributes._from_msg(
- msg, filename, longname)
- if (filename != '.') and (filename != '..'):
+ msg, filename, longname
+ )
+ if (filename != ".") and (filename != ".."):
yield attr
# If we've hit the end of our queued requests, reset nums.
@@ -288,8 +323,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
self._request(CMD_CLOSE, handle)
return
-
- def open(self, filename, mode='r', bufsize=-1):
+ def open(self, filename, mode="r", bufsize=-1):
"""
Open a file on the remote server. The arguments are the same as for
Python's built-in `python:file` (aka `python:open`). A file-like
@@ -322,26 +356,29 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:raises: ``IOError`` -- if the file could not be opened.
"""
filename = self._adjust_cwd(filename)
- self._log(DEBUG, 'open(%r, %r)' % (filename, mode))
+ self._log(DEBUG, "open({!r}, {!r})".format(filename, mode))
imode = 0
- if ('r' in mode) or ('+' in mode):
+ if ("r" in mode) or ("+" in mode):
imode |= SFTP_FLAG_READ
- if ('w' in mode) or ('+' in mode) or ('a' in mode):
+ if ("w" in mode) or ("+" in mode) or ("a" in mode):
imode |= SFTP_FLAG_WRITE
- if 'w' in mode:
+ if "w" in mode:
imode |= SFTP_FLAG_CREATE | SFTP_FLAG_TRUNC
- if 'a' in mode:
+ if "a" in mode:
imode |= SFTP_FLAG_CREATE | SFTP_FLAG_APPEND
- if 'x' in mode:
+ if "x" in mode:
imode |= SFTP_FLAG_CREATE | SFTP_FLAG_EXCL
attrblock = SFTPAttributes()
t, msg = self._request(CMD_OPEN, filename, imode, attrblock)
if t != CMD_HANDLE:
- raise SFTPError('Expected handle')
+ raise SFTPError("Expected handle")
handle = msg.get_binary()
self._log(
DEBUG,
- 'open(%r, %r) -> %s' % (filename, mode, u(hexlify(handle))))
+ "open({!r}, {!r}) -> {}".format(
+ filename, mode, u(hexlify(handle))
+ ),
+ )
return SFTPFile(self, handle, mode, bufsize)
# Python continues to vacillate about "open" vs "file"...
@@ -357,7 +394,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:raises: ``IOError`` -- if the path refers to a folder (directory)
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'remove(%r)' % path)
+ self._log(DEBUG, "remove({!r})".format(path))
self._request(CMD_REMOVE, path)
unlink = remove
@@ -382,7 +419,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
"""
oldpath = self._adjust_cwd(oldpath)
newpath = self._adjust_cwd(newpath)
- self._log(DEBUG, 'rename(%r, %r)' % (oldpath, newpath))
+ self._log(DEBUG, "rename({!r}, {!r})".format(oldpath, newpath))
self._request(CMD_RENAME, oldpath, newpath)
def posix_rename(self, oldpath, newpath):
@@ -402,7 +439,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
"""
oldpath = self._adjust_cwd(oldpath)
newpath = self._adjust_cwd(newpath)
- self._log(DEBUG, 'posix_rename(%r, %r)' % (oldpath, newpath))
+ self._log(DEBUG, "posix_rename({!r}, {!r})".format(oldpath, newpath))
self._request(
CMD_EXTENDED, "posix-rename@openssh.com", oldpath, newpath
)
@@ -417,7 +454,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param int mode: permissions (posix-style) for the newly-created folder
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'mkdir(%r, %r)' % (path, mode))
+ self._log(DEBUG, "mkdir({!r}, {!r})".format(path, mode))
attr = SFTPAttributes()
attr.st_mode = mode
self._request(CMD_MKDIR, path, attr)
@@ -429,7 +466,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param str path: name of the folder to remove
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'rmdir(%r)' % path)
+ self._log(DEBUG, "rmdir({!r})".format(path))
self._request(CMD_RMDIR, path)
def stat(self, path):
@@ -452,10 +489,10 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
file
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'stat(%r)' % path)
+ self._log(DEBUG, "stat({!r})".format(path))
t, msg = self._request(CMD_STAT, path)
if t != CMD_ATTRS:
- raise SFTPError('Expected attributes')
+ raise SFTPError("Expected attributes")
return SFTPAttributes._from_msg(msg)
def lstat(self, path):
@@ -470,10 +507,10 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
file
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'lstat(%r)' % path)
+ self._log(DEBUG, "lstat({!r})".format(path))
t, msg = self._request(CMD_LSTAT, path)
if t != CMD_ATTRS:
- raise SFTPError('Expected attributes')
+ raise SFTPError("Expected attributes")
return SFTPAttributes._from_msg(msg)
def symlink(self, source, dest):
@@ -484,7 +521,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param str dest: path of the newly created symlink
"""
dest = self._adjust_cwd(dest)
- self._log(DEBUG, 'symlink(%r, %r)' % (source, dest))
+ self._log(DEBUG, "symlink({!r}, {!r})".format(source, dest))
source = bytestring(source)
self._request(CMD_SYMLINK, source, dest)
@@ -498,7 +535,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param int mode: new permissions
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'chmod(%r, %r)' % (path, mode))
+ self._log(DEBUG, "chmod({!r}, {!r})".format(path, mode))
attr = SFTPAttributes()
attr.st_mode = mode
self._request(CMD_SETSTAT, path, attr)
@@ -515,7 +552,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param int gid: new group id
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'chown(%r, %r, %r)' % (path, uid, gid))
+ self._log(DEBUG, "chown({!r}, {!r}, {!r})".format(path, uid, gid))
attr = SFTPAttributes()
attr.st_uid, attr.st_gid = uid, gid
self._request(CMD_SETSTAT, path, attr)
@@ -537,7 +574,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
path = self._adjust_cwd(path)
if times is None:
times = (time.time(), time.time())
- self._log(DEBUG, 'utime(%r, %r)' % (path, times))
+ self._log(DEBUG, "utime({!r}, {!r})".format(path, times))
attr = SFTPAttributes()
attr.st_atime, attr.st_mtime = times
self._request(CMD_SETSTAT, path, attr)
@@ -552,7 +589,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param int size: the new size of the file
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'truncate(%r, %r)' % (path, size))
+ self._log(DEBUG, "truncate({!r}, {!r})".format(path, size))
attr = SFTPAttributes()
attr.st_size = size
self._request(CMD_SETSTAT, path, attr)
@@ -567,15 +604,15 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:return: target path, as a `str`
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'readlink(%r)' % path)
+ self._log(DEBUG, "readlink({!r})".format(path))
t, msg = self._request(CMD_READLINK, path)
if t != CMD_NAME:
- raise SFTPError('Expected name response')
+ raise SFTPError("Expected name response")
count = msg.get_int()
if count == 0:
return None
if count != 1:
- raise SFTPError('Readlink returned %d results' % count)
+ raise SFTPError("Readlink returned {} results".format(count))
return _to_unicode(msg.get_string())
def normalize(self, path):
@@ -591,13 +628,13 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:raises: ``IOError`` -- if the path can't be resolved on the server
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'normalize(%r)' % path)
+ self._log(DEBUG, "normalize({!r})".format(path))
t, msg = self._request(CMD_REALPATH, path)
if t != CMD_NAME:
- raise SFTPError('Expected name response')
+ raise SFTPError("Expected name response")
count = msg.get_int()
if count != 1:
- raise SFTPError('Realpath returned %d results' % count)
+ raise SFTPError("Realpath returned {} results".format(count))
return msg.get_text()
def chdir(self, path=None):
@@ -620,8 +657,8 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
self._cwd = None
return
if not stat.S_ISDIR(self.stat(path).st_mode):
- raise SFTPError(
- errno.ENOTDIR, "%s: %s" % (os.strerror(errno.ENOTDIR), path))
+ code = errno.ENOTDIR
+ raise SFTPError(code, "{}: {}".format(os.strerror(code), path))
self._cwd = b(self.normalize(path))
def getcwd(self):
@@ -674,7 +711,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
.. versionadded:: 1.10
"""
- with self.file(remotepath, 'wb') as fr:
+ with self.file(remotepath, "wb") as fr:
fr.set_pipelined(True)
size = self._transfer_with_callback(
reader=fl, writer=fr, file_size=file_size, callback=callback
@@ -683,7 +720,8 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
s = self.stat(remotepath)
if s.st_size != size:
raise IOError(
- 'size mismatch in put! %d != %d' % (s.st_size, size))
+ "size mismatch in put! {} != {}".format(s.st_size, size)
+ )
else:
s = SFTPAttributes()
return s
@@ -717,7 +755,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
``confirm`` param added.
"""
file_size = os.stat(localpath).st_size
- with open(localpath, 'rb') as fl:
+ with open(localpath, "rb") as fl:
return self.putfo(fl, remotepath, file_size, callback, confirm)
def getfo(self, remotepath, fl, callback=None):
@@ -738,7 +776,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
.. versionadded:: 1.10
"""
file_size = self.stat(remotepath).st_size
- with self.open(remotepath, 'rb') as fr:
+ with self.open(remotepath, "rb") as fr:
fr.prefetch(file_size)
return self._transfer_with_callback(
reader=fr, writer=fl, file_size=file_size, callback=callback
@@ -760,12 +798,13 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
.. versionchanged:: 1.7.4
Added the ``callback`` param
"""
- with open(localpath, 'wb') as fl:
+ with open(localpath, "wb") as fl:
size = self.getfo(remotepath, fl, callback)
s = os.stat(localpath)
if s.st_size != size:
raise IOError(
- 'size mismatch in get! %d != %d' % (s.st_size, size))
+ "size mismatch in get! {} != {}".format(s.st_size, size)
+ )
# ...internals...
@@ -803,7 +842,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
try:
t, data = self._read_packet()
except EOFError as e:
- raise SSHException('Server connection dropped: %s' % str(e))
+ raise SSHException("Server connection dropped: {}".format(e))
msg = Message(data)
num = msg.get_int()
self._lock.acquire()
@@ -811,7 +850,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
if num not in self._expecting:
# might be response for a file that was closed before
# responses came back
- self._log(DEBUG, 'Unexpected response #%d' % (num,))
+ self._log(DEBUG, "Unexpected response #{}".format(num))
if waitfor is None:
# just doing a single check
break
diff --git a/paramiko/sftp_file.py b/paramiko/sftp_file.py
index bc34db94..0104d857 100644
--- a/paramiko/sftp_file.py
+++ b/paramiko/sftp_file.py
@@ -32,13 +32,21 @@ from paramiko.common import DEBUG
from paramiko.file import BufferedFile
from paramiko.py3compat import u, long
from paramiko.sftp import (
- CMD_CLOSE, CMD_READ, CMD_DATA, SFTPError, CMD_WRITE, CMD_STATUS, CMD_FSTAT,
- CMD_ATTRS, CMD_FSETSTAT, CMD_EXTENDED,
+ CMD_CLOSE,
+ CMD_READ,
+ CMD_DATA,
+ SFTPError,
+ CMD_WRITE,
+ CMD_STATUS,
+ CMD_FSTAT,
+ CMD_ATTRS,
+ CMD_FSETSTAT,
+ CMD_EXTENDED,
)
from paramiko.sftp_attr import SFTPAttributes
-class SFTPFile (BufferedFile):
+class SFTPFile(BufferedFile):
"""
Proxy object for a file on the remote server, in client mode SFTP.
@@ -50,7 +58,7 @@ class SFTPFile (BufferedFile):
# this size.
MAX_REQUEST_SIZE = 32768
- def __init__(self, sftp, handle, mode='r', bufsize=-1):
+ def __init__(self, sftp, handle, mode="r", bufsize=-1):
BufferedFile.__init__(self)
self.sftp = sftp
self.handle = handle
@@ -83,7 +91,7 @@ class SFTPFile (BufferedFile):
# __del__.)
if self._closed:
return
- self.sftp._log(DEBUG, 'close(%s)' % u(hexlify(self.handle)))
+ self.sftp._log(DEBUG, "close({})".format(u(hexlify(self.handle))))
if self.pipelined:
self.sftp._finish_responses(self)
BufferedFile.close(self)
@@ -102,8 +110,9 @@ class SFTPFile (BufferedFile):
pass
def _data_in_prefetch_requests(self, offset, size):
- k = [x for x in list(self._prefetch_extents.values())
- if x[0] <= offset]
+ k = [
+ x for x in list(self._prefetch_extents.values()) if x[0] <= offset
+ ]
if len(k) == 0:
return False
k.sort(key=lambda x: x[0])
@@ -117,8 +126,8 @@ class SFTPFile (BufferedFile):
# well, we have part of the request. see if another chunk has
# the rest.
return self._data_in_prefetch_requests(
- buf_offset + buf_size,
- offset + size - buf_offset - buf_size)
+ buf_offset + buf_size, offset + size - buf_offset - buf_size
+ )
def _data_in_prefetch_buffers(self, offset):
"""
@@ -174,13 +183,10 @@ class SFTPFile (BufferedFile):
if data is not None:
return data
t, msg = self.sftp._request(
- CMD_READ,
- self.handle,
- long(self._realpos),
- int(size)
+ CMD_READ, self.handle, long(self._realpos), int(size)
)
if t != CMD_DATA:
- raise SFTPError('Expected data')
+ raise SFTPError("Expected data")
return msg.get_string()
def _write(self, data):
@@ -191,18 +197,17 @@ class SFTPFile (BufferedFile):
CMD_WRITE,
self.handle,
long(self._realpos),
- data[:chunk]
+ data[:chunk],
)
self._reqs.append(sftp_async_request)
- if (
- not self.pipelined or
- (len(self._reqs) > 100 and self.sftp.sock.recv_ready())
+ if not self.pipelined or (
+ len(self._reqs) > 100 and self.sftp.sock.recv_ready()
):
while len(self._reqs):
req = self._reqs.popleft()
t, msg = self.sftp._read_response(req)
if t != CMD_STATUS:
- raise SFTPError('Expected status')
+ raise SFTPError("Expected status")
# convert_status already called
return chunk
@@ -277,7 +282,7 @@ class SFTPFile (BufferedFile):
"""
t, msg = self.sftp._request(CMD_FSTAT, self.handle)
if t != CMD_ATTRS:
- raise SFTPError('Expected attributes')
+ raise SFTPError("Expected attributes")
return SFTPAttributes._from_msg(msg)
def chmod(self, mode):
@@ -288,7 +293,9 @@ class SFTPFile (BufferedFile):
:param int mode: new permissions
"""
- self.sftp._log(DEBUG, 'chmod(%s, %r)' % (hexlify(self.handle), mode))
+ self.sftp._log(
+ DEBUG, "chmod({}, {!r})".format(hexlify(self.handle), mode)
+ )
attr = SFTPAttributes()
attr.st_mode = mode
self.sftp._request(CMD_FSETSTAT, self.handle, attr)
@@ -305,7 +312,8 @@ class SFTPFile (BufferedFile):
"""
self.sftp._log(
DEBUG,
- 'chown(%s, %r, %r)' % (hexlify(self.handle), uid, gid))
+ "chown({}, {!r}, {!r})".format(hexlify(self.handle), uid, gid),
+ )
attr = SFTPAttributes()
attr.st_uid, attr.st_gid = uid, gid
self.sftp._request(CMD_FSETSTAT, self.handle, attr)
@@ -325,7 +333,9 @@ class SFTPFile (BufferedFile):
"""
if times is None:
times = (time.time(), time.time())
- self.sftp._log(DEBUG, 'utime(%s, %r)' % (hexlify(self.handle), times))
+ self.sftp._log(
+ DEBUG, "utime({}, {!r})".format(hexlify(self.handle), times)
+ )
attr = SFTPAttributes()
attr.st_atime, attr.st_mtime = times
self.sftp._request(CMD_FSETSTAT, self.handle, attr)
@@ -339,8 +349,8 @@ class SFTPFile (BufferedFile):
:param size: the new size of the file
"""
self.sftp._log(
- DEBUG,
- 'truncate(%s, %r)' % (hexlify(self.handle), size))
+ DEBUG, "truncate({}, {!r})".format(hexlify(self.handle), size)
+ )
attr = SFTPAttributes()
attr.st_size = size
self.sftp._request(CMD_FSETSTAT, self.handle, attr)
@@ -392,8 +402,14 @@ class SFTPFile (BufferedFile):
.. versionadded:: 1.4
"""
t, msg = self.sftp._request(
- CMD_EXTENDED, 'check-file', self.handle,
- hash_algorithm, long(offset), long(length), block_size)
+ CMD_EXTENDED,
+ "check-file",
+ self.handle,
+ hash_algorithm,
+ long(offset),
+ long(length),
+ block_size,
+ )
msg.get_text() # ext
msg.get_text() # alg
data = msg.get_remainder()
@@ -473,15 +489,16 @@ class SFTPFile (BufferedFile):
.. versionadded:: 1.5.4
"""
- self.sftp._log(DEBUG, 'readv(%s, %r)' % (hexlify(self.handle), chunks))
+ self.sftp._log(
+ DEBUG, "readv({}, {!r})".format(hexlify(self.handle), chunks)
+ )
read_chunks = []
for offset, size in chunks:
# don't fetch data that's already in the prefetch buffer
- if (
- self._data_in_prefetch_buffers(offset) or
- self._data_in_prefetch_requests(offset, size)
- ):
+ if self._data_in_prefetch_buffers(
+ offset
+ ) or self._data_in_prefetch_requests(offset, size):
continue
# break up anything larger than the max read size
@@ -518,11 +535,8 @@ class SFTPFile (BufferedFile):
# a lot of them, so it may block.
for offset, length in chunks:
num = self.sftp._async_request(
- self,
- CMD_READ,
- self.handle,
- long(offset),
- int(length))
+ self, CMD_READ, self.handle, long(offset), int(length)
+ )
with self._prefetch_lock:
self._prefetch_extents[num] = (offset, length)
@@ -535,7 +549,7 @@ class SFTPFile (BufferedFile):
self._saved_exception = e
return
if t != CMD_DATA:
- raise SFTPError('Expected data')
+ raise SFTPError("Expected data")
data = msg.get_string()
while True:
with self._prefetch_lock:
diff --git a/paramiko/sftp_handle.py b/paramiko/sftp_handle.py
index ca473900..a7e22f01 100644
--- a/paramiko/sftp_handle.py
+++ b/paramiko/sftp_handle.py
@@ -25,7 +25,7 @@ from paramiko.sftp import SFTP_OP_UNSUPPORTED, SFTP_OK
from paramiko.util import ClosingContextManager
-class SFTPHandle (ClosingContextManager):
+class SFTPHandle(ClosingContextManager):
"""
Abstract object representing a handle to an open file (or folder) in an
SFTP server implementation. Each handle has a string representation used
@@ -36,6 +36,7 @@ class SFTPHandle (ClosingContextManager):
Instances of this class may be used as context managers.
"""
+
def __init__(self, flags=0):
"""
Create a new file handle representing a local file being served over
@@ -63,10 +64,10 @@ class SFTPHandle (ClosingContextManager):
using the default implementations of `read` and `write`, this
method's default implementation should be fine also.
"""
- readfile = getattr(self, 'readfile', None)
+ readfile = getattr(self, "readfile", None)
if readfile is not None:
readfile.close()
- writefile = getattr(self, 'writefile', None)
+ writefile = getattr(self, "writefile", None)
if writefile is not None:
writefile.close()
@@ -88,7 +89,7 @@ class SFTPHandle (ClosingContextManager):
:param int length: number of bytes to attempt to read.
:return: data read from the file, or an SFTP error code, as a `str`.
"""
- readfile = getattr(self, 'readfile', None)
+ readfile = getattr(self, "readfile", None)
if readfile is None:
return SFTP_OP_UNSUPPORTED
try:
@@ -122,7 +123,7 @@ class SFTPHandle (ClosingContextManager):
:param str data: data to write into the file.
:return: an SFTP error code like ``SFTP_OK``.
"""
- writefile = getattr(self, 'writefile', None)
+ writefile = getattr(self, "writefile", None)
if writefile is None:
return SFTP_OP_UNSUPPORTED
try:
diff --git a/paramiko/sftp_server.py b/paramiko/sftp_server.py
index f7d1c657..8265df96 100644
--- a/paramiko/sftp_server.py
+++ b/paramiko/sftp_server.py
@@ -27,7 +27,11 @@ from hashlib import md5, sha1
from paramiko import util
from paramiko.sftp import (
- BaseSFTP, Message, SFTP_FAILURE, SFTP_PERMISSION_DENIED, SFTP_NO_SUCH_FILE,
+ BaseSFTP,
+ Message,
+ SFTP_FAILURE,
+ SFTP_PERMISSION_DENIED,
+ SFTP_NO_SUCH_FILE,
)
from paramiko.sftp_si import SFTPServerInterface
from paramiko.sftp_attr import SFTPAttributes
@@ -38,30 +42,64 @@ from paramiko.server import SubsystemHandler
# known hash algorithms for the "check-file" extension
from paramiko.sftp import (
- CMD_HANDLE, SFTP_DESC, CMD_STATUS, SFTP_EOF, CMD_NAME, SFTP_BAD_MESSAGE,
- CMD_EXTENDED_REPLY, SFTP_FLAG_READ, SFTP_FLAG_WRITE, SFTP_FLAG_APPEND,
- SFTP_FLAG_CREATE, SFTP_FLAG_TRUNC, SFTP_FLAG_EXCL, CMD_NAMES, CMD_OPEN,
- CMD_CLOSE, SFTP_OK, CMD_READ, CMD_DATA, CMD_WRITE, CMD_REMOVE, CMD_RENAME,
- CMD_MKDIR, CMD_RMDIR, CMD_OPENDIR, CMD_READDIR, CMD_STAT, CMD_ATTRS,
- CMD_LSTAT, CMD_FSTAT, CMD_SETSTAT, CMD_FSETSTAT, CMD_READLINK, CMD_SYMLINK,
- CMD_REALPATH, CMD_EXTENDED, SFTP_OP_UNSUPPORTED,
+ CMD_HANDLE,
+ SFTP_DESC,
+ CMD_STATUS,
+ SFTP_EOF,
+ CMD_NAME,
+ SFTP_BAD_MESSAGE,
+ CMD_EXTENDED_REPLY,
+ SFTP_FLAG_READ,
+ SFTP_FLAG_WRITE,
+ SFTP_FLAG_APPEND,
+ SFTP_FLAG_CREATE,
+ SFTP_FLAG_TRUNC,
+ SFTP_FLAG_EXCL,
+ CMD_NAMES,
+ CMD_OPEN,
+ CMD_CLOSE,
+ SFTP_OK,
+ CMD_READ,
+ CMD_DATA,
+ CMD_WRITE,
+ CMD_REMOVE,
+ CMD_RENAME,
+ CMD_MKDIR,
+ CMD_RMDIR,
+ CMD_OPENDIR,
+ CMD_READDIR,
+ CMD_STAT,
+ CMD_ATTRS,
+ CMD_LSTAT,
+ CMD_FSTAT,
+ CMD_SETSTAT,
+ CMD_FSETSTAT,
+ CMD_READLINK,
+ CMD_SYMLINK,
+ CMD_REALPATH,
+ CMD_EXTENDED,
+ SFTP_OP_UNSUPPORTED,
)
-_hash_class = {
- 'sha1': sha1,
- 'md5': md5,
-}
+_hash_class = {"sha1": sha1, "md5": md5}
-class SFTPServer (BaseSFTP, SubsystemHandler):
+class SFTPServer(BaseSFTP, SubsystemHandler):
"""
Server-side SFTP subsystem support. Since this is a `.SubsystemHandler`,
it can be (and is meant to be) set as the handler for ``"sftp"`` requests.
Use `.Transport.set_subsystem_handler` to activate this class.
"""
- def __init__(self, channel, name, server, sftp_si=SFTPServerInterface,
- *largs, **kwargs):
+ def __init__(
+ self,
+ channel,
+ name,
+ server,
+ sftp_si=SFTPServerInterface,
+ *largs,
+ **kwargs
+ ):
"""
The constructor for SFTPServer is meant to be called from within the
`.Transport` as a subsystem handler. ``server`` and any additional
@@ -79,7 +117,7 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
BaseSFTP.__init__(self)
SubsystemHandler.__init__(self, channel, name, server)
transport = channel.get_transport()
- self.logger = util.get_logger(transport.get_log_channel() + '.sftp')
+ self.logger = util.get_logger(transport.get_log_channel() + ".sftp")
self.ultra_debug = transport.get_hexdump()
self.next_handle = 1
# map of handle-string to SFTPHandle for files & folders:
@@ -91,26 +129,26 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
if issubclass(type(msg), list):
for m in msg:
super(SFTPServer, self)._log(
- level,
- "[chan " + self.sock.get_name() + "] " + m)
+ level, "[chan " + self.sock.get_name() + "] " + m
+ )
else:
super(SFTPServer, self)._log(
- level,
- "[chan " + self.sock.get_name() + "] " + msg)
+ level, "[chan " + self.sock.get_name() + "] " + msg
+ )
def start_subsystem(self, name, transport, channel):
self.sock = channel
- self._log(DEBUG, 'Started sftp server on channel %s' % repr(channel))
+ self._log(DEBUG, "Started sftp server on channel {!r}".format(channel))
self._send_server_version()
self.server.session_started()
while True:
try:
t, data = self._read_packet()
except EOFError:
- self._log(DEBUG, 'EOF -- end of session')
+ self._log(DEBUG, "EOF -- end of session")
return
except Exception as e:
- self._log(DEBUG, 'Exception on channel: ' + str(e))
+ self._log(DEBUG, "Exception on channel: " + str(e))
self._log(DEBUG, util.tb_strings())
return
msg = Message(data)
@@ -118,7 +156,7 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
try:
self._process(t, request_number, msg)
except Exception as e:
- self._log(DEBUG, 'Exception in server processing: ' + str(e))
+ self._log(DEBUG, "Exception in server processing: " + str(e))
self._log(DEBUG, util.tb_strings())
# send some kind of failure message, at least
try:
@@ -172,7 +210,7 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
name of the file to alter (should usually be an absolute path).
:param .SFTPAttributes attr: attributes to change.
"""
- if sys.platform != 'win32':
+ if sys.platform != "win32":
# mode operations are meaningless on win32
if attr._flags & attr.FLAG_PERMISSIONS:
os.chmod(filename, attr.st_mode)
@@ -181,7 +219,7 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
if attr._flags & attr.FLAG_AMTIME:
os.utime(filename, (attr.st_atime, attr.st_mtime))
if attr._flags & attr.FLAG_SIZE:
- with open(filename, 'w+') as f:
+ with open(filename, "w+") as f:
f.truncate(attr.st_size)
# ...internals...
@@ -200,8 +238,8 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
item._pack(msg)
else:
raise Exception(
- 'unknown type for {0!r} type {1!r}'.format(
- item, type(item)))
+ "unknown type for {!r} type {!r}".format(item, type(item))
+ )
self._send_packet(t, msg)
def _send_handle_response(self, request_number, handle, folder=False):
@@ -209,7 +247,7 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
# must be error code
self._send_status(request_number, handle)
return
- handle._set_name(b('hx%d' % self.next_handle))
+ handle._set_name(b("hx{:d}".format(self.next_handle)))
self.next_handle += 1
if folder:
self.folder_table[handle._get_name()] = handle
@@ -222,10 +260,10 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
try:
desc = SFTP_DESC[code]
except IndexError:
- desc = 'Unknown'
+ desc = "Unknown"
# some clients expect a "langauge" tag at the end
# (but don't mind it being blank)
- self._response(request_number, CMD_STATUS, code, desc, '')
+ self._response(request_number, CMD_STATUS, code, desc, "")
def _open_folder(self, request_number, path):
resp = self.server.list_folder(path)
@@ -264,7 +302,8 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
block_size = msg.get_int()
if handle not in self.file_table:
self._send_status(
- request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
+ request_number, SFTP_BAD_MESSAGE, "Invalid handle"
+ )
return
f = self.file_table[handle]
for x in alg_list:
@@ -274,19 +313,21 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
break
else:
self._send_status(
- request_number, SFTP_FAILURE, 'No supported hash types found')
+ request_number, SFTP_FAILURE, "No supported hash types found"
+ )
return
if length == 0:
st = f.stat()
if not issubclass(type(st), SFTPAttributes):
- self._send_status(request_number, st, 'Unable to stat file')
+ self._send_status(request_number, st, "Unable to stat file")
return
length = st.st_size - start
if block_size == 0:
block_size = length
if block_size < 256:
self._send_status(
- request_number, SFTP_FAILURE, 'Block size too small')
+ request_number, SFTP_FAILURE, "Block size too small"
+ )
return
sum_out = bytes()
@@ -301,7 +342,8 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
data = f.read(offset, chunklen)
if not isinstance(data, bytes_types):
self._send_status(
- request_number, data, 'Unable to hash file')
+ request_number, data, "Unable to hash file"
+ )
return
hash_obj.update(data)
count += len(data)
@@ -310,7 +352,7 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
msg = Message()
msg.add_int(request_number)
- msg.add_string('check-file')
+ msg.add_string("check-file")
msg.add_string(algname)
msg.add_bytes(sum_out)
self._send_packet(CMD_EXTENDED_REPLY, msg)
@@ -334,13 +376,14 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
return flags
def _process(self, t, request_number, msg):
- self._log(DEBUG, 'Request: %s' % CMD_NAMES[t])
+ self._log(DEBUG, "Request: {}".format(CMD_NAMES[t]))
if t == CMD_OPEN:
path = msg.get_text()
flags = self._convert_pflags(msg.get_int())
attr = SFTPAttributes._from_msg(msg)
self._send_handle_response(
- request_number, self.server.open(path, flags, attr))
+ request_number, self.server.open(path, flags, attr)
+ )
elif t == CMD_CLOSE:
handle = msg.get_binary()
if handle in self.folder_table:
@@ -353,14 +396,16 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
self._send_status(request_number, SFTP_OK)
return
self._send_status(
- request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
+ request_number, SFTP_BAD_MESSAGE, "Invalid handle"
+ )
elif t == CMD_READ:
handle = msg.get_binary()
offset = msg.get_int64()
length = msg.get_int()
if handle not in self.file_table:
self._send_status(
- request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
+ request_number, SFTP_BAD_MESSAGE, "Invalid handle"
+ )
return
data = self.file_table[handle].read(offset, length)
if isinstance(data, (bytes_types, string_types)):
@@ -376,10 +421,12 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
data = msg.get_binary()
if handle not in self.file_table:
self._send_status(
- request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
+ request_number, SFTP_BAD_MESSAGE, "Invalid handle"
+ )
return
self._send_status(
- request_number, self.file_table[handle].write(offset, data))
+ request_number, self.file_table[handle].write(offset, data)
+ )
elif t == CMD_REMOVE:
path = msg.get_text()
self._send_status(request_number, self.server.remove(path))
@@ -387,7 +434,8 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
oldpath = msg.get_text()
newpath = msg.get_text()
self._send_status(
- request_number, self.server.rename(oldpath, newpath))
+ request_number, self.server.rename(oldpath, newpath)
+ )
elif t == CMD_MKDIR:
path = msg.get_text()
attr = SFTPAttributes._from_msg(msg)
@@ -403,7 +451,8 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
handle = msg.get_binary()
if handle not in self.folder_table:
self._send_status(
- request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
+ request_number, SFTP_BAD_MESSAGE, "Invalid handle"
+ )
return
folder = self.folder_table[handle]
self._read_folder(request_number, folder)
@@ -425,7 +474,8 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
handle = msg.get_binary()
if handle not in self.file_table:
self._send_status(
- request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
+ request_number, SFTP_BAD_MESSAGE, "Invalid handle"
+ )
return
resp = self.file_table[handle].stat()
if issubclass(type(resp), SFTPAttributes):
@@ -441,16 +491,19 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
attr = SFTPAttributes._from_msg(msg)
if handle not in self.file_table:
self._response(
- request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
+ request_number, SFTP_BAD_MESSAGE, "Invalid handle"
+ )
return
self._send_status(
- request_number, self.file_table[handle].chattr(attr))
+ request_number, self.file_table[handle].chattr(attr)
+ )
elif t == CMD_READLINK:
path = msg.get_text()
resp = self.server.readlink(path)
if isinstance(resp, (bytes_types, string_types)):
self._response(
- request_number, CMD_NAME, 1, resp, '', SFTPAttributes())
+ request_number, CMD_NAME, 1, resp, "", SFTPAttributes()
+ )
else:
self._send_status(request_number, resp)
elif t == CMD_SYMLINK:
@@ -459,17 +512,19 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
target_path = msg.get_text()
path = msg.get_text()
self._send_status(
- request_number, self.server.symlink(target_path, path))
+ request_number, self.server.symlink(target_path, path)
+ )
elif t == CMD_REALPATH:
path = msg.get_text()
rpath = self.server.canonicalize(path)
self._response(
- request_number, CMD_NAME, 1, rpath, '', SFTPAttributes())
+ request_number, CMD_NAME, 1, rpath, "", SFTPAttributes()
+ )
elif t == CMD_EXTENDED:
tag = msg.get_text()
- if tag == 'check-file':
+ if tag == "check-file":
self._check_file(request_number, msg)
- elif tag == 'posix-rename@openssh.com':
+ elif tag == "posix-rename@openssh.com":
oldpath = msg.get_text()
newpath = msg.get_text()
self._send_status(
diff --git a/paramiko/sftp_si.py b/paramiko/sftp_si.py
index b43b582c..40dc561c 100644
--- a/paramiko/sftp_si.py
+++ b/paramiko/sftp_si.py
@@ -25,7 +25,7 @@ import sys
from paramiko.sftp import SFTP_OP_UNSUPPORTED
-class SFTPServerInterface (object):
+class SFTPServerInterface(object):
"""
This class defines an interface for controlling the behavior of paramiko
when using the `.SFTPServer` subsystem to provide an SFTP server.
@@ -39,6 +39,7 @@ class SFTPServerInterface (object):
All paths are in string form instead of unicode because not all SFTP
clients & servers obey the requirement that paths be encoded in UTF-8.
"""
+
def __init__(self, server, *largs, **kwargs):
"""
Create a new SFTPServerInterface object. This method does nothing by
@@ -281,10 +282,10 @@ class SFTPServerInterface (object):
if os.path.isabs(path):
out = os.path.normpath(path)
else:
- out = os.path.normpath('/' + path)
- if sys.platform == 'win32':
+ out = os.path.normpath("/" + path)
+ if sys.platform == "win32":
# on windows, normalize backslashes to sftp/posix format
- out = out.replace('\\', '/')
+ out = out.replace("\\", "/")
return out
def readlink(self, path):
diff --git a/paramiko/ssh_exception.py b/paramiko/ssh_exception.py
index e9ab8d66..12407d66 100644
--- a/paramiko/ssh_exception.py
+++ b/paramiko/ssh_exception.py
@@ -19,14 +19,14 @@
import socket
-class SSHException (Exception):
+class SSHException(Exception):
"""
Exception raised by failures in SSH2 protocol negotiation or logic errors.
"""
pass
-class AuthenticationException (SSHException):
+class AuthenticationException(SSHException):
"""
Exception raised when authentication failed for some reason. It may be
possible to retry with different credentials. (Other classes specify more
@@ -37,14 +37,14 @@ class AuthenticationException (SSHException):
pass
-class PasswordRequiredException (AuthenticationException):
+class PasswordRequiredException(AuthenticationException):
"""
Exception raised when a password is needed to unlock a private key file.
"""
pass
-class BadAuthenticationType (AuthenticationException):
+class BadAuthenticationType(AuthenticationException):
"""
Exception raised when an authentication type (like password) is used, but
the server isn't allowing that type. (It may only allow public-key, for
@@ -60,28 +60,28 @@ class BadAuthenticationType (AuthenticationException):
AuthenticationException.__init__(self, explanation)
self.allowed_types = types
# for unpickling
- self.args = (explanation, types, )
+ self.args = (explanation, types)
def __str__(self):
- return '{0} (allowed_types={1!r})'.format(
+ return "{} (allowed_types={!r})".format(
SSHException.__str__(self), self.allowed_types
)
-class PartialAuthentication (AuthenticationException):
+class PartialAuthentication(AuthenticationException):
"""
An internal exception thrown in the case of partial authentication.
"""
allowed_types = []
def __init__(self, types):
- AuthenticationException.__init__(self, 'partial authentication')
+ AuthenticationException.__init__(self, "partial authentication")
self.allowed_types = types
# for unpickling
- self.args = (types, )
+ self.args = (types,)
-class ChannelException (SSHException):
+class ChannelException(SSHException):
"""
Exception raised when an attempt to open a new `.Channel` fails.
@@ -89,14 +89,15 @@ class ChannelException (SSHException):
.. versionadded:: 1.6
"""
+
def __init__(self, code, text):
SSHException.__init__(self, text)
self.code = code
# for unpickling
- self.args = (code, text, )
+ self.args = (code, text)
-class BadHostKeyException (SSHException):
+class BadHostKeyException(SSHException):
"""
The host key given by the SSH server did not match what we were expecting.
@@ -106,35 +107,40 @@ class BadHostKeyException (SSHException):
.. versionadded:: 1.6
"""
+
def __init__(self, hostname, got_key, expected_key):
- message = 'Host key for server {0} does not match: got {1}, expected {2}' # noqa
+ message = (
+ "Host key for server {} does not match: got {}, expected {}"
+ ) # noqa
message = message.format(
- hostname, got_key.get_base64(),
- expected_key.get_base64())
+ hostname, got_key.get_base64(), expected_key.get_base64()
+ )
SSHException.__init__(self, message)
self.hostname = hostname
self.key = got_key
self.expected_key = expected_key
# for unpickling
- self.args = (hostname, got_key, expected_key, )
+ self.args = (hostname, got_key, expected_key)
-class ProxyCommandFailure (SSHException):
+class ProxyCommandFailure(SSHException):
"""
The "ProxyCommand" found in the .ssh/config file returned an error.
:param str command: The command line that is generating this exception.
:param str error: The error captured from the proxy command output.
"""
+
def __init__(self, command, error):
- SSHException.__init__(self,
- '"ProxyCommand (%s)" returned non-zero exit status: %s' % (
+ SSHException.__init__(
+ self,
+ '"ProxyCommand ({})" returned non-zero exit status: {}'.format(
command, error
- )
+ ),
)
self.error = error
# for unpickling
- self.args = (command, error, )
+ self.args = (command, error)
class NoValidConnectionsError(socket.error):
@@ -159,23 +165,23 @@ class NoValidConnectionsError(socket.error):
.. versionadded:: 1.16
"""
+
def __init__(self, errors):
"""
:param dict errors:
The errors dict to store, as described by class docstring.
"""
addrs = sorted(errors.keys())
- body = ', '.join([x[0] for x in addrs[:-1]])
+ body = ", ".join([x[0] for x in addrs[:-1]])
tail = addrs[-1][0]
if body:
msg = "Unable to connect to port {0} on {1} or {2}"
else:
msg = "Unable to connect to port {0} on {2}"
super(NoValidConnectionsError, self).__init__(
- None, # stand-in for errno
- msg.format(addrs[0][1], body, tail)
+ None, msg.format(addrs[0][1], body, tail) # stand-in for errno
)
self.errors = errors
def __reduce__(self):
- return (self.__class__, (self.errors, ))
+ return (self.__class__, (self.errors,))
diff --git a/paramiko/ssh_gss.py b/paramiko/ssh_gss.py
index b3c3f72b..eb8826e0 100644
--- a/paramiko/ssh_gss.py
+++ b/paramiko/ssh_gss.py
@@ -51,12 +51,14 @@ _API = "MIT"
try:
import gssapi
+
GSS_EXCEPTIONS = (gssapi.GSSException,)
except (ImportError, OSError):
try:
import pywintypes
import sspicon
import sspi
+
_API = "SSPI"
GSS_EXCEPTIONS = (pywintypes.error,)
except ImportError:
@@ -101,6 +103,7 @@ class _SSH_GSSAuth(object):
Contains the shared variables and methods of `._SSH_GSSAPI` and
`._SSH_SSPI`.
"""
+
def __init__(self, auth_method, gss_deleg_creds):
"""
:param str auth_method: The name of the SSH authentication mechanism
@@ -209,7 +212,7 @@ class _SSH_GSSAuth(object):
"""
mic = self._make_uint32(len(session_id))
mic += session_id
- mic += struct.pack('B', MSG_USERAUTH_REQUEST)
+ mic += struct.pack("B", MSG_USERAUTH_REQUEST)
mic += self._make_uint32(len(username))
mic += username.encode()
mic += self._make_uint32(len(service))
@@ -225,6 +228,7 @@ class _SSH_GSSAPI(_SSH_GSSAuth):
:see: `.GSSAuth`
"""
+
def __init__(self, auth_method, gss_deleg_creds):
"""
:param str auth_method: The name of the SSH authentication mechanism
@@ -234,17 +238,22 @@ class _SSH_GSSAPI(_SSH_GSSAuth):
_SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds)
if self._gss_deleg_creds:
- self._gss_flags = (gssapi.C_PROT_READY_FLAG,
- gssapi.C_INTEG_FLAG,
- gssapi.C_MUTUAL_FLAG,
- gssapi.C_DELEG_FLAG)
+ self._gss_flags = (
+ gssapi.C_PROT_READY_FLAG,
+ gssapi.C_INTEG_FLAG,
+ gssapi.C_MUTUAL_FLAG,
+ gssapi.C_DELEG_FLAG,
+ )
else:
- self._gss_flags = (gssapi.C_PROT_READY_FLAG,
- gssapi.C_INTEG_FLAG,
- gssapi.C_MUTUAL_FLAG)
+ self._gss_flags = (
+ gssapi.C_PROT_READY_FLAG,
+ gssapi.C_INTEG_FLAG,
+ gssapi.C_MUTUAL_FLAG,
+ )
- def ssh_init_sec_context(self, target, desired_mech=None,
- username=None, recv_token=None):
+ def ssh_init_sec_context(
+ self, target, desired_mech=None, username=None, recv_token=None
+ ):
"""
Initialize a GSS-API context.
@@ -262,8 +271,9 @@ class _SSH_GSSAPI(_SSH_GSSAuth):
"""
self._username = username
self._gss_host = target
- targ_name = gssapi.Name("host@" + self._gss_host,
- gssapi.C_NT_HOSTBASED_SERVICE)
+ targ_name = gssapi.Name(
+ "host@" + self._gss_host, gssapi.C_NT_HOSTBASED_SERVICE
+ )
ctx = gssapi.Context()
ctx.flags = self._gss_flags
if desired_mech is None:
@@ -277,15 +287,16 @@ class _SSH_GSSAPI(_SSH_GSSAuth):
token = None
try:
if recv_token is None:
- self._gss_ctxt = gssapi.InitContext(peer_name=targ_name,
- mech_type=krb5_mech,
- req_flags=ctx.flags)
+ self._gss_ctxt = gssapi.InitContext(
+ peer_name=targ_name,
+ mech_type=krb5_mech,
+ req_flags=ctx.flags,
+ )
token = self._gss_ctxt.step(token)
else:
token = self._gss_ctxt.step(recv_token)
except gssapi.GSSException:
- message = "{0} Target: {1}".format(
- sys.exc_info()[1], self._gss_host)
+ message = "{} Target: {}".format(sys.exc_info()[1], self._gss_host)
raise gssapi.GSSException(message)
self._gss_ctxt_status = self._gss_ctxt.established
return token
@@ -305,10 +316,12 @@ class _SSH_GSSAPI(_SSH_GSSAuth):
"""
self._session_id = session_id
if not gss_kex:
- mic_field = self._ssh_build_mic(self._session_id,
- self._username,
- self._service,
- self._auth_method)
+ mic_field = self._ssh_build_mic(
+ self._session_id,
+ self._username,
+ self._service,
+ self._auth_method,
+ )
mic_token = self._gss_ctxt.get_mic(mic_field)
else:
# for key exchange with gssapi-keyex
@@ -349,16 +362,17 @@ class _SSH_GSSAPI(_SSH_GSSAuth):
self._username = username
if self._username is not None:
# server mode
- mic_field = self._ssh_build_mic(self._session_id,
- self._username,
- self._service,
- self._auth_method)
+ mic_field = self._ssh_build_mic(
+ self._session_id,
+ self._username,
+ self._service,
+ self._auth_method,
+ )
self._gss_srv_ctxt.verify_mic(mic_field, mic_token)
else:
# for key exchange with gssapi-keyex
# client mode
- self._gss_ctxt.verify_mic(self._session_id,
- mic_token)
+ self._gss_ctxt.verify_mic(self._session_id, mic_token)
@property
def credentials_delegated(self):
@@ -391,6 +405,7 @@ class _SSH_SSPI(_SSH_GSSAuth):
:see: `.GSSAuth`
"""
+
def __init__(self, auth_method, gss_deleg_creds):
"""
:param str auth_method: The name of the SSH authentication mechanism
@@ -401,18 +416,18 @@ class _SSH_SSPI(_SSH_GSSAuth):
if self._gss_deleg_creds:
self._gss_flags = (
- sspicon.ISC_REQ_INTEGRITY |
- sspicon.ISC_REQ_MUTUAL_AUTH |
- sspicon.ISC_REQ_DELEGATE
+ sspicon.ISC_REQ_INTEGRITY
+ | sspicon.ISC_REQ_MUTUAL_AUTH
+ | sspicon.ISC_REQ_DELEGATE
)
else:
self._gss_flags = (
- sspicon.ISC_REQ_INTEGRITY |
- sspicon.ISC_REQ_MUTUAL_AUTH
+ sspicon.ISC_REQ_INTEGRITY | sspicon.ISC_REQ_MUTUAL_AUTH
)
- def ssh_init_sec_context(self, target, desired_mech=None,
- username=None, recv_token=None):
+ def ssh_init_sec_context(
+ self, target, desired_mech=None, username=None, recv_token=None
+ ):
"""
Initialize a SSPI context.
@@ -438,13 +453,13 @@ class _SSH_SSPI(_SSH_GSSAuth):
raise SSHException("Unsupported mechanism OID.")
try:
if recv_token is None:
- self._gss_ctxt = sspi.ClientAuth("Kerberos",
- scflags=self._gss_flags,
- targetspn=targ_name)
+ self._gss_ctxt = sspi.ClientAuth(
+ "Kerberos", scflags=self._gss_flags, targetspn=targ_name
+ )
error, token = self._gss_ctxt.authorize(recv_token)
token = token[0].Buffer
except pywintypes.error as e:
- e.strerror += ", Target: {1}".format(e, self._gss_host)
+ e.strerror += ", Target: {}".format(e, self._gss_host)
raise
if error == 0:
@@ -475,10 +490,12 @@ class _SSH_SSPI(_SSH_GSSAuth):
"""
self._session_id = session_id
if not gss_kex:
- mic_field = self._ssh_build_mic(self._session_id,
- self._username,
- self._service,
- self._auth_method)
+ mic_field = self._ssh_build_mic(
+ self._session_id,
+ self._username,
+ self._service,
+ self._auth_method,
+ )
mic_token = self._gss_ctxt.sign(mic_field)
else:
# for key exchange with gssapi-keyex
@@ -521,10 +538,12 @@ class _SSH_SSPI(_SSH_GSSAuth):
self._username = username
if username is not None:
# server mode
- mic_field = self._ssh_build_mic(self._session_id,
- self._username,
- self._service,
- self._auth_method)
+ mic_field = self._ssh_build_mic(
+ self._session_id,
+ self._username,
+ self._service,
+ self._auth_method,
+ )
# Verifies data and its signature. If verification fails, an
# sspi.error will be raised.
self._gss_srv_ctxt.verify(mic_field, mic_token)
@@ -542,9 +561,8 @@ class _SSH_SSPI(_SSH_GSSAuth):
:return: ``True`` if credentials are delegated, otherwise ``False``
"""
- return (
- self._gss_flags & sspicon.ISC_REQ_DELEGATE and
- (self._gss_srv_ctxt_status or self._gss_flags)
+ return self._gss_flags & sspicon.ISC_REQ_DELEGATE and (
+ self._gss_srv_ctxt_status or self._gss_flags
)
def save_client_creds(self, client_token):
diff --git a/paramiko/transport.py b/paramiko/transport.py
index c2ef4fcc..4e6cb2c1 100644
--- a/paramiko/transport.py
+++ b/paramiko/transport.py
@@ -39,18 +39,49 @@ from paramiko.auth_handler import AuthHandler
from paramiko.ssh_gss import GSSAuth
from paramiko.channel import Channel
from paramiko.common import (
- xffffffff, cMSG_CHANNEL_OPEN, cMSG_IGNORE, cMSG_GLOBAL_REQUEST, DEBUG,
- MSG_KEXINIT, MSG_IGNORE, MSG_DISCONNECT, MSG_DEBUG, ERROR, WARNING,
- cMSG_UNIMPLEMENTED, INFO, cMSG_KEXINIT, cMSG_NEWKEYS, MSG_NEWKEYS,
- cMSG_REQUEST_SUCCESS, cMSG_REQUEST_FAILURE, CONNECTION_FAILED_CODE,
- OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, OPEN_SUCCEEDED,
- cMSG_CHANNEL_OPEN_FAILURE, cMSG_CHANNEL_OPEN_SUCCESS, MSG_GLOBAL_REQUEST,
- MSG_REQUEST_SUCCESS, MSG_REQUEST_FAILURE, MSG_CHANNEL_OPEN_SUCCESS,
- MSG_CHANNEL_OPEN_FAILURE, MSG_CHANNEL_OPEN, MSG_CHANNEL_SUCCESS,
- MSG_CHANNEL_FAILURE, MSG_CHANNEL_DATA, MSG_CHANNEL_EXTENDED_DATA,
- MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_REQUEST, MSG_CHANNEL_EOF,
- MSG_CHANNEL_CLOSE, MIN_WINDOW_SIZE, MIN_PACKET_SIZE, MAX_WINDOW_SIZE,
- DEFAULT_WINDOW_SIZE, DEFAULT_MAX_PACKET_SIZE, HIGHEST_USERAUTH_MESSAGE_ID,
+ xffffffff,
+ cMSG_CHANNEL_OPEN,
+ cMSG_IGNORE,
+ cMSG_GLOBAL_REQUEST,
+ DEBUG,
+ MSG_KEXINIT,
+ MSG_IGNORE,
+ MSG_DISCONNECT,
+ MSG_DEBUG,
+ ERROR,
+ WARNING,
+ cMSG_UNIMPLEMENTED,
+ INFO,
+ cMSG_KEXINIT,
+ cMSG_NEWKEYS,
+ MSG_NEWKEYS,
+ cMSG_REQUEST_SUCCESS,
+ cMSG_REQUEST_FAILURE,
+ CONNECTION_FAILED_CODE,
+ OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED,
+ OPEN_SUCCEEDED,
+ cMSG_CHANNEL_OPEN_FAILURE,
+ cMSG_CHANNEL_OPEN_SUCCESS,
+ MSG_GLOBAL_REQUEST,
+ MSG_REQUEST_SUCCESS,
+ MSG_REQUEST_FAILURE,
+ MSG_CHANNEL_OPEN_SUCCESS,
+ MSG_CHANNEL_OPEN_FAILURE,
+ MSG_CHANNEL_OPEN,
+ MSG_CHANNEL_SUCCESS,
+ MSG_CHANNEL_FAILURE,
+ MSG_CHANNEL_DATA,
+ MSG_CHANNEL_EXTENDED_DATA,
+ MSG_CHANNEL_WINDOW_ADJUST,
+ MSG_CHANNEL_REQUEST,
+ MSG_CHANNEL_EOF,
+ MSG_CHANNEL_CLOSE,
+ MIN_WINDOW_SIZE,
+ MIN_PACKET_SIZE,
+ MAX_WINDOW_SIZE,
+ DEFAULT_WINDOW_SIZE,
+ DEFAULT_MAX_PACKET_SIZE,
+ HIGHEST_USERAUTH_MESSAGE_ID,
)
from paramiko.compress import ZlibCompressor, ZlibDecompressor
from paramiko.dsskey import DSSKey
@@ -69,7 +100,10 @@ from paramiko.ecdsakey import ECDSAKey
from paramiko.server import ServerInterface
from paramiko.sftp_client import SFTPClient
from paramiko.ssh_exception import (
- SSHException, BadAuthenticationType, ChannelException, ProxyCommandFailure,
+ SSHException,
+ BadAuthenticationType,
+ ChannelException,
+ ProxyCommandFailure,
)
from paramiko.util import retry_on_signal, ClosingContextManager, clamp_value
@@ -77,12 +111,14 @@ from paramiko.util import retry_on_signal, ClosingContextManager, clamp_value
# for thread cleanup
_active_threads = []
+
def _join_lingering_threads():
for thr in _active_threads:
thr.stop_thread()
import atexit
+
atexit.register(_join_lingering_threads)
@@ -99,160 +135,161 @@ class Transport(threading.Thread, ClosingContextManager):
_ENCRYPT = object()
_DECRYPT = object()
- _PROTO_ID = '2.0'
- _CLIENT_ID = 'paramiko_%s' % paramiko.__version__
+ _PROTO_ID = "2.0"
+ _CLIENT_ID = "paramiko_{}".format(paramiko.__version__)
# These tuples of algorithm identifiers are in preference order; do not
# reorder without reason!
_preferred_ciphers = (
- 'aes128-ctr',
- 'aes192-ctr',
- 'aes256-ctr',
- 'aes128-cbc',
- 'aes192-cbc',
- 'aes256-cbc',
- 'blowfish-cbc',
- '3des-cbc',
+ "aes128-ctr",
+ "aes192-ctr",
+ "aes256-ctr",
+ "aes128-cbc",
+ "aes192-cbc",
+ "aes256-cbc",
+ "blowfish-cbc",
+ "3des-cbc",
)
_preferred_macs = (
- 'hmac-sha2-256',
- 'hmac-sha2-512',
- 'hmac-sha1',
- 'hmac-md5',
- 'hmac-sha1-96',
- 'hmac-md5-96',
+ "hmac-sha2-256",
+ "hmac-sha2-512",
+ "hmac-sha1",
+ "hmac-md5",
+ "hmac-sha1-96",
+ "hmac-md5-96",
)
_preferred_keys = (
- 'ssh-ed25519',
- 'ecdsa-sha2-nistp256',
- 'ecdsa-sha2-nistp384',
- 'ecdsa-sha2-nistp521',
- 'ssh-rsa',
- 'ssh-dss',
+ "ssh-ed25519",
+ "ecdsa-sha2-nistp256",
+ "ecdsa-sha2-nistp384",
+ "ecdsa-sha2-nistp521",
+ "ssh-rsa",
+ "ssh-dss",
)
_preferred_kex = (
- 'ecdh-sha2-nistp256',
- 'ecdh-sha2-nistp384',
- 'ecdh-sha2-nistp521',
- 'diffie-hellman-group-exchange-sha256',
- 'diffie-hellman-group-exchange-sha1',
- 'diffie-hellman-group14-sha1',
- 'diffie-hellman-group1-sha1',
+ "ecdh-sha2-nistp256",
+ "ecdh-sha2-nistp384",
+ "ecdh-sha2-nistp521",
+ "diffie-hellman-group-exchange-sha256",
+ "diffie-hellman-group-exchange-sha1",
+ "diffie-hellman-group14-sha1",
+ "diffie-hellman-group1-sha1",
)
_preferred_gsskex = (
- 'gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==',
- 'gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==',
- 'gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==',
+ "gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==",
+ "gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==",
+ "gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==",
)
- _preferred_compression = ('none',)
+ _preferred_compression = ("none",)
_cipher_info = {
- 'aes128-ctr': {
- 'class': algorithms.AES,
- 'mode': modes.CTR,
- 'block-size': 16,
- 'key-size': 16
+ "aes128-ctr": {
+ "class": algorithms.AES,
+ "mode": modes.CTR,
+ "block-size": 16,
+ "key-size": 16,
},
- 'aes192-ctr': {
- 'class': algorithms.AES,
- 'mode': modes.CTR,
- 'block-size': 16,
- 'key-size': 24
+ "aes192-ctr": {
+ "class": algorithms.AES,
+ "mode": modes.CTR,
+ "block-size": 16,
+ "key-size": 24,
},
- 'aes256-ctr': {
- 'class': algorithms.AES,
- 'mode': modes.CTR,
- 'block-size': 16,
- 'key-size': 32
+ "aes256-ctr": {
+ "class": algorithms.AES,
+ "mode": modes.CTR,
+ "block-size": 16,
+ "key-size": 32,
},
- 'blowfish-cbc': {
- 'class': algorithms.Blowfish,
- 'mode': modes.CBC,
- 'block-size': 8,
- 'key-size': 16
+ "blowfish-cbc": {
+ "class": algorithms.Blowfish,
+ "mode": modes.CBC,
+ "block-size": 8,
+ "key-size": 16,
},
- 'aes128-cbc': {
- 'class': algorithms.AES,
- 'mode': modes.CBC,
- 'block-size': 16,
- 'key-size': 16
+ "aes128-cbc": {
+ "class": algorithms.AES,
+ "mode": modes.CBC,
+ "block-size": 16,
+ "key-size": 16,
},
- 'aes192-cbc': {
- 'class': algorithms.AES,
- 'mode': modes.CBC,
- 'block-size': 16,
- 'key-size': 24
+ "aes192-cbc": {
+ "class": algorithms.AES,
+ "mode": modes.CBC,
+ "block-size": 16,
+ "key-size": 24,
},
- 'aes256-cbc': {
- 'class': algorithms.AES,
- 'mode': modes.CBC,
- 'block-size': 16,
- 'key-size': 32
+ "aes256-cbc": {
+ "class": algorithms.AES,
+ "mode": modes.CBC,
+ "block-size": 16,
+ "key-size": 32,
},
- '3des-cbc': {
- 'class': algorithms.TripleDES,
- 'mode': modes.CBC,
- 'block-size': 8,
- 'key-size': 24
+ "3des-cbc": {
+ "class": algorithms.TripleDES,
+ "mode": modes.CBC,
+ "block-size": 8,
+ "key-size": 24,
},
}
-
_mac_info = {
- 'hmac-sha1': {'class': sha1, 'size': 20},
- 'hmac-sha1-96': {'class': sha1, 'size': 12},
- 'hmac-sha2-256': {'class': sha256, 'size': 32},
- 'hmac-sha2-512': {'class': sha512, 'size': 64},
- 'hmac-md5': {'class': md5, 'size': 16},
- 'hmac-md5-96': {'class': md5, 'size': 12},
+ "hmac-sha1": {"class": sha1, "size": 20},
+ "hmac-sha1-96": {"class": sha1, "size": 12},
+ "hmac-sha2-256": {"class": sha256, "size": 32},
+ "hmac-sha2-512": {"class": sha512, "size": 64},
+ "hmac-md5": {"class": md5, "size": 16},
+ "hmac-md5-96": {"class": md5, "size": 12},
}
_key_info = {
- 'ssh-rsa': RSAKey,
- 'ssh-rsa-cert-v01@openssh.com': RSAKey,
- 'ssh-dss': DSSKey,
- 'ssh-dss-cert-v01@openssh.com': DSSKey,
- 'ecdsa-sha2-nistp256': ECDSAKey,
- 'ecdsa-sha2-nistp256-cert-v01@openssh.com': ECDSAKey,
- 'ecdsa-sha2-nistp384': ECDSAKey,
- 'ecdsa-sha2-nistp384-cert-v01@openssh.com': ECDSAKey,
- 'ecdsa-sha2-nistp521': ECDSAKey,
- 'ecdsa-sha2-nistp521-cert-v01@openssh.com': ECDSAKey,
- 'ssh-ed25519': Ed25519Key,
- 'ssh-ed25519-cert-v01@openssh.com': Ed25519Key,
+ "ssh-rsa": RSAKey,
+ "ssh-rsa-cert-v01@openssh.com": RSAKey,
+ "ssh-dss": DSSKey,
+ "ssh-dss-cert-v01@openssh.com": DSSKey,
+ "ecdsa-sha2-nistp256": ECDSAKey,
+ "ecdsa-sha2-nistp256-cert-v01@openssh.com": ECDSAKey,
+ "ecdsa-sha2-nistp384": ECDSAKey,
+ "ecdsa-sha2-nistp384-cert-v01@openssh.com": ECDSAKey,
+ "ecdsa-sha2-nistp521": ECDSAKey,
+ "ecdsa-sha2-nistp521-cert-v01@openssh.com": ECDSAKey,
+ "ssh-ed25519": Ed25519Key,
+ "ssh-ed25519-cert-v01@openssh.com": Ed25519Key,
}
_kex_info = {
- 'diffie-hellman-group1-sha1': KexGroup1,
- 'diffie-hellman-group14-sha1': KexGroup14,
- 'diffie-hellman-group-exchange-sha1': KexGex,
- 'diffie-hellman-group-exchange-sha256': KexGexSHA256,
- 'gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==': KexGSSGroup1,
- 'gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==': KexGSSGroup14,
- 'gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==': KexGSSGex,
- 'ecdh-sha2-nistp256': KexNistp256,
- 'ecdh-sha2-nistp384': KexNistp384,
- 'ecdh-sha2-nistp521': KexNistp521,
+ "diffie-hellman-group1-sha1": KexGroup1,
+ "diffie-hellman-group14-sha1": KexGroup14,
+ "diffie-hellman-group-exchange-sha1": KexGex,
+ "diffie-hellman-group-exchange-sha256": KexGexSHA256,
+ "gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==": KexGSSGroup1,
+ "gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==": KexGSSGroup14,
+ "gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==": KexGSSGex,
+ "ecdh-sha2-nistp256": KexNistp256,
+ "ecdh-sha2-nistp384": KexNistp384,
+ "ecdh-sha2-nistp521": KexNistp521,
}
_compression_info = {
# zlib@openssh.com is just zlib, but only turned on after a successful
# authentication. openssh servers may only offer this type because
# they've had troubles with security holes in zlib in the past.
- 'zlib@openssh.com': (ZlibCompressor, ZlibDecompressor),
- 'zlib': (ZlibCompressor, ZlibDecompressor),
- 'none': (None, None),
+ "zlib@openssh.com": (ZlibCompressor, ZlibDecompressor),
+ "zlib": (ZlibCompressor, ZlibDecompressor),
+ "none": (None, None),
}
_modulus_pack = None
_active_check_timeout = 0.1
- def __init__(self,
- sock,
- default_window_size=DEFAULT_WINDOW_SIZE,
- default_max_packet_size=DEFAULT_MAX_PACKET_SIZE,
- gss_kex=False,
- gss_deleg_creds=True):
+ def __init__(
+ self,
+ sock,
+ default_window_size=DEFAULT_WINDOW_SIZE,
+ default_max_packet_size=DEFAULT_MAX_PACKET_SIZE,
+ gss_kex=False,
+ gss_deleg_creds=True,
+ ):
"""
Create a new SSH session over an existing socket, or socket-like
object. This only creates the `.Transport` object; it doesn't begin
@@ -302,7 +339,7 @@ class Transport(threading.Thread, ClosingContextManager):
if isinstance(sock, string_types):
# convert "host:port" into (host, port)
- hl = sock.split(':', 1)
+ hl = sock.split(":", 1)
self.hostname = hl[0]
if len(hl) == 1:
sock = (hl[0], 22)
@@ -312,7 +349,7 @@ class Transport(threading.Thread, ClosingContextManager):
# connect to the given (host, port)
hostname, port = sock
self.hostname = hostname
- reason = 'No suitable address family'
+ reason = "No suitable address family"
addrinfos = socket.getaddrinfo(
hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM
)
@@ -329,7 +366,8 @@ class Transport(threading.Thread, ClosingContextManager):
break
else:
raise SSHException(
- 'Unable to connect to %s: %s' % (hostname, reason))
+ "Unable to connect to {}: {}".format(hostname, reason)
+ )
# okay, normal socket-ish flow here...
threading.Thread.__init__(self)
self.setDaemon(True)
@@ -340,9 +378,9 @@ class Transport(threading.Thread, ClosingContextManager):
# negotiated crypto parameters
self.packetizer = Packetizer(sock)
- self.local_version = 'SSH-' + self._PROTO_ID + '-' + self._CLIENT_ID
- self.remote_version = ''
- self.local_cipher = self.remote_cipher = ''
+ self.local_version = "SSH-" + self._PROTO_ID + "-" + self._CLIENT_ID
+ self.remote_version = ""
+ self.local_cipher = self.remote_cipher = ""
self.local_kex_init = self.remote_kex_init = None
self.local_mac = self.remote_mac = None
self.local_compression = self.remote_compression = None
@@ -374,8 +412,8 @@ class Transport(threading.Thread, ClosingContextManager):
# tracking open channels
self._channels = ChannelMap()
- self.channel_events = {} # (id -> Event)
- self.channels_seen = {} # (id -> True)
+ self.channel_events = {} # (id -> Event)
+ self.channels_seen = {} # (id -> True)
self._channel_counter = 0
self.default_max_packet_size = default_max_packet_size
self.default_window_size = default_window_size
@@ -387,7 +425,7 @@ class Transport(threading.Thread, ClosingContextManager):
self.clear_to_send = threading.Event()
self.clear_to_send_lock = threading.Lock()
self.clear_to_send_timeout = 30.0
- self.log_name = 'paramiko.transport'
+ self.log_name = "paramiko.transport"
self.logger = util.get_logger(self.log_name)
self.packetizer.set_log(self.logger)
self.auth_handler = None
@@ -415,22 +453,25 @@ class Transport(threading.Thread, ClosingContextManager):
"""
Returns a string representation of this object, for debugging.
"""
- out = '<paramiko.Transport at %s' % hex(long(id(self)) & xffffffff)
+ id_ = hex(long(id(self)) & xffffffff)
+ out = "<paramiko.Transport at {}".format(id_)
if not self.active:
- out += ' (unconnected)'
+ out += " (unconnected)"
else:
- if self.local_cipher != '':
- out += ' (cipher %s, %d bits)' % (
+ if self.local_cipher != "":
+ out += " (cipher {}, {:d} bits)".format(
self.local_cipher,
- self._cipher_info[self.local_cipher]['key-size'] * 8
+ self._cipher_info[self.local_cipher]["key-size"] * 8,
)
if self.is_authenticated():
- out += ' (active; %d open channel(s))' % len(self._channels)
+ out += " (active; {} open channel(s))".format(
+ len(self._channels)
+ )
elif self.initial_kex_done:
- out += ' (connected; awaiting auth)'
+ out += " (connected; awaiting auth)"
else:
- out += ' (connecting)'
- out += '>'
+ out += " (connecting)"
+ out += ">"
return out
def atfork(self):
@@ -541,10 +582,9 @@ class Transport(threading.Thread, ClosingContextManager):
e = self.get_exception()
if e is not None:
raise e
- raise SSHException('Negotiation failed.')
- if (
- event.is_set() or
- (timeout is not None and time.time() >= max_time)
+ raise SSHException("Negotiation failed.")
+ if event.is_set() or (
+ timeout is not None and time.time() >= max_time
):
break
@@ -610,7 +650,7 @@ class Transport(threading.Thread, ClosingContextManager):
e = self.get_exception()
if e is not None:
raise e
- raise SSHException('Negotiation failed.')
+ raise SSHException("Negotiation failed.")
if event.is_set():
break
@@ -677,7 +717,7 @@ class Transport(threading.Thread, ClosingContextManager):
"""
Transport._modulus_pack = ModulusPack()
# places to look for the openssh "moduli" file
- file_list = ['/etc/ssh/moduli', '/usr/local/etc/moduli']
+ file_list = ["/etc/ssh/moduli", "/usr/local/etc/moduli"]
if filename is not None:
file_list.insert(0, filename)
for fn in file_list:
@@ -715,7 +755,7 @@ class Transport(threading.Thread, ClosingContextManager):
:return: public key (`.PKey`) of the remote server
"""
if (not self.active) or (not self.initial_kex_done):
- raise SSHException('No existing session')
+ raise SSHException("No existing session")
return self.host_key
def is_active(self):
@@ -729,10 +769,7 @@ class Transport(threading.Thread, ClosingContextManager):
return self.active
def open_session(
- self,
- window_size=None,
- max_packet_size=None,
- timeout=None,
+ self, window_size=None, max_packet_size=None, timeout=None
):
"""
Request a new channel to the server, of type ``"session"``. This is
@@ -759,10 +796,12 @@ class Transport(threading.Thread, ClosingContextManager):
.. versionchanged:: 1.15
Added the ``window_size`` and ``max_packet_size`` arguments.
"""
- return self.open_channel('session',
- window_size=window_size,
- max_packet_size=max_packet_size,
- timeout=timeout)
+ return self.open_channel(
+ "session",
+ window_size=window_size,
+ max_packet_size=max_packet_size,
+ timeout=timeout,
+ )
def open_x11_channel(self, src_addr=None):
"""
@@ -778,7 +817,7 @@ class Transport(threading.Thread, ClosingContextManager):
`.SSHException` -- if the request is rejected or the session ends
prematurely
"""
- return self.open_channel('x11', src_addr=src_addr)
+ return self.open_channel("x11", src_addr=src_addr)
def open_forward_agent_channel(self):
"""
@@ -792,7 +831,7 @@ class Transport(threading.Thread, ClosingContextManager):
:raises: `.SSHException` --
if the request is rejected or the session ends prematurely
"""
- return self.open_channel('auth-agent@openssh.com')
+ return self.open_channel("auth-agent@openssh.com")
def open_forwarded_tcpip_channel(self, src_addr, dest_addr):
"""
@@ -804,15 +843,17 @@ class Transport(threading.Thread, ClosingContextManager):
:param src_addr: originator's address
:param dest_addr: local (server) connected address
"""
- return self.open_channel('forwarded-tcpip', dest_addr, src_addr)
+ return self.open_channel("forwarded-tcpip", dest_addr, src_addr)
- def open_channel(self,
- kind,
- dest_addr=None,
- src_addr=None,
- window_size=None,
- max_packet_size=None,
- timeout=None):
+ def open_channel(
+ self,
+ kind,
+ dest_addr=None,
+ src_addr=None,
+ window_size=None,
+ max_packet_size=None,
+ timeout=None,
+ ):
"""
Request a new channel to the server. `Channels <.Channel>` are
socket-like objects used for the actual transfer of data across the
@@ -849,7 +890,7 @@ class Transport(threading.Thread, ClosingContextManager):
Added the ``window_size`` and ``max_packet_size`` arguments.
"""
if not self.active:
- raise SSHException('SSH session not active')
+ raise SSHException("SSH session not active")
timeout = 3600 if timeout is None else timeout
self.lock.acquire()
try:
@@ -862,12 +903,12 @@ class Transport(threading.Thread, ClosingContextManager):
m.add_int(chanid)
m.add_int(window_size)
m.add_int(max_packet_size)
- if (kind == 'forwarded-tcpip') or (kind == 'direct-tcpip'):
+ if (kind == "forwarded-tcpip") or (kind == "direct-tcpip"):
m.add_string(dest_addr[0])
m.add_int(dest_addr[1])
m.add_string(src_addr[0])
m.add_int(src_addr[1])
- elif kind == 'x11':
+ elif kind == "x11":
m.add_string(src_addr[0])
m.add_int(src_addr[1])
chan = Channel(chanid)
@@ -885,18 +926,18 @@ class Transport(threading.Thread, ClosingContextManager):
if not self.active:
e = self.get_exception()
if e is None:
- e = SSHException('Unable to open channel.')
+ e = SSHException("Unable to open channel.")
raise e
if event.is_set():
break
elif start_ts + timeout < time.time():
- raise SSHException('Timeout opening channel.')
+ raise SSHException("Timeout opening channel.")
chan = self._channels.get(chanid)
if chan is not None:
return chan
e = self.get_exception()
if e is None:
- e = SSHException('Unable to open channel.')
+ e = SSHException("Unable to open channel.")
raise e
def request_port_forward(self, address, port, handler=None):
@@ -933,20 +974,22 @@ class Transport(threading.Thread, ClosingContextManager):
`.SSHException` -- if the server refused the TCP forward request
"""
if not self.active:
- raise SSHException('SSH session not active')
+ raise SSHException("SSH session not active")
port = int(port)
response = self.global_request(
- 'tcpip-forward', (address, port), wait=True
+ "tcpip-forward", (address, port), wait=True
)
if response is None:
- raise SSHException('TCP forwarding request denied')
+ raise SSHException("TCP forwarding request denied")
if port == 0:
port = response.get_int()
if handler is None:
+
def default_handler(channel, src_addr, dest_addr_port):
# src_addr, src_port = src_addr_port
# dest_addr, dest_port = dest_addr_port
self._queue_incoming_channel(channel)
+
handler = default_handler
self._tcp_handler = handler
return port
@@ -963,7 +1006,7 @@ class Transport(threading.Thread, ClosingContextManager):
if not self.active:
return
self._tcp_handler = None
- self.global_request('cancel-tcpip-forward', (address, port), wait=True)
+ self.global_request("cancel-tcpip-forward", (address, port), wait=True)
def open_sftp_client(self):
"""
@@ -1016,7 +1059,7 @@ class Transport(threading.Thread, ClosingContextManager):
e = self.get_exception()
if e is not None:
raise e
- raise SSHException('Negotiation failed.')
+ raise SSHException("Negotiation failed.")
if self.completion_event.is_set():
break
return
@@ -1032,8 +1075,10 @@ class Transport(threading.Thread, ClosingContextManager):
seconds to wait before sending a keepalive packet (or
0 to disable keepalives).
"""
+
def _request(x=weakref.proxy(self)):
- return x.global_request('keepalive@lag.net', wait=False)
+ return x.global_request("keepalive@lag.net", wait=False)
+
self.packetizer.set_keepalive(interval, _request)
def global_request(self, kind, data=None, wait=True):
@@ -1061,7 +1106,7 @@ class Transport(threading.Thread, ClosingContextManager):
m.add_boolean(wait)
if data is not None:
m.add(*data)
- self._log(DEBUG, 'Sending global request "%s"' % kind)
+ self._log(DEBUG, 'Sending global request "{}"'.format(kind))
self._send_user_message(m)
if not wait:
return None
@@ -1101,7 +1146,7 @@ class Transport(threading.Thread, ClosingContextManager):
def connect(
self,
hostkey=None,
- username='',
+ username="",
password=None,
pkey=None,
gss_host=None,
@@ -1175,33 +1220,43 @@ class Transport(threading.Thread, ClosingContextManager):
if (hostkey is not None) and not gss_kex:
key = self.get_remote_server_key()
if (
- key.get_name() != hostkey.get_name() or
- key.asbytes() != hostkey.asbytes()
+ key.get_name() != hostkey.get_name()
+ or key.asbytes() != hostkey.asbytes()
):
- self._log(DEBUG, 'Bad host key from server')
- self._log(DEBUG, 'Expected: %s: %s' % (
- hostkey.get_name(), repr(hostkey.asbytes()))
+ self._log(DEBUG, "Bad host key from server")
+ self._log(
+ DEBUG,
+ "Expected: {}: {}".format(
+ hostkey.get_name(), repr(hostkey.asbytes())
+ ),
)
- self._log(DEBUG, 'Got : %s: %s' % (
- key.get_name(), repr(key.asbytes()))
+ self._log(
+ DEBUG,
+ "Got : {}: {}".format(
+ key.get_name(), repr(key.asbytes())
+ ),
)
- raise SSHException('Bad host key from server')
- self._log(DEBUG, 'Host key verified (%s)' % hostkey.get_name())
+ raise SSHException("Bad host key from server")
+ self._log(
+ DEBUG, "Host key verified ({})".format(hostkey.get_name())
+ )
if (pkey is not None) or (password is not None) or gss_auth or gss_kex:
if gss_auth:
- self._log(DEBUG, 'Attempting GSS-API auth... (gssapi-with-mic)') # noqa
+ self._log(
+ DEBUG, "Attempting GSS-API auth... (gssapi-with-mic)"
+ ) # noqa
self.auth_gssapi_with_mic(
- username, self.gss_host, gss_deleg_creds,
+ username, self.gss_host, gss_deleg_creds
)
elif gss_kex:
- self._log(DEBUG, 'Attempting GSS-API auth... (gssapi-keyex)')
+ self._log(DEBUG, "Attempting GSS-API auth... (gssapi-keyex)")
self.auth_gssapi_keyex(username)
elif pkey is not None:
- self._log(DEBUG, 'Attempting public-key auth...')
+ self._log(DEBUG, "Attempting public-key auth...")
self.auth_publickey(username, pkey)
else:
- self._log(DEBUG, 'Attempting password auth...')
+ self._log(DEBUG, "Attempting password auth...")
self.auth_password(username, password)
return
@@ -1256,9 +1311,9 @@ class Transport(threading.Thread, ClosingContextManager):
closed.
"""
return (
- self.active and
- self.auth_handler is not None and
- self.auth_handler.is_authenticated()
+ self.active
+ and self.auth_handler is not None
+ and self.auth_handler.is_authenticated()
)
def get_username(self):
@@ -1295,7 +1350,7 @@ class Transport(threading.Thread, ClosingContextManager):
:param str username: the username to authenticate as
:return:
- `list` of auth types permissible for the next stage of
+ list of auth types permissible for the next stage of
authentication (normally empty)
:raises:
@@ -1308,7 +1363,7 @@ class Transport(threading.Thread, ClosingContextManager):
.. versionadded:: 1.5
"""
if (not self.active) or (not self.initial_kex_done):
- raise SSHException('No existing session')
+ raise SSHException("No existing session")
my_event = threading.Event()
self.auth_handler = AuthHandler(self)
self.auth_handler.auth_none(username, my_event)
@@ -1350,7 +1405,7 @@ class Transport(threading.Thread, ClosingContextManager):
``True`` if an attempt at an automated "interactive" password auth
should be made if the server doesn't support normal password auth
:return:
- `list` of auth types permissible for the next stage of
+ list of auth types permissible for the next stage of
authentication (normally empty)
:raises:
@@ -1364,7 +1419,7 @@ class Transport(threading.Thread, ClosingContextManager):
if (not self.active) or (not self.initial_kex_done):
# we should never try to send the password unless we're on a secure
# link
- raise SSHException('No existing session')
+ raise SSHException("No existing session")
if event is None:
my_event = threading.Event()
else:
@@ -1379,12 +1434,13 @@ class Transport(threading.Thread, ClosingContextManager):
except BadAuthenticationType as e:
# if password auth isn't allowed, but keyboard-interactive *is*,
# try to fudge it
- if not fallback or ('keyboard-interactive' not in e.allowed_types):
+ if not fallback or ("keyboard-interactive" not in e.allowed_types):
raise
try:
+
def handler(title, instructions, fields):
if len(fields) > 1:
- raise SSHException('Fallback authentication failed.')
+ raise SSHException("Fallback authentication failed.")
if len(fields) == 0:
# for some reason, at least on os x, a 2nd request will
# be made with zero fields requested. maybe it's just
@@ -1392,6 +1448,7 @@ class Transport(threading.Thread, ClosingContextManager):
# type we're doing here. *shrug* :)
return []
return [password]
+
return self.auth_interactive(username, handler)
except SSHException:
# attempt failed; just raise the original exception
@@ -1421,7 +1478,7 @@ class Transport(threading.Thread, ClosingContextManager):
an event to trigger when the authentication attempt is complete
(whether it was successful or not)
:return:
- `list` of auth types permissible for the next stage of
+ list of auth types permissible for the next stage of
authentication (normally empty)
:raises:
@@ -1434,7 +1491,7 @@ class Transport(threading.Thread, ClosingContextManager):
"""
if (not self.active) or (not self.initial_kex_done):
# we should never try to authenticate unless we're on a secure link
- raise SSHException('No existing session')
+ raise SSHException("No existing session")
if event is None:
my_event = threading.Event()
else:
@@ -1446,7 +1503,7 @@ class Transport(threading.Thread, ClosingContextManager):
return []
return self.auth_handler.wait_for_response(my_event)
- def auth_interactive(self, username, handler, submethods=''):
+ def auth_interactive(self, username, handler, submethods=""):
"""
Authenticate to the server interactively. A handler is used to answer
arbitrary questions from the server. On many servers, this is just a
@@ -1479,7 +1536,7 @@ class Transport(threading.Thread, ClosingContextManager):
:param callable handler: a handler for responding to server questions
:param str submethods: a string list of desired submethods (optional)
:return:
- `list` of auth types permissible for the next stage of
+ list of auth types permissible for the next stage of
authentication (normally empty).
:raises: `.BadAuthenticationType` -- if public-key authentication isn't
@@ -1491,7 +1548,7 @@ class Transport(threading.Thread, ClosingContextManager):
"""
if (not self.active) or (not self.initial_kex_done):
# we should never try to authenticate unless we're on a secure link
- raise SSHException('No existing session')
+ raise SSHException("No existing session")
my_event = threading.Event()
self.auth_handler = AuthHandler(self)
self.auth_handler.auth_interactive(
@@ -1499,7 +1556,7 @@ class Transport(threading.Thread, ClosingContextManager):
)
return self.auth_handler.wait_for_response(my_event)
- def auth_interactive_dumb(self, username, handler=None, submethods=''):
+ def auth_interactive_dumb(self, username, handler=None, submethods=""):
"""
Autenticate to the server interactively but dumber.
Just print the prompt and / or instructions to stdout and send back
@@ -1508,6 +1565,7 @@ class Transport(threading.Thread, ClosingContextManager):
"""
if not handler:
+
def handler(title, instructions, prompt_list):
answers = []
if title:
@@ -1515,9 +1573,10 @@ class Transport(threading.Thread, ClosingContextManager):
if instructions:
print(instructions.strip())
for prompt, show_input in prompt_list:
- print(prompt.strip(), end=' ')
+ print(prompt.strip(), end=" ")
answers.append(input())
return answers
+
return self.auth_interactive(username, handler, submethods)
def auth_gssapi_with_mic(self, username, gss_host, gss_deleg_creds):
@@ -1529,7 +1588,6 @@ class Transport(threading.Thread, ClosingContextManager):
:param bool gss_deleg_creds: Delegate credentials or not
:return: list of auth types permissible for the next stage of
authentication (normally empty)
- :rtype: list
:raises: `.BadAuthenticationType` -- if gssapi-with-mic isn't
allowed by the server (and no event was passed in)
:raises:
@@ -1539,7 +1597,7 @@ class Transport(threading.Thread, ClosingContextManager):
"""
if (not self.active) or (not self.initial_kex_done):
# we should never try to authenticate unless we're on a secure link
- raise SSHException('No existing session')
+ raise SSHException("No existing session")
my_event = threading.Event()
self.auth_handler = AuthHandler(self)
self.auth_handler.auth_gssapi_with_mic(
@@ -1553,7 +1611,7 @@ class Transport(threading.Thread, ClosingContextManager):
:param str username: The username to authenticate as.
:returns:
- a `list` of auth types permissible for the next stage of
+ a list of auth types permissible for the next stage of
authentication (normally empty)
:raises: `.BadAuthenticationType` --
if GSS-API Key Exchange was not performed (and no event was passed
@@ -1564,7 +1622,7 @@ class Transport(threading.Thread, ClosingContextManager):
"""
if (not self.active) or (not self.initial_kex_done):
# we should never try to authenticate unless we're on a secure link
- raise SSHException('No existing session')
+ raise SSHException("No existing session")
my_event = threading.Event()
self.auth_handler = AuthHandler(self)
self.auth_handler.auth_gssapi_keyex(username, my_event)
@@ -1631,9 +1689,9 @@ class Transport(threading.Thread, ClosingContextManager):
.. versionadded:: 1.5.2
"""
if compress:
- self._preferred_compression = ('zlib@openssh.com', 'zlib', 'none')
+ self._preferred_compression = ("zlib@openssh.com", "zlib", "none")
else:
- self._preferred_compression = ('none',)
+ self._preferred_compression = ("none",)
def getpeername(self):
"""
@@ -1647,9 +1705,9 @@ class Transport(threading.Thread, ClosingContextManager):
the address of the remote host, if known, as a ``(str, int)``
tuple.
"""
- gp = getattr(self.sock, 'getpeername', None)
+ gp = getattr(self.sock, "getpeername", None)
if gp is None:
- return 'unknown', 0
+ return "unknown", 0
return gp()
def stop_thread(self):
@@ -1668,10 +1726,10 @@ class Transport(threading.Thread, ClosingContextManager):
# our socket and packetizer are both closed (but where we'd
# otherwise be sitting forever on that recv()).
while (
- self.is_alive() and
- self is not threading.current_thread() and
- not self.sock._closed and
- not self.packetizer.closed
+ self.is_alive()
+ and self is not threading.current_thread()
+ and not self.sock._closed
+ and not self.packetizer.closed
):
self.join(0.1)
@@ -1713,14 +1771,18 @@ class Transport(threading.Thread, ClosingContextManager):
while True:
self.clear_to_send.wait(0.1)
if not self.active:
- self._log(DEBUG, 'Dropping user packet because connection is dead.') # noqa
+ self._log(
+ DEBUG, "Dropping user packet because connection is dead."
+ ) # noqa
return
self.clear_to_send_lock.acquire()
if self.clear_to_send.is_set():
break
self.clear_to_send_lock.release()
if time.time() > start + self.clear_to_send_timeout:
- raise SSHException('Key-exchange timed out waiting for key negotiation') # noqa
+ raise SSHException(
+ "Key-exchange timed out waiting for key negotiation"
+ ) # noqa
try:
self._send_message(data)
finally:
@@ -1744,9 +1806,13 @@ class Transport(threading.Thread, ClosingContextManager):
def _verify_key(self, host_key, sig):
key = self._key_info[self.host_key_type](Message(host_key))
if key is None:
- raise SSHException('Unknown host key type')
+ raise SSHException("Unknown host key type")
if not key.verify_ssh_sig(self.H, Message(sig)):
- raise SSHException('Signature verification (%s) failed.' % self.host_key_type) # noqa
+ raise SSHException(
+ "Signature verification ({}) failed.".format(
+ self.host_key_type
+ )
+ ) # noqa
self.host_key = key
def _compute_key(self, id, nbytes):
@@ -1758,16 +1824,16 @@ class Transport(threading.Thread, ClosingContextManager):
m.add_bytes(self.session_id)
# Fallback to SHA1 for kex engines that fail to specify a hex
# algorithm, or for e.g. transport tests that don't run kexinit.
- hash_algo = getattr(self.kex_engine, 'hash_algo', None)
- hash_select_msg = "kex engine %s specified hash_algo %r" % (
+ hash_algo = getattr(self.kex_engine, "hash_algo", None)
+ hash_select_msg = "kex engine {} specified hash_algo {!r}".format(
self.kex_engine.__class__.__name__, hash_algo
)
if hash_algo is None:
hash_algo = sha1
hash_select_msg += ", falling back to sha1"
- if not hasattr(self, '_logged_hash_selection'):
+ if not hasattr(self, "_logged_hash_selection"):
self._log(DEBUG, hash_select_msg)
- setattr(self, '_logged_hash_selection', True)
+ setattr(self, "_logged_hash_selection", True)
out = sofar = hash_algo(m.asbytes()).digest()
while len(out) < nbytes:
m = Message()
@@ -1781,11 +1847,11 @@ class Transport(threading.Thread, ClosingContextManager):
def _get_cipher(self, name, key, iv, operation):
if name not in self._cipher_info:
- raise SSHException('Unknown client cipher ' + name)
+ raise SSHException("Unknown client cipher " + name)
else:
cipher = Cipher(
- self._cipher_info[name]['class'](key),
- self._cipher_info[name]['mode'](iv),
+ self._cipher_info[name]["class"](key),
+ self._cipher_info[name]["mode"](iv),
backend=default_backend(),
)
if operation is self._ENCRYPT:
@@ -1795,8 +1861,10 @@ class Transport(threading.Thread, ClosingContextManager):
def _set_forward_agent_handler(self, handler):
if handler is None:
+
def default_handler(channel):
self._queue_incoming_channel(channel)
+
self._forward_agent_handler = default_handler
else:
self._forward_agent_handler = handler
@@ -1807,6 +1875,7 @@ class Transport(threading.Thread, ClosingContextManager):
# by default, use the same mechanism as accept()
def default_handler(channel, src_addr_port):
self._queue_incoming_channel(channel)
+
self._x11_handler = default_handler
else:
self._x11_handler = handler
@@ -1840,9 +1909,9 @@ class Transport(threading.Thread, ClosingContextManager):
Otherwise (client mode, authed, or pre-auth message) returns None.
"""
if (
- not self.server_mode or
- ptype <= HIGHEST_USERAUTH_MESSAGE_ID or
- self.is_authenticated()
+ not self.server_mode
+ or ptype <= HIGHEST_USERAUTH_MESSAGE_ID
+ or self.is_authenticated()
):
return None
# WELP. We must be dealing with someone trying to do non-auth things
@@ -1853,13 +1922,13 @@ class Transport(threading.Thread, ClosingContextManager):
reply.add_byte(cMSG_REQUEST_FAILURE)
# Channel opens let us reject w/ a specific type + message.
elif ptype == MSG_CHANNEL_OPEN:
- kind = message.get_text() # noqa
+ kind = message.get_text() # noqa
chanid = message.get_int()
reply.add_byte(cMSG_CHANNEL_OPEN_FAILURE)
reply.add_int(chanid)
reply.add_int(OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED)
- reply.add_string('')
- reply.add_string('en')
+ reply.add_string("")
+ reply.add_string("en")
# NOTE: Post-open channel messages do not need checking; the above will
# reject attemps to open channels, meaning that even if a malicious
# user tries to send a MSG_CHANNEL_REQUEST, it will simply fall under
@@ -1881,13 +1950,16 @@ class Transport(threading.Thread, ClosingContextManager):
_active_threads.append(self)
tid = hex(long(id(self)) & xffffffff)
if self.server_mode:
- self._log(DEBUG, 'starting thread (server mode): %s' % tid)
+ self._log(DEBUG, "starting thread (server mode): {}".format(tid))
else:
- self._log(DEBUG, 'starting thread (client mode): %s' % tid)
+ self._log(DEBUG, "starting thread (client mode): {}".format(tid))
try:
try:
- self.packetizer.write_all(b(self.local_version + '\r\n'))
- self._log(DEBUG, 'Local version/idstring: %s' % self.local_version) # noqa
+ self.packetizer.write_all(b(self.local_version + "\r\n"))
+ self._log(
+ DEBUG,
+ "Local version/idstring: {}".format(self.local_version),
+ ) # noqa
self._check_banner()
# The above is actually very much part of the handshake, but
# sometimes the banner can be read but the machine is not
@@ -1917,7 +1989,11 @@ class Transport(threading.Thread, ClosingContextManager):
continue
if len(self._expected_packet) > 0:
if ptype not in self._expected_packet:
- raise SSHException('Expecting packet from %r, got %d' % (self._expected_packet, ptype)) # noqa
+ raise SSHException(
+ "Expecting packet from {!r}, got {:d}".format(
+ self._expected_packet, ptype
+ )
+ ) # noqa
self._expected_packet = tuple()
if (ptype >= 30) and (ptype <= 41):
self.kex_engine.parse_next(ptype, m)
@@ -1935,44 +2011,55 @@ class Transport(threading.Thread, ClosingContextManager):
if chan is not None:
self._channel_handler_table[ptype](chan, m)
elif chanid in self.channels_seen:
- self._log(DEBUG, 'Ignoring message for dead channel %d' % chanid) # noqa
+ self._log(
+ DEBUG,
+ "Ignoring message for dead channel {:d}".format( # noqa
+ chanid
+ ),
+ )
else:
- self._log(ERROR, 'Channel request for unknown channel %d' % chanid) # noqa
+ self._log(
+ ERROR,
+ "Channel request for unknown channel {:d}".format( # noqa
+ chanid
+ ),
+ )
break
elif (
- self.auth_handler is not None and
- ptype in self.auth_handler._handler_table
+ self.auth_handler is not None
+ and ptype in self.auth_handler._handler_table
):
handler = self.auth_handler._handler_table[ptype]
handler(self.auth_handler, m)
if len(self._expected_packet) > 0:
continue
else:
- self._log(WARNING, 'Oops, unhandled type %d' % ptype)
+ err = "Oops, unhandled type {:d}".format(ptype)
+ self._log(WARNING, err)
msg = Message()
msg.add_byte(cMSG_UNIMPLEMENTED)
msg.add_int(m.seqno)
self._send_message(msg)
self.packetizer.complete_handshake()
except SSHException as e:
- self._log(ERROR, 'Exception: ' + str(e))
+ self._log(ERROR, "Exception: " + str(e))
self._log(ERROR, util.tb_strings())
self.saved_exception = e
except EOFError as e:
- self._log(DEBUG, 'EOF in transport thread')
+ self._log(DEBUG, "EOF in transport thread")
self.saved_exception = e
except socket.error as e:
if type(e.args) is tuple:
if e.args:
- emsg = '%s (%d)' % (e.args[1], e.args[0])
+ emsg = "{} ({:d})".format(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)
+ self._log(ERROR, "Socket exception: " + emsg)
self.saved_exception = e
except Exception as e:
- self._log(ERROR, 'Unknown exception: ' + str(e))
+ self._log(ERROR, "Unknown exception: " + str(e))
self._log(ERROR, util.tb_strings())
self.saved_exception = e
_active_threads.remove(self)
@@ -2001,16 +2088,15 @@ class Transport(threading.Thread, ClosingContextManager):
if self.sys.modules is not None:
raise
-
def _log_agreement(self, which, local, remote):
# Log useful, non-duplicative line re: an agreed-upon algorithm.
# Old code implied algorithms could be asymmetrical (different for
# inbound vs outbound) so we preserve that possibility.
- msg = "{0} agreed: ".format(which)
+ msg = "{} agreed: ".format(which)
if local == remote:
msg += local
else:
- msg += "local={0}, remote={1}".format(local, remote)
+ msg += "local={}, remote={}".format(local, remote)
self._log(DEBUG, msg)
# protocol stages
@@ -2043,32 +2129,32 @@ class Transport(threading.Thread, ClosingContextManager):
raise
except Exception as e:
raise SSHException(
- 'Error reading SSH protocol banner' + str(e)
+ "Error reading SSH protocol banner" + str(e)
)
- if buf[:4] == 'SSH-':
+ if buf[:4] == "SSH-":
break
- self._log(DEBUG, 'Banner: ' + buf)
- if buf[:4] != 'SSH-':
+ self._log(DEBUG, "Banner: " + buf)
+ if buf[:4] != "SSH-":
raise SSHException('Indecipherable protocol version "' + buf + '"')
# save this server version string for later
self.remote_version = buf
- self._log(DEBUG, 'Remote version/idstring: %s' % buf)
+ self._log(DEBUG, "Remote version/idstring: {}".format(buf))
# pull off any attached comment
# NOTE: comment used to be stored in a variable and then...never used.
# since 2003. ca 877cd974b8182d26fa76d566072917ea67b64e67
- i = buf.find(' ')
+ i = buf.find(" ")
if i >= 0:
buf = buf[:i]
# parse out version string and make sure it matches
- segs = buf.split('-', 2)
+ segs = buf.split("-", 2)
if len(segs) < 3:
- raise SSHException('Invalid SSH banner')
+ raise SSHException("Invalid SSH banner")
version = segs[1]
client = segs[2]
- if version != '1.99' and version != '2.0':
- msg = 'Incompatible version ({0} instead of 2.0)'
+ if version != "1.99" and version != "2.0":
+ msg = "Incompatible version ({} instead of 2.0)"
raise SSHException(msg.format(version))
- msg = 'Connected (version {0}, client {1})'.format(version, client)
+ msg = "Connected (version {}, client {})".format(version, client)
self._log(INFO, msg)
def _send_kex_init(self):
@@ -2084,25 +2170,27 @@ class Transport(threading.Thread, ClosingContextManager):
self.gss_kex_used = False
self.in_kex = True
if self.server_mode:
- mp_required_prefix = 'diffie-hellman-group-exchange-sha'
+ mp_required_prefix = "diffie-hellman-group-exchange-sha"
kex_mp = [
- k for k
- in self._preferred_kex
+ k
+ for k in self._preferred_kex
if k.startswith(mp_required_prefix)
]
if (self._modulus_pack is None) and (len(kex_mp) > 0):
# can't do group-exchange if we don't have a pack of potential
# primes
pkex = [
- k for k
- in self.get_security_options().kex
+ k
+ for k in self.get_security_options().kex
if not k.startswith(mp_required_prefix)
]
self.get_security_options().kex = pkex
- available_server_keys = list(filter(
- list(self.server_key_dict.keys()).__contains__,
- self._preferred_keys
- ))
+ available_server_keys = list(
+ filter(
+ list(self.server_key_dict.keys()).__contains__,
+ self._preferred_keys,
+ )
+ )
else:
available_server_keys = self._preferred_keys
@@ -2126,7 +2214,7 @@ class Transport(threading.Thread, ClosingContextManager):
self._send_message(m)
def _parse_kex_init(self, m):
- m.get_bytes(16) # cookie, discarded
+ m.get_bytes(16) # cookie, discarded
kex_algo_list = m.get_list()
server_key_algo_list = m.get_list()
client_encrypt_algo_list = m.get_list()
@@ -2138,20 +2226,32 @@ class Transport(threading.Thread, ClosingContextManager):
client_lang_list = m.get_list()
server_lang_list = m.get_list()
kex_follows = m.get_boolean()
- m.get_int() # unused
-
- self._log(DEBUG,
- 'kex algos:' + str(kex_algo_list) +
- ' server key:' + str(server_key_algo_list) +
- ' client encrypt:' + str(client_encrypt_algo_list) +
- ' server encrypt:' + str(server_encrypt_algo_list) +
- ' client mac:' + str(client_mac_algo_list) +
- ' server mac:' + str(server_mac_algo_list) +
- ' client compress:' + str(client_compress_algo_list) +
- ' server compress:' + str(server_compress_algo_list) +
- ' client lang:' + str(client_lang_list) +
- ' server lang:' + str(server_lang_list) +
- ' kex follows?' + str(kex_follows)
+ m.get_int() # unused
+
+ self._log(
+ DEBUG,
+ "kex algos:"
+ + str(kex_algo_list)
+ + " server key:"
+ + str(server_key_algo_list)
+ + " client encrypt:"
+ + str(client_encrypt_algo_list)
+ + " server encrypt:"
+ + str(server_encrypt_algo_list)
+ + " client mac:"
+ + str(client_mac_algo_list)
+ + " server mac:"
+ + str(server_mac_algo_list)
+ + " client compress:"
+ + str(client_compress_algo_list)
+ + " server compress:"
+ + str(server_compress_algo_list)
+ + " client lang:"
+ + str(client_lang_list)
+ + " server lang:"
+ + str(server_lang_list)
+ + " kex follows?"
+ + str(kex_follows),
)
# as a server, we pick the first item in the client's list that we
@@ -2159,122 +2259,150 @@ class Transport(threading.Thread, ClosingContextManager):
# as a client, we pick the first item in our list that the server
# supports.
if self.server_mode:
- agreed_kex = list(filter(
- self._preferred_kex.__contains__,
- kex_algo_list
- ))
+ agreed_kex = list(
+ filter(self._preferred_kex.__contains__, kex_algo_list)
+ )
else:
- agreed_kex = list(filter(
- kex_algo_list.__contains__,
- self._preferred_kex
- ))
+ agreed_kex = list(
+ filter(kex_algo_list.__contains__, self._preferred_kex)
+ )
if len(agreed_kex) == 0:
- raise SSHException('Incompatible ssh peer (no acceptable kex algorithm)') # noqa
+ raise SSHException(
+ "Incompatible ssh peer (no acceptable kex algorithm)"
+ ) # noqa
self.kex_engine = self._kex_info[agreed_kex[0]](self)
- self._log(DEBUG, "Kex agreed: %s" % agreed_kex[0])
+ self._log(DEBUG, "Kex agreed: {}".format(agreed_kex[0]))
if self.server_mode:
- available_server_keys = list(filter(
- list(self.server_key_dict.keys()).__contains__,
- self._preferred_keys
- ))
- agreed_keys = list(filter(
- available_server_keys.__contains__, server_key_algo_list
- ))
+ available_server_keys = list(
+ filter(
+ list(self.server_key_dict.keys()).__contains__,
+ self._preferred_keys,
+ )
+ )
+ agreed_keys = list(
+ filter(
+ available_server_keys.__contains__, server_key_algo_list
+ )
+ )
else:
- agreed_keys = list(filter(
- server_key_algo_list.__contains__, self._preferred_keys
- ))
+ agreed_keys = list(
+ filter(server_key_algo_list.__contains__, self._preferred_keys)
+ )
if len(agreed_keys) == 0:
- raise SSHException('Incompatible ssh peer (no acceptable host key)') # noqa
+ raise SSHException(
+ "Incompatible ssh peer (no acceptable host key)"
+ ) # noqa
self.host_key_type = agreed_keys[0]
if self.server_mode and (self.get_server_key() is None):
- raise SSHException('Incompatible ssh peer (can\'t match requested host key type)') # noqa
- self._log_agreement(
- 'HostKey', agreed_keys[0], agreed_keys[0]
- )
+ raise SSHException(
+ "Incompatible ssh peer (can't match requested host key type)"
+ ) # noqa
+ self._log_agreement("HostKey", agreed_keys[0], agreed_keys[0])
if self.server_mode:
- agreed_local_ciphers = list(filter(
- self._preferred_ciphers.__contains__,
- server_encrypt_algo_list
- ))
- agreed_remote_ciphers = list(filter(
- self._preferred_ciphers.__contains__,
- client_encrypt_algo_list
- ))
+ agreed_local_ciphers = list(
+ filter(
+ self._preferred_ciphers.__contains__,
+ server_encrypt_algo_list,
+ )
+ )
+ agreed_remote_ciphers = list(
+ filter(
+ self._preferred_ciphers.__contains__,
+ client_encrypt_algo_list,
+ )
+ )
else:
- agreed_local_ciphers = list(filter(
- client_encrypt_algo_list.__contains__,
- self._preferred_ciphers
- ))
- agreed_remote_ciphers = list(filter(
- server_encrypt_algo_list.__contains__,
- self._preferred_ciphers
- ))
+ agreed_local_ciphers = list(
+ filter(
+ client_encrypt_algo_list.__contains__,
+ self._preferred_ciphers,
+ )
+ )
+ agreed_remote_ciphers = list(
+ filter(
+ server_encrypt_algo_list.__contains__,
+ self._preferred_ciphers,
+ )
+ )
if len(agreed_local_ciphers) == 0 or len(agreed_remote_ciphers) == 0:
- raise SSHException('Incompatible ssh server (no acceptable ciphers)') # noqa
+ raise SSHException(
+ "Incompatible ssh server (no acceptable ciphers)"
+ ) # noqa
self.local_cipher = agreed_local_ciphers[0]
self.remote_cipher = agreed_remote_ciphers[0]
self._log_agreement(
- 'Cipher', local=self.local_cipher, remote=self.remote_cipher
+ "Cipher", local=self.local_cipher, remote=self.remote_cipher
)
if self.server_mode:
- agreed_remote_macs = list(filter(
- self._preferred_macs.__contains__, client_mac_algo_list
- ))
- agreed_local_macs = list(filter(
- self._preferred_macs.__contains__, server_mac_algo_list
- ))
+ agreed_remote_macs = list(
+ filter(self._preferred_macs.__contains__, client_mac_algo_list)
+ )
+ agreed_local_macs = list(
+ filter(self._preferred_macs.__contains__, server_mac_algo_list)
+ )
else:
- agreed_local_macs = list(filter(
- client_mac_algo_list.__contains__, self._preferred_macs
- ))
- agreed_remote_macs = list(filter(
- server_mac_algo_list.__contains__, self._preferred_macs
- ))
+ agreed_local_macs = list(
+ filter(client_mac_algo_list.__contains__, self._preferred_macs)
+ )
+ agreed_remote_macs = list(
+ filter(server_mac_algo_list.__contains__, self._preferred_macs)
+ )
if (len(agreed_local_macs) == 0) or (len(agreed_remote_macs) == 0):
- raise SSHException('Incompatible ssh server (no acceptable macs)')
+ raise SSHException("Incompatible ssh server (no acceptable macs)")
self.local_mac = agreed_local_macs[0]
self.remote_mac = agreed_remote_macs[0]
self._log_agreement(
- 'MAC', local=self.local_mac, remote=self.remote_mac
+ "MAC", local=self.local_mac, remote=self.remote_mac
)
if self.server_mode:
- agreed_remote_compression = list(filter(
- self._preferred_compression.__contains__,
- client_compress_algo_list
- ))
- agreed_local_compression = list(filter(
- self._preferred_compression.__contains__,
- server_compress_algo_list
- ))
+ agreed_remote_compression = list(
+ filter(
+ self._preferred_compression.__contains__,
+ client_compress_algo_list,
+ )
+ )
+ agreed_local_compression = list(
+ filter(
+ self._preferred_compression.__contains__,
+ server_compress_algo_list,
+ )
+ )
else:
- agreed_local_compression = list(filter(
- client_compress_algo_list.__contains__,
- self._preferred_compression
- ))
- agreed_remote_compression = list(filter(
- server_compress_algo_list.__contains__,
- self._preferred_compression
- ))
+ agreed_local_compression = list(
+ filter(
+ client_compress_algo_list.__contains__,
+ self._preferred_compression,
+ )
+ )
+ agreed_remote_compression = list(
+ filter(
+ server_compress_algo_list.__contains__,
+ self._preferred_compression,
+ )
+ )
if (
- len(agreed_local_compression) == 0 or
- len(agreed_remote_compression) == 0
+ len(agreed_local_compression) == 0
+ or len(agreed_remote_compression) == 0
):
- msg = 'Incompatible ssh server (no acceptable compression) {0!r} {1!r} {2!r}' # noqa
- raise SSHException(msg.format(
- agreed_local_compression, agreed_remote_compression,
- self._preferred_compression,
- ))
+ msg = "Incompatible ssh server (no acceptable compression)"
+ msg += " {!r} {!r} {!r}"
+ raise SSHException(
+ msg.format(
+ agreed_local_compression,
+ agreed_remote_compression,
+ self._preferred_compression,
+ )
+ )
self.local_compression = agreed_local_compression[0]
self.remote_compression = agreed_remote_compression[0]
self._log_agreement(
- 'Compression',
+ "Compression",
local=self.local_compression,
- remote=self.remote_compression
+ remote=self.remote_compression,
)
# save for computing hash later...
@@ -2287,40 +2415,36 @@ class Transport(threading.Thread, ClosingContextManager):
def _activate_inbound(self):
"""switch on newly negotiated encryption parameters for
inbound traffic"""
- block_size = self._cipher_info[self.remote_cipher]['block-size']
+ block_size = self._cipher_info[self.remote_cipher]["block-size"]
if self.server_mode:
- IV_in = self._compute_key('A', block_size)
+ IV_in = self._compute_key("A", block_size)
key_in = self._compute_key(
- 'C', self._cipher_info[self.remote_cipher]['key-size']
+ "C", self._cipher_info[self.remote_cipher]["key-size"]
)
else:
- IV_in = self._compute_key('B', block_size)
+ IV_in = self._compute_key("B", block_size)
key_in = self._compute_key(
- 'D', self._cipher_info[self.remote_cipher]['key-size']
+ "D", self._cipher_info[self.remote_cipher]["key-size"]
)
engine = self._get_cipher(
self.remote_cipher, key_in, IV_in, self._DECRYPT
)
- mac_size = self._mac_info[self.remote_mac]['size']
- mac_engine = self._mac_info[self.remote_mac]['class']
+ mac_size = self._mac_info[self.remote_mac]["size"]
+ mac_engine = self._mac_info[self.remote_mac]["class"]
# initial mac keys are done in the hash's natural size (not the
# potentially truncated transmission size)
if self.server_mode:
- mac_key = self._compute_key('E', mac_engine().digest_size)
+ mac_key = self._compute_key("E", mac_engine().digest_size)
else:
- mac_key = self._compute_key('F', mac_engine().digest_size)
+ mac_key = self._compute_key("F", mac_engine().digest_size)
self.packetizer.set_inbound_cipher(
engine, block_size, mac_engine, mac_size, mac_key
)
compress_in = self._compression_info[self.remote_compression][1]
- if (
- compress_in is not None and
- (
- self.remote_compression != 'zlib@openssh.com' or
- self.authenticated
- )
+ if compress_in is not None and (
+ self.remote_compression != "zlib@openssh.com" or self.authenticated
):
- self._log(DEBUG, 'Switching on inbound compression ...')
+ self._log(DEBUG, "Switching on inbound compression ...")
self.packetizer.set_inbound_compressor(compress_in())
def _activate_outbound(self):
@@ -2329,37 +2453,37 @@ class Transport(threading.Thread, ClosingContextManager):
m = Message()
m.add_byte(cMSG_NEWKEYS)
self._send_message(m)
- block_size = self._cipher_info[self.local_cipher]['block-size']
+ block_size = self._cipher_info[self.local_cipher]["block-size"]
if self.server_mode:
- IV_out = self._compute_key('B', block_size)
+ IV_out = self._compute_key("B", block_size)
key_out = self._compute_key(
- 'D', self._cipher_info[self.local_cipher]['key-size'])
+ "D", self._cipher_info[self.local_cipher]["key-size"]
+ )
else:
- IV_out = self._compute_key('A', block_size)
+ IV_out = self._compute_key("A", block_size)
key_out = self._compute_key(
- 'C', self._cipher_info[self.local_cipher]['key-size'])
+ "C", self._cipher_info[self.local_cipher]["key-size"]
+ )
engine = self._get_cipher(
- self.local_cipher, key_out, IV_out, self._ENCRYPT)
- mac_size = self._mac_info[self.local_mac]['size']
- mac_engine = self._mac_info[self.local_mac]['class']
+ self.local_cipher, key_out, IV_out, self._ENCRYPT
+ )
+ mac_size = self._mac_info[self.local_mac]["size"]
+ mac_engine = self._mac_info[self.local_mac]["class"]
# initial mac keys are done in the hash's natural size (not the
# potentially truncated transmission size)
if self.server_mode:
- mac_key = self._compute_key('F', mac_engine().digest_size)
+ mac_key = self._compute_key("F", mac_engine().digest_size)
else:
- mac_key = self._compute_key('E', mac_engine().digest_size)
- sdctr = self.local_cipher.endswith('-ctr')
+ mac_key = self._compute_key("E", mac_engine().digest_size)
+ sdctr = self.local_cipher.endswith("-ctr")
self.packetizer.set_outbound_cipher(
- engine, block_size, mac_engine, mac_size, mac_key, sdctr)
+ engine, block_size, mac_engine, mac_size, mac_key, sdctr
+ )
compress_out = self._compression_info[self.local_compression][0]
- if (
- compress_out is not None and
- (
- self.local_compression != 'zlib@openssh.com' or
- self.authenticated
- )
+ if compress_out is not None and (
+ self.local_compression != "zlib@openssh.com" or self.authenticated
):
- self._log(DEBUG, 'Switching on outbound compression ...')
+ self._log(DEBUG, "Switching on outbound compression ...")
self.packetizer.set_outbound_compressor(compress_out())
if not self.packetizer.need_rekey():
self.in_kex = False
@@ -2369,17 +2493,17 @@ class Transport(threading.Thread, ClosingContextManager):
def _auth_trigger(self):
self.authenticated = True
# delayed initiation of compression
- if self.local_compression == 'zlib@openssh.com':
+ if self.local_compression == "zlib@openssh.com":
compress_out = self._compression_info[self.local_compression][0]
- self._log(DEBUG, 'Switching on outbound compression ...')
+ self._log(DEBUG, "Switching on outbound compression ...")
self.packetizer.set_outbound_compressor(compress_out())
- if self.remote_compression == 'zlib@openssh.com':
+ if self.remote_compression == "zlib@openssh.com":
compress_in = self._compression_info[self.remote_compression][1]
- self._log(DEBUG, 'Switching on inbound compression ...')
+ self._log(DEBUG, "Switching on inbound compression ...")
self.packetizer.set_inbound_compressor(compress_in())
def _parse_newkeys(self, m):
- self._log(DEBUG, 'Switch to new keys ...')
+ self._log(DEBUG, "Switch to new keys ...")
self._activate_inbound()
# can also free a bunch of stuff here
self.local_kex_init = self.remote_kex_init = None
@@ -2407,25 +2531,25 @@ class Transport(threading.Thread, ClosingContextManager):
def _parse_disconnect(self, m):
code = m.get_int()
desc = m.get_text()
- self._log(INFO, 'Disconnect (code %d): %s' % (code, desc))
+ self._log(INFO, "Disconnect (code {:d}): {}".format(code, desc))
def _parse_global_request(self, m):
kind = m.get_text()
- self._log(DEBUG, 'Received global request "%s"' % kind)
+ self._log(DEBUG, 'Received global request "{}"'.format(kind))
want_reply = m.get_boolean()
if not self.server_mode:
self._log(
DEBUG,
- 'Rejecting "%s" global request from server.' % kind
+ 'Rejecting "{}" global request from server.'.format(kind),
)
ok = False
- elif kind == 'tcpip-forward':
+ elif kind == "tcpip-forward":
address = m.get_text()
port = m.get_int()
ok = self.server_object.check_port_forward_request(address, port)
if ok:
ok = (ok,)
- elif kind == 'cancel-tcpip-forward':
+ elif kind == "cancel-tcpip-forward":
address = m.get_text()
port = m.get_int()
self.server_object.cancel_port_forward_request(address, port)
@@ -2446,13 +2570,13 @@ class Transport(threading.Thread, ClosingContextManager):
self._send_message(msg)
def _parse_request_success(self, m):
- self._log(DEBUG, 'Global request successful.')
+ self._log(DEBUG, "Global request successful.")
self.global_response = m
if self.completion_event is not None:
self.completion_event.set()
def _parse_request_failure(self, m):
- self._log(DEBUG, 'Global request denied.')
+ self._log(DEBUG, "Global request denied.")
self.global_response = None
if self.completion_event is not None:
self.completion_event.set()
@@ -2464,13 +2588,14 @@ class Transport(threading.Thread, ClosingContextManager):
server_max_packet_size = m.get_int()
chan = self._channels.get(chanid)
if chan is None:
- self._log(WARNING, 'Success for unrequested channel! [??]')
+ self._log(WARNING, "Success for unrequested channel! [??]")
return
self.lock.acquire()
try:
chan._set_remote_channel(
- server_chanid, server_window_size, server_max_packet_size)
- self._log(DEBUG, 'Secsh channel %d opened.' % chanid)
+ server_chanid, server_window_size, server_max_packet_size
+ )
+ self._log(DEBUG, "Secsh channel {:d} opened.".format(chanid))
if chanid in self.channel_events:
self.channel_events[chanid].set()
del self.channel_events[chanid]
@@ -2483,11 +2608,12 @@ class Transport(threading.Thread, ClosingContextManager):
reason = m.get_int()
reason_str = m.get_text()
m.get_text() # ignored language
- reason_text = CONNECTION_FAILED_CODE.get(reason, '(unknown code)')
+ reason_text = CONNECTION_FAILED_CODE.get(reason, "(unknown code)")
self._log(
ERROR,
- 'Secsh channel %d open FAILED: %s: %s' % (
- chanid, reason_str, reason_text)
+ "Secsh channel {:d} open FAILED: {}: {}".format(
+ chanid, reason_str, reason_text
+ ),
)
self.lock.acquire()
try:
@@ -2508,37 +2634,39 @@ class Transport(threading.Thread, ClosingContextManager):
max_packet_size = m.get_int()
reject = False
if (
- kind == 'auth-agent@openssh.com' and
- self._forward_agent_handler is not None
+ kind == "auth-agent@openssh.com"
+ and self._forward_agent_handler is not None
):
- self._log(DEBUG, 'Incoming forward agent connection')
+ self._log(DEBUG, "Incoming forward agent connection")
self.lock.acquire()
try:
my_chanid = self._next_channel()
finally:
self.lock.release()
- elif (kind == 'x11') and (self._x11_handler is not None):
+ elif (kind == "x11") and (self._x11_handler is not None):
origin_addr = m.get_text()
origin_port = m.get_int()
self._log(
DEBUG,
- 'Incoming x11 connection from %s:%d' % (
- origin_addr, origin_port)
+ "Incoming x11 connection from {}:{:d}".format(
+ origin_addr, origin_port
+ ),
)
self.lock.acquire()
try:
my_chanid = self._next_channel()
finally:
self.lock.release()
- elif (kind == 'forwarded-tcpip') and (self._tcp_handler is not None):
+ elif (kind == "forwarded-tcpip") and (self._tcp_handler is not None):
server_addr = m.get_text()
server_port = m.get_int()
origin_addr = m.get_text()
origin_port = m.get_int()
self._log(
DEBUG,
- 'Incoming tcp forwarded connection from %s:%d' % (
- origin_addr, origin_port)
+ "Incoming tcp forwarded connection from {}:{:d}".format(
+ origin_addr, origin_port
+ ),
)
self.lock.acquire()
try:
@@ -2548,7 +2676,8 @@ class Transport(threading.Thread, ClosingContextManager):
elif not self.server_mode:
self._log(
DEBUG,
- 'Rejecting "%s" channel request from server.' % kind)
+ 'Rejecting "{}" channel request from server.'.format(kind),
+ )
reject = True
reason = OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
else:
@@ -2557,7 +2686,7 @@ class Transport(threading.Thread, ClosingContextManager):
my_chanid = self._next_channel()
finally:
self.lock.release()
- if kind == 'direct-tcpip':
+ if kind == "direct-tcpip":
# handle direct-tcpip requests coming from the client
dest_addr = m.get_text()
dest_port = m.get_int()
@@ -2566,23 +2695,25 @@ class Transport(threading.Thread, ClosingContextManager):
reason = self.server_object.check_channel_direct_tcpip_request(
my_chanid,
(origin_addr, origin_port),
- (dest_addr, dest_port)
+ (dest_addr, dest_port),
)
else:
reason = self.server_object.check_channel_request(
- kind, my_chanid)
+ kind, my_chanid
+ )
if reason != OPEN_SUCCEEDED:
self._log(
DEBUG,
- 'Rejecting "%s" channel request from client.' % kind)
+ 'Rejecting "{}" channel request from client.'.format(kind),
+ )
reject = True
if reject:
msg = Message()
msg.add_byte(cMSG_CHANNEL_OPEN_FAILURE)
msg.add_int(chanid)
msg.add_int(reason)
- msg.add_string('')
- msg.add_string('en')
+ msg.add_string("")
+ msg.add_string("en")
self._send_message(msg)
return
@@ -2593,9 +2724,11 @@ class Transport(threading.Thread, ClosingContextManager):
self.channels_seen[my_chanid] = True
chan._set_transport(self)
chan._set_window(
- self.default_window_size, self.default_max_packet_size)
+ self.default_window_size, self.default_max_packet_size
+ )
chan._set_remote_channel(
- chanid, initial_window_size, max_packet_size)
+ chanid, initial_window_size, max_packet_size
+ )
finally:
self.lock.release()
m = Message()
@@ -2605,17 +2738,17 @@ class Transport(threading.Thread, ClosingContextManager):
m.add_int(self.default_window_size)
m.add_int(self.default_max_packet_size)
self._send_message(m)
- self._log(DEBUG, 'Secsh channel %d (%s) opened.', my_chanid, kind)
- if kind == 'auth-agent@openssh.com':
+ self._log(
+ DEBUG, "Secsh channel {:d} ({}) opened.".format(my_chanid, kind)
+ )
+ if kind == "auth-agent@openssh.com":
self._forward_agent_handler(chan)
- elif kind == 'x11':
+ elif kind == "x11":
self._x11_handler(chan, (origin_addr, origin_port))
- elif kind == 'forwarded-tcpip':
+ elif kind == "forwarded-tcpip":
chan.origin_addr = (origin_addr, origin_port)
self._tcp_handler(
- chan,
- (origin_addr, origin_port),
- (server_addr, server_port)
+ chan, (origin_addr, origin_port), (server_addr, server_port)
)
else:
self._queue_incoming_channel(chan)
@@ -2624,7 +2757,7 @@ class Transport(threading.Thread, ClosingContextManager):
m.get_boolean() # always_display
msg = m.get_string()
m.get_string() # language
- self._log(DEBUG, 'Debug msg: {0}'.format(util.safe_string(msg)))
+ self._log(DEBUG, "Debug msg: {}".format(util.safe_string(msg)))
def _get_subsystem_handler(self, name):
try:
@@ -2658,7 +2791,7 @@ class Transport(threading.Thread, ClosingContextManager):
}
-class SecurityOptions (object):
+class SecurityOptions(object):
"""
Simple object containing the security preferences of an ssh transport.
These are tuples of acceptable ciphers, digests, key types, and key
@@ -2670,7 +2803,7 @@ class SecurityOptions (object):
``ValueError`` will be raised. If you try to assign something besides a
tuple to one of the fields, ``TypeError`` will be raised.
"""
- __slots__ = '_transport'
+ __slots__ = "_transport"
def __init__(self, transport):
self._transport = transport
@@ -2679,17 +2812,17 @@ class SecurityOptions (object):
"""
Returns a string representation of this object, for debugging.
"""
- return '<paramiko.SecurityOptions for %s>' % repr(self._transport)
+ return "<paramiko.SecurityOptions for {!r}>".format(self._transport)
def _set(self, name, orig, x):
if type(x) is list:
x = tuple(x)
if type(x) is not tuple:
- raise TypeError('expected tuple or list')
+ raise TypeError("expected tuple or list")
possible = list(getattr(self._transport, orig).keys())
forbidden = [n for n in x if n not in possible]
if len(forbidden) > 0:
- raise ValueError('unknown cipher')
+ raise ValueError("unknown cipher")
setattr(self._transport, name, x)
@property
@@ -2699,7 +2832,7 @@ class SecurityOptions (object):
@ciphers.setter
def ciphers(self, x):
- self._set('_preferred_ciphers', '_cipher_info', x)
+ self._set("_preferred_ciphers", "_cipher_info", x)
@property
def digests(self):
@@ -2708,7 +2841,7 @@ class SecurityOptions (object):
@digests.setter
def digests(self, x):
- self._set('_preferred_macs', '_mac_info', x)
+ self._set("_preferred_macs", "_mac_info", x)
@property
def key_types(self):
@@ -2717,8 +2850,7 @@ class SecurityOptions (object):
@key_types.setter
def key_types(self, x):
- self._set('_preferred_keys', '_key_info', x)
-
+ self._set("_preferred_keys", "_key_info", x)
@property
def kex(self):
@@ -2727,7 +2859,7 @@ class SecurityOptions (object):
@kex.setter
def kex(self, x):
- self._set('_preferred_kex', '_kex_info', x)
+ self._set("_preferred_kex", "_kex_info", x)
@property
def compression(self):
@@ -2736,10 +2868,11 @@ class SecurityOptions (object):
@compression.setter
def compression(self, x):
- self._set('_preferred_compression', '_compression_info', x)
+ self._set("_preferred_compression", "_compression_info", x)
+
+class ChannelMap(object):
-class ChannelMap (object):
def __init__(self):
# (id -> Channel)
self._map = weakref.WeakValueDictionary()
diff --git a/paramiko/util.py b/paramiko/util.py
index de099c0c..399141ad 100644
--- a/paramiko/util.py
+++ b/paramiko/util.py
@@ -49,9 +49,9 @@ def inflate_long(s, always_positive=False):
# noinspection PyAugmentAssignment
s = filler * (4 - len(s) % 4) + s
for i in range(0, len(s), 4):
- out = (out << 32) + struct.unpack('>I', s[i:i + 4])[0]
+ out = (out << 32) + struct.unpack(">I", s[i : i + 4])[0]
if negative:
- out -= (long(1) << (8 * len(s)))
+ out -= long(1) << (8 * len(s))
return out
@@ -66,7 +66,7 @@ def deflate_long(n, add_sign_padding=True):
s = bytes()
n = long(n)
while (n != 0) and (n != -1):
- s = struct.pack('>I', n & xffffffff) + s
+ s = struct.pack(">I", n & xffffffff) + s
n >>= 32
# strip off leading zeros, FFs
for i in enumerate(s):
@@ -81,7 +81,7 @@ def deflate_long(n, add_sign_padding=True):
s = zero_byte
else:
s = max_byte
- s = s[i[0]:]
+ s = s[i[0] :]
if add_sign_padding:
if (n == 0) and (byte_ord(s[0]) >= 0x80):
s = zero_byte + s
@@ -90,11 +90,11 @@ def deflate_long(n, add_sign_padding=True):
return s
-def format_binary(data, prefix=''):
+def format_binary(data, prefix=""):
x = 0
out = []
while len(data) > x + 16:
- out.append(format_binary_line(data[x:x + 16]))
+ out.append(format_binary_line(data[x : x + 16]))
x += 16
if x < len(data):
out.append(format_binary_line(data[x:]))
@@ -102,19 +102,21 @@ def format_binary(data, prefix=''):
def format_binary_line(data):
- left = ' '.join(['%02X' % byte_ord(c) for c in data])
- right = ''.join([('.%c..' % c)[(byte_ord(c) + 63) // 95] for c in data])
- return '%-50s %s' % (left, right)
+ left = " ".join(["{:02X}".format(byte_ord(c)) for c in data])
+ right = "".join(
+ [".{:c}..".format(byte_ord(c))[(byte_ord(c) + 63) // 95] for c in data]
+ )
+ return "{:50s} {}".format(left, right)
def safe_string(s):
- out = b('')
+ out = b""
for c in s:
i = byte_ord(c)
if 32 <= i <= 127:
out += byte_chr(i)
else:
- out += b('%%%02X' % i)
+ out += b("%{:02X}".format(i))
return out
@@ -134,7 +136,7 @@ def bit_length(n):
def tb_strings():
- return ''.join(traceback.format_exception(*sys.exc_info())).split('\n')
+ return "".join(traceback.format_exception(*sys.exc_info())).split("\n")
def generate_key_bytes(hash_alg, salt, key, nbytes):
@@ -185,6 +187,7 @@ def load_host_keys(filename):
nested dict of `.PKey` objects, indexed by hostname and then keytype
"""
from paramiko.hostkeys import HostKeys
+
return HostKeys(filename)
@@ -246,15 +249,17 @@ def log_to_file(filename, level=DEBUG):
if len(l.handlers) > 0:
return
l.setLevel(level)
- f = open(filename, 'a')
+ f = open(filename, "a")
lh = logging.StreamHandler(f)
- frm = '%(levelname)-.3s [%(asctime)s.%(msecs)03d] thr=%(_threadid)-3d %(name)s: %(message)s' # noqa
- lh.setFormatter(logging.Formatter(frm, '%Y%m%d-%H:%M:%S'))
+ frm = "%(levelname)-.3s [%(asctime)s.%(msecs)03d] thr=%(_threadid)-3d"
+ frm += " %(name)s: %(message)s"
+ lh.setFormatter(logging.Formatter(frm, "%Y%m%d-%H:%M:%S"))
l.addHandler(lh)
# make only one filter object, so it doesn't get applied more than once
-class PFilter (object):
+class PFilter(object):
+
def filter(self, record):
record._threadid = get_thread_id()
return True
@@ -290,6 +295,7 @@ def constant_time_bytes_eq(a, b):
class ClosingContextManager(object):
+
def __enter__(self):
return self
diff --git a/paramiko/win_pageant.py b/paramiko/win_pageant.py
index fda3b9c1..2bba789d 100644
--- a/paramiko/win_pageant.py
+++ b/paramiko/win_pageant.py
@@ -44,7 +44,7 @@ win32con_WM_COPYDATA = 74
def _get_pageant_window_object():
- return ctypes.windll.user32.FindWindowA(b('Pageant'), b('Pageant'))
+ return ctypes.windll.user32.FindWindowA(b"Pageant", b"Pageant")
def can_talk_to_agent():
@@ -57,7 +57,7 @@ def can_talk_to_agent():
return bool(_get_pageant_window_object())
-if platform.architecture()[0] == '64bit':
+if platform.architecture()[0] == "64bit":
ULONG_PTR = ctypes.c_uint64
else:
ULONG_PTR = ctypes.c_uint32
@@ -69,9 +69,9 @@ class COPYDATASTRUCT(ctypes.Structure):
http://msdn.microsoft.com/en-us/library/windows/desktop/ms649010%28v=vs.85%29.aspx
"""
_fields_ = [
- ('num_data', ULONG_PTR),
- ('data_size', ctypes.wintypes.DWORD),
- ('data_loc', ctypes.c_void_p),
+ ("num_data", ULONG_PTR),
+ ("data_size", ctypes.wintypes.DWORD),
+ ("data_loc", ctypes.c_void_p),
]
@@ -86,27 +86,29 @@ def _query_pageant(msg):
return None
# create a name for the mmap
- map_name = 'PageantRequest%08x' % thread.get_ident()
+ map_name = "PageantRequest%08x" % thread.get_ident()
- pymap = _winapi.MemoryMap(map_name, _AGENT_MAX_MSGLEN,
- _winapi.get_security_attributes_for_user(),
- )
+ pymap = _winapi.MemoryMap(
+ map_name, _AGENT_MAX_MSGLEN, _winapi.get_security_attributes_for_user()
+ )
with pymap:
pymap.write(msg)
# Create an array buffer containing the mapped filename
char_buffer = array.array("b", b(map_name) + zero_byte) # noqa
char_buffer_address, char_buffer_size = char_buffer.buffer_info()
# Create a string to use for the SendMessage function call
- cds = COPYDATASTRUCT(_AGENT_COPYDATA_ID, char_buffer_size,
- char_buffer_address)
+ cds = COPYDATASTRUCT(
+ _AGENT_COPYDATA_ID, char_buffer_size, char_buffer_address
+ )
- response = ctypes.windll.user32.SendMessageA(hwnd,
- win32con_WM_COPYDATA, ctypes.sizeof(cds), ctypes.byref(cds))
+ response = ctypes.windll.user32.SendMessageA(
+ hwnd, win32con_WM_COPYDATA, ctypes.sizeof(cds), ctypes.byref(cds)
+ )
if response > 0:
pymap.seek(0)
datalen = pymap.read(4)
- retlen = struct.unpack('>I', datalen)[0]
+ retlen = struct.unpack(">I", datalen)[0]
return datalen + pymap.read(retlen)
return None
@@ -127,10 +129,10 @@ class PageantConnection(object):
def recv(self, n):
if self._response is None:
- return ''
+ return ""
ret = self._response[:n]
self._response = self._response[n:]
- if self._response == '':
+ if self._response == "":
self._response = None
return ret
diff --git a/setup.cfg b/setup.cfg
index 8cc271fe..bf86db42 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -9,5 +9,14 @@ omit = paramiko/_winapi.py
[flake8]
exclude = sites,.git,build,dist,demos,tests
-ignore = E124,E125,E128,E261,E301,E302,E303,E402
+# NOTE: W503, E203 are concessions to black 18.0b5 and could be reinstated
+# later if fixed on that end.
+ignore = E124,E125,E128,E261,E301,E302,E303,E402,E721,W503,E203
max-line-length = 79
+
+[tool:pytest]
+# We use pytest-relaxed just for its utils at the moment, so disable it at the
+# plugin level until we adapt test organization to really use it.
+addopts = -p no:relaxed
+# Loop on failure
+looponfailroots = tests paramiko
diff --git a/setup.py b/setup.py
index 1234bfa5..a427ccc3 100644
--- a/setup.py
+++ b/setup.py
@@ -19,12 +19,12 @@
import sys
from setuptools import setup
-if sys.platform == 'darwin':
+if sys.platform == "darwin":
import setup_helper
setup_helper.install_custom_make_tarball()
-longdesc = '''
+longdesc = """
This is a library for making SSH2 connections (client or server).
Emphasis is on using SSH2 as an alternative to SSL for making secure
connections between python scripts. All major ciphers and hash methods
@@ -35,14 +35,14 @@ Required packages:
To install the development version, ``pip install -e
git+https://github.com/paramiko/paramiko/#egg=paramiko``.
-'''
+"""
# Version info -- read without importing
_locals = {}
-with open('paramiko/_version.py') as fp:
+with open("paramiko/_version.py") as fp:
exec(fp.read(), None, _locals)
-version = _locals['__version__']
+version = _locals["__version__"]
setup(
name="paramiko",
@@ -52,32 +52,29 @@ setup(
author="Jeff Forcier",
author_email="jeff@bitprophet.org",
url="https://github.com/paramiko/paramiko/",
- packages=['paramiko'],
- license='LGPL',
- platforms='Posix; MacOS X; Windows',
+ packages=["paramiko"],
+ license="LGPL",
+ platforms="Posix; MacOS X; Windows",
classifiers=[
- 'Development Status :: 5 - Production/Stable',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: '
- 'GNU Library or Lesser General Public License (LGPL)',
- 'Operating System :: OS Independent',
- 'Topic :: Internet',
- 'Topic :: Security :: Cryptography',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.6',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.2',
- 'Programming Language :: Python :: 3.3',
- 'Programming Language :: Python :: 3.4',
- 'Programming Language :: Python :: 3.5',
- 'Programming Language :: Python :: 3.6',
+ "Development Status :: 5 - Production/Stable",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: "
+ "GNU Library or Lesser General Public License (LGPL)",
+ "Operating System :: OS Independent",
+ "Topic :: Internet",
+ "Topic :: Security :: Cryptography",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 2",
+ "Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.4",
+ "Programming Language :: Python :: 3.5",
+ "Programming Language :: Python :: 3.6",
],
install_requires=[
- 'bcrypt>=3.1.3',
- 'cryptography>=1.5',
- 'pynacl>=1.0.1',
- 'pyasn1>=0.1.7',
+ "bcrypt>=3.1.3",
+ "cryptography>=1.5",
+ "pynacl>=1.0.1",
+ "pyasn1>=0.1.7",
],
)
diff --git a/setup_helper.py b/setup_helper.py
index c359a16c..d0a8700e 100644
--- a/setup_helper.py
+++ b/setup_helper.py
@@ -40,6 +40,7 @@ try:
except ImportError:
getgrnam = None
+
def _get_gid(name):
"""Returns a gid, given a group name."""
if getgrnam is None or name is None:
@@ -52,6 +53,7 @@ def _get_gid(name):
return result[2]
return None
+
def _get_uid(name):
"""Returns an uid, given a user name."""
if getpwnam is None or name is None:
@@ -64,8 +66,16 @@ def _get_uid(name):
return result[2]
return None
-def make_tarball(base_name, base_dir, compress='gzip', verbose=0, dry_run=0,
- owner=None, group=None):
+
+def make_tarball(
+ base_name,
+ base_dir,
+ compress="gzip",
+ verbose=0,
+ dry_run=0,
+ owner=None,
+ group=None,
+):
"""Create a tar file from all the files under 'base_dir'.
This file may be compressed.
@@ -87,28 +97,26 @@ def make_tarball(base_name, base_dir, compress='gzip', verbose=0, dry_run=0,
# "create a tree of hardlinks" step! (Would also be nice to
# detect GNU tar to use its 'z' option and save a step.)
- compress_ext = {
- 'gzip': ".gz",
- 'bzip2': '.bz2',
- 'compress': ".Z",
- }
+ compress_ext = {"gzip": ".gz", "bzip2": ".bz2", "compress": ".Z"}
# flags for compression program, each element of list will be an argument
- tarfile_compress_flag = {'gzip': 'gz', 'bzip2': 'bz2'}
- compress_flags = {'compress': ["-f"]}
+ tarfile_compress_flag = {"gzip": "gz", "bzip2": "bz2"}
+ compress_flags = {"compress": ["-f"]}
if compress is not None and compress not in compress_ext.keys():
- raise ValueError("bad value for 'compress': must be None, 'gzip',"
- "'bzip2' or 'compress'")
+ raise ValueError(
+ "bad value for 'compress': must be None, 'gzip',"
+ "'bzip2' or 'compress'"
+ )
archive_name = base_name + ".tar"
if compress and compress in tarfile_compress_flag:
archive_name += compress_ext[compress]
- mode = 'w:' + tarfile_compress_flag.get(compress, '')
+ mode = "w:" + tarfile_compress_flag.get(compress, "")
mkpath(os.path.dirname(archive_name), dry_run=dry_run)
- log.info('Creating tar file %s with mode %s' % (archive_name, mode))
+ log.info("Creating tar file %s with mode %s" % (archive_name, mode))
uid = _get_uid(owner)
gid = _get_gid(group)
@@ -136,18 +144,20 @@ def make_tarball(base_name, base_dir, compress='gzip', verbose=0, dry_run=0,
tar.close()
if compress and compress not in tarfile_compress_flag:
- spawn([compress] + compress_flags[compress] + [archive_name],
- dry_run=dry_run)
+ spawn(
+ [compress] + compress_flags[compress] + [archive_name],
+ dry_run=dry_run,
+ )
return archive_name + compress_ext[compress]
else:
return archive_name
_custom_formats = {
- 'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"),
- 'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"),
- 'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"),
- 'tar': (make_tarball, [('compress', None)], "uncompressed tar file"),
+ "gztar": (make_tarball, [("compress", "gzip")], "gzip'ed tar-file"),
+ "bztar": (make_tarball, [("compress", "bzip2")], "bzip2'ed tar-file"),
+ "ztar": (make_tarball, [("compress", "compress")], "compressed tar file"),
+ "tar": (make_tarball, [("compress", None)], "uncompressed tar file"),
}
# Hack in and insert ourselves into the distutils code base
diff --git a/sites/docs/conf.py b/sites/docs/conf.py
index 5674fed1..eb895804 100644
--- a/sites/docs/conf.py
+++ b/sites/docs/conf.py
@@ -1,16 +1,17 @@
# Obtain shared config values
import os, sys
-sys.path.append(os.path.abspath('..'))
-sys.path.append(os.path.abspath('../..'))
+
+sys.path.append(os.path.abspath(".."))
+sys.path.append(os.path.abspath("../.."))
from shared_conf import *
# Enable autodoc, intersphinx
-extensions.extend(['sphinx.ext.autodoc'])
+extensions.extend(["sphinx.ext.autodoc"])
# Autodoc settings
-autodoc_default_flags = ['members', 'special-members']
+autodoc_default_flags = ["members", "special-members"]
# Sister-site links to WWW
-html_theme_options['extra_nav_links'] = {
- "Main website": 'http://www.paramiko.org',
+html_theme_options["extra_nav_links"] = {
+ "Main website": "http://www.paramiko.org"
}
diff --git a/sites/shared_conf.py b/sites/shared_conf.py
index 99fab315..f4806cf1 100644
--- a/sites/shared_conf.py
+++ b/sites/shared_conf.py
@@ -5,36 +5,29 @@ import alabaster
# Alabaster theme + mini-extension
html_theme_path = [alabaster.get_path()]
-extensions = ['alabaster', 'sphinx.ext.intersphinx']
+extensions = ["alabaster", "sphinx.ext.intersphinx"]
# Paths relative to invoking conf.py - not this shared file
-html_theme = 'alabaster'
+html_theme = "alabaster"
html_theme_options = {
- 'description': "A Python implementation of SSHv2.",
- 'github_user': 'paramiko',
- 'github_repo': 'paramiko',
- 'analytics_id': 'UA-18486793-2',
- 'travis_button': True,
+ "description": "A Python implementation of SSHv2.",
+ "github_user": "paramiko",
+ "github_repo": "paramiko",
+ "analytics_id": "UA-18486793-2",
+ "travis_button": True,
}
html_sidebars = {
- '**': [
- 'about.html',
- 'navigation.html',
- 'searchbox.html',
- 'donate.html',
- ]
+ "**": ["about.html", "navigation.html", "searchbox.html", "donate.html"]
}
# Everything intersphinx's to Python
-intersphinx_mapping = {
- 'python': ('http://docs.python.org/2.6', None),
-}
+intersphinx_mapping = {"python": ("https://docs.python.org/2.7/", None)}
# Regular settings
-project = 'Paramiko'
+project = "Paramiko"
year = datetime.now().year
-copyright = '%d Jeff Forcier' % year
-master_doc = 'index'
-templates_path = ['_templates']
-exclude_trees = ['_build']
-source_suffix = '.rst'
-default_role = 'obj'
+copyright = "{} Jeff Forcier".format(year)
+master_doc = "index"
+templates_path = ["_templates"]
+exclude_trees = ["_build"]
+source_suffix = ".rst"
+default_role = "obj"
diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst
index 66ad53de..b6343c87 100644
--- a/sites/www/changelog.rst
+++ b/sites/www/changelog.rst
@@ -2,6 +2,7 @@
Changelog
=========
+* :release:`2.4.1 <2018-03-12>`
* :release:`2.3.2 <2018-03-12>`
* :release:`2.2.3 <2018-03-12>`
* :release:`2.1.5 <2018-03-12>`
@@ -16,10 +17,32 @@ Changelog
* :bug:`1039` Ed25519 auth key decryption raised an unexpected exception when
given a unicode password string (typical in python 3). Report by Theodor van
Nahl and fix by Pierce Lopez.
+* :release:`2.4.0 <2017-11-14>`
+* :feature:`-` Add a new ``passphrase`` kwarg to `SSHClient.connect
+ <paramiko.client.SSHClient.connect>` so users may disambiguate key-decryption
+ passphrases from password-auth passwords. (This is a backwards compatible
+ change; ``password`` will still pull double duty as a passphrase when
+ ``passphrase`` is not given.)
+* :support:`-` Update ``tearDown`` of client test suite to avoid hangs due to
+ eternally blocking ``accept()`` calls on the internal server thread (which
+ can occur when test code raises an exception before actually connecting to
+ the server.)
* :bug:`1108 (1.17+)` Rename a private method keyword argument (which was named
``async``) so that we're compatible with the upcoming Python 3.7 release
(where ``async`` is a new keyword.) Thanks to ``@vEpiphyte`` for the report.
+* :support:`1100` Updated the test suite & related docs/metadata/config to be
+ compatible with pytest instead of using the old, custom, crufty
+ unittest-based ``test.py``.
+
+ This includes marking known-slow tests (mostly the SFTP ones) so they can be
+ filtered out by ``inv test``'s default behavior; as well as other minor
+ tweaks to test collection and/or display (for example, GSSAPI tests are
+ collected, but skipped, instead of not even being collected by default as in
+ ``test.py``.)
* :support:`- backported` Include LICENSE file in wheel archives.
+* :support:`1070` Drop Python 2.6 and Python 3.3 support; now only 2.7 and 3.4+
+ are supported. If you're unable to upgrade from 2.6 or 3.3, please stick to
+ the Paramiko 2.3.x (or below) release lines.
* :release:`2.3.1 <2017-09-22>`
* :bug:`1071` Certificate support broke the no-certificate case for Ed25519
keys (symptom is an ``AttributeError`` about ``public_blob``.) This went
diff --git a/sites/www/conf.py b/sites/www/conf.py
index c7ba0a86..00944871 100644
--- a/sites/www/conf.py
+++ b/sites/www/conf.py
@@ -3,22 +3,22 @@ import sys
import os
from os.path import abspath, join, dirname
-sys.path.append(abspath(join(dirname(__file__), '..')))
+sys.path.append(abspath(join(dirname(__file__), "..")))
from shared_conf import *
# Releases changelog extension
-extensions.append('releases')
+extensions.append("releases")
releases_release_uri = "https://github.com/paramiko/paramiko/tree/%s"
releases_issue_uri = "https://github.com/paramiko/paramiko/issues/%s"
# Default is 'local' building, but reference the public docs site when building
# under RTD.
-target = join(dirname(__file__), '..', 'docs', '_build')
-if os.environ.get('READTHEDOCS') == 'True':
- target = 'http://docs.paramiko.org/en/latest/'
-intersphinx_mapping['docs'] = (target, None)
+target = join(dirname(__file__), "..", "docs", "_build")
+if os.environ.get("READTHEDOCS") == "True":
+ target = "http://docs.paramiko.org/en/latest/"
+intersphinx_mapping["docs"] = (target, None)
# Sister-site links to API docs
-html_theme_options['extra_nav_links'] = {
- "API Docs": 'http://docs.paramiko.org',
+html_theme_options["extra_nav_links"] = {
+ "API Docs": "http://docs.paramiko.org"
}
diff --git a/sites/www/index.rst b/sites/www/index.rst
index f0a5db8a..26961f24 100644
--- a/sites/www/index.rst
+++ b/sites/www/index.rst
@@ -1,11 +1,11 @@
Welcome to Paramiko!
====================
-Paramiko is a Python (2.6+, 3.3+) implementation of the SSHv2 protocol [#]_,
+Paramiko is a Python (2.7, 3.4+) implementation of the SSHv2 protocol [#]_,
providing both client and server functionality. While it leverages a Python C
-extension for low level cryptography
-(`Cryptography <https://cryptography.io>`_), Paramiko itself is a pure Python
-interface around SSH networking concepts.
+extension for low level cryptography (`Cryptography
+<https://cryptography.io>`_), Paramiko itself is a pure Python interface around
+SSH networking concepts.
This website covers project information for Paramiko such as the changelog,
contribution guidelines, development roadmap, news/blog, and so forth. Detailed
diff --git a/sites/www/installing.rst b/sites/www/installing.rst
index f335a9e7..e6db2dca 100644
--- a/sites/www/installing.rst
+++ b/sites/www/installing.rst
@@ -19,8 +19,8 @@ via `pip <http://pip-installer.org>`_::
$ pip install paramiko
-We currently support **Python 2.6, 2.7, 3.3+, and PyPy**. Users on Python 2.5
-or older (or 3.2 or older) are urged to upgrade.
+We currently support **Python 2.7, 3.4+, and PyPy**. Users on Python 2.6 or
+older (or 3.3 or older) are urged to upgrade.
Paramiko has only one direct hard dependency: the Cryptography library. See
:ref:`cryptography`.
diff --git a/tasks.py b/tasks.py
index 4ddeeee3..fb15728b 100644
--- a/tasks.py
+++ b/tasks.py
@@ -3,71 +3,148 @@ from os.path import join
from shutil import rmtree, copytree
from invoke import Collection, task
+from invocations import travis
+from invocations.checks import blacken
from invocations.docs import docs, www, sites
from invocations.packaging.release import ns as release_coll, publish
from invocations.testing import count_errors
-# Until we move to spec-based testing
+# TODO: this screams out for the invoke missing-feature of "I just wrap task X,
+# assume its signature by default" (even if that is just **kwargs support)
@task
-def test(ctx, coverage=False, flags=""):
- if "--verbose" not in flags.split():
- flags += " --verbose"
- runner = "python"
+def test(
+ ctx,
+ verbose=True,
+ color=True,
+ capture="sys",
+ module=None,
+ k=None,
+ x=False,
+ opts="",
+ coverage=False,
+ include_slow=False,
+ loop_on_fail=False,
+):
+ """
+ Run unit tests via pytest.
+
+ By default, known-slow parts of the suite are SKIPPED unless
+ ``--include-slow`` is given. (Note that ``--include-slow`` does not mesh
+ well with explicit ``--opts="-m=xxx"`` - if ``-m`` is found in ``--opts``,
+ ``--include-slow`` will be ignored!)
+ """
+ if verbose and "--verbose" not in opts and "-v" not in opts:
+ opts += " --verbose"
+ # TODO: forget why invocations.pytest added this; is it to force color when
+ # running headless? Probably?
+ if color:
+ opts += " --color=yes"
+ opts += " --capture={0}".format(capture)
+ if "-m" not in opts and not include_slow:
+ opts += " -m 'not slow'"
+ if k is not None and not ("-k" in opts if opts else False):
+ opts += " -k {}".format(k)
+ if x and not ("-x" in opts if opts else False):
+ opts += " -x"
+ if loop_on_fail and not ("-f" in opts if opts else False):
+ opts += " -f"
+ modstr = ""
+ if module is not None:
+ # NOTE: implicit test_ prefix as we're not on pytest-relaxed yet
+ modstr = " tests/test_{}.py".format(module)
+ # Switch runner depending on coverage or no coverage.
+ # TODO: get pytest's coverage plugin working, IIRC it has issues?
+ runner = "pytest"
if coverage:
- runner = "coverage run --source=paramiko"
+ # Leverage how pytest can be run as 'python -m pytest', and then how
+ # coverage can be told to run things in that manner instead of
+ # expecting a literal .py file.
+ runner = "coverage run --source=paramiko -m pytest"
# Strip SSH_AUTH_SOCK from parent env to avoid pollution by interactive
# users.
+ # TODO: once pytest coverage plugin works, see if there's a pytest-native
+ # way to handle the env stuff too, then we can remove these tasks entirely
+ # in favor of just "run pytest"?
env = dict(os.environ)
- if 'SSH_AUTH_SOCK' in env:
- del env['SSH_AUTH_SOCK']
- cmd = "{0} test.py {1}".format(runner, flags)
+ if "SSH_AUTH_SOCK" in env:
+ del env["SSH_AUTH_SOCK"]
+ cmd = "{} {} {}".format(runner, opts, modstr)
+ # NOTE: we have a pytest.ini and tend to use that over PYTEST_ADDOPTS.
ctx.run(cmd, pty=True, env=env, replace_env=True)
@task
-def coverage(ctx):
- ctx.run("coverage run --source=paramiko test.py --verbose")
+def coverage(ctx, opts=""):
+ """
+ Execute all tests (normal and slow) with coverage enabled.
+ """
+ return test(ctx, coverage=True, include_slow=True, opts=opts)
+
+
+@task
+def guard(ctx, opts=""):
+ """
+ Execute all tests and then watch for changes, re-running.
+ """
+ # TODO if coverage was run via pytest-cov, we could add coverage here too
+ return test(ctx, include_slow=True, loop_on_fail=True, opts=opts)
# Until we stop bundling docs w/ releases. Need to discover use cases first.
# TODO: would be nice to tie this into our own version of build() too, but
# still have publish() use that build()...really need to try out classes!
@task
-def release(ctx, sdist=True, wheel=True, sign=True, dry_run=False):
+def release(ctx, sdist=True, wheel=True, sign=True, dry_run=False, index=None):
"""
Wraps invocations.packaging.publish to add baked-in docs folder.
"""
# Build docs first. Use terribad workaround pending invoke #146
ctx.run("inv docs", pty=True, hide=False)
# Move the built docs into where Epydocs used to live
- target = 'docs'
+ target = "docs"
rmtree(target, ignore_errors=True)
# TODO: make it easier to yank out this config val from the docs coll
- copytree('sites/docs/_build', target)
+ copytree("sites/docs/_build", target)
# Publish
- publish(ctx, sdist=sdist, wheel=wheel, sign=sign, dry_run=dry_run)
+ publish(
+ ctx, sdist=sdist, wheel=wheel, sign=sign, dry_run=dry_run, index=index
+ )
# Remind
- print("\n\nDon't forget to update RTD's versions page for new minor "
- "releases!")
+ print(
+ "\n\nDon't forget to update RTD's versions page for new minor "
+ "releases!"
+ )
# TODO: "replace one task with another" needs a better public API, this is
# using unpublished internals & skips all the stuff add_task() does re:
# aliasing, defaults etc.
-release_coll.tasks['publish'] = release
+release_coll.tasks["publish"] = release
-ns = Collection(test, coverage, release_coll, docs, www, sites, count_errors)
-ns.configure({
- 'packaging': {
- # NOTE: many of these are also set in kwarg defaults above; but having
- # them here too means once we get rid of our custom release(), the
- # behavior stays.
- 'sign': True,
- 'wheel': True,
- 'changelog_file': join(
- www.configuration()['sphinx']['source'],
- 'changelog.rst',
- ),
- },
-})
+ns = Collection(
+ test,
+ coverage,
+ guard,
+ release_coll,
+ docs,
+ www,
+ sites,
+ count_errors,
+ travis,
+ blacken,
+)
+ns.configure(
+ {
+ "packaging": {
+ # NOTE: many of these are also set in kwarg defaults above; but
+ # having them here too means once we get rid of our custom
+ # release(), the behavior stays.
+ "sign": True,
+ "wheel": True,
+ "changelog_file": join(
+ www.configuration()["sphinx"]["source"], "changelog.rst"
+ ),
+ }
+ }
+)
diff --git a/test.py b/test.py
deleted file mode 100755
index 7849c149..00000000
--- a/test.py
+++ /dev/null
@@ -1,196 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
-#
-# This file is part of paramiko.
-#
-# Paramiko is free software; you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation; either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# Paramiko 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 Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-
-"""
-do the unit tests!
-"""
-
-# flake8: noqa
-import os
-import re
-import sys
-import unittest
-from optparse import OptionParser
-import paramiko
-import threading
-from paramiko.py3compat import PY2
-
-sys.path.append('tests')
-
-from tests.test_message import MessageTest
-from tests.test_file import BufferedFileTest
-from tests.test_buffered_pipe import BufferedPipeTest
-from tests.test_util import UtilTest
-from tests.test_hostkeys import HostKeysTest
-from tests.test_pkey import KeyTest
-from tests.test_kex import KexTest
-from tests.test_packetizer import PacketizerTest
-from tests.test_auth import AuthTest
-from tests.test_transport import TransportTest
-from tests.test_ssh_exception import NoValidConnectionsErrorTest
-from tests.test_client import SSHClientTest
-from test_client import SSHClientTest # XXX why shadow the above import?
-from test_gssapi import GSSAPITest
-from test_ssh_gss import GSSAuthTest
-from test_kex_gss import GSSKexTest
-
-default_host = 'localhost'
-default_user = os.environ.get('USER', 'nobody')
-default_keyfile = os.path.join(os.environ.get('HOME', '/'), '.ssh/id_rsa')
-default_passwd = None
-
-
-def iter_suite_tests(suite):
- """Return all tests in a suite, recursing through nested suites"""
- for item in suite._tests:
- if isinstance(item, unittest.TestCase):
- yield item
- elif isinstance(item, unittest.TestSuite):
- for r in iter_suite_tests(item):
- yield r
- else:
- raise Exception('unknown object %r inside test suite %r'
- % (item, suite))
-
-
-def filter_suite_by_re(suite, pattern):
- result = unittest.TestSuite()
- filter_re = re.compile(pattern)
- for test in iter_suite_tests(suite):
- if filter_re.search(test.id()):
- result.addTest(test)
- return result
-
-
-def main():
- parser = OptionParser('usage: %prog [options]')
- parser.add_option('--verbose', action='store_true', dest='verbose', default=False,
- help='verbose display (one line per test)')
- parser.add_option('--no-pkey', action='store_false', dest='use_pkey', default=True,
- help='skip RSA/DSS private key tests (which can take a while)')
- parser.add_option('--no-transport', action='store_false', dest='use_transport', default=True,
- help='skip transport tests (which can take a while)')
- parser.add_option('--no-sftp', action='store_false', dest='use_sftp', default=True,
- help='skip SFTP client/server tests, which can be slow')
- parser.add_option('--no-big-file', action='store_false', dest='use_big_file', default=True,
- help='skip big file SFTP tests, which are slow as molasses')
- parser.add_option('--gssapi-test', action='store_true', dest='gssapi_test', default=False,
- help='Test the used APIs for GSS-API / SSPI authentication')
- parser.add_option('--test-gssauth', action='store_true', dest='test_gssauth', default=False,
- help='Test GSS-API / SSPI authentication for SSHv2. To test this, you need kerberos a infrastructure.\
- Note: Paramiko needs access to your krb5.keytab file. Make it readable for Paramiko or\
- copy the used key to another file and set the environment variable KRB5_KTNAME to this file.')
- parser.add_option('--test-gssapi-keyex', action='store_true', dest='test_gsskex', default=False,
- help='Test GSS-API / SSPI authenticated iffie-Hellman Key Exchange and user\
- authentication. To test this, you need kerberos a infrastructure.\
- Note: Paramiko needs access to your krb5.keytab file. Make it readable for Paramiko or\
- copy the used key to another file and set the environment variable KRB5_KTNAME to this file.')
- parser.add_option('-R', action='store_false', dest='use_loopback_sftp', default=True,
- help='perform SFTP tests against a remote server (by default, SFTP tests ' +
- 'are done through a loopback socket)')
- parser.add_option('-H', '--sftp-host', dest='hostname', type='string', default=default_host,
- metavar='<host>',
- help='[with -R] host for remote sftp tests (default: %s)' % default_host)
- parser.add_option('-U', '--sftp-user', dest='username', type='string', default=default_user,
- metavar='<username>',
- help='[with -R] username for remote sftp tests (default: %s)' % default_user)
- parser.add_option('-K', '--sftp-key', dest='keyfile', type='string', default=default_keyfile,
- metavar='<keyfile>',
- help='[with -R] location of private key for remote sftp tests (default: %s)' %
- default_keyfile)
- parser.add_option('-P', '--sftp-passwd', dest='password', type='string', default=default_passwd,
- metavar='<password>',
- help='[with -R] (optional) password to unlock the private key for remote sftp tests')
- parser.add_option('--krb5_principal', dest='krb5_principal', type='string',
- metavar='<krb5_principal>',
- help='The krb5 principal (your username) for GSS-API / SSPI authentication')
- parser.add_option('--targ_name', dest='targ_name', type='string',
- metavar='<targ_name>',
- help='Target name for GSS-API / SSPI authentication.\
- This is the hosts name you are running the test on in the kerberos database.')
- parser.add_option('--server_mode', action='store_true', dest='server_mode', default=False,
- help='Usage with --gssapi-test. Test the available GSS-API / SSPI server mode to.\
- Note: you need to have access to the kerberos keytab file.')
-
- options, args = parser.parse_args()
-
- # setup logging
- paramiko.util.log_to_file('test.log')
-
- if options.use_sftp:
- from tests.test_sftp import SFTPTest
- if options.use_loopback_sftp:
- SFTPTest.init_loopback()
- else:
- SFTPTest.init(options.hostname, options.username, options.keyfile, options.password)
- if not options.use_big_file:
- SFTPTest.set_big_file_test(False)
- if options.use_big_file:
- from tests.test_sftp_big import BigSFTPTest
-
- suite = unittest.TestSuite()
- suite.addTest(unittest.makeSuite(MessageTest))
- suite.addTest(unittest.makeSuite(BufferedFileTest))
- suite.addTest(unittest.makeSuite(BufferedPipeTest))
- suite.addTest(unittest.makeSuite(UtilTest))
- suite.addTest(unittest.makeSuite(HostKeysTest))
- if options.use_pkey:
- suite.addTest(unittest.makeSuite(KeyTest))
- suite.addTest(unittest.makeSuite(KexTest))
- suite.addTest(unittest.makeSuite(PacketizerTest))
- if options.use_transport:
- suite.addTest(unittest.makeSuite(AuthTest))
- suite.addTest(unittest.makeSuite(TransportTest))
- suite.addTest(unittest.makeSuite(NoValidConnectionsErrorTest))
- suite.addTest(unittest.makeSuite(SSHClientTest))
- if options.use_sftp:
- suite.addTest(unittest.makeSuite(SFTPTest))
- if options.use_big_file:
- suite.addTest(unittest.makeSuite(BigSFTPTest))
- if options.gssapi_test:
- GSSAPITest.init(options.targ_name, options.server_mode)
- suite.addTest(unittest.makeSuite(GSSAPITest))
- if options.test_gssauth:
- GSSAuthTest.init(options.krb5_principal, options.targ_name)
- suite.addTest(unittest.makeSuite(GSSAuthTest))
- if options.test_gsskex:
- GSSKexTest.init(options.krb5_principal, options.targ_name)
- suite.addTest(unittest.makeSuite(GSSKexTest))
- verbosity = 1
- if options.verbose:
- verbosity = 2
-
- runner = unittest.TextTestRunner(verbosity=verbosity)
- if len(args) > 0:
- filter = '|'.join(args)
- suite = filter_suite_by_re(suite, filter)
- result = runner.run(suite)
- # Clean up stale threads from poorly cleaned-up tests.
- # TODO: make that not a problem, jeez
- for thread in threading.enumerate():
- if thread is not threading.currentThread():
- thread.join(timeout=1)
- # Exit correctly
- if not result.wasSuccessful():
- sys.exit(1)
-
-
-if __name__ == '__main__':
- main()
diff --git a/tests/__init__.py b/tests/__init__.py
index 8878f14d..be1d2daa 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,36 +1 @@
-# Copyright (C) 2017 Martin Packman <gzlist@googlemail.com>
-#
-# This file is part of paramiko.
-#
-# Paramiko is free software; you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation; either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# Paramiko 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 Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-
-"""Base classes and helpers for testing paramiko."""
-
-import unittest
-
-from paramiko.py3compat import (
- builtins,
- )
-
-
-def skipUnlessBuiltin(name):
- """Skip decorated test if builtin name does not exist."""
- if getattr(builtins, name, None) is None:
- skip = getattr(unittest, "skip", None)
- if skip is None:
- # Python 2.6 pseudo-skip
- return lambda func: None
- return skip("No builtin " + repr(name))
- return lambda func: func
+# This file's just here so test modules can use explicit-relative imports.
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 00000000..2b509c5c
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,103 @@
+import logging
+import os
+import shutil
+import threading
+
+import pytest
+from paramiko import RSAKey, SFTPServer, SFTP, Transport
+
+from .loop import LoopSocket
+from .stub_sftp import StubServer, StubSFTPServer
+from .util import _support
+
+
+# TODO: not a huge fan of conftest.py files, see if we can move these somewhere
+# 'nicer'.
+
+
+# Perform logging by default; pytest will capture and thus hide it normally,
+# presenting it on error/failure. (But also allow turning it off when doing
+# very pinpoint debugging - e.g. using breakpoints, so you don't want output
+# hiding enabled, but also don't want all the logging to gum up the terminal.)
+if not os.environ.get("DISABLE_LOGGING", False):
+ logging.basicConfig(
+ level=logging.DEBUG,
+ # Also make sure to set up timestamping for more sanity when debugging.
+ format="[%(relativeCreated)s]\t%(levelname)s:%(name)s:%(message)s",
+ datefmt="%H:%M:%S",
+ )
+
+
+def make_sftp_folder():
+ """
+ Ensure expected target temp folder exists on the remote end.
+
+ Will clean it out if it already exists.
+ """
+ # TODO: go back to using the sftp functionality itself for folder setup so
+ # we can test against live SFTP servers again someday. (Not clear if anyone
+ # is/was using the old capability for such, though...)
+ # TODO: something that would play nicer with concurrent testing (but
+ # probably e.g. using thread ID or UUIDs or something; not the "count up
+ # until you find one not used!" crap from before...)
+ # TODO: if we want to lock ourselves even harder into localhost-only
+ # testing (probably not?) could use tempdir modules for this for improved
+ # safety. Then again...why would someone have such a folder???
+ path = os.environ.get("TEST_FOLDER", "paramiko-test-target")
+ # Forcibly nuke this directory locally, since at the moment, the below
+ # fixtures only ever run with a locally scoped stub test server.
+ shutil.rmtree(path, ignore_errors=True)
+ # Then create it anew, again locally, for the same reason.
+ os.mkdir(path)
+ return path
+
+
+@pytest.fixture # (scope='session')
+def sftp_server():
+ """
+ Set up an in-memory SFTP server thread. Yields the client Transport/socket.
+
+ The resulting client Transport (along with all the server components) will
+ be the same object throughout the test session; the `sftp` fixture then
+ creates new higher level client objects wrapped around the client
+ Transport, as necessary.
+ """
+ # Sockets & transports
+ socks = LoopSocket()
+ sockc = LoopSocket()
+ sockc.link(socks)
+ tc = Transport(sockc)
+ ts = Transport(socks)
+ # Auth
+ host_key = RSAKey.from_private_key_file(_support("test_rsa.key"))
+ ts.add_server_key(host_key)
+ # Server setup
+ event = threading.Event()
+ server = StubServer()
+ ts.set_subsystem_handler("sftp", SFTPServer, StubSFTPServer)
+ ts.start_server(event, server)
+ # Wait (so client has time to connect? Not sure. Old.)
+ event.wait(1.0)
+ # Make & yield connection.
+ tc.connect(username="slowdive", password="pygmalion")
+ yield tc
+ # TODO: any need for shutdown? Why didn't old suite do so? Or was that the
+ # point of the "join all threads from threading module" crap in test.py?
+
+
+@pytest.fixture
+def sftp(sftp_server):
+ """
+ Yield an SFTP client connected to the global in-session SFTP server thread.
+ """
+ # Client setup
+ client = SFTP.from_transport(sftp_server)
+ # Work in 'remote' folder setup (as it wants to use the client)
+ # TODO: how cleanest to make this available to tests? Doing it this way is
+ # marginally less bad than the previous 'global'-using setup, but not by
+ # much?
+ client.FOLDER = make_sftp_folder()
+ # Yield client to caller
+ yield client
+ # Clean up - as in make_sftp_folder, we assume local-only exec for now.
+ shutil.rmtree(client.FOLDER, ignore_errors=True)
diff --git a/tests/loop.py b/tests/loop.py
index e805ad96..dd1f5a0c 100644
--- a/tests/loop.py
+++ b/tests/loop.py
@@ -16,21 +16,19 @@
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-"""
-...
-"""
+import socket
+import threading
-import threading, socket
from paramiko.common import asbytes
-class LoopSocket (object):
+class LoopSocket(object):
"""
A LoopSocket looks like a normal socket, but all data written to it is
delivered on the read-end of another LoopSocket, and vice versa. It's
like a software "socketpair".
"""
-
+
def __init__(self):
self.__in_buffer = bytes()
self.__lock = threading.Lock()
@@ -86,7 +84,7 @@ class LoopSocket (object):
self.__cv.notifyAll()
finally:
self.__lock.release()
-
+
def __unlink(self):
m = None
self.__lock.acquire()
@@ -98,5 +96,3 @@ class LoopSocket (object):
self.__lock.release()
if m is not None:
m.__unlink()
-
-
diff --git a/tests/stub_sftp.py b/tests/stub_sftp.py
index 0d673091..ffae635d 100644
--- a/tests/stub_sftp.py
+++ b/tests/stub_sftp.py
@@ -22,14 +22,23 @@ A stub SFTP server for loopback SFTP testing.
import os
import sys
+
from paramiko import (
- ServerInterface, SFTPServerInterface, SFTPServer, SFTPAttributes,
- SFTPHandle, SFTP_OK, SFTP_FAILURE, AUTH_SUCCESSFUL, OPEN_SUCCEEDED,
+ ServerInterface,
+ SFTPServerInterface,
+ SFTPServer,
+ SFTPAttributes,
+ SFTPHandle,
+ SFTP_OK,
+ SFTP_FAILURE,
+ AUTH_SUCCESSFUL,
+ OPEN_SUCCEEDED,
)
from paramiko.common import o666
-class StubServer (ServerInterface):
+class StubServer(ServerInterface):
+
def check_auth_password(self, username, password):
# all are allowed
return AUTH_SUCCESSFUL
@@ -38,7 +47,8 @@ class StubServer (ServerInterface):
return OPEN_SUCCEEDED
-class StubSFTPHandle (SFTPHandle):
+class StubSFTPHandle(SFTPHandle):
+
def stat(self):
try:
return SFTPAttributes.from_stat(os.fstat(self.readfile.fileno()))
@@ -55,11 +65,11 @@ class StubSFTPHandle (SFTPHandle):
return SFTPServer.convert_errno(e.errno)
-class StubSFTPServer (SFTPServerInterface):
+class StubSFTPServer(SFTPServerInterface):
# assume current folder is a fine root
# (the tests always create and eventually delete a subfolder, so there shouldn't be any mess)
ROOT = os.getcwd()
-
+
def _realpath(self, path):
return self.ROOT + self.canonicalize(path)
@@ -69,7 +79,9 @@ class StubSFTPServer (SFTPServerInterface):
out = []
flist = os.listdir(path)
for fname in flist:
- attr = SFTPAttributes.from_stat(os.stat(os.path.join(path, fname)))
+ attr = SFTPAttributes.from_stat(
+ os.stat(os.path.join(path, fname))
+ )
attr.filename = fname
out.append(attr)
return out
@@ -93,9 +105,9 @@ class StubSFTPServer (SFTPServerInterface):
def open(self, path, flags, attr):
path = self._realpath(path)
try:
- binary_flag = getattr(os, 'O_BINARY', 0)
+ binary_flag = getattr(os, "O_BINARY", 0)
flags |= binary_flag
- mode = getattr(attr, 'st_mode', None)
+ mode = getattr(attr, "st_mode", None)
if mode is not None:
fd = os.open(path, flags, mode)
else:
@@ -109,17 +121,17 @@ class StubSFTPServer (SFTPServerInterface):
SFTPServer.set_file_attr(path, attr)
if flags & os.O_WRONLY:
if flags & os.O_APPEND:
- fstr = 'ab'
+ fstr = "ab"
else:
- fstr = 'wb'
+ fstr = "wb"
elif flags & os.O_RDWR:
if flags & os.O_APPEND:
- fstr = 'a+b'
+ fstr = "a+b"
else:
- fstr = 'r+b'
+ fstr = "r+b"
else:
# O_RDONLY (== 0)
- fstr = 'rb'
+ fstr = "rb"
try:
f = os.fdopen(fd, fstr)
except OSError as e:
@@ -158,7 +170,6 @@ class StubSFTPServer (SFTPServerInterface):
return SFTPServer.convert_errno(e.errno)
return SFTP_OK
-
def mkdir(self, path, attr):
path = self._realpath(path)
try:
@@ -187,18 +198,18 @@ class StubSFTPServer (SFTPServerInterface):
def symlink(self, target_path, path):
path = self._realpath(path)
- if (len(target_path) > 0) and (target_path[0] == '/'):
+ if (len(target_path) > 0) and (target_path[0] == "/"):
# absolute symlink
target_path = os.path.join(self.ROOT, target_path[1:])
- if target_path[:2] == '//':
+ if target_path[:2] == "//":
# bug in os.path.join
target_path = target_path[1:]
else:
# compute relative to path
abspath = os.path.join(os.path.dirname(path), target_path)
- if abspath[:len(self.ROOT)] != self.ROOT:
+ if abspath[: len(self.ROOT)] != self.ROOT:
# this symlink isn't going to work anyway -- just break it immediately
- target_path = '<error>'
+ target_path = "<error>"
try:
os.symlink(target_path, path)
except OSError as e:
@@ -213,10 +224,10 @@ class StubSFTPServer (SFTPServerInterface):
return SFTPServer.convert_errno(e.errno)
# if it's absolute, remove the root
if os.path.isabs(symlink):
- if symlink[:len(self.ROOT)] == self.ROOT:
- symlink = symlink[len(self.ROOT):]
- if (len(symlink) == 0) or (symlink[0] != '/'):
- symlink = '/' + symlink
+ if symlink[: len(self.ROOT)] == self.ROOT:
+ symlink = symlink[len(self.ROOT) :]
+ if (len(symlink) == 0) or (symlink[0] != "/"):
+ symlink = "/" + symlink
else:
- symlink = '<error>'
+ symlink = "<error>"
return symlink
diff --git a/tests/test_auth.py b/tests/test_auth.py
index e78397c6..8ad0ac3b 100644
--- a/tests/test_auth.py
+++ b/tests/test_auth.py
@@ -26,62 +26,72 @@ import unittest
from time import sleep
from paramiko import (
- Transport, ServerInterface, RSAKey, DSSKey, BadAuthenticationType,
- InteractiveQuery, AuthenticationException,
+ Transport,
+ ServerInterface,
+ RSAKey,
+ DSSKey,
+ BadAuthenticationType,
+ InteractiveQuery,
+ AuthenticationException,
)
from paramiko import AUTH_FAILED, AUTH_PARTIALLY_SUCCESSFUL, AUTH_SUCCESSFUL
from paramiko.py3compat import u
-from tests.loop import LoopSocket
-from tests.util import test_path
-_pwd = u('\u2022')
+from .loop import LoopSocket
+from .util import _support, slow
-class NullServer (ServerInterface):
+_pwd = u("\u2022")
+
+
+class NullServer(ServerInterface):
paranoid_did_password = False
paranoid_did_public_key = False
- paranoid_key = DSSKey.from_private_key_file(test_path('test_dss.key'))
+ paranoid_key = DSSKey.from_private_key_file(_support("test_dss.key"))
def get_allowed_auths(self, username):
- if username == 'slowdive':
- return 'publickey,password'
- if username == 'paranoid':
- if not self.paranoid_did_password and not self.paranoid_did_public_key:
- return 'publickey,password'
+ if username == "slowdive":
+ return "publickey,password"
+ if username == "paranoid":
+ if (
+ not self.paranoid_did_password
+ and not self.paranoid_did_public_key
+ ):
+ return "publickey,password"
elif self.paranoid_did_password:
- return 'publickey'
+ return "publickey"
else:
- return 'password'
- if username == 'commie':
- return 'keyboard-interactive'
- if username == 'utf8':
- return 'password'
- if username == 'non-utf8':
- return 'password'
- return 'publickey'
+ return "password"
+ if username == "commie":
+ return "keyboard-interactive"
+ if username == "utf8":
+ return "password"
+ if username == "non-utf8":
+ return "password"
+ return "publickey"
def check_auth_password(self, username, password):
- if (username == 'slowdive') and (password == 'pygmalion'):
+ if (username == "slowdive") and (password == "pygmalion"):
return AUTH_SUCCESSFUL
- if (username == 'paranoid') and (password == 'paranoid'):
+ if (username == "paranoid") and (password == "paranoid"):
# 2-part auth (even openssh doesn't support this)
self.paranoid_did_password = True
if self.paranoid_did_public_key:
return AUTH_SUCCESSFUL
return AUTH_PARTIALLY_SUCCESSFUL
- if (username == 'utf8') and (password == _pwd):
+ if (username == "utf8") and (password == _pwd):
return AUTH_SUCCESSFUL
- if (username == 'non-utf8') and (password == '\xff'):
+ if (username == "non-utf8") and (password == "\xff"):
return AUTH_SUCCESSFUL
- if username == 'bad-server':
+ if username == "bad-server":
raise Exception("Ack!")
- if username == 'unresponsive-server':
+ if username == "unresponsive-server":
sleep(5)
return AUTH_SUCCESSFUL
return AUTH_FAILED
def check_auth_publickey(self, username, key):
- if (username == 'paranoid') and (key == self.paranoid_key):
+ if (username == "paranoid") and (key == self.paranoid_key):
# 2-part auth
self.paranoid_did_public_key = True
if self.paranoid_did_password:
@@ -90,19 +100,21 @@ class NullServer (ServerInterface):
return AUTH_FAILED
def check_auth_interactive(self, username, submethods):
- if username == 'commie':
+ if username == "commie":
self.username = username
- return InteractiveQuery('password', 'Please enter a password.', ('Password', False))
+ return InteractiveQuery(
+ "password", "Please enter a password.", ("Password", False)
+ )
return AUTH_FAILED
def check_auth_interactive_response(self, responses):
- if self.username == 'commie':
- if (len(responses) == 1) and (responses[0] == 'cat'):
+ if self.username == "commie":
+ if (len(responses) == 1) and (responses[0] == "cat"):
return AUTH_SUCCESSFUL
return AUTH_FAILED
-class AuthTest (unittest.TestCase):
+class AuthTest(unittest.TestCase):
def setUp(self):
self.socks = LoopSocket()
@@ -118,7 +130,7 @@ class AuthTest (unittest.TestCase):
self.sockc.close()
def start_server(self):
- host_key = RSAKey.from_private_key_file(test_path('test_rsa.key'))
+ host_key = RSAKey.from_private_key_file(_support("test_rsa.key"))
self.public_host_key = RSAKey(data=host_key.asbytes())
self.ts.add_server_key(host_key)
self.event = threading.Event()
@@ -138,13 +150,16 @@ class AuthTest (unittest.TestCase):
"""
self.start_server()
try:
- self.tc.connect(hostkey=self.public_host_key,
- username='unknown', password='error')
+ self.tc.connect(
+ hostkey=self.public_host_key,
+ username="unknown",
+ password="error",
+ )
self.assertTrue(False)
except:
etype, evalue, etb = sys.exc_info()
self.assertEqual(BadAuthenticationType, etype)
- self.assertEqual(['publickey'], evalue.allowed_types)
+ self.assertEqual(["publickey"], evalue.allowed_types)
def test_2_bad_password(self):
"""
@@ -154,12 +169,12 @@ class AuthTest (unittest.TestCase):
self.start_server()
self.tc.connect(hostkey=self.public_host_key)
try:
- self.tc.auth_password(username='slowdive', password='error')
+ self.tc.auth_password(username="slowdive", password="error")
self.assertTrue(False)
except:
etype, evalue, etb = sys.exc_info()
self.assertTrue(issubclass(etype, AuthenticationException))
- self.tc.auth_password(username='slowdive', password='pygmalion')
+ self.tc.auth_password(username="slowdive", password="pygmalion")
self.verify_finished()
def test_3_multipart_auth(self):
@@ -168,10 +183,12 @@ class AuthTest (unittest.TestCase):
"""
self.start_server()
self.tc.connect(hostkey=self.public_host_key)
- remain = self.tc.auth_password(username='paranoid', password='paranoid')
- self.assertEqual(['publickey'], remain)
- key = DSSKey.from_private_key_file(test_path('test_dss.key'))
- remain = self.tc.auth_publickey(username='paranoid', key=key)
+ remain = self.tc.auth_password(
+ username="paranoid", password="paranoid"
+ )
+ self.assertEqual(["publickey"], remain)
+ key = DSSKey.from_private_key_file(_support("test_dss.key"))
+ remain = self.tc.auth_publickey(username="paranoid", key=key)
self.assertEqual([], remain)
self.verify_finished()
@@ -186,10 +203,11 @@ class AuthTest (unittest.TestCase):
self.got_title = title
self.got_instructions = instructions
self.got_prompts = prompts
- return ['cat']
- remain = self.tc.auth_interactive('commie', handler)
- self.assertEqual(self.got_title, 'password')
- self.assertEqual(self.got_prompts, [('Password', False)])
+ return ["cat"]
+
+ remain = self.tc.auth_interactive("commie", handler)
+ self.assertEqual(self.got_title, "password")
+ self.assertEqual(self.got_prompts, [("Password", False)])
self.assertEqual([], remain)
self.verify_finished()
@@ -200,7 +218,7 @@ class AuthTest (unittest.TestCase):
"""
self.start_server()
self.tc.connect(hostkey=self.public_host_key)
- remain = self.tc.auth_password('commie', 'cat')
+ remain = self.tc.auth_password("commie", "cat")
self.assertEqual([], remain)
self.verify_finished()
@@ -210,7 +228,7 @@ class AuthTest (unittest.TestCase):
"""
self.start_server()
self.tc.connect(hostkey=self.public_host_key)
- remain = self.tc.auth_password('utf8', _pwd)
+ remain = self.tc.auth_password("utf8", _pwd)
self.assertEqual([], remain)
self.verify_finished()
@@ -221,7 +239,7 @@ class AuthTest (unittest.TestCase):
"""
self.start_server()
self.tc.connect(hostkey=self.public_host_key)
- remain = self.tc.auth_password('non-utf8', '\xff')
+ remain = self.tc.auth_password("non-utf8", "\xff")
self.assertEqual([], remain)
self.verify_finished()
@@ -233,11 +251,12 @@ class AuthTest (unittest.TestCase):
self.start_server()
self.tc.connect(hostkey=self.public_host_key)
try:
- remain = self.tc.auth_password('bad-server', 'hello')
+ remain = self.tc.auth_password("bad-server", "hello")
except:
etype, evalue, etb = sys.exc_info()
self.assertTrue(issubclass(etype, AuthenticationException))
+ @slow
def test_9_auth_non_responsive(self):
"""
verify that authentication times out if server takes to long to
@@ -247,8 +266,8 @@ class AuthTest (unittest.TestCase):
self.start_server()
self.tc.connect()
try:
- remain = self.tc.auth_password('unresponsive-server', 'hello')
+ remain = self.tc.auth_password("unresponsive-server", "hello")
except:
etype, evalue, etb = sys.exc_info()
self.assertTrue(issubclass(etype, AuthenticationException))
- self.assertTrue('Authentication timeout' in str(evalue))
+ self.assertTrue("Authentication timeout" in str(evalue))
diff --git a/tests/test_buffered_pipe.py b/tests/test_buffered_pipe.py
index eeb4d0ad..9f986a5e 100644
--- a/tests/test_buffered_pipe.py
+++ b/tests/test_buffered_pipe.py
@@ -23,15 +23,16 @@ Some unit tests for BufferedPipe.
import threading
import time
import unittest
+
from paramiko.buffered_pipe import BufferedPipe, PipeTimeout
from paramiko import pipe
from paramiko.py3compat import b
def delay_thread(p):
- p.feed('a')
+ p.feed("a")
time.sleep(0.5)
- p.feed('b')
+ p.feed("b")
p.close()
@@ -41,41 +42,42 @@ def close_thread(p):
class BufferedPipeTest(unittest.TestCase):
+
def test_1_buffered_pipe(self):
p = BufferedPipe()
self.assertTrue(not p.read_ready())
- p.feed('hello.')
+ p.feed("hello.")
self.assertTrue(p.read_ready())
data = p.read(6)
- self.assertEqual(b'hello.', data)
+ self.assertEqual(b"hello.", data)
- p.feed('plus/minus')
- self.assertEqual(b'plu', p.read(3))
- self.assertEqual(b's/m', p.read(3))
- self.assertEqual(b'inus', p.read(4))
+ p.feed("plus/minus")
+ self.assertEqual(b"plu", p.read(3))
+ self.assertEqual(b"s/m", p.read(3))
+ self.assertEqual(b"inus", p.read(4))
p.close()
self.assertTrue(not p.read_ready())
- self.assertEqual(b'', p.read(1))
+ self.assertEqual(b"", p.read(1))
def test_2_delay(self):
p = BufferedPipe()
self.assertTrue(not p.read_ready())
threading.Thread(target=delay_thread, args=(p,)).start()
- self.assertEqual(b'a', p.read(1, 0.1))
+ self.assertEqual(b"a", p.read(1, 0.1))
try:
p.read(1, 0.1)
self.assertTrue(False)
except PipeTimeout:
pass
- self.assertEqual(b'b', p.read(1, 1.0))
- self.assertEqual(b'', p.read(1))
+ self.assertEqual(b"b", p.read(1, 1.0))
+ self.assertEqual(b"", p.read(1))
def test_3_close_while_reading(self):
p = BufferedPipe()
threading.Thread(target=close_thread, args=(p,)).start()
data = p.read(1, 1.0)
- self.assertEqual(b'', data)
+ self.assertEqual(b"", data)
def test_4_or_pipe(self):
p = pipe.make_pipe()
diff --git a/tests/test_client.py b/tests/test_client.py
index 7058f394..4943df29 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -20,51 +20,59 @@
Some unit tests for SSHClient.
"""
-from __future__ import with_statement
+from __future__ import with_statement, print_function
import gc
+import os
import platform
import socket
-from tempfile import mkstemp
import threading
+import time
import unittest
-import weakref
import warnings
-import os
-import time
-from tests.util import test_path
+import weakref
+from tempfile import mkstemp
+
+from pytest_relaxed import raises
import paramiko
from paramiko.pkey import PublicBlob
from paramiko.common import PY2
from paramiko.ssh_exception import SSHException, AuthenticationException
+from .util import _support, slow
+
+
+requires_gss_auth = unittest.skipUnless(
+ paramiko.GSS_AUTH_AVAILABLE, "GSS auth not available"
+)
FINGERPRINTS = {
- 'ssh-dss': b'\x44\x78\xf0\xb9\xa2\x3c\xc5\x18\x20\x09\xff\x75\x5b\xc1\xd2\x6c',
- 'ssh-rsa': b'\x60\x73\x38\x44\xcb\x51\x86\x65\x7f\xde\xda\xa2\x2b\x5a\x57\xd5',
- 'ecdsa-sha2-nistp256': b'\x25\x19\xeb\x55\xe6\xa1\x47\xff\x4f\x38\xd2\x75\x6f\xa5\xd5\x60',
- 'ssh-ed25519': b'\xb3\xd5"\xaa\xf9u^\xe8\xcd\x0e\xea\x02\xb9)\xa2\x80',
+ "ssh-dss": b"\x44\x78\xf0\xb9\xa2\x3c\xc5\x18\x20\x09\xff\x75\x5b\xc1\xd2\x6c",
+ "ssh-rsa": b"\x60\x73\x38\x44\xcb\x51\x86\x65\x7f\xde\xda\xa2\x2b\x5a\x57\xd5",
+ "ecdsa-sha2-nistp256": b"\x25\x19\xeb\x55\xe6\xa1\x47\xff\x4f\x38\xd2\x75\x6f\xa5\xd5\x60",
+ "ssh-ed25519": b'\xb3\xd5"\xaa\xf9u^\xe8\xcd\x0e\xea\x02\xb9)\xa2\x80',
}
class NullServer(paramiko.ServerInterface):
+
def __init__(self, *args, **kwargs):
# Allow tests to enable/disable specific key types
- self.__allowed_keys = kwargs.pop('allowed_keys', [])
+ self.__allowed_keys = kwargs.pop("allowed_keys", [])
# And allow them to set a (single...meh) expected public blob (cert)
- self.__expected_public_blob = kwargs.pop('public_blob', None)
+ self.__expected_public_blob = kwargs.pop("public_blob", None)
super(NullServer, self).__init__(*args, **kwargs)
def get_allowed_auths(self, username):
- if username == 'slowdive':
- return 'publickey,password'
- return 'publickey'
+ if username == "slowdive":
+ return "publickey,password"
+ return "publickey"
def check_auth_password(self, username, password):
- if (username == 'slowdive') and (password == 'pygmalion'):
+ if (username == "slowdive") and (password == "pygmalion"):
return paramiko.AUTH_SUCCESSFUL
- if (username == 'slowdive') and (password == 'unresponsive-server'):
+ if (username == "slowdive") and (password == "unresponsive-server"):
time.sleep(5)
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
@@ -76,13 +84,13 @@ class NullServer(paramiko.ServerInterface):
return paramiko.AUTH_FAILED
# Base check: allowed auth type & fingerprint matches
happy = (
- key.get_name() in self.__allowed_keys and
- key.get_fingerprint() == expected
+ key.get_name() in self.__allowed_keys
+ and key.get_fingerprint() == expected
)
# Secondary check: if test wants assertions about cert data
if (
- self.__expected_public_blob is not None and
- key.public_blob != self.__expected_public_blob
+ self.__expected_public_blob is not None
+ and key.public_blob != self.__expected_public_blob
):
happy = False
return paramiko.AUTH_SUCCESSFUL if happy else paramiko.AUTH_FAILED
@@ -91,50 +99,76 @@ class NullServer(paramiko.ServerInterface):
return paramiko.OPEN_SUCCEEDED
def check_channel_exec_request(self, channel, command):
- if command != b'yes':
+ if command != b"yes":
return False
return True
def check_channel_env_request(self, channel, name, value):
- if name == 'INVALID_ENV':
+ if name == "INVALID_ENV":
return False
- if not hasattr(channel, 'env'):
- setattr(channel, 'env', {})
+ if not hasattr(channel, "env"):
+ setattr(channel, "env", {})
channel.env[name] = value
return True
-class SSHClientTest (unittest.TestCase):
+class ClientTest(unittest.TestCase):
def setUp(self):
self.sockl = socket.socket()
- self.sockl.bind(('localhost', 0))
+ self.sockl.bind(("localhost", 0))
self.sockl.listen(1)
self.addr, self.port = self.sockl.getsockname()
self.connect_kwargs = dict(
hostname=self.addr,
port=self.port,
- username='slowdive',
+ username="slowdive",
look_for_keys=False,
)
self.event = threading.Event()
+ self.kill_event = threading.Event()
def tearDown(self):
- for attr in "tc ts socks sockl".split():
- if hasattr(self, attr):
- getattr(self, attr).close()
-
- def _run(self, allowed_keys=None, delay=0, public_blob=None):
+ # Shut down client Transport
+ if hasattr(self, "tc"):
+ self.tc.close()
+ # Shut down shared socket
+ if hasattr(self, "sockl"):
+ # Signal to server thread that it should shut down early; it checks
+ # this immediately after accept(). (In scenarios where connection
+ # actually succeeded during the test, this becomes a no-op.)
+ self.kill_event.set()
+ # Forcibly connect to server sock in case the server thread is
+ # hanging out in its accept() (e.g. if the client side of the test
+ # fails before it even gets to connecting); there's no other good
+ # way to force an accept() to exit.
+ put_a_sock_in_it = socket.socket()
+ put_a_sock_in_it.connect((self.addr, self.port))
+ put_a_sock_in_it.close()
+ # Then close "our" end of the socket (which _should_ cause the
+ # accept() to bail out, but does not, for some reason. I blame
+ # threading.)
+ self.sockl.close()
+
+ def _run(
+ self, allowed_keys=None, delay=0, public_blob=None, kill_event=None
+ ):
if allowed_keys is None:
allowed_keys = FINGERPRINTS.keys()
self.socks, addr = self.sockl.accept()
+ # If the kill event was set at this point, it indicates an early
+ # shutdown, so bail out now and don't even try setting up a Transport
+ # (which will just verbosely die.)
+ if kill_event and kill_event.is_set():
+ self.socks.close()
+ return
self.ts = paramiko.Transport(self.socks)
- keypath = test_path('test_rsa.key')
+ keypath = _support("test_rsa.key")
host_key = paramiko.RSAKey.from_private_key_file(keypath)
self.ts.add_server_key(host_key)
- keypath = test_path('test_ecdsa_256.key')
+ keypath = _support("test_ecdsa_256.key")
host_key = paramiko.ECDSAKey.from_private_key_file(keypath)
self.ts.add_server_key(host_key)
server = NullServer(allowed_keys=allowed_keys, public_blob=public_blob)
@@ -149,17 +183,21 @@ class SSHClientTest (unittest.TestCase):
The exception is ``allowed_keys`` which is stripped and handed to the
``NullServer`` used for testing.
"""
- run_kwargs = {}
- for key in ('allowed_keys', 'public_blob'):
+ run_kwargs = {"kill_event": self.kill_event}
+ for key in ("allowed_keys", "public_blob"):
run_kwargs[key] = kwargs.pop(key, None)
# Server setup
threading.Thread(target=self._run, kwargs=run_kwargs).start()
- host_key = paramiko.RSAKey.from_private_key_file(test_path('test_rsa.key'))
+ host_key = paramiko.RSAKey.from_private_key_file(
+ _support("test_rsa.key")
+ )
public_host_key = paramiko.RSAKey(data=host_key.asbytes())
# Client setup
self.tc = paramiko.SSHClient()
- self.tc.get_host_keys().add('[%s]:%d' % (self.addr, self.port), 'ssh-rsa', public_host_key)
+ self.tc.get_host_keys().add(
+ "[%s]:%d" % (self.addr, self.port), "ssh-rsa", public_host_key
+ )
# Actual connection
self.tc.connect(**dict(self.connect_kwargs, **kwargs))
@@ -168,54 +206,57 @@ class SSHClientTest (unittest.TestCase):
self.event.wait(1.0)
self.assertTrue(self.event.is_set())
self.assertTrue(self.ts.is_active())
- self.assertEqual('slowdive', self.ts.get_username())
+ self.assertEqual("slowdive", self.ts.get_username())
self.assertEqual(True, self.ts.is_authenticated())
self.assertEqual(False, self.tc.get_transport().gss_kex_used)
# Command execution functions?
- stdin, stdout, stderr = self.tc.exec_command('yes')
+ stdin, stdout, stderr = self.tc.exec_command("yes")
schan = self.ts.accept(1.0)
- schan.send('Hello there.\n')
- schan.send_stderr('This is on stderr.\n')
+ schan.send("Hello there.\n")
+ schan.send_stderr("This is on stderr.\n")
schan.close()
- self.assertEqual('Hello there.\n', stdout.readline())
- self.assertEqual('', stdout.readline())
- self.assertEqual('This is on stderr.\n', stderr.readline())
- self.assertEqual('', stderr.readline())
+ self.assertEqual("Hello there.\n", stdout.readline())
+ self.assertEqual("", stdout.readline())
+ self.assertEqual("This is on stderr.\n", stderr.readline())
+ self.assertEqual("", stderr.readline())
# Cleanup
stdin.close()
stdout.close()
stderr.close()
+
+class SSHClientTest(ClientTest):
+
def test_1_client(self):
"""
verify that the SSHClient stuff works too.
"""
- self._test_connection(password='pygmalion')
+ self._test_connection(password="pygmalion")
def test_2_client_dsa(self):
"""
verify that SSHClient works with a DSA key.
"""
- self._test_connection(key_filename=test_path('test_dss.key'))
+ self._test_connection(key_filename=_support("test_dss.key"))
def test_client_rsa(self):
"""
verify that SSHClient works with an RSA key.
"""
- self._test_connection(key_filename=test_path('test_rsa.key'))
+ self._test_connection(key_filename=_support("test_rsa.key"))
def test_2_5_client_ecdsa(self):
"""
verify that SSHClient works with an ECDSA key.
"""
- self._test_connection(key_filename=test_path('test_ecdsa_256.key'))
+ self._test_connection(key_filename=_support("test_ecdsa_256.key"))
def test_client_ed25519(self):
- self._test_connection(key_filename=test_path('test_ed25519.key'))
+ self._test_connection(key_filename=_support("test_ed25519.key"))
def test_3_multiple_key_files(self):
"""
@@ -223,22 +264,22 @@ class SSHClientTest (unittest.TestCase):
"""
# This is dumb :(
types_ = {
- 'rsa': 'ssh-rsa',
- 'dss': 'ssh-dss',
- 'ecdsa': 'ecdsa-sha2-nistp256',
+ "rsa": "ssh-rsa",
+ "dss": "ssh-dss",
+ "ecdsa": "ecdsa-sha2-nistp256",
}
# Various combos of attempted & valid keys
# TODO: try every possible combo using itertools functions
for attempt, accept in (
- (['rsa', 'dss'], ['dss']), # Original test #3
- (['dss', 'rsa'], ['dss']), # Ordering matters sometimes, sadly
- (['dss', 'rsa', 'ecdsa_256'], ['dss']), # Try ECDSA but fail
- (['rsa', 'ecdsa_256'], ['ecdsa']), # ECDSA success
+ (["rsa", "dss"], ["dss"]), # Original test #3
+ (["dss", "rsa"], ["dss"]), # Ordering matters sometimes, sadly
+ (["dss", "rsa", "ecdsa_256"], ["dss"]), # Try ECDSA but fail
+ (["rsa", "ecdsa_256"], ["ecdsa"]), # ECDSA success
):
try:
self._test_connection(
key_filename=[
- test_path('test_{0}.key'.format(x)) for x in attempt
+ _support("test_{}.key".format(x)) for x in attempt
],
allowed_keys=[types_[x] for x in accept],
)
@@ -254,10 +295,11 @@ class SSHClientTest (unittest.TestCase):
"""
# Until #387 is fixed we have to catch a high-up exception since
# various platforms trigger different errors here >_<
- self.assertRaises(SSHException,
+ self.assertRaises(
+ SSHException,
self._test_connection,
- key_filename=[test_path('test_rsa.key')],
- allowed_keys=['ecdsa-sha2-nistp256'],
+ key_filename=[_support("test_rsa.key")],
+ allowed_keys=["ecdsa-sha2-nistp256"],
)
def test_certs_allowed_as_key_filename_values(self):
@@ -265,9 +307,9 @@ class SSHClientTest (unittest.TestCase):
# They're similar except for which path is given; the expected auth and
# server-side behavior is 100% identical.)
# NOTE: only bothered whipping up one cert per overall class/family.
- for type_ in ('rsa', 'dss', 'ecdsa_256', 'ed25519'):
- cert_name = 'test_{0}.key-cert.pub'.format(type_)
- cert_path = test_path(os.path.join('cert_support', cert_name))
+ for type_ in ("rsa", "dss", "ecdsa_256", "ed25519"):
+ cert_name = "test_{}.key-cert.pub".format(type_)
+ cert_path = _support(os.path.join("cert_support", cert_name))
self._test_connection(
key_filename=cert_path,
public_blob=PublicBlob.from_file(cert_path),
@@ -280,13 +322,13 @@ class SSHClientTest (unittest.TestCase):
# about the server-side key object's public blob. Thus, we can prove
# that a specific cert was found, along with regular authorization
# succeeding proving that the overall flow works.
- for type_ in ('rsa', 'dss', 'ecdsa_256', 'ed25519'):
- key_name = 'test_{0}.key'.format(type_)
- key_path = test_path(os.path.join('cert_support', key_name))
+ for type_ in ("rsa", "dss", "ecdsa_256", "ed25519"):
+ key_name = "test_{}.key".format(type_)
+ key_path = _support(os.path.join("cert_support", key_name))
self._test_connection(
key_filename=key_path,
public_blob=PublicBlob.from_file(
- '{0}-cert.pub'.format(key_path)
+ "{}-cert.pub".format(key_path)
),
)
@@ -301,19 +343,19 @@ class SSHClientTest (unittest.TestCase):
verify that SSHClient's AutoAddPolicy works.
"""
threading.Thread(target=self._run).start()
- hostname = '[%s]:%d' % (self.addr, self.port)
- key_file = test_path('test_ecdsa_256.key')
+ hostname = "[%s]:%d" % (self.addr, self.port)
+ key_file = _support("test_ecdsa_256.key")
public_host_key = paramiko.ECDSAKey.from_private_key_file(key_file)
self.tc = paramiko.SSHClient()
self.tc.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.assertEqual(0, len(self.tc.get_host_keys()))
- self.tc.connect(password='pygmalion', **self.connect_kwargs)
+ self.tc.connect(password="pygmalion", **self.connect_kwargs)
self.event.wait(1.0)
self.assertTrue(self.event.is_set())
self.assertTrue(self.ts.is_active())
- self.assertEqual('slowdive', self.ts.get_username())
+ self.assertEqual("slowdive", self.ts.get_username())
self.assertEqual(True, self.ts.is_authenticated())
self.assertEqual(1, len(self.tc.get_host_keys()))
new_host_key = list(self.tc.get_host_keys()[hostname].values())[0]
@@ -323,9 +365,11 @@ class SSHClientTest (unittest.TestCase):
"""
verify that SSHClient correctly saves a known_hosts file.
"""
- warnings.filterwarnings('ignore', 'tempnam.*')
+ warnings.filterwarnings("ignore", "tempnam.*")
- host_key = paramiko.RSAKey.from_private_key_file(test_path('test_rsa.key'))
+ host_key = paramiko.RSAKey.from_private_key_file(
+ _support("test_rsa.key")
+ )
public_host_key = paramiko.RSAKey(data=host_key.asbytes())
fd, localname = mkstemp()
os.close(fd)
@@ -333,11 +377,13 @@ class SSHClientTest (unittest.TestCase):
client = paramiko.SSHClient()
self.assertEquals(0, len(client.get_host_keys()))
- host_id = '[%s]:%d' % (self.addr, self.port)
+ host_id = "[%s]:%d" % (self.addr, self.port)
- client.get_host_keys().add(host_id, 'ssh-rsa', public_host_key)
+ client.get_host_keys().add(host_id, "ssh-rsa", public_host_key)
self.assertEquals(1, len(client.get_host_keys()))
- self.assertEquals(public_host_key, client.get_host_keys()[host_id]['ssh-rsa'])
+ self.assertEquals(
+ public_host_key, client.get_host_keys()[host_id]["ssh-rsa"]
+ )
client.save_host_keys(localname)
@@ -360,7 +406,7 @@ class SSHClientTest (unittest.TestCase):
self.tc = paramiko.SSHClient()
self.tc.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.assertEqual(0, len(self.tc.get_host_keys()))
- self.tc.connect(**dict(self.connect_kwargs, password='pygmalion'))
+ self.tc.connect(**dict(self.connect_kwargs, password="pygmalion"))
self.event.wait(1.0)
self.assertTrue(self.event.is_set())
@@ -389,7 +435,7 @@ class SSHClientTest (unittest.TestCase):
self.tc = tc
self.tc.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.assertEquals(0, len(self.tc.get_host_keys()))
- self.tc.connect(**dict(self.connect_kwargs, password='pygmalion'))
+ self.tc.connect(**dict(self.connect_kwargs, password="pygmalion"))
self.event.wait(1.0)
self.assertTrue(self.event.is_set())
@@ -404,19 +450,19 @@ class SSHClientTest (unittest.TestCase):
verify that the SSHClient has a configurable banner timeout.
"""
# Start the thread with a 1 second wait.
- threading.Thread(target=self._run, kwargs={'delay': 1}).start()
- host_key = paramiko.RSAKey.from_private_key_file(test_path('test_rsa.key'))
+ threading.Thread(target=self._run, kwargs={"delay": 1}).start()
+ host_key = paramiko.RSAKey.from_private_key_file(
+ _support("test_rsa.key")
+ )
public_host_key = paramiko.RSAKey(data=host_key.asbytes())
self.tc = paramiko.SSHClient()
- self.tc.get_host_keys().add('[%s]:%d' % (self.addr, self.port), 'ssh-rsa', public_host_key)
+ self.tc.get_host_keys().add(
+ "[%s]:%d" % (self.addr, self.port), "ssh-rsa", public_host_key
+ )
# Connect with a half second banner timeout.
kwargs = dict(self.connect_kwargs, banner_timeout=0.5)
- self.assertRaises(
- paramiko.SSHException,
- self.tc.connect,
- **kwargs
- )
+ self.assertRaises(paramiko.SSHException, self.tc.connect, **kwargs)
def test_8_auth_trickledown(self):
"""
@@ -432,12 +478,13 @@ class SSHClientTest (unittest.TestCase):
# 'television' as per tests/test_pkey.py). NOTE: must use
# key_filename, loading the actual key here with PKey will except
# immediately; we're testing the try/except crap within Client.
- key_filename=[test_path('test_rsa_password.key')],
+ key_filename=[_support("test_rsa_password.key")],
# Actual password for default 'slowdive' user
- password='pygmalion',
+ password="pygmalion",
)
self._test_connection(**kwargs)
+ @slow
def test_9_auth_timeout(self):
"""
verify that the SSHClient has a configurable auth timeout
@@ -446,32 +493,24 @@ class SSHClientTest (unittest.TestCase):
self.assertRaises(
AuthenticationException,
self._test_connection,
- password='unresponsive-server',
+ password="unresponsive-server",
auth_timeout=0.5,
)
+ @requires_gss_auth
def test_10_auth_trickledown_gsskex(self):
"""
Failed gssapi-keyex auth doesn't prevent subsequent key auth from succeeding
"""
- if not paramiko.GSS_AUTH_AVAILABLE:
- return # for python 2.6 lacks skipTest
- kwargs = dict(
- gss_kex=True,
- key_filename=[test_path('test_rsa.key')],
- )
+ kwargs = dict(gss_kex=True, key_filename=[_support("test_rsa.key")])
self._test_connection(**kwargs)
+ @requires_gss_auth
def test_11_auth_trickledown_gssauth(self):
"""
Failed gssapi-with-mic auth doesn't prevent subsequent key auth from succeeding
"""
- if not paramiko.GSS_AUTH_AVAILABLE:
- return # for python 2.6 lacks skipTest
- kwargs = dict(
- gss_auth=True,
- key_filename=[test_path('test_rsa.key')],
- )
+ kwargs = dict(gss_auth=True, key_filename=[_support("test_rsa.key")])
self._test_connection(**kwargs)
def test_12_reject_policy(self):
@@ -486,17 +525,17 @@ class SSHClientTest (unittest.TestCase):
self.assertRaises(
paramiko.SSHException,
self.tc.connect,
- password='pygmalion', **self.connect_kwargs
+ password="pygmalion",
+ **self.connect_kwargs
)
+ @requires_gss_auth
def test_13_reject_policy_gsskex(self):
"""
verify that SSHClient's RejectPolicy works,
even if gssapi-keyex was enabled but not used.
"""
# Test for a bug present in paramiko versions released before 2017-08-01
- if not paramiko.GSS_AUTH_AVAILABLE:
- return # for python 2.6 lacks skipTest
threading.Thread(target=self._run).start()
self.tc = paramiko.SSHClient()
@@ -505,14 +544,14 @@ class SSHClientTest (unittest.TestCase):
self.assertRaises(
paramiko.SSHException,
self.tc.connect,
- password='pygmalion',
+ password="pygmalion",
gss_kex=True,
- **self.connect_kwargs
+ **self.connect_kwargs
)
def _client_host_key_bad(self, host_key):
threading.Thread(target=self._run).start()
- hostname = '[%s]:%d' % (self.addr, self.port)
+ hostname = "[%s]:%d" % (self.addr, self.port)
self.tc = paramiko.SSHClient()
self.tc.set_missing_host_key_policy(paramiko.WarningPolicy())
@@ -522,21 +561,21 @@ class SSHClientTest (unittest.TestCase):
self.assertRaises(
paramiko.BadHostKeyException,
self.tc.connect,
- password='pygmalion',
+ password="pygmalion",
**self.connect_kwargs
)
def _client_host_key_good(self, ktype, kfile):
threading.Thread(target=self._run).start()
- hostname = '[%s]:%d' % (self.addr, self.port)
+ hostname = "[%s]:%d" % (self.addr, self.port)
self.tc = paramiko.SSHClient()
self.tc.set_missing_host_key_policy(paramiko.RejectPolicy())
- host_key = ktype.from_private_key_file(test_path(kfile))
+ host_key = ktype.from_private_key_file(_support(kfile))
known_hosts = self.tc.get_host_keys()
known_hosts.add(hostname, host_key.get_name(), host_key)
- self.tc.connect(password='pygmalion', **self.connect_kwargs)
+ self.tc.connect(password="pygmalion", **self.connect_kwargs)
self.event.wait(1.0)
self.assertTrue(self.event.is_set())
self.assertTrue(self.ts.is_active())
@@ -551,46 +590,51 @@ class SSHClientTest (unittest.TestCase):
self._client_host_key_bad(host_key)
def test_host_key_negotiation_3(self):
- self._client_host_key_good(paramiko.ECDSAKey, 'test_ecdsa_256.key')
+ self._client_host_key_good(paramiko.ECDSAKey, "test_ecdsa_256.key")
def test_host_key_negotiation_4(self):
- self._client_host_key_good(paramiko.RSAKey, 'test_rsa.key')
+ self._client_host_key_good(paramiko.RSAKey, "test_rsa.key")
- def test_update_environment(self):
- """
- Verify that environment variables can be set by the client.
- """
+ def _setup_for_env(self):
threading.Thread(target=self._run).start()
self.tc = paramiko.SSHClient()
self.tc.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.assertEqual(0, len(self.tc.get_host_keys()))
- self.tc.connect(self.addr, self.port, username='slowdive', password='pygmalion')
+ self.tc.connect(
+ self.addr, self.port, username="slowdive", password="pygmalion"
+ )
self.event.wait(1.0)
self.assertTrue(self.event.isSet())
self.assertTrue(self.ts.is_active())
- target_env = {b'A': b'B', b'C': b'd'}
+ def test_update_environment(self):
+ """
+ Verify that environment variables can be set by the client.
+ """
+ self._setup_for_env()
+ target_env = {b"A": b"B", b"C": b"d"}
- self.tc.exec_command('yes', environment=target_env)
+ self.tc.exec_command("yes", environment=target_env)
schan = self.ts.accept(1.0)
- self.assertEqual(target_env, getattr(schan, 'env', {}))
+ self.assertEqual(target_env, getattr(schan, "env", {}))
schan.close()
- # Cannot use assertRaises in context manager mode as it is not supported
- # in Python 2.6.
- try:
+ @unittest.skip("Clients normally fail silently, thus so do we, for now")
+ def test_env_update_failures(self):
+ self._setup_for_env()
+ with self.assertRaises(SSHException) as manager:
# Verify that a rejection by the server can be detected
- self.tc.exec_command('yes', environment={b'INVALID_ENV': b''})
- except SSHException as e:
- self.assertTrue('INVALID_ENV' in str(e),
- 'Expected variable name in error message')
- self.assertTrue(isinstance(e.args[1], SSHException),
- 'Expected original SSHException in exception')
- else:
- self.assertFalse(False, 'SSHException was not thrown.')
-
+ self.tc.exec_command("yes", environment={b"INVALID_ENV": b""})
+ self.assertTrue(
+ "INVALID_ENV" in str(manager.exception),
+ "Expected variable name in error message",
+ )
+ self.assertTrue(
+ isinstance(manager.exception.args[1], SSHException),
+ "Expected original SSHException in exception",
+ )
def test_missing_key_policy_accepts_classes_or_instances(self):
"""
@@ -607,3 +651,49 @@ class SSHClientTest (unittest.TestCase):
# Hand in just the class (new behavior)
client.set_missing_host_key_policy(paramiko.AutoAddPolicy)
assert isinstance(client._policy, paramiko.AutoAddPolicy)
+
+
+class PasswordPassphraseTests(ClientTest):
+ # TODO: most of these could reasonably be set up to use mocks/assertions
+ # (e.g. "gave passphrase -> expect PKey was given it as the passphrase")
+ # instead of suffering a real connection cycle.
+ # TODO: in that case, move the below to be part of an integration suite?
+
+ def test_password_kwarg_works_for_password_auth(self):
+ # Straightforward / duplicate of earlier basic password test.
+ self._test_connection(password="pygmalion")
+
+ # TODO: more granular exception pending #387; should be signaling "no auth
+ # methods available" because no key and no password
+ @raises(SSHException)
+ def test_passphrase_kwarg_not_used_for_password_auth(self):
+ # Using the "right" password in the "wrong" field shouldn't work.
+ self._test_connection(passphrase="pygmalion")
+
+ def test_passphrase_kwarg_used_for_key_passphrase(self):
+ # Straightforward again, with new passphrase kwarg.
+ self._test_connection(
+ key_filename=_support("test_rsa_password.key"),
+ passphrase="television",
+ )
+
+ def test_password_kwarg_used_for_passphrase_when_no_passphrase_kwarg_given(
+ self
+ ): # noqa
+ # Backwards compatibility: passphrase in the password field.
+ self._test_connection(
+ key_filename=_support("test_rsa_password.key"),
+ password="television",
+ )
+
+ @raises(AuthenticationException) # TODO: more granular
+ def test_password_kwarg_not_used_for_passphrase_when_passphrase_kwarg_given(
+ self
+ ): # noqa
+ # Sanity: if we're given both fields, the password field is NOT used as
+ # a passphrase.
+ self._test_connection(
+ key_filename=_support("test_rsa_password.key"),
+ password="television",
+ passphrase="wat? lol no",
+ )
diff --git a/tests/test_file.py b/tests/test_file.py
index b33ecd51..deacd60a 100755..100644
--- a/tests/test_file.py
+++ b/tests/test_file.py
@@ -27,21 +27,22 @@ from paramiko.common import linefeed_byte, crlf, cr_byte
from paramiko.file import BufferedFile
from paramiko.py3compat import BytesIO
-from tests import skipUnlessBuiltin
+from .util import needs_builtin
-class LoopbackFile (BufferedFile):
+class LoopbackFile(BufferedFile):
"""
BufferedFile object that you can write data into, and then read it back.
"""
- def __init__(self, mode='r', bufsize=-1):
+
+ def __init__(self, mode="r", bufsize=-1):
BufferedFile.__init__(self)
self._set_mode(mode, bufsize)
self.buffer = BytesIO()
self.offset = 0
def _read(self, size):
- data = self.buffer.getvalue()[self.offset:self.offset+size]
+ data = self.buffer.getvalue()[self.offset : self.offset + size]
self.offset += len(data)
return data
@@ -50,44 +51,46 @@ class LoopbackFile (BufferedFile):
return len(data)
-class BufferedFileTest (unittest.TestCase):
+class BufferedFileTest(unittest.TestCase):
def test_1_simple(self):
- f = LoopbackFile('r')
+ f = LoopbackFile("r")
try:
- f.write(b'hi')
- self.assertTrue(False, 'no exception on write to read-only file')
+ f.write(b"hi")
+ self.assertTrue(False, "no exception on write to read-only file")
except:
pass
f.close()
- f = LoopbackFile('w')
+ f = LoopbackFile("w")
try:
f.read(1)
- self.assertTrue(False, 'no exception to read from write-only file')
+ self.assertTrue(False, "no exception to read from write-only file")
except:
pass
f.close()
def test_2_readline(self):
- f = LoopbackFile('r+U')
- f.write(b'First line.\nSecond line.\r\nThird line.\n' +
- b'Fourth line.\nFinal line non-terminated.')
+ f = LoopbackFile("r+U")
+ f.write(
+ b"First line.\nSecond line.\r\nThird line.\n"
+ + b"Fourth line.\nFinal line non-terminated."
+ )
- self.assertEqual(f.readline(), 'First line.\n')
+ self.assertEqual(f.readline(), "First line.\n")
# universal newline mode should convert this linefeed:
- self.assertEqual(f.readline(), 'Second line.\n')
+ self.assertEqual(f.readline(), "Second line.\n")
# truncated line:
- self.assertEqual(f.readline(7), 'Third l')
- self.assertEqual(f.readline(), 'ine.\n')
+ self.assertEqual(f.readline(7), "Third l")
+ self.assertEqual(f.readline(), "ine.\n")
# newline should be detected and only the fourth line returned
- self.assertEqual(f.readline(39), 'Fourth line.\n')
- self.assertEqual(f.readline(), 'Final line non-terminated.')
- self.assertEqual(f.readline(), '')
+ self.assertEqual(f.readline(39), "Fourth line.\n")
+ self.assertEqual(f.readline(), "Final line non-terminated.")
+ self.assertEqual(f.readline(), "")
f.close()
try:
f.readline()
- self.assertTrue(False, 'no exception on readline of closed file')
+ self.assertTrue(False, "no exception on readline of closed file")
except IOError:
pass
self.assertTrue(linefeed_byte in f.newlines)
@@ -98,11 +101,11 @@ class BufferedFileTest (unittest.TestCase):
"""
try to trick the linefeed detector.
"""
- f = LoopbackFile('r+U')
- f.write(b'First line.\r')
- self.assertEqual(f.readline(), 'First line.\n')
- f.write(b'\nSecond.\r\n')
- self.assertEqual(f.readline(), 'Second.\n')
+ f = LoopbackFile("r+U")
+ f.write(b"First line.\r")
+ self.assertEqual(f.readline(), "First line.\n")
+ f.write(b"\nSecond.\r\n")
+ self.assertEqual(f.readline(), "Second.\n")
f.close()
self.assertEqual(f.newlines, crlf)
@@ -110,51 +113,54 @@ class BufferedFileTest (unittest.TestCase):
"""
verify that write buffering is on.
"""
- f = LoopbackFile('r+', 1)
- f.write(b'Complete line.\nIncomplete line.')
- self.assertEqual(f.readline(), 'Complete line.\n')
- self.assertEqual(f.readline(), '')
- f.write('..\n')
- self.assertEqual(f.readline(), 'Incomplete line...\n')
+ f = LoopbackFile("r+", 1)
+ f.write(b"Complete line.\nIncomplete line.")
+ self.assertEqual(f.readline(), "Complete line.\n")
+ self.assertEqual(f.readline(), "")
+ f.write("..\n")
+ self.assertEqual(f.readline(), "Incomplete line...\n")
f.close()
def test_5_flush(self):
"""
verify that flush will force a write.
"""
- f = LoopbackFile('r+', 512)
- f.write('Not\nquite\n512 bytes.\n')
- self.assertEqual(f.read(1), b'')
+ f = LoopbackFile("r+", 512)
+ f.write("Not\nquite\n512 bytes.\n")
+ self.assertEqual(f.read(1), b"")
f.flush()
- self.assertEqual(f.read(5), b'Not\nq')
- self.assertEqual(f.read(10), b'uite\n512 b')
- self.assertEqual(f.read(9), b'ytes.\n')
- self.assertEqual(f.read(3), b'')
+ self.assertEqual(f.read(5), b"Not\nq")
+ self.assertEqual(f.read(10), b"uite\n512 b")
+ self.assertEqual(f.read(9), b"ytes.\n")
+ self.assertEqual(f.read(3), b"")
f.close()
def test_6_buffering(self):
"""
verify that flushing happens automatically on buffer crossing.
"""
- f = LoopbackFile('r+', 16)
- f.write(b'Too small.')
- self.assertEqual(f.read(4), b'')
- f.write(b' ')
- self.assertEqual(f.read(4), b'')
- f.write(b'Enough.')
- self.assertEqual(f.read(20), b'Too small. Enough.')
+ f = LoopbackFile("r+", 16)
+ f.write(b"Too small.")
+ self.assertEqual(f.read(4), b"")
+ f.write(b" ")
+ self.assertEqual(f.read(4), b"")
+ f.write(b"Enough.")
+ self.assertEqual(f.read(20), b"Too small. Enough.")
f.close()
def test_7_read_all(self):
"""
verify that read(-1) returns everything left in the file.
"""
- f = LoopbackFile('r+', 16)
- f.write(b'The first thing you need to do is open your eyes. ')
- f.write(b'Then, you need to close them again.\n')
+ f = LoopbackFile("r+", 16)
+ f.write(b"The first thing you need to do is open your eyes. ")
+ f.write(b"Then, you need to close them again.\n")
s = f.read(-1)
- self.assertEqual(s, b'The first thing you need to do is open your eyes. Then, you ' +
- b'need to close them again.\n')
+ self.assertEqual(
+ s,
+ b"The first thing you need to do is open your eyes. Then, you "
+ + b"need to close them again.\n",
+ )
f.close()
def test_8_buffering(self):
@@ -162,19 +168,19 @@ class BufferedFileTest (unittest.TestCase):
verify that buffered objects can be written
"""
if sys.version_info[0] == 2:
- f = LoopbackFile('r+', 16)
- f.write(buffer(b'Too small.'))
+ f = LoopbackFile("r+", 16)
+ f.write(buffer(b"Too small."))
f.close()
def test_9_readable(self):
- f = LoopbackFile('r')
+ f = LoopbackFile("r")
self.assertTrue(f.readable())
self.assertFalse(f.writable())
self.assertFalse(f.seekable())
f.close()
def test_A_writable(self):
- f = LoopbackFile('w')
+ f = LoopbackFile("w")
self.assertTrue(f.writable())
self.assertFalse(f.readable())
self.assertFalse(f.seekable())
@@ -182,48 +188,49 @@ class BufferedFileTest (unittest.TestCase):
def test_B_readinto(self):
data = bytearray(5)
- f = LoopbackFile('r+')
+ f = LoopbackFile("r+")
f._write(b"hello")
f.readinto(data)
- self.assertEqual(data, b'hello')
+ self.assertEqual(data, b"hello")
f.close()
def test_write_bad_type(self):
- with LoopbackFile('wb') as f:
+ with LoopbackFile("wb") as f:
self.assertRaises(TypeError, f.write, object())
def test_write_unicode_as_binary(self):
text = u"\xa7 why is writing text to a binary file allowed?\n"
- with LoopbackFile('rb+') as f:
+ with LoopbackFile("rb+") as f:
f.write(text)
self.assertEqual(f.read(), text.encode("utf-8"))
- @skipUnlessBuiltin('memoryview')
+ @needs_builtin("memoryview")
def test_write_bytearray(self):
- with LoopbackFile('rb+') as f:
+ with LoopbackFile("rb+") as f:
f.write(bytearray(12))
self.assertEqual(f.read(), 12 * b"\0")
- @skipUnlessBuiltin('buffer')
+ @needs_builtin("buffer")
def test_write_buffer(self):
data = 3 * b"pretend giant block of data\n"
offsets = range(0, len(data), 8)
- with LoopbackFile('rb+') as f:
+ with LoopbackFile("rb+") as f:
for offset in offsets:
f.write(buffer(data, offset, 8))
self.assertEqual(f.read(), data)
- @skipUnlessBuiltin('memoryview')
+ @needs_builtin("memoryview")
def test_write_memoryview(self):
data = 3 * b"pretend giant block of data\n"
offsets = range(0, len(data), 8)
- with LoopbackFile('rb+') as f:
+ with LoopbackFile("rb+") as f:
view = memoryview(data)
for offset in offsets:
- f.write(view[offset:offset+8])
+ f.write(view[offset : offset + 8])
self.assertEqual(f.read(), data)
-if __name__ == '__main__':
+if __name__ == "__main__":
from unittest import main
+
main()
diff --git a/tests/test_gssapi.py b/tests/test_gssapi.py
index bc220108..d7fbdd53 100644
--- a/tests/test_gssapi.py
+++ b/tests/test_gssapi.py
@@ -25,14 +25,18 @@ Test the used APIs for GSS-API / SSPI authentication
import unittest
import socket
+from .util import needs_gssapi
+
+@needs_gssapi
class GSSAPITest(unittest.TestCase):
- @staticmethod
- def init(hostname=None, srv_mode=False):
- global krb5_mech, targ_name, server_mode
- krb5_mech = "1.2.840.113554.1.2.2"
- targ_name = hostname
- server_mode = srv_mode
+
+ def setup():
+ # TODO: these vars should all come from os.environ or whatever the
+ # approved pytest method is for runtime-configuring test data.
+ self.krb5_mech = "1.2.840.113554.1.2.2"
+ self.targ_name = "hostname"
+ self.server_mode = False
def test_1_pyasn1(self):
"""
@@ -40,9 +44,10 @@ class GSSAPITest(unittest.TestCase):
"""
from pyasn1.type.univ import ObjectIdentifier
from pyasn1.codec.der import encoder, decoder
- oid = encoder.encode(ObjectIdentifier(krb5_mech))
+
+ oid = encoder.encode(ObjectIdentifier(self.krb5_mech))
mech, __ = decoder.decode(oid)
- self.assertEquals(krb5_mech, mech.__str__())
+ self.assertEquals(self.krb5_mech, mech.__str__())
def test_2_gssapi_sspi(self):
"""
@@ -54,6 +59,7 @@ class GSSAPITest(unittest.TestCase):
except ImportError:
import sspicon
import sspi
+
_API = "SSPI"
c_token = None
@@ -61,25 +67,30 @@ class GSSAPITest(unittest.TestCase):
mic_msg = b"G'day Mate!"
if _API == "MIT":
- if server_mode:
- gss_flags = (gssapi.C_PROT_READY_FLAG,
- gssapi.C_INTEG_FLAG,
- gssapi.C_MUTUAL_FLAG,
- gssapi.C_DELEG_FLAG)
+ if self.server_mode:
+ gss_flags = (
+ gssapi.C_PROT_READY_FLAG,
+ gssapi.C_INTEG_FLAG,
+ gssapi.C_MUTUAL_FLAG,
+ gssapi.C_DELEG_FLAG,
+ )
else:
- gss_flags = (gssapi.C_PROT_READY_FLAG,
- gssapi.C_INTEG_FLAG,
- gssapi.C_DELEG_FLAG)
+ gss_flags = (
+ gssapi.C_PROT_READY_FLAG,
+ gssapi.C_INTEG_FLAG,
+ gssapi.C_DELEG_FLAG,
+ )
# Initialize a GSS-API context.
ctx = gssapi.Context()
ctx.flags = gss_flags
- krb5_oid = gssapi.OID.mech_from_string(krb5_mech)
- target_name = gssapi.Name("host@" + targ_name,
- gssapi.C_NT_HOSTBASED_SERVICE)
- gss_ctxt = gssapi.InitContext(peer_name=target_name,
- mech_type=krb5_oid,
- req_flags=ctx.flags)
- if server_mode:
+ krb5_oid = gssapi.OID.mech_from_string(self.krb5_mech)
+ target_name = gssapi.Name(
+ "host@" + self.targ_name, gssapi.C_NT_HOSTBASED_SERVICE
+ )
+ gss_ctxt = gssapi.InitContext(
+ peer_name=target_name, mech_type=krb5_oid, req_flags=ctx.flags
+ )
+ if self.server_mode:
c_token = gss_ctxt.step(c_token)
gss_ctxt_status = gss_ctxt.established
self.assertEquals(False, gss_ctxt_status)
@@ -99,22 +110,22 @@ class GSSAPITest(unittest.TestCase):
# Build MIC
mic_token = gss_ctxt.get_mic(mic_msg)
- if server_mode:
+ if self.server_mode:
# Check MIC
status = gss_srv_ctxt.verify_mic(mic_msg, mic_token)
self.assertEquals(0, status)
else:
gss_flags = (
- sspicon.ISC_REQ_INTEGRITY |
- sspicon.ISC_REQ_MUTUAL_AUTH |
- sspicon.ISC_REQ_DELEGATE
+ sspicon.ISC_REQ_INTEGRITY
+ | sspicon.ISC_REQ_MUTUAL_AUTH
+ | sspicon.ISC_REQ_DELEGATE
)
# Initialize a GSS-API context.
- target_name = "host/" + socket.getfqdn(targ_name)
- gss_ctxt = sspi.ClientAuth("Kerberos",
- scflags=gss_flags,
- targetspn=target_name)
- if server_mode:
+ target_name = "host/" + socket.getfqdn(self.targ_name)
+ gss_ctxt = sspi.ClientAuth(
+ "Kerberos", scflags=gss_flags, targetspn=target_name
+ )
+ if self.server_mode:
error, token = gss_ctxt.authorize(c_token)
c_token = token[0].Buffer
self.assertEquals(0, error)
diff --git a/tests/test_hostkeys.py b/tests/test_hostkeys.py
index 2c7ceeb9..a1b7a9e0 100644
--- a/tests/test_hostkeys.py
+++ b/tests/test_hostkeys.py
@@ -23,6 +23,7 @@ Some unit tests for HostKeys.
from binascii import hexlify
import os
import unittest
+
import paramiko
from paramiko.py3compat import decodebytes
@@ -53,77 +54,80 @@ Ngw3qIch/WgRmMHy4kBq1SsXMjQCte1So6HBMvBPIW5SiMTmjCfZZiw4AYHK+B/JaOwaG9yRg2Ejg\
0d54U0X/NeX5QxuYR6OMJlrkQB7oiW/P/1mwjQgE="""
-class HostKeysTest (unittest.TestCase):
+class HostKeysTest(unittest.TestCase):
def setUp(self):
- with open('hostfile.temp', 'w') as f:
+ with open("hostfile.temp", "w") as f:
f.write(test_hosts_file)
def tearDown(self):
- os.unlink('hostfile.temp')
+ os.unlink("hostfile.temp")
def test_1_load(self):
- hostdict = paramiko.HostKeys('hostfile.temp')
+ hostdict = paramiko.HostKeys("hostfile.temp")
self.assertEqual(2, len(hostdict))
self.assertEqual(1, len(list(hostdict.values())[0]))
self.assertEqual(1, len(list(hostdict.values())[1]))
- fp = hexlify(hostdict['secure.example.com']['ssh-rsa'].get_fingerprint()).upper()
- self.assertEqual(b'E6684DB30E109B67B70FF1DC5C7F1363', fp)
+ fp = hexlify(
+ hostdict["secure.example.com"]["ssh-rsa"].get_fingerprint()
+ ).upper()
+ self.assertEqual(b"E6684DB30E109B67B70FF1DC5C7F1363", fp)
def test_2_add(self):
- hostdict = paramiko.HostKeys('hostfile.temp')
- hh = '|1|BMsIC6cUIP2zBuXR3t2LRcJYjzM=|hpkJMysjTk/+zzUUzxQEa2ieq6c='
+ hostdict = paramiko.HostKeys("hostfile.temp")
+ hh = "|1|BMsIC6cUIP2zBuXR3t2LRcJYjzM=|hpkJMysjTk/+zzUUzxQEa2ieq6c="
key = paramiko.RSAKey(data=decodebytes(keyblob))
- hostdict.add(hh, 'ssh-rsa', key)
+ hostdict.add(hh, "ssh-rsa", key)
self.assertEqual(3, len(list(hostdict)))
- x = hostdict['foo.example.com']
- fp = hexlify(x['ssh-rsa'].get_fingerprint()).upper()
- self.assertEqual(b'7EC91BB336CB6D810B124B1353C32396', fp)
- self.assertTrue(hostdict.check('foo.example.com', key))
+ x = hostdict["foo.example.com"]
+ fp = hexlify(x["ssh-rsa"].get_fingerprint()).upper()
+ self.assertEqual(b"7EC91BB336CB6D810B124B1353C32396", fp)
+ self.assertTrue(hostdict.check("foo.example.com", key))
def test_3_dict(self):
- hostdict = paramiko.HostKeys('hostfile.temp')
- self.assertTrue('secure.example.com' in hostdict)
- self.assertTrue('not.example.com' not in hostdict)
- self.assertTrue('secure.example.com' in hostdict)
- self.assertTrue('not.example.com' not in hostdict)
- x = hostdict.get('secure.example.com', None)
+ hostdict = paramiko.HostKeys("hostfile.temp")
+ self.assertTrue("secure.example.com" in hostdict)
+ self.assertTrue("not.example.com" not in hostdict)
+ self.assertTrue("secure.example.com" in hostdict)
+ self.assertTrue("not.example.com" not in hostdict)
+ x = hostdict.get("secure.example.com", None)
self.assertTrue(x is not None)
- fp = hexlify(x['ssh-rsa'].get_fingerprint()).upper()
- self.assertEqual(b'E6684DB30E109B67B70FF1DC5C7F1363', fp)
+ fp = hexlify(x["ssh-rsa"].get_fingerprint()).upper()
+ self.assertEqual(b"E6684DB30E109B67B70FF1DC5C7F1363", fp)
i = 0
for key in hostdict:
i += 1
self.assertEqual(2, i)
-
+
def test_4_dict_set(self):
- hostdict = paramiko.HostKeys('hostfile.temp')
+ hostdict = paramiko.HostKeys("hostfile.temp")
key = paramiko.RSAKey(data=decodebytes(keyblob))
key_dss = paramiko.DSSKey(data=decodebytes(keyblob_dss))
- hostdict['secure.example.com'] = {
- 'ssh-rsa': key,
- 'ssh-dss': key_dss
- }
- hostdict['fake.example.com'] = {}
- hostdict['fake.example.com']['ssh-rsa'] = key
-
+ hostdict["secure.example.com"] = {"ssh-rsa": key, "ssh-dss": key_dss}
+ hostdict["fake.example.com"] = {}
+ hostdict["fake.example.com"]["ssh-rsa"] = key
+
self.assertEqual(3, len(hostdict))
self.assertEqual(2, len(list(hostdict.values())[0]))
self.assertEqual(1, len(list(hostdict.values())[1]))
self.assertEqual(1, len(list(hostdict.values())[2]))
- fp = hexlify(hostdict['secure.example.com']['ssh-rsa'].get_fingerprint()).upper()
- self.assertEqual(b'7EC91BB336CB6D810B124B1353C32396', fp)
- fp = hexlify(hostdict['secure.example.com']['ssh-dss'].get_fingerprint()).upper()
- self.assertEqual(b'4478F0B9A23CC5182009FF755BC1D26C', fp)
+ fp = hexlify(
+ hostdict["secure.example.com"]["ssh-rsa"].get_fingerprint()
+ ).upper()
+ self.assertEqual(b"7EC91BB336CB6D810B124B1353C32396", fp)
+ fp = hexlify(
+ hostdict["secure.example.com"]["ssh-dss"].get_fingerprint()
+ ).upper()
+ self.assertEqual(b"4478F0B9A23CC5182009FF755BC1D26C", fp)
def test_delitem(self):
- hostdict = paramiko.HostKeys('hostfile.temp')
- target = 'happy.example.com'
- entry = hostdict[target] # will KeyError if not present
+ hostdict = paramiko.HostKeys("hostfile.temp")
+ target = "happy.example.com"
+ entry = hostdict[target] # will KeyError if not present
del hostdict[target]
try:
entry = hostdict[target]
except KeyError:
- pass # Good
+ pass # Good
else:
assert False, "Entry was not deleted from HostKeys on delitem!"
diff --git a/tests/test_kex.py b/tests/test_kex.py
index b7f588f7..13d19d86 100644
--- a/tests/test_kex.py
+++ b/tests/test_kex.py
@@ -24,43 +24,60 @@ from binascii import hexlify, unhexlify
import os
import unittest
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives.asymmetric import ec
+
import paramiko.util
from paramiko.kex_group1 import KexGroup1
from paramiko.kex_gex import KexGex, KexGexSHA256
from paramiko import Message
from paramiko.common import byte_chr
from paramiko.kex_ecdh_nist import KexNistp256
-from cryptography.hazmat.backends import default_backend
-from cryptography.hazmat.primitives.asymmetric import ec
def dummy_urandom(n):
return byte_chr(0xcc) * n
+
def dummy_generate_key_pair(obj):
- private_key_value = 94761803665136558137557783047955027733968423115106677159790289642479432803037
- public_key_numbers = "042bdab212fa8ba1b7c843301682a4db424d307246c7e1e6083c41d9ca7b098bf30b3d63e2ec6278488c135360456cc054b3444ecc45998c08894cbc1370f5f989"
- public_key_numbers_obj = ec.EllipticCurvePublicNumbers.from_encoded_point(ec.SECP256R1(), unhexlify(public_key_numbers))
- obj.P = ec.EllipticCurvePrivateNumbers(private_value=private_key_value, public_numbers=public_key_numbers_obj).private_key(default_backend())
+ private_key_value = (
+ 94761803665136558137557783047955027733968423115106677159790289642479432803037
+ )
+ public_key_numbers = (
+ "042bdab212fa8ba1b7c843301682a4db424d307246c7e1e6083c41d9ca7b098bf30b3d63e2ec6278488c135360456cc054b3444ecc45998c08894cbc1370f5f989"
+ )
+ public_key_numbers_obj = ec.EllipticCurvePublicNumbers.from_encoded_point(
+ ec.SECP256R1(), unhexlify(public_key_numbers)
+ )
+ obj.P = ec.EllipticCurvePrivateNumbers(
+ private_value=private_key_value, public_numbers=public_key_numbers_obj
+ ).private_key(default_backend())
if obj.transport.server_mode:
- obj.Q_S = ec.EllipticCurvePublicNumbers.from_encoded_point(ec.SECP256R1(), unhexlify(public_key_numbers)).public_key(default_backend())
+ obj.Q_S = ec.EllipticCurvePublicNumbers.from_encoded_point(
+ ec.SECP256R1(), unhexlify(public_key_numbers)
+ ).public_key(default_backend())
return
- obj.Q_C = ec.EllipticCurvePublicNumbers.from_encoded_point(ec.SECP256R1(), unhexlify(public_key_numbers)).public_key(default_backend())
+ obj.Q_C = ec.EllipticCurvePublicNumbers.from_encoded_point(
+ ec.SECP256R1(), unhexlify(public_key_numbers)
+ ).public_key(default_backend())
+
+class FakeKey(object):
-class FakeKey (object):
def __str__(self):
- return 'fake-key'
+ return "fake-key"
def asbytes(self):
- return b'fake-key'
+ return b"fake-key"
def sign_ssh_data(self, H):
- return b'fake-sig'
+ return b"fake-sig"
-class FakeModulusPack (object):
- P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF
+class FakeModulusPack(object):
+ P = (
+ 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF
+ )
G = 2
def get_modulus(self, min, ask, max):
@@ -68,10 +85,10 @@ class FakeModulusPack (object):
class FakeTransport(object):
- local_version = 'SSH-2.0-paramiko_1.0'
- remote_version = 'SSH-2.0-lame'
- local_kex_init = 'local-kex-init'
- remote_kex_init = 'remote-kex-init'
+ local_version = "SSH-2.0-paramiko_1.0"
+ remote_version = "SSH-2.0-lame"
+ local_kex_init = "local-kex-init"
+ remote_kex_init = "remote-kex-init"
def _send_message(self, m):
self._message = m
@@ -99,9 +116,11 @@ class FakeTransport(object):
return FakeModulusPack()
-class KexTest (unittest.TestCase):
+class KexTest(unittest.TestCase):
- K = 14730343317708716439807310032871972459448364195094179797249681733965528989482751523943515690110179031004049109375612685505881911274101441415545039654102474376472240501616988799699744135291070488314748284283496055223852115360852283821334858541043710301057312858051901453919067023103730011648890038847384890504
+ K = (
+ 14730343317708716439807310032871972459448364195094179797249681733965528989482751523943515690110179031004049109375612685505881911274101441415545039654102474376472240501616988799699744135291070488314748284283496055223852115360852283821334858541043710301057312858051901453919067023103730011648890038847384890504
+ )
def setUp(self):
self._original_urandom = os.urandom
@@ -118,21 +137,25 @@ class KexTest (unittest.TestCase):
transport.server_mode = False
kex = KexGroup1(transport)
kex.start_kex()
- x = b'1E000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D4'
+ x = (
+ b"1E000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D4"
+ )
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
- self.assertEqual((paramiko.kex_group1._MSG_KEXDH_REPLY,), transport._expect)
+ self.assertEqual(
+ (paramiko.kex_group1._MSG_KEXDH_REPLY,), transport._expect
+ )
# fake "reply"
msg = Message()
- msg.add_string('fake-host-key')
+ msg.add_string("fake-host-key")
msg.add_mpint(69)
- msg.add_string('fake-sig')
+ msg.add_string("fake-sig")
msg.rewind()
kex.parse_next(paramiko.kex_group1._MSG_KEXDH_REPLY, msg)
- H = b'03079780F3D3AD0B3C6DB30C8D21685F367A86D2'
+ H = b"03079780F3D3AD0B3C6DB30C8D21685F367A86D2"
self.assertEqual(self.K, transport._K)
self.assertEqual(H, hexlify(transport._H).upper())
- self.assertEqual((b'fake-host-key', b'fake-sig'), transport._verify)
+ self.assertEqual((b"fake-host-key", b"fake-sig"), transport._verify)
self.assertTrue(transport._activated)
def test_2_group1_server(self):
@@ -140,14 +163,18 @@ class KexTest (unittest.TestCase):
transport.server_mode = True
kex = KexGroup1(transport)
kex.start_kex()
- self.assertEqual((paramiko.kex_group1._MSG_KEXDH_INIT,), transport._expect)
+ self.assertEqual(
+ (paramiko.kex_group1._MSG_KEXDH_INIT,), transport._expect
+ )
msg = Message()
msg.add_mpint(69)
msg.rewind()
kex.parse_next(paramiko.kex_group1._MSG_KEXDH_INIT, msg)
- H = b'B16BF34DD10945EDE84E9C1EF24A14BFDC843389'
- x = b'1F0000000866616B652D6B6579000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D40000000866616B652D736967'
+ H = b"B16BF34DD10945EDE84E9C1EF24A14BFDC843389"
+ x = (
+ b"1F0000000866616B652D6B6579000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D40000000866616B652D736967"
+ )
self.assertEqual(self.K, transport._K)
self.assertEqual(H, hexlify(transport._H).upper())
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
@@ -158,29 +185,35 @@ class KexTest (unittest.TestCase):
transport.server_mode = False
kex = KexGex(transport)
kex.start_kex()
- x = b'22000004000000080000002000'
+ x = b"22000004000000080000002000"
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
- self.assertEqual((paramiko.kex_gex._MSG_KEXDH_GEX_GROUP,), transport._expect)
+ self.assertEqual(
+ (paramiko.kex_gex._MSG_KEXDH_GEX_GROUP,), transport._expect
+ )
msg = Message()
msg.add_mpint(FakeModulusPack.P)
msg.add_mpint(FakeModulusPack.G)
msg.rewind()
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_GROUP, msg)
- x = b'20000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D4'
+ x = (
+ b"20000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D4"
+ )
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
- self.assertEqual((paramiko.kex_gex._MSG_KEXDH_GEX_REPLY,), transport._expect)
+ self.assertEqual(
+ (paramiko.kex_gex._MSG_KEXDH_GEX_REPLY,), transport._expect
+ )
msg = Message()
- msg.add_string('fake-host-key')
+ msg.add_string("fake-host-key")
msg.add_mpint(69)
- msg.add_string('fake-sig')
+ msg.add_string("fake-sig")
msg.rewind()
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_REPLY, msg)
- H = b'A265563F2FA87F1A89BF007EE90D58BE2E4A4BD0'
+ H = b"A265563F2FA87F1A89BF007EE90D58BE2E4A4BD0"
self.assertEqual(self.K, transport._K)
self.assertEqual(H, hexlify(transport._H).upper())
- self.assertEqual((b'fake-host-key', b'fake-sig'), transport._verify)
+ self.assertEqual((b"fake-host-key", b"fake-sig"), transport._verify)
self.assertTrue(transport._activated)
def test_4_gex_old_client(self):
@@ -188,37 +221,49 @@ class KexTest (unittest.TestCase):
transport.server_mode = False
kex = KexGex(transport)
kex.start_kex(_test_old_style=True)
- x = b'1E00000800'
+ x = b"1E00000800"
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
- self.assertEqual((paramiko.kex_gex._MSG_KEXDH_GEX_GROUP,), transport._expect)
+ self.assertEqual(
+ (paramiko.kex_gex._MSG_KEXDH_GEX_GROUP,), transport._expect
+ )
msg = Message()
msg.add_mpint(FakeModulusPack.P)
msg.add_mpint(FakeModulusPack.G)
msg.rewind()
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_GROUP, msg)
- x = b'20000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D4'
+ x = (
+ b"20000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D4"
+ )
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
- self.assertEqual((paramiko.kex_gex._MSG_KEXDH_GEX_REPLY,), transport._expect)
+ self.assertEqual(
+ (paramiko.kex_gex._MSG_KEXDH_GEX_REPLY,), transport._expect
+ )
msg = Message()
- msg.add_string('fake-host-key')
+ msg.add_string("fake-host-key")
msg.add_mpint(69)
- msg.add_string('fake-sig')
+ msg.add_string("fake-sig")
msg.rewind()
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_REPLY, msg)
- H = b'807F87B269EF7AC5EC7E75676808776A27D5864C'
+ H = b"807F87B269EF7AC5EC7E75676808776A27D5864C"
self.assertEqual(self.K, transport._K)
self.assertEqual(H, hexlify(transport._H).upper())
- self.assertEqual((b'fake-host-key', b'fake-sig'), transport._verify)
+ self.assertEqual((b"fake-host-key", b"fake-sig"), transport._verify)
self.assertTrue(transport._activated)
-
+
def test_5_gex_server(self):
transport = FakeTransport()
transport.server_mode = True
kex = KexGex(transport)
kex.start_kex()
- self.assertEqual((paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST, paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST_OLD), transport._expect)
+ self.assertEqual(
+ (
+ paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST,
+ paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST_OLD,
+ ),
+ transport._expect,
+ )
msg = Message()
msg.add_int(1024)
@@ -226,17 +271,25 @@ class KexTest (unittest.TestCase):
msg.add_int(4096)
msg.rewind()
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST, msg)
- x = b'1F0000008100FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF0000000102'
+ x = (
+ b"1F0000008100FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF0000000102"
+ )
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
- self.assertEqual((paramiko.kex_gex._MSG_KEXDH_GEX_INIT,), transport._expect)
+ self.assertEqual(
+ (paramiko.kex_gex._MSG_KEXDH_GEX_INIT,), transport._expect
+ )
msg = Message()
msg.add_mpint(12345)
msg.rewind()
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_INIT, msg)
- K = 67592995013596137876033460028393339951879041140378510871612128162185209509220726296697886624612526735888348020498716482757677848959420073720160491114319163078862905400020959196386947926388406687288901564192071077389283980347784184487280885335302632305026248574716290537036069329724382811853044654824945750581
- H = b'CE754197C21BF3452863B4F44D0B3951F12516EF'
- x = b'210000000866616B652D6B6579000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D40000000866616B652D736967'
+ K = (
+ 67592995013596137876033460028393339951879041140378510871612128162185209509220726296697886624612526735888348020498716482757677848959420073720160491114319163078862905400020959196386947926388406687288901564192071077389283980347784184487280885335302632305026248574716290537036069329724382811853044654824945750581
+ )
+ H = b"CE754197C21BF3452863B4F44D0B3951F12516EF"
+ x = (
+ b"210000000866616B652D6B6579000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D40000000866616B652D736967"
+ )
self.assertEqual(K, transport._K)
self.assertEqual(H, hexlify(transport._H).upper())
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
@@ -247,23 +300,37 @@ class KexTest (unittest.TestCase):
transport.server_mode = True
kex = KexGex(transport)
kex.start_kex()
- self.assertEqual((paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST, paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST_OLD), transport._expect)
+ self.assertEqual(
+ (
+ paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST,
+ paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST_OLD,
+ ),
+ transport._expect,
+ )
msg = Message()
msg.add_int(2048)
msg.rewind()
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST_OLD, msg)
- x = b'1F0000008100FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF0000000102'
+ x = (
+ b"1F0000008100FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF0000000102"
+ )
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
- self.assertEqual((paramiko.kex_gex._MSG_KEXDH_GEX_INIT,), transport._expect)
+ self.assertEqual(
+ (paramiko.kex_gex._MSG_KEXDH_GEX_INIT,), transport._expect
+ )
msg = Message()
msg.add_mpint(12345)
msg.rewind()
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_INIT, msg)
- K = 67592995013596137876033460028393339951879041140378510871612128162185209509220726296697886624612526735888348020498716482757677848959420073720160491114319163078862905400020959196386947926388406687288901564192071077389283980347784184487280885335302632305026248574716290537036069329724382811853044654824945750581
- H = b'B41A06B2E59043CEFC1AE16EC31F1E2D12EC455B'
- x = b'210000000866616B652D6B6579000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D40000000866616B652D736967'
+ K = (
+ 67592995013596137876033460028393339951879041140378510871612128162185209509220726296697886624612526735888348020498716482757677848959420073720160491114319163078862905400020959196386947926388406687288901564192071077389283980347784184487280885335302632305026248574716290537036069329724382811853044654824945750581
+ )
+ H = b"B41A06B2E59043CEFC1AE16EC31F1E2D12EC455B"
+ x = (
+ b"210000000866616B652D6B6579000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D40000000866616B652D736967"
+ )
self.assertEqual(K, transport._K)
self.assertEqual(H, hexlify(transport._H).upper())
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
@@ -274,29 +341,35 @@ class KexTest (unittest.TestCase):
transport.server_mode = False
kex = KexGexSHA256(transport)
kex.start_kex()
- x = b'22000004000000080000002000'
+ x = b"22000004000000080000002000"
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
- self.assertEqual((paramiko.kex_gex._MSG_KEXDH_GEX_GROUP,), transport._expect)
+ self.assertEqual(
+ (paramiko.kex_gex._MSG_KEXDH_GEX_GROUP,), transport._expect
+ )
msg = Message()
msg.add_mpint(FakeModulusPack.P)
msg.add_mpint(FakeModulusPack.G)
msg.rewind()
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_GROUP, msg)
- x = b'20000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D4'
+ x = (
+ b"20000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D4"
+ )
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
- self.assertEqual((paramiko.kex_gex._MSG_KEXDH_GEX_REPLY,), transport._expect)
+ self.assertEqual(
+ (paramiko.kex_gex._MSG_KEXDH_GEX_REPLY,), transport._expect
+ )
msg = Message()
- msg.add_string('fake-host-key')
+ msg.add_string("fake-host-key")
msg.add_mpint(69)
- msg.add_string('fake-sig')
+ msg.add_string("fake-sig")
msg.rewind()
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_REPLY, msg)
- H = b'AD1A9365A67B4496F05594AD1BF656E3CDA0851289A4C1AFF549FEAE50896DF4'
+ H = b"AD1A9365A67B4496F05594AD1BF656E3CDA0851289A4C1AFF549FEAE50896DF4"
self.assertEqual(self.K, transport._K)
self.assertEqual(H, hexlify(transport._H).upper())
- self.assertEqual((b'fake-host-key', b'fake-sig'), transport._verify)
+ self.assertEqual((b"fake-host-key", b"fake-sig"), transport._verify)
self.assertTrue(transport._activated)
def test_8_gex_sha256_old_client(self):
@@ -304,29 +377,35 @@ class KexTest (unittest.TestCase):
transport.server_mode = False
kex = KexGexSHA256(transport)
kex.start_kex(_test_old_style=True)
- x = b'1E00000800'
+ x = b"1E00000800"
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
- self.assertEqual((paramiko.kex_gex._MSG_KEXDH_GEX_GROUP,), transport._expect)
+ self.assertEqual(
+ (paramiko.kex_gex._MSG_KEXDH_GEX_GROUP,), transport._expect
+ )
msg = Message()
msg.add_mpint(FakeModulusPack.P)
msg.add_mpint(FakeModulusPack.G)
msg.rewind()
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_GROUP, msg)
- x = b'20000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D4'
+ x = (
+ b"20000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D4"
+ )
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
- self.assertEqual((paramiko.kex_gex._MSG_KEXDH_GEX_REPLY,), transport._expect)
+ self.assertEqual(
+ (paramiko.kex_gex._MSG_KEXDH_GEX_REPLY,), transport._expect
+ )
msg = Message()
- msg.add_string('fake-host-key')
+ msg.add_string("fake-host-key")
msg.add_mpint(69)
- msg.add_string('fake-sig')
+ msg.add_string("fake-sig")
msg.rewind()
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_REPLY, msg)
- H = b'518386608B15891AE5237DEE08DCADDE76A0BCEFCE7F6DB3AD66BC41D256DFE5'
+ H = b"518386608B15891AE5237DEE08DCADDE76A0BCEFCE7F6DB3AD66BC41D256DFE5"
self.assertEqual(self.K, transport._K)
self.assertEqual(H, hexlify(transport._H).upper())
- self.assertEqual((b'fake-host-key', b'fake-sig'), transport._verify)
+ self.assertEqual((b"fake-host-key", b"fake-sig"), transport._verify)
self.assertTrue(transport._activated)
def test_9_gex_sha256_server(self):
@@ -334,7 +413,13 @@ class KexTest (unittest.TestCase):
transport.server_mode = True
kex = KexGexSHA256(transport)
kex.start_kex()
- self.assertEqual((paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST, paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST_OLD), transport._expect)
+ self.assertEqual(
+ (
+ paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST,
+ paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST_OLD,
+ ),
+ transport._expect,
+ )
msg = Message()
msg.add_int(1024)
@@ -342,17 +427,25 @@ class KexTest (unittest.TestCase):
msg.add_int(4096)
msg.rewind()
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST, msg)
- x = b'1F0000008100FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF0000000102'
+ x = (
+ b"1F0000008100FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF0000000102"
+ )
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
- self.assertEqual((paramiko.kex_gex._MSG_KEXDH_GEX_INIT,), transport._expect)
+ self.assertEqual(
+ (paramiko.kex_gex._MSG_KEXDH_GEX_INIT,), transport._expect
+ )
msg = Message()
msg.add_mpint(12345)
msg.rewind()
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_INIT, msg)
- K = 67592995013596137876033460028393339951879041140378510871612128162185209509220726296697886624612526735888348020498716482757677848959420073720160491114319163078862905400020959196386947926388406687288901564192071077389283980347784184487280885335302632305026248574716290537036069329724382811853044654824945750581
- H = b'CCAC0497CF0ABA1DBF55E1A3995D17F4CC31824B0E8D95CDF8A06F169D050D80'
- x = b'210000000866616B652D6B6579000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D40000000866616B652D736967'
+ K = (
+ 67592995013596137876033460028393339951879041140378510871612128162185209509220726296697886624612526735888348020498716482757677848959420073720160491114319163078862905400020959196386947926388406687288901564192071077389283980347784184487280885335302632305026248574716290537036069329724382811853044654824945750581
+ )
+ H = b"CCAC0497CF0ABA1DBF55E1A3995D17F4CC31824B0E8D95CDF8A06F169D050D80"
+ x = (
+ b"210000000866616B652D6B6579000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D40000000866616B652D736967"
+ )
self.assertEqual(K, transport._K)
self.assertEqual(H, hexlify(transport._H).upper())
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
@@ -363,62 +456,88 @@ class KexTest (unittest.TestCase):
transport.server_mode = True
kex = KexGexSHA256(transport)
kex.start_kex()
- self.assertEqual((paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST, paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST_OLD), transport._expect)
+ self.assertEqual(
+ (
+ paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST,
+ paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST_OLD,
+ ),
+ transport._expect,
+ )
msg = Message()
msg.add_int(2048)
msg.rewind()
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_REQUEST_OLD, msg)
- x = b'1F0000008100FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF0000000102'
+ x = (
+ b"1F0000008100FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF0000000102"
+ )
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
- self.assertEqual((paramiko.kex_gex._MSG_KEXDH_GEX_INIT,), transport._expect)
+ self.assertEqual(
+ (paramiko.kex_gex._MSG_KEXDH_GEX_INIT,), transport._expect
+ )
msg = Message()
msg.add_mpint(12345)
msg.rewind()
kex.parse_next(paramiko.kex_gex._MSG_KEXDH_GEX_INIT, msg)
- K = 67592995013596137876033460028393339951879041140378510871612128162185209509220726296697886624612526735888348020498716482757677848959420073720160491114319163078862905400020959196386947926388406687288901564192071077389283980347784184487280885335302632305026248574716290537036069329724382811853044654824945750581
- H = b'3DDD2AD840AD095E397BA4D0573972DC60F6461FD38A187CACA6615A5BC8ADBB'
- x = b'210000000866616B652D6B6579000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D40000000866616B652D736967'
+ K = (
+ 67592995013596137876033460028393339951879041140378510871612128162185209509220726296697886624612526735888348020498716482757677848959420073720160491114319163078862905400020959196386947926388406687288901564192071077389283980347784184487280885335302632305026248574716290537036069329724382811853044654824945750581
+ )
+ H = b"3DDD2AD840AD095E397BA4D0573972DC60F6461FD38A187CACA6615A5BC8ADBB"
+ x = (
+ b"210000000866616B652D6B6579000000807E2DDB1743F3487D6545F04F1C8476092FB912B013626AB5BCEB764257D88BBA64243B9F348DF7B41B8C814A995E00299913503456983FFB9178D3CD79EB6D55522418A8ABF65375872E55938AB99A84A0B5FC8A1ECC66A7C3766E7E0F80B7CE2C9225FC2DD683F4764244B72963BBB383F529DCF0C5D17740B8A2ADBE9208D40000000866616B652D736967"
+ )
self.assertEqual(K, transport._K)
self.assertEqual(H, hexlify(transport._H).upper())
self.assertEqual(x, hexlify(transport._message.asbytes()).upper())
self.assertTrue(transport._activated)
def test_11_kex_nistp256_client(self):
- K = 91610929826364598472338906427792435253694642563583721654249504912114314269754
+ K = (
+ 91610929826364598472338906427792435253694642563583721654249504912114314269754
+ )
transport = FakeTransport()
transport.server_mode = False
kex = KexNistp256(transport)
kex.start_kex()
- self.assertEqual((paramiko.kex_ecdh_nist._MSG_KEXECDH_REPLY,), transport._expect)
+ self.assertEqual(
+ (paramiko.kex_ecdh_nist._MSG_KEXECDH_REPLY,), transport._expect
+ )
- #fake reply
+ # fake reply
msg = Message()
- msg.add_string('fake-host-key')
- Q_S = unhexlify("043ae159594ba062efa121480e9ef136203fa9ec6b6e1f8723a321c16e62b945f573f3b822258cbcd094b9fa1c125cbfe5f043280893e66863cc0cb4dccbe70210")
+ msg.add_string("fake-host-key")
+ Q_S = unhexlify(
+ "043ae159594ba062efa121480e9ef136203fa9ec6b6e1f8723a321c16e62b945f573f3b822258cbcd094b9fa1c125cbfe5f043280893e66863cc0cb4dccbe70210"
+ )
msg.add_string(Q_S)
- msg.add_string('fake-sig')
+ msg.add_string("fake-sig")
msg.rewind()
kex.parse_next(paramiko.kex_ecdh_nist._MSG_KEXECDH_REPLY, msg)
- H = b'BAF7CE243A836037EB5D2221420F35C02B9AB6C957FE3BDE3369307B9612570A'
+ H = b"BAF7CE243A836037EB5D2221420F35C02B9AB6C957FE3BDE3369307B9612570A"
self.assertEqual(K, kex.transport._K)
self.assertEqual(H, hexlify(transport._H).upper())
- self.assertEqual((b'fake-host-key', b'fake-sig'), transport._verify)
+ self.assertEqual((b"fake-host-key", b"fake-sig"), transport._verify)
self.assertTrue(transport._activated)
def test_12_kex_nistp256_server(self):
- K = 91610929826364598472338906427792435253694642563583721654249504912114314269754
+ K = (
+ 91610929826364598472338906427792435253694642563583721654249504912114314269754
+ )
transport = FakeTransport()
transport.server_mode = True
kex = KexNistp256(transport)
kex.start_kex()
- self.assertEqual((paramiko.kex_ecdh_nist._MSG_KEXECDH_INIT,), transport._expect)
+ self.assertEqual(
+ (paramiko.kex_ecdh_nist._MSG_KEXECDH_INIT,), transport._expect
+ )
- #fake init
- msg=Message()
- Q_C = unhexlify("043ae159594ba062efa121480e9ef136203fa9ec6b6e1f8723a321c16e62b945f573f3b822258cbcd094b9fa1c125cbfe5f043280893e66863cc0cb4dccbe70210")
- H = b'2EF4957AFD530DD3F05DBEABF68D724FACC060974DA9704F2AEE4C3DE861E7CA'
+ # fake init
+ msg = Message()
+ Q_C = unhexlify(
+ "043ae159594ba062efa121480e9ef136203fa9ec6b6e1f8723a321c16e62b945f573f3b822258cbcd094b9fa1c125cbfe5f043280893e66863cc0cb4dccbe70210"
+ )
+ H = b"2EF4957AFD530DD3F05DBEABF68D724FACC060974DA9704F2AEE4C3DE861E7CA"
msg.add_string(Q_C)
msg.rewind()
kex.parse_next(paramiko.kex_ecdh_nist._MSG_KEXECDH_INIT, msg)
diff --git a/tests/test_kex_gss.py b/tests/test_kex_gss.py
index af342a7c..afddee08 100644
--- a/tests/test_kex_gss.py
+++ b/tests/test_kex_gss.py
@@ -31,15 +31,17 @@ import unittest
import paramiko
+from .util import needs_gssapi
-class NullServer (paramiko.ServerInterface):
+
+class NullServer(paramiko.ServerInterface):
def get_allowed_auths(self, username):
- return 'gssapi-keyex'
+ return "gssapi-keyex"
- def check_auth_gssapi_keyex(self, username,
- gss_authenticated=paramiko.AUTH_FAILED,
- cc_file=None):
+ def check_auth_gssapi_keyex(
+ self, username, gss_authenticated=paramiko.AUTH_FAILED, cc_file=None
+ ):
if gss_authenticated == paramiko.AUTH_SUCCESSFUL:
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
@@ -52,12 +54,14 @@ class NullServer (paramiko.ServerInterface):
return paramiko.OPEN_SUCCEEDED
def check_channel_exec_request(self, channel, command):
- if command != 'yes':
+ if command != "yes":
return False
return True
+@needs_gssapi
class GSSKexTest(unittest.TestCase):
+
@staticmethod
def init(username, hostname):
global krb5_principal, targ_name
@@ -83,13 +87,13 @@ class GSSKexTest(unittest.TestCase):
def _run(self):
self.socks, addr = self.sockl.accept()
self.ts = paramiko.Transport(self.socks, gss_kex=True)
- host_key = paramiko.RSAKey.from_private_key_file('tests/test_rsa.key')
+ host_key = paramiko.RSAKey.from_private_key_file("tests/test_rsa.key")
self.ts.add_server_key(host_key)
self.ts.set_gss_host(targ_name)
try:
self.ts.load_server_moduli()
except:
- print ('(Failed to load moduli -- gex will be unsupported.)')
+ print("(Failed to load moduli -- gex will be unsupported.)")
server = NullServer()
self.ts.start_server(self.event, server)
@@ -99,14 +103,21 @@ class GSSKexTest(unittest.TestCase):
Diffie-Hellman Key Exchange and user authentication with the GSS-API
context created during key exchange.
"""
- host_key = paramiko.RSAKey.from_private_key_file('tests/test_rsa.key')
+ host_key = paramiko.RSAKey.from_private_key_file("tests/test_rsa.key")
public_host_key = paramiko.RSAKey(data=host_key.asbytes())
self.tc = paramiko.SSHClient()
- self.tc.get_host_keys().add('[%s]:%d' % (self.hostname, self.port),
- 'ssh-rsa', public_host_key)
- self.tc.connect(self.hostname, self.port, username=self.username,
- gss_auth=True, gss_kex=True, gss_host=gss_host)
+ self.tc.get_host_keys().add(
+ "[%s]:%d" % (self.hostname, self.port), "ssh-rsa", public_host_key
+ )
+ self.tc.connect(
+ self.hostname,
+ self.port,
+ username=self.username,
+ gss_auth=True,
+ gss_kex=True,
+ gss_host=gss_host,
+ )
self.event.wait(1.0)
self.assert_(self.event.is_set())
@@ -115,19 +126,19 @@ class GSSKexTest(unittest.TestCase):
self.assertEquals(True, self.ts.is_authenticated())
self.assertEquals(True, self.tc.get_transport().gss_kex_used)
- stdin, stdout, stderr = self.tc.exec_command('yes')
+ stdin, stdout, stderr = self.tc.exec_command("yes")
schan = self.ts.accept(1.0)
if rekey:
self.tc.get_transport().renegotiate_keys()
- schan.send('Hello there.\n')
- schan.send_stderr('This is on stderr.\n')
+ schan.send("Hello there.\n")
+ schan.send_stderr("This is on stderr.\n")
schan.close()
- self.assertEquals('Hello there.\n', stdout.readline())
- self.assertEquals('', stdout.readline())
- self.assertEquals('This is on stderr.\n', stderr.readline())
- self.assertEquals('', stderr.readline())
+ self.assertEquals("Hello there.\n", stdout.readline())
+ self.assertEquals("", stdout.readline())
+ self.assertEquals("This is on stderr.\n", stderr.readline())
+ self.assertEquals("", stderr.readline())
stdin.close()
stdout.close()
diff --git a/tests/test_message.py b/tests/test_message.py
index f18cae90..c292f4e6 100644
--- a/tests/test_message.py
+++ b/tests/test_message.py
@@ -21,24 +21,34 @@ Some unit tests for ssh protocol message blocks.
"""
import unittest
+
from paramiko.message import Message
from paramiko.common import byte_chr, zero_byte
-class MessageTest (unittest.TestCase):
+class MessageTest(unittest.TestCase):
- __a = b'\x00\x00\x00\x17\x07\x60\xe0\x90\x00\x00\x00\x01\x71\x00\x00\x00\x05\x68\x65\x6c\x6c\x6f\x00\x00\x03\xe8' + b'x' * 1000
- __b = b'\x01\x00\xf3\x00\x3f\x00\x00\x00\x10\x68\x75\x65\x79\x2c\x64\x65\x77\x65\x79\x2c\x6c\x6f\x75\x69\x65'
- __c = b'\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\xf5\xe4\xd3\xc2\xb1\x09\x00\x00\x00\x01\x11\x00\x00\x00\x07\x00\xf5\xe4\xd3\xc2\xb1\x09\x00\x00\x00\x06\x9a\x1b\x2c\x3d\x4e\xf7'
- __d = b'\x00\x00\x00\x05\xff\x00\x00\x00\x05\x11\x22\x33\x44\x55\xff\x00\x00\x00\x0a\x00\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x03\x63\x61\x74\x00\x00\x00\x03\x61\x2c\x62'
+ __a = (
+ b"\x00\x00\x00\x17\x07\x60\xe0\x90\x00\x00\x00\x01\x71\x00\x00\x00\x05\x68\x65\x6c\x6c\x6f\x00\x00\x03\xe8"
+ + b"x" * 1000
+ )
+ __b = (
+ b"\x01\x00\xf3\x00\x3f\x00\x00\x00\x10\x68\x75\x65\x79\x2c\x64\x65\x77\x65\x79\x2c\x6c\x6f\x75\x69\x65"
+ )
+ __c = (
+ b"\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\xf5\xe4\xd3\xc2\xb1\x09\x00\x00\x00\x01\x11\x00\x00\x00\x07\x00\xf5\xe4\xd3\xc2\xb1\x09\x00\x00\x00\x06\x9a\x1b\x2c\x3d\x4e\xf7"
+ )
+ __d = (
+ b"\x00\x00\x00\x05\xff\x00\x00\x00\x05\x11\x22\x33\x44\x55\xff\x00\x00\x00\x0a\x00\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x03\x63\x61\x74\x00\x00\x00\x03\x61\x2c\x62"
+ )
def test_1_encode(self):
msg = Message()
msg.add_int(23)
msg.add_int(123789456)
- msg.add_string('q')
- msg.add_string('hello')
- msg.add_string('x' * 1000)
+ msg.add_string("q")
+ msg.add_string("hello")
+ msg.add_string("x" * 1000)
self.assertEqual(msg.asbytes(), self.__a)
msg = Message()
@@ -47,7 +57,7 @@ class MessageTest (unittest.TestCase):
msg.add_byte(byte_chr(0xf3))
msg.add_bytes(zero_byte + byte_chr(0x3f))
- msg.add_list(['huey', 'dewey', 'louie'])
+ msg.add_list(["huey", "dewey", "louie"])
self.assertEqual(msg.asbytes(), self.__b)
msg = Message()
@@ -62,16 +72,16 @@ class MessageTest (unittest.TestCase):
msg = Message(self.__a)
self.assertEqual(msg.get_int(), 23)
self.assertEqual(msg.get_int(), 123789456)
- self.assertEqual(msg.get_text(), 'q')
- self.assertEqual(msg.get_text(), 'hello')
- self.assertEqual(msg.get_text(), 'x' * 1000)
+ self.assertEqual(msg.get_text(), "q")
+ self.assertEqual(msg.get_text(), "hello")
+ self.assertEqual(msg.get_text(), "x" * 1000)
msg = Message(self.__b)
self.assertEqual(msg.get_boolean(), True)
self.assertEqual(msg.get_boolean(), False)
self.assertEqual(msg.get_byte(), byte_chr(0xf3))
self.assertEqual(msg.get_bytes(2), zero_byte + byte_chr(0x3f))
- self.assertEqual(msg.get_list(), ['huey', 'dewey', 'louie'])
+ self.assertEqual(msg.get_list(), ["huey", "dewey", "louie"])
msg = Message(self.__c)
self.assertEqual(msg.get_int64(), 5)
@@ -86,8 +96,8 @@ class MessageTest (unittest.TestCase):
msg.add(0x1122334455)
msg.add(0xf00000000000000000)
msg.add(True)
- msg.add('cat')
- msg.add(['a', 'b'])
+ msg.add("cat")
+ msg.add(["a", "b"])
self.assertEqual(msg.asbytes(), self.__d)
def test_4_misc(self):
diff --git a/tests/test_packetizer.py b/tests/test_packetizer.py
index 02173292..dbe5993e 100644
--- a/tests/test_packetizer.py
+++ b/tests/test_packetizer.py
@@ -27,27 +27,29 @@ from hashlib import sha1
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import algorithms, Cipher, modes
-from tests.loop import LoopSocket
-
from paramiko import Message, Packetizer, util
from paramiko.common import byte_chr, zero_byte
+from .loop import LoopSocket
+
+
x55 = byte_chr(0x55)
x1f = byte_chr(0x1f)
-class PacketizerTest (unittest.TestCase):
+
+class PacketizerTest(unittest.TestCase):
def test_1_write(self):
rsock = LoopSocket()
wsock = LoopSocket()
rsock.link(wsock)
p = Packetizer(wsock)
- p.set_log(util.get_logger('paramiko.transport'))
+ p.set_log(util.get_logger("paramiko.transport"))
p.set_hexdump(True)
encryptor = Cipher(
algorithms.AES(zero_byte * 16),
modes.CBC(x55 * 16),
- backend=default_backend()
+ backend=default_backend(),
).encryptor()
p.set_outbound_cipher(encryptor, 16, sha1, 12, x1f * 20)
@@ -62,22 +64,27 @@ class PacketizerTest (unittest.TestCase):
data = rsock.recv(100)
# 32 + 12 bytes of MAC = 44
self.assertEqual(44, len(data))
- self.assertEqual(b'\x43\x91\x97\xbd\x5b\x50\xac\x25\x87\xc2\xc4\x6b\xc7\xe9\x38\xc0', data[:16])
+ self.assertEqual(
+ b"\x43\x91\x97\xbd\x5b\x50\xac\x25\x87\xc2\xc4\x6b\xc7\xe9\x38\xc0",
+ data[:16],
+ )
def test_2_read(self):
rsock = LoopSocket()
wsock = LoopSocket()
rsock.link(wsock)
p = Packetizer(rsock)
- p.set_log(util.get_logger('paramiko.transport'))
+ p.set_log(util.get_logger("paramiko.transport"))
p.set_hexdump(True)
decryptor = Cipher(
algorithms.AES(zero_byte * 16),
modes.CBC(x55 * 16),
- backend=default_backend()
+ backend=default_backend(),
).decryptor()
p.set_inbound_cipher(decryptor, 16, sha1, 12, x1f * 20)
- wsock.send(b'\x43\x91\x97\xbd\x5b\x50\xac\x25\x87\xc2\xc4\x6b\xc7\xe9\x38\xc0\x90\xd2\x16\x56\x0d\x71\x73\x61\x38\x7c\x4c\x3d\xfb\x97\x7d\xe2\x6e\x03\xb1\xa0\xc2\x1c\xd6\x41\x41\x4c\xb4\x59')
+ wsock.send(
+ b"\x43\x91\x97\xbd\x5b\x50\xac\x25\x87\xc2\xc4\x6b\xc7\xe9\x38\xc0\x90\xd2\x16\x56\x0d\x71\x73\x61\x38\x7c\x4c\x3d\xfb\x97\x7d\xe2\x6e\x03\xb1\xa0\xc2\x1c\xd6\x41\x41\x4c\xb4\x59"
+ )
cmd, m = p.read_message()
self.assertEqual(100, cmd)
self.assertEqual(100, m.get_int())
@@ -85,18 +92,18 @@ class PacketizerTest (unittest.TestCase):
self.assertEqual(900, m.get_int())
def test_3_closed(self):
- if sys.platform.startswith("win"): # no SIGALRM on windows
+ if sys.platform.startswith("win"): # no SIGALRM on windows
return
rsock = LoopSocket()
wsock = LoopSocket()
rsock.link(wsock)
p = Packetizer(wsock)
- p.set_log(util.get_logger('paramiko.transport'))
+ p.set_log(util.get_logger("paramiko.transport"))
p.set_hexdump(True)
encryptor = Cipher(
algorithms.AES(zero_byte * 16),
modes.CBC(x55 * 16),
- backend=default_backend()
+ backend=default_backend(),
).encryptor()
p.set_outbound_cipher(encryptor, 16, sha1, 12, x1f * 20)
@@ -114,14 +121,17 @@ class PacketizerTest (unittest.TestCase):
import signal
class TimeoutError(Exception):
+
def __init__(self, error_message):
- if hasattr(errno, 'ETIME'):
+ if hasattr(errno, "ETIME"):
self.message = os.sterror(errno.ETIME)
else:
self.messaage = error_message
- def timeout(seconds=1, error_message='Timer expired'):
+ def timeout(seconds=1, error_message="Timer expired"):
+
def decorator(func):
+
def _handle_timeout(signum, frame):
raise TimeoutError(error_message)
@@ -137,5 +147,6 @@ class PacketizerTest (unittest.TestCase):
return wraps(func)(wrapper)
return decorator
+
send = timeout()(p.send_message)
self.assertRaises(EOFError, send, m)
diff --git a/tests/test_pkey.py b/tests/test_pkey.py
index 42d8e6bb..4bbfaba1 100644
--- a/tests/test_pkey.py
+++ b/tests/test_pkey.py
@@ -30,21 +30,34 @@ import base64
from paramiko import RSAKey, DSSKey, ECDSAKey, Ed25519Key, Message, util
from paramiko.py3compat import StringIO, byte_chr, b, bytes, PY2
-from tests.util import test_path
+from .util import _support
+
# from openssh's ssh-keygen
-PUB_RSA = 'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA049W6geFpmsljTwfvI1UmKWWJPNFI74+vNKTk4dmzkQY2yAMs6FhlvhlI8ysU4oj71ZsRYMecHbBbxdN79+JRFVYTKaLqjwGENeTd+yv4q+V2PvZv3fLnzApI3l7EJCqhWwJUHJ1jAkZzqDx0tyOL4uoZpww3nmE0kb3y21tH4c='
-PUB_DSS = 'ssh-dss AAAAB3NzaC1kc3MAAACBAOeBpgNnfRzr/twmAQRu2XwWAp3CFtrVnug6s6fgwj/oLjYbVtjAy6pl/h0EKCWx2rf1IetyNsTxWrniA9I6HeDj65X1FyDkg6g8tvCnaNB8Xp/UUhuzHuGsMIipRxBxw9LF608EqZcj1E3ytktoW5B5OcjrkEoz3xG7C+rpIjYvAAAAFQDwz4UnmsGiSNu5iqjn3uTzwUpshwAAAIEAkxfFeY8P2wZpDjX0MimZl5wkoFQDL25cPzGBuB4OnB8NoUk/yjAHIIpEShw8V+LzouMK5CTJQo5+Ngw3qIch/WgRmMHy4kBq1SsXMjQCte1So6HBMvBPIW5SiMTmjCfZZiw4AYHK+B/JaOwaG9yRg2Ejg4Ok10+XFDxlqZo8Y+wAAACARmR7CCPjodxASvRbIyzaVpZoJ/Z6x7dAumV+ysrV1BVYd0lYukmnjO1kKBWApqpH1ve9XDQYN8zgxM4b16L21kpoWQnZtXrY3GZ4/it9kUgyB7+NwacIBlXa8cMDL7Q/69o0d54U0X/NeX5QxuYR6OMJlrkQB7oiW/P/1mwjQgE='
-PUB_ECDSA_256 = 'ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJSPZm3ZWkvk/Zx8WP+fZRZ5/NBBHnGQwR6uIC6XHGPDIHuWUzIjAwA0bzqkOUffEsbLe+uQgKl5kbc/L8KA/eo='
-PUB_ECDSA_384 = 'ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBBbGibQLW9AAZiGN2hEQxWYYoFaWKwN3PKSaDJSMqmIn1Z9sgRUuw8Y/w502OGvXL/wFk0i2z50l3pWZjD7gfMH7gX5TUiCzwrQkS+Hn1U2S9aF5WJp0NcIzYxXw2r4M2A=='
-PUB_ECDSA_521 = 'ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACaOaFLZGuxa5AW16qj6VLypFbLrEWrt9AZUloCMefxO8bNLjK/O5g0rAVasar1TnyHE9qj4NwzANZASWjQNbc4MAG8vzqezFwLIn/kNyNTsXNfqEko9OgHZknlj2Z79dwTJcRAL4QLcT5aND0EHZLB2fAUDXiWIb2j4rg1mwPlBMiBXA=='
-
-FINGER_RSA = '1024 60:73:38:44:cb:51:86:65:7f:de:da:a2:2b:5a:57:d5'
-FINGER_DSS = '1024 44:78:f0:b9:a2:3c:c5:18:20:09:ff:75:5b:c1:d2:6c'
-FINGER_ECDSA_256 = '256 25:19:eb:55:e6:a1:47:ff:4f:38:d2:75:6f:a5:d5:60'
-FINGER_ECDSA_384 = '384 c1:8d:a0:59:09:47:41:8e:a8:a6:07:01:29:23:b4:65'
-FINGER_ECDSA_521 = '521 44:58:22:52:12:33:16:0e:ce:0e:be:2c:7c:7e:cc:1e'
-SIGNED_RSA = '20:d7:8a:31:21:cb:f7:92:12:f2:a4:89:37:f5:78:af:e6:16:b6:25:b9:97:3d:a2:cd:5f:ca:20:21:73:4c:ad:34:73:8f:20:77:28:e2:94:15:08:d8:91:40:7a:85:83:bf:18:37:95:dc:54:1a:9b:88:29:6c:73:ca:38:b4:04:f1:56:b9:f2:42:9d:52:1b:29:29:b4:4f:fd:c9:2d:af:47:d2:40:76:30:f3:63:45:0c:d9:1d:43:86:0f:1c:70:e2:93:12:34:f3:ac:c5:0a:2f:14:50:66:59:f1:88:ee:c1:4a:e9:d1:9c:4e:46:f0:0e:47:6f:38:74:f1:44:a8'
+PUB_RSA = (
+ "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA049W6geFpmsljTwfvI1UmKWWJPNFI74+vNKTk4dmzkQY2yAMs6FhlvhlI8ysU4oj71ZsRYMecHbBbxdN79+JRFVYTKaLqjwGENeTd+yv4q+V2PvZv3fLnzApI3l7EJCqhWwJUHJ1jAkZzqDx0tyOL4uoZpww3nmE0kb3y21tH4c="
+)
+PUB_DSS = (
+ "ssh-dss AAAAB3NzaC1kc3MAAACBAOeBpgNnfRzr/twmAQRu2XwWAp3CFtrVnug6s6fgwj/oLjYbVtjAy6pl/h0EKCWx2rf1IetyNsTxWrniA9I6HeDj65X1FyDkg6g8tvCnaNB8Xp/UUhuzHuGsMIipRxBxw9LF608EqZcj1E3ytktoW5B5OcjrkEoz3xG7C+rpIjYvAAAAFQDwz4UnmsGiSNu5iqjn3uTzwUpshwAAAIEAkxfFeY8P2wZpDjX0MimZl5wkoFQDL25cPzGBuB4OnB8NoUk/yjAHIIpEShw8V+LzouMK5CTJQo5+Ngw3qIch/WgRmMHy4kBq1SsXMjQCte1So6HBMvBPIW5SiMTmjCfZZiw4AYHK+B/JaOwaG9yRg2Ejg4Ok10+XFDxlqZo8Y+wAAACARmR7CCPjodxASvRbIyzaVpZoJ/Z6x7dAumV+ysrV1BVYd0lYukmnjO1kKBWApqpH1ve9XDQYN8zgxM4b16L21kpoWQnZtXrY3GZ4/it9kUgyB7+NwacIBlXa8cMDL7Q/69o0d54U0X/NeX5QxuYR6OMJlrkQB7oiW/P/1mwjQgE="
+)
+PUB_ECDSA_256 = (
+ "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJSPZm3ZWkvk/Zx8WP+fZRZ5/NBBHnGQwR6uIC6XHGPDIHuWUzIjAwA0bzqkOUffEsbLe+uQgKl5kbc/L8KA/eo="
+)
+PUB_ECDSA_384 = (
+ "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBBbGibQLW9AAZiGN2hEQxWYYoFaWKwN3PKSaDJSMqmIn1Z9sgRUuw8Y/w502OGvXL/wFk0i2z50l3pWZjD7gfMH7gX5TUiCzwrQkS+Hn1U2S9aF5WJp0NcIzYxXw2r4M2A=="
+)
+PUB_ECDSA_521 = (
+ "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACaOaFLZGuxa5AW16qj6VLypFbLrEWrt9AZUloCMefxO8bNLjK/O5g0rAVasar1TnyHE9qj4NwzANZASWjQNbc4MAG8vzqezFwLIn/kNyNTsXNfqEko9OgHZknlj2Z79dwTJcRAL4QLcT5aND0EHZLB2fAUDXiWIb2j4rg1mwPlBMiBXA=="
+)
+
+FINGER_RSA = "1024 60:73:38:44:cb:51:86:65:7f:de:da:a2:2b:5a:57:d5"
+FINGER_DSS = "1024 44:78:f0:b9:a2:3c:c5:18:20:09:ff:75:5b:c1:d2:6c"
+FINGER_ECDSA_256 = "256 25:19:eb:55:e6:a1:47:ff:4f:38:d2:75:6f:a5:d5:60"
+FINGER_ECDSA_384 = "384 c1:8d:a0:59:09:47:41:8e:a8:a6:07:01:29:23:b4:65"
+FINGER_ECDSA_521 = "521 44:58:22:52:12:33:16:0e:ce:0e:be:2c:7c:7e:cc:1e"
+SIGNED_RSA = (
+ "20:d7:8a:31:21:cb:f7:92:12:f2:a4:89:37:f5:78:af:e6:16:b6:25:b9:97:3d:a2:cd:5f:ca:20:21:73:4c:ad:34:73:8f:20:77:28:e2:94:15:08:d8:91:40:7a:85:83:bf:18:37:95:dc:54:1a:9b:88:29:6c:73:ca:38:b4:04:f1:56:b9:f2:42:9d:52:1b:29:29:b4:4f:fd:c9:2d:af:47:d2:40:76:30:f3:63:45:0c:d9:1d:43:86:0f:1c:70:e2:93:12:34:f3:ac:c5:0a:2f:14:50:66:59:f1:88:ee:c1:4a:e9:d1:9c:4e:46:f0:0e:47:6f:38:74:f1:44:a8"
+)
RSA_PRIVATE_OUT = """\
-----BEGIN RSA PRIVATE KEY-----
@@ -106,10 +119,14 @@ L4QLcT5aND0EHZLB2fAUDXiWIb2j4rg1mwPlBMiBXA==
-----END EC PRIVATE KEY-----
"""
-x1234 = b'\x01\x02\x03\x04'
+x1234 = b"\x01\x02\x03\x04"
-TEST_KEY_BYTESTR_2 = '\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x01#\x00\x00\x00\x81\x00\xd3\x8fV\xea\x07\x85\xa6k%\x8d<\x1f\xbc\x8dT\x98\xa5\x96$\xf3E#\xbe>\xbc\xd2\x93\x93\x87f\xceD\x18\xdb \x0c\xb3\xa1a\x96\xf8e#\xcc\xacS\x8a#\xefVlE\x83\x1epv\xc1o\x17M\xef\xdf\x89DUXL\xa6\x8b\xaa<\x06\x10\xd7\x93w\xec\xaf\xe2\xaf\x95\xd8\xfb\xd9\xbfw\xcb\x9f0)#y{\x10\x90\xaa\x85l\tPru\x8c\t\x19\xce\xa0\xf1\xd2\xdc\x8e/\x8b\xa8f\x9c0\xdey\x84\xd2F\xf7\xcbmm\x1f\x87'
-TEST_KEY_BYTESTR_3 = '\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x01#\x00\x00\x00\x00ӏV\x07k%<\x1fT$E#>ғfD\x18 \x0cae#̬S#VlE\x1epvo\x17M߉DUXL<\x06\x10דw\u2bd5ٿw˟0)#y{\x10l\tPru\t\x19Π\u070e/f0yFmm\x1f'
+TEST_KEY_BYTESTR_2 = (
+ "\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x01#\x00\x00\x00\x81\x00\xd3\x8fV\xea\x07\x85\xa6k%\x8d<\x1f\xbc\x8dT\x98\xa5\x96$\xf3E#\xbe>\xbc\xd2\x93\x93\x87f\xceD\x18\xdb \x0c\xb3\xa1a\x96\xf8e#\xcc\xacS\x8a#\xefVlE\x83\x1epv\xc1o\x17M\xef\xdf\x89DUXL\xa6\x8b\xaa<\x06\x10\xd7\x93w\xec\xaf\xe2\xaf\x95\xd8\xfb\xd9\xbfw\xcb\x9f0)#y{\x10\x90\xaa\x85l\tPru\x8c\t\x19\xce\xa0\xf1\xd2\xdc\x8e/\x8b\xa8f\x9c0\xdey\x84\xd2F\xf7\xcbmm\x1f\x87"
+)
+TEST_KEY_BYTESTR_3 = (
+ "\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x01#\x00\x00\x00\x00ӏV\x07k%<\x1fT$E#>ғfD\x18 \x0cae#̬S#VlE\x1epvo\x17M߉DUXL<\x06\x10דw\u2bd5ٿw˟0)#y{\x10l\tPru\t\x19Π\u070e/f0yFmm\x1f"
+)
class KeyTest(unittest.TestCase):
@@ -126,21 +143,22 @@ class KeyTest(unittest.TestCase):
"""
with open(keyfile, "r") as fh:
self.assertEqual(
- fh.readline()[:-1],
- "-----BEGIN RSA PRIVATE KEY-----"
+ fh.readline()[:-1], "-----BEGIN RSA PRIVATE KEY-----"
)
self.assertEqual(fh.readline()[:-1], "Proc-Type: 4,ENCRYPTED")
self.assertEqual(fh.readline()[0:10], "DEK-Info: ")
def test_1_generate_key_bytes(self):
- key = util.generate_key_bytes(md5, x1234, 'happy birthday', 30)
- exp = b'\x61\xE1\xF2\x72\xF4\xC1\xC4\x56\x15\x86\xBD\x32\x24\x98\xC0\xE9\x24\x67\x27\x80\xF4\x7B\xB3\x7D\xDA\x7D\x54\x01\x9E\x64'
+ key = util.generate_key_bytes(md5, x1234, "happy birthday", 30)
+ exp = (
+ b"\x61\xE1\xF2\x72\xF4\xC1\xC4\x56\x15\x86\xBD\x32\x24\x98\xC0\xE9\x24\x67\x27\x80\xF4\x7B\xB3\x7D\xDA\x7D\x54\x01\x9E\x64"
+ )
self.assertEqual(exp, key)
def test_2_load_rsa(self):
- key = RSAKey.from_private_key_file(test_path('test_rsa.key'))
- self.assertEqual('ssh-rsa', key.get_name())
- exp_rsa = b(FINGER_RSA.split()[1].replace(':', ''))
+ key = RSAKey.from_private_key_file(_support("test_rsa.key"))
+ self.assertEqual("ssh-rsa", key.get_name())
+ exp_rsa = b(FINGER_RSA.split()[1].replace(":", ""))
my_rsa = hexlify(key.get_fingerprint())
self.assertEqual(exp_rsa, my_rsa)
self.assertEqual(PUB_RSA.split()[1], key.get_base64())
@@ -154,18 +172,20 @@ class KeyTest(unittest.TestCase):
self.assertEqual(key, key2)
def test_3_load_rsa_password(self):
- key = RSAKey.from_private_key_file(test_path('test_rsa_password.key'), 'television')
- self.assertEqual('ssh-rsa', key.get_name())
- exp_rsa = b(FINGER_RSA.split()[1].replace(':', ''))
+ key = RSAKey.from_private_key_file(
+ _support("test_rsa_password.key"), "television"
+ )
+ self.assertEqual("ssh-rsa", key.get_name())
+ exp_rsa = b(FINGER_RSA.split()[1].replace(":", ""))
my_rsa = hexlify(key.get_fingerprint())
self.assertEqual(exp_rsa, my_rsa)
self.assertEqual(PUB_RSA.split()[1], key.get_base64())
self.assertEqual(1024, key.get_bits())
def test_4_load_dss(self):
- key = DSSKey.from_private_key_file(test_path('test_dss.key'))
- self.assertEqual('ssh-dss', key.get_name())
- exp_dss = b(FINGER_DSS.split()[1].replace(':', ''))
+ key = DSSKey.from_private_key_file(_support("test_dss.key"))
+ self.assertEqual("ssh-dss", key.get_name())
+ exp_dss = b(FINGER_DSS.split()[1].replace(":", ""))
my_dss = hexlify(key.get_fingerprint())
self.assertEqual(exp_dss, my_dss)
self.assertEqual(PUB_DSS.split()[1], key.get_base64())
@@ -179,9 +199,11 @@ class KeyTest(unittest.TestCase):
self.assertEqual(key, key2)
def test_5_load_dss_password(self):
- key = DSSKey.from_private_key_file(test_path('test_dss_password.key'), 'television')
- self.assertEqual('ssh-dss', key.get_name())
- exp_dss = b(FINGER_DSS.split()[1].replace(':', ''))
+ key = DSSKey.from_private_key_file(
+ _support("test_dss_password.key"), "television"
+ )
+ self.assertEqual("ssh-dss", key.get_name())
+ exp_dss = b(FINGER_DSS.split()[1].replace(":", ""))
my_dss = hexlify(key.get_fingerprint())
self.assertEqual(exp_dss, my_dss)
self.assertEqual(PUB_DSS.split()[1], key.get_base64())
@@ -189,7 +211,7 @@ class KeyTest(unittest.TestCase):
def test_6_compare_rsa(self):
# verify that the private & public keys compare equal
- key = RSAKey.from_private_key_file(test_path('test_rsa.key'))
+ key = RSAKey.from_private_key_file(_support("test_rsa.key"))
self.assertEqual(key, key)
pub = RSAKey(data=key.asbytes())
self.assertTrue(key.can_sign())
@@ -198,7 +220,7 @@ class KeyTest(unittest.TestCase):
def test_7_compare_dss(self):
# verify that the private & public keys compare equal
- key = DSSKey.from_private_key_file(test_path('test_dss.key'))
+ key = DSSKey.from_private_key_file(_support("test_dss.key"))
self.assertEqual(key, key)
pub = DSSKey(data=key.asbytes())
self.assertTrue(key.can_sign())
@@ -207,77 +229,79 @@ class KeyTest(unittest.TestCase):
def test_8_sign_rsa(self):
# verify that the rsa private key can sign and verify
- key = RSAKey.from_private_key_file(test_path('test_rsa.key'))
- msg = key.sign_ssh_data(b'ice weasels')
+ key = RSAKey.from_private_key_file(_support("test_rsa.key"))
+ msg = key.sign_ssh_data(b"ice weasels")
self.assertTrue(type(msg) is Message)
msg.rewind()
- self.assertEqual('ssh-rsa', msg.get_text())
- sig = bytes().join([byte_chr(int(x, 16)) for x in SIGNED_RSA.split(':')])
+ self.assertEqual("ssh-rsa", msg.get_text())
+ sig = bytes().join(
+ [byte_chr(int(x, 16)) for x in SIGNED_RSA.split(":")]
+ )
self.assertEqual(sig, msg.get_binary())
msg.rewind()
pub = RSAKey(data=key.asbytes())
- self.assertTrue(pub.verify_ssh_sig(b'ice weasels', msg))
+ self.assertTrue(pub.verify_ssh_sig(b"ice weasels", msg))
def test_9_sign_dss(self):
# verify that the dss private key can sign and verify
- key = DSSKey.from_private_key_file(test_path('test_dss.key'))
- msg = key.sign_ssh_data(b'ice weasels')
+ key = DSSKey.from_private_key_file(_support("test_dss.key"))
+ msg = key.sign_ssh_data(b"ice weasels")
self.assertTrue(type(msg) is Message)
msg.rewind()
- self.assertEqual('ssh-dss', msg.get_text())
+ self.assertEqual("ssh-dss", msg.get_text())
# can't do the same test as we do for RSA, because DSS signatures
# are usually different each time. but we can test verification
# anyway so it's ok.
self.assertEqual(40, len(msg.get_binary()))
msg.rewind()
pub = DSSKey(data=key.asbytes())
- self.assertTrue(pub.verify_ssh_sig(b'ice weasels', msg))
+ self.assertTrue(pub.verify_ssh_sig(b"ice weasels", msg))
def test_A_generate_rsa(self):
key = RSAKey.generate(1024)
- msg = key.sign_ssh_data(b'jerri blank')
+ msg = key.sign_ssh_data(b"jerri blank")
msg.rewind()
- self.assertTrue(key.verify_ssh_sig(b'jerri blank', msg))
+ self.assertTrue(key.verify_ssh_sig(b"jerri blank", msg))
def test_B_generate_dss(self):
key = DSSKey.generate(1024)
- msg = key.sign_ssh_data(b'jerri blank')
+ msg = key.sign_ssh_data(b"jerri blank")
msg.rewind()
- self.assertTrue(key.verify_ssh_sig(b'jerri blank', msg))
+ self.assertTrue(key.verify_ssh_sig(b"jerri blank", msg))
def test_C_generate_ecdsa(self):
key = ECDSAKey.generate()
- msg = key.sign_ssh_data(b'jerri blank')
+ msg = key.sign_ssh_data(b"jerri blank")
msg.rewind()
- self.assertTrue(key.verify_ssh_sig(b'jerri blank', msg))
+ self.assertTrue(key.verify_ssh_sig(b"jerri blank", msg))
self.assertEqual(key.get_bits(), 256)
- self.assertEqual(key.get_name(), 'ecdsa-sha2-nistp256')
+ self.assertEqual(key.get_name(), "ecdsa-sha2-nistp256")
key = ECDSAKey.generate(bits=256)
- msg = key.sign_ssh_data(b'jerri blank')
+ msg = key.sign_ssh_data(b"jerri blank")
msg.rewind()
- self.assertTrue(key.verify_ssh_sig(b'jerri blank', msg))
+ self.assertTrue(key.verify_ssh_sig(b"jerri blank", msg))
self.assertEqual(key.get_bits(), 256)
- self.assertEqual(key.get_name(), 'ecdsa-sha2-nistp256')
+ self.assertEqual(key.get_name(), "ecdsa-sha2-nistp256")
key = ECDSAKey.generate(bits=384)
- msg = key.sign_ssh_data(b'jerri blank')
+ msg = key.sign_ssh_data(b"jerri blank")
msg.rewind()
- self.assertTrue(key.verify_ssh_sig(b'jerri blank', msg))
+ self.assertTrue(key.verify_ssh_sig(b"jerri blank", msg))
self.assertEqual(key.get_bits(), 384)
- self.assertEqual(key.get_name(), 'ecdsa-sha2-nistp384')
+ self.assertEqual(key.get_name(), "ecdsa-sha2-nistp384")
key = ECDSAKey.generate(bits=521)
- msg = key.sign_ssh_data(b'jerri blank')
+ msg = key.sign_ssh_data(b"jerri blank")
msg.rewind()
- self.assertTrue(key.verify_ssh_sig(b'jerri blank', msg))
+ self.assertTrue(key.verify_ssh_sig(b"jerri blank", msg))
self.assertEqual(key.get_bits(), 521)
- self.assertEqual(key.get_name(), 'ecdsa-sha2-nistp521')
+ self.assertEqual(key.get_name(), "ecdsa-sha2-nistp521")
def test_10_load_ecdsa_256(self):
- key = ECDSAKey.from_private_key_file(test_path('test_ecdsa_256.key'))
- self.assertEqual('ecdsa-sha2-nistp256', key.get_name())
- exp_ecdsa = b(FINGER_ECDSA_256.split()[1].replace(':', ''))
+ key = ECDSAKey.from_private_key_file(_support("test_ecdsa_256.key"))
+ self.assertEqual("ecdsa-sha2-nistp256", key.get_name())
+ exp_ecdsa = b(FINGER_ECDSA_256.split()[1].replace(":", ""))
my_ecdsa = hexlify(key.get_fingerprint())
self.assertEqual(exp_ecdsa, my_ecdsa)
self.assertEqual(PUB_ECDSA_256.split()[1], key.get_base64())
@@ -291,9 +315,11 @@ class KeyTest(unittest.TestCase):
self.assertEqual(key, key2)
def test_11_load_ecdsa_password_256(self):
- key = ECDSAKey.from_private_key_file(test_path('test_ecdsa_password_256.key'), b'television')
- self.assertEqual('ecdsa-sha2-nistp256', key.get_name())
- exp_ecdsa = b(FINGER_ECDSA_256.split()[1].replace(':', ''))
+ key = ECDSAKey.from_private_key_file(
+ _support("test_ecdsa_password_256.key"), b"television"
+ )
+ self.assertEqual("ecdsa-sha2-nistp256", key.get_name())
+ exp_ecdsa = b(FINGER_ECDSA_256.split()[1].replace(":", ""))
my_ecdsa = hexlify(key.get_fingerprint())
self.assertEqual(exp_ecdsa, my_ecdsa)
self.assertEqual(PUB_ECDSA_256.split()[1], key.get_base64())
@@ -301,7 +327,7 @@ class KeyTest(unittest.TestCase):
def test_12_compare_ecdsa_256(self):
# verify that the private & public keys compare equal
- key = ECDSAKey.from_private_key_file(test_path('test_ecdsa_256.key'))
+ key = ECDSAKey.from_private_key_file(_support("test_ecdsa_256.key"))
self.assertEqual(key, key)
pub = ECDSAKey(data=key.asbytes())
self.assertTrue(key.can_sign())
@@ -310,11 +336,11 @@ class KeyTest(unittest.TestCase):
def test_13_sign_ecdsa_256(self):
# verify that the rsa private key can sign and verify
- key = ECDSAKey.from_private_key_file(test_path('test_ecdsa_256.key'))
- msg = key.sign_ssh_data(b'ice weasels')
+ key = ECDSAKey.from_private_key_file(_support("test_ecdsa_256.key"))
+ msg = key.sign_ssh_data(b"ice weasels")
self.assertTrue(type(msg) is Message)
msg.rewind()
- self.assertEqual('ecdsa-sha2-nistp256', msg.get_text())
+ self.assertEqual("ecdsa-sha2-nistp256", msg.get_text())
# ECDSA signatures, like DSS signatures, tend to be different
# each time, so we can't compare against a "known correct"
# signature.
@@ -322,12 +348,12 @@ class KeyTest(unittest.TestCase):
msg.rewind()
pub = ECDSAKey(data=key.asbytes())
- self.assertTrue(pub.verify_ssh_sig(b'ice weasels', msg))
+ self.assertTrue(pub.verify_ssh_sig(b"ice weasels", msg))
def test_14_load_ecdsa_384(self):
- key = ECDSAKey.from_private_key_file(test_path('test_ecdsa_384.key'))
- self.assertEqual('ecdsa-sha2-nistp384', key.get_name())
- exp_ecdsa = b(FINGER_ECDSA_384.split()[1].replace(':', ''))
+ key = ECDSAKey.from_private_key_file(_support("test_ecdsa_384.key"))
+ self.assertEqual("ecdsa-sha2-nistp384", key.get_name())
+ exp_ecdsa = b(FINGER_ECDSA_384.split()[1].replace(":", ""))
my_ecdsa = hexlify(key.get_fingerprint())
self.assertEqual(exp_ecdsa, my_ecdsa)
self.assertEqual(PUB_ECDSA_384.split()[1], key.get_base64())
@@ -341,9 +367,11 @@ class KeyTest(unittest.TestCase):
self.assertEqual(key, key2)
def test_15_load_ecdsa_password_384(self):
- key = ECDSAKey.from_private_key_file(test_path('test_ecdsa_password_384.key'), b'television')
- self.assertEqual('ecdsa-sha2-nistp384', key.get_name())
- exp_ecdsa = b(FINGER_ECDSA_384.split()[1].replace(':', ''))
+ key = ECDSAKey.from_private_key_file(
+ _support("test_ecdsa_password_384.key"), b"television"
+ )
+ self.assertEqual("ecdsa-sha2-nistp384", key.get_name())
+ exp_ecdsa = b(FINGER_ECDSA_384.split()[1].replace(":", ""))
my_ecdsa = hexlify(key.get_fingerprint())
self.assertEqual(exp_ecdsa, my_ecdsa)
self.assertEqual(PUB_ECDSA_384.split()[1], key.get_base64())
@@ -351,7 +379,7 @@ class KeyTest(unittest.TestCase):
def test_16_compare_ecdsa_384(self):
# verify that the private & public keys compare equal
- key = ECDSAKey.from_private_key_file(test_path('test_ecdsa_384.key'))
+ key = ECDSAKey.from_private_key_file(_support("test_ecdsa_384.key"))
self.assertEqual(key, key)
pub = ECDSAKey(data=key.asbytes())
self.assertTrue(key.can_sign())
@@ -360,11 +388,11 @@ class KeyTest(unittest.TestCase):
def test_17_sign_ecdsa_384(self):
# verify that the rsa private key can sign and verify
- key = ECDSAKey.from_private_key_file(test_path('test_ecdsa_384.key'))
- msg = key.sign_ssh_data(b'ice weasels')
+ key = ECDSAKey.from_private_key_file(_support("test_ecdsa_384.key"))
+ msg = key.sign_ssh_data(b"ice weasels")
self.assertTrue(type(msg) is Message)
msg.rewind()
- self.assertEqual('ecdsa-sha2-nistp384', msg.get_text())
+ self.assertEqual("ecdsa-sha2-nistp384", msg.get_text())
# ECDSA signatures, like DSS signatures, tend to be different
# each time, so we can't compare against a "known correct"
# signature.
@@ -372,12 +400,12 @@ class KeyTest(unittest.TestCase):
msg.rewind()
pub = ECDSAKey(data=key.asbytes())
- self.assertTrue(pub.verify_ssh_sig(b'ice weasels', msg))
+ self.assertTrue(pub.verify_ssh_sig(b"ice weasels", msg))
def test_18_load_ecdsa_521(self):
- key = ECDSAKey.from_private_key_file(test_path('test_ecdsa_521.key'))
- self.assertEqual('ecdsa-sha2-nistp521', key.get_name())
- exp_ecdsa = b(FINGER_ECDSA_521.split()[1].replace(':', ''))
+ key = ECDSAKey.from_private_key_file(_support("test_ecdsa_521.key"))
+ self.assertEqual("ecdsa-sha2-nistp521", key.get_name())
+ exp_ecdsa = b(FINGER_ECDSA_521.split()[1].replace(":", ""))
my_ecdsa = hexlify(key.get_fingerprint())
self.assertEqual(exp_ecdsa, my_ecdsa)
self.assertEqual(PUB_ECDSA_521.split()[1], key.get_base64())
@@ -394,9 +422,11 @@ class KeyTest(unittest.TestCase):
self.assertEqual(key, key2)
def test_19_load_ecdsa_password_521(self):
- key = ECDSAKey.from_private_key_file(test_path('test_ecdsa_password_521.key'), b'television')
- self.assertEqual('ecdsa-sha2-nistp521', key.get_name())
- exp_ecdsa = b(FINGER_ECDSA_521.split()[1].replace(':', ''))
+ key = ECDSAKey.from_private_key_file(
+ _support("test_ecdsa_password_521.key"), b"television"
+ )
+ self.assertEqual("ecdsa-sha2-nistp521", key.get_name())
+ exp_ecdsa = b(FINGER_ECDSA_521.split()[1].replace(":", ""))
my_ecdsa = hexlify(key.get_fingerprint())
self.assertEqual(exp_ecdsa, my_ecdsa)
self.assertEqual(PUB_ECDSA_521.split()[1], key.get_base64())
@@ -404,7 +434,7 @@ class KeyTest(unittest.TestCase):
def test_20_compare_ecdsa_521(self):
# verify that the private & public keys compare equal
- key = ECDSAKey.from_private_key_file(test_path('test_ecdsa_521.key'))
+ key = ECDSAKey.from_private_key_file(_support("test_ecdsa_521.key"))
self.assertEqual(key, key)
pub = ECDSAKey(data=key.asbytes())
self.assertTrue(key.can_sign())
@@ -413,11 +443,11 @@ class KeyTest(unittest.TestCase):
def test_21_sign_ecdsa_521(self):
# verify that the rsa private key can sign and verify
- key = ECDSAKey.from_private_key_file(test_path('test_ecdsa_521.key'))
- msg = key.sign_ssh_data(b'ice weasels')
+ key = ECDSAKey.from_private_key_file(_support("test_ecdsa_521.key"))
+ msg = key.sign_ssh_data(b"ice weasels")
self.assertTrue(type(msg) is Message)
msg.rewind()
- self.assertEqual('ecdsa-sha2-nistp521', msg.get_text())
+ self.assertEqual("ecdsa-sha2-nistp521", msg.get_text())
# ECDSA signatures, like DSS signatures, tend to be different
# each time, so we can't compare against a "known correct"
# signature.
@@ -425,14 +455,14 @@ class KeyTest(unittest.TestCase):
msg.rewind()
pub = ECDSAKey(data=key.asbytes())
- self.assertTrue(pub.verify_ssh_sig(b'ice weasels', msg))
+ self.assertTrue(pub.verify_ssh_sig(b"ice weasels", msg))
def test_salt_size(self):
# Read an existing encrypted private key
- file_ = test_path('test_rsa_password.key')
- password = 'television'
- newfile = file_ + '.new'
- newpassword = 'radio'
+ file_ = _support("test_rsa_password.key")
+ password = "television"
+ newfile = file_ + ".new"
+ newpassword = "radio"
key = RSAKey(filename=file_, password=password)
# Write out a newly re-encrypted copy with a new password.
# When the bug under test exists, this will ValueError.
@@ -446,20 +476,20 @@ class KeyTest(unittest.TestCase):
os.remove(newfile)
def test_stringification(self):
- key = RSAKey.from_private_key_file(test_path('test_rsa.key'))
+ key = RSAKey.from_private_key_file(_support("test_rsa.key"))
comparable = TEST_KEY_BYTESTR_2 if PY2 else TEST_KEY_BYTESTR_3
self.assertEqual(str(key), comparable)
def test_ed25519(self):
- key1 = Ed25519Key.from_private_key_file(test_path('test_ed25519.key'))
+ key1 = Ed25519Key.from_private_key_file(_support("test_ed25519.key"))
key2 = Ed25519Key.from_private_key_file(
- test_path('test_ed25519_password.key'), b'abc123'
+ _support("test_ed25519_password.key"), b"abc123"
)
self.assertNotEqual(key1.asbytes(), key2.asbytes())
def test_ed25519_compare(self):
# verify that the private & public keys compare equal
- key = Ed25519Key.from_private_key_file(test_path('test_ed25519.key'))
+ key = Ed25519Key.from_private_key_file(_support("test_ed25519.key"))
self.assertEqual(key, key)
pub = Ed25519Key(data=key.asbytes())
self.assertTrue(key.can_sign())
@@ -469,25 +499,25 @@ class KeyTest(unittest.TestCase):
def test_ed25519_nonbytes_password(self):
# https://github.com/paramiko/paramiko/issues/1039
key = Ed25519Key.from_private_key_file(
- test_path('test_ed25519_password.key'),
+ _support("test_ed25519_password.key"),
# NOTE: not a bytes. Amusingly, the test above for same key DOES
# explicitly cast to bytes...code smell!
- 'abc123',
+ "abc123",
)
# No exception -> it's good. Meh.
def test_ed25519_load_from_file_obj(self):
- with open(test_path('test_ed25519.key')) as pkey_fileobj:
+ with open(_support("test_ed25519.key")) as pkey_fileobj:
key = Ed25519Key.from_private_key(pkey_fileobj)
self.assertEqual(key, key)
self.assertTrue(key.can_sign())
def test_keyfile_is_actually_encrypted(self):
# Read an existing encrypted private key
- file_ = test_path('test_rsa_password.key')
- password = 'television'
- newfile = file_ + '.new'
- newpassword = 'radio'
+ file_ = _support("test_rsa_password.key")
+ password = "television"
+ newfile = file_ + ".new"
+ newpassword = "radio"
key = RSAKey(filename=file_, password=password)
# Write out a newly re-encrypted copy with a new password.
# When the bug under test exists, this will ValueError.
@@ -502,19 +532,21 @@ class KeyTest(unittest.TestCase):
# test_client.py; this and nearby cert tests are more about the gritty
# details.
# PKey.load_certificate
- key_path = test_path(os.path.join('cert_support', 'test_rsa.key'))
+ key_path = _support(os.path.join("cert_support", "test_rsa.key"))
key = RSAKey.from_private_key_file(key_path)
self.assertTrue(key.public_blob is None)
- cert_path = test_path(
- os.path.join('cert_support', 'test_rsa.key-cert.pub')
+ cert_path = _support(
+ os.path.join("cert_support", "test_rsa.key-cert.pub")
)
key.load_certificate(cert_path)
self.assertTrue(key.public_blob is not None)
- self.assertEqual(key.public_blob.key_type, 'ssh-rsa-cert-v01@openssh.com')
- self.assertEqual(key.public_blob.comment, 'test_rsa.key.pub')
+ self.assertEqual(
+ key.public_blob.key_type, "ssh-rsa-cert-v01@openssh.com"
+ )
+ self.assertEqual(key.public_blob.comment, "test_rsa.key.pub")
# Delve into blob contents, for test purposes
msg = Message(key.public_blob.key_blob)
- self.assertEqual(msg.get_text(), 'ssh-rsa-cert-v01@openssh.com')
+ self.assertEqual(msg.get_text(), "ssh-rsa-cert-v01@openssh.com")
nonce = msg.get_string()
e = msg.get_mpint()
n = msg.get_mpint()
@@ -524,10 +556,10 @@ class KeyTest(unittest.TestCase):
self.assertEqual(msg.get_int64(), 1234)
# Prevented from loading certificate that doesn't match
- key_path = test_path(os.path.join('cert_support', 'test_ed25519.key'))
+ key_path = _support(os.path.join("cert_support", "test_ed25519.key"))
key1 = Ed25519Key.from_private_key_file(key_path)
self.assertRaises(
ValueError,
key1.load_certificate,
- test_path('test_rsa.key-cert.pub'),
+ _support("test_rsa.key-cert.pub"),
)
diff --git a/tests/test_sftp.py b/tests/test_sftp.py
index b3c7bf98..576b69b7 100755..100644
--- a/tests/test_sftp.py
+++ b/tests/test_sftp.py
@@ -32,17 +32,20 @@ import warnings
from binascii import hexlify
from tempfile import mkstemp
+import pytest
+
import paramiko
+import paramiko.util
from paramiko.py3compat import PY2, b, u, StringIO
from paramiko.common import o777, o600, o666, o644
-from tests import skipUnlessBuiltin
-from tests.stub_sftp import StubServer, StubSFTPServer
-from tests.loop import LoopSocket
-from tests.util import test_path
-import paramiko.util
from paramiko.sftp_attr import SFTPAttributes
-ARTICLE = '''
+from .util import needs_builtin
+from .stub_sftp import StubServer, StubSFTPServer
+from .util import _support, slow
+
+
+ARTICLE = """
Insulin sensitivity and liver insulin receptor structure in ducks from two
genera
@@ -67,7 +70,7 @@ receptors. Therefore the ducks from the two genera exhibit an alpha-beta-
structure for liver insulin receptors and a clear difference in the number of
liver insulin receptors. Their sensitivity to insulin is, however, similarly
decreased compared with chicken.
-'''
+"""
# Here is how unicode characters are encoded over 1 to 6 bytes in utf-8
@@ -79,372 +82,271 @@ decreased compared with chicken.
# U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
# Note that: hex(int('11000011',2)) == '0xc3'
# Thus, the following 2-bytes sequence is not valid utf8: "invalid continuation byte"
-NON_UTF8_DATA = b'\xC3\xC3'
+NON_UTF8_DATA = b"\xC3\xC3"
-FOLDER = os.environ.get('TEST_FOLDER', 'temp-testing000')
+unicode_folder = u"\u00fcnic\u00f8de" if PY2 else "\u00fcnic\u00f8de"
+utf8_folder = b"/\xc3\xbcnic\xc3\xb8\x64\x65"
-sftp = None
-tc = None
-g_big_file_test = True
-# we need to use eval(compile()) here because Py3.2 doesn't support the 'u' marker for unicode
-# this test is the only line in the entire program that has to be treated specially to support Py3.2
-unicode_folder = eval(compile(r"u'\u00fcnic\u00f8de'" if PY2 else r"'\u00fcnic\u00f8de'", 'test_sftp.py', 'eval'))
-utf8_folder = b'/\xc3\xbcnic\xc3\xb8\x64\x65'
-
-
-def get_sftp():
- global sftp
- return sftp
-
-
-class SFTPTest (unittest.TestCase):
- @staticmethod
- def init(hostname, username, keyfile, passwd):
- global sftp, tc
-
- t = paramiko.Transport(hostname)
- tc = t
- try:
- key = paramiko.RSAKey.from_private_key_file(keyfile, passwd)
- except paramiko.PasswordRequiredException:
- sys.stderr.write('\n\nparamiko.RSAKey.from_private_key_file REQUIRES PASSWORD.\n')
- sys.stderr.write('You have two options:\n')
- sys.stderr.write('* Use the "-K" option to point to a different (non-password-protected)\n')
- sys.stderr.write(' private key file.\n')
- sys.stderr.write('* Use the "-P" option to provide the password needed to unlock this private\n')
- sys.stderr.write(' key.\n')
- sys.stderr.write('\n')
- sys.exit(1)
- try:
- t.connect(username=username, pkey=key)
- except paramiko.SSHException:
- t.close()
- sys.stderr.write('\n\nparamiko.Transport.connect FAILED.\n')
- sys.stderr.write('There are several possible reasons why it might fail so quickly:\n\n')
- sys.stderr.write('* The host to connect to (%s) is not a valid SSH server.\n' % hostname)
- sys.stderr.write(' (Use the "-H" option to change the host.)\n')
- sys.stderr.write('* The username to auth as (%s) is invalid.\n' % username)
- sys.stderr.write(' (Use the "-U" option to change the username.)\n')
- sys.stderr.write('* The private key given (%s) is not accepted by the server.\n' % keyfile)
- sys.stderr.write(' (Use the "-K" option to provide a different key file.)\n')
- sys.stderr.write('\n')
- sys.exit(1)
- sftp = paramiko.SFTP.from_transport(t)
-
- @staticmethod
- def init_loopback():
- global sftp, tc
-
- socks = LoopSocket()
- sockc = LoopSocket()
- sockc.link(socks)
- tc = paramiko.Transport(sockc)
- ts = paramiko.Transport(socks)
-
- host_key = paramiko.RSAKey.from_private_key_file(test_path('test_rsa.key'))
- ts.add_server_key(host_key)
- event = threading.Event()
- server = StubServer()
- ts.set_subsystem_handler('sftp', paramiko.SFTPServer, StubSFTPServer)
- ts.start_server(event, server)
- tc.connect(username='slowdive', password='pygmalion')
- event.wait(1.0)
-
- sftp = paramiko.SFTP.from_transport(tc)
-
- @staticmethod
- def set_big_file_test(onoff):
- global g_big_file_test
- g_big_file_test = onoff
-
- def setUp(self):
- global FOLDER
- for i in range(1000):
- FOLDER = FOLDER[:-3] + '%03d' % i
- try:
- sftp.mkdir(FOLDER)
- break
- except (IOError, OSError):
- pass
- def tearDown(self):
- #sftp.chdir()
- sftp.rmdir(FOLDER)
+@slow
+class TestSFTP(object):
- def test_1_file(self):
+ def test_1_file(self, sftp):
"""
verify that we can create a file.
"""
- f = sftp.open(FOLDER + '/test', 'w')
+ f = sftp.open(sftp.FOLDER + "/test", "w")
try:
- self.assertEqual(f.stat().st_size, 0)
+ assert f.stat().st_size == 0
finally:
f.close()
- sftp.remove(FOLDER + '/test')
+ sftp.remove(sftp.FOLDER + "/test")
- def test_2_close(self):
+ def test_2_close(self, sftp):
"""
- verify that closing the sftp session doesn't do anything bad, and that
- a new one can be opened.
+ Verify that SFTP session close() causes a socket error on next action.
"""
- global sftp
sftp.close()
- try:
- sftp.open(FOLDER + '/test2', 'w')
- self.fail('expected exception')
- except:
- pass
- sftp = paramiko.SFTP.from_transport(tc)
+ with pytest.raises(socket.error, match="Socket is closed"):
+ sftp.open(sftp.FOLDER + "/test2", "w")
- def test_2_sftp_can_be_used_as_context_manager(self):
+ def test_2_sftp_can_be_used_as_context_manager(self, sftp):
"""
verify that the sftp session is closed when exiting the context manager
"""
- global sftp
with sftp:
pass
- try:
- sftp.open(FOLDER + '/test2', 'w')
- self.fail('expected exception')
- except (EOFError, socket.error):
- pass
- finally:
- sftp = paramiko.SFTP.from_transport(tc)
+ with pytest.raises(socket.error, match="Socket is closed"):
+ sftp.open(sftp.FOLDER + "/test2", "w")
- def test_3_write(self):
+ def test_3_write(self, sftp):
"""
verify that a file can be created and written, and the size is correct.
"""
try:
- with sftp.open(FOLDER + '/duck.txt', 'w') as f:
+ with sftp.open(sftp.FOLDER + "/duck.txt", "w") as f:
f.write(ARTICLE)
- self.assertEqual(sftp.stat(FOLDER + '/duck.txt').st_size, 1483)
+ assert sftp.stat(sftp.FOLDER + "/duck.txt").st_size == 1483
finally:
- sftp.remove(FOLDER + '/duck.txt')
+ sftp.remove(sftp.FOLDER + "/duck.txt")
- def test_3_sftp_file_can_be_used_as_context_manager(self):
+ def test_3_sftp_file_can_be_used_as_context_manager(self, sftp):
"""
verify that an opened file can be used as a context manager
"""
try:
- with sftp.open(FOLDER + '/duck.txt', 'w') as f:
+ with sftp.open(sftp.FOLDER + "/duck.txt", "w") as f:
f.write(ARTICLE)
- self.assertEqual(sftp.stat(FOLDER + '/duck.txt').st_size, 1483)
+ assert sftp.stat(sftp.FOLDER + "/duck.txt").st_size == 1483
finally:
- sftp.remove(FOLDER + '/duck.txt')
+ sftp.remove(sftp.FOLDER + "/duck.txt")
- def test_4_append(self):
+ def test_4_append(self, sftp):
"""
verify that a file can be opened for append, and tell() still works.
"""
try:
- with sftp.open(FOLDER + '/append.txt', 'w') as f:
- f.write('first line\nsecond line\n')
- self.assertEqual(f.tell(), 23)
-
- with sftp.open(FOLDER + '/append.txt', 'a+') as f:
- f.write('third line!!!\n')
- self.assertEqual(f.tell(), 37)
- self.assertEqual(f.stat().st_size, 37)
+ with sftp.open(sftp.FOLDER + "/append.txt", "w") as f:
+ f.write("first line\nsecond line\n")
+ assert f.tell() == 23
+
+ with sftp.open(sftp.FOLDER + "/append.txt", "a+") as f:
+ f.write("third line!!!\n")
+ assert f.tell() == 37
+ assert f.stat().st_size == 37
f.seek(-26, f.SEEK_CUR)
- self.assertEqual(f.readline(), 'second line\n')
+ assert f.readline() == "second line\n"
finally:
- sftp.remove(FOLDER + '/append.txt')
+ sftp.remove(sftp.FOLDER + "/append.txt")
- def test_5_rename(self):
+ def test_5_rename(self, sftp):
"""
verify that renaming a file works.
"""
try:
- with sftp.open(FOLDER + '/first.txt', 'w') as f:
- f.write('content!\n')
- sftp.rename(FOLDER + '/first.txt', FOLDER + '/second.txt')
- try:
- sftp.open(FOLDER + '/first.txt', 'r')
- self.assertTrue(False, 'no exception on reading nonexistent file')
- except IOError:
- pass
- with sftp.open(FOLDER + '/second.txt', 'r') as f:
+ with sftp.open(sftp.FOLDER + "/first.txt", "w") as f:
+ f.write("content!\n")
+ sftp.rename(
+ sftp.FOLDER + "/first.txt", sftp.FOLDER + "/second.txt"
+ )
+ with pytest.raises(IOError, match="No such file"):
+ sftp.open(sftp.FOLDER + "/first.txt", "r")
+ with sftp.open(sftp.FOLDER + "/second.txt", "r") as f:
f.seek(-6, f.SEEK_END)
- self.assertEqual(u(f.read(4)), 'tent')
+ assert u(f.read(4)) == "tent"
finally:
+ # TODO: this is gross, make some sort of 'remove if possible' / 'rm
+ # -f' a-like, jeez
try:
- sftp.remove(FOLDER + '/first.txt')
+ sftp.remove(sftp.FOLDER + "/first.txt")
except:
pass
try:
- sftp.remove(FOLDER + '/second.txt')
+ sftp.remove(sftp.FOLDER + "/second.txt")
except:
pass
-
- def test_5a_posix_rename(self):
+ def test_5a_posix_rename(self, sftp):
"""Test posix-rename@openssh.com protocol extension."""
try:
# first check that the normal rename works as specified
- with sftp.open(FOLDER + '/a', 'w') as f:
- f.write('one')
- sftp.rename(FOLDER + '/a', FOLDER + '/b')
- with sftp.open(FOLDER + '/a', 'w') as f:
- f.write('two')
- try:
- sftp.rename(FOLDER + '/a', FOLDER + '/b')
- self.assertTrue(False, 'no exception when rename-ing onto existing file')
- except (OSError, IOError):
- pass
+ with sftp.open(sftp.FOLDER + "/a", "w") as f:
+ f.write("one")
+ sftp.rename(sftp.FOLDER + "/a", sftp.FOLDER + "/b")
+ with sftp.open(sftp.FOLDER + "/a", "w") as f:
+ f.write("two")
+ with pytest.raises(IOError): # actual message seems generic
+ sftp.rename(sftp.FOLDER + "/a", sftp.FOLDER + "/b")
# now check with the posix_rename
- sftp.posix_rename(FOLDER + '/a', FOLDER + '/b')
- with sftp.open(FOLDER + '/b', 'r') as f:
+ sftp.posix_rename(sftp.FOLDER + "/a", sftp.FOLDER + "/b")
+ with sftp.open(sftp.FOLDER + "/b", "r") as f:
data = u(f.read())
- self.assertEqual('two', data, "Contents of renamed file not the same as original file")
+ err = "Contents of renamed file not the same as original file"
+ assert "two" == data, err
finally:
try:
- sftp.remove(FOLDER + '/a')
+ sftp.remove(sftp.FOLDER + "/a")
except:
pass
try:
- sftp.remove(FOLDER + '/b')
+ sftp.remove(sftp.FOLDER + "/b")
except:
pass
-
- def test_6_folder(self):
+ def test_6_folder(self, sftp):
"""
create a temporary folder, verify that we can create a file in it, then
remove the folder and verify that we can't create a file in it anymore.
"""
- sftp.mkdir(FOLDER + '/subfolder')
- sftp.open(FOLDER + '/subfolder/test', 'w').close()
- sftp.remove(FOLDER + '/subfolder/test')
- sftp.rmdir(FOLDER + '/subfolder')
- try:
- sftp.open(FOLDER + '/subfolder/test')
- # shouldn't be able to create that file
- self.assertTrue(False, 'no exception at dummy file creation')
- except IOError:
- pass
+ sftp.mkdir(sftp.FOLDER + "/subfolder")
+ sftp.open(sftp.FOLDER + "/subfolder/test", "w").close()
+ sftp.remove(sftp.FOLDER + "/subfolder/test")
+ sftp.rmdir(sftp.FOLDER + "/subfolder")
+ # shouldn't be able to create that file if dir removed
+ with pytest.raises(IOError, match="No such file"):
+ sftp.open(sftp.FOLDER + "/subfolder/test")
- def test_7_listdir(self):
+ def test_7_listdir(self, sftp):
"""
verify that a folder can be created, a bunch of files can be placed in
it, and those files show up in sftp.listdir.
"""
try:
- sftp.open(FOLDER + '/duck.txt', 'w').close()
- sftp.open(FOLDER + '/fish.txt', 'w').close()
- sftp.open(FOLDER + '/tertiary.py', 'w').close()
-
- x = sftp.listdir(FOLDER)
- self.assertEqual(len(x), 3)
- self.assertTrue('duck.txt' in x)
- self.assertTrue('fish.txt' in x)
- self.assertTrue('tertiary.py' in x)
- self.assertTrue('random' not in x)
+ sftp.open(sftp.FOLDER + "/duck.txt", "w").close()
+ sftp.open(sftp.FOLDER + "/fish.txt", "w").close()
+ sftp.open(sftp.FOLDER + "/tertiary.py", "w").close()
+
+ x = sftp.listdir(sftp.FOLDER)
+ assert len(x) == 3
+ assert "duck.txt" in x
+ assert "fish.txt" in x
+ assert "tertiary.py" in x
+ assert "random" not in x
finally:
- sftp.remove(FOLDER + '/duck.txt')
- sftp.remove(FOLDER + '/fish.txt')
- sftp.remove(FOLDER + '/tertiary.py')
+ sftp.remove(sftp.FOLDER + "/duck.txt")
+ sftp.remove(sftp.FOLDER + "/fish.txt")
+ sftp.remove(sftp.FOLDER + "/tertiary.py")
- def test_7_5_listdir_iter(self):
+ def test_7_5_listdir_iter(self, sftp):
"""
listdir_iter version of above test
"""
try:
- sftp.open(FOLDER + '/duck.txt', 'w').close()
- sftp.open(FOLDER + '/fish.txt', 'w').close()
- sftp.open(FOLDER + '/tertiary.py', 'w').close()
-
- x = [x.filename for x in sftp.listdir_iter(FOLDER)]
- self.assertEqual(len(x), 3)
- self.assertTrue('duck.txt' in x)
- self.assertTrue('fish.txt' in x)
- self.assertTrue('tertiary.py' in x)
- self.assertTrue('random' not in x)
+ sftp.open(sftp.FOLDER + "/duck.txt", "w").close()
+ sftp.open(sftp.FOLDER + "/fish.txt", "w").close()
+ sftp.open(sftp.FOLDER + "/tertiary.py", "w").close()
+
+ x = [x.filename for x in sftp.listdir_iter(sftp.FOLDER)]
+ assert len(x) == 3
+ assert "duck.txt" in x
+ assert "fish.txt" in x
+ assert "tertiary.py" in x
+ assert "random" not in x
finally:
- sftp.remove(FOLDER + '/duck.txt')
- sftp.remove(FOLDER + '/fish.txt')
- sftp.remove(FOLDER + '/tertiary.py')
+ sftp.remove(sftp.FOLDER + "/duck.txt")
+ sftp.remove(sftp.FOLDER + "/fish.txt")
+ sftp.remove(sftp.FOLDER + "/tertiary.py")
- def test_8_setstat(self):
+ def test_8_setstat(self, sftp):
"""
verify that the setstat functions (chown, chmod, utime, truncate) work.
"""
try:
- with sftp.open(FOLDER + '/special', 'w') as f:
- f.write('x' * 1024)
+ with sftp.open(sftp.FOLDER + "/special", "w") as f:
+ f.write("x" * 1024)
- stat = sftp.stat(FOLDER + '/special')
- sftp.chmod(FOLDER + '/special', (stat.st_mode & ~o777) | o600)
- stat = sftp.stat(FOLDER + '/special')
+ stat = sftp.stat(sftp.FOLDER + "/special")
+ sftp.chmod(sftp.FOLDER + "/special", (stat.st_mode & ~o777) | o600)
+ stat = sftp.stat(sftp.FOLDER + "/special")
expected_mode = o600
- if sys.platform == 'win32':
+ if sys.platform == "win32":
# chmod not really functional on windows
expected_mode = o666
- if sys.platform == 'cygwin':
+ if sys.platform == "cygwin":
# even worse.
expected_mode = o644
- self.assertEqual(stat.st_mode & o777, expected_mode)
- self.assertEqual(stat.st_size, 1024)
+ assert stat.st_mode & o777 == expected_mode
+ assert stat.st_size == 1024
mtime = stat.st_mtime - 3600
atime = stat.st_atime - 1800
- sftp.utime(FOLDER + '/special', (atime, mtime))
- stat = sftp.stat(FOLDER + '/special')
- self.assertEqual(stat.st_mtime, mtime)
- if sys.platform not in ('win32', 'cygwin'):
- self.assertEqual(stat.st_atime, atime)
+ sftp.utime(sftp.FOLDER + "/special", (atime, mtime))
+ stat = sftp.stat(sftp.FOLDER + "/special")
+ assert stat.st_mtime == mtime
+ if sys.platform not in ("win32", "cygwin"):
+ assert stat.st_atime == atime
# can't really test chown, since we'd have to know a valid uid.
- sftp.truncate(FOLDER + '/special', 512)
- stat = sftp.stat(FOLDER + '/special')
- self.assertEqual(stat.st_size, 512)
+ sftp.truncate(sftp.FOLDER + "/special", 512)
+ stat = sftp.stat(sftp.FOLDER + "/special")
+ assert stat.st_size == 512
finally:
- sftp.remove(FOLDER + '/special')
+ sftp.remove(sftp.FOLDER + "/special")
- def test_9_fsetstat(self):
+ def test_9_fsetstat(self, sftp):
"""
verify that the fsetstat functions (chown, chmod, utime, truncate)
work on open files.
"""
try:
- with sftp.open(FOLDER + '/special', 'w') as f:
- f.write('x' * 1024)
+ with sftp.open(sftp.FOLDER + "/special", "w") as f:
+ f.write("x" * 1024)
- with sftp.open(FOLDER + '/special', 'r+') as f:
+ with sftp.open(sftp.FOLDER + "/special", "r+") as f:
stat = f.stat()
f.chmod((stat.st_mode & ~o777) | o600)
stat = f.stat()
expected_mode = o600
- if sys.platform == 'win32':
+ if sys.platform == "win32":
# chmod not really functional on windows
expected_mode = o666
- if sys.platform == 'cygwin':
+ if sys.platform == "cygwin":
# even worse.
expected_mode = o644
- self.assertEqual(stat.st_mode & o777, expected_mode)
- self.assertEqual(stat.st_size, 1024)
+ assert stat.st_mode & o777 == expected_mode
+ assert stat.st_size == 1024
mtime = stat.st_mtime - 3600
atime = stat.st_atime - 1800
f.utime((atime, mtime))
stat = f.stat()
- self.assertEqual(stat.st_mtime, mtime)
- if sys.platform not in ('win32', 'cygwin'):
- self.assertEqual(stat.st_atime, atime)
+ assert stat.st_mtime == mtime
+ if sys.platform not in ("win32", "cygwin"):
+ assert stat.st_atime == atime
# can't really test chown, since we'd have to know a valid uid.
f.truncate(512)
stat = f.stat()
- self.assertEqual(stat.st_size, 512)
+ assert stat.st_size == 512
finally:
- sftp.remove(FOLDER + '/special')
+ sftp.remove(sftp.FOLDER + "/special")
- def test_A_readline_seek(self):
+ def test_A_readline_seek(self, sftp):
"""
create a text file and write a bunch of text into it. then count the lines
in the file, and seek around to retrieve particular lines. this should
@@ -452,10 +354,10 @@ class SFTPTest (unittest.TestCase):
buffering is reset on 'seek'.
"""
try:
- with sftp.open(FOLDER + '/duck.txt', 'w') as f:
+ with sftp.open(sftp.FOLDER + "/duck.txt", "w") as f:
f.write(ARTICLE)
- with sftp.open(FOLDER + '/duck.txt', 'r+') as f:
+ with sftp.open(sftp.FOLDER + "/duck.txt", "r+") as f:
line_number = 0
loc = 0
pos_list = []
@@ -463,35 +365,38 @@ class SFTPTest (unittest.TestCase):
line_number += 1
pos_list.append(loc)
loc = f.tell()
- self.assertTrue(f.seekable())
+ assert f.seekable()
f.seek(pos_list[6], f.SEEK_SET)
- self.assertEqual(f.readline(), 'Nouzilly, France.\n')
+ assert f.readline(), "Nouzilly == France.\n"
f.seek(pos_list[17], f.SEEK_SET)
- self.assertEqual(f.readline()[:4], 'duck')
+ assert f.readline()[:4] == "duck"
f.seek(pos_list[10], f.SEEK_SET)
- self.assertEqual(f.readline(), 'duck types were equally resistant to exogenous insulin compared with chicken.\n')
+ assert (
+ f.readline()
+ == "duck types were equally resistant to exogenous insulin compared with chicken.\n"
+ )
finally:
- sftp.remove(FOLDER + '/duck.txt')
+ sftp.remove(sftp.FOLDER + "/duck.txt")
- def test_B_write_seek(self):
+ def test_B_write_seek(self, sftp):
"""
create a text file, seek back and change part of it, and verify that the
changes worked.
"""
try:
- with sftp.open(FOLDER + '/testing.txt', 'w') as f:
- f.write('hello kitty.\n')
+ with sftp.open(sftp.FOLDER + "/testing.txt", "w") as f:
+ f.write("hello kitty.\n")
f.seek(-5, f.SEEK_CUR)
- f.write('dd')
+ f.write("dd")
- self.assertEqual(sftp.stat(FOLDER + '/testing.txt').st_size, 13)
- with sftp.open(FOLDER + '/testing.txt', 'r') as f:
+ assert sftp.stat(sftp.FOLDER + "/testing.txt").st_size == 13
+ with sftp.open(sftp.FOLDER + "/testing.txt", "r") as f:
data = f.read(20)
- self.assertEqual(data, b'hello kiddy.\n')
+ assert data == b"hello kiddy.\n"
finally:
- sftp.remove(FOLDER + '/testing.txt')
+ sftp.remove(sftp.FOLDER + "/testing.txt")
- def test_C_symlink(self):
+ def test_C_symlink(self, sftp):
"""
create a symlink and then check that lstat doesn't follow it.
"""
@@ -500,390 +405,390 @@ class SFTPTest (unittest.TestCase):
return
try:
- with sftp.open(FOLDER + '/original.txt', 'w') as f:
- f.write('original\n')
- sftp.symlink('original.txt', FOLDER + '/link.txt')
- self.assertEqual(sftp.readlink(FOLDER + '/link.txt'), 'original.txt')
+ with sftp.open(sftp.FOLDER + "/original.txt", "w") as f:
+ f.write("original\n")
+ sftp.symlink("original.txt", sftp.FOLDER + "/link.txt")
+ assert sftp.readlink(sftp.FOLDER + "/link.txt") == "original.txt"
- with sftp.open(FOLDER + '/link.txt', 'r') as f:
- self.assertEqual(f.readlines(), ['original\n'])
+ with sftp.open(sftp.FOLDER + "/link.txt", "r") as f:
+ assert f.readlines() == ["original\n"]
- cwd = sftp.normalize('.')
- if cwd[-1] == '/':
+ cwd = sftp.normalize(".")
+ if cwd[-1] == "/":
cwd = cwd[:-1]
- abs_path = cwd + '/' + FOLDER + '/original.txt'
- sftp.symlink(abs_path, FOLDER + '/link2.txt')
- self.assertEqual(abs_path, sftp.readlink(FOLDER + '/link2.txt'))
+ abs_path = cwd + "/" + sftp.FOLDER + "/original.txt"
+ sftp.symlink(abs_path, sftp.FOLDER + "/link2.txt")
+ assert abs_path == sftp.readlink(sftp.FOLDER + "/link2.txt")
- self.assertEqual(sftp.lstat(FOLDER + '/link.txt').st_size, 12)
- self.assertEqual(sftp.stat(FOLDER + '/link.txt').st_size, 9)
+ assert sftp.lstat(sftp.FOLDER + "/link.txt").st_size == 12
+ assert sftp.stat(sftp.FOLDER + "/link.txt").st_size == 9
# the sftp server may be hiding extra path members from us, so the
# length may be longer than we expect:
- self.assertTrue(sftp.lstat(FOLDER + '/link2.txt').st_size >= len(abs_path))
- self.assertEqual(sftp.stat(FOLDER + '/link2.txt').st_size, 9)
- self.assertEqual(sftp.stat(FOLDER + '/original.txt').st_size, 9)
+ assert sftp.lstat(sftp.FOLDER + "/link2.txt").st_size >= len(
+ abs_path
+ )
+ assert sftp.stat(sftp.FOLDER + "/link2.txt").st_size == 9
+ assert sftp.stat(sftp.FOLDER + "/original.txt").st_size == 9
finally:
try:
- sftp.remove(FOLDER + '/link.txt')
+ sftp.remove(sftp.FOLDER + "/link.txt")
except:
pass
try:
- sftp.remove(FOLDER + '/link2.txt')
+ sftp.remove(sftp.FOLDER + "/link2.txt")
except:
pass
try:
- sftp.remove(FOLDER + '/original.txt')
+ sftp.remove(sftp.FOLDER + "/original.txt")
except:
pass
- def test_D_flush_seek(self):
+ def test_D_flush_seek(self, sftp):
"""
verify that buffered writes are automatically flushed on seek.
"""
try:
- with sftp.open(FOLDER + '/happy.txt', 'w', 1) as f:
- f.write('full line.\n')
- f.write('partial')
+ with sftp.open(sftp.FOLDER + "/happy.txt", "w", 1) as f:
+ f.write("full line.\n")
+ f.write("partial")
f.seek(9, f.SEEK_SET)
- f.write('?\n')
+ f.write("?\n")
- with sftp.open(FOLDER + '/happy.txt', 'r') as f:
- self.assertEqual(f.readline(), u('full line?\n'))
- self.assertEqual(f.read(7), b'partial')
+ with sftp.open(sftp.FOLDER + "/happy.txt", "r") as f:
+ assert f.readline() == u("full line?\n")
+ assert f.read(7) == b"partial"
finally:
try:
- sftp.remove(FOLDER + '/happy.txt')
+ sftp.remove(sftp.FOLDER + "/happy.txt")
except:
pass
- def test_E_realpath(self):
+ def test_E_realpath(self, sftp):
"""
test that realpath is returning something non-empty and not an
error.
"""
- pwd = sftp.normalize('.')
- self.assertTrue(len(pwd) > 0)
- f = sftp.normalize('./' + FOLDER)
- self.assertTrue(len(f) > 0)
- self.assertEqual(os.path.join(pwd, FOLDER), f)
+ pwd = sftp.normalize(".")
+ assert len(pwd) > 0
+ f = sftp.normalize("./" + sftp.FOLDER)
+ assert len(f) > 0
+ assert os.path.join(pwd, sftp.FOLDER) == f
- def test_F_mkdir(self):
+ def test_F_mkdir(self, sftp):
"""
verify that mkdir/rmdir work.
"""
- try:
- sftp.mkdir(FOLDER + '/subfolder')
- except:
- self.assertTrue(False, 'exception creating subfolder')
- try:
- sftp.mkdir(FOLDER + '/subfolder')
- self.assertTrue(False, 'no exception overwriting subfolder')
- except IOError:
- pass
- try:
- sftp.rmdir(FOLDER + '/subfolder')
- except:
- self.assertTrue(False, 'exception removing subfolder')
- try:
- sftp.rmdir(FOLDER + '/subfolder')
- self.assertTrue(False, 'no exception removing nonexistent subfolder')
- except IOError:
- pass
+ sftp.mkdir(sftp.FOLDER + "/subfolder")
+ with pytest.raises(IOError): # generic msg only
+ sftp.mkdir(sftp.FOLDER + "/subfolder")
+ sftp.rmdir(sftp.FOLDER + "/subfolder")
+ with pytest.raises(IOError, match="No such file"):
+ sftp.rmdir(sftp.FOLDER + "/subfolder")
- def test_G_chdir(self):
+ def test_G_chdir(self, sftp):
"""
verify that chdir/getcwd work.
"""
- root = sftp.normalize('.')
- if root[-1] != '/':
- root += '/'
- try:
- sftp.mkdir(FOLDER + '/alpha')
- sftp.chdir(FOLDER + '/alpha')
- sftp.mkdir('beta')
- self.assertEqual(root + FOLDER + '/alpha', sftp.getcwd())
- self.assertEqual(['beta'], sftp.listdir('.'))
-
- sftp.chdir('beta')
- with sftp.open('fish', 'w') as f:
- f.write('hello\n')
- sftp.chdir('..')
- self.assertEqual(['fish'], sftp.listdir('beta'))
- sftp.chdir('..')
- self.assertEqual(['fish'], sftp.listdir('alpha/beta'))
+ root = sftp.normalize(".")
+ if root[-1] != "/":
+ root += "/"
+ try:
+ sftp.mkdir(sftp.FOLDER + "/alpha")
+ sftp.chdir(sftp.FOLDER + "/alpha")
+ sftp.mkdir("beta")
+ assert root + sftp.FOLDER + "/alpha" == sftp.getcwd()
+ assert ["beta"] == sftp.listdir(".")
+
+ sftp.chdir("beta")
+ with sftp.open("fish", "w") as f:
+ f.write("hello\n")
+ sftp.chdir("..")
+ assert ["fish"] == sftp.listdir("beta")
+ sftp.chdir("..")
+ assert ["fish"] == sftp.listdir("alpha/beta")
finally:
sftp.chdir(root)
try:
- sftp.unlink(FOLDER + '/alpha/beta/fish')
+ sftp.unlink(sftp.FOLDER + "/alpha/beta/fish")
except:
pass
try:
- sftp.rmdir(FOLDER + '/alpha/beta')
+ sftp.rmdir(sftp.FOLDER + "/alpha/beta")
except:
pass
try:
- sftp.rmdir(FOLDER + '/alpha')
+ sftp.rmdir(sftp.FOLDER + "/alpha")
except:
pass
- def test_H_get_put(self):
+ def test_H_get_put(self, sftp):
"""
verify that get/put work.
"""
- warnings.filterwarnings('ignore', 'tempnam.*')
+ warnings.filterwarnings("ignore", "tempnam.*")
fd, localname = mkstemp()
os.close(fd)
- text = b'All I wanted was a plastic bunny rabbit.\n'
- with open(localname, 'wb') as f:
+ text = b"All I wanted was a plastic bunny rabbit.\n"
+ with open(localname, "wb") as f:
f.write(text)
saved_progress = []
def progress_callback(x, y):
saved_progress.append((x, y))
- sftp.put(localname, FOLDER + '/bunny.txt', progress_callback)
- with sftp.open(FOLDER + '/bunny.txt', 'rb') as f:
- self.assertEqual(text, f.read(128))
- self.assertEqual([(41, 41)], saved_progress)
+ sftp.put(localname, sftp.FOLDER + "/bunny.txt", progress_callback)
+
+ with sftp.open(sftp.FOLDER + "/bunny.txt", "rb") as f:
+ assert text == f.read(128)
+ assert [(41, 41)] == saved_progress
os.unlink(localname)
fd, localname = mkstemp()
os.close(fd)
saved_progress = []
- sftp.get(FOLDER + '/bunny.txt', localname, progress_callback)
+ sftp.get(sftp.FOLDER + "/bunny.txt", localname, progress_callback)
- with open(localname, 'rb') as f:
- self.assertEqual(text, f.read(128))
- self.assertEqual([(41, 41)], saved_progress)
+ with open(localname, "rb") as f:
+ assert text == f.read(128)
+ assert [(41, 41)] == saved_progress
os.unlink(localname)
- sftp.unlink(FOLDER + '/bunny.txt')
+ sftp.unlink(sftp.FOLDER + "/bunny.txt")
- def test_I_check(self):
+ def test_I_check(self, sftp):
"""
verify that file.check() works against our own server.
(it's an sftp extension that we support, and may be the only ones who
support it.)
"""
- with sftp.open(FOLDER + '/kitty.txt', 'w') as f:
- f.write('here kitty kitty' * 64)
-
- try:
- with sftp.open(FOLDER + '/kitty.txt', 'r') as f:
- sum = f.check('sha1')
- self.assertEqual('91059CFC6615941378D413CB5ADAF4C5EB293402', u(hexlify(sum)).upper())
- sum = f.check('md5', 0, 512)
- self.assertEqual('93DE4788FCA28D471516963A1FE3856A', u(hexlify(sum)).upper())
- sum = f.check('md5', 0, 0, 510)
- self.assertEqual('EB3B45B8CD55A0707D99B177544A319F373183D241432BB2157AB9E46358C4AC90370B5CADE5D90336FC1716F90B36D6',
- u(hexlify(sum)).upper())
+ with sftp.open(sftp.FOLDER + "/kitty.txt", "w") as f:
+ f.write("here kitty kitty" * 64)
+
+ try:
+ with sftp.open(sftp.FOLDER + "/kitty.txt", "r") as f:
+ sum = f.check("sha1")
+ assert (
+ "91059CFC6615941378D413CB5ADAF4C5EB293402"
+ == u(hexlify(sum)).upper()
+ )
+ sum = f.check("md5", 0, 512)
+ assert (
+ "93DE4788FCA28D471516963A1FE3856A"
+ == u(hexlify(sum)).upper()
+ )
+ sum = f.check("md5", 0, 0, 510)
+ assert (
+ u(hexlify(sum)).upper()
+ == "EB3B45B8CD55A0707D99B177544A319F373183D241432BB2157AB9E46358C4AC90370B5CADE5D90336FC1716F90B36D6"
+ ) # noqa
finally:
- sftp.unlink(FOLDER + '/kitty.txt')
+ sftp.unlink(sftp.FOLDER + "/kitty.txt")
- def test_J_x_flag(self):
+ def test_J_x_flag(self, sftp):
"""
verify that the 'x' flag works when opening a file.
"""
- sftp.open(FOLDER + '/unusual.txt', 'wx').close()
+ sftp.open(sftp.FOLDER + "/unusual.txt", "wx").close()
try:
try:
- sftp.open(FOLDER + '/unusual.txt', 'wx')
- self.fail('expected exception')
+ sftp.open(sftp.FOLDER + "/unusual.txt", "wx")
+ self.fail("expected exception")
except IOError:
pass
finally:
- sftp.unlink(FOLDER + '/unusual.txt')
+ sftp.unlink(sftp.FOLDER + "/unusual.txt")
- def test_K_utf8(self):
+ def test_K_utf8(self, sftp):
"""
verify that unicode strings are encoded into utf8 correctly.
"""
- with sftp.open(FOLDER + '/something', 'w') as f:
- f.write('okay')
+ with sftp.open(sftp.FOLDER + "/something", "w") as f:
+ f.write("okay")
try:
- sftp.rename(FOLDER + '/something', FOLDER + '/' + unicode_folder)
- sftp.open(b(FOLDER) + utf8_folder, 'r')
+ sftp.rename(
+ sftp.FOLDER + "/something", sftp.FOLDER + "/" + unicode_folder
+ )
+ sftp.open(b(sftp.FOLDER) + utf8_folder, "r")
except Exception as e:
- self.fail('exception ' + str(e))
- sftp.unlink(b(FOLDER) + utf8_folder)
+ self.fail("exception " + str(e))
+ sftp.unlink(b(sftp.FOLDER) + utf8_folder)
- def test_L_utf8_chdir(self):
- sftp.mkdir(FOLDER + '/' + unicode_folder)
+ def test_L_utf8_chdir(self, sftp):
+ sftp.mkdir(sftp.FOLDER + "/" + unicode_folder)
try:
- sftp.chdir(FOLDER + '/' + unicode_folder)
- with sftp.open('something', 'w') as f:
- f.write('okay')
- sftp.unlink('something')
+ sftp.chdir(sftp.FOLDER + "/" + unicode_folder)
+ with sftp.open("something", "w") as f:
+ f.write("okay")
+ sftp.unlink("something")
finally:
sftp.chdir()
- sftp.rmdir(FOLDER + '/' + unicode_folder)
+ sftp.rmdir(sftp.FOLDER + "/" + unicode_folder)
- def test_M_bad_readv(self):
+ def test_M_bad_readv(self, sftp):
"""
verify that readv at the end of the file doesn't essplode.
"""
- sftp.open(FOLDER + '/zero', 'w').close()
+ sftp.open(sftp.FOLDER + "/zero", "w").close()
try:
- with sftp.open(FOLDER + '/zero', 'r') as f:
+ with sftp.open(sftp.FOLDER + "/zero", "r") as f:
f.readv([(0, 12)])
- with sftp.open(FOLDER + '/zero', 'r') as f:
+ with sftp.open(sftp.FOLDER + "/zero", "r") as f:
file_size = f.stat().st_size
f.prefetch(file_size)
f.read(100)
finally:
- sftp.unlink(FOLDER + '/zero')
+ sftp.unlink(sftp.FOLDER + "/zero")
- def test_N_put_without_confirm(self):
+ def test_N_put_without_confirm(self, sftp):
"""
verify that get/put work without confirmation.
"""
- warnings.filterwarnings('ignore', 'tempnam.*')
+ warnings.filterwarnings("ignore", "tempnam.*")
fd, localname = mkstemp()
os.close(fd)
- text = b'All I wanted was a plastic bunny rabbit.\n'
- with open(localname, 'wb') as f:
+ text = b"All I wanted was a plastic bunny rabbit.\n"
+ with open(localname, "wb") as f:
f.write(text)
saved_progress = []
def progress_callback(x, y):
saved_progress.append((x, y))
- res = sftp.put(localname, FOLDER + '/bunny.txt', progress_callback, False)
- self.assertEqual(SFTPAttributes().attr, res.attr)
+ res = sftp.put(
+ localname, sftp.FOLDER + "/bunny.txt", progress_callback, False
+ )
- with sftp.open(FOLDER + '/bunny.txt', 'r') as f:
- self.assertEqual(text, f.read(128))
- self.assertEqual((41, 41), saved_progress[-1])
+ assert SFTPAttributes().attr == res.attr
+
+ with sftp.open(sftp.FOLDER + "/bunny.txt", "r") as f:
+ assert text == f.read(128)
+ assert (41, 41) == saved_progress[-1]
os.unlink(localname)
- sftp.unlink(FOLDER + '/bunny.txt')
+ sftp.unlink(sftp.FOLDER + "/bunny.txt")
- def test_O_getcwd(self):
+ def test_O_getcwd(self, sftp):
"""
verify that chdir/getcwd work.
"""
- self.assertEqual(None, sftp.getcwd())
- root = sftp.normalize('.')
- if root[-1] != '/':
- root += '/'
+ assert sftp.getcwd() == None
+ root = sftp.normalize(".")
+ if root[-1] != "/":
+ root += "/"
try:
- sftp.mkdir(FOLDER + '/alpha')
- sftp.chdir(FOLDER + '/alpha')
- self.assertEqual('/' + FOLDER + '/alpha', sftp.getcwd())
+ sftp.mkdir(sftp.FOLDER + "/alpha")
+ sftp.chdir(sftp.FOLDER + "/alpha")
+ assert sftp.getcwd() == "/" + sftp.FOLDER + "/alpha"
finally:
sftp.chdir(root)
try:
- sftp.rmdir(FOLDER + '/alpha')
+ sftp.rmdir(sftp.FOLDER + "/alpha")
except:
pass
- def XXX_test_M_seek_append(self):
+ def XXX_test_M_seek_append(self, sftp):
"""
verify that seek does't affect writes during append.
does not work except through paramiko. :( openssh fails.
"""
try:
- with sftp.open(FOLDER + '/append.txt', 'a') as f:
- f.write('first line\nsecond line\n')
+ with sftp.open(sftp.FOLDER + "/append.txt", "a") as f:
+ f.write("first line\nsecond line\n")
f.seek(11, f.SEEK_SET)
- f.write('third line\n')
+ f.write("third line\n")
- with sftp.open(FOLDER + '/append.txt', 'r') as f:
- self.assertEqual(f.stat().st_size, 34)
- self.assertEqual(f.readline(), 'first line\n')
- self.assertEqual(f.readline(), 'second line\n')
- self.assertEqual(f.readline(), 'third line\n')
+ with sftp.open(sftp.FOLDER + "/append.txt", "r") as f:
+ assert f.stat().st_size == 34
+ assert f.readline() == "first line\n"
+ assert f.readline() == "second line\n"
+ assert f.readline() == "third line\n"
finally:
- sftp.remove(FOLDER + '/append.txt')
+ sftp.remove(sftp.FOLDER + "/append.txt")
- def test_putfo_empty_file(self):
+ def test_putfo_empty_file(self, sftp):
"""
Send an empty file and confirm it is sent.
"""
- target = FOLDER + '/empty file.txt'
+ target = sftp.FOLDER + "/empty file.txt"
stream = StringIO()
try:
attrs = sftp.putfo(stream, target)
# the returned attributes should not be null
- self.assertNotEqual(attrs, None)
+ assert attrs is not None
finally:
sftp.remove(target)
-
- def test_N_file_with_percent(self):
+ # TODO: this test doesn't actually fail if the regression (removing '%'
+ # expansion to '%%' within sftp.py's def _log()) is removed - stacktraces
+ # appear but they're clearly emitted from subthreads that have no error
+ # handling. No point running it until that is fixed somehow.
+ @pytest.mark.skip("Doesn't prove anything right now")
+ def test_N_file_with_percent(self, sftp):
"""
verify that we can create a file with a '%' in the filename.
( it needs to be properly escaped by _log() )
"""
- self.assertTrue( paramiko.util.get_logger("paramiko").handlers, "This unit test requires logging to be enabled" )
- f = sftp.open(FOLDER + '/test%file', 'w')
+ f = sftp.open(sftp.FOLDER + "/test%file", "w")
try:
- self.assertEqual(f.stat().st_size, 0)
+ assert f.stat().st_size == 0
finally:
f.close()
- sftp.remove(FOLDER + '/test%file')
-
+ sftp.remove(sftp.FOLDER + "/test%file")
- def test_O_non_utf8_data(self):
+ def test_O_non_utf8_data(self, sftp):
"""Test write() and read() of non utf8 data"""
try:
- with sftp.open('%s/nonutf8data' % FOLDER, 'w') as f:
+ with sftp.open("%s/nonutf8data" % sftp.FOLDER, "w") as f:
f.write(NON_UTF8_DATA)
- with sftp.open('%s/nonutf8data' % FOLDER, 'r') as f:
+ with sftp.open("%s/nonutf8data" % sftp.FOLDER, "r") as f:
data = f.read()
- self.assertEqual(data, NON_UTF8_DATA)
- with sftp.open('%s/nonutf8data' % FOLDER, 'wb') as f:
+ assert data == NON_UTF8_DATA
+ with sftp.open("%s/nonutf8data" % sftp.FOLDER, "wb") as f:
f.write(NON_UTF8_DATA)
- with sftp.open('%s/nonutf8data' % FOLDER, 'rb') as f:
+ with sftp.open("%s/nonutf8data" % sftp.FOLDER, "rb") as f:
data = f.read()
- self.assertEqual(data, NON_UTF8_DATA)
+ assert data == NON_UTF8_DATA
finally:
- sftp.remove('%s/nonutf8data' % FOLDER)
+ sftp.remove("%s/nonutf8data" % sftp.FOLDER)
-
- def test_sftp_attributes_empty_str(self):
+ def test_sftp_attributes_empty_str(self, sftp):
sftp_attributes = SFTPAttributes()
- self.assertEqual(str(sftp_attributes), "?--------- 1 0 0 0 (unknown date) ?")
+ assert (
+ str(sftp_attributes)
+ == "?--------- 1 0 0 0 (unknown date) ?"
+ )
- @skipUnlessBuiltin('buffer')
- def test_write_buffer(self):
+ @needs_builtin("buffer")
+ def test_write_buffer(self, sftp):
"""Test write() using a buffer instance."""
- data = 3 * b'A potentially large block of data to chunk up.\n'
+ data = 3 * b"A potentially large block of data to chunk up.\n"
try:
- with sftp.open('%s/write_buffer' % FOLDER, 'wb') as f:
+ with sftp.open("%s/write_buffer" % sftp.FOLDER, "wb") as f:
for offset in range(0, len(data), 8):
f.write(buffer(data, offset, 8))
- with sftp.open('%s/write_buffer' % FOLDER, 'rb') as f:
- self.assertEqual(f.read(), data)
+ with sftp.open("%s/write_buffer" % sftp.FOLDER, "rb") as f:
+ assert f.read() == data
finally:
- sftp.remove('%s/write_buffer' % FOLDER)
+ sftp.remove("%s/write_buffer" % sftp.FOLDER)
- @skipUnlessBuiltin('memoryview')
- def test_write_memoryview(self):
+ @needs_builtin("memoryview")
+ def test_write_memoryview(self, sftp):
"""Test write() using a memoryview instance."""
- data = 3 * b'A potentially large block of data to chunk up.\n'
+ data = 3 * b"A potentially large block of data to chunk up.\n"
try:
- with sftp.open('%s/write_memoryview' % FOLDER, 'wb') as f:
+ with sftp.open("%s/write_memoryview" % sftp.FOLDER, "wb") as f:
view = memoryview(data)
for offset in range(0, len(data), 8):
- f.write(view[offset:offset+8])
+ f.write(view[offset : offset + 8])
- with sftp.open('%s/write_memoryview' % FOLDER, 'rb') as f:
- self.assertEqual(f.read(), data)
+ with sftp.open("%s/write_memoryview" % sftp.FOLDER, "rb") as f:
+ assert f.read() == data
finally:
- sftp.remove('%s/write_memoryview' % FOLDER)
-
-
-if __name__ == '__main__':
- SFTPTest.init_loopback()
- # logging is required by test_N_file_with_percent
- paramiko.util.log_to_file('test_sftp.log')
- from unittest import main
- main()
+ sftp.remove("%s/write_memoryview" % sftp.FOLDER)
diff --git a/tests/test_sftp_big.py b/tests/test_sftp_big.py
index cfad5682..97c0eb90 100644
--- a/tests/test_sftp_big.py
+++ b/tests/test_sftp_big.py
@@ -31,107 +31,95 @@ import time
import unittest
from paramiko.common import o660
-from tests.test_sftp import get_sftp
-FOLDER = os.environ.get('TEST_FOLDER', 'temp-testing000')
+from .util import slow
-class BigSFTPTest (unittest.TestCase):
+@slow
+class TestBigSFTP(object):
- def setUp(self):
- global FOLDER
- sftp = get_sftp()
- for i in range(1000):
- FOLDER = FOLDER[:-3] + '%03d' % i
- try:
- sftp.mkdir(FOLDER)
- break
- except (IOError, OSError):
- pass
-
- def tearDown(self):
- sftp = get_sftp()
- sftp.rmdir(FOLDER)
-
- def test_1_lots_of_files(self):
+ def test_1_lots_of_files(self, sftp):
"""
create a bunch of files over the same session.
"""
- sftp = get_sftp()
numfiles = 100
try:
for i in range(numfiles):
- with sftp.open('%s/file%d.txt' % (FOLDER, i), 'w', 1) as f:
- f.write('this is file #%d.\n' % i)
- sftp.chmod('%s/file%d.txt' % (FOLDER, i), o660)
+ with sftp.open(
+ "%s/file%d.txt" % (sftp.FOLDER, i), "w", 1
+ ) as f:
+ f.write("this is file #%d.\n" % i)
+ sftp.chmod("%s/file%d.txt" % (sftp.FOLDER, i), o660)
# now make sure every file is there, by creating a list of filenmes
# and reading them in random order.
numlist = list(range(numfiles))
while len(numlist) > 0:
r = numlist[random.randint(0, len(numlist) - 1)]
- with sftp.open('%s/file%d.txt' % (FOLDER, r)) as f:
- self.assertEqual(f.readline(), 'this is file #%d.\n' % r)
+ with sftp.open("%s/file%d.txt" % (sftp.FOLDER, r)) as f:
+ assert f.readline() == "this is file #%d.\n" % r
numlist.remove(r)
finally:
for i in range(numfiles):
try:
- sftp.remove('%s/file%d.txt' % (FOLDER, i))
+ sftp.remove("%s/file%d.txt" % (sftp.FOLDER, i))
except:
pass
- def test_2_big_file(self):
+ def test_2_big_file(self, sftp):
"""
write a 1MB file with no buffering.
"""
- sftp = get_sftp()
- kblob = (1024 * b'x')
+ kblob = 1024 * b"x"
start = time.time()
try:
- with sftp.open('%s/hongry.txt' % FOLDER, 'w') as f:
+ with sftp.open("%s/hongry.txt" % sftp.FOLDER, "w") as f:
for n in range(1024):
f.write(kblob)
if n % 128 == 0:
- sys.stderr.write('.')
- sys.stderr.write(' ')
+ sys.stderr.write(".")
+ sys.stderr.write(" ")
- self.assertEqual(sftp.stat('%s/hongry.txt' % FOLDER).st_size, 1024 * 1024)
+ assert (
+ sftp.stat("%s/hongry.txt" % sftp.FOLDER).st_size == 1024 * 1024
+ )
end = time.time()
- sys.stderr.write('%ds ' % round(end - start))
-
+ sys.stderr.write("%ds " % round(end - start))
+
start = time.time()
- with sftp.open('%s/hongry.txt' % FOLDER, 'r') as f:
+ with sftp.open("%s/hongry.txt" % sftp.FOLDER, "r") as f:
for n in range(1024):
data = f.read(1024)
- self.assertEqual(data, kblob)
+ assert data == kblob
end = time.time()
- sys.stderr.write('%ds ' % round(end - start))
+ sys.stderr.write("%ds " % round(end - start))
finally:
- sftp.remove('%s/hongry.txt' % FOLDER)
+ sftp.remove("%s/hongry.txt" % sftp.FOLDER)
- def test_3_big_file_pipelined(self):
+ def test_3_big_file_pipelined(self, sftp):
"""
write a 1MB file, with no linefeeds, using pipelining.
"""
- sftp = get_sftp()
- kblob = bytes().join([struct.pack('>H', n) for n in range(512)])
+ kblob = bytes().join([struct.pack(">H", n) for n in range(512)])
start = time.time()
try:
- with sftp.open('%s/hongry.txt' % FOLDER, 'wb') as f:
+ with sftp.open("%s/hongry.txt" % sftp.FOLDER, "wb") as f:
f.set_pipelined(True)
for n in range(1024):
f.write(kblob)
if n % 128 == 0:
- sys.stderr.write('.')
- sys.stderr.write(' ')
+ sys.stderr.write(".")
+ sys.stderr.write(" ")
- self.assertEqual(sftp.stat('%s/hongry.txt' % FOLDER).st_size, 1024 * 1024)
+ assert (
+ sftp.stat("%s/hongry.txt" % sftp.FOLDER).st_size == 1024 * 1024
+ )
end = time.time()
- sys.stderr.write('%ds ' % round(end - start))
-
+ sys.stderr.write("%ds " % round(end - start))
+
start = time.time()
- with sftp.open('%s/hongry.txt' % FOLDER, 'rb') as f:
+ with sftp.open("%s/hongry.txt" % sftp.FOLDER, "rb") as f:
file_size = f.stat().st_size
f.prefetch(file_size)
@@ -145,36 +133,39 @@ class BigSFTPTest (unittest.TestCase):
chunk = size - n
data = f.read(chunk)
offset = n % 1024
- self.assertEqual(data, k2blob[offset:offset + chunk])
+ assert data == k2blob[offset : offset + chunk]
n += chunk
end = time.time()
- sys.stderr.write('%ds ' % round(end - start))
+ sys.stderr.write("%ds " % round(end - start))
finally:
- sftp.remove('%s/hongry.txt' % FOLDER)
+ sftp.remove("%s/hongry.txt" % sftp.FOLDER)
- def test_4_prefetch_seek(self):
- sftp = get_sftp()
- kblob = bytes().join([struct.pack('>H', n) for n in range(512)])
+ def test_4_prefetch_seek(self, sftp):
+ kblob = bytes().join([struct.pack(">H", n) for n in range(512)])
try:
- with sftp.open('%s/hongry.txt' % FOLDER, 'wb') as f:
+ with sftp.open("%s/hongry.txt" % sftp.FOLDER, "wb") as f:
f.set_pipelined(True)
for n in range(1024):
f.write(kblob)
if n % 128 == 0:
- sys.stderr.write('.')
- sys.stderr.write(' ')
-
- self.assertEqual(sftp.stat('%s/hongry.txt' % FOLDER).st_size, 1024 * 1024)
-
+ sys.stderr.write(".")
+ sys.stderr.write(" ")
+
+ assert (
+ sftp.stat("%s/hongry.txt" % sftp.FOLDER).st_size == 1024 * 1024
+ )
+
start = time.time()
k2blob = kblob + kblob
chunk = 793
for i in range(10):
- with sftp.open('%s/hongry.txt' % FOLDER, 'rb') as f:
+ with sftp.open("%s/hongry.txt" % sftp.FOLDER, "rb") as f:
file_size = f.stat().st_size
f.prefetch(file_size)
- base_offset = (512 * 1024) + 17 * random.randint(1000, 2000)
+ base_offset = (512 * 1024) + 17 * random.randint(
+ 1000, 2000
+ )
offsets = [base_offset + j * chunk for j in range(100)]
# randomly seek around and read them out
for j in range(100):
@@ -183,33 +174,36 @@ class BigSFTPTest (unittest.TestCase):
f.seek(offset)
data = f.read(chunk)
n_offset = offset % 1024
- self.assertEqual(data, k2blob[n_offset:n_offset + chunk])
+ assert data == k2blob[n_offset : n_offset + chunk]
offset += chunk
end = time.time()
- sys.stderr.write('%ds ' % round(end - start))
+ sys.stderr.write("%ds " % round(end - start))
finally:
- sftp.remove('%s/hongry.txt' % FOLDER)
+ sftp.remove("%s/hongry.txt" % sftp.FOLDER)
- def test_5_readv_seek(self):
- sftp = get_sftp()
- kblob = bytes().join([struct.pack('>H', n) for n in range(512)])
+ def test_5_readv_seek(self, sftp):
+ kblob = bytes().join([struct.pack(">H", n) for n in range(512)])
try:
- with sftp.open('%s/hongry.txt' % FOLDER, 'wb') as f:
+ with sftp.open("%s/hongry.txt" % sftp.FOLDER, "wb") as f:
f.set_pipelined(True)
for n in range(1024):
f.write(kblob)
if n % 128 == 0:
- sys.stderr.write('.')
- sys.stderr.write(' ')
+ sys.stderr.write(".")
+ sys.stderr.write(" ")
- self.assertEqual(sftp.stat('%s/hongry.txt' % FOLDER).st_size, 1024 * 1024)
+ assert (
+ sftp.stat("%s/hongry.txt" % sftp.FOLDER).st_size == 1024 * 1024
+ )
start = time.time()
k2blob = kblob + kblob
chunk = 793
for i in range(10):
- with sftp.open('%s/hongry.txt' % FOLDER, 'rb') as f:
- base_offset = (512 * 1024) + 17 * random.randint(1000, 2000)
+ with sftp.open("%s/hongry.txt" % sftp.FOLDER, "rb") as f:
+ base_offset = (512 * 1024) + 17 * random.randint(
+ 1000, 2000
+ )
# make a bunch of offsets and put them in random order
offsets = [base_offset + j * chunk for j in range(100)]
readv_list = []
@@ -221,155 +215,162 @@ class BigSFTPTest (unittest.TestCase):
for i in range(len(readv_list)):
offset = readv_list[i][0]
n_offset = offset % 1024
- self.assertEqual(next(ret), k2blob[n_offset:n_offset + chunk])
+ assert next(ret) == k2blob[n_offset : n_offset + chunk]
end = time.time()
- sys.stderr.write('%ds ' % round(end - start))
+ sys.stderr.write("%ds " % round(end - start))
finally:
- sftp.remove('%s/hongry.txt' % FOLDER)
+ sftp.remove("%s/hongry.txt" % sftp.FOLDER)
- def test_6_lots_of_prefetching(self):
+ def test_6_lots_of_prefetching(self, sftp):
"""
prefetch a 1MB file a bunch of times, discarding the file object
without using it, to verify that paramiko doesn't get confused.
"""
- sftp = get_sftp()
- kblob = (1024 * b'x')
+ kblob = 1024 * b"x"
try:
- with sftp.open('%s/hongry.txt' % FOLDER, 'w') as f:
+ with sftp.open("%s/hongry.txt" % sftp.FOLDER, "w") as f:
f.set_pipelined(True)
for n in range(1024):
f.write(kblob)
if n % 128 == 0:
- sys.stderr.write('.')
- sys.stderr.write(' ')
+ sys.stderr.write(".")
+ sys.stderr.write(" ")
- self.assertEqual(sftp.stat('%s/hongry.txt' % FOLDER).st_size, 1024 * 1024)
+ assert (
+ sftp.stat("%s/hongry.txt" % sftp.FOLDER).st_size == 1024 * 1024
+ )
for i in range(10):
- with sftp.open('%s/hongry.txt' % FOLDER, 'r') as f:
+ with sftp.open("%s/hongry.txt" % sftp.FOLDER, "r") as f:
file_size = f.stat().st_size
f.prefetch(file_size)
- with sftp.open('%s/hongry.txt' % FOLDER, 'r') as f:
+ with sftp.open("%s/hongry.txt" % sftp.FOLDER, "r") as f:
file_size = f.stat().st_size
f.prefetch(file_size)
for n in range(1024):
data = f.read(1024)
- self.assertEqual(data, kblob)
+ assert data == kblob
if n % 128 == 0:
- sys.stderr.write('.')
- sys.stderr.write(' ')
+ sys.stderr.write(".")
+ sys.stderr.write(" ")
finally:
- sftp.remove('%s/hongry.txt' % FOLDER)
-
- def test_7_prefetch_readv(self):
+ sftp.remove("%s/hongry.txt" % sftp.FOLDER)
+
+ def test_7_prefetch_readv(self, sftp):
"""
verify that prefetch and readv don't conflict with each other.
"""
- sftp = get_sftp()
- kblob = bytes().join([struct.pack('>H', n) for n in range(512)])
+ kblob = bytes().join([struct.pack(">H", n) for n in range(512)])
try:
- with sftp.open('%s/hongry.txt' % FOLDER, 'wb') as f:
+ with sftp.open("%s/hongry.txt" % sftp.FOLDER, "wb") as f:
f.set_pipelined(True)
for n in range(1024):
f.write(kblob)
if n % 128 == 0:
- sys.stderr.write('.')
- sys.stderr.write(' ')
-
- self.assertEqual(sftp.stat('%s/hongry.txt' % FOLDER).st_size, 1024 * 1024)
+ sys.stderr.write(".")
+ sys.stderr.write(" ")
+
+ assert (
+ sftp.stat("%s/hongry.txt" % sftp.FOLDER).st_size == 1024 * 1024
+ )
- with sftp.open('%s/hongry.txt' % FOLDER, 'rb') as f:
+ with sftp.open("%s/hongry.txt" % sftp.FOLDER, "rb") as f:
file_size = f.stat().st_size
f.prefetch(file_size)
data = f.read(1024)
- self.assertEqual(data, kblob)
+ assert data == kblob
chunk_size = 793
base_offset = 512 * 1024
k2blob = kblob + kblob
- chunks = [(base_offset + (chunk_size * i), chunk_size) for i in range(20)]
+ chunks = [
+ (base_offset + (chunk_size * i), chunk_size)
+ for i in range(20)
+ ]
for data in f.readv(chunks):
offset = base_offset % 1024
- self.assertEqual(chunk_size, len(data))
- self.assertEqual(k2blob[offset:offset + chunk_size], data)
+ assert chunk_size == len(data)
+ assert k2blob[offset : offset + chunk_size] == data
base_offset += chunk_size
- sys.stderr.write(' ')
+ sys.stderr.write(" ")
finally:
- sftp.remove('%s/hongry.txt' % FOLDER)
-
- def test_8_large_readv(self):
+ sftp.remove("%s/hongry.txt" % sftp.FOLDER)
+
+ def test_8_large_readv(self, sftp):
"""
verify that a very large readv is broken up correctly and still
returned as a single blob.
"""
- sftp = get_sftp()
- kblob = bytes().join([struct.pack('>H', n) for n in range(512)])
+ kblob = bytes().join([struct.pack(">H", n) for n in range(512)])
try:
- with sftp.open('%s/hongry.txt' % FOLDER, 'wb') as f:
+ with sftp.open("%s/hongry.txt" % sftp.FOLDER, "wb") as f:
f.set_pipelined(True)
for n in range(1024):
f.write(kblob)
if n % 128 == 0:
- sys.stderr.write('.')
- sys.stderr.write(' ')
+ sys.stderr.write(".")
+ sys.stderr.write(" ")
+
+ assert (
+ sftp.stat("%s/hongry.txt" % sftp.FOLDER).st_size == 1024 * 1024
+ )
- self.assertEqual(sftp.stat('%s/hongry.txt' % FOLDER).st_size, 1024 * 1024)
-
- with sftp.open('%s/hongry.txt' % FOLDER, 'rb') as f:
+ with sftp.open("%s/hongry.txt" % sftp.FOLDER, "rb") as f:
data = list(f.readv([(23 * 1024, 128 * 1024)]))
- self.assertEqual(1, len(data))
+ assert len(data) == 1
data = data[0]
- self.assertEqual(128 * 1024, len(data))
-
- sys.stderr.write(' ')
+ assert len(data) == 128 * 1024
+
+ sys.stderr.write(" ")
finally:
- sftp.remove('%s/hongry.txt' % FOLDER)
-
- def test_9_big_file_big_buffer(self):
+ sftp.remove("%s/hongry.txt" % sftp.FOLDER)
+
+ def test_9_big_file_big_buffer(self, sftp):
"""
write a 1MB file, with no linefeeds, and a big buffer.
"""
- sftp = get_sftp()
- mblob = (1024 * 1024 * 'x')
+ mblob = 1024 * 1024 * "x"
try:
- with sftp.open('%s/hongry.txt' % FOLDER, 'w', 128 * 1024) as f:
+ with sftp.open(
+ "%s/hongry.txt" % sftp.FOLDER, "w", 128 * 1024
+ ) as f:
f.write(mblob)
- self.assertEqual(sftp.stat('%s/hongry.txt' % FOLDER).st_size, 1024 * 1024)
+ assert (
+ sftp.stat("%s/hongry.txt" % sftp.FOLDER).st_size == 1024 * 1024
+ )
finally:
- sftp.remove('%s/hongry.txt' % FOLDER)
-
- def test_A_big_file_renegotiate(self):
+ sftp.remove("%s/hongry.txt" % sftp.FOLDER)
+
+ def test_A_big_file_renegotiate(self, sftp):
"""
write a 1MB file, forcing key renegotiation in the middle.
"""
- sftp = get_sftp()
t = sftp.sock.get_transport()
t.packetizer.REKEY_BYTES = 512 * 1024
- k32blob = (32 * 1024 * 'x')
+ k32blob = 32 * 1024 * "x"
try:
- with sftp.open('%s/hongry.txt' % FOLDER, 'w', 128 * 1024) as f:
+ with sftp.open(
+ "%s/hongry.txt" % sftp.FOLDER, "w", 128 * 1024
+ ) as f:
for i in range(32):
f.write(k32blob)
- self.assertEqual(sftp.stat('%s/hongry.txt' % FOLDER).st_size, 1024 * 1024)
- self.assertNotEqual(t.H, t.session_id)
-
+ assert (
+ sftp.stat("%s/hongry.txt" % sftp.FOLDER).st_size == 1024 * 1024
+ )
+ assert t.H != t.session_id
+
# try to read it too.
- with sftp.open('%s/hongry.txt' % FOLDER, 'r', 128 * 1024) as f:
+ with sftp.open(
+ "%s/hongry.txt" % sftp.FOLDER, "r", 128 * 1024
+ ) as f:
file_size = f.stat().st_size
f.prefetch(file_size)
total = 0
while total < 1024 * 1024:
total += len(f.read(32 * 1024))
finally:
- sftp.remove('%s/hongry.txt' % FOLDER)
+ sftp.remove("%s/hongry.txt" % sftp.FOLDER)
t.packetizer.REKEY_BYTES = pow(2, 30)
-
-
-if __name__ == '__main__':
- from tests.test_sftp import SFTPTest
- SFTPTest.init_loopback()
- from unittest import main
- main()
diff --git a/tests/test_ssh_exception.py b/tests/test_ssh_exception.py
index 18f2a97d..6cc5d06a 100644
--- a/tests/test_ssh_exception.py
+++ b/tests/test_ssh_exception.py
@@ -4,28 +4,33 @@ import unittest
from paramiko.ssh_exception import NoValidConnectionsError
-class NoValidConnectionsErrorTest (unittest.TestCase):
+class NoValidConnectionsErrorTest(unittest.TestCase):
def test_pickling(self):
# Regression test for https://github.com/paramiko/paramiko/issues/617
- exc = NoValidConnectionsError({('127.0.0.1', '22'): Exception()})
+ exc = NoValidConnectionsError({("127.0.0.1", "22"): Exception()})
new_exc = pickle.loads(pickle.dumps(exc))
self.assertEqual(type(exc), type(new_exc))
self.assertEqual(str(exc), str(new_exc))
self.assertEqual(exc.args, new_exc.args)
def test_error_message_for_single_host(self):
- exc = NoValidConnectionsError({('127.0.0.1', '22'): Exception()})
+ exc = NoValidConnectionsError({("127.0.0.1", "22"): Exception()})
assert "Unable to connect to port 22 on 127.0.0.1" in str(exc)
def test_error_message_for_two_hosts(self):
- exc = NoValidConnectionsError({('127.0.0.1', '22'): Exception(),
- ('::1', '22'): Exception()})
+ exc = NoValidConnectionsError(
+ {("127.0.0.1", "22"): Exception(), ("::1", "22"): Exception()}
+ )
assert "Unable to connect to port 22 on 127.0.0.1 or ::1" in str(exc)
def test_error_message_for_multiple_hosts(self):
- exc = NoValidConnectionsError({('127.0.0.1', '22'): Exception(),
- ('::1', '22'): Exception(),
- ('10.0.0.42', '22'): Exception()})
+ exc = NoValidConnectionsError(
+ {
+ ("127.0.0.1", "22"): Exception(),
+ ("::1", "22"): Exception(),
+ ("10.0.0.42", "22"): Exception(),
+ }
+ )
exp = "Unable to connect to port 22 on 10.0.0.42, 127.0.0.1 or ::1"
assert exp in str(exc)
diff --git a/tests/test_ssh_gss.py b/tests/test_ssh_gss.py
index d8d05d2b..cee6ce89 100644
--- a/tests/test_ssh_gss.py
+++ b/tests/test_ssh_gss.py
@@ -29,17 +29,18 @@ import unittest
import paramiko
-from tests.util import test_path
-from tests.test_client import FINGERPRINTS
+from .util import _support, needs_gssapi
+from .test_client import FINGERPRINTS
-class NullServer (paramiko.ServerInterface):
+
+class NullServer(paramiko.ServerInterface):
def get_allowed_auths(self, username):
- return 'gssapi-with-mic,publickey'
+ return "gssapi-with-mic,publickey"
- def check_auth_gssapi_with_mic(self, username,
- gss_authenticated=paramiko.AUTH_FAILED,
- cc_file=None):
+ def check_auth_gssapi_with_mic(
+ self, username, gss_authenticated=paramiko.AUTH_FAILED, cc_file=None
+ ):
if gss_authenticated == paramiko.AUTH_SUCCESSFUL:
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
@@ -61,23 +62,21 @@ class NullServer (paramiko.ServerInterface):
return paramiko.OPEN_SUCCEEDED
def check_channel_exec_request(self, channel, command):
- if command != 'yes':
+ if command != "yes":
return False
return True
+@needs_gssapi
class GSSAuthTest(unittest.TestCase):
- @staticmethod
- def init(username, hostname):
- global krb5_principal, targ_name
- krb5_principal = username
- targ_name = hostname
def setUp(self):
- self.username = krb5_principal
- self.hostname = socket.getfqdn(targ_name)
+ # TODO: username and targ_name should come from os.environ or whatever
+ # the approved pytest method is for runtime-configuring test data.
+ self.username = "krb5_principal"
+ self.hostname = socket.getfqdn("targ_name")
self.sockl = socket.socket()
- self.sockl.bind((targ_name, 0))
+ self.sockl.bind(("targ_name", 0))
self.sockl.listen(1)
self.addr, self.port = self.sockl.getsockname()
self.event = threading.Event()
@@ -92,7 +91,7 @@ class GSSAuthTest(unittest.TestCase):
def _run(self):
self.socks, addr = self.sockl.accept()
self.ts = paramiko.Transport(self.socks)
- host_key = paramiko.RSAKey.from_private_key_file('tests/test_rsa.key')
+ host_key = paramiko.RSAKey.from_private_key_file("tests/test_rsa.key")
self.ts.add_server_key(host_key)
server = NullServer()
self.ts.start_server(self.event, server)
@@ -103,15 +102,22 @@ class GSSAuthTest(unittest.TestCase):
The exception is ... no exception yet
"""
- host_key = paramiko.RSAKey.from_private_key_file('tests/test_rsa.key')
+ host_key = paramiko.RSAKey.from_private_key_file("tests/test_rsa.key")
public_host_key = paramiko.RSAKey(data=host_key.asbytes())
self.tc = paramiko.SSHClient()
self.tc.set_missing_host_key_policy(paramiko.WarningPolicy())
- self.tc.get_host_keys().add('[%s]:%d' % (self.addr, self.port),
- 'ssh-rsa', public_host_key)
- self.tc.connect(hostname=self.addr, port=self.port, username=self.username, gss_host=self.hostname,
- gss_auth=True, **kwargs)
+ self.tc.get_host_keys().add(
+ "[%s]:%d" % (self.addr, self.port), "ssh-rsa", public_host_key
+ )
+ self.tc.connect(
+ hostname=self.addr,
+ port=self.port,
+ username=self.username,
+ gss_host=self.hostname,
+ gss_auth=True,
+ **kwargs
+ )
self.event.wait(1.0)
self.assert_(self.event.is_set())
@@ -119,17 +125,17 @@ class GSSAuthTest(unittest.TestCase):
self.assertEquals(self.username, self.ts.get_username())
self.assertEquals(True, self.ts.is_authenticated())
- stdin, stdout, stderr = self.tc.exec_command('yes')
+ stdin, stdout, stderr = self.tc.exec_command("yes")
schan = self.ts.accept(1.0)
- schan.send('Hello there.\n')
- schan.send_stderr('This is on stderr.\n')
+ schan.send("Hello there.\n")
+ schan.send_stderr("This is on stderr.\n")
schan.close()
- self.assertEquals('Hello there.\n', stdout.readline())
- self.assertEquals('', stdout.readline())
- self.assertEquals('This is on stderr.\n', stderr.readline())
- self.assertEquals('', stderr.readline())
+ self.assertEquals("Hello there.\n", stdout.readline())
+ self.assertEquals("", stdout.readline())
+ self.assertEquals("This is on stderr.\n", stderr.readline())
+ self.assertEquals("", stderr.readline())
stdin.close()
stdout.close()
@@ -140,14 +146,17 @@ class GSSAuthTest(unittest.TestCase):
Verify that Paramiko can handle SSHv2 GSS-API / SSPI authentication
(gssapi-with-mic) in client and server mode.
"""
- self._test_connection(allow_agent=False,
- look_for_keys=False)
+ self._test_connection(allow_agent=False, look_for_keys=False)
def test_2_auth_trickledown(self):
"""
Failed gssapi-with-mic auth doesn't prevent subsequent key auth from succeeding
"""
- self.hostname = "this_host_does_not_exists_and_causes_a_GSSAPI-exception"
- self._test_connection(key_filename=[test_path('test_rsa.key')],
- allow_agent=False,
- look_for_keys=False)
+ self.hostname = (
+ "this_host_does_not_exists_and_causes_a_GSSAPI-exception"
+ )
+ self._test_connection(
+ key_filename=[_support("test_rsa.key")],
+ allow_agent=False,
+ look_for_keys=False,
+ )
diff --git a/tests/test_transport.py b/tests/test_transport.py
index 99cbc3e0..c05d6781 100644
--- a/tests/test_transport.py
+++ b/tests/test_transport.py
@@ -32,20 +32,32 @@ from hashlib import sha1
import unittest
from paramiko import (
- Transport, SecurityOptions, ServerInterface, RSAKey, DSSKey, SSHException,
- ChannelException, Packetizer, Channel,
+ Transport,
+ SecurityOptions,
+ ServerInterface,
+ RSAKey,
+ DSSKey,
+ SSHException,
+ ChannelException,
+ Packetizer,
+ Channel,
)
from paramiko import AUTH_FAILED, AUTH_SUCCESSFUL
from paramiko import OPEN_SUCCEEDED, OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
from paramiko.common import (
- MSG_KEXINIT, cMSG_CHANNEL_WINDOW_ADJUST, MIN_PACKET_SIZE, MIN_WINDOW_SIZE,
- MAX_WINDOW_SIZE, DEFAULT_WINDOW_SIZE, DEFAULT_MAX_PACKET_SIZE,
+ MSG_KEXINIT,
+ cMSG_CHANNEL_WINDOW_ADJUST,
+ MIN_PACKET_SIZE,
+ MIN_WINDOW_SIZE,
+ MAX_WINDOW_SIZE,
+ DEFAULT_WINDOW_SIZE,
+ DEFAULT_MAX_PACKET_SIZE,
)
from paramiko.py3compat import bytes
from paramiko.message import Message
-from tests import skipUnlessBuiltin
-from tests.loop import LoopSocket
-from tests.util import test_path
+
+from .util import needs_builtin, _support, slow
+from .loop import LoopSocket
LONG_BANNER = """\
@@ -61,28 +73,28 @@ Maybe.
"""
-class NullServer (ServerInterface):
+class NullServer(ServerInterface):
paranoid_did_password = False
paranoid_did_public_key = False
- paranoid_key = DSSKey.from_private_key_file(test_path('test_dss.key'))
+ paranoid_key = DSSKey.from_private_key_file(_support("test_dss.key"))
def get_allowed_auths(self, username):
- if username == 'slowdive':
- return 'publickey,password'
- return 'publickey'
+ if username == "slowdive":
+ return "publickey,password"
+ return "publickey"
def check_auth_password(self, username, password):
- if (username == 'slowdive') and (password == 'pygmalion'):
+ if (username == "slowdive") and (password == "pygmalion"):
return AUTH_SUCCESSFUL
return AUTH_FAILED
def check_channel_request(self, kind, chanid):
- if kind == 'bogus':
+ if kind == "bogus":
return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
return OPEN_SUCCEEDED
def check_channel_exec_request(self, channel, command):
- if command != b'yes':
+ if command != b"yes":
return False
return True
@@ -95,9 +107,16 @@ class NullServer (ServerInterface):
# tho that's only supposed to occur if the request cannot be served.
# For now, leaving that the default unless test supplies specific
# 'acceptable' request kind
- return kind == 'acceptable'
-
- def check_channel_x11_request(self, channel, single_connection, auth_protocol, auth_cookie, screen_number):
+ return kind == "acceptable"
+
+ def check_channel_x11_request(
+ self,
+ channel,
+ single_connection,
+ auth_protocol,
+ auth_cookie,
+ screen_number,
+ ):
self._x11_single_connection = single_connection
self._x11_auth_protocol = auth_protocol
self._x11_auth_cookie = auth_cookie
@@ -106,7 +125,7 @@ class NullServer (ServerInterface):
def check_port_forward_request(self, addr, port):
self._listen = socket.socket()
- self._listen.bind(('127.0.0.1', 0))
+ self._listen.bind(("127.0.0.1", 0))
self._listen.listen(1)
return self._listen.getsockname()[1]
@@ -120,6 +139,7 @@ class NullServer (ServerInterface):
class TransportTest(unittest.TestCase):
+
def setUp(self):
self.socks = LoopSocket()
self.sockc = LoopSocket()
@@ -134,9 +154,9 @@ class TransportTest(unittest.TestCase):
self.sockc.close()
def setup_test_server(
- self, client_options=None, server_options=None, connect_kwargs=None,
+ self, client_options=None, server_options=None, connect_kwargs=None
):
- host_key = RSAKey.from_private_key_file(test_path('test_rsa.key'))
+ host_key = RSAKey.from_private_key_file(_support("test_rsa.key"))
public_host_key = RSAKey(data=host_key.asbytes())
self.ts.add_server_key(host_key)
@@ -152,8 +172,8 @@ class TransportTest(unittest.TestCase):
if connect_kwargs is None:
connect_kwargs = dict(
hostkey=public_host_key,
- username='slowdive',
- password='pygmalion',
+ username="slowdive",
+ password="pygmalion",
)
self.tc.connect(**connect_kwargs)
event.wait(1.0)
@@ -163,11 +183,11 @@ class TransportTest(unittest.TestCase):
def test_1_security_options(self):
o = self.tc.get_security_options()
self.assertEqual(type(o), SecurityOptions)
- self.assertTrue(('aes256-cbc', 'blowfish-cbc') != o.ciphers)
- o.ciphers = ('aes256-cbc', 'blowfish-cbc')
- self.assertEqual(('aes256-cbc', 'blowfish-cbc'), o.ciphers)
+ self.assertTrue(("aes256-cbc", "blowfish-cbc") != o.ciphers)
+ o.ciphers = ("aes256-cbc", "blowfish-cbc")
+ self.assertEqual(("aes256-cbc", "blowfish-cbc"), o.ciphers)
try:
- o.ciphers = ('aes256-cbc', 'made-up-cipher')
+ o.ciphers = ("aes256-cbc", "made-up-cipher")
self.assertTrue(False)
except ValueError:
pass
@@ -187,12 +207,18 @@ class TransportTest(unittest.TestCase):
o.compression = o.compression
def test_2_compute_key(self):
- self.tc.K = 123281095979686581523377256114209720774539068973101330872763622971399429481072519713536292772709507296759612401802191955568143056534122385270077606457721553469730659233569339356140085284052436697480759510519672848743794433460113118986816826624865291116513647975790797391795651716378444844877749505443714557929
- self.tc.H = b'\x0C\x83\x07\xCD\xE6\x85\x6F\xF3\x0B\xA9\x36\x84\xEB\x0F\x04\xC2\x52\x0E\x9E\xD3'
+ self.tc.K = (
+ 123281095979686581523377256114209720774539068973101330872763622971399429481072519713536292772709507296759612401802191955568143056534122385270077606457721553469730659233569339356140085284052436697480759510519672848743794433460113118986816826624865291116513647975790797391795651716378444844877749505443714557929
+ )
+ self.tc.H = (
+ b"\x0C\x83\x07\xCD\xE6\x85\x6F\xF3\x0B\xA9\x36\x84\xEB\x0F\x04\xC2\x52\x0E\x9E\xD3"
+ )
self.tc.session_id = self.tc.H
- key = self.tc._compute_key('C', 32)
- self.assertEqual(b'207E66594CA87C44ECCBA3B3CD39FDDB378E6FDB0F97C54B2AA0CFBF900CD995',
- hexlify(key).upper())
+ key = self.tc._compute_key("C", 32)
+ self.assertEqual(
+ b"207E66594CA87C44ECCBA3B3CD39FDDB378E6FDB0F97C54B2AA0CFBF900CD995",
+ hexlify(key).upper(),
+ )
def test_3_simple(self):
"""
@@ -200,7 +226,7 @@ class TransportTest(unittest.TestCase):
loopback sockets. this is hardly "simple" but it's simpler than the
later tests. :)
"""
- host_key = RSAKey.from_private_key_file(test_path('test_rsa.key'))
+ host_key = RSAKey.from_private_key_file(_support("test_rsa.key"))
public_host_key = RSAKey(data=host_key.asbytes())
self.ts.add_server_key(host_key)
event = threading.Event()
@@ -211,13 +237,14 @@ class TransportTest(unittest.TestCase):
self.assertEqual(False, self.tc.is_authenticated())
self.assertEqual(False, self.ts.is_authenticated())
self.ts.start_server(event, server)
- self.tc.connect(hostkey=public_host_key,
- username='slowdive', password='pygmalion')
+ self.tc.connect(
+ hostkey=public_host_key, username="slowdive", password="pygmalion"
+ )
event.wait(1.0)
self.assertTrue(event.is_set())
self.assertTrue(self.ts.is_active())
- self.assertEqual('slowdive', self.tc.get_username())
- self.assertEqual('slowdive', self.ts.get_username())
+ self.assertEqual("slowdive", self.tc.get_username())
+ self.assertEqual("slowdive", self.ts.get_username())
self.assertEqual(True, self.tc.is_authenticated())
self.assertEqual(True, self.ts.is_authenticated())
@@ -225,7 +252,7 @@ class TransportTest(unittest.TestCase):
"""
verify that a long banner doesn't mess up the handshake.
"""
- host_key = RSAKey.from_private_key_file(test_path('test_rsa.key'))
+ host_key = RSAKey.from_private_key_file(_support("test_rsa.key"))
public_host_key = RSAKey(data=host_key.asbytes())
self.ts.add_server_key(host_key)
event = threading.Event()
@@ -233,8 +260,9 @@ class TransportTest(unittest.TestCase):
self.assertTrue(not event.is_set())
self.socks.send(LONG_BANNER)
self.ts.start_server(event, server)
- self.tc.connect(hostkey=public_host_key,
- username='slowdive', password='pygmalion')
+ self.tc.connect(
+ hostkey=public_host_key, username="slowdive", password="pygmalion"
+ )
event.wait(1.0)
self.assertTrue(event.is_set())
self.assertTrue(self.ts.is_active())
@@ -244,12 +272,14 @@ class TransportTest(unittest.TestCase):
verify that the client can demand odd handshake settings, and can
renegotiate keys in mid-stream.
"""
+
def force_algorithms(options):
- options.ciphers = ('aes256-cbc',)
- options.digests = ('hmac-md5-96',)
+ options.ciphers = ("aes256-cbc",)
+ options.digests = ("hmac-md5-96",)
+
self.setup_test_server(client_options=force_algorithms)
- self.assertEqual('aes256-cbc', self.tc.local_cipher)
- self.assertEqual('aes256-cbc', self.tc.remote_cipher)
+ self.assertEqual("aes256-cbc", self.tc.local_cipher)
+ self.assertEqual("aes256-cbc", self.tc.remote_cipher)
self.assertEqual(12, self.tc.packetizer.get_mac_size_out())
self.assertEqual(12, self.tc.packetizer.get_mac_size_in())
@@ -257,15 +287,16 @@ class TransportTest(unittest.TestCase):
self.tc.renegotiate_keys()
self.ts.send_ignore(1024)
+ @slow
def test_5_keepalive(self):
"""
verify that the keepalive will be sent.
"""
self.setup_test_server()
- self.assertEqual(None, getattr(self.server, '_global_request', None))
+ self.assertEqual(None, getattr(self.server, "_global_request", None))
self.tc.set_keepalive(1)
time.sleep(2)
- self.assertEqual('keepalive@lag.net', self.server._global_request)
+ self.assertEqual("keepalive@lag.net", self.server._global_request)
def test_6_exec_command(self):
"""
@@ -276,39 +307,41 @@ class TransportTest(unittest.TestCase):
chan = self.tc.open_session()
schan = self.ts.accept(1.0)
try:
- chan.exec_command(b'command contains \xfc and is not a valid UTF-8 string')
+ chan.exec_command(
+ b"command contains \xfc and is not a valid UTF-8 string"
+ )
self.assertTrue(False)
except SSHException:
pass
chan = self.tc.open_session()
- chan.exec_command('yes')
+ chan.exec_command("yes")
schan = self.ts.accept(1.0)
- schan.send('Hello there.\n')
- schan.send_stderr('This is on stderr.\n')
+ schan.send("Hello there.\n")
+ schan.send_stderr("This is on stderr.\n")
schan.close()
f = chan.makefile()
- self.assertEqual('Hello there.\n', f.readline())
- self.assertEqual('', f.readline())
+ self.assertEqual("Hello there.\n", f.readline())
+ self.assertEqual("", f.readline())
f = chan.makefile_stderr()
- self.assertEqual('This is on stderr.\n', f.readline())
- self.assertEqual('', f.readline())
+ self.assertEqual("This is on stderr.\n", f.readline())
+ self.assertEqual("", f.readline())
# now try it with combined stdout/stderr
chan = self.tc.open_session()
- chan.exec_command('yes')
+ chan.exec_command("yes")
schan = self.ts.accept(1.0)
- schan.send('Hello there.\n')
- schan.send_stderr('This is on stderr.\n')
+ schan.send("Hello there.\n")
+ schan.send_stderr("This is on stderr.\n")
schan.close()
chan.set_combine_stderr(True)
f = chan.makefile()
- self.assertEqual('Hello there.\n', f.readline())
- self.assertEqual('This is on stderr.\n', f.readline())
- self.assertEqual('', f.readline())
-
+ self.assertEqual("Hello there.\n", f.readline())
+ self.assertEqual("This is on stderr.\n", f.readline())
+ self.assertEqual("", f.readline())
+
def test_6a_channel_can_be_used_as_context_manager(self):
"""
verify that exec_command() does something reasonable.
@@ -317,13 +350,13 @@ class TransportTest(unittest.TestCase):
with self.tc.open_session() as chan:
with self.ts.accept(1.0) as schan:
- chan.exec_command('yes')
- schan.send('Hello there.\n')
+ chan.exec_command("yes")
+ schan.send("Hello there.\n")
schan.close()
f = chan.makefile()
- self.assertEqual('Hello there.\n', f.readline())
- self.assertEqual('', f.readline())
+ self.assertEqual("Hello there.\n", f.readline())
+ self.assertEqual("", f.readline())
def test_7_invoke_shell(self):
"""
@@ -333,11 +366,11 @@ class TransportTest(unittest.TestCase):
chan = self.tc.open_session()
chan.invoke_shell()
schan = self.ts.accept(1.0)
- chan.send('communist j. cat\n')
+ chan.send("communist j. cat\n")
f = schan.makefile()
- self.assertEqual('communist j. cat\n', f.readline())
+ self.assertEqual("communist j. cat\n", f.readline())
chan.close()
- self.assertEqual('', f.readline())
+ self.assertEqual("", f.readline())
def test_8_channel_exception(self):
"""
@@ -345,8 +378,8 @@ class TransportTest(unittest.TestCase):
"""
self.setup_test_server()
try:
- chan = self.tc.open_channel('bogus')
- self.fail('expected exception')
+ chan = self.tc.open_channel("bogus")
+ self.fail("expected exception")
except ChannelException as e:
self.assertTrue(e.code == OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED)
@@ -358,8 +391,8 @@ class TransportTest(unittest.TestCase):
chan = self.tc.open_session()
schan = self.ts.accept(1.0)
- chan.exec_command('yes')
- schan.send('Hello there.\n')
+ chan.exec_command("yes")
+ schan.send("Hello there.\n")
self.assertTrue(not chan.exit_status_ready())
# trigger an EOF
schan.shutdown_read()
@@ -368,8 +401,8 @@ class TransportTest(unittest.TestCase):
schan.close()
f = chan.makefile()
- self.assertEqual('Hello there.\n', f.readline())
- self.assertEqual('', f.readline())
+ self.assertEqual("Hello there.\n", f.readline())
+ self.assertEqual("", f.readline())
count = 0
while not chan.exit_status_ready():
time.sleep(0.1)
@@ -394,7 +427,7 @@ class TransportTest(unittest.TestCase):
self.assertEqual([], w)
self.assertEqual([], e)
- schan.send('hello\n')
+ schan.send("hello\n")
# something should be ready now (give it 1 second to appear)
for i in range(10):
@@ -406,7 +439,7 @@ class TransportTest(unittest.TestCase):
self.assertEqual([], w)
self.assertEqual([], e)
- self.assertEqual(b'hello\n', chan.recv(6))
+ self.assertEqual(b"hello\n", chan.recv(6))
# and, should be dead again now
r, w, e = select.select([chan], [], [], 0.1)
@@ -441,12 +474,12 @@ class TransportTest(unittest.TestCase):
self.setup_test_server()
self.tc.packetizer.REKEY_BYTES = 16384
chan = self.tc.open_session()
- chan.exec_command('yes')
+ chan.exec_command("yes")
schan = self.ts.accept(1.0)
self.assertEqual(self.tc.H, self.tc.session_id)
for i in range(20):
- chan.send('x' * 1024)
+ chan.send("x" * 1024)
chan.close()
# allow a few seconds for the rekeying to complete
@@ -462,18 +495,20 @@ class TransportTest(unittest.TestCase):
"""
verify that zlib compression is basically working.
"""
+
def force_compression(o):
- o.compression = ('zlib',)
+ o.compression = ("zlib",)
+
self.setup_test_server(force_compression, force_compression)
chan = self.tc.open_session()
- chan.exec_command('yes')
+ chan.exec_command("yes")
schan = self.ts.accept(1.0)
bytes = self.tc.packetizer._Packetizer__sent_bytes
- chan.send('x' * 1024)
+ chan.send("x" * 1024)
bytes2 = self.tc.packetizer._Packetizer__sent_bytes
- block_size = self.tc._cipher_info[self.tc.local_cipher]['block-size']
- mac_size = self.tc._mac_info[self.tc.local_mac]['size']
+ block_size = self.tc._cipher_info[self.tc.local_cipher]["block-size"]
+ mac_size = self.tc._mac_info[self.tc.local_mac]["size"]
# tests show this is actually compressed to *52 bytes*! including packet overhead! nice!! :)
self.assertTrue(bytes2 - bytes < 1024)
self.assertEqual(16 + block_size + mac_size, bytes2 - bytes)
@@ -487,29 +522,32 @@ class TransportTest(unittest.TestCase):
"""
self.setup_test_server()
chan = self.tc.open_session()
- chan.exec_command('yes')
+ chan.exec_command("yes")
schan = self.ts.accept(1.0)
requested = []
+
def handler(c, addr_port):
addr, port = addr_port
requested.append((addr, port))
self.tc._queue_incoming_channel(c)
- self.assertEqual(None, getattr(self.server, '_x11_screen_number', None))
+ self.assertEqual(
+ None, getattr(self.server, "_x11_screen_number", None)
+ )
cookie = chan.request_x11(0, single_connection=True, handler=handler)
self.assertEqual(0, self.server._x11_screen_number)
- self.assertEqual('MIT-MAGIC-COOKIE-1', self.server._x11_auth_protocol)
+ self.assertEqual("MIT-MAGIC-COOKIE-1", self.server._x11_auth_protocol)
self.assertEqual(cookie, self.server._x11_auth_cookie)
self.assertEqual(True, self.server._x11_single_connection)
- x11_server = self.ts.open_x11_channel(('localhost', 6093))
+ x11_server = self.ts.open_x11_channel(("localhost", 6093))
x11_client = self.tc.accept()
- self.assertEqual('localhost', requested[0][0])
+ self.assertEqual("localhost", requested[0][0])
self.assertEqual(6093, requested[0][1])
- x11_server.send('hello')
- self.assertEqual(b'hello', x11_client.recv(5))
+ x11_server.send("hello")
+ self.assertEqual(b"hello", x11_client.recv(5))
x11_server.close()
x11_client.close()
@@ -523,33 +561,36 @@ class TransportTest(unittest.TestCase):
"""
self.setup_test_server()
chan = self.tc.open_session()
- chan.exec_command('yes')
+ chan.exec_command("yes")
schan = self.ts.accept(1.0)
requested = []
+
def handler(c, origin_addr_port, server_addr_port):
requested.append(origin_addr_port)
requested.append(server_addr_port)
self.tc._queue_incoming_channel(c)
- port = self.tc.request_port_forward('127.0.0.1', 0, handler)
+ port = self.tc.request_port_forward("127.0.0.1", 0, handler)
self.assertEqual(port, self.server._listen.getsockname()[1])
cs = socket.socket()
- cs.connect(('127.0.0.1', port))
+ cs.connect(("127.0.0.1", port))
ss, _ = self.server._listen.accept()
- sch = self.ts.open_forwarded_tcpip_channel(ss.getsockname(), ss.getpeername())
+ sch = self.ts.open_forwarded_tcpip_channel(
+ ss.getsockname(), ss.getpeername()
+ )
cch = self.tc.accept()
- sch.send('hello')
- self.assertEqual(b'hello', cch.recv(5))
+ sch.send("hello")
+ self.assertEqual(b"hello", cch.recv(5))
sch.close()
cch.close()
ss.close()
cs.close()
# now cancel it.
- self.tc.cancel_port_forward('127.0.0.1', port)
+ self.tc.cancel_port_forward("127.0.0.1", port)
self.assertTrue(self.server._listen is None)
def test_F_port_forwarding(self):
@@ -559,27 +600,29 @@ class TransportTest(unittest.TestCase):
"""
self.setup_test_server()
chan = self.tc.open_session()
- chan.exec_command('yes')
+ chan.exec_command("yes")
schan = self.ts.accept(1.0)
# open a port on the "server" that the client will ask to forward to.
greeting_server = socket.socket()
- greeting_server.bind(('127.0.0.1', 0))
+ greeting_server.bind(("127.0.0.1", 0))
greeting_server.listen(1)
greeting_port = greeting_server.getsockname()[1]
- cs = self.tc.open_channel('direct-tcpip', ('127.0.0.1', greeting_port), ('', 9000))
+ cs = self.tc.open_channel(
+ "direct-tcpip", ("127.0.0.1", greeting_port), ("", 9000)
+ )
sch = self.ts.accept(1.0)
cch = socket.socket()
cch.connect(self.server._tcpip_dest)
ss, _ = greeting_server.accept()
- ss.send(b'Hello!\n')
+ ss.send(b"Hello!\n")
ss.close()
sch.send(cch.recv(8192))
sch.close()
- self.assertEqual(b'Hello!\n', cs.recv(7))
+ self.assertEqual(b"Hello!\n", cs.recv(7))
cs.close()
def test_G_stderr_select(self):
@@ -598,7 +641,7 @@ class TransportTest(unittest.TestCase):
self.assertEqual([], w)
self.assertEqual([], e)
- schan.send_stderr('hello\n')
+ schan.send_stderr("hello\n")
# something should be ready now (give it 1 second to appear)
for i in range(10):
@@ -610,7 +653,7 @@ class TransportTest(unittest.TestCase):
self.assertEqual([], w)
self.assertEqual([], e)
- self.assertEqual(b'hello\n', chan.recv_stderr(6))
+ self.assertEqual(b"hello\n", chan.recv_stderr(6))
# and, should be dead again now
r, w, e = select.select([chan], [], [], 0.1)
@@ -632,8 +675,8 @@ class TransportTest(unittest.TestCase):
self.assertEqual(chan.send_ready(), True)
total = 0
- K = '*' * 1024
- limit = 1+(64 * 2 ** 15)
+ K = "*" * 1024
+ limit = 1 + (64 * 2 ** 15)
while total < limit:
chan.send(K)
total += len(K)
@@ -695,8 +738,11 @@ class TransportTest(unittest.TestCase):
# expires, a deadlock is assumed.
class SendThread(threading.Thread):
+
def __init__(self, chan, iterations, done_event):
- threading.Thread.__init__(self, None, None, self.__class__.__name__)
+ threading.Thread.__init__(
+ self, None, None, self.__class__.__name__
+ )
self.setDaemon(True)
self.chan = chan
self.iterations = iterations
@@ -706,19 +752,22 @@ class TransportTest(unittest.TestCase):
def run(self):
try:
- for i in range(1, 1+self.iterations):
+ for i in range(1, 1 + self.iterations):
if self.done_event.is_set():
break
self.watchdog_event.set()
- #print i, "SEND"
+ # print i, "SEND"
self.chan.send("x" * 2048)
finally:
self.done_event.set()
self.watchdog_event.set()
class ReceiveThread(threading.Thread):
+
def __init__(self, chan, done_event):
- threading.Thread.__init__(self, None, None, self.__class__.__name__)
+ threading.Thread.__init__(
+ self, None, None, self.__class__.__name__
+ )
self.setDaemon(True)
self.chan = chan
self.done_event = done_event
@@ -741,30 +790,34 @@ class TransportTest(unittest.TestCase):
self.ts.packetizer.REKEY_BYTES = 2048
chan = self.tc.open_session()
- chan.exec_command('yes')
+ chan.exec_command("yes")
schan = self.ts.accept(1.0)
# Monkey patch the client's Transport._handler_table so that the client
# sends MSG_CHANNEL_WINDOW_ADJUST whenever it receives an initial
# MSG_KEXINIT. This is used to simulate the effect of network latency
# on a real MSG_CHANNEL_WINDOW_ADJUST message.
- self.tc._handler_table = self.tc._handler_table.copy() # copy per-class dictionary
+ self.tc._handler_table = (
+ self.tc._handler_table.copy()
+ ) # copy per-class dictionary
_negotiate_keys = self.tc._handler_table[MSG_KEXINIT]
+
def _negotiate_keys_wrapper(self, m):
- if self.local_kex_init is None: # Remote side sent KEXINIT
+ if self.local_kex_init is None: # Remote side sent KEXINIT
# Simulate in-transit MSG_CHANNEL_WINDOW_ADJUST by sending it
# before responding to the incoming MSG_KEXINIT.
m2 = Message()
m2.add_byte(cMSG_CHANNEL_WINDOW_ADJUST)
m2.add_int(chan.remote_chanid)
- m2.add_int(1) # bytes to add
+ m2.add_int(1) # bytes to add
self._send_message(m2)
return _negotiate_keys(self, m)
+
self.tc._handler_table[MSG_KEXINIT] = _negotiate_keys_wrapper
# Parameters for the test
- iterations = 500 # The deadlock does not happen every time, but it
- # should after many iterations.
+ iterations = 500 # The deadlock does not happen every time, but it
+ # should after many iterations.
timeout = 5
# This event is set when the test is completed
@@ -806,20 +859,25 @@ class TransportTest(unittest.TestCase):
"""
verify that we conform to the rfc of packet and window sizes.
"""
- for val, correct in [(4095, MIN_PACKET_SIZE),
- (None, DEFAULT_MAX_PACKET_SIZE),
- (2**32, MAX_WINDOW_SIZE)]:
+ for val, correct in [
+ (4095, MIN_PACKET_SIZE),
+ (None, DEFAULT_MAX_PACKET_SIZE),
+ (2 ** 32, MAX_WINDOW_SIZE),
+ ]:
self.assertEqual(self.tc._sanitize_packet_size(val), correct)
def test_K_sanitze_window_size(self):
"""
verify that we conform to the rfc of packet and window sizes.
"""
- for val, correct in [(32767, MIN_WINDOW_SIZE),
- (None, DEFAULT_WINDOW_SIZE),
- (2**32, MAX_WINDOW_SIZE)]:
+ for val, correct in [
+ (32767, MIN_WINDOW_SIZE),
+ (None, DEFAULT_WINDOW_SIZE),
+ (2 ** 32, MAX_WINDOW_SIZE),
+ ]:
self.assertEqual(self.tc._sanitize_window_size(val), correct)
+ @slow
def test_L_handshake_timeout(self):
"""
verify that we can get a hanshake timeout.
@@ -832,15 +890,17 @@ class TransportTest(unittest.TestCase):
# (Doing this on the server's transport *sounds* more 'correct' but
# actually doesn't work nearly as well for whatever reason.)
class SlowPacketizer(Packetizer):
+
def read_message(self):
time.sleep(1)
return super(SlowPacketizer, self).read_message()
+
# NOTE: prettttty sure since the replaced .packetizer Packetizer is now
# no longer doing anything with its copy of the socket...everything'll
# be fine. Even tho it's a bit squicky.
self.tc.packetizer = SlowPacketizer(self.tc.sock)
# Continue with regular test red tape.
- host_key = RSAKey.from_private_key_file(test_path('test_rsa.key'))
+ host_key = RSAKey.from_private_key_file(_support("test_rsa.key"))
public_host_key = RSAKey(data=host_key.asbytes())
self.ts.add_server_key(host_key)
event = threading.Event()
@@ -848,10 +908,13 @@ class TransportTest(unittest.TestCase):
self.assertTrue(not event.is_set())
self.tc.handshake_timeout = 0.000000000001
self.ts.start_server(event, server)
- self.assertRaises(EOFError, self.tc.connect,
- hostkey=public_host_key,
- username='slowdive',
- password='pygmalion')
+ self.assertRaises(
+ EOFError,
+ self.tc.connect,
+ hostkey=public_host_key,
+ username="slowdive",
+ password="pygmalion",
+ )
def test_M_select_after_close(self):
"""
@@ -892,13 +955,13 @@ class TransportTest(unittest.TestCase):
expected = text.encode("utf-8")
self.assertEqual(sfile.read(len(expected)), expected)
- @skipUnlessBuiltin('buffer')
+ @needs_builtin("buffer")
def test_channel_send_buffer(self):
"""
verify sending buffer instances to a channel
"""
self.setup_test_server()
- data = 3 * b'some test data\n whole'
+ data = 3 * b"some test data\n whole"
with self.tc.open_session() as chan:
schan = self.ts.accept(1.0)
if schan is None:
@@ -915,13 +978,13 @@ class TransportTest(unittest.TestCase):
chan.sendall(buffer(data))
self.assertEqual(sfile.read(len(data)), data)
- @skipUnlessBuiltin('memoryview')
+ @needs_builtin("memoryview")
def test_channel_send_memoryview(self):
"""
verify sending memoryview instances to a channel
"""
self.setup_test_server()
- data = 3 * b'some test data\n whole'
+ data = 3 * b"some test data\n whole"
with self.tc.open_session() as chan:
schan = self.ts.accept(1.0)
if schan is None:
@@ -932,7 +995,7 @@ class TransportTest(unittest.TestCase):
sent = 0
view = memoryview(data)
while sent < len(view):
- sent += chan.send(view[sent:sent+8])
+ sent += chan.send(view[sent : sent + 8])
self.assertEqual(sfile.read(len(data)), data)
# sendall() accepts a memoryview instance
@@ -952,7 +1015,7 @@ class TransportTest(unittest.TestCase):
self.setup_test_server(connect_kwargs={})
# NOTE: this dummy global request kind would normally pass muster
# from the test server.
- self.tc.global_request('acceptable')
+ self.tc.global_request("acceptable")
# Global requests never raise exceptions, even on failure (not sure why
# this was the original design...ugh.) Best we can do to tell failure
# happened is that the client transport's global_response was set back
@@ -967,7 +1030,7 @@ class TransportTest(unittest.TestCase):
# an exception on the client side, unlike the general case...)
self.setup_test_server(connect_kwargs={})
try:
- self.tc.request_port_forward('localhost', 1234)
+ self.tc.request_port_forward("localhost", 1234)
except SSHException as e:
assert "forwarding request denied" in str(e)
else:
diff --git a/tests/test_util.py b/tests/test_util.py
index 7880e156..23b2e86a 100644
--- a/tests/test_util.py
+++ b/tests/test_util.py
@@ -30,6 +30,7 @@ import paramiko.util
from paramiko.util import lookup_ssh_host_config as host_config, safe_string
from paramiko.py3compat import StringIO, byte_ord, b
+
# Note some lines in this configuration have trailing spaces on purpose
test_config_file = """\
Host *
@@ -66,53 +67,71 @@ from paramiko import *
class UtilTest(unittest.TestCase):
+
def test_import(self):
"""
verify that all the classes can be imported from paramiko.
"""
symbols = list(globals().keys())
- self.assertTrue('Transport' in symbols)
- self.assertTrue('SSHClient' in symbols)
- self.assertTrue('MissingHostKeyPolicy' in symbols)
- self.assertTrue('AutoAddPolicy' in symbols)
- self.assertTrue('RejectPolicy' in symbols)
- self.assertTrue('WarningPolicy' in symbols)
- self.assertTrue('SecurityOptions' in symbols)
- self.assertTrue('SubsystemHandler' in symbols)
- self.assertTrue('Channel' in symbols)
- self.assertTrue('RSAKey' in symbols)
- self.assertTrue('DSSKey' in symbols)
- self.assertTrue('Message' in symbols)
- self.assertTrue('SSHException' in symbols)
- self.assertTrue('AuthenticationException' in symbols)
- self.assertTrue('PasswordRequiredException' in symbols)
- self.assertTrue('BadAuthenticationType' in symbols)
- self.assertTrue('ChannelException' in symbols)
- self.assertTrue('SFTP' in symbols)
- self.assertTrue('SFTPFile' in symbols)
- self.assertTrue('SFTPHandle' in symbols)
- self.assertTrue('SFTPClient' in symbols)
- self.assertTrue('SFTPServer' in symbols)
- self.assertTrue('SFTPError' in symbols)
- self.assertTrue('SFTPAttributes' in symbols)
- self.assertTrue('SFTPServerInterface' in symbols)
- self.assertTrue('ServerInterface' in symbols)
- self.assertTrue('BufferedFile' in symbols)
- self.assertTrue('Agent' in symbols)
- self.assertTrue('AgentKey' in symbols)
- self.assertTrue('HostKeys' in symbols)
- self.assertTrue('SSHConfig' in symbols)
- self.assertTrue('util' in symbols)
+ self.assertTrue("Transport" in symbols)
+ self.assertTrue("SSHClient" in symbols)
+ self.assertTrue("MissingHostKeyPolicy" in symbols)
+ self.assertTrue("AutoAddPolicy" in symbols)
+ self.assertTrue("RejectPolicy" in symbols)
+ self.assertTrue("WarningPolicy" in symbols)
+ self.assertTrue("SecurityOptions" in symbols)
+ self.assertTrue("SubsystemHandler" in symbols)
+ self.assertTrue("Channel" in symbols)
+ self.assertTrue("RSAKey" in symbols)
+ self.assertTrue("DSSKey" in symbols)
+ self.assertTrue("Message" in symbols)
+ self.assertTrue("SSHException" in symbols)
+ self.assertTrue("AuthenticationException" in symbols)
+ self.assertTrue("PasswordRequiredException" in symbols)
+ self.assertTrue("BadAuthenticationType" in symbols)
+ self.assertTrue("ChannelException" in symbols)
+ self.assertTrue("SFTP" in symbols)
+ self.assertTrue("SFTPFile" in symbols)
+ self.assertTrue("SFTPHandle" in symbols)
+ self.assertTrue("SFTPClient" in symbols)
+ self.assertTrue("SFTPServer" in symbols)
+ self.assertTrue("SFTPError" in symbols)
+ self.assertTrue("SFTPAttributes" in symbols)
+ self.assertTrue("SFTPServerInterface" in symbols)
+ self.assertTrue("ServerInterface" in symbols)
+ self.assertTrue("BufferedFile" in symbols)
+ self.assertTrue("Agent" in symbols)
+ self.assertTrue("AgentKey" in symbols)
+ self.assertTrue("HostKeys" in symbols)
+ self.assertTrue("SSHConfig" in symbols)
+ self.assertTrue("util" in symbols)
def test_parse_config(self):
global test_config_file
f = StringIO(test_config_file)
config = paramiko.util.parse_ssh_config(f)
- self.assertEqual(config._config,
- [{'host': ['*'], 'config': {}}, {'host': ['*'], 'config': {'identityfile': ['~/.ssh/id_rsa'], 'user': 'robey'}},
- {'host': ['*.example.com'], 'config': {'user': 'bjork', 'port': '3333'}},
- {'host': ['*'], 'config': {'crazy': 'something dumb'}},
- {'host': ['spoo.example.com'], 'config': {'crazy': 'something else'}}])
+ self.assertEqual(
+ config._config,
+ [
+ {"host": ["*"], "config": {}},
+ {
+ "host": ["*"],
+ "config": {
+ "identityfile": ["~/.ssh/id_rsa"],
+ "user": "robey",
+ },
+ },
+ {
+ "host": ["*.example.com"],
+ "config": {"user": "bjork", "port": "3333"},
+ },
+ {"host": ["*"], "config": {"crazy": "something dumb"}},
+ {
+ "host": ["spoo.example.com"],
+ "config": {"crazy": "something else"},
+ },
+ ],
+ )
def test_host_config(self):
global test_config_file
@@ -120,44 +139,57 @@ class UtilTest(unittest.TestCase):
config = paramiko.util.parse_ssh_config(f)
for host, values in {
- 'irc.danger.com': {'crazy': 'something dumb',
- 'hostname': 'irc.danger.com',
- 'user': 'robey'},
- 'irc.example.com': {'crazy': 'something dumb',
- 'hostname': 'irc.example.com',
- 'user': 'robey',
- 'port': '3333'},
- 'spoo.example.com': {'crazy': 'something dumb',
- 'hostname': 'spoo.example.com',
- 'user': 'robey',
- 'port': '3333'}
+ "irc.danger.com": {
+ "crazy": "something dumb",
+ "hostname": "irc.danger.com",
+ "user": "robey",
+ },
+ "irc.example.com": {
+ "crazy": "something dumb",
+ "hostname": "irc.example.com",
+ "user": "robey",
+ "port": "3333",
+ },
+ "spoo.example.com": {
+ "crazy": "something dumb",
+ "hostname": "spoo.example.com",
+ "user": "robey",
+ "port": "3333",
+ },
}.items():
- values = dict(values,
+ values = dict(
+ values,
hostname=host,
- identityfile=[os.path.expanduser("~/.ssh/id_rsa")]
+ identityfile=[os.path.expanduser("~/.ssh/id_rsa")],
)
self.assertEqual(
- paramiko.util.lookup_ssh_host_config(host, config),
- values
+ paramiko.util.lookup_ssh_host_config(host, config), values
)
def test_generate_key_bytes(self):
- x = paramiko.util.generate_key_bytes(sha1, b'ABCDEFGH', 'This is my secret passphrase.', 64)
- hex = ''.join(['%02x' % byte_ord(c) for c in x])
- self.assertEqual(hex, '9110e2f6793b69363e58173e9436b13a5a4b339005741d5c680e505f57d871347b4239f14fb5c46e857d5e100424873ba849ac699cea98d729e57b3e84378e8b')
+ x = paramiko.util.generate_key_bytes(
+ sha1, b"ABCDEFGH", "This is my secret passphrase.", 64
+ )
+ hex = "".join(["%02x" % byte_ord(c) for c in x])
+ self.assertEqual(
+ hex,
+ "9110e2f6793b69363e58173e9436b13a5a4b339005741d5c680e505f57d871347b4239f14fb5c46e857d5e100424873ba849ac699cea98d729e57b3e84378e8b",
+ )
def test_host_keys(self):
- with open('hostfile.temp', 'w') as f:
+ with open("hostfile.temp", "w") as f:
f.write(test_hosts_file)
try:
- hostdict = paramiko.util.load_host_keys('hostfile.temp')
+ hostdict = paramiko.util.load_host_keys("hostfile.temp")
self.assertEqual(2, len(hostdict))
self.assertEqual(1, len(list(hostdict.values())[0]))
self.assertEqual(1, len(list(hostdict.values())[1]))
- fp = hexlify(hostdict['secure.example.com']['ssh-rsa'].get_fingerprint()).upper()
- self.assertEqual(b'E6684DB30E109B67B70FF1DC5C7F1363', fp)
+ fp = hexlify(
+ hostdict["secure.example.com"]["ssh-rsa"].get_fingerprint()
+ ).upper()
+ self.assertEqual(b"E6684DB30E109B67B70FF1DC5C7F1363", fp)
finally:
- os.unlink('hostfile.temp')
+ os.unlink("hostfile.temp")
def test_host_config_expose_issue_33(self):
test_config_file = """
@@ -172,36 +204,44 @@ Host *
"""
f = StringIO(test_config_file)
config = paramiko.util.parse_ssh_config(f)
- host = 'www13.example.com'
+ host = "www13.example.com"
self.assertEqual(
paramiko.util.lookup_ssh_host_config(host, config),
- {'hostname': host, 'port': '22'}
+ {"hostname": host, "port": "22"},
)
def test_eintr_retry(self):
- self.assertEqual('foo', paramiko.util.retry_on_signal(lambda: 'foo'))
+ self.assertEqual("foo", paramiko.util.retry_on_signal(lambda: "foo"))
# Variables that are set by raises_intr
intr_errors_remaining = [3]
call_count = [0]
+
def raises_intr():
call_count[0] += 1
if intr_errors_remaining[0] > 0:
intr_errors_remaining[0] -= 1
- raise IOError(errno.EINTR, 'file', 'interrupted system call')
+ raise IOError(errno.EINTR, "file", "interrupted system call")
+
self.assertTrue(paramiko.util.retry_on_signal(raises_intr) is None)
self.assertEqual(0, intr_errors_remaining[0])
self.assertEqual(4, call_count[0])
def raises_ioerror_not_eintr():
- raise IOError(errno.ENOENT, 'file', 'file not found')
- self.assertRaises(IOError,
- lambda: paramiko.util.retry_on_signal(raises_ioerror_not_eintr))
+ raise IOError(errno.ENOENT, "file", "file not found")
+
+ self.assertRaises(
+ IOError,
+ lambda: paramiko.util.retry_on_signal(raises_ioerror_not_eintr),
+ )
def raises_other_exception():
- raise AssertionError('foo')
- self.assertRaises(AssertionError,
- lambda: paramiko.util.retry_on_signal(raises_other_exception))
+ raise AssertionError("foo")
+
+ self.assertRaises(
+ AssertionError,
+ lambda: paramiko.util.retry_on_signal(raises_other_exception),
+ )
def test_proxycommand_config_equals_parsing(self):
"""
@@ -216,17 +256,18 @@ Host equals-delimited
"""
f = StringIO(conf)
config = paramiko.util.parse_ssh_config(f)
- for host in ('space-delimited', 'equals-delimited'):
+ for host in ("space-delimited", "equals-delimited"):
self.assertEqual(
- host_config(host, config)['proxycommand'],
- 'foo bar=biz baz'
+ host_config(host, config)["proxycommand"], "foo bar=biz baz"
)
def test_proxycommand_interpolation(self):
"""
ProxyCommand should perform interpolation on the value
"""
- config = paramiko.util.parse_ssh_config(StringIO("""
+ config = paramiko.util.parse_ssh_config(
+ StringIO(
+ """
Host specific
Port 37
ProxyCommand host %h port %p lol
@@ -237,28 +278,32 @@ Host portonly
Host *
Port 25
ProxyCommand host %h port %p
-"""))
+"""
+ )
+ )
for host, val in (
- ('foo.com', "host foo.com port 25"),
- ('specific', "host specific port 37 lol"),
- ('portonly', "host portonly port 155"),
+ ("foo.com", "host foo.com port 25"),
+ ("specific", "host specific port 37 lol"),
+ ("portonly", "host portonly port 155"),
):
- self.assertEqual(
- host_config(host, config)['proxycommand'],
- val
- )
+ self.assertEqual(host_config(host, config)["proxycommand"], val)
def test_proxycommand_tilde_expansion(self):
"""
Tilde (~) should be expanded inside ProxyCommand
"""
- config = paramiko.util.parse_ssh_config(StringIO("""
+ config = paramiko.util.parse_ssh_config(
+ StringIO(
+ """
Host test
ProxyCommand ssh -F ~/.ssh/test_config bastion nc %h %p
-"""))
+"""
+ )
+ )
self.assertEqual(
- 'ssh -F %s/.ssh/test_config bastion nc test 22' % os.path.expanduser('~'),
- host_config('test', config)['proxycommand']
+ "ssh -F %s/.ssh/test_config bastion nc test 22"
+ % os.path.expanduser("~"),
+ host_config("test", config)["proxycommand"],
)
def test_host_config_test_negation(self):
@@ -277,10 +322,10 @@ Host *
"""
f = StringIO(test_config_file)
config = paramiko.util.parse_ssh_config(f)
- host = 'www13.example.com'
+ host = "www13.example.com"
self.assertEqual(
paramiko.util.lookup_ssh_host_config(host, config),
- {'hostname': host, 'port': '8080'}
+ {"hostname": host, "port": "8080"},
)
def test_host_config_test_proxycommand(self):
@@ -295,20 +340,24 @@ Host proxy-without-equal-divisor
ProxyCommand foo=bar:%h-%p
"""
for host, values in {
- 'proxy-with-equal-divisor-and-space' :{'hostname': 'proxy-with-equal-divisor-and-space',
- 'proxycommand': 'foo=bar'},
- 'proxy-with-equal-divisor-and-no-space':{'hostname': 'proxy-with-equal-divisor-and-no-space',
- 'proxycommand': 'foo=bar'},
- 'proxy-without-equal-divisor' :{'hostname': 'proxy-without-equal-divisor',
- 'proxycommand':
- 'foo=bar:proxy-without-equal-divisor-22'}
+ "proxy-with-equal-divisor-and-space": {
+ "hostname": "proxy-with-equal-divisor-and-space",
+ "proxycommand": "foo=bar",
+ },
+ "proxy-with-equal-divisor-and-no-space": {
+ "hostname": "proxy-with-equal-divisor-and-no-space",
+ "proxycommand": "foo=bar",
+ },
+ "proxy-without-equal-divisor": {
+ "hostname": "proxy-without-equal-divisor",
+ "proxycommand": "foo=bar:proxy-without-equal-divisor-22",
+ },
}.items():
f = StringIO(test_config_file)
config = paramiko.util.parse_ssh_config(f)
self.assertEqual(
- paramiko.util.lookup_ssh_host_config(host, config),
- values
+ paramiko.util.lookup_ssh_host_config(host, config), values
)
def test_host_config_test_identityfile(self):
@@ -326,19 +375,21 @@ Host dsa2*
IdentityFile id_dsa22
"""
for host, values in {
- 'foo' :{'hostname': 'foo',
- 'identityfile': ['id_dsa0', 'id_dsa1']},
- 'dsa2' :{'hostname': 'dsa2',
- 'identityfile': ['id_dsa0', 'id_dsa1', 'id_dsa2', 'id_dsa22']},
- 'dsa22' :{'hostname': 'dsa22',
- 'identityfile': ['id_dsa0', 'id_dsa1', 'id_dsa22']}
+ "foo": {"hostname": "foo", "identityfile": ["id_dsa0", "id_dsa1"]},
+ "dsa2": {
+ "hostname": "dsa2",
+ "identityfile": ["id_dsa0", "id_dsa1", "id_dsa2", "id_dsa22"],
+ },
+ "dsa22": {
+ "hostname": "dsa22",
+ "identityfile": ["id_dsa0", "id_dsa1", "id_dsa22"],
+ },
}.items():
f = StringIO(test_config_file)
config = paramiko.util.parse_ssh_config(f)
self.assertEqual(
- paramiko.util.lookup_ssh_host_config(host, config),
- values
+ paramiko.util.lookup_ssh_host_config(host, config), values
)
def test_config_addressfamily_and_lazy_fqdn(self):
@@ -350,7 +401,9 @@ AddressFamily inet
IdentityFile something_%l_using_fqdn
"""
config = paramiko.util.parse_ssh_config(StringIO(test_config))
- assert config.lookup('meh') # will die during lookup() if bug regresses
+ assert config.lookup(
+ "meh"
+ ) # will die during lookup() if bug regresses
def test_clamp_value(self):
self.assertEqual(32768, paramiko.util.clamp_value(32767, 32768, 32769))
@@ -366,7 +419,9 @@ IdentityFile something_%l_using_fqdn
def test_get_hostnames(self):
f = StringIO(test_config_file)
config = paramiko.util.parse_ssh_config(f)
- self.assertEqual(config.get_hostnames(), set(['*', '*.example.com', 'spoo.example.com']))
+ self.assertEqual(
+ config.get_hostnames(), {"*", "*.example.com", "spoo.example.com"}
+ )
def test_quoted_host_names(self):
test_config_file = """\
@@ -383,27 +438,23 @@ Host param4 "p a r" "p" "par" para
Port 4444
"""
res = {
- 'param pam': {'hostname': 'param pam', 'port': '1111'},
- 'param': {'hostname': 'param', 'port': '1111'},
- 'pam': {'hostname': 'pam', 'port': '1111'},
-
- 'param2': {'hostname': 'param2', 'port': '2222'},
-
- 'param3': {'hostname': 'param3', 'port': '3333'},
- 'parara': {'hostname': 'parara', 'port': '3333'},
-
- 'param4': {'hostname': 'param4', 'port': '4444'},
- 'p a r': {'hostname': 'p a r', 'port': '4444'},
- 'p': {'hostname': 'p', 'port': '4444'},
- 'par': {'hostname': 'par', 'port': '4444'},
- 'para': {'hostname': 'para', 'port': '4444'},
+ "param pam": {"hostname": "param pam", "port": "1111"},
+ "param": {"hostname": "param", "port": "1111"},
+ "pam": {"hostname": "pam", "port": "1111"},
+ "param2": {"hostname": "param2", "port": "2222"},
+ "param3": {"hostname": "param3", "port": "3333"},
+ "parara": {"hostname": "parara", "port": "3333"},
+ "param4": {"hostname": "param4", "port": "4444"},
+ "p a r": {"hostname": "p a r", "port": "4444"},
+ "p": {"hostname": "p", "port": "4444"},
+ "par": {"hostname": "par", "port": "4444"},
+ "para": {"hostname": "para", "port": "4444"},
}
f = StringIO(test_config_file)
config = paramiko.util.parse_ssh_config(f)
for host, values in res.items():
self.assertEquals(
- paramiko.util.lookup_ssh_host_config(host, config),
- values
+ paramiko.util.lookup_ssh_host_config(host, config), values
)
def test_quoted_params_in_config(self):
@@ -419,62 +470,54 @@ Host param3 parara
IdentityFile "test rsa key"
"""
res = {
- 'param pam': {'hostname': 'param pam', 'identityfile': ['id_rsa']},
- 'param': {'hostname': 'param', 'identityfile': ['id_rsa']},
- 'pam': {'hostname': 'pam', 'identityfile': ['id_rsa']},
-
- 'param2': {'hostname': 'param2', 'identityfile': ['test rsa key']},
-
- 'param3': {'hostname': 'param3', 'identityfile': ['id_rsa', 'test rsa key']},
- 'parara': {'hostname': 'parara', 'identityfile': ['id_rsa', 'test rsa key']},
+ "param pam": {"hostname": "param pam", "identityfile": ["id_rsa"]},
+ "param": {"hostname": "param", "identityfile": ["id_rsa"]},
+ "pam": {"hostname": "pam", "identityfile": ["id_rsa"]},
+ "param2": {"hostname": "param2", "identityfile": ["test rsa key"]},
+ "param3": {
+ "hostname": "param3",
+ "identityfile": ["id_rsa", "test rsa key"],
+ },
+ "parara": {
+ "hostname": "parara",
+ "identityfile": ["id_rsa", "test rsa key"],
+ },
}
f = StringIO(test_config_file)
config = paramiko.util.parse_ssh_config(f)
for host, values in res.items():
self.assertEquals(
- paramiko.util.lookup_ssh_host_config(host, config),
- values
+ paramiko.util.lookup_ssh_host_config(host, config), values
)
def test_quoted_host_in_config(self):
conf = SSHConfig()
correct_data = {
- 'param': ['param'],
- '"param"': ['param'],
-
- 'param pam': ['param', 'pam'],
- '"param" "pam"': ['param', 'pam'],
- '"param" pam': ['param', 'pam'],
- 'param "pam"': ['param', 'pam'],
-
- 'param "pam" p': ['param', 'pam', 'p'],
- '"param" pam "p"': ['param', 'pam', 'p'],
-
- '"pa ram"': ['pa ram'],
- '"pa ram" pam': ['pa ram', 'pam'],
- 'param "p a m"': ['param', 'p a m'],
+ "param": ["param"],
+ '"param"': ["param"],
+ "param pam": ["param", "pam"],
+ '"param" "pam"': ["param", "pam"],
+ '"param" pam': ["param", "pam"],
+ 'param "pam"': ["param", "pam"],
+ 'param "pam" p': ["param", "pam", "p"],
+ '"param" pam "p"': ["param", "pam", "p"],
+ '"pa ram"': ["pa ram"],
+ '"pa ram" pam': ["pa ram", "pam"],
+ 'param "p a m"': ["param", "p a m"],
}
- incorrect_data = [
- 'param"',
- '"param',
- 'param "pam',
- 'param "pam" "p a',
- ]
+ incorrect_data = ['param"', '"param', 'param "pam', 'param "pam" "p a']
for host, values in correct_data.items():
- self.assertEquals(
- conf._get_hosts(host),
- values
- )
+ self.assertEquals(conf._get_hosts(host), values)
for host in incorrect_data:
self.assertRaises(Exception, conf._get_hosts, host)
def test_safe_string(self):
- vanilla = b("vanilla")
- has_bytes = b("has \7\3 bytes")
+ vanilla = b"vanilla"
+ has_bytes = b"has \7\3 bytes"
safe_vanilla = safe_string(vanilla)
safe_has_bytes = safe_string(has_bytes)
- expected_bytes = b("has %07%03 bytes")
- err = "{0!r} != {1!r}"
+ expected_bytes = b"has %07%03 bytes"
+ err = "{!r} != {!r}"
msg = err.format(safe_vanilla, vanilla)
assert safe_vanilla == vanilla, msg
msg = err.format(safe_has_bytes, expected_bytes)
@@ -489,15 +532,18 @@ Host proxycommand-with-equals-none
ProxyCommand=None
"""
for host, values in {
- 'proxycommand-standard-none': {'hostname': 'proxycommand-standard-none'},
- 'proxycommand-with-equals-none': {'hostname': 'proxycommand-with-equals-none'}
+ "proxycommand-standard-none": {
+ "hostname": "proxycommand-standard-none"
+ },
+ "proxycommand-with-equals-none": {
+ "hostname": "proxycommand-with-equals-none"
+ },
}.items():
f = StringIO(test_config_file)
config = paramiko.util.parse_ssh_config(f)
self.assertEqual(
- paramiko.util.lookup_ssh_host_config(host, config),
- values
+ paramiko.util.lookup_ssh_host_config(host, config), values
)
def test_proxycommand_none_masking(self):
@@ -520,12 +566,10 @@ Host *
# backwards compatibility reasons in 1.x/2.x) appear completely blank,
# as if the host had no ProxyCommand whatsoever.
# Threw another unrelated host in there just for sanity reasons.
- self.assertFalse('proxycommand' in config.lookup('specific-host'))
+ self.assertFalse("proxycommand" in config.lookup("specific-host"))
self.assertEqual(
- config.lookup('other-host')['proxycommand'],
- 'other-proxy'
+ config.lookup("other-host")["proxycommand"], "other-proxy"
)
self.assertEqual(
- config.lookup('some-random-host')['proxycommand'],
- 'default-proxy'
+ config.lookup("some-random-host")["proxycommand"], "default-proxy"
)
diff --git a/tests/util.py b/tests/util.py
index c1b43da8..4ca02374 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -1,6 +1,27 @@
-import os
+from os.path import dirname, realpath, join
-root_path = os.path.dirname(os.path.realpath(__file__))
+import pytest
-def test_path(filename):
- return os.path.join(root_path, filename)
+from paramiko.py3compat import builtins
+
+
+def _support(filename):
+ return join(dirname(realpath(__file__)), filename)
+
+
+# TODO: consider using pytest.importorskip('gssapi') instead? We presumably
+# still need CLI configurability for the Kerberos parameters, though, so can't
+# JUST key off presence of GSSAPI optional dependency...
+# TODO: anyway, s/True/os.environ.get('RUN_GSSAPI', False)/ or something.
+needs_gssapi = pytest.mark.skipif(True, reason="No GSSAPI to test")
+
+
+def needs_builtin(name):
+ """
+ Skip decorated test if builtin name does not exist.
+ """
+ reason = "Test requires a builtin '{}'".format(name)
+ return pytest.mark.skipif(not hasattr(builtins, name), reason=reason)
+
+
+slow = pytest.mark.slow