summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--MANIFEST.in2
-rw-r--r--Makefile1
-rw-r--r--README11
-rwxr-xr-xforward.py210
-rw-r--r--paramiko/__init__.py7
-rw-r--r--setup.py4
6 files changed, 227 insertions, 8 deletions
diff --git a/MANIFEST.in b/MANIFEST.in
index 1368ae24..2c65a02f 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,3 @@
-include ChangeLog LICENSE test.py demo.py demo_simple.py demo_server.py demo_dss_key demo_rsa_key
+include ChangeLog LICENSE test.py demo.py demo_simple.py demo_server.py demo_dss_key demo_rsa_key forward.py
recursive-include docs *
recursive-include tests *.py
diff --git a/Makefile b/Makefile
index a4e2c02c..873fbb10 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,7 @@
# doduo (04jan04) - 0.9
# eevee (08mar04)
# fearow (23apr04)
+# gyarados (31may04)
release:
python ./setup.py sdist --formats=zip
diff --git a/README b/README
index 4be8bcc6..41bf6d31 100644
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
paramiko 0.9
-"fearow" release, 23 apr 2004
+"gyarados" release, 31 may 2004
Copyright (c) 2003-2004 Robey Pointer <robey@lag.net>
@@ -117,6 +117,15 @@ the best and easiest examples of how to use the SFTP class.
highlights of what's new in each release:
+v0.9 GYARADOS
+* Transport.open_channel() -- supports local & remote port forwarding now
+* now imports UTF-8 encodings explicitly as a hint to "freeze" utilities
+* no longer rejects older SFTP servers
+* default packet size bumped to 8kB
+* fixed deadlock in closing a channel
+* Transport.connect() -- fixed bug where it would always fail when given a
+ host key to verify
+
v0.9 FEAROW
* Transport.send_ignore() -- send random ignored bytes
* RSAKey/DSSKey added from_private_key_file() as a factory constructor;
diff --git a/forward.py b/forward.py
new file mode 100755
index 00000000..0f5eacfc
--- /dev/null
+++ b/forward.py
@@ -0,0 +1,210 @@
+#!/usr/bin/python
+
+"""
+Sample script showing how to do local port forwarding over paramiko.
+
+This script connects to the requested SSH server and sets up local port
+forwarding (the openssh -L option) from a local port through a tunneled
+connection to a destination reachable from the SSH server machine.
+
+It uses SocketServer and select, so may not work on Windows.
+"""
+
+import sys
+import os
+import socket
+import select
+import SocketServer
+import getpass
+import base64
+from optparse import OptionParser
+
+import paramiko
+
+DEFAULT_PORT = 4000
+SSH_PORT = 22
+VERBOSE = True
+READPASS = False
+
+
+class ForwardServer (SocketServer.ThreadingTCPServer):
+ daemon_threads = True
+ allow_reuse_address = True
+
+
+class Handler (SocketServer.BaseRequestHandler):
+
+ def handle(self):
+ try:
+ chan = self.ssh_transport.open_channel('direct-tcpip',
+ (self.chain_host, self.chain_port),
+ self.request.getpeername())
+ except Exception, e:
+ verbose('Incoming request to %s:%d failed: %s' % (self.chain_host,
+ self.chain_port,
+ repr(e)))
+ return
+
+ verbose('Connected! Tunnel open.')
+ while True:
+ r, w, x = select.select([self.request, chan], [], [])
+ if self.request in r:
+ data = self.request.recv(1024)
+ if len(data) == 0:
+ break
+ chan.send(data)
+ if chan in r:
+ data = chan.recv(1024)
+ if len(data) == 0:
+ break
+ self.request.send(data)
+ chan.close()
+ self.request.close()
+ verbose('Tunnel closed.')
+
+
+def forward_tunnel(local_port, remote_host, remote_port, transport):
+ # this is a little convoluted, but lets me configure things for the Handler
+ # object. (SocketServer doesn't give Handlers any way to access the outer
+ # server normally.)
+ class SubHander (Handler):
+ chain_host = remote_host
+ chain_port = remote_port
+ ssh_transport = transport
+ ForwardServer(('', local_port), SubHander).serve_forever()
+
+def load_host_keys():
+ filename = os.path.expanduser('~/.ssh/known_hosts')
+ keys = {}
+ try:
+ f = open(filename, 'r')
+ except Exception, e:
+ print '*** Unable to open host keys file (%s)' % filename
+ return
+ for line in f:
+ keylist = line.split(' ')
+ if len(keylist) != 3:
+ continue
+ hostlist, keytype, key = keylist
+ hosts = hostlist.split(',')
+ for host in hosts:
+ if not keys.has_key(host):
+ keys[host] = {}
+ keys[host][keytype] = base64.decodestring(key)
+ f.close()
+ return keys
+
+def find_default_key_file():
+ filename = os.path.expanduser('~/.ssh/id_rsa')
+ if os.access(filename, os.R_OK):
+ return filename
+ filename = os.path.expanduser('~/.ssh/id_dsa')
+ if os.access(filename, os.R_OK):
+ return filename
+ return ''
+
+def verbose(s):
+ if VERBOSE:
+ print s
+
+
+#####
+
+
+parser = OptionParser(usage='usage: %prog [options] <remote-addr>:<remote-port>',
+ version='%prog 1.0')
+parser.add_option('-q', '--quiet', action='store_false', dest='verbose', default=VERBOSE,
+ help='squelch all informational output')
+parser.add_option('-l', '--local-port', action='store', type='int', dest='port',
+ default=DEFAULT_PORT,
+ help='local port to forward (default: %d)' % DEFAULT_PORT)
+parser.add_option('-r', '--host', action='store', type='string', dest='ssh_host',
+ help='SSH host to tunnel through (required)')
+parser.add_option('-p', '--port', action='store', type='int', dest='ssh_port', default=SSH_PORT,
+ help='SSH port to tunnel through (default: %d)' % SSH_PORT)
+parser.add_option('-u', '--user', action='store', type='string', dest='user',
+ default=getpass.getuser(),
+ help='username for SSH authentication (default: %s)' % getpass.getuser())
+parser.add_option('-K', '--key', action='store', type='string', dest='keyfile',
+ default=find_default_key_file(),
+ help='private key file to use for SSH authentication')
+parser.add_option('', '--no-key', action='store_false', dest='use_key', default=True,
+ help='don\'t look for or use a private key file')
+parser.add_option('-P', '--password', action='store_true', dest='readpass', default=READPASS,
+ help='read password (for key or password auth) from stdin')
+options, args = parser.parse_args()
+
+VERBOSE = options.verbose
+READPASS = options.readpass
+
+
+if len(args) != 1:
+ parser.error('Incorrect number of arguments.')
+remote_host = args[0]
+if ':' not in remote_host:
+ parser.error('Remote port missing.')
+remote_host, remote_port = remote_host.split(':', 1)
+try:
+ remote_port = int(remote_port)
+except:
+ parser.error('Remote port must be a number.')
+
+if not options.ssh_host:
+ parser.error('SSH host is required.')
+if ':' in options.ssh_host:
+ options.ssh_host, options.ssh_port = options.ssh_host.split(':', 1)
+ try:
+ options.ssh_port = int(options.ssh_port)
+ except:
+ parser.error('SSH port must be a number.')
+
+host_keys = load_host_keys()
+if not host_keys.has_key(options.ssh_host):
+ print '*** Warning: no host key for %s' % options.ssh_host
+ expected_host_key_type = None
+ expected_host_key = None
+else:
+ expected_host_key_type = host_keys[options.ssh_host].keys()[0]
+ expected_host_key = host_keys[options.ssh_host][expected_host_key_type]
+
+key = None
+password = None
+if options.use_key:
+ try:
+ key = paramiko.RSAKey.from_private_key_file(options.keyfile)
+ except paramiko.PasswordRequiredException:
+ if not READPASS:
+ print '*** Password needed for keyfile (use -P): %s' % options.keyfile
+ sys.exit(1)
+ key_password = raw_input()
+ try:
+ key = paramiko.RSAKey.from_private_key_file(options.keyfile, key_password)
+ except:
+ print '*** Unable to read keyfile: %s' % options.keyfile
+ sys.exit(1)
+ except:
+ pass
+
+if key is None:
+ # try reading a password then
+ if not READPASS:
+ print '*** Either a valid private key or password is required (use -K or -P).'
+ sys.exit(1)
+ password = raw_input()
+
+verbose('Connecting to ssh host %s:%d ...' % (options.ssh_host, options.ssh_port))
+
+transport = paramiko.Transport((options.ssh_host, options.ssh_port))
+transport.connect(hostkeytype=expected_host_key_type,
+ hostkey=expected_host_key,
+ username=options.user,
+ password=password,
+ pkey=key)
+
+verbose('Now forwarding port %d to %s:%d ...' % (options.port, remote_host, remote_port))
+
+try:
+ forward_tunnel(options.port, remote_host, remote_port, transport)
+except KeyboardInterrupt:
+ print 'Port forwarding stopped.'
+ sys.exit(0)
diff --git a/paramiko/__init__.py b/paramiko/__init__.py
index d39f7628..24503699 100644
--- a/paramiko/__init__.py
+++ b/paramiko/__init__.py
@@ -48,7 +48,7 @@ released under the GNU Lesser General Public License (LGPL).
Website: U{http://www.lag.net/~robey/paramiko/}
-@version: 0.9 (fearow)
+@version: 0.9 (gyarados)
@author: Robey Pointer
@contact: robey@lag.net
@license: GNU Lesser General Public License (LGPL)
@@ -61,9 +61,8 @@ if sys.version_info < (2, 2):
__author__ = "Robey Pointer <robey@lag.net>"
-__date__ = "23 Apr 2004"
-__version__ = "0.9-fearow"
-#__credits__ = "Huzzah!"
+__date__ = "31 May 2004"
+__version__ = "0.9-gyarados"
__license__ = "GNU Lesser General Public License (LGPL)"
diff --git a/setup.py b/setup.py
index a5ce8ecd..62a6da49 100644
--- a/setup.py
+++ b/setup.py
@@ -13,13 +13,13 @@ Required packages:
'''
setup(name = "paramiko",
- version = "0.9-fearow",
+ version = "0.9-gyarados",
description = "SSH2 protocol library",
author = "Robey Pointer",
author_email = "robey@lag.net",
url = "http://www.lag.net/~robey/paramiko/",
packages = [ 'paramiko' ],
- download_url = 'http://www.lag.net/~robey/paramiko/paramiko-0.9-fearow.zip',
+ download_url = 'http://www.lag.net/~robey/paramiko/paramiko-0.9-gyarados.zip',
license = 'LGPL',
platforms = 'Posix; MacOS X; Windows',
classifiers = [ 'Development Status :: 3 - Alpha',