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