xref: /csrg-svn/libexec/telnetd/slc.c (revision 38905)
1 /*
2  * Copyright (c) 1989 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)slc.c	5.1 (Berkeley) 09/01/89";
20 #endif /* not lint */
21 
22 #include "telnetd.h"
23 
24 #ifdef	LINEMODE
25 /*
26  * local varibles
27  */
28 static char	*def_slcbuf = (char *)0;;
29 static int	def_slclen = 0;
30 static int	slcchange;	/* change to slc is requested */
31 static char	*slcptr;	/* pointer into slc buffer */
32 static char	slcbuf[NSLC*6];	/* buffer for slc negotiation */
33 
34 /*
35  * send_slc
36  *
37  * Write out the current special characters to the client.
38  */
39 send_slc()
40 {
41 	register int i;
42 
43 	/*
44 	 * Send out list of triplets of special characters
45 	 * to client.  We only send info on the characters
46 	 * that are currently supported.
47 	 */
48 	for (i = 1; i <= NSLC; i++) {
49 		if ((slctab[i].current.flag & SLC_LEVELBITS) != SLC_NOSUPPORT) {
50 			add_slc((unsigned char)i, slctab[i].current.flag,
51 							slctab[i].current.val);
52 		}
53 	}
54 
55 }  /* end of send_slc */
56 
57 /*
58  * default_slc
59  *
60  * Set pty special characters to all the defaults.
61  */
62 default_slc()
63 {
64 	register int i;
65 
66 	for (i = 1; i <= NSLC; i++) {
67 		slctab[i].current.flag = slctab[i].defset.flag;
68 		slctab[i].current.val = slctab[i].defset.val;
69 		if (slctab[i].sptr) {
70 			*(slctab[i].sptr) = slctab[i].defset.val;
71 		}
72 	}
73 	slcchange = 1;
74 
75 }  /* end of default_slc */
76 #endif	LINEMODE
77 
78 /*
79  * get_slc_defaults
80  *
81  * Initialize the slc mapping table.
82  */
83 get_slc_defaults()
84 {
85 	register int i;
86 
87 	init_termbuf();
88 
89 	for (i = 1; i <= NSLC; i++) {
90 		slctab[i].defset.flag =
91 			spcset(i, &slctab[i].defset.val, &slctab[i].sptr);
92 		slctab[i].current.flag = SLC_NOSUPPORT;
93 		slctab[i].current.val = 0;
94 	}
95 
96 }  /* end of get_slc_defaults */
97 
98 #ifdef	LINEMODE
99 /*
100  * add_slc
101  *
102  * Add an slc triplet to the slc buffer.
103  */
104 add_slc(func, flag, val)
105 	register unsigned char func, flag, val;
106 {
107 
108 	if (func == 0xff)
109 		*slcptr++ = 0xff;
110 	*slcptr++ = func;
111 
112 	if (flag == 0xff)
113 		*slcptr++ = 0xff;
114 	*slcptr++ = flag;
115 
116 	if (val == 0xff)
117 		*slcptr++ = 0xff;
118 	*slcptr++ = val;
119 
120 }  /* end of add_slc */
121 
122 /*
123  * start_slc
124  *
125  * Get ready to process incoming slc's and respond to them.
126  *
127  * The parameter getit is non-zero if it is necessary to grab a copy
128  * of the terminal control structures.
129  */
130 start_slc(getit)
131 	register int getit;
132 {
133 
134 	slcchange = 0;
135 	if (getit)
136 		init_termbuf();
137 	(void) sprintf(slcbuf, "%c%c%c%c", IAC, SB, TELOPT_LINEMODE, LM_SLC);
138 	slcptr = slcbuf + 4;
139 
140 }  /* end of start_slc */
141 
142 /*
143  * end_slc
144  *
145  * Finish up the slc negotiation.  If something to send, then send it.
146  */
147 end_slc(bufp)
148 register char **bufp;
149 {
150 	register int len;
151 	void netflush();
152 
153 	/*
154 	 * If a change has occured, store the new terminal control
155 	 * structures back to the terminal driver.
156 	 */
157 	if (slcchange) {
158 		set_termbuf();
159 	}
160 
161 	/*
162 	 * If the pty state has not yet been fully processed and there is a
163 	 * deferred slc request from the client, then do not send any
164 	 * sort of slc negotiation now.  We will respond to the client's
165 	 * request very soon.
166 	 */
167 	if (def_slcbuf && (terminit() == 0)) {
168 		return;
169 	}
170 
171 	if (slcptr > (slcbuf + 4)) {
172 		if (bufp) {
173 			*bufp = &slcbuf[4];
174 			return(slcptr - slcbuf - 4);
175 		} else {
176 			(void) sprintf(slcptr, "%c%c", IAC, SE);
177 			slcptr += 2;
178 			len = slcptr - slcbuf;
179 			writenet(slcbuf, len);
180 			netflush();  /* force it out immediately */
181 		}
182 	}
183 
184 }  /* end of end_slc */
185 
186 /*
187  * process_slc
188  *
189  * Figure out what to do about the client's slc
190  */
191 process_slc(func, flag, val)
192 	register unsigned char func, flag, val;
193 {
194 	register int hislevel, mylevel, ack;
195 
196 	/*
197 	 * Ensure that we know something about this function
198 	 */
199 	if (func > NSLC) {
200 		add_slc(func, SLC_NOSUPPORT, 0);
201 		return;
202 	}
203 
204 	/*
205 	 * Process the special case requests of 0 SLC_DEFAULT 0
206 	 * and 0 SLC_VARIABLE 0.  Be a little forgiving here, don't
207 	 * worry about whether the value is actually 0 or not.
208 	 */
209 	if (func == 0) {
210 		if ((flag = flag & SLC_LEVELBITS) == SLC_DEFAULT) {
211 			default_slc();
212 		}
213 		if (flag == SLC_DEFAULT || flag == SLC_VARIABLE) {
214 			send_slc();
215 		}
216 		return;
217 	}
218 
219 	/*
220 	 * Appears to be a function that we know something about.  So
221 	 * get on with it and see what we know.
222 	 */
223 
224 	hislevel = flag & SLC_LEVELBITS;
225 	mylevel = slctab[func].current.flag & SLC_LEVELBITS;
226 	ack = flag & SLC_ACK;
227 	/*
228 	 * ignore the command if:
229 	 * the function value and level are the same as what we already have;
230 	 * or the level is the same and the ack bit is set
231 	 */
232 	if (hislevel == mylevel && (val == slctab[func].current.val || ack)) {
233 		return;
234 	} else {
235 		change_slc(func, flag, val);
236 	}
237 
238 }  /* end of process_slc */
239 
240 /*
241  * change_slc
242  *
243  * Process a request to change one of our special characters.
244  * Compare client's request with what we are capable of supporting.
245  */
246 change_slc(func, flag, val)
247 	register unsigned char func, flag, val;
248 {
249 	register int hislevel, mylevel;
250 
251 	hislevel = flag & SLC_LEVELBITS;
252 	mylevel = slctab[func].defset.flag & SLC_LEVELBITS;
253 	/*
254 	 * If client is setting a function to NOSUPPORT
255 	 * or DEFAULT, then we can easily and directly
256 	 * accomodate the request.
257 	 */
258 	if (hislevel == SLC_NOSUPPORT) {
259 		slctab[func].current.flag = flag;
260 		slctab[func].current.val = val;
261 		flag |= SLC_ACK;
262 		add_slc(func, flag, val);
263 		return;
264 	}
265 	if (hislevel == SLC_DEFAULT) {
266 		/*
267 		 * Special case here.  If client tells us to use
268 		 * the default on a function we don't support, then
269 		 * return NOSUPPORT instead of what we may have as a
270 		 * default level of DEFAULT.
271 		 */
272 		if (mylevel == SLC_DEFAULT) {
273 			slctab[func].current.flag = SLC_NOSUPPORT;
274 		} else {
275 			slctab[func].current.flag = slctab[func].defset.flag;
276 		}
277 		slctab[func].current.val = slctab[func].defset.val;
278 		add_slc(func, slctab[func].current.flag,
279 						slctab[func].current.val);
280 		return;
281 	}
282 
283 	/*
284 	 * Client wants us to change to a new value or he
285 	 * is telling us that he can't change to our value.
286 	 * Some of the slc's we support and can change,
287 	 * some we do support but can't change,
288 	 * and others we don't support at all.
289 	 * If we can change it then we have a pointer to
290 	 * the place to put the new value, so change it,
291 	 * otherwise, continue the negotiation.
292 	 */
293 	if (slctab[func].sptr) {
294 		/*
295 		 * We can change this one.
296 		 */
297 		slctab[func].current.val = val;
298 		*(slctab[func].sptr) = val;
299 		slctab[func].current.flag = flag;
300 		flag |= SLC_ACK;
301 		slcchange = 1;
302 		add_slc(func, flag, val);
303 	} else {
304 		/*
305 		* It is not possible for us to support this
306 		* request as he asks.
307 		*
308 		* If our level is DEFAULT, then just ack whatever was
309 		* sent.
310 		*
311 		* If he can't change and we can't change,
312 		* then degenerate to NOSUPPORT.
313 		*
314 		* Otherwise we send our level back to him, (CANTCHANGE
315 		* or NOSUPPORT) and if CANTCHANGE, send
316 		* our value as well.
317 		*/
318 		if (mylevel == SLC_DEFAULT) {
319 			slctab[func].current.flag = flag;
320 			slctab[func].current.val = val;
321 			flag |= SLC_ACK;
322 		} else if (hislevel == SLC_CANTCHANGE &&
323 				    mylevel == SLC_CANTCHANGE) {
324 			flag &= ~SLC_LEVELBITS;
325 			flag |= SLC_NOSUPPORT;
326 			slctab[func].current.flag = flag;
327 		} else {
328 			flag &= ~SLC_LEVELBITS;
329 			flag |= mylevel;
330 			slctab[func].current.flag = flag;
331 			if (mylevel == SLC_CANTCHANGE) {
332 				slctab[func].current.val =
333 					slctab[func].defset.val;
334 				val = slctab[func].current.val;
335 			}
336 
337 		}
338 		add_slc(func, flag, val);
339 	}
340 
341 }  /* end of change_slc */
342 
343 /*
344  * check_slc
345  *
346  * Check the special characters in use and notify the client if any have
347  * changed.  Only those characters that are capable of being changed are
348  * likely to have changed.  If a local change occurs, kick the support level
349  * and flags up to the defaults.
350  */
351 check_slc()
352 {
353 	register int i;
354 
355 	for (i = 1; i <= NSLC; i++) {
356 #if	defined(USE_TERMIO) && defined(SYSV_TERMIO)
357 		/*
358 		 * In a perfect world this would be a neat little
359 		 * function.  But in this world, we should not notify
360 		 * client of changes to the VEOF char when
361 		 * ICANON is off, because it is not representing
362 		 * a special character.
363 		 */
364 		if (!tty_isediting() && i == SLC_EOF)
365 			continue;
366 #endif	/* defined(USE_TERMIO) && defined(SYSV_TERMIO) */
367 		if (slctab[i].sptr &&
368 				(*(slctab[i].sptr) != slctab[i].current.val)) {
369 			slctab[i].current.val = *(slctab[i].sptr);
370 			slctab[i].current.flag = slctab[i].defset.flag;
371 			add_slc((unsigned char)i, slctab[i].current.flag,
372 						slctab[i].current.val);
373 		}
374 	}
375 
376 }  /* check_slc */
377 
378 /*
379  * do_opt_slc
380  *
381  * Process an slc option buffer.  Defer processing of incoming slc's
382  * until after the terminal state has been processed.  Save the first slc
383  * request that comes along, but discard all others.
384  *
385  * ptr points to the beginning of the buffer, len is the length.
386  */
387 do_opt_slc(ptr, len)
388 register char *ptr;
389 register int len;
390 {
391 	register unsigned char func, flag, val;
392 	register char *end = (char *)(ptr + len);
393 	char *malloc();
394 
395 	if (terminit()) {  /* go ahead */
396 		while (ptr < end) {
397 			func = *ptr++;
398 			if (ptr >= end) break;
399 			flag = *ptr++;
400 			if (ptr >= end) break;
401 			val = *ptr++;
402 
403 			process_slc(func, flag, val);
404 
405 		}
406 	} else {
407 		/*
408 		 * save this slc buffer if it is the first, otherwise dump
409 		 * it.
410 		 */
411 		if (def_slcbuf == (char *)0) {
412 			def_slclen = len;
413 			def_slcbuf = malloc((unsigned)len);
414 			if (def_slcbuf == (char *)0)
415 				return;  /* too bad */
416 			bcopy(ptr, def_slcbuf, len);
417 		}
418 	}
419 
420 }  /* end of do_opt_slc */
421 
422 /*
423  * deferslc
424  *
425  * Do slc stuff that was deferred.
426  */
427 deferslc()
428 {
429 	if (def_slcbuf) {
430 		start_slc(1);
431 		do_opt_slc(def_slcbuf, def_slclen);
432 		end_slc(0);
433 		free(def_slcbuf);
434 		def_slcbuf = (char *)0;
435 		def_slclen = 0;
436 	}
437 
438 }  /* end of deferslc */
439 
440 #endif	/* LINEMODE */
441