1 /* $NetBSD: tip.c,v 1.24 2003/08/07 11:16:19 agc Exp $ */ 2 3 /* 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\ 35 The Regents of the University of California. All rights reserved.\n"); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)tip.c 8.1 (Berkeley) 6/6/93"; 41 #endif 42 __RCSID("$NetBSD: tip.c,v 1.24 2003/08/07 11:16:19 agc Exp $"); 43 #endif /* not lint */ 44 45 /* 46 * tip - UNIX link to other systems 47 * tip [-v] [-speed] system-name 48 * or 49 * cu phone-number [-s speed] [-l line] [-a acu] 50 */ 51 #include "tip.h" 52 #include "pathnames.h" 53 54 /* 55 * Baud rate mapping table 56 */ 57 int rates[] = { 58 0, 50, 75, 110, 134, 150, 200, 300, 600, 59 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, -1 60 }; 61 62 int escape __P((void)); 63 int main __P((int, char **)); 64 void intprompt __P((int)); 65 void tipin __P((void)); 66 67 char PNbuf[256]; /* This limits the size of a number */ 68 69 int 70 main(argc, argv) 71 int argc; 72 char *argv[]; 73 { 74 char *system = NULL; 75 int i; 76 char *p; 77 char sbuf[12]; 78 int fcarg; 79 80 gid = getgid(); 81 egid = getegid(); 82 uid = getuid(); 83 euid = geteuid(); 84 if (equal(basename(argv[0]), "cu")) { 85 cumode = 1; 86 cumain(argc, argv); 87 goto cucommon; 88 } 89 90 if (argc > 4) { 91 fprintf(stderr, "usage: tip [-v] [-speed] [system-name]\n"); 92 exit(1); 93 } 94 if (!isatty(0)) { 95 fprintf(stderr, "tip: must be interactive\n"); 96 exit(1); 97 } 98 99 for (; argc > 1; argv++, argc--) { 100 if (argv[1][0] != '-') 101 system = argv[1]; 102 else switch (argv[1][1]) { 103 104 case 'v': 105 vflag++; 106 break; 107 108 case '0': case '1': case '2': case '3': case '4': 109 case '5': case '6': case '7': case '8': case '9': 110 BR = atoi(&argv[1][1]); 111 break; 112 113 default: 114 fprintf(stderr, "tip: %s, unknown option\n", argv[1]); 115 break; 116 } 117 } 118 119 if (system == NULL) 120 goto notnumber; 121 if (isalpha((unsigned char)*system)) 122 goto notnumber; 123 /* 124 * System name is really a phone number... 125 * Copy the number then stomp on the original (in case the number 126 * is private, we don't want 'ps' or 'w' to find it). 127 */ 128 if (strlen(system) > sizeof PNbuf - 1) { 129 fprintf(stderr, "tip: phone number too long (max = %d bytes)\n", 130 (int)sizeof(PNbuf) - 1); 131 exit(1); 132 } 133 strncpy(PNbuf, system, sizeof PNbuf - 1); 134 PNbuf[sizeof PNbuf - 1] = '\0'; 135 for (p = system; *p; p++) 136 *p = '\0'; 137 PN = PNbuf; 138 (void)snprintf(sbuf, sizeof sbuf, "tip%d", (int)BR); 139 system = sbuf; 140 141 notnumber: 142 (void)signal(SIGINT, cleanup); 143 (void)signal(SIGQUIT, cleanup); 144 (void)signal(SIGHUP, cleanup); 145 (void)signal(SIGTERM, cleanup); 146 147 if ((i = hunt(system)) == 0) { 148 printf("all ports busy\n"); 149 exit(3); 150 } 151 if (i == -1) { 152 printf("link down\n"); 153 (void)uu_unlock(uucplock); 154 exit(3); 155 } 156 setbuf(stdout, NULL); 157 loginit(); 158 159 /* 160 * Now that we have the logfile and the ACU open 161 * return to the real uid and gid. These things will 162 * be closed on exit. Swap real and effective uid's 163 * so we can get the original permissions back 164 * for removing the uucp lock. 165 */ 166 user_uid(); 167 168 /* 169 * Kludge, their's no easy way to get the initialization 170 * in the right order, so force it here 171 */ 172 if ((PH = getenv("PHONES")) == NULL) 173 PH = _PATH_PHONES; 174 vinit(); /* init variables */ 175 setparity("even"); /* set the parity table */ 176 if ((i = speed(number(value(BAUDRATE)))) == 0) { 177 printf("tip: bad baud rate %d\n", (int)number(value(BAUDRATE))); 178 daemon_uid(); 179 (void)uu_unlock(uucplock); 180 exit(3); 181 } 182 183 /* 184 * Hardwired connections require the 185 * line speed set before they make any transmissions 186 * (this is particularly true of things like a DF03-AC) 187 */ 188 if (HW) 189 ttysetup(i); 190 if ((p = connect()) != NULL) { 191 printf("\07%s\n[EOT]\n", p); 192 daemon_uid(); 193 (void)uu_unlock(uucplock); 194 exit(1); 195 } 196 if (!HW) 197 ttysetup(i); 198 199 /* 200 * Direct connections with no carrier require using O_NONBLOCK on 201 * open, but we don't want to keep O_NONBLOCK after open because it 202 * will cause busy waits. 203 */ 204 if (DC && 205 ((fcarg = fcntl(FD, F_GETFL, 0)) < 0 || 206 fcntl(FD, F_SETFL, fcarg & ~O_NONBLOCK) < 0)) { 207 printf("tip: can't clear O_NONBLOCK: %s", strerror (errno)); 208 daemon_uid(); 209 (void)uu_unlock(uucplock); 210 exit(1); 211 } 212 213 cucommon: 214 /* 215 * From here down the code is shared with 216 * the "cu" version of tip. 217 */ 218 219 tcgetattr(0, &defterm); 220 term = defterm; 221 term.c_lflag &= ~(ICANON|IEXTEN|ECHO); 222 term.c_iflag &= ~(INPCK|ICRNL); 223 term.c_oflag &= ~OPOST; 224 term.c_cc[VMIN] = 1; 225 term.c_cc[VTIME] = 0; 226 defchars = term; 227 term.c_cc[VINTR] = term.c_cc[VQUIT] = term.c_cc[VSUSP] = 228 term.c_cc[VDSUSP] = term.c_cc[VDISCARD] = 229 term.c_cc[VLNEXT] = _POSIX_VDISABLE; 230 raw(); 231 232 pipe(fildes); pipe(repdes); 233 (void)signal(SIGALRM, alrmtimeout); 234 235 /* 236 * Everything's set up now: 237 * connection established (hardwired or dialup) 238 * line conditioned (baud rate, mode, etc.) 239 * internal data structures (variables) 240 * so, fork one process for local side and one for remote. 241 */ 242 printf("%s", cumode ? "Connected\r\n" : "\07connected\r\n"); 243 switch (pid = fork()) { 244 default: 245 tipin(); 246 break; 247 case 0: 248 tipout(); 249 break; 250 case -1: 251 err(1, "can't fork"); 252 } 253 /*NOTREACHED*/ 254 exit(0); /* XXX: pacify gcc */ 255 } 256 257 void 258 cleanup(dummy) 259 int dummy; 260 { 261 262 daemon_uid(); 263 (void)uu_unlock(uucplock); 264 if (odisc) 265 ioctl(0, TIOCSETD, (char *)&odisc); 266 exit(0); 267 } 268 269 /* 270 * Muck with user ID's. We are setuid to the owner of the lock 271 * directory when we start. user_uid() reverses real and effective 272 * ID's after startup, to run with the user's permissions. 273 * daemon_uid() switches back to the privileged uid for unlocking. 274 * Finally, to avoid running a shell with the wrong real uid, 275 * shell_uid() sets real and effective uid's to the user's real ID. 276 */ 277 static int uidswapped; 278 279 void 280 user_uid() 281 { 282 283 if (uidswapped == 0) { 284 seteuid(uid); 285 uidswapped = 1; 286 } 287 } 288 289 void 290 daemon_uid() 291 { 292 293 if (uidswapped) { 294 seteuid(euid); 295 uidswapped = 0; 296 } 297 } 298 299 void 300 shell_uid() 301 { 302 303 seteuid(uid); 304 } 305 306 /* 307 * put the controlling keyboard into raw mode 308 */ 309 void 310 raw() 311 { 312 313 tcsetattr(0, TCSADRAIN, &term); 314 } 315 316 317 /* 318 * return keyboard to normal mode 319 */ 320 void 321 unraw() 322 { 323 324 tcsetattr(0, TCSADRAIN, &defterm); 325 } 326 327 static jmp_buf promptbuf; 328 329 /* 330 * Print string ``s'', then read a string 331 * in from the terminal. Handles signals & allows use of 332 * normal erase and kill characters. 333 */ 334 int 335 prompt(s, p, l) 336 char *s; 337 char *p; 338 size_t l; 339 { 340 int c; 341 char *b = p; 342 sig_t oint, oquit; 343 344 #if __GNUC__ /* XXX: pacify gcc */ 345 (void)&p; 346 #endif 347 348 stoprompt = 0; 349 oint = signal(SIGINT, intprompt); 350 oquit = signal(SIGQUIT, SIG_IGN); 351 unraw(); 352 printf("%s", s); 353 if (setjmp(promptbuf) == 0) 354 while ((c = getchar()) != -1 && (*p = c) != '\n' && 355 b + l > p) 356 p++; 357 *p = '\0'; 358 359 raw(); 360 (void)signal(SIGINT, oint); 361 (void)signal(SIGQUIT, oquit); 362 return (stoprompt || p == b); 363 } 364 365 /* 366 * Interrupt service routine during prompting 367 */ 368 void 369 intprompt(dummy) 370 int dummy; 371 { 372 373 (void)signal(SIGINT, SIG_IGN); 374 stoprompt = 1; 375 printf("\r\n"); 376 longjmp(promptbuf, 1); 377 } 378 379 /* 380 * ****TIPIN TIPIN**** 381 */ 382 void 383 tipin() 384 { 385 char gch, bol = 1; 386 387 /* 388 * Kinda klugey here... 389 * check for scripting being turned on from the .tiprc file, 390 * but be careful about just using setscript(), as we may 391 * send a SIGEMT before tipout has a chance to set up catching 392 * it; so wait a second, then setscript() 393 */ 394 if (boolean(value(SCRIPT))) { 395 sleep(1); 396 setscript(); 397 } 398 399 while (1) { 400 gch = getchar()&STRIP_PAR; 401 if ((gch == character(value(ESCAPE))) && bol) { 402 if (!(gch = escape())) 403 continue; 404 } else if (!cumode && 405 gch && gch == character(value(RAISECHAR))) { 406 setboolean(value(RAISE), !boolean(value(RAISE))); 407 continue; 408 } else if (gch == '\r') { 409 bol = 1; 410 xpwrite(FD, &gch, 1); 411 if (boolean(value(HALFDUPLEX))) 412 printf("\r\n"); 413 continue; 414 } else if (!cumode && gch && gch == character(value(FORCE))) 415 gch = getchar()&STRIP_PAR; 416 bol = any(gch, value(EOL)); 417 if (boolean(value(RAISE)) && islower((unsigned char)gch)) 418 gch = toupper(gch); 419 xpwrite(FD, &gch, 1); 420 if (boolean(value(HALFDUPLEX))) 421 printf("%c", gch); 422 } 423 } 424 425 /* 426 * Escape handler -- 427 * called on recognition of ``escapec'' at the beginning of a line 428 */ 429 int 430 escape() 431 { 432 char gch; 433 esctable_t *p; 434 char c = character(value(ESCAPE)); 435 436 gch = (getchar()&STRIP_PAR); 437 for (p = etable; p->e_char; p++) 438 if (p->e_char == gch) { 439 if ((p->e_flags&PRIV) && uid) 440 continue; 441 printf("%s", ctrl(c)); 442 (*p->e_func)(gch); 443 return (0); 444 } 445 /* ESCAPE ESCAPE forces ESCAPE */ 446 if (c != gch) 447 xpwrite(FD, &c, 1); 448 return (gch); 449 } 450 451 int 452 speed(n) 453 int n; 454 { 455 int *p; 456 457 for (p = rates; *p != -1; p++) 458 if (*p == n) 459 return n; 460 return 0; 461 } 462 463 int 464 any(c, p) 465 char c, *p; 466 { 467 468 while (p && *p) 469 if (*p++ == c) 470 return (1); 471 return (0); 472 } 473 474 char * 475 interp(s) 476 char *s; 477 { 478 static char buf[256]; 479 char *p = buf, c, *q; 480 481 while ((c = *s++) != 0 && buf + sizeof buf - p > 2) { 482 for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++) 483 if (*q++ == c) { 484 *p++ = '\\'; *p++ = *q; 485 goto next; 486 } 487 if (c < 040) { 488 *p++ = '^'; *p++ = c + 'A'-1; 489 } else if (c == 0177) { 490 *p++ = '^'; *p++ = '?'; 491 } else 492 *p++ = c; 493 next: 494 ; 495 } 496 *p = '\0'; 497 return (buf); 498 } 499 500 char * 501 ctrl(c) 502 char c; 503 { 504 static char s[3]; 505 506 if (c < 040 || c == 0177) { 507 s[0] = '^'; 508 s[1] = c == 0177 ? '?' : c+'A'-1; 509 s[2] = '\0'; 510 } else { 511 s[0] = c; 512 s[1] = '\0'; 513 } 514 return (s); 515 } 516 517 /* 518 * Help command 519 */ 520 void 521 help(c) 522 char c; 523 { 524 esctable_t *p; 525 526 printf("%c\r\n", c); 527 for (p = etable; p->e_char; p++) { 528 if ((p->e_flags&PRIV) && uid) 529 continue; 530 printf("%2s", ctrl(character(value(ESCAPE)))); 531 printf("%-2s %c %s\r\n", ctrl(p->e_char), 532 p->e_flags&EXP ? '*': ' ', p->e_help); 533 } 534 } 535 536 /* 537 * Set up the "remote" tty's state 538 */ 539 void 540 ttysetup(speed) 541 int speed; 542 { 543 struct termios cntrl; 544 545 tcgetattr(FD, &cntrl); 546 cfsetospeed(&cntrl, speed); 547 cfsetispeed(&cntrl, speed); 548 cntrl.c_cflag &= ~(CSIZE|PARENB); 549 cntrl.c_cflag |= CS8; 550 if (DC) 551 cntrl.c_cflag |= CLOCAL; 552 cntrl.c_iflag &= ~(ISTRIP|ICRNL); 553 cntrl.c_oflag &= ~OPOST; 554 cntrl.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO); 555 cntrl.c_cc[VMIN] = 1; 556 cntrl.c_cc[VTIME] = 0; 557 if (boolean(value(TAND))) 558 cntrl.c_iflag |= IXOFF; 559 tcsetattr(FD, TCSAFLUSH, &cntrl); 560 } 561 562 static char partab[0200]; 563 564 /* 565 * Do a write to the remote machine with the correct parity. 566 * We are doing 8 bit wide output, so we just generate a character 567 * with the right parity and output it. 568 */ 569 void 570 xpwrite(fd, buf, n) 571 int fd; 572 char *buf; 573 int n; 574 { 575 int i; 576 char *bp; 577 578 bp = buf; 579 if (bits8 == 0) 580 for (i = 0; i < n; i++) { 581 *bp = partab[(*bp) & 0177]; 582 bp++; 583 } 584 if (write(fd, buf, n) < 0) { 585 if (errno == EIO) 586 tipabort("Lost carrier."); 587 /* this is questionable */ 588 perror("write"); 589 } 590 } 591 592 /* 593 * Build a parity table with appropriate high-order bit. 594 */ 595 void 596 setparity(defparity) 597 char *defparity; 598 { 599 int i, flip, clr, set; 600 char *parity; 601 602 if (value(PARITY) == NULL || (value(PARITY))[0] == '\0') 603 value(PARITY) = defparity; 604 parity = value(PARITY); 605 if (equal(parity, "none")) { 606 bits8 = 1; 607 return; 608 } 609 bits8 = 0; 610 flip = 0; 611 clr = 0377; 612 set = 0; 613 if (equal(parity, "odd")) 614 flip = 0200; /* reverse bit 7 */ 615 else if (equal(parity, "zero")) 616 clr = 0177; /* turn off bit 7 */ 617 else if (equal(parity, "one")) 618 set = 0200; /* turn on bit 7 */ 619 else if (!equal(parity, "even")) { 620 (void) fprintf(stderr, "%s: unknown parity value\r\n", parity); 621 (void) fflush(stderr); 622 } 623 for (i = 0; i < 0200; i++) 624 partab[i] = ((evenpartab[i] ^ flip) | set) & clr; 625 } 626