diff --git a/lib/modules/Protocols.pmod/TELNET.pmod b/lib/modules/Protocols.pmod/TELNET.pmod index ff97cbcc15b331190203a170a2ebf7f522b68fa6..54f6b9908b9c5baa268a1f0bbb9d72f21de67f17 100644 --- a/lib/modules/Protocols.pmod/TELNET.pmod +++ b/lib/modules/Protocols.pmod/TELNET.pmod @@ -1,5 +1,5 @@ // -// $Id: TELNET.pmod,v 1.2 1998/04/05 19:05:44 grubba Exp $ +// $Id: TELNET.pmod,v 1.3 1998/04/23 21:24:50 grubba Exp $ // // The TELNET protocol as described by RFC 764 and others. // @@ -12,6 +12,13 @@ #define DWRITE(X) #endif /* TELNET_DEBUG */ +//. Implements TELNET as described by RFC 764 and RFC 854 +//. +//. Also implements the Q method of TELNET option negotiation +//. as specified by RFC 1143. + + + //. o protocol //. Implementation of the TELNET protocol. class protocol @@ -67,6 +74,19 @@ class protocol 255:"IAC", // Interpret As Command ]); + //. + option_states + //. Negotiation state of all WILL/WON'T options. + static private array(int) option_states = allocate(256); + + // See RFC 1143 for the use and meaning of these. + constant option_us_yes = 0x0001; + constant option_us_want = 0x0002; + constant option_us_opposite = 0x0010; + + constant option_him_yes = 0x0100; + constant option_him_want = 0x0200; + constant option_him_opposite = 0x1000; + //. + to_send //. Data queued to be sent. static private string to_send = ""; @@ -146,6 +166,76 @@ class protocol } } + //. - enable_option + //. Starts negotiation to enable a TELNET option. + //. > option - The option to enable. + void enable_option(int option) + { + if ((option < 0) || (option > 255)) { + throw(({ sprintf("Bad TELNET option #%d\n", option), backtrace() })); + } + switch(option_states[option] & 0xff00) { + case 0x0000: /* NO */ + case 0x1000: /* NO OPPOSITE */ + option_states[option] &= ~option_him_opposite; + option_states[option] |= option_him_want | option_him_yes; + to_send += sprintf("\377\375%c", option); // DO option + break; + case 0x0100: /* YES */ + case 0x1100: /* YES OPPOSITE */ + /* Error: Already enabled */ + break; + case 0x0200: /* WANTNO EMPTY */ + /* Error: Cannot initiate new request in the middle of negotiation. */ + /* FIXME: **********************/ + break; + case 0x1200: /* WANTNO OPPOSITE */ + /* Error: Already queued an enable request */ + break; + case 0x1300: /* WANTYES OPPOSITE */ + /* Error: Already negotiating for enable */ + break; + case 0x0300: /* WANTYES EMPTY */ + option_states[option] &= ~option_him_opposite; + break; + } + } + + //. - disable_option + //. Starts negotiation to disable a TELNET option. + //. > option - The option to disable. + void disable_option(int option) + { + if ((option < 0) || (option > 255)) { + throw(({ sprintf("Bad TELNET option #%d\n", option), backtrace() })); + } + switch(option_states[option] & 0xff00) { + case 0x0000: /* NO */ + case 0x1000: /* NO OPPOSITE */ + /* Error: Already disabled */ + break; + case 0x0100: /* YES */ + case 0x1100: /* YES OPPOSITE */ + option_states[option] &= ~(option_him_opposite | option_him_yes); + option_states[option] |= option_him_want; + to_send += sprintf("\377\376%c", option); // DON'T option + break; + case 0x0200: /* WANTNO EMPTY */ + /* Error: Already negotiating for disable */ + break; + case 0x1200: /* WANTNO OPPOSITE */ + option_states[option] &= ~option_him_opposite; + break; + case 0x1300: /* WANTYES OPPOSITE */ + /* Error: Cannot initiate new request in the middle of negotiation. */ + /* FIXME: **********************/ + break; + case 0x0300: /* WANTYES EMPTY */ + /* Error: Already queued an disable request */ + break; + } + } + //. + synch //. Indicates wether we are in synch-mode or not. static private int synch = 0; @@ -180,7 +270,7 @@ class protocol DWRITE("TELNET: Data Mark\n"); // Data Mark handing. s = s[1..]; - sync = 0; + synch = 0; } // A single read() can contain multiple or partial commands @@ -239,22 +329,188 @@ class protocol a[i] = a[i][1..]; break; case "WILL": + int option = a[i][1]; + int state = option_states[option]; + a[i] = a[i][2..]; + + DWRITE(sprintf("WILL %d, state 0x%04x\n", option, state)); + + switch(state & 0xff00) { + case 0x0000: /* NO */ + case 0x1000: /* NO OPPOSITE */ + if ((fun = (cb["WILL"] || default_cb["WILL"])) && + fun(option)) { + /* Agree about enabling */ + option_states[option] |= option_him_yes; + to_send += sprintf("\377\375%c", option); // DO option + } else { + to_send += sprintf("\377\376%c", option); // DON'T option + } + break; + case 0x0100: /* YES */ + case 0x1100: /* YES OPPOSITE */ + /* Ignore */ + break; + case 0x0200: /* WANTNO EMPTY */ + state &= ~option_him_want; + if ((fun = (cb["Enable_Option"] || + default_cb["Enable_Option"]))) { + fun(option); + } + break; + case 0x1200: /* WANTNO OPPOSITE */ + state &= ~(option_him_yes|option_him_opposite); + to_send += sprintf("\377\376%c", option); // DON'T option + break; + case 0x0300: /* WANTYES EMPTY */ + case 0x1300: /* WANTYES OPPOSITE */ + // Error: DON'T answered by WILL + option_states[option] &= ~0x1200; + option_states[option] |= option_him_yes; + if ((fun = (cb["Enable_Option"] || + default_cb["Enable_Option"]))) { + fun(option); + } + break; + } + + state = option_states[option]; + DWRITE(sprintf("=> WILL %d, state 0x%04x\n", option, state)); + + break; case "WON'T": + int option = a[i][1]; + int state = option_states[option]; + a[i] = a[i][2..]; + + DWRITE(sprintf("WON'T %d, state 0x%04x\n", option, state)); + + switch(state & 0xff00) { + case 0x0000: /* NO */ + case 0x1000: /* NO OPPOSITE */ + /* Ignore */ + break; + case 0x0100: /* YES */ + case 0x1100: /* YES OPPOSITE */ + option_states[option] &= ~0xff00; + to_send += sprintf("\377\376%c", option); // DON'T option + if ((fun = (cb["Disable_Option"] || + default_cb["Disable_Option"]))) { + fun(option); + } + break; + case 0x0200: /* WANTNO EMPTY */ + case 0x0300: /* WANTYES EMPTY */ + case 0x1300: /* WANTYES OPPOSITE */ + option_states[option] &= ~0xff00; + break; + case 0x1200: /* WANTNO OPPOSITE */ + option_states[option] &= ~option_him_opposite; + option_states[option] |= option_him_yes; + to_send += sprintf("\377\375%c", option); // DO option + break; + } + + state = option_states[option]; + DWRITE(sprintf("=> WON'T %d, state 0x%04x\n", option, state)); + + break; case "DO": - case "DON'T": - if (fun = (cb[name] || default_cb[name])) { - fun(a[i][1]); + int option = a[i][1]; + int state = option_states[option]; + a[i] = a[i][2..]; + + DWRITE(sprintf("DO %d, state 0x%04x\n", option, state)); + + switch(state & 0xff) { + case 0x00: /* NO */ + case 0x10: /* NO OPPOSITE */ + if ((fun = (cb["DO"] || default_cb["DO"])) && + fun(option)) { + /* Agree about enabling */ + DWRITE("AGREE\n"); + option_states[option] |= option_us_yes; + to_send += sprintf("\377\373%c", option); // WILL option + } else { + DWRITE("DISAGREE\n"); + to_send += sprintf("\377\374%c", option); // WON'T option + } + break; + case 0x01: /* YES */ + case 0x11: /* YES OPPOSITE */ + /* Ignore */ + break; + case 0x02: /* WANTNO EMPTY */ + state &= ~option_us_want; + if ((fun = (cb["Enable_Option"] || + default_cb["Enable_Option"]))) { + fun(option); + } + break; + case 0x12: /* WANTNO OPPOSITE */ + state &= ~(option_us_yes|option_us_opposite); + to_send += sprintf("\377\374%c", option); // WON'T option + break; + case 0x03: /* WANTYES EMPTY */ + case 0x13: /* WANTYES OPPOSITE */ + option_states[option] &= ~0x12; + option_states[option] |= option_us_yes; + if ((fun = (cb["Enable_Option"] || + default_cb["Enable_Option"]))) { + fun(option); + } + break; } + + state = option_states[option]; + DWRITE(sprintf("=> DO %d, state 0x%04x\n", option, state)); + + break; + case "DON'T": + int option = a[i][1]; + int state = option_states[option]; a[i] = a[i][2..]; + + DWRITE(sprintf("DON'T %d, state 0x%04x\n", option, state)); + + switch(state & 0xff) { + case 0x00: /* NO */ + case 0x10: /* NO OPPOSITE */ + /* Ignore */ + break; + case 0x01: /* YES */ + case 0x11: /* YES OPPOSITE */ + option_states[option] &= ~0xff; + to_send += sprintf("\377\374%c", option); // WON'T option + if ((fun = (cb["Disable_Option"] || + default_cb["Disable_Option"]))) { + fun(option); + } + break; + case 0x02: /* WANTNO EMPTY */ + case 0x03: /* WANTYES EMPTY */ + case 0x13: /* WANTYES OPPOSITE */ + option_states[option] &= ~0xff; + break; + case 0x12: /* WANTNO OPPOSITE */ + option_states[option] &= ~option_us_opposite; + option_states[option] |= option_us_yes; + to_send += sprintf("\377\373%c", option); // WILL option + break; + } + + state = option_states[option]; + DWRITE(sprintf("=> DON'T %d, state 0x%04x\n", option, state)); + break; case "DM": // Data Mark - if (sync) { + if (synch) { for (j=0; j < i; j++) { a[j] = ""; } } a[i] = a[i][1..]; - sync = 0; + synch = 0; break; } } else { @@ -268,7 +524,7 @@ class protocol line = rest + line; } if (lineno < (sizeof(lines)-1)) { - if ((!sync) && read_cb) { + if ((!synch) && read_cb) { DWRITE(sprintf("TELNET: Calling read_callback(X, \"%s\")\n", line)); read_cb(id, line);