2
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3
# See LICENSE for details.
6
"""TELNET implementation, with line-oriented command handling.
11
"As of Twisted 2.1, twisted.protocols.telnet is deprecated. "
12
"See twisted.conch.telnet for the current, supported API.",
19
from cStringIO import StringIO
21
from StringIO import StringIO
24
from twisted import copyright
25
from twisted.internet import protocol
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
33
# Characters gleaned from the various (and conflicting) RFCs. Not all of these are correct.
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
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
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
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
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
78
WILL = chr(251) # Indicates the desire to begin
79
# performing, or confirmation that
80
# you are now performing, the
82
WONT = chr(252) # Indicates the refusal to perform,
83
# or continue performing, the
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
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.
99
ECHO = chr(1) # User-to-Server: Asks the server to send
100
# Echos of the transmitted data.
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.
106
SUPGA = chr(3) # Supress Go Ahead...? "Modern" telnet servers
107
# are supposed to do this.
109
LINEMODE = chr(34) # I don't care that Jon Postel is dead.
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.
123
NOECHO= chr(131) # User-to-Server: Asks the server not to
124
# return Echos of the transmitted data.
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.
141
def multireplace(st, dct):
142
for k, v in dct.items():
143
st = st.replace(k, v)
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_*.
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.
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
170
delimiters = ['\r\n', '\r\000']
173
def write(self, data):
174
"""Send the given data over my transport."""
175
self.transport.write(data)
178
def connectionMade(self):
179
"""I will write a welcomeMessage and loginPrompt to the client."""
180
self.write(self.welcomeMessage() + self.loginPrompt())
182
def welcomeMessage(self):
183
"""Override me to return a string which will be sent to the client
185
x = self.factory.__class__
186
return ("\r\n" + x.__module__ + '.' + x.__name__ +
187
'\r\nTwisted %s\r\n' % copyright.version
190
def loginPrompt(self):
191
"""Override me to return a 'login:'-type prompt."""
194
def iacSBchunk(self, chunk):
197
def iac_DO(self, feature):
200
def iac_DONT(self, feature):
203
def iac_WILL(self, feature):
206
def iac_WONT(self, feature):
209
def iac_IP(self, feature):
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.
217
mode = getattr(self, "telnet_"+self.mode)(line)
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."""
227
self.write(IAC+WILL+ECHO+"password: ")
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
234
self.write(IAC+WONT+ECHO+"*****\r\n")
236
checked = self.checkUserAndPass(self.username, paswd)
244
def telnet_Command(self, cmd):
245
"""The default 'command processing' mode. You probably want to
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
253
self.buffer = self.buffer + chunk
256
for delim in self.delimiters:
257
idx = self.buffer.find(delim)
262
buf, self.buffer = self.buffer[:idx], self.buffer[idx+2:]
263
self.processLine(buf)
264
if self.mode == 'Done':
265
self.transport.loseConnection()
267
for delim in self.delimiters:
268
idx = self.buffer.find(delim)
272
def dataReceived(self, data):
274
# silly little IAC state-machine
277
# working on an IAC request state
279
# we're in SB mode, getting a chunk
280
if self.iacByte == SB:
282
self.iacSBchunk(chunk.getvalue())
289
# got all I need to know state
291
getattr(self, 'iac_%s' % iacBytes[self.iacByte])(char)
297
# got IAC, this is my W/W/D/D (or perhaps sb)
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
306
why = self.processChunk(c)
313
# chunks are of a relatively indeterminate size.
316
why = self.processChunk(c)
321
"""Called after the user succesfully logged in.
323
Override in subclasses.