~davide-cst00/uftp/0.2

« back to all changes in this revision

Viewing changes to paramiko/proxy.py

  • Committer: Davide Costa
  • Date: 2018-09-14 12:21:37 UTC
  • Revision ID: davide.cst00@gmail.com-20180914122137-j0ycqb4tk9z2r1k5
Xenial release 0.2.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2012  Yipit, Inc <coders@yipit.com>
 
2
#
 
3
# This file is part of paramiko.
 
4
#
 
5
# Paramiko is free software; you can redistribute it and/or modify it under the
 
6
# terms of the GNU Lesser General Public License as published by the Free
 
7
# Software Foundation; either version 2.1 of the License, or (at your option)
 
8
# any later version.
 
9
#
 
10
# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY
 
11
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 
12
# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
 
13
# details.
 
14
#
 
15
# You should have received a copy of the GNU Lesser General Public License
 
16
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
 
17
# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 
18
 
 
19
 
 
20
import os
 
21
from shlex import split as shlsplit
 
22
import signal
 
23
from select import select
 
24
import socket
 
25
import time
 
26
 
 
27
from paramiko.ssh_exception import ProxyCommandFailure
 
28
from paramiko.util import ClosingContextManager
 
29
 
 
30
 
 
31
class ProxyCommand(ClosingContextManager):
 
32
    """
 
33
    Wraps a subprocess running ProxyCommand-driven programs.
 
34
 
 
35
    This class implements a the socket-like interface needed by the
 
36
    `.Transport` and `.Packetizer` classes. Using this class instead of a
 
37
    regular socket makes it possible to talk with a Popen'd command that will
 
38
    proxy traffic between the client and a server hosted in another machine.
 
39
 
 
40
    Instances of this class may be used as context managers.
 
41
    """
 
42
    def __init__(self, command_line):
 
43
        """
 
44
        Create a new CommandProxy instance. The instance created by this
 
45
        class can be passed as an argument to the `.Transport` class.
 
46
 
 
47
        :param str command_line:
 
48
            the command that should be executed and used as the proxy.
 
49
        """
 
50
        # NOTE: subprocess import done lazily so platforms without it (e.g.
 
51
        # GAE) can still import us during overall Paramiko load.
 
52
        from subprocess import Popen, PIPE
 
53
        self.cmd = shlsplit(command_line)
 
54
        self.process = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE,
 
55
                             bufsize=0)
 
56
        self.timeout = None
 
57
 
 
58
    def send(self, content):
 
59
        """
 
60
        Write the content received from the SSH client to the standard
 
61
        input of the forked command.
 
62
 
 
63
        :param str content: string to be sent to the forked command
 
64
        """
 
65
        try:
 
66
            self.process.stdin.write(content)
 
67
        except IOError as e:
 
68
            # There was a problem with the child process. It probably
 
69
            # died and we can't proceed. The best option here is to
 
70
            # raise an exception informing the user that the informed
 
71
            # ProxyCommand is not working.
 
72
            raise ProxyCommandFailure(' '.join(self.cmd), e.strerror)
 
73
        return len(content)
 
74
 
 
75
    def recv(self, size):
 
76
        """
 
77
        Read from the standard output of the forked program.
 
78
 
 
79
        :param int size: how many chars should be read
 
80
 
 
81
        :return: the string of bytes read, which may be shorter than requested
 
82
        """
 
83
        try:
 
84
            buffer = b''
 
85
            start = time.time()
 
86
            while len(buffer) < size:
 
87
                select_timeout = None
 
88
                if self.timeout is not None:
 
89
                    elapsed = (time.time() - start)
 
90
                    if elapsed >= self.timeout:
 
91
                        raise socket.timeout()
 
92
                    select_timeout = self.timeout - elapsed
 
93
 
 
94
                r, w, x = select(
 
95
                    [self.process.stdout], [], [], select_timeout)
 
96
                if r and r[0] == self.process.stdout:
 
97
                    buffer += os.read(
 
98
                        self.process.stdout.fileno(), size - len(buffer))
 
99
            return buffer
 
100
        except socket.timeout:
 
101
            if buffer:
 
102
                # Don't raise socket.timeout, return partial result instead
 
103
                return buffer
 
104
            raise  # socket.timeout is a subclass of IOError
 
105
        except IOError as e:
 
106
            raise ProxyCommandFailure(' '.join(self.cmd), e.strerror)
 
107
 
 
108
    def close(self):
 
109
        os.kill(self.process.pid, signal.SIGTERM)
 
110
 
 
111
    @property
 
112
    def closed(self):
 
113
        return self.process.returncode is not None
 
114
 
 
115
    @property
 
116
    def _closed(self):
 
117
        # Concession to Python 3 socket-like API
 
118
        return self.closed
 
119
 
 
120
    def settimeout(self, timeout):
 
121
        self.timeout = timeout