tip.c revision 28686
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: tip.c,v 1.4 1997/08/22 22:14:15 imp Exp $";
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 *, size_t));
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, sz)
381	char *s;
382	register char *p;
383	size_t sz;
384{
385	register char *b = p;
386	sig_t oint, oquit;
387
388	stoprompt = 0;
389	oint = signal(SIGINT, intprompt);
390	oquit = signal(SIGQUIT, SIG_IGN);
391	unraw();
392	printf("%s", s);
393	if (setjmp(promptbuf) == 0)
394		while ((*p = getchar()) != EOF && *p != '\n' && --sz > 0)
395			p++;
396	*p = '\0';
397
398	raw();
399	(void)signal(SIGINT, oint);
400	(void)signal(SIGQUIT, oquit);
401	return (stoprompt || p == b);
402}
403
404/*
405 * Interrupt service routine during prompting
406 */
407void
408intprompt()
409{
410
411	(void)signal(SIGINT, SIG_IGN);
412	stoprompt = 1;
413	printf("\r\n");
414	longjmp(promptbuf, 1);
415}
416
417/*
418 * ****TIPIN   TIPIN****
419 */
420void
421tipin()
422{
423	int i;
424	char gch, bol = 1;
425
426	/*
427	 * Kinda klugey here...
428	 *   check for scripting being turned on from the .tiprc file,
429	 *   but be careful about just using setscript(), as we may
430	 *   send a SIGEMT before tipout has a chance to set up catching
431	 *   it; so wait a second, then setscript()
432	 */
433	if (boolean(value(SCRIPT))) {
434		sleep(1);
435		setscript();
436	}
437
438	while (1) {
439		i = getchar();
440		if (i == EOF)
441			break;
442		gch = i&0177;
443		if ((gch == character(value(ESCAPE))) && bol) {
444			if (!(gch = escape()))
445				continue;
446		} else if (!cumode && gch == character(value(RAISECHAR))) {
447			boolean(value(RAISE)) = !boolean(value(RAISE));
448			continue;
449		} else if (gch == '\r') {
450			bol = 1;
451			pwrite(FD, &gch, 1);
452			if (boolean(value(HALFDUPLEX)))
453				printf("\r\n");
454			continue;
455		} else if (!cumode && gch == character(value(FORCE))) {
456			i = getchar();
457			if (i == EOF)
458				break;
459			gch = i & 0177;
460		}
461		bol = any(gch, value(EOL));
462		if (boolean(value(RAISE)) && islower(gch))
463			gch = toupper(gch);
464		pwrite(FD, &gch, 1);
465		if (boolean(value(HALFDUPLEX)))
466			printf("%c", gch);
467	}
468}
469
470extern esctable_t etable[];
471
472/*
473 * Escape handler --
474 *  called on recognition of ``escapec'' at the beginning of a line
475 */
476char
477escape()
478{
479	register char gch;
480	register esctable_t *p;
481	char c = character(value(ESCAPE));
482	int i;
483
484	i = getchar();
485	if (i == EOF)
486		return 0;
487	gch = (i&0177);
488	for (p = etable; p->e_char; p++)
489		if (p->e_char == gch) {
490			if ((p->e_flags&PRIV) && uid)
491				continue;
492			printf("%s", ctrl(c));
493			(*p->e_func)(gch);
494			return (0);
495		}
496	/* ESCAPE ESCAPE forces ESCAPE */
497	if (c != gch)
498		pwrite(FD, &c, 1);
499	return (gch);
500}
501
502int
503speed(n)
504	int n;
505{
506#if HAVE_TERMIOS
507	return (n);
508#else
509	register CONST int *p;
510
511	for (p = bauds; *p != -1;  p++)
512		if (*p == n)
513			return (p - bauds);
514	return (NULL);
515#endif
516}
517
518int
519any(c, p)
520	register char c, *p;
521{
522	while (p && *p)
523		if (*p++ == c)
524			return (1);
525	return (0);
526}
527
528int
529size(s)
530	register char	*s;
531{
532	register int i = 0;
533
534	while (s && *s++)
535		i++;
536	return (i);
537}
538
539char *
540interp(s)
541	register char *s;
542{
543	static char buf[256];
544	register char *p = buf, c, *q;
545
546	while ((c = *s++)) {
547		for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++)
548			if (*q++ == c) {
549				*p++ = '\\'; *p++ = *q;
550				goto next;
551			}
552		if (c < 040) {
553			*p++ = '^'; *p++ = c + 'A'-1;
554		} else if (c == 0177) {
555			*p++ = '^'; *p++ = '?';
556		} else
557			*p++ = c;
558	next:
559		;
560	}
561	*p = '\0';
562	return (buf);
563}
564
565char *
566ctrl(c)
567	char c;
568{
569	static char s[3];
570
571	if (c < 040 || c == 0177) {
572		s[0] = '^';
573		s[1] = c == 0177 ? '?' : c+'A'-1;
574		s[2] = '\0';
575	} else {
576		s[0] = c;
577		s[1] = '\0';
578	}
579	return (s);
580}
581
582/*
583 * Help command
584 */
585void
586help(c)
587	char c;
588{
589	register esctable_t *p;
590
591	printf("%c\r\n", c);
592	for (p = etable; p->e_char; p++) {
593		if ((p->e_flags&PRIV) && uid)
594			continue;
595		printf("%2s", ctrl(character(value(ESCAPE))));
596		printf("%-2s %c   %s\r\n", ctrl(p->e_char),
597			p->e_flags&EXP ? '*': ' ', p->e_help);
598	}
599}
600
601/*
602 * Set up the "remote" tty's state
603 */
604void
605ttysetup (int speed)
606{
607#if HAVE_TERMIOS
608	struct termios termios;
609	tcgetattr (FD, &termios);
610	if (boolean(value(TAND)))
611		termios.c_iflag = IXOFF;
612	else
613		termios.c_iflag = 0;
614#ifndef _POSIX_SOURCE
615	termios.c_lflag = (PENDIN|ECHOKE|ECHOE);
616#else
617	termios.c_lflag = (PENDIN|ECHOE);
618#endif
619	termios.c_cflag = (CLOCAL|HUPCL|CREAD|CS8);
620	termios.c_ispeed = termios.c_ospeed = speed;
621	tcsetattr (FD, TCSANOW, &termios);
622#else /* HAVE_TERMIOS */
623	unsigned bits = LDECCTQ;
624
625	arg.sg_ispeed = arg.sg_ospeed = speed;
626	arg.sg_flags = RAW;
627	if (boolean(value(TAND)))
628		arg.sg_flags |= TANDEM;
629	ioctl(FD, TIOCSETP, (char *)&arg);
630	ioctl(FD, TIOCLBIS, (char *)&bits);
631#endif /* HAVE_TERMIOS */
632}
633
634/*
635 * Return "simple" name from a file name,
636 * strip leading directories.
637 */
638char *
639sname(s)
640	register char *s;
641{
642	register char *p = s;
643
644	while (*s)
645		if (*s++ == '/')
646			p = s;
647	return (p);
648}
649
650static char partab[0200];
651static int bits8;
652
653/*
654 * Do a write to the remote machine with the correct parity.
655 * We are doing 8 bit wide output, so we just generate a character
656 * with the right parity and output it.
657 */
658void
659pwrite(fd, buf, n)
660	int fd;
661	char *buf;
662	register int n;
663{
664	register int i;
665	register char *bp;
666	extern int errno;
667
668	bp = buf;
669	if (bits8 == 0)
670		for (i = 0; i < n; i++) {
671			*bp = partab[(*bp) & 0177];
672			bp++;
673		}
674	if (write(fd, buf, n) < 0) {
675		if (errno == EIO)
676			tipabort("Lost carrier.");
677		if (errno == ENODEV)
678			tipabort("tty not available.");
679		tipabort("Something wrong...");
680	}
681}
682
683/*
684 * Build a parity table with appropriate high-order bit.
685 */
686void
687setparity(defparity)
688	char *defparity;
689{
690	register int i, flip, clr, set;
691	char *parity;
692	extern char evenpartab[];
693
694	if (value(PARITY) == NOSTR)
695		value(PARITY) = defparity;
696	parity = value(PARITY);
697	if (equal(parity, "none")) {
698		bits8 = 1;
699		return;
700	}
701	bits8 = 0;
702	flip = 0;
703	clr = 0377;
704	set = 0;
705	if (equal(parity, "odd"))
706		flip = 0200;			/* reverse bit 7 */
707	else if (equal(parity, "zero"))
708		clr = 0177;			/* turn off bit 7 */
709	else if (equal(parity, "one"))
710		set = 0200;			/* turn on bit 7 */
711	else if (!equal(parity, "even")) {
712		(void) fprintf(stderr, "%s: unknown parity value\r\n", parity);
713		(void) fflush(stderr);
714	}
715	for (i = 0; i < 0200; i++)
716		partab[i] = evenpartab[i] ^ flip | set & clr;
717}
718