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