129088Smarkm/*
229088Smarkm * Copyright (c) 1989, 1993
329088Smarkm *	The Regents of the University of California.  All rights reserved.
429088Smarkm *
529088Smarkm * Redistribution and use in source and binary forms, with or without
629088Smarkm * modification, are permitted provided that the following conditions
729088Smarkm * are met:
829088Smarkm * 1. Redistributions of source code must retain the above copyright
929088Smarkm *    notice, this list of conditions and the following disclaimer.
1029088Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1129088Smarkm *    notice, this list of conditions and the following disclaimer in the
1229088Smarkm *    documentation and/or other materials provided with the distribution.
1329088Smarkm * 3. All advertising materials mentioning features or use of this software
1429088Smarkm *    must display the following acknowledgement:
1529088Smarkm *	This product includes software developed by the University of
1629088Smarkm *	California, Berkeley and its contributors.
1729088Smarkm * 4. Neither the name of the University nor the names of its contributors
1829088Smarkm *    may be used to endorse or promote products derived from this software
1929088Smarkm *    without specific prior written permission.
2029088Smarkm *
2129088Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2229088Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2329088Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2429088Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2529088Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2629088Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2729088Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2829088Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2929088Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3029088Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3129088Smarkm * SUCH DAMAGE.
3229088Smarkm */
3329088Smarkm
34114630Sobrien#if 0
3529088Smarkm#ifndef lint
3629181Smarkmstatic const char sccsid[] = "@(#)slc.c	8.2 (Berkeley) 5/30/95";
3731622Scharnier#endif
38114630Sobrien#endif
39114630Sobrien#include <sys/cdefs.h>
40114630Sobrien__FBSDID("$FreeBSD$");
4129088Smarkm
4229088Smarkm#include "telnetd.h"
4329088Smarkm
4429088Smarkm#ifdef	LINEMODE
4529088Smarkm/*
4631622Scharnier * local variables
4729088Smarkm */
4829088Smarkmstatic unsigned char	*def_slcbuf = (unsigned char *)0;
4929088Smarkmstatic int		def_slclen = 0;
5029088Smarkmstatic int		slcchange;	/* change to slc is requested */
5129088Smarkmstatic unsigned char	*slcptr;	/* pointer into slc buffer */
5229088Smarkmstatic unsigned char	slcbuf[NSLC*6];	/* buffer for slc negotiation */
5329088Smarkm
5429088Smarkm/*
5529088Smarkm * send_slc
5629088Smarkm *
5729088Smarkm * Write out the current special characters to the client.
5829088Smarkm */
5987139Smarkmvoid
6087139Smarkmsend_slc(void)
6129088Smarkm{
6287139Smarkm	int i;
6329088Smarkm
6429088Smarkm	/*
6529088Smarkm	 * Send out list of triplets of special characters
6629088Smarkm	 * to client.  We only send info on the characters
6729088Smarkm	 * that are currently supported.
6829088Smarkm	 */
6929088Smarkm	for (i = 1; i <= NSLC; i++) {
7029088Smarkm		if ((slctab[i].defset.flag & SLC_LEVELBITS) == SLC_NOSUPPORT)
7129088Smarkm			continue;
7229088Smarkm		add_slc((unsigned char)i, slctab[i].current.flag,
7329088Smarkm							slctab[i].current.val);
7429088Smarkm	}
7529088Smarkm
7629088Smarkm}  /* end of send_slc */
7729088Smarkm
7829088Smarkm/*
7929088Smarkm * default_slc
8029088Smarkm *
8129088Smarkm * Set pty special characters to all the defaults.
8229088Smarkm */
8387139Smarkmstatic void
8487139Smarkmdefault_slc(void)
8529088Smarkm{
8687139Smarkm	int i;
8729088Smarkm
8829088Smarkm	for (i = 1; i <= NSLC; i++) {
8929088Smarkm		slctab[i].current.val = slctab[i].defset.val;
9029088Smarkm		if (slctab[i].current.val == (cc_t)(_POSIX_VDISABLE))
9129088Smarkm			slctab[i].current.flag = SLC_NOSUPPORT;
9229088Smarkm		else
9329088Smarkm			slctab[i].current.flag = slctab[i].defset.flag;
9429088Smarkm		if (slctab[i].sptr) {
9529088Smarkm			*(slctab[i].sptr) = slctab[i].defset.val;
9629088Smarkm		}
9729088Smarkm	}
9829088Smarkm	slcchange = 1;
9929088Smarkm
10029088Smarkm}  /* end of default_slc */
10129088Smarkm#endif	/* LINEMODE */
10229088Smarkm
10329088Smarkm/*
10429088Smarkm * get_slc_defaults
10529088Smarkm *
10629088Smarkm * Initialize the slc mapping table.
10729088Smarkm */
10887139Smarkmvoid
10987139Smarkmget_slc_defaults(void)
11029088Smarkm{
11187139Smarkm	int i;
11229088Smarkm
11329088Smarkm	init_termbuf();
11429088Smarkm
11529088Smarkm	for (i = 1; i <= NSLC; i++) {
11629088Smarkm		slctab[i].defset.flag =
11729088Smarkm			spcset(i, &slctab[i].defset.val, &slctab[i].sptr);
11829088Smarkm		slctab[i].current.flag = SLC_NOSUPPORT;
11929088Smarkm		slctab[i].current.val = 0;
12029088Smarkm	}
12129088Smarkm
12229088Smarkm}  /* end of get_slc_defaults */
12329088Smarkm
12429088Smarkm#ifdef	LINEMODE
12529088Smarkm/*
12629088Smarkm * add_slc
12729088Smarkm *
12829088Smarkm * Add an slc triplet to the slc buffer.
12929088Smarkm */
13087139Smarkmvoid
13187139Smarkmadd_slc(char func, char flag, cc_t val)
13229088Smarkm{
13329088Smarkm
13429088Smarkm	if ((*slcptr++ = (unsigned char)func) == 0xff)
13529088Smarkm		*slcptr++ = 0xff;
13629088Smarkm
13729088Smarkm	if ((*slcptr++ = (unsigned char)flag) == 0xff)
13829088Smarkm		*slcptr++ = 0xff;
13929088Smarkm
14029088Smarkm	if ((*slcptr++ = (unsigned char)val) == 0xff)
14129088Smarkm		*slcptr++ = 0xff;
14229088Smarkm
14329088Smarkm}  /* end of add_slc */
14429088Smarkm
14529088Smarkm/*
14629088Smarkm * start_slc
14729088Smarkm *
14829088Smarkm * Get ready to process incoming slc's and respond to them.
14929088Smarkm *
15029088Smarkm * The parameter getit is non-zero if it is necessary to grab a copy
15129088Smarkm * of the terminal control structures.
15229088Smarkm */
15387139Smarkmvoid
15487139Smarkmstart_slc(int getit)
15529088Smarkm{
15629088Smarkm
15729088Smarkm	slcchange = 0;
15829088Smarkm	if (getit)
15929088Smarkm		init_termbuf();
16029088Smarkm	(void) sprintf((char *)slcbuf, "%c%c%c%c",
16129088Smarkm					IAC, SB, TELOPT_LINEMODE, LM_SLC);
16229088Smarkm	slcptr = slcbuf + 4;
16329088Smarkm
16429088Smarkm}  /* end of start_slc */
16529088Smarkm
16629088Smarkm/*
16729088Smarkm * end_slc
16829088Smarkm *
16929088Smarkm * Finish up the slc negotiation.  If something to send, then send it.
17029088Smarkm */
17187139Smarkmint
17287139Smarkmend_slc(unsigned char **bufp)
17329088Smarkm{
17487139Smarkm	int len;
17529088Smarkm
17629088Smarkm	/*
17729088Smarkm	 * If a change has occured, store the new terminal control
17829088Smarkm	 * structures back to the terminal driver.
17929088Smarkm	 */
18029088Smarkm	if (slcchange) {
18129088Smarkm		set_termbuf();
18229088Smarkm	}
18329088Smarkm
18429088Smarkm	/*
18529088Smarkm	 * If the pty state has not yet been fully processed and there is a
18629088Smarkm	 * deferred slc request from the client, then do not send any
18729088Smarkm	 * sort of slc negotiation now.  We will respond to the client's
18829088Smarkm	 * request very soon.
18929088Smarkm	 */
19029088Smarkm	if (def_slcbuf && (terminit() == 0)) {
19129088Smarkm		return(0);
19229088Smarkm	}
19329088Smarkm
19429088Smarkm	if (slcptr > (slcbuf + 4)) {
19529088Smarkm		if (bufp) {
19629088Smarkm			*bufp = &slcbuf[4];
19729088Smarkm			return(slcptr - slcbuf - 4);
19829088Smarkm		} else {
19929088Smarkm			(void) sprintf((char *)slcptr, "%c%c", IAC, SE);
20029088Smarkm			slcptr += 2;
20129088Smarkm			len = slcptr - slcbuf;
20280038Sru			output_datalen(slcbuf, len);
20329088Smarkm			netflush();  /* force it out immediately */
20429088Smarkm			DIAG(TD_OPTIONS, printsub('>', slcbuf+2, len-2););
20529088Smarkm		}
20629088Smarkm	}
20729088Smarkm	return (0);
20829088Smarkm
20929088Smarkm}  /* end of end_slc */
21029088Smarkm
21129088Smarkm/*
21229088Smarkm * process_slc
21329088Smarkm *
21429088Smarkm * Figure out what to do about the client's slc
21529088Smarkm */
21687139Smarkmvoid
21787139Smarkmprocess_slc(unsigned char func, unsigned char flag, cc_t val)
21829088Smarkm{
21987139Smarkm	int hislevel, mylevel, ack;
22029088Smarkm
22129088Smarkm	/*
22229088Smarkm	 * Ensure that we know something about this function
22329088Smarkm	 */
22429088Smarkm	if (func > NSLC) {
22529088Smarkm		add_slc(func, SLC_NOSUPPORT, 0);
22629088Smarkm		return;
22729088Smarkm	}
22829088Smarkm
22929088Smarkm	/*
23029088Smarkm	 * Process the special case requests of 0 SLC_DEFAULT 0
23129088Smarkm	 * and 0 SLC_VARIABLE 0.  Be a little forgiving here, don't
23229088Smarkm	 * worry about whether the value is actually 0 or not.
23329088Smarkm	 */
23429088Smarkm	if (func == 0) {
23529088Smarkm		if ((flag = flag & SLC_LEVELBITS) == SLC_DEFAULT) {
23629088Smarkm			default_slc();
23729088Smarkm			send_slc();
23829088Smarkm		} else if (flag == SLC_VARIABLE) {
23929088Smarkm			send_slc();
24029088Smarkm		}
24129088Smarkm		return;
24229088Smarkm	}
24329088Smarkm
24429088Smarkm	/*
24529088Smarkm	 * Appears to be a function that we know something about.  So
24629088Smarkm	 * get on with it and see what we know.
24729088Smarkm	 */
24829088Smarkm
24929088Smarkm	hislevel = flag & SLC_LEVELBITS;
25029088Smarkm	mylevel = slctab[func].current.flag & SLC_LEVELBITS;
25129088Smarkm	ack = flag & SLC_ACK;
25229088Smarkm	/*
25329088Smarkm	 * ignore the command if:
25429088Smarkm	 * the function value and level are the same as what we already have;
25529088Smarkm	 * or the level is the same and the ack bit is set
25629088Smarkm	 */
25729088Smarkm	if (hislevel == mylevel && (val == slctab[func].current.val || ack)) {
25829088Smarkm		return;
25929088Smarkm	} else if (ack) {
26029088Smarkm		/*
26129088Smarkm		 * If we get here, we got an ack, but the levels don't match.
26229088Smarkm		 * This shouldn't happen.  If it does, it is probably because
26329088Smarkm		 * we have sent two requests to set a variable without getting
26429088Smarkm		 * a response between them, and this is the first response.
26529088Smarkm		 * So, ignore it, and wait for the next response.
26629088Smarkm		 */
26729088Smarkm		return;
26829088Smarkm	} else {
26929088Smarkm		change_slc(func, flag, val);
27029088Smarkm	}
27129088Smarkm
27229088Smarkm}  /* end of process_slc */
27329088Smarkm
27429088Smarkm/*
27529088Smarkm * change_slc
27629088Smarkm *
27729088Smarkm * Process a request to change one of our special characters.
27829088Smarkm * Compare client's request with what we are capable of supporting.
27929088Smarkm */
28087139Smarkmvoid
28187139Smarkmchange_slc(char func, char flag, cc_t val)
28229088Smarkm{
28387139Smarkm	int hislevel, mylevel;
28429088Smarkm
28529088Smarkm	hislevel = flag & SLC_LEVELBITS;
28629181Smarkm	mylevel = slctab[(int)func].defset.flag & SLC_LEVELBITS;
28729088Smarkm	/*
28829088Smarkm	 * If client is setting a function to NOSUPPORT
28929088Smarkm	 * or DEFAULT, then we can easily and directly
29029088Smarkm	 * accomodate the request.
29129088Smarkm	 */
29229088Smarkm	if (hislevel == SLC_NOSUPPORT) {
29329181Smarkm		slctab[(int)func].current.flag = flag;
29429181Smarkm		slctab[(int)func].current.val = (cc_t)_POSIX_VDISABLE;
29529088Smarkm		flag |= SLC_ACK;
29629088Smarkm		add_slc(func, flag, val);
29729088Smarkm		return;
29829088Smarkm	}
29929088Smarkm	if (hislevel == SLC_DEFAULT) {
30029088Smarkm		/*
30129088Smarkm		 * Special case here.  If client tells us to use
30229088Smarkm		 * the default on a function we don't support, then
30329088Smarkm		 * return NOSUPPORT instead of what we may have as a
30429088Smarkm		 * default level of DEFAULT.
30529088Smarkm		 */
30629088Smarkm		if (mylevel == SLC_DEFAULT) {
30729181Smarkm			slctab[(int)func].current.flag = SLC_NOSUPPORT;
30829088Smarkm		} else {
30929181Smarkm			slctab[(int)func].current.flag = slctab[(int)func].defset.flag;
31029088Smarkm		}
31129181Smarkm		slctab[(int)func].current.val = slctab[(int)func].defset.val;
31229181Smarkm		add_slc(func, slctab[(int)func].current.flag,
31329181Smarkm						slctab[(int)func].current.val);
31429088Smarkm		return;
31529088Smarkm	}
31629088Smarkm
31729088Smarkm	/*
31829088Smarkm	 * Client wants us to change to a new value or he
31929088Smarkm	 * is telling us that he can't change to our value.
32029088Smarkm	 * Some of the slc's we support and can change,
32129088Smarkm	 * some we do support but can't change,
32229088Smarkm	 * and others we don't support at all.
32329088Smarkm	 * If we can change it then we have a pointer to
32429088Smarkm	 * the place to put the new value, so change it,
32529088Smarkm	 * otherwise, continue the negotiation.
32629088Smarkm	 */
32729181Smarkm	if (slctab[(int)func].sptr) {
32829088Smarkm		/*
32929088Smarkm		 * We can change this one.
33029088Smarkm		 */
33129181Smarkm		slctab[(int)func].current.val = val;
33229181Smarkm		*(slctab[(int)func].sptr) = val;
33329181Smarkm		slctab[(int)func].current.flag = flag;
33429088Smarkm		flag |= SLC_ACK;
33529088Smarkm		slcchange = 1;
33629088Smarkm		add_slc(func, flag, val);
33729088Smarkm	} else {
33829088Smarkm		/*
33929088Smarkm		* It is not possible for us to support this
34029088Smarkm		* request as he asks.
34129088Smarkm		*
34229088Smarkm		* If our level is DEFAULT, then just ack whatever was
34329088Smarkm		* sent.
34429088Smarkm		*
34529088Smarkm		* If he can't change and we can't change,
34629088Smarkm		* then degenerate to NOSUPPORT.
34729088Smarkm		*
34829088Smarkm		* Otherwise we send our level back to him, (CANTCHANGE
34929088Smarkm		* or NOSUPPORT) and if CANTCHANGE, send
35029088Smarkm		* our value as well.
35129088Smarkm		*/
35229088Smarkm		if (mylevel == SLC_DEFAULT) {
35329181Smarkm			slctab[(int)func].current.flag = flag;
35429181Smarkm			slctab[(int)func].current.val = val;
35529088Smarkm			flag |= SLC_ACK;
35629088Smarkm		} else if (hislevel == SLC_CANTCHANGE &&
35729088Smarkm				    mylevel == SLC_CANTCHANGE) {
35829088Smarkm			flag &= ~SLC_LEVELBITS;
35929088Smarkm			flag |= SLC_NOSUPPORT;
36029181Smarkm			slctab[(int)func].current.flag = flag;
36129088Smarkm		} else {
36229088Smarkm			flag &= ~SLC_LEVELBITS;
36329088Smarkm			flag |= mylevel;
36429181Smarkm			slctab[(int)func].current.flag = flag;
36529088Smarkm			if (mylevel == SLC_CANTCHANGE) {
36629181Smarkm				slctab[(int)func].current.val =
36729181Smarkm					slctab[(int)func].defset.val;
36829181Smarkm				val = slctab[(int)func].current.val;
36929088Smarkm			}
37029088Smarkm		}
37129088Smarkm		add_slc(func, flag, val);
37229088Smarkm	}
37329088Smarkm
37429088Smarkm}  /* end of change_slc */
37529088Smarkm
37629088Smarkm#if	defined(USE_TERMIO) && (VEOF == VMIN)
37729088Smarkmcc_t oldeofc = '\004';
37829088Smarkm#endif
37929088Smarkm
38029088Smarkm/*
38129088Smarkm * check_slc
38229088Smarkm *
38329088Smarkm * Check the special characters in use and notify the client if any have
38429088Smarkm * changed.  Only those characters that are capable of being changed are
38529088Smarkm * likely to have changed.  If a local change occurs, kick the support level
38629088Smarkm * and flags up to the defaults.
38729088Smarkm */
38887139Smarkmvoid
38987139Smarkmcheck_slc(void)
39029088Smarkm{
39187139Smarkm	int i;
39229088Smarkm
39329088Smarkm	for (i = 1; i <= NSLC; i++) {
39429088Smarkm#if	defined(USE_TERMIO) && (VEOF == VMIN)
39529088Smarkm		/*
39629088Smarkm		 * In a perfect world this would be a neat little
39729088Smarkm		 * function.  But in this world, we should not notify
39829088Smarkm		 * client of changes to the VEOF char when
39929088Smarkm		 * ICANON is off, because it is not representing
40029088Smarkm		 * a special character.
40129088Smarkm		 */
40229088Smarkm		if (i == SLC_EOF) {
40329088Smarkm			if (!tty_isediting())
40429088Smarkm				continue;
40529088Smarkm			else if (slctab[i].sptr)
40629088Smarkm				oldeofc = *(slctab[i].sptr);
40729088Smarkm		}
40829088Smarkm#endif	/* defined(USE_TERMIO) && defined(SYSV_TERMIO) */
40929088Smarkm		if (slctab[i].sptr &&
41029088Smarkm				(*(slctab[i].sptr) != slctab[i].current.val)) {
41129088Smarkm			slctab[i].current.val = *(slctab[i].sptr);
41229088Smarkm			if (*(slctab[i].sptr) == (cc_t)_POSIX_VDISABLE)
41329088Smarkm				slctab[i].current.flag = SLC_NOSUPPORT;
41429088Smarkm			else
41529088Smarkm				slctab[i].current.flag = slctab[i].defset.flag;
41629088Smarkm			add_slc((unsigned char)i, slctab[i].current.flag,
41729088Smarkm						slctab[i].current.val);
41829088Smarkm		}
41929088Smarkm	}
42029088Smarkm}  /* check_slc */
42129088Smarkm
42229088Smarkm/*
42329088Smarkm * do_opt_slc
42429088Smarkm *
42529088Smarkm * Process an slc option buffer.  Defer processing of incoming slc's
42629088Smarkm * until after the terminal state has been processed.  Save the first slc
42729088Smarkm * request that comes along, but discard all others.
42829088Smarkm *
42929088Smarkm * ptr points to the beginning of the buffer, len is the length.
43029088Smarkm */
43187139Smarkmvoid
43287139Smarkmdo_opt_slc(unsigned char *ptr, int len)
43329088Smarkm{
43487139Smarkm	unsigned char func, flag;
43529088Smarkm	cc_t val;
43687139Smarkm	unsigned char *end = ptr + len;
43729088Smarkm
43829088Smarkm	if (terminit()) {  /* go ahead */
43929088Smarkm		while (ptr < end) {
44029088Smarkm			func = *ptr++;
44129088Smarkm			if (ptr >= end) break;
44229088Smarkm			flag = *ptr++;
44329088Smarkm			if (ptr >= end) break;
44429088Smarkm			val = (cc_t)*ptr++;
44529088Smarkm
44629088Smarkm			process_slc(func, flag, val);
44729088Smarkm
44829088Smarkm		}
44929088Smarkm	} else {
45029088Smarkm		/*
45129088Smarkm		 * save this slc buffer if it is the first, otherwise dump
45229088Smarkm		 * it.
45329088Smarkm		 */
45429088Smarkm		if (def_slcbuf == (unsigned char *)0) {
45529088Smarkm			def_slclen = len;
45629088Smarkm			def_slcbuf = (unsigned char *)malloc((unsigned)len);
45729088Smarkm			if (def_slcbuf == (unsigned char *)0)
45829088Smarkm				return;  /* too bad */
45929088Smarkm			memmove(def_slcbuf, ptr, len);
46029088Smarkm		}
46129088Smarkm	}
46229088Smarkm
46329088Smarkm}  /* end of do_opt_slc */
46429088Smarkm
46529088Smarkm/*
46629088Smarkm * deferslc
46729088Smarkm *
46829088Smarkm * Do slc stuff that was deferred.
46929088Smarkm */
47087139Smarkmvoid
47187139Smarkmdeferslc(void)
47229088Smarkm{
47329088Smarkm	if (def_slcbuf) {
47429088Smarkm		start_slc(1);
47529088Smarkm		do_opt_slc(def_slcbuf, def_slclen);
47629088Smarkm		(void) end_slc(0);
47729088Smarkm		free(def_slcbuf);
47829088Smarkm		def_slcbuf = (unsigned char *)0;
47929088Smarkm		def_slclen = 0;
48029088Smarkm	}
48129088Smarkm
48229088Smarkm}  /* end of deferslc */
48329088Smarkm
48429088Smarkm#endif	/* LINEMODE */
485