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