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