Skip to content
Snippets Groups Projects
Commit 4ea0c780 authored by Henrik (Grubba) Grubbström's avatar Henrik (Grubba) Grubbström
Browse files

The TELNET protocol.

Rev: lib/modules/Protocols.pmod/TELNET.pmod:1.1
parent aec58f67
No related branches found
No related tags found
No related merge requests found
......@@ -39,6 +39,7 @@ testfont binary
/lib/modules/LR.pmod/scanner.pike foreign_ident
/lib/modules/MIME.pmod foreign_ident
/lib/modules/PDB.pmod foreign_ident
/lib/modules/Protocols.pmod/TELNET.pmod foreign_ident
/lib/modules/SSL.pmod/alert.pike foreign_ident
/lib/modules/SSL.pmod/cipher.pike foreign_ident
/lib/modules/SSL.pmod/connection.pike foreign_ident
......
//
// $Id: TELNET.pmod,v 1.1 1998/04/04 14:23:18 grubba Exp $
//
// The TELNET protocol as described by RFC 764 and others.
//
// Henrik Grubbström <grubba@idonex.se> 1998-04-04
//
#ifdef TELNET_DEBUG
#define DWRITE(X) werror(X)
#else
#define DWRITE(X)
#endif /* TELNET_DEBUG */
//. o protocol
//. Implementation of the TELNET protocol.
class protocol
{
//. + fd
//. The connection.
static object fd;
//. + cb
//. Mapping containing extra callbacks.
static private mapping cb;
//. + id
//. Value to send to the callbacks.
static private mixed id;
//. + write_cb
//. Write callback.
static private function(mixed|void:string) write_cb;
//. + read_cb
//. Read callback.
static private function(mixed, string:void) read_cb;
//. + close_cb
//. Close callback.
static private function(mixed|void:void) close_cb;
//. + TelnetCodes
//. Mapping of telnet codes to their abreviations.
static private constant TelnetCodes = ([
236:"EOF", // End Of File
237:"SUSP", // Suspend Process
238:"ABORT", // Abort Process
239:"EOR", // End Of Record
// The following ones are specified in RFC 959:
240:"SE", // Subnegotiation End
241:"NOP", // No Operation
242:"DM", // Data Mark
243:"BRK", // Break
244:"IP", // Interrupt Process
245:"AO", // Abort Output
246:"AYT", // Are You There
247:"EC", // Erase Character
248:"EL", // Erase Line
249:"GA", // Go Ahead
250:"SB", // Subnegotiation
251:"WILL", // Desire to begin/confirmation of option
252:"WON'T", // Refusal to begin/continue option
253:"DO", // Request to begin/confirmation of option
254:"DON'T", // Demand/confirmation of stop of option
255:"IAC", // Interpret As Command
]);
//. + to_send
//. Data queued to be sent.
static private string to_send = "";
//. - send
//. Queues data to be sent to the other end of the connection.
//. > s - String to send.
static private void send(string s)
{
to_send += s;
}
//. - send_data
//. Callback called when it is clear to send data over the connection.
//. This function does the actual sending.
static private void send_data()
{
if (!sizeof(to_send)) {
to_send = write_cb(id);
}
if (!to_send) {
// Support for delayed close.
fd->close();
fd = 0;
} else if (sizeof(to_send)) {
int n = fd->write(to_send);
to_send = to_send[n..];
}
}
//. + default_cb
//. Mapping with the default handlers for TELNET commands.
static private mapping(string:function) default_cb = ([
"BRK":lambda() {
destruct();
throw(0);
},
"AYT":lambda() {
send("\377\361"); // NOP
},
"WILL":lambda(int code) {
send(sprintf("\377\376%c", code)); // DON'T xxx
},
"DO":lambda(int code) {
send(sprintf("\377\374%c", code)); // WON'T xxx
},
]);
//. + synch
//. Indicates wether we are in synch-mode or not.
static private int synch = 0;
//. - got_oob
//. Callback called when Out-Of-Band data has been received.
//. > ignored - The id from the connection.
//. > s - The Out-Of-Band data received.
static private void got_oob(mixed ignored, string s)
{
DWRITE(sprintf("TELNET: got_oob(\"%s\")\n", s));
synch = synch || (s == "\377");
if (cb["URG"]) {
cb["URG"](id, s);
}
}
//. + rest
//. Contains data left over from the line-buffering.
static private string rest = "";
//. - got_data
//. Callback called when normal data has been received.
//. This function also does most of the TELNET protocol parsing.
//. > ignored - The id from the connection.
//. > s - The received data.
static private void got_data(mixed ignored, string s)
{
DWRITE(sprintf("TELNET: got_data(\"%s\")\n", s));
if (sizeof(s) && (s[0] == 242)) {
DWRITE("TELNET: Data Mark\n");
// Data Mark handing.
s = s[1..];
sync = 0;
}
// A single read() can contain multiple or partial commands
// RFC 1123 4.1.2.10
array lines = s/"\r\n";
int lineno;
for(lineno = 0; lineno < sizeof(lines); lineno++) {
string line = lines[lineno];
if (search(line, "\377") != -1) {
array a = line / "\377";
string parsed_line = a[0];
int i;
for (i=1; i < sizeof(a); i++) {
string part = a[i];
if (sizeof(part)) {
string name = TelnetCodes[part[0]];
DWRITE(sprintf("TELNET: Code %s\n", name || "Unknown"));
int j;
function fun;
switch (name) {
case 0:
// FIXME: Should probably have a warning here.
break;
default:
if (fun = (cb[name] || default_cb[name])) {
mixed err = catch {
fun();
};
if (err) {
throw(err);
} else if (!zero_type(err)) {
// We were just destructed.
return;
}
}
a[i] = a[i][1..];
break;
case "EC": // Erase Character
for (j=i; j--;) {
if (sizeof(a[j])) {
a[j] = a[j][..sizeof(a[j])-2];
break;
}
}
a[i] = a[i][1..];
break;
case "EL": // Erase Line
for (j=0; j < i; j++) {
a[j] = "";
}
a[i] = a[i][1..];
break;
case "WILL":
case "WON'T":
case "DO":
case "DON'T":
if (fun = (cb[name] || default_cb[name])) {
fun(a[i][1]);
}
a[i] = a[i][2..];
break;
case "DM": // Data Mark
if (sync) {
for (j=0; j < i; j++) {
a[j] = "";
}
}
a[i] = a[i][1..];
sync = 0;
break;
}
} else {
a[i] = "\377";
i++;
}
}
line = a * "";
}
if (!lineno) {
line = rest + line;
}
if (lineno < (sizeof(lines)-1)) {
if ((!sync) && read_cb) {
DWRITE(sprintf("TELNET: Calling read_callback(X, \"%s\")\n",
line));
read_cb(id, line);
}
} else {
DWRITE(sprintf("TELNET: Partial line is \"%s\"\n", line));
rest = line;
}
}
}
//. - set_write_callback
//. Sets the callback to be called when it is clear to send.
//. > w_cb - The new read callback.
void set_write_callback(function(mixed|void:string) w_cb)
{
write_cb = w_cb;
fd->set_nonblocking(got_data, w_cb && send_data, close_cb, got_oob);
}
//. - create
//. Creates a TELNET protocol handler, and sets its callbacks.
//. > f - File to use for the connection.
//. > r_cb - Function to call when data has arrived.
//. > w_cb - Function to call when data can be sent.
//. > c_cb - Function to call when the connection is closed.
//. > callbacks - Mapping with callbacks for the various TELNET commands.
//. > new_id - Value to send to the various callbacks.
void create(object f,
function(mixed,string:void) r_cb,
function(mixed|void:string) w_cb,
function(mixed|void:void) c_cb,
mapping callbacks, mixed|void new_id)
{
fd = f;
cb = callbacks;
read_cb = r_cb;
write_cb = w_cb;
close_cb = c_cb;
id = new_id;
fd->set_nonblocking(got_data, w_cb && send_data, close_cb, got_oob);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment