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