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