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