rlogin.c revision 1.15 1 1.15 tls /* $NetBSD: rlogin.c,v 1.15 1997/01/09 06:57:46 tls Exp $ */
2 1.4 cgd
3 1.1 cgd /*
4 1.4 cgd * Copyright (c) 1983, 1990, 1993
5 1.4 cgd * The Regents of the University of California. All rights reserved.
6 1.1 cgd *
7 1.1 cgd * Redistribution and use in source and binary forms, with or without
8 1.1 cgd * modification, are permitted provided that the following conditions
9 1.1 cgd * are met:
10 1.1 cgd * 1. Redistributions of source code must retain the above copyright
11 1.1 cgd * notice, this list of conditions and the following disclaimer.
12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 cgd * notice, this list of conditions and the following disclaimer in the
14 1.1 cgd * documentation and/or other materials provided with the distribution.
15 1.1 cgd * 3. All advertising materials mentioning features or use of this software
16 1.1 cgd * must display the following acknowledgement:
17 1.1 cgd * This product includes software developed by the University of
18 1.1 cgd * California, Berkeley and its contributors.
19 1.1 cgd * 4. Neither the name of the University nor the names of its contributors
20 1.1 cgd * may be used to endorse or promote products derived from this software
21 1.1 cgd * without specific prior written permission.
22 1.1 cgd *
23 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 1.1 cgd * SUCH DAMAGE.
34 1.1 cgd */
35 1.1 cgd
36 1.1 cgd #ifndef lint
37 1.4 cgd static char copyright[] =
38 1.4 cgd "@(#) Copyright (c) 1983, 1990, 1993\n\
39 1.4 cgd The Regents of the University of California. All rights reserved.\n";
40 1.1 cgd #endif /* not lint */
41 1.1 cgd
42 1.1 cgd #ifndef lint
43 1.4 cgd #if 0
44 1.15 tls static char sccsid[] = "@(#)rlogin.c 8.4 (Berkeley) 4/29/95";
45 1.4 cgd #else
46 1.15 tls static char rcsid[] = "$NetBSD: rlogin.c,v 1.15 1997/01/09 06:57:46 tls Exp $";
47 1.4 cgd #endif
48 1.1 cgd #endif /* not lint */
49 1.1 cgd
50 1.1 cgd /*
51 1.1 cgd * rlogin - remote login
52 1.1 cgd */
53 1.1 cgd #include <sys/param.h>
54 1.5 mycroft #include <sys/ioctl.h>
55 1.1 cgd #include <sys/socket.h>
56 1.1 cgd #include <sys/time.h>
57 1.1 cgd #include <sys/resource.h>
58 1.1 cgd #include <sys/wait.h>
59 1.15 tls #include <sys/ioctl.h>
60 1.1 cgd
61 1.1 cgd #include <netinet/in.h>
62 1.1 cgd #include <netinet/in_systm.h>
63 1.1 cgd #include <netinet/ip.h>
64 1.1 cgd
65 1.1 cgd #include <errno.h>
66 1.4 cgd #include <fcntl.h>
67 1.4 cgd #include <netdb.h>
68 1.1 cgd #include <pwd.h>
69 1.4 cgd #include <setjmp.h>
70 1.5 mycroft #include <termios.h>
71 1.4 cgd #include <signal.h>
72 1.1 cgd #include <stdio.h>
73 1.4 cgd #include <stdlib.h>
74 1.4 cgd #include <string.h>
75 1.1 cgd #include <unistd.h>
76 1.4 cgd
77 1.4 cgd #ifdef __STDC__
78 1.4 cgd #include <stdarg.h>
79 1.4 cgd #else
80 1.4 cgd #include <varargs.h>
81 1.4 cgd #endif
82 1.1 cgd
83 1.1 cgd #ifdef KERBEROS
84 1.1 cgd #include <kerberosIV/des.h>
85 1.1 cgd #include <kerberosIV/krb.h>
86 1.1 cgd
87 1.4 cgd #include "krb.h"
88 1.4 cgd
89 1.1 cgd CREDENTIALS cred;
90 1.1 cgd Key_schedule schedule;
91 1.1 cgd int use_kerberos = 1, doencrypt;
92 1.1 cgd char dst_realm_buf[REALM_SZ], *dest_realm = NULL;
93 1.1 cgd #endif
94 1.1 cgd
95 1.1 cgd #ifndef TIOCPKT_WINDOW
96 1.1 cgd #define TIOCPKT_WINDOW 0x80
97 1.1 cgd #endif
98 1.1 cgd
99 1.1 cgd /* concession to Sun */
100 1.1 cgd #ifndef SIGUSR1
101 1.1 cgd #define SIGUSR1 30
102 1.1 cgd #endif
103 1.1 cgd
104 1.5 mycroft #ifndef CCEQ
105 1.5 mycroft #define CCEQ(val, c) (c == val ? val != _POSIX_VDISABLE : 0)
106 1.5 mycroft #endif
107 1.5 mycroft
108 1.5 mycroft int eight, rem;
109 1.5 mycroft struct termios deftty;
110 1.1 cgd
111 1.1 cgd int noescape;
112 1.1 cgd u_char escapechar = '~';
113 1.1 cgd
114 1.4 cgd #ifdef OLDSUN
115 1.1 cgd struct winsize {
116 1.1 cgd unsigned short ws_row, ws_col;
117 1.1 cgd unsigned short ws_xpixel, ws_ypixel;
118 1.1 cgd };
119 1.4 cgd #else
120 1.4 cgd #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
121 1.1 cgd #endif
122 1.1 cgd struct winsize winsize;
123 1.1 cgd
124 1.4 cgd void catch_child __P((int));
125 1.4 cgd void copytochild __P((int));
126 1.15 tls __dead void doit __P((sigset_t *));
127 1.4 cgd __dead void done __P((int));
128 1.4 cgd void echo __P((char));
129 1.4 cgd u_int getescape __P((char *));
130 1.4 cgd void lostpeer __P((int));
131 1.4 cgd void mode __P((int));
132 1.4 cgd void msg __P((char *));
133 1.4 cgd void oob __P((int));
134 1.15 tls int reader __P((sigset_t *));
135 1.4 cgd void sendwindow __P((void));
136 1.4 cgd void setsignal __P((int));
137 1.15 tls int speed __P((int));
138 1.4 cgd void sigwinch __P((int));
139 1.6 mycroft void stop __P((int));
140 1.4 cgd __dead void usage __P((void));
141 1.4 cgd void writer __P((void));
142 1.4 cgd void writeroob __P((int));
143 1.4 cgd
144 1.4 cgd #ifdef KERBEROS
145 1.4 cgd void warning __P((const char *, ...));
146 1.4 cgd #endif
147 1.4 cgd #ifdef OLDSUN
148 1.4 cgd int get_window_size __P((int, struct winsize *));
149 1.1 cgd #endif
150 1.1 cgd
151 1.4 cgd int
152 1.1 cgd main(argc, argv)
153 1.1 cgd int argc;
154 1.4 cgd char *argv[];
155 1.1 cgd {
156 1.1 cgd struct passwd *pw;
157 1.1 cgd struct servent *sp;
158 1.5 mycroft struct termios tty;
159 1.15 tls sigset_t smask;
160 1.1 cgd int argoff, ch, dflag, one, uid;
161 1.12 mrg int i, len, len2;
162 1.12 mrg char *host, *p, *user, term[1024] = "network";
163 1.12 mrg speed_t ospeed;
164 1.15 tls struct sigaction sa;
165 1.1 cgd
166 1.1 cgd argoff = dflag = 0;
167 1.1 cgd one = 1;
168 1.1 cgd host = user = NULL;
169 1.1 cgd
170 1.15 tls if (p = strrchr(argv[0], '/'))
171 1.1 cgd ++p;
172 1.1 cgd else
173 1.1 cgd p = argv[0];
174 1.1 cgd
175 1.15 tls if (strcmp(p, "rlogin") != 0)
176 1.1 cgd host = p;
177 1.1 cgd
178 1.1 cgd /* handle "rlogin host flags" */
179 1.1 cgd if (!host && argc > 2 && argv[1][0] != '-') {
180 1.1 cgd host = argv[1];
181 1.1 cgd argoff = 1;
182 1.1 cgd }
183 1.1 cgd
184 1.1 cgd #ifdef KERBEROS
185 1.1 cgd #define OPTIONS "8EKLde:k:l:x"
186 1.1 cgd #else
187 1.1 cgd #define OPTIONS "8EKLde:l:"
188 1.1 cgd #endif
189 1.1 cgd while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF)
190 1.1 cgd switch(ch) {
191 1.1 cgd case '8':
192 1.1 cgd eight = 1;
193 1.1 cgd break;
194 1.1 cgd case 'E':
195 1.1 cgd noescape = 1;
196 1.1 cgd break;
197 1.1 cgd case 'K':
198 1.1 cgd #ifdef KERBEROS
199 1.1 cgd use_kerberos = 0;
200 1.1 cgd #endif
201 1.1 cgd break;
202 1.1 cgd case 'd':
203 1.1 cgd dflag = 1;
204 1.1 cgd break;
205 1.1 cgd case 'e':
206 1.4 cgd noescape = 0;
207 1.1 cgd escapechar = getescape(optarg);
208 1.1 cgd break;
209 1.1 cgd #ifdef KERBEROS
210 1.1 cgd case 'k':
211 1.1 cgd dest_realm = dst_realm_buf;
212 1.1 cgd (void)strncpy(dest_realm, optarg, REALM_SZ);
213 1.1 cgd break;
214 1.1 cgd #endif
215 1.1 cgd case 'l':
216 1.1 cgd user = optarg;
217 1.1 cgd break;
218 1.1 cgd #ifdef CRYPT
219 1.1 cgd #ifdef KERBEROS
220 1.1 cgd case 'x':
221 1.1 cgd doencrypt = 1;
222 1.1 cgd des_set_key(cred.session, schedule);
223 1.1 cgd break;
224 1.1 cgd #endif
225 1.1 cgd #endif
226 1.1 cgd case '?':
227 1.1 cgd default:
228 1.1 cgd usage();
229 1.1 cgd }
230 1.1 cgd optind += argoff;
231 1.1 cgd argc -= optind;
232 1.1 cgd argv += optind;
233 1.1 cgd
234 1.1 cgd /* if haven't gotten a host yet, do so */
235 1.1 cgd if (!host && !(host = *argv++))
236 1.1 cgd usage();
237 1.1 cgd
238 1.1 cgd if (*argv)
239 1.1 cgd usage();
240 1.1 cgd
241 1.15 tls if (!(pw = getpwuid(uid = getuid())))
242 1.15 tls errx(1, "unknown user id.");
243 1.15 tls /* Accept user1@host format, though "-l user2" overrides user1 */
244 1.15 tls p = strchr(host, '@');
245 1.15 tls if (p) {
246 1.15 tls *p = '\0';
247 1.15 tls if (!user && p > host)
248 1.15 tls user = host;
249 1.15 tls host = p + 1;
250 1.15 tls if (*host == '\0')
251 1.15 tls usage();
252 1.1 cgd }
253 1.1 cgd if (!user)
254 1.1 cgd user = pw->pw_name;
255 1.1 cgd
256 1.1 cgd sp = NULL;
257 1.1 cgd #ifdef KERBEROS
258 1.1 cgd if (use_kerberos) {
259 1.1 cgd sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
260 1.1 cgd if (sp == NULL) {
261 1.1 cgd use_kerberos = 0;
262 1.1 cgd warning("can't get entry for %s/tcp service",
263 1.1 cgd doencrypt ? "eklogin" : "klogin");
264 1.1 cgd }
265 1.1 cgd }
266 1.1 cgd #endif
267 1.1 cgd if (sp == NULL)
268 1.1 cgd sp = getservbyname("login", "tcp");
269 1.15 tls if (sp == NULL)
270 1.15 tls errx(1, "login/tcp: unknown service.");
271 1.1 cgd
272 1.12 mrg if (p = getenv("TERM")) {
273 1.12 mrg (void)strncpy(term, p, sizeof(term) - 1);
274 1.12 mrg term[sizeof(term) - 1] = '\0';
275 1.12 mrg }
276 1.12 mrg len = strlen(term);
277 1.12 mrg if (len < (sizeof(term) - 1) && tcgetattr(0, &tty) == 0) {
278 1.12 mrg /* start at 2 to include the / */
279 1.13 thorpej for (ospeed = i = cfgetospeed(&tty), len2 = 2; i > 9; len2++)
280 1.12 mrg i /= 10;
281 1.12 mrg
282 1.14 explorer if (len + len2 < sizeof(term))
283 1.14 explorer (void)snprintf(term + len, len2 + 1, "/%d", ospeed);
284 1.1 cgd }
285 1.1 cgd
286 1.1 cgd (void)get_window_size(0, &winsize);
287 1.1 cgd
288 1.15 tls sigemptyset(&sa.sa_mask);
289 1.15 tls sa.sa_flags = SA_RESTART;
290 1.15 tls sa.sa_handler = lostpeer;
291 1.15 tls (void)sigaction(SIGPIPE, &sa, (struct sigaction *) 0);
292 1.1 cgd /* will use SIGUSR1 for window size hack, so hold it off */
293 1.15 tls sigemptyset(&smask);
294 1.15 tls sigaddset(&smask, SIGURG);
295 1.15 tls sigaddset(&smask, SIGUSR1);
296 1.15 tls (void)sigprocmask(SIG_SETMASK, &smask, &smask);
297 1.4 cgd /*
298 1.4 cgd * We set SIGURG and SIGUSR1 below so that an
299 1.4 cgd * incoming signal will be held pending rather than being
300 1.4 cgd * discarded. Note that these routines will be ready to get
301 1.4 cgd * a signal by the time that they are unblocked below.
302 1.4 cgd */
303 1.15 tls sa.sa_handler = copytochild;
304 1.15 tls (void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
305 1.15 tls sa.sa_handler = writeroob;
306 1.15 tls (void)sigaction(SIGUSR1, &sa, (struct sigaction *) 0);
307 1.1 cgd
308 1.1 cgd #ifdef KERBEROS
309 1.1 cgd try_connect:
310 1.1 cgd if (use_kerberos) {
311 1.4 cgd struct hostent *hp;
312 1.4 cgd
313 1.4 cgd /* Fully qualify hostname (needed for krb_realmofhost). */
314 1.4 cgd hp = gethostbyname(host);
315 1.15 tls if (hp != NULL && !(host = strdup(hp->h_name)))
316 1.15 tls errx(1, "%s", strerror(ENOMEM));
317 1.4 cgd
318 1.1 cgd rem = KSUCCESS;
319 1.1 cgd errno = 0;
320 1.1 cgd if (dest_realm == NULL)
321 1.1 cgd dest_realm = krb_realmofhost(host);
322 1.1 cgd
323 1.1 cgd #ifdef CRYPT
324 1.1 cgd if (doencrypt)
325 1.1 cgd rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
326 1.1 cgd dest_realm, &cred, schedule);
327 1.1 cgd else
328 1.1 cgd #endif /* CRYPT */
329 1.1 cgd rem = krcmd(&host, sp->s_port, user, term, 0,
330 1.1 cgd dest_realm);
331 1.1 cgd if (rem < 0) {
332 1.1 cgd use_kerberos = 0;
333 1.1 cgd sp = getservbyname("login", "tcp");
334 1.15 tls if (sp == NULL)
335 1.15 tls errx(1, "unknown service login/tcp.");
336 1.1 cgd if (errno == ECONNREFUSED)
337 1.1 cgd warning("remote host doesn't support Kerberos");
338 1.1 cgd if (errno == ENOENT)
339 1.1 cgd warning("can't provide Kerberos auth data");
340 1.1 cgd goto try_connect;
341 1.1 cgd }
342 1.1 cgd } else {
343 1.1 cgd #ifdef CRYPT
344 1.15 tls if (doencrypt)
345 1.15 tls errx(1, "the -x flag requires Kerberos authentication.");
346 1.1 cgd #endif /* CRYPT */
347 1.1 cgd rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
348 1.1 cgd }
349 1.1 cgd #else
350 1.1 cgd rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
351 1.1 cgd #endif /* KERBEROS */
352 1.1 cgd
353 1.1 cgd if (rem < 0)
354 1.1 cgd exit(1);
355 1.1 cgd
356 1.1 cgd if (dflag &&
357 1.1 cgd setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
358 1.15 tls warn("setsockopt DEBUG (ignored)");
359 1.1 cgd one = IPTOS_LOWDELAY;
360 1.1 cgd if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0)
361 1.15 tls warn("setsockopt TOS (ignored)");
362 1.1 cgd
363 1.1 cgd (void)setuid(uid);
364 1.15 tls doit(&smask);
365 1.1 cgd /*NOTREACHED*/
366 1.1 cgd }
367 1.1 cgd
368 1.15 tls #if BSD >= 198810
369 1.15 tls int
370 1.15 tls speed(fd)
371 1.15 tls int fd;
372 1.15 tls {
373 1.15 tls struct termios tt;
374 1.15 tls
375 1.15 tls (void)tcgetattr(fd, &tt);
376 1.15 tls
377 1.15 tls return ((int) cfgetispeed(&tt));
378 1.15 tls }
379 1.15 tls #else
380 1.15 tls int speeds[] = { /* for older systems, B0 .. EXTB */
381 1.15 tls 0, 50, 75, 110,
382 1.15 tls 134, 150, 200, 300,
383 1.15 tls 600, 1200, 1800, 2400,
384 1.15 tls 4800, 9600, 19200, 38400
385 1.15 tls };
386 1.15 tls
387 1.15 tls int
388 1.15 tls speed(fd)
389 1.15 tls int fd;
390 1.15 tls {
391 1.15 tls struct termios tt;
392 1.15 tls
393 1.15 tls (void)tcgetattr(fd, &tt);
394 1.15 tls
395 1.15 tls return (speeds[(int)cfgetispeed(&tt)]);
396 1.15 tls }
397 1.15 tls #endif
398 1.15 tls
399 1.15 tls pid_t child;
400 1.15 tls struct termios deftt;
401 1.15 tls struct termios nott;
402 1.1 cgd
403 1.4 cgd void
404 1.15 tls doit(smask)
405 1.15 tls sigset_t *smask;
406 1.1 cgd {
407 1.15 tls int i;
408 1.15 tls struct sigaction sa;
409 1.1 cgd
410 1.15 tls for (i = 0; i < NCCS; i++)
411 1.15 tls nott.c_cc[i] = _POSIX_VDISABLE;
412 1.15 tls tcgetattr(0, &deftt);
413 1.15 tls nott.c_cc[VSTART] = deftt.c_cc[VSTART];
414 1.15 tls nott.c_cc[VSTOP] = deftt.c_cc[VSTOP];
415 1.15 tls sigemptyset(&sa.sa_mask);
416 1.15 tls sa.sa_flags = SA_RESTART;
417 1.15 tls sa.sa_handler = SIG_IGN;
418 1.15 tls (void)sigaction(SIGINT, &sa, (struct sigaction *) 0);
419 1.4 cgd setsignal(SIGHUP);
420 1.4 cgd setsignal(SIGQUIT);
421 1.5 mycroft mode(1);
422 1.1 cgd child = fork();
423 1.1 cgd if (child == -1) {
424 1.15 tls warn("fork");
425 1.1 cgd done(1);
426 1.1 cgd }
427 1.1 cgd if (child == 0) {
428 1.15 tls mode(1);
429 1.15 tls if (reader(smask) == 0) {
430 1.1 cgd msg("connection closed.");
431 1.1 cgd exit(0);
432 1.1 cgd }
433 1.1 cgd sleep(1);
434 1.5 mycroft msg("\aconnection closed.");
435 1.1 cgd exit(1);
436 1.1 cgd }
437 1.1 cgd
438 1.1 cgd /*
439 1.1 cgd * We may still own the socket, and may have a pending SIGURG (or might
440 1.4 cgd * receive one soon) that we really want to send to the reader. When
441 1.4 cgd * one of these comes in, the trap copytochild simply copies such
442 1.4 cgd * signals to the child. We can now unblock SIGURG and SIGUSR1
443 1.4 cgd * that were set above.
444 1.1 cgd */
445 1.15 tls (void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
446 1.15 tls sa.sa_handler = catch_child;
447 1.15 tls (void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
448 1.1 cgd writer();
449 1.1 cgd msg("closed connection.");
450 1.1 cgd done(0);
451 1.1 cgd }
452 1.1 cgd
453 1.1 cgd /* trap a signal, unless it is being ignored. */
454 1.4 cgd void
455 1.4 cgd setsignal(sig)
456 1.1 cgd int sig;
457 1.1 cgd {
458 1.15 tls struct sigaction sa;
459 1.15 tls sigset_t sigs;
460 1.1 cgd
461 1.15 tls sigemptyset(&sigs);
462 1.15 tls sigaddset(&sigs, sig);
463 1.15 tls sigprocmask(SIG_BLOCK, &sigs, &sigs);
464 1.15 tls
465 1.15 tls sigemptyset(&sa.sa_mask);
466 1.15 tls sa.sa_handler = exit;
467 1.15 tls sa.sa_flags = SA_RESTART;
468 1.15 tls (void)sigaction(sig, &sa, &sa);
469 1.15 tls if (sa.sa_handler == SIG_IGN)
470 1.15 tls (void)sigaction(sig, &sa, (struct sigaction *) 0);
471 1.15 tls
472 1.15 tls (void)sigprocmask(SIG_SETMASK, &sigs, (sigset_t *) 0);
473 1.1 cgd }
474 1.1 cgd
475 1.4 cgd __dead void
476 1.1 cgd done(status)
477 1.1 cgd int status;
478 1.1 cgd {
479 1.15 tls pid_t w;
480 1.15 tls int wstatus;
481 1.15 tls struct sigaction sa;
482 1.1 cgd
483 1.1 cgd mode(0);
484 1.1 cgd if (child > 0) {
485 1.1 cgd /* make sure catch_child does not snap it up */
486 1.15 tls sigemptyset(&sa.sa_mask);
487 1.15 tls sa.sa_handler = SIG_DFL;
488 1.15 tls sa.sa_flags = 0;
489 1.15 tls (void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
490 1.1 cgd if (kill(child, SIGKILL) >= 0)
491 1.15 tls while ((w = wait(&wstatus)) > 0 && w != child)
492 1.15 tls continue;
493 1.1 cgd }
494 1.1 cgd exit(status);
495 1.1 cgd }
496 1.1 cgd
497 1.1 cgd int dosigwinch;
498 1.1 cgd
499 1.1 cgd /*
500 1.1 cgd * This is called when the reader process gets the out-of-band (urgent)
501 1.1 cgd * request to turn on the window-changing protocol.
502 1.1 cgd */
503 1.1 cgd void
504 1.4 cgd writeroob(signo)
505 1.4 cgd int signo;
506 1.1 cgd {
507 1.15 tls struct sigaction sa;
508 1.15 tls
509 1.1 cgd if (dosigwinch == 0) {
510 1.1 cgd sendwindow();
511 1.15 tls sigemptyset(&sa.sa_mask);
512 1.15 tls sa.sa_handler = sigwinch;
513 1.15 tls sa.sa_flags = SA_RESTART;
514 1.15 tls (void)sigaction(SIGWINCH, &sa, (struct sigaction *) 0);
515 1.1 cgd }
516 1.1 cgd dosigwinch = 1;
517 1.1 cgd }
518 1.1 cgd
519 1.1 cgd void
520 1.4 cgd catch_child(signo)
521 1.4 cgd int signo;
522 1.1 cgd {
523 1.15 tls int status;
524 1.15 tls pid_t pid;
525 1.1 cgd
526 1.1 cgd for (;;) {
527 1.15 tls pid = waitpid(-1, &status, WNOHANG|WUNTRACED);
528 1.1 cgd if (pid == 0)
529 1.1 cgd return;
530 1.1 cgd /* if the child (reader) dies, just quit */
531 1.4 cgd if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
532 1.15 tls done(WEXITSTATUS(status) | WTERMSIG(status));
533 1.1 cgd }
534 1.1 cgd /* NOTREACHED */
535 1.1 cgd }
536 1.1 cgd
537 1.1 cgd /*
538 1.1 cgd * writer: write to remote: 0 -> line.
539 1.1 cgd * ~. terminate
540 1.1 cgd * ~^Z suspend rlogin process.
541 1.1 cgd * ~<delayed-suspend char> suspend rlogin process, but leave reader alone.
542 1.1 cgd */
543 1.4 cgd void
544 1.1 cgd writer()
545 1.1 cgd {
546 1.1 cgd register int bol, local, n;
547 1.1 cgd char c;
548 1.1 cgd
549 1.1 cgd bol = 1; /* beginning of line */
550 1.1 cgd local = 0;
551 1.1 cgd for (;;) {
552 1.1 cgd n = read(STDIN_FILENO, &c, 1);
553 1.1 cgd if (n <= 0) {
554 1.1 cgd if (n < 0 && errno == EINTR)
555 1.1 cgd continue;
556 1.1 cgd break;
557 1.1 cgd }
558 1.1 cgd /*
559 1.1 cgd * If we're at the beginning of the line and recognize a
560 1.1 cgd * command character, then we echo locally. Otherwise,
561 1.1 cgd * characters are echo'd remotely. If the command character
562 1.1 cgd * is doubled, this acts as a force and local echo is
563 1.1 cgd * suppressed.
564 1.1 cgd */
565 1.1 cgd if (bol) {
566 1.1 cgd bol = 0;
567 1.1 cgd if (!noescape && c == escapechar) {
568 1.1 cgd local = 1;
569 1.1 cgd continue;
570 1.1 cgd }
571 1.1 cgd } else if (local) {
572 1.1 cgd local = 0;
573 1.5 mycroft if (c == '.' || CCEQ(deftty.c_cc[VEOF], c)) {
574 1.1 cgd echo(c);
575 1.1 cgd break;
576 1.1 cgd }
577 1.6 mycroft if (CCEQ(deftty.c_cc[VSUSP], c)) {
578 1.1 cgd bol = 1;
579 1.1 cgd echo(c);
580 1.6 mycroft stop(1);
581 1.6 mycroft continue;
582 1.6 mycroft }
583 1.6 mycroft if (CCEQ(deftty.c_cc[VDSUSP], c)) {
584 1.6 mycroft bol = 1;
585 1.6 mycroft echo(c);
586 1.6 mycroft stop(0);
587 1.1 cgd continue;
588 1.1 cgd }
589 1.1 cgd if (c != escapechar)
590 1.1 cgd #ifdef CRYPT
591 1.1 cgd #ifdef KERBEROS
592 1.1 cgd if (doencrypt)
593 1.4 cgd (void)des_write(rem,
594 1.4 cgd (char *)&escapechar, 1);
595 1.1 cgd else
596 1.1 cgd #endif
597 1.1 cgd #endif
598 1.1 cgd (void)write(rem, &escapechar, 1);
599 1.1 cgd }
600 1.1 cgd
601 1.1 cgd #ifdef CRYPT
602 1.1 cgd #ifdef KERBEROS
603 1.1 cgd if (doencrypt) {
604 1.1 cgd if (des_write(rem, &c, 1) == 0) {
605 1.1 cgd msg("line gone");
606 1.1 cgd break;
607 1.1 cgd }
608 1.1 cgd } else
609 1.1 cgd #endif
610 1.1 cgd #endif
611 1.1 cgd if (write(rem, &c, 1) == 0) {
612 1.1 cgd msg("line gone");
613 1.1 cgd break;
614 1.1 cgd }
615 1.5 mycroft bol = CCEQ(deftty.c_cc[VKILL], c) ||
616 1.5 mycroft CCEQ(deftty.c_cc[VEOF], c) ||
617 1.5 mycroft CCEQ(deftty.c_cc[VINTR], c) ||
618 1.5 mycroft CCEQ(deftty.c_cc[VSUSP], c) ||
619 1.1 cgd c == '\r' || c == '\n';
620 1.1 cgd }
621 1.1 cgd }
622 1.1 cgd
623 1.4 cgd void
624 1.4 cgd #if __STDC__
625 1.4 cgd echo(register char c)
626 1.4 cgd #else
627 1.1 cgd echo(c)
628 1.4 cgd register char c;
629 1.4 cgd #endif
630 1.1 cgd {
631 1.1 cgd register char *p;
632 1.1 cgd char buf[8];
633 1.1 cgd
634 1.1 cgd p = buf;
635 1.1 cgd c &= 0177;
636 1.1 cgd *p++ = escapechar;
637 1.1 cgd if (c < ' ') {
638 1.1 cgd *p++ = '^';
639 1.1 cgd *p++ = c + '@';
640 1.1 cgd } else if (c == 0177) {
641 1.1 cgd *p++ = '^';
642 1.1 cgd *p++ = '?';
643 1.1 cgd } else
644 1.1 cgd *p++ = c;
645 1.1 cgd *p++ = '\r';
646 1.1 cgd *p++ = '\n';
647 1.1 cgd (void)write(STDOUT_FILENO, buf, p - buf);
648 1.1 cgd }
649 1.1 cgd
650 1.4 cgd void
651 1.6 mycroft stop(all)
652 1.6 mycroft int all;
653 1.1 cgd {
654 1.15 tls struct sigaction sa;
655 1.15 tls
656 1.1 cgd mode(0);
657 1.15 tls sigemptyset(&sa.sa_mask);
658 1.15 tls sa.sa_handler = SIG_IGN;
659 1.15 tls sa.sa_flags = SA_RESTART;
660 1.15 tls (void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
661 1.6 mycroft (void)kill(all ? 0 : getpid(), SIGTSTP);
662 1.15 tls sa.sa_handler = catch_child;
663 1.15 tls (void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
664 1.1 cgd mode(1);
665 1.4 cgd sigwinch(0); /* check for size changes */
666 1.1 cgd }
667 1.1 cgd
668 1.1 cgd void
669 1.4 cgd sigwinch(signo)
670 1.4 cgd int signo;
671 1.1 cgd {
672 1.1 cgd struct winsize ws;
673 1.1 cgd
674 1.1 cgd if (dosigwinch && get_window_size(0, &ws) == 0 &&
675 1.15 tls memcmp(&ws, &winsize, sizeof(ws))) {
676 1.1 cgd winsize = ws;
677 1.1 cgd sendwindow();
678 1.1 cgd }
679 1.1 cgd }
680 1.1 cgd
681 1.1 cgd /*
682 1.1 cgd * Send the window size to the server via the magic escape
683 1.1 cgd */
684 1.4 cgd void
685 1.1 cgd sendwindow()
686 1.1 cgd {
687 1.1 cgd struct winsize *wp;
688 1.1 cgd char obuf[4 + sizeof (struct winsize)];
689 1.1 cgd
690 1.1 cgd wp = (struct winsize *)(obuf+4);
691 1.1 cgd obuf[0] = 0377;
692 1.1 cgd obuf[1] = 0377;
693 1.1 cgd obuf[2] = 's';
694 1.1 cgd obuf[3] = 's';
695 1.1 cgd wp->ws_row = htons(winsize.ws_row);
696 1.1 cgd wp->ws_col = htons(winsize.ws_col);
697 1.1 cgd wp->ws_xpixel = htons(winsize.ws_xpixel);
698 1.1 cgd wp->ws_ypixel = htons(winsize.ws_ypixel);
699 1.1 cgd
700 1.1 cgd #ifdef CRYPT
701 1.1 cgd #ifdef KERBEROS
702 1.1 cgd if(doencrypt)
703 1.1 cgd (void)des_write(rem, obuf, sizeof(obuf));
704 1.1 cgd else
705 1.1 cgd #endif
706 1.1 cgd #endif
707 1.1 cgd (void)write(rem, obuf, sizeof(obuf));
708 1.1 cgd }
709 1.1 cgd
710 1.1 cgd /*
711 1.1 cgd * reader: read from remote: line -> 1
712 1.1 cgd */
713 1.1 cgd #define READING 1
714 1.1 cgd #define WRITING 2
715 1.1 cgd
716 1.1 cgd jmp_buf rcvtop;
717 1.15 tls pid_t ppid;
718 1.15 tls int rcvcnt, rcvstate;
719 1.1 cgd char rcvbuf[8 * 1024];
720 1.1 cgd
721 1.1 cgd void
722 1.4 cgd oob(signo)
723 1.4 cgd int signo;
724 1.1 cgd {
725 1.5 mycroft struct termios tty;
726 1.8 mycroft int atmark, n, rcvd;
727 1.1 cgd char waste[BUFSIZ], mark;
728 1.1 cgd
729 1.1 cgd rcvd = 0;
730 1.4 cgd while (recv(rem, &mark, 1, MSG_OOB) < 0) {
731 1.1 cgd switch (errno) {
732 1.1 cgd case EWOULDBLOCK:
733 1.1 cgd /*
734 1.1 cgd * Urgent data not here yet. It may not be possible
735 1.1 cgd * to send it yet if we are blocked for output and
736 1.1 cgd * our input buffer is full.
737 1.1 cgd */
738 1.1 cgd if (rcvcnt < sizeof(rcvbuf)) {
739 1.1 cgd n = read(rem, rcvbuf + rcvcnt,
740 1.1 cgd sizeof(rcvbuf) - rcvcnt);
741 1.1 cgd if (n <= 0)
742 1.1 cgd return;
743 1.1 cgd rcvd += n;
744 1.1 cgd } else {
745 1.1 cgd n = read(rem, waste, sizeof(waste));
746 1.1 cgd if (n <= 0)
747 1.1 cgd return;
748 1.1 cgd }
749 1.1 cgd continue;
750 1.1 cgd default:
751 1.1 cgd return;
752 1.4 cgd }
753 1.1 cgd }
754 1.1 cgd if (mark & TIOCPKT_WINDOW) {
755 1.1 cgd /* Let server know about window size changes */
756 1.1 cgd (void)kill(ppid, SIGUSR1);
757 1.1 cgd }
758 1.1 cgd if (!eight && (mark & TIOCPKT_NOSTOP)) {
759 1.5 mycroft (void)tcgetattr(0, &tty);
760 1.5 mycroft tty.c_iflag &= ~IXON;
761 1.5 mycroft (void)tcsetattr(0, TCSANOW, &tty);
762 1.1 cgd }
763 1.1 cgd if (!eight && (mark & TIOCPKT_DOSTOP)) {
764 1.5 mycroft (void)tcgetattr(0, &tty);
765 1.5 mycroft tty.c_iflag |= (deftty.c_iflag & IXON);
766 1.5 mycroft (void)tcsetattr(0, TCSANOW, &tty);
767 1.1 cgd }
768 1.1 cgd if (mark & TIOCPKT_FLUSHWRITE) {
769 1.8 mycroft (void)tcflush(1, TCIOFLUSH);
770 1.1 cgd for (;;) {
771 1.1 cgd if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
772 1.15 tls warn("ioctl SIOCATMARK (ignored)");
773 1.1 cgd break;
774 1.1 cgd }
775 1.1 cgd if (atmark)
776 1.1 cgd break;
777 1.1 cgd n = read(rem, waste, sizeof (waste));
778 1.1 cgd if (n <= 0)
779 1.1 cgd break;
780 1.1 cgd }
781 1.1 cgd /*
782 1.1 cgd * Don't want any pending data to be output, so clear the recv
783 1.1 cgd * buffer. If we were hanging on a write when interrupted,
784 1.1 cgd * don't want it to restart. If we were reading, restart
785 1.1 cgd * anyway.
786 1.1 cgd */
787 1.1 cgd rcvcnt = 0;
788 1.1 cgd longjmp(rcvtop, 1);
789 1.1 cgd }
790 1.1 cgd
791 1.1 cgd /* oob does not do FLUSHREAD (alas!) */
792 1.1 cgd
793 1.1 cgd /*
794 1.1 cgd * If we filled the receive buffer while a read was pending, longjmp
795 1.1 cgd * to the top to restart appropriately. Don't abort a pending write,
796 1.1 cgd * however, or we won't know how much was written.
797 1.1 cgd */
798 1.1 cgd if (rcvd && rcvstate == READING)
799 1.1 cgd longjmp(rcvtop, 1);
800 1.1 cgd }
801 1.1 cgd
802 1.1 cgd /* reader: read from remote: line -> 1 */
803 1.4 cgd int
804 1.15 tls reader(smask)
805 1.15 tls sigset_t *smask;
806 1.1 cgd {
807 1.15 tls pid_t pid;
808 1.15 tls int n, remaining;
809 1.4 cgd char *bufp;
810 1.15 tls struct sigaction sa;
811 1.1 cgd
812 1.4 cgd #if BSD >= 43 || defined(SUNOS4)
813 1.4 cgd pid = getpid(); /* modern systems use positives for pid */
814 1.1 cgd #else
815 1.4 cgd pid = -getpid(); /* old broken systems use negatives */
816 1.1 cgd #endif
817 1.15 tls sigemptyset(&sa.sa_mask);
818 1.15 tls sa.sa_flags = SA_RESTART;
819 1.15 tls sa.sa_handler = SIG_IGN;
820 1.15 tls (void)sigaction(SIGTTOU, &sa, (struct sigaction *) 0);
821 1.15 tls sa.sa_handler = oob;
822 1.15 tls (void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
823 1.1 cgd ppid = getppid();
824 1.1 cgd (void)fcntl(rem, F_SETOWN, pid);
825 1.1 cgd (void)setjmp(rcvtop);
826 1.15 tls (void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
827 1.4 cgd bufp = rcvbuf;
828 1.1 cgd for (;;) {
829 1.1 cgd while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
830 1.1 cgd rcvstate = WRITING;
831 1.1 cgd n = write(STDOUT_FILENO, bufp, remaining);
832 1.1 cgd if (n < 0) {
833 1.1 cgd if (errno != EINTR)
834 1.4 cgd return (-1);
835 1.1 cgd continue;
836 1.1 cgd }
837 1.1 cgd bufp += n;
838 1.1 cgd }
839 1.1 cgd bufp = rcvbuf;
840 1.1 cgd rcvcnt = 0;
841 1.1 cgd rcvstate = READING;
842 1.1 cgd
843 1.1 cgd #ifdef CRYPT
844 1.1 cgd #ifdef KERBEROS
845 1.1 cgd if (doencrypt)
846 1.1 cgd rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf));
847 1.1 cgd else
848 1.1 cgd #endif
849 1.1 cgd #endif
850 1.1 cgd rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
851 1.1 cgd if (rcvcnt == 0)
852 1.1 cgd return (0);
853 1.1 cgd if (rcvcnt < 0) {
854 1.1 cgd if (errno == EINTR)
855 1.1 cgd continue;
856 1.15 tls warn("read");
857 1.4 cgd return (-1);
858 1.1 cgd }
859 1.1 cgd }
860 1.1 cgd }
861 1.1 cgd
862 1.4 cgd void
863 1.1 cgd mode(f)
864 1.4 cgd int f;
865 1.1 cgd {
866 1.5 mycroft struct termios tty;
867 1.5 mycroft
868 1.5 mycroft switch (f) {
869 1.1 cgd case 0:
870 1.5 mycroft (void)tcsetattr(0, TCSANOW, &deftty);
871 1.1 cgd break;
872 1.1 cgd case 1:
873 1.5 mycroft (void)tcgetattr(0, &deftty);
874 1.5 mycroft tty = deftty;
875 1.7 mycroft /* This is loosely derived from sys/compat/tty_compat.c. */
876 1.7 mycroft tty.c_lflag &= ~(ECHO|ICANON|ISIG|IEXTEN);
877 1.5 mycroft tty.c_iflag &= ~ICRNL;
878 1.5 mycroft tty.c_oflag &= ~OPOST;
879 1.9 christos tty.c_cc[VMIN] = 1;
880 1.9 christos tty.c_cc[VTIME] = 0;
881 1.5 mycroft if (eight) {
882 1.5 mycroft tty.c_iflag &= IXOFF;
883 1.5 mycroft tty.c_cflag &= ~(CSIZE|PARENB);
884 1.5 mycroft tty.c_cflag |= CS8;
885 1.5 mycroft }
886 1.5 mycroft (void)tcsetattr(0, TCSANOW, &tty);
887 1.1 cgd break;
888 1.15 tls
889 1.1 cgd default:
890 1.1 cgd return;
891 1.1 cgd }
892 1.1 cgd }
893 1.1 cgd
894 1.1 cgd void
895 1.4 cgd lostpeer(signo)
896 1.4 cgd int signo;
897 1.1 cgd {
898 1.15 tls struct sigaction sa;
899 1.15 tls sa.sa_flags = SA_RESTART;
900 1.15 tls sa.sa_handler = SIG_IGN;
901 1.15 tls (void)sigaction(SIGPIPE, &sa, (struct sigaction *)0);
902 1.5 mycroft msg("\aconnection closed.");
903 1.1 cgd done(1);
904 1.1 cgd }
905 1.1 cgd
906 1.1 cgd /* copy SIGURGs to the child process. */
907 1.1 cgd void
908 1.4 cgd copytochild(signo)
909 1.4 cgd int signo;
910 1.1 cgd {
911 1.15 tls
912 1.1 cgd (void)kill(child, SIGURG);
913 1.1 cgd }
914 1.1 cgd
915 1.4 cgd void
916 1.1 cgd msg(str)
917 1.1 cgd char *str;
918 1.1 cgd {
919 1.15 tls
920 1.1 cgd (void)fprintf(stderr, "rlogin: %s\r\n", str);
921 1.1 cgd }
922 1.1 cgd
923 1.1 cgd #ifdef KERBEROS
924 1.1 cgd /* VARARGS */
925 1.4 cgd void
926 1.4 cgd #if __STDC__
927 1.4 cgd warning(const char *fmt, ...)
928 1.4 cgd #else
929 1.4 cgd warning(fmt, va_alist)
930 1.4 cgd char *fmt;
931 1.4 cgd va_dcl
932 1.4 cgd #endif
933 1.1 cgd {
934 1.1 cgd va_list ap;
935 1.1 cgd
936 1.1 cgd (void)fprintf(stderr, "rlogin: warning, using standard rlogin: ");
937 1.4 cgd #ifdef __STDC__
938 1.4 cgd va_start(ap, fmt);
939 1.4 cgd #else
940 1.1 cgd va_start(ap);
941 1.4 cgd #endif
942 1.1 cgd vfprintf(stderr, fmt, ap);
943 1.1 cgd va_end(ap);
944 1.1 cgd (void)fprintf(stderr, ".\n");
945 1.1 cgd }
946 1.1 cgd #endif
947 1.1 cgd
948 1.4 cgd __dead void
949 1.1 cgd usage()
950 1.1 cgd {
951 1.1 cgd (void)fprintf(stderr,
952 1.15 tls "usage: rlogin [ -%s]%s[-e char] [ -l username ] [username@]host\n",
953 1.1 cgd #ifdef KERBEROS
954 1.1 cgd #ifdef CRYPT
955 1.4 cgd "8EKLx", " [-k realm] ");
956 1.1 cgd #else
957 1.4 cgd "8EKL", " [-k realm] ");
958 1.1 cgd #endif
959 1.1 cgd #else
960 1.1 cgd "8EL", " ");
961 1.1 cgd #endif
962 1.1 cgd exit(1);
963 1.1 cgd }
964 1.1 cgd
965 1.1 cgd /*
966 1.4 cgd * The following routine provides compatibility (such as it is) between older
967 1.1 cgd * Suns and others. Suns have only a `ttysize', so we convert it to a winsize.
968 1.1 cgd */
969 1.4 cgd #ifdef OLDSUN
970 1.4 cgd int
971 1.1 cgd get_window_size(fd, wp)
972 1.1 cgd int fd;
973 1.1 cgd struct winsize *wp;
974 1.1 cgd {
975 1.1 cgd struct ttysize ts;
976 1.1 cgd int error;
977 1.1 cgd
978 1.1 cgd if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
979 1.4 cgd return (error);
980 1.1 cgd wp->ws_row = ts.ts_lines;
981 1.1 cgd wp->ws_col = ts.ts_cols;
982 1.1 cgd wp->ws_xpixel = 0;
983 1.1 cgd wp->ws_ypixel = 0;
984 1.4 cgd return (0);
985 1.1 cgd }
986 1.1 cgd #endif
987 1.1 cgd
988 1.4 cgd u_int
989 1.1 cgd getescape(p)
990 1.1 cgd register char *p;
991 1.1 cgd {
992 1.1 cgd long val;
993 1.1 cgd int len;
994 1.1 cgd
995 1.1 cgd if ((len = strlen(p)) == 1) /* use any single char, including '\' */
996 1.4 cgd return ((u_int)*p);
997 1.1 cgd /* otherwise, \nnn */
998 1.1 cgd if (*p == '\\' && len >= 2 && len <= 4) {
999 1.4 cgd val = strtol(++p, NULL, 8);
1000 1.1 cgd for (;;) {
1001 1.1 cgd if (!*++p)
1002 1.4 cgd return ((u_int)val);
1003 1.1 cgd if (*p < '0' || *p > '8')
1004 1.1 cgd break;
1005 1.1 cgd }
1006 1.1 cgd }
1007 1.1 cgd msg("illegal option value -- e");
1008 1.1 cgd usage();
1009 1.1 cgd /* NOTREACHED */
1010 1.1 cgd }
1011