xref: /original-bsd/usr.bin/rlogin/rlogin.c (revision a910c8b7)
1 #ifndef lint
2 static char sccsid[] = "@(#)rlogin.c	4.17 (Berkeley) 85/03/17";
3 #endif
4 
5 /*
6  * rlogin - remote login
7  */
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <sys/wait.h>
11 
12 #include <netinet/in.h>
13 
14 #include <stdio.h>
15 #include <sgtty.h>
16 #include <errno.h>
17 #include <pwd.h>
18 #include <signal.h>
19 #include <netdb.h>
20 #include <setjmp.h>
21 
22 char	*index(), *rindex(), *malloc(), *getenv();
23 struct	passwd *getpwuid();
24 char	*name;
25 int	rem;
26 char	cmdchar = '~';
27 int	eight;
28 char	*speeds[] =
29     { "0", "50", "75", "110", "134", "150", "200", "300",
30       "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" };
31 char	term[256] = "network";
32 extern	int errno;
33 int	lostpeer();
34 int	nosigwin;
35 jmp_buf	winsizechanged;
36 struct	winsize winsize;
37 int	sigwinch();
38 
39 main(argc, argv)
40 	int argc;
41 	char **argv;
42 {
43 	char *host, *cp;
44 	struct sgttyb ttyb;
45 	struct passwd *pwd;
46 	struct servent *sp;
47 	int uid, options = 0;
48 	int on = 1;
49 
50 	host = rindex(argv[0], '/');
51 	if (host)
52 		host++;
53 	else
54 		host = argv[0];
55 	argv++, --argc;
56 	if (!strcmp(host, "rlogin"))
57 		host = *argv++, --argc;
58 another:
59 	if (argc > 0 && !strcmp(*argv, "-d")) {
60 		argv++, argc--;
61 		options |= SO_DEBUG;
62 		goto another;
63 	}
64 	if (argc > 0 && !strcmp(*argv, "-l")) {
65 		argv++, argc--;
66 		if (argc == 0)
67 			goto usage;
68 		name = *argv++; argc--;
69 		goto another;
70 	}
71 	if (argc > 0 && !strncmp(*argv, "-e", 2)) {
72 		cmdchar = argv[0][2];
73 		argv++, argc--;
74 		goto another;
75 	}
76 	if (argc > 0 && !strcmp(*argv, "-8")) {
77 		eight = 1;
78 		argv++, argc--;
79 		goto another;
80 	}
81 	if (argc > 0 && !strcmp(*argv, "-w")) {
82 		nosigwin++;
83 		argv++, argc--;
84 		goto another;
85 	}
86 	if (host == 0)
87 		goto usage;
88 	if (argc > 0)
89 		goto usage;
90 	pwd = getpwuid(getuid());
91 	if (pwd == 0) {
92 		fprintf(stderr, "Who are you?\n");
93 		exit(1);
94 	}
95 	sp = getservbyname("login", "tcp");
96 	if (sp == 0) {
97 		fprintf(stderr, "rlogin: login/tcp: unknown service\n");
98 		exit(2);
99 	}
100 	cp = getenv("TERM");
101 	if (cp)
102 		strcpy(term, cp);
103 	if (ioctl(0, TIOCGETP, &ttyb) == 0) {
104 		strcat(term, "/");
105 		strcat(term, speeds[ttyb.sg_ospeed]);
106 	}
107 	if (!nosigwin && ioctl(0, TIOCGWINSZ, &winsize) == 0) {
108 		cp = index(term, '\0');
109 		sprintf(cp, "/%u,%u,%u,%u", winsize.ws_row, winsize.ws_col,
110 		    winsize.ws_xpixel, winsize.ws_ypixel);
111 	}
112 	signal(SIGPIPE, lostpeer);
113         rem = rcmd(&host, sp->s_port, pwd->pw_name,
114 	    name ? name : pwd->pw_name, term, 0);
115         if (rem < 0)
116                 exit(1);
117 	if (options & SO_DEBUG &&
118 	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0)
119 		perror("rlogin: setsockopt (SO_DEBUG)");
120 	uid = getuid();
121 	if (setuid(uid) < 0) {
122 		perror("rlogin: setuid");
123 		exit(1);
124 	}
125 	doit();
126 	/*NOTREACHED*/
127 usage:
128 	fprintf(stderr,
129 	    "usage: rlogin host [ -ex ] [ -l username ] [ -8 ]\n");
130 	exit(1);
131 }
132 
133 #define CRLF "\r\n"
134 
135 int	child;
136 int	catchild();
137 
138 int	defflags, tabflag;
139 char	deferase, defkill;
140 struct	tchars deftc;
141 struct	ltchars defltc;
142 struct	tchars notc =	{ -1, -1, -1, -1, -1, -1 };
143 struct	ltchars noltc =	{ -1, -1, -1, -1, -1, -1 };
144 
145 doit()
146 {
147 	int exit();
148 	struct sgttyb sb;
149 
150 	ioctl(0, TIOCGETP, (char *)&sb);
151 	defflags = sb.sg_flags;
152 	tabflag = defflags & TBDELAY;
153 	defflags &= ECHO | CRMOD;
154 	deferase = sb.sg_erase;
155 	defkill = sb.sg_kill;
156 	ioctl(0, TIOCGETC, (char *)&deftc);
157 	notc.t_startc = deftc.t_startc;
158 	notc.t_stopc = deftc.t_stopc;
159 	ioctl(0, TIOCGLTC, (char *)&defltc);
160 	signal(SIGINT, exit);
161 	signal(SIGHUP, exit);
162 	signal(SIGQUIT, exit);
163 	child = fork();
164 	if (child == -1) {
165 		perror("rlogin: fork");
166 		done();
167 	}
168 	signal(SIGINT, SIG_IGN);
169 	mode(1);
170 	if (child == 0) {
171 		reader();
172 		sleep(1);
173 		prf("\007Connection closed.");
174 		exit(3);
175 	}
176 	signal(SIGCHLD, catchild);
177 	if (!nosigwin)
178 		signal(SIGWINCH, sigwinch);
179 	writer();
180 	prf("Closed connection.");
181 	done();
182 }
183 
184 done()
185 {
186 
187 	mode(0);
188 	if (child > 0 && kill(child, SIGKILL) >= 0)
189 		wait((int *)0);
190 	exit(0);
191 }
192 
193 catchild()
194 {
195 	union wait status;
196 	int pid;
197 
198 again:
199 	pid = wait3(&status, WNOHANG|WUNTRACED, 0);
200 	if (pid == 0)
201 		return;
202 	/*
203 	 * if the child (reader) dies, just quit
204 	 */
205 	if (pid < 0 || pid == child && !WIFSTOPPED(status))
206 		done();
207 	goto again;
208 }
209 
210 /*
211  * writer: write to remote: 0 -> line.
212  * ~.	terminate
213  * ~^Z	suspend rlogin process.
214  * ~^Y  suspend rlogin process, but leave reader alone.
215  */
216 writer()
217 {
218 	char obuf[600], c;
219 	register char *op;
220 	register n;
221 
222 	/*
223 	 * Handle SIGWINCH's with in-band signaling of new
224 	 * window size.  It seems reasonable that we flush
225 	 * pending input and not force out of band signal
226 	 * as this most likely will occur from an input device
227 	 * other than the keyboard (e.g. a mouse).
228 	 *
229 	 * The hack of using 0377 to signal an in-band signal
230 	 * is pretty bad, but otherwise we'd be forced to
231 	 * either get complicated (use MSG_OOB) or go to a
232 	 * serious (telnet-style) protocol.
233 	 */
234 	if (setjmp(winsizechanged)) {
235 		struct winsize *wp = (struct winsize *)(obuf+4);
236 
237 		obuf[0] = 0377;			/* XXX */
238 		obuf[1] = 0377;			/* XXX */
239 		obuf[2] = 's';			/* XXX */
240 		obuf[3] = 's';			/* XXX */
241 		wp->ws_row = htons(winsize.ws_row);
242 		wp->ws_col = htons(winsize.ws_col);
243 		wp->ws_xpixel = htons(winsize.ws_xpixel);
244 		wp->ws_ypixel = htons(winsize.ws_ypixel);
245 		(void) write(rem, obuf, 4+sizeof (*wp));
246 	}
247 top:
248 	op = obuf;
249 	for (;;) {
250 		int local;
251 
252 		n = read(0, &c, 1);
253 		if (n <= 0) {
254 			if (n < 0 && errno == EINTR)
255 				continue;
256 			break;
257 		}
258 		if (!eight)
259 			c &= 0177;
260 		/*
261 		 * If we're at the beginning of the line
262 		 * and recognize a command character, then
263 		 * we echo locally.  Otherwise, characters
264 		 * are echo'd remotely.  If the command
265 		 * character is doubled, this acts as a
266 		 * force and local echo is suppressed.
267 		 */
268 		if (op == obuf)
269 			local = (c == cmdchar);
270 		if (op == obuf + 1 && *obuf == cmdchar)
271 			local = (c != cmdchar);
272 		if (!local) {
273 			if (write(rem, &c, 1) == 0) {
274 				prf("line gone");
275 				return;
276 			}
277 			if (!eight)
278 				c &= 0177;
279 		} else {
280 			if (c == '\r' || c == '\n') {
281 				char cmdc = obuf[1];
282 
283 				if (cmdc == '.' || cmdc == deftc.t_eofc) {
284 					write(0, CRLF, sizeof(CRLF));
285 					return;
286 				}
287 				if (cmdc == defltc.t_suspc ||
288 				    cmdc == defltc.t_dsuspc) {
289 					stop(cmdc);
290 					goto top;
291 				}
292 				*op++ = c;
293 				write(rem, obuf, op - obuf);
294 				goto top;
295 			}
296 			write(1, &c, 1);
297 		}
298 		*op++ = c;
299 		if (c == deferase) {
300 			op -= 2;
301 			if (op < obuf)
302 				goto top;
303 		}
304 		if (c == defkill || c == deftc.t_eofc ||
305 		    c == '\r' || c == '\n')
306 			goto top;
307 		if (op >= &obuf[sizeof (obuf)])
308 			op--;
309 	}
310 }
311 
312 stop(cmdc)
313 	char cmdc;
314 {
315 	struct winsize ws;
316 
317 	write(0, CRLF, sizeof(CRLF));
318 	mode(0);
319 	signal(SIGCHLD, SIG_IGN);
320 	kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
321 	signal(SIGCHLD, catchild);
322 	mode(1);
323 	sigwinch();			/* check for size changes */
324 }
325 
326 sigwinch()
327 {
328 	struct winsize ws;
329 
330 	if (!nosigwin && ioctl(0, TIOCGWINSZ, &ws) == 0 &&
331 	    bcmp(&ws, &winsize, sizeof (ws))) {
332 		winsize = ws;
333 		longjmp(winsizechanged, 1);
334 	}
335 }
336 
337 oob()
338 {
339 	int out = 1+1, atmark;
340 	char waste[BUFSIZ], mark;
341 
342 	ioctl(1, TIOCFLUSH, (char *)&out);
343 	for (;;) {
344 		if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
345 			perror("ioctl");
346 			break;
347 		}
348 		if (atmark)
349 			break;
350 		(void) read(rem, waste, sizeof (waste));
351 	}
352 	recv(rem, &mark, 1, MSG_OOB);
353 	if (mark & TIOCPKT_NOSTOP) {
354 		notc.t_stopc = -1;
355 		notc.t_startc = -1;
356 		ioctl(0, TIOCSETC, (char *)&notc);
357 	}
358 	if (mark & TIOCPKT_DOSTOP) {
359 		notc.t_stopc = deftc.t_stopc;
360 		notc.t_startc = deftc.t_startc;
361 		ioctl(0, TIOCSETC, (char *)&notc);
362 	}
363 }
364 
365 /*
366  * reader: read from remote: line -> 1
367  */
368 reader()
369 {
370 	char rb[BUFSIZ];
371 	register int cnt;
372 
373 	signal(SIGURG, oob);
374 	{ int pid = -getpid();
375 	  ioctl(rem, SIOCSPGRP, (char *)&pid); }
376 	for (;;) {
377 		cnt = read(rem, rb, sizeof (rb));
378 		if (cnt == 0)
379 			break;
380 		if (cnt < 0) {
381 			if (errno == EINTR)
382 				continue;
383 			break;
384 		}
385 		write(1, rb, cnt);
386 	}
387 }
388 
389 mode(f)
390 {
391 	struct tchars *tc;
392 	struct ltchars *ltc;
393 	struct sgttyb sb;
394 
395 	ioctl(0, TIOCGETP, (char *)&sb);
396 	switch (f) {
397 
398 	case 0:
399 		sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
400 		sb.sg_flags |= defflags|tabflag;
401 		tc = &deftc;
402 		ltc = &defltc;
403 		sb.sg_kill = defkill;
404 		sb.sg_erase = deferase;
405 		break;
406 
407 	case 1:
408 		sb.sg_flags |= (eight ? RAW : CBREAK);
409 		sb.sg_flags &= ~defflags;
410 		/* preserve tab delays, but turn off XTABS */
411 		if ((sb.sg_flags & TBDELAY) == XTABS)
412 			sb.sg_flags &= ~TBDELAY;
413 		tc = &notc;
414 		ltc = &noltc;
415 		sb.sg_kill = sb.sg_erase = -1;
416 		break;
417 
418 	default:
419 		return;
420 	}
421 	ioctl(0, TIOCSLTC, (char *)ltc);
422 	ioctl(0, TIOCSETC, (char *)tc);
423 	ioctl(0, TIOCSETN, (char *)&sb);
424 }
425 
426 /*VARARGS*/
427 prf(f, a1, a2, a3)
428 	char *f;
429 {
430 	fprintf(stderr, f, a1, a2, a3);
431 	fprintf(stderr, CRLF);
432 }
433 
434 lostpeer()
435 {
436 	signal(SIGPIPE, SIG_IGN);
437 	prf("\007Connection closed.");
438 	done();
439 }
440