1 /* $NetBSD: tip.c,v 1.62 2019/02/28 17:41:27 gson 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.62 2019/02/28 17:41:27 gson 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 __dead static void tipusage(void); 58 59 int escape(void); 60 __dead static void intprompt(int); 61 __dead static void tipin(void); 62 63 char PNbuf[256]; /* This limits the size of a number */ 64 65 static char path_phones[] = _PATH_PHONES; 66 67 int 68 main(int argc, char *argv[]) 69 { 70 char *System = NULL; 71 int c, i; 72 char *p; 73 const char *q; 74 char sbuf[12]; 75 int cmdlineBR; 76 int fcarg; 77 78 setprogname(argv[0]); 79 gid = getgid(); 80 egid = getegid(); 81 uid = getuid(); 82 euid = geteuid(); 83 if (strcmp(getprogname(), "cu") == 0) { 84 cumode = 1; 85 cumain(argc, argv); 86 goto cucommon; 87 } 88 89 if (argc > 4) 90 tipusage(); 91 92 if (!isatty(0)) 93 errx(EXIT_FAILURE, "must be interactive"); 94 95 cmdlineBR = 0; 96 while((c = getopt(argc, argv, "v0123456789")) != -1) { 97 switch(c) { 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 cmdlineBR = cmdlineBR * 10 + (c - '0'); 106 BR = cmdlineBR; 107 break; 108 109 default: 110 warnx("%s, unknown option", argv[1]); 111 break; 112 } 113 } 114 115 argc -= optind; 116 argv += optind; 117 118 if (argc != 1) 119 tipusage(); 120 else 121 System = argv[0]; 122 123 124 if (System == NULL) 125 goto notnumber; 126 if (isalpha((unsigned char)*System)) 127 goto notnumber; 128 /* 129 * System name is really a phone number... 130 * Copy the number then stomp on the original (in case the number 131 * is private, we don't want 'ps' or 'w' to find it). 132 */ 133 if (strlen(System) > sizeof PNbuf - 1) { 134 errx(1, "phone number too long (max = %d bytes)", 135 (int)sizeof(PNbuf) - 1); 136 } 137 (void)strlcpy(PNbuf, System, sizeof(PNbuf)); 138 for (p = System; *p; p++) 139 *p = '\0'; 140 PN = PNbuf; 141 (void)snprintf(sbuf, sizeof sbuf, "tip%d", (int)BR); 142 System = sbuf; 143 144 notnumber: 145 (void)signal(SIGINT, cleanup); 146 (void)signal(SIGQUIT, cleanup); 147 (void)signal(SIGHUP, cleanup); 148 (void)signal(SIGTERM, cleanup); 149 150 if ((i = hunt(System)) == 0) { 151 errx(3, "all ports busy"); 152 } 153 if (i == -1) { 154 errx(3, "link down"); 155 } 156 setbuf(stdout, NULL); 157 158 /* 159 * Kludge, their's no easy way to get the initialization 160 * in the right order, so force it here 161 */ 162 if ((PH = getenv("PHONES")) == NULL) 163 PH = path_phones; 164 vinit(); /* init variables */ 165 setparity("none"); /* set the parity table */ 166 167 /* 168 * Hardwired connections require the 169 * line speed set before they make any transmissions 170 * (this is particularly true of things like a DF03-AC) 171 */ 172 if (HW) { 173 if (ttysetup((speed_t)number(value(BAUDRATE))) != 0) { 174 errx(3, "bad baud rate %d", 175 (int)number(value(BAUDRATE))); 176 } 177 } 178 if ((q = tip_connect()) != NULL) { 179 errx(1, "\07%s\n[EOT]", q); 180 } 181 if (!HW) { 182 if (ttysetup((speed_t)number(value(BAUDRATE))) != 0) { 183 errx(3, "bad baud rate %d", 184 (int)number(value(BAUDRATE))); 185 } 186 } 187 188 189 cucommon: 190 /* 191 * From here down the code is shared with 192 * the "cu" version of tip. 193 */ 194 195 /* 196 * Direct connections with no carrier require using O_NONBLOCK on 197 * open, but we don't want to keep O_NONBLOCK after open because it 198 * will cause busy waits. 199 */ 200 if (DC && 201 ((fcarg = fcntl(FD, F_GETFL, 0)) < 0 || 202 fcntl(FD, F_SETFL, fcarg & ~O_NONBLOCK) < 0)) { 203 err(1, "can't clear O_NONBLOCK"); 204 } 205 206 (void)tcgetattr(0, &defterm); 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 (void)pipe(attndes); 220 (void)pipe(fildes); 221 (void)pipe(repdes); 222 (void)signal(SIGALRM, alrmtimeout); 223 224 /* 225 * Everything's set up now: 226 * connection established (hardwired or dialup) 227 * line conditioned (baud rate, mode, etc.) 228 * internal data structures (variables) 229 * so, fork one process for local side and one for remote. 230 */ 231 (void)printf("%s", cumode ? "Connected\r\n" : "\07connected\r\n"); 232 switch (pid = fork()) { 233 default: 234 tipin(); 235 break; 236 case 0: 237 tipout(); 238 break; 239 case -1: 240 err(1, "can't fork"); 241 } 242 /*NOTREACHED*/ 243 exit(0); /* XXX: pacify gcc */ 244 } 245 246 void 247 tipusage(void) 248 { 249 (void)fprintf(stderr, "usage: %s [-v] [-speed] system-name\n", 250 getprogname()); 251 (void)fprintf(stderr, " %s [-v] [-speed] phone-number\n", 252 getprogname()); 253 exit(1); 254 } 255 256 void 257 /*ARGSUSED*/ 258 cleanup(int dummy __unused) 259 { 260 261 if (odisc) 262 (void)ioctl(0, TIOCSETD, &odisc); 263 _exit(0); 264 } 265 266 /* 267 * put the controlling keyboard into raw mode 268 */ 269 void 270 raw(void) 271 { 272 273 (void)tcsetattr(0, TCSADRAIN, &term); 274 } 275 276 277 /* 278 * return keyboard to normal mode 279 */ 280 void 281 unraw(void) 282 { 283 284 (void)tcsetattr(0, TCSADRAIN, &defterm); 285 } 286 287 static jmp_buf promptbuf; 288 289 /* 290 * Print string ``s'', then read a string 291 * in from the terminal. Handles signals & allows use of 292 * normal erase and kill characters. 293 */ 294 int 295 prompt(const char *s, char *volatile p, size_t l) 296 { 297 int c; 298 char *b = p; 299 sig_t oint, oquit; 300 301 stoprompt = 0; 302 oint = signal(SIGINT, intprompt); 303 oquit = signal(SIGQUIT, SIG_IGN); 304 unraw(); 305 (void)printf("%s", s); 306 if (setjmp(promptbuf) == 0) 307 while ((c = getchar()) != EOF && (*p = c) != '\n' && 308 b + l > p) 309 p++; 310 *p = '\0'; 311 312 raw(); 313 (void)signal(SIGINT, oint); 314 (void)signal(SIGQUIT, oquit); 315 return (stoprompt || p == b); 316 } 317 318 /* 319 * Interrupt service routine during prompting 320 */ 321 static void 322 /*ARGSUSED*/ 323 intprompt(int dummy __unused) 324 { 325 326 (void)signal(SIGINT, SIG_IGN); 327 stoprompt = 1; 328 (void)printf("\r\n"); 329 longjmp(promptbuf, 1); 330 } 331 332 /* 333 * getchar() wrapper that checks for EOF on the local end. 334 */ 335 static int 336 xgetchar(void) 337 { 338 int c = getchar(); 339 if (__predict_false(c == EOF)) { 340 cleanup(SIGHUP); 341 /* NOTREACHED */ 342 } 343 344 return c & STRIP_PAR; 345 } 346 347 348 /* 349 * ****TIPIN TIPIN**** 350 */ 351 static void 352 tipin(void) 353 { 354 char gch, bol = 1; 355 356 /* 357 * Kinda klugey here... 358 * check for scripting being turned on from the .tiprc file, 359 * but be careful about just using setscript(), as we may 360 * send a SIGEMT before tipout has a chance to set up catching 361 * it; so wait a second, then setscript() 362 */ 363 if (boolean(value(SCRIPT))) { 364 (void)sleep(1); 365 setscript(); 366 } 367 368 for (;;) { 369 gch = xgetchar(); 370 if ((gch == character(value(ESCAPE))) && bol) { 371 if (!(gch = escape())) 372 continue; 373 } else if (!cumode && 374 gch && gch == character(value(RAISECHAR))) { 375 setboolean(value(RAISE), !boolean(value(RAISE))); 376 continue; 377 } else if (gch == '\r' || gch == '\n') { 378 bol = 1; 379 xpwrite(FD, &gch, 1); 380 if (boolean(value(HALFDUPLEX))) 381 (void)printf("%s\n", gch == '\r' ? "\r" : ""); 382 continue; 383 } else if (!cumode && gch && gch == character(value(FORCE))) 384 gch = xgetchar(); 385 bol = any(gch, value(EOL)); 386 if (boolean(value(RAISE)) && islower((unsigned char)gch)) 387 gch = toupper((unsigned char)gch); 388 xpwrite(FD, &gch, 1); 389 if (boolean(value(HALFDUPLEX))) 390 (void)printf("%c", gch); 391 } 392 } 393 394 /* 395 * Escape handler -- 396 * called on recognition of ``escapec'' at the beginning of a line 397 */ 398 int 399 escape(void) 400 { 401 char gch; 402 esctable_t *p; 403 char c = character(value(ESCAPE)); 404 405 gch = xgetchar(); 406 for (p = etable; p->e_char; p++) 407 if (p->e_char == gch) { 408 if ((p->e_flags&PRIV) && uid) 409 continue; 410 (void)printf("%s", ctrl(c)); 411 (*p->e_func)(gch); 412 return (0); 413 } 414 /* ESCAPE ESCAPE forces ESCAPE */ 415 if (c != gch) 416 xpwrite(FD, &c, 1); 417 return (gch); 418 } 419 420 int 421 any(char c, const char *p) 422 { 423 424 while (p && *p) 425 if (*p++ == c) 426 return (1); 427 return (0); 428 } 429 430 char * 431 interp(const char *s) 432 { 433 static char buf[256]; 434 char *p = buf, c; 435 const char *q; 436 437 while ((c = *s++) != 0 && buf + sizeof buf - p > 2) { 438 for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++) 439 if (*q++ == c) { 440 *p++ = '\\'; *p++ = *q; 441 goto next; 442 } 443 if (c < 040) { 444 *p++ = '^'; *p++ = c + 'A'-1; 445 } else if (c == 0177) { 446 *p++ = '^'; *p++ = '?'; 447 } else 448 *p++ = c; 449 next: 450 ; 451 } 452 *p = '\0'; 453 return (buf); 454 } 455 456 char * 457 ctrl(char c) 458 { 459 static char s[3]; 460 461 if (c < 040 || c == 0177) { 462 s[0] = '^'; 463 s[1] = c == 0177 ? '?' : c+'A'-1; 464 s[2] = '\0'; 465 } else { 466 s[0] = c; 467 s[1] = '\0'; 468 } 469 return (s); 470 } 471 472 /* 473 * Help command 474 */ 475 void 476 help(char c) 477 { 478 esctable_t *p; 479 480 (void)printf("%c\r\n", c); 481 for (p = etable; p->e_char; p++) { 482 if ((p->e_flags&PRIV) && uid) 483 continue; 484 (void)printf("%2s", ctrl(character(value(ESCAPE)))); 485 (void)printf("%-2s %c %s\r\n", ctrl(p->e_char), 486 p->e_flags&EXP ? '*': ' ', p->e_help); 487 } 488 } 489 490 /* 491 * Set up the "remote" tty's state 492 */ 493 int 494 ttysetup(speed_t spd) 495 { 496 struct termios cntrl; 497 498 (void)tcgetattr(FD, &cntrl); 499 (void)cfsetospeed(&cntrl, spd); 500 (void)cfsetispeed(&cntrl, spd); 501 cntrl.c_cflag &= ~(CSIZE|PARENB); 502 cntrl.c_cflag |= CS8; 503 if (DC) 504 cntrl.c_cflag |= CLOCAL; 505 if (boolean(value(HARDWAREFLOW))) 506 cntrl.c_cflag |= CRTSCTS; 507 cntrl.c_iflag &= ~(ISTRIP|ICRNL); 508 cntrl.c_oflag &= ~OPOST; 509 cntrl.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO); 510 cntrl.c_cc[VMIN] = 1; 511 cntrl.c_cc[VTIME] = 0; 512 if (boolean(value(TAND))) 513 cntrl.c_iflag |= IXOFF|IXON; 514 else 515 cntrl.c_iflag &= ~(IXOFF|IXON); 516 return tcsetattr(FD, TCSAFLUSH, &cntrl); 517 } 518 519 static char partab[0200]; 520 521 /* 522 * Do a write to the remote machine with the correct parity. 523 * We are doing 8 bit wide output, so we just generate a character 524 * with the right parity and output it. 525 */ 526 void 527 xpwrite(int fd, char *buf, size_t n) 528 { 529 size_t i; 530 char *bp; 531 532 bp = buf; 533 if (bits8 == 0) 534 for (i = 0; i < n; i++) { 535 *bp = partab[(*bp) & 0177]; 536 bp++; 537 } 538 if (write(fd, buf, n) < 0) { 539 if (errno == EIO) 540 tipabort("Lost carrier."); 541 /* this is questionable */ 542 warn("write"); 543 } 544 } 545 546 /* 547 * Build a parity table with appropriate high-order bit. 548 */ 549 void 550 setparity(const char *defparity) 551 { 552 int i, flip, clr, set; 553 const char *parity; 554 static char *curpar; 555 556 if (value(PARITY) == NULL || ((char *)value(PARITY))[0] == '\0') { 557 if (curpar != NULL) 558 free(curpar); 559 value(PARITY) = curpar = strdup(defparity); 560 } 561 parity = value(PARITY); 562 if (strcmp(parity, "none") == 0) { 563 bits8 = 1; 564 return; 565 } 566 bits8 = 0; 567 flip = 0; 568 clr = 0377; 569 set = 0; 570 if (strcmp(parity, "odd") == 0) 571 flip = 0200; /* reverse bit 7 */ 572 else if (strcmp(parity, "zero") == 0) 573 clr = 0177; /* turn off bit 7 */ 574 else if (strcmp(parity, "one") == 0) 575 set = 0200; /* turn on bit 7 */ 576 else if (strcmp(parity, "even") != 0) { 577 (void)fprintf(stderr, "%s: unknown parity value\r\n", parity); 578 (void)fflush(stderr); 579 } 580 for (i = 0; i < 0200; i++) 581 partab[i] = ((evenpartab[i] ^ flip) | set) & clr; 582 } 583