home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / msr313src.zip / msntnd.c < prev    next >
C/C++ Source or Header  |  1993-07-12  |  28KB  |  969 lines

  1. /* File MSNTND.C
  2.  * Telnet driver
  3.  *
  4.  * Copyright (C) 1985, 1993, Trustees of Columbia University in the 
  5.  * City of New York.  Permission is granted to any individual or institution
  6.  * to use this software as long as it is not sold for profit.  This copyright
  7.  * notice must be retained.  This software may not be included in commercial
  8.  * products without written permission of Columbia University.
  9.  *
  10.  * Written for MS-DOS Kermit by Joe R. Doupnik, 
  11.  *  Utah State University, jrd@cc.usu.edu, jrd@usu.Bitnet,
  12.  *  and by Frank da Cruz, Columbia Univ., fdc@watsun.cc.columbia.edu.
  13.  * With earlier contributions by Erick Engelke of the University of
  14.  *  Waterloo, Waterloo, Ontario, Canada.
  15.  *
  16.  * Last edit
  17.  * 16 June 1993 v3.13
  18.  */
  19. #include "msntcp.h"
  20. #include "msnlib.h"
  21.  
  22. #define    MAXSESSIONS 6
  23. #define MSGBUFLEN 512
  24.  
  25. /* TCP/IP Telnet negotiation support code */
  26. #define IAC     255
  27. #define DONT    254
  28. #define DO      253
  29. #define WONT    252
  30. #define WILL    251
  31. #define SB      250
  32. #define BREAK   243
  33. #define SE      240
  34.  
  35. #define TELOPT_ECHO     1
  36. #define TELOPT_SGA      3
  37. #define TELOPT_STATUS   5
  38. #define TELOPT_TTYPE    24
  39. #define TELOPT_NAWS    31
  40. #define NTELOPTS        24
  41.  
  42. #define BAPICON        0xa0    /* 3Com BAPI, connect to port */
  43. #define BAPIDISC    0xa1    /* 3Com BAPI, disconnect */
  44. #define BAPIWRITE    0xa4    /* 3Com BAPI, write block */
  45. #define BAPIREAD    0xa5    /* 3Com BAPI, read block */
  46. #define BAPIBRK        0xa6    /* 3Com BAPI, send short break */
  47. #define BAPISTAT    0xa7    /* 3Com BAPI, read status (# chars avail) */
  48. #define BAPIHERE    0xaf    /* 3Com BAPI, presence check */
  49. #define BAPIEECM    0xb0    /* 3Com BAPI, enable/disable ECM char */
  50. #define BAPIECM        0xb1    /* 3Com BAPI, trap Enter Command Mode char */
  51. #define BAPIPING    0xb2    /* Send Ping, Kermit extension of BAPI */
  52. #define BAPISTAT_SUC    0    /* function successful */
  53. #define BAPISTAT_NCW    1    /* no character written */
  54. #define BAPISTAT_NCR    2    /* no character read */
  55. #define BAPISTAT_NSS    3    /* no such session */
  56. #define BAPISTAT_NOS    7    /* session aborted */
  57. #define BAPISTAT_NSF    9    /* no such function */
  58.  
  59.  
  60. #define TSBUFSIZ 41
  61. static byte sb[TSBUFSIZ];    /* Buffer for subnegotiations */
  62.  
  63. static byte *termtype;        /* Telnet, used in negotiations */
  64. static int sgaflg = 0;        /* Telnet SGA flag */
  65. static int dosga  = 0;        /* Telnet 1 if I sent DO SGA from tn_ini() */
  66. static int wttflg = 0;        /* Telnet Will Termtype flag */
  67. static int wnawsflg = 0;    /* Telnet Will NAWS option flag */
  68. static ttinccnt = 0;        /* Telnet count of chars in read socket */
  69. static int in_IAC = 0;        /* non-zero when doing Telnet Options*/
  70. int echo = 1;            /* Telnet echo, default on, but hate it */
  71.  
  72. struct    {
  73.     word ident;
  74.     char *name;
  75.     } termarray[]=        /* terminal type names, from mssdef.h */
  76.         {
  77.         {0,"UNKNOWN"}, {1,"H-19"}, {2,"VT52"}, {4,"VT100"},
  78.         {8,"VT102"}, {0x10,"VT220"}, {0x20,"VT320"}, {0x40,"TEK4014"},
  79.         {0x80,"VIP7809"}, {0x100, "PT200"}, {0x200, "D463"},
  80.         {0x400, "D470"}, {0xff,"UNKNOWN"}
  81.         };
  82.  
  83. extern  byte FAR * bapiadr;    /* Far address of local Telnet client's buf */
  84. extern    int bapireq, bapiret;    /* count of chars in/out */
  85. extern    longword my_ip_addr, sin_mask; /* binary of our IP, netmask */
  86. extern    longword ipbcast;    /* binary IP of broadcast */
  87. extern    byte * def_domain;    /* default domain string */
  88. extern    byte * hostname;    /* our name from BootP server */
  89. static    longword host;        /* binary IP of host */
  90. int    doslevel = 1;        /* operating at DOS level if != 0 */
  91. word    sock_delay = 30;
  92. static    tcp_Socket *s;        /* ptr to active socket */
  93. int    msgcnt;            /* count of chars in message buffer */
  94. byte    msgbuf[MSGBUFLEN+1];    /* message buffer for client */
  95.  
  96. extern    int hookvect(void);
  97. extern    int unhookvect(void);
  98. extern    int kecho(int);
  99. extern    void readback(void);
  100. int    session_close(int);
  101. int    session_rotate(int);
  102. int    tn_ini(void);
  103. int    ttinc(void);
  104. int    tn_doop(word);
  105. int    send_iac(byte, int);
  106. int    tn_sttyp(void);
  107. int    tn_snaws(void);
  108. int    subnegotiate(void);
  109. void    optdebug(byte, int);
  110.  
  111. extern byte kmyip[];            /* our IP number */
  112. extern byte knetmask[];            /* our netmask */
  113. extern byte kdomain[];            /* our domain */
  114. extern byte kgateway[];            /* our gateway */
  115. extern byte kns1[];            /* our nameserver #1 */
  116. extern byte kns2[];            /* our nameserver #2 */
  117. extern byte kbcast[];            /* broadcast address pattern */
  118. extern byte khost[];            /* remote host name/IP # */
  119. extern word kport;            /* remote host TCP port */
  120. extern word kserver;            /* if Kermit is in server mode */
  121. extern byte ktttype[];            /* user term type override string */
  122. extern word kterm;            /* terminal type index, from mssdef.h*/
  123. extern byte kterm_lines;        /* terminal screen height */
  124. extern byte kterm_cols;            /* terminal screen width */
  125. extern byte kbtpserver[];        /* IP of Bootp host answering query */
  126. extern byte kdebug;            /* non-zero if debug mode is active */
  127.  
  128. static struct sessioninfo
  129.     {
  130.     tcp_Socket socketdata;        /* current socket storage area */
  131.     int sessionid;            /* identifying session */
  132.     int tn_inited;            /* Telnet Options initied */
  133.     int echo;            /* local echo state (0=remote) */
  134.     } session[MAXSESSIONS];
  135.  
  136. static unsigned num_sessions = 0;        /* qty of active sessions */
  137. static int active = -1;                /* ident of active session */
  138.     
  139. /* this runs at Kermit task level */
  140. int
  141. main(void)                    /* start TCP from Kermit */
  142. {
  143.     int status = 0;
  144.     register byte *p;
  145.     register int i;
  146.  
  147.     doslevel = 1;            /* say at DOS level here */
  148.     if (num_sessions == 0)        /* then initialize the system */
  149.     {
  150.     my_ip_addr = 0L;
  151.     def_domain = kdomain;        /* define our domain */
  152.     host = 0L;        /* BELONGS IN SESSION, but is global */
  153.     for (i = 0; i < MAXSESSIONS; i++)    /* init sessioninfo array */
  154.         session[i].sessionid = -1;
  155.  
  156.     if (hookvect() == 0)
  157.         {
  158.         outs("\r\n Hooking vectors failed");
  159.         goto anyerr;
  160.         }
  161.     
  162.     s = NULL;            /* no TCP socket yet */
  163.     if (tcp_init() == 0)        /* init TCP code */
  164.         {
  165.         outs("\r\n Unable to initialize TCP/IP system, quitting");
  166.         goto anyerr;
  167.         }
  168.  
  169.         /* set Ethernet broadcast to all 1's or all 0's */
  170.     ipbcast = resolve(kbcast);    /* IP broadcast address */
  171.     bootphost = ipbcast;        /* set Bootp to this IP too */
  172.  
  173.     if ((p = strchr(kmyip, ' ')) != NULL)    /* have a space */
  174.         *p = '\0';            /* terminate on the space */
  175.  
  176.     if (stricmp(kmyip, "bootp") == 0)
  177.         {
  178.         if (dobootp() != 0)    /* use BOOTP for Internet address */
  179.             {
  180.             outs("\r\n BOOTP query failed. Quitting.");
  181.             goto anyerr;
  182.             }
  183.         ntoa(kmyip, my_ip_addr);
  184.         if (sin_mask != 0L)
  185.             ntoa(knetmask, sin_mask);
  186.         if (arp_rpt_gateway(0) != 0L)
  187.             ntoa(kgateway, arp_rpt_gateway(0));
  188.         if (last_nameserver > 0) 
  189.             ntoa(kns1, def_nameservers[0]);
  190.         if (last_nameserver > 1)
  191.             ntoa(kns2, def_nameservers[1]);
  192.         if (kdomain[0] == '\0' && hostname[0] != '\0')
  193.                         /* construct domain */
  194.             if (p = strchr(hostname, '.')) /* find dot */
  195.                 strcpy(kdomain, &p[1]);
  196.         readback();    /* readback these to Kermit main */
  197.         }
  198.     else
  199.         {
  200.         if (stricmp(kmyip, "rarp") == 0)
  201.             {
  202.             my_ip_addr = 0L;    /* clear our IP for RARP */
  203.             if (pkt_rarp_init() == 0)
  204.                 goto anyerr;    /* no RARP handle */
  205.  
  206.             if (do_rarp() == 0)     /* use RARP */
  207.                 {
  208.                 outs("\r\n RARP query failed.");
  209.                 goto anyerr;
  210.                 }
  211.             ntoa(kmyip, my_ip_addr);
  212.             readback();
  213.             }
  214.         else                 /* convert to 32 bit binary */
  215.             my_ip_addr = resolve(kmyip);
  216.         }
  217.  
  218.     if (my_ip_addr == 0L)
  219.             {            /* something drastically wrong */
  220.         outs("\r\n Cannot understand my IP address, terminating");
  221.         goto anyerr;
  222.         }
  223.  
  224.     if ((sin_mask = resolve(knetmask)) == 0L)
  225.             {            /* something drastically wrong */
  226.         outs("\r\n Bad network submask, terminating");
  227.         goto anyerr;
  228.         }
  229.  
  230.     if (stricmp(kns1, "unknown"))
  231.         add_server(&last_nameserver, MAX_NAMESERVERS, def_nameservers,
  232.             resolve(kns1));
  233.     if (stricmp(kns2, "unknown"))
  234.         add_server(&last_nameserver, MAX_NAMESERVERS, def_nameservers,
  235.         resolve(kns2));
  236.  
  237.     if (stricmp(kgateway, "unknown"))
  238.         arp_add_gateway(kgateway, 0L);
  239.  
  240.     }    /* end of initial system setup */
  241.  
  242. /* starting a new session */
  243.  
  244.     if (num_sessions >= MAXSESSIONS)
  245.         {
  246.         outs("\nAll sessions are in use. Sorry\n");
  247.         return (-1);        /* say can't do another, fail */
  248.         }
  249.  
  250.     status = -1;
  251.     for (i = 0; i < MAXSESSIONS; i++)    /* find free ident */
  252.         if (session[i].sessionid < 0)
  253.             {
  254.             s = &session[i].socketdata;    /* active socket */
  255.             session[i].sessionid = i;    /* ident */
  256.             status = i;
  257.             active = i;        /* identify active session */
  258.             session[i].echo = echo;    /* Telnet echo from mainpgm */
  259.             session[i].tn_inited = 0; /* do Telnet Options init */
  260.             num_sessions++;    /* say have another active session */
  261.             break;
  262.             }
  263.  
  264.     if (status == -1)
  265.         goto sock_err;        /* report bad news and exit */
  266.     status = 0;
  267.  
  268.     if (ktttype[0] != '\0')        /* if user override given */
  269.         termtype = ktttype;
  270.     else                /* get termtype from real use */
  271.         {
  272.         for (i = 0; termarray[i].ident != 0xff; i++)
  273.                 if (termarray[i].ident == kterm)    /* match */
  274.                 break;
  275.         termtype = termarray[i].name;
  276.         }
  277.  
  278.     /* kserver is set by main body SERVER command. If khost[0] is '*'
  279.     then also behave as a server/listener for any main body mode. */
  280.  
  281.     if ((khost[0] == '*') || (kserver != 0))
  282.         {
  283.         host = 0L;        /* force no host IP at this stage */
  284.         tcp_listen(s, kport, host, 0, 0); /* post a listen */
  285.         }
  286.     else                    /* normal client mode */
  287.         {
  288.         outs("\r\n Resolving address of host ");
  289.         outs(khost); outs(" ...");
  290.         if ((host = resolve(khost)) == 0)
  291.             {
  292.             outs( "\r\n Cannot resolve address of host ");
  293.             outs(khost);
  294.             goto anyerr;
  295.             }
  296.         outs("\r\n");        /* get clean screen line */
  297.         }
  298.  
  299.     if (host == my_ip_addr)
  300.         {
  301.         outs("\r\n Cannot talk to myself, sorry.");
  302.         goto anyerr;
  303.         }
  304.  
  305.     if ((khost[0] == '*') || (kserver != 0)) /* if server */
  306.         {
  307.         doslevel = 0;            /* end of DOS level i/o */
  308.         if (kserver == 0)
  309.             outs("\r\n Operating as a Telnet server. Waiting...");
  310.         }
  311.     else                    /* we are a client */
  312.         {
  313.         if (tcp_open(s, 0, host, kport) == 0)
  314.             {
  315.             outs("\r\n Unable to contact the host.");
  316.             outs("\r\n The host may be down or");
  317.             outs(" a gateway may be needed.");
  318.             goto anyerr;
  319.             }
  320.         sock_wait_established(s, sock_delay, NULL, &status);
  321.         }
  322.  
  323.     if (sock_mode(s, TCP_MODE_NAGLE) == 0)
  324.         {
  325.         outs("\r\n Unable to set socket mode");
  326.         goto anyerr;
  327.         }
  328.     doslevel = 0;            /* end of DOS level i/o */
  329.     return (active);         /* return handle of this session */
  330.  
  331.  
  332. sock_err:
  333.     switch (status)
  334.         {
  335.         case 1 : outs("\r\n Session is closed");
  336.             break;
  337.         case -1:/* outs("\r\n Cannot start a connection");*/
  338.             break;
  339.         }
  340.  
  341. anyerr:    session_close(active);
  342.     if (num_sessions == 0)
  343.         {
  344.         tcp_shutdown();
  345.         eth_release();            /* do absolutely */
  346.         unhookvect();
  347.         doslevel = 1;            /* at DOS level i/o */
  348.         }
  349.     return (-1);                /* say have stopped */
  350. }
  351.  
  352. /* this runs at Kermit task level */
  353. int
  354. exit(int value)                /* stop TCP from Kermit */
  355. {
  356.     int i;
  357.  
  358.     doslevel = 1;            /* tell i/o routines this is DOS */
  359.     for (i = 0; i < MAXSESSIONS; i++)
  360.         session_close(i);    /* close session, free buffers */
  361.     num_sessions = 0;
  362.     tcp_shutdown();            /* force the issue if necessary */
  363.     eth_release();            /* do absolutely */
  364.     unhookvect();
  365.     return (-1);
  366. }
  367.  
  368. /* return the int of the next available session after session "ident", or
  369.    -1 if none. Do not actually change sessions. 
  370. */
  371. int
  372. session_rotate(int ident)
  373. {    
  374.     register int i, j;
  375.     for (i = 1; i <= MAXSESSIONS; i++)
  376.         {
  377.         j = (i + ident) % MAXSESSIONS;    /* modulo maxsessions */
  378.         if (session[j].sessionid != -1)
  379.             return (j);
  380.         }
  381.     return (-1);
  382. }
  383.  
  384. /* Change to session ident "ident" and active to "ident", return "active"
  385.    if that session is valid, else  return -1.
  386. */
  387. int
  388. session_change(int ident)
  389. {                    /* change active session to ident */
  390.  
  391.     if (ident < 0 || ident >= MAXSESSIONS)
  392.         return (-1);
  393.  
  394.     if (session[ident].sessionid < 0)
  395.         return (-1);            /* inactive session */
  396.  
  397.     s = &session[ident].socketdata;        /* watch mid-stream stuff */
  398.     kecho(echo = session[ident].echo);    /* update Kermit main body */
  399.     return (active = ident);        /* ident of active session */
  400. }
  401.  
  402. /* Close session "ident". Return ident of next available session, if any,
  403.    without actually changing sessions. Returns -1 if no more sessions or
  404.    if ident is out of legal range or if the session is already closed.
  405. */
  406. int
  407. session_close(int ident)    /* close a particular, ident, session */
  408. {
  409.     if (ident >= 0 && ident < MAXSESSIONS &&
  410.         session[ident].sessionid >= 0)        /* graceful close */
  411.         {
  412.         sock_close(&session[ident].socketdata);
  413.             /* free receive and send socket data buffers */
  414.             /* this is pushing the tcp timeout a bit much */
  415.         free(session[ident].socketdata.rdata);
  416.         free(session[ident].socketdata.sdata);
  417.                     /* clear pointers to same */
  418.         session[ident].socketdata.rdata = NULL; 
  419.         session[ident].socketdata.sdata = NULL;
  420.         session[ident].sessionid = -1;        /* marked closed */
  421.         session[ident].tn_inited = 0;        /* not inited */
  422.         if (num_sessions > 0) num_sessions--;/* qty active sessions */
  423.         }
  424.                 /* activate, rtn next available session */
  425.     return (session_change(session_rotate(ident)));
  426. }
  427.  
  428. /* This is called by the main body of Kermit to transfer data. It returns
  429.    the transfer status as a BAPI valued int. */
  430.  
  431. int
  432. serial_handler(word cmd)
  433. {
  434.     int i, cmdstatus;
  435.     register int ch;
  436.     byte FAR * bp;
  437.     extern int session_change(int);
  438.  
  439.     if (session[active].tn_inited == 0)    /* if not initialized yet */
  440.         if (tn_ini() == -1)    /* init Telnet negotiations */
  441.             return (BAPISTAT_NOS);    /* fatal error, quit */
  442.  
  443.     tcp_tick(s);            /* catch up on packet reading */
  444.  
  445.     cmdstatus = BAPISTAT_SUC;    /* success so far */
  446.     switch (cmd)            /* cmd is function code */
  447.         {
  448.         case BAPIWRITE:        /* write a block, bapireq chars */
  449.             if (khost[0] == '*' && s->state != tcp_StateESTAB)
  450.                 {
  451.                 bapiret = bapireq; /* discard output and */
  452.                 break;    /* send nothing until client appears*/
  453.                 }
  454.             if (session[active].echo)
  455.                 if (sock_flushnext(s) == 0) /* send next now */
  456.                     cmdstatus = BAPISTAT_NOS; /* no sess*/
  457.             if (s->state == tcp_StateESTAB)
  458.                 bapiret = sock_write(s, bapiadr, bapireq);
  459.             else 
  460.                 {
  461.                 cmdstatus = BAPISTAT_NOS; /* no session */
  462.                 bapiret = bapireq;    /* discard data */
  463.                 break;
  464.                 }
  465.  
  466.         /* if terminal serving with no local echoing do echo here */
  467.             if (session[active].echo == 0 && 
  468.                     khost[0] == '*' && kserver == 0)
  469.                 {         /* echo to us */
  470.                 i = bapiret > MSGBUFLEN-msgcnt? 
  471.                         MSGBUFLEN-msgcnt: bapiret;
  472.                 bcopyff(bapiadr, msgbuf, i);
  473.                 outsn(msgbuf, i);
  474.                 }
  475.             break;
  476.  
  477.         case BAPIREAD:        /* read block, count of bapireq */
  478.                         /* is IAC present? */
  479.             i = fstchr(s->rdata, s->rdatalen, IAC);
  480.             bapiret = 0;
  481.  
  482.             if (i < 0)
  483.                 {
  484.                 cmdstatus = BAPISTAT_NCR;/* nothing present */
  485.                 break;
  486.                 }
  487.             if (i > 0 && in_IAC == 0)  /* read up to IAC */
  488.                 {
  489.                 if (i > bapireq) i = bapireq;
  490.                  bapiret = sock_fastread(s, bapiadr, i);
  491.  
  492.         /* if terminal serving with local echoing then echo to host */
  493.                 if (session[active].echo != 0 && 
  494.                         khost[0] == '*' && 
  495.                         kserver == 0)
  496.                     sock_write(s, bapiadr, bapiret);
  497.                 break;
  498.                 }
  499.  
  500.             bp = bapiadr;    /* use slow reading for Options */
  501.             ttinccnt = 0;    /* setup to read from buffer */
  502.                 /* state is here, IAC style, vs fast mode */
  503.             in_IAC = 1;
  504.             for (i = 0; i < bapireq; i++)
  505.                 {
  506.                 if ((ch = ttinc()) == -1)
  507.                     break;        /* no char */
  508.                 ch &= 0xff;
  509.                     if ( ch == IAC )  /* Telnet options intro */
  510.                     {        /* do Options */
  511.                     if ((ch = ttinc()) == -1)
  512.                         break;    /* do more later */
  513.                     if ((ch &= 0xff) != IAC)
  514.                         {   /* IAC IAC means IAC */
  515.                         tn_doop(ch); /* negotiate */
  516.                         continue;
  517.                         }
  518.                     }
  519.                 in_IAC = 0;         /* end of mode */
  520.                 *bp++ = (byte) (ch & 0xff);
  521.                 bapiret++;
  522.                 }
  523.             session[active].echo = echo;
  524.             break;
  525.  
  526.         case BAPIBRK:            /* send BREAK */
  527.             sock_putc(s, IAC);    /* char to socket buffer */
  528.             sock_putc(s, BREAK);
  529.             if (sock_flush(s) == 0)    /* send it now, with push */
  530.                 cmdstatus = BAPISTAT_NOS; /* no session */
  531.             break;
  532.  
  533.         case BAPIPING:            /* Ping current host */
  534.             do_ping(khost, host);
  535.             break;
  536.  
  537.         case BAPISTAT:        /* check read status (chars avail) */
  538.             bapiret = sock_dataready(s); /* # chars available */
  539.              break;
  540.  
  541.         case BAPIDISC:            /* close this connection */
  542.             session_close(active);
  543.             /* fall through to do session rotate */
  544.         case BAPIECM:
  545.             i = session_rotate(active);    /* get new ident */
  546.             if (i != -1)            /* if exists */
  547.                 i = session_change(i);    /* make active */
  548.             bapiret = 0;    /* no chars processed */
  549.             return (i);    /* New session or -1, special */
  550.  
  551.         default: cmdstatus = BAPISTAT_NSF;/* unsupported function */
  552.             break;
  553.         }
  554.  
  555.     if ((sock_dataready(s) == 0) && (msgcnt == 0) &&  
  556.         (s->sisopen == SOCKET_CLOSED))    /* no data and no session */
  557.             return (BAPISTAT_NOS);    /* means exit session */
  558.     else
  559.         return (cmdstatus);        /* stuff is yet unread */
  560. }
  561.  
  562. /* ttinc   - destructively read char from socket buffer, return -1 if fail */
  563.  
  564. int 
  565. ttinc(void)
  566. {
  567.     byte ch;
  568.     
  569.     if (ttinccnt <= 0)            /* if no known chars */
  570.         {
  571.         tcp_tick(s);            /* read another packet */
  572.         ttinccnt = sock_dataready(s); /* count available, if any */
  573.         }
  574.     if (sock_fastread(s, &ch, 1) != 0)    /* qty chars returned */
  575.         {
  576.         ttinccnt--;            /* one less char in socket */
  577.         return (0xff & ch);
  578.         }
  579.     return (-1);
  580. }
  581.  
  582. /* Initialize a telnet connection */
  583. /* Returns -1 on error, 0 is ok */
  584.  
  585. int 
  586. tn_ini(void)
  587. {
  588.     sgaflg = 0;            /* SGA flag starts out this way */
  589.     wttflg = 0;            /* Did not send WILL TERM TYPE yet. */
  590.     wnawsflg = 0;            /* Did not send NAWS info yet */
  591.     dosga  = 0;            /* Did not send DO SGA yet. */
  592.     in_IAC = 0;            /* haven't done IAC reception yet */
  593.     ttinccnt = 0;            /* count of chars in read socket */
  594.     session[active].tn_inited = 1;    /* say we are doing this proc */
  595.     kecho(echo = 1);         /* start with echo to ourselves */
  596.                 /* if not server or not Telnet port */
  597.     if (khost[0] == '*' || kserver != 0 ||     kport != 23)
  598.             return (0);        /* don't go first */
  599.     if (send_iac( WILL, TELOPT_TTYPE )) return( -1 );
  600.     if (send_iac( WILL, TELOPT_NAWS)) return(-1);
  601.     if (send_iac( DO, TELOPT_SGA )) return( -1 );
  602.     wttflg = 1;            /* Remember I offered TTYPE */
  603.     wnawsflg = 1;            /* Remember I offered NAWS */
  604.     dosga = 1;            /* Remember I sent DO SGA */
  605.     return(0);
  606. }
  607.  
  608. /*
  609.  * send_iac - send interupt character and pertanent stuff
  610.  *        - return 0 on success
  611.  */
  612.  
  613. int
  614. send_iac(byte cmd, int opt)
  615. {
  616.     byte io_data[3];
  617.  
  618.     io_data[0] = IAC;
  619.     io_data[1] = cmd;
  620.     io_data[2] = (byte)(opt & 0xff);
  621.     if (sock_fastwrite(s, io_data, 3) == 0)
  622.         return (1);        /* failed to write */
  623.     if (kdebug != 0)
  624.         {
  625.         outs("Opt send ");
  626.         optdebug(cmd, opt);
  627.         if (cmd != SB) outs("\r\n");
  628.         }
  629.  
  630.     tcp_tick(s);
  631.     return (0);
  632. }
  633.  
  634. /*
  635.  * Process in-band Telnet negotiation characters from the remote host.
  636.  * Call with the telnet IAC character and the current duplex setting
  637.  * (0 = remote echo, 1 = local echo).
  638.  * Returns:
  639.  *  -1 on success or char 0x255 (IAC) when IAC is the first char read here.
  640.  */
  641.  
  642. int 
  643. tn_doop(word ch)
  644. {                    /* enter after reading IAC char */
  645.     register int c, x;
  646.  
  647.         if (ch < SB) return(0);        /* ch is not in range of Options */
  648.  
  649.     if ((x = ttinc()) == -1)    /* read Option character */
  650.         return (-1);        /* nothing there */
  651.     x &= 0xff;
  652.     c = ch;                /* use register'd character */
  653.  
  654.     if (kdebug != 0)
  655.         {
  656.         outs("Opt recv ");
  657.         optdebug((byte)c, x);
  658.         if (c != SB) outs("\r\n");
  659.         }
  660.  
  661.     switch (x) {
  662.       case TELOPT_ECHO:                 /* ECHO negotiation */
  663.     if (c == WILL)            /* Host says it will echo */
  664.         {
  665.         if (echo != 0)        /* reply only if change required */
  666.             {
  667.             send_iac(DO,x);    /* Please do */
  668.             kecho(echo = 0); /* echo is from the other side */
  669.             }
  670.         break;
  671.         }
  672.  
  673.         if (c == WONT)            /* Host says it won't echo */
  674.         {
  675.         if (echo == 0)            /* If we not echoing now */
  676.             {
  677.             send_iac(DONT,x);    /* agree to no host echo */
  678.             kecho(echo = 1);     /* do local echoing */
  679.             }
  680.         break;
  681.         }
  682.  
  683.     if (c == DO)
  684.         {            /* Host wants me to echo to it */
  685.         send_iac(WONT,x);    /* I say I won't */
  686.         break;
  687.             }
  688.     break;                /* do not respond to DONT */
  689.  
  690.       case TELOPT_SGA:                  /* Suppress Go-Ahead */
  691.     if (c == WONT)            /* Host says it won't sup go-aheads */
  692.         {
  693.         if (sgaflg == 0)
  694.             send_iac(DONT, x);    /* acknowledge */
  695.         sgaflg = 1;            /* suppress, remember */
  696.         if (echo == 0)            /* if we're not echoing, */
  697.             kecho(echo = 1);    /* switch to local echo */
  698.         break;
  699.         }
  700.  
  701.         if (c == WILL)            /* Host says it will use go aheads */
  702.         {
  703.         if (sgaflg || !dosga)        /* ACK only if necessary */
  704.             {
  705.             sgaflg = 0;        /* do go-aheads, remember */
  706.             send_iac(DO,x);        /* this is a change, so ACK */
  707.                     }
  708.         break;
  709.             }
  710.     break;                /* no response to other cases */
  711.  
  712.       case TELOPT_TTYPE:                /* Terminal Type */
  713.         switch (c) {
  714.           case DO:                      /* DO terminal type */
  715.         if (wttflg == 0) {        /* If I haven't said so before, */
  716.         send_iac(WILL, x);    /* say I'll send it if asked */
  717.         wttflg++;
  718.         }
  719.         break;
  720.  
  721.           case SB:            /* enter subnegotiations */
  722.         if (wttflg == 0)
  723.             break;            /* we have not been introduced yet */
  724.         if (subnegotiate() != 0)    /* successful negotiation */
  725.         tn_sttyp();        /* report terminal type */
  726.         break;
  727.             
  728.           default:                      /* ignore other TTYPE Options */
  729.         break;
  730.         }                /* end of inner switch (c) */
  731.     break;
  732.  
  733.     case TELOPT_NAWS:        /* terminal width and height */
  734.         switch (c) {
  735.           case DO:                      /* DO terminal type */
  736.         if (wnawsflg == 0) {    /* If I haven't said so before, */
  737.         send_iac(WILL, x);    /* say I'll send it if asked */
  738.         wnawsflg++;
  739.         }
  740.         break;
  741.  
  742.           case SB:            /* enter subnegotiations */
  743.         if (wnawsflg == 0)
  744.             break;            /* we have not been introduced yet */
  745.         if (subnegotiate() != 0)    /* successful negotiation */
  746.         tn_snaws();        /* report screen size */
  747.         break;
  748.  
  749.           default:                      /* ignore other NAWS Options */
  750.         break;
  751.         }                /* end of inner switch (c) */
  752.     break;
  753.  
  754.  
  755.       default:                /* all other Options: refuse nicely */
  756.     switch(c) {
  757.           case WILL:                    /* You will? */
  758.         send_iac(DONT,x);    /* Please don't */
  759.         break;
  760.           case DO:                      /* You want me to? */
  761.         send_iac(WONT,x);    /* I won't */
  762.         break;
  763.  
  764.           case DONT:
  765.         send_iac(WONT,x);    /* I won't */
  766.         break;
  767.  
  768.           case WONT:                    /* You won't? */
  769.         break;            /* Good */
  770.  
  771.       default:
  772.           break;            /* unknown character, discard */
  773.           }                /* end of default switch (c) */
  774.         break;
  775.     }                    /* end switch (x) */
  776.     return (-1);            /* say done with Telnet Options */
  777. }
  778.  
  779. /* Perform Telnet Option subnegotiation. SB byte has been read. Consume
  780.    through IAC SE. Return 1 if successful, else 0.
  781. */
  782. int
  783. subnegotiate(void)
  784. {
  785.     register word flag, y;
  786.      word n;
  787.  
  788.             n = flag = 0;               /* flag for when done reading SB */
  789.             while (n < TSBUFSIZ)
  790.             {            /* loop looking for IAC SE */
  791.                 if ((y = ttinc()) == -1)
  792.             break;        /* nothing there */
  793.         y &= 0xff;              /* make sure it's just 8 bits */
  794.         sb[n++] = (byte) y;    /* save what we got in buffer */
  795.  
  796.         if (kdebug != 0)
  797.             {
  798.             if (y == SE) outs(" se\r\n");
  799.             else
  800.             if (y != IAC)
  801.                 {
  802.                 if (n == 1 && y == 1)
  803.                     outs(" send");
  804.                 else
  805.                     {
  806.                     outs(" \\x"); 
  807.                     outhex((byte)y);
  808.                     }
  809.                 }
  810.             }
  811.  
  812.         if (y == IAC)         /* If this is an IAC */
  813.             {
  814.             if (flag)        /* If previous char was IAC */
  815.                 {
  816.             n--;        /* it's quoted, keep one IAC */
  817.             flag = 0;    /* and turn off the flag. */
  818.             }
  819.             else flag = 1;    /* Otherwise set the flag. */
  820.             }
  821.         else if (flag)      /* Something else following IAC */
  822.             {
  823.             if (y != SE)    /* If not SE, it's a protocol error */
  824.               flag = 0;
  825.             break;
  826.                     }        /* end of if (y == IAC) */
  827.         }            /* end while */
  828.  
  829.         if (flag == 0 || y == -1)    /* no option IAC SE */
  830.            return (0);        /* flag == 0 is invalid SB */
  831.  
  832.         if ( *sb == 1 )        /* wants us to report option */
  833.         return (1);        /* say can do report */
  834.         else
  835.             return (0);
  836. }
  837.  
  838. /* Telnet send terminal type */
  839. /* Returns -1 on error, 0 on success */
  840.  
  841. int 
  842. tn_sttyp(void)
  843. {                            /* Send telnet terminal type. */
  844.     register byte *ttn;
  845.     register int ttnl;        /* Name & length of terminal type. */
  846.  
  847.     ttn = termtype;        /* we already got this from environment */
  848.     if ((*ttn == 0) || ((ttnl = strlen(ttn)) >= TSBUFSIZ)) {
  849.         ttn = "UNKNOWN";
  850.         ttnl = 7;
  851.     }
  852.  
  853.     ttn = strcpy(&sb[1], ttn);        /* Copy to subnegotiation buffer */
  854.     while (*ttn != NULL)
  855.         {
  856.         if (*ttn >= 'a' && *ttn <= 'z') *ttn += (byte)('A' - 'a');
  857.         ttn++;
  858.         }
  859.     *sb    = 0;                /* 'is'... */
  860.     *ttn++ = IAC;
  861.     *ttn   = SE;
  862.  
  863.     send_iac((byte)SB, TELOPT_TTYPE);    /* Send: Terminal Type */
  864.     sock_flushnext(s);            /* send on next write */
  865.     sock_fastwrite(s, sb, ttnl + 3);
  866.     if (kdebug != 0)
  867.         {
  868.     int i;
  869.     outs(" "); for (i = 0; i < ttnl; i++) outch(sb[i+1]);
  870.     outs(" se\r\n");
  871.     }
  872.     return (0);
  873. }
  874.  
  875. /* Send terminal width and height (characters). RFC 1073 */
  876. int
  877. tn_snaws(void)
  878. {
  879.     char sbuf[6] = {0,0, 0,0, (char)IAC, (char)SE};
  880.  
  881.     sbuf[1] = kterm_cols;
  882.     sbuf[3] = kterm_lines;
  883.     send_iac((byte)SB, TELOPT_NAWS);    /* Send: Terminal size */
  884.     sock_flushnext(s);            /* send on next write */
  885.     sock_fastwrite(s, sbuf, 6);
  886.         if (kdebug != 0)
  887.             {
  888.         outs(" \\x"); 
  889.         outhex(sbuf[1]);
  890.         outs(" \\x"); 
  891.         outhex(sbuf[3]);
  892.         outs(" se\r\n");
  893.         }
  894.     return (0);
  895. }
  896.  
  897. /* assist displaying of Telnet Options negotiation material */
  898. void
  899. optdebug(byte cmd, int option)
  900. {
  901.     switch (cmd)
  902.         {
  903.         case WILL: outs("will ");
  904.                 break;
  905.         case WONT: outs("wont ");
  906.                 break;
  907.         case DO: outs("do ");
  908.                 break;
  909.         case DONT: outs("dont ");
  910.                 break;
  911.         case SB: outs("sb ");
  912.                 break;
  913.         default: outs("\\x "); outhex(cmd);
  914.                 break;
  915.         }            /* end of switch c */
  916.     switch (option)
  917.         {
  918.         case TELOPT_ECHO: outs("echo");
  919.                 break;
  920.         case TELOPT_SGA: outs("sga");
  921.                 break;
  922.         case TELOPT_TTYPE: outs("ttype");
  923.                 break;
  924.         case TELOPT_NAWS: outs("naws");
  925.                 break;
  926.         default: outs("\\x"); outhex((byte)option);
  927.                 break;
  928.         }
  929. }
  930.  
  931.  
  932. /* Compose a nice greeting message for incoming Telnet connections. Called
  933.    by tcp_handler() in the tcp_StateLISTEN section. It also notifies the
  934.    local terminal emulator of the client's presence and address. */
  935.  
  936. void
  937. server_hello(tcp_Socket *s)
  938. {
  939.     char hellomsg[MSGBUFLEN];        /* work buffer, keep short */
  940.     register int len;
  941.  
  942.     strcpy(hellomsg,
  943.         "\r\n Welcome to the MS-DOS Kermit Telnet server at [");
  944.     ntoa(&hellomsg[strlen(hellomsg)], my_ip_addr);        /* our IP */
  945.     strcat(hellomsg, "].\r\n");        /* as [dotted decimal] */
  946.     if (kserver != 0)            /* if file serving */
  947.         {
  948.         strcat(hellomsg," Escape back to your Kermit prompt and");
  949.         strcat(hellomsg," issue Kermit file server commands.\r\n\n");
  950.         }
  951.     else                    /* if terminal emulating */
  952.         {
  953.         strcat(hellomsg,
  954.             " You are talking to the terminal emulator,\r\n");
  955.         strcat(hellomsg, " adjust local echoing accordingly.\r\n");
  956.         }
  957.  
  958.         /* stuff string in socket buffer, adjust socket data length */
  959.     bcopyff(hellomsg, &s->sdata[s->sdatalen], 
  960.                 len = strlen(hellomsg));
  961.     s->sdatalen += len;
  962.                 /* tell main body the news */
  963.     strcpy(hellomsg, "\r\n Connection starting from [");
  964.     ntoa(&hellomsg[strlen(hellomsg)], s->hisaddr);    /* their IP */
  965.     strcat(hellomsg, "].\r\n");
  966.     outs(hellomsg);        /* send connection info to main body */
  967. }
  968.     
  969.