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