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