|
1 // utility.c |
|
2 // |
|
3 // © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved. |
|
4 // |
|
5 /* |
|
6 * Copyright (c) 1989, 1993 |
|
7 * The Regents of the University of California. All rights reserved. |
|
8 * |
|
9 * Redistribution and use in source and binary forms, with or without |
|
10 * modification, are permitted provided that the following conditions |
|
11 * are met: |
|
12 * 1. Redistributions of source code must retain the above copyright |
|
13 * notice, this list of conditions and the following disclaimer. |
|
14 * 2. Redistributions in binary form must reproduce the above copyright |
|
15 * notice, this list of conditions and the following disclaimer in the |
|
16 * documentation and/or other materials provided with the distribution. |
|
17 * 3. 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 #define PRINTOPTIONS |
|
35 #include "telnetd.h" |
|
36 |
|
37 #ifndef __SYMBIAN32__ |
|
38 __RCSID("$Heimdal: utility.c,v 1.27 2001/09/03 05:54:17 assar Exp $" |
|
39 "$NetBSD: utility.c,v 1.2 2003/08/07 09:15:30 agc Exp $"); |
|
40 #endif |
|
41 /* |
|
42 * utility functions performing io related tasks |
|
43 */ |
|
44 |
|
45 /* |
|
46 * ttloop |
|
47 * |
|
48 * A small subroutine to flush the network output buffer, get some |
|
49 * data from the network, and pass it through the telnet state |
|
50 * machine. We also flush the pty input buffer (by dropping its data) |
|
51 * if it becomes too full. |
|
52 * |
|
53 * return 0 if OK or 1 if interrupted by a signal. |
|
54 */ |
|
55 |
|
56 int |
|
57 ttloop(void) |
|
58 { |
|
59 DIAG(TD_REPORT, { |
|
60 output_data("td: ttloop\r\n"); |
|
61 }); |
|
62 |
|
63 if (nfrontp-nbackp) |
|
64 netflush(); |
|
65 |
|
66 ncc = read(net, netibuf, sizeof netibuf); |
|
67 if (ncc < 0) { |
|
68 if (errno == EINTR) |
|
69 return 1; |
|
70 #ifndef __SYMBIAN32__ |
|
71 syslog(LOG_INFO, "ttloop: read: %m\n"); |
|
72 #endif |
|
73 exit(1); |
|
74 } else if (ncc == 0) { |
|
75 #ifndef __SYMBIAN32__ |
|
76 syslog(LOG_INFO, "ttloop: peer died\n"); |
|
77 #endif |
|
78 exit(1); |
|
79 } |
|
80 DIAG(TD_REPORT, { |
|
81 output_data("td: ttloop read %d chars\r\n", ncc); |
|
82 }); |
|
83 netip = netibuf; |
|
84 telrcv(); /* state machine */ |
|
85 if (ncc > 0) { |
|
86 pfrontp = pbackp = ptyobuf; |
|
87 telrcv(); |
|
88 } |
|
89 return 0; |
|
90 } /* end of ttloop */ |
|
91 |
|
92 /* |
|
93 * Check a descriptor to see if out of band data exists on it. |
|
94 */ |
|
95 int |
|
96 stilloob(int s) |
|
97 { |
|
98 static struct timeval timeout = { 0 }; |
|
99 fd_set excepts; |
|
100 int value; |
|
101 |
|
102 if (s >= FD_SETSIZE) |
|
103 fatal(ourpty, "fd too large"); |
|
104 |
|
105 do { |
|
106 FD_ZERO(&excepts); |
|
107 FD_SET(s, &excepts); |
|
108 value = select(s+1, 0, 0, &excepts, &timeout); |
|
109 } while ((value == -1) && (errno == EINTR)); |
|
110 |
|
111 if (value < 0) { |
|
112 fatalperror(ourpty, "select"); |
|
113 } |
|
114 if (FD_ISSET(s, &excepts)) { |
|
115 return 1; |
|
116 } else { |
|
117 return 0; |
|
118 } |
|
119 } |
|
120 |
|
121 void |
|
122 ptyflush(void) |
|
123 { |
|
124 int n; |
|
125 |
|
126 if ((n = pfrontp - pbackp) > 0) { |
|
127 DIAG((TD_REPORT | TD_PTYDATA), { |
|
128 output_data("td: ptyflush %d chars\r\n", n); |
|
129 }); |
|
130 DIAG(TD_PTYDATA, printdata("pd", pbackp, n)); |
|
131 n = write(ourpty, pbackp, n); |
|
132 } |
|
133 if (n < 0) { |
|
134 if (errno == EWOULDBLOCK || errno == EINTR) |
|
135 return; |
|
136 cleanup(0); |
|
137 } |
|
138 pbackp += n; |
|
139 if (pbackp == pfrontp) |
|
140 pbackp = pfrontp = ptyobuf; |
|
141 } |
|
142 |
|
143 /* |
|
144 * nextitem() |
|
145 * |
|
146 * Return the address of the next "item" in the TELNET data |
|
147 * stream. This will be the address of the next character if |
|
148 * the current address is a user data character, or it will |
|
149 * be the address of the character following the TELNET command |
|
150 * if the current address is a TELNET IAC ("I Am a Command") |
|
151 * character. |
|
152 */ |
|
153 char * |
|
154 nextitem(char *current) |
|
155 { |
|
156 if ((*current&0xff) != IAC) { |
|
157 return current+1; |
|
158 } |
|
159 switch (*(current+1)&0xff) { |
|
160 case DO: |
|
161 case DONT: |
|
162 case WILL: |
|
163 case WONT: |
|
164 return current+3; |
|
165 case SB:{ |
|
166 /* loop forever looking for the SE */ |
|
167 char *look = current+2; |
|
168 |
|
169 for (;;) { |
|
170 if ((*look++&0xff) == IAC) { |
|
171 if ((*look++&0xff) == SE) { |
|
172 return look; |
|
173 } |
|
174 } |
|
175 } |
|
176 } |
|
177 default: |
|
178 return current+2; |
|
179 } |
|
180 } |
|
181 |
|
182 |
|
183 /* |
|
184 * netclear() |
|
185 * |
|
186 * We are about to do a TELNET SYNCH operation. Clear |
|
187 * the path to the network. |
|
188 * |
|
189 * Things are a bit tricky since we may have sent the first |
|
190 * byte or so of a previous TELNET command into the network. |
|
191 * So, we have to scan the network buffer from the beginning |
|
192 * until we are up to where we want to be. |
|
193 * |
|
194 * A side effect of what we do, just to keep things |
|
195 * simple, is to clear the urgent data pointer. The principal |
|
196 * caller should be setting the urgent data pointer AFTER calling |
|
197 * us in any case. |
|
198 */ |
|
199 void |
|
200 netclear(void) |
|
201 { |
|
202 char *thisitem, *next; |
|
203 char *good; |
|
204 #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ |
|
205 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) |
|
206 |
|
207 #ifdef ENCRYPTION |
|
208 thisitem = nclearto > netobuf ? nclearto : netobuf; |
|
209 #else |
|
210 thisitem = netobuf; |
|
211 #endif |
|
212 |
|
213 while ((next = nextitem(thisitem)) <= nbackp) { |
|
214 thisitem = next; |
|
215 } |
|
216 |
|
217 /* Now, thisitem is first before/at boundary. */ |
|
218 |
|
219 #ifdef ENCRYPTION |
|
220 good = nclearto > netobuf ? nclearto : netobuf; |
|
221 #else |
|
222 good = netobuf; /* where the good bytes go */ |
|
223 #endif |
|
224 |
|
225 while (nfrontp > thisitem) { |
|
226 if (wewant(thisitem)) { |
|
227 int length; |
|
228 |
|
229 next = thisitem; |
|
230 do { |
|
231 next = nextitem(next); |
|
232 } while (wewant(next) && (nfrontp > next)); |
|
233 length = next-thisitem; |
|
234 memmove(good, thisitem, length); |
|
235 good += length; |
|
236 thisitem = next; |
|
237 } else { |
|
238 thisitem = nextitem(thisitem); |
|
239 } |
|
240 } |
|
241 |
|
242 nbackp = netobuf; |
|
243 nfrontp = good; /* next byte to be sent */ |
|
244 neturg = 0; |
|
245 } /* end of netclear */ |
|
246 |
|
247 extern int not42; |
|
248 |
|
249 /* |
|
250 * netflush |
|
251 * Send as much data as possible to the network, |
|
252 * handling requests for urgent data. |
|
253 */ |
|
254 void |
|
255 netflush(void) |
|
256 { |
|
257 int n; |
|
258 |
|
259 if ((n = nfrontp - nbackp) > 0) { |
|
260 DIAG(TD_REPORT, |
|
261 { n += output_data("td: netflush %d chars\r\n", n); |
|
262 }); |
|
263 #ifdef ENCRYPTION |
|
264 if (encrypt_output) { |
|
265 char *s = nclearto ? nclearto : nbackp; |
|
266 if (nfrontp - s > 0) { |
|
267 (*encrypt_output)((unsigned char *)s, nfrontp-s); |
|
268 nclearto = nfrontp; |
|
269 } |
|
270 } |
|
271 #endif |
|
272 /* |
|
273 * if no urgent data, or if the other side appears to be an |
|
274 * old 4.2 client (and thus unable to survive TCP urgent data), |
|
275 * write the entire buffer in non-OOB mode. |
|
276 */ |
|
277 #if 1 /* remove this to make it work between solaris 2.6 and linux */ |
|
278 if ((neturg == 0) || (not42 == 0)) { |
|
279 #endif |
|
280 n = write(net, nbackp, n); /* normal write */ |
|
281 #if 1 /* remove this to make it work between solaris 2.6 and linux */ |
|
282 } else { |
|
283 n = neturg - nbackp; |
|
284 /* |
|
285 * In 4.2 (and 4.3) systems, there is some question about |
|
286 * what byte in a sendOOB operation is the "OOB" data. |
|
287 * To make ourselves compatible, we only send ONE byte |
|
288 * out of band, the one WE THINK should be OOB (though |
|
289 * we really have more the TCP philosophy of urgent data |
|
290 * rather than the Unix philosophy of OOB data). |
|
291 */ |
|
292 if (n > 1) { |
|
293 n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ |
|
294 } else { |
|
295 n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ |
|
296 } |
|
297 } |
|
298 #endif |
|
299 } |
|
300 if (n < 0) { |
|
301 if (errno == EWOULDBLOCK || errno == EINTR) |
|
302 return; |
|
303 cleanup(0); |
|
304 } |
|
305 nbackp += n; |
|
306 #ifdef ENCRYPTION |
|
307 if (nbackp > nclearto) |
|
308 nclearto = 0; |
|
309 #endif |
|
310 if (nbackp >= neturg) { |
|
311 neturg = 0; |
|
312 } |
|
313 if (nbackp == nfrontp) { |
|
314 nbackp = nfrontp = netobuf; |
|
315 #ifdef ENCRYPTION |
|
316 nclearto = 0; |
|
317 #endif |
|
318 } |
|
319 return; |
|
320 } |
|
321 |
|
322 |
|
323 /* |
|
324 * writenet |
|
325 * |
|
326 * Just a handy little function to write a bit of raw data to the net. |
|
327 * It will force a transmit of the buffer if necessary |
|
328 * |
|
329 * arguments |
|
330 * ptr - A pointer to a character string to write |
|
331 * len - How many bytes to write |
|
332 */ |
|
333 void |
|
334 writenet(unsigned char *ptr, int len) |
|
335 { |
|
336 /* flush buffer if no room for new data) */ |
|
337 while ((&netobuf[BUFSIZ] - nfrontp) < len) { |
|
338 /* if this fails, don't worry, buffer is a little big */ |
|
339 netflush(); |
|
340 } |
|
341 |
|
342 memmove(nfrontp, ptr, len); |
|
343 nfrontp += len; |
|
344 } |
|
345 |
|
346 |
|
347 /* |
|
348 * miscellaneous functions doing a variety of little jobs follow ... |
|
349 */ |
|
350 |
|
351 |
|
352 void fatal(int f, char *msg) |
|
353 { |
|
354 char buf[BUFSIZ]; |
|
355 |
|
356 snprintf(buf, sizeof(buf), "telnetd: %s.\r\n", msg); |
|
357 #ifdef ENCRYPTION |
|
358 if (encrypt_output) { |
|
359 /* |
|
360 * Better turn off encryption first.... |
|
361 * Hope it flushes... |
|
362 */ |
|
363 encrypt_send_end(); |
|
364 netflush(); |
|
365 } |
|
366 #endif |
|
367 write(f, buf, (int)strlen(buf)); |
|
368 sleep(1); /*XXX*/ |
|
369 exit(1); |
|
370 } |
|
371 |
|
372 void |
|
373 fatalperror_errno(int f, const char *msg, int error) |
|
374 { |
|
375 char buf[BUFSIZ]; |
|
376 |
|
377 snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(error)); |
|
378 fatal(f, buf); |
|
379 } |
|
380 |
|
381 void |
|
382 fatalperror(int f, const char *msg) |
|
383 { |
|
384 fatalperror_errno(f, msg, errno); |
|
385 } |
|
386 |
|
387 char editedhost[32]; |
|
388 |
|
389 void edithost(char *pat, char *host) |
|
390 { |
|
391 char *res = editedhost; |
|
392 |
|
393 if (!pat) |
|
394 pat = ""; |
|
395 while (*pat) { |
|
396 switch (*pat) { |
|
397 |
|
398 case '#': |
|
399 if (*host) |
|
400 host++; |
|
401 break; |
|
402 |
|
403 case '@': |
|
404 if (*host) |
|
405 *res++ = *host++; |
|
406 break; |
|
407 |
|
408 default: |
|
409 *res++ = *pat; |
|
410 break; |
|
411 } |
|
412 if (res == &editedhost[sizeof editedhost - 1]) { |
|
413 *res = '\0'; |
|
414 return; |
|
415 } |
|
416 pat++; |
|
417 } |
|
418 if (*host) |
|
419 strlcpy (res, host, |
|
420 sizeof editedhost - (res - editedhost)); |
|
421 else |
|
422 *res = '\0'; |
|
423 editedhost[sizeof editedhost - 1] = '\0'; |
|
424 } |
|
425 |
|
426 static char *putlocation; |
|
427 |
|
428 void |
|
429 putstr(char *s) |
|
430 { |
|
431 |
|
432 while (*s) |
|
433 putchr(*s++); |
|
434 } |
|
435 |
|
436 void |
|
437 putchr(int cc) |
|
438 { |
|
439 *putlocation++ = cc; |
|
440 } |
|
441 |
|
442 /* |
|
443 * This is split on two lines so that SCCS will not see the M |
|
444 * between two % signs and expand it... |
|
445 */ |
|
446 static char fmtstr[] = { "%l:%M" "%P on %A, %d %B %Y" }; |
|
447 |
|
448 void putf(char *cp, char *where) |
|
449 { |
|
450 #ifdef HAVE_UNAME |
|
451 struct utsname name; |
|
452 #endif |
|
453 char *slash; |
|
454 time_t t; |
|
455 char db[100]; |
|
456 |
|
457 /* if we don't have uname, set these to sensible values */ |
|
458 char *sysname = "Unix", |
|
459 *machine = "", |
|
460 *release = "", |
|
461 *version = ""; |
|
462 |
|
463 #ifdef HAVE_UNAME |
|
464 uname(&name); |
|
465 sysname=name.sysname; |
|
466 machine=name.machine; |
|
467 release=name.release; |
|
468 version=name.version; |
|
469 #endif |
|
470 |
|
471 putlocation = where; |
|
472 |
|
473 while (*cp) { |
|
474 if (*cp != '%') { |
|
475 putchr(*cp++); |
|
476 continue; |
|
477 } |
|
478 switch (*++cp) { |
|
479 |
|
480 case 't': |
|
481 #ifdef STREAMSPTY |
|
482 /* names are like /dev/pts/2 -- we want pts/2 */ |
|
483 slash = strchr(line+1, '/'); |
|
484 #else |
|
485 slash = strrchr(line, '/'); |
|
486 #endif |
|
487 if (slash == (char *) 0) |
|
488 putstr(line); |
|
489 else |
|
490 putstr(&slash[1]); |
|
491 break; |
|
492 |
|
493 case 'h': |
|
494 putstr(editedhost); |
|
495 break; |
|
496 |
|
497 case 's': |
|
498 putstr(sysname); |
|
499 break; |
|
500 |
|
501 case 'm': |
|
502 putstr(machine); |
|
503 break; |
|
504 |
|
505 case 'r': |
|
506 putstr(release); |
|
507 break; |
|
508 |
|
509 case 'v': |
|
510 putstr(version); |
|
511 break; |
|
512 |
|
513 case 'd': |
|
514 time(&t); |
|
515 strftime(db, sizeof(db), fmtstr, localtime(&t)); |
|
516 putstr(db); |
|
517 break; |
|
518 |
|
519 case '%': |
|
520 putchr('%'); |
|
521 break; |
|
522 } |
|
523 cp++; |
|
524 } |
|
525 } |
|
526 |
|
527 #ifdef DIAGNOSTICS |
|
528 /* |
|
529 * Print telnet options and commands in plain text, if possible. |
|
530 */ |
|
531 void |
|
532 printoption(char *fmt, int option) |
|
533 { |
|
534 if (TELOPT_OK(option)) |
|
535 output_data("%s %s\r\n", |
|
536 fmt, |
|
537 TELOPT(option)); |
|
538 else if (TELCMD_OK(option)) |
|
539 output_data("%s %s\r\n", |
|
540 fmt, |
|
541 TELCMD(option)); |
|
542 else |
|
543 output_data("%s %d\r\n", |
|
544 fmt, |
|
545 option); |
|
546 return; |
|
547 } |
|
548 |
|
549 void |
|
550 printsub(int direction, unsigned char *pointer, int length) |
|
551 /* '<' or '>' */ |
|
552 /* where suboption data sits */ |
|
553 /* length of suboption data */ |
|
554 { |
|
555 int i = 0; |
|
556 unsigned char buf[512]; |
|
557 |
|
558 if (!(diagnostic & TD_OPTIONS)) |
|
559 return; |
|
560 |
|
561 if (direction) { |
|
562 output_data("td: %s suboption ", |
|
563 direction == '<' ? "recv" : "send"); |
|
564 if (length >= 3) { |
|
565 int j; |
|
566 |
|
567 i = pointer[length-2]; |
|
568 j = pointer[length-1]; |
|
569 |
|
570 if (i != IAC || j != SE) { |
|
571 output_data("(terminated by "); |
|
572 if (TELOPT_OK(i)) |
|
573 output_data("%s ", |
|
574 TELOPT(i)); |
|
575 else if (TELCMD_OK(i)) |
|
576 output_data("%s ", |
|
577 TELCMD(i)); |
|
578 else |
|
579 output_data("%d ", |
|
580 i); |
|
581 if (TELOPT_OK(j)) |
|
582 output_data("%s", |
|
583 TELOPT(j)); |
|
584 else if (TELCMD_OK(j)) |
|
585 output_data("%s", |
|
586 TELCMD(j)); |
|
587 else |
|
588 output_data("%d", |
|
589 j); |
|
590 output_data(", not IAC SE!) "); |
|
591 } |
|
592 } |
|
593 length -= 2; |
|
594 } |
|
595 if (length < 1) { |
|
596 output_data("(Empty suboption??\?)"); |
|
597 return; |
|
598 } |
|
599 switch (pointer[0]) { |
|
600 case TELOPT_TTYPE: |
|
601 output_data("TERMINAL-TYPE "); |
|
602 switch (pointer[1]) { |
|
603 case TELQUAL_IS: |
|
604 output_data("IS \"%.*s\"", |
|
605 length-2, |
|
606 (char *)pointer+2); |
|
607 break; |
|
608 case TELQUAL_SEND: |
|
609 output_data("SEND"); |
|
610 break; |
|
611 default: |
|
612 output_data("- unknown qualifier %d (0x%x).", |
|
613 pointer[1], pointer[1]); |
|
614 } |
|
615 break; |
|
616 case TELOPT_TSPEED: |
|
617 output_data("TERMINAL-SPEED"); |
|
618 if (length < 2) { |
|
619 output_data(" (empty suboption??\?)"); |
|
620 break; |
|
621 } |
|
622 switch (pointer[1]) { |
|
623 case TELQUAL_IS: |
|
624 output_data(" IS %.*s", length-2, (char *)pointer+2); |
|
625 break; |
|
626 default: |
|
627 if (pointer[1] == 1) |
|
628 output_data(" SEND"); |
|
629 else |
|
630 output_data(" %d (unknown)", pointer[1]); |
|
631 for (i = 2; i < length; i++) { |
|
632 output_data(" ?%d?", pointer[i]); |
|
633 } |
|
634 break; |
|
635 } |
|
636 break; |
|
637 |
|
638 case TELOPT_LFLOW: |
|
639 output_data("TOGGLE-FLOW-CONTROL"); |
|
640 if (length < 2) { |
|
641 output_data(" (empty suboption??\?)"); |
|
642 break; |
|
643 } |
|
644 switch (pointer[1]) { |
|
645 case LFLOW_OFF: |
|
646 output_data(" OFF"); |
|
647 break; |
|
648 case LFLOW_ON: |
|
649 output_data(" ON"); |
|
650 break; |
|
651 case LFLOW_RESTART_ANY: |
|
652 output_data(" RESTART-ANY"); |
|
653 break; |
|
654 case LFLOW_RESTART_XON: |
|
655 output_data(" RESTART-XON"); |
|
656 break; |
|
657 default: |
|
658 output_data(" %d (unknown)", |
|
659 pointer[1]); |
|
660 } |
|
661 for (i = 2; i < length; i++) { |
|
662 output_data(" ?%d?", |
|
663 pointer[i]); |
|
664 } |
|
665 break; |
|
666 |
|
667 case TELOPT_NAWS: |
|
668 output_data("NAWS"); |
|
669 if (length < 2) { |
|
670 output_data(" (empty suboption??\?)"); |
|
671 break; |
|
672 } |
|
673 if (length == 2) { |
|
674 output_data(" ?%d?", |
|
675 pointer[1]); |
|
676 break; |
|
677 } |
|
678 output_data(" %u %u(%u)", |
|
679 pointer[1], |
|
680 pointer[2], |
|
681 (((unsigned int)pointer[1])<<8) + pointer[2]); |
|
682 if (length == 4) { |
|
683 output_data(" ?%d?", |
|
684 pointer[3]); |
|
685 break; |
|
686 } |
|
687 output_data(" %u %u(%u)", |
|
688 pointer[3], |
|
689 pointer[4], |
|
690 (((unsigned int)pointer[3])<<8) + pointer[4]); |
|
691 for (i = 5; i < length; i++) { |
|
692 output_data(" ?%d?", |
|
693 pointer[i]); |
|
694 } |
|
695 break; |
|
696 |
|
697 case TELOPT_LINEMODE: |
|
698 output_data("LINEMODE "); |
|
699 if (length < 2) { |
|
700 output_data(" (empty suboption??\?)"); |
|
701 break; |
|
702 } |
|
703 switch (pointer[1]) { |
|
704 case WILL: |
|
705 output_data("WILL "); |
|
706 goto common; |
|
707 case WONT: |
|
708 output_data("WONT "); |
|
709 goto common; |
|
710 case DO: |
|
711 output_data("DO "); |
|
712 goto common; |
|
713 case DONT: |
|
714 output_data("DONT "); |
|
715 common: |
|
716 if (length < 3) { |
|
717 output_data("(no option??\?)"); |
|
718 break; |
|
719 } |
|
720 switch (pointer[2]) { |
|
721 case LM_FORWARDMASK: |
|
722 output_data("Forward Mask"); |
|
723 for (i = 3; i < length; i++) { |
|
724 output_data(" %x", pointer[i]); |
|
725 } |
|
726 break; |
|
727 default: |
|
728 output_data("%d (unknown)", |
|
729 pointer[2]); |
|
730 for (i = 3; i < length; i++) { |
|
731 output_data(" %d", |
|
732 pointer[i]); |
|
733 } |
|
734 break; |
|
735 } |
|
736 break; |
|
737 |
|
738 case LM_SLC: |
|
739 output_data("SLC"); |
|
740 for (i = 2; i < length - 2; i += 3) { |
|
741 if (SLC_NAME_OK(pointer[i+SLC_FUNC])) |
|
742 output_data(" %s", |
|
743 SLC_NAME(pointer[i+SLC_FUNC])); |
|
744 else |
|
745 output_data(" %d", |
|
746 pointer[i+SLC_FUNC]); |
|
747 switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) { |
|
748 case SLC_NOSUPPORT: |
|
749 output_data(" NOSUPPORT"); |
|
750 break; |
|
751 case SLC_CANTCHANGE: |
|
752 output_data(" CANTCHANGE"); |
|
753 break; |
|
754 case SLC_VARIABLE: |
|
755 output_data(" VARIABLE"); |
|
756 break; |
|
757 case SLC_DEFAULT: |
|
758 output_data(" DEFAULT"); |
|
759 break; |
|
760 } |
|
761 output_data("%s%s%s", |
|
762 pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "", |
|
763 pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "", |
|
764 pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : ""); |
|
765 if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN| |
|
766 SLC_FLUSHOUT| SLC_LEVELBITS)) { |
|
767 output_data("(0x%x)", |
|
768 pointer[i+SLC_FLAGS]); |
|
769 } |
|
770 output_data(" %d;", |
|
771 pointer[i+SLC_VALUE]); |
|
772 if ((pointer[i+SLC_VALUE] == IAC) && |
|
773 (pointer[i+SLC_VALUE+1] == IAC)) |
|
774 i++; |
|
775 } |
|
776 for (; i < length; i++) { |
|
777 output_data(" ?%d?", |
|
778 pointer[i]); |
|
779 } |
|
780 break; |
|
781 |
|
782 case LM_MODE: |
|
783 output_data("MODE "); |
|
784 if (length < 3) { |
|
785 output_data("(no mode??\?)"); |
|
786 break; |
|
787 } |
|
788 { |
|
789 char tbuf[32]; |
|
790 snprintf(tbuf, |
|
791 sizeof(tbuf), |
|
792 "%s%s%s%s%s", |
|
793 pointer[2]&MODE_EDIT ? "|EDIT" : "", |
|
794 pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "", |
|
795 pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "", |
|
796 pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "", |
|
797 pointer[2]&MODE_ACK ? "|ACK" : ""); |
|
798 output_data("%s", |
|
799 tbuf[1] ? &tbuf[1] : "0"); |
|
800 } |
|
801 if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) { |
|
802 output_data(" (0x%x)", |
|
803 pointer[2]); |
|
804 } |
|
805 for (i = 3; i < length; i++) { |
|
806 output_data(" ?0x%x?", |
|
807 pointer[i]); |
|
808 } |
|
809 break; |
|
810 default: |
|
811 output_data("%d (unknown)", |
|
812 pointer[1]); |
|
813 for (i = 2; i < length; i++) { |
|
814 output_data(" %d", pointer[i]); |
|
815 } |
|
816 } |
|
817 break; |
|
818 |
|
819 case TELOPT_STATUS: { |
|
820 char *cp; |
|
821 int j, k; |
|
822 |
|
823 output_data("STATUS"); |
|
824 |
|
825 switch (pointer[1]) { |
|
826 default: |
|
827 if (pointer[1] == TELQUAL_SEND) |
|
828 output_data(" SEND"); |
|
829 else |
|
830 output_data(" %d (unknown)", |
|
831 pointer[1]); |
|
832 for (i = 2; i < length; i++) { |
|
833 output_data(" ?%d?", |
|
834 pointer[i]); |
|
835 } |
|
836 break; |
|
837 case TELQUAL_IS: |
|
838 output_data(" IS\r\n"); |
|
839 |
|
840 for (i = 2; i < length; i++) { |
|
841 switch(pointer[i]) { |
|
842 case DO: cp = "DO"; goto common2; |
|
843 case DONT: cp = "DONT"; goto common2; |
|
844 case WILL: cp = "WILL"; goto common2; |
|
845 case WONT: cp = "WONT"; goto common2; |
|
846 common2: |
|
847 i++; |
|
848 if (TELOPT_OK(pointer[i])) |
|
849 output_data(" %s %s", |
|
850 cp, |
|
851 TELOPT(pointer[i])); |
|
852 else |
|
853 output_data(" %s %d", |
|
854 cp, |
|
855 pointer[i]); |
|
856 |
|
857 output_data("\r\n"); |
|
858 break; |
|
859 |
|
860 case SB: |
|
861 output_data(" SB "); |
|
862 i++; |
|
863 j = k = i; |
|
864 while (j < length) { |
|
865 if (pointer[j] == SE) { |
|
866 if (j+1 == length) |
|
867 break; |
|
868 if (pointer[j+1] == SE) |
|
869 j++; |
|
870 else |
|
871 break; |
|
872 } |
|
873 pointer[k++] = pointer[j++]; |
|
874 } |
|
875 printsub(0, &pointer[i], k - i); |
|
876 if (i < length) { |
|
877 output_data(" SE"); |
|
878 i = j; |
|
879 } else |
|
880 i = j - 1; |
|
881 |
|
882 output_data("\r\n"); |
|
883 |
|
884 break; |
|
885 |
|
886 default: |
|
887 output_data(" %d", |
|
888 pointer[i]); |
|
889 break; |
|
890 } |
|
891 } |
|
892 break; |
|
893 } |
|
894 break; |
|
895 } |
|
896 |
|
897 case TELOPT_XDISPLOC: |
|
898 output_data("X-DISPLAY-LOCATION "); |
|
899 switch (pointer[1]) { |
|
900 case TELQUAL_IS: |
|
901 output_data("IS \"%.*s\"", |
|
902 length-2, |
|
903 (char *)pointer+2); |
|
904 break; |
|
905 case TELQUAL_SEND: |
|
906 output_data("SEND"); |
|
907 break; |
|
908 default: |
|
909 output_data("- unknown qualifier %d (0x%x).", |
|
910 pointer[1], pointer[1]); |
|
911 } |
|
912 break; |
|
913 |
|
914 case TELOPT_NEW_ENVIRON: |
|
915 output_data("NEW-ENVIRON "); |
|
916 goto env_common1; |
|
917 case TELOPT_OLD_ENVIRON: |
|
918 output_data("OLD-ENVIRON"); |
|
919 env_common1: |
|
920 switch (pointer[1]) { |
|
921 case TELQUAL_IS: |
|
922 output_data("IS "); |
|
923 goto env_common; |
|
924 case TELQUAL_SEND: |
|
925 output_data("SEND "); |
|
926 goto env_common; |
|
927 case TELQUAL_INFO: |
|
928 output_data("INFO "); |
|
929 env_common: |
|
930 { |
|
931 int noquote = 2; |
|
932 for (i = 2; i < length; i++ ) { |
|
933 switch (pointer[i]) { |
|
934 case NEW_ENV_VAR: |
|
935 output_data("\" VAR " + noquote); |
|
936 noquote = 2; |
|
937 break; |
|
938 |
|
939 case NEW_ENV_VALUE: |
|
940 output_data("\" VALUE " + noquote); |
|
941 noquote = 2; |
|
942 break; |
|
943 |
|
944 case ENV_ESC: |
|
945 output_data("\" ESC " + noquote); |
|
946 noquote = 2; |
|
947 break; |
|
948 |
|
949 case ENV_USERVAR: |
|
950 output_data("\" USERVAR " + noquote); |
|
951 noquote = 2; |
|
952 break; |
|
953 |
|
954 default: |
|
955 if (isprint(pointer[i]) && pointer[i] != '"') { |
|
956 if (noquote) { |
|
957 output_data ("\""); |
|
958 noquote = 0; |
|
959 } |
|
960 output_data ("%c", pointer[i]); |
|
961 } else { |
|
962 output_data("\" %03o " + noquote, |
|
963 pointer[i]); |
|
964 noquote = 2; |
|
965 } |
|
966 break; |
|
967 } |
|
968 } |
|
969 if (!noquote) |
|
970 output_data ("\""); |
|
971 break; |
|
972 } |
|
973 } |
|
974 break; |
|
975 |
|
976 #ifdef AUTHENTICATION |
|
977 case TELOPT_AUTHENTICATION: |
|
978 output_data("AUTHENTICATION"); |
|
979 |
|
980 if (length < 2) { |
|
981 output_data(" (empty suboption??\?)"); |
|
982 break; |
|
983 } |
|
984 switch (pointer[1]) { |
|
985 case TELQUAL_REPLY: |
|
986 case TELQUAL_IS: |
|
987 output_data(" %s ", |
|
988 (pointer[1] == TELQUAL_IS) ? |
|
989 "IS" : "REPLY"); |
|
990 if (AUTHTYPE_NAME_OK(pointer[2])) |
|
991 output_data("%s ", |
|
992 AUTHTYPE_NAME(pointer[2])); |
|
993 else |
|
994 output_data("%d ", |
|
995 pointer[2]); |
|
996 if (length < 3) { |
|
997 output_data("(partial suboption??\?)"); |
|
998 break; |
|
999 } |
|
1000 output_data("%s|%s", |
|
1001 ((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ? |
|
1002 "CLIENT" : "SERVER", |
|
1003 ((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? |
|
1004 "MUTUAL" : "ONE-WAY"); |
|
1005 |
|
1006 auth_printsub(&pointer[1], length - 1, buf, sizeof(buf)); |
|
1007 output_data("%s", |
|
1008 buf); |
|
1009 break; |
|
1010 |
|
1011 case TELQUAL_SEND: |
|
1012 i = 2; |
|
1013 output_data(" SEND "); |
|
1014 while (i < length) { |
|
1015 if (AUTHTYPE_NAME_OK(pointer[i])) |
|
1016 output_data("%s ", |
|
1017 AUTHTYPE_NAME(pointer[i])); |
|
1018 else |
|
1019 output_data("%d ", |
|
1020 pointer[i]); |
|
1021 if (++i >= length) { |
|
1022 output_data("(partial suboption??\?)"); |
|
1023 break; |
|
1024 } |
|
1025 output_data("%s|%s ", |
|
1026 ((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ? |
|
1027 "CLIENT" : "SERVER", |
|
1028 ((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? |
|
1029 "MUTUAL" : "ONE-WAY"); |
|
1030 ++i; |
|
1031 } |
|
1032 break; |
|
1033 |
|
1034 case TELQUAL_NAME: |
|
1035 i = 2; |
|
1036 output_data(" NAME \"%.*s\"", |
|
1037 length - 2, |
|
1038 pointer); |
|
1039 break; |
|
1040 |
|
1041 default: |
|
1042 for (i = 2; i < length; i++) { |
|
1043 output_data(" ?%d?", |
|
1044 pointer[i]); |
|
1045 } |
|
1046 break; |
|
1047 } |
|
1048 break; |
|
1049 #endif |
|
1050 |
|
1051 #ifdef ENCRYPTION |
|
1052 case TELOPT_ENCRYPT: |
|
1053 output_data("ENCRYPT"); |
|
1054 if (length < 2) { |
|
1055 output_data(" (empty suboption?)"); |
|
1056 break; |
|
1057 } |
|
1058 switch (pointer[1]) { |
|
1059 case ENCRYPT_START: |
|
1060 output_data(" START"); |
|
1061 break; |
|
1062 |
|
1063 case ENCRYPT_END: |
|
1064 output_data(" END"); |
|
1065 break; |
|
1066 |
|
1067 case ENCRYPT_REQSTART: |
|
1068 output_data(" REQUEST-START"); |
|
1069 break; |
|
1070 |
|
1071 case ENCRYPT_REQEND: |
|
1072 output_data(" REQUEST-END"); |
|
1073 break; |
|
1074 |
|
1075 case ENCRYPT_IS: |
|
1076 case ENCRYPT_REPLY: |
|
1077 output_data(" %s ", |
|
1078 (pointer[1] == ENCRYPT_IS) ? |
|
1079 "IS" : "REPLY"); |
|
1080 if (length < 3) { |
|
1081 output_data(" (partial suboption?)"); |
|
1082 break; |
|
1083 } |
|
1084 if (ENCTYPE_NAME_OK(pointer[2])) |
|
1085 output_data("%s ", |
|
1086 ENCTYPE_NAME(pointer[2])); |
|
1087 else |
|
1088 output_data(" %d (unknown)", |
|
1089 pointer[2]); |
|
1090 |
|
1091 encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf)); |
|
1092 output_data("%s", |
|
1093 buf); |
|
1094 break; |
|
1095 |
|
1096 case ENCRYPT_SUPPORT: |
|
1097 i = 2; |
|
1098 output_data(" SUPPORT "); |
|
1099 while (i < length) { |
|
1100 if (ENCTYPE_NAME_OK(pointer[i])) |
|
1101 output_data("%s ", |
|
1102 ENCTYPE_NAME(pointer[i])); |
|
1103 else |
|
1104 output_data("%d ", |
|
1105 pointer[i]); |
|
1106 i++; |
|
1107 } |
|
1108 break; |
|
1109 |
|
1110 case ENCRYPT_ENC_KEYID: |
|
1111 output_data(" ENC_KEYID %d", pointer[1]); |
|
1112 goto encommon; |
|
1113 |
|
1114 case ENCRYPT_DEC_KEYID: |
|
1115 output_data(" DEC_KEYID %d", pointer[1]); |
|
1116 goto encommon; |
|
1117 |
|
1118 default: |
|
1119 output_data(" %d (unknown)", pointer[1]); |
|
1120 encommon: |
|
1121 for (i = 2; i < length; i++) { |
|
1122 output_data(" %d", pointer[i]); |
|
1123 } |
|
1124 break; |
|
1125 } |
|
1126 break; |
|
1127 #endif |
|
1128 |
|
1129 default: |
|
1130 if (TELOPT_OK(pointer[0])) |
|
1131 output_data("%s (unknown)", |
|
1132 TELOPT(pointer[0])); |
|
1133 else |
|
1134 output_data("%d (unknown)", |
|
1135 pointer[i]); |
|
1136 for (i = 1; i < length; i++) { |
|
1137 output_data(" %d", pointer[i]); |
|
1138 } |
|
1139 break; |
|
1140 } |
|
1141 output_data("\r\n"); |
|
1142 } |
|
1143 |
|
1144 /* |
|
1145 * Dump a data buffer in hex and ascii to the output data stream. |
|
1146 */ |
|
1147 void |
|
1148 printdata(char *tag, char *ptr, int cnt) |
|
1149 { |
|
1150 int i; |
|
1151 char xbuf[30]; |
|
1152 |
|
1153 while (cnt) { |
|
1154 /* flush net output buffer if no room for new data) */ |
|
1155 if ((&netobuf[BUFSIZ] - nfrontp) < 80) { |
|
1156 netflush(); |
|
1157 } |
|
1158 |
|
1159 /* add a line of output */ |
|
1160 output_data("%s: ", tag); |
|
1161 for (i = 0; i < 20 && cnt; i++) { |
|
1162 output_data("%02x", *ptr); |
|
1163 if (isprint((unsigned char)*ptr)) { |
|
1164 xbuf[i] = *ptr; |
|
1165 } else { |
|
1166 xbuf[i] = '.'; |
|
1167 } |
|
1168 if (i % 2) { |
|
1169 output_data(" "); |
|
1170 } |
|
1171 cnt--; |
|
1172 ptr++; |
|
1173 } |
|
1174 xbuf[i] = '\0'; |
|
1175 output_data(" %s\r\n", xbuf); |
|
1176 } |
|
1177 } |
|
1178 #endif /* DIAGNOSTICS */ |