Annotation of src/usr.bin/tip/tip.c, Revision 1.35
1.35 ! deraadt 1: /* $OpenBSD: tip.c,v 1.34 2008/06/01 18:38:29 sobrado Exp $ */
1.5 millert 2: /* $NetBSD: tip.c,v 1.13 1997/04/20 00:03:05 mellon Exp $ */
1.1 deraadt 3:
4: /*
5: * Copyright (c) 1983, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
1.20 millert 16: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 17: * may be used to endorse or promote products derived from this software
18: * without specific prior written permission.
19: *
20: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30: * SUCH DAMAGE.
31: */
32:
33: /*
34: * tip - UNIX link to other systems
35: * tip [-v] [-speed] system-name
36: * or
37: * cu phone-number [-s speed] [-l line] [-a acu]
38: */
39: #include "tip.h"
40: #include "pathnames.h"
41:
1.2 deraadt 42: int disc = TTYDISC; /* tip normally runs this way */
1.1 deraadt 43: char PNbuf[256]; /* This limits the size of a number */
44:
1.26 moritz 45: static void intprompt(int);
46: static void tipin(void);
47: static int escape(void);
48:
1.8 deraadt 49: int
1.25 deraadt 50: main(int argc, char *argv[])
1.1 deraadt 51: {
1.33 moritz 52: char *sys = NULL, sbuf[12], *p;
1.15 millert 53: int i;
1.32 mcbride 54:
55: /* XXX preserve previous braindamaged behavior */
56: setboolean(value(DC), TRUE);
1.1 deraadt 57:
58: gid = getgid();
59: egid = getegid();
60: uid = getuid();
61: euid = geteuid();
1.13 millert 62: if (equal(__progname, "cu")) {
1.1 deraadt 63: cumode = 1;
64: cumain(argc, argv);
65: goto cucommon;
66: }
67:
68: if (argc > 4) {
1.34 sobrado 69: fprintf(stderr, "usage: tip [-nv] [-speed] [system-name]\n");
1.1 deraadt 70: exit(1);
71: }
72: if (!isatty(0)) {
1.13 millert 73: fprintf(stderr, "%s: must be interactive\n", __progname);
1.1 deraadt 74: exit(1);
75: }
76:
77: for (; argc > 1; argv++, argc--) {
78: if (argv[1][0] != '-')
1.27 deraadt 79: sys = argv[1];
1.1 deraadt 80: else switch (argv[1][1]) {
81:
82: case 'v':
83: vflag++;
84: break;
85:
1.9 todd 86: case 'n':
87: noesc++;
88: break;
89:
1.1 deraadt 90: case '0': case '1': case '2': case '3': case '4':
91: case '5': case '6': case '7': case '8': case '9':
92: BR = atoi(&argv[1][1]);
93: break;
94:
95: default:
1.13 millert 96: fprintf(stderr, "%s: %s, unknown option\n", __progname,
97: argv[1]);
1.1 deraadt 98: break;
99: }
100: }
101:
1.33 moritz 102: if (sys == NULL)
1.1 deraadt 103: goto notnumber;
1.27 deraadt 104: if (isalpha(*sys))
1.1 deraadt 105: goto notnumber;
106: /*
107: * System name is really a phone number...
108: * Copy the number then stomp on the original (in case the number
109: * is private, we don't want 'ps' or 'w' to find it).
110: */
1.27 deraadt 111: if (strlen(sys) > sizeof PNbuf - 1) {
1.13 millert 112: fprintf(stderr, "%s: phone number too long (max = %d bytes)\n",
1.14 pvalchev 113: __progname, (int)sizeof(PNbuf) - 1);
1.1 deraadt 114: exit(1);
115: }
1.27 deraadt 116: strlcpy(PNbuf, sys, sizeof PNbuf - 1);
117: for (p = sys; *p; p++)
1.1 deraadt 118: *p = '\0';
119: PN = PNbuf;
1.8 deraadt 120: (void)snprintf(sbuf, sizeof(sbuf), "tip%ld", BR);
1.27 deraadt 121: sys = sbuf;
1.1 deraadt 122:
123: notnumber:
124: (void)signal(SIGINT, cleanup);
125: (void)signal(SIGQUIT, cleanup);
126: (void)signal(SIGHUP, cleanup);
127: (void)signal(SIGTERM, cleanup);
1.24 otto 128: (void)signal(SIGCHLD, SIG_DFL);
1.1 deraadt 129:
1.27 deraadt 130: if ((i = hunt(sys)) == 0) {
1.1 deraadt 131: printf("all ports busy\n");
132: exit(3);
133: }
134: if (i == -1) {
135: printf("link down\n");
136: (void)uu_unlock(uucplock);
137: exit(3);
138: }
139: setbuf(stdout, NULL);
140: loginit();
141:
142: /*
143: * Now that we have the logfile and the ACU open
144: * return to the real uid and gid. These things will
145: * be closed on exit. Swap real and effective uid's
146: * so we can get the original permissions back
147: * for removing the uucp lock.
148: */
149: user_uid();
150:
151: /*
152: * Kludge, their's no easy way to get the initialization
153: * in the right order, so force it here
154: */
1.33 moritz 155: if ((PH = getenv("PHONES")) == NULL)
1.1 deraadt 156: PH = _PATH_PHONES;
157: vinit(); /* init variables */
1.12 millert 158: setparity("none"); /* set the parity table */
1.1 deraadt 159:
160: /*
161: * Hardwired connections require the
162: * line speed set before they make any transmissions
163: * (this is particularly true of things like a DF03-AC)
164: */
1.19 millert 165: if (HW && ttysetup(number(value(BAUDRATE)))) {
166: fprintf(stderr, "%s: bad baud rate %ld\n", __progname,
167: number(value(BAUDRATE)));
168: daemon_uid();
169: (void)uu_unlock(uucplock);
170: exit(3);
171: }
1.25 deraadt 172: if ((p = con())) {
1.1 deraadt 173: printf("\07%s\n[EOT]\n", p);
174: daemon_uid();
175: (void)uu_unlock(uucplock);
176: exit(1);
177: }
1.19 millert 178: if (!HW && ttysetup(number(value(BAUDRATE)))) {
179: fprintf(stderr, "%s: bad baud rate %ld\n", __progname,
180: number(value(BAUDRATE)));
181: daemon_uid();
182: (void)uu_unlock(uucplock);
183: exit(3);
184: }
1.1 deraadt 185: cucommon:
186: /*
187: * From here down the code is shared with
188: * the "cu" version of tip.
189: */
1.10 jason 190:
191: i = fcntl(FD, F_GETFL);
192: if (i == -1) {
193: perror("fcntl");
1.23 deraadt 194: cleanup(0);
1.10 jason 195: }
196: i = fcntl(FD, F_SETFL, i & ~O_NONBLOCK);
197: if (i == -1) {
198: perror("fcntl");
1.23 deraadt 199: cleanup(0);
1.10 jason 200: }
1.1 deraadt 201:
1.2 deraadt 202: tcgetattr(0, &defterm);
1.22 deraadt 203: gotdefterm = 1;
1.2 deraadt 204: term = defterm;
205: term.c_lflag &= ~(ICANON|IEXTEN|ECHO);
206: term.c_iflag &= ~(INPCK|ICRNL);
207: term.c_oflag &= ~OPOST;
208: term.c_cc[VMIN] = 1;
209: term.c_cc[VTIME] = 0;
210: defchars = term;
211: term.c_cc[VINTR] = term.c_cc[VQUIT] = term.c_cc[VSUSP] =
1.25 deraadt 212: term.c_cc[VDSUSP] = term.c_cc[VDISCARD] =
213: term.c_cc[VLNEXT] = _POSIX_VDISABLE;
1.1 deraadt 214: raw();
215:
216: pipe(fildes); pipe(repdes);
217: (void)signal(SIGALRM, timeout);
1.29 deraadt 218:
219: if (value(LINEDISC) != TTYDISC) {
220: int ld = (int)value(LINEDISC);
221: ioctl(FD, TIOCSETD, &ld);
222: }
1.1 deraadt 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: printf(cumode ? "Connected\r\n" : "\07connected\r\n");
1.23 deraadt 232: tipin_pid = getpid();
233: if ((tipout_pid = fork()))
1.1 deraadt 234: tipin();
235: else
236: tipout();
237: /*NOTREACHED*/
1.8 deraadt 238: exit(0);
1.1 deraadt 239: }
240:
241: void
1.23 deraadt 242: cleanup(int signo)
1.1 deraadt 243: {
244: daemon_uid();
245: (void)uu_unlock(uucplock);
246: if (odisc)
1.27 deraadt 247: ioctl(0, TIOCSETD, &odisc);
1.22 deraadt 248: unraw();
1.23 deraadt 249: if (signo && tipout_pid) {
250: kill(tipout_pid, signo);
251: wait(NULL);
252: }
1.1 deraadt 253: exit(0);
254: }
255:
256: /*
257: * Muck with user ID's. We are setuid to the owner of the lock
258: * directory when we start. user_uid() reverses real and effective
259: * ID's after startup, to run with the user's permissions.
260: * daemon_uid() switches back to the privileged uid for unlocking.
261: * Finally, to avoid running a shell with the wrong real uid,
262: * shell_uid() sets real and effective uid's to the user's real ID.
263: */
264: static int uidswapped;
265:
1.8 deraadt 266: void
1.25 deraadt 267: user_uid(void)
1.1 deraadt 268: {
269: if (uidswapped == 0) {
270: seteuid(uid);
271: uidswapped = 1;
272: }
273: }
274:
1.8 deraadt 275: void
1.25 deraadt 276: daemon_uid(void)
1.1 deraadt 277: {
278:
279: if (uidswapped) {
280: seteuid(euid);
281: uidswapped = 0;
282: }
283: }
284:
1.8 deraadt 285: void
1.25 deraadt 286: shell_uid(void)
1.1 deraadt 287: {
1.7 deraadt 288: setegid(gid);
1.1 deraadt 289: seteuid(uid);
290: }
291:
292: /*
293: * put the controlling keyboard into raw mode
294: */
1.8 deraadt 295: void
1.25 deraadt 296: raw(void)
1.1 deraadt 297: {
1.2 deraadt 298: tcsetattr(0, TCSADRAIN, &term);
1.1 deraadt 299: }
300:
301:
302: /*
303: * return keyboard to normal mode
304: */
1.8 deraadt 305: void
1.25 deraadt 306: unraw(void)
1.1 deraadt 307: {
1.22 deraadt 308: if (gotdefterm)
309: tcsetattr(0, TCSADRAIN, &defterm);
1.1 deraadt 310: }
311:
312: static jmp_buf promptbuf;
313:
314: /*
315: * Print string ``s'', then read a string
316: * in from the terminal. Handles signals & allows use of
317: * normal erase and kill characters.
318: */
1.8 deraadt 319: int
1.25 deraadt 320: prompt(char *s, char *p, size_t sz)
1.1 deraadt 321: {
1.15 millert 322: int c;
323: char *b = p;
1.1 deraadt 324: sig_t oint, oquit;
325:
326: stoprompt = 0;
327: oint = signal(SIGINT, intprompt);
328: oquit = signal(SIGQUIT, SIG_IGN);
329: unraw();
330: printf("%s", s);
331: if (setjmp(promptbuf) == 0)
1.6 millert 332: while ((c = getchar()) != EOF && (*p = c) != '\n' && --sz > 0)
1.1 deraadt 333: p++;
334: *p = '\0';
335:
336: raw();
337: (void)signal(SIGINT, oint);
338: (void)signal(SIGQUIT, oquit);
339: return (stoprompt || p == b);
340: }
341:
342: /*
343: * Interrupt service routine during prompting
344: */
1.27 deraadt 345: /*ARGSUSED*/
1.26 moritz 346: static void
347: intprompt(int signo)
1.1 deraadt 348: {
349: (void)signal(SIGINT, SIG_IGN);
350: stoprompt = 1;
351: printf("\r\n");
352: longjmp(promptbuf, 1);
353: }
354:
355: /*
356: * ****TIPIN TIPIN****
357: */
1.26 moritz 358: static void
1.25 deraadt 359: tipin(void)
1.1 deraadt 360: {
1.27 deraadt 361: int bol = 1;
1.16 deraadt 362: int gch;
1.17 deraadt 363: char ch;
1.1 deraadt 364:
365: /*
366: * Kinda klugey here...
367: * check for scripting being turned on from the .tiprc file,
368: * but be careful about just using setscript(), as we may
369: * send a SIGEMT before tipout has a chance to set up catching
370: * it; so wait a second, then setscript()
371: */
372: if (boolean(value(SCRIPT))) {
373: sleep(1);
374: setscript();
375: }
376:
377: while (1) {
1.2 deraadt 378: gch = getchar()&STRIP_PAR;
1.16 deraadt 379: /* XXX does not check for EOF */
1.1 deraadt 380: if ((gch == character(value(ESCAPE))) && bol) {
1.9 todd 381: if (!noesc) {
382: if (!(gch = escape()))
383: continue;
384: }
1.1 deraadt 385: } else if (!cumode && gch == character(value(RAISECHAR))) {
1.4 millert 386: setboolean(value(RAISE), !boolean(value(RAISE)));
1.1 deraadt 387: continue;
388: } else if (gch == '\r') {
389: bol = 1;
1.17 deraadt 390: ch = gch;
391: parwrite(FD, &ch, 1);
1.1 deraadt 392: if (boolean(value(HALFDUPLEX)))
393: printf("\r\n");
394: continue;
395: } else if (!cumode && gch == character(value(FORCE)))
1.2 deraadt 396: gch = getchar()&STRIP_PAR;
1.1 deraadt 397: bol = any(gch, value(EOL));
398: if (boolean(value(RAISE)) && islower(gch))
399: gch = toupper(gch);
1.17 deraadt 400: ch = gch;
401: parwrite(FD, &ch, 1);
1.1 deraadt 402: if (boolean(value(HALFDUPLEX)))
1.17 deraadt 403: printf("%c", ch);
1.1 deraadt 404: }
405: }
406:
407: extern esctable_t etable[];
408:
409: /*
410: * Escape handler --
411: * called on recognition of ``escapec'' at the beginning of a line
412: */
1.26 moritz 413: static int
1.25 deraadt 414: escape(void)
1.1 deraadt 415: {
1.16 deraadt 416: int gch;
1.15 millert 417: esctable_t *p;
1.1 deraadt 418: char c = character(value(ESCAPE));
419:
1.2 deraadt 420: gch = (getchar()&STRIP_PAR);
1.16 deraadt 421: /* XXX does not check for EOF */
1.1 deraadt 422: for (p = etable; p->e_char; p++)
423: if (p->e_char == gch) {
424: if ((p->e_flags&PRIV) && uid)
425: continue;
426: printf("%s", ctrl(c));
427: (*p->e_func)(gch);
428: return (0);
429: }
430: /* ESCAPE ESCAPE forces ESCAPE */
431: if (c != gch)
1.11 deraadt 432: parwrite(FD, &c, 1);
1.1 deraadt 433: return (gch);
434: }
435:
1.8 deraadt 436: int
1.25 deraadt 437: any(int cc, char *p)
1.1 deraadt 438: {
1.8 deraadt 439: char c = cc;
1.1 deraadt 440: while (p && *p)
441: if (*p++ == c)
442: return (1);
443: return (0);
444: }
445:
1.28 deraadt 446: size_t
1.25 deraadt 447: size(char *s)
1.1 deraadt 448: {
1.28 deraadt 449: size_t i = 0;
1.1 deraadt 450:
451: while (s && *s++)
452: i++;
453: return (i);
454: }
455:
456: char *
1.25 deraadt 457: interp(char *s)
1.1 deraadt 458: {
459: static char buf[256];
1.15 millert 460: char *p = buf, c, *q;
1.1 deraadt 461:
1.8 deraadt 462: while ((c = *s++)) {
1.1 deraadt 463: for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++)
464: if (*q++ == c) {
465: *p++ = '\\'; *p++ = *q;
466: goto next;
467: }
468: if (c < 040) {
469: *p++ = '^'; *p++ = c + 'A'-1;
470: } else if (c == 0177) {
471: *p++ = '^'; *p++ = '?';
472: } else
473: *p++ = c;
474: next:
475: ;
476: }
477: *p = '\0';
478: return (buf);
479: }
480:
481: char *
1.25 deraadt 482: ctrl(char c)
1.1 deraadt 483: {
484: static char s[3];
485:
486: if (c < 040 || c == 0177) {
487: s[0] = '^';
488: s[1] = c == 0177 ? '?' : c+'A'-1;
489: s[2] = '\0';
490: } else {
491: s[0] = c;
492: s[1] = '\0';
493: }
494: return (s);
495: }
496:
497: /*
498: * Help command
499: */
1.8 deraadt 500: void
1.26 moritz 501: help(int c)
1.1 deraadt 502: {
1.15 millert 503: esctable_t *p;
1.1 deraadt 504:
505: printf("%c\r\n", c);
506: for (p = etable; p->e_char; p++) {
507: if ((p->e_flags&PRIV) && uid)
508: continue;
509: printf("%2s", ctrl(character(value(ESCAPE))));
510: printf("%-2s %c %s\r\n", ctrl(p->e_char),
511: p->e_flags&EXP ? '*': ' ', p->e_help);
512: }
513: }
514:
515: /*
516: * Set up the "remote" tty's state
517: */
1.19 millert 518: int
1.25 deraadt 519: ttysetup(int speed)
1.1 deraadt 520: {
1.2 deraadt 521: struct termios cntrl;
1.1 deraadt 522:
1.19 millert 523: if (tcgetattr(FD, &cntrl))
524: return (-1);
525: cfsetspeed(&cntrl, speed);
1.2 deraadt 526: cntrl.c_cflag &= ~(CSIZE|PARENB);
527: cntrl.c_cflag |= CS8;
1.5 millert 528: if (boolean(value(DC)))
529: cntrl.c_cflag |= CLOCAL;
1.21 millert 530: if (boolean(value(HARDWAREFLOW)))
1.25 deraadt 531: cntrl.c_cflag |= CRTSCTS;
1.2 deraadt 532: cntrl.c_iflag &= ~(ISTRIP|ICRNL);
533: cntrl.c_oflag &= ~OPOST;
534: cntrl.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO);
535: cntrl.c_cc[VMIN] = 1;
536: cntrl.c_cc[VTIME] = 0;
1.1 deraadt 537: if (boolean(value(TAND)))
1.2 deraadt 538: cntrl.c_iflag |= IXOFF;
1.19 millert 539: return (tcsetattr(FD, TCSAFLUSH, &cntrl));
1.1 deraadt 540: }
541:
542: static char partab[0200];
543:
544: /*
545: * Do a write to the remote machine with the correct parity.
546: * We are doing 8 bit wide output, so we just generate a character
547: * with the right parity and output it.
548: */
1.8 deraadt 549: void
1.27 deraadt 550: parwrite(int fd, char *buf, size_t n)
1.1 deraadt 551: {
1.15 millert 552: int i;
553: char *bp;
1.1 deraadt 554:
555: bp = buf;
556: if (bits8 == 0)
557: for (i = 0; i < n; i++) {
558: *bp = partab[(*bp) & 0177];
559: bp++;
560: }
561: if (write(fd, buf, n) < 0) {
562: if (errno == EIO)
563: tipabort("Lost carrier.");
564: /* this is questionable */
565: perror("write");
566: }
567: }
568:
569: /*
570: * Build a parity table with appropriate high-order bit.
571: */
1.8 deraadt 572: void
1.25 deraadt 573: setparity(char *defparity)
1.1 deraadt 574: {
1.15 millert 575: int i, flip, clr, set;
1.1 deraadt 576: char *parity;
1.4 millert 577: extern const unsigned char evenpartab[];
1.1 deraadt 578:
1.33 moritz 579: if (value(PARITY) == NULL)
1.1 deraadt 580: value(PARITY) = defparity;
581: parity = value(PARITY);
582: if (equal(parity, "none")) {
583: bits8 = 1;
584: return;
585: }
586: bits8 = 0;
587: flip = 0;
588: clr = 0377;
589: set = 0;
590: if (equal(parity, "odd"))
591: flip = 0200; /* reverse bit 7 */
592: else if (equal(parity, "zero"))
593: clr = 0177; /* turn off bit 7 */
594: else if (equal(parity, "one"))
595: set = 0200; /* turn on bit 7 */
596: else if (!equal(parity, "even")) {
597: (void) fprintf(stderr, "%s: unknown parity value\r\n", parity);
598: (void) fflush(stderr);
599: }
600: for (i = 0; i < 0200; i++)
1.18 hugh 601: partab[i] = ((evenpartab[i] ^ flip) | set) & clr;
1.1 deraadt 602: }