home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / mskermit / msntnd.c < prev    next >
C/C++ Source or Header  |  2020-01-01  |  35KB  |  1,243 lines

  1. /* File MSNTND.C
  2.  * Telnet driver
  3.  *
  4.  *    Copyright (C) 1982, 1997, Trustees of Columbia University in the 
  5.  *    City of New York.  The MS-DOS Kermit software may not be, in whole 
  6.  *    or in part, licensed or sold for profit as a software product itself,
  7.  *    nor may it be included in or distributed with commercial products
  8.  *    or otherwise distributed by commercial concerns to their clients 
  9.  *    or customers without written permission of the Office of Kermit 
  10.  *    Development and Distribution, Columbia University.  This copyright 
  11.  *    notice must not be removed, altered, or obscured.
  12.  *
  13.  * Written for MS-DOS Kermit by Joe R. Doupnik, 
  14.  *  Utah State University, jrd@cc.usu.edu, jrd@usu.Bitnet,
  15.  *  and by Frank da Cruz, Columbia Univ., fdc@watsun.cc.columbia.edu.
  16.  * With earlier contributions by Erick Engelke of the University of
  17.  *  Waterloo, Waterloo, Ontario, Canada.
  18.  *
  19.  * Last edit
  20.  * 12 Jan 1995 v3.14
  21.  */
  22. #include "msntcp.h"
  23. #include "msnlib.h"
  24.  
  25. #define    MAXSESSIONS 6
  26. #define MSGBUFLEN 512
  27.  
  28. /* TCP/IP Telnet negotiation support code */
  29. #define IAC     255
  30. #define DONT    254
  31. #define DO      253
  32. #define WONT    252
  33. #define WILL    251
  34. #define SB      250
  35. #define AYT    246
  36. #define BREAK   243
  37. #define SE      240
  38. #define EOR    239
  39.  
  40. #define    TELOPT_BINARY    0
  41. #define TELOPT_ECHO     1
  42. #define TELOPT_SGA      3
  43. #define TELOPT_STATUS   5
  44. #define TELOPT_TTYPE    24
  45. #define TELOPT_EOR    25
  46. #define TELOPT_NAWS    31
  47.  
  48. #define BIN_REJECT    0    /* Binary mode, decline option */
  49. #define BIN_REQUEST    1    /* Binary mode, option has been requested */
  50. #define BIN_RESPONSE    2    /* Binary mode, option has been answered */
  51.  
  52. #define BAPICON        0xa0    /* 3Com BAPI, connect to port */
  53. #define BAPIDISC    0xa1    /* 3Com BAPI, disconnect */
  54. #define BAPIWRITE    0xa4    /* 3Com BAPI, write block */
  55. #define BAPIREAD    0xa5    /* 3Com BAPI, read block */
  56. #define BAPIBRK        0xa6    /* 3Com BAPI, send short break */
  57. #define BAPISTAT    0xa7    /* 3Com BAPI, read status (# chars avail) */
  58. #define BAPIHERE    0xaf    /* 3Com BAPI, presence check */
  59. #define BAPIEECM    0xb0    /* 3Com BAPI, enable/disable ECM char */
  60. #define BAPIECM        0xb1    /* 3Com BAPI, trap Enter Command Mode char */
  61. #define BAPIPING    0xb2    /* Send Ping, Kermit extension of BAPI */
  62. #define BAPITO_3270    0xb3    /* Write to 3270, Kermit extension of BAPI */
  63. #define BAPINAWS    0xb4    /* Send NAWS update, Kermit extension */
  64.  
  65. #define BAPISTAT_SUC    0    /* function successful */
  66. #define BAPISTAT_NCW    1    /* no character written */
  67. #define BAPISTAT_NCR    2    /* no character read */
  68. #define BAPISTAT_NSS    3    /* no such session */
  69. #define BAPISTAT_NOS    7    /* session aborted */
  70. #define BAPISTAT_NSF    9    /* no such function */
  71.  
  72. #define    SUCCESS        0    /* tcp_status for main body of Kermit */
  73. #define NO_DRIVER    1
  74. #define NO_LOCAL_ADDRESS 2
  75. #define BOOTP_FAILED    3
  76. #define RARP_FAILED    4
  77. #define BAD_SUBNET_MASK 5
  78. #define SESSIONS_EXCEEDED 6
  79. #define HOST_UNKNOWN    7
  80. #define HOST_UNREACHABLE 8
  81. #define CONNECTION_REJECTED 9
  82.  
  83. #define TSBUFSIZ 41
  84. static byte sb[TSBUFSIZ];    /* Buffer for subnegotiations */
  85.  
  86. static byte *termtype;        /* Telnet, used in negotiations */
  87. /*static int sgaflg = 0;        /* Telnet SGA flag */
  88.  
  89.  
  90. int sgaflg = 0;            /* Telnet SGA flag */
  91. static int dosga  = 0;        /* Telnet 1 if I sent DO SGA from tn_ini() */
  92. static int wttflg = 0;        /* Telnet Will Termtype flag */
  93. static int doEOR = 0;        /* Telnet EOR done if not zero */
  94. byte echo = 1;            /* Telnet echo, default on, but hate it */
  95. byte bootmethod = BOOT_FIXED;    /* Boot method (fixed, bootp, rarp) */
  96.  
  97. struct    {
  98.     word ident;
  99.     char *name;
  100.     } termarray[]=        /* terminal type names, from mssdef.h */
  101.         {
  102.         {0,"UNKNOWN"}, {1,"H-19"}, {2,"VT52"}, {4,"VT100"},
  103.         {8,"VT102"}, {0x10,"VT220"}, {0x20,"VT320"}, {0x40,"TEK4014"},
  104.         {0x80,"VIP7809"}, {0x100, "PT200"}, {0x200, "D463"},
  105.         {0x400, "D470"}, {0x800, "wyse50"},
  106.         {0x2000, "ANSI"}, {0xff,"UNKNOWN"}
  107.         };
  108.  
  109. extern  byte FAR * bapiadr;    /* Far address of local Telnet client's buf */
  110. extern    int bapireq, bapiret;    /* count of chars in/out */
  111. extern    longword my_ip_addr, sin_mask; /* binary of our IP, netmask */
  112. extern    longword ipbcast;    /* binary IP of broadcast */
  113. extern    byte * def_domain;    /* default domain string */
  114. extern    byte * hostname;    /* our name from BootP server */
  115. static    longword host;        /* binary IP of host */
  116. byte    tempip[18] = {0};    /* for IP string from NET.CFG, count, string*/
  117. byte    dobinary = 0;        /* local NVT-ASCII (0) or Binary (1) mode */
  118. byte    inbinary = 0;        /* incoming Binary, Option status */
  119. byte    outbinary = 0;        /* outgoing Binary, Option status */
  120. byte    do_greeting;        /* non-zero to show Telnet server herald */
  121. int    doslevel = 1;        /* operating at DOS level if != 0 */
  122. static    tcp_Socket *s;        /* ptr to active socket */
  123. int    msgcnt = 0;        /* count of chars in message buffer */
  124. byte    msgbuf[MSGBUFLEN+1] = {0}; /* message buffer for client */
  125.  
  126. extern    int hookvect(void);
  127. extern    int unhookvect(void);
  128. extern    void kecho(byte);
  129. extern    void kmode(byte);
  130. extern    void readback(void);
  131. extern    void get_kscreen(void);
  132.  
  133. int    session_close(int);
  134. int    session_rotate(int);
  135. void    session_cleanout(void);
  136. int    tn_ini(void);
  137. int    ttinc(void);
  138. int    tn_doop(word);
  139. int    send_iac(byte, int);
  140. int    tn_sttyp(void);
  141. int    tn_snaws(void);
  142. int    subnegotiate(void);
  143. void    optdebug(byte, int);
  144. void    server_hello(tcp_Socket *);
  145.  
  146. extern byte kmyip[];            /* our IP number */
  147. extern byte knetmask[];            /* our netmask */
  148. extern byte kdomain[];            /* our domain */
  149. extern byte kgateway[];            /* our gateway */
  150. extern byte kns1[];            /* our nameserver #1 */
  151. extern byte kns2[];            /* our nameserver #2 */
  152. extern byte kbcast[];            /* broadcast address pattern */
  153. extern byte khost[];            /* remote host name/IP # */
  154. extern word kport;            /* remote host TCP port */
  155. extern word kserver;            /* if Kermit is in server mode */
  156. extern byte ktttype[];            /* user term type override string */
  157. extern byte kterm;            /* terminal type index, from mssdef.h*/
  158. extern byte kterm_lines;        /* terminal screen height */
  159. extern word kterm_cols;            /* terminal screen width */
  160. extern byte kbtpserver[];        /* IP of Bootp host answering query */
  161. extern byte kdebug;            /* non-zero if debug mode is active */
  162. extern byte ktnmode;            /* 0=NVT-ASCII, 1=BINARY */
  163. extern word tcp_status;            /* report errors and > 0 */
  164.  
  165. static struct sessioninfo
  166.     {
  167.     tcp_Socket socketdata;        /* current socket storage area */
  168.     int sessionid;            /* identifying session */
  169.     int tn_inited;            /* Telnet Options inited */
  170.     byte echo;            /* local echo state (0=remote) */
  171.     byte dobinary;            /* mode NVT-ASCII (0) or Binary (1) */
  172.     byte server_mode;        /* if Telnet server */
  173.     byte nawsflg;            /* NAWS flag (1=offered, 2=wanted) */
  174.     } session[MAXSESSIONS];
  175.  
  176. static int num_sessions = 0;        /* qty of active sessions */
  177. static int active = -1;            /* ident of active session */
  178.     
  179. /* this runs at Kermit task level */
  180. int
  181. tnmain(void)                    /* start TCP from Kermit */
  182. {
  183.     int status = 0;
  184.     register byte *p;
  185.     register int i;
  186.  
  187.     doslevel = 1;            /* at DOS level i/o */
  188.     tcp_status = SUCCESS;        /* assume all this works */
  189.  
  190.     if (num_sessions == 0)        /* then initialize the system */
  191.     {
  192.     my_ip_addr = 0L;
  193.     def_domain = kdomain;        /* define our domain */
  194.     host = 0L;        /* BELONGS IN SESSION, but is global */
  195.     msgcnt = 0;
  196.  
  197.     for (i = 0; i < MAXSESSIONS; i++)    /* init sessioninfo array */
  198.         {
  199.         session[i].sessionid = -1;
  200.         session[i].socketdata.sisopen = SOCKET_CLOSED;
  201.         }
  202.  
  203.     if (hookvect() == 0)
  204.         {
  205.         outs("\r\n Hooking vectors failed");
  206.         tcp_status = NO_DRIVER;
  207.         goto anyerr;
  208.         }
  209.     
  210.     s = NULL;            /* no TCP socket yet */
  211.     if (tcp_init() == 0)        /* init TCP code */
  212.         {
  213.         outs("\r\n Unable to initialize TCP/IP system, quitting");
  214.         tcp_status = NO_DRIVER;
  215.         goto anyerr;
  216.         }
  217.  
  218.     if (tempip[1] != '\0' && stricmp(kmyip, "Telebit-PPP") == 0)
  219.         strcpy(kmyip, &tempip[1]);    /* our IP from NET.CFG */
  220.  
  221.     if (kdebug)        /* can debug to file */
  222.         doslevel = 0;    /* say not at DOS level, to buffer msgs */
  223.  
  224.         /* set Ethernet broadcast to all 1's or all 0's */
  225.     ipbcast = resolve(kbcast);    /* IP broadcast address */
  226.     bootphost = ipbcast;        /* set Bootp to this IP too */
  227.  
  228.     if ((p = strchr(kmyip, ' ')) != NULL)    /* have a space */
  229.         *p = '\0';            /* terminate on the space */
  230.  
  231.     if (kmyip == NULL)
  232.         {
  233.         outs("\r\n No TCP/IP address specified for this station.");
  234.         tcp_status = NO_LOCAL_ADDRESS;
  235.         goto anyerr;
  236.         }
  237.  
  238.     if (stricmp(kmyip, "bootp") == 0) bootmethod = BOOT_BOOTP;
  239.     if (stricmp(kmyip, "DHCP") == 0) bootmethod = BOOT_DHCP;
  240.     if (bootmethod == BOOT_BOOTP || bootmethod == BOOT_DHCP)
  241.         {
  242.         if (do_bootp() != 0)
  243.             {
  244.             outs("\r\n Bootp/DHCP query failed. Quitting.");
  245.             tcp_status = BOOTP_FAILED;
  246.             goto anyerr;
  247.             }
  248.  
  249.         ntoa(kmyip, my_ip_addr);
  250.         if (sin_mask != 0L)
  251.             ntoa(knetmask, sin_mask);
  252.         if (arp_rpt_gateway(0) != 0L)
  253.             ntoa(kgateway, arp_rpt_gateway(0));
  254.         if (last_nameserver > 0) 
  255.             ntoa(kns1, def_nameservers[0]);
  256.         if (last_nameserver > 1)
  257.             ntoa(kns2, def_nameservers[1]);
  258.         if (kdomain[0] == '\0' && hostname[0] != '\0')
  259.                         /* construct domain */
  260.             if (p = strchr(hostname, '.')) /* find dot */
  261.                 strcpy(kdomain, &p[1]);
  262.         }
  263.  
  264.     if (stricmp(kmyip, "rarp") == 0 || bootmethod == BOOT_RARP)
  265.         {
  266.         if (pkt_rarp_init() == 0)
  267.             {
  268.             tcp_status = NO_DRIVER;
  269.             goto anyerr;    /* no RARP handle */
  270.             }
  271.  
  272.         if (do_rarp() == 0)     /* use RARP */
  273.             {
  274.             outs("\r\n RARP query failed.");
  275.             tcp_status = RARP_FAILED;
  276.             goto anyerr;
  277.             }
  278.         ntoa(kmyip, my_ip_addr);
  279.         bootmethod = BOOT_RARP;
  280.         }
  281.  
  282.     if ((my_ip_addr = resolve(kmyip)) == 0L)
  283.             {            /* something drastically wrong */
  284.         outs("\r\n Cannot understand my IP address, terminating");
  285.         tcp_status = NO_LOCAL_ADDRESS;
  286.         goto anyerr;
  287.         }
  288.     ntoa(kmyip, my_ip_addr);    /* binary to dotted decimal */
  289.     readback();
  290.  
  291.     if ((sin_mask = resolve(knetmask)) == 0L)
  292.             {            /* something drastically wrong */
  293.         outs("\r\n Bad network submask, terminating");
  294.         tcp_status = BAD_SUBNET_MASK;
  295.         goto anyerr;
  296.         }
  297.  
  298.     if (stricmp(kns1, "unknown"))
  299.         add_server(&last_nameserver, MAX_NAMESERVERS, def_nameservers,
  300.             resolve(kns1));
  301.     if (stricmp(kns2, "unknown"))
  302.         add_server(&last_nameserver, MAX_NAMESERVERS, def_nameservers,
  303.         resolve(kns2));
  304.  
  305.     if (stricmp(kgateway, "unknown"))
  306.         arp_add_gateway(kgateway, 0L);
  307.  
  308.     }    /* end of initial system setup */
  309.  
  310.  
  311. /* starting a new session */
  312.     session_cleanout();        /* clean out deceased sessions */
  313.     msgcnt = 0;
  314.  
  315.     if (num_sessions >= MAXSESSIONS)
  316.         {
  317.         tcp_status = SESSIONS_EXCEEDED;
  318.         outs("\nAll sessions are in use. Sorry\n");
  319.         return (-1);        /* say can't do another, fail */
  320.         }
  321.  
  322.     status = -1;
  323.     for (i = 0; i < MAXSESSIONS; i++)    /* find free ident */
  324.         if (session[i].sessionid < 0 && 
  325.                 session[i].socketdata.sisopen == SOCKET_CLOSED)
  326.             {
  327.             s = &session[i].socketdata;    /* active socket */
  328.             s->sisopen = SOCKET_CLOSED;
  329.             session[i].sessionid = i;    /* ident */
  330.             status = i;
  331.             active = i;        /* identify active session */
  332.             session[i].echo = echo = 1;/* Telnet echo from mainpgm */
  333.                          /* mode, assume NVT-ASCII */
  334.             session[i].dobinary = dobinary = 0;
  335.             session[i].tn_inited = 0; /* do Telnet Options init */
  336.             session[i].server_mode = 0; /* assume not server */
  337.             num_sessions++;    /* say have another active session */
  338.             break;
  339.             }
  340.  
  341.     if (status == -1)
  342.         {
  343.         tcp_status = SESSIONS_EXCEEDED;
  344.         goto sock_err;        /* report bad news and exit */
  345.         }
  346.     status = 0;
  347.  
  348.     if (ktttype[0] != '\0')        /* if user override given */
  349.         termtype = ktttype;
  350.     else                /* get termtype from real use */
  351.         {
  352.         for (i = 0; termarray[i].ident != 0xff; i++)
  353.                 if (termarray[i].ident == kterm)    /* match */
  354.                 break;
  355.         termtype = termarray[i].name;
  356.         }
  357.  
  358.     /* kserver is set by main body SERVER command. If khost[0] is '*'
  359.     then also behave as a server/listener for any main body mode. */
  360.  
  361.     if ((khost[0] == '*') || (kserver != 0))
  362.         {
  363.         if (khost[0] == '*')
  364.             host = 0L;        /* force no host IP */
  365.         else
  366.             {
  367.             outs("\r\n Resolving address of host ");
  368.             outs(khost); outs(" ...");
  369.             if ((host = resolve(khost)) == 0)
  370.                 {
  371.                 outs( "\r\n Cannot resolve address of host ");
  372.                 outs(khost);
  373.                 tcp_status = HOST_UNKNOWN;
  374.                 goto anyerr;
  375.                 }
  376.             }
  377.         session[active].server_mode = 1;  /* say being a server */
  378.         tcp_listen(s, kport, host, 0, 0); /* post a listen */
  379.         }
  380.     else                    /* normal client mode */
  381.         {
  382.         session[active].server_mode = 0;  /* say being a client */
  383.         outs("\r\n Resolving address of host ");
  384.         outs(khost); outs(" ...");
  385.         if ((host = resolve(khost)) == 0)
  386.             {
  387.             outs( "\r\n Cannot resolve address of host ");
  388.             outs(khost);
  389.             tcp_status = HOST_UNKNOWN;
  390.             goto anyerr;
  391.             }
  392.         outs("\r\n");        /* get clean screen line */
  393.         }
  394.  
  395.     if ((host == my_ip_addr) || ((host & 0x7f000000L) == 0x7f000000L))
  396.         {
  397.         outs("\r\n Cannot talk to myself, sorry.");
  398.         tcp_status = HOST_UNREACHABLE;
  399.         goto anyerr;
  400.         }
  401.     do_greeting = FALSE;        /* Telnet server greeting msg */
  402.  
  403.     if (session[active].server_mode != 0) /* if server */
  404.         {
  405.         doslevel = 0;            /* end of DOS level i/o */
  406.         if (kserver == 0)
  407.             outs("\r\n Operating as a Telnet server. Waiting...");
  408.         do_greeting = TRUE;
  409.         }
  410.     else                    /* we are a client */
  411.         {
  412.         if (tcp_open(s, 0, host, kport) == 0)
  413.             {
  414.             outs("\r\n Unable to contact the host.");
  415.             outs("\r\n The host may be down or");
  416.             outs(" a gateway may be needed.");
  417.             tcp_status = HOST_UNREACHABLE;
  418.             goto anyerr;
  419.             }
  420.         sock_wait_established(s, 30, NULL, &status);
  421.         }
  422.  
  423.     doslevel = 0;            /* end of DOS level i/o */
  424.     return (active);         /* return handle of this session */
  425.  
  426.  
  427. sock_err:
  428.     switch (status)
  429.         {
  430.         case 1 : outs("\r\n Session is closed");
  431.             break;
  432.         case -1:/* outs("\r\n Cannot start a connection");*/
  433.             break;
  434.         }
  435.  
  436. anyerr:    session_close(active);
  437.     if (num_sessions <= 0)
  438.         {
  439.         tcp_shutdown();
  440.         eth_release();            /* do absolutely */
  441.         unhookvect();
  442.         msgcnt = 0;            /* clear message buffer */
  443.         doslevel = 1;            /* at DOS level i/o */
  444.         }
  445.     return (-1);                /* say have stopped */
  446. }
  447.  
  448. /* this runs at Kermit task level */
  449. int
  450. tnexit(int value)            /* stop TCP from Kermit */
  451. {
  452.     register int i;
  453.  
  454.     for (i = 0; i < MAXSESSIONS; i++)
  455.         session_close(i);    /* close session, free buffers */
  456.     num_sessions = 0;
  457.     tcp_shutdown();            /* force the issue if necessary */
  458.     eth_release();            /* do absolutely */
  459.     unhookvect();
  460.     msgcnt = 0;            /* clear message buffer */
  461.     return (-1);
  462. }
  463.  
  464. /* return the int of the next available session after session "ident", or
  465.    return -1 if none. Do not actually change sessions. 
  466. */
  467. int
  468. session_rotate(int ident)
  469. {    
  470.     register int i, j;
  471.     
  472.     session_cleanout();        /* clean out deceased sessions */
  473.     if (ident == -1) ident = 0;
  474.  
  475.     for (i = 1; i <= MAXSESSIONS; i++)
  476.         {
  477.         j = (i + ident) % MAXSESSIONS;    /* modulo maxsessions */
  478.         if (session[j].sessionid != -1)     /* if active */
  479.             return (j);
  480.         }
  481.     return (-1);
  482. }
  483.  
  484. /* Change to session ident "ident" and active to "ident", return "active"
  485.    if that session is valid, else  return -1.
  486. */
  487. int
  488. session_change(int ident)
  489. {                    /* change active session to ident */
  490.     if (ident == -1 || ident >= MAXSESSIONS)
  491.         return (-1);
  492.  
  493.     if (session[ident].sessionid == -1)
  494.         return (-1);            /* inactive session */
  495.  
  496.     s = &session[ident].socketdata;        /* watch mid-stream stuff */
  497.     kecho(echo = session[ident].echo);    /* update Kermit main body */
  498.     kmode(dobinary = session[ident].dobinary); /* mode */
  499.     return (active = ident);        /* ident of active session */
  500. }
  501.  
  502. /* Close session "ident". Return ident of next available session, if any,
  503.    without actually changing sessions. Returns -1 if no more sessions or
  504.    if ident is out of legal range or if the session is already closed.
  505. */
  506. int
  507. session_close(int ident)    /* close a particular, ident, session */
  508. {
  509.     register tcp_Socket *s;
  510.  
  511.     if (ident != -1 && ident < MAXSESSIONS &&
  512.         session[ident].sessionid != -1)        /* graceful close */
  513.         {
  514.         s = &session[ident].socketdata;
  515.         s->rdatalen = 0;        /* flush read buffer */
  516.         sock_close(s);
  517.         }
  518.     session_cleanout();    /* clean out deceased sessions */
  519.                 /* activate, rtn next available session */
  520.     return (session_change(session_rotate(ident)));
  521. }
  522.  
  523. /* Adjust num_sessions to reflect deceased sessions.
  524.    Memory freeing is done here to ensure we are not within DOS.
  525. */
  526. void
  527. session_cleanout(void)
  528. {
  529.     register int i;
  530.     register tcp_Socket *s;
  531.  
  532.     for (i = 0; i < MAXSESSIONS; i++)    /* clean out old sessions */
  533.         if ((session[i].sessionid != -1) &&
  534.             (session[i].socketdata.sisopen == SOCKET_CLOSED))
  535.             {
  536.             num_sessions--;        /* qty active sessions */
  537.             session[i].sessionid = -1; /* user level closed */
  538.             s = &session[i].socketdata;
  539.             if (s->sdata != NULL)
  540.                 {
  541.                 free(s->sdata);        /* free send buffer */
  542.                 s->sdata = NULL;    /* clear pointer */
  543.                 s->sdatalen = 0;
  544.                 }
  545.             if (s->rdata != NULL)
  546.                 {
  547.                 free(s->rdata);        /* free recv buffer */
  548.                 s->rdata = NULL;    /* clear pointer */
  549.                 s->rdatalen = 0;
  550.                 }
  551.             }
  552. }
  553.  
  554. /* This is called by the main body of Kermit to transfer data. It returns
  555.    the transfer status as a BAPI valued int. */
  556.  
  557. int
  558. serial_handler(word cmd)
  559. {
  560.     int cmdstatus;
  561.     register int i, ch;
  562.     extern int session_change(int);
  563.  
  564.     if (session[active].tn_inited == 0)    /* if not initialized yet */
  565.         if (tn_ini() == -1)    /* init Telnet negotiations */
  566.             return (BAPISTAT_NOS);    /* fatal error, quit */
  567.  
  568.     tcp_tick(s);            /* catch up on packet reading */
  569.  
  570.     cmdstatus = BAPISTAT_SUC;    /* success so far */
  571.  
  572.     if (do_greeting == TRUE && session[active].server_mode && 
  573.         s->state == tcp_StateESTAB)
  574.         {
  575.         do_greeting = FALSE;
  576.         server_hello(s);    /* send greeting message */
  577.         }
  578.  
  579.     switch (cmd)            /* cmd is function code */
  580.         {
  581.         case BAPIWRITE:        /* write a block, bapireq chars */
  582.             if (session[active].server_mode != 0 
  583.                     && s->state != tcp_StateESTAB)
  584.                 {
  585.                 bapiret = bapireq; /* discard output and */
  586.                 break;    /* send nothing until client appears*/
  587.                 }
  588.             if (s->state == tcp_StateESTAB)
  589.                 {
  590.                 bapiret =  sock_write(s, bapiadr, bapireq);
  591.                 if (bapiret == -1)     /* no session */
  592.                     cmdstatus = BAPISTAT_NOS;
  593.                 }
  594.             else 
  595.                 {
  596.                 cmdstatus = BAPISTAT_NOS; /* no session */
  597.                 bapiret = bapireq;    /* discard data */
  598.                 break;
  599.                 }
  600.  
  601.         /* if terminal serving with no local echoing do echo here */
  602.             if (session[active].echo == 0 && 
  603.                     session[active].server_mode != 0 && 
  604.                         kserver == 0)
  605.                 {         /* echo to us */
  606.                 i = bapiret > MSGBUFLEN-msgcnt? 
  607.                         MSGBUFLEN-msgcnt: bapiret;
  608.                 bcopyff(bapiadr, msgbuf, i);
  609.                 outsn(msgbuf, i);
  610.                 }
  611.             break;
  612.  
  613.         case BAPIREAD:        /* read block, count of bapireq */
  614.             bapiret = 0;
  615.             /* i = byte count in buffer preceeding an IAC */
  616.             /* first, shorten search if bapireq < data in buf */
  617.             if (s->rdatalen == 0)
  618.                 {
  619.                 cmdstatus = BAPISTAT_NCR;/* nothing present */
  620.                 break;
  621.                 }
  622.  
  623.             i = (bapireq > s->rdatalen)? s->rdatalen: bapireq;
  624.             i = fstchr(s->rdata, i, IAC);
  625.             if (i < 0)    /* negative -> nothing present */
  626.                 {
  627.                 cmdstatus = BAPISTAT_NCR;/* nothing present */
  628.                 break;
  629.                 }
  630.  
  631.             if (i > 0)              /* read up to IAC */
  632.                 {
  633.                 if (i > bapireq)    /* safety check */
  634.                     i = bapireq;
  635.                  bapiret = sock_fastread(s, bapiadr, i);
  636.  
  637.         /* if terminal serving with local echoing then echo to host */
  638.                 if (session[active].echo != 0 && 
  639.                     session[active].server_mode != 0 &&
  640.                         kserver == 0)
  641.                     sock_write(s, bapiadr, bapiret);
  642.                 break;
  643.                 }
  644.  
  645.         /* i = 0. IAC is at start of buffer, get escaped byte(s) */
  646.         /* Delay until two bytes, or three for Telnet Options */
  647.             if ((s->rdatalen < 2) || (s->rdatalen == 2 && 
  648.                 s->rdata[1] >= SB && s->rdata[1] != IAC))
  649.                 {
  650.                 cmdstatus = BAPISTAT_NCR;/* nothing present */
  651.                 break;
  652.                 }
  653.     
  654.             if ((ch = ttinc()) == -1)    /* read the IAC */
  655.                      break;        /* no char */
  656.             ch &= 0xff;
  657.             if (ch != IAC)            /* not an IAC */
  658.                 {
  659.                 *bapiadr = (byte) ch;
  660.                 bapiret++;
  661.                 break;
  662.                 }
  663.                           /* get escaped byte */
  664.             if ((ch = ttinc()) == -1)
  665.                 break;        /* none present */
  666.             switch (ch &= 0xff)    /* dispatch on escaped byte */
  667.                 {
  668.                 case AYT:    /* Are You There */
  669.                         sock_write(s, "Yes\r\n", 5);
  670.                     break;
  671.                 case IAC:    /* IAC IAC yields just IAC */
  672.                     *bapiadr = (byte) ch;
  673.                     bapiret++;
  674.                     break;
  675.                 default:
  676.                     if (ch < SB)    /* ignore if not */
  677.                         break;    /*  an Option */
  678.                     tn_doop(ch);    /* do Options */
  679.                     session[active].echo = echo;
  680.                     session[active].dobinary = dobinary;
  681.                     break;
  682.                 }
  683.             break;
  684.  
  685.         case BAPIBRK:            /* send BREAK */
  686.             {
  687.             byte cmd[2];
  688.  
  689.             cmd[0] = IAC; cmd[1] = BREAK;
  690.             if (sock_write(s, cmd, 2) == -1)
  691.                 cmdstatus = BAPISTAT_NOS; /* no session */
  692.             break;
  693.             }
  694.         case BAPIPING:            /* Ping current host */
  695.             do_ping(khost, host);
  696.             break;
  697.  
  698.         case BAPINAWS:    /* if want to send screen size update */
  699.             if (session[active].nawsflg == 2) /* if wanted */
  700.                 tn_snaws();    /* send NAWS Option */
  701.             break;
  702.  
  703.         case BAPISTAT:        /* check read status (chars avail) */
  704.             bapiret = sock_dataready(s); /* # chars available */
  705.              break;
  706.  
  707.         case BAPIDISC:            /* close this connection */
  708.             session_close(active);
  709.             /* fall through to do session rotate */
  710.         case BAPIECM:
  711.             i = session_rotate(active);    /* get new ident */
  712.             if (i != -1)            /* if exists */
  713.                 i = session_change(i);    /* make active */
  714.             bapiret = 0;    /* no chars processed */
  715.             return (i);    /* New session or -1, special */
  716.  
  717.         default: cmdstatus = BAPISTAT_NSF;/* unsupported function */
  718.             break;
  719.         }
  720.  
  721.     if ((s->sisopen == SOCKET_CLOSED) && (msgcnt == 0) &&  
  722.         (sock_dataready(s) == 0))    /* no data and no session */
  723.             {
  724.             session_close(active);    /* close deceased */
  725.             return (BAPISTAT_NOS);    /* means exit session */
  726.             }
  727.     else
  728.         return (cmdstatus);        /* stuff is yet unread */
  729. }
  730.  
  731. /* ttinc   - destructively read char from socket buffer, return -1 if fail */
  732.  
  733. int 
  734. ttinc(void)
  735. {
  736.     byte ch;
  737.     
  738.     tcp_tick(s);                /* read another packet */
  739.     if (sock_fastread(s, &ch, 1) != 0)    /* qty chars returned */
  740.         return (0xff & ch);
  741.     return (-1);
  742. }
  743.  
  744. /* Initialize a telnet connection */
  745. /* Returns -1 on error, 0 is ok */
  746.  
  747. int 
  748. tn_ini(void)
  749. {
  750.     sgaflg = 0;            /* SGA flag starts out this way */
  751.     wttflg = 0;            /* Did not send WILL TERM TYPE yet. */
  752.     session[active].nawsflg = 0;    /* Did not send NAWS info yet */
  753.     dosga  = 0;            /* Did not send DO SGA yet. */
  754.     dobinary = 0;            /* presume NVT-ASCII result */
  755.     inbinary = outbinary = BIN_REJECT; /* NVT-ASCII mode (reject binary)*/
  756.     doEOR = 0;
  757.     session[active].tn_inited = 1;    /* say we are doing this proc */
  758.     kecho(echo = 1);        /* start with local echoing */
  759.                 /* if not server or not Telnet port */
  760.     if (session[active].server_mode != 0 ||    kport != 23)
  761.             return (0);        /* don't go first */
  762.         
  763.     if (send_iac(WILL, TELOPT_TTYPE)) return( -1 );
  764.     wttflg = 1;            /* remember we offered TTYPE */
  765.     if (send_iac(WILL, TELOPT_NAWS)) return(-1);
  766.     session[active].nawsflg = 1;    /* remember we offered NAWS */
  767.     if (send_iac(DO, TELOPT_SGA)) return( -1 );
  768.     dosga = 1;            /* remember we sent DO SGA */
  769.     if (ktnmode != 0)        /* if we want Binary mode */
  770.         {
  771.         outbinary = inbinary = BIN_REQUEST;
  772.         if (send_iac(DO, TELOPT_BINARY)) return (-1);
  773.         if (send_iac(WILL, TELOPT_BINARY)) return (-1);
  774.         }            /* remember said WILL and DO BINARY */
  775.     kmode(dobinary);        /* tell main body current NVT state */
  776.     return(0);
  777. }
  778.  
  779. /*
  780.  * send_iac - send interupt character and pertanent stuff
  781.  *        - return 0 on success
  782.  */
  783.  
  784. int
  785. send_iac(byte cmd, int opt)
  786. {
  787.     byte io_data[3];
  788.  
  789.     io_data[0] = IAC;
  790.     io_data[1] = cmd;
  791.     io_data[2] = (byte)(opt & 0xff);
  792.     if (sock_write(s, io_data, 3) != 3 )
  793.         return (1);            /* failed to write */
  794.     if (kdebug != 0)
  795.         {
  796.         outs("Opt send ");
  797.         optdebug(cmd, opt);
  798.         outs("\r\n");
  799.         }
  800.     return (0);
  801. }
  802.  
  803. /*
  804.  * Process in-band Telnet negotiation characters from the remote host.
  805.  * Call with the telnet IAC character and the current duplex setting
  806.  * (0 = remote echo, 1 = local echo).
  807.  * Returns:
  808.  *  -1 on success or char 0x255 (IAC) when IAC is the first char read here.
  809.  */
  810.  
  811. int 
  812. tn_doop(word ch)
  813. {                    /* enter after reading IAC char */
  814.     register int c, x;
  815.  
  816.         if (ch < SB) return(0);        /* ch is not in range of Options */
  817.  
  818.     if ((x = ttinc()) == -1)    /* read Option character */
  819.         return (-1);        /* nothing there */
  820.     x &= 0xff;
  821.     c = ch;                /* use register'd character */
  822.  
  823.     if (kdebug)
  824.         {
  825.         outs("Opt recv ");
  826.         optdebug((byte)c, x);
  827.         if ((byte)c != SB) outs("\r\n");
  828.         }
  829.  
  830.     switch (x) {
  831.       case TELOPT_ECHO:                 /* ECHO negotiation */
  832.     if (c == WILL)            /* Host says it will echo */
  833.         {
  834.         if (echo != 0)        /* reply only if change required */
  835.             {
  836.             send_iac(DO,x);    /* Please do */
  837.             kecho(echo = 0); /* echo is from the other side */
  838.             }
  839.         break;
  840.         }
  841.  
  842.         if (c == WONT)            /* Host says it won't echo */
  843.         {
  844.         if (echo == 0)            /* If we not echoing now */
  845.             {
  846.             send_iac(DONT,x);    /* agree to no host echo */
  847.             kecho(echo = 1);     /* do local echoing */
  848.             }
  849.         break;
  850.         }
  851.  
  852.     if (c == DO)
  853.         {            /* Host wants me to echo to it */
  854.         send_iac(WONT,x);    /* I say I won't */
  855.         break;
  856.             }
  857.     break;                /* do not respond to DONT */
  858.  
  859.       case TELOPT_SGA:                  /* Suppress Go-Ahead */
  860.     if (c == WONT)            /* Host says it won't sup go-aheads */
  861.         {
  862.         if (sgaflg == 0)
  863.             send_iac(DONT, x);    /* acknowledge */
  864.         sgaflg = 1;            /* no suppress, remember */
  865.         if (echo == 0)            /* if we're not echoing, */
  866.             kecho(echo = 1);    /* switch to local echo */
  867.         break;
  868.         }
  869.  
  870.         if (c == WILL)            /* Host says it will sup go aheads */
  871.         {
  872.         if (sgaflg || !dosga)        /* ACK only if necessary */
  873.             {
  874.             sgaflg = 0;        /* supp go-aheads, remember */
  875.             dosga++;        /* remember said DO */
  876.             send_iac(DO,x);        /* this is a change, so ACK */
  877.                     }
  878.         break;
  879.             }
  880. /*
  881.   Note: The following is proper behavior, and required for talking to the
  882.   Apertus interface to the NOTIS library system, e.g. at Iowa State U:
  883.   scholar.iastate.edu.  Without this reply, the server hangs forever.  This
  884.   code should not be loop-inducing, since Kermit never sends WILL SGA as
  885.   an initial bid, so if DO SGA comes, it is never an ACK.
  886. */
  887.     if (c == DO || c == DONT)    /* Server wants me to SGA, or not */
  888.         {
  889.         if (send_iac(WILL,x) < 0) /* I have to say WILL SGA, */
  890.             return(-1);    /* even tho I'm not changing state */
  891.         break;            /* or else server might hang. */
  892.         }
  893.     break;
  894.  
  895.       case TELOPT_TTYPE:                /* Terminal Type */
  896.         switch (c) {
  897.           case DO:                      /* DO terminal type */
  898.         if (wttflg == 0) {        /* If I haven't said so before, */
  899.         send_iac(WILL, x);    /* say I'll send it if asked */
  900.         wttflg++;
  901.         }
  902.         break;
  903.  
  904.           case SB:            /* enter subnegotiations */
  905.         if (wttflg == 0)
  906.             break;            /* we have not been introduced yet */
  907.         if (subnegotiate() != 0)    /* successful negotiation */
  908.         tn_sttyp();        /* report terminal type */
  909.         break;
  910.             
  911.           default:                      /* ignore other TTYPE Options */
  912.         break;
  913.         }                /* end of inner switch (c) */
  914.     break;
  915.  
  916.     case TELOPT_NAWS:        /* terminal width and height */
  917.         switch (c) {
  918.           case DO:                      /* DO terminal type */
  919.         if (session[active].nawsflg == 0) /* If haven't said so before, */
  920.         send_iac(WILL, x);    /* say we will send it if asked */
  921.         tn_snaws();            /* report screen size */
  922.         session[active].nawsflg = 2;    /* say NAWS is wanted */
  923.         break;
  924.  
  925.           default:                      /* ignore other NAWS Options */
  926.         break;
  927.         }                /* end of inner switch (c) */
  928.     break;
  929.  
  930.     case TELOPT_BINARY:
  931.     switch (c) {
  932.         case DO:        /* what they want to receive from us*/
  933.             if (outbinary & BIN_REQUEST != BIN_REQUEST)
  934.                 {        /* if have not sent WILL */ 
  935.                 send_iac(WILL, x);    /* we can send bin */
  936.                 outbinary |= BIN_REQUEST;
  937.                 }
  938.             /* ensure the other direction works the same way */
  939.             if (inbinary & BIN_REQUEST != BIN_REQUEST)
  940.                 {        /* if have not sent DO */
  941.                 send_iac(DO, x);    /* want you to bin */
  942.                 inbinary |= BIN_REQUEST;
  943.                 }
  944.             outbinary |= BIN_RESPONSE;    /* they said DO */
  945.             /* if we said WILL then BIN_REQUEST will be on */
  946.             break;
  947.         case WILL:        /* what they want to send to us */
  948.             if (inbinary & BIN_REQUEST != BIN_REQUEST)
  949.                 {        /* if we have not said DO */
  950.                 send_iac(DO, x);    /* want you to bin */
  951.                 inbinary |= BIN_REQUEST;
  952.                 }
  953.             /* ensure the other direction works the same way */
  954.             if (outbinary & BIN_REQUEST != BIN_REQUEST)
  955.                 {        /* if we have not said WILL */    
  956.                 send_iac(WILL, x);    /* we can send bin */
  957.                 outbinary |= BIN_REQUEST;
  958.                 }
  959.             inbinary |= BIN_RESPONSE;    /* they said WILL */
  960.             /* if we said DO then BIN_REQUEST will be on */
  961.             break;
  962.  
  963.         case DONT:        /* they will not receive binary */
  964.             if (inbinary & BIN_REQUEST == BIN_REQUEST) {
  965.             send_iac(WONT, x);    /* we won't receive binary */
  966.             inbinary = BIN_REJECT | BIN_RESPONSE;
  967.             }
  968.             break;
  969.         case WONT:        /* they will not send binary */
  970.             if (outbinary & BIN_REQUEST == BIN_REQUEST) {
  971.             send_iac(WONT, x);    /* we will not send binary */
  972.             outbinary = BIN_REJECT | BIN_RESPONSE;
  973.             }
  974.             break;
  975.         }        /* end of Binary switch (c) */
  976.                     /* if have both answers */
  977.     if ((inbinary & outbinary & BIN_RESPONSE) == BIN_RESPONSE)
  978.         {
  979.         dobinary = ((inbinary & outbinary & BIN_REQUEST)
  980.                 == BIN_REQUEST) ? 1: 0;
  981.         kmode( dobinary );    /* update main body, 1 = binary */
  982.         }
  983.     break;
  984.  
  985.     case TELOPT_EOR:
  986.     switch (c) {
  987.         case DO:
  988.             if (ktnmode != 0 && doEOR == 0) {
  989.             send_iac(WILL, x);    /* say I will */
  990.             doEOR++;
  991.             break;
  992.             }
  993.             if (ktnmode == 0) {
  994.             send_iac(WONT, x);    /* I won't do EOR to you */
  995.             doEOR = 0;
  996.             }
  997.             break;
  998.         case WILL:
  999.             if (ktnmode != 0 && doEOR == 0) {
  1000.             send_iac(DO, x);    /* tell host to do EORs */
  1001.             doEOR++;
  1002.             break;
  1003.             }
  1004.             if (ktnmode == 0) {
  1005.             send_iac(DONT, x);    /* don't do EORs */
  1006.             doEOR = 0;
  1007.             }
  1008.             break;
  1009.         case DONT:
  1010.             if (doEOR != 0) {
  1011.             send_iac(WONT, x);    /* say we won't */
  1012.             doEOR = 0;
  1013.             }
  1014.             break;
  1015.         case WONT:
  1016.             if (doEOR != 0) {
  1017.             send_iac(DONT, x);    /* say don't do it */
  1018.             doEOR = 0;
  1019.             }
  1020.             break;
  1021.         }        /* send of EOR switch (c) */
  1022.     break;
  1023.  
  1024.       default:                /* all other Options: refuse nicely */
  1025.     switch(c) {
  1026.           case WILL:                    /* You will? */
  1027.         send_iac(DONT,x);    /* Please don't */
  1028.         break;
  1029.           case DO:                      /* You want me to? */
  1030.         send_iac(WONT,x);    /* I won't */
  1031.         break;
  1032.  
  1033.           case DONT:
  1034.         send_iac(WONT,x);    /* I won't */
  1035.         break;
  1036.  
  1037.           case WONT:                    /* You won't? */
  1038.         break;            /* Good */
  1039.  
  1040.       default:
  1041.           break;            /* unknown character, discard */
  1042.           }                /* end of default switch (c) */
  1043.         break;
  1044.     }                    /* end switch (x) */
  1045.     return (-1);            /* say done with Telnet Options */
  1046. }
  1047.  
  1048. /* Perform Telnet Option subnegotiation. SB byte has been read. Consume
  1049.    through IAC SE. Return 1 if successful, else 0.
  1050. */
  1051. int
  1052. subnegotiate(void)
  1053. {
  1054.     register word flag, y;
  1055.      word n;
  1056.  
  1057.             n = flag = 0;               /* flag for when done reading SB */
  1058.             while (n < TSBUFSIZ)
  1059.             {            /* loop looking for IAC SE */
  1060.                 if ((y = ttinc()) == -1)
  1061.             continue;     /* nothing there */
  1062.         y &= 0xff;              /* make sure it's just 8 bits */
  1063.         sb[n++] = (byte) y;    /* save what we got in buffer */
  1064.  
  1065.         if (kdebug)
  1066.             {
  1067.             if (y == SE) outs(" se\r\n");
  1068.             else
  1069.             if (y != IAC)
  1070.                 {
  1071.                 if (n == 1 && y == 1)
  1072.                     outs(" send");
  1073.                 else
  1074.                     {
  1075.                     outs(" \\x"); 
  1076.                     outhex((byte)y);
  1077.                     }
  1078.                 }
  1079.             }
  1080.  
  1081.         if (y == IAC)         /* If this is an IAC */
  1082.             {
  1083.             if (flag)        /* If previous char was IAC */
  1084.                 {
  1085.             n--;        /* it's quoted, keep one IAC */
  1086.             flag = 0;    /* and turn off the flag. */
  1087.             }
  1088.             else flag = 1;    /* Otherwise set the flag. */
  1089.             }
  1090.         else if (flag)      /* Something else following IAC */
  1091.             {
  1092.             if (y != SE)    /* If not SE, it's a protocol error */
  1093.               flag = 0;
  1094.             break;
  1095.                     }        /* end of if (y == IAC) */
  1096.         }            /* end while */
  1097.  
  1098.         if (flag == 0 || y == -1)    /* no option IAC SE */
  1099.            return (0);        /* flag == 0 is invalid SB */
  1100.  
  1101.         if ( *sb == 1 )        /* wants us to report option */
  1102.         return (1);        /* say can do report */
  1103.         else
  1104.             return (0);
  1105. }
  1106.  
  1107. /* Telnet send terminal type */
  1108. /* Returns -1 on error, 0 on success */
  1109.  
  1110. int 
  1111. tn_sttyp(void)
  1112. {                                    /* Send telnet terminal type. */
  1113.     register byte *ttn;
  1114.     register int ttnl;        /* Name & length of terminal type. */
  1115.  
  1116.     ttn = termtype;        /* we already have this from environment */
  1117.     if ((*ttn == 0) || ((ttnl = strlen(ttn)) >= TSBUFSIZ))
  1118.         {
  1119.         ttn = "UNKNOWN";
  1120.             ttnl = 7;
  1121.         }
  1122.     sb[0] = (byte)IAC;
  1123.     sb[1] = (byte)SB;
  1124.     sb[2] = (byte)TELOPT_TTYPE;
  1125.     sb[3] = 0;            /* 'is'... */
  1126.     ttn = strcpy(&sb[4], ttn);    /* Copy to subnegotiation buffer */
  1127.     ttn = &sb[ttnl + 4];        /* go to end of buffer */
  1128.     *ttn++ = (byte)IAC;
  1129.     *ttn   = (byte)SE;
  1130.  
  1131.     sock_write(s, sb, ttnl + 6);
  1132.     if (kdebug)
  1133.         {
  1134.         int i;
  1135.  
  1136.         outs("Opt send ");
  1137.         optdebug(SB, TELOPT_TTYPE);
  1138.         outs(" is "); 
  1139.         for (i = 0; i < ttnl; i++) outch(sb[i+4]);
  1140.         outs(" se\r\n");
  1141.         }
  1142.     return (0);
  1143. }
  1144.  
  1145. /* Send terminal width and height (characters). RFC 1073 */
  1146. /* IAC SB NAWS <16-bit value> <16-bit value> IAC SE */
  1147. int
  1148. tn_snaws(void)
  1149. {
  1150.     static byte sbuf[9] = {(byte)IAC, (byte)SB, (byte)TELOPT_NAWS,
  1151.                 0,0, 0,0, (byte)IAC, (byte)SE};
  1152.  
  1153.     get_kscreen();                /* get current screen */
  1154.     sbuf[4] = kterm_cols;
  1155.     sbuf[6] = kterm_lines;
  1156.     sock_write(s, sbuf, sizeof(sbuf));
  1157.         if (kdebug)
  1158.             {
  1159.         outs("Opt send ");
  1160.         optdebug(SB, TELOPT_NAWS);
  1161.         outs(" \\x"); 
  1162.         outhex(sbuf[4]);
  1163.         outs(" \\x"); 
  1164.         outhex(sbuf[6]);
  1165.         outs(" se\r\n");
  1166.         }
  1167.     return (0);
  1168. }
  1169.  
  1170. /* assist displaying of Telnet Options negotiation material */
  1171. void
  1172. optdebug(byte cmd, int option)
  1173. {
  1174.     switch (cmd)
  1175.         {
  1176.         case WILL: outs("will ");
  1177.                 break;
  1178.         case WONT: outs("wont ");
  1179.                 break;
  1180.         case DO: outs("do ");
  1181.                 break;
  1182.         case DONT: outs("dont ");
  1183.                 break;
  1184.         case SB: outs("sb ");
  1185.                 break;
  1186.         default: outs("\\x "); outhex(cmd);
  1187.                 break;
  1188.         }            /* end of switch c */
  1189.     switch (option)
  1190.         {
  1191.         case TELOPT_BINARY: outs("binary");
  1192.                 break;
  1193.         case TELOPT_ECHO: outs("echo");
  1194.                 break;
  1195.         case TELOPT_SGA: outs("sga");
  1196.                 break;
  1197.         case TELOPT_TTYPE: outs("ttype");
  1198.                 break;
  1199.         case TELOPT_EOR: outs("eor");
  1200.                 break;
  1201.         case TELOPT_NAWS: outs("naws");
  1202.                 break;
  1203.         default: outs("\\x"); outhex((byte)option);
  1204.                 break;
  1205.         }
  1206. }
  1207.  
  1208.  
  1209. /* Compose a nice greeting message for incoming Telnet connections. Called
  1210.    by tcp_handler() in the tcp_StateLISTEN section. It also notifies the
  1211.    local terminal emulator of the client's presence and address. */
  1212.  
  1213. void
  1214. server_hello(tcp_Socket *s)
  1215. {
  1216.     char hellomsg[MSGBUFLEN];        /* work buffer, keep short */
  1217.  
  1218.     strcpy(hellomsg,
  1219.         "\r\n Welcome to the MS-DOS Kermit Telnet server at [");
  1220.     ntoa(&hellomsg[strlen(hellomsg)], my_ip_addr);        /* our IP */
  1221.     strcat(hellomsg, "].\r\n");        /* as [dotted decimal] */
  1222.     if (kserver != 0)            /* if file serving */
  1223.         {
  1224.         strcat(hellomsg," Escape back to your Kermit prompt and");
  1225.         strcat(hellomsg," issue Kermit file server commands.\r\n\n");
  1226.         }
  1227.     else                    /* if terminal emulating */
  1228.         {
  1229.         strcat(hellomsg,
  1230.             " You are talking to the terminal emulator,\r\n");
  1231.         strcat(hellomsg, " adjust local echoing accordingly.\r\n");
  1232.         }
  1233.  
  1234.         
  1235.     sock_write(s, hellomsg, strlen(hellomsg));    /* tell remote */
  1236.                 /* tell main body the news */
  1237.     strcpy(hellomsg, "\r\n Connection starting from [");
  1238.     ntoa(&hellomsg[strlen(hellomsg)], s->hisaddr);    /* their IP */
  1239.     strcat(hellomsg, "].\r\n");
  1240.     outs(hellomsg);        /* send connection info to main body */
  1241. }
  1242.  
  1243.