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