diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 17:47:36 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 17:47:36 +0000 |
commit | 3c7813683b1845959aca706eaa23f062a006356b (patch) | |
tree | ecba42f14f0c919d94332e2633d9b0e6834c9cec /demos/rforward.py | |
parent | Initial commit. (diff) | |
download | paramiko-3c7813683b1845959aca706eaa23f062a006356b.tar.xz paramiko-3c7813683b1845959aca706eaa23f062a006356b.zip |
Adding upstream version 3.4.0.upstream/3.4.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'demos/rforward.py')
-rwxr-xr-x | demos/rforward.py | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/demos/rforward.py b/demos/rforward.py new file mode 100755 index 0000000..200634a --- /dev/null +++ b/demos/rforward.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python + +# Copyright (C) 2008 Robey Pointer <robeypointer@gmail.com> +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" +Sample script showing how to do remote port forwarding over paramiko. + +This script connects to the requested SSH server and sets up remote port +forwarding (the openssh -R option) from a remote port through a tunneled +connection to a destination reachable from the local machine. +""" + +import getpass +import os +import socket +import select +import sys +import threading +from optparse import OptionParser + +import paramiko + +SSH_PORT = 22 +DEFAULT_PORT = 4000 + +g_verbose = True + + +def handler(chan, host, port): + sock = socket.socket() + try: + sock.connect((host, port)) + except Exception as e: + verbose("Forwarding request to %s:%d failed: %r" % (host, port, e)) + return + + verbose( + "Connected! Tunnel open %r -> %r -> %r" + % (chan.origin_addr, chan.getpeername(), (host, port)) + ) + while True: + r, w, x = select.select([sock, chan], [], []) + if sock in r: + data = sock.recv(1024) + if len(data) == 0: + break + chan.send(data) + if chan in r: + data = chan.recv(1024) + if len(data) == 0: + break + sock.send(data) + chan.close() + sock.close() + verbose("Tunnel closed from %r" % (chan.origin_addr,)) + + +def reverse_forward_tunnel(server_port, remote_host, remote_port, transport): + transport.request_port_forward("", server_port) + while True: + chan = transport.accept(1000) + if chan is None: + continue + thr = threading.Thread( + target=handler, args=(chan, remote_host, remote_port) + ) + thr.setDaemon(True) + thr.start() + + +def verbose(s): + if g_verbose: + print(s) + + +HELP = """\ +Set up a reverse forwarding tunnel across an SSH server, using paramiko. A +port on the SSH server (given with -p) is forwarded across an SSH session +back to the local machine, and out to a remote site reachable from this +network. This is similar to the openssh -R option. +""" + + +def get_host_port(spec, default_port): + "parse 'hostname:22' into a host and port, with the port optional" + args = (spec.split(":", 1) + [default_port])[:2] + args[1] = int(args[1]) + return args[0], args[1] + + +def parse_options(): + global g_verbose + + parser = OptionParser( + usage="usage: %prog [options] <ssh-server>[:<server-port>]", + version="%prog 1.0", + description=HELP, + ) + parser.add_option( + "-q", + "--quiet", + action="store_false", + dest="verbose", + default=True, + help="squelch all informational output", + ) + parser.add_option( + "-p", + "--remote-port", + action="store", + type="int", + dest="port", + default=DEFAULT_PORT, + help="port on server to forward (default: %d)" % DEFAULT_PORT, + ) + parser.add_option( + "-u", + "--user", + action="store", + type="string", + dest="user", + default=getpass.getuser(), + help="username for SSH authentication (default: %s)" + % getpass.getuser(), + ) + parser.add_option( + "-K", + "--key", + action="store", + type="string", + dest="keyfile", + default=None, + help="private key file to use for SSH authentication", + ) + parser.add_option( + "", + "--no-key", + action="store_false", + dest="look_for_keys", + default=True, + help="don't look for or use a private key file", + ) + parser.add_option( + "-P", + "--password", + action="store_true", + dest="readpass", + default=False, + help="read password (for key or password auth) from stdin", + ) + parser.add_option( + "-r", + "--remote", + action="store", + type="string", + dest="remote", + default=None, + metavar="host:port", + help="remote host and port to forward to", + ) + options, args = parser.parse_args() + + if len(args) != 1: + parser.error("Incorrect number of arguments.") + if options.remote is None: + parser.error("Remote address required (-r).") + + g_verbose = options.verbose + server_host, server_port = get_host_port(args[0], SSH_PORT) + remote_host, remote_port = get_host_port(options.remote, SSH_PORT) + return options, (server_host, server_port), (remote_host, remote_port) + + +def main(): + options, server, remote = parse_options() + + password = None + if options.readpass: + password = getpass.getpass("Enter SSH password: ") + + client = paramiko.SSHClient() + client.load_system_host_keys() + client.set_missing_host_key_policy(paramiko.WarningPolicy()) + + verbose("Connecting to ssh host %s:%d ..." % (server[0], server[1])) + try: + client.connect( + server[0], + server[1], + username=options.user, + key_filename=options.keyfile, + look_for_keys=options.look_for_keys, + password=password, + ) + except Exception as e: + print("*** Failed to connect to %s:%d: %r" % (server[0], server[1], e)) + sys.exit(1) + + verbose( + "Now forwarding remote port %d to %s:%d ..." + % (options.port, remote[0], remote[1]) + ) + + try: + reverse_forward_tunnel( + options.port, remote[0], remote[1], client.get_transport() + ) + except KeyboardInterrupt: + print("C-c: Port forwarding stopped.") + sys.exit(0) + + +if __name__ == "__main__": + main() |