2011-10-23 03:47:50 -04:00
# Copyright (C) 2011 Jeff Forcier <jeff@bitprophet.org>
2004-01-04 05:26:00 -05:00
#
2011-10-23 03:00:43 -04:00
# This file is part of ssh.
2004-01-04 05:26:00 -05:00
#
2011-10-23 03:00:43 -04:00
# 'ssh' is free software; you can redistribute it and/or modify it under the
2004-01-04 05:26:00 -05:00
# 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.
#
2011-10-23 03:00:43 -04:00
# 'ssh' is distrubuted in the hope that it will be useful, but WITHOUT ANY
2004-01-04 05:26:00 -05:00
# 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
2011-10-23 03:00:43 -04:00
# along with 'ssh'; if not, write to the Free Software Foundation, Inc.,
2004-01-04 05:26:00 -05:00
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
2004-01-04 04:29:13 -05:00
"""
2005-08-09 03:40:07 -04:00
L { Transport } handles the core SSH2 protocol .
2004-01-04 04:29:13 -05:00
"""
2005-10-13 14:51:18 -04:00
import os
import socket
import string
import struct
import sys
import threading
import time
2005-07-17 16:06:29 -04:00
import weakref
2004-04-05 06:12:59 -04:00
2011-11-17 20:30:00 -05:00
import ssh
2011-10-23 03:00:43 -04:00
from ssh import util
from ssh . auth_handler import AuthHandler
from ssh . channel import Channel
from ssh . common import *
from ssh . compress import ZlibCompressor , ZlibDecompressor
from ssh . dsskey import DSSKey
from ssh . kex_gex import KexGex
from ssh . kex_group1 import KexGroup1
from ssh . message import Message
from ssh . packet import Packetizer , NeedRekeyException
from ssh . primes import ModulusPack
from ssh . rsakey import RSAKey
from ssh . server import ServerInterface
from ssh . sftp_client import SFTPClient
from ssh . ssh_exception import SSHException , BadAuthenticationType , ChannelException
2003-11-04 03:34:24 -05:00
2011-05-21 22:29:38 -04:00
from Crypto import Random
2009-07-19 18:04:54 -04:00
from Crypto . Cipher import Blowfish , AES , DES3 , ARC4
2006-02-03 13:08:29 -05:00
from Crypto . Hash import SHA , MD5
2009-07-19 18:04:54 -04:00
try :
from Crypto . Util import Counter
except ImportError :
2011-10-23 03:00:43 -04:00
from ssh . util import Counter
2003-11-04 03:34:24 -05:00
2004-03-08 04:47:47 -05:00
# for thread cleanup
_active_threads = [ ]
def _join_lingering_threads ( ) :
for thr in _active_threads :
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
thr . stop_thread ( )
2004-03-08 04:47:47 -05:00
import atexit
atexit . register ( _join_lingering_threads )
2004-08-30 16:22:10 -04:00
class SecurityOptions ( object ) :
"""
Simple object containing the security preferences of an ssh transport .
2004-08-30 22:44:56 -04:00
These are tuples of acceptable ciphers , digests , key types , and key
2004-08-30 16:22:10 -04:00
exchange algorithms , listed in order of preference .
2004-08-30 22:44:56 -04:00
Changing the contents and / or order of these fields affects the underlying
L { Transport } ( but only if you change them before starting the session ) .
2011-10-23 03:00:43 -04:00
If you try to add an algorithm that ssh doesn ' t recognize,
2004-08-30 22:44:56 -04:00
C { ValueError } will be raised . If you try to assign something besides a
2004-09-25 17:47:19 -04:00
tuple to one of the fields , C { TypeError } will be raised .
2004-08-30 16:22:10 -04:00
"""
2005-12-02 16:15:44 -05:00
__slots__ = [ ' ciphers ' , ' digests ' , ' key_types ' , ' kex ' , ' compression ' , ' _transport ' ]
2004-08-30 16:22:10 -04:00
def __init__ ( self , transport ) :
self . _transport = transport
2004-08-30 22:44:56 -04:00
def __repr__ ( self ) :
"""
Returns a string representation of this object , for debugging .
2004-09-03 18:39:20 -04:00
@rtype : str
2004-08-30 22:44:56 -04:00
"""
2011-10-23 03:00:43 -04:00
return ' <ssh.SecurityOptions for %s > ' % repr ( self . _transport )
2004-08-30 22:44:56 -04:00
2004-08-30 16:22:10 -04:00
def _get_ciphers ( self ) :
return self . _transport . _preferred_ciphers
def _get_digests ( self ) :
return self . _transport . _preferred_macs
def _get_key_types ( self ) :
return self . _transport . _preferred_keys
def _get_kex ( self ) :
return self . _transport . _preferred_kex
2009-11-02 00:33:13 -05:00
2005-12-02 16:15:44 -05:00
def _get_compression ( self ) :
return self . _transport . _preferred_compression
2004-08-30 16:22:10 -04:00
2004-08-30 22:44:56 -04:00
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 ' )
possible = getattr ( self . _transport , orig ) . keys ( )
2006-01-15 01:30:08 -05:00
forbidden = filter ( lambda n : n not in possible , x )
if len ( forbidden ) > 0 :
2004-08-30 22:44:56 -04:00
raise ValueError ( ' unknown cipher ' )
setattr ( self . _transport , name , x )
def _set_ciphers ( self , x ) :
self . _set ( ' _preferred_ciphers ' , ' _cipher_info ' , x )
def _set_digests ( self , x ) :
self . _set ( ' _preferred_macs ' , ' _mac_info ' , x )
def _set_key_types ( self , x ) :
self . _set ( ' _preferred_keys ' , ' _key_info ' , x )
2004-08-30 16:22:10 -04:00
def _set_kex ( self , x ) :
2004-08-30 22:44:56 -04:00
self . _set ( ' _preferred_kex ' , ' _kex_info ' , x )
2009-11-02 00:33:13 -05:00
2005-12-02 16:15:44 -05:00
def _set_compression ( self , x ) :
self . _set ( ' _preferred_compression ' , ' _compression_info ' , x )
2004-08-30 16:22:10 -04:00
ciphers = property ( _get_ciphers , _set_ciphers , None ,
" Symmetric encryption ciphers " )
digests = property ( _get_digests , _set_digests , None ,
" Digest (one-way hash) algorithms " )
key_types = property ( _get_key_types , _set_key_types , None ,
" Public-key algorithms " )
kex = property ( _get_kex , _set_kex , None , " Key exchange algorithms " )
2005-12-02 16:15:44 -05:00
compression = property ( _get_compression , _set_compression , None ,
" Compression algorithms " )
2004-08-30 16:22:10 -04:00
2008-03-23 04:21:10 -04:00
class ChannelMap ( object ) :
def __init__ ( self ) :
# (id -> Channel)
self . _map = weakref . WeakValueDictionary ( )
self . _lock = threading . Lock ( )
def put ( self , chanid , chan ) :
self . _lock . acquire ( )
try :
self . _map [ chanid ] = chan
finally :
self . _lock . release ( )
2009-11-02 00:33:13 -05:00
2008-03-23 04:21:10 -04:00
def get ( self , chanid ) :
self . _lock . acquire ( )
try :
return self . _map . get ( chanid , None )
finally :
self . _lock . release ( )
2009-11-02 00:33:13 -05:00
2008-03-23 04:21:10 -04:00
def delete ( self , chanid ) :
self . _lock . acquire ( )
try :
try :
del self . _map [ chanid ]
except KeyError :
pass
finally :
self . _lock . release ( )
2009-11-02 00:33:13 -05:00
2008-03-23 04:21:10 -04:00
def values ( self ) :
self . _lock . acquire ( )
try :
return self . _map . values ( )
finally :
self . _lock . release ( )
2009-11-02 00:33:13 -05:00
2008-03-23 04:21:10 -04:00
def __len__ ( self ) :
self . _lock . acquire ( )
try :
return len ( self . _map )
finally :
self . _lock . release ( )
2005-08-09 03:40:07 -04:00
class Transport ( threading . Thread ) :
2003-12-30 02:18:20 -05:00
"""
2005-08-09 03:40:07 -04:00
An SSH Transport attaches to a stream ( usually a socket ) , negotiates an
encrypted session , authenticates , and then creates stream tunnels , called
L { Channel } s , across the session . Multiple channels can be multiplexed
across a single session ( and often are , in the case of port forwardings ) .
2003-12-30 02:18:20 -05:00
"""
2005-08-09 03:40:07 -04:00
2003-12-30 02:18:20 -05:00
_PROTO_ID = ' 2.0 '
2011-11-17 20:30:00 -05:00
_CLIENT_ID = ' ssh_ %s ' % ( ssh . __version__ )
2003-11-04 03:34:24 -05:00
2009-07-19 18:04:54 -04:00
_preferred_ciphers = ( ' aes128-ctr ' , ' aes256-ctr ' , ' aes128-cbc ' , ' blowfish-cbc ' , ' aes256-cbc ' , ' 3des-cbc ' ,
' arcfour128 ' , ' arcfour256 ' )
2004-08-30 22:44:56 -04:00
_preferred_macs = ( ' hmac-sha1 ' , ' hmac-md5 ' , ' hmac-sha1-96 ' , ' hmac-md5-96 ' )
_preferred_keys = ( ' ssh-rsa ' , ' ssh-dss ' )
_preferred_kex = ( ' diffie-hellman-group1-sha1 ' , ' diffie-hellman-group-exchange-sha1 ' )
2005-12-02 16:15:44 -05:00
_preferred_compression = ( ' none ' , )
2009-11-02 00:33:13 -05:00
2003-12-27 22:20:42 -05:00
_cipher_info = {
2009-07-19 18:04:54 -04:00
' aes128-ctr ' : { ' class ' : AES , ' mode ' : AES . MODE_CTR , ' block-size ' : 16 , ' key-size ' : 16 } ,
' aes256-ctr ' : { ' class ' : AES , ' mode ' : AES . MODE_CTR , ' block-size ' : 16 , ' key-size ' : 32 } ,
2003-11-04 03:34:24 -05:00
' blowfish-cbc ' : { ' class ' : Blowfish , ' mode ' : Blowfish . MODE_CBC , ' block-size ' : 8 , ' key-size ' : 16 } ,
' aes128-cbc ' : { ' class ' : AES , ' mode ' : AES . MODE_CBC , ' block-size ' : 16 , ' key-size ' : 16 } ,
' aes256-cbc ' : { ' class ' : AES , ' mode ' : AES . MODE_CBC , ' block-size ' : 16 , ' key-size ' : 32 } ,
' 3des-cbc ' : { ' class ' : DES3 , ' mode ' : DES3 . MODE_CBC , ' block-size ' : 8 , ' key-size ' : 24 } ,
2009-07-19 18:04:54 -04:00
' arcfour128 ' : { ' class ' : ARC4 , ' mode ' : None , ' block-size ' : 8 , ' key-size ' : 16 } ,
' arcfour256 ' : { ' class ' : ARC4 , ' mode ' : None , ' block-size ' : 8 , ' key-size ' : 32 } ,
2003-11-04 03:34:24 -05:00
}
2003-12-27 22:20:42 -05:00
_mac_info = {
2003-11-04 03:34:24 -05:00
' hmac-sha1 ' : { ' class ' : SHA , ' size ' : 20 } ,
' hmac-sha1-96 ' : { ' class ' : SHA , ' size ' : 12 } ,
' hmac-md5 ' : { ' class ' : MD5 , ' size ' : 16 } ,
' hmac-md5-96 ' : { ' class ' : MD5 , ' size ' : 12 } ,
}
2004-08-30 22:44:56 -04:00
_key_info = {
' ssh-rsa ' : RSAKey ,
' ssh-dss ' : DSSKey ,
}
2003-12-27 22:20:42 -05:00
_kex_info = {
2003-11-04 03:34:24 -05:00
' diffie-hellman-group1-sha1 ' : KexGroup1 ,
' diffie-hellman-group-exchange-sha1 ' : KexGex ,
}
2009-11-02 00:33:13 -05:00
2005-12-02 16:15:44 -05:00
_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 ) ,
}
2003-11-04 03:34:24 -05:00
2003-12-30 02:18:20 -05:00
_modulus_pack = None
2003-11-04 03:34:24 -05:00
def __init__ ( self , sock ) :
2003-12-30 02:18:20 -05:00
"""
Create a new SSH session over an existing socket , or socket - like
object . This only creates the Transport object ; it doesn ' t begin the
SSH session yet . Use L { connect } or L { start_client } to begin a client
session , or L { start_server } to begin a server session .
If the object is not actually a socket , it must have the following
methods :
2004-09-03 18:39:20 -04:00
- C { send ( str ) } : Writes from 1 to C { len ( str ) } bytes , and
2003-12-30 02:18:20 -05:00
returns an int representing the number of bytes written . Returns
0 or raises C { EOFError } if the stream has been closed .
- C { recv ( int ) } : Reads from 1 to C { int } bytes and returns them as a
string . Returns 0 or raises C { EOFError } if the stream has been
closed .
2004-09-25 17:47:19 -04:00
- C { close ( ) } : Closes the socket .
- C { settimeout ( n ) } : Sets a ( float ) timeout on I / O operations .
2003-12-30 02:18:20 -05:00
2004-01-26 21:04:59 -05:00
For ease of use , you may also pass in an address ( as a tuple ) or a host
string as the C { sock } argument . ( A host string is a hostname with an
optional port ( separated by C { " : " } ) which will be converted into a
tuple of C { ( hostname , port ) } . ) A socket will be connected to this
address and used for communication . Exceptions from the C { socket } call
may be thrown in this case .
2003-12-30 02:18:20 -05:00
@param sock : a socket or socket - like object to create the session over .
@type sock : socket
2004-12-13 02:29:38 -05:00
"""
2009-02-16 04:24:26 -05:00
if isinstance ( sock , ( str , unicode ) ) :
2004-01-26 21:04:59 -05:00
# convert "host:port" into (host, port)
hl = sock . split ( ' : ' , 1 )
if len ( hl ) == 1 :
sock = ( hl [ 0 ] , 22 )
else :
sock = ( hl [ 0 ] , int ( hl [ 1 ] ) )
if type ( sock ) is tuple :
# connect to the given (host, port)
hostname , port = sock
2010-05-13 06:56:39 -04:00
reason = ' No suitable address family '
2009-11-01 21:58:23 -05:00
for ( family , socktype , proto , canonname , sockaddr ) in socket . getaddrinfo ( hostname , port , socket . AF_UNSPEC , socket . SOCK_STREAM ) :
2009-10-31 19:10:52 -04:00
if socktype == socket . SOCK_STREAM :
af = family
addr = sockaddr
2010-05-13 06:56:39 -04:00
sock = socket . socket ( af , socket . SOCK_STREAM )
try :
sock . connect ( ( hostname , port ) )
except socket . error , e :
reason = str ( e )
else :
break
2009-10-31 19:10:52 -04:00
else :
2010-05-13 06:56:39 -04:00
raise SSHException (
' Unable to connect to %s : %s ' % ( hostname , reason ) )
2004-01-26 21:04:59 -05:00
# okay, normal socket-ish flow here...
2005-07-17 16:06:29 -04:00
threading . Thread . __init__ ( self )
2009-02-16 04:24:26 -05:00
self . setDaemon ( True )
2010-08-02 18:13:08 -04:00
self . rng = rng
2003-11-04 03:34:24 -05:00
self . sock = sock
2004-04-06 04:16:02 -04:00
# Python < 2.3 doesn't have the settimeout method - RogerB
try :
2004-08-26 20:57:40 -04:00
# we set the timeout so we can check self.active periodically to
# see if we should bail. socket.timeout exception is never
# propagated.
2004-04-06 04:16:02 -04:00
self . sock . settimeout ( 0.1 )
except AttributeError :
pass
2006-01-15 01:30:08 -05:00
2003-11-04 03:34:24 -05:00
# negotiated crypto parameters
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
self . packetizer = Packetizer ( sock )
2003-12-30 02:18:20 -05:00
self . local_version = ' SSH- ' + self . _PROTO_ID + ' - ' + self . _CLIENT_ID
2003-11-04 03:34:24 -05:00
self . remote_version = ' '
self . local_cipher = self . remote_cipher = ' '
self . local_kex_init = self . remote_kex_init = None
2006-01-15 01:30:08 -05:00
self . local_mac = self . remote_mac = None
self . local_compression = self . remote_compression = None
2003-11-04 03:34:24 -05:00
self . session_id = None
2006-01-15 01:30:08 -05:00
self . host_key_type = None
self . host_key = None
2009-11-02 00:33:13 -05:00
2006-01-15 01:30:08 -05:00
# state used during negotiation
self . kex_engine = None
self . H = None
self . K = None
2003-12-27 22:20:42 -05:00
self . active = False
2004-04-07 11:52:07 -04:00
self . initial_kex_done = False
2005-05-14 02:21:36 -04:00
self . in_kex = False
2006-01-15 01:30:08 -05:00
self . authenticated = False
2006-07-23 19:55:48 -04:00
self . _expected_packet = tuple ( )
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
self . lock = threading . Lock ( ) # synchronization (always higher level than write_lock)
2006-01-15 01:30:08 -05:00
# tracking open channels
2008-03-23 04:21:10 -04:00
self . _channels = ChannelMap ( )
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
self . channel_events = { } # (id -> Event)
2005-10-13 14:51:18 -04:00
self . channels_seen = { } # (id -> True)
2006-11-20 19:11:17 -05:00
self . _channel_counter = 1
2003-11-04 03:34:24 -05:00
self . window_size = 65536
2005-10-30 16:13:36 -05:00
self . max_packet_size = 34816
2011-11-13 12:19:19 -05:00
self . _forward_agent_handler = None
2006-11-20 19:11:17 -05:00
self . _x11_handler = None
2006-12-02 21:33:34 -05:00
self . _tcp_handler = None
2006-01-15 01:30:08 -05:00
2003-12-30 02:18:20 -05:00
self . saved_exception = None
2004-08-26 20:57:40 -04:00
self . clear_to_send = threading . Event ( )
2005-10-29 16:47:04 -04:00
self . clear_to_send_lock = threading . Lock ( )
2009-07-19 19:00:39 -04:00
self . clear_to_send_timeout = 30.0
2011-10-23 03:00:43 -04:00
self . log_name = ' ssh.transport '
2005-02-28 02:09:02 -05:00
self . logger = util . get_logger ( self . log_name )
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
self . packetizer . set_log ( self . logger )
2005-08-09 03:40:07 -04:00
self . auth_handler = None
2006-01-15 01:30:08 -05:00
self . global_response = None # response Message from an arbitrary global request
self . completion_event = None # user-defined event callbacks
2006-04-12 16:36:12 -04:00
self . banner_timeout = 15 # how long (seconds) to wait for the SSH banner
2003-11-04 03:34:24 -05:00
# server mode:
2004-08-30 22:44:56 -04:00
self . server_mode = False
2004-08-26 20:57:40 -04:00
self . server_object = None
2003-11-04 03:34:24 -05:00
self . server_key_dict = { }
2003-11-09 16:14:21 -05:00
self . server_accepts = [ ]
self . server_accept_cv = threading . Condition ( self . lock )
2004-09-05 03:41:45 -04:00
self . subsystem_table = { }
2003-11-04 03:34:24 -05:00
2003-12-30 17:24:21 -05:00
def __repr__ ( self ) :
"""
Returns a string representation of this object , for debugging .
2004-09-03 18:39:20 -04:00
@rtype : str
2003-12-30 17:24:21 -05:00
"""
2011-10-23 03:00:43 -04:00
out = ' <ssh.Transport at %s ' % hex ( long ( id ( self ) ) & 0xffffffff L )
2003-12-30 17:24:21 -05:00
if not self . active :
2004-09-03 18:39:20 -04:00
out + = ' (unconnected) '
2003-12-30 17:24:21 -05:00
else :
2004-09-03 18:39:20 -04:00
if self . local_cipher != ' ' :
out + = ' (cipher %s , %d bits) ' % ( self . local_cipher ,
self . _cipher_info [ self . local_cipher ] [ ' key-size ' ] * 8 )
2005-08-09 03:40:07 -04:00
if self . is_authenticated ( ) :
2008-03-23 04:21:10 -04:00
out + = ' (active; %d open channel(s)) ' % len ( self . _channels )
2005-08-09 03:40:07 -04:00
elif self . initial_kex_done :
out + = ' (connected; awaiting auth) '
2004-09-03 18:39:20 -04:00
else :
2005-08-09 03:40:07 -04:00
out + = ' (connecting) '
2003-12-30 17:24:21 -05:00
out + = ' > '
return out
2009-11-02 00:33:13 -05:00
2006-01-19 01:55:03 -05:00
def atfork ( self ) :
"""
Terminate this Transport without closing the session . On posix
systems , if a Transport is open during process forking , both parent
and child will share the underlying socket , but only one process can
use the connection ( without corrupting the session ) . Use this method
to clean up a Transport object without disrupting the other process .
2009-11-02 00:33:13 -05:00
2006-02-19 23:04:47 -05:00
@since : 1.5 .3
2006-01-19 01:55:03 -05:00
"""
self . sock . close ( )
self . close ( )
2003-12-30 17:24:21 -05:00
2004-08-30 16:22:10 -04:00
def get_security_options ( self ) :
"""
Return a L { SecurityOptions } object which can be used to tweak the
encryption algorithms this transport will permit , and the order of
preference for them .
@return : an object that can be used to change the preferred algorithms
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
for encryption , digest ( hash ) , public key , and key exchange .
2004-08-30 16:22:10 -04:00
@rtype : L { SecurityOptions }
"""
return SecurityOptions ( self )
2003-11-04 03:34:24 -05:00
def start_client ( self , event = None ) :
2003-12-31 01:31:43 -05:00
"""
Negotiate a new SSH2 session as a client . This is the first step after
creating a new L { Transport } . A separate thread is created for protocol
2005-07-16 12:41:39 -04:00
negotiation .
2009-11-02 00:33:13 -05:00
2005-07-16 12:41:39 -04:00
If an event is passed in , this method returns immediately . When
negotiation is done ( successful or not ) , the given C { Event } will
2003-12-31 01:31:43 -05:00
be triggered . On failure , L { is_active } will return C { False } .
2009-11-02 00:33:13 -05:00
2005-07-16 12:41:39 -04:00
( Since 1.4 ) If C { event } is C { None } , this method will not return until
negotation is done . On success , the method returns normally .
Otherwise an SSHException is raised .
2003-12-31 01:31:43 -05:00
After a successful negotiation , you will usually want to authenticate ,
calling L { auth_password < Transport . auth_password > } or
L { auth_publickey < Transport . auth_publickey > } .
@note : L { connect } is a simpler method for connecting as a client .
2009-11-02 00:33:13 -05:00
2003-12-31 01:31:43 -05:00
@note : After calling this method ( or L { start_server } or L { connect } ) ,
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
you should no longer directly read from or write to the original
socket object .
2003-12-31 01:31:43 -05:00
2005-07-16 12:41:39 -04:00
@param event : an event to trigger when negotiation is complete
( optional )
2003-12-31 01:31:43 -05:00
@type event : threading . Event
2005-07-16 12:41:39 -04:00
@raise SSHException : if negotiation fails ( and no C { event } was passed
in )
2003-12-31 01:31:43 -05:00
"""
2005-02-28 02:16:22 -05:00
self . active = True
2005-07-16 12:41:39 -04:00
if event is not None :
# async, return immediately and let the app poll for completion
self . completion_event = event
self . start ( )
return
# synchronous, wait for a result
self . completion_event = event = threading . Event ( )
2003-11-04 03:34:24 -05:00
self . start ( )
2011-05-21 22:29:38 -04:00
Random . atfork ( )
2005-07-16 12:41:39 -04:00
while True :
event . wait ( 0.1 )
if not self . active :
e = self . get_exception ( )
if e is not None :
raise e
raise SSHException ( ' Negotiation failed. ' )
if event . isSet ( ) :
break
2003-11-04 03:34:24 -05:00
2004-08-26 20:57:40 -04:00
def start_server ( self , event = None , server = None ) :
2003-12-31 01:31:43 -05:00
"""
Negotiate a new SSH2 session as a server . This is the first step after
creating a new L { Transport } and setting up your server host key ( s ) . A
2005-07-16 12:41:39 -04:00
separate thread is created for protocol negotiation .
2009-11-02 00:33:13 -05:00
2005-07-16 12:41:39 -04:00
If an event is passed in , this method returns immediately . When
negotiation is done ( successful or not ) , the given C { Event } will
2003-12-31 01:31:43 -05:00
be triggered . On failure , L { is_active } will return C { False } .
2009-11-02 00:33:13 -05:00
2005-07-16 12:41:39 -04:00
( Since 1.4 ) If C { event } is C { None } , this method will not return until
negotation is done . On success , the method returns normally .
Otherwise an SSHException is raised .
2003-12-31 01:31:43 -05:00
After a successful negotiation , the client will need to authenticate .
Override the methods
2004-08-26 20:57:40 -04:00
L { get_allowed_auths < ServerInterface . get_allowed_auths > } ,
L { check_auth_none < ServerInterface . check_auth_none > } ,
L { check_auth_password < ServerInterface . check_auth_password > } , and
L { check_auth_publickey < ServerInterface . check_auth_publickey > } in the
given C { server } object to control the authentication process .
2003-12-31 01:31:43 -05:00
After a successful authentication , the client should request to open
2004-08-26 20:57:40 -04:00
a channel . Override
L { check_channel_request < ServerInterface . check_channel_request > } in the
given C { server } object to allow channels to be opened .
2003-12-31 01:31:43 -05:00
@note : After calling this method ( or L { start_client } or L { connect } ) ,
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
you should no longer directly read from or write to the original
socket object .
2003-12-31 01:31:43 -05:00
@param event : an event to trigger when negotiation is complete .
@type event : threading . Event
2004-08-26 20:57:40 -04:00
@param server : an object used to perform authentication and create
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
L { Channel } s .
2004-08-26 20:57:40 -04:00
@type server : L { server . ServerInterface }
2005-07-16 12:41:39 -04:00
@raise SSHException : if negotiation fails ( and no C { event } was passed
in )
2003-12-31 01:31:43 -05:00
"""
2004-08-26 20:57:40 -04:00
if server is None :
server = ServerInterface ( )
2004-08-30 22:44:56 -04:00
self . server_mode = True
2004-08-26 20:57:40 -04:00
self . server_object = server
2005-02-28 02:16:22 -05:00
self . active = True
2005-07-16 12:41:39 -04:00
if event is not None :
# async, return immediately and let the app poll for completion
self . completion_event = event
self . start ( )
return
# synchronous, wait for a result
self . completion_event = event = threading . Event ( )
2003-11-04 03:34:24 -05:00
self . start ( )
2005-07-16 12:41:39 -04:00
while True :
event . wait ( 0.1 )
if not self . active :
e = self . get_exception ( )
if e is not None :
raise e
raise SSHException ( ' Negotiation failed. ' )
if event . isSet ( ) :
break
2003-11-04 03:34:24 -05:00
def add_server_key ( self , key ) :
2003-12-30 02:18:20 -05:00
"""
Add a host key to the list of keys used for server mode . When behaving
as a server , the host key is used to sign certain packets during the
SSH2 negotiation , so that the client can trust that we are who we say
we are . Because this is used for signing , the key must contain private
2005-07-02 21:26:35 -04:00
key info , not just the public half . Only one key of each type ( RSA or
DSS ) is kept .
2009-11-02 00:33:13 -05:00
2003-12-30 02:18:20 -05:00
@param key : the host key to add , usually an L { RSAKey < rsakey . RSAKey > } or
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
L { DSSKey < dsskey . DSSKey > } .
2003-12-30 02:18:20 -05:00
@type key : L { PKey < pkey . PKey > }
"""
2003-11-04 03:34:24 -05:00
self . server_key_dict [ key . get_name ( ) ] = key
def get_server_key ( self ) :
2003-12-30 17:24:21 -05:00
"""
Return the active host key , in server mode . After negotiating with the
client , this method will return the negotiated host key . If only one
type of host key was set with L { add_server_key } , that ' s the only key
that will ever be returned . But in cases where you have set more than
one type of host key ( for example , an RSA key and a DSS key ) , the key
type will be negotiated by the client , and this method will return the
key of the type agreed on . If the host key has not been negotiated
yet , C { None } is returned . In client mode , the behavior is undefined .
@return : host key of the type negotiated by the client , or C { None } .
@rtype : L { PKey < pkey . PKey > }
"""
2003-11-04 03:34:24 -05:00
try :
return self . server_key_dict [ self . host_key_type ]
except KeyError :
2006-08-28 21:22:31 -04:00
pass
return None
2003-11-04 03:34:24 -05:00
2003-12-30 02:18:20 -05:00
def load_server_moduli ( filename = None ) :
2003-12-27 22:20:42 -05:00
"""
I { ( optional ) }
Load a file of prime moduli for use in doing group - exchange key
negotiation in server mode . It ' s a rather obscure option and can be
safely ignored .
In server mode , the remote client may request " group-exchange " key
negotiation , which asks the server to send a random prime number that
fits certain criteria . These primes are pretty difficult to compute ,
so they can ' t be generated on demand. But many systems contain a file
of suitable primes ( usually named something like C { / etc / ssh / moduli } ) .
If you call C { load_server_moduli } and it returns C { True } , then this
file of primes has been loaded and we will support " group-exchange " in
server mode . Otherwise server mode will just claim that it doesn ' t
support that method of key negotiation .
@param filename : optional path to the moduli file , if you happen to
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
know that it ' s not in a standard location.
2004-09-03 18:39:20 -04:00
@type filename : str
2003-12-27 22:20:42 -05:00
@return : True if a moduli file was successfully loaded ; False
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
otherwise .
2004-04-05 06:37:18 -04:00
@rtype : bool
2009-11-02 00:33:13 -05:00
2003-12-27 22:20:42 -05:00
@note : This has no effect when used in client mode .
"""
2010-08-02 18:13:08 -04:00
Transport . _modulus_pack = ModulusPack ( rng )
2003-12-27 22:20:42 -05:00
# places to look for the openssh "moduli" file
file_list = [ ' /etc/ssh/moduli ' , ' /usr/local/etc/moduli ' ]
if filename is not None :
file_list . insert ( 0 , filename )
for fn in file_list :
try :
2005-08-09 03:40:07 -04:00
Transport . _modulus_pack . read_file ( fn )
2003-12-27 22:20:42 -05:00
return True
except IOError :
pass
# none succeeded
2005-08-09 03:40:07 -04:00
Transport . _modulus_pack = None
2003-12-27 22:20:42 -05:00
return False
2003-12-30 02:18:20 -05:00
load_server_moduli = staticmethod ( load_server_moduli )
2003-12-27 22:20:42 -05:00
2003-11-04 03:34:24 -05:00
def close ( self ) :
2003-12-27 22:20:42 -05:00
"""
Close this session , and any open channels that are tied to it .
"""
2006-11-12 01:30:54 -05:00
if not self . active :
return
2003-12-27 22:20:42 -05:00
self . active = False
2006-02-08 22:03:47 -05:00
self . packetizer . close ( )
2006-12-02 21:33:34 -05:00
self . join ( )
2008-03-23 04:21:10 -04:00
for chan in self . _channels . values ( ) :
2006-02-08 22:03:47 -05:00
chan . _unlink ( )
2003-11-04 03:34:24 -05:00
2003-11-09 16:14:21 -05:00
def get_remote_server_key ( self ) :
2003-12-27 22:20:42 -05:00
"""
Return the host key of the server ( in client mode ) .
2004-04-05 18:32:03 -04:00
@note : Previously this call returned a tuple of ( key type , key string ) .
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
You can get the same effect by calling
L { PKey . get_name < pkey . PKey . get_name > } for the key type , and
C { str ( key ) } for the key string .
2003-12-27 22:20:42 -05:00
@raise SSHException : if no session is currently active .
2009-11-02 00:33:13 -05:00
2006-05-03 22:52:37 -04:00
@return : public key of the remote server
2004-04-08 02:31:08 -04:00
@rtype : L { PKey < pkey . PKey > }
2003-12-27 22:20:42 -05:00
"""
2003-11-04 03:34:24 -05:00
if ( not self . active ) or ( not self . initial_kex_done ) :
2003-11-10 03:49:50 -05:00
raise SSHException ( ' No existing session ' )
2004-04-05 18:32:03 -04:00
return self . host_key
2003-11-04 03:34:24 -05:00
def is_active ( self ) :
2003-12-27 22:20:42 -05:00
"""
Return true if this session is active ( open ) .
2003-11-04 03:34:24 -05:00
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
@return : True if the session is still active ( open ) ; False if the
2006-05-03 22:52:37 -04:00
session is closed
2004-04-05 06:37:18 -04:00
@rtype : bool
2003-12-27 22:20:42 -05:00
"""
return self . active
2003-11-04 03:34:24 -05:00
def open_session ( self ) :
2003-12-31 01:31:43 -05:00
"""
Request a new channel to the server , of type C { " session " } . This
is just an alias for C { open_channel ( ' session ' ) } .
2006-05-03 22:52:37 -04:00
@return : a new L { Channel }
2003-12-31 01:31:43 -05:00
@rtype : L { Channel }
2009-11-02 00:33:13 -05:00
2006-05-03 22:52:37 -04:00
@raise SSHException : if the request is rejected or the session ends
prematurely
2003-12-31 01:31:43 -05:00
"""
2003-11-04 03:34:24 -05:00
return self . open_channel ( ' session ' )
2006-11-20 19:11:17 -05:00
def open_x11_channel ( self , src_addr = None ) :
"""
Request a new channel to the client , of type C { " x11 " } . This
is just an alias for C { open_channel ( ' x11 ' , src_addr = src_addr ) } .
@param src_addr : the source address of the x11 server ( port is the
x11 port , ie . 6010 )
@type src_addr : ( str , int )
@return : a new L { Channel }
@rtype : L { Channel }
2009-11-02 00:33:13 -05:00
2006-11-20 19:11:17 -05:00
@raise SSHException : if the request is rejected or the session ends
prematurely
"""
return self . open_channel ( ' x11 ' , src_addr = src_addr )
2011-11-17 18:33:22 -05:00
2011-11-13 12:19:19 -05:00
def open_forward_agent_channel ( self ) :
2011-11-17 18:33:22 -05:00
"""
Request a new channel to the client , of type
C { " auth-agent@openssh.com " } .
This is just an alias for C { open_channel ( ' auth-agent@openssh.com ' ) } .
@return : a new L { Channel }
@rtype : L { Channel }
@raise SSHException : if the request is rejected or the session ends
prematurely
"""
return self . open_channel ( ' auth-agent@openssh.com ' )
2009-11-02 00:33:13 -05:00
2007-02-12 13:30:21 -05:00
def open_forwarded_tcpip_channel ( self , ( src_addr , src_port ) , ( dest_addr , dest_port ) ) :
"""
Request a new channel back to the client , of type C { " forwarded-tcpip " } .
This is used after a client has requested port forwarding , for sending
incoming connections back to the client .
2009-11-02 00:33:13 -05:00
2007-02-12 13:30:21 -05:00
@param src_addr : originator ' s address
@param src_port : originator ' s port
@param dest_addr : local ( server ) connected address
@param dest_port : local ( server ) connected port
"""
return self . open_channel ( ' forwarded-tcpip ' , ( dest_addr , dest_port ) , ( src_addr , src_port ) )
2009-11-02 00:33:13 -05:00
2004-05-29 14:56:10 -04:00
def open_channel ( self , kind , dest_addr = None , src_addr = None ) :
2003-12-31 01:31:43 -05:00
"""
Request a new channel to the server . L { Channel } s are socket - like
objects used for the actual transfer of data across the session .
You may only request a channel after negotiating encryption ( using
2004-12-13 02:29:38 -05:00
L { connect } or L { start_client } ) and authenticating .
2003-12-31 01:31:43 -05:00
2004-05-29 14:56:10 -04:00
@param kind : the kind of channel requested ( usually C { " session " } ,
2006-12-02 21:33:34 -05:00
C { " forwarded-tcpip " } , C { " direct-tcpip " } , or C { " x11 " } )
2004-05-29 14:56:10 -04:00
@type kind : str
@param dest_addr : the destination address of this port forwarding ,
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
if C { kind } is C { " forwarded-tcpip " } or C { " direct-tcpip " } ( ignored
2006-05-03 22:52:37 -04:00
for other channel types )
2004-05-29 14:56:10 -04:00
@type dest_addr : ( str , int )
@param src_addr : the source address of this port forwarding , if
2006-12-02 21:33:34 -05:00
C { kind } is C { " forwarded-tcpip " } , C { " direct-tcpip " } , or C { " x11 " }
2004-05-29 14:56:10 -04:00
@type src_addr : ( str , int )
2006-05-03 22:52:37 -04:00
@return : a new L { Channel } on success
2003-12-31 01:31:43 -05:00
@rtype : L { Channel }
2006-05-03 22:52:37 -04:00
@raise SSHException : if the request is rejected or the session ends
prematurely
2003-12-31 01:31:43 -05:00
"""
2004-09-03 18:39:20 -04:00
if not self . active :
2010-04-25 19:42:45 -04:00
raise SSHException ( ' SSH session not active ' )
2004-11-22 02:04:31 -05:00
self . lock . acquire ( )
2003-11-04 03:34:24 -05:00
try :
2006-11-20 19:11:17 -05:00
chanid = self . _next_channel ( )
2003-11-04 03:34:24 -05:00
m = Message ( )
2004-04-05 06:12:59 -04:00
m . add_byte ( chr ( MSG_CHANNEL_OPEN ) )
2003-11-04 03:34:24 -05:00
m . add_string ( kind )
m . add_int ( chanid )
m . add_int ( self . window_size )
m . add_int ( self . max_packet_size )
2004-05-29 14:56:10 -04:00
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 ] )
2006-11-20 19:11:17 -05:00
elif kind == ' x11 ' :
m . add_string ( src_addr [ 0 ] )
m . add_int ( src_addr [ 1 ] )
2008-03-23 04:21:10 -04:00
chan = Channel ( chanid )
self . _channels . put ( chanid , chan )
2003-11-04 03:34:24 -05:00
self . channel_events [ chanid ] = event = threading . Event ( )
2005-10-13 14:51:18 -04:00
self . channels_seen [ chanid ] = True
2003-12-27 22:20:42 -05:00
chan . _set_transport ( self )
2003-12-30 02:18:20 -05:00
chan . _set_window ( self . window_size , self . max_packet_size )
2003-11-04 03:34:24 -05:00
finally :
self . lock . release ( )
2005-10-29 16:47:04 -04:00
self . _send_user_message ( m )
2006-05-03 22:52:37 -04:00
while True :
2003-11-04 03:34:24 -05:00
event . wait ( 0.1 ) ;
if not self . active :
2006-05-03 22:52:37 -04:00
e = self . get_exception ( )
if e is None :
e = SSHException ( ' Unable to open channel. ' )
raise e
2003-11-04 03:34:24 -05:00
if event . isSet ( ) :
break
2008-03-23 04:21:10 -04:00
chan = self . _channels . get ( chanid )
if chan is not None :
return chan
2006-05-03 22:52:37 -04:00
e = self . get_exception ( )
if e is None :
e = SSHException ( ' Unable to open channel. ' )
raise e
2004-04-01 21:41:43 -05:00
2006-12-02 21:33:34 -05:00
def request_port_forward ( self , address , port , handler = None ) :
"""
Ask the server to forward TCP connections from a listening port on
the server , across this SSH session .
2009-11-02 00:33:13 -05:00
2006-12-02 21:33:34 -05:00
If a handler is given , that handler is called from a different thread
whenever a forwarded connection arrives . The handler parameters are : :
2009-11-02 00:33:13 -05:00
2006-12-02 21:33:34 -05:00
handler ( channel , ( origin_addr , origin_port ) , ( server_addr , server_port ) )
2009-11-02 00:33:13 -05:00
2006-12-02 21:33:34 -05:00
where C { server_addr } and C { server_port } are the address and port that
the server was listening on .
2009-11-02 00:33:13 -05:00
2006-12-02 21:33:34 -05:00
If no handler is set , the default behavior is to send new incoming
forwarded connections into the accept queue , to be picked up via
L { accept } .
2009-11-02 00:33:13 -05:00
2006-12-02 21:33:34 -05:00
@param address : the address to bind when forwarding
@type address : str
@param port : the port to forward , or 0 to ask the server to allocate
any port
@type port : int
@param handler : optional handler for incoming forwarded connections
@type handler : function ( Channel , ( str , int ) , ( str , int ) )
@return : the port # allocated by the server
@rtype : int
2009-11-02 00:33:13 -05:00
2006-12-02 21:33:34 -05:00
@raise SSHException : if the server refused the TCP forward request
"""
if not self . active :
raise SSHException ( ' SSH session not active ' )
address = str ( address )
port = int ( port )
response = self . global_request ( ' tcpip-forward ' , ( address , port ) , wait = True )
if response is None :
raise SSHException ( ' TCP forwarding request denied ' )
if port == 0 :
port = response . get_int ( )
if handler is None :
def default_handler ( channel , ( src_addr , src_port ) , ( dest_addr , dest_port ) ) :
self . _queue_incoming_channel ( channel )
handler = default_handler
self . _tcp_handler = handler
return port
def cancel_port_forward ( self , address , port ) :
"""
Ask the server to cancel a previous port - forwarding request . No more
connections to the given address & port will be forwarded across this
ssh connection .
2009-11-02 00:33:13 -05:00
2006-12-02 21:33:34 -05:00
@param address : the address to stop forwarding
@type address : str
@param port : the port to stop forwarding
@type port : int
"""
if not self . active :
return
self . _tcp_handler = None
2007-02-12 13:30:21 -05:00
self . global_request ( ' cancel-tcpip-forward ' , ( address , port ) , wait = True )
2009-11-02 00:33:13 -05:00
2005-04-30 15:21:43 -04:00
def open_sftp_client ( self ) :
"""
Create an SFTP client channel from an open transport . On success ,
an SFTP session will be opened with the remote host , and a new
SFTPClient object will be returned .
@return : a new L { SFTPClient } object , referring to an sftp session
( channel ) across this transport
@rtype : L { SFTPClient }
"""
return SFTPClient . from_transport ( self )
2004-04-01 21:41:43 -05:00
def send_ignore ( self , bytes = None ) :
"""
Send a junk packet across the encrypted link . This is sometimes used
to add " noise " to a connection to confuse would - be attackers . It can
also be used as a keep - alive for long lived connections traversing
firewalls .
@param bytes : the number of random bytes to send in the payload of the
2005-07-02 21:26:35 -04:00
ignored packet - - defaults to a random number from 10 to 41.
2004-04-01 21:41:43 -05:00
@type bytes : int
"""
m = Message ( )
2004-04-05 06:12:59 -04:00
m . add_byte ( chr ( MSG_IGNORE ) )
2004-04-01 21:41:43 -05:00
if bytes is None :
2010-08-02 18:13:08 -04:00
bytes = ( ord ( rng . read ( 1 ) ) % 32 ) + 10
m . add_bytes ( rng . read ( bytes ) )
2004-08-26 20:57:40 -04:00
self . _send_user_message ( m )
2004-04-01 21:41:43 -05:00
2003-12-30 02:18:20 -05:00
def renegotiate_keys ( self ) :
"""
Force this session to switch to new keys . Normally this is done
automatically after the session hits a certain number of packets or
bytes sent or received , but this method gives you the option of forcing
new keys whenever you want . Negotiating new keys causes a pause in
traffic both ways as the two sides swap keys and do computations . This
2006-05-03 22:52:37 -04:00
method returns when the session has switched to new keys .
2003-12-30 02:18:20 -05:00
2006-05-03 22:52:37 -04:00
@raise SSHException : if the key renegotiation failed ( which causes the
session to end )
2003-12-30 02:18:20 -05:00
"""
self . completion_event = threading . Event ( )
self . _send_kex_init ( )
2006-05-03 22:52:37 -04:00
while True :
self . completion_event . wait ( 0.1 )
2003-12-30 02:18:20 -05:00
if not self . active :
2006-05-03 22:52:37 -04:00
e = self . get_exception ( )
if e is not None :
raise e
raise SSHException ( ' Negotiation failed. ' )
2003-12-30 02:18:20 -05:00
if self . completion_event . isSet ( ) :
break
2006-05-03 22:52:37 -04:00
return
2003-12-30 02:18:20 -05:00
2004-04-07 11:52:07 -04:00
def set_keepalive ( self , interval ) :
"""
Turn on / off keepalive packets ( default is off ) . If this is set , after
C { interval } seconds without sending any data over the connection , a
" keepalive " packet will be sent ( and ignored by the remote host ) . This
can be useful to keep connections alive over a NAT , for example .
2009-11-02 00:33:13 -05:00
2004-04-07 11:52:07 -04:00
@param interval : seconds to wait before sending a keepalive packet ( or
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
0 to disable keepalives ) .
2004-04-07 11:52:07 -04:00
@type interval : int
"""
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
self . packetizer . set_keepalive ( interval ,
2006-02-09 02:35:16 -05:00
lambda x = weakref . proxy ( self ) : x . global_request ( ' keepalive@lag.net ' , wait = False ) )
2004-04-07 11:52:07 -04:00
2004-04-05 06:37:18 -04:00
def global_request ( self , kind , data = None , wait = True ) :
"""
Make a global request to the remote host . These are normally
extensions to the SSH2 protocol .
@param kind : name of the request .
2004-09-03 18:39:20 -04:00
@type kind : str
2004-04-05 06:37:18 -04:00
@param data : an optional tuple containing additional data to attach
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
to the request .
2004-04-05 06:37:18 -04:00
@type data : tuple
@param wait : C { True } if this method should not return until a response
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
is received ; C { False } otherwise .
2004-04-05 06:37:18 -04:00
@type wait : bool
@return : a L { Message } containing possible additional data if the
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
request was successful ( or an empty L { Message } if C { wait } was
C { False } ) ; C { None } if the request was denied .
2004-04-05 06:37:18 -04:00
@rtype : L { Message }
"""
if wait :
self . completion_event = threading . Event ( )
m = Message ( )
m . add_byte ( chr ( MSG_GLOBAL_REQUEST ) )
m . add_string ( kind )
m . add_boolean ( wait )
if data is not None :
2005-07-06 21:10:57 -04:00
m . add ( * data )
2004-04-07 11:52:07 -04:00
self . _log ( DEBUG , ' Sending global request " %s " ' % kind )
2004-08-26 20:57:40 -04:00
self . _send_user_message ( m )
2004-04-05 06:37:18 -04:00
if not wait :
2005-06-28 03:23:41 -04:00
return None
2004-04-05 06:37:18 -04:00
while True :
self . completion_event . wait ( 0.1 )
if not self . active :
2005-06-28 03:23:41 -04:00
return None
2004-04-05 06:37:18 -04:00
if self . completion_event . isSet ( ) :
break
return self . global_response
2003-12-30 02:18:20 -05:00
def accept ( self , timeout = None ) :
2005-07-02 21:26:35 -04:00
"""
Return the next channel opened by the client over this transport , in
server mode . If no channel is opened before the given timeout , C { None }
is returned .
2009-11-02 00:33:13 -05:00
2005-07-02 21:26:35 -04:00
@param timeout : seconds to wait for a channel , or C { None } to wait
forever
@type timeout : int
@return : a new Channel opened by the client
@rtype : L { Channel }
"""
2004-12-10 22:43:18 -05:00
self . lock . acquire ( )
2003-12-30 02:18:20 -05:00
try :
if len ( self . server_accepts ) > 0 :
chan = self . server_accepts . pop ( 0 )
else :
self . server_accept_cv . wait ( timeout )
if len ( self . server_accepts ) > 0 :
chan = self . server_accepts . pop ( 0 )
else :
# timeout
chan = None
finally :
self . lock . release ( )
return chan
2004-10-18 00:54:27 -04:00
def connect ( self , hostkey = None , username = ' ' , password = None , pkey = None ) :
2003-12-30 02:18:20 -05:00
"""
Negotiate an SSH2 session , and optionally verify the server ' s host key
and authenticate using a password or private key . This is a shortcut
for L { start_client } , L { get_remote_server_key } , and
2003-12-31 01:31:43 -05:00
L { Transport . auth_password } or L { Transport . auth_publickey } . Use those
methods if you want more control .
2003-12-30 02:18:20 -05:00
You can use this method immediately after creating a Transport to
negotiate encryption with a server . If it fails , an exception will be
thrown . On success , the method will return cleanly , and an encrypted
session exists . You may immediately call L { open_channel } or
L { open_session } to get a L { Channel } object , which is used for data
transfer .
@note : If you fail to supply a password or private key , this method may
succeed , but a subsequent L { open_channel } or L { open_session } call may
fail because you haven ' t authenticated yet.
@param hostkey : the host key expected from the server , or C { None } if
2005-02-26 16:11:04 -05:00
you don ' t want to do host key verification.
2004-10-18 00:54:27 -04:00
@type hostkey : L { PKey < pkey . PKey > }
2003-12-30 02:18:20 -05:00
@param username : the username to authenticate as .
2004-09-03 18:39:20 -04:00
@type username : str
2003-12-30 02:18:20 -05:00
@param password : a password to use for authentication , if you want to
2005-02-26 16:11:04 -05:00
use password authentication ; otherwise C { None } .
2004-09-03 18:39:20 -04:00
@type password : str
2003-12-30 02:18:20 -05:00
@param pkey : a private key to use for authentication , if you want to
2005-02-26 16:11:04 -05:00
use private key authentication ; otherwise C { None } .
2003-12-30 02:18:20 -05:00
@type pkey : L { PKey < pkey . PKey > }
2009-11-02 00:33:13 -05:00
2003-12-30 02:18:20 -05:00
@raise SSHException : if the SSH2 negotiation fails , the host key
2005-02-26 16:11:04 -05:00
supplied by the server is incorrect , or authentication fails .
2003-12-30 02:18:20 -05:00
"""
2004-10-18 00:54:27 -04:00
if hostkey is not None :
self . _preferred_keys = [ hostkey . get_name ( ) ]
2003-11-04 03:34:24 -05:00
2005-07-16 12:41:39 -04:00
self . start_client ( )
2003-12-30 02:18:20 -05:00
# check host key if we were given one
2004-10-18 00:54:27 -04:00
if ( hostkey is not None ) :
2004-05-29 14:56:10 -04:00
key = self . get_remote_server_key ( )
2004-10-18 00:54:27 -04:00
if ( key . get_name ( ) != hostkey . get_name ( ) ) or ( str ( key ) != str ( hostkey ) ) :
2004-04-07 02:07:29 -04:00
self . _log ( DEBUG , ' Bad host key from server ' )
2004-10-18 00:54:27 -04:00
self . _log ( DEBUG , ' Expected: %s : %s ' % ( hostkey . get_name ( ) , repr ( str ( hostkey ) ) ) )
2004-05-29 14:56:10 -04:00
self . _log ( DEBUG , ' Got : %s : %s ' % ( key . get_name ( ) , repr ( str ( key ) ) ) )
2003-12-30 02:18:20 -05:00
raise SSHException ( ' Bad host key from server ' )
2004-10-18 00:54:27 -04:00
self . _log ( DEBUG , ' Host key verified ( %s ) ' % hostkey . get_name ( ) )
2003-12-30 02:18:20 -05:00
if ( pkey is not None ) or ( password is not None ) :
if password is not None :
self . _log ( DEBUG , ' Attempting password auth... ' )
2004-12-10 22:43:18 -05:00
self . auth_password ( username , password )
2003-12-30 02:18:20 -05:00
else :
2004-12-10 22:43:18 -05:00
self . _log ( DEBUG , ' Attempting public-key auth... ' )
self . auth_publickey ( username , pkey )
2003-12-30 02:18:20 -05:00
return
2009-11-02 00:33:13 -05:00
2004-12-10 22:43:18 -05:00
def get_exception ( self ) :
"""
Return any exception that happened during the last server request .
This can be used to fetch more specific error information after using
calls like L { start_client } . The exception ( if any ) is cleared after
this call .
2009-11-02 00:33:13 -05:00
2004-12-10 22:43:18 -05:00
@return : an exception , or C { None } if there is no stored exception .
@rtype : Exception
2009-11-02 00:33:13 -05:00
2004-12-10 22:43:18 -05:00
@since : 1.1
"""
self . lock . acquire ( )
try :
e = self . saved_exception
self . saved_exception = None
return e
finally :
self . lock . release ( )
2003-12-30 02:18:20 -05:00
2004-10-31 22:43:28 -05:00
def set_subsystem_handler ( self , name , handler , * larg , * * kwarg ) :
2004-09-05 03:41:45 -04:00
"""
2005-04-06 03:24:28 -04:00
Set the handler class for a subsystem in server mode . If a request
2004-10-31 22:43:28 -05:00
for this subsystem is made on an open ssh channel later , this handler
will be constructed and called - - see L { SubsystemHandler } for more
detailed documentation .
Any extra parameters ( including keyword arguments ) are saved and
passed to the L { SubsystemHandler } constructor later .
2004-09-05 03:41:45 -04:00
@param name : name of the subsystem .
@type name : str
@param handler : subclass of L { SubsystemHandler } that handles this
2005-04-06 03:24:28 -04:00
subsystem .
2004-09-05 03:41:45 -04:00
@type handler : class
"""
try :
self . lock . acquire ( )
2004-10-31 22:43:28 -05:00
self . subsystem_table [ name ] = ( handler , larg , kwarg )
2004-09-05 03:41:45 -04:00
finally :
self . lock . release ( )
2009-11-02 00:33:13 -05:00
2005-08-09 03:40:07 -04:00
def is_authenticated ( self ) :
"""
Return true if this session is active and authenticated .
@return : True if the session is still open and has been authenticated
successfully ; False if authentication failed and / or the session is
closed .
@rtype : bool
"""
return self . active and ( self . auth_handler is not None ) and self . auth_handler . is_authenticated ( )
2009-11-02 00:33:13 -05:00
2005-08-09 03:40:07 -04:00
def get_username ( self ) :
"""
Return the username this connection is authenticated for . If the
session is not authenticated ( or authentication failed ) , this method
returns C { None } .
@return : username that was authenticated , or C { None } .
@rtype : string
"""
if not self . active or ( self . auth_handler is None ) :
return None
return self . auth_handler . get_username ( )
2005-09-21 18:42:11 -04:00
def auth_none ( self , username ) :
"""
Try to authenticate to the server using no authentication at all .
This will almost always fail . It may be useful for determining the
list of authentication types supported by the server , by catching the
L { BadAuthenticationType } exception raised .
2009-11-02 00:33:13 -05:00
2005-09-21 18:42:11 -04:00
@param username : the username to authenticate as
@type username : string
@return : list of auth types permissible for the next stage of
authentication ( normally empty )
@rtype : list
@raise BadAuthenticationType : if " none " authentication isn ' t allowed
by the server for this user
@raise SSHException : if the authentication failed due to a network
error
2009-11-02 00:33:13 -05:00
2005-09-21 18:42:11 -04:00
@since : 1.5
"""
if ( not self . active ) or ( not self . initial_kex_done ) :
raise SSHException ( ' No existing session ' )
my_event = threading . Event ( )
self . auth_handler = AuthHandler ( self )
self . auth_handler . auth_none ( username , my_event )
return self . auth_handler . wait_for_response ( my_event )
def auth_password ( self , username , password , event = None , fallback = True ) :
2005-08-09 03:40:07 -04:00
"""
Authenticate to the server using a password . The username and password
are sent over an encrypted link .
2009-11-02 00:33:13 -05:00
2005-08-09 03:40:07 -04:00
If an C { event } is passed in , this method will return immediately , and
the event will be triggered once authentication succeeds or fails . On
success , L { is_authenticated } will return C { True } . On failure , you may
use L { get_exception } to get more detailed error information .
Since 1.1 , if no event is passed , this method will block until the
authentication succeeds or fails . On failure , an exception is raised .
Otherwise , the method simply returns .
2009-11-02 00:33:13 -05:00
2005-09-21 18:42:11 -04:00
Since 1.5 , if no event is passed and C { fallback } is C { True } ( the
default ) , if the server doesn ' t support plain password authentication
but does support so - called " keyboard-interactive " mode , an attempt
will be made to authenticate using this interactive mode . If it fails ,
the normal exception will be thrown as if the attempt had never been
made . This is useful for some recent Gentoo and Debian distributions ,
which turn off plain password authentication in a misguided belief
that interactive authentication is " more secure " . ( It ' s not.)
2009-11-02 00:33:13 -05:00
2005-08-09 03:40:07 -04:00
If the server requires multi - step authentication ( which is very rare ) ,
this method will return a list of auth types permissible for the next
step . Otherwise , in the normal case , an empty list is returned .
2009-11-02 00:33:13 -05:00
2005-08-09 03:40:07 -04:00
@param username : the username to authenticate as
2008-01-23 23:50:17 -05:00
@type username : str
2005-08-09 03:40:07 -04:00
@param password : the password to authenticate with
2008-01-23 23:50:17 -05:00
@type password : str or unicode
2005-08-09 03:40:07 -04:00
@param event : an event to trigger when the authentication attempt is
complete ( whether it was successful or not )
@type event : threading . Event
2005-09-21 18:42:11 -04:00
@param fallback : C { True } if an attempt at an automated " interactive "
password auth should be made if the server doesn ' t support normal
password auth
@type fallback : bool
2005-08-09 03:40:07 -04:00
@return : list of auth types permissible for the next stage of
authentication ( normally empty )
@rtype : list
2009-11-02 00:33:13 -05:00
2005-08-09 03:40:07 -04:00
@raise BadAuthenticationType : if password authentication isn ' t
allowed by the server for this user ( and no event was passed in )
2006-05-09 12:45:49 -04:00
@raise AuthenticationException : if the authentication failed ( and no
event was passed in )
@raise SSHException : if there was a network error
2005-08-09 03:40:07 -04:00
"""
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 ' )
if event is None :
my_event = threading . Event ( )
else :
my_event = event
self . auth_handler = AuthHandler ( self )
self . auth_handler . auth_password ( username , password , my_event )
if event is not None :
# caller wants to wait for event themselves
return [ ]
2005-09-21 18:42:11 -04:00
try :
return self . auth_handler . wait_for_response ( my_event )
except BadAuthenticationType , x :
# if password auth isn't allowed, but keyboard-interactive *is*, try to fudge it
2006-01-15 01:30:08 -05:00
if not fallback or ( ' keyboard-interactive ' not in x . allowed_types ) :
2005-09-21 18:42:11 -04:00
raise
try :
def handler ( title , instructions , fields ) :
if len ( fields ) > 1 :
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
# to try to fake out automated scripting of the exact
# type we're doing here. *shrug* :)
return [ ]
return [ password ]
return self . auth_interactive ( username , handler )
except SSHException , ignored :
# attempt failed; just raise the original exception
raise x
2006-05-03 22:52:37 -04:00
return None
2005-08-09 03:40:07 -04:00
def auth_publickey ( self , username , key , event = None ) :
"""
Authenticate to the server using a private key . The key is used to
sign data from the server , so it must include the private part .
2009-11-02 00:33:13 -05:00
2005-08-09 03:40:07 -04:00
If an C { event } is passed in , this method will return immediately , and
the event will be triggered once authentication succeeds or fails . On
success , L { is_authenticated } will return C { True } . On failure , you may
use L { get_exception } to get more detailed error information .
2009-11-02 00:33:13 -05:00
2005-08-09 03:40:07 -04:00
Since 1.1 , if no event is passed , this method will block until the
authentication succeeds or fails . On failure , an exception is raised .
Otherwise , the method simply returns .
If the server requires multi - step authentication ( which is very rare ) ,
this method will return a list of auth types permissible for the next
step . Otherwise , in the normal case , an empty list is returned .
2005-09-21 18:42:11 -04:00
@param username : the username to authenticate as
2005-08-09 03:40:07 -04:00
@type username : string
2005-09-21 18:42:11 -04:00
@param key : the private key to authenticate with
2005-08-09 03:40:07 -04:00
@type key : L { PKey < pkey . PKey > }
@param event : an event to trigger when the authentication attempt is
complete ( whether it was successful or not )
@type event : threading . Event
@return : list of auth types permissible for the next stage of
2006-05-09 12:45:49 -04:00
authentication ( normally empty )
2005-08-09 03:40:07 -04:00
@rtype : list
2009-11-02 00:33:13 -05:00
2005-08-09 03:40:07 -04:00
@raise BadAuthenticationType : if public - key authentication isn ' t
2006-05-09 12:45:49 -04:00
allowed by the server for this user ( and no event was passed in )
@raise AuthenticationException : if the authentication failed ( and no
event was passed in )
@raise SSHException : if there was a network error
2005-08-09 03:40:07 -04:00
"""
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 ' )
if event is None :
my_event = threading . Event ( )
else :
my_event = event
self . auth_handler = AuthHandler ( self )
self . auth_handler . auth_publickey ( username , key , my_event )
if event is not None :
# caller wants to wait for event themselves
return [ ]
return self . auth_handler . wait_for_response ( my_event )
2009-11-02 00:33:13 -05:00
2005-09-21 18:42:11 -04:00
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
dumb wrapper around PAM .
2009-11-02 00:33:13 -05:00
2005-09-21 18:42:11 -04:00
This method will block until the authentication succeeds or fails ,
peroidically calling the handler asynchronously to get answers to
authentication questions . The handler may be called more than once
if the server continues to ask questions .
2009-11-02 00:33:13 -05:00
2005-09-21 18:42:11 -04:00
The handler is expected to be a callable that will handle calls of the
form : C { handler ( title , instructions , prompt_list ) } . The C { title } is
meant to be a dialog - window title , and the C { instructions } are user
instructions ( both are strings ) . C { prompt_list } will be a list of
prompts , each prompt being a tuple of C { ( str , bool ) } . The string is
the prompt and the boolean indicates whether the user text should be
echoed .
2009-11-02 00:33:13 -05:00
2005-09-21 18:42:11 -04:00
A sample call would thus be :
C { handler ( ' title ' , ' instructions ' , [ ( ' Password: ' , False ) ] ) } .
2009-11-02 00:33:13 -05:00
2005-09-21 18:42:11 -04:00
The handler should return a list or tuple of answers to the server ' s
questions .
2009-11-02 00:33:13 -05:00
2005-09-21 18:42:11 -04:00
If the server requires multi - step authentication ( which is very rare ) ,
this method will return a list of auth types permissible for the next
step . Otherwise , in the normal case , an empty list is returned .
@param username : the username to authenticate as
@type username : string
@param handler : a handler for responding to server questions
@type handler : callable
@param submethods : a string list of desired submethods ( optional )
@type submethods : str
@return : list of auth types permissible for the next stage of
authentication ( normally empty ) .
@rtype : list
2009-11-02 00:33:13 -05:00
2005-09-21 18:42:11 -04:00
@raise BadAuthenticationType : if public - key authentication isn ' t
allowed by the server for this user
2006-05-09 12:45:49 -04:00
@raise AuthenticationException : if the authentication failed
@raise SSHException : if there was a network error
2009-11-02 00:33:13 -05:00
2005-09-21 18:42:11 -04:00
@since : 1.5
"""
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 ' )
my_event = threading . Event ( )
self . auth_handler = AuthHandler ( self )
self . auth_handler . auth_interactive ( username , handler , my_event , submethods )
return self . auth_handler . wait_for_response ( my_event )
2004-09-05 03:41:45 -04:00
2004-11-22 02:04:31 -05:00
def set_log_channel ( self , name ) :
"""
Set the channel for this transport ' s logging. The default is
2011-10-23 03:00:43 -04:00
C { " ssh.transport " } but it can be set to anything you want .
2005-01-17 05:09:09 -05:00
( See the C { logging } module for more info . ) SSH Channels will log
to a sub - channel of the one specified .
2004-11-22 02:04:31 -05:00
2006-05-07 20:22:14 -04:00
@param name : new channel name for logging
2004-11-22 02:04:31 -05:00
@type name : str
@since : 1.1
"""
2005-01-17 05:09:09 -05:00
self . log_name = name
2005-02-28 02:09:02 -05:00
self . logger = util . get_logger ( name )
2006-01-26 15:50:08 -05:00
self . packetizer . set_log ( self . logger )
2004-11-22 02:04:31 -05:00
2005-01-17 05:09:09 -05:00
def get_log_channel ( self ) :
"""
Return the channel name used for this transport ' s logging.
@return : channel name .
@rtype : str
@since : 1.2
"""
return self . log_name
2005-01-25 00:17:55 -05:00
def set_hexdump ( self , hexdump ) :
"""
Turn on / off logging a hex dump of protocol traffic at DEBUG level in
the logs . Normally you would want this off ( which is the default ) ,
but if you are debugging something , it may be useful .
@param hexdump : C { True } to log protocol traffix ( in hex ) to the log ;
C { False } otherwise .
@type hexdump : bool
"""
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
self . packetizer . set_hexdump ( hexdump )
2009-11-02 00:33:13 -05:00
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
def get_hexdump ( self ) :
"""
Return C { True } if the transport is currently logging hex dumps of
protocol traffic .
2009-11-02 00:33:13 -05:00
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
@return : C { True } if hex dumps are being logged
@rtype : bool
2009-11-02 00:33:13 -05:00
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
@since : 1.4
"""
return self . packetizer . get_hexdump ( )
2009-11-02 00:33:13 -05:00
2005-12-02 16:15:44 -05:00
def use_compression ( self , compress = True ) :
"""
Turn on / off compression . This will only have an affect before starting
the transport ( ie before calling L { connect } , etc ) . By default ,
2006-12-25 20:26:31 -05:00
compression is off since it negatively affects interactive sessions .
2009-11-02 00:33:13 -05:00
2005-12-02 16:15:44 -05:00
@param compress : C { True } to ask the remote client / server to compress
traffic ; C { False } to refuse compression
@type compress : bool
2009-11-02 00:33:13 -05:00
2005-12-02 16:15:44 -05:00
@since : 1.5 .2
"""
if compress :
self . _preferred_compression = ( ' zlib@openssh.com ' , ' zlib ' , ' none ' )
else :
self . _preferred_compression = ( ' none ' , )
2009-11-02 00:33:13 -05:00
2006-02-06 01:42:28 -05:00
def getpeername ( self ) :
"""
Return the address of the remote side of this Transport , if possible .
This is effectively a wrapper around C { ' getpeername ' } on the underlying
socket . If the socket - like object has no C { ' getpeername ' } method ,
then C { ( " unknown " , 0 ) } is returned .
2009-11-02 00:33:13 -05:00
2006-02-06 01:42:28 -05:00
@return : the address if the remote host , if known
@rtype : tuple ( str , int )
"""
gp = getattr ( self . sock , ' getpeername ' , None )
if gp is None :
return ( ' unknown ' , 0 )
return gp ( )
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
def stop_thread ( self ) :
self . active = False
self . packetizer . close ( )
2005-01-25 00:17:55 -05:00
2003-12-30 02:18:20 -05:00
### internals...
2009-11-02 00:33:13 -05:00
2006-11-20 19:11:17 -05:00
def _log ( self , level , msg , * args ) :
2005-01-25 00:17:55 -05:00
if issubclass ( type ( msg ) , list ) :
2003-12-30 17:24:21 -05:00
for m in msg :
self . logger . log ( level , m )
else :
2006-11-20 19:11:17 -05:00
self . logger . log ( level , msg , * args )
2003-12-30 17:24:21 -05:00
def _get_modulus_pack ( self ) :
" used by KexGex to find primes for group exchange "
return self . _modulus_pack
2006-11-20 19:11:17 -05:00
def _next_channel ( self ) :
" you are holding the lock "
chanid = self . _channel_counter
2008-03-23 04:21:10 -04:00
while self . _channels . get ( chanid ) is not None :
2006-11-20 19:11:17 -05:00
self . _channel_counter = ( self . _channel_counter + 1 ) & 0xffffff
chanid = self . _channel_counter
self . _channel_counter = ( self . _channel_counter + 1 ) & 0xffffff
return chanid
2003-12-27 22:20:42 -05:00
def _unlink_channel ( self , chanid ) :
" used by a Channel to remove itself from the active channel list "
2008-03-23 04:21:10 -04:00
self . _channels . delete ( chanid )
2003-11-04 03:34:24 -05:00
2003-12-27 22:20:42 -05:00
def _send_message ( self , data ) :
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
self . packetizer . send_message ( data )
2003-11-04 03:34:24 -05:00
2004-08-26 20:57:40 -04:00
def _send_user_message ( self , data ) :
"""
send a message , but block if we ' re in key negotiation. this is used
for user - initiated requests .
"""
2009-07-19 19:00:39 -04:00
start = time . time ( )
2005-02-15 10:47:02 -05:00
while True :
2004-08-26 20:57:40 -04:00
self . clear_to_send . wait ( 0.1 )
if not self . active :
self . _log ( DEBUG , ' Dropping user packet because connection is dead. ' )
return
2005-10-29 16:47:04 -04:00
self . clear_to_send_lock . acquire ( )
2004-08-26 20:57:40 -04:00
if self . clear_to_send . isSet ( ) :
break
2005-10-29 16:47:04 -04:00
self . clear_to_send_lock . release ( )
2009-07-19 19:00:39 -04:00
if time . time ( ) > start + self . clear_to_send_timeout :
raise SSHException ( ' Key-exchange timed out waiting for key negotiation ' )
2005-10-29 16:47:04 -04:00
try :
self . _send_message ( data )
finally :
self . clear_to_send_lock . release ( )
2004-08-26 20:57:40 -04:00
2003-12-27 22:20:42 -05:00
def _set_K_H ( self , k , h ) :
2003-11-04 03:34:24 -05:00
" used by a kex object to set the K (root key) and H (exchange hash) "
self . K = k
self . H = h
if self . session_id == None :
self . session_id = h
2006-07-23 19:55:48 -04:00
def _expect_packet ( self , * ptypes ) :
2003-12-27 22:20:42 -05:00
" used by a kex object to register the next packet type it expects to see "
2006-07-23 19:55:48 -04:00
self . _expected_packet = tuple ( ptypes )
2003-12-27 22:20:42 -05:00
2003-12-30 17:24:21 -05:00
def _verify_key ( self , host_key , sig ) :
2004-08-30 22:44:56 -04:00
key = self . _key_info [ self . host_key_type ] ( Message ( host_key ) )
2004-10-18 00:54:27 -04:00
if key is None :
2003-11-10 03:49:50 -05:00
raise SSHException ( ' Unknown host key type ' )
2003-11-04 03:34:24 -05:00
if not key . verify_ssh_sig ( self . H , Message ( sig ) ) :
2009-02-16 04:35:22 -05:00
raise SSHException ( ' Signature verification ( %s ) failed. ' % self . host_key_type )
2004-04-05 18:32:03 -04:00
self . host_key = key
2003-11-04 03:34:24 -05:00
2003-12-27 22:20:42 -05:00
def _compute_key ( self , id , nbytes ) :
2003-11-04 03:34:24 -05:00
" id is ' A ' - ' F ' for the various keys used by ssh "
m = Message ( )
m . add_mpint ( self . K )
m . add_bytes ( self . H )
m . add_byte ( id )
m . add_bytes ( self . session_id )
out = sofar = SHA . new ( str ( m ) ) . digest ( )
while len ( out ) < nbytes :
m = Message ( )
m . add_mpint ( self . K )
m . add_bytes ( self . H )
m . add_bytes ( sofar )
2006-01-15 01:30:08 -05:00
digest = SHA . new ( str ( m ) ) . digest ( )
out + = digest
sofar + = digest
2003-11-04 03:34:24 -05:00
return out [ : nbytes ]
2003-12-27 22:20:42 -05:00
def _get_cipher ( self , name , key , iv ) :
2006-08-29 14:18:36 -04:00
if name not in self . _cipher_info :
2003-11-10 03:49:50 -05:00
raise SSHException ( ' Unknown client cipher ' + name )
2009-07-19 18:04:54 -04:00
if name in ( ' arcfour128 ' , ' arcfour256 ' ) :
# arcfour cipher
cipher = self . _cipher_info [ name ] [ ' class ' ] . new ( key )
# as per RFC 4345, the first 1536 bytes of keystream
# generated by the cipher MUST be discarded
cipher . encrypt ( " " * 1536 )
return cipher
elif name . endswith ( " -ctr " ) :
# CTR modes, we need a counter
counter = Counter . new ( nbits = self . _cipher_info [ name ] [ ' block-size ' ] * 8 , initial_value = util . inflate_long ( iv , True ) )
return self . _cipher_info [ name ] [ ' class ' ] . new ( key , self . _cipher_info [ name ] [ ' mode ' ] , iv , counter )
else :
return self . _cipher_info [ name ] [ ' class ' ] . new ( key , self . _cipher_info [ name ] [ ' mode ' ] , iv )
2003-11-04 03:34:24 -05:00
2011-11-13 12:19:19 -05:00
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
2006-11-20 19:11:17 -05:00
def _set_x11_handler ( self , handler ) :
# only called if a channel has turned on x11 forwarding
if handler is None :
# by default, use the same mechanism as accept()
2006-12-02 21:33:34 -05:00
def default_handler ( channel , ( src_addr , src_port ) ) :
self . _queue_incoming_channel ( channel )
self . _x11_handler = default_handler
2006-11-20 19:11:17 -05:00
else :
2006-12-02 21:33:34 -05:00
self . _x11_handler = handler
2006-11-20 19:11:17 -05:00
def _queue_incoming_channel ( self , channel ) :
self . lock . acquire ( )
try :
self . server_accepts . append ( channel )
self . server_accept_cv . notify ( )
finally :
self . lock . release ( )
2009-11-02 00:33:13 -05:00
2005-07-17 16:06:29 -04:00
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
# indefinitely, creating a GC cycle and not letting Transport ever be
2009-02-17 21:30:48 -05:00
# GC'd. it's a bug in Thread.)
2009-11-02 00:33:13 -05:00
2011-10-23 03:33:52 -04:00
# Required to prevent RNG errors when running inside many subprocess
# containers.
Random . atfork ( )
2005-02-28 02:16:22 -05:00
# active=True occurs before the thread is launched, to avoid a race
2004-03-08 04:47:47 -05:00
_active_threads . append ( self )
2005-02-28 02:16:22 -05:00
if self . server_mode :
self . _log ( DEBUG , ' starting thread (server mode): %s ' % hex ( long ( id ( self ) ) & 0xffffffff L ) )
else :
self . _log ( DEBUG , ' starting thread (client mode): %s ' % hex ( long ( id ( self ) ) & 0xffffffff L ) )
2003-11-04 03:34:24 -05:00
try :
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
self . packetizer . write_all ( self . local_version + ' \r \n ' )
2003-12-27 22:20:42 -05:00
self . _check_banner ( )
self . _send_kex_init ( )
2006-07-23 19:55:48 -04:00
self . _expect_packet ( MSG_KEXINIT )
2003-11-04 03:34:24 -05:00
while self . active :
2005-05-14 02:21:36 -04:00
if self . packetizer . need_rekey ( ) and not self . in_kex :
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
self . _send_kex_init ( )
2005-10-29 16:47:04 -04:00
try :
ptype , m = self . packetizer . read_message ( )
except NeedRekeyException :
continue
2004-04-05 06:12:59 -04:00
if ptype == MSG_IGNORE :
2003-11-04 03:34:24 -05:00
continue
2004-04-05 06:12:59 -04:00
elif ptype == MSG_DISCONNECT :
2003-12-27 22:20:42 -05:00
self . _parse_disconnect ( m )
self . active = False
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
self . packetizer . close ( )
2003-11-04 03:34:24 -05:00
break
2004-04-05 06:12:59 -04:00
elif ptype == MSG_DEBUG :
2003-12-27 22:20:42 -05:00
self . _parse_debug ( m )
2003-11-04 03:34:24 -05:00
continue
2006-07-23 19:55:48 -04:00
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 ) )
self . _expected_packet = tuple ( )
2003-11-04 03:34:24 -05:00
if ( ptype > = 30 ) and ( ptype < = 39 ) :
self . kex_engine . parse_next ( ptype , m )
continue
2006-08-29 14:18:36 -04:00
if ptype in self . _handler_table :
2003-12-27 22:20:42 -05:00
self . _handler_table [ ptype ] ( self , m )
2006-08-29 14:18:36 -04:00
elif ptype in self . _channel_handler_table :
2003-11-04 03:34:24 -05:00
chanid = m . get_int ( )
2008-03-23 04:21:10 -04:00
chan = self . _channels . get ( chanid )
if chan is not None :
self . _channel_handler_table [ ptype ] ( chan , m )
2006-08-29 14:18:36 -04:00
elif chanid in self . channels_seen :
2005-10-13 14:51:18 -04:00
self . _log ( DEBUG , ' Ignoring message for dead channel %d ' % chanid )
2004-09-03 18:39:20 -04:00
else :
self . _log ( ERROR , ' Channel request for unknown channel %d ' % chanid )
self . active = False
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
self . packetizer . close ( )
2006-08-29 14:18:36 -04:00
elif ( self . auth_handler is not None ) and ( ptype in self . auth_handler . _handler_table ) :
2005-08-09 03:40:07 -04:00
self . auth_handler . _handler_table [ ptype ] ( self . auth_handler , m )
2003-11-04 03:34:24 -05:00
else :
2003-12-27 22:20:42 -05:00
self . _log ( WARNING , ' Oops, unhandled type %d ' % ptype )
2003-11-04 03:34:24 -05:00
msg = Message ( )
2004-04-05 06:12:59 -04:00
msg . add_byte ( chr ( MSG_UNIMPLEMENTED ) )
2003-11-04 03:34:24 -05:00
msg . add_int ( m . seqno )
2003-12-27 22:20:42 -05:00
self . _send_message ( msg )
2003-11-10 03:49:50 -05:00
except SSHException , e :
2004-05-16 20:43:43 -04:00
self . _log ( ERROR , ' Exception: ' + str ( e ) )
self . _log ( ERROR , util . tb_strings ( ) )
2003-12-30 02:18:20 -05:00
self . saved_exception = e
2003-11-04 03:34:24 -05:00
except EOFError , e :
2004-08-26 20:57:40 -04:00
self . _log ( DEBUG , ' EOF in transport thread ' )
#self._log(DEBUG, util.tb_strings())
2003-12-30 02:18:20 -05:00
self . saved_exception = e
2005-07-02 21:26:35 -04:00
except socket . error , e :
if type ( e . args ) is tuple :
emsg = ' %s ( %d ) ' % ( e . args [ 1 ] , e . args [ 0 ] )
else :
emsg = e . args
self . _log ( ERROR , ' Socket exception: ' + emsg )
self . saved_exception = e
2003-11-04 03:34:24 -05:00
except Exception , e :
2004-05-16 20:43:43 -04:00
self . _log ( ERROR , ' Unknown exception: ' + str ( e ) )
self . _log ( ERROR , util . tb_strings ( ) )
2003-12-30 02:18:20 -05:00
self . saved_exception = e
2004-03-08 04:47:47 -05:00
_active_threads . remove ( self )
2008-03-23 04:21:10 -04:00
for chan in self . _channels . values ( ) :
2004-04-07 02:07:29 -04:00
chan . _unlink ( )
2003-11-04 03:34:24 -05:00
if self . active :
2003-12-27 22:20:42 -05:00
self . active = False
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
self . packetizer . close ( )
2003-11-04 03:34:24 -05:00
if self . completion_event != None :
self . completion_event . set ( )
2005-08-09 03:40:07 -04:00
if self . auth_handler is not None :
self . auth_handler . abort ( )
2003-12-30 02:18:20 -05:00
for event in self . channel_events . values ( ) :
event . set ( )
2006-09-30 23:45:25 -04:00
try :
self . lock . acquire ( )
self . server_accept_cv . notify ( )
finally :
self . lock . release ( )
2003-11-04 03:34:24 -05:00
self . sock . close ( )
2003-12-30 02:18:20 -05:00
### protocol stages
2003-12-27 22:20:42 -05:00
2003-11-04 03:34:24 -05:00
2003-12-27 22:20:42 -05:00
def _negotiate_keys ( self , m ) :
2003-11-10 03:49:50 -05:00
# throws SSHException on anything unusual
2005-10-29 16:47:04 -04:00
self . clear_to_send_lock . acquire ( )
try :
self . clear_to_send . clear ( )
finally :
self . clear_to_send_lock . release ( )
2003-11-04 03:34:24 -05:00
if self . local_kex_init == None :
# remote side wants to renegotiate
2003-12-27 22:20:42 -05:00
self . _send_kex_init ( )
self . _parse_kex_init ( m )
2003-11-04 03:34:24 -05:00
self . kex_engine . start_kex ( )
2003-12-27 22:20:42 -05:00
def _check_banner ( self ) :
2003-11-04 03:34:24 -05:00
# this is slow, but we only have to do it once
2009-02-17 21:30:48 -05:00
for i in range ( 100 ) :
2006-04-12 16:36:12 -04:00
# give them 15 seconds for the first line, then just 2 seconds
# each additional line. (some sites have very high latency.)
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
if i == 0 :
2006-04-12 16:36:12 -04:00
timeout = self . banner_timeout
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
else :
timeout = 2
try :
2006-01-15 01:30:08 -05:00
buf = self . packetizer . readline ( timeout )
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
except Exception , x :
raise SSHException ( ' Error reading SSH protocol banner ' + str ( x ) )
2006-01-15 01:30:08 -05:00
if buf [ : 4 ] == ' SSH- ' :
2003-11-04 03:34:24 -05:00
break
2006-01-15 01:30:08 -05:00
self . _log ( DEBUG , ' Banner: ' + buf )
if buf [ : 4 ] != ' SSH- ' :
raise SSHException ( ' Indecipherable protocol version " ' + buf + ' " ' )
2003-11-04 03:34:24 -05:00
# save this server version string for later
2006-01-15 01:30:08 -05:00
self . remote_version = buf
2003-11-04 03:34:24 -05:00
# pull off any attached comment
comment = ' '
2006-01-15 01:30:08 -05:00
i = string . find ( buf , ' ' )
2003-11-04 03:34:24 -05:00
if i > = 0 :
2006-01-15 01:30:08 -05:00
comment = buf [ i + 1 : ]
buf = buf [ : i ]
2003-11-04 03:34:24 -05:00
# parse out version string and make sure it matches
2006-01-15 01:30:08 -05:00
segs = buf . split ( ' - ' , 2 )
2003-11-09 16:14:21 -05:00
if len ( segs ) < 3 :
2003-11-10 03:49:50 -05:00
raise SSHException ( ' Invalid SSH banner ' )
2003-11-09 16:14:21 -05:00
version = segs [ 1 ]
client = segs [ 2 ]
2003-11-04 03:34:24 -05:00
if version != ' 1.99 ' and version != ' 2.0 ' :
2003-11-10 03:49:50 -05:00
raise SSHException ( ' Incompatible version ( %s instead of 2.0) ' % ( version , ) )
2003-12-27 22:20:42 -05:00
self . _log ( INFO , ' Connected (version %s , client %s ) ' % ( version , client ) )
2003-11-04 03:34:24 -05:00
2003-12-27 22:20:42 -05:00
def _send_kex_init ( self ) :
"""
announce to the other side that we ' d like to negotiate keys, and what
kind of key negotiation we support .
"""
2005-10-29 16:47:04 -04:00
self . clear_to_send_lock . acquire ( )
try :
self . clear_to_send . clear ( )
finally :
self . clear_to_send_lock . release ( )
2005-05-14 02:21:36 -04:00
self . in_kex = True
2003-11-09 23:54:02 -05:00
if self . server_mode :
2004-08-30 16:22:10 -04:00
if ( self . _modulus_pack is None ) and ( ' diffie-hellman-group-exchange-sha1 ' in self . _preferred_kex ) :
2003-12-27 22:20:42 -05:00
# can't do group-exchange if we don't have a pack of potential primes
2004-09-03 18:39:20 -04:00
pkex = list ( self . get_security_options ( ) . kex )
pkex . remove ( ' diffie-hellman-group-exchange-sha1 ' )
self . get_security_options ( ) . kex = pkex
2003-12-27 22:20:42 -05:00
available_server_keys = filter ( self . server_key_dict . keys ( ) . __contains__ ,
2004-08-30 16:22:10 -04:00
self . _preferred_keys )
2003-12-27 22:20:42 -05:00
else :
2004-08-30 16:22:10 -04:00
available_server_keys = self . _preferred_keys
2003-12-23 01:44:56 -05:00
2003-11-04 03:34:24 -05:00
m = Message ( )
2004-04-05 06:12:59 -04:00
m . add_byte ( chr ( MSG_KEXINIT ) )
2010-08-02 18:13:08 -04:00
m . add_bytes ( rng . read ( 16 ) )
2005-07-06 21:10:57 -04:00
m . add_list ( self . _preferred_kex )
m . add_list ( available_server_keys )
m . add_list ( self . _preferred_ciphers )
m . add_list ( self . _preferred_ciphers )
m . add_list ( self . _preferred_macs )
m . add_list ( self . _preferred_macs )
2005-12-02 16:15:44 -05:00
m . add_list ( self . _preferred_compression )
m . add_list ( self . _preferred_compression )
2005-07-06 21:10:57 -04:00
m . add_string ( ' ' )
m . add_string ( ' ' )
2005-05-14 02:21:36 -04:00
m . add_boolean ( False )
2003-11-04 03:34:24 -05:00
m . add_int ( 0 )
# save a copy for later (needed to compute a hash)
self . local_kex_init = str ( m )
2003-12-27 22:20:42 -05:00
self . _send_message ( m )
2003-11-04 03:34:24 -05:00
2003-12-27 22:20:42 -05:00
def _parse_kex_init ( self , m ) :
2003-11-04 03:34:24 -05:00
cookie = m . get_bytes ( 16 )
kex_algo_list = m . get_list ( )
server_key_algo_list = m . get_list ( )
client_encrypt_algo_list = m . get_list ( )
server_encrypt_algo_list = m . get_list ( )
client_mac_algo_list = m . get_list ( )
server_mac_algo_list = m . get_list ( )
client_compress_algo_list = m . get_list ( )
server_compress_algo_list = m . get_list ( )
client_lang_list = m . get_list ( )
server_lang_list = m . get_list ( )
kex_follows = m . get_boolean ( )
unused = m . get_int ( )
2005-12-02 16:15:44 -05:00
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 ) )
2003-11-04 03:34:24 -05:00
# as a server, we pick the first item in the client's list that we support.
# as a client, we pick the first item in our list that the server supports.
if self . server_mode :
2004-08-30 16:22:10 -04:00
agreed_kex = filter ( self . _preferred_kex . __contains__ , kex_algo_list )
2003-11-04 03:34:24 -05:00
else :
2004-08-30 16:22:10 -04:00
agreed_kex = filter ( kex_algo_list . __contains__ , self . _preferred_kex )
2003-11-04 03:34:24 -05:00
if len ( agreed_kex ) == 0 :
2003-11-10 03:49:50 -05:00
raise SSHException ( ' Incompatible ssh peer (no acceptable kex algorithm) ' )
2003-12-27 22:20:42 -05:00
self . kex_engine = self . _kex_info [ agreed_kex [ 0 ] ] ( self )
2003-11-04 03:34:24 -05:00
if self . server_mode :
2003-12-23 01:44:56 -05:00
available_server_keys = filter ( self . server_key_dict . keys ( ) . __contains__ ,
2004-08-30 16:22:10 -04:00
self . _preferred_keys )
2003-12-23 01:44:56 -05:00
agreed_keys = filter ( available_server_keys . __contains__ , server_key_algo_list )
2003-11-04 03:34:24 -05:00
else :
2004-08-30 16:22:10 -04:00
agreed_keys = filter ( server_key_algo_list . __contains__ , self . _preferred_keys )
2003-11-04 03:34:24 -05:00
if len ( agreed_keys ) == 0 :
2003-11-10 03:49:50 -05:00
raise SSHException ( ' Incompatible ssh peer (no acceptable host key) ' )
2003-11-04 03:34:24 -05:00
self . host_key_type = agreed_keys [ 0 ]
if self . server_mode and ( self . get_server_key ( ) is None ) :
2003-11-10 03:49:50 -05:00
raise SSHException ( ' Incompatible ssh peer (can \' t match requested host key type) ' )
2003-11-04 03:34:24 -05:00
if self . server_mode :
2004-08-30 16:22:10 -04:00
agreed_local_ciphers = filter ( self . _preferred_ciphers . __contains__ ,
2003-11-04 03:34:24 -05:00
server_encrypt_algo_list )
2004-08-30 16:22:10 -04:00
agreed_remote_ciphers = filter ( self . _preferred_ciphers . __contains__ ,
2003-11-04 03:34:24 -05:00
client_encrypt_algo_list )
else :
agreed_local_ciphers = filter ( client_encrypt_algo_list . __contains__ ,
2004-08-30 16:22:10 -04:00
self . _preferred_ciphers )
2003-11-04 03:34:24 -05:00
agreed_remote_ciphers = filter ( server_encrypt_algo_list . __contains__ ,
2004-08-30 16:22:10 -04:00
self . _preferred_ciphers )
2003-11-04 03:34:24 -05:00
if ( len ( agreed_local_ciphers ) == 0 ) or ( len ( agreed_remote_ciphers ) == 0 ) :
2003-11-10 03:49:50 -05:00
raise SSHException ( ' Incompatible ssh server (no acceptable ciphers) ' )
2003-11-04 03:34:24 -05:00
self . local_cipher = agreed_local_ciphers [ 0 ]
self . remote_cipher = agreed_remote_ciphers [ 0 ]
2003-12-27 22:20:42 -05:00
self . _log ( DEBUG , ' Ciphers agreed: local= %s , remote= %s ' % ( self . local_cipher , self . remote_cipher ) )
2003-11-04 03:34:24 -05:00
if self . server_mode :
2004-08-30 16:22:10 -04:00
agreed_remote_macs = filter ( self . _preferred_macs . __contains__ , client_mac_algo_list )
agreed_local_macs = filter ( self . _preferred_macs . __contains__ , server_mac_algo_list )
2003-11-04 03:34:24 -05:00
else :
2004-08-30 16:22:10 -04:00
agreed_local_macs = filter ( client_mac_algo_list . __contains__ , self . _preferred_macs )
agreed_remote_macs = filter ( server_mac_algo_list . __contains__ , self . _preferred_macs )
2003-11-04 03:34:24 -05:00
if ( len ( agreed_local_macs ) == 0 ) or ( len ( agreed_remote_macs ) == 0 ) :
2003-11-10 03:49:50 -05:00
raise SSHException ( ' Incompatible ssh server (no acceptable macs) ' )
2003-11-04 03:34:24 -05:00
self . local_mac = agreed_local_macs [ 0 ]
self . remote_mac = agreed_remote_macs [ 0 ]
2005-12-02 16:15:44 -05:00
if self . server_mode :
agreed_remote_compression = filter ( self . _preferred_compression . __contains__ , client_compress_algo_list )
agreed_local_compression = filter ( self . _preferred_compression . __contains__ , server_compress_algo_list )
else :
agreed_local_compression = filter ( client_compress_algo_list . __contains__ , self . _preferred_compression )
agreed_remote_compression = filter ( server_compress_algo_list . __contains__ , self . _preferred_compression )
if ( len ( agreed_local_compression ) == 0 ) or ( len ( agreed_remote_compression ) == 0 ) :
raise SSHException ( ' Incompatible ssh server (no acceptable compression) %r %r %r ' % ( 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 ( DEBUG , ' using kex %s ; server key type %s ; cipher: local %s , remote %s ; mac: local %s , remote %s ; compression: local %s , remote %s ' %
2003-12-27 22:20:42 -05:00
( agreed_kex [ 0 ] , self . host_key_type , self . local_cipher , self . remote_cipher , self . local_mac ,
2005-12-02 16:15:44 -05:00
self . remote_mac , self . local_compression , self . remote_compression ) )
2003-11-04 03:34:24 -05:00
# save for computing hash later...
# now wait! openssh has a bug (and others might too) where there are
# actually some extra bytes (one NUL byte in openssh's case) added to
# the end of the packet but not parsed. turns out we need to throw
# away those bytes because they aren't part of the hash.
2004-04-05 06:12:59 -04:00
self . remote_kex_init = chr ( MSG_KEXINIT ) + m . get_so_far ( )
2003-11-04 03:34:24 -05:00
2003-12-27 22:20:42 -05:00
def _activate_inbound ( self ) :
2003-11-04 03:34:24 -05:00
" switch on newly negotiated encryption parameters for inbound traffic "
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
block_size = self . _cipher_info [ self . remote_cipher ] [ ' block-size ' ]
2003-11-04 03:34:24 -05:00
if self . server_mode :
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
IV_in = self . _compute_key ( ' A ' , block_size )
2003-12-27 22:20:42 -05:00
key_in = self . _compute_key ( ' C ' , self . _cipher_info [ self . remote_cipher ] [ ' key-size ' ] )
2003-11-04 03:34:24 -05:00
else :
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
IV_in = self . _compute_key ( ' B ' , block_size )
2003-12-27 22:20:42 -05:00
key_in = self . _compute_key ( ' D ' , self . _cipher_info [ self . remote_cipher ] [ ' key-size ' ] )
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
engine = self . _get_cipher ( self . remote_cipher , key_in , IV_in )
mac_size = self . _mac_info [ self . remote_mac ] [ ' size ' ]
mac_engine = self . _mac_info [ self . remote_mac ] [ ' class ' ]
2003-11-04 03:34:24 -05:00
# initial mac keys are done in the hash's natural size (not the potentially truncated
# transmission size)
if self . server_mode :
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
mac_key = self . _compute_key ( ' E ' , mac_engine . digest_size )
2003-11-04 03:34:24 -05:00
else :
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
mac_key = self . _compute_key ( ' F ' , mac_engine . digest_size )
self . packetizer . set_inbound_cipher ( engine , block_size , mac_engine , mac_size , mac_key )
2005-12-02 16:15:44 -05:00
compress_in = self . _compression_info [ self . remote_compression ] [ 1 ]
2005-12-03 23:32:51 -05:00
if ( compress_in is not None ) and ( ( self . remote_compression != ' zlib@openssh.com ' ) or self . authenticated ) :
2005-12-02 16:15:44 -05:00
self . _log ( DEBUG , ' Switching on inbound compression ... ' )
self . packetizer . set_inbound_compressor ( compress_in ( ) )
2003-11-04 03:34:24 -05:00
2003-12-27 22:20:42 -05:00
def _activate_outbound ( self ) :
2003-11-04 03:34:24 -05:00
" switch on newly negotiated encryption parameters for outbound traffic "
m = Message ( )
2004-04-05 06:12:59 -04:00
m . add_byte ( chr ( MSG_NEWKEYS ) )
2003-12-27 22:20:42 -05:00
self . _send_message ( m )
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
block_size = self . _cipher_info [ self . local_cipher ] [ ' block-size ' ]
2003-11-04 03:34:24 -05:00
if self . server_mode :
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
IV_out = self . _compute_key ( ' B ' , block_size )
2003-12-27 22:20:42 -05:00
key_out = self . _compute_key ( ' D ' , self . _cipher_info [ self . local_cipher ] [ ' key-size ' ] )
2003-11-04 03:34:24 -05:00
else :
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
IV_out = self . _compute_key ( ' A ' , block_size )
2003-12-27 22:20:42 -05:00
key_out = self . _compute_key ( ' C ' , self . _cipher_info [ self . local_cipher ] [ ' key-size ' ] )
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
engine = self . _get_cipher ( self . local_cipher , key_out , IV_out )
mac_size = self . _mac_info [ self . local_mac ] [ ' size ' ]
mac_engine = self . _mac_info [ self . local_mac ] [ ' class ' ]
2003-11-04 03:34:24 -05:00
# initial mac keys are done in the hash's natural size (not the potentially truncated
# transmission size)
if self . server_mode :
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
mac_key = self . _compute_key ( ' F ' , mac_engine . digest_size )
2003-11-04 03:34:24 -05:00
else :
[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-5]
split out Packetizer, fix banner detection bug, new unit test
split out a chunk of BaseTransport into a Packetizer class, which handles
the in/out packet data, ciphers, etc. it didn't make the code any smaller
(transport.py is still close to 1500 lines, which is awful) but it did split
out a coherent chunk of functionality into a discrete unit.
in the process, fixed a bug that alain spineux pointed out: the banner
check was too forgiving and would block forever waiting for an SSH banner.
now it waits 5 seconds for the first line, and 2 seconds for each subsequent
line, before giving up.
added a unit test to test keepalive, since i wasn't sure that was still
working after pulling out Packetizer.
2005-05-01 04:04:59 -04:00
mac_key = self . _compute_key ( ' E ' , mac_engine . digest_size )
self . packetizer . set_outbound_cipher ( engine , block_size , mac_engine , mac_size , mac_key )
2005-12-02 16:15:44 -05:00
compress_out = self . _compression_info [ self . local_compression ] [ 0 ]
2005-12-03 23:32:51 -05:00
if ( compress_out is not None ) and ( ( self . local_compression != ' zlib@openssh.com ' ) or self . authenticated ) :
2005-12-02 16:15:44 -05:00
self . _log ( DEBUG , ' Switching on outbound compression ... ' )
self . packetizer . set_outbound_compressor ( compress_out ( ) )
2005-05-14 02:21:36 -04:00
if not self . packetizer . need_rekey ( ) :
self . in_kex = False
2003-12-27 22:20:42 -05:00
# we always expect to receive NEWKEYS now
2006-07-23 19:55:48 -04:00
self . _expect_packet ( MSG_NEWKEYS )
2003-11-04 03:34:24 -05:00
2005-12-02 16:15:44 -05:00
def _auth_trigger ( self ) :
2005-12-03 23:32:51 -05:00
self . authenticated = True
2005-12-02 16:15:44 -05:00
# delayed initiation of compression
if self . local_compression == ' zlib@openssh.com ' :
compress_out = self . _compression_info [ self . local_compression ] [ 0 ]
self . _log ( DEBUG , ' Switching on outbound compression ... ' )
self . packetizer . set_outbound_compressor ( compress_out ( ) )
if self . remote_compression == ' zlib@openssh.com ' :
compress_in = self . _compression_info [ self . remote_compression ] [ 1 ]
self . _log ( DEBUG , ' Switching on inbound compression ... ' )
self . packetizer . set_inbound_compressor ( compress_in ( ) )
2003-12-27 22:20:42 -05:00
def _parse_newkeys ( self , m ) :
self . _log ( DEBUG , ' Switch to new keys ... ' )
self . _activate_inbound ( )
2003-11-04 03:34:24 -05:00
# can also free a bunch of stuff here
self . local_kex_init = self . remote_kex_init = None
2005-06-28 03:23:41 -04:00
self . K = None
2005-07-17 16:06:29 -04:00
self . kex_engine = None
2005-08-09 03:40:07 -04:00
if self . server_mode and ( self . auth_handler is None ) :
# create auth handler for server mode
self . auth_handler = AuthHandler ( self )
2003-11-04 03:34:24 -05:00
if not self . initial_kex_done :
# this was the first key exchange
2004-04-07 11:52:07 -04:00
self . initial_kex_done = True
2003-11-04 03:34:24 -05:00
# send an event?
if self . completion_event != None :
self . completion_event . set ( )
2004-08-26 20:57:40 -04:00
# it's now okay to send data again (if this was a re-key)
2005-05-14 02:21:36 -04:00
if not self . packetizer . need_rekey ( ) :
self . in_kex = False
2005-10-29 16:47:04 -04:00
self . clear_to_send_lock . acquire ( )
try :
self . clear_to_send . set ( )
finally :
self . clear_to_send_lock . release ( )
2003-11-04 03:34:24 -05:00
return
2003-12-27 22:20:42 -05:00
def _parse_disconnect ( self , m ) :
2003-11-04 03:34:24 -05:00
code = m . get_int ( )
desc = m . get_string ( )
2003-12-27 22:20:42 -05:00
self . _log ( INFO , ' Disconnect (code %d ): %s ' % ( code , desc ) )
2003-11-09 16:14:21 -05:00
2004-04-05 06:37:18 -04:00
def _parse_global_request ( self , m ) :
kind = m . get_string ( )
2004-04-07 11:52:07 -04:00
self . _log ( DEBUG , ' Received global request " %s " ' % kind )
2004-04-05 06:37:18 -04:00
want_reply = m . get_boolean ( )
2006-04-28 13:49:59 -04:00
if not self . server_mode :
2006-11-20 19:11:17 -05:00
self . _log ( DEBUG , ' Rejecting " %s " global request from server. ' % kind )
2006-04-28 13:49:59 -04:00
ok = False
2006-12-02 21:33:34 -05:00
elif kind == ' tcpip-forward ' :
address = m . get_string ( )
port = m . get_int ( )
ok = self . server_object . check_port_forward_request ( address , port )
if ok != False :
ok = ( ok , )
elif kind == ' cancel-tcpip-forward ' :
address = m . get_string ( )
port = m . get_int ( )
self . server_object . cancel_port_forward_request ( address , port )
ok = True
2006-04-28 13:49:59 -04:00
else :
ok = self . server_object . check_global_request ( kind , m )
2004-04-05 06:37:18 -04:00
extra = ( )
if type ( ok ) is tuple :
extra = ok
ok = True
if want_reply :
msg = Message ( )
if ok :
msg . add_byte ( chr ( MSG_REQUEST_SUCCESS ) )
2005-07-06 21:10:57 -04:00
msg . add ( * extra )
2004-04-05 06:37:18 -04:00
else :
msg . add_byte ( chr ( MSG_REQUEST_FAILURE ) )
self . _send_message ( msg )
def _parse_request_success ( self , m ) :
2004-04-07 11:52:07 -04:00
self . _log ( DEBUG , ' Global request successful. ' )
2004-04-05 06:37:18 -04:00
self . global_response = m
if self . completion_event is not None :
self . completion_event . set ( )
2009-11-02 00:33:13 -05:00
2004-04-05 06:37:18 -04:00
def _parse_request_failure ( self , m ) :
2004-04-07 11:52:07 -04:00
self . _log ( DEBUG , ' Global request denied. ' )
2004-04-05 06:37:18 -04:00
self . global_response = None
if self . completion_event is not None :
self . completion_event . set ( )
2003-12-27 22:20:42 -05:00
def _parse_channel_open_success ( self , m ) :
2003-11-04 03:34:24 -05:00
chanid = m . get_int ( )
server_chanid = m . get_int ( )
server_window_size = m . get_int ( )
server_max_packet_size = m . get_int ( )
2008-03-23 04:21:10 -04:00
chan = self . _channels . get ( chanid )
if chan is None :
2003-12-27 22:20:42 -05:00
self . _log ( WARNING , ' Success for unrequested channel! [??] ' )
2003-11-04 03:34:24 -05:00
return
2005-06-28 03:23:41 -04:00
self . lock . acquire ( )
2003-11-04 03:34:24 -05:00
try :
2003-12-30 02:18:20 -05:00
chan . _set_remote_channel ( server_chanid , server_window_size , server_max_packet_size )
2003-12-27 22:20:42 -05:00
self . _log ( INFO , ' Secsh channel %d opened. ' % chanid )
2006-08-29 14:18:36 -04:00
if chanid in self . channel_events :
2003-11-04 03:34:24 -05:00
self . channel_events [ chanid ] . set ( )
del self . channel_events [ chanid ]
finally :
self . lock . release ( )
return
2003-12-27 22:20:42 -05:00
def _parse_channel_open_failure ( self , m ) :
2003-11-04 03:34:24 -05:00
chanid = m . get_int ( )
reason = m . get_int ( )
reason_str = m . get_string ( )
lang = m . get_string ( )
2006-05-03 22:52:37 -04:00
reason_text = CONNECTION_FAILED_CODE . get ( reason , ' (unknown code) ' )
2003-12-27 22:20:42 -05:00
self . _log ( INFO , ' Secsh channel %d open FAILED: %s : %s ' % ( chanid , reason_str , reason_text ) )
2006-05-03 22:52:37 -04:00
self . lock . acquire ( )
2003-11-04 03:34:24 -05:00
try :
2006-05-03 22:52:37 -04:00
self . saved_exception = ChannelException ( reason , reason_text )
2006-08-29 14:18:36 -04:00
if chanid in self . channel_events :
2008-03-23 04:21:10 -04:00
self . _channels . delete ( chanid )
2006-08-29 14:18:36 -04:00
if chanid in self . channel_events :
2003-11-04 03:34:24 -05:00
self . channel_events [ chanid ] . set ( )
del self . channel_events [ chanid ]
finally :
2003-11-09 16:14:21 -05:00
self . lock . release ( )
2003-11-04 03:34:24 -05:00
return
2003-12-27 22:20:42 -05:00
def _parse_channel_open ( self , m ) :
2003-11-04 03:34:24 -05:00
kind = m . get_string ( )
chanid = m . get_int ( )
2003-11-09 16:14:21 -05:00
initial_window_size = m . get_int ( )
max_packet_size = m . get_int ( )
reject = False
2011-11-13 12:19:19 -05:00
if ( kind == ' auth-agent@openssh.com ' ) and ( self . _forward_agent_handler is not None ) :
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 ) :
2006-11-20 19:11:17 -05:00
origin_addr = m . get_string ( )
origin_port = m . get_int ( )
self . _log ( DEBUG , ' Incoming x11 connection from %s : %d ' % ( origin_addr , origin_port ) )
self . lock . acquire ( )
try :
my_chanid = self . _next_channel ( )
finally :
self . lock . release ( )
2006-12-02 21:33:34 -05:00
elif ( kind == ' forwarded-tcpip ' ) and ( self . _tcp_handler is not None ) :
server_addr = m . get_string ( )
server_port = m . get_int ( )
origin_addr = m . get_string ( )
origin_port = m . get_int ( )
self . _log ( DEBUG , ' Incoming tcp forwarded connection from %s : %d ' % ( origin_addr , origin_port ) )
self . lock . acquire ( )
try :
my_chanid = self . _next_channel ( )
finally :
self . lock . release ( )
2006-11-20 19:11:17 -05:00
elif not self . server_mode :
2003-12-27 22:20:42 -05:00
self . _log ( DEBUG , ' Rejecting " %s " channel request from server. ' % kind )
2003-11-09 16:14:21 -05:00
reject = True
2004-09-03 18:39:20 -04:00
reason = OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
2003-11-09 16:14:21 -05:00
else :
2004-11-22 02:04:31 -05:00
self . lock . acquire ( )
2003-11-09 16:14:21 -05:00
try :
2006-11-20 19:11:17 -05:00
my_chanid = self . _next_channel ( )
2003-11-09 16:14:21 -05:00
finally :
self . lock . release ( )
2007-12-31 00:29:50 -05:00
if kind == ' direct-tcpip ' :
# handle direct-tcpip requests comming from the client
dest_addr = m . get_string ( )
dest_port = m . get_int ( )
origin_addr = m . get_string ( )
origin_port = m . get_int ( )
reason = self . server_object . check_channel_direct_tcpip_request (
2009-11-02 00:33:13 -05:00
my_chanid , ( origin_addr , origin_port ) ,
2007-12-31 00:29:50 -05:00
( dest_addr , dest_port ) )
else :
reason = self . server_object . check_channel_request ( kind , my_chanid )
2004-09-03 18:39:20 -04:00
if reason != OPEN_SUCCEEDED :
2003-12-27 22:20:42 -05:00
self . _log ( DEBUG , ' Rejecting " %s " channel request from client. ' % kind )
2003-11-09 16:14:21 -05:00
reject = True
if reject :
msg = Message ( )
2004-04-05 06:12:59 -04:00
msg . add_byte ( chr ( MSG_CHANNEL_OPEN_FAILURE ) )
2003-11-09 16:14:21 -05:00
msg . add_int ( chanid )
msg . add_int ( reason )
msg . add_string ( ' ' )
msg . add_string ( ' en ' )
2003-12-27 22:20:42 -05:00
self . _send_message ( msg )
2003-11-09 16:14:21 -05:00
return
2009-11-02 00:33:13 -05:00
2004-09-03 18:39:20 -04:00
chan = Channel ( my_chanid )
2008-03-23 04:21:10 -04:00
self . lock . acquire ( )
2003-11-09 16:14:21 -05:00
try :
2008-03-23 04:21:10 -04:00
self . _channels . put ( my_chanid , chan )
2005-10-13 14:51:18 -04:00
self . channels_seen [ my_chanid ] = True
2003-12-27 22:20:42 -05:00
chan . _set_transport ( self )
2003-12-30 02:18:20 -05:00
chan . _set_window ( self . window_size , self . max_packet_size )
chan . _set_remote_channel ( chanid , initial_window_size , max_packet_size )
2003-11-09 16:14:21 -05:00
finally :
self . lock . release ( )
m = Message ( )
2004-04-05 06:12:59 -04:00
m . add_byte ( chr ( MSG_CHANNEL_OPEN_SUCCESS ) )
2003-11-09 16:14:21 -05:00
m . add_int ( chanid )
m . add_int ( my_chanid )
m . add_int ( self . window_size )
m . add_int ( self . max_packet_size )
2003-12-27 22:20:42 -05:00
self . _send_message ( m )
2006-11-20 19:11:17 -05:00
self . _log ( INFO , ' Secsh channel %d ( %s ) opened. ' , my_chanid , kind )
2011-11-13 12:19:19 -05:00
if kind == ' auth-agent@openssh.com ' :
self . _forward_agent_handler ( chan )
elif kind == ' x11 ' :
2006-11-20 19:11:17 -05:00
self . _x11_handler ( chan , ( origin_addr , origin_port ) )
2006-12-02 21:33:34 -05:00
elif kind == ' forwarded-tcpip ' :
2008-02-20 00:59:38 -05:00
chan . origin_addr = ( origin_addr , origin_port )
2006-12-02 21:33:34 -05:00
self . _tcp_handler ( chan , ( origin_addr , origin_port ) , ( server_addr , server_port ) )
2006-11-20 19:11:17 -05:00
else :
self . _queue_incoming_channel ( chan )
2003-11-09 16:14:21 -05:00
2003-12-27 22:20:42 -05:00
def _parse_debug ( self , m ) :
2003-11-04 03:34:24 -05:00
always_display = m . get_boolean ( )
msg = m . get_string ( )
lang = m . get_string ( )
2004-04-06 04:16:02 -04:00
self . _log ( DEBUG , ' Debug msg: ' + util . safe_string ( msg ) )
2003-12-27 22:20:42 -05:00
2004-09-05 03:41:45 -04:00
def _get_subsystem_handler ( self , name ) :
try :
self . lock . acquire ( )
2006-08-29 14:18:36 -04:00
if name not in self . subsystem_table :
2004-10-31 22:43:28 -05:00
return ( None , [ ] , { } )
2004-09-05 03:41:45 -04:00
return self . subsystem_table [ name ]
finally :
self . lock . release ( )
2003-12-27 22:20:42 -05:00
_handler_table = {
2004-04-05 06:12:59 -04:00
MSG_NEWKEYS : _parse_newkeys ,
2004-04-05 06:37:18 -04:00
MSG_GLOBAL_REQUEST : _parse_global_request ,
MSG_REQUEST_SUCCESS : _parse_request_success ,
MSG_REQUEST_FAILURE : _parse_request_failure ,
2004-04-05 06:12:59 -04:00
MSG_CHANNEL_OPEN_SUCCESS : _parse_channel_open_success ,
MSG_CHANNEL_OPEN_FAILURE : _parse_channel_open_failure ,
MSG_CHANNEL_OPEN : _parse_channel_open ,
MSG_KEXINIT : _negotiate_keys ,
2003-11-04 03:34:24 -05:00
}
2003-12-27 22:20:42 -05:00
_channel_handler_table = {
2004-04-05 06:12:59 -04:00
MSG_CHANNEL_SUCCESS : Channel . _request_success ,
MSG_CHANNEL_FAILURE : Channel . _request_failed ,
MSG_CHANNEL_DATA : Channel . _feed ,
2004-12-10 03:27:43 -05:00
MSG_CHANNEL_EXTENDED_DATA : Channel . _feed_extended ,
2004-04-05 06:12:59 -04:00
MSG_CHANNEL_WINDOW_ADJUST : Channel . _window_adjust ,
MSG_CHANNEL_REQUEST : Channel . _handle_request ,
MSG_CHANNEL_EOF : Channel . _handle_eof ,
MSG_CHANNEL_CLOSE : Channel . _handle_close ,
2003-11-04 03:34:24 -05:00
}