"Fossies" - the Fresh Open Source Software Archive

Member "delegate9.9.13/src/telnet.c" (20 Jul 2010, 58550 Bytes) of package /linux/misc/old/delegate9.9.13.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "telnet.c" see the Fossies "Dox" file reference documentation.

    1 /*////////////////////////////////////////////////////////////////////////
    2 Copyright (c) 1994-2000 Yutaka Sato and ETL,AIST,MITI
    3 Copyright (c) 2001-2006 National Institute of Advanced Industrial Science and Technology (AIST)
    4 AIST-Product-ID: 2000-ETL-198715-01, H14PRO-049, H15PRO-165, H18PRO-443
    5 
    6 Permission to use this material for noncommercial and/or evaluation
    7 purpose, copy this material for your own use, and distribute the copies
    8 via publicly accessible on-line media, without fee, is hereby granted
    9 provided that the above copyright notice and this permission notice
   10 appear in all copies.
   11 AIST MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY OF THIS
   12 MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS", WITHOUT ANY EXPRESS
   13 OR IMPLIED WARRANTIES.
   14 /////////////////////////////////////////////////////////////////////////
   15 Content-Type:   program/C; charset=US-ASCII
   16 Program:    telnet.c (telnet proxy)
   17 Author:     Yutaka Sato <ysato@etl.go.jp>
   18 Description:
   19 History:
   20     940304  created
   21 //////////////////////////////////////////////////////////////////////#*/
   22 #include <ctype.h>
   23 #include <errno.h>
   24 #include <stdio.h>
   25 #include "ystring.h"
   26 #include "vsignal.h"
   27 #include "delegate.h"
   28 #include "fpoll.h"
   29 #include "auth.h"
   30 #include "proc.h"
   31 
   32 extern int IO_TIMEOUT;
   33 
   34 int connectToSsh(Connection *Conn,const char *host,int port,PCStr(user),PCStr(pass));
   35 int makeXproxy(Connection *Conn,PVStr(pxdisplay),PCStr(display),PVStr(pxhost),PCStr(relhost),PCStr(me),int timeo);
   36 extern int *ccx_global;
   37 
   38 static int ccx_telnet(int*ccx,PVStr(buf),int *ip,int cc);
   39 static int rewriteTelnet(int direction,PVStr(buf),int cc);
   40 static void putIAC(FILE *tc,int com,int what);
   41 
   42 typedef struct {
   43     MStr(   te_Xdisplay,MaxHostNameLen);
   44     MStr(   te_Xproxy,MaxHostNameLen);
   45     int te_Xpid;
   46     int te_THREAD;
   47     int te_THREAD_exiting;
   48   const char   *te_useTHREAD;
   49     int te_env_valid;
   50     jmp_buf te_tel_env;
   51     int te_gotSIGURG;
   52     int te_dump_commands;
   53     int te_ClientsWill[128];
   54     int te_ServersWill[128];
   55     int te_ClientsDO[256];
   56     int te_docontrol;
   57     MStr(   te__opt,8);
   58     int te_keepalive;
   59     MStr(   te_ccxbTOCL,64);
   60     MStr(   te_ccxbTOSV,64);
   61     CCXP    te_ccxTOCL;
   62     CCXP    te_ccxTOSV;
   63 } TelnetEnv;
   64 static TelnetEnv *telnetEnv;
   65 #define Xdisplay    telnetEnv->te_Xdisplay
   66 /**/
   67 #define Xproxy      telnetEnv->te_Xproxy
   68 /**/
   69 #define Xpid        telnetEnv->te_Xpid
   70 #define THREAD      telnetEnv->te_THREAD
   71 #define THREAD_exiting  telnetEnv->te_THREAD_exiting
   72 #define useTHREAD   telnetEnv->te_useTHREAD
   73 #define env_valid   telnetEnv->te_env_valid
   74 #define tel_env     telnetEnv->te_tel_env
   75 #define gotSIGURG   telnetEnv->te_gotSIGURG
   76 #define dump_commands   telnetEnv->te_dump_commands
   77 #define ClientsWill telnetEnv->te_ClientsWill
   78 #define ServersWill telnetEnv->te_ServersWill
   79 #define ClientsDO   telnetEnv->te_ClientsDO
   80 #define docontrol   telnetEnv->te_docontrol
   81 #define _opt        telnetEnv->te__opt
   82 /**/
   83 #define keepalive   telnetEnv->te_keepalive
   84 #define ccxTOCL     telnetEnv->te_ccxTOCL
   85 #define ccxTOSV     telnetEnv->te_ccxTOSV
   86 #define ccxbTOCL    telnetEnv->te_ccxbTOCL
   87 #define ccxbTOSV    telnetEnv->te_ccxbTOSV
   88 
   89 void scan_TELNETCONF(Connection*_,PCStr(conf))
   90 {   CStr(what,32);
   91     CStr(value,64);
   92     int ival;
   93 
   94     if( telnetEnv == 0 )
   95         telnetEnv = NewStruct(TelnetEnv);
   96 
   97     fieldScan(conf,what,value);
   98     ival = atoi(value);
   99     if( strcaseeq(what,"keepalive") ){
  100         if( ival == 0 )
  101             ival = 30;
  102         keepalive = ival;
  103     }
  104 }
  105 
  106 static int guessedHalfdup = -1;
  107 
  108 static void sigPIPE(int sig){
  109     signal(SIGPIPE,SIG_IGN);
  110     if( env_valid ){
  111         env_valid = 0;
  112         if( !THREAD ) longjmp(tel_env,SIGPIPE);
  113     }
  114 }
  115 static void sigTERM(int sig){
  116     signal(SIGTERM,SIG_IGN);
  117     if( env_valid ){
  118         env_valid = 0;
  119         if( !THREAD ) longjmp(tel_env,SIGTERM);
  120     }
  121 }
  122 
  123 #define CtoS    1   /* client to server */
  124 #define StoC    2   /* server to client */
  125 #define CtoD    3   /* client to delegate */
  126 #define DtoS    4   /* delegate to server */
  127 #define DtoC    5   /* delegate to client */
  128 
  129 #define SE  240
  130 #define NOP 241
  131 #define IP  244 /* Interrupt Process */
  132 #define EC  247
  133 #define EL  248
  134 #define SB  250
  135 #define WILL    251
  136 #define WONT    252
  137 #define DO  253
  138 #define DONT    254
  139 #define IAC 255
  140 
  141 #define SYNCH   242
  142             /* DM DataMark */
  143 
  144 #define IS  0
  145 #define VAR 0
  146 #define VALUE   1
  147 
  148 #define O_ECHO       1
  149 #define O_SUPAHEAD   3  /* Suppress Go Ahead */
  150 #define O_STATUS     5  /* Give Status */
  151 #define O_TM         6  /* Timing-Mark */
  152 #define O_NAOL       8  /* Negotiate About Output Line width */
  153 #define O_NAOP       9  /* Negotiate About Output Page size */
  154 #define O_TERMTYPE  24  /* Teminal Type */
  155 #define O_NAWS      31  /* Negotiate About Window Size */
  156 #define O_TSPEED    32  /* Terminal Speed */
  157 #define O_LFLOW     33  /* Remote Flow Control */
  158 #define O_XDISPLOC  35  /* X Display Location */
  159 #define O_ENVIRON   36  /* Environment Variables */
  160 #define O_AUTH      37  /* Authentication */
  161 #define O_NENVIRON  39  /* New Environment Option RFC1572 */
  162 
  163 static char NOPstr[2] = { IAC, NOP };
  164 
  165 static void sigurg(int sig)
  166 {   const char *ssig;
  167 
  168     signal(SIGURG,sigurg);
  169     gotSIGURG += 1;
  170     if( sig == SIGURG ) ssig = "URG"; else
  171     if( sig == SIGIO  ) ssig = "IO"; else
  172                 ssig = "??";
  173     sv1log("got SIG%s (%d)\n",ssig,gotSIGURG);
  174 }
  175 
  176 /*
  177 #include <sys/sockio.h>
  178 setRecvSIGURG(sock)
  179 {   int pgrp;
  180 
  181     signal(SIGURG,sigurg);
  182     pgrp = getpid();
  183     ioctl(sock,SIOCSPGRP,&pgrp);
  184     ioctl(sock,SIOCGPGRP,&pgrp);
  185     sv1log("set receive SIGURG [%d] %d\n",sock,pgrp);
  186 }
  187 */
  188 
  189 int recvPEEK(int sock,PVStr(buf),int size);
  190 void CCXcounts(CCXP ccx);
  191 int CCXsize();
  192 static int withTM(int sock){
  193     int rcc;
  194     CStr(buf,3);
  195 
  196     /* reset error status of CCX on break */
  197     if( ccxTOSV ){
  198         bcopy(ccxbTOSV,ccxTOSV,CCXsize());
  199     }
  200     if( ccxTOCL ){
  201         bcopy(ccxbTOCL,ccxTOCL,CCXsize());
  202     }
  203 
  204     /* 9.4.0 withTM() is since 9.0.3-pre18, but recvPEEK() breaks the
  205      * relay of relayOOB()
  206     rcc = recvPEEK(sock,AVStr(buf),sizeof(buf));
  207     if( rcc == 3 )
  208     if( (0xFF&buf[0]) == IAC && (0xFF&buf[1]) == WILL && buf[2] == O_TM ){
  209         sv1log("relay TimingMark before OOB DataMark...\n");
  210         return 1;
  211     }
  212     */
  213     return 0;
  214 }
  215 static int Read(PCStr(what),int sock,int dst,PVStr(buf),int fsbsize,int timeout)
  216 {   int cnt,rcc,ci,cj;
  217     const char *sdir;
  218     int start,nready;
  219 
  220     rcc = 0;
  221     start = time(0);
  222     if( keepalive ){
  223         timeout = keepalive;
  224     }
  225     for( cnt = 0; cnt < 10; cnt++ ){
  226         if( timeout == -1 )
  227             nready = 1;
  228         else
  229         if( THREAD )
  230             nready = thread_PollIn(sock,timeout*1000);
  231         else    nready = PollIn(sock,timeout*1000);
  232 
  233         if( THREAD_exiting ){
  234             sv1log("THREAD_exiting: PollIn()=%d\n",nready);
  235             rcc = -1;
  236             break;
  237         }
  238 
  239         if( nready < 0 ){
  240             sv1log("nready = %d, errno=%d\n",nready,errno);
  241             rcc = -1;
  242             break;
  243         }
  244         if( nready == 0 ){
  245             if( keepalive ){
  246                 Verbose("%s generate NOP\n",what);
  247                 rcc = sizeof(NOPstr);
  248                 Bcopy(NOPstr,buf,rcc);
  249                 break;
  250             }
  251 
  252             if( timeout <= time(0)-start ){
  253                 sv1log("Timedout %d sec. (by TIMEOUT=io:%ds)\n",
  254                     timeout,timeout);
  255                 rcc = -1;
  256                 break;
  257             }
  258 
  259             if( relayOOB(sock,dst) )
  260                 continue;
  261 
  262             msleep(10);
  263             continue;
  264         }
  265         errno = 0;
  266 
  267         if( gotOOB(sock) )
  268         if( !withTM(sock) )
  269         if( 0 < withOOB(sock) ){
  270             if( relayOOB(sock,dst) ){
  271                 /* should skip inputs until SYNCH */
  272             }
  273             continue;
  274         }
  275 
  276         rcc = read(sock,(char*)buf,QVSSize(buf,fsbsize));
  277         if( rcc <= 0 ){
  278             sv1log("sock=%d read=%d, errno=%d\n",sock,rcc,errno);
  279             break;
  280         }
  281         if( THREAD_exiting ){
  282             sv1log("THREAD_exiting: read()=%d\n",rcc);
  283             break;
  284         }
  285 
  286         if( !gotSIGURG ){
  287             break;
  288         }
  289 
  290         if( rcc <= 0 ){
  291             sv1log("%s read failed (%d) waiting SYNCH (%d)\n",
  292                 what,cnt,gotSIGURG);
  293             if( 0 < cnt ){
  294                 sv1log("#### faield (%d) wait 0.1 second.\n",cnt);
  295                 msleep(100);
  296             }
  297             continue;
  298         }
  299 
  300         for( ci = 0; ci < rcc; ci++ )
  301         if( (buf[ci] & 0xFF) == SYNCH ){
  302             sv1log("%s got SYNCH (%d)\n",what,gotSIGURG);
  303             gotSIGURG = 0;
  304             ci++;
  305             break;
  306         }
  307         if( ci == rcc )
  308             continue;
  309 
  310         rcc -= ci;
  311         for( cj = 0; cj < rcc; cj++ )
  312             setVStrElem(buf,cj,buf[ci+cj]); /**/
  313         break;
  314     }
  315     for( ci = 0; ci < rcc; ci++ ){
  316         if( (buf[ci] & 0xFF) == SYNCH )
  317             Verbose("######## SYNCH in non Sync mode.\n");
  318     }
  319     return rcc;
  320 }
  321 
  322 #define C_S 1
  323 #define S_C 2
  324 
  325 /*
  326  * The following stuff is to control ECHOing (and editing with DEL or
  327  * so) of user-input at the proxy-login dialog (with "Host:" prompt)
  328  *
  329  * The default status of ECHO option is WONT/DONT ECHO as described
  330  * in RFC857.  But some clients programs (like MS's one) seem to
  331  * expect the server to be WILL ECHO by default, making full-duplex
  332  * (character by character) communication without doing local-echo...
  333  * (it's the case of WinNT/2K but not the case in Win95/98/Me/XP...)
  334  * Doing ECHO negotiation between DeleGate and client is a possible
  335  * solution, like in older version of DeleGate, but it spoiles
  336  * the transparent relaying between a server and a client.
  337  *
  338  * In secondary connections after closing the first session to a
  339  * server, a client may be in different status from the initial one
  340  * (probably in explicit DO ECHO and DO SUPGA). It might make "Host:"
  341  * dialogue (its editing) be different from the first one (may not be
  342  * harmful), and make inconsistency with the server (can be harmful)...
  343  */
  344 static int isHalfdup(){
  345     int doga;
  346 
  347     if( doga = ClientsDO[O_SUPAHEAD] ) 
  348         return doga == DONT;
  349     if( 0 < guessedHalfdup )
  350         return 1;
  351     return 0; /* it should be 0 based on RFC858... */ 
  352 }
  353 static int willEcho(){
  354     int doecho;
  355 
  356     /* explicitly ordered by the client */
  357     if( doecho = ClientsDO[O_ECHO] )
  358         return doecho == DO;
  359 
  360     /* negotiated something leaving ECHO as default */
  361     if( ClientsDO[O_SUPAHEAD] )
  362         return 0;
  363 
  364     /* suppress redundant echo in half-duplex communication */
  365     if( isHalfdup() )
  366         return 0;
  367 
  368     /* it should be 0 baesd on RFC857... */
  369     /* 0 for Win95 series but 1 for WinNT series */
  370     return 1;
  371 }
  372 static void logEcho()
  373 {   const char *echo;
  374     const char *supga;
  375 
  376     switch( ClientsDO[O_ECHO] ){
  377         case DO:  echo = "DoEcho"; break;
  378         case DONT:echo = "DontEcho"; break;
  379         default:  echo = "none"; break;
  380     }
  381     switch( ClientsDO[O_SUPAHEAD] ){
  382         case DO:  supga = "DoSupGA"; break;
  383         case DONT:supga = "DontSupGA"; break;
  384         default:  supga = "none"; break;
  385     }
  386     sv1log("%s-Echo [client-says:%s,%s], Half=%d(%d)\n",
  387         willEcho()?"WILL":"WONT",echo,supga,
  388         isHalfdup(),guessedHalfdup);
  389 }
  390 
  391 static void clearServersWill()
  392 {   int si;
  393 
  394     for( si = 0; si < 128; si++)
  395         ServersWill[si] = 0;
  396 }
  397 static int numServersWill()
  398 {   int si,num;
  399 
  400     num = 0;
  401     for( si = 0; si < 128; si++ )
  402         if( ServersWill[si] )
  403             num++;
  404     return num;
  405 }
  406 
  407 static const char *code(int ch)
  408 {   const char *mn;
  409     int co;
  410 
  411     co = 1;
  412     switch( ch ){
  413         case SE:  co = 0; mn = "SE";        break;
  414         case 241: co = 0; mn = "NOP";       break;
  415         case 242: co = 0; mn = "DataMark";  break;
  416         case 243: co = 0; mn = "Break";     break;
  417         case 244: co = 0; mn = "InterruptProcess";  break;
  418         case 245: co = 0; mn = "AbortOutput";   break;
  419         case 246: co = 0; mn = "AreYouThere";   break;
  420         case 247: co = 0; mn = "Erasecharacter";    break;
  421         case 248: co = 0; mn = "EraseLine"; break;
  422         case 249: co = 0; mn = "Goahead";   break;
  423         case SB:  co = 0; mn = "SB";        break;
  424         case WILL:co = 1; mn = "WILL";      break;
  425         case WONT:co = 1; mn = "WONT";      break;
  426         case DO:  co = 1; mn = "DO";        break;
  427         case DONT:co = 1; mn = "DONT";      break;
  428         case IAC: co = 1; mn = "IAC";       break;
  429         default:  co = 0; mn = NULL;        break;
  430     }
  431     return mn;
  432 }
  433 static const char *option(int ch)
  434 {
  435     switch( ch ){
  436         case O_ECHO:     return "Echo";
  437         case O_TM:     return "Timingmark";
  438         case O_SUPAHEAD: return "SuppressGoAhead";
  439         case O_STATUS:   return "GiveStatus";
  440         case O_TERMTYPE: return "TerminalType";
  441         case O_NAOP:     return "NegotiateAboutOutputPageSize";
  442         case O_NAWS:     return "NegotiateAboutWindowSize";
  443         case O_TSPEED:   return "TerminalSpeed";
  444         case O_LFLOW:    return "RemoteFlowControl";
  445         case O_XDISPLOC: return "XDisplayLocation";
  446         case O_ENVIRON:  return "EnvironmentValiables";
  447         case O_NENVIRON: return "NewEnvironment";
  448     }
  449     sprintf(_opt,"%d",ch);
  450     return _opt;
  451 }
  452 
  453 static void controlcommand(Connection *Conn,int toC,int fromC)
  454 {   CStr(code,2);
  455     CStr(msg,128);
  456 
  457     IGNRETP write(toC,"\r\n> ",4);
  458     IGNRETP read(fromC,code,1);
  459     code[1] = 0;
  460     sprintf(msg,"%c\r\n",code[0]);
  461     IGNRETP write(toC,msg,strlen(msg));
  462     global_setCCX(Conn,AVStr(code),AVStr(msg));
  463     IGNRETP write(toC,msg,strlen(msg));
  464 }
  465 
  466 static int scanCommands(int direction,PVStr(buf),int cc)
  467 {   int i,cch,ch,cont;
  468     CStr(vch,2048);
  469     const char *mn;
  470     const char *sdir;
  471 
  472     switch( direction ){
  473         case CtoS: sdir = "CS"; break;
  474         case DtoS: sdir = "DS"; break;
  475         case CtoD: sdir = "CD"; break;
  476         case StoC: sdir = "SC"; break;
  477         case DtoC: sdir = "DC"; break;
  478         default:   sdir = "??"; break;
  479     }
  480 
  481     for(i = 0; i < cc; i++){
  482 
  483         if( direction == CtoS ){
  484         cc += rewriteTelnet(direction,QVStr(buf+i,buf),cc-i);
  485         }
  486 
  487         ch = buf[i] & 0xFF;
  488         vch[0] = 0;
  489         if( ch == IAC ){
  490             ch = buf[++i] & 0xFF;
  491         if( WILL <= ch && ch <= DONT ){
  492             cch = ch;
  493             mn = code(cch);
  494                 ch = buf[++i] & 0xFF;
  495             sprintf(vch,"%-4s %s",mn,option(ch));
  496             if( direction == CtoS || direction == CtoD ){
  497                 if( ch == O_XDISPLOC ){
  498                     if( cch == WONT && Xdisplay[0] ){
  499                         Verbose("WONT->WILL %s\n",option(ch));
  500                         cch = WILL;
  501                         setVStrElem(buf,i-1,cch);
  502                         mn = code(cch);
  503                         sprintf(vch,"%-4s %s",mn,option(ch));
  504                     }
  505                 }
  506                 if( ch == O_TERMTYPE || ch == O_ENVIRON )
  507                 Verbose("%s Client-Says %s\n",sdir,vch);
  508                 if( cch == WILL
  509                  || cch == WONT && ClientsWill[ch] == 0 )
  510                     ClientsWill[ch] = cch;
  511                 if( cch == DO || cch == DONT )
  512                     ClientsDO[ch] = cch;
  513             }else
  514             if( direction == StoC ){
  515                 ServersWill[ch] = cch;
  516             }
  517         }else
  518         if( ch == NOP ){
  519             sprintf(vch,"NOP");
  520         }else
  521         if( ch == SB ){
  522             strcpy(vch,"SB,");
  523             ch = buf[++i];
  524             strcat(vch,option(ch));
  525 
  526             for(i++; i < cc; i++ ){
  527                 ch = buf[i] & 0xFF;
  528                 if( ' ' < ch && ch < 0x7F ){
  529                     if( buf[i-1]<=' ' || 0x7F<=buf[i-1] )
  530                         strcat(vch,",");
  531                     Xsprintf(TVStr(vch),"%c",ch);
  532                 }else
  533                 if( mn = code(ch) ){
  534                     strcat(vch,",");
  535                     strcat(vch,mn);
  536                 }else{
  537                     strcat(vch,",");
  538                     Xsprintf(TVStr(vch),"%d",ch);
  539                 }
  540                 if( ch == SE )
  541                     break;
  542             }
  543         }
  544             Verbose("%s[%3d] %02x:%3d %s\n",sdir,i,ch,ch,vch);
  545         }else{
  546         if( dump_commands & (direction<<8) )
  547             sv1log("%s[%3d] %02x:%3d %c\n",
  548             sdir,i,ch,ch,(' '<ch&&ch<0x7F)?ch:' ');
  549         if( direction == StoC ){
  550             /* code conversion */
  551             if( ccxTOCL ){
  552                 cc = ccx_telnet((int*)ccxTOCL,AVStr(buf),&i,cc);
  553                 if( (0xFF & buf[i]) == IAC ) i--;
  554             }else
  555             if( ccx_global ) cc = ccx_telnet(ccx_global,AVStr(buf),&i,cc);
  556         }else
  557         if( direction == CtoS ){
  558             if( ccxTOSV ){
  559                 cc = ccx_telnet((int*)ccxTOSV,AVStr(buf),&i,cc);
  560                 if( (0xFF & buf[i]) == IAC ) i--;
  561             }
  562             if( ccx_global ){
  563                 /* conversion switch command ... */
  564                 /*
  565             static numCC;
  566                 if( ch == ('C'-0x40) ){
  567                     numCC++;
  568                     if( 2 < numCC ){
  569                         sv1log("## ctrl-C*%d\n",numCC);
  570                         numCC = 0;
  571                         if( i+1 == cc ){
  572                             docontrol = 1;
  573                             cc--;
  574                         }
  575                     }
  576                 }else   numCC = 0;
  577                 */
  578             }
  579         }
  580         }
  581     }
  582     return cc;
  583 }
  584 static int ccx_telnet(int*ccx,PVStr(buf),int *ip,int cc)
  585 {   CStr(out,0x8000);
  586     CStr(tmp,0x8000);
  587     int i,j,k,l,tc,oc;
  588     int fc;
  589 
  590     i = *ip;
  591     if( (0xFF & buf[i]) == SYNCH ){
  592         return cc;
  593     }
  594     for( j = i; j < cc; j++ )
  595         if( (buf[j] & 0xFF) == IAC )
  596             break;
  597 
  598     if( j == i ){
  599         return cc;
  600     }
  601 
  602 /*
  603  fprintf(stderr,
  604 "-- %3d/%3d [%2X [%3d][%3d] %2X] %2X/%3d %2X/%3d %2X %2X %2X %2X %2X %2X ",
  605 j-i,cc,
  606 0<j?0xFF&buf[j-1]:0,i,j,j<cc?0xFF&buf[j]:0,
  607 0xFF&buf[i+0],0xFF&buf[i+0],
  608 0xFF&buf[i+1],0xFF&buf[i+1],
  609 0xFF&buf[i+2],0xFF&buf[i+3],
  610 0xFF&buf[i+4],0xFF&buf[i+5],
  611 0xFF&buf[i+6],0xFF&buf[i+7]
  612  ); CCXcounts((CCXP)ccx);
  613 */
  614 
  615     oc = CCXexec((CCXP)ccx,buf+i,j-i,AVStr(out),sizeof(out));
  616     /*
  617     fc = CCXexec((CCXP)ccx,"",0,DVStr(out,oc),sizeof(out)-oc);
  618     oc += fc;
  619     */
  620 
  621     tc = 0;
  622     for( k = j; k < cc; k++ ){
  623         if( elnumof(tmp) <= tc ){
  624             break;
  625         }
  626         setVStrElemInc(tmp,tc,buf[k]); /**/
  627     }
  628     for( k = 0; k < oc; k++ ){
  629         assertVStr(buf,buf+i+1);
  630         setVStrElemInc(buf,i,out[k]); /**/
  631     }
  632     l = i;
  633     for( k = 0; k < tc; k++ ){
  634         assertVStr(buf,buf+l+1);
  635         setVStrElemInc(buf,l,tmp[k]); /**/
  636     }
  637     cc = l;
  638 
  639     *ip = i;
  640     return cc;
  641 }
  642 
  643 static int getCommand(FILE *fp,UTag *Tcbuf,int off)
  644 {   int ci,ch,ach;
  645     refQStr(cbuf,Tcbuf->ut_addr); /**/
  646     int cx;
  647 
  648     cbuf = (char*)Tcbuf->ut_addr + off;
  649     cx = Tcbuf->ut_size - off - 1;
  650 
  651     ci = 0;
  652     ch = getc(fp);
  653     setVStrElemInc(cbuf,ci,ch);
  654 
  655     if( WILL <= ch && ch <= DONT ){
  656         ach = getc(fp);
  657         setVStrElemInc(cbuf,ci,ach);
  658     }else
  659     if( ch == SB ){
  660         do {
  661             if( cx <= ci ){
  662                 notify_overflow("Telnet Command",Tcbuf->ut_addr,off+ci);
  663                 discardBuffered(fp);
  664                 break;
  665             }
  666             ch = getc(fp);
  667             if( ch == EOF )
  668                 break;
  669             setVStrElemInc(cbuf,ci,ch);
  670         } while(ch != SE);
  671     }
  672     return ci;
  673 }
  674 /**/
  675 static int xgetline(void *visible,FILE *fc,FILE *tc,UTag *Tline,UTag *Tcbuf)
  676 {   int ready;
  677     int ch,ci;
  678     int endCR;
  679     int lx;
  680     int ii;
  681     const char *cb;
  682     int timeout;
  683     CStr(cbufbuf,1024);
  684     refQStr(line,Tline->ut_addr); /**/
  685     defQStr(cbuf); /*indirect*//**/
  686     int lsiz,csiz;
  687     UTag Tcbufb;
  688 
  689     line = (char*)Tline->ut_addr;
  690     lsiz = Tline->ut_size;
  691     if( Tcbuf == NULL ){
  692         Tcbuf = &Tcbufb;
  693         setQStr(Tcbuf->ut_addr,cbufbuf,sizeof(cbufbuf));
  694         Tcbuf->ut_size = sizeof(cbufbuf);
  695     }
  696     setQStr(cbuf,Tcbuf->ut_addr,Tcbuf->ut_size);
  697     csiz = Tcbuf->ut_size;
  698     endCR = 0;
  699     ci = 0;
  700     lx = 0;
  701 
  702     if( Xproxy[0] )
  703         timeout = LOGIN_TIMEOUT * 1000 * 4;
  704     else    timeout = LOGIN_TIMEOUT * 1000;
  705 
  706     /*
  707      * "Password:" for proxy-authorization should be hidden.
  708      * To do so, DeleGate must notify that I WILL DO FULL DUPLEX ECHO
  709      * to the client. (This had been the default before 6.1.3)
  710      */
  711     if( !visible ){
  712         if( ClientsDO[O_ECHO] != DO && ClientsWill[O_ECHO] != WONT )
  713             putIAC(tc,WILL,O_ECHO);
  714         if( ClientsDO[O_SUPAHEAD] != DO )
  715             putIAC(tc,WILL,O_SUPAHEAD);
  716     }
  717 
  718     for(;;){
  719         if( lsiz-1 <= lx ){
  720             notify_overflow("Telnet Input",line,lx);
  721             discardBuffered(fc);
  722             break;
  723         }
  724         if( csiz-1 <= ci ){
  725             notify_overflow("Telnet Command",cbuf,ci);
  726             discardBuffered(fc);
  727             break;
  728         }
  729         if( fflush(tc) == EOF )
  730             break;
  731 
  732         ready = fPollIn(fc,timeout);
  733         if( ready <= 0 ){
  734             if( ready == 0 ){
  735  fprintf(tc,"\r\n---------- PROXY-TELNET login: TIMEOUT(%d).\r\n",
  736                     timeout);
  737                 fflush(tc);
  738                 return EOF;
  739             }
  740             break;
  741         }
  742         ch = fgetc(fc);
  743 
  744         if( ch != IAC && ch != 0 ){
  745             if( guessedHalfdup < 0 ){
  746                 if( ready_cc(fc) <= 0 )
  747                     guessedHalfdup = 0;
  748                 else    guessedHalfdup = 1;
  749             }
  750             if( lx == 0 )
  751                 logEcho();
  752         }
  753 
  754         if( visible ){
  755             if( willEcho() )
  756             if( isprint(ch) ){
  757                 putc(ch,tc);
  758                 fflush(tc);
  759             }
  760 
  761             if( ch == '?' ){
  762                 if( fPollIn(fc,300) == 0 ){
  763                     setVStrElemInc(line,lx,ch);
  764                     goto EOL;
  765                 }
  766             }
  767         }else{
  768             if( isprint(ch) || ch == '\t' ){
  769                 putc('*',tc);
  770                 fflush(tc);
  771             }
  772         }
  773 
  774         if( ch != IAC )
  775             Verbose("CD %x\n",ch);
  776 
  777         switch( ch ){
  778             case IAC:
  779                 setVStrElem(cbuf,ci,ch);
  780                 ii = 1 + getCommand(fc,Tcbuf,ci+1);
  781                 ci += scanCommands(CtoS,
  782                     QVStr(cbuf+ci,Tcbuf->ut_addr),ii);
  783                 break;
  784 
  785             case 0: break;
  786             case '\001': break; /* ? */
  787 
  788             case 'D'-0x40:
  789                 fprintf(tc,"\r\n");
  790                 fflush(tc);
  791             case EOF:
  792                 Verbose("--EOF from client--\n");
  793                 return EOF;
  794             case 0x7F:
  795             case 'H'-0x40:
  796                 if( 0 < lx ){
  797                     if( willEcho() )
  798                     fwrite("\b \b",1,3,tc);
  799                     lx--;
  800                 }
  801                 break;
  802 
  803             case 'U'-0x40:
  804             {   for( ii = 0; ii < lx; ii++ ) fputc('\b',tc);
  805                 for( ii = 0; ii < lx; ii++ ) fputc(' ',tc);
  806                 for( ii = 0; ii < lx; ii++ ) fputc('\b',tc);
  807                 lx = 0;
  808                 break;
  809             }
  810 
  811             case '\r':
  812                 endCR = 1; /* X-( */
  813                 if( 0 < ready_cc(fc) ){
  814                     int nch;
  815                     nch = getc(fc);
  816                     if( nch == '\n' ){
  817                         ch = nch;
  818                         endCR = 0;
  819                     }else   ungetc(nch,fc);
  820                 }
  821             case '\n':
  822                 Verbose("EOL char = %x\n",ch);
  823                 goto EOL;
  824             default:
  825                 setVStrElemInc(line,lx,ch);
  826                 break;
  827         }
  828     } EOL:
  829     setVStrEnd(line,lx);
  830 
  831     if( willEcho() )
  832     fputs("\r\n",tc);
  833 
  834     Tcbuf->ut_leng = ci;
  835     return ci;
  836 }
  837 
  838 #define addIbuf(v) setVStrElemInc(ibuf,ii,v)
  839 static void putIAC(FILE *tc,int com,int what)
  840 {   JStr(ibuf,32);
  841     char ii;
  842 
  843     ii = 0;
  844     addIbuf(IAC); addIbuf(com); addIbuf(what);
  845     ii = scanCommands(DtoC,AVStr(ibuf),ii);
  846     fwrite(ibuf,1,ii,tc);
  847 }
  848 static void sayWelcome(Connection *Conn,FILE *tc)
  849 {   const char *aurl;
  850     CStr(rurl,256);
  851     CStr(msg,2048);
  852 
  853     aurl = "/-/builtin/mssgs/telnet/telnet-banner.dhtml";
  854     getBuiltinData(Conn,"TELNET-banner",aurl,AVStr(msg),sizeof(msg),AVStr(rurl));
  855     put_eval_dhtml(Conn,rurl,tc,msg);
  856 }
  857 static void putHELP(Connection *Conn,FILE *tc)
  858 {   const char *aurl;
  859     CStr(rurl,256);
  860     CStr(msg,2048);
  861 
  862     aurl = "/-/builtin/mssgs/telnet/telnet-help.dhtml";
  863     getBuiltinData(Conn,"TELNET-help",aurl,AVStr(msg),sizeof(msg),AVStr(rurl));
  864     put_eval_dhtml(Conn,rurl,tc,msg);
  865 }
  866 static void cantConnMessage(Connection *Conn,PCStr(serv),FILE *tc)
  867 {   const char *aurl;
  868     CStr(rurl,256);
  869     CStr(msg,2048);
  870 
  871     aurl = "/-/builtin/mssgs/telnet/telnet-cantconn.dhtml";
  872     getBuiltinData(Conn,"TELNET-cantconn",aurl,AVStr(msg),sizeof(msg),AVStr(rurl));
  873     put_eval_dhtml(Conn,rurl,tc,msg);
  874 }
  875 static void ConnectedMessage(Connection *Conn,PCStr(serv),FILE *tc,PCStr(comline))
  876 {
  877     fprintf(tc,"-- Connected to %s.\r\n",serv);
  878     if( toProxy ){
  879         fprintf(tc,"Connected to another telnet-proxy.\r\n");
  880         fprintf(tc,"Your input[%s] is forwarded to it.\r\n",comline);
  881     }
  882     fflush(tc);
  883 }
  884 
  885 static int get_hostname(Connection *Conn,FILE *fc,FILE *tc,UTag *Tline,UTag *Tcbuf,int ncom)
  886 {   int ci;
  887     CStr(clhost,MaxHostNameLen);
  888 
  889 /*
  890     putIAC(tc,WILL,O_ECHO);
  891     putIAC(tc,WILL,O_SUPAHEAD);
  892 */
  893 
  894     if( ncom == 0 )
  895         sayWelcome(Conn,tc);
  896 
  897     if( ncom == 0 )
  898     if( !source_permittedX(Conn) ){
  899         getpeerNAME(FromC,AVStr(clhost));
  900         fprintf(tc,
  901         "!!!!!!!! HOST <%s> not permitted by DeleGate\r\n",clhost);
  902         fflush(tc);
  903         service_permitted(Conn,"telnet"); /* delay */
  904         return EOF;
  905     }
  906 
  907     fprintf(tc,">> Host name: ");
  908     if( fflush(tc) == EOF )
  909         return EOF;
  910     ci = xgetline((void*)1,fc,tc,Tline,Tcbuf);
  911     if( ci == EOF )
  912         return EOF;
  913 
  914     fflush(tc);
  915     return 0;
  916 }
  917 static void relayClientsWill(FILE *ts,int op)
  918 {   int co;
  919 
  920     if( co = ClientsWill[op] ){
  921         Verbose("DS Client-Said %s %s.\n",code(co),option(op));
  922         putc(IAC,ts);
  923         putc(co,ts);
  924         putc(op,ts);
  925     }
  926 }
  927 static void relayClientsWills(FILE *ts)
  928 {   int op;
  929     for( op = 0; op < 128; op++ )
  930         relayClientsWill(ts,op);
  931 }
  932 
  933 static int telnetCS(Connection *Conn,int fcbsize,int omask)
  934 {   CStr(buf,0x4000);
  935     int gotsig;
  936     int rcc;
  937     int count,total;
  938 
  939     if( FromC < 0 ){
  940         sv1log("CS-REALY: NO INPUT\n");
  941         Finish(0);
  942     }
  943 
  944     total = count = 0;
  945     if( fcbsize == 0 )
  946         fcbsize = 1;
  947     if( sizeof(buf) < fcbsize )
  948         fcbsize = sizeof(buf);
  949 
  950     if( (gotsig = setjmp(tel_env)) == 0 ){
  951         env_valid = 1;
  952         sigsetmask(omask);
  953         for(;;){
  954             rcc = Read("CS",FromC,ToS,AVStr(buf),fcbsize,IO_TIMEOUT);
  955             if( rcc <= 0 )
  956                 break;
  957 
  958             if( dump_commands && C_S ){
  959                 rcc = scanCommands(CtoS,AVStr(buf),rcc);
  960             }
  961 
  962             count += 1;
  963             total += rcc;
  964 
  965             if( docontrol ){
  966                 docontrol = 0;
  967                 controlcommand(Conn,ToC,FromC);
  968                 if( rcc == 0 )
  969                     continue;
  970             }
  971 
  972             if( ToS < 0 )
  973                 continue;
  974 
  975             if( write(ToS,buf,rcc) <= 0 )
  976                 break;
  977         }
  978         if( rcc <= 0 ) sv1log("CS-EOF\n");
  979     }
  980     env_valid = 0;
  981     sv1log("CS-RELAY[%d>%d]: %dBytes %dI/O buf=%d\n",
  982         FromC,ToS,total,count,fcbsize);
  983 
  984     if( gotsig == SIGPIPE ){
  985         /* the server has done but the response data
  986          * to the client may remain (for safety...)
  987          */
  988         sv1log("CS-SIGPIPE\n");
  989         sleep(1);
  990     }
  991     return total;
  992 }
  993 static int telnetSC(Connection *Conn,int fsbsize,int omask)
  994 {   CStr(buf,0x4000);
  995     int gotsig;
  996     int rcc,wcc;
  997     int count,total;
  998 
  999     total = count = 0;
 1000     if( fsbsize == 0 )
 1001         fsbsize = 1;
 1002     if( sizeof(buf) < fsbsize )
 1003         fsbsize = sizeof(buf);
 1004 
 1005     if( setjmp(tel_env) == 0 ){
 1006         env_valid = 1;
 1007         sigsetmask(omask);
 1008         for(;;){
 1009             rcc = Read("SC",FromS,ToC,AVStr(buf),fsbsize,IO_TIMEOUT);
 1010             if( rcc <= 0 )
 1011                 break;
 1012 
 1013             count += 1;
 1014             total += rcc;
 1015             if( (dump_commands & S_C) || CCX0 ){
 1016                 rcc = scanCommands(StoC,AVStr(buf),rcc);
 1017                 if( rcc == 0 ){
 1018                     /* ESC seq. pending in CCX0 */
 1019                     continue;
 1020                 }
 1021             }
 1022 
 1023             if( (wcc = write(ToC,buf,rcc)) <= 0 )
 1024                 break;
 1025         }
 1026         if( rcc <= 0 ) sv1log("SC-EOF\n");
 1027     }
 1028     env_valid = 0;
 1029     sv1log("SC-RELAY[%d<%d]: %dBytes %dI/O buf=%d\n",
 1030         ToC,FromS,total,count,fsbsize);
 1031     return total;
 1032 }
 1033 
 1034 static void THREADexit(Connection *Conn)
 1035 {   int efd;
 1036 
 1037     if( THREAD && THREAD_exiting == 0 ){
 1038         THREAD_exiting = 1;
 1039         if( !IsAlive(FromC) ) efd = FromC; else
 1040         if( !IsAlive(FromS) ) efd = FromS; else
 1041         {
 1042             sv1log("THREAD_exit: both side are alive ? %d,%d\n",
 1043                 FromC,FromS);
 1044             Finish(-1);
 1045         }
 1046         sv1log("THREAD_exit: dup closed socket %d to %d/%d,%d/%d\n",
 1047             efd,FromC,ToC,FromS,ToS);
 1048         if( efd != FromC ) dup2(efd,FromC);
 1049         if( efd != ToC   ) dup2(efd,ToC);
 1050         if( efd != FromS ) dup2(efd,FromS);
 1051         if( efd != ToS   ) dup2(efd,ToS);
 1052 
 1053         /*
 1054          * wait the child thread to exit on closed socket {From,To}{C,S}
 1055          * no return if this is the child thread.
 1056          */
 1057         msleep(10);
 1058         if( THREAD_exiting != 2 )
 1059             sv1log("THREAD_exit: failed, exiting=%d\n",THREAD_exiting);
 1060     }
 1061     else
 1062     if( THREAD ){
 1063         THREAD_exiting++;
 1064     }
 1065 }
 1066 static int SC1(Connection *Conn,int fsbsize,int omask)
 1067 {   int cc;
 1068 
 1069     dumpTimer();
 1070     /*
 1071     setRecvSIGURG(FromS);
 1072     */
 1073     cc = telnetSC(Conn,fsbsize,omask);
 1074     THREADexit(Conn);
 1075     return cc;
 1076 }
 1077 static int CS1(Connection *Conn,int fcbsize,int omask)
 1078 {   int cc;
 1079 
 1080     dumpTimer();
 1081     /*
 1082     setRecvSIGURG(FromC);
 1083     */
 1084     cc = telnetCS(Conn,fcbsize,omask);
 1085     THREADexit(Conn);
 1086     return cc;
 1087 }
 1088 
 1089 static int relayCS0(Connection *Conn,int bsize){
 1090     CStr(buf,0x4000);
 1091     int rcc;
 1092     int icc;
 1093 
 1094     rcc = Read("CS",FromC,ToS,AVStr(buf),bsize,-1);
 1095     icc = rcc;
 1096     if( rcc <= 0 )
 1097         return -1;
 1098     /*
 1099     if( dump_commands && C_S ){
 1100     */
 1101     if( (dump_commands && C_S) || ccxTOSV ){
 1102         rcc = scanCommands(CtoS,AVStr(buf),rcc);
 1103     }
 1104     if( docontrol ){
 1105         docontrol = 0;
 1106         controlcommand(Conn,ToC,FromC);
 1107         if( rcc == 0 )
 1108             return 0;
 1109     }
 1110     if( ToS < 0 )
 1111         return 0;
 1112     if( write(ToS,buf,rcc) <= 0 )
 1113         return -1;
 1114     return rcc;
 1115 }
 1116 static int relaySC0(Connection *Conn,int bsize){
 1117     CStr(buf,0x4000);
 1118     int rcc;
 1119     int icc;
 1120 
 1121     rcc = Read("SC",FromS,ToC,AVStr(buf),bsize,-1);
 1122     icc = rcc;
 1123     if( rcc <= 0 )
 1124         return -1;
 1125 /*
 1126     if( dump_commands && S_C ){
 1127 */
 1128     /*
 1129     if( dump_commands & S_C ){
 1130     */
 1131     if( (dump_commands & S_C) || ccxTOCL ){
 1132         rcc = scanCommands(StoC,AVStr(buf),rcc);
 1133     }
 1134     if( docontrol ){
 1135         docontrol = 0;
 1136         controlcommand(Conn,ToS,FromS);
 1137         if( rcc == 0 )
 1138             return 0;
 1139     }
 1140     if( ToC < 0 )
 1141         return 0;
 1142     if( write(ToC,buf,rcc) <= 0 )
 1143         return -1;
 1144     return rcc;
 1145 }
 1146 static int bidirectional_relay2(Connection *Conn,int fcbsize,int fsbsize)
 1147 {   register int ppid,cpid;
 1148     int omask;
 1149     int total;
 1150     void (*opipe)(int);
 1151     void (*oterm)(int);
 1152     void (*ointr)(int);
 1153 
 1154     CStr(snoop,64);
 1155     int sf;
 1156     if( 0 <= find_CMAP(Conn,"DUMP",AVStr(snoop)) ){
 1157         sf = 0;
 1158         if( strcasestr(snoop,"tosv") ) sf |= C_S|(CtoS<<8);
 1159         if( strcasestr(snoop,"tocl") ) sf |= S_C|(StoC<<8);
 1160         dump_commands |= sf;
 1161     }
 1162 
 1163     Verbose("buffer: CS=%d[%d>%d] SC=%d[%d>%d] (%s)\n",
 1164         fcbsize,FromC,ToS,fsbsize,FromS,ToC,
 1165         "Polling");
 1166 
 1167     omask = sigblock(sigmask(SIGPIPE)|sigmask(SIGTERM));
 1168     opipe = Vsignal(SIGPIPE,sigPIPE);
 1169     oterm = Vsignal(SIGTERM,sigTERM);
 1170     ointr = Vsignal(SIGINT, sigTERM);
 1171 
 1172     if( FromC < 0 ){
 1173         total = telnetSC(Conn,fsbsize,omask);
 1174     }else
 1175     if( FromS < 0 ){
 1176         total = telnetCS(Conn,fcbsize,omask);
 1177     }else{
 1178         int fds[2];
 1179         int rds[2];
 1180         int fi;
 1181         int nready;
 1182         int rcc;
 1183         int count[2];
 1184         int total[2];
 1185 
 1186         fds[0] = FromC;
 1187         fds[1] = FromS;
 1188         count[0] = 0; count[1] = 0;
 1189         total[0] = 0; total[1] = 0;
 1190 
 1191         if( CCXactive(CCX_TOCL) ){
 1192             Bcopy(CCX_TOCL,ccxbTOCL,sizeof(ccxbTOCL));
 1193             ccxTOCL = CCX_TOCL;
 1194         }else   ccxTOCL = 0;
 1195         if( CCXactive(CCX_TOSV) ){
 1196             Bcopy(CCX_TOSV,ccxbTOSV,sizeof(ccxbTOSV));
 1197             ccxTOSV = CCX_TOSV;
 1198         }else   ccxTOSV = 0;
 1199 
 1200         for(;;){
 1201             nready = PollIns(IO_TIMEOUT*1000,2,fds,rds); 
 1202             if( nready <= 0 )
 1203                 break;
 1204 
 1205             if( nready == 0 && errno == 0 || gotOOB(-1) ){
 1206                 int fi,sync;
 1207                 sync = 0;
 1208                 for( fi = 0; fi < 2; fi++ ){
 1209                     if( withOOB(fds[fi]) )
 1210                     if( !withTM(fds[fi]) )
 1211                     sync += relayOOB(fds[fi],fds[(fi+1)%2]);
 1212                 }
 1213                 if( 0 < sync )
 1214                     continue;
 1215             }
 1216             if( 0 < rds[0] ){
 1217                 rcc = relayCS0(Conn,fcbsize);
 1218                 if( rcc < 0 ){
 1219                     sv1log("CS-EOF\n");
 1220                     break;
 1221                 }
 1222                 count[0] += 1;
 1223                 total[0] += rcc;
 1224             }
 1225             if( 0 < rds[1] ){
 1226                 rcc = relaySC0(Conn,fcbsize);
 1227                 if( rcc < 0 ){
 1228                     sv1log("SC-EOF\n");
 1229                     break;
 1230                 }
 1231                 count[1] += 1;
 1232                 total[1] += rcc;
 1233             }
 1234 
 1235         }
 1236         sv1log("CS-RELAY[%d>%d]: %dBytes %dI/O buf=%d\n",
 1237             FromC,ToS,total[0],count[0],fcbsize);
 1238         sv1log("SC-RELAY[%d<%d]: %dBytes %dI/O buf=%d\n",
 1239             ToC,FromS,total[1],count[1],fsbsize);
 1240     }
 1241 
 1242     signal(SIGPIPE,opipe);
 1243     signal(SIGTERM,oterm);
 1244     signal(SIGTERM,ointr);
 1245     sigsetmask(omask);
 1246     return total;
 1247 }
 1248 static int bidirectional_relay(Connection *Conn,int fcbsize,int fsbsize)
 1249 {   register int ppid,cpid;
 1250     int omask;
 1251     int total;
 1252     void (*opipe)(int);
 1253     void (*oterm)(int);
 1254     void (*ointr)(int);
 1255 
 1256     CStr(snoop,64);
 1257     int sf;
 1258     if( 0 <= find_CMAP(Conn,"DUMP",AVStr(snoop)) ){
 1259         sf = 0;
 1260         if( strcasestr(snoop,"tosv") ) sf |= C_S|(CtoS<<8);
 1261         if( strcasestr(snoop,"tocl") ) sf |= S_C|(StoC<<8);
 1262         dump_commands |= sf;
 1263     }
 1264 
 1265     useTHREAD = INHERENT_thread();
 1266 
 1267     Verbose("buffer: CS=%d[%d>%d] SC=%d[%d>%d] (%s)\n",
 1268         fcbsize,FromC,ToS,fsbsize,FromS,ToC,
 1269         useTHREAD?useTHREAD:"FORK");
 1270 
 1271     omask = sigblock(sigmask(SIGPIPE)|sigmask(SIGTERM));
 1272     opipe = Vsignal(SIGPIPE,sigPIPE);
 1273     oterm = Vsignal(SIGTERM,sigTERM);
 1274     ointr = Vsignal(SIGINT, sigTERM);
 1275 
 1276     if( FromC < 0 ){
 1277         total = telnetSC(Conn,fsbsize,omask);
 1278     }else
 1279     if( FromS < 0 ){
 1280         total = telnetCS(Conn,fcbsize,omask);
 1281     }else
 1282     if( useTHREAD ){
 1283         THREAD = 1;
 1284         THREAD_exiting = 0;
 1285         thread_fork(0,STX_tid,"CS1",(IFUNCP)CS1,Conn,fsbsize,omask);
 1286         SC1(Conn,fcbsize,omask /*,ppid*/);
 1287         THREAD = 0;
 1288     }else{
 1289         ppid = getpid();
 1290         if( (cpid = Fork("bidirectiona_relay")) == 0 ){
 1291             total = SC1(Conn,fsbsize,omask);
 1292             signal(SIGTERM,SIG_IGN);
 1293             signal(SIGPIPE,SIG_IGN);
 1294             Kill(ppid,SIGTERM);
 1295             Finish(0);
 1296         }else{
 1297             total = CS1(Conn,fcbsize,omask);
 1298             signal(SIGTERM,SIG_IGN);
 1299             signal(SIGPIPE,SIG_IGN);
 1300             Kill(cpid,SIGTERM);
 1301             wait(0);
 1302         }
 1303     }
 1304 
 1305     signal(SIGPIPE,opipe);
 1306     signal(SIGTERM,oterm);
 1307     signal(SIGTERM,ointr);
 1308     sigsetmask(omask);
 1309     return total;
 1310 }
 1311 
 1312 static void getServersWill(int fromS,FILE *tc)
 1313 {   int rcc;
 1314     CStr(scbuf,4096);
 1315 
 1316     if( PollIn(fromS,1000) ){
 1317         rcc = readTIMEOUT(fromS,AVStr(scbuf),sizeof(scbuf));
 1318         sv1log("######## %d bytes from the server\n",rcc);
 1319         rcc = scanCommands(StoC,AVStr(scbuf),rcc);
 1320         fwrite(scbuf,1,rcc,tc);
 1321         fflush(tc);
 1322     }
 1323 }
 1324 
 1325 static int putXdisplay(FILE *ts,PCStr(display))
 1326 {   JStr(ibuf,128);
 1327     char ch;
 1328     int ii,ij;
 1329     int opt;
 1330 
 1331     if( ServersWill[O_XDISPLOC] != DO && ServersWill[O_ENVIRON] != DO )
 1332         return 0;
 1333 
 1334     if( ServersWill[O_XDISPLOC] == DO )
 1335         opt = O_XDISPLOC;
 1336     else    opt = O_ENVIRON;
 1337 
 1338 if( opt == O_ENVIRON )
 1339     return 0;
 1340 
 1341     ii = 0;
 1342     addIbuf(IAC); addIbuf(WILL); addIbuf(opt);
 1343     addIbuf(IAC); addIbuf(SB);   addIbuf(opt);
 1344     addIbuf(IS);
 1345     if( opt == O_ENVIRON ){
 1346         addIbuf(VAR);
 1347         for( ij = 0; ch = "DISPLAY"[ij]; ij++ ){
 1348             addIbuf(ch);
 1349         }
 1350         addIbuf(VALUE);
 1351     }
 1352     for( ij = 0; ch = display[ij]; ij++ ){
 1353         if( elnumof(ibuf) <= ii+3 )
 1354             break;
 1355         addIbuf(ch);
 1356     }
 1357     addIbuf(IAC); addIbuf(SE);
 1358     ii = scanCommands(DtoC,AVStr(ibuf),ii);
 1359     fwrite(ibuf,1,ii,ts);
 1360     return 1;
 1361 }
 1362 
 1363 static int makeXproxy1(Connection *Conn,PVStr(myname),PCStr(peername),int peerport)
 1364 {   CStr(me,MaxHostNameLen);
 1365     CStr(myhp,MaxHostNameLen);
 1366     int xpid;
 1367 
 1368 /*
 1369 Socks can accept only one X connection ...
 1370 
 1371     if( Conn->sv_viaSocks ){
 1372         CStr(rhost,64);
 1373         int rport,rsock;
 1374 
 1375         rsock = bindViaSocks(Conn,DST_HOST,DST_PORT,rhost,&rport);
 1376         sv1log("#### sock=%d host=%s port=%d\n",rsock,rhost,rport);
 1377         close(sock);
 1378     }
 1379 */
 1380 
 1381     ClientIF_HP(Conn,AVStr(myhp));
 1382     sprintf(me,"%s://%s/",DFLT_PROTO,myhp);
 1383     xpid = makeXproxy(Conn,AVStr(Xproxy),Xdisplay,AVStr(myname),peername,me,0);
 1384 
 1385     /* if SRCIF="proxyDisplay:*:X" is given */{
 1386         CStr(xdisp,MaxHostNameLen);
 1387         const char *dp;
 1388         int xport;
 1389 
 1390         if( SRCIFfor(Conn,"X",peername,peerport,AVStr(xdisp),&xport) ){
 1391             if( dp = strchr(Xproxy,':') ){
 1392                 sv1log("#### Xproxy = %s (%s)\n",xdisp,Xproxy);
 1393                 Strrplc(AVStr(Xproxy),dp-Xproxy,xdisp);
 1394             }
 1395         }
 1396     }
 1397 
 1398     sv1log("#### Xproxy[%d]: %s <- %s <- %s\n",
 1399         xpid,Xdisplay,Xproxy,peername);
 1400     return xpid;
 1401 }
 1402 
 1403 static void proxy_telnet(Connection *Conn)
 1404 {   FILE *tc,*fc,*ts;
 1405     CStr(comline,256);
 1406     CStr(cbuf,256);
 1407     CStr(command,256);
 1408     CStr(hostport,MaxHostNameLen);
 1409     CStr(serv,MaxHostNameLen);
 1410     CStr(clnt,MaxHostNameLen);
 1411     const char *addr;
 1412     CStr(auth,256);
 1413     CStr(auser,256);
 1414     CStr(ahost,MaxHostNameLen);
 1415     const char *iuser;
 1416     int port;
 1417     int csize;
 1418     int ns,ncom;
 1419     const char *mount_opts;
 1420     UTag Tcomline,Tcbuf;
 1421 
 1422     tc = fdopen(ToC,"w");
 1423     fc = fdopen(ToC,"r");
 1424 
 1425     Xpid = 0;
 1426     Xdisplay[0] = 0;
 1427     dump_commands |= C_S; /* to check WILL/WONT TERMTYPE ... */
 1428 
 1429     setQStr(Tcomline.ut_addr,comline,sizeof(comline));
 1430     Tcomline.ut_size = sizeof(comline);
 1431     setQStr(Tcbuf.ut_addr,cbuf,sizeof(cbuf));
 1432     Tcbuf.ut_size = sizeof(cbuf);
 1433 
 1434     ncom = 0;
 1435     for(ns = 0;;ns++){
 1436         if( get_hostname(Conn,fc,tc,&Tcomline,&Tcbuf,ncom) == EOF ){
 1437             sv1log("EOF from the client\n");
 1438             break;
 1439         }
 1440         csize = Tcbuf.ut_leng;
 1441         ncom++;
 1442 
 1443         if( comline[0] == 0 ){
 1444             sv1log("EMPTY line for QUIT\n");
 1445             break;
 1446         }
 1447 
 1448         if( mount_opts = CTX_mount_url_to(Conn,NULL,"GET",AVStr(comline)) ){
 1449             if( strncasecmp(comline,"telnet://",9) == 0 ){
 1450                 const char *dp;
 1451                 wordscanY(comline+9,AVStr(comline),sizeof(comline),"^/? \t\r\n");
 1452                 if( dp = strchr(comline,':') )
 1453                     *(char*)dp = ' '; /**/
 1454             }
 1455         }
 1456 
 1457         command[0] = hostport[0] = 0;
 1458         Xsscanf(comline,"%s %[^\r\n]",AVStr(command),AVStr(hostport));
 1459         if( streq(command,"q") || streq(command,"quit") || streq(command,"exit") ){
 1460             sv1log("[%s] command from the client\n",command);
 1461             break;
 1462         }else
 1463         if( streq(command,"help") || streq(command,"?") ){
 1464             putHELP(Conn,tc);
 1465             fflush(tc);
 1466             continue;
 1467         }else
 1468         if( streq(command,"x") || streq(command,"x-gw") ){
 1469             CStr(host,128);
 1470             int port;
 1471 
 1472             sv1log("TIS compati. proxy-telnet with X-proxy\n");
 1473             host[0] = 0;
 1474             if( Xsscanf(hostport,"%[^:]:%d",AVStr(host),&port) != 2 ){
 1475                 if( host[0] == 0 )
 1476                     getpeerNAME(FromC,AVStr(hostport));
 1477                 strcat(hostport,":0");
 1478                 fprintf(tc,"####[regarded as] x-gw %s\r\n",hostport);
 1479                 fflush(tc);
 1480             }
 1481             strcpy(Xdisplay,hostport);
 1482             continue;
 1483         }else
 1484         if( streq(command,"a") || streq(command,"accept") ){
 1485             CStr(myname,MaxHostNameLen);
 1486 
 1487             if( hostport[0] == 0 ){
 1488                 fprintf(tc,"???? Usage: accept hostname\r\n");
 1489                 fflush(tc);
 1490                 continue;
 1491             }
 1492             hostIFfor(hostport,AVStr(myname));
 1493             sv1log("myname = %s\n",myname);
 1494             if( myname[0] == 0 ){
 1495                 fprintf(tc,"???? cannot find a route to %s\r\n",hostport);
 1496                 fflush(tc);
 1497                 continue;
 1498             }
 1499 
 1500             if( 0 < Xpid )
 1501                 Kill(Xpid,SIGTERM);
 1502 
 1503             Xpid = makeXproxy1(Conn,AVStr(myname),hostport,0);
 1504             fprintf(tc,"####[X-Proxy] accept host '%s'\r\n",hostport);
 1505             fprintf(tc,"####[started] use DISPLAY '%s'\r\n",Xproxy);
 1506             fflush(tc);
 1507             continue;
 1508         }else
 1509         if( streq(command,"j") ){
 1510             CStr(stat,256);
 1511             CCXcreate("*",hostport,CCX_TOCL);
 1512             if( CCXactive(CCX_TOCL) ){
 1513                 Bcopy(CCX_TOCL,ccxbTOCL,sizeof(ccxbTOCL));
 1514                 ccxTOCL = CCX_TOCL;
 1515             }else   ccxTOCL = 0;
 1516 
 1517             global_setCCX(Conn,AVStr(hostport),AVStr(stat));
 1518             fprintf(tc,"-- charcode conversion [%s]%s\r\n",
 1519                 hostport,stat);
 1520             fflush(tc);
 1521             continue;
 1522         }else
 1523         if( streq(command,"t") || streq(command,"telnet")
 1524          || streq(command,"c") || streq(command,"connect") ){
 1525             sv1log("TIS compati. proxy-telnet\n");
 1526         }else{
 1527             strcpy(hostport,comline);
 1528         }
 1529         serv[0] = 0;
 1530         port = DFLT_PORT;
 1531         if( strneq(hostport,"-ssh.",5) ){
 1532             strcpy(REAL_PROTO,"ssh");
 1533             ovstrcpy(hostport,hostport+5);
 1534             REAL_PORT = 22;
 1535             port = 22;
 1536         }
 1537         if( strstr(hostport,"@-ssh.") ){
 1538             IStr(auth,128);
 1539             const char *dp;
 1540             dp = wordScanY(hostport,auth,"^@");
 1541             if( strneq(dp,"@-ssh.",6) ){
 1542                 strcpy(REAL_PROTO,"ssh");
 1543                 sprintf(hostport,"%s@%s",auth,dp+6);
 1544                 REAL_PORT = 22;
 1545                 port = 22;
 1546             }
 1547         }
 1548         Xsscanf(hostport,"%s %d",AVStr(serv),&port);
 1549 
 1550         if( serv[0] == 0 ){
 1551             sv1log("empty command for QUIT form the client\n");
 1552             break;
 1553         }
 1554 
 1555         auser[0] = ahost[0] = 0;
 1556         if( doAUTH0(Conn,fc,tc,"telnet",serv,port,AVStr(auser),AVStr(ahost),(iFUNCP)xgetline,NULL) == EOF )
 1557             break;
 1558         /* DFLT_HOST and DFLT_PORT is set as side effect in doAUTH0() */
 1559 
 1560         if( Xdisplay[0] ){
 1561             /* suppress direct connection by MASTER */
 1562             strcpy(D_SERVER,"telnet://-/");
 1563         }
 1564 
 1565         if( iuser = getClientHostPortUser(Conn,AVStr(clnt),NULL) )
 1566             Xsprintf(TVStr(clnt),"(%s)",iuser);
 1567         auth[0] = 0;
 1568         if( auser[0] )
 1569             sprintf(auth,"<%s@%s>",auser,ahost);
 1570 
 1571         sv1log("TELNET LOGIN FROM %s%s TO %s\n",clnt,auth,serv);
 1572 fputLog(Conn,"Login","TELNET; from=%s%s; to=%s\n",clnt,auth,serv);
 1573 
 1574         if( (addr = gethostaddr(serv)) == NULL )
 1575             addr = "unknown host";
 1576 
 1577         if( mount_opts == NULL )
 1578         fprintf(tc,"-- Trying %s [%s:%d] ...\r\n",
 1579             serv,addr,DST_PORT);
 1580         fflush(tc);
 1581 
 1582         ConnError = 0;
 1583 
 1584         if( streq(DST_PROTO,"ssh") || DST_PORT == 22 ){
 1585             int tos;
 1586             IStr(user,128);
 1587             if( strchr(serv,'@') ){
 1588                 wordScanY(serv,user,"^@");
 1589             }
 1590             tos = connectToSsh(Conn,DST_HOST,DST_PORT,user,"");
 1591             sv1log("SSHGW[%d] %s:%d\n",tos,DST_HOST,DST_PORT);
 1592             if( tos != -1 ){
 1593                 ToS = FromS = tos;
 1594                 bidirectional_relay2(Conn,0x2000,0x2000);
 1595             }else{
 1596                 cantConnMessage(Conn,serv,tc);
 1597                 fflush(tc);
 1598             }
 1599         }else
 1600         if( connect_to_serv(Conn,FromC,ToC,0) < 0 ){
 1601             cantConnMessage(Conn,serv,tc);
 1602             fflush(tc);
 1603         }else{
 1604             ConnectedMessage(Conn,serv,tc,comline);
 1605             clearServersWill();
 1606             getServersWill(FromS,tc);
 1607 
 1608             ts = fdopen(dup(ToS),"w");
 1609             if( csize ){
 1610                 Verbose("C-S %d bytes\n",csize);
 1611                 fwrite(cbuf,1,csize,ts);
 1612                 fflush(ts);
 1613             }
 1614             if( Xdisplay[0] ){
 1615                 CStr(myname,MaxHostNameLen);
 1616                 CStr(peername,MaxHostNameLen);
 1617                 int port;
 1618 
 1619                 gethostNAME(ToS,AVStr(myname));
 1620                 /*
 1621                 getpeerNAME(ToS,peername);
 1622                 Xpid = makeXproxy1(Conn,myname,peername);
 1623                 */
 1624                 port = getpeerNAME(ToS,AVStr(peername));
 1625                 Xpid = makeXproxy1(Conn,AVStr(myname),peername,port);
 1626             }
 1627 /*
 1628             if( toProxy ){
 1629 */
 1630             if( toProxy || toMaster && Xdisplay[0] ){
 1631                 putIAC(ts,DO,O_ECHO); /* enable server ECHO */
 1632 
 1633                 sv1log("#### connected to Proxy telnet\n");
 1634                 /* fprintf(ts,"telnet %s\r\n",serv); */
 1635                 if( Xdisplay[0] ){
 1636                     fprintf(ts,"x-gw %s\r\n",Xproxy);
 1637                     fflush(ts);
 1638                 }
 1639                 fprintf(ts,"%s",comline);
 1640                 if( toMaster )
 1641                     fprintf(ts,"\r\n");
 1642             }else{
 1643                 if( Xdisplay[0] ){
 1644                     int set;
 1645 
 1646                     set = putXdisplay(ts,Xproxy);
 1647                     fprintf(tc,"####[set %s] setenv DISPLAY %s\r\n\r\n",
 1648                         set?"automatically":"manually",Xproxy);
 1649                     fflush(tc);
 1650                 }
 1651             }
 1652 
 1653             /* Some telnet client (at least SunOS's one)
 1654              * does not repeat WILL/WONT twice.
 1655              */
 1656             if( numServersWill() != 0 ){ /* is Telnet server */
 1657             relayClientsWills(ts);
 1658             }else{
 1659             /* how the client can be reset to initial state ? */
 1660                 /*
 1661                 sv1log("#### force kludge line mode ...\n");
 1662                 putIAC(tc,WONT,O_ECHO);
 1663                 putIAC(tc,WILL,O_SUPAHEAD);
 1664                 putIAC(tc,DO,O_TIMING);
 1665                 ... skip until TIMING-MARK ...
 1666                 */
 1667             }
 1668 
 1669             fclose(ts);
 1670             /*
 1671             bidirectional_relay(Conn,1024,1024);
 1672             */
 1673             bidirectional_relay2(Conn,1024,1024);
 1674             fprintf(tc,"\r\n");
 1675 
 1676             if( 0 < Xpid ){
 1677                 Kill(Xpid,SIGTERM);
 1678                 Xpid = 0;
 1679             }
 1680             ncom = 0;
 1681             if( ImMaster ){
 1682                 break;
 1683             }
 1684         }
 1685     }
 1686     if( 0 < Xpid ){
 1687         Kill(Xpid,SIGTERM);
 1688         Xpid = 0;
 1689     }
 1690 }
 1691 
 1692 static void AsServer(Connection *Conn)
 1693 {   FILE *fc,*tc,*fp;
 1694     CStr(uname,128);
 1695     CStr(myhost,128);
 1696     CStr(user,128);
 1697     CStr(pass,128);
 1698     JStr(ibuf,128);
 1699     int ii;
 1700     CStr(line,1024);
 1701     const char *dp;
 1702     CStr(com,128);
 1703     const char *arg;
 1704     CStr(pwd,1024);
 1705 
 1706     ClientIF_name(Conn,FromC,AVStr(myhost));
 1707     Uname(AVStr(uname));
 1708 
 1709     fc = fdopen(FromC,"r");
 1710     tc = fdopen(ToC,"w");
 1711     fputs("\r\n",tc);
 1712     fprintf(tc,"%s (%s) Telnet-%s\r\n",uname,myhost,DELEGATE_version());
 1713     fputs("\r\n",tc);
 1714 
 1715     ii = 0;
 1716     addIbuf(IAC); addIbuf(WONT); addIbuf(O_ECHO);
 1717     addIbuf(IAC); addIbuf(DO);   addIbuf(O_ECHO);
 1718     fwrite(ibuf,1,ii,tc);
 1719     fflush(tc);
 1720     scanCommands(DtoC,AVStr(ibuf),ii);
 1721     ii = read(fileno(fc),line,sizeof(line));
 1722     scanCommands(CtoD,AVStr(ibuf),ii);
 1723 
 1724     fputs("UserName: ",tc);
 1725     fflush(tc);
 1726     if( fgets(line,sizeof(line),fc) == NULL )
 1727         goto EXIT;
 1728     lineScan(line,user);
 1729     sv1log("USER: %s\n",user);
 1730 
 1731     ii = 0;
 1732     addIbuf(IAC); addIbuf(WILL); addIbuf(O_ECHO);
 1733     addIbuf(IAC); addIbuf(DONT); addIbuf(O_ECHO);
 1734     fwrite(ibuf,1,ii,tc);
 1735     fflush(tc);
 1736     scanCommands(DtoC,AVStr(ibuf),ii);
 1737     ii = read(fileno(fc),line,sizeof(line));
 1738     scanCommands(CtoD,AVStr(ibuf),ii);
 1739 
 1740     fputs("PassWord: ",tc);
 1741     fflush(tc);
 1742     if( fgets(line,sizeof(line),fc) == NULL )
 1743         goto EXIT;
 1744     fputs("\r\n",tc);
 1745     lineScan(line,pass);
 1746     if( Authenticate(Conn,"localhost",user,pass,"/") < 0 )
 1747         goto EXIT;
 1748 
 1749     ii = 0;
 1750     addIbuf(IAC); addIbuf(WONT); addIbuf(O_ECHO);
 1751     addIbuf(IAC); addIbuf(DO);   addIbuf(O_ECHO);
 1752     fwrite(ibuf,1,ii,tc);
 1753     fflush(tc);
 1754     scanCommands(DtoC,AVStr(ibuf),ii);
 1755     ii = read(fileno(fc),line,sizeof(line));
 1756     scanCommands(CtoD,AVStr(ibuf),ii);
 1757 
 1758     for(;;){
 1759         IGNRETS getcwd(pwd,sizeof(pwd));
 1760         fprintf(tc,"%s> ",pwd);
 1761         fflush(tc);
 1762 
 1763         if( fgets(line,sizeof(line),fc) == NULL )
 1764             break;
 1765         sv1log("%s> %s",pwd,line);
 1766         if( dp = strpbrk(line,"\r\n") )
 1767             truncVStr(dp);
 1768         arg = wordScan(line,com);
 1769         while( *arg == ' ' || *arg == '\t' )
 1770             arg++;
 1771         if( strcmp(com,"cd") == 0 || strcmp(com,"chdir") == 0 ){
 1772             IGNRETZ chdir(arg);
 1773             continue;
 1774         }
 1775         if( strcmp(com,"exit") == 0 || strcmp(com,"quit") == 0 )
 1776             break;
 1777 
 1778         fp = popen(line,"r");
 1779         while( fgets(line,sizeof(line),fp) != NULL ){
 1780             if( dp = strpbrk(line,"\r\n") )
 1781                 truncVStr(dp);
 1782             fputs(line,tc);
 1783             fputs("\r\n",tc);
 1784         }
 1785 
 1786         fflush(tc);
 1787         pclose(fp);
 1788     }
 1789 EXIT:
 1790     fclose(tc);
 1791     fclose(fc);
 1792 }
 1793 
 1794 /*
 1795  * relaying to non-Telnet arbitrary port is enabled with REMITTABLE="telnet"
 1796  */
 1797 static int telnetonly(Connection *Conn)
 1798 {   int relayany;
 1799     Port sv;
 1800 
 1801     sv = Conn->sv;
 1802     strcpy(REAL_HOST,"-");
 1803     strcpy(REAL_PROTO,"telnet");
 1804     REAL_PORT = 99999;
 1805     relayany = service_permitted2(Conn,"telnet",1);
 1806     Conn->sv = sv;
 1807     return !relayany;
 1808 }
 1809 
 1810 static struct { 
 1811     char *ne_USER;
 1812 } clenv;
 1813 static char doNewEnviron[] = {IAC,SB,O_NENVIRON,1,IAC,SE};
 1814 static int getUSERenv(FILE *fc,FILE *tc,AuthInfo *au){
 1815     int rcc,nrcc;
 1816     int timeout = 15*1000;
 1817     IStr(buf,1024+1);
 1818     IStr(nbuf,1024+1);
 1819     const unsigned char *up = (const unsigned char*)buf;
 1820     const unsigned char *vp = (const unsigned char*)nbuf;
 1821     IStr(userb,MaxHostNameLen);
 1822 
 1823     putIAC(tc,DO,O_NENVIRON); fflush(tc); /* ask USER */
 1824     IGNRETP write(fileno(tc),doNewEnviron,sizeof(doNewEnviron));
 1825 
 1826     for(;;){
 1827         if( PollIn(fileno(fc),timeout) == 0 ){
 1828             break;
 1829         }
 1830         rcc = read(fileno(fc),buf,sizeof(buf)-1);
 1831         if( 0 < rcc ){
 1832             Bcopy(buf,nbuf,rcc);
 1833             nrcc = scanCommands(CtoS,AVStr(nbuf),rcc);
 1834         }
 1835         if( 3 <= rcc && up[0] == 0xFF ){
 1836             if( up[1] == WILL && up[2] == O_AUTH ){
 1837                 putIAC(tc,DONT,O_AUTH);
 1838                 fflush(tc);
 1839             }
 1840             if( up[1] == WILL || up[1] == SB )
 1841             if( up[2] == O_NENVIRON ){
 1842                 if( clenv.ne_USER ){
 1843                 strcpy(userb,clenv.ne_USER);
 1844                 if( strpbrk(userb,":@") ){
 1845                     bzero(au,sizeof(AuthInfo));
 1846                     decomp_siteX("sftp",userb,au);
 1847  sv1log("--USER[%s]:%d @ HOST[%s]:%d\n",
 1848  au->i_user,istrlen(au->i_pass),au->i_Host,au->i_Port);
 1849                 }else{
 1850                     bzero(au,sizeof(AuthInfo));
 1851                     strcpy(au->i_user,clenv.ne_USER);
 1852                 }
 1853                 return 1;
 1854                 }else{
 1855                 timeout = 300;
 1856                 }
 1857             }
 1858         }
 1859     }
 1860     return 0;
 1861 }
 1862 int service_telnet1(Connection *Conn);
 1863 int service_telnet(Connection *Conn)
 1864 {
 1865     int rcode;
 1866     rcode = service_telnet1(Conn);
 1867     return rcode;
 1868 }
 1869 int service_telnet1(Connection *Conn)
 1870 {
 1871     if( telnetEnv == 0 )
 1872         telnetEnv = NewStruct(TelnetEnv);
 1873 
 1874     if( LOG_VERBOSE )
 1875         dump_commands = C_S | S_C;
 1876 
 1877     if( strncaseeq(DFLT_HOST,"-ssh",4)
 1878      && (DFLT_HOST[4] == '.' || DFLT_HOST[4] == 0) ){
 1879         ovstrcpy(DFLT_PROTO,"ssh");
 1880         if( DFLT_HOST[4] == 0 )
 1881             strcpy(DFLT_HOST,"-");
 1882         else    ovstrcpy(DFLT_HOST,DFLT_HOST+5);
 1883         DFLT_PORT = 22;
 1884         sv1log(">>>> ssh://%s:%d\n",DFLT_HOST,DFLT_PORT);
 1885     }
 1886 
 1887     if( BORN_SPECIALIST )
 1888     if( strcmp(iSERVER_PROTO,"telnet") == 0 )
 1889     if( strcmp(DFLT_HOST,"-.-") == 0 ){
 1890         AsServer(Conn);
 1891         return 0;
 1892     }
 1893 
 1894     /* -l user[@host] */
 1895     if( strcaseeq(DFLT_PROTO,"ssh") )
 1896     if( DFLT_AUTH == 0 ){
 1897         FILE *tc = fdopen(dup(ToC),"w");
 1898         FILE *fc = fdopen(dup(FromC),"r");
 1899         AuthInfo au;
 1900         if( getUSERenv(fc,tc,&au) ){
 1901             if( au.i_Host[0] ){
 1902                 strcpy(DFLT_HOST,au.i_Host);
 1903             }
 1904             if( DFLT_AUTH == 0 )
 1905                 DFLT_AUTH = (AuthInfo*)malloc(sizeof(AuthInfo));
 1906             *DFLT_AUTH = au;
 1907         }
 1908         fclose(tc);
 1909         fclose(fc);
 1910     }
 1911 
 1912     if( isMYSELF(DFLT_HOST) ){
 1913         ImProxy = 1;
 1914         if( PollIn(FromC,10) == 0 ){
 1915             /* disable local echo in telnet client on Win95 series,
 1916              * but don't disable local echo necessary for
 1917              * non-Telnet protocols.
 1918              */
 1919             if( telnetonly(Conn) ){
 1920                 IGNRETP write(ToC,NOPstr,2);
 1921                 scanCommands(DtoC,CVStr(NOPstr),2);
 1922             }
 1923         }
 1924         proxy_telnet(Conn);
 1925         return 0;
 1926     }
 1927 
 1928     if( CTX_auth(Conn,NULL,NULL) ) /* with AUTHORIZER */
 1929     {
 1930         FILE *fc = fdopen(FromC,"r");
 1931         FILE *tc = fdopen(ToC,"w");
 1932         CStr(auser,256);
 1933         CStr(ahost,MaxHostNameLen);
 1934         int dport = DFLT_PORT;
 1935 
 1936         IGNRETP write(ToC,NOPstr,2);
 1937         scanCommands(DtoC,CVStr(NOPstr),2);
 1938 
 1939         auser[0] = ahost[0] = 0;
 1940         DFLT_PORT = 0; /* to escape "already authorized" */
 1941         if( doAUTH0(Conn,fc,tc,"telnet",DST_HOST,dport,AVStr(auser),AVStr(ahost),(iFUNCP)xgetline,NULL) == EOF ){
 1942             return -1;
 1943         }
 1944         DFLT_PORT = dport;
 1945         fcloseFILE(fc);
 1946         fcloseFILE(tc);
 1947     }
 1948     if( streq(DST_PROTO,"ssh") || DST_PORT == 22 ){
 1949         int tos;
 1950         IStr(user,128);
 1951         IStr(pass,128);
 1952         strcpy(user,DFLT_USER);
 1953         strcpy(pass,DFLT_PASS);
 1954         /* see MYAUTH? */
 1955 
 1956         tos = connectToSsh(Conn,DST_HOST,DST_PORT,user,pass);
 1957         sv1log("SSHGW[%d] %s:%d\n",tos,DST_HOST,DST_PORT);
 1958         if( tos != -1 ){
 1959             ToS = FromS = tos;
 1960         }
 1961     }
 1962     if( ToC < 0 || ToS < 0 )
 1963         connect_to_serv(Conn,FromC,ToC,0);
 1964 
 1965     if( ToC < 0 || ToS < 0 )
 1966         return -1;
 1967 
 1968     /* Telnet clients may not start any negotiation when the
 1969      * target port is not the standard telnet port, because the server
 1970      * could be a non telnet server.  Telnet relay server is normally
 1971      * bound to non standard telnet port, thus initiating Telnet
 1972      * negotiation in the relay will be helpful for clients to notice
 1973      * that he is connected with telnet server.
 1974      */
 1975     if( PollIn(FromS,10) == 0 ){
 1976         IGNRETP write(ToC,NOPstr,2);
 1977         scanCommands(DtoC,CVStr(NOPstr),2);
 1978     }
 1979     /*
 1980     {   CStr(buf,3);
 1981         buf[0] = IAC; buf[1] = WILL; buf[2] = O_SUPAHEAD;
 1982         write(ToC,buf,3);
 1983         scanCommands(DtoC,AVStr(buf),3);
 1984     }
 1985     */
 1986     /*
 1987     bidirectional_relay(Conn,0x2000,0x2000);
 1988     */
 1989     bidirectional_relay2(Conn,0x2000,0x2000);
 1990     return 0;
 1991 }
 1992 
 1993 
 1994 /*
 1995  *  parse and rewrite telnet protocol
 1996  */
 1997 
 1998 #define EOA     0x100
 1999 #define START       0x101
 2000 #define LOOP        0x102
 2001 #define STR     0x103
 2002 #define RW_TERM     0x104
 2003 #define RW_XDISPLOC 0x105
 2004 #define RW_ENVIRON  0x106
 2005 #define RW_NENVIRON 0x107
 2006 
 2007 static short Trans[][32] = {
 2008  { IAC, SB, O_ENVIRON,  IS, START, VALUE, STR, VAR, STR, RW_ENVIRON, LOOP },
 2009  { IAC, SB, O_NENVIRON, IS, START, VAR, STR, VALUE, STR, RW_NENVIRON, LOOP },
 2010  { IAC, SB, O_TERMTYPE, IS, START, STR, RW_TERM,    EOA },
 2011  { IAC, SB, O_XDISPLOC, IS, START, STR, RW_XDISPLOC, EOA },
 2012  0
 2013 };
 2014 
 2015 /*
 2016 static int getString(PCStr(buf),int cc,int c0,PVStr(str))
 2017 */
 2018 static int getString(PCStr(buf),int cc,int c0,int cx,PVStr(str))
 2019 {   int ci,si;
 2020     unsigned char ch;
 2021 
 2022     si = 0;
 2023     for(ci = c0; ci < cc; ci++ ){
 2024         assertVStr(str,str+si+1);
 2025         ch = buf[ci];
 2026         if( ch == cx ){
 2027             /* the next literal char. in Trans[] */
 2028             if( ch == VAR || ch == VALUE )
 2029                 break;
 2030         }
 2031         setVStrElemInc(str,si,ch); /**/
 2032         if( ch < ' ' || 0x7F < ch )
 2033             break;
 2034     }
 2035     setVStrEnd(str,si);
 2036     return ci;
 2037 }
 2038 static int removeString(PVStr(buf),int cc,int from,int to)
 2039 {   int len,ci;
 2040 
 2041     len = to - from;
 2042     for( ci = from; ci < cc; ci++ )
 2043         setVStrElem(buf,ci,buf[ci+len]); /**/
 2044     return -len;
 2045 }
 2046 static int replaceString(PVStr(buf),int cc,int from,int to,PCStr(xnew))
 2047 {   int len,nlen,inc;
 2048     int ci;
 2049 
 2050     len = to - from;
 2051     nlen = strlen(xnew);
 2052     inc = nlen - len;
 2053 
 2054     if( 0 < inc ){
 2055         for( ci = cc-1; to <= ci; ci-- ){
 2056             assertVStr(buf,buf+ci+inc);
 2057             setVStrElem(buf,ci+inc,buf[ci]); /**/
 2058         }
 2059     }else{
 2060         for( ci = to;   ci <  cc; ci++ ){
 2061             assertVStr(buf,buf+ci+inc);
 2062             setVStrElem(buf,ci+inc,buf[ci]); /**/
 2063         }
 2064     }
 2065     for( ci = 0; ci < nlen; ci++ ){
 2066         assertVStr(buf,buf+from+ci);
 2067         setVStrElem(buf,from+ci,xnew[ci]); /**/
 2068     }
 2069     return inc;
 2070 }
 2071 
 2072 static int rewriteTelnet(int direction,PVStr(buf),int cc)
 2073 {   int si,sji,sjp;
 2074     int chp;
 2075     unsigned char chi;
 2076     short *sb1;
 2077     int inc = 0,inc1;
 2078     ACStr(str,4,2048);
 2079     int strx;
 2080     int start_sjp;
 2081     int start_sji;
 2082     int str_sji = 0;
 2083 
 2084     for( si = 0; ; si++ ){
 2085         sb1 = Trans[si];
 2086         if( sb1[0] == 0 )
 2087         break;
 2088 
 2089         sji = 0;
 2090         strx = 0;
 2091         for( sjp = 0;; sjp++ ){
 2092             chp = sb1[sjp];
 2093         chi = buf[sji];
 2094 
 2095         switch( chp ){
 2096           case EOA:
 2097             goto NEXTALT;
 2098 
 2099           case START:
 2100             strx = 0;
 2101             start_sji = sji;
 2102             start_sjp = sjp;
 2103             break;
 2104 
 2105           case LOOP:
 2106             sjp = start_sjp -1;
 2107             break;
 2108 
 2109           case STR:
 2110             str_sji = sji; /* the beginning of STR */
 2111             sji = getString(buf,cc,sji,sb1[sjp+1],EVStr(str[strx]));
 2112             /*
 2113             sji = getString(buf,cc,sji,EVStr(str[strx]));
 2114             */
 2115             strx++;
 2116             break;
 2117 
 2118           case RW_XDISPLOC:
 2119             if( direction == CtoS )
 2120             if( Xproxy[0] && !streq(Xproxy,str[0]) ){
 2121                 sv1log("DISPLAY [%s] -> [%s]\n",str[0],Xproxy);
 2122                 inc1 = replaceString(AVStr(buf),cc,start_sji,sji,
 2123                     Xproxy);
 2124                 inc += inc1;
 2125                 sji += inc1;
 2126             }
 2127             break;
 2128 
 2129           case RW_ENVIRON:
 2130             sv1log("ENVIRON [%s=%s]\n",str[0],str[1]);
 2131 
 2132             if( streq(str[0],"DISPLAY") )
 2133             if( direction == CtoS )
 2134             if( Xproxy[0] && !streq(Xproxy,str[1]) ){
 2135                 inc1 = replaceString(AVStr(buf),cc,start_sji,sji,
 2136                     Xproxy);
 2137                 inc += inc1;
 2138                 sji += inc1;
 2139             }
 2140             break;
 2141 
 2142           case RW_NENVIRON: /* rewriting VALUE STR for DISPLAY */
 2143             sv1log("NEW-ENVIRON [%s=%s]\n",str[0],str[1]);
 2144             if( streq(str[0],"USER") )
 2145             if( direction == CtoS || direction == CtoD )
 2146             {
 2147                 clenv.ne_USER = stralloc(str[1]);
 2148                 clenv.ne_USER[strlen(str[1])-1] = 0;/*bug*/
 2149             }
 2150             if( streq(str[0],"DISPLAY") )
 2151             if( direction == CtoS )
 2152             if( Xproxy[0] && !streq(Xproxy,str[1]) ){
 2153                 sv1log("DISPLAY [%s] -> [%s]\n",str[1],Xproxy);
 2154                 inc1 = replaceString(AVStr(buf),cc,str_sji,sji,
 2155                     Xproxy);
 2156                 inc += inc1;
 2157                 sji += inc1;
 2158             }
 2159             break;
 2160 
 2161           default:
 2162             if( chp != chi )
 2163                 goto NEXTALT;
 2164             sji++;
 2165         }
 2166         } NEXTALT:;
 2167     }
 2168     return inc;
 2169 }
 2170 
 2171 /*////////////////////////////////////////////////////////////////////////
 2172 Copyright (c) 2007 National Institute of Advanced Industrial Science and Technology (AIST)
 2173 AIST-Product-ID: 2000-ETL-198715-01, H14PRO-049, H15PRO-165, H18PRO-443
 2174 
 2175 Permission to use this material for noncommercial and/or evaluation
 2176 purpose, copy this material for your own use,
 2177 without fee, is hereby granted
 2178 provided that the above copyright notice and this permission notice
 2179 appear in all copies.
 2180 AIST MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY OF THIS
 2181 MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS", WITHOUT ANY EXPRESS
 2182 OR IMPLIED WARRANTIES.
 2183 //////////////////////////////////////////////////////////////////////////
 2184 Content-Type:   program/C; charset=US-ASCII
 2185 Program:    telnetgw.c
 2186 Author:     Yutaka Sato <y.sato@delegate.org>
 2187 Description:
 2188 History:
 2189     071126  created
 2190 //////////////////////////////////////////////////////////////////////#*/
 2191 #if defined(__KURO_BOX__)
 2192 #include <pty.h>
 2193 #define Forkpty(pty,name) forkpty(pty,name,NULL,NULL)
 2194 #else
 2195 int Forkpty(int *pty,char *name);
 2196 #endif
 2197 int Stty(int fd,const char *mode);
 2198 int Gtty(int fd,const char *mode);
 2199 
 2200 char *fgetsTO(PVStr(b),int z,FILE *f,int t1,int t2);
 2201 FILE *fopenTO(PCStr(path),PCStr(mode),int timeout);
 2202 static const char *ssh_com = "ssh -p %d %s@%s";
 2203 static int sshPty = -1;
 2204 static int sshPid = 0;
 2205 static int sshThread = 0;
 2206 static int badPass = 0;
 2207 
 2208 static void relay_resp(FILE *fs,FILE *tc,int timeout,PVStr(resp),PCStr(com)){
 2209     CStr(res,1024);
 2210     refQStr(rp,resp);
 2211     refQStr(dp,res);
 2212     int tout0;
 2213     int tout;
 2214 
 2215     if( 0 < timeout )
 2216         tout0 = tout = timeout;
 2217     else    tout0 = tout = 3*1000;
 2218 
 2219     if( resp )
 2220         setVStrEnd(resp,0);
 2221     if( fPollIn(fs,tout) <= 0 ){
 2222         sv1log("--SSH >> response timeout [%s]%d\n",com,tout);
 2223         return;
 2224     }
 2225 
 2226     for(;;){
 2227         if( 0 < fgetsBuffered(AVStr(res),sizeof(res),fs) ){
 2228         }else
 2229         if( fgetsTO(AVStr(res),sizeof(res),fs,tout,1) == NULL ){
 2230             break;
 2231         }
 2232         if( strchr(res,'\n') )
 2233             sv1log("--SSH >>(%s) %s",com,res);
 2234         else    sv1log("--SSH >>(%s/NO-NL)[%s]\n",com,res);
 2235         if( dp = strpbrk(res,"\r\n") )
 2236             strcpy(dp,"\r\n");
 2237         if( streq(res,"\r\n") || streq(res,"\n") ){
 2238             /* ignore empty lines */
 2239         }else
 2240         if( rp ){
 2241             strcpy(rp,res);
 2242             rp += strlen(rp);
 2243         }
 2244         tout = 1;
 2245     }
 2246 }
 2247 static void closeAll(){
 2248     int fi;
 2249     int rcode;
 2250     int lfd = curLogFd();
 2251     for( fi = 3; fi < 64; fi++ ){
 2252         if( fi == lfd )
 2253             continue;
 2254         rcode = close(fi);
 2255     }
 2256 }
 2257 static int badpass(PCStr(resp)){
 2258     if( badPass ){
 2259         if( resp[0] == '\r'
 2260          || resp[0] == '\n'
 2261         ){
 2262             sv1log("--SSH: SKIP in BAD PASS [%s]\n",resp);
 2263             return badPass;
 2264         }
 2265     }
 2266     if( strtailstr(resp,"Password:")
 2267      || strtailstr(resp,"Password: ")
 2268      || strtailstr(resp,"password: ")
 2269      || strstr(resp,"Enter passphrase for key")
 2270     ){
 2271         sv1log("--SSH: BAD PASS [%s]\n",resp);
 2272         badPass = 1;
 2273     }else{
 2274         sv1log("--SSH: PASS OK [%s]\n",resp);
 2275         badPass = 0;
 2276     }
 2277     return badPass;
 2278 }
 2279 
 2280 int dupLogFd();
 2281 void execsystem(PCStr(what),PCStr(pathcom));
 2282 static int forkSsh(PCStr(host),int port,PCStr(user),PCStr(pass),int tf[2],FILE *fc,FILE *tc){
 2283     int pid;
 2284     int pty;
 2285     IStr(name,128);
 2286     int slog = LOG_type1;
 2287     LOG_type1 &= ~L_CONSOLE; /* 9.9.8 suppress logging to pty by child */
 2288 
 2289     if( curLogFd() == fileno(stderr) ){
 2290         dupLogFd(); /* 9.8.2 not to send log-output to pty */
 2291     }
 2292     pid = Forkpty(&pty,(char*)name);
 2293     if( 0 < pid ){
 2294         CStr(resp,0x10000);
 2295         FILE *fs;
 2296 
 2297         PollIn(pty,3*1000); /* wait response from ssh */
 2298         LOG_type1 = slog; /* LOG_type1 is on memory shared with child */
 2299         sshPid = pid;
 2300         sshPty = pty;
 2301         sv1log("--SSH: pid=%d pty master %d %s\n",sshPid,pty,name);
 2302         tf[0] = pty;
 2303         tf[1] = pty;
 2304 
 2305         msleep(100);
 2306         fs = fdopen(pty,"r");
 2307         relay_resp(fs,tc,15*1000,AVStr(resp),"FORK-1");
 2308         if( badpass(resp) && *pass ){
 2309             IStr(req,128);
 2310             sprintf(req,"%s\r\n",pass);
 2311             IGNRETP write(pty,req,strlen(req));
 2312             relay_resp(fs,tc,15*1000,AVStr(resp),"FORK-2");
 2313             badpass(resp);
 2314         }
 2315         fprintf(tc,"%s",resp);
 2316         fflush(tc);
 2317         return 0;
 2318     }else{
 2319         CStr(com,1024);
 2320         int rcode;
 2321         sprintf(com,"");
 2322         Xsprintf(TVStr(com),ssh_com,port,user,host);
 2323         sv1log("--SSH: %s\n",com);
 2324 
 2325         closeAll();
 2326         rcode = Stty(0,"-echo,-echonl");
 2327         execsystem("ssh",com);
 2328         printf("ssh exec(%s) failed\n",com);
 2329         _exit(0);
 2330         return 0;
 2331     }
 2332 }
 2333 static void setpty(int pty,FILE *tc){
 2334     IStr(com,128);
 2335     sv1log("----STTY I%X O%X L%X C%X\n",
 2336         Gtty(pty,"iflags"),Gtty(pty,"oflags"),
 2337         Gtty(pty,"lflags"),Gtty(pty,"cflags"));
 2338 
 2339     Stty(pty,"-echo"); // BSD
 2340 
 2341     sprintf(com,"stty echo\n");
 2342     IGNRETP write(pty,com,strlen(com)); // for FedoraCore and SSH/Cygwin
 2343     /* shoul skip the response */
 2344 
 2345     Stty(pty,"icrnl");
 2346     putIAC(tc,WILL,O_SUPAHEAD); fflush(tc);
 2347     putIAC(tc,DONT,O_ECHO); fflush(tc);
 2348     putIAC(tc,WILL,O_ECHO); fflush(tc);
 2349     putIAC(tc,DO,O_NAWS); /* initiate negotiation for WindowSize */
 2350 }
 2351 int getwinsize(int fd,int *row,int *col);
 2352 int setwinsize(int fd,int row,int col);
 2353 static void syncpty(int pty,FILE *tc){
 2354     /*
 2355     int row,col;
 2356     row = col = 0;
 2357     getwinsize(pty,&row,&col);
 2358     fprintf(stderr,"----STTY I%X O%X L%X C%X WinSize=%d,%d\n",
 2359         Gtty(pty,"iflags"),Gtty(pty,"oflags"),
 2360         Gtty(pty,"lflags"),Gtty(pty,"cflags"),
 2361         row,col);
 2362     */
 2363 }
 2364 static int setWinSize(int pty,FILE *tc,unsigned PCStr(ub),int rcc){
 2365     int i,ocol,orow,col,row,nrow,ncol;
 2366     if( ub[1] == WILL || ub[1] == SB )
 2367     if( ub[2] == O_NAWS ){
 2368         if( ub[1] == SB ){
 2369             col = ub[4];
 2370             row = ub[6];
 2371         }else{
 2372             col = ub[7];
 2373             row = ub[9];
 2374         }
 2375         getwinsize(pty,&orow,&ocol);
 2376         setwinsize(pty,row,col);
 2377         //setwinsize(pty,row-1,col-1); // for SSH/Cygwin??
 2378         nrow = ncol = 0;
 2379         getwinsize(pty,&nrow,&ncol);
 2380         /*
 2381         for(i = 0; i < rcc; i++)
 2382             fprintf(stderr,"[%2d] %02X/%3d\n",i,ub[i],ub[i]);
 2383         */
 2384         sv1log("----WinSize [%d,%d][%d,%d][%d,%d]\n",
 2385             ocol,orow,col,row,ncol,nrow);
 2386         return 1;
 2387     }
 2388     return 0;
 2389 }
 2390 static int relay1(FILE *tc,int fs,int ts,int cl){
 2391     int fdv[2];
 2392     int rdv[2];
 2393     int rdy,rcc,wcc;
 2394     CStr(buf,1024+1);
 2395     const unsigned char *ub = (const unsigned char*)buf;
 2396     int xpid;
 2397     int rcode;
 2398 
 2399     fdv[0] = cl;
 2400     fdv[1] = fs;
 2401 
 2402     if( badPass ){
 2403         Stty(sshPty,"-echo");
 2404         putIAC(tc,WILL,O_ECHO);
 2405         putIAC(tc,DONT,O_ECHO);
 2406         fflush(tc);
 2407     }else{
 2408         setpty(sshPty,tc);
 2409     }
 2410     for(;;){
 2411         rdy = PollIns(IO_TIMEOUT*1000,2,fdv,rdv);
 2412         syncpty(sshPty,tc);
 2413 
 2414         if( rdy <= 0 ){
 2415             break;
 2416         }
 2417         if( rdv[0] ){ /* from client */
 2418             rcc = read(fdv[0],buf,sizeof(buf)-1);
 2419             if( rcc <= 0 ) break;
 2420             if( 2 <= rcc && ub[rcc-2] == '\r' && ub[rcc-1] == 0 ){
 2421                 sv1log("C-S CR %d [%X][%d]\n",rcc,ub[0],ub[1]);
 2422                 rcc--;
 2423             }
 2424             if( ub[0] == 0xFF ){
 2425                 if( setWinSize(sshPty,tc,ub,rcc) ){
 2426                 }else
 2427                 if( ub[1] == DO && ub[2] == O_SUPAHEAD ){
 2428                     sv1log("C-S %d DO SUPGA\n",rcc);
 2429                     putIAC(tc,WILL,O_SUPAHEAD);
 2430                 }else
 2431                 if( ub[1] == DO && ub[2] == O_ECHO ){
 2432                     sv1log("C-S %d DO ECHO\n",rcc);
 2433                     /*
 2434                     Stty(sshPty,"echo,echonl");
 2435                     */
 2436                     putIAC(tc,WILL,O_ECHO);
 2437                 }else{
 2438                     fprintf(stderr,"C-S %d [%X][%d][%d]\n",
 2439                         rcc,ub[0],ub[1],ub[2]);
 2440                 }
 2441                 fflush(tc);
 2442                 continue;
 2443             }
 2444             /* ... scan Telnet command and erase ... */
 2445             wcc = write(ts,buf,rcc);
 2446             if( wcc <= 0 ) break;
 2447         }
 2448         if( rdv[1] ){ /* from ssh */
 2449             rcc = read(fdv[1],buf,sizeof(buf)-1);
 2450             if( rcc <= 0 ) break;
 2451             wcc = write(cl,buf,rcc);
 2452             if( wcc <= 0 ) break;
 2453             if( 1 <= rcc && ub[rcc-1] == '\n' )
 2454             if( rcc == 1 || ub[rcc-2] != '\r' )
 2455             {
 2456                 sv1log("S-C NL to LF-CR %d\n",rcc);
 2457                 IGNRETP write(cl,"\r",1);
 2458             }
 2459             if( 1 <= rcc && ub[rcc-1] == '\r' ){
 2460                 sv1log("S-C CR to NL %d\n",rcc);
 2461                 IGNRETP write(cl,"\n",1);
 2462             }
 2463             if( badPass ){
 2464                 setVStrEnd(buf,rcc);
 2465                 if( !badpass(buf) ){
 2466                     setpty(sshPty,tc);
 2467                 }
 2468             }
 2469         }
 2470     }
 2471     close(fs);
 2472     if( fs != ts ) close(ts);
 2473     close(cl);
 2474     xpid = NoHangWait();
 2475     return 0;
 2476 }
 2477 static int relay0(PCStr(host),int port,PCStr(user),PCStr(pass),int cl){
 2478     int sshv[2]; /* to the pty of ssh */
 2479     FILE *fc;
 2480     FILE *tc;
 2481     AuthInfo au;
 2482 
 2483     fc = fdopen(cl,"r");
 2484     tc = fdopen(cl,"w");
 2485     if( user[0] == 0 || host[0] == 0 || streq(host,"-") ){
 2486         if( getUSERenv(fc,tc,&au) ){
 2487             if( au.i_user[0] ) user = au.i_user;
 2488             if( au.i_pass[0] ) pass = au.i_pass;
 2489             if( au.i_Host[0] ) host = au.i_Host;
 2490             if( au.i_Port    ) port = au.i_Port;
 2491         }else{
 2492  sv1log("--USER not given\n");
 2493  fprintf(tc,"*** Send the user name as:\r\n");
 2494  fprintf(tc,"*** %% telnet -l UsrName delegate-host\r\n");
 2495             fclose(tc);
 2496             fclose(fc);
 2497             return 0;
 2498         }
 2499     }
 2500     forkSsh(host,port,user,pass,sshv,fc,tc);
 2501     relay1(tc,sshv[0],sshv[1],cl);
 2502     return 0;
 2503 }
 2504 int connectToSsh(Connection *Conn,PCStr(host),int port,PCStr(user),PCStr(pass)){
 2505     int sockv[2];
 2506     int nready;
 2507     int err;
 2508 
 2509     if( sshThread ){
 2510         err = thread_wait(sshThread,1000);
 2511         Verbose("--PrevThread %X err=%d\n",sshThread,err);
 2512         sshThread = 0;
 2513     } 
 2514     if( sshPty != -1 ){
 2515         err = close(sshPty);
 2516         Verbose("--PrevPty %d err=%d\n",sshPty,err);
 2517         sshPty = -1;
 2518     }
 2519     if( sshPid ){
 2520         int xpid;
 2521         xpid = NoHangWait();
 2522         Verbose("--PrevPid %d %d\n",sshPid,xpid);
 2523         sshPid = 0;
 2524     }
 2525     badPass = 0;
 2526     Socketpair(sockv);
 2527     sshThread = thread_fork(0x40000,STX_tid,"relay0",(IFUNCP)relay0,host,port,user,pass,sockv[0]);
 2528     nready = PollIn(sockv[1],15*1000);
 2529     return sockv[1];
 2530 }