pike.git / lib / modules / Protocols.pmod / TELNET.pmod

version» Context lines:

pike.git/lib/modules/Protocols.pmod/TELNET.pmod:2:   // The TELNET protocol as described by RFC 764 and others.   //   // Henrik Grubbström <grubba@roxen.com> 1998-04-04   //      #pike __REAL_VERSION__      // #define TELNET_DEBUG      #ifdef TELNET_DEBUG - #define DWRITE(X) werror(X) + #define DWRITE(X ...) werror("TELNET: " X)   #else - #define DWRITE(X) + #define DWRITE(X ...)   #endif /* TELNET_DEBUG */    - //! Implements TELNET as described by RFC 764 and RFC 854 + //! Implements TELNET as described by @rfc{764@} and @rfc{854@}.   //!   //! Also implements the Q method of TELNET option negotiation - //! as specified by RFC 1143. + //! as specified by @rfc{1143@}.      /* Extract from RFC 1143:    *    * EXAMPLE STATE MACHINE FOR THE Q METHOD OF IMPLEMENTING    * TELNET OPTION NEGOTIATION    *    * There are two sides, we (us) and he (him). We keep four variables:    *    * us: state of option on our side (NO/WANTNO/WANTYES/YES)    * usq: a queue bit (EMPTY/OPPOSITE) if us is WANTNO or WANTYES
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:324:       // See RFC 1143 for the use and meaning of these.       protected constant UNKNOWN = 0;    protected constant YES = 1;    protected constant NO = 2;    protected constant WANT = 4;    protected constant OPPOSITE = 8;       //! Negotiation states of all WILL/WON'T options. -  //! See RFC 1143 for a description of the states. +  //! See @rfc{1143@} for a description of the states.    protected array(int) remote_options = allocate(256,NO);    protected array(int) local_options = allocate(256,NO);          //! Data queued to be sent.    protected string to_send = "";       //! Indicates that connection should be closed    protected int done;       //! Tells if we have set the nonblocking write callback or not.    protected int nonblocking_write;       //! Turns on the write callback if apropriate.    protected void enable_write()    { -  DWRITE("TELNET: enable_write()\n"); +  DWRITE("enable_write()\n");    if (!nonblocking_write && (write_cb || sizeof(to_send) || done)) { -  DWRITE("TELNET: enable_write(): Enabling non-blocking() in enable_write()\n"); +  DWRITE("Enabling non-blocking() in enable_write()\n");    fd->set_nonblocking(got_data, send_data, close_cb, got_oob);    nonblocking_write = 1;    } else { -  DWRITE("TELNET: enable_write(): Calling send_data()\n"); +  DWRITE("Calling send_data()\n");    send_data();    }    }       //! Turns off the write callback if apropriate.    protected void disable_write()    { -  DWRITE("TELNET: disable_write()\n"); +  DWRITE("disable_write()\n");    if (!write_cb && !sizeof(to_send) && !done && nonblocking_write) { -  DWRITE("TELNET: disable_write(): Calling fd->set_nonblocking()\n"); +  DWRITE("Calling fd->set_nonblocking()\n");    fd->set_nonblocking(got_data, 0, close_cb, got_oob);    nonblocking_write = 0;    }    }       //! Queues data to be sent to the other end of the connection.    //!    //! @param s    //! String to send.    void write(string s)    { -  DWRITE(sprintf("TELNET: writing :%O\n",s)); +  DWRITE("Writing :%O\n", s);    to_send += replace(s, C(IAC), C2(IAC,IAC));    enable_write();    }       //! Queues raw data to be sent to the other end of the connection.    //!    //! @param s    //! String with raw telnet data to send.    void write_raw(string s)    {
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:395:    void close()    {    done=1;    enable_write();    }       //! Callback called when it is clear to send data over the connection.    //! This function does the actual sending.    protected void send_data()    { -  DWRITE("TELNET: Entering send_data()\n"); +  DWRITE("Entering send_data()\n");    if (!sizeof(to_send)) { -  DWRITE("TELNET: Nothing to send!\n"); +  DWRITE("Nothing to send!\n");    if (write_cb) { -  DWRITE("TELNET: We have a write callback!\n"); +  DWRITE("We have a write callback!\n");    // FIXME: What if the callback calls something that calls    // write() or write_raw() above?    if(!(to_send = write_cb(id)))    { -  DWRITE("TELNET: Write callback did not write anything!\n"); +  DWRITE("Write callback did not write anything!\n");    done=1;    to_send="";    }    }    }       if (sizeof(to_send))    { -  DWRITE(sprintf("TELNET: We now have data to send! (%d bytes)\n", -  sizeof(to_send))); +  DWRITE("We now have data to send! (%d bytes)\n", sizeof(to_send));    if (to_send[0] == 242) {    // DataMark needs extra quoting... Stupid. -  DWRITE("TELNET: Found datamark @ offset 0!\n"); +  DWRITE("Found datamark @ offset 0!\n");    to_send = C2(IAC,NOP) + to_send;    }       int n = fd->write(to_send);       to_send = to_send[n..];    } else if(done) { -  DWRITE("TELNET: Closing fd!\n"); +  DWRITE("Closing fd!\n");    fd->close();    fd=0;    nonblocking_write=0;    return;    }    disable_write();    }       //! Sends a TELNET synch command.    void send_synch()
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:470:    //!    //! @param option    //! The option to disable.      #define CONTROL(OPTIONS,DO,DONT,WILL,WONT,YES,NO) \    void send_##DO(int option) \    { \    if ((option < 0) || (option > 255)) { \    error( "Bad TELNET option #%d\n", option); \    } \ -  DWRITE(sprintf("TELNET: send_" #DO "(%s) state is %d\n", \ -  lookup_telopt[option] || (string)option, \ -  OPTIONS##_options[option])); \ +  DWRITE("send_" #DO "(%s) state is %d\n", \ +  lookup_telopt[option] || (string)option, \ +  OPTIONS##_options[option]); \    switch(OPTIONS##_options[option]) { \    case NO: \    case UNKNOWN: \    OPTIONS##_options[option]= WANT | YES; \ -  DWRITE(sprintf("TELNET: => " #DO " %s\n", \ -  lookup_telopt[option] || (string)option)); \ -  to_send += sprintf("%c%c%c",IAC,DO,option); \ +  DWRITE("=> " #DO " %s\n", \ +  lookup_telopt[option] || (string)option); \ +  to_send += C3(IAC,DO,option); \    break; \    \    case YES: /* Already enabled */ \    case WANT | YES: /* Will be enabled soon */ \    case WANT | NO | OPPOSITE: /* Already queued an enable request. */ \    break; \    \    case WANT | NO: \    case WANT | YES | OPPOSITE: \    OPTIONS##_options[option]^=OPPOSITE; \    break; \    \    default: \ -  error("TELNET: Strange remote_options[%d]=%d\n", \ +  error("Strange remote_options[%d]=%d\n", \    option, remote_options[option]); \    /* ERROR: weird state! */ \    break; \    } \    enable_write(); \    }       //! @ignore    CONTROL(remote,DO,DONT,WILL,WONT,YES,NO)   
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:559:    {    return call_callback(DO,opt);    }       void send_SB(int|string ... args)    {    to_send+=    C2(IAC,SB)+    replace(Array.map(args,lambda(int|string s)    { -  return sprintf(intp(s)?"%c":"%s",s); +  return intp(s) ? sprintf("%c", s) : s;    })*"",C(IAC),C2(IAC,IAC))+    C2(IAC,SE);    enable_write();    }          //! Indicates whether we are in synch-mode or not.    protected int synch = 0;       protected mixed call_callback(mixed what, mixed ... args)
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:609:    //!    //! @param ignored    //! The id from the connection.    //!    //! @param s    //! The Out-Of-Band data received.    //!    protected void got_oob(mixed ignored, string s)    {   #ifdef TELNET_DEBUG -  werror("TELNET: got_oob(\"%s\")\n",Array.map(values(s),lambda(int s) { +  werror("TELNET: got_oob(\"%s\")\n", map(values(s),lambda(int s) {    switch(s)    {    case ' '..'z': -  return sprintf("%c",s); +  return C(s);       default:    return sprintf("\\0x%02x",s);    }    })*"");   #endif    synch = synch || (s == C(IAC));    call_callback("URG",id,s);    }       //! Calls @[read_cb()].    //!    //! Specifically provided for overloading    //!    protected void call_read_cb(string data)    { -  DWRITE("TELNET: Fnurgel!\n"); +  DWRITE("call_read_cb()\n");    if(read_cb && sizeof(data)) read_cb(id,data);    }       //! Callback called when normal data has been received.    //! This function also does most of the TELNET protocol parsing.    //!    //! @param ignored    //! The id from the connection.    //! @param s    //! The received data.    //!    protected void got_data(mixed ignored, string line)    { - #ifdef TELNET_DEBUG -  werror("TELNET: got_data(%O)\n",line); - #endif +  DWRITE("got_data(%O)\n", line);       if (sizeof(line) && (line[0] == DM)) { -  DWRITE("TELNET: Data Mark\n"); +  DWRITE("Data Mark\n");    // Data Mark handing.    line = line[1..];    synch = 0;    }       if (has_value(line, C(IAC))) {    array a = line / C(IAC);       string parsed_line = a[0];    int i;    for (i=1; i < sizeof(a); i++) {    string part = a[i];    if (sizeof(part)) {    -  DWRITE(sprintf("TELNET: Code %s\n", -  lookup_telnetcodes[part[0]] || (string)part[0])); +  DWRITE("Code %s\n", +  lookup_telnetcodes[part[0]] || (string)part[0]);       switch (part[0]) {    default:    call_callback(part[0]);    a[i] = a[i][1..];    break;       // FIXME, find true end of subnegotiation!    case SB:    call_callback(SB,part[1..]);
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:705:    break;   #endif      #define HANDLE(OPTIONS,WILL,WONT,DO,DONT) \    case WILL: \    { \    int option = a[i][1]; \    int state = OPTIONS##_options[option]; \    a[i] = a[i][2..]; \    \ -  DWRITE(sprintf(#WILL " %s, state 0x%04x\n", \ -  lookup_telopt[option], state)); \ +  DWRITE(#WILL " %s, state 0x%04x\n", \ +  lookup_telopt[option], state); \    \    switch(state) { \    case NO: \    case UNKNOWN: \    if (WILL##_callback(option)) \    { \    /* Agree about enabling */ \    state=YES; \    send_##DO(option); \    } else { \
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:741:    case WANT | NO | OPPOSITE: \    state=YES; \    break; \    \    case WANT | YES | OPPOSITE: \    state=WANT | NO; \    send_##DONT(option); \    break; \    \    default: \ -  error("TELNET: Strange remote_options[%d]=%d\n", \ +  error("Strange remote_options[%d]=%d\n", \    option,remote_options[option]); \    /* Weird state ! */ \    } \ -  DWRITE(sprintf("TELNET: => " #WILL " %s, state 0x%04x\n", \ -  lookup_telopt[option], state)); \ +  DWRITE("=> " #WILL " %s, state 0x%04x\n", \ +  lookup_telopt[option], state); \    set_##OPTIONS##_option(option,state); \    break; \    } \    \    case WONT: \    { \    int option = a[i][1]; \    int state = OPTIONS##_options[option]; \    a[i] = a[i][2..]; \    \ -  DWRITE(sprintf(#WONT " %s, state 0x%04x\n", \ -  lookup_telopt[option], state)); \ +  DWRITE(#WONT " %s, state 0x%04x\n", \ +  lookup_telopt[option], state); \    \    switch(state) \    { \    case UNKNOWN: \    case NO: \    state=NO; \    break; \    \    case YES: \    state=NO; \
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:787:    state=WANT | YES; \    send_##DO(option); \    break; \    \    case WANT | YES: \    case WANT | YES | OPPOSITE: \    state=NO; \    break; \    \    default: \ -  error("TELNET: Strange remote_options[%d]=%d\n", \ +  error("Strange remote_options[%d]=%d\n", \    option, remote_options[option]); \    /* Weird state */ \    } \    \ -  DWRITE(sprintf("TELNET: => " #WONT " %s, state 0x%04x\n", \ -  lookup_telopt[option], state)); \ +  DWRITE("=> " #WONT " %s, state 0x%04x\n", \ +  lookup_telopt[option], state); \    set_##OPTIONS##_option(option,state); \    } \    break       HANDLE(remote,WILL,WONT,DO,DONT);    HANDLE(local,DO,DONT,WILL,WONT);       case DM: // Data Mark    if (synch) {    for (int j=0; j < i; j++) {
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:817:    a[i] = a[i][1..];    synch = 0;    break;    }    } else {    // IAC IAC => IAC    a[i] = C(IAC);    i++;    }    } - // werror("%O\n",a); +     line = a * "";    }       if ((!synch)) { - #ifdef TELNET_DEBUG -  werror("TELNET: calling read_callback(X,%O)\n", line); - #endif +  DWRITE("Calling read_callback(X,%O)\n", line);    call_read_cb(line);    }    enable_write();    }       //!    void set_read_callback(function(mixed,string:void) r_cb)    {    read_cb = r_cb;    fd->set_read_callback(got_data);
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:850:    {    return read_cb;    }       //! Sets the callback to be called when it is clear to send.    //!    //! @param w_cb    //! The new write callback.    void set_write_callback(function(mixed|void:string) w_cb)    { -  DWRITE(sprintf("TELNET: set_write_callback(%O)\n", w_cb)); +  DWRITE("set_write_callback(%O)\n", w_cb);    write_cb = w_cb;    if (w_cb) {    enable_write();    } else {    disable_write();    }    }       //!    function(mixed|void:string) query_write_callback()
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:938:    //!    //! @seealso    //! @[create()]    void set_nonblocking(function(mixed,string:void) r_cb,    function(mixed|void:string) w_cb,    function(mixed|void:void) c_cb)    {    read_cb = r_cb;    write_cb = w_cb;    close_cb = c_cb; -  DWRITE(sprintf("TELNET: set_nonblocking(): " +  DWRITE("set_nonblocking(): "    "Calling fd->set_nonblocking() %O %O\n", -  w_cb, w_cb || send_data)); +  w_cb, w_cb || send_data);    fd->set_nonblocking(got_data, w_cb && send_data, close_cb, got_oob);    nonblocking_write = !!w_cb;    }       void set_blocking()    {    read_cb = 0;    write_cb = 0;    close_cb = 0;    fd->set_blocking();
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:972:   {    //! Based on the generic TELNET protocol handler.    inherit protocol;       protected string line_buffer="";       protected void call_read_cb(string data)    {    if(read_cb)    { -  DWRITE(sprintf("TELNET: Line callback... %O\n",data)); +  DWRITE("Line callback... %O\n", data);    data=replace(data,    ({"\r\n", "\n", "\r", "\r\0"}),    ({"\r", "\r", "\r", "\r",}));    line_buffer+=data;    array(string) tmp=line_buffer/"\r";    line_buffer=tmp[-1];    for(int e=0;e<sizeof(tmp)-1;e++) read_cb(id,tmp[e]+"\n");    }    }   
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:1056:    }       protected void call_read_cb(string data)    {    if(read_cb)    {    if(!icanon)    {    if(sizeof(data)) read_cb(id,data);    }else{ -  DWRITE(sprintf("TELNET: Line callback... %O\n",data)); +  DWRITE("Line callback... %O\n", data);    data=replace(data,    ({"\r\n","\r\n","\r","\r\0"}),    ({"\r", "\r", "\r","\r",}));    line_buffer+=data;    array(string) tmp=line_buffer/"\r";    line_buffer=tmp[-1];    for(int e=0;e<sizeof(tmp)-1;e++)    {    read_cb(id,tmp[e]+"\n");    write(prompt);
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:1096:    //! @[Stdio.File()->tcsetattr()]    int tcsetattr(mapping(string:int) options, string|void when)    {    ( options->ECHO ? send_WONT : send_WILL )(TELOPT_ECHO);    ( (icanon=options->ICANON) ? send_DONT : send_DO )(TELOPT_LINEMODE);    }       //! Turn on/off echo mode.    void set_secret(int onoff)    { -  DWRITE(sprintf("TELNET: set_secret(%d)\n", onoff)); +  DWRITE("set_secret(%d)\n", onoff);    if(readline)    { -  DWRITE("TELNET: setting secret via Stdio.Readline\n"); +  DWRITE("setting secret via Stdio.Readline\n");    readline->set_echo(!onoff);    }else{ -  DWRITE("TELNET: setting secret via telnet option\n"); +  DWRITE("setting secret via telnet option\n");    ( onoff ? send_WILL : send_WONT )(TELOPT_ECHO);    } -  DWRITE(sprintf("TELNET: LOCAL TELNET ECHO STATE IS %O\n", local_options[TELOPT_ECHO])); -  DWRITE(sprintf("TELNET: REMOTE TELNET ECHO STATE IS %O\n", remote_options[TELOPT_ECHO])); +  DWRITE("LOCAL TELNET ECHO STATE IS %O\n", local_options[TELOPT_ECHO]); +  DWRITE("REMOTE TELNET ECHO STATE IS %O\n", remote_options[TELOPT_ECHO]);       }       // NB: Ought to have an id2 as well, but since Stdio.Readline    // doesn't use the id argument, there's no need.    //    // NB: The same argument could be made regarding write_cb2,    // but there there's a potential that Stdio.Readline    // might start using the write callback in the future.    //
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:1150:    close_cb2(id);    }       protected string prompt="";    protected mixed call_callback(mixed what, mixed ... args)    {    switch(what)    {    case SB:    string data=args[0]; -  DWRITE(sprintf("TELNET: SB callback %O\n",data)); +  DWRITE("SB callback %O\n", data);    switch(data[0])    {    case TELOPT_TTYPE:    if(data[1]==TELQUAL_IS)    {    if(!readline)    {    term=data[2..]; -  DWRITE(sprintf("TELNET.Readline: Enabling READLINE, term=%s\n", -  term)); +  DWRITE("TELNET.Readline: Enabling READLINE, term=%s\n", term);    // This fix for the secret mode might not    // be the best way to do things, but it seems to    // work.    int secret_mode = (local_options[TELOPT_ECHO] == YES);    set_secret(0); -  readline=Stdio.Readline(this_program::this, lower_case(term)); +  readline=Stdio.Readline(this::this, lower_case(term));    set_secret(secret_mode);    readline->get_input_controller()->set_close_callback(readline_close_callback);    DWRITE("TELNET.Readline: calling readline->set_nonblocking()\n");    readline->set_nonblocking(readline_callback); -  DWRITE(sprintf("TELNET: Setting the readline prompt to %O\n", prompt)); +  DWRITE("Setting the readline prompt to %O\n", prompt);    readline->set_prompt(prompt); -  DWRITE(sprintf("TELNET: LOCAL TELNET ECHO STATE IS %O\n", local_options[TELOPT_ECHO])); -  DWRITE(sprintf("TELNET: REMOTE TELNET ECHO STATE IS %O\n", remote_options[TELOPT_ECHO])); +  DWRITE("LOCAL TELNET ECHO STATE IS %O\n", local_options[TELOPT_ECHO]); +  DWRITE("REMOTE TELNET ECHO STATE IS %O\n", remote_options[TELOPT_ECHO]);       readline->enable_history(200);    /* enable data processing */    }    }    break;       case TELOPT_NAWS:    if(sscanf(data[1..],"%2c%2c",width,height)==2)    if(readline)
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:1197:    }    }    }       void remote_option_callback(int opt, int onoff)    {    switch(opt)    {    case TELOPT_TTYPE:    if(onoff) -  { +     send_SB(TELOPT_TTYPE,TELQUAL_SEND); -  }else{ +  else +  {    werror("Revert to line mode not yet operational.\n");    /* revert to stupid line mode */    }    }    ::remote_option_callback(opt,onoff);    }       protected void setup()    {    send_DO(TELOPT_SGA);
pike.git/lib/modules/Protocols.pmod/TELNET.pmod:1232:    }else{    write(replace(s,"\n","\r\n"));    }    }       //! Set the readline prompt.    void set_prompt(string s)    {    if(readline)    { -  DWRITE(sprintf("TELNET: Setting readline prompt to %O\n", s)); +  DWRITE("Setting readline prompt to %O\n", s);    prompt=s;    readline->set_prompt(prompt);    }else{    if(prompt!=s)    { -  DWRITE(sprintf("TELNET: Setting prompt without readline to %O\n", s)); +  DWRITE("Setting prompt without readline to %O\n", s);       // What is the point of this if-statement?    // I think that write(s) should be called every time!    // Agehall 2004-04-25    //    // No idea; it looks like it checks if the old prompt    // is a prefix of the new prompt. It also probably ought    // to use message() rather than write().    // Grubba 2008-12-04    if(s[..sizeof(prompt)-1]==prompt)