# 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