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