1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 """Backdoor Python access.
25
26 This module implements a server that allows one to telnet to a socket and get a
27 Python prompt in a process.
28
29 Simply spawn a thread running the `serve` function to start a backdoor server.
30 """
31
32 VERSION_STRING = '$Id: //prod/main/ap/shrapnel/coro/backdoor.py#6 $'
33
34 import coro
35 import cStringIO
36 import fcntl
37 import sys
38 import traceback
39 import os
40
41
42
43
44
45
46
47
48
49
51
52 - def __init__ (self, sock, line_separator='\r\n', welcome_message=None, global_dict=None):
53 self.sock = sock
54 self.buffer = ''
55 self.lines = []
56 self.multilines = []
57 self.line_separator = line_separator
58 self.welcome_message = welcome_message
59 self.global_dict = global_dict
60
61
62 if not sys.__dict__.has_key('ps1'):
63 sys.ps1 = '>>> '
64 if not sys.__dict__.has_key('ps2'):
65 sys.ps2 = '... '
66
67 - def send (self, data):
72
74 if self.multilines:
75 self.send (sys.ps2)
76 else:
77 self.send (sys.ps1)
78
80 if self.lines:
81 l = self.lines[0]
82 self.lines = self.lines[1:]
83 return l
84 else:
85 while not self.lines:
86 block = self.sock.recv (8192)
87 if not block:
88 return None
89 elif block == '\004':
90 self.sock.close()
91 return None
92 else:
93 self.buffer = self.buffer + block
94 lines = self.buffer.split (self.line_separator)
95 for l in lines[:-1]:
96 self.lines.append (l)
97 self.buffer = lines[-1]
98 return self.read_line()
99
101 self.send ('Python ' + sys.version + self.line_separator)
102 self.send (sys.copyright + self.line_separator)
103 if self.welcome_message is not None:
104
105
106 lines = self.welcome_message.split ('\n')
107 if lines[-1] != '':
108 lines.append('')
109 self.send (self.line_separator.join (lines))
110
112 self.send_welcome_message()
113
114 if self.global_dict is None:
115
116 env = sys.modules['__main__'].__dict__.copy()
117 else:
118 env = self.global_dict.copy()
119
120 while 1:
121 self.prompt()
122 line = self.read_line()
123 if line is None:
124 break
125 elif self.multilines:
126 self.multilines.append(line)
127 if line == '':
128 code = '\n'.join (self.multilines)
129 self.parse(code, env)
130
131
132
133 self.multilines = []
134 else:
135 self.parse(line, env)
136
137 - def parse(self, line, env):
138 save = sys.stdout, sys.stderr
139 output = cStringIO.StringIO()
140 try:
141 try:
142 sys.stdout = sys.stderr = output
143 co = compile (line, repr(self), 'eval')
144 result = eval (co, env)
145 if result is not None:
146 print repr(result)
147 env['_'] = result
148 except SyntaxError:
149 try:
150 co = compile (line, repr(self), 'exec')
151 exec co in env
152 except SyntaxError, msg:
153
154 if not self.multilines and msg[0] == 'unexpected EOF while parsing':
155 self.multilines.append(line)
156 else:
157 traceback.print_exc()
158 except:
159 traceback.print_exc()
160 except:
161 traceback.print_exc()
162 finally:
163 sys.stdout, sys.stderr = save
164 self.send (output.getvalue())
165 del output
166
167 -def client (conn, addr, welcome_message=None, global_dict=None):
170
171 -def serve (port=None, ip='', unix_path=None, welcome_message=None, global_dict=None):
172 """Backdoor server function.
173
174 This function will listen on the backdoor socket and spawn new threads for
175 each connection.
176
177 :Parameters:
178 - `port`: The IPv4 port to listen on (defaults to automatically choose
179 an unused port between 8023->8033). May also be a list of ports.
180 - `ip`: The IP to listen on. Defaults to all IP's.
181 - `unix_path`: The unix path to listen on. If this is specified, then
182 it will use unix-domain sockets, otherwise it will use IPv4 sockets.
183 - `welcome_message`: A welcome message to display when a user logs in.
184 - `global_dict`: The global dictionary to use for client sessions.
185 """
186 import errno
187 if unix_path:
188 try:
189 os.remove (unix_path)
190 except OSError, why:
191 if why[0]==errno.ENOENT:
192 pass
193 else:
194 raise
195 s = coro.make_socket (coro.PF.LOCAL, coro.SOCK.STREAM)
196 s.bind (unix_path)
197 coro.print_stderr('Backdoor started on unix socket %s\n' % unix_path)
198 else:
199 s = coro.make_socket (coro.PF.INET, coro.SOCK.STREAM)
200 s.set_reuse_addr()
201 if port is None:
202 ports = xrange(8023, 8033)
203 else:
204 if type(port) is int:
205 ports = [port]
206 else:
207 ports = port
208 for i in ports:
209 try:
210 s.bind ((ip, i))
211 coro.print_stderr('Backdoor started on port %d\n' % i)
212 break
213 except OSError, why:
214 if why[0] != errno.EADDRINUSE:
215 raise OSError, why
216 else:
217 raise Exception, "couldn't bind a port (try not specifying a port)"
218
219 flags = fcntl.fcntl(s.fileno(), fcntl.F_GETFD)
220 fcntl.fcntl(s.fileno(), fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)
221
222 s.listen (1024)
223 while 1:
224 conn, addr = s.accept()
225 coro.print_stderr ('incoming connection from %r\n' % (conn.getsockname(),))
226 thread = coro.spawn (client, conn, addr, welcome_message, global_dict)
227 thread.set_name('backdoor session')
228
229 if __name__ == '__main__':
230 thread = coro.spawn (serve, welcome_message='Testing backdoor.py')
231 thread.set_name('backdoor server')
232 coro.event_loop (30.0)
233