root / trunk / twisted / protocols / telnet.py

Revision 15092, 12.0 kB (checked in by exarkun, 4 years ago)

Remove unused imports; fix NameErrors?

Notes: twisted.runner.inetdtap.py is a mess
Notes: old cred really needs to go
Notes: lore is a mess
Notes: more notes later

Line 
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])(char)
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
Note: See TracBrowser for help on using the browser.