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