tip.c revision 77120
1/* 2 * Copyright (c) 1983, 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 35static const char copyright[] = 36"@(#) Copyright (c) 1983, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38#endif /* not lint */ 39 40#ifndef lint 41#if 0 42static char sccsid[] = "@(#)tip.c 8.1 (Berkeley) 6/6/93"; 43#endif 44static const char rcsid[] = 45 "$FreeBSD: head/usr.bin/tip/tip/tip.c 77120 2001-05-24 09:27:02Z phk $"; 46#endif /* not lint */ 47 48/* 49 Forward declarations 50*/ 51void ttysetup (int speed); 52 53/* 54 * tip - UNIX link to other systems 55 * tip [-v] [-speed] system-name 56 * or 57 * cu phone-number [-s speed] [-l line] [-a acu] 58 */ 59 60#include <err.h> 61#include <errno.h> 62#include <sys/types.h> 63#include <libutil.h> 64#include "tipconf.h" 65#include "tip.h" 66#include "pathnames.h" 67 68/* 69 * Baud rate mapping table 70 */ 71#if !HAVE_TERMIOS 72CONST int bauds[] = { 73 0, 50, 75, 110, 134, 150, 200, 300, 600, 74 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, -1 75}; 76#endif 77 78#if !HAVE_TERMIOS 79int disc = OTTYDISC; /* tip normally runs this way */ 80#endif 81 82void intprompt(); 83void timeout(); 84void killchild(); 85void cleanup(); 86void tipdone(); 87char *sname(); 88char PNbuf[256]; /* This limits the size of a number */ 89 90static void usage __P((void)); 91void setparity __P((char *)); 92void xpwrite __P((int, char *, int)); 93char escape __P((void)); 94void tipin __P((void)); 95int prompt __P((char *, char *, size_t)); 96void unraw __P((void)); 97void shell_uid __P((void)); 98void daemon_uid __P((void)); 99void user_uid __P((void)); 100int speed __P((int)); 101 102int 103main(argc, argv) 104 char *argv[]; 105{ 106 char *system = NOSTR; 107 register int i; 108 register char *p; 109 char sbuf[12]; 110 111 gid = getgid(); 112 egid = getegid(); 113 uid = getuid(); 114 euid = geteuid(); 115 116#if INCLUDE_CU_INTERFACE 117 if (equal(sname(argv[0]), "cu")) { 118 cumode = 1; 119 cumain(argc, argv); 120 goto cucommon; 121 } 122#endif /* INCLUDE_CU_INTERFACE */ 123 124 if (argc > 4) 125 usage(); 126 if (!isatty(0)) 127 errx(1, "must be interactive"); 128 129 for (; argc > 1; argv++, argc--) { 130 if (argv[1][0] != '-') 131 system = argv[1]; 132 else switch (argv[1][1]) { 133 134 case 'v': 135 vflag++; 136 break; 137 138 case '0': case '1': case '2': case '3': case '4': 139 case '5': case '6': case '7': case '8': case '9': 140 BR = atoi(&argv[1][1]); 141 break; 142 143 default: 144 warnx("%s, unknown option", argv[1]); 145 break; 146 } 147 } 148 149 if (system == NOSTR) 150 goto notnumber; 151 if (isalpha(*system)) 152 goto notnumber; 153 /* 154 * System name is really a phone number... 155 * Copy the number then stomp on the original (in case the number 156 * is private, we don't want 'ps' or 'w' to find it). 157 */ 158 if (strlen(system) > sizeof(PNbuf) - 1) 159 errx(1, "phone number too long (max = %d bytes)", sizeof PNbuf - 1); 160 strncpy(PNbuf, system, sizeof(PNbuf) - 1); 161 for (p = system; *p; p++) 162 *p = '\0'; 163 PN = PNbuf; 164 (void)snprintf(sbuf, sizeof(sbuf), "tip%ld", BR); 165 system = sbuf; 166 167notnumber: 168 (void)signal(SIGINT, cleanup); 169 (void)signal(SIGQUIT, cleanup); 170 (void)signal(SIGHUP, cleanup); 171 (void)signal(SIGTERM, cleanup); 172 (void)signal(SIGUSR1, tipdone); 173 174 if ((i = hunt(system)) == 0) { 175 printf("all ports busy\n"); 176 exit(3); 177 } 178 if (i == -1) { 179 printf("link down\n"); 180 (void)uu_unlock(uucplock); 181 exit(3); 182 } 183 setbuf(stdout, NULL); 184 loginit(); 185 186 /* 187 * Kludge, their's no easy way to get the initialization 188 * in the right order, so force it here 189 */ 190 if ((PH = getenv("PHONES")) == NOSTR) 191 PH = _PATH_PHONES; 192 vinit(); /* init variables */ 193 setparity("even"); /* set the parity table */ 194 if ((i = speed(number(value(BAUDRATE)))) == 0) { 195 printf("tip: bad baud rate %d\n", number(value(BAUDRATE))); 196 (void)uu_unlock(uucplock); 197 exit(3); 198 } 199 200 /* 201 * Now that we have the logfile and the ACU open 202 * return to the real uid and gid. These things will 203 * be closed on exit. Swap real and effective uid's 204 * so we can get the original permissions back 205 * for removing the uucp lock. 206 */ 207 user_uid(); 208 209 /* 210 * Hardwired connections require the 211 * line speed set before they make any transmissions 212 * (this is particularly true of things like a DF03-AC) 213 */ 214 if (HW) 215 ttysetup(i); 216 if ((p = connect())) { 217 printf("\07%s\n[EOT]\n", p); 218 daemon_uid(); 219 (void)uu_unlock(uucplock); 220 exit(1); 221 } 222 if (!HW) 223 ttysetup(i); 224/* cucommon:*/ 225 /* 226 * From here down the code is shared with 227 * the "cu" version of tip. 228 */ 229 230#if HAVE_TERMIOS 231 tcgetattr (0, &otermios); 232 ctermios = otermios; 233#ifndef _POSIX_SOURCE 234 ctermios.c_iflag = (IMAXBEL|IXANY|ISTRIP|IXON|BRKINT); 235 ctermios.c_lflag = (PENDIN|IEXTEN|ISIG|ECHOCTL|ECHOE|ECHOKE); 236#else 237 ctermios.c_iflag = (ISTRIP|IXON|BRKINT); 238 ctermios.c_lflag = (PENDIN|IEXTEN|ISIG|ECHOE); 239#endif 240 ctermios.c_cflag = (CLOCAL|HUPCL|CREAD|CS8); 241 ctermios.c_cc[VINTR] = ctermios.c_cc[VQUIT] = -1; 242 ctermios.c_cc[VSUSP] = ctermios.c_cc[VDSUSP] = ctermios.c_cc[VDISCARD] = 243 ctermios.c_cc[VLNEXT] = -1; 244#else /* HAVE_TERMIOS */ 245 ioctl(0, TIOCGETP, (char *)&defarg); 246 ioctl(0, TIOCGETC, (char *)&defchars); 247 ioctl(0, TIOCGLTC, (char *)&deflchars); 248 ioctl(0, TIOCGETD, (char *)&odisc); 249 arg = defarg; 250 arg.sg_flags = ANYP | CBREAK; 251 tchars = defchars; 252 tchars.t_intrc = tchars.t_quitc = -1; 253 ltchars = deflchars; 254 ltchars.t_suspc = ltchars.t_dsuspc = ltchars.t_flushc 255 = ltchars.t_lnextc = -1; 256#endif /* HAVE_TERMIOS */ 257 raw(); 258 259 pipe(fildes); pipe(repdes); 260 (void)signal(SIGALRM, timeout); 261 262 /* 263 * Everything's set up now: 264 * connection established (hardwired or dialup) 265 * line conditioned (baud rate, mode, etc.) 266 * internal data structures (variables) 267 * so, fork one process for local side and one for remote. 268 */ 269 printf(cumode ? "Connected\r\n" : "\07connected\r\n"); 270 271 if (LI != NOSTR && tiplink (LI, 0) != 0) { 272 tipabort ("login failed"); 273 } 274 275 if ((pid = fork())) 276 tipin(); 277 else 278 tipout(); 279 /*NOTREACHED*/ 280} 281 282static void 283usage() 284{ 285 fprintf(stderr, "usage: tip [-v] [-speed] [system-name]\n"); 286 exit(1); 287} 288 289void 290killchild() 291{ 292 if (pid != 0) { 293 kill(pid, SIGTERM); 294 pid = 0; 295 } 296} 297 298void 299cleanup() 300{ 301 302 daemon_uid(); 303 (void)uu_unlock(uucplock); 304#if !HAVE_TERMIOS 305 if (odisc) 306 ioctl(0, TIOCSETD, (char *)&odisc); 307#endif 308 exit(0); 309} 310 311void 312tipdone() 313{ 314 tipabort("Hangup."); 315} 316/* 317 * Muck with user ID's. We are setuid to the owner of the lock 318 * directory when we start. user_uid() reverses real and effective 319 * ID's after startup, to run with the user's permissions. 320 * daemon_uid() switches back to the privileged uid for unlocking. 321 * Finally, to avoid running a shell with the wrong real uid, 322 * shell_uid() sets real and effective uid's to the user's real ID. 323 */ 324static int uidswapped; 325 326void 327user_uid() 328{ 329 if (uidswapped == 0) { 330 seteuid(uid); 331 uidswapped = 1; 332 } 333} 334 335void 336daemon_uid() 337{ 338 if (uidswapped) { 339 seteuid(euid); 340 uidswapped = 0; 341 } 342} 343 344void 345shell_uid() 346{ 347 setegid(gid); 348 seteuid(uid); 349} 350 351/* 352 * put the controlling keyboard into raw mode 353 */ 354void 355raw () 356{ 357#if HAVE_TERMIOS 358 tcsetattr (0, TCSANOW, &ctermios); 359#else /* HAVE_TERMIOS */ 360 361 ioctl(0, TIOCSETP, &arg); 362 ioctl(0, TIOCSETC, &tchars); 363 ioctl(0, TIOCSLTC, <chars); 364 ioctl(0, TIOCSETD, (char *)&disc); 365#endif /* HAVE_TERMIOS */ 366} 367 368 369/* 370 * return keyboard to normal mode 371 */ 372void 373unraw() 374{ 375#if HAVE_TERMIOS 376 tcsetattr (0, TCSANOW, &otermios); 377#else /* HAVE_TERMIOS */ 378 379 ioctl(0, TIOCSETD, (char *)&odisc); 380 ioctl(0, TIOCSETP, (char *)&defarg); 381 ioctl(0, TIOCSETC, (char *)&defchars); 382 ioctl(0, TIOCSLTC, (char *)&deflchars); 383#endif /* HAVE_TERMIOS */ 384} 385 386static jmp_buf promptbuf; 387 388/* 389 * Print string ``s'', then read a string 390 * in from the terminal. Handles signals & allows use of 391 * normal erase and kill characters. 392 */ 393int 394prompt(s, p, sz) 395 char *s; 396 register char *p; 397 size_t sz; 398{ 399 register char *b = p; 400 sig_t oint, oquit; 401 402 stoprompt = 0; 403 oint = signal(SIGINT, intprompt); 404 oquit = signal(SIGQUIT, SIG_IGN); 405 unraw(); 406 printf("%s", s); 407 if (setjmp(promptbuf) == 0) 408 while ((*p = getchar()) != EOF && *p != '\n' && --sz > 0) 409 p++; 410 *p = '\0'; 411 412 raw(); 413 (void)signal(SIGINT, oint); 414 (void)signal(SIGQUIT, oquit); 415 return (stoprompt || p == b); 416} 417 418/* 419 * Interrupt service routine during prompting 420 */ 421void 422intprompt() 423{ 424 425 (void)signal(SIGINT, SIG_IGN); 426 stoprompt = 1; 427 printf("\r\n"); 428 longjmp(promptbuf, 1); 429} 430 431/* 432 * ****TIPIN TIPIN**** 433 */ 434void 435tipin() 436{ 437 int i; 438 char gch, bol = 1; 439 440 atexit(killchild); 441 442 /* 443 * Kinda klugey here... 444 * check for scripting being turned on from the .tiprc file, 445 * but be careful about just using setscript(), as we may 446 * send a SIGEMT before tipout has a chance to set up catching 447 * it; so wait a second, then setscript() 448 */ 449 if (boolean(value(SCRIPT))) { 450 sleep(1); 451 setscript(); 452 } 453 454 while (1) { 455 i = getchar(); 456 if (i == EOF) 457 break; 458 gch = i&0177; 459 if ((gch == character(value(ESCAPE))) && bol) { 460 if (!(gch = escape())) 461 continue; 462 } else if (!cumode && gch == character(value(RAISECHAR))) { 463 boolean(value(RAISE)) = !boolean(value(RAISE)); 464 continue; 465 } else if (gch == '\r') { 466 bol = 1; 467 xpwrite(FD, &gch, 1); 468 if (boolean(value(HALFDUPLEX))) 469 printf("\r\n"); 470 continue; 471 } else if (!cumode && gch == character(value(FORCE))) { 472 i = getchar(); 473 if (i == EOF) 474 break; 475 gch = i & 0177; 476 } 477 bol = any(gch, value(EOL)); 478 if (boolean(value(RAISE)) && islower(gch)) 479 gch = toupper(gch); 480 xpwrite(FD, &gch, 1); 481 if (boolean(value(HALFDUPLEX))) 482 printf("%c", gch); 483 } 484} 485 486extern esctable_t etable[]; 487 488/* 489 * Escape handler -- 490 * called on recognition of ``escapec'' at the beginning of a line 491 */ 492char 493escape() 494{ 495 register char gch; 496 register esctable_t *p; 497 char c = character(value(ESCAPE)); 498 int i; 499 500 i = getchar(); 501 if (i == EOF) 502 return 0; 503 gch = (i&0177); 504 for (p = etable; p->e_char; p++) 505 if (p->e_char == gch) { 506 if ((p->e_flags&PRIV) && uid) 507 continue; 508 printf("%s", ctrl(c)); 509 (*p->e_func)(gch); 510 return (0); 511 } 512 /* ESCAPE ESCAPE forces ESCAPE */ 513 if (c != gch) 514 xpwrite(FD, &c, 1); 515 return (gch); 516} 517 518int 519speed(n) 520 int n; 521{ 522#if HAVE_TERMIOS 523 return (n); 524#else 525 register CONST int *p; 526 527 for (p = bauds; *p != -1; p++) 528 if (*p == n) 529 return (p - bauds); 530 return (NULL); 531#endif 532} 533 534int 535any(c, p) 536 register char c, *p; 537{ 538 while (p && *p) 539 if (*p++ == c) 540 return (1); 541 return (0); 542} 543 544int 545size(s) 546 register char *s; 547{ 548 register int i = 0; 549 550 while (s && *s++) 551 i++; 552 return (i); 553} 554 555char * 556interp(s) 557 register char *s; 558{ 559 static char buf[256]; 560 register char *p = buf, c, *q; 561 562 while ((c = *s++)) { 563 for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++) 564 if (*q++ == c) { 565 *p++ = '\\'; *p++ = *q; 566 goto next; 567 } 568 if (c < 040) { 569 *p++ = '^'; *p++ = c + 'A'-1; 570 } else if (c == 0177) { 571 *p++ = '^'; *p++ = '?'; 572 } else 573 *p++ = c; 574 next: 575 ; 576 } 577 *p = '\0'; 578 return (buf); 579} 580 581char * 582ctrl(c) 583 char c; 584{ 585 static char s[3]; 586 587 if (c < 040 || c == 0177) { 588 s[0] = '^'; 589 s[1] = c == 0177 ? '?' : c+'A'-1; 590 s[2] = '\0'; 591 } else { 592 s[0] = c; 593 s[1] = '\0'; 594 } 595 return (s); 596} 597 598/* 599 * Help command 600 */ 601void 602help(c) 603 char c; 604{ 605 register esctable_t *p; 606 607 printf("%c\r\n", c); 608 for (p = etable; p->e_char; p++) { 609 if ((p->e_flags&PRIV) && uid) 610 continue; 611 printf("%2s", ctrl(character(value(ESCAPE)))); 612 printf("%-2s %c %s\r\n", ctrl(p->e_char), 613 p->e_flags&EXP ? '*': ' ', p->e_help); 614 } 615} 616 617/* 618 * Set up the "remote" tty's state 619 */ 620void 621ttysetup (int speed) 622{ 623#if HAVE_TERMIOS 624 struct termios termios; 625 tcgetattr (FD, &termios); 626 if (boolean(value(TAND))) 627 termios.c_iflag = IXOFF; 628 else 629 termios.c_iflag = 0; 630#ifndef _POSIX_SOURCE 631 termios.c_lflag = (PENDIN|ECHOKE|ECHOE); 632#else 633 termios.c_lflag = (PENDIN|ECHOE); 634#endif 635 termios.c_cflag = (CLOCAL|HUPCL|CREAD|CS8); 636 termios.c_ispeed = termios.c_ospeed = speed; 637 tcsetattr (FD, TCSANOW, &termios); 638#else /* HAVE_TERMIOS */ 639 unsigned bits = LDECCTQ; 640 641 arg.sg_ispeed = arg.sg_ospeed = speed; 642 arg.sg_flags = RAW; 643 if (boolean(value(TAND))) 644 arg.sg_flags |= TANDEM; 645 ioctl(FD, TIOCSETP, (char *)&arg); 646 ioctl(FD, TIOCLBIS, (char *)&bits); 647#endif /* HAVE_TERMIOS */ 648} 649 650/* 651 * Return "simple" name from a file name, 652 * strip leading directories. 653 */ 654char * 655sname(s) 656 register char *s; 657{ 658 register char *p = s; 659 660 while (*s) 661 if (*s++ == '/') 662 p = s; 663 return (p); 664} 665 666static char partab[0200]; 667static int bits8; 668 669/* 670 * Do a write to the remote machine with the correct parity. 671 * We are doing 8 bit wide output, so we just generate a character 672 * with the right parity and output it. 673 */ 674void 675xpwrite(fd, buf, n) 676 int fd; 677 char *buf; 678 register int n; 679{ 680 register int i; 681 register char *bp; 682 683 bp = buf; 684 if (bits8 == 0) 685 for (i = 0; i < n; i++) { 686 *bp = partab[(*bp) & 0177]; 687 bp++; 688 } 689 if (write(fd, buf, n) < 0) { 690 if (errno == EIO) 691 tipabort("Lost carrier."); 692 if (errno == ENODEV) 693 tipabort("tty not available."); 694 tipabort("Something wrong..."); 695 } 696} 697 698/* 699 * Build a parity table with appropriate high-order bit. 700 */ 701void 702setparity(defparity) 703 char *defparity; 704{ 705 register int i, flip, clr, set; 706 char *parity; 707 extern char evenpartab[]; 708 709 if (value(PARITY) == NOSTR) 710 value(PARITY) = defparity; 711 parity = value(PARITY); 712 if (equal(parity, "none")) { 713 bits8 = 1; 714 return; 715 } 716 bits8 = 0; 717 flip = 0; 718 clr = 0377; 719 set = 0; 720 if (equal(parity, "odd")) 721 flip = 0200; /* reverse bit 7 */ 722 else if (equal(parity, "zero")) 723 clr = 0177; /* turn off bit 7 */ 724 else if (equal(parity, "one")) 725 set = 0200; /* turn on bit 7 */ 726 else if (!equal(parity, "even")) { 727 (void) fprintf(stderr, "%s: unknown parity value\r\n", parity); 728 (void) fflush(stderr); 729 } 730 for (i = 0; i < 0200; i++) 731 partab[i] = (evenpartab[i] ^ flip) | (set & clr); 732} 733