1 /* 2 * Copyright (c) 1983, 1990 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 char copyright[] = 10 "@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n\ 11 All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)rlogin.c 5.31 (Berkeley) 09/27/90"; 16 #endif /* not lint */ 17 18 /* 19 * $Source: mit/rlogin/RCS/rlogin.c,v $ 20 * $Header: mit/rlogin/RCS/rlogin.c,v 5.2 89/07/26 12:11:21 kfall 21 * Exp Locker: kfall $ 22 */ 23 24 /* 25 * rlogin - remote login 26 */ 27 #include <sys/param.h> 28 #include <sys/file.h> 29 #include <sys/socket.h> 30 #include <sys/signal.h> 31 #include <sys/time.h> 32 #include <sys/resource.h> 33 #include <sys/wait.h> 34 35 #include <netinet/in.h> 36 #include <netinet/in_systm.h> 37 #include <netinet/ip.h> 38 #include <netdb.h> 39 40 #include <sgtty.h> 41 #include <setjmp.h> 42 #include <varargs.h> 43 #include <errno.h> 44 #include <pwd.h> 45 #include <stdio.h> 46 #include <unistd.h> 47 #include <string.h> 48 49 #ifdef KERBEROS 50 #include <kerberosIV/des.h> 51 #include <kerberosIV/krb.h> 52 53 CREDENTIALS cred; 54 Key_schedule schedule; 55 int use_kerberos = 1, encrypt; 56 char dst_realm_buf[REALM_SZ], *dest_realm = NULL; 57 extern char *krb_realmofhost(); 58 #endif 59 60 #ifndef TIOCPKT_WINDOW 61 #define TIOCPKT_WINDOW 0x80 62 #endif 63 64 /* concession to Sun */ 65 #ifndef SIGUSR1 66 #define SIGUSR1 30 67 #endif 68 69 extern int errno; 70 int eight, litout, rem; 71 72 int noescape; 73 u_char escapechar = '~'; 74 75 char *speeds[] = { 76 "0", "50", "75", "110", "134", "150", "200", "300", "600", "1200", 77 "1800", "2400", "4800", "9600", "19200", "38400" 78 }; 79 80 #ifdef sun 81 struct winsize { 82 unsigned short ws_row, ws_col; 83 unsigned short ws_xpixel, ws_ypixel; 84 }; 85 #endif 86 struct winsize winsize; 87 88 #ifndef sun 89 #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp) 90 #endif 91 92 void exit(); 93 94 main(argc, argv) 95 int argc; 96 char **argv; 97 { 98 extern char *optarg; 99 extern int optind; 100 struct passwd *pw; 101 struct servent *sp; 102 struct sgttyb ttyb; 103 long omask; 104 int argoff, ch, dflag, one, uid; 105 char *host, *p, *user, term[1024]; 106 void lostpeer(); 107 u_char getescape(); 108 char *getenv(); 109 110 argoff = dflag = 0; 111 one = 1; 112 host = user = NULL; 113 114 if (p = rindex(argv[0], '/')) 115 ++p; 116 else 117 p = argv[0]; 118 119 if (strcmp(p, "rlogin")) 120 host = p; 121 122 /* handle "rlogin host flags" */ 123 if (!host && argc > 2 && argv[1][0] != '-') { 124 host = argv[1]; 125 argoff = 1; 126 } 127 128 #ifdef KERBEROS 129 #define OPTIONS "8EKLde:k:l:x" 130 #else 131 #define OPTIONS "8EKLde:l:" 132 #endif 133 while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF) 134 switch(ch) { 135 case '8': 136 eight = 1; 137 break; 138 case 'E': 139 noescape = 1; 140 break; 141 case 'K': 142 #ifdef KERBEROS 143 use_kerberos = 0; 144 #endif 145 break; 146 case 'L': 147 litout = 1; 148 break; 149 case 'd': 150 dflag = 1; 151 break; 152 case 'e': 153 escapechar = getescape(optarg); 154 break; 155 #ifdef KERBEROS 156 case 'k': 157 dest_realm = dst_realm_buf; 158 (void)strncpy(dest_realm, optarg, REALM_SZ); 159 break; 160 #endif 161 case 'l': 162 user = optarg; 163 break; 164 #if defined(KERBEROS) && defined(CRYPT) 165 case 'x': 166 encrypt = 1; 167 des_set_key(cred.session, schedule); 168 break; 169 #endif 170 case '?': 171 default: 172 usage(); 173 } 174 optind += argoff; 175 argc -= optind; 176 argv += optind; 177 178 /* if haven't gotten a host yet, do so */ 179 if (!host && !(host = *argv++)) 180 usage(); 181 182 if (*argv) 183 usage(); 184 185 if (!(pw = getpwuid(uid = getuid()))) { 186 (void)fprintf(stderr, "rlogin: unknown user id.\n"); 187 exit(1); 188 } 189 if (!user) 190 user = pw->pw_name; 191 192 sp = NULL; 193 #ifdef KERBEROS 194 if (use_kerberos) { 195 sp = getservbyname((encrypt ? "eklogin" : "klogin"), "tcp"); 196 if (sp == NULL) { 197 use_kerberos = 0; 198 warning("can't get entry for %s/tcp service", 199 encrypt ? "eklogin" : "klogin"); 200 } 201 } 202 #endif 203 if (sp == NULL) 204 sp = getservbyname("login", "tcp"); 205 if (sp == NULL) { 206 (void)fprintf(stderr, "rlogin: login/tcp: unknown service.\n"); 207 exit(1); 208 } 209 210 (void)strcpy(term, (p = getenv("TERM")) ? p : "network"); 211 if (ioctl(0, TIOCGETP, &ttyb) == 0) { 212 (void)strcat(term, "/"); 213 (void)strcat(term, speeds[ttyb.sg_ospeed]); 214 } 215 216 (void)get_window_size(0, &winsize); 217 218 (void)signal(SIGPIPE, lostpeer); 219 /* will use SIGUSR1 for window size hack, so hold it off */ 220 omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1)); 221 222 #ifdef KERBEROS 223 try_connect: 224 if (use_kerberos) { 225 rem = KSUCCESS; 226 errno = 0; 227 if (dest_realm == NULL) 228 dest_realm = krb_realmofhost(host); 229 230 #ifdef CRYPT 231 if (encrypt) 232 rem = krcmd_mutual(&host, sp->s_port, user, term, 0, 233 dest_realm, &cred, schedule); 234 else 235 #endif /* CRYPT */ 236 rem = krcmd(&host, sp->s_port, user, term, 0, 237 dest_realm); 238 if (rem < 0) { 239 use_kerberos = 0; 240 sp = getservbyname("login", "tcp"); 241 if (sp == NULL) { 242 (void)fprintf(stderr, 243 "rlogin: unknown service login/tcp.\n"); 244 exit(1); 245 } 246 if (errno == ECONNREFUSED) 247 warning("remote host doesn't support Kerberos"); 248 if (errno == ENOENT) 249 warning("can't provide Kerberos auth data"); 250 goto try_connect; 251 } 252 } else { 253 #ifdef CRYPT 254 if (encrypt) { 255 (void)fprintf(stderr, 256 "rlogin: the -x flag requires Kerberos authentication.\n"); 257 exit(1); 258 } 259 #endif /* CRYPT */ 260 rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0); 261 } 262 #else 263 rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0); 264 #endif /* KERBEROS */ 265 266 if (rem < 0) 267 exit(1); 268 269 if (dflag && 270 setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0) 271 (void)fprintf(stderr, "rlogin: setsockopt: %s.\n", 272 strerror(errno)); 273 one = IPTOS_LOWDELAY; 274 if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0) 275 perror("rlogin: setsockopt TOS (ignored)"); 276 277 (void)setuid(uid); 278 doit(omask); 279 /*NOTREACHED*/ 280 } 281 282 int child, defflags, deflflags, tabflag; 283 char deferase, defkill; 284 struct tchars deftc; 285 struct ltchars defltc; 286 struct tchars notc = { -1, -1, -1, -1, -1, -1 }; 287 struct ltchars noltc = { -1, -1, -1, -1, -1, -1 }; 288 289 doit(omask) 290 long omask; 291 { 292 struct sgttyb sb; 293 void catch_child(), copytochild(), exit(), writeroob(); 294 295 (void)ioctl(0, TIOCGETP, (char *)&sb); 296 defflags = sb.sg_flags; 297 tabflag = defflags & TBDELAY; 298 defflags &= ECHO | CRMOD; 299 deferase = sb.sg_erase; 300 defkill = sb.sg_kill; 301 (void)ioctl(0, TIOCLGET, (char *)&deflflags); 302 (void)ioctl(0, TIOCGETC, (char *)&deftc); 303 notc.t_startc = deftc.t_startc; 304 notc.t_stopc = deftc.t_stopc; 305 (void)ioctl(0, TIOCGLTC, (char *)&defltc); 306 (void)signal(SIGINT, SIG_IGN); 307 setsignal(SIGHUP, exit); 308 setsignal(SIGQUIT, exit); 309 child = fork(); 310 if (child == -1) { 311 (void)fprintf(stderr, "rlogin: fork: %s.\n", strerror(errno)); 312 done(1); 313 } 314 if (child == 0) { 315 mode(1); 316 if (reader(omask) == 0) { 317 msg("connection closed."); 318 exit(0); 319 } 320 sleep(1); 321 msg("\007connection closed."); 322 exit(1); 323 } 324 325 /* 326 * We may still own the socket, and may have a pending SIGURG (or might 327 * receive one soon) that we really want to send to the reader. Set a 328 * trap that simply copies such signals to the child. 329 */ 330 (void)signal(SIGURG, copytochild); 331 (void)signal(SIGUSR1, writeroob); 332 (void)sigsetmask(omask); 333 (void)signal(SIGCHLD, catch_child); 334 writer(); 335 msg("closed connection."); 336 done(0); 337 } 338 339 /* trap a signal, unless it is being ignored. */ 340 setsignal(sig, act) 341 int sig; 342 void (*act)(); 343 { 344 int omask = sigblock(sigmask(sig)); 345 346 if (signal(sig, act) == SIG_IGN) 347 (void)signal(sig, SIG_IGN); 348 (void)sigsetmask(omask); 349 } 350 351 done(status) 352 int status; 353 { 354 int w; 355 356 mode(0); 357 if (child > 0) { 358 /* make sure catch_child does not snap it up */ 359 (void)signal(SIGCHLD, SIG_DFL); 360 if (kill(child, SIGKILL) >= 0) 361 while ((w = wait((union wait *)0)) > 0 && w != child); 362 } 363 exit(status); 364 } 365 366 int dosigwinch; 367 368 /* 369 * This is called when the reader process gets the out-of-band (urgent) 370 * request to turn on the window-changing protocol. 371 */ 372 void 373 writeroob() 374 { 375 void sigwinch(); 376 377 if (dosigwinch == 0) { 378 sendwindow(); 379 (void)signal(SIGWINCH, sigwinch); 380 } 381 dosigwinch = 1; 382 } 383 384 void 385 catch_child() 386 { 387 union wait status; 388 int pid; 389 390 for (;;) { 391 pid = wait3(&status, WNOHANG|WUNTRACED, (struct rusage *)0); 392 if (pid == 0) 393 return; 394 /* if the child (reader) dies, just quit */ 395 if (pid < 0 || pid == child && !WIFSTOPPED(status)) 396 done((int)(status.w_termsig | status.w_retcode)); 397 } 398 /* NOTREACHED */ 399 } 400 401 /* 402 * writer: write to remote: 0 -> line. 403 * ~. terminate 404 * ~^Z suspend rlogin process. 405 * ~<delayed-suspend char> suspend rlogin process, but leave reader alone. 406 */ 407 writer() 408 { 409 register int bol, local, n; 410 char c; 411 412 bol = 1; /* beginning of line */ 413 local = 0; 414 for (;;) { 415 n = read(STDIN_FILENO, &c, 1); 416 if (n <= 0) { 417 if (n < 0 && errno == EINTR) 418 continue; 419 break; 420 } 421 /* 422 * If we're at the beginning of the line and recognize a 423 * command character, then we echo locally. Otherwise, 424 * characters are echo'd remotely. If the command character 425 * is doubled, this acts as a force and local echo is 426 * suppressed. 427 */ 428 if (bol) { 429 bol = 0; 430 if (!noescape && c == escapechar) { 431 local = 1; 432 continue; 433 } 434 } else if (local) { 435 local = 0; 436 if (c == '.' || c == deftc.t_eofc) { 437 echo(c); 438 break; 439 } 440 if (c == defltc.t_suspc || c == defltc.t_dsuspc) { 441 bol = 1; 442 echo(c); 443 stop(c); 444 continue; 445 } 446 if (c != escapechar) 447 #if defined(KERBEROS) && defined(CRYPT) 448 if (encrypt) 449 (void)des_write(rem, &escapechar, 1); 450 else 451 #endif 452 (void)write(rem, &escapechar, 1); 453 } 454 455 #if defined(KERBEROS) && defined(CRYPT) 456 if (encrypt) { 457 if (des_write(rem, &c, 1) == 0) { 458 msg("line gone"); 459 break; 460 } 461 } else 462 #endif 463 if (write(rem, &c, 1) == 0) { 464 msg("line gone"); 465 break; 466 } 467 bol = c == defkill || c == deftc.t_eofc || 468 c == deftc.t_intrc || c == defltc.t_suspc || 469 c == '\r' || c == '\n'; 470 } 471 } 472 473 echo(c) 474 register char c; 475 { 476 register char *p; 477 char buf[8]; 478 479 p = buf; 480 c &= 0177; 481 *p++ = escapechar; 482 if (c < ' ') { 483 *p++ = '^'; 484 *p++ = c + '@'; 485 } else if (c == 0177) { 486 *p++ = '^'; 487 *p++ = '?'; 488 } else 489 *p++ = c; 490 *p++ = '\r'; 491 *p++ = '\n'; 492 (void)write(STDOUT_FILENO, buf, p - buf); 493 } 494 495 stop(cmdc) 496 char cmdc; 497 { 498 mode(0); 499 (void)signal(SIGCHLD, SIG_IGN); 500 (void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP); 501 (void)signal(SIGCHLD, catch_child); 502 mode(1); 503 sigwinch(); /* check for size changes */ 504 } 505 506 void 507 sigwinch() 508 { 509 struct winsize ws; 510 511 if (dosigwinch && get_window_size(0, &ws) == 0 && 512 bcmp(&ws, &winsize, sizeof(ws))) { 513 winsize = ws; 514 sendwindow(); 515 } 516 } 517 518 /* 519 * Send the window size to the server via the magic escape 520 */ 521 sendwindow() 522 { 523 struct winsize *wp; 524 char obuf[4 + sizeof (struct winsize)]; 525 526 wp = (struct winsize *)(obuf+4); 527 obuf[0] = 0377; 528 obuf[1] = 0377; 529 obuf[2] = 's'; 530 obuf[3] = 's'; 531 wp->ws_row = htons(winsize.ws_row); 532 wp->ws_col = htons(winsize.ws_col); 533 wp->ws_xpixel = htons(winsize.ws_xpixel); 534 wp->ws_ypixel = htons(winsize.ws_ypixel); 535 536 #if defined(KERBEROS) && defined(CRYPT) 537 if(encrypt) 538 (void)des_write(rem, obuf, sizeof(obuf)); 539 else 540 #endif 541 (void)write(rem, obuf, sizeof(obuf)); 542 } 543 544 /* 545 * reader: read from remote: line -> 1 546 */ 547 #define READING 1 548 #define WRITING 2 549 550 jmp_buf rcvtop; 551 int ppid, rcvcnt, rcvstate; 552 char rcvbuf[8 * 1024]; 553 554 void 555 oob() 556 { 557 struct sgttyb sb; 558 int atmark, n, out, rcvd; 559 char waste[BUFSIZ], mark; 560 561 out = O_RDWR; 562 rcvd = 0; 563 while (recv(rem, &mark, 1, MSG_OOB) < 0) 564 switch (errno) { 565 case EWOULDBLOCK: 566 /* 567 * Urgent data not here yet. It may not be possible 568 * to send it yet if we are blocked for output and 569 * our input buffer is full. 570 */ 571 if (rcvcnt < sizeof(rcvbuf)) { 572 n = read(rem, rcvbuf + rcvcnt, 573 sizeof(rcvbuf) - rcvcnt); 574 if (n <= 0) 575 return; 576 rcvd += n; 577 } else { 578 n = read(rem, waste, sizeof(waste)); 579 if (n <= 0) 580 return; 581 } 582 continue; 583 default: 584 return; 585 } 586 if (mark & TIOCPKT_WINDOW) { 587 /* Let server know about window size changes */ 588 (void)kill(ppid, SIGUSR1); 589 } 590 if (!eight && (mark & TIOCPKT_NOSTOP)) { 591 (void)ioctl(0, TIOCGETP, (char *)&sb); 592 sb.sg_flags &= ~CBREAK; 593 sb.sg_flags |= RAW; 594 (void)ioctl(0, TIOCSETN, (char *)&sb); 595 notc.t_stopc = -1; 596 notc.t_startc = -1; 597 (void)ioctl(0, TIOCSETC, (char *)¬c); 598 } 599 if (!eight && (mark & TIOCPKT_DOSTOP)) { 600 (void)ioctl(0, TIOCGETP, (char *)&sb); 601 sb.sg_flags &= ~RAW; 602 sb.sg_flags |= CBREAK; 603 (void)ioctl(0, TIOCSETN, (char *)&sb); 604 notc.t_stopc = deftc.t_stopc; 605 notc.t_startc = deftc.t_startc; 606 (void)ioctl(0, TIOCSETC, (char *)¬c); 607 } 608 if (mark & TIOCPKT_FLUSHWRITE) { 609 (void)ioctl(1, TIOCFLUSH, (char *)&out); 610 for (;;) { 611 if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 612 (void)fprintf(stderr, "rlogin: ioctl: %s.\n", 613 strerror(errno)); 614 break; 615 } 616 if (atmark) 617 break; 618 n = read(rem, waste, sizeof (waste)); 619 if (n <= 0) 620 break; 621 } 622 /* 623 * Don't want any pending data to be output, so clear the recv 624 * buffer. If we were hanging on a write when interrupted, 625 * don't want it to restart. If we were reading, restart 626 * anyway. 627 */ 628 rcvcnt = 0; 629 longjmp(rcvtop, 1); 630 } 631 632 /* oob does not do FLUSHREAD (alas!) */ 633 634 /* 635 * If we filled the receive buffer while a read was pending, longjmp 636 * to the top to restart appropriately. Don't abort a pending write, 637 * however, or we won't know how much was written. 638 */ 639 if (rcvd && rcvstate == READING) 640 longjmp(rcvtop, 1); 641 } 642 643 /* reader: read from remote: line -> 1 */ 644 reader(omask) 645 int omask; 646 { 647 void oob(); 648 649 #if !defined(BSD) || BSD < 43 650 int pid = -getpid(); 651 #else 652 int pid = getpid(); 653 #endif 654 int n, remaining; 655 char *bufp = rcvbuf; 656 657 (void)signal(SIGTTOU, SIG_IGN); 658 (void)signal(SIGURG, oob); 659 ppid = getppid(); 660 (void)fcntl(rem, F_SETOWN, pid); 661 (void)setjmp(rcvtop); 662 (void)sigsetmask(omask); 663 for (;;) { 664 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { 665 rcvstate = WRITING; 666 n = write(STDOUT_FILENO, bufp, remaining); 667 if (n < 0) { 668 if (errno != EINTR) 669 return(-1); 670 continue; 671 } 672 bufp += n; 673 } 674 bufp = rcvbuf; 675 rcvcnt = 0; 676 rcvstate = READING; 677 678 #if defined(KERBEROS) && defined(CRYPT) 679 if (encrypt) 680 rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf)); 681 else 682 #endif 683 rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); 684 if (rcvcnt == 0) 685 return (0); 686 if (rcvcnt < 0) { 687 if (errno == EINTR) 688 continue; 689 (void)fprintf(stderr, "rlogin: read: %s.\n", 690 strerror(errno)); 691 return(-1); 692 } 693 } 694 } 695 696 mode(f) 697 { 698 struct ltchars *ltc; 699 struct sgttyb sb; 700 struct tchars *tc; 701 int lflags; 702 703 (void)ioctl(0, TIOCGETP, (char *)&sb); 704 (void)ioctl(0, TIOCLGET, (char *)&lflags); 705 switch(f) { 706 case 0: 707 sb.sg_flags &= ~(CBREAK|RAW|TBDELAY); 708 sb.sg_flags |= defflags|tabflag; 709 tc = &deftc; 710 ltc = &defltc; 711 sb.sg_kill = defkill; 712 sb.sg_erase = deferase; 713 lflags = deflflags; 714 break; 715 case 1: 716 sb.sg_flags |= (eight ? RAW : CBREAK); 717 sb.sg_flags &= ~defflags; 718 /* preserve tab delays, but turn off XTABS */ 719 if ((sb.sg_flags & TBDELAY) == XTABS) 720 sb.sg_flags &= ~TBDELAY; 721 tc = ¬c; 722 ltc = &noltc; 723 sb.sg_kill = sb.sg_erase = -1; 724 if (litout) 725 lflags |= LLITOUT; 726 break; 727 default: 728 return; 729 } 730 (void)ioctl(0, TIOCSLTC, (char *)ltc); 731 (void)ioctl(0, TIOCSETC, (char *)tc); 732 (void)ioctl(0, TIOCSETN, (char *)&sb); 733 (void)ioctl(0, TIOCLSET, (char *)&lflags); 734 } 735 736 void 737 lostpeer() 738 { 739 (void)signal(SIGPIPE, SIG_IGN); 740 msg("\007connection closed."); 741 done(1); 742 } 743 744 /* copy SIGURGs to the child process. */ 745 void 746 copytochild() 747 { 748 (void)kill(child, SIGURG); 749 } 750 751 msg(str) 752 char *str; 753 { 754 (void)fprintf(stderr, "rlogin: %s\r\n", str); 755 } 756 757 #ifdef KERBEROS 758 /* VARARGS */ 759 warning(va_alist) 760 va_dcl 761 { 762 va_list ap; 763 char *fmt; 764 765 (void)fprintf(stderr, "rlogin: warning, using standard rlogin: "); 766 va_start(ap); 767 fmt = va_arg(ap, char *); 768 vfprintf(stderr, fmt, ap); 769 va_end(ap); 770 (void)fprintf(stderr, ".\n"); 771 } 772 #endif 773 774 usage() 775 { 776 (void)fprintf(stderr, 777 "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n", 778 #ifdef KERBEROS 779 #ifdef CRYPT 780 "8ELx", " [-k realm] "); 781 #else 782 "8EL", " [-k realm] "); 783 #endif 784 #else 785 "8EL", " "); 786 #endif 787 exit(1); 788 } 789 790 /* 791 * The following routine provides compatibility (such as it is) between 4.2BSD 792 * Suns and others. Suns have only a `ttysize', so we convert it to a winsize. 793 */ 794 #ifdef sun 795 get_window_size(fd, wp) 796 int fd; 797 struct winsize *wp; 798 { 799 struct ttysize ts; 800 int error; 801 802 if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0) 803 return(error); 804 wp->ws_row = ts.ts_lines; 805 wp->ws_col = ts.ts_cols; 806 wp->ws_xpixel = 0; 807 wp->ws_ypixel = 0; 808 return(0); 809 } 810 #endif 811 812 u_char 813 getescape(p) 814 register char *p; 815 { 816 long val; 817 int len; 818 819 if ((len = strlen(p)) == 1) /* use any single char, including '\' */ 820 return((u_char)*p); 821 /* otherwise, \nnn */ 822 if (*p == '\\' && len >= 2 && len <= 4) { 823 val = strtol(++p, (char **)NULL, 8); 824 for (;;) { 825 if (!*++p) 826 return((u_char)val); 827 if (*p < '0' || *p > '8') 828 break; 829 } 830 } 831 msg("illegal option value -- e"); 832 usage(); 833 /* NOTREACHED */ 834 } 835