OLD | NEW |
| (Empty) |
1 | |
2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
3 # See LICENSE for details. | |
4 | |
5 | |
6 """TELNET implementation, with line-oriented command handling. | |
7 """ | |
8 | |
9 import warnings | |
10 warnings.warn( | |
11 "As of Twisted 2.1, twisted.protocols.telnet is deprecated. " | |
12 "See twisted.conch.telnet for the current, supported API.", | |
13 DeprecationWarning, | |
14 stacklevel=2) | |
15 | |
16 | |
17 # System Imports | |
18 try: | |
19 from cStringIO import StringIO | |
20 except ImportError: | |
21 from StringIO import StringIO | |
22 | |
23 # Twisted Imports | |
24 from twisted import copyright | |
25 from twisted.internet import protocol | |
26 | |
27 # Some utility chars. | |
28 ESC = chr(27) # ESC for doing fanciness | |
29 BOLD_MODE_ON = ESC+"[1m" # turn bold on | |
30 BOLD_MODE_OFF= ESC+"[m" # no char attributes | |
31 | |
32 | |
33 # Characters gleaned from the various (and conflicting) RFCs. Not all of these
are correct. | |
34 | |
35 NULL = chr(0) # No operation. | |
36 LF = chr(10) # Moves the printer to the | |
37 # next print line, keeping the | |
38 # same horizontal position. | |
39 CR = chr(13) # Moves the printer to the left | |
40 # margin of the current line. | |
41 BEL = chr(7) # Produces an audible or | |
42 # visible signal (which does | |
43 # NOT move the print head). | |
44 BS = chr(8) # Moves the print head one | |
45 # character position towards | |
46 # the left margin. | |
47 HT = chr(9) # Moves the printer to the | |
48 # next horizontal tab stop. | |
49 # It remains unspecified how | |
50 # either party determines or | |
51 # establishes where such tab | |
52 # stops are located. | |
53 VT = chr(11) # Moves the printer to the | |
54 # next vertical tab stop. It | |
55 # remains unspecified how | |
56 # either party determines or | |
57 # establishes where such tab | |
58 # stops are located. | |
59 FF = chr(12) # Moves the printer to the top | |
60 # of the next page, keeping | |
61 # the same horizontal position. | |
62 SE = chr(240) # End of subnegotiation parameters. | |
63 NOP= chr(241) # No operation. | |
64 DM = chr(242) # "Data Mark": The data stream portion | |
65 # of a Synch. This should always be | |
66 # accompanied by a TCP Urgent | |
67 # notification. | |
68 BRK= chr(243) # NVT character Break. | |
69 IP = chr(244) # The function Interrupt Process. | |
70 AO = chr(245) # The function Abort Output | |
71 AYT= chr(246) # The function Are You There. | |
72 EC = chr(247) # The function Erase Character. | |
73 EL = chr(248) # The function Erase Line | |
74 GA = chr(249) # The Go Ahead signal. | |
75 SB = chr(250) # Indicates that what follows is | |
76 # subnegotiation of the indicated | |
77 # option. | |
78 WILL = chr(251) # Indicates the desire to begin | |
79 # performing, or confirmation that | |
80 # you are now performing, the | |
81 # indicated option. | |
82 WONT = chr(252) # Indicates the refusal to perform, | |
83 # or continue performing, the | |
84 # indicated option. | |
85 DO = chr(253) # Indicates the request that the | |
86 # other party perform, or | |
87 # confirmation that you are expecting | |
88 # the other party to perform, the | |
89 # indicated option. | |
90 DONT = chr(254) # Indicates the demand that the | |
91 # other party stop performing, | |
92 # or confirmation that you are no | |
93 # longer expecting the other party | |
94 # to perform, the indicated option. | |
95 IAC = chr(255) # Data Byte 255. | |
96 | |
97 # features | |
98 | |
99 ECHO = chr(1) # User-to-Server: Asks the server to send | |
100 # Echos of the transmitted data. | |
101 | |
102 # Server-to User: States that the server is | |
103 # sending echos of the transmitted data. | |
104 # Sent only as a reply to ECHO or NO ECHO. | |
105 | |
106 SUPGA = chr(3) # Supress Go Ahead...? "Modern" telnet servers | |
107 # are supposed to do this. | |
108 | |
109 LINEMODE = chr(34) # I don't care that Jon Postel is dead. | |
110 | |
111 HIDE = chr(133) # The intention is that a server will send | |
112 # this signal to a user system which is | |
113 # echoing locally (to the user) when the user | |
114 # is about to type something secret (e.g. a | |
115 # password). In this case, the user system | |
116 # is to suppress local echoing or overprint | |
117 # the input (or something) until the server | |
118 # sends a NOECHO signal. In situations where | |
119 # the user system is not echoing locally, | |
120 # this signal must not be sent by the server. | |
121 | |
122 | |
123 NOECHO= chr(131) # User-to-Server: Asks the server not to | |
124 # return Echos of the transmitted data. | |
125 # | |
126 # Server-to-User: States that the server is | |
127 # not sending echos of the transmitted data. | |
128 # Sent only as a reply to ECHO or NO ECHO, | |
129 # or to end the hide your input. | |
130 | |
131 | |
132 | |
133 iacBytes = { | |
134 DO: 'DO', | |
135 DONT: 'DONT', | |
136 WILL: 'WILL', | |
137 WONT: 'WONT', | |
138 IP: 'IP' | |
139 } | |
140 | |
141 def multireplace(st, dct): | |
142 for k, v in dct.items(): | |
143 st = st.replace(k, v) | |
144 return st | |
145 | |
146 class Telnet(protocol.Protocol): | |
147 """I am a Protocol for handling Telnet connections. I have two | |
148 sets of special methods, telnet_* and iac_*. | |
149 | |
150 telnet_* methods get called on every line sent to me. The method | |
151 to call is decided by the current mode. The initial mode is 'User'; | |
152 this means that telnet_User is the first telnet_* method to be called. | |
153 All telnet_* methods should return a string which specifies the mode | |
154 to go into next; thus dictating which telnet_* method to call next. | |
155 For example, the default telnet_User method returns 'Password' to go | |
156 into Password mode, and the default telnet_Password method returns | |
157 'Command' to go into Command mode. | |
158 | |
159 The iac_* methods are less-used; they are called when an IAC telnet | |
160 byte is received. You can define iac_DO, iac_DONT, iac_WILL, iac_WONT, | |
161 and iac_IP methods to do what you want when one of these bytes is | |
162 received.""" | |
163 | |
164 | |
165 gotIAC = 0 | |
166 iacByte = None | |
167 lastLine = None | |
168 buffer = '' | |
169 echo = 0 | |
170 delimiters = ['\r\n', '\r\000'] | |
171 mode = "User" | |
172 | |
173 def write(self, data): | |
174 """Send the given data over my transport.""" | |
175 self.transport.write(data) | |
176 | |
177 | |
178 def connectionMade(self): | |
179 """I will write a welcomeMessage and loginPrompt to the client.""" | |
180 self.write(self.welcomeMessage() + self.loginPrompt()) | |
181 | |
182 def welcomeMessage(self): | |
183 """Override me to return a string which will be sent to the client | |
184 before login.""" | |
185 x = self.factory.__class__ | |
186 return ("\r\n" + x.__module__ + '.' + x.__name__ + | |
187 '\r\nTwisted %s\r\n' % copyright.version | |
188 ) | |
189 | |
190 def loginPrompt(self): | |
191 """Override me to return a 'login:'-type prompt.""" | |
192 return "username: " | |
193 | |
194 def iacSBchunk(self, chunk): | |
195 pass | |
196 | |
197 def iac_DO(self, feature): | |
198 pass | |
199 | |
200 def iac_DONT(self, feature): | |
201 pass | |
202 | |
203 def iac_WILL(self, feature): | |
204 pass | |
205 | |
206 def iac_WONT(self, feature): | |
207 pass | |
208 | |
209 def iac_IP(self, feature): | |
210 pass | |
211 | |
212 def processLine(self, line): | |
213 """I call a method that looks like 'telnet_*' where '*' is filled | |
214 in by the current mode. telnet_* methods should return a string which | |
215 will become the new mode. If None is returned, the mode will not change
. | |
216 """ | |
217 mode = getattr(self, "telnet_"+self.mode)(line) | |
218 if mode is not None: | |
219 self.mode = mode | |
220 | |
221 def telnet_User(self, user): | |
222 """I take a username, set it to the 'self.username' attribute, | |
223 print out a password prompt, and switch to 'Password' mode. If | |
224 you want to do something else when the username is received (ie, | |
225 create a new user if the user doesn't exist), override me.""" | |
226 self.username = user | |
227 self.write(IAC+WILL+ECHO+"password: ") | |
228 return "Password" | |
229 | |
230 def telnet_Password(self, paswd): | |
231 """I accept a password as an argument, and check it with the | |
232 checkUserAndPass method. If the login is successful, I call | |
233 loggedIn().""" | |
234 self.write(IAC+WONT+ECHO+"*****\r\n") | |
235 try: | |
236 checked = self.checkUserAndPass(self.username, paswd) | |
237 except: | |
238 return "Done" | |
239 if not checked: | |
240 return "Done" | |
241 self.loggedIn() | |
242 return "Command" | |
243 | |
244 def telnet_Command(self, cmd): | |
245 """The default 'command processing' mode. You probably want to | |
246 override me.""" | |
247 return "Command" | |
248 | |
249 def processChunk(self, chunk): | |
250 """I take a chunk of data and delegate out to telnet_* methods | |
251 by way of processLine. If the current mode is 'Done', I'll close | |
252 the connection. """ | |
253 self.buffer = self.buffer + chunk | |
254 | |
255 #yech. | |
256 for delim in self.delimiters: | |
257 idx = self.buffer.find(delim) | |
258 if idx != -1: | |
259 break | |
260 | |
261 while idx != -1: | |
262 buf, self.buffer = self.buffer[:idx], self.buffer[idx+2:] | |
263 self.processLine(buf) | |
264 if self.mode == 'Done': | |
265 self.transport.loseConnection() | |
266 | |
267 for delim in self.delimiters: | |
268 idx = self.buffer.find(delim) | |
269 if idx != -1: | |
270 break | |
271 | |
272 def dataReceived(self, data): | |
273 chunk = StringIO() | |
274 # silly little IAC state-machine | |
275 for char in data: | |
276 if self.gotIAC: | |
277 # working on an IAC request state | |
278 if self.iacByte: | |
279 # we're in SB mode, getting a chunk | |
280 if self.iacByte == SB: | |
281 if char == SE: | |
282 self.iacSBchunk(chunk.getvalue()) | |
283 chunk = StringIO() | |
284 del self.iacByte | |
285 del self.gotIAC | |
286 else: | |
287 chunk.write(char) | |
288 else: | |
289 # got all I need to know state | |
290 try: | |
291 getattr(self, 'iac_%s' % iacBytes[self.iacByte])(cha
r) | |
292 except KeyError: | |
293 pass | |
294 del self.iacByte | |
295 del self.gotIAC | |
296 else: | |
297 # got IAC, this is my W/W/D/D (or perhaps sb) | |
298 self.iacByte = char | |
299 elif char == IAC: | |
300 # Process what I've got so far before going into | |
301 # the IAC state; don't want to process characters | |
302 # in an inconsistent state with what they were | |
303 # received in. | |
304 c = chunk.getvalue() | |
305 if c: | |
306 why = self.processChunk(c) | |
307 if why: | |
308 return why | |
309 chunk = StringIO() | |
310 self.gotIAC = 1 | |
311 else: | |
312 chunk.write(char) | |
313 # chunks are of a relatively indeterminate size. | |
314 c = chunk.getvalue() | |
315 if c: | |
316 why = self.processChunk(c) | |
317 if why: | |
318 return why | |
319 | |
320 def loggedIn(self): | |
321 """Called after the user succesfully logged in. | |
322 | |
323 Override in subclasses. | |
324 """ | |
325 pass | |
OLD | NEW |