mirror of https://github.com/python/cpython.git
182 lines
4.2 KiB
Python
182 lines
4.2 KiB
Python
|
# Telnet client library
|
||
|
|
||
|
import socket
|
||
|
import select
|
||
|
import string
|
||
|
import regsub
|
||
|
|
||
|
# Tunable parameters
|
||
|
TIMEOUT = 30.0
|
||
|
DEBUGLEVEL = 1
|
||
|
|
||
|
# Telnet protocol defaults
|
||
|
TELNET_PORT = 23
|
||
|
|
||
|
# Telnet protocol characters (don't change)
|
||
|
IAC = chr(255) # "Interpret As Command"
|
||
|
DONT = chr(254)
|
||
|
DO = chr(253)
|
||
|
WONT = chr(252)
|
||
|
WILL = chr(251)
|
||
|
|
||
|
|
||
|
# Telnet interface class
|
||
|
|
||
|
class Telnet:
|
||
|
|
||
|
# Constructor
|
||
|
def __init__(self, host, port):
|
||
|
self.debuglevel = DEBUGLEVEL
|
||
|
self.host = host
|
||
|
if not port: port = TELNET_PORT
|
||
|
self.port = port
|
||
|
self.timeout = TIMEOUT
|
||
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||
|
self.sock.connect((self.host, self.port))
|
||
|
self.rawq = ''
|
||
|
self.irawq = 0
|
||
|
self.cookedq = ''
|
||
|
|
||
|
# Destructor
|
||
|
def __del__(self):
|
||
|
self.close()
|
||
|
|
||
|
# Print debug message
|
||
|
def msg(self, msg, *args):
|
||
|
if self.debuglevel > 0:
|
||
|
print 'TELNET:', msg%args
|
||
|
|
||
|
# Set debug level
|
||
|
def set_debuglevel(self, debuglevel):
|
||
|
self.debuglevel = debuglevel
|
||
|
|
||
|
# Set time-out on certain reads
|
||
|
def set_timeout(self, timeout):
|
||
|
self.timeout = float(timeout)
|
||
|
|
||
|
# Explicit close
|
||
|
def close(self):
|
||
|
if self.sock:
|
||
|
self.sock.close()
|
||
|
self.sock = None
|
||
|
|
||
|
# Return socket (e.g. for select)
|
||
|
def get_socket(self):
|
||
|
return self.sock
|
||
|
|
||
|
# Return socket's fileno (e.g. for select)
|
||
|
def fileno(self):
|
||
|
return self.sock.fileno()
|
||
|
|
||
|
# Write a string to the socket, doubling any IAC characters
|
||
|
def write(self, buffer):
|
||
|
if IAC in buffer:
|
||
|
buffer = regsub.gsub(IAC, IAC+IAC, buffer)
|
||
|
self.sock.send(buffer)
|
||
|
|
||
|
# Read until a given string is encountered or until timeout
|
||
|
def read_until(self, match):
|
||
|
## self.msg('read_until(%s)' % `match`)
|
||
|
n = len(match)
|
||
|
self.process_rawq()
|
||
|
i = string.find(self.cookedq, match)
|
||
|
if i < 0:
|
||
|
i = max(0, len(self.cookedq)-n)
|
||
|
self.fill_cookedq()
|
||
|
i = string.find(self.cookedq, match, i)
|
||
|
if i >= 0:
|
||
|
i = i+n
|
||
|
buf = self.cookedq[:i]
|
||
|
self.cookedq = self.cookedq[i:]
|
||
|
## self.msg('read_until(%s) -> %s' % (`match`, `buf`))
|
||
|
return buf
|
||
|
while select.select([self], [], [], self.timeout) == \
|
||
|
([self], [], []):
|
||
|
i = max(0, len(self.cookedq)-n)
|
||
|
self.fill_rawq()
|
||
|
self.process_rawq()
|
||
|
i = string.find(self.cookedq, match, i)
|
||
|
if i >= 0:
|
||
|
i = i+n
|
||
|
buf = self.cookedq[:i]
|
||
|
self.cookedq = self.cookedq[i:]
|
||
|
## self.msg('read_until(%s) -> %s' %
|
||
|
## (`match`, `buf`))
|
||
|
return buf
|
||
|
buf = self.cookedq
|
||
|
self.cookedq = ''
|
||
|
## self.msg('read_until(%s) -> %s' % (`match`, `buf`))
|
||
|
return buf
|
||
|
|
||
|
# Read everything that's possible without really blocking
|
||
|
def read_now(self):
|
||
|
self.fill_cookedq()
|
||
|
buf = self.cookedq
|
||
|
self.cookedq = ''
|
||
|
## self.msg('read_now() --> %s' % `buf`)
|
||
|
return buf
|
||
|
|
||
|
# Fill cooked queue without blocking
|
||
|
def fill_cookedq(self):
|
||
|
self.process_rawq()
|
||
|
while select.select([self], [], [], 0) == ([self], [], []):
|
||
|
self.fill_rawq()
|
||
|
if not self.rawq:
|
||
|
raise EOFError
|
||
|
self.process_rawq()
|
||
|
|
||
|
# Transfer from raw queue to cooked queue
|
||
|
def process_rawq(self):
|
||
|
# There is some silliness going on here in an attempt
|
||
|
# to avoid quadratic behavior with large inputs...
|
||
|
buf = ''
|
||
|
while self.rawq:
|
||
|
c = self.rawq_getchar()
|
||
|
if c != IAC:
|
||
|
buf = buf + c
|
||
|
if len(buf) >= 44:
|
||
|
## self.msg('transfer: %s' % `buf`)
|
||
|
self.cookedq = self.cookedq + buf
|
||
|
buf = ''
|
||
|
continue
|
||
|
c = self.rawq_getchar()
|
||
|
if c == IAC:
|
||
|
buf = buf + c
|
||
|
elif c in (DO, DONT):
|
||
|
opt = self.rawq_getchar()
|
||
|
self.msg('IAC %s %d',
|
||
|
c == DO and 'DO' or 'DONT',
|
||
|
ord(c))
|
||
|
self.sock.send(IAC + WONT + opt)
|
||
|
elif c in (WILL, WONT):
|
||
|
opt = self.rawq_getchar()
|
||
|
self.msg('IAC %s %d',
|
||
|
c == WILL and 'WILL' or 'WONT',
|
||
|
ord(c))
|
||
|
else:
|
||
|
self.msg('IAC %s not recognized' % `c`)
|
||
|
## self.msg('transfer: %s' % `buf`)
|
||
|
self.cookedq = self.cookedq + buf
|
||
|
|
||
|
# Get next char from raw queue, blocking if necessary
|
||
|
def rawq_getchar(self):
|
||
|
if not self.rawq:
|
||
|
self.fill_rawq()
|
||
|
if self.irawq >= len(self.rawq):
|
||
|
raise EOFError
|
||
|
c = self.rawq[self.irawq]
|
||
|
self.irawq = self.irawq + 1
|
||
|
if self.irawq >= len(self.rawq):
|
||
|
self.rawq = ''
|
||
|
self.irawq = 0
|
||
|
return c
|
||
|
|
||
|
# Fill raw queue
|
||
|
def fill_rawq(self):
|
||
|
if self.irawq >= len(self.rawq):
|
||
|
self.rawq = ''
|
||
|
self.irawq = 0
|
||
|
buf = self.sock.recv(50)
|
||
|
## self.msg('fill_rawq(): %s' % `buf`)
|
||
|
self.rawq = self.rawq + buf
|