1 /* 2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * Copyright (c) 1983 Regents of the University of California. 8 * All rights reserved. The Berkeley software License Agreement 9 * specifies the terms and conditions for redistribution. 10 */ 11 12 /* 13 * tip - UNIX link to other systems 14 * tip [-v] [-speed] system-name 15 * or 16 * cu phone-number [-s speed] [-l line] [-a acu] 17 */ 18 #include "tip.h" 19 #include <sys/wait.h> 20 #include <locale.h> 21 22 int vflag; 23 int cumode; 24 FILE *phfd; 25 26 /* 27 * Baud rate mapping table 28 */ 29 int bauds[] = { 30 0, 50, 75, 110, 134, 150, 200, 300, 600, 31 1200, 1800, 2400, 4800, 9600, 19200, 38400, 32 57600, 76800, 115200, 153600, 230400, 307200, 33 460800, 921600, -1 34 }; 35 36 extern void tipout(void) __NORETURN; 37 extern void timeout(void); 38 extern esctable_t etable[]; 39 extern unsigned char evenpartab[]; 40 41 void intprompt(void); 42 void deadkid(void); 43 void cleanup(void); 44 void tipin(void) __NORETURN; 45 unsigned char escape(void); 46 char *sname(char *); 47 char PNbuf[256]; /* This limits the size of a number */ 48 int noparity = 0; 49 50 int 51 main(int argc, char *argv[]) 52 { 53 char *system = NOSTR; 54 int i; 55 char *p; 56 char sbuf[15]; 57 58 gid = getgid(); 59 egid = getegid(); 60 uid = getuid(); 61 euid = geteuid(); 62 if (equal(sname(argv[0]), "cu")) { 63 cumode = 1; 64 cumain(argc, argv); 65 goto cucommon; 66 } 67 68 if (argc > 4) { 69 (void) fprintf(stderr, 70 "usage: tip [-v] [-speed] [system-name]\n"); 71 return (1); 72 } 73 if (!isatty(0)) { 74 (void) fprintf(stderr, "tip: must be interactive\n"); 75 return (1); 76 } 77 78 for (; argc > 1; argv++, argc--) { 79 if (argv[1][0] != '-') 80 system = argv[1]; 81 else switch (argv[1][1]) { 82 83 case 'v': 84 vflag++; 85 break; 86 87 case '0': case '1': case '2': case '3': case '4': 88 case '5': case '6': case '7': case '8': case '9': 89 BR = atoi(&argv[1][1]); 90 break; 91 92 default: 93 (void) fprintf(stderr, "tip: %s, unknown option\n", 94 argv[1]); 95 break; 96 } 97 } 98 99 (void) setlocale(LC_CTYPE, ""); 100 101 if (system == NOSTR) 102 goto notnumber; 103 for (p = system; *p; p++) 104 if (isalpha(*p)) 105 goto notnumber; 106 /* 107 * System name is really a phone number... 108 * Copy the number then stomp on the original (in case the number 109 * is private, we don't want 'ps' or 'w' to find it). 110 */ 111 if (strlen(system) > sizeof (PNbuf) - 1) { 112 (void) fprintf(stderr, 113 "tip: phone number too long (max = %d bytes)\n", 114 sizeof (PNbuf) - 1); 115 return (1); 116 } 117 (void) strncpy(PNbuf, system, sizeof (PNbuf) - 1); 118 for (p = system; *p; p++) 119 *p = '\0'; 120 PN = PNbuf; 121 (void) snprintf(sbuf, sizeof (sbuf), "tip%d", BR); 122 system = sbuf; 123 124 notnumber: 125 (void) signal(SIGINT, (sig_handler_t)cleanup); 126 (void) signal(SIGQUIT, (sig_handler_t)cleanup); 127 (void) signal(SIGHUP, (sig_handler_t)cleanup); 128 (void) signal(SIGTERM, (sig_handler_t)cleanup); 129 130 if ((i = hunt(system)) == 0) { 131 (void) printf("all ports busy\n"); 132 return (3); 133 } 134 if (i == -1) { 135 (void) printf("link down\n"); 136 delock(uucplock); 137 return (3); 138 } 139 setbuf(stdout, NULL); 140 loginit(); 141 142 /* 143 * Now that we have the logfile and the ACU open 144 * return to the real uid and gid. These things will 145 * be closed on exit. The saved-setuid uid and gid 146 * allows us to get the original setuid permissions back 147 * for removing the uucp lock. 148 */ 149 userperm(); 150 151 /* 152 * Kludge, there's no easy way to get the initialization 153 * in the right order, so force it here. 154 * Do the open here, before we change back to real uid. 155 * We will check whether the open succeeded later, when 156 * (and if) we actually go to use the file. 157 */ 158 if ((PH = getenv("PHONES")) == NOSTR) { 159 myperm(); 160 PH = "/etc/phones"; 161 } 162 phfd = fopen(PH, "r"); 163 164 userperm(); 165 166 vinit(); /* init variables */ 167 setparity("none"); /* set the parity table */ 168 if ((i = speed(number(value(BAUDRATE)))) == 0) { 169 (void) printf("tip: bad baud rate %d\n", 170 number(value(BAUDRATE))); 171 myperm(); 172 delock(uucplock); 173 return (3); 174 } 175 176 177 /* 178 * Hardwired connections require the 179 * line speed set before they make any transmissions 180 * (this is particularly true of things like a DF03-AC) 181 */ 182 if (HW) 183 ttysetup(i); 184 if (p = connect()) { 185 (void) printf("\07%s\n[EOT]\n", p); 186 myperm(); 187 delock(uucplock); 188 return (1); 189 } 190 191 /* 192 * Always setup the tty again here in case hardware flow 193 * control was selected, which can only be set after the 194 * connection is made, or in case this is not a hardwired 195 * modem (rare these days) that likewise can only be setup 196 * after the connection is made. 197 */ 198 ttysetup(i); 199 cucommon: 200 /* 201 * From here down the code is shared with 202 * the "cu" version of tip. 203 */ 204 205 (void) ioctl(0, TCGETS, (char *)&defarg); 206 arg = defarg; 207 /* turn off input processing */ 208 arg.c_lflag &= ~(ICANON|ISIG|ECHO|IEXTEN); 209 arg.c_cc[VMIN] = 1; 210 arg.c_cc[VTIME] = 0; 211 arg.c_iflag &= ~(INPCK|IXON|IXOFF|ICRNL); 212 arg.c_oflag = 0; /* turn off all output processing */ 213 /* handle tandem mode in case was set in remote file */ 214 if (boolean(value(TAND))) 215 tandem("on"); 216 else 217 tandem("off"); 218 raw(); 219 220 (void) pipe(fildes); (void) pipe(repdes); 221 (void) signal(SIGALRM, (sig_handler_t)timeout); 222 223 /* 224 * Everything's set up now: 225 * connection established (hardwired or dialup) 226 * line conditioned (baud rate, mode, etc.) 227 * internal data structures (variables) 228 * so, fork one process for local side and one for remote. 229 */ 230 if (CM != NOSTR) { 231 (void) sleep(2); /* let line settle */ 232 parwrite(FD, (unsigned char *)CM, strlen(CM)); 233 } 234 (void) printf(cumode ? "Connected\r\n" : "\07connected\r\n"); 235 (void) signal(SIGCHLD, (sig_handler_t)deadkid); 236 if (pid = fork()) 237 tipin(); 238 else 239 tipout(); 240 /*NOTREACHED*/ 241 } 242 243 void 244 deadkid(void) 245 { 246 247 if (pid >= 0 && waitpid(pid, NULL, WNOHANG) == pid) 248 tip_abort("Connection Closed"); 249 } 250 251 void 252 cleanup(void) 253 { 254 255 if (uid != getuid()) { 256 myperm(); 257 } 258 delock(uucplock); 259 exit(0); 260 } 261 262 /* 263 * put the controlling keyboard into raw mode 264 */ 265 void 266 raw(void) 267 { 268 269 (void) ioctl(0, TCSETSF, (char *)&arg); 270 } 271 272 273 /* 274 * return keyboard to normal mode 275 */ 276 void 277 unraw(void) 278 { 279 280 (void) ioctl(0, TCSETSF, (char *)&defarg); 281 } 282 283 /* 284 * switch to using invoking user's permissions 285 */ 286 void 287 userperm(void) 288 { 289 290 (void) setegid(gid); 291 (void) seteuid(uid); 292 } 293 294 /* 295 * switch to using my special (setuid) permissions 296 */ 297 void 298 myperm(void) 299 { 300 301 (void) setegid(egid); 302 (void) seteuid(euid); 303 } 304 305 static sigjmp_buf promptbuf; 306 307 /* 308 * Print string ``s'', then read a string 309 * in from the terminal. Handles signals & allows use of 310 * normal erase and kill characters. 311 */ 312 int 313 prompt(char *s, char *p, size_t len) 314 { 315 char *b = p; 316 int c; 317 sig_handler_t ointr, oquit; 318 319 stoprompt = 0; 320 ointr = signal(SIGINT, (sig_handler_t)intprompt); 321 oquit = signal(SIGQUIT, SIG_IGN); 322 unraw(); 323 (void) printf("%s", s); 324 if (sigsetjmp(promptbuf, 1) == 0) 325 while (p < b + len - 1 && 326 ((c = getchar()) != EOF) && (c != '\n')) 327 *p++ = c; 328 *p = '\0'; 329 330 raw(); 331 (void) signal(SIGINT, ointr); 332 (void) signal(SIGQUIT, oquit); 333 return (stoprompt || p == b); 334 } 335 336 /* 337 * Interrupt service routine during prompting 338 */ 339 void 340 intprompt(void) 341 { 342 343 (void) signal(SIGINT, SIG_IGN); 344 (void) signal(SIGQUIT, SIG_IGN); 345 stoprompt = 1; 346 (void) printf("\r\n"); 347 siglongjmp(promptbuf, 1); 348 } 349 350 /* 351 * ****TIPIN TIPIN**** 352 */ 353 void 354 tipin(void) 355 { 356 unsigned char gch, c; 357 int bol = 1; 358 359 /* 360 * Kinda klugey here... 361 * check for scripting being turned on from the .tiprc file, 362 * but be careful about just using setscript(), as we may 363 * send a SIGEMT before tipout has a chance to set up catching 364 * it; so wait a second, then setscript() 365 */ 366 if (boolean(value(SCRIPT))) { 367 (void) sleep(1); 368 setscript(); 369 } 370 371 for (;;) { 372 gch = getchar()&0377; 373 if ((gch == character(value(ESCAPE))) && bol) { 374 if (!(gch = escape())) 375 continue; 376 } else if (!cumode && gch == character(value(RAISECHAR))) { 377 boolean(value(RAISE)) = !boolean(value(RAISE)); 378 continue; 379 } else if (gch == '\r') { 380 bol = 1; 381 parwrite(FD, &gch, 1); 382 if (boolean(value(HALFDUPLEX))) 383 (void) printf("\r\n"); 384 continue; 385 } else if (!cumode && gch == character(value(FORCE))) 386 gch = getchar()&0377; 387 bol = any(gch, value(EOL)); 388 if (boolean(value(RAISE)) && islower(gch)) 389 gch = toupper(gch); 390 c = gch; 391 parwrite(FD, &gch, 1); 392 if (boolean(value(HALFDUPLEX))) 393 (void) putchar(c); 394 } 395 } 396 397 /* 398 * Escape handler -- 399 * called on recognition of ``escapec'' at the beginning of a line 400 */ 401 unsigned char 402 escape(void) 403 { 404 unsigned char gch; 405 esctable_t *p; 406 char c = character(value(ESCAPE)); 407 408 gch = (getchar()&0377); 409 for (p = etable; p->e_char; p++) 410 if (p->e_char == gch) { 411 if ((p->e_flags&PRIV) && uid) 412 continue; 413 (void) printf("%s", ctrl(c)); 414 (*p->e_func)(gch); 415 return (0); 416 } 417 /* ESCAPE ESCAPE forces ESCAPE */ 418 if (c != gch) 419 parwrite(FD, (unsigned char *)&c, 1); 420 return (gch); 421 } 422 423 int 424 speed(int n) 425 { 426 int *p; 427 428 for (p = bauds; *p != -1; p++) 429 if (*p == n) 430 return (p - bauds); 431 return (0); 432 } 433 434 int 435 any(char c, char *p) 436 { 437 while (p && *p) 438 if (*p++ == c) 439 return (1); 440 return (0); 441 } 442 443 char * 444 interp(char *s) 445 { 446 static char buf[256]; 447 char *p = buf, c, *q; 448 449 while (c = *s++) { 450 for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++) 451 if (*q++ == c) { 452 *p++ = '\\'; *p++ = *q; 453 goto next; 454 } 455 if (c < 040) { 456 *p++ = '^'; *p++ = c + 'A'-1; 457 } else if (c == 0177) { 458 *p++ = '^'; *p++ = '?'; 459 } else 460 *p++ = c; 461 next: 462 ; 463 } 464 *p = '\0'; 465 return (buf); 466 } 467 468 char * 469 ctrl(char c) 470 { 471 static char s[3]; 472 473 if (c < 040 || c == 0177) { 474 s[0] = '^'; 475 s[1] = c == 0177 ? '?' : c+'A'-1; 476 s[2] = '\0'; 477 } else { 478 s[0] = c; 479 s[1] = '\0'; 480 } 481 return (s); 482 } 483 484 /* 485 * Help command 486 */ 487 void 488 help(int c) 489 { 490 esctable_t *p; 491 492 (void) printf("%c\r\n", c); 493 for (p = etable; p->e_char; p++) { 494 if ((p->e_flags&PRIV) && uid) 495 continue; 496 (void) printf("%2s", ctrl(character(value(ESCAPE)))); 497 (void) printf("%-2s %c %s\r\n", ctrl(p->e_char), 498 p->e_flags&EXP ? '*': ' ', p->e_help); 499 } 500 } 501 502 /* 503 * Set up the "remote" tty's state 504 */ 505 void 506 ttysetup(int speed) 507 { 508 struct termios buf; 509 char *loc; 510 511 (void) ioctl(FD, TCGETS, (char *)&buf); 512 buf.c_cflag &= (CREAD|HUPCL|CLOCAL|CRTSCTS|CRTSXOFF); 513 buf.c_cflag |= CS8; 514 (void) cfsetospeed(&buf, speed); 515 if (boolean(value(HARDWAREFLOW))) { 516 int i = TIOCM_CAR; 517 518 /* 519 * Only set hardware flow control if carrier is up, 520 * because some devices require both CD and RTS to 521 * be up before sending. 522 */ 523 (void) ioctl(FD, TIOCMGET, &i); 524 if (i & TIOCM_CAR) 525 buf.c_cflag |= (CRTSCTS|CRTSXOFF); 526 } 527 528 /* 529 * Careful to only penalize the 8-bit users here on the 530 * incoming tty port. The default 7-bit users will 531 * still get the parity bit from the other side's login 532 * process (which happens to be the default for sun tip 533 * configurations). 534 */ 535 loc = setlocale(LC_CTYPE, NULL); 536 if (noparity && loc != 0 && strcmp(loc, "C") != 0) 537 buf.c_iflag = 0; 538 else 539 buf.c_iflag = ISTRIP; 540 buf.c_oflag = 0; 541 buf.c_lflag = 0; 542 buf.c_cc[VMIN] = 1; 543 buf.c_cc[VTIME] = 0; 544 (void) ioctl(FD, TCSETSF, (char *)&buf); 545 } 546 547 /* 548 * Return "simple" name from a file name, 549 * strip leading directories. 550 */ 551 char * 552 sname(char *s) 553 { 554 char *p = s; 555 556 while (*s) 557 if (*s++ == '/') 558 p = s; 559 return (p); 560 } 561 562 static char partab[0400]; 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 parwrite(int fd, unsigned char *buf, int n) 571 { 572 int i; 573 unsigned char *bp; 574 575 bp = buf; 576 for (i = 0; i < n; i++) { 577 *bp = partab[(*bp)&0377]; 578 bp++; 579 } 580 if (write(fd, buf, n) < 0) { 581 if (errno == EIO || errno == ENXIO) 582 tip_abort("Lost carrier."); 583 /* this is questionable */ 584 perror("write"); 585 } 586 } 587 588 /* 589 * Build a parity table with appropriate high-order bit. 590 */ 591 void 592 setparity(char *defparity) 593 { 594 int i; 595 char *parity; 596 597 if (value(PARITY) == NOSTR) 598 value(PARITY) = defparity; 599 parity = value(PARITY); 600 for (i = 0; i < 0400; i++) 601 partab[i] = evenpartab[i]; 602 if (equal(parity, "even")) { 603 /* EMPTY */ 604 } else if (equal(parity, "odd")) { 605 for (i = 0; i < 0400; i++) 606 partab[i] ^= 0200; /* reverse bit 7 */ 607 } else if (equal(parity, "none")) { 608 /* Do nothing so we can pass thru 8-bit chars */ 609 noparity = 1; 610 for (i = 0; i < 0400; i++) 611 partab[i] = i; 612 } else if (equal(parity, "zero")) { 613 for (i = 0; i < 0400; i++) 614 partab[i] &= ~0200; /* turn off bit 7 */ 615 } else if (equal(parity, "one")) { 616 for (i = 0; i < 0400; i++) 617 partab[i] |= 0200; /* turn on bit 7 */ 618 } else { 619 (void) fprintf(stderr, "%s: unknown parity value\n", PA); 620 (void) fflush(stderr); 621 } 622 } 623