slc.c revision 1.6 1 /* $NetBSD: slc.c,v 1.6 1997/10/08 08:45:10 mrg 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.6 1997/10/08 08:45:10 mrg 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 int end_slc __P((unsigned char **));
59 void process_slc __P((u_int, u_int, cc_t));
60
61 /*
62 * send_slc
63 *
64 * Write out the current special characters to the client.
65 */
66 void
67 send_slc()
68 {
69 register int i;
70
71 /*
72 * Send out list of triplets of special characters
73 * to client. We only send info on the characters
74 * that are currently supported.
75 */
76 for (i = 1; i <= NSLC; i++) {
77 if ((slctab[i].defset.flag & SLC_LEVELBITS) == SLC_NOSUPPORT)
78 continue;
79 add_slc((unsigned char)i, slctab[i].current.flag,
80 slctab[i].current.val);
81 }
82
83 } /* end of send_slc */
84
85 /*
86 * default_slc
87 *
88 * Set pty special characters to all the defaults.
89 */
90 void
91 default_slc()
92 {
93 register int i;
94
95 for (i = 1; i <= NSLC; i++) {
96 slctab[i].current.val = slctab[i].defset.val;
97 if (slctab[i].current.val == (cc_t)(_POSIX_VDISABLE))
98 slctab[i].current.flag = SLC_NOSUPPORT;
99 else
100 slctab[i].current.flag = slctab[i].defset.flag;
101 if (slctab[i].sptr) {
102 *(slctab[i].sptr) = slctab[i].defset.val;
103 }
104 }
105 slcchange = 1;
106
107 } /* end of default_slc */
108 #endif /* LINEMODE */
109
110 /*
111 * get_slc_defaults
112 *
113 * Initialize the slc mapping table.
114 */
115 void
116 get_slc_defaults()
117 {
118 register int i;
119
120 init_termbuf();
121
122 for (i = 1; i <= NSLC; i++) {
123 slctab[i].defset.flag =
124 spcset(i, &slctab[i].defset.val, &slctab[i].sptr);
125 slctab[i].current.flag = SLC_NOSUPPORT;
126 slctab[i].current.val = 0;
127 }
128
129 } /* end of get_slc_defaults */
130
131 #ifdef LINEMODE
132 /*
133 * add_slc
134 *
135 * Add an slc triplet to the slc buffer.
136 */
137 void
138 add_slc(func, flag, val)
139 register char func, flag;
140 register cc_t val;
141 {
142
143 if ((*slcptr++ = (unsigned char)func) == 0xff)
144 *slcptr++ = 0xff;
145
146 if ((*slcptr++ = (unsigned char)flag) == 0xff)
147 *slcptr++ = 0xff;
148
149 if ((*slcptr++ = (unsigned char)val) == 0xff)
150 *slcptr++ = 0xff;
151
152 } /* end of add_slc */
153
154 /*
155 * start_slc
156 *
157 * Get ready to process incoming slc's and respond to them.
158 *
159 * The parameter getit is non-zero if it is necessary to grab a copy
160 * of the terminal control structures.
161 */
162 void
163 start_slc(getit)
164 register int getit;
165 {
166
167 slcchange = 0;
168 if (getit)
169 init_termbuf();
170 (void)snprintf((char *)slcbuf, sizeof slcbuf, "%c%c%c%c",
171 IAC, SB, TELOPT_LINEMODE, LM_SLC);
172 slcptr = slcbuf + 4;
173
174 } /* end of start_slc */
175
176 /*
177 * end_slc
178 *
179 * Finish up the slc negotiation. If something to send, then send it.
180 */
181 int
182 end_slc(bufp)
183 register unsigned char **bufp;
184 {
185 register int len;
186
187 /*
188 * If a change has occured, store the new terminal control
189 * structures back to the terminal driver.
190 */
191 if (slcchange) {
192 set_termbuf();
193 }
194
195 /*
196 * If the pty state has not yet been fully processed and there is a
197 * deferred slc request from the client, then do not send any
198 * sort of slc negotiation now. We will respond to the client's
199 * request very soon.
200 */
201 if (def_slcbuf && (terminit() == 0)) {
202 return(0);
203 }
204
205 if (slcptr > (slcbuf + 4)) {
206 if (bufp) {
207 *bufp = &slcbuf[4];
208 return(slcptr - slcbuf - 4);
209 } else {
210 (void) sprintf((char *)slcptr, "%c%c", IAC, SE);
211 slcptr += 2;
212 len = slcptr - slcbuf;
213 writenet(slcbuf, len);
214 netflush(); /* force it out immediately */
215 DIAG(TD_OPTIONS, printsub('>', slcbuf+2, len-2););
216 }
217 }
218 return (0);
219
220 } /* end of end_slc */
221
222 /*
223 * process_slc
224 *
225 * Figure out what to do about the client's slc
226 */
227 void
228 process_slc(func, flag, val)
229 u_int func, flag;
230 cc_t val;
231 {
232 int hislevel, mylevel, ack;
233
234 /*
235 * Ensure that we know something about this function
236 */
237 if (func > NSLC) {
238 add_slc(func, SLC_NOSUPPORT, 0);
239 return;
240 }
241
242 /*
243 * Process the special case requests of 0 SLC_DEFAULT 0
244 * and 0 SLC_VARIABLE 0. Be a little forgiving here, don't
245 * worry about whether the value is actually 0 or not.
246 */
247 if (func == 0) {
248 if ((flag = flag & SLC_LEVELBITS) == SLC_DEFAULT) {
249 default_slc();
250 send_slc();
251 } else if (flag == SLC_VARIABLE) {
252 send_slc();
253 }
254 return;
255 }
256
257 /*
258 * Appears to be a function that we know something about. So
259 * get on with it and see what we know.
260 */
261
262 hislevel = flag & SLC_LEVELBITS;
263 mylevel = slctab[func].current.flag & SLC_LEVELBITS;
264 ack = flag & SLC_ACK;
265 /*
266 * ignore the command if:
267 * the function value and level are the same as what we already have;
268 * or the level is the same and the ack bit is set
269 */
270 if (hislevel == mylevel && (val == slctab[func].current.val || ack)) {
271 return;
272 } else if (ack) {
273 /*
274 * If we get here, we got an ack, but the levels don't match.
275 * This shouldn't happen. If it does, it is probably because
276 * we have sent two requests to set a variable without getting
277 * a response between them, and this is the first response.
278 * So, ignore it, and wait for the next response.
279 */
280 return;
281 } else {
282 change_slc(func, flag, val);
283 }
284
285 } /* end of process_slc */
286
287 /*
288 * change_slc
289 *
290 * Process a request to change one of our special characters.
291 * Compare client's request with what we are capable of supporting.
292 */
293 void
294 change_slc(func, flag, val)
295 int func, flag;
296 cc_t val;
297 {
298 int hislevel, mylevel;
299
300 hislevel = flag & SLC_LEVELBITS;
301 mylevel = slctab[func].defset.flag & SLC_LEVELBITS;
302 /*
303 * If client is setting a function to NOSUPPORT
304 * or DEFAULT, then we can easily and directly
305 * accomodate the request.
306 */
307 if (hislevel == SLC_NOSUPPORT) {
308 slctab[func].current.flag = flag;
309 slctab[func].current.val = (cc_t)_POSIX_VDISABLE;
310 flag |= SLC_ACK;
311 add_slc(func, flag, val);
312 return;
313 }
314 if (hislevel == SLC_DEFAULT) {
315 /*
316 * Special case here. If client tells us to use
317 * the default on a function we don't support, then
318 * return NOSUPPORT instead of what we may have as a
319 * default level of DEFAULT.
320 */
321 if (mylevel == SLC_DEFAULT) {
322 slctab[func].current.flag = SLC_NOSUPPORT;
323 } else {
324 slctab[func].current.flag = slctab[func].defset.flag;
325 }
326 slctab[func].current.val = slctab[func].defset.val;
327 add_slc(func, slctab[func].current.flag,
328 slctab[func].current.val);
329 return;
330 }
331
332 /*
333 * Client wants us to change to a new value or he
334 * is telling us that he can't change to our value.
335 * Some of the slc's we support and can change,
336 * some we do support but can't change,
337 * and others we don't support at all.
338 * If we can change it then we have a pointer to
339 * the place to put the new value, so change it,
340 * otherwise, continue the negotiation.
341 */
342 if (slctab[func].sptr) {
343 /*
344 * We can change this one.
345 */
346 slctab[func].current.val = val;
347 *(slctab[func].sptr) = val;
348 slctab[func].current.flag = flag;
349 flag |= SLC_ACK;
350 slcchange = 1;
351 add_slc(func, flag, val);
352 } else {
353 /*
354 * It is not possible for us to support this
355 * request as he asks.
356 *
357 * If our level is DEFAULT, then just ack whatever was
358 * sent.
359 *
360 * If he can't change and we can't change,
361 * then degenerate to NOSUPPORT.
362 *
363 * Otherwise we send our level back to him, (CANTCHANGE
364 * or NOSUPPORT) and if CANTCHANGE, send
365 * our value as well.
366 */
367 if (mylevel == SLC_DEFAULT) {
368 slctab[func].current.flag = flag;
369 slctab[func].current.val = val;
370 flag |= SLC_ACK;
371 } else if (hislevel == SLC_CANTCHANGE &&
372 mylevel == SLC_CANTCHANGE) {
373 flag &= ~SLC_LEVELBITS;
374 flag |= SLC_NOSUPPORT;
375 slctab[func].current.flag = flag;
376 } else {
377 flag &= ~SLC_LEVELBITS;
378 flag |= mylevel;
379 slctab[func].current.flag = flag;
380 if (mylevel == SLC_CANTCHANGE) {
381 slctab[func].current.val =
382 slctab[func].defset.val;
383 val = slctab[func].current.val;
384 }
385
386 }
387 add_slc(func, flag, val);
388 }
389
390 } /* end of change_slc */
391
392 #if defined(USE_TERMIO) && (VEOF == VMIN)
393 cc_t oldeofc = '\004';
394 #endif
395
396 /*
397 * check_slc
398 *
399 * Check the special characters in use and notify the client if any have
400 * changed. Only those characters that are capable of being changed are
401 * likely to have changed. If a local change occurs, kick the support level
402 * and flags up to the defaults.
403 */
404 void
405 check_slc()
406 {
407 register int i;
408
409 for (i = 1; i <= NSLC; i++) {
410 #if defined(USE_TERMIO) && (VEOF == VMIN)
411 /*
412 * In a perfect world this would be a neat little
413 * function. But in this world, we should not notify
414 * client of changes to the VEOF char when
415 * ICANON is off, because it is not representing
416 * a special character.
417 */
418 if (i == SLC_EOF) {
419 if (!tty_isediting())
420 continue;
421 else if (slctab[i].sptr)
422 oldeofc = *(slctab[i].sptr);
423 }
424 #endif /* defined(USE_TERMIO) && defined(SYSV_TERMIO) */
425 if (slctab[i].sptr &&
426 (*(slctab[i].sptr) != slctab[i].current.val)) {
427 slctab[i].current.val = *(slctab[i].sptr);
428 if (*(slctab[i].sptr) == (cc_t)_POSIX_VDISABLE)
429 slctab[i].current.flag = SLC_NOSUPPORT;
430 else
431 slctab[i].current.flag = slctab[i].defset.flag;
432 add_slc((unsigned char)i, slctab[i].current.flag,
433 slctab[i].current.val);
434 }
435 }
436
437 } /* check_slc */
438
439 /*
440 * do_opt_slc
441 *
442 * Process an slc option buffer. Defer processing of incoming slc's
443 * until after the terminal state has been processed. Save the first slc
444 * request that comes along, but discard all others.
445 *
446 * ptr points to the beginning of the buffer, len is the length.
447 */
448 void
449 do_opt_slc(ptr, len)
450 register unsigned char *ptr;
451 register int len;
452 {
453 register unsigned char func, flag;
454 cc_t val;
455 register unsigned char *end = ptr + len;
456
457 if (terminit()) { /* go ahead */
458 while (ptr < end) {
459 func = *ptr++;
460 if (ptr >= end) break;
461 flag = *ptr++;
462 if (ptr >= end) break;
463 val = (cc_t)*ptr++;
464
465 process_slc((u_int)func, (u_int)flag, val);
466
467 }
468 } else {
469 /*
470 * save this slc buffer if it is the first, otherwise dump
471 * it.
472 */
473 if (def_slcbuf == (unsigned char *)0) {
474 def_slclen = len;
475 def_slcbuf = (unsigned char *)malloc((unsigned)len);
476 if (def_slcbuf == (unsigned char *)0)
477 return; /* too bad */
478 bcopy(ptr, def_slcbuf, len);
479 }
480 }
481
482 } /* end of do_opt_slc */
483
484 /*
485 * deferslc
486 *
487 * Do slc stuff that was deferred.
488 */
489 void
490 deferslc()
491 {
492 if (def_slcbuf) {
493 start_slc(1);
494 do_opt_slc(def_slcbuf, def_slclen);
495 (void) end_slc(0);
496 free(def_slcbuf);
497 def_slcbuf = (unsigned char *)0;
498 def_slclen = 0;
499 }
500
501 } /* end of deferslc */
502
503 #endif /* LINEMODE */
504