Guido van Rossum | 74b3f8a | 1993-10-28 09:53:13 +0000 | [diff] [blame^] | 1 | # Telnet client library |
| 2 | |
| 3 | import socket |
| 4 | import select |
| 5 | import string |
| 6 | import regsub |
| 7 | |
| 8 | # Tunable parameters |
| 9 | TIMEOUT = 30.0 |
| 10 | DEBUGLEVEL = 1 |
| 11 | |
| 12 | # Telnet protocol defaults |
| 13 | TELNET_PORT = 23 |
| 14 | |
| 15 | # Telnet protocol characters (don't change) |
| 16 | IAC = chr(255) # "Interpret As Command" |
| 17 | DONT = chr(254) |
| 18 | DO = chr(253) |
| 19 | WONT = chr(252) |
| 20 | WILL = chr(251) |
| 21 | |
| 22 | |
| 23 | # Telnet interface class |
| 24 | |
| 25 | class Telnet: |
| 26 | |
| 27 | # Constructor |
| 28 | def __init__(self, host, port): |
| 29 | self.debuglevel = DEBUGLEVEL |
| 30 | self.host = host |
| 31 | if not port: port = TELNET_PORT |
| 32 | self.port = port |
| 33 | self.timeout = TIMEOUT |
| 34 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 35 | self.sock.connect((self.host, self.port)) |
| 36 | self.rawq = '' |
| 37 | self.irawq = 0 |
| 38 | self.cookedq = '' |
| 39 | |
| 40 | # Destructor |
| 41 | def __del__(self): |
| 42 | self.close() |
| 43 | |
| 44 | # Print debug message |
| 45 | def msg(self, msg, *args): |
| 46 | if self.debuglevel > 0: |
| 47 | print 'TELNET:', msg%args |
| 48 | |
| 49 | # Set debug level |
| 50 | def set_debuglevel(self, debuglevel): |
| 51 | self.debuglevel = debuglevel |
| 52 | |
| 53 | # Set time-out on certain reads |
| 54 | def set_timeout(self, timeout): |
| 55 | self.timeout = float(timeout) |
| 56 | |
| 57 | # Explicit close |
| 58 | def close(self): |
| 59 | if self.sock: |
| 60 | self.sock.close() |
| 61 | self.sock = None |
| 62 | |
| 63 | # Return socket (e.g. for select) |
| 64 | def get_socket(self): |
| 65 | return self.sock |
| 66 | |
| 67 | # Return socket's fileno (e.g. for select) |
| 68 | def fileno(self): |
| 69 | return self.sock.fileno() |
| 70 | |
| 71 | # Write a string to the socket, doubling any IAC characters |
| 72 | def write(self, buffer): |
| 73 | if IAC in buffer: |
| 74 | buffer = regsub.gsub(IAC, IAC+IAC, buffer) |
| 75 | self.sock.send(buffer) |
| 76 | |
| 77 | # Read until a given string is encountered or until timeout |
| 78 | def read_until(self, match): |
| 79 | ## self.msg('read_until(%s)' % `match`) |
| 80 | n = len(match) |
| 81 | self.process_rawq() |
| 82 | i = string.find(self.cookedq, match) |
| 83 | if i < 0: |
| 84 | i = max(0, len(self.cookedq)-n) |
| 85 | self.fill_cookedq() |
| 86 | i = string.find(self.cookedq, match, i) |
| 87 | if i >= 0: |
| 88 | i = i+n |
| 89 | buf = self.cookedq[:i] |
| 90 | self.cookedq = self.cookedq[i:] |
| 91 | ## self.msg('read_until(%s) -> %s' % (`match`, `buf`)) |
| 92 | return buf |
| 93 | while select.select([self], [], [], self.timeout) == \ |
| 94 | ([self], [], []): |
| 95 | i = max(0, len(self.cookedq)-n) |
| 96 | self.fill_rawq() |
| 97 | self.process_rawq() |
| 98 | i = string.find(self.cookedq, match, i) |
| 99 | if i >= 0: |
| 100 | i = i+n |
| 101 | buf = self.cookedq[:i] |
| 102 | self.cookedq = self.cookedq[i:] |
| 103 | ## self.msg('read_until(%s) -> %s' % |
| 104 | ## (`match`, `buf`)) |
| 105 | return buf |
| 106 | buf = self.cookedq |
| 107 | self.cookedq = '' |
| 108 | ## self.msg('read_until(%s) -> %s' % (`match`, `buf`)) |
| 109 | return buf |
| 110 | |
| 111 | # Read everything that's possible without really blocking |
| 112 | def read_now(self): |
| 113 | self.fill_cookedq() |
| 114 | buf = self.cookedq |
| 115 | self.cookedq = '' |
| 116 | ## self.msg('read_now() --> %s' % `buf`) |
| 117 | return buf |
| 118 | |
| 119 | # Fill cooked queue without blocking |
| 120 | def fill_cookedq(self): |
| 121 | self.process_rawq() |
| 122 | while select.select([self], [], [], 0) == ([self], [], []): |
| 123 | self.fill_rawq() |
| 124 | if not self.rawq: |
| 125 | raise EOFError |
| 126 | self.process_rawq() |
| 127 | |
| 128 | # Transfer from raw queue to cooked queue |
| 129 | def process_rawq(self): |
| 130 | # There is some silliness going on here in an attempt |
| 131 | # to avoid quadratic behavior with large inputs... |
| 132 | buf = '' |
| 133 | while self.rawq: |
| 134 | c = self.rawq_getchar() |
| 135 | if c != IAC: |
| 136 | buf = buf + c |
| 137 | if len(buf) >= 44: |
| 138 | ## self.msg('transfer: %s' % `buf`) |
| 139 | self.cookedq = self.cookedq + buf |
| 140 | buf = '' |
| 141 | continue |
| 142 | c = self.rawq_getchar() |
| 143 | if c == IAC: |
| 144 | buf = buf + c |
| 145 | elif c in (DO, DONT): |
| 146 | opt = self.rawq_getchar() |
| 147 | self.msg('IAC %s %d', |
| 148 | c == DO and 'DO' or 'DONT', |
| 149 | ord(c)) |
| 150 | self.sock.send(IAC + WONT + opt) |
| 151 | elif c in (WILL, WONT): |
| 152 | opt = self.rawq_getchar() |
| 153 | self.msg('IAC %s %d', |
| 154 | c == WILL and 'WILL' or 'WONT', |
| 155 | ord(c)) |
| 156 | else: |
| 157 | self.msg('IAC %s not recognized' % `c`) |
| 158 | ## self.msg('transfer: %s' % `buf`) |
| 159 | self.cookedq = self.cookedq + buf |
| 160 | |
| 161 | # Get next char from raw queue, blocking if necessary |
| 162 | def rawq_getchar(self): |
| 163 | if not self.rawq: |
| 164 | self.fill_rawq() |
| 165 | if self.irawq >= len(self.rawq): |
| 166 | raise EOFError |
| 167 | c = self.rawq[self.irawq] |
| 168 | self.irawq = self.irawq + 1 |
| 169 | if self.irawq >= len(self.rawq): |
| 170 | self.rawq = '' |
| 171 | self.irawq = 0 |
| 172 | return c |
| 173 | |
| 174 | # Fill raw queue |
| 175 | def fill_rawq(self): |
| 176 | if self.irawq >= len(self.rawq): |
| 177 | self.rawq = '' |
| 178 | self.irawq = 0 |
| 179 | buf = self.sock.recv(50) |
| 180 | ## self.msg('fill_rawq(): %s' % `buf`) |
| 181 | self.rawq = self.rawq + buf |