summaryrefslogtreecommitdiffhomepage
path: root/paramiko/transport.py
diff options
context:
space:
mode:
Diffstat (limited to 'paramiko/transport.py')
-rw-r--r--paramiko/transport.py82
1 files changed, 68 insertions, 14 deletions
diff --git a/paramiko/transport.py b/paramiko/transport.py
index fff11a1d..c43db6e2 100644
--- a/paramiko/transport.py
+++ b/paramiko/transport.py
@@ -42,7 +42,8 @@ from paramiko.common import xffffffff, cMSG_CHANNEL_OPEN, cMSG_IGNORE, \
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
+ MSG_CHANNEL_EOF, MSG_CHANNEL_CLOSE, MIN_PACKET_SIZE, MAX_WINDOW_SIZE, \
+ DEFAULT_WINDOW_SIZE, DEFAULT_MAX_PACKET_SIZE
from paramiko.compress import ZlibCompressor, ZlibDecompressor
from paramiko.dsskey import DSSKey
from paramiko.kex_gex import KexGex
@@ -57,7 +58,7 @@ from paramiko.server import ServerInterface
from paramiko.sftp_client import SFTPClient
from paramiko.ssh_exception import (SSHException, BadAuthenticationType,
ChannelException, ProxyCommandFailure)
-from paramiko.util import retry_on_signal
+from paramiko.util import retry_on_signal, clamp_value
from Crypto.Cipher import Blowfish, AES, DES3, ARC4
try:
@@ -135,7 +136,10 @@ class Transport (threading.Thread):
_modulus_pack = None
- def __init__(self, sock):
+ def __init__(self,
+ sock,
+ default_window_size=DEFAULT_WINDOW_SIZE,
+ default_max_packet_size=DEFAULT_MAX_PACKET_SIZE):
"""
Create a new SSH session over an existing socket, or socket-like
object. This only creates the `.Transport` object; it doesn't begin the
@@ -161,8 +165,19 @@ class Transport (threading.Thread):
address and used for communication. Exceptions from the ``socket``
call may be thrown in this case.
+ .. note:: Modifying the the window and packet sizes might have adverse
+ effects on your channels created from this transport. The
+ default values are the same as in the OpenSSH code base and have
+ been battle tested.
+
:param socket sock:
a socket or socket-like object to create the session over.
+ :param int default_window_size:
+ sets the default window size on the transport. (defaults to
+ 2097152)
+ :param int default_max_packet_size:
+ sets the default max packet size on the transport. (defaults to
+ 32768)
"""
self.active = False
@@ -232,8 +247,8 @@ class Transport (threading.Thread):
self.channel_events = {} # (id -> Event)
self.channels_seen = {} # (id -> True)
self._channel_counter = 1
- self.window_size = 65536
- self.max_packet_size = 34816
+ self.default_max_packet_size = default_max_packet_size
+ self.default_window_size = default_window_size
self._forward_agent_handler = None
self._x11_handler = None
self._tcp_handler = None
@@ -528,18 +543,29 @@ class Transport (threading.Thread):
"""
return self.active
- def open_session(self):
+ def open_session(self, window_size=None, max_packet_size=None):
"""
Request a new channel to the server, of type ``"session"``. This is
just an alias for calling `open_channel` with an argument of
``"session"``.
+ .. note:: Modifying the the window and packet sizes might have adverse
+ effects on the session created. The default values are the same
+ as in the OpenSSH code base and have been battle tested.
+
+ :param int window_size:
+ optional window size for this session.
+ :param int max_packet_size:
+ optional max packet size for this session.
+
:return: a new `.Channel`
:raises SSHException: if the request is rejected or the session ends
prematurely
"""
- return self.open_channel('session')
+ return self.open_channel('session',
+ window_size=window_size,
+ max_packet_size=max_packet_size)
def open_x11_channel(self, src_addr=None):
"""
@@ -581,13 +607,22 @@ class Transport (threading.Thread):
"""
return self.open_channel('forwarded-tcpip', dest_addr, src_addr)
- def open_channel(self, kind, dest_addr=None, src_addr=None):
+ def open_channel(self,
+ kind,
+ dest_addr=None,
+ src_addr=None,
+ window_size=None,
+ max_packet_size=None):
"""
Request a new channel to the server. `Channels <.Channel>` are
socket-like objects used for the actual transfer of data across the
session. You may only request a channel after negotiating encryption
(using `connect` or `start_client`) and authenticating.
+ .. note:: Modifying the the window and packet sizes might have adverse
+ effects on the channel created. The default values are the same
+ as in the OpenSSH code base and have been battle tested.
+
:param str kind:
the kind of channel requested (usually ``"session"``,
``"forwarded-tcpip"``, ``"direct-tcpip"``, or ``"x11"``)
@@ -597,6 +632,11 @@ class Transport (threading.Thread):
``"direct-tcpip"`` (ignored for other channel types)
:param src_addr: the source address of this port forwarding, if
``kind`` is ``"forwarded-tcpip"``, ``"direct-tcpip"``, or ``"x11"``
+ :param int window_size:
+ optional window size for this session.
+ :param int max_packet_size:
+ optional max packet size for this session.
+
:return: a new `.Channel` on success
:raises SSHException: if the request is rejected or the session ends
@@ -606,13 +646,15 @@ class Transport (threading.Thread):
raise SSHException('SSH session not active')
self.lock.acquire()
try:
+ window_size = self._sanitize_window_size(window_size)
+ max_packet_size = self._sanitize_packet_size(max_packet_size)
chanid = self._next_channel()
m = Message()
m.add_byte(cMSG_CHANNEL_OPEN)
m.add_string(kind)
m.add_int(chanid)
- m.add_int(self.window_size)
- m.add_int(self.max_packet_size)
+ m.add_int(window_size)
+ m.add_int(max_packet_size)
if (kind == 'forwarded-tcpip') or (kind == 'direct-tcpip'):
m.add_string(dest_addr[0])
m.add_int(dest_addr[1])
@@ -626,7 +668,7 @@ class Transport (threading.Thread):
self.channel_events[chanid] = event = threading.Event()
self.channels_seen[chanid] = True
chan._set_transport(self)
- chan._set_window(self.window_size, self.max_packet_size)
+ chan._set_window(window_size, max_packet_size)
finally:
self.lock.release()
self._send_user_message(m)
@@ -670,6 +712,7 @@ class Transport (threading.Thread):
:param callable handler:
optional handler for incoming forwarded connections, of the form
``func(Channel, (str, int), (str, int))``.
+
:return: the port number (`int`) allocated by the server
:raises SSHException: if the server refused the TCP forward request
@@ -1391,6 +1434,17 @@ class Transport (threading.Thread):
finally:
self.lock.release()
+ def _sanitize_window_size(self, window_size):
+ if window_size is None:
+ window_size = self.default_window_size
+ return clamp_value(MIN_PACKET_SIZE, window_size, MAX_WINDOW_SIZE)
+
+ def _sanitize_packet_size(self, max_packet_size):
+ if max_packet_size is None:
+ max_packet_size = self.default_max_packet_size
+ return clamp_value(MIN_PACKET_SIZE, max_packet_size, MAX_WINDOW_SIZE)
+
+
def run(self):
# (use the exposed "run" method, because if we specify a thread target
# of a private method, threading.Thread will keep a reference to it
@@ -1954,7 +2008,7 @@ class Transport (threading.Thread):
self._channels.put(my_chanid, chan)
self.channels_seen[my_chanid] = True
chan._set_transport(self)
- chan._set_window(self.window_size, self.max_packet_size)
+ chan._set_window(self.default_window_size, self.default_max_packet_size)
chan._set_remote_channel(chanid, initial_window_size, max_packet_size)
finally:
self.lock.release()
@@ -1962,8 +2016,8 @@ class Transport (threading.Thread):
m.add_byte(cMSG_CHANNEL_OPEN_SUCCESS)
m.add_int(chanid)
m.add_int(my_chanid)
- m.add_int(self.window_size)
- m.add_int(self.max_packet_size)
+ 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':