1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 from datetime import datetime
21 import os
22 from shlex import split as shlsplit
23 import signal
24 from subprocess import Popen, PIPE
25 from select import select
26 import socket
27
28 from paramiko.ssh_exception import ProxyCommandFailure
29
30
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 """
41 """
42 Create a new CommandProxy instance. The instance created by this
43 class can be passed as an argument to the `.Transport` class.
44
45 :param str command_line:
46 the command that should be executed and used as the proxy.
47 """
48 self.cmd = shlsplit(command_line)
49 self.process = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
50 self.timeout = None
51 self.buffer = []
52
53 - def send(self, content):
54 """
55 Write the content received from the SSH client to the standard
56 input of the forked command.
57
58 :param str content: string to be sent to the forked command
59 """
60 try:
61 self.process.stdin.write(content)
62 except IOError as e:
63
64
65
66
67 raise ProxyCommandFailure(' '.join(self.cmd), e.strerror)
68 return len(content)
69
70 - def recv(self, size):
71 """
72 Read from the standard output of the forked program.
73
74 :param int size: how many chars should be read
75
76 :return: the length of the read content, as an `int`
77 """
78 try:
79 start = datetime.now()
80 while len(self.buffer) < size:
81 if self.timeout is not None:
82 elapsed = (datetime.now() - start).microseconds
83 timeout = self.timeout * 1000 * 1000
84 if elapsed >= timeout:
85 raise socket.timeout()
86 r, w, x = select([self.process.stdout], [], [], 0.0)
87 if r and r[0] == self.process.stdout:
88 b = os.read(self.process.stdout.fileno(), 1)
89
90
91
92 self.buffer.append(b)
93 result = ''.join(self.buffer)
94 self.buffer = []
95 return result
96 except socket.timeout:
97 raise
98 except IOError as e:
99 raise ProxyCommandFailure(' '.join(self.cmd), e.strerror)
100
102 os.kill(self.process.pid, signal.SIGTERM)
103
105 self.timeout = timeout
106