1 /* $NetBSD: tip.c,v 1.48 2007/03/09 23:45:21 hubertf 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\n\ 38 The Regents of the University of California. All rights reserved.\n"); 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.48 2007/03/09 23:45:21 hubertf 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 static void tipusage(void); 58 59 int escape(void); 60 int main(int, char **); 61 void intprompt(int); 62 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 gid = getgid(); 80 egid = getegid(); 81 uid = getuid(); 82 euid = geteuid(); 83 if (equal(basename(argv[0]), "cu")) { 84 cumode = 1; 85 cumain(argc, argv); 86 goto cucommon; 87 } 88 89 if (argc > 4) { 90 tipusage(); 91 } 92 if (!isatty(0)) { 93 (void)fprintf(stderr, "%s: must be interactive\n", getprogname()); 94 exit(1); 95 } 96 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 (void)snprintf(brbuf, sizeof(brbuf) -1, "%s%c", brbuf, c); 107 BR = atoi(brbuf); 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 (void)printf("all ports busy\n"); 153 exit(3); 154 } 155 if (i == -1) { 156 errx(3, "link down\n"); 157 } 158 setbuf(stdout, NULL); 159 160 /* 161 * Kludge, their's no easy way to get the initialization 162 * in the right order, so force it here 163 */ 164 if ((PH = getenv("PHONES")) == NULL) 165 PH = path_phones; 166 vinit(); /* init variables */ 167 setparity("none"); /* set the parity table */ 168 169 /* 170 * Hardwired connections require the 171 * line speed set before they make any transmissions 172 * (this is particularly true of things like a DF03-AC) 173 */ 174 if (HW) { 175 if (ttysetup((speed_t)number(value(BAUDRATE))) != 0) { 176 errx(3, "bad baud rate %d", 177 (int)number(value(BAUDRATE))); 178 } 179 } 180 if ((q = tip_connect()) != NULL) { 181 errx(1, "\07%s\n[EOT]\n", q); 182 } 183 if (!HW) { 184 if (ttysetup((speed_t)number(value(BAUDRATE))) != 0) { 185 errx(3, "bad baud rate %d", 186 (int)number(value(BAUDRATE))); 187 } 188 } 189 190 191 cucommon: 192 /* 193 * From here down the code is shared with 194 * the "cu" version of tip. 195 */ 196 197 /* 198 * Direct connections with no carrier require using O_NONBLOCK on 199 * open, but we don't want to keep O_NONBLOCK after open because it 200 * will cause busy waits. 201 */ 202 if (DC && 203 ((fcarg = fcntl(FD, F_GETFL, 0)) < 0 || 204 fcntl(FD, F_SETFL, fcarg & ~O_NONBLOCK) < 0)) { 205 err(1, "can't clear O_NONBLOCK"); 206 } 207 208 (void)tcgetattr(0, &defterm); 209 term = defterm; 210 term.c_lflag &= ~(ICANON|IEXTEN|ECHO); 211 term.c_iflag &= ~(INPCK|ICRNL); 212 term.c_oflag &= ~OPOST; 213 term.c_cc[VMIN] = 1; 214 term.c_cc[VTIME] = 0; 215 defchars = term; 216 term.c_cc[VINTR] = term.c_cc[VQUIT] = term.c_cc[VSUSP] = 217 term.c_cc[VDSUSP] = term.c_cc[VDISCARD] = 218 term.c_cc[VLNEXT] = _POSIX_VDISABLE; 219 raw(); 220 221 (void)pipe(attndes); 222 (void)pipe(fildes); 223 (void)pipe(repdes); 224 (void)signal(SIGALRM, alrmtimeout); 225 226 /* 227 * Everything's set up now: 228 * connection established (hardwired or dialup) 229 * line conditioned (baud rate, mode, etc.) 230 * internal data structures (variables) 231 * so, fork one process for local side and one for remote. 232 */ 233 (void)printf("%s", cumode ? "Connected\r\n" : "\07connected\r\n"); 234 switch (pid = fork()) { 235 default: 236 tipin(); 237 break; 238 case 0: 239 tipout(); 240 break; 241 case -1: 242 err(1, "can't fork"); 243 } 244 /*NOTREACHED*/ 245 exit(0); /* XXX: pacify gcc */ 246 } 247 248 void 249 tipusage(void) 250 { 251 (void)fprintf(stderr, "usage: %s [-v] [-speed] system-name\n", 252 getprogname()); 253 (void)fprintf(stderr, " %s [-v] [-speed] phone-number\n", 254 getprogname()); 255 exit(1); 256 } 257 258 void 259 /*ARGSUSED*/ 260 cleanup(int dummy __unused) 261 { 262 263 if (odisc) 264 (void)ioctl(0, TIOCSETD, &odisc); 265 exit(0); 266 } 267 268 /* 269 * put the controlling keyboard into raw mode 270 */ 271 void 272 raw(void) 273 { 274 275 (void)tcsetattr(0, TCSADRAIN, &term); 276 } 277 278 279 /* 280 * return keyboard to normal mode 281 */ 282 void 283 unraw(void) 284 { 285 286 (void)tcsetattr(0, TCSADRAIN, &defterm); 287 } 288 289 static jmp_buf promptbuf; 290 291 /* 292 * Print string ``s'', then read a string 293 * in from the terminal. Handles signals & allows use of 294 * normal erase and kill characters. 295 */ 296 int 297 prompt(const char *s, char *volatile p, size_t l) 298 { 299 int c; 300 char *b = p; 301 sig_t oint, oquit; 302 303 stoprompt = 0; 304 oint = signal(SIGINT, intprompt); 305 oquit = signal(SIGQUIT, SIG_IGN); 306 unraw(); 307 (void)printf("%s", s); 308 if (setjmp(promptbuf) == 0) 309 while ((c = getchar()) != -1 && (*p = c) != '\n' && 310 b + l > p) 311 p++; 312 *p = '\0'; 313 314 raw(); 315 (void)signal(SIGINT, oint); 316 (void)signal(SIGQUIT, oquit); 317 return (stoprompt || p == b); 318 } 319 320 /* 321 * Interrupt service routine during prompting 322 */ 323 void 324 /*ARGSUSED*/ 325 intprompt(int dummy __unused) 326 { 327 328 (void)signal(SIGINT, SIG_IGN); 329 stoprompt = 1; 330 (void)printf("\r\n"); 331 longjmp(promptbuf, 1); 332 } 333 334 /* 335 * ****TIPIN TIPIN**** 336 */ 337 void 338 tipin(void) 339 { 340 char gch, bol = 1; 341 342 /* 343 * Kinda klugey here... 344 * check for scripting being turned on from the .tiprc file, 345 * but be careful about just using setscript(), as we may 346 * send a SIGEMT before tipout has a chance to set up catching 347 * it; so wait a second, then setscript() 348 */ 349 if (boolean(value(SCRIPT))) { 350 (void)sleep(1); 351 setscript(); 352 } 353 354 for (;;) { 355 gch = getchar()&STRIP_PAR; 356 if ((gch == character(value(ESCAPE))) && bol) { 357 if (!(gch = escape())) 358 continue; 359 } else if (!cumode && 360 gch && gch == character(value(RAISECHAR))) { 361 setboolean(value(RAISE), !boolean(value(RAISE))); 362 continue; 363 } else if (gch == '\r') { 364 bol = 1; 365 xpwrite(FD, &gch, 1); 366 if (boolean(value(HALFDUPLEX))) 367 (void)printf("\r\n"); 368 continue; 369 } else if (!cumode && gch && gch == character(value(FORCE))) 370 gch = getchar()&STRIP_PAR; 371 bol = any(gch, value(EOL)); 372 if (boolean(value(RAISE)) && islower((unsigned char)gch)) 373 gch = toupper((unsigned char)gch); 374 xpwrite(FD, &gch, 1); 375 if (boolean(value(HALFDUPLEX))) 376 (void)printf("%c", gch); 377 } 378 } 379 380 /* 381 * Escape handler -- 382 * called on recognition of ``escapec'' at the beginning of a line 383 */ 384 int 385 escape(void) 386 { 387 char gch; 388 esctable_t *p; 389 char c = character(value(ESCAPE)); 390 391 gch = (getchar()&STRIP_PAR); 392 for (p = etable; p->e_char; p++) 393 if (p->e_char == gch) { 394 if ((p->e_flags&PRIV) && uid) 395 continue; 396 (void)printf("%s", ctrl(c)); 397 (*p->e_func)(gch); 398 return (0); 399 } 400 /* ESCAPE ESCAPE forces ESCAPE */ 401 if (c != gch) 402 xpwrite(FD, &c, 1); 403 return (gch); 404 } 405 406 int 407 any(char c, const char *p) 408 { 409 410 while (p && *p) 411 if (*p++ == c) 412 return (1); 413 return (0); 414 } 415 416 char * 417 interp(const char *s) 418 { 419 static char buf[256]; 420 char *p = buf, c; 421 const char *q; 422 423 while ((c = *s++) != 0 && buf + sizeof buf - p > 2) { 424 for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++) 425 if (*q++ == c) { 426 *p++ = '\\'; *p++ = *q; 427 goto next; 428 } 429 if (c < 040) { 430 *p++ = '^'; *p++ = c + 'A'-1; 431 } else if (c == 0177) { 432 *p++ = '^'; *p++ = '?'; 433 } else 434 *p++ = c; 435 next: 436 ; 437 } 438 *p = '\0'; 439 return (buf); 440 } 441 442 char * 443 ctrl(char c) 444 { 445 static char s[3]; 446 447 if (c < 040 || c == 0177) { 448 s[0] = '^'; 449 s[1] = c == 0177 ? '?' : c+'A'-1; 450 s[2] = '\0'; 451 } else { 452 s[0] = c; 453 s[1] = '\0'; 454 } 455 return (s); 456 } 457 458 /* 459 * Help command 460 */ 461 void 462 help(char c) 463 { 464 esctable_t *p; 465 466 (void)printf("%c\r\n", c); 467 for (p = etable; p->e_char; p++) { 468 if ((p->e_flags&PRIV) && uid) 469 continue; 470 (void)printf("%2s", ctrl(character(value(ESCAPE)))); 471 (void)printf("%-2s %c %s\r\n", ctrl(p->e_char), 472 p->e_flags&EXP ? '*': ' ', p->e_help); 473 } 474 } 475 476 /* 477 * Set up the "remote" tty's state 478 */ 479 int 480 ttysetup(speed_t spd) 481 { 482 struct termios cntrl; 483 484 (void)tcgetattr(FD, &cntrl); 485 (void)cfsetospeed(&cntrl, spd); 486 (void)cfsetispeed(&cntrl, spd); 487 cntrl.c_cflag &= ~(CSIZE|PARENB); 488 cntrl.c_cflag |= CS8; 489 if (DC) 490 cntrl.c_cflag |= CLOCAL; 491 if (boolean(value(HARDWAREFLOW))) 492 cntrl.c_cflag |= CRTSCTS; 493 cntrl.c_iflag &= ~(ISTRIP|ICRNL); 494 cntrl.c_oflag &= ~OPOST; 495 cntrl.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO); 496 cntrl.c_cc[VMIN] = 1; 497 cntrl.c_cc[VTIME] = 0; 498 if (boolean(value(TAND))) 499 cntrl.c_iflag |= IXOFF; 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 int 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 (equal(parity, "none")) { 547 bits8 = 1; 548 return; 549 } 550 bits8 = 0; 551 flip = 0; 552 clr = 0377; 553 set = 0; 554 if (equal(parity, "odd")) 555 flip = 0200; /* reverse bit 7 */ 556 else if (equal(parity, "zero")) 557 clr = 0177; /* turn off bit 7 */ 558 else if (equal(parity, "one")) 559 set = 0200; /* turn on bit 7 */ 560 else if (!equal(parity, "even")) { 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