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