mirror of
https://github.com/freebsd/freebsd-src.git
synced 2024-12-04 23:19:00 +00:00
496 lines
12 KiB
C
496 lines
12 KiB
C
/*
|
|
* Copyright (c) 1989, 1993
|
|
* The 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. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. 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.
|
|
*/
|
|
|
|
#ifndef lint
|
|
#if 0
|
|
static char sccsid[] = "@(#)slc.c 8.1 (Berkeley) 6/4/93";
|
|
#endif
|
|
static const char rcsid[] =
|
|
"$FreeBSD$";
|
|
#endif /* not lint */
|
|
|
|
#include "telnetd.h"
|
|
|
|
#ifdef LINEMODE
|
|
/*
|
|
* local variables
|
|
*/
|
|
static unsigned char *def_slcbuf = (unsigned char *)0;
|
|
static int def_slclen = 0;
|
|
static int slcchange; /* change to slc is requested */
|
|
static unsigned char *slcptr; /* pointer into slc buffer */
|
|
static unsigned char slcbuf[NSLC*6]; /* buffer for slc negotiation */
|
|
|
|
/*
|
|
* send_slc
|
|
*
|
|
* Write out the current special characters to the client.
|
|
*/
|
|
void
|
|
send_slc()
|
|
{
|
|
register int i;
|
|
|
|
/*
|
|
* Send out list of triplets of special characters
|
|
* to client. We only send info on the characters
|
|
* that are currently supported.
|
|
*/
|
|
for (i = 1; i <= NSLC; i++) {
|
|
if ((slctab[i].defset.flag & SLC_LEVELBITS) == SLC_NOSUPPORT)
|
|
continue;
|
|
add_slc((unsigned char)i, slctab[i].current.flag,
|
|
slctab[i].current.val);
|
|
}
|
|
|
|
} /* end of send_slc */
|
|
|
|
/*
|
|
* default_slc
|
|
*
|
|
* Set pty special characters to all the defaults.
|
|
*/
|
|
void
|
|
default_slc()
|
|
{
|
|
register int i;
|
|
|
|
for (i = 1; i <= NSLC; i++) {
|
|
slctab[i].current.val = slctab[i].defset.val;
|
|
if (slctab[i].current.val == (cc_t)(_POSIX_VDISABLE))
|
|
slctab[i].current.flag = SLC_NOSUPPORT;
|
|
else
|
|
slctab[i].current.flag = slctab[i].defset.flag;
|
|
if (slctab[i].sptr) {
|
|
*(slctab[i].sptr) = slctab[i].defset.val;
|
|
}
|
|
}
|
|
slcchange = 1;
|
|
|
|
} /* end of default_slc */
|
|
#endif /* LINEMODE */
|
|
|
|
/*
|
|
* get_slc_defaults
|
|
*
|
|
* Initialize the slc mapping table.
|
|
*/
|
|
void
|
|
get_slc_defaults()
|
|
{
|
|
register int i;
|
|
|
|
init_termbuf();
|
|
|
|
for (i = 1; i <= NSLC; i++) {
|
|
slctab[i].defset.flag =
|
|
spcset(i, &slctab[i].defset.val, &slctab[i].sptr);
|
|
slctab[i].current.flag = SLC_NOSUPPORT;
|
|
slctab[i].current.val = 0;
|
|
}
|
|
|
|
} /* end of get_slc_defaults */
|
|
|
|
#ifdef LINEMODE
|
|
/*
|
|
* add_slc
|
|
*
|
|
* Add an slc triplet to the slc buffer.
|
|
*/
|
|
void
|
|
add_slc(func, flag, val)
|
|
register char func, flag;
|
|
register cc_t val;
|
|
{
|
|
|
|
if ((*slcptr++ = (unsigned char)func) == 0xff)
|
|
*slcptr++ = 0xff;
|
|
|
|
if ((*slcptr++ = (unsigned char)flag) == 0xff)
|
|
*slcptr++ = 0xff;
|
|
|
|
if ((*slcptr++ = (unsigned char)val) == 0xff)
|
|
*slcptr++ = 0xff;
|
|
|
|
} /* end of add_slc */
|
|
|
|
/*
|
|
* start_slc
|
|
*
|
|
* Get ready to process incoming slc's and respond to them.
|
|
*
|
|
* The parameter getit is non-zero if it is necessary to grab a copy
|
|
* of the terminal control structures.
|
|
*/
|
|
void
|
|
start_slc(getit)
|
|
register int getit;
|
|
{
|
|
|
|
slcchange = 0;
|
|
if (getit)
|
|
init_termbuf();
|
|
(void) sprintf((char *)slcbuf, "%c%c%c%c",
|
|
IAC, SB, TELOPT_LINEMODE, LM_SLC);
|
|
slcptr = slcbuf + 4;
|
|
|
|
} /* end of start_slc */
|
|
|
|
/*
|
|
* end_slc
|
|
*
|
|
* Finish up the slc negotiation. If something to send, then send it.
|
|
*/
|
|
int
|
|
end_slc(bufp)
|
|
register unsigned char **bufp;
|
|
{
|
|
register int len;
|
|
void netflush();
|
|
|
|
/*
|
|
* If a change has occured, store the new terminal control
|
|
* structures back to the terminal driver.
|
|
*/
|
|
if (slcchange) {
|
|
set_termbuf();
|
|
}
|
|
|
|
/*
|
|
* If the pty state has not yet been fully processed and there is a
|
|
* deferred slc request from the client, then do not send any
|
|
* sort of slc negotiation now. We will respond to the client's
|
|
* request very soon.
|
|
*/
|
|
if (def_slcbuf && (terminit() == 0)) {
|
|
return(0);
|
|
}
|
|
|
|
if (slcptr > (slcbuf + 4)) {
|
|
if (bufp) {
|
|
*bufp = &slcbuf[4];
|
|
return(slcptr - slcbuf - 4);
|
|
} else {
|
|
(void) sprintf((char *)slcptr, "%c%c", IAC, SE);
|
|
slcptr += 2;
|
|
len = slcptr - slcbuf;
|
|
writenet(slcbuf, len);
|
|
netflush(); /* force it out immediately */
|
|
DIAG(TD_OPTIONS, printsub('>', slcbuf+2, len-2););
|
|
}
|
|
}
|
|
return (0);
|
|
|
|
} /* end of end_slc */
|
|
|
|
/*
|
|
* process_slc
|
|
*
|
|
* Figure out what to do about the client's slc
|
|
*/
|
|
void
|
|
process_slc(func, flag, val)
|
|
register unsigned char func, flag;
|
|
register cc_t val;
|
|
{
|
|
register int hislevel, mylevel, ack;
|
|
|
|
/*
|
|
* Ensure that we know something about this function
|
|
*/
|
|
if (func > NSLC) {
|
|
add_slc(func, SLC_NOSUPPORT, 0);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Process the special case requests of 0 SLC_DEFAULT 0
|
|
* and 0 SLC_VARIABLE 0. Be a little forgiving here, don't
|
|
* worry about whether the value is actually 0 or not.
|
|
*/
|
|
if (func == 0) {
|
|
if ((flag = flag & SLC_LEVELBITS) == SLC_DEFAULT) {
|
|
default_slc();
|
|
send_slc();
|
|
} else if (flag == SLC_VARIABLE) {
|
|
send_slc();
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Appears to be a function that we know something about. So
|
|
* get on with it and see what we know.
|
|
*/
|
|
|
|
hislevel = flag & SLC_LEVELBITS;
|
|
mylevel = slctab[func].current.flag & SLC_LEVELBITS;
|
|
ack = flag & SLC_ACK;
|
|
/*
|
|
* ignore the command if:
|
|
* the function value and level are the same as what we already have;
|
|
* or the level is the same and the ack bit is set
|
|
*/
|
|
if (hislevel == mylevel && (val == slctab[func].current.val || ack)) {
|
|
return;
|
|
} else if (ack) {
|
|
/*
|
|
* If we get here, we got an ack, but the levels don't match.
|
|
* This shouldn't happen. If it does, it is probably because
|
|
* we have sent two requests to set a variable without getting
|
|
* a response between them, and this is the first response.
|
|
* So, ignore it, and wait for the next response.
|
|
*/
|
|
return;
|
|
} else {
|
|
change_slc(func, flag, val);
|
|
}
|
|
|
|
} /* end of process_slc */
|
|
|
|
/*
|
|
* change_slc
|
|
*
|
|
* Process a request to change one of our special characters.
|
|
* Compare client's request with what we are capable of supporting.
|
|
*/
|
|
void
|
|
change_slc(func, flag, val)
|
|
register char func, flag;
|
|
register cc_t val;
|
|
{
|
|
register int hislevel, mylevel;
|
|
|
|
hislevel = flag & SLC_LEVELBITS;
|
|
mylevel = slctab[(int)func].defset.flag & SLC_LEVELBITS;
|
|
/*
|
|
* If client is setting a function to NOSUPPORT
|
|
* or DEFAULT, then we can easily and directly
|
|
* accomodate the request.
|
|
*/
|
|
if (hislevel == SLC_NOSUPPORT) {
|
|
slctab[(int)func].current.flag = flag;
|
|
slctab[(int)func].current.val = (cc_t)_POSIX_VDISABLE;
|
|
flag |= SLC_ACK;
|
|
add_slc(func, flag, val);
|
|
return;
|
|
}
|
|
if (hislevel == SLC_DEFAULT) {
|
|
/*
|
|
* Special case here. If client tells us to use
|
|
* the default on a function we don't support, then
|
|
* return NOSUPPORT instead of what we may have as a
|
|
* default level of DEFAULT.
|
|
*/
|
|
if (mylevel == SLC_DEFAULT) {
|
|
slctab[(int)func].current.flag = SLC_NOSUPPORT;
|
|
} else {
|
|
slctab[(int)func].current.flag = slctab[(int)func].defset.flag;
|
|
}
|
|
slctab[(int)func].current.val = slctab[(int)func].defset.val;
|
|
add_slc(func, slctab[(int)func].current.flag,
|
|
slctab[(int)func].current.val);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Client wants us to change to a new value or he
|
|
* is telling us that he can't change to our value.
|
|
* Some of the slc's we support and can change,
|
|
* some we do support but can't change,
|
|
* and others we don't support at all.
|
|
* If we can change it then we have a pointer to
|
|
* the place to put the new value, so change it,
|
|
* otherwise, continue the negotiation.
|
|
*/
|
|
if (slctab[(int)func].sptr) {
|
|
/*
|
|
* We can change this one.
|
|
*/
|
|
slctab[(int)func].current.val = val;
|
|
*(slctab[(int)func].sptr) = val;
|
|
slctab[(int)func].current.flag = flag;
|
|
flag |= SLC_ACK;
|
|
slcchange = 1;
|
|
add_slc(func, flag, val);
|
|
} else {
|
|
/*
|
|
* It is not possible for us to support this
|
|
* request as he asks.
|
|
*
|
|
* If our level is DEFAULT, then just ack whatever was
|
|
* sent.
|
|
*
|
|
* If he can't change and we can't change,
|
|
* then degenerate to NOSUPPORT.
|
|
*
|
|
* Otherwise we send our level back to him, (CANTCHANGE
|
|
* or NOSUPPORT) and if CANTCHANGE, send
|
|
* our value as well.
|
|
*/
|
|
if (mylevel == SLC_DEFAULT) {
|
|
slctab[(int)func].current.flag = flag;
|
|
slctab[(int)func].current.val = val;
|
|
flag |= SLC_ACK;
|
|
} else if (hislevel == SLC_CANTCHANGE &&
|
|
mylevel == SLC_CANTCHANGE) {
|
|
flag &= ~SLC_LEVELBITS;
|
|
flag |= SLC_NOSUPPORT;
|
|
slctab[(int)func].current.flag = flag;
|
|
} else {
|
|
flag &= ~SLC_LEVELBITS;
|
|
flag |= mylevel;
|
|
slctab[(int)func].current.flag = flag;
|
|
if (mylevel == SLC_CANTCHANGE) {
|
|
slctab[(int)func].current.val =
|
|
slctab[(int)func].defset.val;
|
|
val = slctab[(int)func].current.val;
|
|
}
|
|
}
|
|
add_slc(func, flag, val);
|
|
}
|
|
|
|
} /* end of change_slc */
|
|
|
|
#if defined(USE_TERMIO) && (VEOF == VMIN)
|
|
cc_t oldeofc = '\004';
|
|
#endif
|
|
|
|
/*
|
|
* check_slc
|
|
*
|
|
* Check the special characters in use and notify the client if any have
|
|
* changed. Only those characters that are capable of being changed are
|
|
* likely to have changed. If a local change occurs, kick the support level
|
|
* and flags up to the defaults.
|
|
*/
|
|
void
|
|
check_slc()
|
|
{
|
|
register int i;
|
|
|
|
for (i = 1; i <= NSLC; i++) {
|
|
#if defined(USE_TERMIO) && (VEOF == VMIN)
|
|
/*
|
|
* In a perfect world this would be a neat little
|
|
* function. But in this world, we should not notify
|
|
* client of changes to the VEOF char when
|
|
* ICANON is off, because it is not representing
|
|
* a special character.
|
|
*/
|
|
if (i == SLC_EOF) {
|
|
if (!tty_isediting())
|
|
continue;
|
|
else if (slctab[i].sptr)
|
|
oldeofc = *(slctab[i].sptr);
|
|
}
|
|
#endif /* defined(USE_TERMIO) && defined(SYSV_TERMIO) */
|
|
if (slctab[i].sptr &&
|
|
(*(slctab[i].sptr) != slctab[i].current.val)) {
|
|
slctab[i].current.val = *(slctab[i].sptr);
|
|
if (*(slctab[i].sptr) == (cc_t)_POSIX_VDISABLE)
|
|
slctab[i].current.flag = SLC_NOSUPPORT;
|
|
else
|
|
slctab[i].current.flag = slctab[i].defset.flag;
|
|
add_slc((unsigned char)i, slctab[i].current.flag,
|
|
slctab[i].current.val);
|
|
}
|
|
}
|
|
} /* check_slc */
|
|
|
|
/*
|
|
* do_opt_slc
|
|
*
|
|
* Process an slc option buffer. Defer processing of incoming slc's
|
|
* until after the terminal state has been processed. Save the first slc
|
|
* request that comes along, but discard all others.
|
|
*
|
|
* ptr points to the beginning of the buffer, len is the length.
|
|
*/
|
|
void
|
|
do_opt_slc(ptr, len)
|
|
register unsigned char *ptr;
|
|
register int len;
|
|
{
|
|
register unsigned char func, flag;
|
|
cc_t val;
|
|
register unsigned char *end = ptr + len;
|
|
|
|
if (terminit()) { /* go ahead */
|
|
while (ptr < end) {
|
|
func = *ptr++;
|
|
if (ptr >= end) break;
|
|
flag = *ptr++;
|
|
if (ptr >= end) break;
|
|
val = (cc_t)*ptr++;
|
|
|
|
process_slc(func, flag, val);
|
|
|
|
}
|
|
} else {
|
|
/*
|
|
* save this slc buffer if it is the first, otherwise dump
|
|
* it.
|
|
*/
|
|
if (def_slcbuf == (unsigned char *)0) {
|
|
def_slclen = len;
|
|
def_slcbuf = (unsigned char *)malloc((unsigned)len);
|
|
if (def_slcbuf == (unsigned char *)0)
|
|
return; /* too bad */
|
|
bcopy(ptr, def_slcbuf, len);
|
|
}
|
|
}
|
|
|
|
} /* end of do_opt_slc */
|
|
|
|
/*
|
|
* deferslc
|
|
*
|
|
* Do slc stuff that was deferred.
|
|
*/
|
|
void
|
|
deferslc()
|
|
{
|
|
if (def_slcbuf) {
|
|
start_slc(1);
|
|
do_opt_slc(def_slcbuf, def_slclen);
|
|
(void) end_slc(0);
|
|
free(def_slcbuf);
|
|
def_slcbuf = (unsigned char *)0;
|
|
def_slclen = 0;
|
|
}
|
|
|
|
} /* end of deferslc */
|
|
|
|
#endif /* LINEMODE */
|