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