]>
Commit | Line | Data |
---|---|---|
b7080c8e A |
1 | /* |
2 | * Copyright (c) 1989, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by the University of | |
16 | * California, Berkeley and its contributors. | |
17 | * 4. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
32 | */ | |
33 | ||
34 | #ifndef lint | |
8052502f A |
35 | #if 0 |
36 | static char sccsid[] = "@(#)utility.c 8.2 (Berkeley) 12/15/93"; | |
37 | #endif | |
38 | static const char rcsid[] = | |
39 | "$FreeBSD: src/libexec/telnetd/utility.c,v 1.15 2001/07/20 15:14:03 ru Exp $"; | |
b7080c8e A |
40 | #endif /* not lint */ |
41 | ||
8052502f A |
42 | #ifdef __FreeBSD__ |
43 | #include <locale.h> | |
44 | #include <sys/utsname.h> | |
45 | #endif | |
b7080c8e A |
46 | #define PRINTOPTIONS |
47 | #include "telnetd.h" | |
48 | ||
49 | /* | |
50 | * utility functions performing io related tasks | |
51 | */ | |
52 | ||
53 | /* | |
54 | * ttloop | |
55 | * | |
56 | * A small subroutine to flush the network output buffer, get some data | |
57 | * from the network, and pass it through the telnet state machine. We | |
58 | * also flush the pty input buffer (by dropping its data) if it becomes | |
59 | * too full. | |
60 | */ | |
61 | ||
62 | void | |
63 | ttloop() | |
64 | { | |
b7080c8e | 65 | |
8052502f A |
66 | DIAG(TD_REPORT, output_data("td: ttloop\r\n")); |
67 | if (nfrontp - nbackp > 0) { | |
b7080c8e A |
68 | netflush(); |
69 | } | |
70 | ncc = read(net, netibuf, sizeof netibuf); | |
71 | if (ncc < 0) { | |
8052502f | 72 | syslog(LOG_INFO, "ttloop: read: %m"); |
b7080c8e A |
73 | exit(1); |
74 | } else if (ncc == 0) { | |
8052502f | 75 | syslog(LOG_INFO, "ttloop: peer died: %m"); |
b7080c8e A |
76 | exit(1); |
77 | } | |
8052502f | 78 | DIAG(TD_REPORT, output_data("td: ttloop read %d chars\r\n", ncc)); |
b7080c8e A |
79 | netip = netibuf; |
80 | telrcv(); /* state machine */ | |
81 | if (ncc > 0) { | |
82 | pfrontp = pbackp = ptyobuf; | |
83 | telrcv(); | |
84 | } | |
85 | } /* end of ttloop */ | |
86 | ||
87 | /* | |
88 | * Check a descriptor to see if out of band data exists on it. | |
89 | */ | |
90 | int | |
91 | stilloob(s) | |
92 | int s; /* socket number */ | |
93 | { | |
94 | static struct timeval timeout = { 0 }; | |
95 | fd_set excepts; | |
96 | int value; | |
97 | ||
98 | do { | |
99 | FD_ZERO(&excepts); | |
100 | FD_SET(s, &excepts); | |
8052502f | 101 | memset((char *)&timeout, 0, sizeof timeout); |
b7080c8e A |
102 | value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); |
103 | } while ((value == -1) && (errno == EINTR)); | |
104 | ||
105 | if (value < 0) { | |
106 | fatalperror(pty, "select"); | |
107 | } | |
108 | if (FD_ISSET(s, &excepts)) { | |
109 | return 1; | |
110 | } else { | |
111 | return 0; | |
112 | } | |
113 | } | |
114 | ||
115 | void | |
116 | ptyflush() | |
117 | { | |
118 | int n; | |
119 | ||
120 | if ((n = pfrontp - pbackp) > 0) { | |
8052502f A |
121 | DIAG(TD_REPORT | TD_PTYDATA, |
122 | output_data("td: ptyflush %d chars\r\n", n)); | |
b7080c8e A |
123 | DIAG(TD_PTYDATA, printdata("pd", pbackp, n)); |
124 | n = write(pty, pbackp, n); | |
125 | } | |
126 | if (n < 0) { | |
127 | if (errno == EWOULDBLOCK || errno == EINTR) | |
128 | return; | |
129 | cleanup(0); | |
130 | } | |
131 | pbackp += n; | |
132 | if (pbackp == pfrontp) | |
133 | pbackp = pfrontp = ptyobuf; | |
134 | } | |
135 | ||
136 | /* | |
137 | * nextitem() | |
138 | * | |
139 | * Return the address of the next "item" in the TELNET data | |
140 | * stream. This will be the address of the next character if | |
141 | * the current address is a user data character, or it will | |
142 | * be the address of the character following the TELNET command | |
143 | * if the current address is a TELNET IAC ("I Am a Command") | |
144 | * character. | |
145 | */ | |
146 | char * | |
147 | nextitem(current) | |
148 | char *current; | |
149 | { | |
150 | if ((*current&0xff) != IAC) { | |
151 | return current+1; | |
152 | } | |
153 | switch (*(current+1)&0xff) { | |
154 | case DO: | |
155 | case DONT: | |
156 | case WILL: | |
157 | case WONT: | |
158 | return current+3; | |
159 | case SB: /* loop forever looking for the SE */ | |
160 | { | |
161 | register char *look = current+2; | |
162 | ||
163 | for (;;) { | |
164 | if ((*look++&0xff) == IAC) { | |
165 | if ((*look++&0xff) == SE) { | |
166 | return look; | |
167 | } | |
168 | } | |
169 | } | |
170 | } | |
171 | default: | |
172 | return current+2; | |
173 | } | |
174 | } /* end of nextitem */ | |
175 | ||
176 | ||
177 | /* | |
178 | * netclear() | |
179 | * | |
180 | * We are about to do a TELNET SYNCH operation. Clear | |
181 | * the path to the network. | |
182 | * | |
183 | * Things are a bit tricky since we may have sent the first | |
184 | * byte or so of a previous TELNET command into the network. | |
185 | * So, we have to scan the network buffer from the beginning | |
186 | * until we are up to where we want to be. | |
187 | * | |
188 | * A side effect of what we do, just to keep things | |
189 | * simple, is to clear the urgent data pointer. The principal | |
190 | * caller should be setting the urgent data pointer AFTER calling | |
191 | * us in any case. | |
192 | */ | |
193 | void | |
194 | netclear() | |
195 | { | |
196 | register char *thisitem, *next; | |
197 | char *good; | |
198 | #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ | |
199 | ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) | |
200 | ||
b7080c8e | 201 | thisitem = netobuf; |
b7080c8e A |
202 | |
203 | while ((next = nextitem(thisitem)) <= nbackp) { | |
204 | thisitem = next; | |
205 | } | |
206 | ||
207 | /* Now, thisitem is first before/at boundary. */ | |
208 | ||
b7080c8e | 209 | good = netobuf; /* where the good bytes go */ |
b7080c8e A |
210 | |
211 | while (nfrontp > thisitem) { | |
212 | if (wewant(thisitem)) { | |
213 | int length; | |
214 | ||
215 | next = thisitem; | |
216 | do { | |
217 | next = nextitem(next); | |
218 | } while (wewant(next) && (nfrontp > next)); | |
219 | length = next-thisitem; | |
8052502f | 220 | bcopy(thisitem, good, length); |
b7080c8e A |
221 | good += length; |
222 | thisitem = next; | |
223 | } else { | |
224 | thisitem = nextitem(thisitem); | |
225 | } | |
226 | } | |
227 | ||
228 | nbackp = netobuf; | |
229 | nfrontp = good; /* next byte to be sent */ | |
230 | neturg = 0; | |
231 | } /* end of netclear */ | |
232 | ||
233 | /* | |
234 | * netflush | |
235 | * Send as much data as possible to the network, | |
236 | * handling requests for urgent data. | |
237 | */ | |
238 | void | |
239 | netflush() | |
240 | { | |
241 | int n; | |
242 | extern int not42; | |
243 | ||
8052502f A |
244 | while ((n = nfrontp - nbackp) > 0) { |
245 | #if 0 | |
246 | /* XXX This causes output_data() to recurse and die */ | |
247 | ||
248 | DIAG(TD_REPORT, { | |
249 | n += output_data("td: netflush %d chars\r\n", n); | |
250 | }); | |
251 | #endif | |
b7080c8e A |
252 | /* |
253 | * if no urgent data, or if the other side appears to be an | |
254 | * old 4.2 client (and thus unable to survive TCP urgent data), | |
255 | * write the entire buffer in non-OOB mode. | |
256 | */ | |
257 | if ((neturg == 0) || (not42 == 0)) { | |
258 | n = write(net, nbackp, n); /* normal write */ | |
259 | } else { | |
260 | n = neturg - nbackp; | |
261 | /* | |
262 | * In 4.2 (and 4.3) systems, there is some question about | |
263 | * what byte in a sendOOB operation is the "OOB" data. | |
264 | * To make ourselves compatible, we only send ONE byte | |
265 | * out of band, the one WE THINK should be OOB (though | |
266 | * we really have more the TCP philosophy of urgent data | |
267 | * rather than the Unix philosophy of OOB data). | |
268 | */ | |
269 | if (n > 1) { | |
270 | n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ | |
271 | } else { | |
272 | n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ | |
273 | } | |
274 | } | |
8052502f A |
275 | if (n == -1) { |
276 | if (errno == EWOULDBLOCK || errno == EINTR) | |
277 | continue; | |
278 | cleanup(0); | |
279 | /* NOTREACHED */ | |
280 | } | |
281 | nbackp += n; | |
282 | if (nbackp >= neturg) { | |
283 | neturg = 0; | |
284 | } | |
285 | if (nbackp == nfrontp) { | |
286 | nbackp = nfrontp = netobuf; | |
287 | } | |
b7080c8e A |
288 | } |
289 | return; | |
290 | } /* end of netflush */ | |
291 | ||
292 | ||
b7080c8e A |
293 | /* |
294 | * miscellaneous functions doing a variety of little jobs follow ... | |
295 | */ | |
296 | ||
297 | ||
298 | void | |
299 | fatal(f, msg) | |
300 | int f; | |
301 | char *msg; | |
302 | { | |
303 | char buf[BUFSIZ]; | |
304 | ||
8052502f | 305 | (void) snprintf(buf, sizeof(buf), "telnetd: %s.\r\n", msg); |
b7080c8e A |
306 | (void) write(f, buf, (int)strlen(buf)); |
307 | sleep(1); /*XXX*/ | |
308 | exit(1); | |
309 | } | |
310 | ||
311 | void | |
312 | fatalperror(f, msg) | |
313 | int f; | |
314 | char *msg; | |
315 | { | |
316 | char buf[BUFSIZ], *strerror(); | |
317 | ||
8052502f | 318 | (void) snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno)); |
b7080c8e A |
319 | fatal(f, buf); |
320 | } | |
321 | ||
322 | char editedhost[32]; | |
323 | ||
324 | void | |
325 | edithost(pat, host) | |
326 | register char *pat; | |
327 | register char *host; | |
328 | { | |
329 | register char *res = editedhost; | |
b7080c8e A |
330 | |
331 | if (!pat) | |
332 | pat = ""; | |
333 | while (*pat) { | |
334 | switch (*pat) { | |
335 | ||
336 | case '#': | |
337 | if (*host) | |
338 | host++; | |
339 | break; | |
340 | ||
341 | case '@': | |
342 | if (*host) | |
343 | *res++ = *host++; | |
344 | break; | |
345 | ||
346 | default: | |
347 | *res++ = *pat; | |
348 | break; | |
349 | } | |
350 | if (res == &editedhost[sizeof editedhost - 1]) { | |
351 | *res = '\0'; | |
352 | return; | |
353 | } | |
354 | pat++; | |
355 | } | |
356 | if (*host) | |
357 | (void) strncpy(res, host, | |
358 | sizeof editedhost - (res - editedhost) -1); | |
359 | else | |
360 | *res = '\0'; | |
361 | editedhost[sizeof editedhost - 1] = '\0'; | |
362 | } | |
363 | ||
364 | static char *putlocation; | |
365 | ||
366 | void | |
367 | putstr(s) | |
368 | register char *s; | |
369 | { | |
370 | ||
371 | while (*s) | |
372 | putchr(*s++); | |
373 | } | |
374 | ||
375 | void | |
376 | putchr(cc) | |
377 | int cc; | |
378 | { | |
379 | *putlocation++ = cc; | |
380 | } | |
381 | ||
8052502f A |
382 | #ifdef __FreeBSD__ |
383 | static char fmtstr[] = { "%+" }; | |
384 | #else | |
b7080c8e A |
385 | /* |
386 | * This is split on two lines so that SCCS will not see the M | |
387 | * between two % signs and expand it... | |
388 | */ | |
389 | static char fmtstr[] = { "%l:%M\ | |
390 | %P on %A, %d %B %Y" }; | |
8052502f | 391 | #endif |
b7080c8e A |
392 | |
393 | void | |
394 | putf(cp, where) | |
395 | register char *cp; | |
396 | char *where; | |
397 | { | |
398 | char *slash; | |
399 | time_t t; | |
400 | char db[100]; | |
401 | #ifdef STREAMSPTY | |
8052502f | 402 | extern char *index(); |
b7080c8e | 403 | #else |
8052502f A |
404 | extern char *rindex(); |
405 | #endif | |
406 | #ifdef __FreeBSD__ | |
407 | static struct utsname kerninfo; | |
408 | ||
409 | if (!*kerninfo.sysname) | |
410 | uname(&kerninfo); | |
b7080c8e A |
411 | #endif |
412 | ||
413 | putlocation = where; | |
414 | ||
415 | while (*cp) { | |
8052502f A |
416 | if (*cp =='\n') { |
417 | putstr("\r\n"); | |
418 | cp++; | |
419 | continue; | |
420 | } else if (*cp != '%') { | |
b7080c8e A |
421 | putchr(*cp++); |
422 | continue; | |
423 | } | |
424 | switch (*++cp) { | |
425 | ||
426 | case 't': | |
427 | #ifdef STREAMSPTY | |
428 | /* names are like /dev/pts/2 -- we want pts/2 */ | |
8052502f | 429 | slash = index(line+1, '/'); |
b7080c8e | 430 | #else |
8052502f | 431 | slash = rindex(line, '/'); |
b7080c8e A |
432 | #endif |
433 | if (slash == (char *) 0) | |
434 | putstr(line); | |
435 | else | |
436 | putstr(&slash[1]); | |
437 | break; | |
438 | ||
439 | case 'h': | |
440 | putstr(editedhost); | |
441 | break; | |
442 | ||
443 | case 'd': | |
8052502f A |
444 | #ifdef __FreeBSD__ |
445 | setlocale(LC_TIME, ""); | |
446 | #endif | |
b7080c8e A |
447 | (void)time(&t); |
448 | (void)strftime(db, sizeof(db), fmtstr, localtime(&t)); | |
449 | putstr(db); | |
450 | break; | |
451 | ||
8052502f A |
452 | #ifdef __FreeBSD__ |
453 | case 's': | |
454 | putstr(kerninfo.sysname); | |
455 | break; | |
456 | ||
457 | case 'm': | |
458 | putstr(kerninfo.machine); | |
459 | break; | |
460 | ||
461 | case 'r': | |
462 | putstr(kerninfo.release); | |
463 | break; | |
464 | ||
465 | case 'v': | |
466 | putstr(kerninfo.version); | |
467 | break; | |
468 | #endif | |
469 | ||
b7080c8e A |
470 | case '%': |
471 | putchr('%'); | |
472 | break; | |
473 | } | |
474 | cp++; | |
475 | } | |
476 | } | |
477 | ||
478 | #ifdef DIAGNOSTICS | |
479 | /* | |
480 | * Print telnet options and commands in plain text, if possible. | |
481 | */ | |
482 | void | |
483 | printoption(fmt, option) | |
484 | register char *fmt; | |
485 | register int option; | |
486 | { | |
487 | if (TELOPT_OK(option)) | |
8052502f | 488 | output_data("%s %s\r\n", fmt, TELOPT(option)); |
b7080c8e | 489 | else if (TELCMD_OK(option)) |
8052502f | 490 | output_data("%s %s\r\n", fmt, TELCMD(option)); |
b7080c8e | 491 | else |
8052502f | 492 | output_data("%s %d\r\n", fmt, option); |
b7080c8e A |
493 | return; |
494 | } | |
495 | ||
496 | void | |
497 | printsub(direction, pointer, length) | |
498 | char direction; /* '<' or '>' */ | |
499 | unsigned char *pointer; /* where suboption data sits */ | |
500 | int length; /* length of suboption data */ | |
501 | { | |
8052502f | 502 | register int i = 0; |
b7080c8e | 503 | |
8052502f | 504 | if (!(diagnostic & TD_OPTIONS)) |
b7080c8e A |
505 | return; |
506 | ||
507 | if (direction) { | |
8052502f | 508 | output_data("td: %s suboption ", |
b7080c8e | 509 | direction == '<' ? "recv" : "send"); |
b7080c8e A |
510 | if (length >= 3) { |
511 | register int j; | |
512 | ||
513 | i = pointer[length-2]; | |
514 | j = pointer[length-1]; | |
515 | ||
516 | if (i != IAC || j != SE) { | |
8052502f | 517 | output_data("(terminated by "); |
b7080c8e | 518 | if (TELOPT_OK(i)) |
8052502f | 519 | output_data("%s ", TELOPT(i)); |
b7080c8e | 520 | else if (TELCMD_OK(i)) |
8052502f | 521 | output_data("%s ", TELCMD(i)); |
b7080c8e | 522 | else |
8052502f | 523 | output_data("%d ", i); |
b7080c8e | 524 | if (TELOPT_OK(j)) |
8052502f | 525 | output_data("%s", TELOPT(j)); |
b7080c8e | 526 | else if (TELCMD_OK(j)) |
8052502f | 527 | output_data("%s", TELCMD(j)); |
b7080c8e | 528 | else |
8052502f A |
529 | output_data("%d", j); |
530 | output_data(", not IAC SE!) "); | |
b7080c8e A |
531 | } |
532 | } | |
533 | length -= 2; | |
534 | } | |
535 | if (length < 1) { | |
8052502f | 536 | output_data("(Empty suboption??\?)"); |
b7080c8e A |
537 | return; |
538 | } | |
539 | switch (pointer[0]) { | |
540 | case TELOPT_TTYPE: | |
8052502f | 541 | output_data("TERMINAL-TYPE "); |
b7080c8e A |
542 | switch (pointer[1]) { |
543 | case TELQUAL_IS: | |
8052502f | 544 | output_data("IS \"%.*s\"", length-2, (char *)pointer+2); |
b7080c8e A |
545 | break; |
546 | case TELQUAL_SEND: | |
8052502f | 547 | output_data("SEND"); |
b7080c8e A |
548 | break; |
549 | default: | |
8052502f | 550 | output_data( |
b7080c8e A |
551 | "- unknown qualifier %d (0x%x).", |
552 | pointer[1], pointer[1]); | |
553 | } | |
b7080c8e A |
554 | break; |
555 | case TELOPT_TSPEED: | |
8052502f | 556 | output_data("TERMINAL-SPEED"); |
b7080c8e | 557 | if (length < 2) { |
8052502f | 558 | output_data(" (empty suboption??\?)"); |
b7080c8e A |
559 | break; |
560 | } | |
561 | switch (pointer[1]) { | |
562 | case TELQUAL_IS: | |
8052502f | 563 | output_data(" IS %.*s", length-2, (char *)pointer+2); |
b7080c8e A |
564 | break; |
565 | default: | |
566 | if (pointer[1] == 1) | |
8052502f | 567 | output_data(" SEND"); |
b7080c8e | 568 | else |
8052502f | 569 | output_data(" %d (unknown)", pointer[1]); |
b7080c8e | 570 | for (i = 2; i < length; i++) { |
8052502f | 571 | output_data(" ?%d?", pointer[i]); |
b7080c8e A |
572 | } |
573 | break; | |
574 | } | |
575 | break; | |
576 | ||
577 | case TELOPT_LFLOW: | |
8052502f | 578 | output_data("TOGGLE-FLOW-CONTROL"); |
b7080c8e | 579 | if (length < 2) { |
8052502f | 580 | output_data(" (empty suboption??\?)"); |
b7080c8e A |
581 | break; |
582 | } | |
583 | switch (pointer[1]) { | |
584 | case LFLOW_OFF: | |
8052502f | 585 | output_data(" OFF"); break; |
b7080c8e | 586 | case LFLOW_ON: |
8052502f | 587 | output_data(" ON"); break; |
b7080c8e | 588 | case LFLOW_RESTART_ANY: |
8052502f | 589 | output_data(" RESTART-ANY"); break; |
b7080c8e | 590 | case LFLOW_RESTART_XON: |
8052502f | 591 | output_data(" RESTART-XON"); break; |
b7080c8e | 592 | default: |
8052502f | 593 | output_data(" %d (unknown)", pointer[1]); |
b7080c8e | 594 | } |
b7080c8e | 595 | for (i = 2; i < length; i++) { |
8052502f | 596 | output_data(" ?%d?", pointer[i]); |
b7080c8e A |
597 | } |
598 | break; | |
599 | ||
600 | case TELOPT_NAWS: | |
8052502f | 601 | output_data("NAWS"); |
b7080c8e | 602 | if (length < 2) { |
8052502f | 603 | output_data(" (empty suboption??\?)"); |
b7080c8e A |
604 | break; |
605 | } | |
606 | if (length == 2) { | |
8052502f | 607 | output_data(" ?%d?", pointer[1]); |
b7080c8e A |
608 | break; |
609 | } | |
8052502f | 610 | output_data(" %d %d (%d)", |
b7080c8e A |
611 | pointer[1], pointer[2], |
612 | (int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2]))); | |
b7080c8e | 613 | if (length == 4) { |
8052502f | 614 | output_data(" ?%d?", pointer[3]); |
b7080c8e A |
615 | break; |
616 | } | |
8052502f | 617 | output_data(" %d %d (%d)", |
b7080c8e A |
618 | pointer[3], pointer[4], |
619 | (int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4]))); | |
b7080c8e | 620 | for (i = 5; i < length; i++) { |
8052502f | 621 | output_data(" ?%d?", pointer[i]); |
b7080c8e A |
622 | } |
623 | break; | |
624 | ||
625 | case TELOPT_LINEMODE: | |
8052502f | 626 | output_data("LINEMODE "); |
b7080c8e | 627 | if (length < 2) { |
8052502f | 628 | output_data(" (empty suboption??\?)"); |
b7080c8e A |
629 | break; |
630 | } | |
631 | switch (pointer[1]) { | |
632 | case WILL: | |
8052502f | 633 | output_data("WILL "); |
b7080c8e A |
634 | goto common; |
635 | case WONT: | |
8052502f | 636 | output_data("WONT "); |
b7080c8e A |
637 | goto common; |
638 | case DO: | |
8052502f | 639 | output_data("DO "); |
b7080c8e A |
640 | goto common; |
641 | case DONT: | |
8052502f | 642 | output_data("DONT "); |
b7080c8e | 643 | common: |
b7080c8e | 644 | if (length < 3) { |
8052502f | 645 | output_data("(no option??\?)"); |
b7080c8e A |
646 | break; |
647 | } | |
648 | switch (pointer[2]) { | |
649 | case LM_FORWARDMASK: | |
8052502f | 650 | output_data("Forward Mask"); |
b7080c8e | 651 | for (i = 3; i < length; i++) { |
8052502f | 652 | output_data(" %x", pointer[i]); |
b7080c8e A |
653 | } |
654 | break; | |
655 | default: | |
8052502f | 656 | output_data("%d (unknown)", pointer[2]); |
b7080c8e | 657 | for (i = 3; i < length; i++) { |
8052502f | 658 | output_data(" %d", pointer[i]); |
b7080c8e A |
659 | } |
660 | break; | |
661 | } | |
662 | break; | |
663 | ||
664 | case LM_SLC: | |
8052502f | 665 | output_data("SLC"); |
b7080c8e A |
666 | for (i = 2; i < length - 2; i += 3) { |
667 | if (SLC_NAME_OK(pointer[i+SLC_FUNC])) | |
8052502f | 668 | output_data(" %s", SLC_NAME(pointer[i+SLC_FUNC])); |
b7080c8e | 669 | else |
8052502f | 670 | output_data(" %d", pointer[i+SLC_FUNC]); |
b7080c8e A |
671 | switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) { |
672 | case SLC_NOSUPPORT: | |
8052502f | 673 | output_data(" NOSUPPORT"); break; |
b7080c8e | 674 | case SLC_CANTCHANGE: |
8052502f | 675 | output_data(" CANTCHANGE"); break; |
b7080c8e | 676 | case SLC_VARIABLE: |
8052502f | 677 | output_data(" VARIABLE"); break; |
b7080c8e | 678 | case SLC_DEFAULT: |
8052502f | 679 | output_data(" DEFAULT"); break; |
b7080c8e | 680 | } |
8052502f | 681 | output_data("%s%s%s", |
b7080c8e A |
682 | pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "", |
683 | pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "", | |
684 | pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : ""); | |
b7080c8e A |
685 | if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN| |
686 | SLC_FLUSHOUT| SLC_LEVELBITS)) { | |
8052502f | 687 | output_data("(0x%x)", pointer[i+SLC_FLAGS]); |
b7080c8e | 688 | } |
8052502f | 689 | output_data(" %d;", pointer[i+SLC_VALUE]); |
b7080c8e A |
690 | if ((pointer[i+SLC_VALUE] == IAC) && |
691 | (pointer[i+SLC_VALUE+1] == IAC)) | |
692 | i++; | |
693 | } | |
694 | for (; i < length; i++) { | |
8052502f | 695 | output_data(" ?%d?", pointer[i]); |
b7080c8e A |
696 | } |
697 | break; | |
698 | ||
699 | case LM_MODE: | |
8052502f | 700 | output_data("MODE "); |
b7080c8e | 701 | if (length < 3) { |
8052502f | 702 | output_data("(no mode??\?)"); |
b7080c8e A |
703 | break; |
704 | } | |
705 | { | |
706 | char tbuf[32]; | |
707 | sprintf(tbuf, "%s%s%s%s%s", | |
708 | pointer[2]&MODE_EDIT ? "|EDIT" : "", | |
709 | pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "", | |
710 | pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "", | |
711 | pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "", | |
712 | pointer[2]&MODE_ACK ? "|ACK" : ""); | |
8052502f | 713 | output_data("%s", tbuf[1] ? &tbuf[1] : "0"); |
b7080c8e A |
714 | } |
715 | if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) { | |
8052502f | 716 | output_data(" (0x%x)", pointer[2]); |
b7080c8e A |
717 | } |
718 | for (i = 3; i < length; i++) { | |
8052502f | 719 | output_data(" ?0x%x?", pointer[i]); |
b7080c8e A |
720 | } |
721 | break; | |
722 | default: | |
8052502f | 723 | output_data("%d (unknown)", pointer[1]); |
b7080c8e | 724 | for (i = 2; i < length; i++) { |
8052502f | 725 | output_data(" %d", pointer[i]); |
b7080c8e A |
726 | } |
727 | } | |
728 | break; | |
729 | ||
730 | case TELOPT_STATUS: { | |
731 | register char *cp; | |
732 | register int j, k; | |
733 | ||
8052502f | 734 | output_data("STATUS"); |
b7080c8e A |
735 | |
736 | switch (pointer[1]) { | |
737 | default: | |
738 | if (pointer[1] == TELQUAL_SEND) | |
8052502f | 739 | output_data(" SEND"); |
b7080c8e | 740 | else |
8052502f | 741 | output_data(" %d (unknown)", pointer[1]); |
b7080c8e | 742 | for (i = 2; i < length; i++) { |
8052502f | 743 | output_data(" ?%d?", pointer[i]); |
b7080c8e A |
744 | } |
745 | break; | |
746 | case TELQUAL_IS: | |
8052502f | 747 | output_data(" IS\r\n"); |
b7080c8e A |
748 | |
749 | for (i = 2; i < length; i++) { | |
750 | switch(pointer[i]) { | |
751 | case DO: cp = "DO"; goto common2; | |
752 | case DONT: cp = "DONT"; goto common2; | |
753 | case WILL: cp = "WILL"; goto common2; | |
754 | case WONT: cp = "WONT"; goto common2; | |
755 | common2: | |
756 | i++; | |
757 | if (TELOPT_OK(pointer[i])) | |
8052502f | 758 | output_data(" %s %s", cp, TELOPT(pointer[i])); |
b7080c8e | 759 | else |
8052502f | 760 | output_data(" %s %d", cp, pointer[i]); |
b7080c8e | 761 | |
8052502f | 762 | output_data("\r\n"); |
b7080c8e A |
763 | break; |
764 | ||
765 | case SB: | |
8052502f | 766 | output_data(" SB "); |
b7080c8e A |
767 | i++; |
768 | j = k = i; | |
769 | while (j < length) { | |
770 | if (pointer[j] == SE) { | |
771 | if (j+1 == length) | |
772 | break; | |
773 | if (pointer[j+1] == SE) | |
774 | j++; | |
775 | else | |
776 | break; | |
777 | } | |
778 | pointer[k++] = pointer[j++]; | |
779 | } | |
780 | printsub(0, &pointer[i], k - i); | |
781 | if (i < length) { | |
8052502f | 782 | output_data(" SE"); |
b7080c8e A |
783 | i = j; |
784 | } else | |
785 | i = j - 1; | |
786 | ||
8052502f | 787 | output_data("\r\n"); |
b7080c8e A |
788 | |
789 | break; | |
790 | ||
791 | default: | |
8052502f | 792 | output_data(" %d", pointer[i]); |
b7080c8e A |
793 | break; |
794 | } | |
795 | } | |
796 | break; | |
797 | } | |
798 | break; | |
799 | } | |
800 | ||
801 | case TELOPT_XDISPLOC: | |
8052502f | 802 | output_data("X-DISPLAY-LOCATION "); |
b7080c8e A |
803 | switch (pointer[1]) { |
804 | case TELQUAL_IS: | |
8052502f | 805 | output_data("IS \"%.*s\"", length-2, (char *)pointer+2); |
b7080c8e A |
806 | break; |
807 | case TELQUAL_SEND: | |
8052502f | 808 | output_data("SEND"); |
b7080c8e A |
809 | break; |
810 | default: | |
8052502f | 811 | output_data("- unknown qualifier %d (0x%x).", |
b7080c8e A |
812 | pointer[1], pointer[1]); |
813 | } | |
b7080c8e A |
814 | break; |
815 | ||
816 | case TELOPT_NEW_ENVIRON: | |
8052502f | 817 | output_data("NEW-ENVIRON "); |
b7080c8e A |
818 | goto env_common1; |
819 | case TELOPT_OLD_ENVIRON: | |
8052502f | 820 | output_data("OLD-ENVIRON"); |
b7080c8e | 821 | env_common1: |
b7080c8e A |
822 | switch (pointer[1]) { |
823 | case TELQUAL_IS: | |
8052502f | 824 | output_data("IS "); |
b7080c8e A |
825 | goto env_common; |
826 | case TELQUAL_SEND: | |
8052502f | 827 | output_data("SEND "); |
b7080c8e A |
828 | goto env_common; |
829 | case TELQUAL_INFO: | |
8052502f | 830 | output_data("INFO "); |
b7080c8e | 831 | env_common: |
b7080c8e A |
832 | { |
833 | register int noquote = 2; | |
834 | for (i = 2; i < length; i++ ) { | |
835 | switch (pointer[i]) { | |
836 | case NEW_ENV_VAR: | |
8052502f | 837 | output_data("\" VAR " + noquote); |
b7080c8e A |
838 | noquote = 2; |
839 | break; | |
840 | ||
841 | case NEW_ENV_VALUE: | |
8052502f | 842 | output_data("\" VALUE " + noquote); |
b7080c8e A |
843 | noquote = 2; |
844 | break; | |
845 | ||
846 | case ENV_ESC: | |
8052502f | 847 | output_data("\" ESC " + noquote); |
b7080c8e A |
848 | noquote = 2; |
849 | break; | |
850 | ||
851 | case ENV_USERVAR: | |
8052502f | 852 | output_data("\" USERVAR " + noquote); |
b7080c8e A |
853 | noquote = 2; |
854 | break; | |
855 | ||
856 | default: | |
b7080c8e A |
857 | if (isprint(pointer[i]) && pointer[i] != '"') { |
858 | if (noquote) { | |
8052502f | 859 | output_data("\""); |
b7080c8e A |
860 | noquote = 0; |
861 | } | |
8052502f | 862 | output_data("%c", pointer[i]); |
b7080c8e | 863 | } else { |
8052502f | 864 | output_data("\" %03o " + noquote, |
b7080c8e | 865 | pointer[i]); |
b7080c8e A |
866 | noquote = 2; |
867 | } | |
868 | break; | |
869 | } | |
870 | } | |
871 | if (!noquote) | |
8052502f | 872 | output_data("\""); |
b7080c8e A |
873 | break; |
874 | } | |
875 | } | |
876 | break; | |
877 | ||
878 | #if defined(AUTHENTICATION) | |
879 | case TELOPT_AUTHENTICATION: | |
8052502f | 880 | output_data("AUTHENTICATION"); |
b7080c8e A |
881 | |
882 | if (length < 2) { | |
8052502f | 883 | output_data(" (empty suboption??\?)"); |
b7080c8e A |
884 | break; |
885 | } | |
886 | switch (pointer[1]) { | |
887 | case TELQUAL_REPLY: | |
888 | case TELQUAL_IS: | |
8052502f | 889 | output_data(" %s ", (pointer[1] == TELQUAL_IS) ? |
b7080c8e | 890 | "IS" : "REPLY"); |
b7080c8e | 891 | if (AUTHTYPE_NAME_OK(pointer[2])) |
8052502f | 892 | output_data("%s ", AUTHTYPE_NAME(pointer[2])); |
b7080c8e | 893 | else |
8052502f | 894 | output_data("%d ", pointer[2]); |
b7080c8e | 895 | if (length < 3) { |
8052502f | 896 | output_data("(partial suboption??\?)"); |
b7080c8e A |
897 | break; |
898 | } | |
8052502f | 899 | output_data("%s|%s", |
b7080c8e A |
900 | ((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ? |
901 | "CLIENT" : "SERVER", | |
902 | ((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? | |
903 | "MUTUAL" : "ONE-WAY"); | |
b7080c8e | 904 | |
8052502f A |
905 | { |
906 | char buf[512]; | |
907 | auth_printsub(&pointer[1], length - 1, buf, sizeof(buf)); | |
908 | output_data("%s", buf); | |
909 | } | |
b7080c8e A |
910 | break; |
911 | ||
912 | case TELQUAL_SEND: | |
913 | i = 2; | |
8052502f | 914 | output_data(" SEND "); |
b7080c8e A |
915 | while (i < length) { |
916 | if (AUTHTYPE_NAME_OK(pointer[i])) | |
8052502f | 917 | output_data("%s ", AUTHTYPE_NAME(pointer[i])); |
b7080c8e | 918 | else |
8052502f | 919 | output_data("%d ", pointer[i]); |
b7080c8e | 920 | if (++i >= length) { |
8052502f | 921 | output_data("(partial suboption??\?)"); |
b7080c8e A |
922 | break; |
923 | } | |
8052502f | 924 | output_data("%s|%s ", |
b7080c8e A |
925 | ((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ? |
926 | "CLIENT" : "SERVER", | |
927 | ((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? | |
928 | "MUTUAL" : "ONE-WAY"); | |
b7080c8e A |
929 | ++i; |
930 | } | |
931 | break; | |
932 | ||
933 | case TELQUAL_NAME: | |
8052502f | 934 | output_data(" NAME \"%.*s\"", length - 2, pointer + 2); |
b7080c8e A |
935 | break; |
936 | ||
937 | default: | |
938 | for (i = 2; i < length; i++) { | |
8052502f | 939 | output_data(" ?%d?", pointer[i]); |
b7080c8e A |
940 | } |
941 | break; | |
942 | } | |
943 | break; | |
944 | #endif | |
945 | ||
b7080c8e A |
946 | |
947 | default: | |
948 | if (TELOPT_OK(pointer[0])) | |
8052502f | 949 | output_data("%s (unknown)", TELOPT(pointer[0])); |
b7080c8e | 950 | else |
8052502f | 951 | output_data("%d (unknown)", pointer[i]); |
b7080c8e | 952 | for (i = 1; i < length; i++) { |
8052502f | 953 | output_data(" %d", pointer[i]); |
b7080c8e A |
954 | } |
955 | break; | |
956 | } | |
8052502f | 957 | output_data("\r\n"); |
b7080c8e A |
958 | } |
959 | ||
960 | /* | |
961 | * Dump a data buffer in hex and ascii to the output data stream. | |
962 | */ | |
963 | void | |
964 | printdata(tag, ptr, cnt) | |
965 | register char *tag; | |
966 | register char *ptr; | |
967 | register int cnt; | |
968 | { | |
969 | register int i; | |
970 | char xbuf[30]; | |
971 | ||
972 | while (cnt) { | |
973 | /* flush net output buffer if no room for new data) */ | |
974 | if ((&netobuf[BUFSIZ] - nfrontp) < 80) { | |
975 | netflush(); | |
976 | } | |
977 | ||
978 | /* add a line of output */ | |
8052502f | 979 | output_data("%s: ", tag); |
b7080c8e | 980 | for (i = 0; i < 20 && cnt; i++) { |
8052502f | 981 | output_data("%02x", *ptr); |
b7080c8e A |
982 | if (isprint(*ptr)) { |
983 | xbuf[i] = *ptr; | |
984 | } else { | |
985 | xbuf[i] = '.'; | |
986 | } | |
987 | if (i % 2) { | |
8052502f | 988 | output_data(" "); |
b7080c8e A |
989 | } |
990 | cnt--; | |
991 | ptr++; | |
992 | } | |
993 | xbuf[i] = '\0'; | |
8052502f | 994 | output_data(" %s\r\n", xbuf ); |
b7080c8e A |
995 | } |
996 | } | |
997 | #endif /* DIAGNOSTICS */ |