Telnet.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 $Header$
00004 
00005 */
00006 
00007 
00008 #define TELOPTS
00009 #define TELCMDS
00010 #include <arpa/telnet.h>
00011 #include <strings.h>
00012 
00013 #include "Error.h"
00014 
00015 #include "Telnet.h"
00016 
00017 // Public methods.
00018 
00019 Telnet::Telnet(Connection *conn, GetLine *gl) : Object()
00020 {
00021   if (!conn || !gl) {
00022     failure(ERROR(MSG_NULL_POINTER));
00023     return;
00024   }
00025   _connection = conn;
00026   _gl = gl;
00027 }
00028 
00029 Telnet::~Telnet()
00030 {
00031 }
00032 
00033 int Telnet::iac(unsigned char command, unsigned char option1,
00034                 unsigned char option2, unsigned char option3,
00035                 unsigned char option4, unsigned char option5)
00036 {
00037   int res;
00038 
00039   if (FAILED(res = iac(_connection, command, option1, option2, option3, option4, option5)))
00040     return ERROR_BACKTRACE(res);
00041   return OK;
00042 }
00043 
00044 int Telnet::iac(Connection *conn, unsigned char command, unsigned char option1,
00045                 unsigned char option2, unsigned char option3,
00046                 unsigned char option4, unsigned char option5)
00047 {
00048   char b[7];
00049   int blen = 3;
00050   int actuallyWritten;
00051   int res;
00052 
00053   b[0] = IAC;
00054   b[1] = command;
00055   b[2] = option1;
00056   if (option2) {
00057     b[3] = option2;
00058     ++blen;
00059   }
00060   if (option3) {
00061     b[4] = option3;
00062     ++blen;
00063   }
00064   if (option4) {
00065     b[5] = option4;
00066     ++blen;
00067   }
00068   if (option5) {
00069     b[6] = option5;
00070     ++blen;
00071   }
00072 
00073   if (FAILED(res = conn->write(b, blen, actuallyWritten)))
00074     return ERROR_BACKTRACE(res);
00075   if (actuallyWritten != blen)
00076     return ERROR(MSG_SOCKET_CANNOT_WRITE, strerror(errno));
00077 
00078   return OK;
00079 }
00080 
00081 // The function invoked when IAC is accepted. The function executes all
00082 // telnet specific commands/options parsing and negotiation.
00083 int Telnet::parse(int *readCnt)
00084 {
00085   uint8_t cmd, opt, act;
00086   int intCmd;
00087   int res;
00088 
00089   *readCnt = 0;
00090   _isTelnet = true;
00091   if (FAILED(res = _connection->read((char *)(&cmd), 1, 1)))
00092     return ERROR_BACKTRACE(res);
00093   if (FAILED(res = _connection->read((char *)(&opt), 1, 1)))
00094     return ERROR_BACKTRACE(res);
00095   *readCnt += 2;
00096   intCmd = cmd;
00097 
00098   // Command.
00099   if (!TELCMD_OK(intCmd))
00100     return ERROR(ERROR_TELNET_INVALID_COMMAND, cmd & 0xff, cmd & 0xff);
00101   if (!TELOPT_OK(opt))
00102     return ERROR(ERROR_TELNET_INVALID_OPTION, opt & 0xff, opt & 0xff);
00103 
00104   DEBUG("Telnet CMD: '%s' '%s'.", TELCMD(cmd), TELOPT(opt));
00105   switch (cmd) {
00106     case DONT:
00107     case DO:
00108     case WONT:
00109     case WILL:
00110       if (FAILED(res = negotiateOptions(cmd, opt)))
00111         return ERROR_BACKTRACE(res);
00112       break;
00113     case SB:
00114       switch (opt) {
00115         case TELOPT_NAWS:
00116         {
00117           uint8_t buf[4];
00118 
00119           if (FAILED(res = _connection->read((char *)buf, sizeof(buf), 1)))
00120             return ERROR_BACKTRACE(res);
00121           *readCnt += 4;
00122           _screenWidth = (buf[0] << 8) | buf[1];
00123           _screenHeight = (buf[2] << 8) | buf[3];
00124           DEBUG("Terminal size: %ux%u.", _screenWidth, _screenHeight);
00125           if (FAILED(res = _gl->setWidth(_screenWidth)))
00126             return ERROR_BACKTRACE(res);
00127           goto skip_sb;
00128         }
00129         case TELOPT_TTYPE:
00130           if (FAILED(res = _connection->read((char *)(&act), 1, 1)))
00131             return ERROR_BACKTRACE(res);
00132           ++(*readCnt);
00133           if (act == TELQUAL_IS) {
00134             int i = 0;
00135             for (;;) {
00136               if ((res = _connection->read((char *)(&act), 1, 1)))
00137                 return ERROR_BACKTRACE(res);
00138               ++(*readCnt);
00139               if (i < (int)TELNET_LIMIT_TERM_NAME_SIZE - 1 && act != IAC)
00140                  _termName[i++] = act;
00141               else {
00142                 _termName[i] = '\0';
00143                 break;
00144               }
00145             }
00146           }
00147           DEBUG("Terminal name: '%s'.", _termName);
00148           goto skip_sb;
00149       }
00150 skip_sb:
00151       // Just ignore not supported Subnegotiation option.
00152       do {
00153         if (FAILED(res = _connection->read((char *)(&act), 1, 1)))
00154           return ERROR_BACKTRACE(res);
00155         ++(*readCnt);
00156 
00157 #ifdef DEBUG_MODE
00158         DEBUG("Subnegotiation ignored sequence:");
00159         DEBUG("%d (0x%02x)", act & 0xff, act & 0xff);
00160         if (act >= ' ' && act <= '~')
00161           DEBUG("/%c/", act & 0xff);
00162 #endif
00163 
00164       } while (act != SE);
00165       break;
00166     case GA:
00167       break;
00168     case EL:
00169       break;
00170     case EC:
00171       break;
00172     case AYT:
00173       break;
00174     case AO:
00175       break;
00176     case IP:
00177       break;
00178     case BREAK:
00179       break;
00180     case DM:
00181       break;
00182     case NOP:
00183       break;
00184     case SE:
00185       break;
00186     case EOR:
00187       break;
00188     case ABORT:
00189       break;
00190     case SUSP:
00191       break;
00192     case xEOF:
00193       break;
00194     default:
00195       DEBUG("telnet command '%s' is not supported.", TELCMD(cmd));
00196   }
00197 
00198   return OK;
00199 }
00200 
00201 int Telnet::setEcho(Connection *conn, const bool on)
00202 {
00203   int res;
00204   if (FAILED(res = iac(conn, WILL, TELOPT_ECHO, on ? 1 : 0)))
00205     return ERROR_BACKTRACE(res);
00206   return OK;
00207 }
00208 
00209 // Private methods.
00210 
00211 // The function invoked when IAC and DO/DONT/WONT/WILL is accepted.
00212 // The finction executes all telnet specific option negotiations.
00213 int Telnet::negotiateOptions(const uint8_t cmd, const uint8_t opt)
00214 {
00215   int res;
00216 
00217   switch(opt) {
00218     case TELOPT_ECHO:
00219       switch (cmd) {
00220         case WONT:
00221         case DO:
00222           // OK, I do the echo, client won't
00223           _echo = true;
00224           DEBUG("Echo mode is OK.");
00225           break;
00226         case DONT:
00227           // Client says - do not do echo. No, I disagree, I do.
00228           if (_echoCount++ < 4)
00229             if (FAILED(res = iac(WILL, TELOPT_ECHO, 0, 0, 0, 0)))
00230               return ERROR_BACKTRACE(res);
00231           break;
00232         case WILL:
00233           // Client says - I will echo. OK, but client shouldn't
00234           if (_echoCount++ < 4)
00235             if (FAILED(res = iac(DONT, TELOPT_ECHO, 0, 0, 0, 0)))
00236               return ERROR_BACKTRACE(res);
00237           break;
00238       }
00239       break;
00240     case TELOPT_SGA:
00241       switch (cmd) {
00242         case WONT:
00243         case DO:
00244           // OK, I will work SGA mode, client won't
00245           _sga = true;
00246           DEBUG("SGA mode is OK.");
00247           break;
00248         case DONT:
00249           // Client says - do not work in SGA mode. No, I disagree, I do.
00250           if (_sgaCount++ < 4)
00251             if (FAILED(res = iac(WILL, TELOPT_SGA, 0, 0, 0, 0)))
00252               return ERROR_BACKTRACE(res);
00253             break;
00254         case WILL:
00255           // Client says - I should work in SGA mode. OK, but client shouldn't.
00256           if (_sgaCount++ < 4)
00257             if (FAILED(res = iac(DONT, TELOPT_SGA, 0, 0, 0, 0)))
00258               return ERROR_BACKTRACE(res);
00259             break;
00260       }
00261       break;
00262     case TELOPT_TTYPE:
00263       if (cmd == WILL)
00264         if (FAILED(res = iac(SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE, 0)))
00265           return ERROR_BACKTRACE(res);
00266         break;
00267     case TELOPT_NAWS:
00268         // OK, we accept screen sizes in telnet_parse().
00269         break;
00270     default:
00271       DEBUG("telnet mode '%s' is not supported.", TELOPT(opt));
00272       if (cmd == WILL)
00273         if (FAILED(res = iac(DONT, opt, 0, 0, 0, 0)))
00274           return ERROR_BACKTRACE(res);
00275       else
00276         if (cmd == DO)
00277           if (FAILED(res = iac(WONT, opt, 0, 0, 0, 0)))
00278             return ERROR_BACKTRACE(res);
00279         break;
00280   }
00281 
00282   return OK;
00283 }

Generated on Thu Sep 6 20:11:25 2007 for Pylon Application Platform by  doxygen 1.5.1