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