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