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