1 /* $NetBSD: tip.c,v 1.59 2016/09/05 00:40:30 sevan 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.59 2016/09/05 00:40:30 sevan 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()) != -1 && (*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 * ****TIPIN TIPIN**** 334 */ 335 static void 336 tipin(void) 337 { 338 char gch, bol = 1; 339 340 /* 341 * Kinda klugey here... 342 * check for scripting being turned on from the .tiprc file, 343 * but be careful about just using setscript(), as we may 344 * send a SIGEMT before tipout has a chance to set up catching 345 * it; so wait a second, then setscript() 346 */ 347 if (boolean(value(SCRIPT))) { 348 (void)sleep(1); 349 setscript(); 350 } 351 352 for (;;) { 353 gch = getchar()&STRIP_PAR; 354 if ((gch == character(value(ESCAPE))) && bol) { 355 if (!(gch = escape())) 356 continue; 357 } else if (!cumode && 358 gch && gch == character(value(RAISECHAR))) { 359 setboolean(value(RAISE), !boolean(value(RAISE))); 360 continue; 361 } else if (gch == '\r') { 362 bol = 1; 363 xpwrite(FD, &gch, 1); 364 if (boolean(value(HALFDUPLEX))) 365 (void)printf("\r\n"); 366 continue; 367 } else if (!cumode && gch && gch == character(value(FORCE))) 368 gch = getchar()&STRIP_PAR; 369 bol = any(gch, value(EOL)); 370 if (boolean(value(RAISE)) && islower((unsigned char)gch)) 371 gch = toupper((unsigned char)gch); 372 xpwrite(FD, &gch, 1); 373 if (boolean(value(HALFDUPLEX))) 374 (void)printf("%c", gch); 375 } 376 } 377 378 /* 379 * Escape handler -- 380 * called on recognition of ``escapec'' at the beginning of a line 381 */ 382 int 383 escape(void) 384 { 385 char gch; 386 esctable_t *p; 387 char c = character(value(ESCAPE)); 388 389 gch = (getchar()&STRIP_PAR); 390 for (p = etable; p->e_char; p++) 391 if (p->e_char == gch) { 392 if ((p->e_flags&PRIV) && uid) 393 continue; 394 (void)printf("%s", ctrl(c)); 395 (*p->e_func)(gch); 396 return (0); 397 } 398 /* ESCAPE ESCAPE forces ESCAPE */ 399 if (c != gch) 400 xpwrite(FD, &c, 1); 401 return (gch); 402 } 403 404 int 405 any(char c, const char *p) 406 { 407 408 while (p && *p) 409 if (*p++ == c) 410 return (1); 411 return (0); 412 } 413 414 char * 415 interp(const char *s) 416 { 417 static char buf[256]; 418 char *p = buf, c; 419 const char *q; 420 421 while ((c = *s++) != 0 && buf + sizeof buf - p > 2) { 422 for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++) 423 if (*q++ == c) { 424 *p++ = '\\'; *p++ = *q; 425 goto next; 426 } 427 if (c < 040) { 428 *p++ = '^'; *p++ = c + 'A'-1; 429 } else if (c == 0177) { 430 *p++ = '^'; *p++ = '?'; 431 } else 432 *p++ = c; 433 next: 434 ; 435 } 436 *p = '\0'; 437 return (buf); 438 } 439 440 char * 441 ctrl(char c) 442 { 443 static char s[3]; 444 445 if (c < 040 || c == 0177) { 446 s[0] = '^'; 447 s[1] = c == 0177 ? '?' : c+'A'-1; 448 s[2] = '\0'; 449 } else { 450 s[0] = c; 451 s[1] = '\0'; 452 } 453 return (s); 454 } 455 456 /* 457 * Help command 458 */ 459 void 460 help(char c) 461 { 462 esctable_t *p; 463 464 (void)printf("%c\r\n", c); 465 for (p = etable; p->e_char; p++) { 466 if ((p->e_flags&PRIV) && uid) 467 continue; 468 (void)printf("%2s", ctrl(character(value(ESCAPE)))); 469 (void)printf("%-2s %c %s\r\n", ctrl(p->e_char), 470 p->e_flags&EXP ? '*': ' ', p->e_help); 471 } 472 } 473 474 /* 475 * Set up the "remote" tty's state 476 */ 477 int 478 ttysetup(speed_t spd) 479 { 480 struct termios cntrl; 481 482 (void)tcgetattr(FD, &cntrl); 483 (void)cfsetospeed(&cntrl, spd); 484 (void)cfsetispeed(&cntrl, spd); 485 cntrl.c_cflag &= ~(CSIZE|PARENB); 486 cntrl.c_cflag |= CS8; 487 if (DC) 488 cntrl.c_cflag |= CLOCAL; 489 if (boolean(value(HARDWAREFLOW))) 490 cntrl.c_cflag |= CRTSCTS; 491 cntrl.c_iflag &= ~(ISTRIP|ICRNL); 492 cntrl.c_oflag &= ~OPOST; 493 cntrl.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO); 494 cntrl.c_cc[VMIN] = 1; 495 cntrl.c_cc[VTIME] = 0; 496 if (boolean(value(TAND))) 497 cntrl.c_iflag |= IXOFF|IXON; 498 else 499 cntrl.c_iflag &= ~(IXOFF|IXON); 500 return tcsetattr(FD, TCSAFLUSH, &cntrl); 501 } 502 503 static char partab[0200]; 504 505 /* 506 * Do a write to the remote machine with the correct parity. 507 * We are doing 8 bit wide output, so we just generate a character 508 * with the right parity and output it. 509 */ 510 void 511 xpwrite(int fd, char *buf, size_t n) 512 { 513 size_t i; 514 char *bp; 515 516 bp = buf; 517 if (bits8 == 0) 518 for (i = 0; i < n; i++) { 519 *bp = partab[(*bp) & 0177]; 520 bp++; 521 } 522 if (write(fd, buf, n) < 0) { 523 if (errno == EIO) 524 tipabort("Lost carrier."); 525 /* this is questionable */ 526 warn("write"); 527 } 528 } 529 530 /* 531 * Build a parity table with appropriate high-order bit. 532 */ 533 void 534 setparity(const char *defparity) 535 { 536 int i, flip, clr, set; 537 const char *parity; 538 static char *curpar; 539 540 if (value(PARITY) == NULL || ((char *)value(PARITY))[0] == '\0') { 541 if (curpar != NULL) 542 free(curpar); 543 value(PARITY) = curpar = strdup(defparity); 544 } 545 parity = value(PARITY); 546 if (strcmp(parity, "none") == 0) { 547 bits8 = 1; 548 return; 549 } 550 bits8 = 0; 551 flip = 0; 552 clr = 0377; 553 set = 0; 554 if (strcmp(parity, "odd") == 0) 555 flip = 0200; /* reverse bit 7 */ 556 else if (strcmp(parity, "zero") == 0) 557 clr = 0177; /* turn off bit 7 */ 558 else if (strcmp(parity, "one") == 0) 559 set = 0200; /* turn on bit 7 */ 560 else if (strcmp(parity, "even") != 0) { 561 (void)fprintf(stderr, "%s: unknown parity value\r\n", parity); 562 (void)fflush(stderr); 563 } 564 for (i = 0; i < 0200; i++) 565 partab[i] = ((evenpartab[i] ^ flip) | set) & clr; 566 } 567