home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_400 / 438_01 / stelnet.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-20  |  25.7 KB  |  952 lines

  1. /*
  2.  * stelnet - A telnet client for the serial port
  3.  * Version 1.00 (December 20th, 1994)
  4.  *
  5.  * Copyright 1994 Riku Saikkonen (riku.saikkonen@pcb.compart.fi)
  6.  *
  7.  * This program is free software; you can redistribute and/or modify it
  8.  * under the terms of the GNU General Public License version 2 as published
  9.  * by the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful, but
  12.  * WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
  14.  * Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License along
  17.  * with this program (as the file COPYING); if not, write to the Free
  18.  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  *
  20.  * The author, Riku Saikkonen, can be contacted via the following means:
  21.  * E-mail: riku.saikkonen@pcb.compart.fi (preferred) or
  22.  *         risaikko@freenet.hut.fi
  23.  * Paper mail: Riku Saikkonen
  24.  *             Alakartanontie 4 B 63
  25.  *             SF-02360  ESPOO
  26.  *             Finland
  27.  */
  28.  
  29. /*
  30.  * Known bugs:
  31.  * ZModem ZR[Q]INIT to telnet without a reply activates ZModem 8-bit clean
  32.  *   mode (ZDLE ZDLE exits it)
  33.  *
  34.  * Todo list:
  35.  * user-selectable terminal types
  36.  * don't beep on local screen in put_ser() (affects only warnings etc.)
  37.  * debug mode (show IACs)
  38.  * ECHO option (local echo)
  39.  * TELNET options 5 (STATUS), 32-38?
  40.  * non-SGA mode + send Go Ahead command
  41.  * telnet handshaking (how?)
  42.  * telnet/serial options in synchronisation with sob/tob
  43.  *   (difficult, not very useful)
  44.  */
  45.  
  46. #include <stdio.h>
  47. #include <conio.h>
  48. #include <stdlib.h>
  49. #include <string.h>
  50. #include <ctype.h>
  51. #include <dos.h>
  52. #include <time.h>
  53. #include <tcp.h>
  54.  
  55. #define BANNER "stelnet 1.00"
  56. #define COPYRIGHT1 \
  57.   "Copyright 1994 Riku Saikkonen (riku.saikkonen@pcb.compart.fi)"
  58. #define COPYRIGHT2 \
  59.   "stelnet is free software, distributed under the terms of the GNU " \
  60.     "General\r\n" \
  61.   "Public License version 2, and comes with ABSOLUTELY NO WARRANTY; for " \
  62.     "details,\r\n" \
  63.   "see the documentation distributed with the program.\r\n"
  64.  
  65. /* Default telnet, serial buffer sizes (override with makefile) */
  66. #ifndef TIBLEN
  67. #define TIBLEN 512
  68. #endif
  69. #ifndef SIBLEN
  70. #define SIBLEN 512
  71. #endif
  72. #ifndef TOBLEN
  73. #define TOBLEN 8192
  74. #endif
  75. #ifndef SOBLEN
  76. #define SOBLEN 8192
  77. #endif
  78. #ifndef SOBLIMIT
  79. #define SOBLIMIT 512
  80. #endif
  81.  
  82. /* Default loopcheck value (override with makefile) */
  83. #ifndef LOOPCHECK
  84. #define LOOPCHECK 100
  85. #endif
  86.  
  87. typedef unsigned char BYTE;
  88. typedef unsigned int  WORD;
  89.  
  90. #define SE 240
  91. #define BRK 243
  92. #define IP 244
  93. #define AO 245
  94. #define AYT 246
  95. #define EC 247
  96. #define EL 248
  97. #define SB 250
  98. #define WILL 251
  99. #define WONT 252
  100. #define DO 253
  101. #define DONT 254
  102. #define IAC 255
  103.  
  104. #define IS 0
  105. #define SEND 1
  106.  
  107. #define TELOPT_BINARY 0
  108. #define TELOPT_ECHO 1
  109. #define TELOPT_SGA 3
  110. #define TELOPT_TTYPE 24
  111.  
  112. #define addsob(c) { if (sobp>=SOBLEN) sobp=0; /* Wrap */ \
  113.     if (sobp==sobs && sobp!=0) \
  114.       {sobs++;soverrun++;} /* Buffer overrun; lose first character */ \
  115.     sob[sobp]=c;sobp++; }
  116.  
  117. #define addtob(c) { if (tobp>=TOBLEN) tobp=0; /* Wrap */ \
  118.     if (tobp==tobs && tobp!=0) \
  119.       {tobs++;toverrun++;} /* Buffer overrun; lose first character */ \
  120.     tob[tobp]=c;tobp++; }
  121.  
  122. #define putstr(s) {fputs(s,stdout);}
  123.  
  124. void syntax(void);
  125.  
  126. int init_tel(void);
  127. void deinit_tel(void);
  128. #define ok_tel() tcp_tick(socket)
  129. #define read_tel(buf,sz) sock_fastread(socket,buf,sz)
  130. #define write_tel(buf,sz) sock_fastwrite(socket,buf,sz)
  131.  
  132. int init_ser(void);
  133. int read_ser(BYTE *buf, int sz);
  134. int write_ser(BYTE *buf, int sz);
  135. int ok_ser(void);
  136. int put_ser(char *s);
  137. void deinit_ser(void);
  138.  
  139. int comport;
  140. union REGS reg;
  141. struct SREGS sreg;
  142.  
  143. char hostname[128];
  144. int port;
  145. unsigned long hostnum;
  146. tcp_Socket *socket;
  147. tcp_Socket socketdata;
  148.  
  149. int opt_bin;
  150. int copt_cd,copt_localscr,copt_idle,copt_sendinit,copt_8bit;
  151. char copt_username[30];
  152.  
  153. int main(int argc, char *argv[])
  154. {
  155.   static BYTE tib[TIBLEN],tob[TOBLEN];
  156.   static BYTE sib[SIBLEN],sob[SOBLEN];
  157.   BYTE sibc,tibc;
  158.   static FILE *fp;
  159.   int sobs,sobp,tobs,tobp,sibend,tibend;
  160.   int i,j;
  161.   unsigned u=0;
  162.   int sercmd,teliac,quit,timewarn,itimewarn;
  163.   time_t stime,etime,itime,curtime;
  164.   unsigned char sercmdkey;
  165.   long soverrun,toverrun;
  166.   int clean8bit,clean8bitz,zmframe;
  167.   int loopcount,loop2count,notidle;
  168.   static char s[256],exit8bits[11]="-\030\030stelnet";
  169.  
  170.   putstr(BANNER "\n" COPYRIGHT1 "\n" COPYRIGHT2 "\n");
  171.  
  172.   if (argc<4) syntax();
  173.  
  174.   comport=atoi(argv[1]);
  175.   if (comport<1 || comport>8)
  176.   {
  177.     putstr("Invalid COM port!\n");
  178.     exit(10);
  179.   }
  180.   comport--;
  181.  
  182.   itime=etime=0;
  183.   copt_cd=copt_sendinit=1;copt_localscr=copt_idle=copt_8bit=0;
  184.   itimewarn=timewarn=0;
  185.   strcpy(copt_username,"Unknown");
  186.   stime=time(NULL);
  187.   sercmdkey=255;
  188.  
  189.   for (i=4;i<argc;i++)
  190.   {
  191.     switch (tolower(argv[i][0]))
  192.     {
  193.       case 'i':
  194.         copt_idle=1;
  195.         putstr("Idle timeout checking on.\n");
  196.         break;
  197.       case 't':
  198.         timewarn=atoi(&argv[i][1]);
  199.         etime=stime+timewarn*60;
  200.         timewarn=300;
  201.         putstr("Time limit set.\n");
  202.         break;
  203.       case '8': copt_8bit=1;putstr("8-bit clean mode.\n");break;
  204.       case 'e':
  205.         sercmdkey=atoi(&argv[i][1]);
  206.         sprintf(s,"Escape character set to ASCII %d.\n",sercmdkey);
  207.         putstr(s);
  208.         break;
  209.       case 'c': copt_cd=0;putstr("Carrier Detect checking off.\n");break;
  210.       case 'l': copt_localscr=1;putstr("Local screen on.\n");break;
  211.       case 'x':
  212.         copt_sendinit=0;putstr("Not sending initialisation codes.\n");
  213.         break;
  214.       case 'd': /* Door information file */
  215.         if ((fp=fopen(&argv[i][2],"rb"))==NULL)
  216.         {
  217.           putstr("Error opening door information file!\n");
  218.           exit(9);
  219.         }
  220.         switch (tolower(argv[i][1]))
  221.         {
  222.           case 'p': /* PCBOARD.SYS */
  223.             fseek(fp,84,SEEK_SET);
  224.             fread(copt_username,sizeof(unsigned char),25,fp);
  225.             copt_username[25]=0; /* Purge spaces from right !!! */
  226.             fread(&u,sizeof(unsigned int),1,fp);
  227.             if (u<1440) /* !!! */
  228.             {
  229.               etime=stime+u*60;
  230.               timewarn=300;
  231.             }
  232.             putstr("PCBOARD.SYS read.\n");
  233.             break;
  234.           default: syntax();
  235.         }
  236.         fclose(fp);
  237.         break;
  238.       default: syntax();
  239.     }
  240.   }
  241.  
  242.   if (init_ser()<0)
  243.   {
  244.     putstr("Error initialising serial port!\n");
  245.     exit(3);
  246.   }
  247.  
  248.   /* VT-100: ESC c (reset), ESC [1;1f (home), ESC [2J (clear screen) */
  249.   if (copt_sendinit) put_ser("\033c\033[1;1f\033[2J");
  250.  
  251.   put_ser("\r\n" BANNER "\r\n" COPYRIGHT1 "\r\n" COPYRIGHT2 "\r\n");
  252.  
  253.   sprintf(s,"Serial escape character is ASCII %d.\r\n",sercmdkey);
  254.   putstr(s);
  255.  
  256.   if (copt_8bit)
  257.     put_ser("In 8-bit clean mode; exit with '<255><^X><^X>stelnet'.\r\n");
  258.  
  259.   if (etime!=0)
  260.   {
  261.     sprintf(s,"Time left: %.1f min\r\n",(etime-time(NULL))/60.0);
  262.     put_ser(s);
  263.   }
  264.  
  265.   strcpy(hostname,argv[2]);
  266.   port=atoi(argv[3]);
  267.  
  268.   sprintf(s,"\r\nTrying to connect to %s port %d...\r\n",hostname,port);
  269.   put_ser(s);
  270.  
  271.   i=init_tel();
  272.   if (i<0)
  273.   {
  274.     if (i==-1) put_ser("Connection refused.");
  275.     else put_ser("Name resolver not working!");
  276.     put_ser(" Exiting.\r\n");
  277.     deinit_ser();
  278.     if (i==-1) exit(2); else exit(4);
  279.   }
  280.  
  281.   put_ser("Connected. Type <ASCII 255> ? for a list of commands.\r\n");
  282.  
  283.   sobs=sobp=tobs=tobp=sercmd=teliac=quit=0;
  284.   soverrun=toverrun=0;
  285.   zmframe=clean8bitz=0;
  286.   clean8bit=copt_8bit;
  287.   sibc=tibc=0;
  288.   loopcount=loop2count=notidle=1;
  289.   itimewarn=6;
  290.  
  291.   /* Telnet initialisation IACs */
  292.   if (copt_sendinit)
  293.   {
  294.     tob[0]=IAC;tob[1]=DO;tob[2]=TELOPT_BINARY;
  295.     tob[3]=IAC;tob[4]=WILL;tob[5]=TELOPT_BINARY;opt_bin=1;
  296.     tob[6]=IAC;tob[7]=DO;tob[8]=TELOPT_ECHO;
  297.     tob[9]=IAC;tob[10]=WILL;tob[11]=TELOPT_SGA;
  298.     tob[12]=IAC;tob[13]=DO;tob[14]=TELOPT_SGA;
  299.     tob[15]=IAC;tob[16]=WILL;tob[17]=TELOPT_TTYPE;
  300.     tobp=18;
  301.   }
  302.  
  303.   do
  304.   {
  305.     loopcount--;
  306.  
  307.     if (!loopcount)
  308.     {
  309.       if (!ok_ser())
  310.       {
  311.         putstr("stelnet: Serial port error (Carrier Detect lost)!\n");
  312.         deinit_tel();deinit_ser();exit(3);
  313.       }
  314.       if (!ok_tel()) break;
  315.     }
  316.  
  317. #if SOBLIMIT != 0
  318.     if ((sobs<=sobp)?(sobp-sobs<SOBLEN-SOBLIMIT):(sobs-sobp>SOBLIMIT))
  319.     {
  320. #endif
  321.       if ((tibend=read_tel(tib,TIBLEN))<0) break;
  322. #if SOBLIMIT != 0
  323.     } else tibend=0;
  324. #endif
  325.  
  326.     sibend=read_ser(sib,SIBLEN);
  327.  
  328.     if (!loopcount && copt_localscr)
  329.       if (kbhit())
  330.         if (sibend<SIBLEN) {sib[sibend]=getch();sibend++;}
  331.  
  332.     for (i=0;i<tibend;i++)
  333.     {
  334.       tibc=tib[i];
  335.       if (teliac)
  336.       {
  337.     switch (teliac)
  338.     {
  339.       case 1: /* IAC */
  340.             switch (tibc)
  341.         {
  342.           case DO: teliac=2;break;
  343.           case DONT: teliac=3;break;
  344.           case WILL: teliac=4;break;
  345.           case WONT: teliac=5;break;
  346.           case SB: teliac=6;break;
  347.           case IP:
  348.         teliac=0;
  349.         quit=2;
  350.         break;
  351.           case AYT:
  352.         teliac=0;
  353.                 /* Send "[yes]\n" in reply */
  354.                 addtob('[');addtob('y');addtob('e');addtob('s');addtob(']');
  355.                 addtob(10);
  356.         break;
  357.           case EC:
  358.         teliac=0;
  359.                 /* VT-100 destructive backspace (BS + space + BS) */
  360.         addsob(8);addsob(32);addsob(8);
  361.         break;
  362.           case EL:
  363.         teliac=0;
  364.                 /* VT-100 erase line (CR + erase to EOL) */
  365.         addsob(13);addsob(27);addsob('K');
  366.         break;
  367.           case IAC: teliac=0;addsob(255);break;
  368.           default: teliac=0;break; /* Ignore others */
  369.         }
  370.         break;
  371.       case 2: /* IAC DO */
  372.         teliac=0;
  373.             switch (tibc)
  374.         {
  375.           case TELOPT_ECHO:
  376.         /* Never echo ? !!! */
  377.         addtob(IAC);addtob(WONT);addtob(TELOPT_ECHO);
  378.         break;
  379.           case TELOPT_BINARY:
  380.         addtob(IAC);addtob(WILL);addtob(TELOPT_BINARY);
  381.         opt_bin=1;
  382.         break;
  383.               case TELOPT_SGA:
  384.         /* Never sends GA anyway */
  385.                 addtob(IAC);addtob(WILL);addtob(TELOPT_SGA);
  386.         break;
  387.           case TELOPT_TTYPE:
  388.         addtob(IAC);addtob(WILL);addtob(TELOPT_TTYPE);
  389.         break;
  390.               default: /* Wont */
  391.                 addtob(IAC);addtob(WONT);addtob(tibc);
  392.                 break;
  393.         }
  394.         break;
  395.       case 3: /* IAC DONT */
  396.         teliac=0;
  397.             switch (tibc)
  398.         {
  399.           case TELOPT_ECHO:
  400.         addtob(IAC);addtob(WONT);addtob(TELOPT_ECHO);
  401.         break;
  402.               case TELOPT_BINARY:
  403.         addtob(IAC);addtob(WONT);addtob(TELOPT_BINARY);
  404.                 put_ser("\r\nstelnet: Non-binary (7-bit) mode activated "
  405.                   "by remote request.\r\n");
  406.         opt_bin=0;
  407.         break;
  408.               case TELOPT_SGA:
  409.         /* Never sends GA anyway */
  410.         addtob(IAC);addtob(WONT);addtob(TELOPT_SGA);
  411.         break;
  412.               case TELOPT_TTYPE:
  413.         addtob(IAC);addtob(WONT);addtob(TELOPT_TTYPE);
  414.         break;
  415.               default: /* Wont */
  416.                 addtob(IAC);addtob(WONT);addtob(tibc);
  417.                 break;
  418.             }
  419.             break;
  420.           case 4: /* IAC WILL */
  421.           case 5: /* IAC WONT */
  422.             /* Nothing to do; ignore */
  423.         teliac=0;
  424.         break;
  425.           case 6: /* IAC SB */
  426.             if (tibc==TELOPT_TTYPE) teliac=7;
  427.             else if (tibc==IAC) teliac=8; /* Ignore string */
  428.         break;
  429.           case 7: /* IAC SB TERMINAL-TYPE */
  430.             teliac=6;
  431.             if (tibc==SEND)
  432.         {
  433.               addtob(IAC);addtob(SB);addtob(TELOPT_TTYPE);addtob(IS);
  434.               /* VT-100: (RFC 1340) "DEC-VT100" (termcap) !!! */
  435.               addtob('D');addtob('E');addtob('C');addtob('-');
  436.               addtob('V');addtob('T');addtob('1');addtob('0');addtob('0');
  437.               addtob(IAC);addtob(SE);
  438.               break;
  439.         }
  440.         break;
  441.       case 8: /* IAC SB ... IAC */
  442.             if (tibc==SE) teliac=0; else teliac=6;
  443.         break;
  444.       default: break;
  445.     }
  446.       } else
  447.       {
  448.         if (tibc==IAC) teliac=1;
  449.     else
  450.     {
  451.           /* Feel free to receive 8-bit even without binary mode ? !!! */
  452.           addsob(tibc);
  453.           if (copt_localscr && tibc!=7) putchar(tibc); /* Don't beep */
  454.     }
  455.       }
  456.     }
  457.  
  458.     if (sibend>0) notidle=1;
  459.     if (!loopcount && notidle) {itime=time(NULL);notidle=0;itimewarn=6;}
  460.  
  461.     for (i=0;i<sibend;i++)
  462.     {
  463.       sibc=sib[i];
  464.       if (sibc==sercmdkey || sercmd) /* Command key */
  465.       {
  466.         if (!sercmd) sercmd=1; /* Skip command key */
  467.         else if (clean8bit)
  468.         {
  469.           if (sibc==exit8bits[sercmd])
  470.           {
  471.             sercmd++;
  472.             if (sercmd>9)
  473.             {
  474.               put_ser("\r\nstelnet: Exiting 8-bit clean mode.\r\n");
  475.               clean8bit=clean8bitz=0;
  476.               sercmd=0;
  477.             }
  478.           }
  479.           else
  480.           {
  481.             addtob(sercmdkey);
  482.             if (sercmdkey==IAC) addtob(IAC); /* IAC -> IAC IAC */
  483.             for (j=1;j<sercmd;j++) addtob(exit8bits[j]);
  484.             addtob(sibc);
  485.             if (sibc==IAC) addtob(IAC); /* IAC -> IAC IAC */
  486.             sercmd=0;
  487.           }
  488.         } else if (sercmd==2) /* <cmdkey> k */
  489.         {
  490.           sercmdkey=sibc;
  491.           sprintf(s,"\r\nstelnet: Serial escape character set to ASCII "
  492.             "%d.\r\n",sercmdkey);
  493.           put_ser(s);
  494.           sercmd=0;
  495.         } else if (sibc==sercmdkey && sercmdkey!='e')
  496.         {
  497.           /* Send the command key */
  498.           addtob(sercmdkey);
  499.           if (sercmdkey==IAC) addtob(IAC); /* IAC -> IAC IAC */
  500.           sercmd=0;
  501.         } else { /* Process command */
  502.           sercmd=0;
  503.           switch (tolower(sibc))
  504.           {
  505.             case 'c':
  506.               put_ser("\r\nstelnet: Close connection command\r\n");
  507.           quit=1;
  508.           break;
  509.             case '8':
  510.               if (opt_bin)
  511.               {
  512.                 sprintf(s,"\r\nstelnet: 8-bit clean mode entered "
  513.                   "(exit with '<%d><^X><^X>stelnet').\r\n",sercmdkey);
  514.                 put_ser(s);
  515.                 clean8bit=1;clean8bitz=0;
  516.               } else put_ser("\r\nstelnet: Not in binary mode!\r\n");
  517.               break;
  518.         case 'i': addtob(IAC);addtob(IP);break;
  519.         case 'r': addtob(IAC);addtob(BRK);break;
  520.         case 'a': addtob(IAC);addtob(AO);break;
  521.         case 't': addtob(IAC);addtob(AYT);break;
  522.             case 'e':
  523.               sercmd=2; /* Wait for the character */
  524.               break;
  525.             case 's':
  526.               put_ser("\r\n" BANNER "\r\nConnection status:\r\n");
  527.               put_ser("Connected to ");
  528.               put_ser(hostname);
  529.               put_ser(" (");
  530.               put_ser(itoa((hostnum&0xff000000L)>>24,s,10));
  531.               put_ser(".");
  532.               put_ser(itoa((hostnum&0x00ff0000L)>>16,s,10));
  533.               put_ser(".");
  534.               put_ser(itoa((hostnum&0x0000ff00L)>>8,s,10));
  535.               put_ser(".");
  536.               put_ser(itoa(hostnum&0x000000ffL,s,10));
  537.               put_ser(") port ");
  538.               put_ser(itoa(port,s,10));
  539.               put_ser("\r\n");
  540.               /* For some odd reason, this:
  541.                * sprintf(s," (%d.%d.%d.%d) port %d\r\n",
  542.                *   ((hostnum&0xff000000L)>>24),((hostnum&0x00ff0000L)>>16),
  543.                *   ((hostnum&0x0000ff00L)>>8),((hostnum&0x000000ffL)),
  544.                *   port);
  545.                * didn't work! (BC++ 3.0 bug?)
  546.                */
  547.           put_ser("Binary mode ");
  548.               put_ser((opt_bin)?"on.\r\n":"off.\r\n");
  549.               if (etime!=0)
  550.           {
  551.                 sprintf(s,"Time left: %.1f min\r\n",
  552.                   (etime-time(NULL))/60.0);
  553.         put_ser(s);
  554.           }
  555.           if (soverrun>0)
  556.           {
  557.                 sprintf(s,"%ld characters lost due to serial port output "
  558.                   "overflow.\r\n",soverrun);
  559.         put_ser(s);
  560.           }
  561.           if (toverrun>0)
  562.           {
  563.                 sprintf(s,"%ld characters lost due to telnet output "
  564.                   "overflow.\r\n",toverrun);
  565.         put_ser(s);
  566.           }
  567.               sprintf(s,"Connection %s on the local screen.\r\n",
  568.                 (copt_localscr)?"shown":"not shown");
  569.               put_ser(s);
  570.               sprintf(s,"Idle timeout checking %s.\r\n\r\n",
  571.                 (copt_idle)?"on":"off");
  572.               put_ser(s);
  573.               break;
  574.             case '?': /* Help */
  575.               put_ser("\r\n" BANNER "\r\n");
  576.               put_ser("stelnet commands (precede with "
  577.                 "the escape character):\r\n");
  578.               put_ser("c Forcefully close the connection and exit "
  579.                 "stelnet\r\n");
  580.               put_ser("8 (in binary mode) Enter 8-bit clean mode\r\n");
  581.               put_ser("  (exit with '<escchar><^X><^X>stelnet')\r\n");
  582.               put_ser("i Send telnet Interrupt Process code (IAC IP)\r\n");
  583.               put_ser("r Send telnet NVT BRK code (IAC BRK)\r\n");
  584.               put_ser("a Send telnet Abort Output code (IAC AO)\r\n");
  585.               put_ser("t Send telnet Are You There code (IAC AYT)\r\n");
  586.               put_ser("e<key> Set the escape character to <key>\r\n");
  587.               put_ser("s Display connection status\r\n");
  588.               put_ser("? Display this help text\r\n");
  589.               put_ser("<escchar> Send the escape character through "
  590.                 "telnet\r\n");
  591.               put_ser("\r\n");
  592.               break;
  593.             default: break;
  594.           }
  595.         }
  596.       } else
  597.       {
  598.         switch (zmframe)
  599.         {
  600.           case 0:
  601.             if (sibc=='*') zmframe=1;
  602.             else if (clean8bitz && sibc==24) zmframe=10;
  603.             break;
  604.           case 1: /* ZPAD */
  605.             if (sibc==24) zmframe=2; else if (sibc!='*') zmframe=0;
  606.             break;
  607.           case 2: /* ZPAD ZDLE */
  608.             if (sibc=='A' || sibc=='C') zmframe=3;
  609.             else if (sibc=='B') zmframe=4;
  610.             else zmframe=0;
  611.             break;
  612.           case 3: /* ZPAD ZDLE ZBIN */
  613.             if (clean8bitz)
  614.             {
  615.               if (sibc==8) /* ZFIN */
  616.                 clean8bit=clean8bitz=0;
  617.             } else
  618.             {
  619.               if (sibc==0 || sibc==1) /* ZRQINIT / ZRINIT */
  620.                 if (!clean8bit) clean8bit=clean8bitz=1;
  621.             }
  622.             zmframe=0;
  623.             break;
  624.           case 4: /* ZPAD ZDLE ZHEX */
  625.             if (sibc=='0') zmframe=5; else zmframe=0;
  626.             break;
  627.           case 5: /* ZPAD ZDLE ZHEX '0' */
  628.             if (clean8bitz)
  629.             {
  630.               if (sibc=='8') /* ZFIN (hex) */
  631.                 clean8bit=clean8bitz=0;
  632.             } else
  633.             {
  634.               if (sibc=='0' || sibc=='1') /* ZRQINIT / ZRINIT (hex) */
  635.                 if (!clean8bit) clean8bit=clean8bitz=1;
  636.             }
  637.             zmframe=0;
  638.             break;
  639.           case 10: /* (clean8bitz) ZDLE */
  640.             if (sibc==24) /* Second ZDLE */
  641.               clean8bit=clean8bitz=0;
  642.             zmframe=0;
  643.             break;
  644.           default: break;
  645.         }
  646.  
  647.         if (!opt_bin) sibc&=0x7f; /* Non-binary: Remove 8th bit */
  648.         addtob(sibc);
  649.         if (sibc==IAC) addtob(IAC); /* IAC -> IAC IAC */
  650.         /* Local echo !!! */
  651.         /* if (opt_echo) {if (copt_localscr) putchar(sibc); */
  652.         /* addsob(sibc);} */
  653.       }
  654.     }
  655.  
  656.     if (sobs<sobp)
  657.     {
  658.       /* sobs+=write_ser(&sob[sobs],sobp-sobs); didn't work! */
  659.       /* (another BC++ 3.0 bug?) */
  660.       i=write_ser(&sob[sobs],sobp-sobs);
  661.       sobs+=i;
  662.     } else if (sobs>=sobp && sobs!=0)
  663.     {
  664.       i=write_ser(&sob[sobs],SOBLEN-sobs);
  665.       sobs+=i;
  666.       if (sobs>=SOBLEN) sobs=0; /* Wrap */
  667.     }
  668.     if (sobs==sobp && i!=0) sobs=sobp=0; /* buffer empty -> both = 0 */
  669.  
  670.     if (tobs<tobp)
  671.     {
  672.       i=write_tel(&tob[tobs],tobp-tobs);
  673.       if (i<0) break;
  674.       tobs+=i;
  675.     } else if (tobs>=tobp && tobs!=0)
  676.     {
  677.       i=write_tel(&tob[tobs],TOBLEN-tobs);
  678.       if (i<0) break;
  679.       tobs+=i;
  680.       if (tobs>=TOBLEN) tobs=0; /* Wrap */
  681.     }
  682.     if (tobs==tobp && i!=0) tobs=tobp=0; /* buffer empty -> both = 0 */
  683.  
  684.     if (!loopcount)
  685.     {
  686.       if (etime!=0)
  687.       {
  688.         if (time(NULL)>(etime-timewarn))
  689.         {
  690.           if (timewarn<=0)
  691.           {
  692.             put_ser("\r\nstelnet: No time left!\007\r\n");
  693.             break;
  694.           } else
  695.           {
  696.             sprintf(s,
  697.               "\r\nstelnet: Only %d minute%s of time left!\007\r\n",
  698.               timewarn/60,(timewarn==60)?"":"s");
  699.             put_ser(s);
  700.             timewarn-=60;
  701.           }
  702.         }
  703.       }
  704.  
  705.       if (copt_idle)
  706.       {
  707.         if (time(NULL)>(itime+(6-itimewarn)*10+240))
  708.         {
  709.           if (itimewarn<=0)
  710.           {
  711.             put_ser("\r\nstelnet: Idle timeout!\007\r\n");
  712.             break;
  713.           } else
  714.           {
  715.             put_ser(
  716.               "\r\nstelnet: Idle timeout soon! Do something!\007\r\n");
  717.             itimewarn--;
  718.           }
  719.         }
  720.       }
  721.  
  722.       if (!copt_localscr)
  723.       {
  724.         loop2count--;
  725.         if (!loop2count)
  726.         {
  727.           curtime=time(NULL);
  728.           putstr(BANNER "\n");
  729.           sprintf(s,"User %s\nConnected to %s port %d for %d min",
  730.             copt_username,hostname,port,(curtime-stime)/60);
  731.           putstr(s);
  732.  
  733.           if (etime!=0)
  734.           {
  735.             sprintf(s," (%d min left).\n",(etime-curtime)/60);
  736.             putstr(s);
  737.           } else putstr(".\n");
  738.           sprintf(s,"Idle for %d second(s).",curtime-itime);
  739.           putstr(s);
  740.           if (clean8bit)
  741.           {
  742.             putstr(" 8-bit clean mode");
  743.             if (copt_8bit) putstr(" (from command line)");
  744.             if (clean8bitz) putstr(" (Zmodem)");
  745.             putstr(".");
  746.           }
  747.           putstr("\n\n");
  748.           loop2count=LOOPCHECK;
  749.         }
  750.       }
  751.  
  752.       loopcount=LOOPCHECK;
  753.     }
  754.   } while (!quit);
  755.  
  756.   if (quit>0) /* Non-error return */
  757.   {
  758.     /* Flush sob for 10 seconds */
  759.  
  760.     for (i=0;i<100;i++)
  761.     {
  762.       if (sobs<sobp)
  763.       {
  764.         sobs+=write_ser(&sob[sobs],sobp-sobs);
  765.       } else if (sobs>=sobp && sobs!=0)
  766.       {
  767.         sobs+=write_ser(&sob[sobs],SOBLEN-sobs);
  768.         if (sobs>=SOBLEN) sobs=0; /* Wrap */
  769.       }
  770.       if (sobs==sobp) break;
  771.       delay(100);
  772.     }
  773.  
  774.     switch (quit)
  775.     {
  776.       case 1: put_ser("\r\nstelnet: Local abort.");break;
  777.       case 2: put_ser("\r\nstelnet: Aborted by remote.");break;
  778.       default: break;
  779.     }
  780.   }
  781.  
  782.   deinit_tel();
  783.  
  784.   put_ser("\r\nstelnet: Connection closed, exiting.\r\n");
  785.  
  786.   if (soverrun>0)
  787.   {
  788.     sprintf(s,
  789.       "%ld characters lost due to serial port output overflow.\r\n",
  790.       soverrun);
  791.     put_ser(s);
  792.   }
  793.   if (toverrun>0)
  794.   {
  795.     sprintf(s,"%ld characters lost due to telnet output overflow.\r\n",
  796.       toverrun);
  797.     put_ser(s);
  798.   }
  799.  
  800.   deinit_ser();
  801.  
  802.   if (etime!=0)
  803.   {
  804.     if (time(NULL)>etime) exit(1); else exit(0);
  805.   } else exit(0);
  806.  
  807.   return 0;
  808. }
  809.  
  810. void syntax(void)
  811. {
  812.   putstr("Syntax: stelnet <comport> <host> <port> [options]\n"
  813.     "  comport = COM port number (1-8)\n"
  814.     "  host = the host name or IP address to connect to\n"
  815.     "  port = the TCP/IP port to connect to\n"
  816.     "Options (put as separate arguments):\n"
  817.     "  t<n> = set a n-minute time limit\n"
  818.     "  i = set a 5-minute idle timeout\n"
  819.     "  l = show the connection on the local screen and read the keyboard\n"
  820.     "  c = disable Carrier Detect detection on the serial port\n"
  821.     "  x = don't send telnet/serial initialisation codes\n"
  822.     "  8 = enter 8-bit clean mode at start\n"
  823.     "  d<t><filename> = read a door information file (<t> = type,\n"
  824.     "    <filename> = full file name with path)\n"
  825.     "    Types: p = PCBOARD.SYS\n"
  826.     "Example: stelnet 1 10.0.0.1 15 dpc:\\pcb\\node1\\pcboard.sys i\n"
  827.     "Errorlevels at return:\n"
  828.     "  0 = ok, 1 = out of time, 2 = connection refused, "
  829.       "3 = serial error,\n"
  830.     "  4 = network error, 9 = error in reading door information file,\n"
  831.     "  10 = error in command line\n");
  832.   exit(10);
  833. }
  834.  
  835. int init_tel(void)
  836. {
  837.   int i;
  838.  
  839.   sock_init();
  840.   dbug_init();
  841.  
  842.   hostnum=resolve(hostname);
  843.   if (hostnum==0) return -2; /* Name resolver failure */
  844.   socket=&socketdata;
  845.   if (!tcp_open(socket,0,hostnum,port,0)) return -1;
  846.   sock_wait_established(socket,sock_delay,0,&i);
  847.   sock_mode(socket,TCP_MODE_NAGLE);
  848.  
  849.   return 0;
  850.  
  851. sock_err:
  852.   return -1;
  853. }
  854.  
  855. void deinit_tel(void)
  856. {
  857.   int i;
  858.  
  859.   sock_close(socket);
  860.   sock_wait_closed(socket,sock_delay,0,&i);
  861.  
  862.   return;
  863. sock_err:
  864.   return;
  865. }
  866.  
  867. int init_ser()
  868. {
  869.   reg.h.ah=0x04;
  870.   reg.x.bx=0;
  871.   reg.x.dx=comport;
  872.   int86(0x14,®,®);
  873.   if (reg.x.ax!=0x1954) return -1; else return 0;
  874. }
  875.  
  876. int read_ser(BYTE *buf, int sz)
  877. {
  878.   reg.h.ah=0x18;
  879.   sreg.es=FP_SEG(buf);
  880.   reg.x.di=FP_OFF(buf);
  881.   reg.x.cx=sz;
  882.   reg.x.dx=comport;
  883.   int86x(0x14,®,®,&sreg);
  884.   return reg.x.ax;
  885. }
  886.  
  887. int write_ser(BYTE *buf, int sz)
  888. {
  889.   reg.h.ah=0x19;
  890.   sreg.es=FP_SEG(buf);
  891.   reg.x.di=FP_OFF(buf);
  892.   reg.x.cx=sz;
  893.   reg.x.dx=comport;
  894.   int86x(0x14,®,®,&sreg);
  895.   return reg.x.ax;
  896. }
  897.  
  898. int ok_ser(void)
  899. {
  900.   if (!copt_cd) return 1;
  901.   reg.h.ah=0x03;
  902.   reg.x.dx=comport;
  903.   int86(0x14,®,®);
  904.   if ((reg.h.al&0x80)==0) return 0; else return 1; /* Carrier Detect */
  905. }
  906.  
  907. int put_ser(char *s)
  908. {
  909.   char *sp,*ep;
  910.   int err=0,i;
  911.  
  912.   putstr(s);
  913.  
  914.   sp=s;ep=s+strlen(s);
  915.  
  916.   while (sp<ep)
  917.   {
  918.     i=write_ser(sp,ep-sp);
  919.     sp+=i;
  920.     if (i==0)
  921.     {
  922.       err++;
  923.       delay(100);
  924.       if (err>30) return -1;
  925.     } else err=0;
  926.   }
  927.   return 0;
  928. }
  929.  
  930. void deinit_ser(void)
  931. {
  932.   int i;
  933.  
  934.   /* Wait max. 10 s to flush output buffer */
  935.   for (i=0;i<100;i++)
  936.   {
  937.     reg.h.ah=0x03;
  938.     reg.x.dx=comport;
  939.     int86(0x14,®,®);
  940.     if ((reg.h.ah&0x40)!=0) break; /* Output buffer empty */
  941.  
  942.     delay(100);
  943.   }
  944.  
  945.   /* Deinitialise FOSSIL */
  946.   reg.h.ah=0x05;
  947.   reg.x.dx=comport;
  948.   int86(0x14,®,®);
  949. }
  950.  
  951. /* End of stelnet.c */
  952.