1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """
20 L{ProxyCommand}.
21 """
22
23 from datetime import datetime
24 import os
25 from shlex import split as shlsplit
26 import signal
27 from subprocess import Popen, PIPE
28 from select import select
29 import socket
30
31 from paramiko.ssh_exception import ProxyCommandFailure
32
33
35 """
36 Wraps a subprocess running ProxyCommand-driven programs.
37
38 This class implements a the socket-like interface needed by the
39 L{Transport} and L{Packetizer} classes. Using this class instead of a
40 regular socket makes it possible to talk with a Popen'd command that will
41 proxy traffic between the client and a server hosted in another machine.
42 """
44 """
45 Create a new CommandProxy instance. The instance created by this
46 class can be passed as an argument to the L{Transport} class.
47
48 @param command_line: the command that should be executed and
49 used as the proxy.
50 @type command_line: str
51 """
52 self.cmd = shlsplit(command_line)
53 self.process = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
54 self.timeout = None
55 self.buffer = []
56
57 - def send(self, content):
58 """
59 Write the content received from the SSH client to the standard
60 input of the forked command.
61
62 @param content: string to be sent to the forked command
63 @type content: str
64 """
65 try:
66 self.process.stdin.write(content)
67 except IOError, e:
68
69
70
71
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 size: how many chars should be read
80 @type size: int
81
82 @return: the length of the read content
83 @rtype: int
84 """
85 try:
86 start = datetime.now()
87 while len(self.buffer) < size:
88 if self.timeout is not None:
89 elapsed = (datetime.now() - start).microseconds
90 timeout = self.timeout * 1000 * 1000
91 if elapsed >= timeout:
92 raise socket.timeout()
93 r, w, x = select([self.process.stdout], [], [], 0.0)
94 if r and r[0] == self.process.stdout:
95 b = os.read(self.process.stdout.fileno(), 1)
96
97
98
99 self.buffer.append(b)
100 result = ''.join(self.buffer)
101 self.buffer = []
102 return result
103 except socket.timeout:
104 raise
105 except IOError, e:
106 raise ProxyCommandFailure(' '.join(self.cmd), e.strerror)
107
109 os.kill(self.process.pid, signal.SIGTERM)
110
112 self.timeout = timeout
113