home *** CD-ROM | disk | FTP | other *** search
/ kermit.columbia.edu / kermit.columbia.edu.tar / kermit.columbia.edu / sredird / telnetcpcd-1.09.tar.gz / telnetcpcd-1.09.tar / rfc2217.c < prev    next >
C/C++ Source or Header  |  2003-08-12  |  82KB  |  3,209 lines

  1. /*
  2.     rfc2217.c
  3.  
  4.     Copyright (c) 2002,2003 Thomas J Pinkl <tom@pinkl.com>
  5.  
  6.     RFC2217 is the Telnet Com Port Control option.
  7.  
  8.     Version 1.00    02/20/2002
  9.     Version 1.01    02/22/2002        Changes to modemstate logic.
  10.     Version 1.02    03/04/2002        Misc. comments added.
  11.     Version 1.03    03/05/2002        Added ask_client_signature flag.
  12.     Version 1.04    03/06/2002        Added config file parameter that controls
  13.                                     where or not we send replies to Purge Data
  14.                                     commands.
  15.     Version 1.05    03/07/2002        In serial_cleanup(), set the serial device 
  16.                                     back to non-blocking mode before doing 
  17.                                     anything else.  Fixed a bug in cs2uint() 
  18.                                     where the wrong value was returned.  Fixed 
  19.                                     a bug in the syslog message which reports 
  20.                                     the current datasize in the 
  21.                                     respond_telnet_cpc_datasize_subopt() 
  22.                                     function.  Added telnet_cpc_datasize2str().
  23.     Version 1.06    04/08/2002        Added tcsetattr() calls in serial_cleanup() 
  24.                                     to make the temporary baud rate change to 
  25.                                     B0 effective.
  26.     Version 1.07    04/22/2002        If the client sends us a Notify Modemstate 
  27.                                     or Linestate query, then send a reply, 
  28.                                     without waiting for the poll interval to 
  29.                                     expire.
  30.     Version 1.08    04/23/2002        Changes in serial_init_termios() to reset
  31.                                     INLCR,ICRNL,ONLCR,OCRNL.
  32.     Version 1.09    04/24/2002        #ifdef'd out the "track carrier" code. 
  33.                                     Added code to flush the serial port when 
  34.                                     the client connects and disconnects.
  35.     Version 1.10    05/02/2002        In get_modem_signals(), added conditionally
  36.                                     compiled code (see TIOCMGET_CLOCAL_HACK) to 
  37.                                     temporarily clear CLOCAL while we read the
  38.                                     modem signals.  NOTE: this doesn't work; 
  39.                                     CD is still always off on OSR5.
  40.     Version 1.11    05/02/2002        In check_modemstate(), added conditionally 
  41.                                     compiled code (see CD_FOLLOWS_DSR) to make
  42.                                     the CD signal track the DSR signal.  Needed
  43.                                     for SCO OSR5 (at least in version 5.0.5).
  44.     Version 1.12    05/22/2002        Added the modem device name to the string 
  45.                                     returned as the server's signature.
  46.     Version 1.13    10/24/2002        Use snprintf() vs. sprintf().  Added the 
  47.                                     ability to timeout idle connections, in 
  48.                                     rfc2217_proc().
  49.     Version 1.14    11/18/2002        Added Telnet LOGOUT option handling and 
  50.                                     tied it into our idle timer.
  51.     Version 1.15    11/25/2002        Commented out the Telnet LOGOUT code in 
  52.                                     order to test C-Kermit 8.x without it.
  53.     Version 1.16    11/25/2002        Put back the Telnet LOGOUT code. 
  54.     Version 1.17    12/06/2002        'noquote' option
  55.     Version 1.18    12/12/2002        Added cpc2skt buffer for internally 
  56.                                     generated stuff that we send to the client.
  57.     Version 1.19    03/24/2003        Added IGNCR to the list of input options 
  58.                                     that we disable.
  59. */
  60.  
  61. #include "telnetcpcd.h"
  62.  
  63. /* for the tnmode[] array */
  64. #define CLIENT 0x00
  65. #define SERVER 0x01
  66.  
  67. /* global variables */
  68. TELNET_OPTIONS tnoptions[MAX_TELNET_OPTIONS];
  69. int tnmode[2];
  70. int session_state = RESUME;
  71. int carrier_state = NO_CARRIER;
  72. int break_signaled = 0;
  73. int ask_client_signature = 1;
  74. int client_logged_in = 0;
  75. SERIAL_INFO *si = NULL;
  76. /* these default values are dictated by RFC2217.  don't change them! */
  77. unsigned char linestate_mask = 0x00;
  78. unsigned char linestate = 0;
  79. unsigned char modemstate_mask = 0xff;
  80. unsigned char modemstate = 0;
  81.  
  82. /*
  83.     this function is called from handle_connection() in server.c, 
  84.     as a result of our parent process accept()'ing a new socket 
  85.     connection.
  86.  
  87.     returns 0 on success, non-zero on failure.
  88. */
  89. int rfc2217(int sockfd)
  90. {
  91.     extern SERIAL_INFO *si;                /* global ptr */
  92.     SERIAL_INFO *modem;                    /* a modem from the pool */
  93.     int fd;                                /* modem fd */
  94.     MYTERMIOS oldterm;                    /* original termios */
  95.     MYTERMIOS newterm;                    /* our custom termios */
  96.     char *p;                            /* gp ptr */
  97.  
  98.     /* allocate a modem and prepare it for use */
  99.     modem = serial_init(&fd,&oldterm,&newterm);
  100.     if (modem == NULL) {
  101.         p = "Unable to allocate a modem for you.\r\n";
  102.         write(sockfd,p,strlen(p));
  103.         return(1);
  104.     }
  105.  
  106.     /* store a global ptr to the modem's SERIAL_INFO */
  107.     si = modem;
  108.  
  109.     /* set socket options */
  110.     network_init(sockfd,BLOCKING);
  111.  
  112.     /* pass data between the modem and the socket */
  113.     rfc2217_proc(sockfd,fd,&newterm,modem);
  114.  
  115.     /* restore the modem line to its original state */
  116.     serial_cleanup(modem,&fd,&oldterm,&newterm);
  117.     si = NULL;
  118.  
  119.     return(0);
  120. }
  121.  
  122. /*
  123.     this function passes data between the modem and the socket.
  124.  
  125.     - init the telnet options structure
  126.     - send initial telnet options: Com Port Control, Binary, etc.
  127.     - set up select() call and enable a timeout
  128.     - enter select() loop
  129.     - use select() timeout to poll modem for state changes
  130.     - also check for pending signals; SIGINT indicates BREAK condition
  131.     - buffer data to/from modem/socket
  132.     - handle telnet option negotiations
  133.     - watch i/o channels for the IAC char and escape it, when present
  134.     - exit select() loop on socket EOF
  135.  
  136.     returns 0 on success, non-zero on failure (eg. failed to allocate 
  137.     memory for buffers).
  138. */
  139. int rfc2217_proc(int sockfd,int modemfd,MYTERMIOS *newterm,SERIAL_INFO *modem)
  140. {
  141.     extern int errno;
  142.     extern int signo_child;                /* what signal did we receive? */
  143.     extern struct config_t conf;        /* built from config file */
  144.     extern int client_logged_in;        /* is the client still "logged in"? */
  145.     BUFFER *skt2mdm;                    /* buffer for socket -> modem */
  146.     BUFFER *mdm2skt;                    /* buffer for modem -> socket */
  147.     BUFFER *cpc2skt;                    /* buffer for us -> socket */
  148.     struct timeval tv;                    /* timer for select() */
  149.     int tvsecs;                            /* what to put into tv.tv_sec */
  150.     time_t now,elapsed,idle_cnt;
  151.     int idle;                            /* connection went idle */
  152.     int ret;                            /* return value of select() */
  153.     int maxfd;                            /* highest fd + 1 */
  154.     int error;                            /* error flag */
  155.     fd_set orig_fds;                    /* original fd struct */
  156.     fd_set read_fds;                    /* read fd struct */
  157.  
  158.     debug(DBG_INF,"sockfd fd=%d, modem fd=%d",sockfd,modemfd);
  159.  
  160.     /* allocate buffers */
  161.     skt2mdm = bfmalloc("network",SZ_BUFFER);
  162.     mdm2skt = bfmalloc("serial",SZ_BUFFER);
  163.     cpc2skt = bfmalloc("options",SZ_BUFFER);
  164.     if ((skt2mdm == NULL) || (mdm2skt == NULL) || (cpc2skt == NULL)) {
  165.         bffree(skt2mdm);
  166.         bffree(mdm2skt);
  167.         bffree(cpc2skt);
  168.         return(1);
  169.     }
  170.     bfdump(skt2mdm,1);                    /* 1 == verbose dump */
  171.     bfdump(mdm2skt,1);
  172.     bfdump(cpc2skt,1);
  173.  
  174.     /* init telnet options structure and send initial options */
  175.     telnet_init(sockfd,cpc2skt);
  176.  
  177.     /* set up select loop */
  178.     maxfd = MAX(sockfd,modemfd) + 1;
  179.     FD_ZERO(&orig_fds);
  180.     FD_SET(sockfd,&orig_fds);
  181.     FD_SET(modemfd,&orig_fds);
  182.     error = 0;
  183.     client_logged_in = 1;
  184.  
  185.     /* calc select() timeout value */
  186.     idle = 0;
  187.     idle_cnt = 0;
  188.     tvsecs = MIN(conf.ms_pollinterval,conf.ls_pollinterval);
  189.     if (conf.idletimer > 0) {
  190.         tvsecs = MIN(conf.idletimer,tvsecs);
  191.     }
  192.  
  193.     /* select loop */
  194.     while ((client_logged_in) && (! idle) && (! error)) {
  195.  
  196.         /* check for idle connection */
  197.         if (conf.idletimer > 0) {
  198.             if (idle_cnt >= conf.idletimer) {
  199.                 if (! idle) {            /* we're just going into idle state */
  200.                     idle++;                /* set the idle state flag */
  201.                     debug(DBG_INF,"terminating idle connection on modem %s",modem->device);
  202.  
  203.                     /* let the user know what's happening */
  204.                     bfstrcat(cpc2skt,"\r\ntelnetcpcd: terminating idle connection\r\n");
  205.                     bfwrite(sockfd,cpc2skt);
  206.  
  207.                     /* tell the client to log out */
  208.                     send_telnet_option(sockfd,cpc2skt,DO,TELOPT_LOGOUT);
  209.                     msleep(250000);        /* sleep for 1/4 second */
  210.                 }
  211.  
  212.                 /*
  213.                     we want to go through the select loop one last time, 
  214.                     in order to process the client's reply to DO LOGOUT.
  215.                 */
  216.             }
  217.         }
  218.  
  219.         tv.tv_sec = tvsecs;                /* set timeout */
  220.         tv.tv_usec = 0;
  221.         read_fds = orig_fds;            /* structure copy */
  222.         now = time((time_t *) NULL);    /* current time, in seconds */
  223.         ret = select(maxfd,&read_fds,NULL,NULL,&tv);
  224.         switch (ret) {
  225.         case -1:                        /* select error */
  226.             if (errno != EINTR)
  227.                 syslog_perror("select() error");
  228.  
  229.             elapsed = time((time_t *) NULL) - now;    /* seconds spent in select() */
  230.             idle_cnt += elapsed;        /* accumulate idle time */
  231.             break;
  232.         case 0:                            /* select timeout */
  233.             elapsed = time((time_t *) NULL) - now;    /* seconds spent in select() */
  234.             idle_cnt += elapsed;        /* accumulate idle time */
  235.  
  236.             advise_client_of_state_changes(sockfd,modemfd,cpc2skt);
  237.             break;
  238.         default:                        /* select fds for read/write */
  239.             idle_cnt = 0;                /* reset idle counter */
  240.             if (FD_ISSET(sockfd,&read_fds)) {
  241.                 error = read_socket(sockfd,modemfd,skt2mdm,cpc2skt);
  242.                 if (! error) {
  243.                     error = write_modem(modemfd,skt2mdm);
  244.                 }
  245.                 if (error) continue;    /* this will break the while loop */
  246.             }
  247.             if (FD_ISSET(modemfd,&read_fds)) {
  248.                 error = read_modem(modemfd,mdm2skt);
  249.                 if (! error) {
  250.                     error = write_socket(sockfd,mdm2skt);
  251.                 }
  252.                 if (error) continue;    /* this will break the while loop */
  253.             }
  254.             advise_client_of_state_changes(sockfd,modemfd,cpc2skt);
  255.             break;
  256.         }
  257.         handle_pending_signal();        /* did we receive any signals? */
  258.  
  259.         /* react to loss of carrier */
  260. #if 0    /* tjp, 04/24/2002 */
  261.         if (modem->track_carrier) {
  262.             if (check_carrier_state(modemfd) == LOST_CARRIER) {
  263.                 ++error;                /* will break the while() loop */
  264.                 advise_client_of_state_changes(sockfd,modemfd,cpc2skt);
  265.             }
  266.         }
  267. #endif
  268.     }    /* while() loop ends */
  269.  
  270.     handle_pending_signal();            /* did we receive any signals? */
  271.  
  272.     bffree(skt2mdm);                    /* release the buffers */
  273.     bffree(mdm2skt);
  274.     bffree(cpc2skt);
  275.     return(0);
  276. }
  277.  
  278. /*
  279.     this function is called from handle_pending_signal() upon 
  280.     receipt of a SIGINT
  281. */
  282. void rfc2217_sigint(void)
  283. {
  284.     extern int break_signaled;
  285.     extern unsigned char linestate;
  286.  
  287.     break_signaled = 1;
  288.     linestate |= CPC_LINESTATE_BREAK_DETECT;
  289. }
  290.  
  291. /*
  292.     read from the socket 
  293.  
  294.     returns 0 on success, 1 on failure (including EOF)
  295. */
  296. int read_socket(int sockfd,int modemfd,BUFFER *skt2mdm,BUFFER *cpc2skt)
  297. {
  298.     extern int useconds;            /* i/o wait time */
  299.     int n;
  300.     int ret;
  301.  
  302.     /* wait a bit before reading the socket */
  303.     if (useconds > 0) {
  304.         msleep(useconds);
  305.     }
  306.  
  307.     n = bfread(sockfd,skt2mdm);
  308.     if (n < 0) {                    /* error on read */
  309.         ret = 1;
  310.     } else if (n == 0) {            /* no data read or EOF */
  311.         if (bfeof(skt2mdm)) {
  312.             ret = 1;                /* socket EOF */
  313.             debug(DBG_INF,"read EOF on socket fd %d",sockfd);
  314.         } else {
  315.             ret = 0;                /* probably interrupted by a signal */
  316.         }
  317.     } else {                        /* some data was read */
  318.         ret = 0;
  319.         bfdump(skt2mdm,0);            /* debugging dump of BUFFER */
  320.  
  321.         /* check for IAC char */
  322.         if (bfstrchr(skt2mdm,IAC) != NULL) {
  323.             process_telnet_options(sockfd,modemfd,skt2mdm,cpc2skt);
  324.             bfdump(skt2mdm,0);        /* debugging dump of BUFFER */
  325.         }
  326.     }
  327.     return(ret);
  328. }
  329.  
  330. /*
  331.     write to the modem
  332.  
  333.     returns 0 on success, 1 on failure
  334. */
  335. int write_modem(int modemfd,BUFFER *skt2mdm)
  336. {
  337.     int n;
  338.     int ret;
  339.  
  340.     n = bfwrite(modemfd,skt2mdm);
  341.     if (n < 0) {                    /* error on write */
  342.         ret = 1;
  343.     } else if (n == 0) {            /* no data written */
  344.         ret = 0;
  345.     } else {                        /* some data was written */
  346.         ret = 0;
  347.         if (bfamount(skt2mdm) == 0)    /* no data left in buffer */
  348.             linestate &= ~CPC_LINESTATE_DATA_READY;
  349.         bfdump(skt2mdm,0);            /* debugging dump of BUFFER */
  350.     }
  351.     return(ret);
  352. }
  353.  
  354. /*
  355.     read from the modem 
  356.  
  357.     returns 0 on success, 1 on failure (including EOF)
  358. */
  359. int read_modem(int modemfd,BUFFER *mdm2skt)
  360. {
  361.     extern unsigned char linestate;
  362.     extern int noquote;                /* don't quote IAC char */
  363.     extern int useconds;            /* i/o wait time */
  364.     int n;
  365.     int ret;
  366.  
  367.     /* wait a bit before reading the serial port */
  368.     if (useconds > 0) {
  369.         msleep(useconds);
  370.     }
  371.  
  372.     n = bfread(modemfd,mdm2skt);
  373.     if (n < 0) {                    /* error on read */
  374.         ret = 1;
  375.     } else if (n == 0) {            /* no data read or EOF */
  376.         if (bfeof(mdm2skt)) {
  377.             ret = 1;                /* modem EOF */
  378.             debug(DBG_INF,"read EOF on modem fd %d",modemfd);
  379.         } else {
  380.             ret = 0;
  381.         }
  382.     } else {                        /* some data was read */
  383.         ret = 0;
  384.         linestate |= CPC_LINESTATE_DATA_READY;
  385.         bfdump(mdm2skt,0);            /* debugging dump of BUFFER */
  386.  
  387.         /* check for IAC char unless 'noquote' option was enabled */
  388.         if (! noquote) {
  389.             if (bfstrchr(mdm2skt,IAC) != NULL) {
  390.                 escape_iac_chars(mdm2skt);
  391.                 bfdump(mdm2skt,0);    /* debugging dump of BUFFER */
  392.             }
  393.         }
  394.     }
  395.     return(ret);
  396. }
  397.  
  398. /*
  399.     write to the socket
  400.  
  401.     returns 0 on success, 1 on failure
  402. */
  403. int write_socket(int sockfd,BUFFER *mdm2skt)
  404. {
  405.     extern int session_state;
  406.     int n;
  407.     int ret;
  408.  
  409.     if (session_state == SUSPEND)
  410.         return(0);
  411.  
  412.     n = bfwrite(sockfd,mdm2skt);
  413.     if (n < 0) {                    /* error on write */
  414.         ret = 1;
  415.     } else if (n == 0) {            /* no data written */
  416.         ret = 0;
  417.     } else {                        /* some data was written */
  418.         ret = 0;
  419.         bfdump(mdm2skt,0);            /* debugging dump of BUFFER */
  420.     }
  421.     return(ret);
  422. }
  423.  
  424. /*
  425.     this function:
  426.  
  427.     - selects a modem from the pool of available modems
  428.     - opens the modem device file in non-blocking mode
  429.     - saves the modem line's original termios settings
  430.     - configures the modem line's termios settings for raw i/o
  431.     - puts the modem device into blocking mode again
  432.     - (optionally) flushes the modem device
  433.     - opens a debug log, named for the selected modem device
  434.  
  435.     on success, we return a SERIAL_INFO ptr for the selected modem, 
  436.     plus the file descriptor returned by open(), the original termios 
  437.     settings, and the new termios settings.
  438.  
  439.     on failure, a NULL ptr is returned.
  440. */
  441. SERIAL_INFO *serial_init(int *fd,MYTERMIOS *oldterm,MYTERMIOS *newterm)
  442. {
  443.     extern int errno;
  444.     extern char *progname;                /* our program name */
  445.     extern char *version;                /* version string */
  446.     extern struct config_t conf;        /* built from config file */
  447.     char log[PATH_MAX];                    /* name of debug log */
  448.     SERIAL_INFO *v;
  449.     int flags;
  450.     int ret;
  451.  
  452.     /* allocate a modem */
  453.     v = select_modem(&conf);
  454.     if (v == NULL) {
  455.         syslog(LOG_ERR,"unable to allocate a modem");
  456.         return(v);
  457.     } else {
  458.         syslog(LOG_INFO,"using modem %s",v->device);
  459.     }
  460.  
  461.     /* open the modem */
  462.     *fd = open(v->device,O_RDWR|O_NOCTTY|O_NDELAY);
  463.     if (*fd < 0) {
  464.         syslog(LOG_ERR,"open(%s,...) error: %s",v->device,strerror(errno));
  465.         release_modem(v);
  466.         return(NULL);
  467.     }
  468.  
  469.     /* configure the termios settings */
  470.     ret = serial_init_termios(v,fd,oldterm,newterm);
  471.     if (ret != 0) {
  472.         close(*fd);
  473.         release_modem(v);
  474.         return(NULL);
  475.     }
  476.  
  477.     /* put the modem device back to blocking mode */
  478.     flags = fcntl(*fd,F_GETFL,0);
  479.     if (flags != -1) {
  480.         flags &= ~O_NONBLOCK;
  481.         flags = fcntl(*fd,F_SETFL,flags);
  482.     }
  483.     if (flags == -1) {
  484.         syslog(LOG_ERR,"warning: cannot remove O_NONBLOCK from modem device %s (%s)",v->device,strerror(errno));
  485.     }
  486.  
  487.     /* flush the serial port (both input and output) */
  488.     if (v->conn_flush) {
  489.         ret = tcflush(*fd,TCIOFLUSH);
  490.         if (ret != 0) {
  491.             syslog(LOG_ERR,"warning: cannot flush modem device %s (%s)",v->device,strerror(errno));
  492.         } else {
  493.             syslog(LOG_INFO,"flushed modem device %s",v->device);
  494.         }
  495.     }
  496.  
  497.     /* open the debug log */
  498.     if ((conf.debuglog == NULL) || (*conf.debuglog == '\0') || (strcasecmp(conf.debuglog,"syslog") == 0)) {
  499.         log[0] = '\0';                /* send debug output to syslog */
  500.     } else {
  501.         snprintf(log,sizeof(log),conf.debuglog,mybasename(v->device));
  502.     }
  503.     open_debug(log,conf.debuglevel,progname);
  504.     debug(DBG_ERR,"%s %s",progname,version);
  505.  
  506.     /* return the SERIAL_INFO ptr */
  507.     return(v);
  508. }
  509.  
  510. int serial_init_termios(SERIAL_INFO *v,int *fd,MYTERMIOS *oldterm,MYTERMIOS *newterm)
  511. {
  512.     int ret;
  513.  
  514.     /* get two copies of the current termios settings */
  515.     ret = tcgetattr(*fd,&(oldterm->ts));
  516.     if (ret == 0) {
  517.         ret = tcgetattr(*fd,&(newterm->ts));
  518.     }
  519.     if (ret != 0) {
  520.         syslog(LOG_ERR,"tcgetattr() failed on modem %s",v->device);
  521.         return(ret);
  522.     }
  523.  
  524.     /* get two copies of the extended termiox settings */
  525. #ifdef USE_TERMIOX
  526.     ret = ioctl(*fd,TCGETX,&(oldterm->tx));
  527.     if (ret == 0) {
  528.         ret = ioctl(*fd,TCGETX,&(newterm->tx));
  529.     }
  530.     if (ret != 0) {
  531.         syslog(LOG_ERR,"ioctl(fd,TCGETX,...) failed on modem %s",v->device);
  532.         return(ret);
  533.     }
  534. #endif
  535.  
  536.     /* configure the modem line speed */
  537.     ret = cfsetospeed(&(newterm->ts),ulong2speed_t(v->speed));
  538.     if (ret == 0) {
  539.         ret = cfsetispeed(&(newterm->ts),ulong2speed_t(v->speed));
  540.     }
  541.     if (ret != 0) {
  542.         syslog(LOG_ERR,"cfsetospeed() failed on modem %s, speed %lu",v->device,v->speed);
  543.         return(ret);
  544.     }
  545.  
  546.     /* configure the modem line control options */
  547.     newterm->ts.c_cflag |= (CLOCAL|CREAD);
  548.     switch (v->parity) {
  549.     case PARITY_ODD:
  550.         newterm->ts.c_cflag |= PARENB;
  551.         newterm->ts.c_cflag |= PARODD;
  552.         newterm->ts.c_cflag &= ~CSTOPB;
  553.         break;
  554.     case PARITY_EVEN:
  555.         newterm->ts.c_cflag |= PARENB;
  556.         newterm->ts.c_cflag &= ~PARODD;
  557.         newterm->ts.c_cflag &= ~CSTOPB;
  558.         break;
  559.     case PARITY_NONE:
  560.     default:
  561.         newterm->ts.c_cflag &= ~PARENB;
  562.         newterm->ts.c_cflag &= ~CSTOPB;
  563.         break;
  564.     }
  565.     newterm->ts.c_cflag &= ~CSIZE;
  566.     newterm->ts.c_cflag |= uint2cs(v->databits);
  567.  
  568.     /* enable/disable hardware flow control */
  569.     if (v->flowcontrol == HARDWARE_FLOW) {
  570. #ifdef USE_TERMIOX
  571.         newterm->tx.x_hflag |= (RTSXOFF|CTSXON);
  572. #else
  573.         newterm->ts.c_cflag |= CRTSCTS;
  574. #endif
  575.     } else {
  576. #ifdef USE_TERMIOX
  577.         newterm->tx.x_hflag &= ~(RTSXOFF|CTSXON);
  578. #else
  579.         newterm->ts.c_cflag &= ~CRTSCTS;
  580. #endif
  581.     }
  582.  
  583.     /* configure the modem line local options for raw mode */
  584.     newterm->ts.c_lflag &= ~(ICANON|ECHO|ECHOE|ISIG);
  585.  
  586.     /* configure the modem line input options for parity */
  587.     if (newterm->ts.c_cflag & PARENB) {
  588.         newterm->ts.c_iflag |= (INPCK|ISTRIP);
  589.     } else {
  590.         newterm->ts.c_iflag &= ~(INPCK|ISTRIP);
  591.     }
  592.  
  593.     /* send us SIGINT when a break condition is present */
  594.     newterm->ts.c_iflag |= BRKINT;
  595.  
  596.     /* enable/disable software flow control */
  597.     if (v->flowcontrol == SOFTWARE_FLOW) {
  598.         newterm->ts.c_iflag |= (IXON|IXOFF|IXANY);
  599.     } else {
  600.         newterm->ts.c_iflag &= ~(IXON|IXOFF|IXANY);
  601.     }
  602.  
  603.     /* disable these input options */
  604.     newterm->ts.c_iflag &= ~(INLCR|ICRNL|IGNCR);
  605.  
  606.     /* configure the modem line output options for raw mode */
  607.     newterm->ts.c_oflag &= ~OPOST;
  608.  
  609.     /*
  610.         some systems (eg. Linux kernel 2.4.9) may do output processing, 
  611.         even when OPOST has been reset.  so reset these explicitly.
  612.     */
  613.     newterm->ts.c_oflag &= ~(ONLCR|OCRNL);
  614.  
  615.     /*
  616.         since we've specified O_NDELAY on the open(), we cannot
  617.         specify character and packet timeouts via c_cc[VMIN] 
  618.         and c_cc[VTIME].
  619.     */
  620. #if 0
  621.     newterm->ts.c_cc[VMIN] = 0;
  622.     newterm->ts.c_cc[VTIME] = 10;
  623. #endif
  624.  
  625.     /* activate the new termios settings */
  626.     ret = tcsetattr(*fd,TCSAFLUSH,&(newterm->ts));
  627.     if (ret != 0) {
  628.         syslog(LOG_ERR,"tcsetattr() failed on modem %s",v->device);
  629.         tcsetattr(*fd,TCSANOW,&(oldterm->ts));
  630.         return(ret);
  631.     }
  632.  
  633.     /* activate the new termiox settings */
  634. #ifdef USE_TERMIOX
  635.     ret = ioctl(*fd,TCSETXW,&(newterm->tx));
  636.     if (ret != 0) {
  637.         syslog(LOG_ERR,"ioctl(fd,TCSETXW,...) failed on modem %s",v->device);
  638.         tcsetattr(*fd,TCSANOW,&(oldterm->ts));
  639.         return(ret);
  640.     }
  641. #endif
  642.  
  643.     return(0);
  644. }
  645.  
  646. /*
  647.     this function:
  648.  
  649.     - puts the modem device back to non-blocking mode 
  650.     - hangs up the modem by setting the speed to B0
  651.     - (optionally) flushes the modem device
  652.     - restores the previous modem line termios settings 
  653.     - closes the modem device file
  654.     - releases the modem back to the pool
  655.     - closes the debug log
  656.  
  657.     it returns nothing.
  658. */
  659. void serial_cleanup(SERIAL_INFO *v,int *fd,MYTERMIOS *oldterm,MYTERMIOS *newterm)
  660. {
  661.     extern int errno;
  662.     speed_t current_speed;
  663.     int flags;
  664.     int ret;
  665.  
  666.     syslog(LOG_INFO,"releasing modem %s",v->device);
  667.  
  668.     /* put the modem device back to non-blocking mode */
  669.     flags = fcntl(*fd,F_GETFL,0);
  670.     if (flags != -1) {
  671.         flags |= O_NONBLOCK;
  672.         flags = fcntl(*fd,F_SETFL,flags);
  673.     }
  674.     if (flags == -1) {
  675.         syslog(LOG_ERR,"cannot set O_NONBLOCK on modem device %s (%s)",v->device,strerror(errno));
  676.     }
  677.  
  678.     /* briefly set the speed to zero to force a hangup */
  679.     current_speed = cfgetospeed(&(newterm->ts));
  680.     cfsetospeed(&(newterm->ts),B0);
  681.     tcsetattr(*fd,TCSADRAIN,&(newterm->ts));
  682.     sleep(1);
  683.     cfsetospeed(&(newterm->ts),current_speed);
  684.     tcsetattr(*fd,TCSADRAIN,&(newterm->ts));
  685.  
  686.     /* flush the serial port (both input and output) */
  687.     if (v->disc_flush) {
  688.         ret = tcflush(*fd,TCIOFLUSH);
  689.         if (ret != 0) {
  690.             syslog(LOG_ERR,"warning: cannot flush modem device %s (%s)",v->device,strerror(errno));
  691.         } else {
  692.             syslog(LOG_INFO,"flushed modem device %s",v->device);
  693.         }
  694.     }
  695.  
  696.     /* restore the old termiox settings */
  697. #ifdef USE_TERMIOX
  698.     ioctl(*fd,TCSETXW,&(oldterm->tx));
  699. #endif
  700.  
  701.     /* restore the old termios settings */
  702.     tcsetattr(*fd,TCSADRAIN,&(oldterm->ts));
  703.  
  704.     /* close the modem device file */
  705.     close(*fd);
  706.     *fd = -1;
  707.  
  708.     /* release the modem back to the pool */
  709.     release_modem(v);
  710.  
  711.     /* close the debug log */
  712.     close_debug();
  713. }
  714.  
  715. /*
  716.     convert an unsigned long speed to its correspsonding 
  717.     baud rate symbol
  718. */
  719. speed_t ulong2speed_t(unsigned long s)
  720. {
  721.     speed_t speed;
  722.  
  723.     switch (s) {
  724.     case 0:
  725.         speed = B0;
  726.         break;
  727.     case 50:
  728.         speed = B50;
  729.         break;
  730.     case 75:
  731.         speed = B75;
  732.         break;
  733.     case 110:
  734.         speed = B110;
  735.         break;
  736.     case 134:
  737.         speed = B134;
  738.         break;
  739.     case 150:
  740.         speed = B150;
  741.         break;
  742.     case 200:
  743.         speed = B200;
  744.         break;
  745.     case 300:
  746.         speed = B300;
  747.         break;
  748.     case 600:
  749.         speed = B600;
  750.         break;
  751.     case 1200:
  752.         speed = B1200;
  753.         break;
  754.     case 1800:
  755.         speed = B1800;
  756.         break;
  757.     case 2400:
  758.         speed = B2400;
  759.         break;
  760.     case 4800:
  761.         speed = B4800;
  762.         break;
  763.     case 9600:
  764.         speed = B9600;
  765.         break;
  766.     case 19200:
  767.         speed = B19200;
  768.         break;
  769.     case 38400:
  770.         speed = B38400;
  771.         break;
  772. #ifdef B57600
  773.     case 57600:
  774.         speed = B57600;
  775.         break;
  776. #endif
  777. #ifdef B115200
  778.     case 115200:
  779.         speed = B115200;
  780.         break;
  781. #endif
  782. #ifdef B230400
  783.     case 230400:
  784.         speed = B230400;
  785.         break;
  786. #endif
  787. #ifdef B460800
  788.     case 460800:
  789.         speed = B460800;
  790.         break;
  791. #endif
  792.     default:
  793.         speed = B9600;
  794.         break;
  795.     }
  796.     return(speed);
  797. }
  798.  
  799. /*
  800.     convert a baud rate symbol to the correspsonding 
  801.     unsigned long speed 
  802.     
  803. */
  804. unsigned long speed_t2ulong(speed_t s)
  805. {
  806.     unsigned long speed;
  807.  
  808.     switch (s) {
  809.     case B0:
  810.         speed = 0;
  811.         break;
  812.     case B50:
  813.         speed = 50;
  814.         break;
  815.     case B75:
  816.         speed = 75;
  817.         break;
  818.     case B110:
  819.         speed = 110;
  820.         break;
  821.     case B134:
  822.         speed = 134;
  823.         break;
  824.     case B150:
  825.         speed = 150;
  826.         break;
  827.     case B200:
  828.         speed = 200;
  829.         break;
  830.     case B300:
  831.         speed = 300;
  832.         break;
  833.     case B600:
  834.         speed = 600;
  835.         break;
  836.     case B1200:
  837.         speed = 1200;
  838.         break;
  839.     case B1800:
  840.         speed = 1800;
  841.         break;
  842.     case B2400:
  843.         speed = 2400;
  844.         break;
  845.     case B4800:
  846.         speed = 4800;
  847.         break;
  848.     case B9600:
  849.         speed = 9600;
  850.         break;
  851.     case B19200:
  852.         speed = 19200;
  853.         break;
  854.     case B38400:
  855.         speed = 38400;
  856.         break;
  857. #ifdef B57600
  858.     case B57600:
  859.         speed = 57600;
  860.         break;
  861. #endif
  862. #ifdef B115200
  863.     case B115200:
  864.         speed = 115200;
  865.         break;
  866. #endif
  867. #ifdef B230400
  868.     case B230400:
  869.         speed = 230400;
  870.         break;
  871. #endif
  872. #ifdef B460800
  873.     case B460800:
  874.         speed = 460800;
  875.         break;
  876. #endif
  877.     default:
  878.         speed = 9600;
  879.         break;
  880.     }
  881.     return(speed);
  882. }
  883.  
  884. /*
  885.     convert the number of data bits, specified as an integer, 
  886.     to the corresponding CSn symbol.
  887. */
  888. unsigned long uint2cs(unsigned int dbits)
  889. {
  890.     unsigned long cs;
  891.  
  892.     switch (dbits) {
  893.     case 8:
  894.         cs = CS8;
  895.         break;
  896.     case 7:
  897.         cs = CS7;
  898.         break;
  899.     case 6:
  900.         cs = CS6;
  901.         break;
  902.     case 5:
  903.         cs = CS5;
  904.         break;
  905.     default:
  906.         cs = CS8;
  907.         break;
  908.     }
  909.     return(cs);
  910. }
  911.  
  912. /*
  913.     convert a data bits CSn symbol to an integer.
  914. */
  915. unsigned int cs2uint(unsigned long cs)
  916. {
  917.     unsigned int dbits;
  918.  
  919.     switch (cs) {
  920.     case CS8:
  921.         dbits = 8;
  922.         break;
  923.     case CS7:
  924.         dbits = 7;
  925.         break;
  926.     case CS6:
  927.         dbits = 6;
  928.         break;
  929.     case CS5:
  930.         dbits = 5;
  931.         break;
  932.     default:
  933.         dbits = 8;
  934.         break;
  935.     }
  936.     return(dbits);
  937. }
  938.  
  939. /*
  940.     convert a data bits CSn symbol to a string.
  941. */
  942. char *cs2str(unsigned long cs)
  943. {
  944.     char *str;
  945.  
  946.     switch (cs) {
  947.     case CS8:
  948.         str = "CS8";
  949.         break;
  950.     case CS7:
  951.         str = "CS7";
  952.         break;
  953.     case CS6:
  954.         str = "CS6";
  955.         break;
  956.     case CS5:
  957.         str = "CS5";
  958.         break;
  959.     default:
  960.         str = "CS8";
  961.         break;
  962.     }
  963.     return(str);
  964. }
  965.  
  966. /*
  967.     convert a Telnet CPC_DATASIZE_xxx symbol to a string.
  968. */
  969. char *telnet_cpc_datasize2str(unsigned char datasize)
  970. {
  971.     char *str;
  972.  
  973.     switch (datasize) {
  974.     case CPC_DATASIZE_CS8:
  975.         str = "CS8";
  976.         break;
  977.     case CPC_DATASIZE_CS7:
  978.         str = "CS7";
  979.         break;
  980.     case CPC_DATASIZE_CS6:
  981.         str = "CS6";
  982.         break;
  983.     case CPC_DATASIZE_CS5:
  984.         str = "CS5";
  985.         break;
  986.     default:
  987.         str = "CS8";
  988.         break;
  989.     }
  990.     return(str);
  991. }
  992.  
  993. /*
  994.     this function:
  995.  
  996.     - sets socket options: keepalive, non-blocking
  997.  
  998.     it returns 0 on success, non-zero on failure.
  999. */
  1000. int network_init(int sockfd,int blockopt)
  1001. {
  1002.     extern int errno;
  1003.     int i;
  1004.     int opt;
  1005.     int flags;
  1006.     int error;
  1007.  
  1008.     /*
  1009.         set the SO_KEEPALIVE socket option and set the socket 
  1010.         to non-blocking mode (ie. O_NONBLOCK).
  1011.     */
  1012.     error = 0;
  1013.     opt = 1;
  1014.     if (setsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,&opt,sizeof(opt)) < 0) {
  1015.         syslog(LOG_ERR,"cannot set socket SO_KEEPALIVE (%s)",strerror(errno));
  1016.         error++;
  1017.     }
  1018.     flags = fcntl(sockfd,F_GETFL,0);
  1019.     if (flags != -1) {
  1020.         if (blockopt == NONBLOCKING) {
  1021.             flags |= O_NONBLOCK;
  1022.         } else {
  1023.             flags &= ~O_NONBLOCK;
  1024.         }
  1025.         flags = fcntl(sockfd,F_SETFL,flags);
  1026.     }
  1027.     if (flags == -1) {
  1028.         if (blockopt == NONBLOCKING) {
  1029.             syslog(LOG_ERR,"cannot set socket O_NONBLOCK (%s)",strerror(errno));
  1030.         } else {
  1031.             syslog(LOG_ERR,"cannot remove O_NONBLOCK from socket (%s)",strerror(errno));
  1032.         }
  1033.         error++;
  1034.     }
  1035.     return(error);
  1036. }
  1037.  
  1038. /*
  1039.     this function:
  1040.  
  1041.     - init's the telnet options structure
  1042.     - init's some global variables related to telnet options
  1043.     - sends initial telnet options: Com Port Control, Binary, etc.
  1044.  
  1045.     it always returns 0.
  1046. */
  1047. int telnet_init(int sockfd,BUFFER *cpc2skt)
  1048. {
  1049.     extern TELNET_OPTIONS tnoptions[];
  1050.     extern int tnmode[];
  1051.     extern int session_state;
  1052.     extern int break_signaled;
  1053.     extern int ask_client_signature;
  1054.     int i;
  1055.  
  1056.     /*
  1057.         init all telnet options to "off"
  1058.     */
  1059.     for (i = 0; i < MAX_TELNET_OPTIONS; i++) {
  1060.         tnoptions[i].sent_will = 0;
  1061.         tnoptions[i].sent_do = 0;
  1062.         tnoptions[i].sent_wont = 0;
  1063.         tnoptions[i].sent_dont = 0;
  1064.         tnoptions[i].server = 0;
  1065.         tnoptions[i].client = 0;
  1066.     }
  1067.  
  1068.     /* initially we're in ASCII mode */
  1069.     tnmode[CLIENT] = ASCII;
  1070.     tnmode[SERVER] = ASCII;
  1071.  
  1072.     /* init other global variables */
  1073.     session_state = RESUME;
  1074.     break_signaled = 0;
  1075.     ask_client_signature = 1;
  1076.  
  1077.     /*
  1078.         send initial telnet option negotiations
  1079.     */
  1080.     send_telnet_option(sockfd,cpc2skt,DO,   TELOPT_COM_PORT_OPTION);
  1081.     send_telnet_option(sockfd,cpc2skt,WILL, TELOPT_BINARY);
  1082.     send_telnet_option(sockfd,cpc2skt,DO,   TELOPT_BINARY);
  1083.     send_telnet_option(sockfd,cpc2skt,WILL, TELOPT_ECHO);
  1084.     send_telnet_option(sockfd,cpc2skt,WILL, TELOPT_SGA);
  1085.     send_telnet_option(sockfd,cpc2skt,DO,   TELOPT_SGA);
  1086.  
  1087.     return(0);
  1088. }
  1089.  
  1090. /*
  1091.     send a telnet option
  1092.  
  1093.     returns 0 on success, 1 on failure
  1094. */
  1095. int send_telnet_option(int sockfd,BUFFER *cpc2skt,unsigned char optcode,unsigned char option)
  1096. {
  1097.     extern int errno;
  1098.     extern int session_state;
  1099.     unsigned char optstr[4];
  1100.     int size;
  1101.     int ret;
  1102.  
  1103.     /* make sure we only send it once */
  1104.     if (telnet_option_was_sent(optcode,option)) {
  1105.         syslog(LOG_INFO,"telnet option %s %s already sent",telnet_optcode2str(optcode),telnet_option2str(option));
  1106.         return(0);
  1107.     }
  1108.  
  1109.     /* build telnet option string */
  1110.     optstr[0] = IAC;
  1111.     optstr[1] = optcode;
  1112.     optstr[2] = option;
  1113.     optstr[3] = '\0';
  1114.     size = 3;
  1115.  
  1116.     /* append it to the buffer */
  1117.     bfstrncat(cpc2skt,optstr,size);
  1118.  
  1119.     /* send it */
  1120.     if (session_state == RESUME) {
  1121.         bfdump(cpc2skt,0);        /* debugging dump of BUFFER */
  1122.         ret = bfwrite(sockfd,cpc2skt);
  1123.         if (ret >= size) {        /* buffer may have contained more than just our optstr */
  1124.             mark_telnet_option_as_sent(optcode,option);
  1125.             syslog(LOG_INFO,"sent telnet option %s %s",telnet_optcode2str(optcode),telnet_option2str(option));
  1126.             return(0);
  1127.         } else {
  1128.             syslog(LOG_ERR,"error sending telnet option %s %s",telnet_optcode2str(optcode),telnet_option2str(option));
  1129.             return(1);
  1130.         }
  1131.     }
  1132.     return(0);
  1133. }
  1134.  
  1135. /*
  1136.     send a telnet Com Port Control (CPC) suboption
  1137.  
  1138.     returns 0 on success, 1 on failure
  1139. */
  1140. int send_telnet_cpc_suboption(int sockfd,BUFFER *cpc2skt,unsigned char suboptcode,unsigned char *command,int cmdlen)
  1141. {
  1142.     extern int errno;
  1143.     extern int session_state;
  1144.     static unsigned char optstr[MAX_TELNET_CPC_COMMAND_LEN+8];
  1145.     unsigned char *p;
  1146.     unsigned char *cp;
  1147.     unsigned long value;
  1148.     int size;
  1149.     int i;
  1150.     int ret;
  1151.  
  1152.     /*
  1153.         if cmdlen is 1, 2 or 4 then interpret the command as a value
  1154.     */
  1155.     if (cmdlen == 1) {
  1156.         value = (unsigned long) *command;
  1157.     } else if (cmdlen == 2) {
  1158.         value = (unsigned long) ntohs(*((unsigned short *) command));
  1159.     } else if (cmdlen == 4) {
  1160.         value = ntohl(*((unsigned long *) command));
  1161.     } else {
  1162.         value = 0L;
  1163.     }
  1164.  
  1165.     /* build telnet CPC suboption string */
  1166.     optstr[0] = IAC;
  1167.     optstr[1] = SB;
  1168.     optstr[2] = TELOPT_COM_PORT_OPTION;
  1169.     optstr[3] = suboptcode;
  1170.     size = 4;
  1171.     if (cmdlen >= MAX_TELNET_CPC_COMMAND_LEN) {
  1172.         cmdlen = MAX_TELNET_CPC_COMMAND_LEN - 1;
  1173.     }
  1174.     p = &optstr[4];
  1175.  
  1176.     /* the command string may contain the IAC char, which must be escaped */
  1177.     cp = command;
  1178.     for (i = 0; i < cmdlen; i++) {
  1179.         if (*cp == IAC) {
  1180.             *p++ = IAC;                        /* add a 2nd IAC char to optstr */
  1181.             size++;                            /* adj size */
  1182.             if (size > MAX_TELNET_CPC_COMMAND_LEN) break;
  1183.         }
  1184.         *p++ = *cp++;
  1185.         size++;
  1186.         if (size > MAX_TELNET_CPC_COMMAND_LEN) break;
  1187.     }
  1188.     *p++ = IAC;
  1189.     *p++ = SE;
  1190.     *p = '\0';
  1191.     size += 2;        /* count the IAC and the SE */
  1192.  
  1193.     /* append it to the buffer */
  1194.     bfstrncat(cpc2skt,optstr,size);
  1195.  
  1196.     /* send it */
  1197.     if (session_state == RESUME) {
  1198.         bfdump(cpc2skt,0);        /* debugging dump of BUFFER */
  1199.         ret = bfwrite(sockfd,cpc2skt);
  1200.         if (ret >= size) {        /* buffer may have contained more than just our optstr */
  1201.             telnet_cpc_log_subopt("sent",suboptcode,value,command,cmdlen);
  1202.             return(0);
  1203.         } else {
  1204.             telnet_cpc_log_subopt("error sending",suboptcode,value,command,cmdlen);
  1205.             return(1);
  1206.         }
  1207.     }
  1208.     return(0);
  1209. }
  1210.  
  1211. /*
  1212.     returns 1 if the specified optcode was already sent for 
  1213.     the specified option.  otherwise it returns 0.
  1214. */
  1215. int telnet_option_was_sent(unsigned char optcode,unsigned char option)
  1216. {
  1217.     extern TELNET_OPTIONS tnoptions[];
  1218.     int was_sent;
  1219.  
  1220.     switch (optcode) {
  1221.     case WILL:
  1222.         was_sent = tnoptions[option].sent_will;
  1223.         break;
  1224.     case DO:
  1225.         was_sent = tnoptions[option].sent_do;
  1226.         break;
  1227.     case WONT:
  1228.         was_sent = tnoptions[option].sent_wont;
  1229.         break;
  1230.     case DONT:
  1231.         was_sent = tnoptions[option].sent_dont;
  1232.         break;
  1233.     default:
  1234.         was_sent = 0;
  1235.         break;
  1236.     }
  1237.     return(was_sent);
  1238. }
  1239.  
  1240. /*
  1241.     mark our telnet options structure to indicate that the specified 
  1242.     opt code for the specified option, has been sent to our client.
  1243. */
  1244. void mark_telnet_option_as_sent(unsigned char optcode,unsigned char option)
  1245. {
  1246.     extern TELNET_OPTIONS tnoptions[];
  1247.  
  1248.     switch (optcode) {
  1249.     case WILL:
  1250.         tnoptions[option].sent_will = 1;
  1251.         break;
  1252.     case DO:
  1253.         tnoptions[option].sent_do = 1;
  1254.         break;
  1255.     case WONT:
  1256.         tnoptions[option].sent_wont = 1;
  1257.         break;
  1258.     case DONT:
  1259.         tnoptions[option].sent_dont = 1;
  1260.         break;
  1261.     default:
  1262.         syslog(LOG_ERR,"unknown telnet option code: %d",(int) optcode);
  1263.         break;
  1264.     }
  1265. }
  1266.  
  1267. /*
  1268.     mark our telnet options structure to indicate that the specified 
  1269.     opt code for the specified option, can be sent to our client.
  1270. */
  1271. void mark_telnet_option_as_not_sent(unsigned char optcode,unsigned char option)
  1272. {
  1273.     extern TELNET_OPTIONS tnoptions[];
  1274.  
  1275.     switch (optcode) {
  1276.     case WILL:
  1277.         tnoptions[option].sent_will = 0;
  1278.         break;
  1279.     case DO:
  1280.         tnoptions[option].sent_do = 0;
  1281.         break;
  1282.     case WONT:
  1283.         tnoptions[option].sent_wont = 0;
  1284.         break;
  1285.     case DONT:
  1286.         tnoptions[option].sent_dont = 0;
  1287.         break;
  1288.     default:
  1289.         syslog(LOG_ERR,"unknown telnet option code: %d",(int) optcode);
  1290.         break;
  1291.     }
  1292. }
  1293.  
  1294. /*
  1295.     mark our telnet options structure to indicate that the specified 
  1296.     option has been negotiated, server to client.
  1297. */
  1298. void enable_telnet_server_option(unsigned char option)
  1299. {
  1300.     extern TELNET_OPTIONS tnoptions[];
  1301.  
  1302.     tnoptions[option].server = 1;
  1303. }
  1304.  
  1305. /*
  1306.     mark our telnet options structure to indicate that the specified 
  1307.     option has not been negotiated, server to client.
  1308. */
  1309. void disable_telnet_server_option(unsigned char option)
  1310. {
  1311.     extern TELNET_OPTIONS tnoptions[];
  1312.  
  1313.     tnoptions[option].server = 0;
  1314. }
  1315.  
  1316. /*
  1317.     mark our telnet options structure to indicate that the specified 
  1318.     option has been negotiated, client to server.
  1319. */
  1320. void enable_telnet_client_option(unsigned char option)
  1321. {
  1322.     extern TELNET_OPTIONS tnoptions[];
  1323.  
  1324.     tnoptions[option].client = 1;
  1325. }
  1326.  
  1327. /*
  1328.     mark our telnet options structure to indicate that the specified 
  1329.     option has not been negotiated, client to server.
  1330. */
  1331. void disable_telnet_client_option(unsigned char option)
  1332. {
  1333.     extern TELNET_OPTIONS tnoptions[];
  1334.  
  1335.     tnoptions[option].client = 0;
  1336. }
  1337.  
  1338. /*
  1339.     is the specified telnet option enabled, server to client?
  1340. */
  1341. int telnet_server_option_is_enabled(unsigned char option)
  1342. {
  1343.     extern TELNET_OPTIONS tnoptions[];
  1344.     int enabled;
  1345.  
  1346.     enabled = tnoptions[option].server;
  1347.     return(enabled);
  1348. }
  1349.  
  1350. /*
  1351.     is the specified telnet option enabled, client to server?
  1352. */
  1353. int telnet_client_option_is_enabled(unsigned char option)
  1354. {
  1355.     extern TELNET_OPTIONS tnoptions[];
  1356.     int enabled;
  1357.  
  1358.     enabled = tnoptions[option].client;
  1359.     return(enabled);
  1360. }
  1361.  
  1362. /*
  1363.     is the specified telnet option disabled, server to client?
  1364. */
  1365. int telnet_server_option_is_disabled(unsigned char option)
  1366. {
  1367.     return(! telnet_server_option_is_enabled(option));
  1368. }
  1369.  
  1370. /*
  1371.     is the specified telnet option disabled, client to server?
  1372. */
  1373. int telnet_client_option_is_disabled(unsigned char option)
  1374. {
  1375.     return(! telnet_client_option_is_enabled(option));
  1376. }
  1377.  
  1378. /*
  1379.     this function is called when the IAC char is detected in the data 
  1380.     that was read from the socket.  if the buffer contains one or more 
  1381.     telnet option strings, then we act on them and remove them from 
  1382.     the buffer (so they won't be passed on to the modem).
  1383. */
  1384. void process_telnet_options(int sockfd,int modemfd,BUFFER *skt2mdm,BUFFER *cpc2skt)
  1385. {
  1386.     unsigned char *ptr;
  1387.     unsigned char *iac;
  1388.     unsigned char *se;
  1389.     unsigned char optcode;
  1390.     unsigned char option;
  1391.     int done;
  1392.     int size;
  1393.  
  1394.     /* sanity checks */
  1395.     if (skt2mdm == NULL) return;
  1396.     if (cpc2skt == NULL) return;
  1397.  
  1398.     /*
  1399.         peek at the buffer.  ptr points to its "active" portion 
  1400.         and size holds the number of chars buffered.
  1401.     */
  1402.     ptr = bfpeek(skt2mdm,&size);
  1403.  
  1404.     /* get pointer to IAC char */
  1405.     iac = mystrchr(ptr,IAC,size);
  1406.     if (iac == NULL) return;
  1407.  
  1408.     debug(DBG_VINF,"processing telnet options received from client");
  1409.  
  1410.     /* adjust size */
  1411.     size -= (iac - ptr);
  1412.     if (size < 2) return;                /* 2 is the minimum telnet command length */
  1413.  
  1414.     done = 0;
  1415.     while (! done) {
  1416.  
  1417.         /* decide what to do next, based upon the char following the IAC */
  1418.         optcode = (unsigned char) *(iac + 1);
  1419.         switch (optcode) {
  1420.         case IAC:                        /* a double IAC */
  1421.             bfrmstr(skt2mdm,iac,1);        /* remove one IAC char */
  1422.             iac++;                        /* point past remaining IAC */
  1423.             size -= 2;
  1424.             break;
  1425.         case WILL:                        /* WILL, DO, DONT, WONT */
  1426.         case DO:
  1427.         case DONT:
  1428.         case WONT:
  1429.             option = (unsigned char) *(iac + 2);
  1430.             respond_telnet_option(sockfd,cpc2skt,optcode,option);
  1431.             bfrmstr(skt2mdm,iac,3);        /* remove those 3 bytes */
  1432.             size -= 3;
  1433.             break;
  1434.         case SB:                        /* sub-option */
  1435.             option = (unsigned char) *(iac + 2);
  1436.             if ((option == TELOPT_COM_PORT_OPTION) && (telnet_client_option_is_enabled(option))) {
  1437.                 process_telnet_cpc_suboption(sockfd,modemfd,skt2mdm,cpc2skt,iac+3,size-3);
  1438.             } else {
  1439.                 syslog(LOG_ERR,"ignoring telnet suboption negotiations for %s",telnet_option2str(option));
  1440.             }
  1441.             se = mystrchr(iac+3,SE,size-3);    /* find SE char */
  1442.             if (se != NULL) {
  1443.                 bfrmstr(skt2mdm,iac,(se - iac + 1));    /* remove telnet options str from buffer */
  1444.                 size -= (se - iac + 1);
  1445.             } else {
  1446.                 iac += 3;
  1447.                 size -= 3;
  1448.             }
  1449.             break;
  1450.         case GA:                        /* standard telnet control functions (see RFC854) */
  1451.         case EL:
  1452.         case EC:
  1453.         case AYT:
  1454.         case AO:
  1455.         case IP:
  1456.         case BREAK:
  1457.         case DM:
  1458.         case NOP:
  1459.         default:                        /* invalid char after IAC.  RFC854 says treat it as NOP. */
  1460.             syslog(LOG_ERR,"ignoring telnet %s command",telnet_optcode2str(optcode));
  1461.             bfrmstr(skt2mdm,iac,2);        /* remove those 2 bytes */
  1462.             size -= 2;
  1463.             break;
  1464.         }
  1465.  
  1466.         if (size < 2) {                    /* 2 is the minimum telnet command length */
  1467.             ++done;
  1468.         }
  1469.  
  1470.         /* find next IAC char */
  1471.         if (! done) {
  1472.             ptr = iac;
  1473.             iac = mystrchr(ptr,IAC,size);
  1474.             if (iac == NULL) {
  1475.                 ++done;
  1476.             } else {
  1477.                 /* adjust size */
  1478.                 size -= (iac - ptr);
  1479.                 if (size < 2) {            /* 2 is the minimum telnet command length */
  1480.                     ++done;
  1481.                 }
  1482.             }
  1483.         }
  1484.     }
  1485. }
  1486.  
  1487. /*
  1488.     send a response to a telnet option request
  1489.  
  1490.     returns 0 on success, 1 on failure
  1491. */
  1492. int respond_telnet_option(int sockfd,BUFFER *cpc2skt,unsigned char optcode,unsigned char option)
  1493. {
  1494.     extern int errno;
  1495.     extern int client_logged_in;        /* is the client still "logged in"? */
  1496.     int ret = 0;
  1497.     unsigned char nakcode;
  1498.  
  1499.     syslog(LOG_INFO,"received telnet option %s %s",telnet_optcode2str(optcode),telnet_option2str(option));
  1500.  
  1501.     switch (option) {
  1502.     case TELOPT_COM_PORT_OPTION:
  1503.     case TELOPT_ECHO:
  1504.     case TELOPT_SGA:
  1505.         ret = respond_known_telnet_option(sockfd,cpc2skt,optcode,option);
  1506.         break;
  1507.     case TELOPT_LOGOUT:
  1508.         ret = respond_known_telnet_option(sockfd,cpc2skt,optcode,option);
  1509.         if ((optcode == WILL) || (optcode == DO)) {
  1510.             client_logged_in = 0;        /* client and server agree to the logout */
  1511.         }
  1512.         break;
  1513.     case TELOPT_BINARY:
  1514.         ret = respond_telnet_binary_option(sockfd,cpc2skt,optcode,option);
  1515.         break;
  1516.     default:
  1517.         switch (optcode) {
  1518.         case WILL:
  1519.         case WONT:
  1520.             nakcode = DONT;
  1521.             break;
  1522.         case DO:
  1523.         case DONT:
  1524.             nakcode = WONT;
  1525.             break;
  1526.         }
  1527.         /* send a negative acknowledgement */
  1528.         ret = send_telnet_option(sockfd,cpc2skt,nakcode,option);
  1529.         break;
  1530.     }
  1531.  
  1532.     /*
  1533.         Jeffrey Altman added similar code to sredird.c.  I'm not sure why.
  1534.     */
  1535. #if 0
  1536.     switch (optcode) {
  1537.     case WILL:
  1538.         mark_telnet_option_as_not_sent(DO,option);
  1539.         mark_telnet_option_as_not_sent(DONT,option);
  1540.         break;
  1541.     case DO:
  1542.         mark_telnet_option_as_not_sent(WILL,option);
  1543.         mark_telnet_option_as_not_sent(WONT,option);
  1544.         break;
  1545.     case WONT:
  1546.         mark_telnet_option_as_not_sent(DO,option);
  1547.         mark_telnet_option_as_not_sent(DONT,option);
  1548.         break;
  1549.     case DONT:
  1550.         mark_telnet_option_as_not_sent(WILL,option);
  1551.         mark_telnet_option_as_not_sent(WONT,option);
  1552.         break;
  1553.     default:
  1554.         break;
  1555.     }
  1556. #endif
  1557.  
  1558.     return(ret);
  1559. }
  1560.  
  1561. /*
  1562.     respond to the optcode+option that we've received from the client
  1563. */
  1564. int respond_known_telnet_option(int sockfd,BUFFER *cpc2skt,unsigned char optcode,unsigned char option)
  1565. {
  1566.     int ret;
  1567.  
  1568.     /* just in case */
  1569.     if (option == TELOPT_BINARY) {
  1570.         return(respond_telnet_binary_option(sockfd,cpc2skt,optcode,option));
  1571.     }
  1572.  
  1573.     ret = 0;
  1574.     switch (optcode) {
  1575.     case WILL:                                            /* server DO, client WILL */
  1576.         if (telnet_client_option_is_disabled(option)) {    /* prevent option loop */
  1577.             ret = send_telnet_option(sockfd,cpc2skt,DO,option);
  1578.             enable_telnet_client_option(option);        /* server <<== client */
  1579.         }
  1580.         break;
  1581.     case WONT:                                            /* server DO, client WONT */
  1582.         if (telnet_client_option_is_enabled(option)) {    /* prevent option loop */
  1583.             ret = send_telnet_option(sockfd,cpc2skt,DONT,option);
  1584.             disable_telnet_client_option(option);
  1585.         }
  1586.         break;
  1587.     case DO:                                            /* server WILL, client DO */
  1588.         if (telnet_server_option_is_disabled(option)) {    /* prevent option loop */
  1589.             ret = send_telnet_option(sockfd,cpc2skt,WILL,option);
  1590.             enable_telnet_server_option(option);        /* server ==>> client */
  1591.         }
  1592.         break;
  1593.     case DONT:                                            /* server WILL, client DONT */
  1594.         if (telnet_server_option_is_enabled(option)) {    /* prevent option loop */
  1595.             ret = send_telnet_option(sockfd,cpc2skt,WONT,option);
  1596.             disable_telnet_server_option(option);
  1597.         }
  1598.         break;
  1599.     default:
  1600.         break;
  1601.     }
  1602.  
  1603.     return(ret);
  1604. }
  1605.  
  1606. /*
  1607.     respond to the optcode+option that we've received from the client
  1608. */
  1609. int respond_telnet_binary_option(int sockfd,BUFFER *cpc2skt,unsigned char optcode,unsigned char option)
  1610. {
  1611.     extern int tnmode[];
  1612.     int ret;
  1613.  
  1614.     /* just in case */
  1615.     if (option != TELOPT_BINARY) {
  1616.         return(respond_known_telnet_option(sockfd,cpc2skt,optcode,option));
  1617.     }
  1618.  
  1619.     ret = 0;
  1620.     switch (optcode) {
  1621.     case WILL:                                            /* server DO, client WILL */
  1622.         if (telnet_client_option_is_disabled(option)) {    /* prevent option loop */
  1623.             ret = send_telnet_option(sockfd,cpc2skt,DO,option);
  1624.             enable_telnet_client_option(option);        /* server <<== client */
  1625.             if (tnmode[CLIENT] != BINARY) {
  1626.                 syslog(LOG_INFO,"telnet connection is now in BINARY mode (server <<== client)");
  1627.                 tnmode[CLIENT] = BINARY;
  1628.             }
  1629.         }
  1630.         break;
  1631.     case WONT:                                            /* server DO, client WONT */
  1632.         if (telnet_client_option_is_enabled(option)) {    /* prevent option loop */
  1633.             ret = send_telnet_option(sockfd,cpc2skt,DONT,option);
  1634.             disable_telnet_client_option(option);
  1635.             if (tnmode[CLIENT] != ASCII) {
  1636.                 syslog(LOG_INFO,"telnet connection is now in ASCII mode (server <<== client)");
  1637.                 tnmode[CLIENT] = ASCII;
  1638.             }
  1639.         }
  1640.         break;
  1641.     case DO:                                            /* server WILL, client DO */
  1642.         if (telnet_server_option_is_disabled(option)) {    /* prevent option loop */
  1643.             ret = send_telnet_option(sockfd,cpc2skt,WILL,option);
  1644.             enable_telnet_server_option(option);        /* server ==>> client */
  1645.             if (tnmode[SERVER] != BINARY) {
  1646.                 syslog(LOG_INFO,"telnet connection is now in BINARY mode (server ==>> client)");
  1647.                 tnmode[SERVER] = BINARY;
  1648.             }
  1649.         }
  1650.         break;
  1651.     case DONT:                                            /* server WILL, client DONT */
  1652.         if (telnet_server_option_is_enabled(option)) {    /* prevent option loop */
  1653.             ret = send_telnet_option(sockfd,cpc2skt,WONT,option);
  1654.             disable_telnet_server_option(option);
  1655.             if (tnmode[SERVER] != ASCII) {
  1656.                 syslog(LOG_INFO,"telnet connection is now in ASCII mode (server ==>> client)");
  1657.                 tnmode[SERVER] = ASCII;
  1658.             }
  1659.         }
  1660.         break;
  1661.     default:
  1662.         break;
  1663.     }
  1664.     return(ret);
  1665. }
  1666.  
  1667. /*
  1668.     optstr points to a Telnet CPC option string which contains at least 
  1669.     the option code, plus the IAC and SE characters.
  1670. */
  1671. int process_telnet_cpc_suboption(int sockfd,int modemfd,BUFFER *skt2mdm,BUFFER *cpc2skt,unsigned char *optstr,int optlen)
  1672. {
  1673.     extern int session_state;
  1674.     static unsigned char command[MAX_TELNET_CPC_COMMAND_LEN];
  1675.     unsigned char suboptcode;
  1676.     unsigned char *iac;
  1677.     unsigned char *se;
  1678.     unsigned char *ptr;
  1679.     unsigned long value;
  1680.     int len;
  1681.     int n;
  1682.     int ret;
  1683.  
  1684.     /* sanity checking */
  1685.     if (optlen < 3) return(1);
  1686.  
  1687.     suboptcode = *optstr++;
  1688.     optlen--;
  1689.  
  1690.     /* get ptrs to IAC SE sequence which terminates the command */
  1691.     se = mystrchr(optstr,SE,optlen);    /* find SE char */
  1692.     if (se == NULL) return(1);
  1693.     iac = se - 1;                        /* ptr to IAC char */
  1694.     if (*iac != IAC) return(1);
  1695.  
  1696.     /* calc length of command.  may be zero. */
  1697.     len = iac - optstr;
  1698.     if (len >= MAX_TELNET_CPC_COMMAND_LEN) {
  1699.         len = MAX_TELNET_CPC_COMMAND_LEN - 1;
  1700.     }
  1701.  
  1702.     /*
  1703.         copy command to our buffer and null-terminate it.  note that 
  1704.         the command may be empty. 
  1705.     */
  1706.     if (len > 0) memcpy(command,optstr,len);
  1707.     command[len] = '\0';
  1708.  
  1709.     /*
  1710.         process IAC escapes within the command
  1711.     */
  1712.     iac = mystrchr(command,IAC,len);
  1713.     while (iac != NULL) {
  1714.         if (*(iac+1) == IAC) {                /* double IAC */
  1715.             iac++;                            /* point to 2nd IAC */
  1716.             n = len - (iac - command);        /* # chars after 2nd IAC */
  1717.             for (ptr = iac; n > 0; n--) {    /* shift chars 1 position left */
  1718.                 *ptr = *(ptr+1);
  1719.                 ptr++;
  1720.             }
  1721.             ptr = iac;                        /* *iac is no longer IAC */
  1722.             len--;                            /* adj len for removed IAC */
  1723.         } else {                            /* else, not double IAC */
  1724.             ptr = iac + 1;
  1725.         }
  1726.         n = len - (ptr - command);            /* # chars left in command */
  1727.         iac = mystrchr(ptr,IAC,n);            /* any more IAC chars? */
  1728.     }
  1729.  
  1730.     /*
  1731.         if len is 1, 2 or 4 then interpret the command as a value
  1732.     */
  1733.     if (len == 1) {
  1734.         value = (unsigned long) command[0];
  1735.     } else if (len == 2) {
  1736.         value = (unsigned long) ntohs(*((unsigned short *) command));
  1737.     } else if (len == 4) {
  1738.         value = ntohl(*((unsigned long *) command));
  1739.     } else {
  1740.         value = 0L;
  1741.     }
  1742.  
  1743.     /* log which telnet CPC suboption we've received, its args, and the arg value */
  1744.     telnet_cpc_log_subopt("received",suboptcode,value,command,len);
  1745.  
  1746.     /* process the telnet CPC suboption */
  1747.     ret = 0;
  1748.     switch (suboptcode) {
  1749.     case CPC_SIGNATURE_C2S:
  1750.     case CPC_SIGNATURE_S2C:
  1751.         ret = respond_telnet_cpc_signature_subopt(sockfd,cpc2skt,suboptcode,command,len);
  1752.         break;
  1753.     case CPC_SET_BAUDRATE_C2S:
  1754.     case CPC_SET_BAUDRATE_S2C:
  1755.         ret = respond_telnet_cpc_baudrate_subopt(sockfd,modemfd,cpc2skt,suboptcode,value);
  1756.         break;
  1757.     case CPC_SET_DATASIZE_C2S:
  1758.     case CPC_SET_DATASIZE_S2C:
  1759.         ret = respond_telnet_cpc_datasize_subopt(sockfd,modemfd,cpc2skt,suboptcode,value);
  1760.         break;
  1761.     case CPC_SET_PARITY_C2S:
  1762.     case CPC_SET_PARITY_S2C:
  1763.         ret = respond_telnet_cpc_parity_subopt(sockfd,modemfd,cpc2skt,suboptcode,value);
  1764.         break;
  1765.     case CPC_SET_STOPSIZE_C2S:
  1766.     case CPC_SET_STOPSIZE_S2C:
  1767.         ret = respond_telnet_cpc_stopsize_subopt(sockfd,modemfd,cpc2skt,suboptcode,value);
  1768.         break;
  1769.     case CPC_SET_CONTROL_C2S:
  1770.     case CPC_SET_CONTROL_S2C:
  1771.         ret = respond_telnet_cpc_control_subopt(sockfd,modemfd,cpc2skt,suboptcode,value);
  1772.         break;
  1773.     /*
  1774.         the flow control suspend and resume commands are the only ones for 
  1775.         which no reply is expected by the client
  1776.     */
  1777.     case CPC_FLOWCONTROL_SUSPEND_C2S:
  1778.     case CPC_FLOWCONTROL_SUSPEND_S2C:
  1779.         syslog(LOG_INFO,"telnet CPC client suspends the session");
  1780.         session_state = SUSPEND;
  1781.         break;
  1782.     case CPC_FLOWCONTROL_RESUME_C2S:
  1783.     case CPC_FLOWCONTROL_RESUME_S2C:
  1784.         syslog(LOG_INFO,"telnet CPC client resumes the session");
  1785.         session_state = RESUME;
  1786.         break;
  1787.  
  1788.     case CPC_SET_LINESTATE_MASK_C2S:
  1789.     case CPC_SET_LINESTATE_MASK_S2C:
  1790.     case CPC_NOTIFY_LINESTATE_C2S:
  1791.     case CPC_NOTIFY_LINESTATE_S2C:
  1792.         ret = respond_telnet_cpc_linestate_subopt(sockfd,modemfd,cpc2skt,suboptcode,value);
  1793.         break;
  1794.     case CPC_SET_MODEMSTATE_MASK_C2S:
  1795.     case CPC_SET_MODEMSTATE_MASK_S2C:
  1796.     case CPC_NOTIFY_MODEMSTATE_C2S:
  1797.     case CPC_NOTIFY_MODEMSTATE_S2C:
  1798.         ret = respond_telnet_cpc_modemstate_subopt(sockfd,modemfd,cpc2skt,suboptcode,value);
  1799.         break;
  1800.     case CPC_PURGE_DATA_C2S:
  1801.     case CPC_PURGE_DATA_S2C:
  1802.         ret = respond_telnet_cpc_purge_data_subopt(sockfd,modemfd,skt2mdm,cpc2skt,suboptcode,value);
  1803.         break;
  1804.     default:
  1805.         break;
  1806.     }
  1807.  
  1808.     return(ret);
  1809. }
  1810.  
  1811. int respond_telnet_cpc_signature_subopt(int sockfd,BUFFER *cpc2skt,unsigned char suboptcode,unsigned char *command,int cmdlen)
  1812. {
  1813.     extern char *progname;                /* our program name */
  1814.     extern char *version;                /* version string */
  1815.     extern SERIAL_INFO *si;                /* global modem ptr */
  1816.     extern int ask_client_signature;    /* ask client for its signature? */
  1817.     char format[32];                    /* holds format string */
  1818.     char *p;
  1819.     int len;
  1820.     int ret;
  1821.  
  1822.     if (*command == '\0') {            /* client wants us to send our signature */
  1823.         strcpy(format,"%s %s");        /* initial format: program name and version number */
  1824.         len = strlen(progname);
  1825.         len += strlen(version);
  1826.         len++;                        /* +1 for a space between 'progname' and 'version' */
  1827.         if (si != NULL) {
  1828.             if (si->device != NULL) {
  1829.                 strcat(format,", %s");
  1830.                 len += 2;            /* +2 for a comma and a space */
  1831.                 len += strlen(si->device);
  1832.             }
  1833.             if (si->description != NULL) {
  1834.                 strcat(format,", %s");
  1835.                 len += 2;            /* +2 for a comma and a space */
  1836.                 len += strlen(si->description);
  1837.             }
  1838.         }
  1839.         len++;                        /* +1 for a null terminator */
  1840.         p = malloc(len);
  1841.         if (p != NULL) {
  1842.             if ((si != NULL) && ((si->device != NULL) || (si->description != NULL))) {
  1843.                 if ((si->device != NULL) && (si->description != NULL)) {
  1844.                     snprintf(p,len,format,progname,version,si->device,si->description);
  1845.                 } else {
  1846.                     if (si->device != NULL) {
  1847.                         snprintf(p,len,format,progname,version,si->device);
  1848.                     } else {
  1849.                         snprintf(p,len,format,progname,version,si->description);
  1850.                     }
  1851.                 }
  1852.             } else {
  1853.                 snprintf(p,len,format,progname,version);
  1854.             }
  1855.             ret = send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SIGNATURE_S2C,(unsigned char *) p,len-1);    /* -1 for the null */
  1856.             free(p);
  1857.         } else {
  1858.             syslog(LOG_ERR,"unable to allocate memory for signature");
  1859.             p = "telnetcpcd";
  1860.             ret = send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SIGNATURE_S2C,(unsigned char *) p,strlen(p));
  1861.         }
  1862.         if (ret == 0) {                /* now request client signature, but only once */
  1863.             if (ask_client_signature) {
  1864.                 ret = send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SIGNATURE_C2S,(unsigned char *) "",0);
  1865.                 ask_client_signature = 0;
  1866.             }
  1867.         }
  1868.     } else {                        /* client has sent us their signature */
  1869.         syslog(LOG_INFO,"telnet CPC client signature: %s",command);
  1870.         ret = 0;
  1871.     }
  1872.     return(ret);
  1873. }
  1874.  
  1875. int respond_telnet_cpc_baudrate_subopt(int sockfd,int modemfd,BUFFER *cpc2skt,unsigned char suboptcode,unsigned long value)
  1876. {
  1877.     unsigned int netvalue;
  1878.     int ret;
  1879.  
  1880.     if (value == 0L) {                /* client wants us to send the baudrate */
  1881.         value = get_baudrate(modemfd);
  1882.         netvalue = (unsigned int) htonl(value);
  1883.         syslog(LOG_INFO,"telnet CPC client requests the baudrate; sending %lu",value);
  1884.         ret = send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SET_BAUDRATE_S2C,(unsigned char *) &netvalue,4);
  1885.     } else {                        /* client wants to set a new baudrate */
  1886.         syslog(LOG_INFO,"telnet CPC client is setting the baudrate to %lu",value);
  1887.         ret = set_baudrate(modemfd,value);
  1888.  
  1889.         /* send new baudrate in reply */
  1890.         value = get_baudrate(modemfd);
  1891.         netvalue = (unsigned int) htonl(value);
  1892.         ret += send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SET_BAUDRATE_S2C,(unsigned char *) &netvalue,4);
  1893.     }
  1894.     return(ret);
  1895. }
  1896.  
  1897. unsigned long get_baudrate(int modemfd)
  1898. {
  1899.     struct termios term;                /* current termios */
  1900.     speed_t speed;
  1901.     int ret;
  1902.  
  1903.     ret = tcgetattr(modemfd,&term);
  1904.     if (ret == 0) {
  1905.         speed = cfgetospeed(&term);
  1906.         return(speed_t2ulong(speed));
  1907.     } else {
  1908.         syslog_perror("tcgetattr() error");
  1909.         return(9600);
  1910.     }
  1911. }
  1912.  
  1913. int set_baudrate(int modemfd,unsigned long value)
  1914. {
  1915.     struct termios term;                /* current termios */
  1916.     speed_t speed;
  1917.     int ret;
  1918.  
  1919.     ret = tcgetattr(modemfd,&term);
  1920.     if (ret == 0) {
  1921.         speed = ulong2speed_t(value);
  1922.         cfsetospeed(&term,speed);
  1923.         ret = tcsetattr(modemfd,TCSAFLUSH,&term);
  1924.         if (ret != 0) {
  1925.             syslog_perror("tcsetattr() error");
  1926.         }
  1927.     } else {
  1928.         syslog_perror("tcgetattr() error");
  1929.     }
  1930.     return(ret);
  1931. }
  1932.  
  1933. int respond_telnet_cpc_datasize_subopt(int sockfd,int modemfd,BUFFER *cpc2skt,unsigned char suboptcode,unsigned long value)
  1934. {
  1935.     unsigned char datasize;
  1936.     int ret;
  1937.  
  1938.     if (value == CPC_DATASIZE_QUERY) {    /* client wants us to send the data size */
  1939.         datasize = get_datasize(modemfd);
  1940.         syslog(LOG_INFO,"telnet CPC client requests the data size; sending %s",telnet_cpc_datasize2str(datasize));
  1941.         ret = send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SET_DATASIZE_S2C,(unsigned char *) &datasize,1);
  1942.     } else {                            /* client wants to set a new data size */
  1943.         syslog(LOG_INFO,"telnet CPC client is setting the data size to %s",telnet_cpc_datasize2str((unsigned char) value));
  1944.         ret = set_datasize(modemfd,value);
  1945.  
  1946.         /* send new datasize in reply */
  1947.         datasize = get_datasize(modemfd);
  1948.         ret += send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SET_DATASIZE_S2C,(unsigned char *) &datasize,1);
  1949.     }
  1950.     return(ret);
  1951. }
  1952.  
  1953. unsigned char get_datasize(int modemfd)
  1954. {
  1955.     struct termios term;                /* current termios */
  1956.     unsigned long cs;
  1957.     int ret;
  1958.  
  1959.     ret = tcgetattr(modemfd,&term);
  1960.     if (ret == 0) {
  1961.         cs = term.c_cflag & CSIZE;
  1962.         switch (cs) {
  1963.         case CS8:
  1964.             return(CPC_DATASIZE_CS8);
  1965.         case CS7:
  1966.             return(CPC_DATASIZE_CS7);
  1967.         case CS6:
  1968.             return(CPC_DATASIZE_CS6);
  1969.         case CS5:
  1970.             return(CPC_DATASIZE_CS5);
  1971.         default:
  1972.             return(CPC_DATASIZE_CS8);
  1973.         }
  1974.     } else {
  1975.         syslog_perror("tcgetattr() error");
  1976.         return(CPC_DATASIZE_CS8);
  1977.     }
  1978. }
  1979.  
  1980. int set_datasize(int modemfd,unsigned long value)
  1981. {
  1982.     struct termios term;                /* current termios */
  1983.     int ret;
  1984.  
  1985.     ret = tcgetattr(modemfd,&term);
  1986.     if (ret == 0) {
  1987.         term.c_cflag &= ~CSIZE;
  1988.         switch (value) {
  1989.         case CPC_DATASIZE_CS8:
  1990.             term.c_cflag |= CS8;
  1991.             break;
  1992.         case CPC_DATASIZE_CS7:
  1993.             term.c_cflag |= CS7;
  1994.             break;
  1995.         case CPC_DATASIZE_CS6:
  1996.             term.c_cflag |= CS6;
  1997.             break;
  1998.         case CPC_DATASIZE_CS5:
  1999.             term.c_cflag |= CS5;
  2000.             break;
  2001.         default:
  2002.             term.c_cflag |= CS8;
  2003.             break;
  2004.         }
  2005.         ret = tcsetattr(modemfd,TCSAFLUSH,&term);
  2006.         if (ret != 0) {
  2007.             syslog_perror("tcsetattr() error");
  2008.         }
  2009.     } else {
  2010.         syslog_perror("tcgetattr() error");
  2011.     }
  2012.     return(ret);
  2013. }
  2014.  
  2015. int respond_telnet_cpc_parity_subopt(int sockfd,int modemfd,BUFFER *cpc2skt,unsigned char suboptcode,unsigned long value)
  2016. {
  2017.     unsigned char parity;
  2018.     int ret;
  2019.  
  2020.     if (value == CPC_PARITY_QUERY) {    /* client wants us to send the parity */
  2021.         parity = get_parity(modemfd);
  2022.         syslog(LOG_INFO,"telnet CPC client requests the parity setting; sending \"%s\"",telnet_cpc_parity2str(parity));
  2023.         ret = send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SET_PARITY_S2C,(unsigned char *) &parity,1);
  2024.     } else {                            /* client wants to set a new parity setting */
  2025.         syslog(LOG_INFO,"telnet CPC client is setting the parity to \"%s\"",telnet_cpc_parity2str((unsigned char) value));
  2026.         ret = set_parity(modemfd,value);
  2027.  
  2028.         /* send new parity setting in reply */
  2029.         parity = get_parity(modemfd);
  2030.         ret += send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SET_PARITY_S2C,(unsigned char *) &parity,1);
  2031.     }
  2032.     return(ret);
  2033. }
  2034.  
  2035. unsigned char get_parity(int modemfd)
  2036. {
  2037.     struct termios term;                /* current termios */
  2038.     int ret;
  2039.  
  2040.     ret = tcgetattr(modemfd,&term);
  2041.     if (ret == 0) {
  2042.         if (term.c_cflag & PARENB) {    /* if parity is enabled */
  2043.             if (term.c_cflag & PARODD) {
  2044.                 return(CPC_PARITY_ODD);    /* odd parity */
  2045.             } else {
  2046.                 return(CPC_PARITY_EVEN);/* even parity */
  2047.             }
  2048.         } else {
  2049.             return(CPC_PARITY_NONE);    /* no parity */
  2050.         }
  2051.     } else {
  2052.         syslog_perror("tcgetattr() error");
  2053.         return(CPC_PARITY_NONE);        /* no parity */
  2054.     }
  2055. }
  2056.  
  2057. int set_parity(int modemfd,unsigned long value)
  2058. {
  2059.     struct termios term;                /* current termios */
  2060.     int ret;
  2061.  
  2062.     ret = tcgetattr(modemfd,&term);
  2063.     if (ret == 0) {
  2064.         switch (value) {
  2065.         case CPC_PARITY_ODD:
  2066.             term.c_cflag |= PARENB;
  2067.             term.c_cflag |= PARODD;
  2068.             term.c_cflag &= ~CSTOPB;
  2069.             break;
  2070.         case CPC_PARITY_EVEN:
  2071.             term.c_cflag |= PARENB;
  2072.             term.c_cflag &= ~PARODD;
  2073.             term.c_cflag &= ~CSTOPB;
  2074.             break;
  2075.         case CPC_PARITY_MARK:
  2076.         case CPC_PARITY_SPACE:
  2077.             syslog(LOG_ERR,"ignoring request to set %s parity, will use None instead",telnet_cpc_parity2str((unsigned char) value));
  2078.             /* fall through to CPC_PARITY_NONE */
  2079.         case CPC_PARITY_NONE:
  2080.         default:
  2081.             term.c_cflag &= ~PARENB;
  2082.             term.c_cflag &= ~CSTOPB;
  2083.             break;
  2084.         }
  2085.         ret = tcsetattr(modemfd,TCSAFLUSH,&term);
  2086.         if (ret != 0) {
  2087.             syslog_perror("tcsetattr() error");
  2088.         }
  2089.     } else {
  2090.         syslog_perror("tcgetattr() error");
  2091.     }
  2092.     return(ret);
  2093. }
  2094.  
  2095. int respond_telnet_cpc_stopsize_subopt(int sockfd,int modemfd,BUFFER *cpc2skt,unsigned char suboptcode,unsigned long value)
  2096. {
  2097.     unsigned char stopbits;
  2098.     int ret;
  2099.  
  2100.     if (value == CPC_STOPSIZE_QUERY) {    /* client wants us to send the number of stop bits */
  2101.         stopbits = get_stopsize(modemfd);
  2102.         syslog(LOG_INFO,"telnet CPC client requests the number of stop bits; sending %s",telnet_cpc_stopsize2str(stopbits));
  2103.         ret = send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SET_STOPSIZE_S2C,(unsigned char *) &stopbits,1);
  2104.     } else {                            /* client wants to set a new number of stop bits */
  2105.         syslog(LOG_INFO,"telnet CPC client is setting the number of stop bits to %s",telnet_cpc_stopsize2str((unsigned char) value));
  2106.         ret = set_stopsize(modemfd,value);
  2107.  
  2108.         /* send new stopsize in reply */
  2109.         stopbits = get_stopsize(modemfd);
  2110.         ret += send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SET_STOPSIZE_S2C,(unsigned char *) &stopbits,1);
  2111.     }
  2112.     return(ret);
  2113. }
  2114.  
  2115. unsigned char get_stopsize(int modemfd)
  2116. {
  2117.     struct termios term;                /* current termios */
  2118.     int ret;
  2119.  
  2120.     ret = tcgetattr(modemfd,&term);
  2121.     if (ret == 0) {
  2122.         if (term.c_cflag & CSTOPB) {
  2123.             return(CPC_STOPSIZE_2BITS);
  2124.         } else {
  2125.             return(CPC_STOPSIZE_1BIT);
  2126.         }
  2127.     } else {
  2128.         syslog_perror("tcgetattr() error");
  2129.         return(CPC_STOPSIZE_1BIT);
  2130.     }
  2131. }
  2132.  
  2133. int set_stopsize(int modemfd,unsigned long value)
  2134. {
  2135.     struct termios term;                /* current termios */
  2136.     int ret;
  2137.  
  2138.     ret = tcgetattr(modemfd,&term);
  2139.     if (ret == 0) {
  2140.         switch (value) {
  2141.         case CPC_STOPSIZE_2BITS:
  2142.             term.c_cflag &= CSTOPB;
  2143.             break;
  2144.         case CPC_STOPSIZE_15BITS:
  2145.             syslog(LOG_ERR,"ignoring request to set 1.5 stop bits, will use 1 instead");
  2146.             /* fall through to CPC_STOPSIZE_1BIT */
  2147.         case CPC_STOPSIZE_1BIT:
  2148.         default:
  2149.             term.c_cflag &= ~CSTOPB;
  2150.             break;
  2151.         }
  2152.         ret = tcsetattr(modemfd,TCSAFLUSH,&term);
  2153.         if (ret != 0) {
  2154.             syslog_perror("tcsetattr() error");
  2155.         }
  2156.     } else {
  2157.         syslog_perror("tcgetattr() error");
  2158.     }
  2159.     return(ret);
  2160. }
  2161.  
  2162. int respond_telnet_cpc_control_subopt(int sockfd,int modemfd,BUFFER *cpc2skt,unsigned char suboptcode,unsigned long value)
  2163. {
  2164.     unsigned char flowcode;
  2165.     int ret;
  2166.  
  2167.     switch (value) {
  2168.     case CPC_SET_CONTROL_FLOW_QUERY:    /* client wants us to send flow control info */
  2169.     case CPC_SET_CONTROL_INFLOW_QUERY:
  2170.         flowcode = get_flowcontrol(modemfd);
  2171.         if (value == CPC_SET_CONTROL_INFLOW_QUERY) {
  2172.             /* translate CPC_SET_CONTROL_FLOW_xxx codes to CPC_SET_CONTROL_INFLOW_xxx codes */
  2173.             switch (flowcode) {
  2174.             case CPC_SET_CONTROL_FLOW_NONE:
  2175.                 flowcode = CPC_SET_CONTROL_INFLOW_NONE;
  2176.                 break;
  2177.             case CPC_SET_CONTROL_FLOW_XONXOFF:
  2178.                 flowcode = CPC_SET_CONTROL_INFLOW_XONXOFF;
  2179.                 break;
  2180.             case CPC_SET_CONTROL_FLOW_HARDWARE:
  2181.                 flowcode = CPC_SET_CONTROL_INFLOW_HARDWARE;
  2182.                 break;
  2183.             }
  2184.         }
  2185.         syslog(LOG_INFO,"telnet CPC client requests %sbound flow control info; sending \"%s\"",
  2186.             (value == CPC_SET_CONTROL_FLOW_QUERY) ? "out" : "in",telnet_cpc_flowcontrol2str(flowcode));
  2187.         ret = send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SET_CONTROL_S2C,(unsigned char *) &flowcode,1);
  2188.         break;
  2189.     case CPC_SET_CONTROL_FLOW_NONE:        /* client wants to set a new outbound flow control setting */
  2190.     case CPC_SET_CONTROL_FLOW_XONXOFF:
  2191.     case CPC_SET_CONTROL_FLOW_HARDWARE:
  2192.     case CPC_SET_CONTROL_FLOW_DCD:
  2193.     case CPC_SET_CONTROL_FLOW_DSR:
  2194.         syslog(LOG_INFO,"telnet CPC client is setting the outbound flow control to \"%s\"",
  2195.             telnet_cpc_flowcontrol2str((unsigned char) value));
  2196.         ret = set_flowcontrol(modemfd,value);
  2197.  
  2198.         /* send new flow control setting in reply */
  2199.         flowcode = get_flowcontrol(modemfd);
  2200.         ret += send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SET_CONTROL_S2C,(unsigned char *) &flowcode,1);
  2201.         break;
  2202.  
  2203.     /*
  2204.         since we don't support setting inbound flow control separately from 
  2205.         outbound flow control, we fake it by replying to the client with 
  2206.         whatever value was sent to us.
  2207.     */
  2208.     case CPC_SET_CONTROL_INFLOW_NONE:    /* client wants to set a new inbound flow control setting */
  2209.     case CPC_SET_CONTROL_INFLOW_XONXOFF:
  2210.     case CPC_SET_CONTROL_INFLOW_HARDWARE:
  2211.     case CPC_SET_CONTROL_INFLOW_DTR:
  2212.         syslog(LOG_INFO,"telnet CPC client is setting the inbound flow control to \"%s\"",
  2213.             telnet_cpc_flowcontrol2str((unsigned char) value));
  2214.  
  2215.         /* send the requested flow control setting in reply */
  2216.         flowcode = (unsigned char) value;
  2217.         ret = send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SET_CONTROL_S2C,(unsigned char *) &flowcode,1);
  2218.         break;
  2219.  
  2220.     case CPC_SET_CONTROL_BREAK_QUERY:
  2221.     case CPC_SET_CONTROL_BREAK_ON:
  2222.     case CPC_SET_CONTROL_BREAK_OFF:
  2223.         ret = respond_telnet_cpc_control_break_subopt(sockfd,cpc2skt,suboptcode,value);
  2224.         break;
  2225.  
  2226.     case CPC_SET_CONTROL_DTR_QUERY:
  2227.     case CPC_SET_CONTROL_DTR_ON:
  2228.     case CPC_SET_CONTROL_DTR_OFF:
  2229.         ret = respond_telnet_cpc_control_dtr_subopt(sockfd,modemfd,cpc2skt,suboptcode,value);
  2230.         break;
  2231.  
  2232.     case CPC_SET_CONTROL_RTS_QUERY:
  2233.     case CPC_SET_CONTROL_RTS_ON:
  2234.     case CPC_SET_CONTROL_RTS_OFF:
  2235.         ret = respond_telnet_cpc_control_rts_subopt(sockfd,modemfd,cpc2skt,suboptcode,value);
  2236.         break;
  2237.  
  2238.     default:
  2239.         ret = 0;
  2240.         break;
  2241.     }
  2242.     return(ret);
  2243. }
  2244.  
  2245. int respond_telnet_cpc_control_break_subopt(int sockfd,BUFFER *cpc2skt,unsigned char suboptcode,unsigned long value)
  2246. {
  2247.     extern int break_signaled;
  2248.     unsigned char breakcode;
  2249.     int ret;
  2250.  
  2251.     switch (value) {
  2252.     case CPC_SET_CONTROL_BREAK_QUERY:    /* client wants us to send the break state */
  2253.         syslog(LOG_INFO,"telnet CPC client requests the break state; sending Break %s",(break_signaled) ? "On" : "Off");
  2254.         break;
  2255.     case CPC_SET_CONTROL_BREAK_ON:        /* client wants us to turn on the break state */
  2256.         syslog(LOG_INFO,"telnet CPC client sets the break state On");
  2257.         break_signaled = 1;
  2258.         break;
  2259.     case CPC_SET_CONTROL_BREAK_OFF:        /* client wants us to turn off the break state */
  2260.         syslog(LOG_INFO,"telnet CPC client sets the break state Off");
  2261.         break_signaled = 0;
  2262.         break;
  2263.     default:
  2264.         break;
  2265.     }
  2266.  
  2267.     /* send current break state in reply */
  2268.     if (break_signaled) {
  2269.         breakcode = CPC_SET_CONTROL_BREAK_ON;
  2270.     } else {
  2271.         breakcode = CPC_SET_CONTROL_BREAK_OFF;
  2272.     }
  2273.     ret = send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SET_CONTROL_S2C,(unsigned char *) &breakcode,1);
  2274.     return(ret);
  2275. }
  2276.  
  2277. int respond_telnet_cpc_control_dtr_subopt(int sockfd,int modemfd,BUFFER *cpc2skt,unsigned char suboptcode,unsigned long value)
  2278. {
  2279.     unsigned char dtrcode;
  2280.     unsigned int modemsignals;
  2281.     char *p;
  2282.     int ret;
  2283.  
  2284.     ret = 0;
  2285.     switch (value) {
  2286.     case CPC_SET_CONTROL_DTR_QUERY:        /* client wants us to send the DTR state */
  2287.         modemsignals = get_modem_signals(modemfd);
  2288.         if (modemsignals & TIOCM_DTR) {
  2289.             dtrcode = CPC_SET_CONTROL_DTR_ON;
  2290.             p = "On";
  2291.         } else {
  2292.             dtrcode = CPC_SET_CONTROL_DTR_OFF;
  2293.             p = "Off";
  2294.         }
  2295.         syslog(LOG_INFO,"telnet CPC client requests DTR state; sending DTR %s",p);
  2296.         break;
  2297.     case CPC_SET_CONTROL_DTR_ON:
  2298.         syslog(LOG_INFO,"telnet CPC client requests that DTR be raised");
  2299.         dtrcode = (unsigned char) value;
  2300.         ret = set_modem_signals(modemfd,(unsigned int) value);
  2301.         break;
  2302.     case CPC_SET_CONTROL_DTR_OFF:
  2303.         syslog(LOG_INFO,"telnet CPC client requests that DTR be lowered");
  2304.         dtrcode = (unsigned char) value;
  2305.         ret = set_modem_signals(modemfd,(unsigned int) value);
  2306.         break;
  2307.     default:
  2308.         break;
  2309.     }
  2310.  
  2311.     /* send current DTR value in reply */
  2312.     ret += send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SET_CONTROL_S2C,(unsigned char *) &dtrcode,1);
  2313.     return(ret);
  2314. }
  2315.  
  2316. int respond_telnet_cpc_control_rts_subopt(int sockfd,int modemfd,BUFFER *cpc2skt,unsigned char suboptcode,unsigned long value)
  2317. {
  2318.     unsigned char rtscode;
  2319.     unsigned int modemsignals;
  2320.     char *p;
  2321.     int ret;
  2322.  
  2323.     ret = 0;
  2324.     switch (value) {
  2325.     case CPC_SET_CONTROL_RTS_QUERY:        /* client wants us to send the RTS state */
  2326.         modemsignals = get_modem_signals(modemfd);
  2327.         if (modemsignals & TIOCM_RTS) {
  2328.             rtscode = CPC_SET_CONTROL_RTS_ON;
  2329.             p = "On";
  2330.         } else {
  2331.             rtscode = CPC_SET_CONTROL_RTS_OFF;
  2332.             p = "Off";
  2333.         }
  2334.         syslog(LOG_INFO,"telnet CPC client requests RTS state; sending RTS %s",p);
  2335.         break;
  2336.     case CPC_SET_CONTROL_RTS_ON:
  2337.         syslog(LOG_INFO,"telnet CPC client requests that RTS be raised");
  2338.         rtscode = (unsigned char) value;
  2339.         ret = set_modem_signals(modemfd,(unsigned int) value);
  2340.         break;
  2341.     case CPC_SET_CONTROL_RTS_OFF:
  2342.         syslog(LOG_INFO,"telnet CPC client requests that RTS be lowered");
  2343.         rtscode = (unsigned char) value;
  2344.         ret = set_modem_signals(modemfd,(unsigned int) value);
  2345.         break;
  2346.     default:
  2347.         break;
  2348.     }
  2349.  
  2350.     /* send current RTS value in reply */
  2351.     ret += send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SET_CONTROL_S2C,(unsigned char *) &rtscode,1);
  2352.     return(ret);
  2353. }
  2354.  
  2355. unsigned char get_flowcontrol(int modemfd)
  2356. {
  2357.     struct termios term;                /* current termios */
  2358.     int ret;
  2359. #ifdef USE_TERMIOX
  2360.     struct termiox termx;
  2361. #endif
  2362.  
  2363.     ret = tcgetattr(modemfd,&term);
  2364.     if (ret != 0) {
  2365.         syslog_perror("tcgetattr() error");
  2366.     }
  2367. #ifdef USE_TERMIOX
  2368.     if (ret == 0) {
  2369.         ret = ioctl(modemfd,TCGETX,&termx);
  2370.         if (ret != 0) {
  2371.             syslog_perror("ioctl(modemfd,TCGETX,) error");
  2372.         }
  2373.     }
  2374. #endif
  2375.     if (ret == 0) {
  2376.         if (term.c_iflag & IXON) {
  2377.             return(CPC_SET_CONTROL_FLOW_XONXOFF);
  2378. #ifdef USE_TERMIOX
  2379.         } else if (termx.x_hflag & (RTSXOFF|CTSXON)) {
  2380. #else
  2381.         } else if (term.c_cflag & CRTSCTS) {
  2382. #endif
  2383.             return(CPC_SET_CONTROL_FLOW_HARDWARE);
  2384.         } else {
  2385.             return(CPC_SET_CONTROL_FLOW_NONE);
  2386.         }
  2387.     } else {
  2388.         return(CPC_SET_CONTROL_FLOW_NONE);
  2389.     }
  2390. }
  2391.  
  2392. int set_flowcontrol(int modemfd,unsigned long value)
  2393. {
  2394.     struct termios term;                /* current termios */
  2395. #ifdef USE_TERMIOX
  2396.     struct termiox termx;
  2397. #endif
  2398.     int ret;
  2399.  
  2400.     ret = tcgetattr(modemfd,&term);
  2401.     if (ret != 0) {
  2402.         syslog_perror("tcgetattr() error");
  2403.     }
  2404. #ifdef USE_TERMIOX
  2405.     if (ret == 0) {
  2406.         ret = ioctl(modemfd,TCGETX,&termx);
  2407.         if (ret != 0) {
  2408.             syslog_perror("ioctl(modemfd,TCGETX,) error");
  2409.         }
  2410.     }
  2411. #endif
  2412.     if (ret == 0) {
  2413.         switch (value) {
  2414.         case CPC_SET_CONTROL_FLOW_NONE:
  2415.         case CPC_SET_CONTROL_INFLOW_NONE:
  2416. #ifdef USE_TERMIOX
  2417.             termx.x_hflag &= ~(RTSXOFF|CTSXON);
  2418. #else
  2419.             term.c_cflag &= ~CRTSCTS;
  2420. #endif
  2421.             term.c_iflag &= ~(IXON|IXOFF|IXANY);
  2422.             break;
  2423.         case CPC_SET_CONTROL_FLOW_XONXOFF:
  2424.         case CPC_SET_CONTROL_INFLOW_XONXOFF:
  2425. #ifdef USE_TERMIOX
  2426.             termx.x_hflag &= ~(RTSXOFF|CTSXON);
  2427. #else
  2428.             term.c_cflag &= ~CRTSCTS;
  2429. #endif
  2430.             term.c_iflag |= (IXON|IXOFF|IXANY);
  2431.             break;
  2432.         case CPC_SET_CONTROL_FLOW_HARDWARE:
  2433.         case CPC_SET_CONTROL_INFLOW_HARDWARE:
  2434.         case CPC_SET_CONTROL_FLOW_DCD:
  2435.         case CPC_SET_CONTROL_INFLOW_DTR:
  2436.         case CPC_SET_CONTROL_FLOW_DSR:
  2437. #ifdef USE_TERMIOX
  2438.             termx.x_hflag |= (RTSXOFF|CTSXON);
  2439. #else
  2440.             term.c_cflag |= CRTSCTS;
  2441. #endif
  2442.             term.c_iflag &= ~(IXON|IXOFF|IXANY);
  2443.             break;
  2444.         default:
  2445.             break;
  2446.         }
  2447.         ret = tcsetattr(modemfd,TCSAFLUSH,&term);
  2448.         if (ret != 0) {
  2449.             syslog_perror("tcsetattr() error");
  2450.         }
  2451. #ifdef USE_TERMIOX
  2452.         if (ret == 0) {
  2453.             ret = ioctl(modemfd,TCSETXW,&termx);
  2454.             if (ret != 0) {
  2455.                 syslog_perror("ioctl(modemfd,TCSETXW,) error");
  2456.             }
  2457.         }
  2458. #endif
  2459.     }
  2460.     return(ret);
  2461. }
  2462.  
  2463. unsigned int get_modem_signals(int modemfd)
  2464. {
  2465.     unsigned int modemsignals;
  2466.     int ret;
  2467. #ifdef TIOCMGET_CLOCAL_HACK
  2468.     struct sigaction act;                /* new SIGHUP handler */
  2469.     struct sigaction oact;                /* saved SIGHUP handler */
  2470.     struct termios term;                /* current termios */
  2471.     int restore_term;                    /* restore termios? */
  2472.     int restore_sig;                    /* restore signal handler? */
  2473.  
  2474.     restore_term = 0;                    /* initially we don't need to restore the termios settings */
  2475.     restore_sig = 0;                    /*   or the SIGHUP signal handler */
  2476.  
  2477.     /* get the current termios settings.  if the CLOCAL bit is set ... */
  2478.     ret = tcgetattr(modemfd,&term);
  2479.     if ((ret == 0) && (term.c_cflag & CLOCAL)) {
  2480.         /* we're going to ignore SIGHUP and save the prior SIGHUP signal handler */
  2481.         act.sa_handler = SIG_IGN;
  2482.         sigemptyset(&act.sa_mask);
  2483.         act.sa_flags = 0;
  2484.         if (sigaction(SIGHUP,&act,&oact) == 0) {
  2485.             restore_sig = 1;
  2486.             /* now clear the CLOCAL bit to establish modem control */
  2487.             term.c_cflag &= ~CLOCAL;
  2488.             if (tcsetattr(modemfd,TCSANOW,&term) == 0) {
  2489.                 restore_term = 1;
  2490.             } else {
  2491.                 syslog(LOG_ERR,"tcsetattr() failed while trying to clear CLOCAL");
  2492.             }
  2493.         } else {
  2494.             syslog_perror("sigaction(SIGHUP,&act,&oact) error");
  2495.         }
  2496.     } else {
  2497.         if (ret != 0) {
  2498.             syslog(LOG_ERR,"tcgetattr() failed while trying to get modem signals");
  2499.         }
  2500.     }
  2501. #endif    /* TIOCMGET_CLOCAL_HACK */
  2502.  
  2503.     ret = ioctl(modemfd,TIOCMGET,&modemsignals);
  2504.     if (ret != 0) {
  2505.         syslog_perror("ioctl(modemfd,TIOCMGET,...) error");
  2506.         modemsignals = 0;
  2507.     }
  2508.  
  2509. #ifdef TIOCMGET_CLOCAL_HACK
  2510.     if (restore_term) {
  2511.         term.c_cflag |= CLOCAL;            /* set CLOCAL again */
  2512.         if (tcsetattr(modemfd,TCSANOW,&term) != 0) {
  2513.             syslog(LOG_ERR,"tcsetattr() failed while trying to set CLOCAL");
  2514.         }
  2515.     }
  2516.     if (restore_sig) {
  2517.         if (sigaction(SIGHUP,&oact,NULL) != 0) {
  2518.             syslog_perror("sigaction(SIGHUP,&oact,NULL) error");
  2519.         }
  2520.     }
  2521. #endif    /* TIOCMGET_CLOCAL_HACK */
  2522.  
  2523.     return(modemsignals);
  2524. }
  2525.  
  2526. int set_modem_signals(int modemfd,unsigned long value)
  2527. {
  2528.     unsigned int modemsignals;
  2529.     int ret;
  2530.  
  2531.     ret = ioctl(modemfd,TIOCMGET,&modemsignals);
  2532.     if (ret == 0) {
  2533.         switch (value) {
  2534.         case CPC_SET_CONTROL_DTR_ON:
  2535.             modemsignals |= TIOCM_DTR;
  2536.             break;
  2537.         case CPC_SET_CONTROL_DTR_OFF:
  2538.             modemsignals &= ~TIOCM_DTR;
  2539.             break;
  2540.         case CPC_SET_CONTROL_RTS_ON:
  2541.             modemsignals |= TIOCM_RTS;
  2542.             break;
  2543.         case CPC_SET_CONTROL_RTS_OFF:
  2544.             modemsignals &= ~TIOCM_RTS;
  2545.             break;
  2546.         }
  2547.         ret = ioctl(modemfd,TIOCMSET,&modemsignals);
  2548.         if (ret != 0) {
  2549.             syslog_perror("ioctl(modemfd,TIOCMSET,...) error");
  2550.         }
  2551.     } else {
  2552.         syslog_perror("ioctl(modemfd,TIOCMGET,...) error");
  2553.     }
  2554.     return(ret);
  2555. }
  2556.  
  2557. void advise_client_of_state_changes(int sockfd,int modemfd,BUFFER *cpc2skt)
  2558. {
  2559.     if (telnet_client_option_is_enabled(TELOPT_COM_PORT_OPTION)) {
  2560.         /* advise client of any modemstate changes */
  2561.         respond_telnet_cpc_modemstate_subopt(sockfd,modemfd,cpc2skt,CPC_NOTIFY_MODEMSTATE_S2C,0);
  2562.  
  2563.         /* advise client of any pending linestate changes */
  2564.         respond_telnet_cpc_linestate_subopt(sockfd,modemfd,cpc2skt,CPC_NOTIFY_LINESTATE_S2C,0);
  2565.     }
  2566. }
  2567.  
  2568. unsigned char check_modemstate(int modemfd,int query)
  2569. {
  2570.     extern struct config_t conf;                /* built from config file */
  2571.     extern unsigned char modemstate;
  2572.     static time_t last_ms_check = 0L;
  2573.     time_t now;
  2574.     unsigned int modemsignals;
  2575.     unsigned char new_modemstate;
  2576.  
  2577.     /* only update modemstate every ms_pollinterval seconds unless client sent a query */
  2578.     now = time((time_t *) NULL);
  2579.     if (! query) {
  2580.         if (now < (last_ms_check + conf.ms_pollinterval))
  2581.             return(modemstate);
  2582.     }
  2583.  
  2584.     modemsignals = get_modem_signals(modemfd);
  2585.     new_modemstate = 0;
  2586.     if (modemsignals & TIOCM_CAR) new_modemstate |= CPC_MODEMSTATE_CD;
  2587.     if (modemsignals & TIOCM_RNG) new_modemstate |= CPC_MODEMSTATE_RI;
  2588.     if (modemsignals & TIOCM_DSR) new_modemstate |= CPC_MODEMSTATE_DSR;
  2589.     if (modemsignals & TIOCM_CTS) new_modemstate |= CPC_MODEMSTATE_CTS;
  2590. #ifdef CD_FOLLOWS_DSR
  2591.     if (modemsignals & TIOCM_DSR) {
  2592.         new_modemstate |= CPC_MODEMSTATE_CD;
  2593.     } else {
  2594.         new_modemstate &= ~CPC_MODEMSTATE_CD;
  2595.     }
  2596. #endif
  2597.     /* calc the delta */
  2598.     if ((new_modemstate & CPC_MODEMSTATE_CD) != (modemstate & CPC_MODEMSTATE_CD)) 
  2599.         new_modemstate |= CPC_MODEMSTATE_DELTA_CD;
  2600.     if ((new_modemstate & CPC_MODEMSTATE_RI) != (modemstate & CPC_MODEMSTATE_RI)) 
  2601.         new_modemstate |= CPC_MODEMSTATE_TRLEDGE_RI;
  2602.     if ((new_modemstate & CPC_MODEMSTATE_DSR) != (modemstate & CPC_MODEMSTATE_DSR)) 
  2603.         new_modemstate |= CPC_MODEMSTATE_DELTA_DSR;
  2604.     if ((new_modemstate & CPC_MODEMSTATE_CTS) != (modemstate & CPC_MODEMSTATE_CTS)) 
  2605.         new_modemstate |= CPC_MODEMSTATE_DELTA_CTS;
  2606.  
  2607.     last_ms_check = now;
  2608.     return(new_modemstate);
  2609. }
  2610.  
  2611. int check_carrier_state(int modemfd)
  2612. {
  2613.     extern int carrier_state;
  2614.     int new_carrier_state;
  2615.     unsigned int modemsignals;
  2616.  
  2617.     modemsignals = get_modem_signals(modemfd);
  2618.  
  2619.     /* adjust carrier state */
  2620.     new_carrier_state = carrier_state;
  2621.     switch (carrier_state) {
  2622.     case NO_CARRIER:
  2623.         if (modemsignals & TIOCM_CAR) {
  2624.             syslog(LOG_INFO,"got CARRIER");
  2625.             new_carrier_state = GOT_CARRIER;
  2626.         }
  2627.         break;
  2628.     case GOT_CARRIER:
  2629.         if (! (modemsignals & TIOCM_CAR)) {
  2630.             syslog(LOG_INFO,"lost CARRIER");
  2631.             new_carrier_state = LOST_CARRIER;
  2632.         }
  2633.         break;
  2634.     case LOST_CARRIER:
  2635.         if (modemsignals & TIOCM_CAR) {
  2636.             syslog(LOG_INFO,"got CARRIER again");
  2637.             new_carrier_state = GOT_CARRIER;
  2638.         } else {
  2639.             syslog(LOG_INFO,"no CARRIER");
  2640.             new_carrier_state = NO_CARRIER;
  2641.         }
  2642.         break;
  2643.     }
  2644.     carrier_state = new_carrier_state;
  2645.     return(carrier_state);
  2646. }
  2647.  
  2648. int respond_telnet_cpc_linestate_subopt(int sockfd,int modemfd,BUFFER *cpc2skt,unsigned char suboptcode,unsigned long value)
  2649. {
  2650.     extern struct config_t conf;                /* built from config file */
  2651.     extern unsigned char linestate_mask;
  2652.     extern unsigned char linestate;
  2653.     static time_t last_ls_check = 0L;
  2654.     time_t now;
  2655.     int query;
  2656.     int ret;
  2657.     unsigned char statechg;
  2658.  
  2659.     query = 0;
  2660.     ret = 0;
  2661.     switch (suboptcode) {
  2662.     case CPC_SET_LINESTATE_MASK_C2S:
  2663.     case CPC_SET_LINESTATE_MASK_S2C:
  2664.         linestate_mask = (unsigned char) value;
  2665.         syslog(LOG_INFO,"telnet CPC client sets a new linestate mask: 0x%02x",linestate_mask);
  2666.  
  2667.         /* send the linestate mask back in reply */
  2668.         ret = send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SET_LINESTATE_MASK_S2C,(unsigned char *) &linestate_mask,1);
  2669.         break;
  2670.     case CPC_NOTIFY_LINESTATE_C2S:
  2671.         query = 1;                        /* client sent a query */
  2672.         /* FALL THROUGH */
  2673.     case CPC_NOTIFY_LINESTATE_S2C:
  2674.         /* only update linestate every ls_pollinterval seconds (unless client sent a query) */
  2675.         now = time((time_t *) NULL);
  2676.         if ((now >= (last_ls_check + conf.ls_pollinterval)) || (query)) {
  2677.             statechg = linestate & linestate_mask;
  2678.             if (statechg) {
  2679.                 syslog(LOG_INFO,"telnet CPC linestate: %s",telnet_cpc_linestate2str(statechg));
  2680.                 ret = send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_NOTIFY_LINESTATE_S2C,(unsigned char *) &statechg,1);
  2681.             }
  2682.             linestate = 0;
  2683.             last_ls_check = now;
  2684.         }
  2685.         break;
  2686.     }
  2687.     return(ret);
  2688. }
  2689.  
  2690. int respond_telnet_cpc_modemstate_subopt(int sockfd,int modemfd,BUFFER *cpc2skt,unsigned char suboptcode,unsigned long value)
  2691. {
  2692.     extern unsigned char modemstate_mask;
  2693.     extern unsigned char modemstate;
  2694.     unsigned char new_modemstate;
  2695.     int query;
  2696.     int ret;
  2697.  
  2698.     query = 0;
  2699.     ret = 0;
  2700.     switch (suboptcode) {
  2701.     case CPC_SET_MODEMSTATE_MASK_C2S:
  2702.     case CPC_SET_MODEMSTATE_MASK_S2C:
  2703.         modemstate_mask = (unsigned char) value;
  2704.         syslog(LOG_INFO,"telnet CPC client sets a new modemstate mask: 0x%02x",modemstate_mask);
  2705.  
  2706.         /* send the modemstate mask back in reply */
  2707.         ret = send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_SET_MODEMSTATE_MASK_S2C,(unsigned char *) &modemstate_mask,1);
  2708.         break;
  2709.     case CPC_NOTIFY_MODEMSTATE_C2S:
  2710.         query = 1;                        /* client sent a query */
  2711.         /* FALL THROUGH */
  2712.     case CPC_NOTIFY_MODEMSTATE_S2C:
  2713.         new_modemstate = check_modemstate(modemfd,query);
  2714.         if ((new_modemstate != modemstate) || (query)) {    /* modemstate has changed or client sent a query */
  2715.             if (new_modemstate & modemstate_mask) {            /* is the client interested? */
  2716.                 syslog(LOG_INFO,"telnet CPC modemstate: %s",telnet_cpc_modemstate2str(new_modemstate));
  2717.                 ret = send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_NOTIFY_MODEMSTATE_S2C,(unsigned char *) &new_modemstate,1);
  2718.             }
  2719.             modemstate = new_modemstate;        /* update saved modemstate */
  2720.         }
  2721.         break;
  2722.     }
  2723.     return(ret);
  2724. }
  2725.  
  2726. int respond_telnet_cpc_purge_data_subopt(int sockfd,int modemfd,BUFFER *skt2mdm,BUFFER *cpc2skt,unsigned char suboptcode,unsigned long value)
  2727. {
  2728.     extern struct config_t conf;                /* built from config file */
  2729.     int ret;
  2730.     char *p;
  2731.     unsigned char purgecode;
  2732.  
  2733.     switch (value) {
  2734.     case CPC_PURGEDATA_RECVBUFF:
  2735.         ret = tcflush(modemfd,TCIFLUSH);
  2736.         bfclear(cpc2skt);
  2737.         p = "Receive buffer";
  2738.         break;
  2739.     case CPC_PURGEDATA_XMITBUFF:
  2740.         ret = tcflush(modemfd,TCOFLUSH);
  2741.         bfclear(skt2mdm);
  2742.         p = "Transmit buffer";
  2743.         break;
  2744.     case CPC_PURGEDATA_BOTH:
  2745.         ret = tcflush(modemfd,TCIOFLUSH);
  2746.         bfclear(skt2mdm);
  2747.         bfclear(cpc2skt);
  2748.         p = "both the Transmit and Receive buffers";
  2749.         break;
  2750.     default:
  2751.         ret = 0;
  2752.         p = "Unknown buffer";
  2753.         break;
  2754.     }
  2755.     syslog(LOG_INFO,"telnet CPC client requests data purge of %s",p);
  2756.  
  2757.     /* send the purge data command back in reply */
  2758.     if (conf.reply_purge_data) {
  2759.         purgecode = (unsigned char) value;
  2760.         ret += send_telnet_cpc_suboption(sockfd,cpc2skt,CPC_PURGE_DATA_S2C,(unsigned char *) &purgecode,1);
  2761.     }
  2762.     return(ret);
  2763. }
  2764.  
  2765. char *telnet_optcode2str(unsigned char optcode)
  2766. {
  2767.     char *str;
  2768.  
  2769.     switch (optcode) {
  2770.     case IAC:
  2771.         str = "IAC";
  2772.         break;
  2773.     case DONT:
  2774.         str = "DONT";
  2775.         break;
  2776.     case DO:
  2777.         str = "DO";
  2778.         break;
  2779.     case WONT:
  2780.         str = "WONT";
  2781.         break;
  2782.     case WILL:
  2783.         str = "WILL";
  2784.         break;
  2785.     case SB:
  2786.         str = "SB";
  2787.         break;
  2788.     case GA:
  2789.         str = "GA";
  2790.         break;
  2791.     case EL:
  2792.         str = "EL";
  2793.         break;
  2794.     case EC:
  2795.         str = "EC";
  2796.         break;
  2797.     case AYT:
  2798.         str = "AYT";
  2799.         break;
  2800.     case AO:
  2801.         str = "AO";
  2802.         break;
  2803.     case IP:
  2804.         str = "IP";
  2805.         break;
  2806.     case BREAK:
  2807.         str = "BREAK";
  2808.         break;
  2809.     case DM:
  2810.         str = "DM";
  2811.         break;
  2812.     case NOP:
  2813.         str = "NOP";
  2814.         break;
  2815.     case SE:
  2816.         str = "SE";
  2817.         break;
  2818.     default:
  2819.         str = "UNKNOWN";
  2820.         break;
  2821.     }
  2822.     return(str);
  2823. }
  2824.  
  2825. char *telnet_option2str(unsigned char option)
  2826. {
  2827.     static char unknown[16];
  2828.     char *str;
  2829.     char *p;
  2830.  
  2831.     switch (option) {
  2832.     case TELOPT_BINARY:
  2833.         str = "Binary";
  2834.         break;
  2835.     case TELOPT_ECHO:
  2836.         str = "Echo";
  2837.         break;
  2838.     case TELOPT_SGA:
  2839.         str = "Suppress Go Ahead";
  2840.         break;
  2841.     case TELOPT_LOGOUT:
  2842.         str = "Logout";
  2843.         break;
  2844.     case TELOPT_TTYPE:
  2845.         str = "Terminal Type";
  2846.         break;
  2847.     case TELOPT_NAWS:
  2848.         str = "Window Size";
  2849.         break;
  2850.     case TELOPT_XDISPLOC:
  2851.         str = "X Display Location";
  2852.         break;
  2853.     case TELOPT_AUTHENTICATION:
  2854.         str = "Authentication";
  2855.         break;
  2856.     case TELOPT_ENCRYPT:
  2857.         str = "Encryption";
  2858.         break;
  2859.     case TELOPT_NEW_ENVIRON:
  2860.         str = "New Environment";
  2861.         break;
  2862.     case TELOPT_COM_PORT_OPTION:
  2863.         str = "Com Port Control";
  2864.         break;
  2865.     case TELOPT_KERMIT:
  2866.         str = "Kermit";
  2867.         break;
  2868.     default:
  2869.         strcpy(unknown,"Option xxx");
  2870.         p = strstr(unknown,"xxx");
  2871.         snprintf(p,3,"%d",option);
  2872.         str = unknown;
  2873.         break;
  2874.     }
  2875.     return(str);
  2876. }
  2877.  
  2878. char *telnet_cpc_subopt2str(unsigned char suboptcode)
  2879. {
  2880.     static char unknown[16];
  2881.     char *str;
  2882.     char *p;
  2883.  
  2884.     switch (suboptcode) {
  2885.     case CPC_SIGNATURE_C2S:
  2886.         str = "Signature C2S";
  2887.         break;
  2888.     case CPC_SIGNATURE_S2C:
  2889.         str = "Signature S2C";
  2890.         break;
  2891.     case CPC_SET_BAUDRATE_C2S:
  2892.         str = "Set Baudrate C2S";
  2893.         break;
  2894.     case CPC_SET_BAUDRATE_S2C:
  2895.         str = "Set Baudrate S2C";
  2896.         break;
  2897.     case CPC_SET_DATASIZE_C2S:
  2898.         str = "Set Datasize C2S";
  2899.         break;
  2900.     case CPC_SET_DATASIZE_S2C:
  2901.         str = "Set Datasize S2C";
  2902.         break;
  2903.     case CPC_SET_PARITY_C2S:
  2904.         str = "Set Parity C2S";
  2905.         break;
  2906.     case CPC_SET_PARITY_S2C:
  2907.         str = "Set Parity S2C";
  2908.         break;
  2909.     case CPC_SET_STOPSIZE_C2S:
  2910.         str = "Set Stopsize C2S";
  2911.         break;
  2912.     case CPC_SET_STOPSIZE_S2C:
  2913.         str = "Set Stopsize S2C";
  2914.         break;
  2915.     case CPC_SET_CONTROL_C2S:
  2916.         str = "Set Control C2S";
  2917.         break;
  2918.     case CPC_SET_CONTROL_S2C:
  2919.         str = "Set Control S2C";
  2920.         break;
  2921.     case CPC_NOTIFY_LINESTATE_C2S:
  2922.         str = "Notify Linestate C2S";
  2923.         break;
  2924.     case CPC_NOTIFY_LINESTATE_S2C:
  2925.         str = "Notify Linestate S2C";
  2926.         break;
  2927.     case CPC_NOTIFY_MODEMSTATE_C2S:
  2928.         str = "Notify Modemstate C2S";
  2929.         break;
  2930.     case CPC_NOTIFY_MODEMSTATE_S2C:
  2931.         str = "Notify Modemstate S2C";
  2932.         break;
  2933.     case CPC_FLOWCONTROL_SUSPEND_C2S:
  2934.         str = "Suspend Flowcontrol C2S";
  2935.         break;
  2936.     case CPC_FLOWCONTROL_SUSPEND_S2C:
  2937.         str = "Suspend Flowcontrol S2C";
  2938.         break;
  2939.     case CPC_FLOWCONTROL_RESUME_C2S:
  2940.         str = "Resume Flowcontrol C2S";
  2941.         break;
  2942.     case CPC_FLOWCONTROL_RESUME_S2C:
  2943.         str = "Resume Flowcontrol S2C";
  2944.         break;
  2945.     case CPC_SET_LINESTATE_MASK_C2S:
  2946.         str = "Set Linestate Mask C2S";
  2947.         break;
  2948.     case CPC_SET_LINESTATE_MASK_S2C:
  2949.         str = "Set Linestate Mask S2C";
  2950.         break;
  2951.     case CPC_SET_MODEMSTATE_MASK_C2S:
  2952.         str = "Set Modemstate Mask C2S";
  2953.         break;
  2954.     case CPC_SET_MODEMSTATE_MASK_S2C:
  2955.         str = "Set Modemstate Mask S2C";
  2956.         break;
  2957.     case CPC_PURGE_DATA_C2S:
  2958.         str = "Purge Data C2S";
  2959.         break;
  2960.     case CPC_PURGE_DATA_S2C:
  2961.         str = "Purge Data S2C";
  2962.         break;
  2963.     default:
  2964.         strcpy(unknown,"Code xxx");
  2965.         p = strstr(unknown,"xxx");
  2966.         snprintf(p,3,"%d",suboptcode);
  2967.         str = unknown;
  2968.         break;
  2969.     }
  2970.     return(str);
  2971. }
  2972.  
  2973. char *telnet_cpc_parity2str(unsigned char optcode)
  2974. {
  2975.     static char unknown[16];
  2976.     char *str;
  2977.     char *p;
  2978.  
  2979.     switch (optcode) {
  2980.     case CPC_PARITY_NONE:
  2981.         str = "None";
  2982.         break;
  2983.     case CPC_PARITY_ODD:
  2984.         str = "Odd";
  2985.         break;
  2986.     case CPC_PARITY_EVEN:
  2987.         str = "Even";
  2988.         break;
  2989.     case CPC_PARITY_MARK:
  2990.         str = "Mark";
  2991.         break;
  2992.     case CPC_PARITY_SPACE:
  2993.         str = "Space";
  2994.         break;
  2995.     default:
  2996.         strcpy(unknown,"Code xxx");
  2997.         p = strstr(unknown,"xxx");
  2998.         snprintf(p,3,"%d",optcode);
  2999.         str = unknown;
  3000.         break;
  3001.     }
  3002.     return(str);
  3003. }
  3004.  
  3005. char *telnet_cpc_stopsize2str(unsigned char optcode)
  3006. {
  3007.     static char unknown[16];
  3008.     char *str;
  3009.     char *p;
  3010.  
  3011.     switch (optcode) {
  3012.     case CPC_STOPSIZE_1BIT:
  3013.         str = "1";
  3014.         break;
  3015.     case CPC_STOPSIZE_2BITS:
  3016.         str = "2";
  3017.         break;
  3018.     case CPC_STOPSIZE_15BITS:
  3019.         str = "1.5";
  3020.         break;
  3021.     default:
  3022.         strcpy(unknown,"Code xxx");
  3023.         p = strstr(unknown,"xxx");
  3024.         snprintf(p,3,"%d",optcode);
  3025.         str = unknown;
  3026.         break;
  3027.     }
  3028.     return(str);
  3029. }
  3030.  
  3031. char *telnet_cpc_modemstate2str(unsigned char modemstate)
  3032. {
  3033.     static char str[128];
  3034.  
  3035.     str[0] = '\0';
  3036.  
  3037.     if (modemstate & CPC_MODEMSTATE_CD) 
  3038.         strcat(str,"CD ");
  3039.     if (modemstate & CPC_MODEMSTATE_RI) 
  3040.         strcat(str,"RI ");
  3041.     if (modemstate & CPC_MODEMSTATE_DSR) 
  3042.         strcat(str,"DSR ");
  3043.     if (modemstate & CPC_MODEMSTATE_CTS) 
  3044.         strcat(str,"CTS ");
  3045.     if (modemstate & CPC_MODEMSTATE_DELTA_CD) 
  3046.         strcat(str,"CD~ ");
  3047.     if (modemstate & CPC_MODEMSTATE_TRLEDGE_RI) 
  3048.         strcat(str,"RI~ ");
  3049.     if (modemstate & CPC_MODEMSTATE_DELTA_DSR) 
  3050.         strcat(str,"DSR~ ");
  3051.     if (modemstate & CPC_MODEMSTATE_DELTA_CTS) 
  3052.         strcat(str,"CTS~ ");
  3053.  
  3054.     return(str);
  3055. }
  3056.  
  3057. char *telnet_cpc_linestate2str(unsigned char linestate)
  3058. {
  3059.     static char str[128];
  3060.     char *p;
  3061.     int len;
  3062.  
  3063.     str[0] = '\0';
  3064.  
  3065.     if (linestate & CPC_LINESTATE_TIMEOUT_ERROR)
  3066.         strcat(str,"Timeout Error, ");
  3067.     if (linestate & CPC_LINESTATE_TSR_EMPTY)
  3068.         strcat(str,"TSR Empty, ");
  3069.     if (linestate & CPC_LINESTATE_THR_EMPTY)
  3070.         strcat(str,"THR Empty, ");
  3071.     if (linestate & CPC_LINESTATE_BREAK_DETECT)
  3072.         strcat(str,"Break Detect, ");
  3073.     if (linestate & CPC_LINESTATE_FRAMING_ERROR)
  3074.         strcat(str,"Framing Error, ");
  3075.     if (linestate & CPC_LINESTATE_PARITY_ERROR)
  3076.         strcat(str,"Parity Error, ");
  3077.     if (linestate & CPC_LINESTATE_OVERRUN_ERROR)
  3078.         strcat(str,"Overrun Error, ");
  3079.     if (linestate & CPC_LINESTATE_DATA_READY)
  3080.         strcat(str,"Data Ready, ");
  3081.  
  3082.     len = strlen(str);
  3083.     p = str + len - 1;                /* point to char before the null */
  3084.                                     /* removing trailing ", " */
  3085.     while ((*p == ' ') || (*p == ',')) {
  3086.         p--;
  3087.     }
  3088.     p++;
  3089.     *p = '\0';
  3090.     return(str);
  3091. }
  3092.  
  3093. char *telnet_cpc_flowcontrol2str(unsigned char optcode)
  3094. {
  3095.     static char unknown[16];
  3096.     char *str;
  3097.     char *p;
  3098.  
  3099.     switch (optcode) {
  3100.     case CPC_SET_CONTROL_FLOW_NONE:
  3101.     case CPC_SET_CONTROL_INFLOW_NONE:
  3102.         str = "None";
  3103.         break;
  3104.     case CPC_SET_CONTROL_FLOW_XONXOFF:
  3105.     case CPC_SET_CONTROL_INFLOW_XONXOFF:
  3106.         str = "Xon/Xoff";
  3107.         break;
  3108.     case CPC_SET_CONTROL_FLOW_HARDWARE:
  3109.     case CPC_SET_CONTROL_INFLOW_HARDWARE:
  3110.         str = "RTS/CTS";
  3111.         break;
  3112.     default:
  3113.         strcpy(unknown,"Code xxx");
  3114.         p = strstr(unknown,"xxx");
  3115.         snprintf(p,3,"%d",optcode);
  3116.         str = unknown;
  3117.         break;
  3118.     }
  3119.     return(str);
  3120. }
  3121.  
  3122. /*
  3123.     this function is called when the IAC char is detected in the data 
  3124.     that was read from the modem.  for every IAC char in the buffer, 
  3125.     we "escape" it by inserting an additional IAC char.
  3126. */
  3127. void escape_iac_chars(BUFFER *mdm2skt)
  3128. {
  3129.     unsigned char *ptr;
  3130.     unsigned char *iac;
  3131.     unsigned char *se;
  3132.     unsigned char optcode;
  3133.     unsigned char option;
  3134.     int done;
  3135.     int size;
  3136.  
  3137.     /* sanity checks */
  3138.     if (mdm2skt == NULL) return;
  3139.  
  3140.     /*
  3141.         peek at the buffer.  ptr points to its "active" portion 
  3142.         and size holds the number of chars buffered.
  3143.     */
  3144.     ptr = bfpeek(mdm2skt,&size);
  3145.  
  3146.     /* get pointer to IAC char */
  3147.     iac = mystrchr(ptr,IAC,size);
  3148.     if (iac == NULL) return;
  3149.  
  3150.     debug(DBG_VINF,"escaping IAC chars received from modem");
  3151.  
  3152.     /* adjust size */
  3153.     size -= (iac - ptr);
  3154.  
  3155.     done = 0;
  3156.     while (! done) {
  3157.         /* insert another IAC char */
  3158.         bfinsch(mdm2skt,iac,IAC);
  3159.         size++;                        /* account for added char */
  3160.  
  3161.         /* point past the sequence of 2 IAC chars */
  3162.         iac += 2;
  3163.         size -= 2;
  3164.  
  3165.         /* find next IAC char */
  3166.         if (size > 0) {
  3167.             ptr = iac;
  3168.             iac = mystrchr(ptr,IAC,size);
  3169.             if (iac == NULL) {
  3170.                 ++done;
  3171.             } else {
  3172.                 /* adjust size */
  3173.                 size -= (iac - ptr);
  3174.             }
  3175.         } else {
  3176.             ++done;
  3177.         }
  3178.     }
  3179. }
  3180.  
  3181. /*
  3182.     log some info about a telnet CPC suboption: its name, its value, 
  3183.     and a hex dump of the command string.
  3184. */
  3185. void telnet_cpc_log_subopt(char *prefix,unsigned char suboptcode,unsigned long value,unsigned char *command,int cmdlen)
  3186. {
  3187.     static char fmt[1024];
  3188.  
  3189.     fmt[0] = '\0';
  3190.     if ((prefix != NULL) && (*prefix != '\0'))
  3191.         snprintf(fmt,sizeof(fmt),"%s ",prefix);
  3192.     strcat(fmt,"telnet CPC suboption \"");
  3193.     strcat(fmt,telnet_cpc_subopt2str(suboptcode));
  3194.     strcat(fmt,"\", value ");
  3195.     if (value < 256) {
  3196.         strcat(fmt,"0x%02x");            /* one byte value, show as hex */
  3197.     } else {
  3198.         strcat(fmt,"%lu");                /* multi-byte value, show as long decimal */
  3199.     }
  3200.     if (cmdlen > 1)
  3201.         strcat(fmt,", hex dump:");
  3202.  
  3203.     syslog(LOG_INFO,fmt,value);            /* send to syslog */
  3204.  
  3205.     if (cmdlen > 1)
  3206.         memdump((char *) command,cmdlen,NULL);    /* will go to syslog() too */
  3207. }
  3208.  
  3209.