1 /* 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#) Copyright (c) 1983, 1993 The Regents of the University of California. All rights reserved. 30 * @(#)tip.c 8.1 (Berkeley) 6/6/93 31 * $FreeBSD: src/usr.bin/tip/tip/tip.c,v 1.12.2.2 2001/06/02 08:08:24 phk Exp $ 32 */ 33 34 /* 35 Forward declarations 36 */ 37 void ttysetup (int speed); 38 39 /* 40 * tip - UNIX link to other systems 41 * tip [-v] [-speed] system-name 42 * or 43 * cu phone-number [-s speed] [-l line] [-a acu] 44 */ 45 46 #include <err.h> 47 #include <errno.h> 48 #include <sys/types.h> 49 #include <libutil.h> 50 #include "tip.h" 51 #include "pathnames.h" 52 53 static void intprompt(int); 54 static void killchild(void); 55 static void tipdone(int); 56 static char *sname(char *); 57 char PNbuf[256]; /* This limits the size of a number */ 58 59 static void usage(void); 60 void setparity(char *); 61 void xpwrite(int, char *, int); 62 char escape(void); 63 void tipin(void); 64 int prompt(char *, char *, size_t); 65 void unraw(void); 66 void shell_uid(void); 67 void daemon_uid(void); 68 void user_uid(void); 69 70 int 71 main(int argc, char *argv[]) 72 { 73 char *system = NULL; 74 int i; 75 char *p; 76 char sbuf[12]; 77 78 gid = getgid(); 79 egid = getegid(); 80 uid = getuid(); 81 euid = geteuid(); 82 83 if (equal(sname(argv[0]), "cu")) { 84 cumode = 1; 85 cumain(argc, argv); 86 goto cucommon; 87 } 88 89 if (argc > 4) 90 usage(); 91 if (!isatty(0)) 92 errx(1, "must be interactive"); 93 94 for (; argc > 1; argv++, argc--) { 95 if (argv[1][0] != '-') 96 system = argv[1]; 97 else switch (argv[1][1]) { 98 99 case 'v': 100 vflag++; 101 break; 102 103 case '0': case '1': case '2': case '3': case '4': 104 case '5': case '6': case '7': case '8': case '9': 105 BR = atoi(&argv[1][1]); 106 break; 107 108 default: 109 warnx("%s, unknown option", argv[1]); 110 break; 111 } 112 } 113 114 if (system == NULL) 115 goto notnumber; 116 if (isalpha(*system)) 117 goto notnumber; 118 /* 119 * System name is really a phone number... 120 * Copy the number then stomp on the original (in case the number 121 * is private, we don't want 'ps' or 'w' to find it). 122 */ 123 if (strlen(system) > sizeof(PNbuf) - 1) 124 errx(1, "phone number too long (max = %zd bytes)", sizeof PNbuf - 1); 125 strncpy(PNbuf, system, sizeof(PNbuf) - 1); 126 for (p = system; *p; p++) 127 *p = '\0'; 128 PN = PNbuf; 129 (void)snprintf(sbuf, sizeof(sbuf), "tip%ld", BR); 130 system = sbuf; 131 132 notnumber: 133 (void)signal(SIGINT, cleanup); 134 (void)signal(SIGQUIT, cleanup); 135 (void)signal(SIGHUP, cleanup); 136 (void)signal(SIGTERM, cleanup); 137 (void)signal(SIGUSR1, tipdone); 138 139 if ((i = hunt(system)) == 0) { 140 printf("all ports busy\n"); 141 exit(3); 142 } 143 if (i == -1) { 144 printf("link down\n"); 145 (void)uu_unlock(uucplock); 146 exit(3); 147 } 148 setbuf(stdout, NULL); 149 loginit(); 150 151 /* 152 * Kludge, their's no easy way to get the initialization 153 * in the right order, so force it here 154 */ 155 if ((PH = getenv("PHONES")) == NULL) 156 PH = _PATH_PHONES; 157 vinit(); /* init variables */ 158 setparity("even"); /* set the parity table */ 159 if ((i = speed(number(value(BAUDRATE)))) == 0) { 160 printf("tip: bad baud rate %d\n", number(value(BAUDRATE))); 161 (void)uu_unlock(uucplock); 162 exit(3); 163 } 164 165 /* 166 * Now that we have the logfile and the ACU open 167 * return to the real uid and gid. These things will 168 * be closed on exit. Swap real and effective uid's 169 * so we can get the original permissions back 170 * for removing the uucp lock. 171 */ 172 user_uid(); 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 printf("\07%s\n[EOT]\n", p); 183 daemon_uid(); 184 (void)uu_unlock(uucplock); 185 exit(1); 186 } 187 if (!HW) 188 ttysetup(i); 189 cucommon: 190 /* 191 * From here down the code is shared with 192 * the "cu" version of tip. 193 */ 194 195 tcgetattr (0, &otermios); 196 ctermios = otermios; 197 ctermios.c_iflag = (IMAXBEL|IXANY|ISTRIP|IXON|BRKINT); 198 ctermios.c_lflag = (PENDIN|IEXTEN|ISIG|ECHOCTL|ECHOE|ECHOKE); 199 ctermios.c_cflag = (CLOCAL|HUPCL|CREAD|CS8); 200 ctermios.c_cc[VINTR] = ctermios.c_cc[VQUIT] = -1; 201 ctermios.c_cc[VSUSP] = ctermios.c_cc[VDSUSP] = ctermios.c_cc[VDISCARD] = 202 ctermios.c_cc[VLNEXT] = -1; 203 raw(); 204 205 pipe(fildes); pipe(repdes); 206 (void)signal(SIGALRM, timeoutfunc); 207 208 /* 209 * Everything's set up now: 210 * connection established (hardwired or dialup) 211 * line conditioned (baud rate, mode, etc.) 212 * internal data structures (variables) 213 * so, fork one process for local side and one for remote. 214 */ 215 printf(cumode ? "Connected\r\n" : "\07connected\r\n"); 216 217 if (LI != NULL && tiplink (LI, 0) != 0) { 218 tipabort ("login failed"); 219 } 220 221 if ((pid = fork())) 222 tipin(); 223 else 224 tipout(); 225 /*NOTREACHED*/ 226 } 227 228 static void 229 usage(void) 230 { 231 fprintf(stderr, "usage: tip [-v] [-speed] [system-name]\n"); 232 exit(1); 233 } 234 235 void 236 killchild(void) 237 { 238 if (pid != 0) { 239 kill(pid, SIGTERM); 240 pid = 0; 241 } 242 } 243 244 void 245 cleanup(int signo) 246 { 247 248 daemon_uid(); 249 (void)uu_unlock(uucplock); 250 exit(0); 251 } 252 253 void 254 tipdone(int signo) 255 { 256 tipabort("Hangup."); 257 } 258 /* 259 * Muck with user ID's. We are setuid to the owner of the lock 260 * directory when we start. user_uid() reverses real and effective 261 * ID's after startup, to run with the user's permissions. 262 * daemon_uid() switches back to the privileged uid for unlocking. 263 * Finally, to avoid running a shell with the wrong real uid, 264 * shell_uid() sets real and effective uid's to the user's real ID. 265 */ 266 static int uidswapped; 267 268 void 269 user_uid(void) 270 { 271 if (uidswapped == 0) { 272 seteuid(uid); 273 uidswapped = 1; 274 } 275 } 276 277 void 278 daemon_uid(void) 279 { 280 if (uidswapped) { 281 seteuid(euid); 282 uidswapped = 0; 283 } 284 } 285 286 void 287 shell_uid(void) 288 { 289 setegid(gid); 290 seteuid(uid); 291 } 292 293 /* 294 * put the controlling keyboard into raw mode 295 */ 296 void 297 raw(void) 298 { 299 tcsetattr (0, TCSANOW, &ctermios); 300 } 301 302 303 /* 304 * return keyboard to normal mode 305 */ 306 void 307 unraw(void) 308 { 309 tcsetattr (0, TCSANOW, &otermios); 310 } 311 312 static jmp_buf promptbuf; 313 314 /* 315 * Print string ``s'', then read a string 316 * in from the terminal. Handles signals & allows use of 317 * normal erase and kill characters. 318 */ 319 int 320 prompt(char *s, char *p, size_t sz) 321 { 322 char *b = p; 323 sig_t oint, oquit; 324 325 stoprompt = 0; 326 oint = signal(SIGINT, intprompt); 327 oquit = signal(SIGQUIT, SIG_IGN); 328 unraw(); 329 printf("%s", s); 330 if (setjmp(promptbuf) == 0) 331 while ((*p = getchar()) != EOF && *p != '\n' && --sz > 0) 332 p++; 333 *p = '\0'; 334 335 raw(); 336 (void)signal(SIGINT, oint); 337 (void)signal(SIGQUIT, oquit); 338 return (stoprompt || p == b); 339 } 340 341 /* 342 * Interrupt service routine during prompting 343 */ 344 void 345 intprompt(int signo) 346 { 347 348 (void)signal(SIGINT, SIG_IGN); 349 stoprompt = 1; 350 printf("\r\n"); 351 longjmp(promptbuf, 1); 352 } 353 354 /* 355 * ****TIPIN TIPIN**** 356 */ 357 void 358 tipin(void) 359 { 360 int i; 361 char gch, bol = 1; 362 363 atexit(killchild); 364 365 /* 366 * Kinda klugey here... 367 * check for scripting being turned on from the .tiprc file, 368 * but be careful about just using setscript(), as we may 369 * send a SIGEMT before tipout has a chance to set up catching 370 * it; so wait a second, then setscript() 371 */ 372 if (boolean(value(SCRIPT))) { 373 sleep(1); 374 setscript(); 375 } 376 377 while (1) { 378 i = getchar(); 379 if (i == EOF) 380 break; 381 gch = i&0177; 382 if ((gch == character(value(ESCAPE))) && bol) { 383 if (!(gch = escape())) 384 continue; 385 } else if (!cumode && gch == character(value(RAISECHAR))) { 386 boolean(value(RAISE)) = !boolean(value(RAISE)); 387 continue; 388 } else if (gch == '\r') { 389 bol = 1; 390 xpwrite(FD, &gch, 1); 391 if (boolean(value(HALFDUPLEX))) 392 printf("\r\n"); 393 continue; 394 } else if (!cumode && gch == character(value(FORCE))) { 395 i = getchar(); 396 if (i == EOF) 397 break; 398 gch = i & 0177; 399 } 400 bol = any(gch, value(EOL)); 401 if (boolean(value(RAISE)) && islower(gch)) 402 gch = toupper(gch); 403 xpwrite(FD, &gch, 1); 404 if (boolean(value(HALFDUPLEX))) 405 printf("%c", gch); 406 } 407 } 408 409 extern esctable_t etable[]; 410 411 /* 412 * Escape handler -- 413 * called on recognition of ``escapec'' at the beginning of a line 414 */ 415 char 416 escape(void) 417 { 418 char gch; 419 esctable_t *p; 420 char c = character(value(ESCAPE)); 421 int i; 422 423 i = getchar(); 424 if (i == EOF) 425 return 0; 426 gch = (i&0177); 427 for (p = etable; p->e_char; p++) 428 if (p->e_char == gch) { 429 if ((p->e_flags&PRIV) && uid) 430 continue; 431 printf("%s", ctrl(c)); 432 (*p->e_func)(gch); 433 return (0); 434 } 435 /* ESCAPE ESCAPE forces ESCAPE */ 436 if (c != gch) 437 xpwrite(FD, &c, 1); 438 return (gch); 439 } 440 441 int 442 speed(int n) 443 { 444 return (n); 445 } 446 447 int 448 any(char c, char *p) 449 { 450 while (p && *p) 451 if (*p++ == c) 452 return (1); 453 return (0); 454 } 455 456 int 457 size(char *s) 458 { 459 int i = 0; 460 461 while (s && *s++) 462 i++; 463 return (i); 464 } 465 466 char * 467 interp(char *s) 468 { 469 static char buf[256]; 470 char *p = buf, c, *q; 471 472 while ((c = *s++)) { 473 for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++) 474 if (*q++ == c) { 475 *p++ = '\\'; *p++ = *q; 476 goto next; 477 } 478 if (c < 040) { 479 *p++ = '^'; *p++ = c + 'A'-1; 480 } else if (c == 0177) { 481 *p++ = '^'; *p++ = '?'; 482 } else 483 *p++ = c; 484 next: 485 ; 486 } 487 *p = '\0'; 488 return (buf); 489 } 490 491 char * 492 ctrl(char c) 493 { 494 static char s[3]; 495 496 if (c < 040 || c == 0177) { 497 s[0] = '^'; 498 s[1] = c == 0177 ? '?' : c+'A'-1; 499 s[2] = '\0'; 500 } else { 501 s[0] = c; 502 s[1] = '\0'; 503 } 504 return (s); 505 } 506 507 /* 508 * Help command 509 */ 510 void 511 help(int c) 512 { 513 esctable_t *p; 514 515 printf("%c\r\n", c); 516 for (p = etable; p->e_char; p++) { 517 if ((p->e_flags&PRIV) && uid) 518 continue; 519 printf("%2s", ctrl(character(value(ESCAPE)))); 520 printf("%-2s %c %s\r\n", ctrl(p->e_char), 521 (p->e_flags&EXP) ? '*': ' ', p->e_help); 522 } 523 } 524 525 /* 526 * Set up the "remote" tty's state 527 */ 528 void 529 ttysetup (int speed) 530 { 531 struct termios termios; 532 tcgetattr (FD, &termios); 533 if (boolean(value(TAND))) 534 termios.c_iflag = IXOFF; 535 else 536 termios.c_iflag = 0; 537 termios.c_lflag = (PENDIN|ECHOKE|ECHOE); 538 termios.c_cflag = (CLOCAL|HUPCL|CREAD|CS8); 539 termios.c_ispeed = termios.c_ospeed = speed; 540 tcsetattr (FD, TCSANOW, &termios); 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[0200]; 559 static int bits8; 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 xpwrite(int fd, char *buf, int n) 568 { 569 int i; 570 char *bp; 571 572 bp = buf; 573 if (bits8 == 0) 574 for (i = 0; i < n; i++) { 575 *bp = partab[(*bp) & 0177]; 576 bp++; 577 } 578 if (write(fd, buf, n) < 0) { 579 if (errno == EIO) 580 tipabort("Lost carrier."); 581 if (errno == ENODEV) 582 tipabort("tty not available."); 583 tipabort("Something wrong..."); 584 } 585 } 586 587 /* 588 * Build a parity table with appropriate high-order bit. 589 */ 590 void 591 setparity(char *defparity) 592 { 593 int i, flip, clr, set; 594 char *parity; 595 extern char evenpartab[]; 596 597 if (value(PARITY) == NULL) 598 value(PARITY) = defparity; 599 parity = value(PARITY); 600 if (equal(parity, "none")) { 601 bits8 = 1; 602 return; 603 } 604 bits8 = 0; 605 flip = 0; 606 clr = 0377; 607 set = 0; 608 if (equal(parity, "odd")) 609 flip = 0200; /* reverse bit 7 */ 610 else if (equal(parity, "zero")) 611 clr = 0177; /* turn off bit 7 */ 612 else if (equal(parity, "one")) 613 set = 0200; /* turn on bit 7 */ 614 else if (!equal(parity, "even")) { 615 (void) fprintf(stderr, "%s: unknown parity value\r\n", parity); 616 (void) fflush(stderr); 617 } 618 for (i = 0; i < 0200; i++) 619 partab[i] = (evenpartab[i] ^ flip) | (set & clr); 620 } 621