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