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