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