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