/*
 * Copyright (c) 1996-2007, OpenFWTK Development Group
 * All rights reserved. See LICENSE.
 *
 * Copyright (c) 1989 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

char copyright[] =
  "@(#) Copyright (c) 1989 Regents of the University of California.\n"
  "All rights reserved.\n";

/*
 * From: @(#)telnetd.c	5.48 (Berkeley) 3/1/91
 */
char telnetd_rcsid[] = 
  "$Id: telnetd.c,v 1.15 2007/09/10 02:49:12 arkenoi Exp $";

#define NO_URGENT 

#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include "telnetd.h"
#include "firewall2.h"

#if defined(SOLARIS) || defined(HPUX) 
#include <sys/stropts.h>                                                        
#endif  

#ifdef HPUX
#include <sys/ptyio.h>
#endif

#ifndef TIOCPKT                                                               
#include <sys/ioccom.h>                                                       
#include <sys/filio.h>                                                        
#include <sys/tty.h>                                                          
#include <sys/ptyvar.h>                                                       
#endif  

#ifndef _PATH_TTY                                                             
#define _PATH_TTY "/dev/tty"                                                 
#endif

extern char* ip2name(struct in_addr);

/* In Linux, this is an enum */
#if defined(__linux__) || defined(IPPROTO_IP)
#define HAS_IPPROTO_IP
#endif

extern  size_t 	strlcat(char*,const char*,size_t);
extern  size_t 	strlcpy(char*,const char*,size_t);

void doit(void);
static int terminaltypeok(const char *s);


#if defined(linux) || defined (SOLARIS)
extern void     initsetproctitle(int,char**);
extern void setproctitle(const char *fmt, ...);
#endif

/*
 * I/O data buffers,
 * pointers, and counters.
 */
char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
char	ptyibuf2[BUFSIZ];

int	hostinfo = 1;			/* do we print login banner? */

int debug = 0;

extern void ShowUsage(void);

char authsrv_host[BUFSIZ + 1];
unsigned short int authsrv_port;
struct sockaddr_in from;
/*
 * getterminaltype
 *
 *	Ask the other end to send along its terminal type and speed.
 * Output is the variable terminaltype filled in.
 */

static void _gettermname(void);

static
int
getterminaltype(char *name)
{
    int retval = -1;
    (void)name;

    settimer(baseline);

#if	defined(ENCRYPT)
    send_will(TELOPT_ENCRYPT, 1);
#endif
    send_do(TELOPT_TTYPE, 1);
    send_do(TELOPT_TSPEED, 1);
    while (
#if	defined(ENCRYPT)
	   his_do_dont_is_changing(TELOPT_ENCRYPT) ||
#endif
	   his_will_wont_is_changing(TELOPT_TTYPE) ||
	   his_will_wont_is_changing(TELOPT_TSPEED) ||
	   his_will_wont_is_changing(TELOPT_XDISPLOC) ||
	   his_will_wont_is_changing(TELOPT_ENVIRON)) {
	ttloop();
    }
#if	defined(ENCRYPT)
    /*
     * Wait for the negotiation of what type of encryption we can
     * send with.  If autoencrypt is not set, this will just return.
     */
    if (his_state_is_will(TELOPT_ENCRYPT)) {
	encrypt_wait();
    }
#endif
    if (his_state_is_will(TELOPT_TSPEED)) {
	netoprintf("%c%c%c%c%c%c", 
		   IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE);
    }
    if (his_state_is_will(TELOPT_XDISPLOC)) {
	netoprintf("%c%c%c%c%c%c", 
		   IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE);
    }
    if (his_state_is_will(TELOPT_ENVIRON)) {
	netoprintf("%c%c%c%c%c%c", 
		   IAC, SB, TELOPT_ENVIRON, TELQUAL_SEND, IAC, SE);
    }
    if (his_state_is_will(TELOPT_TTYPE)) {
       netoprintf("%c%c%c%c%c%c", 
		  IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE);
    }
    if (his_state_is_will(TELOPT_TSPEED)) {
	while (sequenceIs(tspeedsubopt, baseline))
	    ttloop();
    }
    if (his_state_is_will(TELOPT_XDISPLOC)) {
	while (sequenceIs(xdisplocsubopt, baseline))
	    ttloop();
    }
    if (his_state_is_will(TELOPT_ENVIRON)) {
	while (sequenceIs(environsubopt, baseline))
	    ttloop();
    }
    if (his_state_is_will(TELOPT_TTYPE)) {
	char first[256], last[256];

	while (sequenceIs(ttypesubopt, baseline))
	    ttloop();

	/*
	 * If the other side has already disabled the option, then
	 * we have to just go with what we (might) have already gotten.
	 */
	if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) {
	    /*
	     * Due to state.c, terminaltype points to a static char[41].
	     * Therefore, this assert cannot fail, and therefore, strings
	     * arising from "terminaltype" can be safely strcpy'd into
	     * first[] or last[].
	     */
	    /* 
	     * Despite that, gcc keeps complaining. Well, let's keep it
	     * happy - ArkanoiD
	     */
	    assert(strlen(terminaltype) < sizeof(first));

	    strlcpy(first, terminaltype, sizeof(first));

	    for(;;) {
		/*
		 * Save the unknown name, and request the next name.
		 */
		strlcpy(last, terminaltype, sizeof(last));

		_gettermname();
		assert(strlen(terminaltype) < sizeof(first));

		if (terminaltypeok(terminaltype))
		    break;

		if (!strcmp(last, terminaltype) ||
		    his_state_is_wont(TELOPT_TTYPE)) {
		    /*
		     * We've hit the end.  If this is the same as
		     * the first name, just go with it.
		     */
		    if (!strcmp(first, terminaltype))
			break;
		    /*
		     * Get the terminal name one more time, so that
		     * RFC1091 compliant telnets will cycle back to
		     * the start of the list.
		     */
		     _gettermname();
		    assert(strlen(terminaltype) < sizeof(first));

		    if (strcmp(first, terminaltype)) {
			/*
			 * first[] came from terminaltype, so it must fit
			 * back in.
			 */
			strlcpy(terminaltype, first, sizeof(first));
		    }
		    break;
		}
	    }
	}
    }
    return(retval);
}  /* end of getterminaltype */

static
void
_gettermname(void)
{
    /*
     * If the client turned off the option,
     * we can't send another request, so we
     * just return.
     */
    if (his_state_is_wont(TELOPT_TTYPE))
	return;

    settimer(baseline);
    netoprintf("%c%c%c%c%c%c", IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE);
    while (sequenceIs(ttypesubopt, baseline))
	ttloop();
}

static int
terminaltypeok(const char *s)
{
    /* char buf[2048]; */

    if (terminaltype == NULL)
	return(1);

    /*
     * Fix from Chris Evans: if it has a / in it, termcap will
     * treat it as a filename. Oops.
     */
    if (strchr(s, '/')) {
	return 0;
    }

    /*
     * If it's absurdly long, accept it without asking termcap.
     *
     * This means that it won't get seen again until after login,
     * at which point exploiting buffer problems in termcap doesn't
     * gain one anything.
     *
     * It's possible this limit ought to be raised to 128, but nothing
     * in my termcap is more than 64, 64 is _plenty_ for most, and while
     * buffers aren't likely to be smaller than 64, they might be 80 and
     * thus less than 128.
     */
    if (strlen(s) > 63) {
       return 0;
    }

    /*
     * tgetent() will return 1 if the type is known, and
     * 0 if it is not known.  If it returns -1, it couldn't
     * open the database.  But if we can't open the database,
     * it won't help to say we failed, because we won't be
     * able to verify anything else.  So, we treat -1 like 1.
     */

    /*
     * Don't do this - tgetent is not really trustworthy. Assume
     * the terminal type is one we know; terminal types are pretty
     * standard now. And if it isn't, it's unlikely we're going to
     * know anything else the remote telnet might send as an alias
     * for it.
     *
     * if (tgetent(buf, s) == 0)
     *    return(0);
     */
    return(1);
}

#ifndef	MAXHOSTNAMELEN
#define	MAXHOSTNAMELEN 64
#endif	/* MAXHOSTNAMELEN */

char host_name[MAXHOSTNAMELEN];

extern void telnet(int, int);

/*
 * Get a pty, scan input lines.
 */
void
doit()
{
	const char *host;
	int level;
	char user_name[256];

	/*
	 * Find an available pty to use.
	 */
	pty = getpty();
	if (pty < 0)
		fatal(net, "All network ports in use");

	/* get name of connected client */
	host = proxy_stats.rladdr;

	/* Get local host name */
	{
		struct hostent *h;
		gethostname(host_name, sizeof(host_name));
		h = gethostbyname(host_name);
		if (h) {
		    strncpy(host_name, h->h_name, sizeof(host_name));
		    host_name[sizeof(host_name)-1] = 0;
		}
	}

	init_env();
	/*
	 * get terminal type.
	 */
	*user_name = 0;
	level = getterminaltype(user_name);
	setenv("TERM", terminaltype ? terminaltype : "ansi", 1);

	/* TODO list stuff provided by Laszlo Vecsey <master@internexus.net> */

	/*
	 * Set REMOTEHOST environment variable
	 */

#if defined(linux) || defined (SOLARIS)
	setproctitle("%s", host);
#endif

	setenv("REMOTEHOST", host, 0);


	/*
	 * Start up the login process on the slave side of the terminal
	 */
	startslave(proxy_stats.riaddr, level);

	telnet(net, pty);  /* begin server processing */

	/*NOTREACHED*/
}  /* end of doit */

/*
 * Main loop.  Select from pty and network, and
 * hand data to telnet receiver finite state machine.
 */
void telnet(int f, int p)
{
    int on = 1;
    const char *IM;

    /*
     * Initialize the slc mapping table.
     */
    get_slc_defaults();

    /*
     * Do some tests where it is desireable to wait for a response.
     * Rather than doing them slowly, one at a time, do them all
     * at once.
     */
    if (my_state_is_wont(TELOPT_SGA))
	send_will(TELOPT_SGA, 1);
    /*
     * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
     * because 4.2 clients are unable to deal with TCP urgent data.
     *
     * To find out, we send out a "DO ECHO".  If the remote system
     * answers "WILL ECHO" it is probably a 4.2 client, and we note
     * that fact ("WILL ECHO" ==> that the client will echo what
     * WE, the server, sends it; it does NOT mean that the client will
     * echo the terminal input).
     */
    send_do(TELOPT_ECHO, 1);
    /*
     * Send along a couple of other options that we wish to negotiate.
     */
    send_do(TELOPT_NAWS, 1);
    send_will(TELOPT_STATUS, 1);
    flowmode = 1;  /* default flow control state */
    send_do(TELOPT_LFLOW, 1);
    
    /*
     * Spin, waiting for a response from the DO ECHO.  However,
     * some REALLY DUMB telnets out there might not respond
     * to the DO ECHO.  So, we spin looking for NAWS, (most dumb
     * telnets so far seem to respond with WONT for a DO that
     * they don't understand...) because by the time we get the
     * response, it will already have processed the DO ECHO.
     * Kludge upon kludge.
     */
    while (his_will_wont_is_changing(TELOPT_NAWS)) {
	ttloop();
    }
    
    /*
     * But...
     * The client might have sent a WILL NAWS as part of its
     * startup code; if so, we'll be here before we get the
     * response to the DO ECHO.  We'll make the assumption
     * that any implementation that understands about NAWS
     * is a modern enough implementation that it will respond
     * to our DO ECHO request; hence we'll do another spin
     * waiting for the ECHO option to settle down, which is
     * what we wanted to do in the first place...
     */
    if (his_want_state_is_will(TELOPT_ECHO) &&
	his_state_is_will(TELOPT_NAWS)) {
	while (his_will_wont_is_changing(TELOPT_ECHO))
	    ttloop();
    }
    /*
     * On the off chance that the telnet client is broken and does not
     * respond to the DO ECHO we sent, (after all, we did send the
     * DO NAWS negotiation after the DO ECHO, and we won't get here
     * until a response to the DO NAWS comes back) simulate the
     * receipt of a will echo.  This will also send a WONT ECHO
     * to the client, since we assume that the client failed to
     * respond because it believes that it is already in DO ECHO
     * mode, which we do not want.
     */
    if (his_want_state_is_will(TELOPT_ECHO)) {
	DIAG(TD_OPTIONS, netoprintf("td: simulating recv\r\n"););
	willoption(TELOPT_ECHO);
    }
    
    /*
     * Finally, to clean things up, we turn on our echo.  This
     * will break stupid 4.2 telnets out of local terminal echo.
     */
    
    if (my_state_is_wont(TELOPT_ECHO))
	send_will(TELOPT_ECHO, 1);
    
    /*
     * Turn on packet mode
     */
#ifdef SOLARIS                                                                
/*
 * This commented out code below would turn on packet mode on Solaris 
 * but it works quite differently on Solaris comparing to BSD systems,
 * so we don't turn this mode here and don't process control data below
 * that would be generated in the packet mode. As far as i understand 
 * this control data is used to process ioctl requests, but auth agent works
 * fine without it.
 */

/* 
    ioctl(p, I_PUSH, "pckt");                                               
    ioctl(p, I_SRDOPT, RMSGN|RPROTDAT);                                     
*/    
#else       
    ioctl(p, TIOCPKT, (char *)&on);
#endif
  
    /*
     * Call telrcv() once to pick up anything received during
     * terminal type negotiation, 4.2/4.3 determination, and
     * linemode negotiation.
     */
    telrcv();
    
    ioctl(f, FIONBIO, (char *)&on);
    ioctl(p, FIONBIO, (char *)&on);

#if defined(SO_OOBINLINE)
    setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
#endif	/* defined(SO_OOBINLINE) */
    
#ifdef	SIGTSTP
    signal(SIGTSTP, SIG_IGN);
#endif
#ifdef	SIGTTOU
    /*
     * Ignoring SIGTTOU keeps the kernel from blocking us
     * in ttioct() in /sys/tty.c.
     */
    signal(SIGTTOU, SIG_IGN);
#endif
    
    signal(SIGCHLD, cleanup);
    
#ifdef TIOCNOTTY
    {
	register int t;
	t = open(_PATH_TTY, O_RDWR);
	if (t >= 0) {
	    (void) ioctl(t, TIOCNOTTY, (char *)0);
	    (void) close(t);
	}
    }
#endif
    
    /*
     * Show banner that getty never gave.
     *
     * We put the banner in the pty input buffer.  This way, it
     * gets carriage return null processing, etc., just like all
     * other pty --> client data.
     */
    
    if (getenv("USER"))
	hostinfo = 0;
    
    IM = DEFAULT_IM;

    if (hostinfo && *IM)
	putf(IM, ptyibuf2);
    
    if (pcc) strncat(ptyibuf2, ptyip, pcc+1);
    ptyip = ptyibuf2;
    pcc = (int) strlen(ptyip);

    DIAG(TD_REPORT, netoprintf("td: Entering processing loop\r\n"););
    
    for (;;) {
	fd_set ibits, obits, xbits;
	int c, hifd;
	
	if (ncc < 0 && pcc < 0)
	    break;
	
	FD_ZERO(&ibits);
	FD_ZERO(&obits);
	FD_ZERO(&xbits);
	hifd=0;
	/*
	 * Never look for input if there's still
	 * stuff in the corresponding output buffer
	 */
	if (nfrontp - nbackp || pcc > 0) {
	    FD_SET(f, &obits);
	    if (f >= hifd) hifd = f+1;
	} 
	else {
	    FD_SET(p, &ibits);
	    if (p >= hifd) hifd = p+1;
	}
	if (pfrontp - pbackp || ncc > 0) {
	    FD_SET(p, &obits);
	    if (p >= hifd) hifd = p+1;
	} 
	else {
	    FD_SET(f, &ibits);
	    if (f >= hifd) hifd = f+1;
	}
	if (!SYNCHing) {
	    FD_SET(f, &xbits);
	    if (f >= hifd) hifd = f+1;
	}
	if ((c = select(hifd, &ibits, &obits, &xbits,
			(struct timeval *)0)) < 1) {
	    if (c == -1) {
		if (errno == EINTR) {
		    continue;
		}
	    }
	    sleep(5);
	    continue;
	}
	
	/*
	 * Any urgent data?
	 */
	if (FD_ISSET(net, &xbits)) {
	    SYNCHing = 1;
	}
	
	/*
	 * Something to read from the network...
	 */
	if (FD_ISSET(net, &ibits)) {
#if !defined(SO_OOBINLINE)
	    /*
	     * In 4.2 (and 4.3 beta) systems, the
	     * OOB indication and data handling in the kernel
	     * is such that if two separate TCP Urgent requests
	     * come in, one byte of TCP data will be overlaid.
	     * This is fatal for Telnet, but we try to live
	     * with it.
	     *
	     * In addition, in 4.2 (and...), a special protocol
	     * is needed to pick up the TCP Urgent data in
	     * the correct sequence.
	     *
	     * What we do is:  if we think we are in urgent
	     * mode, we look to see if we are "at the mark".
	     * If we are, we do an OOB receive.  If we run
	     * this twice, we will do the OOB receive twice,
	     * but the second will fail, since the second
	     * time we were "at the mark", but there wasn't
	     * any data there (the kernel doesn't reset
	     * "at the mark" until we do a normal read).
	     * Once we've read the OOB data, we go ahead
	     * and do normal reads.
	     *
	     * There is also another problem, which is that
	     * since the OOB byte we read doesn't put us
	     * out of OOB state, and since that byte is most
	     * likely the TELNET DM (data mark), we would
	     * stay in the TELNET SYNCH (SYNCHing) state.
	     * So, clocks to the rescue.  If we've "just"
	     * received a DM, then we test for the
	     * presence of OOB data when the receive OOB
	     * fails (and AFTER we did the normal mode read
	     * to clear "at the mark").
	     */
	    if (SYNCHing) {
		int atmark;
		
		ioctl(net, SIOCATMARK, (char *)&atmark);
		if (atmark) {
		    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
		    if ((ncc == -1) && (errno == EINVAL)) {
			ncc = read(net, netibuf, sizeof (netibuf));
			if (sequenceIs(didnetreceive, gotDM)) {
			    SYNCHing = stilloob(net);
			}
		    }
		} 
		else {
		    ncc = read(net, netibuf, sizeof (netibuf));
		}
	    } 
	    else {
		ncc = read(net, netibuf, sizeof (netibuf));
	    }
	    settimer(didnetreceive);
#else	/* !defined(SO_OOBINLINE)) */
	    ncc = (int) read(net, netibuf, sizeof (netibuf));
#endif	/* !defined(SO_OOBINLINE)) */
	    if (ncc < 0 && errno == EWOULDBLOCK)
		ncc = 0;
	    else {
		if (ncc <= 0) {
		    break;
		}
		netip = netibuf;
	    }
	    DIAG((TD_REPORT | TD_NETDATA),
		 netoprintf("td: netread %d chars\r\n", ncc););
	    DIAG(TD_NETDATA, printdata("nd", netip, ncc));
	}
	
	/*
	 * Something to read from the pty...
	 */
	if (FD_ISSET(p, &ibits)) {
	    pcc = (int) read(p, ptyibuf, BUFSIZ);
	    /*
	     * On some systems, if we try to read something
	     * off the master side before the slave side is
	     * opened, we get EIO.
	     */
	    if (pcc < 0 && (errno == EWOULDBLOCK || errno == EIO)) {
		pcc = 0;
	    } 
	    else {
		if (pcc <= 0)
		    break;
#ifndef SOLARIS
/*
 * The code below process control data would be generated in packet mode.
 * But for Solaris for turn this mode off because works differently comparing
 * to the BSD systems, so just skip the code for Solaris
 */
  
		if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
		    netclear();	/* clear buffer back */
#ifndef	NO_URGENT
		    /*
		     * There are client telnets on some
		     * operating systems get screwed up
		     * royally if we send them urgent
		     * mode data.
		     */
		    netoprintf("%c%c", IAC, DM);
		    neturg = nfrontp-1; /* off by one XXX */
#endif
		}
		if (his_state_is_will(TELOPT_LFLOW) &&
		    (ptyibuf[0] &
		     (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
			netoprintf("%c%c%c%c%c%c",
				   IAC, SB, TELOPT_LFLOW,
				   ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
				   IAC, SE);
		}
		pcc--;
		ptyip = ptyibuf+1;
#else
		ptyip = ptyibuf;
#endif   /* Solaris */
	    }
	}
	
	while (pcc > 0) {
	    if ((&netobuf[BUFSIZ] - nfrontp) < 2)
		break;
	    c = *ptyip++ & 0377, pcc--;
	    if (c == IAC)
		*nfrontp++ = c;
	    *nfrontp++ = c;
	    if ((c == '\r'  ) && (my_state_is_wont(TELOPT_BINARY))) {
		if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
		    *nfrontp++ = *ptyip++ & 0377;
		    pcc--;
		} 
		else *nfrontp++ = '\0';
	    }
	}

	if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
	    netflush();
	if (ncc > 0)
	    telrcv();
	if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
	    ptyflush();
    }
    cleanup(0);
}  /* end of telnet */
	
#ifndef	TCSIG
# ifdef	TIOCSIG
#  define TCSIG TIOCSIG
# endif
#endif

/*
 * Send interrupt to process on other side of pty.
 * If it is in raw mode, just write NULL;
 * otherwise, write intr char.
 */
void interrupt(void) {
    ptyflush();	/* half-hearted */
    
#ifdef	TCSIG
    (void) ioctl(pty, TCSIG, (char *)SIGINT);
#else	/* TCSIG */
    init_termbuf();
    *pfrontp++ = slctab[SLC_IP].sptr ?
	 (unsigned char)*slctab[SLC_IP].sptr : '\177';
#endif	/* TCSIG */
}

/*
 * Send quit to process on other side of pty.
 * If it is in raw mode, just write NULL;
 * otherwise, write quit char.
 */
void sendbrk(void) {
    ptyflush();	/* half-hearted */
#ifdef	TCSIG
    (void) ioctl(pty, TCSIG, (char *)SIGQUIT);
#else	/* TCSIG */
    init_termbuf();
    *pfrontp++ = slctab[SLC_ABORT].sptr ?
	 (unsigned char)*slctab[SLC_ABORT].sptr : '\034';
#endif	/* TCSIG */
}

void sendsusp(void) {
#ifdef	SIGTSTP
    ptyflush();	/* half-hearted */
# ifdef	TCSIG
    (void) ioctl(pty, TCSIG, (char *)SIGTSTP);
# else	/* TCSIG */
    *pfrontp++ = slctab[SLC_SUSP].sptr ?
	(unsigned char)*slctab[SLC_SUSP].sptr : '\032';
# endif	/* TCSIG */
#endif	/* SIGTSTP */
}

/*
 * When we get an AYT, if ^T is enabled, use that.  Otherwise,
 * just send back "[Yes]".
 */
void recv_ayt(void) {
#if	defined(SIGINFO) && defined(TCSIG)
    if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) {
	(void) ioctl(pty, TCSIG, (char *)SIGINFO);
	return;
    }
#endif
    netoprintf("\r\n[%s : yes]\r\n", host_name);
}

void doeof(void) {
    init_termbuf();

    *pfrontp++ = slctab[SLC_EOF].sptr ?
	     (unsigned char)*slctab[SLC_EOF].sptr : '\004';
}
