1 /*
2 * Copyright (c) 1983, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 */
7
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1983, 1993\n\
11 The Regents of the University of California. All rights reserved.\n";
12 #endif /* not lint */
13
14 #ifndef lint
15 static char sccsid[] = "@(#)tip.c 8.1 (Berkeley) 06/06/93";
16 #endif /* not lint */
17
18 /*
19 * tip - UNIX link to other systems
20 * tip [-v] [-speed] system-name
21 * or
22 * cu phone-number [-s speed] [-l line] [-a acu]
23 */
24 #include "tip.h"
25 #include "pathnames.h"
26
27 /*
28 * Baud rate mapping table
29 */
30 int bauds[] = {
31 0, 50, 75, 110, 134, 150, 200, 300, 600,
32 1200, 1800, 2400, 4800, 9600, 19200, -1
33 };
34
35 int disc = OTTYDISC; /* tip normally runs this way */
36 void intprompt();
37 void timeout();
38 void cleanup();
39 char *sname();
40 char PNbuf[256]; /* This limits the size of a number */
41
main(argc,argv)42 main(argc, argv)
43 char *argv[];
44 {
45 char *system = NOSTR;
46 register int i;
47 register char *p;
48 char sbuf[12];
49
50 gid = getgid();
51 egid = getegid();
52 uid = getuid();
53 euid = geteuid();
54 if (equal(sname(argv[0]), "cu")) {
55 cumode = 1;
56 cumain(argc, argv);
57 goto cucommon;
58 }
59
60 if (argc > 4) {
61 fprintf(stderr, "usage: tip [-v] [-speed] [system-name]\n");
62 exit(1);
63 }
64 if (!isatty(0)) {
65 fprintf(stderr, "tip: must be interactive\n");
66 exit(1);
67 }
68
69 for (; argc > 1; argv++, argc--) {
70 if (argv[1][0] != '-')
71 system = argv[1];
72 else switch (argv[1][1]) {
73
74 case 'v':
75 vflag++;
76 break;
77
78 case '0': case '1': case '2': case '3': case '4':
79 case '5': case '6': case '7': case '8': case '9':
80 BR = atoi(&argv[1][1]);
81 break;
82
83 default:
84 fprintf(stderr, "tip: %s, unknown option\n", argv[1]);
85 break;
86 }
87 }
88
89 if (system == NOSTR)
90 goto notnumber;
91 if (isalpha(*system))
92 goto notnumber;
93 /*
94 * System name is really a phone number...
95 * Copy the number then stomp on the original (in case the number
96 * is private, we don't want 'ps' or 'w' to find it).
97 */
98 if (strlen(system) > sizeof PNbuf - 1) {
99 fprintf(stderr, "tip: phone number too long (max = %d bytes)\n",
100 sizeof PNbuf - 1);
101 exit(1);
102 }
103 strncpy( PNbuf, system, sizeof PNbuf - 1 );
104 for (p = system; *p; p++)
105 *p = '\0';
106 PN = PNbuf;
107 (void)sprintf(sbuf, "tip%d", BR);
108 system = sbuf;
109
110 notnumber:
111 (void)signal(SIGINT, cleanup);
112 (void)signal(SIGQUIT, cleanup);
113 (void)signal(SIGHUP, cleanup);
114 (void)signal(SIGTERM, cleanup);
115
116 if ((i = hunt(system)) == 0) {
117 printf("all ports busy\n");
118 exit(3);
119 }
120 if (i == -1) {
121 printf("link down\n");
122 (void)uu_unlock(uucplock);
123 exit(3);
124 }
125 setbuf(stdout, NULL);
126 loginit();
127
128 /*
129 * Kludge, their's no easy way to get the initialization
130 * in the right order, so force it here
131 */
132 if ((PH = getenv("PHONES")) == NOSTR)
133 PH = _PATH_PHONES;
134 vinit(); /* init variables */
135 setparity("even"); /* set the parity table */
136 if ((i = speed(number(value(BAUDRATE)))) == NULL) {
137 printf("tip: bad baud rate %d\n", number(value(BAUDRATE)));
138 (void)uu_unlock(uucplock);
139 exit(3);
140 }
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 * Hardwired connections require the
153 * line speed set before they make any transmissions
154 * (this is particularly true of things like a DF03-AC)
155 */
156 if (HW)
157 ttysetup(i);
158 if (p = connect()) {
159 printf("\07%s\n[EOT]\n", p);
160 daemon_uid();
161 (void)uu_unlock(uucplock);
162 exit(1);
163 }
164 if (!HW)
165 ttysetup(i);
166 cucommon:
167 /*
168 * From here down the code is shared with
169 * the "cu" version of tip.
170 */
171
172 ioctl(0, TIOCGETP, (char *)&defarg);
173 ioctl(0, TIOCGETC, (char *)&defchars);
174 ioctl(0, TIOCGLTC, (char *)&deflchars);
175 ioctl(0, TIOCGETD, (char *)&odisc);
176 arg = defarg;
177 arg.sg_flags = ANYP | CBREAK;
178 tchars = defchars;
179 tchars.t_intrc = tchars.t_quitc = -1;
180 ltchars = deflchars;
181 ltchars.t_suspc = ltchars.t_dsuspc = ltchars.t_flushc
182 = ltchars.t_lnextc = -1;
183 raw();
184
185 pipe(fildes); pipe(repdes);
186 (void)signal(SIGALRM, timeout);
187
188 /*
189 * Everything's set up now:
190 * connection established (hardwired or dialup)
191 * line conditioned (baud rate, mode, etc.)
192 * internal data structures (variables)
193 * so, fork one process for local side and one for remote.
194 */
195 printf(cumode ? "Connected\r\n" : "\07connected\r\n");
196 if (pid = fork())
197 tipin();
198 else
199 tipout();
200 /*NOTREACHED*/
201 }
202
203 void
cleanup()204 cleanup()
205 {
206
207 daemon_uid();
208 (void)uu_unlock(uucplock);
209 if (odisc)
210 ioctl(0, TIOCSETD, (char *)&odisc);
211 exit(0);
212 }
213
214 /*
215 * Muck with user ID's. We are setuid to the owner of the lock
216 * directory when we start. user_uid() reverses real and effective
217 * ID's after startup, to run with the user's permissions.
218 * daemon_uid() switches back to the privileged uid for unlocking.
219 * Finally, to avoid running a shell with the wrong real uid,
220 * shell_uid() sets real and effective uid's to the user's real ID.
221 */
222 static int uidswapped;
223
user_uid()224 user_uid()
225 {
226 if (uidswapped == 0) {
227 seteuid(uid);
228 uidswapped = 1;
229 }
230 }
231
daemon_uid()232 daemon_uid()
233 {
234
235 if (uidswapped) {
236 seteuid(euid);
237 uidswapped = 0;
238 }
239 }
240
shell_uid()241 shell_uid()
242 {
243
244 seteuid(uid);
245 }
246
247 /*
248 * put the controlling keyboard into raw mode
249 */
raw()250 raw()
251 {
252
253 ioctl(0, TIOCSETP, &arg);
254 ioctl(0, TIOCSETC, &tchars);
255 ioctl(0, TIOCSLTC, <chars);
256 ioctl(0, TIOCSETD, (char *)&disc);
257 }
258
259
260 /*
261 * return keyboard to normal mode
262 */
unraw()263 unraw()
264 {
265
266 ioctl(0, TIOCSETD, (char *)&odisc);
267 ioctl(0, TIOCSETP, (char *)&defarg);
268 ioctl(0, TIOCSETC, (char *)&defchars);
269 ioctl(0, TIOCSLTC, (char *)&deflchars);
270 }
271
272 static jmp_buf promptbuf;
273
274 /*
275 * Print string ``s'', then read a string
276 * in from the terminal. Handles signals & allows use of
277 * normal erase and kill characters.
278 */
prompt(s,p)279 prompt(s, p)
280 char *s;
281 register char *p;
282 {
283 register char *b = p;
284 sig_t oint, oquit;
285
286 stoprompt = 0;
287 oint = signal(SIGINT, intprompt);
288 oquit = signal(SIGQUIT, SIG_IGN);
289 unraw();
290 printf("%s", s);
291 if (setjmp(promptbuf) == 0)
292 while ((*p = getchar()) != EOF && *p != '\n')
293 p++;
294 *p = '\0';
295
296 raw();
297 (void)signal(SIGINT, oint);
298 (void)signal(SIGQUIT, oquit);
299 return (stoprompt || p == b);
300 }
301
302 /*
303 * Interrupt service routine during prompting
304 */
305 void
intprompt()306 intprompt()
307 {
308
309 (void)signal(SIGINT, SIG_IGN);
310 stoprompt = 1;
311 printf("\r\n");
312 longjmp(promptbuf, 1);
313 }
314
315 /*
316 * ****TIPIN TIPIN****
317 */
tipin()318 tipin()
319 {
320 char gch, bol = 1;
321
322 /*
323 * Kinda klugey here...
324 * check for scripting being turned on from the .tiprc file,
325 * but be careful about just using setscript(), as we may
326 * send a SIGEMT before tipout has a chance to set up catching
327 * it; so wait a second, then setscript()
328 */
329 if (boolean(value(SCRIPT))) {
330 sleep(1);
331 setscript();
332 }
333
334 while (1) {
335 gch = getchar()&0177;
336 if ((gch == character(value(ESCAPE))) && bol) {
337 if (!(gch = escape()))
338 continue;
339 } else if (!cumode && gch == character(value(RAISECHAR))) {
340 boolean(value(RAISE)) = !boolean(value(RAISE));
341 continue;
342 } else if (gch == '\r') {
343 bol = 1;
344 pwrite(FD, &gch, 1);
345 if (boolean(value(HALFDUPLEX)))
346 printf("\r\n");
347 continue;
348 } else if (!cumode && gch == character(value(FORCE)))
349 gch = getchar()&0177;
350 bol = any(gch, value(EOL));
351 if (boolean(value(RAISE)) && islower(gch))
352 gch = toupper(gch);
353 pwrite(FD, &gch, 1);
354 if (boolean(value(HALFDUPLEX)))
355 printf("%c", gch);
356 }
357 }
358
359 extern esctable_t etable[];
360
361 /*
362 * Escape handler --
363 * called on recognition of ``escapec'' at the beginning of a line
364 */
escape()365 escape()
366 {
367 register char gch;
368 register esctable_t *p;
369 char c = character(value(ESCAPE));
370
371 gch = (getchar()&0177);
372 for (p = etable; p->e_char; p++)
373 if (p->e_char == gch) {
374 if ((p->e_flags&PRIV) && uid)
375 continue;
376 printf("%s", ctrl(c));
377 (*p->e_func)(gch);
378 return (0);
379 }
380 /* ESCAPE ESCAPE forces ESCAPE */
381 if (c != gch)
382 pwrite(FD, &c, 1);
383 return (gch);
384 }
385
speed(n)386 speed(n)
387 int n;
388 {
389 register int *p;
390
391 for (p = bauds; *p != -1; p++)
392 if (*p == n)
393 return (p - bauds);
394 return (NULL);
395 }
396
any(c,p)397 any(c, p)
398 register char c, *p;
399 {
400 while (p && *p)
401 if (*p++ == c)
402 return (1);
403 return (0);
404 }
405
size(s)406 size(s)
407 register char *s;
408 {
409 register int i = 0;
410
411 while (s && *s++)
412 i++;
413 return (i);
414 }
415
416 char *
interp(s)417 interp(s)
418 register char *s;
419 {
420 static char buf[256];
421 register char *p = buf, c, *q;
422
423 while (c = *s++) {
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 *
ctrl(c)443 ctrl(c)
444 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 */
help(c)462 help(c)
463 char c;
464 {
465 register esctable_t *p;
466
467 printf("%c\r\n", c);
468 for (p = etable; p->e_char; p++) {
469 if ((p->e_flags&PRIV) && uid)
470 continue;
471 printf("%2s", ctrl(character(value(ESCAPE))));
472 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 */
ttysetup(speed)480 ttysetup(speed)
481 int speed;
482 {
483 unsigned bits = LDECCTQ;
484
485 arg.sg_ispeed = arg.sg_ospeed = speed;
486 arg.sg_flags = RAW;
487 if (boolean(value(TAND)))
488 arg.sg_flags |= TANDEM;
489 ioctl(FD, TIOCSETP, (char *)&arg);
490 ioctl(FD, TIOCLBIS, (char *)&bits);
491 }
492
493 /*
494 * Return "simple" name from a file name,
495 * strip leading directories.
496 */
497 char *
sname(s)498 sname(s)
499 register char *s;
500 {
501 register char *p = s;
502
503 while (*s)
504 if (*s++ == '/')
505 p = s;
506 return (p);
507 }
508
509 static char partab[0200];
510 static int bits8;
511
512 /*
513 * Do a write to the remote machine with the correct parity.
514 * We are doing 8 bit wide output, so we just generate a character
515 * with the right parity and output it.
516 */
pwrite(fd,buf,n)517 pwrite(fd, buf, n)
518 int fd;
519 char *buf;
520 register int n;
521 {
522 register int i;
523 register char *bp;
524 extern int errno;
525
526 bp = buf;
527 if (bits8 == 0)
528 for (i = 0; i < n; i++) {
529 *bp = partab[(*bp) & 0177];
530 bp++;
531 }
532 if (write(fd, buf, n) < 0) {
533 if (errno == EIO)
534 tipabort("Lost carrier.");
535 /* this is questionable */
536 perror("write");
537 }
538 }
539
540 /*
541 * Build a parity table with appropriate high-order bit.
542 */
setparity(defparity)543 setparity(defparity)
544 char *defparity;
545 {
546 register int i, flip, clr, set;
547 char *parity;
548 extern char evenpartab[];
549
550 if (value(PARITY) == NOSTR)
551 value(PARITY) = defparity;
552 parity = value(PARITY);
553 if (equal(parity, "none")) {
554 bits8 = 1;
555 return;
556 }
557 bits8 = 0;
558 flip = 0;
559 clr = 0377;
560 set = 0;
561 if (equal(parity, "odd"))
562 flip = 0200; /* reverse bit 7 */
563 else if (equal(parity, "zero"))
564 clr = 0177; /* turn off bit 7 */
565 else if (equal(parity, "one"))
566 set = 0200; /* turn on bit 7 */
567 else if (!equal(parity, "even")) {
568 (void) fprintf(stderr, "%s: unknown parity value\r\n", parity);
569 (void) fflush(stderr);
570 }
571 for (i = 0; i < 0200; i++)
572 partab[i] = evenpartab[i] ^ flip | set & clr;
573 }
574