tip.c revision 1.3 1 /*
2 * Copyright (c) 1983 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 char copyright[] =
36 "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
37 All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 static char sccsid[] = "@(#)tip.c 5.15 (Berkeley) 2/4/91";
42 #endif /* not lint */
43
44 /*
45 * tip - UNIX link to other systems
46 * tip [-v] [-speed] system-name
47 * or
48 * cu phone-number [-s speed] [-l line] [-a acu]
49 */
50 #include "tip.h"
51 #include "pathnames.h"
52
53 /*
54 * Baud rate mapping table
55 */
56 int bauds[] = {
57 0, 50, 75, 110, 134, 150, 200, 300, 600,
58 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, -1
59 };
60
61 int disc = OTTYDISC; /* tip normally runs this way */
62 void intprompt();
63 void timeout();
64 void cleanup();
65 char *sname();
66 char PNbuf[256]; /* This limits the size of a number */
67
68 main(argc, argv)
69 char *argv[];
70 {
71 char *system = NOSTR;
72 register int i;
73 register char *p;
74 char sbuf[12];
75
76 gid = getgid();
77 egid = getegid();
78 uid = getuid();
79 euid = geteuid();
80 if (equal(sname(argv[0]), "cu")) {
81 cumode = 1;
82 cumain(argc, argv);
83 goto cucommon;
84 }
85
86 if (argc > 4) {
87 fprintf(stderr, "usage: tip [-v] [-speed] [system-name]\n");
88 exit(1);
89 }
90 if (!isatty(0)) {
91 fprintf(stderr, "tip: must be interactive\n");
92 exit(1);
93 }
94
95 for (; argc > 1; argv++, argc--) {
96 if (argv[1][0] != '-')
97 system = argv[1];
98 else switch (argv[1][1]) {
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 BR = atoi(&argv[1][1]);
107 break;
108
109 default:
110 fprintf(stderr, "tip: %s, unknown option\n", 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, "tip: phone number too long (max = %d bytes)\n",
126 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)sprintf(sbuf, "tip%d", 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
142 if ((i = hunt(system)) == 0) {
143 printf("all ports busy\n");
144 exit(3);
145 }
146 if (i == -1) {
147 printf("link down\n");
148 (void)uu_unlock(uucplock);
149 exit(3);
150 }
151 setbuf(stdout, NULL);
152 loginit();
153
154 /*
155 * Now that we have the logfile and the ACU open
156 * return to the real uid and gid. These things will
157 * be closed on exit. Swap real and effective uid's
158 * so we can get the original permissions back
159 * for removing the uucp lock.
160 */
161 user_uid();
162
163 /*
164 * Kludge, their's no easy way to get the initialization
165 * in the right order, so force it here
166 */
167 if ((PH = getenv("PHONES")) == NOSTR)
168 PH = _PATH_PHONES;
169 vinit(); /* init variables */
170 setparity("even"); /* set the parity table */
171 if ((i = speed(number(value(BAUDRATE)))) == NULL) {
172 printf("tip: bad baud rate %d\n", number(value(BAUDRATE)));
173 daemon_uid();
174 (void)uu_unlock(uucplock);
175 exit(3);
176 }
177
178 /*
179 * Hardwired connections require the
180 * line speed set before they make any transmissions
181 * (this is particularly true of things like a DF03-AC)
182 */
183 if (HW)
184 ttysetup(i);
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)
192 ttysetup(i);
193 cucommon:
194 /*
195 * From here down the code is shared with
196 * the "cu" version of tip.
197 */
198
199 ioctl(0, TIOCGETP, (char *)&defarg);
200 ioctl(0, TIOCGETC, (char *)&defchars);
201 ioctl(0, TIOCGLTC, (char *)&deflchars);
202 ioctl(0, TIOCGETD, (char *)&odisc);
203 arg = defarg;
204 arg.sg_flags = ANYP | CBREAK;
205 tchars = defchars;
206 tchars.t_intrc = tchars.t_quitc = -1;
207 ltchars = deflchars;
208 ltchars.t_suspc = ltchars.t_dsuspc = ltchars.t_flushc
209 = ltchars.t_lnextc = -1;
210 raw();
211
212 pipe(fildes); pipe(repdes);
213 (void)signal(SIGALRM, timeout);
214
215 /*
216 * Everything's set up now:
217 * connection established (hardwired or dialup)
218 * line conditioned (baud rate, mode, etc.)
219 * internal data structures (variables)
220 * so, fork one process for local side and one for remote.
221 */
222 printf(cumode ? "Connected\r\n" : "\07connected\r\n");
223 if (pid = fork())
224 tipin();
225 else
226 tipout();
227 /*NOTREACHED*/
228 }
229
230 void
231 cleanup()
232 {
233
234 daemon_uid();
235 (void)uu_unlock(uucplock);
236 if (odisc)
237 ioctl(0, TIOCSETD, (char *)&odisc);
238 exit(0);
239 }
240
241 /*
242 * Muck with user ID's. We are setuid to the owner of the lock
243 * directory when we start. user_uid() reverses real and effective
244 * ID's after startup, to run with the user's permissions.
245 * daemon_uid() switches back to the privileged uid for unlocking.
246 * Finally, to avoid running a shell with the wrong real uid,
247 * shell_uid() sets real and effective uid's to the user's real ID.
248 */
249 static int uidswapped;
250
251 user_uid()
252 {
253 if (uidswapped == 0) {
254 setregid(egid, gid);
255 setreuid(euid, uid);
256 uidswapped = 1;
257 }
258 }
259
260 daemon_uid()
261 {
262
263 if (uidswapped) {
264 setreuid(uid, euid);
265 setregid(gid, egid);
266 uidswapped = 0;
267 }
268 }
269
270 shell_uid()
271 {
272
273 setreuid(uid, uid);
274 setregid(gid, gid);
275 }
276
277 /*
278 * put the controlling keyboard into raw mode
279 */
280 raw()
281 {
282
283 ioctl(0, TIOCSETP, &arg);
284 ioctl(0, TIOCSETC, &tchars);
285 ioctl(0, TIOCSLTC, <chars);
286 ioctl(0, TIOCSETD, (char *)&disc);
287 }
288
289
290 /*
291 * return keyboard to normal mode
292 */
293 unraw()
294 {
295
296 ioctl(0, TIOCSETD, (char *)&odisc);
297 ioctl(0, TIOCSETP, (char *)&defarg);
298 ioctl(0, TIOCSETC, (char *)&defchars);
299 ioctl(0, TIOCSLTC, (char *)&deflchars);
300 }
301
302 static jmp_buf promptbuf;
303
304 /*
305 * Print string ``s'', then read a string
306 * in from the terminal. Handles signals & allows use of
307 * normal erase and kill characters.
308 */
309 prompt(s, p)
310 char *s;
311 register char *p;
312 {
313 register char *b = p;
314 sig_t oint, oquit;
315
316 stoprompt = 0;
317 oint = signal(SIGINT, intprompt);
318 oquit = signal(SIGQUIT, SIG_IGN);
319 unraw();
320 printf("%s", s);
321 if (setjmp(promptbuf) == 0)
322 while ((*p = getchar()) != EOF && *p != '\n')
323 p++;
324 *p = '\0';
325
326 raw();
327 (void)signal(SIGINT, oint);
328 (void)signal(SIGQUIT, oquit);
329 return (stoprompt || p == b);
330 }
331
332 /*
333 * Interrupt service routine during prompting
334 */
335 void
336 intprompt()
337 {
338
339 (void)signal(SIGINT, SIG_IGN);
340 stoprompt = 1;
341 printf("\r\n");
342 longjmp(promptbuf, 1);
343 }
344
345 /*
346 * ****TIPIN TIPIN****
347 */
348 tipin()
349 {
350 char gch, bol = 1;
351
352 /*
353 * Kinda klugey here...
354 * check for scripting being turned on from the .tiprc file,
355 * but be careful about just using setscript(), as we may
356 * send a SIGEMT before tipout has a chance to set up catching
357 * it; so wait a second, then setscript()
358 */
359 if (boolean(value(SCRIPT))) {
360 sleep(1);
361 setscript();
362 }
363
364 while (1) {
365 gch = getchar()&0177;
366 if ((gch == character(value(ESCAPE))) && bol) {
367 if (!(gch = escape()))
368 continue;
369 } else if (!cumode && gch == character(value(RAISECHAR))) {
370 boolean(value(RAISE)) = !boolean(value(RAISE));
371 continue;
372 } else if (gch == '\r') {
373 bol = 1;
374 pwrite(FD, &gch, 1);
375 if (boolean(value(HALFDUPLEX)))
376 printf("\r\n");
377 continue;
378 } else if (!cumode && gch == character(value(FORCE)))
379 gch = getchar()&0177;
380 bol = any(gch, value(EOL));
381 if (boolean(value(RAISE)) && islower(gch))
382 gch = toupper(gch);
383 pwrite(FD, &gch, 1);
384 if (boolean(value(HALFDUPLEX)))
385 printf("%c", gch);
386 }
387 }
388
389 /*
390 * Escape handler --
391 * called on recognition of ``escapec'' at the beginning of a line
392 */
393 escape()
394 {
395 register char gch;
396 register esctable_t *p;
397 char c = character(value(ESCAPE));
398 extern esctable_t etable[];
399
400 gch = (getchar()&0177);
401 for (p = etable; p->e_char; p++)
402 if (p->e_char == gch) {
403 if ((p->e_flags&PRIV) && uid)
404 continue;
405 printf("%s", ctrl(c));
406 (*p->e_func)(gch);
407 return (0);
408 }
409 /* ESCAPE ESCAPE forces ESCAPE */
410 if (c != gch)
411 pwrite(FD, &c, 1);
412 return (gch);
413 }
414
415 speed(n)
416 int n;
417 {
418 register int *p;
419
420 for (p = bauds; *p != -1; p++)
421 if (*p == n)
422 return (p - bauds);
423 return (NULL);
424 }
425
426 any(c, p)
427 register char c, *p;
428 {
429 while (p && *p)
430 if (*p++ == c)
431 return (1);
432 return (0);
433 }
434
435 size(s)
436 register char *s;
437 {
438 register int i = 0;
439
440 while (s && *s++)
441 i++;
442 return (i);
443 }
444
445 char *
446 interp(s)
447 register char *s;
448 {
449 static char buf[256];
450 register char *p = buf, c, *q;
451
452 while (c = *s++) {
453 for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++)
454 if (*q++ == c) {
455 *p++ = '\\'; *p++ = *q;
456 goto next;
457 }
458 if (c < 040) {
459 *p++ = '^'; *p++ = c + 'A'-1;
460 } else if (c == 0177) {
461 *p++ = '^'; *p++ = '?';
462 } else
463 *p++ = c;
464 next:
465 ;
466 }
467 *p = '\0';
468 return (buf);
469 }
470
471 char *
472 ctrl(c)
473 char c;
474 {
475 static char s[3];
476
477 if (c < 040 || c == 0177) {
478 s[0] = '^';
479 s[1] = c == 0177 ? '?' : c+'A'-1;
480 s[2] = '\0';
481 } else {
482 s[0] = c;
483 s[1] = '\0';
484 }
485 return (s);
486 }
487
488 /*
489 * Help command
490 */
491 help(c)
492 char c;
493 {
494 register esctable_t *p;
495 extern esctable_t etable[];
496
497 printf("%c\r\n", c);
498 for (p = etable; p->e_char; p++) {
499 if ((p->e_flags&PRIV) && uid)
500 continue;
501 printf("%2s", ctrl(character(value(ESCAPE))));
502 printf("%-2s %c %s\r\n", ctrl(p->e_char),
503 p->e_flags&EXP ? '*': ' ', p->e_help);
504 }
505 }
506
507 /*
508 * Set up the "remote" tty's state
509 */
510 ttysetup(speed)
511 int speed;
512 {
513 unsigned bits = LDECCTQ;
514
515 arg.sg_ispeed = arg.sg_ospeed = speed;
516 arg.sg_flags = RAW;
517 if (boolean(value(TAND)))
518 arg.sg_flags |= TANDEM;
519 ioctl(FD, TIOCSETP, (char *)&arg);
520 ioctl(FD, TIOCLBIS, (char *)&bits);
521 }
522
523 /*
524 * Return "simple" name from a file name,
525 * strip leading directories.
526 */
527 char *
528 sname(s)
529 register char *s;
530 {
531 register char *p = s;
532
533 while (*s)
534 if (*s++ == '/')
535 p = s;
536 return (p);
537 }
538
539 static char partab[0200];
540 static int bits8;
541
542 /*
543 * Do a write to the remote machine with the correct parity.
544 * We are doing 8 bit wide output, so we just generate a character
545 * with the right parity and output it.
546 */
547 pwrite(fd, buf, n)
548 int fd;
549 char *buf;
550 register int n;
551 {
552 register int i;
553 register char *bp;
554 extern int errno;
555
556 bp = buf;
557 if (bits8 == 0) {
558 static char *mbp; static sz;
559
560 if (mbp == 0 || n > sz) {
561 if (mbp)
562 free(mbp);
563 mbp = (char *) malloc(n);
564 sz = n;
565 }
566
567 bp = mbp;
568 for (i = 0; i < n; i++)
569 *bp++ = partab[*buf++ & 0177];
570 bp = mbp;
571 }
572 if (write(fd, bp, n) < 0) {
573 if (errno == EIO)
574 tipabort("Lost carrier.");
575 /* this is questionable */
576 perror("write");
577 }
578 }
579
580 /*
581 * Build a parity table with appropriate high-order bit.
582 */
583 setparity(defparity)
584 char *defparity;
585 {
586 register int i, flip, clr, set;
587 char *parity;
588 extern char evenpartab[];
589
590 if (value(PARITY) == NOSTR)
591 value(PARITY) = defparity;
592 parity = value(PARITY);
593 if (equal(parity, "none")) {
594 bits8 = 1;
595 return;
596 }
597 bits8 = 0;
598 flip = 0;
599 clr = 0377;
600 set = 0;
601 if (equal(parity, "odd"))
602 flip = 0200; /* reverse bit 7 */
603 else if (equal(parity, "zero"))
604 clr = 0177; /* turn off bit 7 */
605 else if (equal(parity, "one"))
606 set = 0200; /* turn on bit 7 */
607 else if (!equal(parity, "even")) {
608 (void) fprintf(stderr, "%s: unknown parity value\r\n", parity);
609 (void) fflush(stderr);
610 }
611 for (i = 0; i < 0200; i++)
612 partab[i] = evenpartab[i] ^ flip | set & clr;
613 }
614