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