rlogin.c revision 1.4 1 1.4 cgd /* $NetBSD: rlogin.c,v 1.4 1995/03/21 07:58:38 cgd 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.4 cgd static char sccsid[] = "@(#)rlogin.c 8.1 (Berkeley) 6/6/93";
45 1.4 cgd #else
46 1.4 cgd static char rcsid[] = "$NetBSD: rlogin.c,v 1.4 1995/03/21 07:58:38 cgd 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.1 cgd #include <sys/socket.h>
55 1.1 cgd #include <sys/time.h>
56 1.1 cgd #include <sys/resource.h>
57 1.1 cgd #include <sys/wait.h>
58 1.1 cgd
59 1.1 cgd #include <netinet/in.h>
60 1.1 cgd #include <netinet/in_systm.h>
61 1.1 cgd #include <netinet/ip.h>
62 1.1 cgd
63 1.1 cgd #include <errno.h>
64 1.4 cgd #include <fcntl.h>
65 1.4 cgd #include <netdb.h>
66 1.1 cgd #include <pwd.h>
67 1.4 cgd #include <setjmp.h>
68 1.4 cgd #include <sgtty.h>
69 1.4 cgd #include <signal.h>
70 1.1 cgd #include <stdio.h>
71 1.4 cgd #include <stdlib.h>
72 1.4 cgd #include <string.h>
73 1.1 cgd #include <unistd.h>
74 1.4 cgd
75 1.4 cgd #ifdef __STDC__
76 1.4 cgd #include <stdarg.h>
77 1.4 cgd #else
78 1.4 cgd #include <varargs.h>
79 1.4 cgd #endif
80 1.1 cgd
81 1.1 cgd #ifdef KERBEROS
82 1.1 cgd #include <kerberosIV/des.h>
83 1.1 cgd #include <kerberosIV/krb.h>
84 1.1 cgd
85 1.4 cgd #include "krb.h"
86 1.4 cgd
87 1.1 cgd CREDENTIALS cred;
88 1.1 cgd Key_schedule schedule;
89 1.1 cgd int use_kerberos = 1, doencrypt;
90 1.1 cgd char dst_realm_buf[REALM_SZ], *dest_realm = NULL;
91 1.1 cgd #endif
92 1.1 cgd
93 1.1 cgd #ifndef TIOCPKT_WINDOW
94 1.1 cgd #define TIOCPKT_WINDOW 0x80
95 1.1 cgd #endif
96 1.1 cgd
97 1.1 cgd /* concession to Sun */
98 1.1 cgd #ifndef SIGUSR1
99 1.1 cgd #define SIGUSR1 30
100 1.1 cgd #endif
101 1.1 cgd
102 1.1 cgd int eight, litout, rem;
103 1.1 cgd
104 1.1 cgd int noescape;
105 1.1 cgd u_char escapechar = '~';
106 1.1 cgd
107 1.1 cgd char *speeds[] = {
108 1.1 cgd "0", "50", "75", "110", "134", "150", "200", "300", "600", "1200",
109 1.3 deraadt "1800", "2400", "4800", "9600", "19200", "38400", "57600", "115200"
110 1.1 cgd };
111 1.1 cgd
112 1.4 cgd #ifdef OLDSUN
113 1.1 cgd struct winsize {
114 1.1 cgd unsigned short ws_row, ws_col;
115 1.1 cgd unsigned short ws_xpixel, ws_ypixel;
116 1.1 cgd };
117 1.4 cgd #else
118 1.4 cgd #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
119 1.1 cgd #endif
120 1.1 cgd struct winsize winsize;
121 1.1 cgd
122 1.4 cgd void catch_child __P((int));
123 1.4 cgd void copytochild __P((int));
124 1.4 cgd __dead void doit __P((long));
125 1.4 cgd __dead void done __P((int));
126 1.4 cgd void echo __P((char));
127 1.4 cgd u_int getescape __P((char *));
128 1.4 cgd void lostpeer __P((int));
129 1.4 cgd void mode __P((int));
130 1.4 cgd void msg __P((char *));
131 1.4 cgd void oob __P((int));
132 1.4 cgd int reader __P((int));
133 1.4 cgd void sendwindow __P((void));
134 1.4 cgd void setsignal __P((int));
135 1.4 cgd void sigwinch __P((int));
136 1.4 cgd void stop __P((char));
137 1.4 cgd __dead void usage __P((void));
138 1.4 cgd void writer __P((void));
139 1.4 cgd void writeroob __P((int));
140 1.4 cgd
141 1.4 cgd #ifdef KERBEROS
142 1.4 cgd void warning __P((const char *, ...));
143 1.4 cgd #endif
144 1.4 cgd #ifdef OLDSUN
145 1.4 cgd int get_window_size __P((int, struct winsize *));
146 1.1 cgd #endif
147 1.1 cgd
148 1.4 cgd int
149 1.1 cgd main(argc, argv)
150 1.1 cgd int argc;
151 1.4 cgd char *argv[];
152 1.1 cgd {
153 1.1 cgd extern char *optarg;
154 1.1 cgd extern int optind;
155 1.1 cgd struct passwd *pw;
156 1.1 cgd struct servent *sp;
157 1.1 cgd struct sgttyb ttyb;
158 1.1 cgd long omask;
159 1.1 cgd int argoff, ch, dflag, one, uid;
160 1.1 cgd char *host, *p, *user, term[1024];
161 1.1 cgd
162 1.1 cgd argoff = dflag = 0;
163 1.1 cgd one = 1;
164 1.1 cgd host = user = NULL;
165 1.1 cgd
166 1.1 cgd if (p = rindex(argv[0], '/'))
167 1.1 cgd ++p;
168 1.1 cgd else
169 1.1 cgd p = argv[0];
170 1.1 cgd
171 1.1 cgd if (strcmp(p, "rlogin"))
172 1.1 cgd host = p;
173 1.1 cgd
174 1.1 cgd /* handle "rlogin host flags" */
175 1.1 cgd if (!host && argc > 2 && argv[1][0] != '-') {
176 1.1 cgd host = argv[1];
177 1.1 cgd argoff = 1;
178 1.1 cgd }
179 1.1 cgd
180 1.1 cgd #ifdef KERBEROS
181 1.1 cgd #define OPTIONS "8EKLde:k:l:x"
182 1.1 cgd #else
183 1.1 cgd #define OPTIONS "8EKLde:l:"
184 1.1 cgd #endif
185 1.1 cgd while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF)
186 1.1 cgd switch(ch) {
187 1.1 cgd case '8':
188 1.1 cgd eight = 1;
189 1.1 cgd break;
190 1.1 cgd case 'E':
191 1.1 cgd noescape = 1;
192 1.1 cgd break;
193 1.1 cgd case 'K':
194 1.1 cgd #ifdef KERBEROS
195 1.1 cgd use_kerberos = 0;
196 1.1 cgd #endif
197 1.1 cgd break;
198 1.1 cgd case 'L':
199 1.1 cgd litout = 1;
200 1.1 cgd break;
201 1.1 cgd case 'd':
202 1.1 cgd dflag = 1;
203 1.1 cgd break;
204 1.1 cgd case 'e':
205 1.4 cgd noescape = 0;
206 1.1 cgd escapechar = getescape(optarg);
207 1.1 cgd break;
208 1.1 cgd #ifdef KERBEROS
209 1.1 cgd case 'k':
210 1.1 cgd dest_realm = dst_realm_buf;
211 1.1 cgd (void)strncpy(dest_realm, optarg, REALM_SZ);
212 1.1 cgd break;
213 1.1 cgd #endif
214 1.1 cgd case 'l':
215 1.1 cgd user = optarg;
216 1.1 cgd break;
217 1.1 cgd #ifdef CRYPT
218 1.1 cgd #ifdef KERBEROS
219 1.1 cgd case 'x':
220 1.1 cgd doencrypt = 1;
221 1.1 cgd des_set_key(cred.session, schedule);
222 1.1 cgd break;
223 1.1 cgd #endif
224 1.1 cgd #endif
225 1.1 cgd case '?':
226 1.1 cgd default:
227 1.1 cgd usage();
228 1.1 cgd }
229 1.1 cgd optind += argoff;
230 1.1 cgd argc -= optind;
231 1.1 cgd argv += optind;
232 1.1 cgd
233 1.1 cgd /* if haven't gotten a host yet, do so */
234 1.1 cgd if (!host && !(host = *argv++))
235 1.1 cgd usage();
236 1.1 cgd
237 1.1 cgd if (*argv)
238 1.1 cgd usage();
239 1.1 cgd
240 1.1 cgd if (!(pw = getpwuid(uid = getuid()))) {
241 1.1 cgd (void)fprintf(stderr, "rlogin: unknown user id.\n");
242 1.1 cgd exit(1);
243 1.1 cgd }
244 1.1 cgd if (!user)
245 1.1 cgd user = pw->pw_name;
246 1.1 cgd
247 1.1 cgd sp = NULL;
248 1.1 cgd #ifdef KERBEROS
249 1.1 cgd if (use_kerberos) {
250 1.1 cgd sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
251 1.1 cgd if (sp == NULL) {
252 1.1 cgd use_kerberos = 0;
253 1.1 cgd warning("can't get entry for %s/tcp service",
254 1.1 cgd doencrypt ? "eklogin" : "klogin");
255 1.1 cgd }
256 1.1 cgd }
257 1.1 cgd #endif
258 1.1 cgd if (sp == NULL)
259 1.1 cgd sp = getservbyname("login", "tcp");
260 1.1 cgd if (sp == NULL) {
261 1.1 cgd (void)fprintf(stderr, "rlogin: login/tcp: unknown service.\n");
262 1.1 cgd exit(1);
263 1.1 cgd }
264 1.1 cgd
265 1.1 cgd (void)strcpy(term, (p = getenv("TERM")) ? p : "network");
266 1.1 cgd if (ioctl(0, TIOCGETP, &ttyb) == 0) {
267 1.1 cgd (void)strcat(term, "/");
268 1.4 cgd (void)strcat(term, speeds[(int)ttyb.sg_ospeed]);
269 1.1 cgd }
270 1.1 cgd
271 1.1 cgd (void)get_window_size(0, &winsize);
272 1.1 cgd
273 1.1 cgd (void)signal(SIGPIPE, lostpeer);
274 1.1 cgd /* will use SIGUSR1 for window size hack, so hold it off */
275 1.1 cgd omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
276 1.4 cgd /*
277 1.4 cgd * We set SIGURG and SIGUSR1 below so that an
278 1.4 cgd * incoming signal will be held pending rather than being
279 1.4 cgd * discarded. Note that these routines will be ready to get
280 1.4 cgd * a signal by the time that they are unblocked below.
281 1.4 cgd */
282 1.4 cgd (void)signal(SIGURG, copytochild);
283 1.4 cgd (void)signal(SIGUSR1, writeroob);
284 1.1 cgd
285 1.1 cgd #ifdef KERBEROS
286 1.1 cgd try_connect:
287 1.1 cgd if (use_kerberos) {
288 1.4 cgd struct hostent *hp;
289 1.4 cgd
290 1.4 cgd /* Fully qualify hostname (needed for krb_realmofhost). */
291 1.4 cgd hp = gethostbyname(host);
292 1.4 cgd if (hp != NULL && !(host = strdup(hp->h_name))) {
293 1.4 cgd (void)fprintf(stderr, "rlogin: %s\n",
294 1.4 cgd strerror(ENOMEM));
295 1.4 cgd exit(1);
296 1.4 cgd }
297 1.4 cgd
298 1.1 cgd rem = KSUCCESS;
299 1.1 cgd errno = 0;
300 1.1 cgd if (dest_realm == NULL)
301 1.1 cgd dest_realm = krb_realmofhost(host);
302 1.1 cgd
303 1.1 cgd #ifdef CRYPT
304 1.1 cgd if (doencrypt)
305 1.1 cgd rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
306 1.1 cgd dest_realm, &cred, schedule);
307 1.1 cgd else
308 1.1 cgd #endif /* CRYPT */
309 1.1 cgd rem = krcmd(&host, sp->s_port, user, term, 0,
310 1.1 cgd dest_realm);
311 1.1 cgd if (rem < 0) {
312 1.1 cgd use_kerberos = 0;
313 1.1 cgd sp = getservbyname("login", "tcp");
314 1.1 cgd if (sp == NULL) {
315 1.1 cgd (void)fprintf(stderr,
316 1.1 cgd "rlogin: unknown service login/tcp.\n");
317 1.1 cgd exit(1);
318 1.1 cgd }
319 1.1 cgd if (errno == ECONNREFUSED)
320 1.1 cgd warning("remote host doesn't support Kerberos");
321 1.1 cgd if (errno == ENOENT)
322 1.1 cgd warning("can't provide Kerberos auth data");
323 1.1 cgd goto try_connect;
324 1.1 cgd }
325 1.1 cgd } else {
326 1.1 cgd #ifdef CRYPT
327 1.1 cgd if (doencrypt) {
328 1.1 cgd (void)fprintf(stderr,
329 1.1 cgd "rlogin: the -x flag requires Kerberos authentication.\n");
330 1.1 cgd exit(1);
331 1.1 cgd }
332 1.1 cgd #endif /* CRYPT */
333 1.1 cgd rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
334 1.1 cgd }
335 1.1 cgd #else
336 1.1 cgd rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
337 1.1 cgd #endif /* KERBEROS */
338 1.1 cgd
339 1.1 cgd if (rem < 0)
340 1.1 cgd exit(1);
341 1.1 cgd
342 1.1 cgd if (dflag &&
343 1.1 cgd setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
344 1.1 cgd (void)fprintf(stderr, "rlogin: setsockopt: %s.\n",
345 1.1 cgd strerror(errno));
346 1.1 cgd one = IPTOS_LOWDELAY;
347 1.1 cgd if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0)
348 1.1 cgd perror("rlogin: setsockopt TOS (ignored)");
349 1.1 cgd
350 1.1 cgd (void)setuid(uid);
351 1.1 cgd doit(omask);
352 1.1 cgd /*NOTREACHED*/
353 1.1 cgd }
354 1.1 cgd
355 1.1 cgd int child, defflags, deflflags, tabflag;
356 1.1 cgd char deferase, defkill;
357 1.1 cgd struct tchars deftc;
358 1.1 cgd struct ltchars defltc;
359 1.1 cgd struct tchars notc = { -1, -1, -1, -1, -1, -1 };
360 1.1 cgd struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
361 1.1 cgd
362 1.4 cgd void
363 1.1 cgd doit(omask)
364 1.1 cgd long omask;
365 1.1 cgd {
366 1.1 cgd struct sgttyb sb;
367 1.1 cgd
368 1.1 cgd (void)ioctl(0, TIOCGETP, (char *)&sb);
369 1.1 cgd defflags = sb.sg_flags;
370 1.1 cgd tabflag = defflags & TBDELAY;
371 1.1 cgd defflags &= ECHO | CRMOD;
372 1.1 cgd deferase = sb.sg_erase;
373 1.1 cgd defkill = sb.sg_kill;
374 1.4 cgd (void)ioctl(0, TIOCLGET, &deflflags);
375 1.4 cgd (void)ioctl(0, TIOCGETC, &deftc);
376 1.1 cgd notc.t_startc = deftc.t_startc;
377 1.1 cgd notc.t_stopc = deftc.t_stopc;
378 1.4 cgd (void)ioctl(0, TIOCGLTC, &defltc);
379 1.1 cgd (void)signal(SIGINT, SIG_IGN);
380 1.4 cgd setsignal(SIGHUP);
381 1.4 cgd setsignal(SIGQUIT);
382 1.1 cgd child = fork();
383 1.1 cgd if (child == -1) {
384 1.1 cgd (void)fprintf(stderr, "rlogin: fork: %s.\n", strerror(errno));
385 1.1 cgd done(1);
386 1.1 cgd }
387 1.1 cgd if (child == 0) {
388 1.1 cgd mode(1);
389 1.1 cgd if (reader(omask) == 0) {
390 1.1 cgd msg("connection closed.");
391 1.1 cgd exit(0);
392 1.1 cgd }
393 1.1 cgd sleep(1);
394 1.1 cgd msg("\007connection closed.");
395 1.1 cgd exit(1);
396 1.1 cgd }
397 1.1 cgd
398 1.1 cgd /*
399 1.1 cgd * We may still own the socket, and may have a pending SIGURG (or might
400 1.4 cgd * receive one soon) that we really want to send to the reader. When
401 1.4 cgd * one of these comes in, the trap copytochild simply copies such
402 1.4 cgd * signals to the child. We can now unblock SIGURG and SIGUSR1
403 1.4 cgd * that were set above.
404 1.1 cgd */
405 1.1 cgd (void)sigsetmask(omask);
406 1.1 cgd (void)signal(SIGCHLD, catch_child);
407 1.1 cgd writer();
408 1.1 cgd msg("closed connection.");
409 1.1 cgd done(0);
410 1.1 cgd }
411 1.1 cgd
412 1.1 cgd /* trap a signal, unless it is being ignored. */
413 1.4 cgd void
414 1.4 cgd setsignal(sig)
415 1.1 cgd int sig;
416 1.1 cgd {
417 1.1 cgd int omask = sigblock(sigmask(sig));
418 1.1 cgd
419 1.4 cgd if (signal(sig, exit) == SIG_IGN)
420 1.1 cgd (void)signal(sig, SIG_IGN);
421 1.1 cgd (void)sigsetmask(omask);
422 1.1 cgd }
423 1.1 cgd
424 1.4 cgd __dead void
425 1.1 cgd done(status)
426 1.1 cgd int status;
427 1.1 cgd {
428 1.1 cgd int w, wstatus;
429 1.1 cgd
430 1.1 cgd mode(0);
431 1.1 cgd if (child > 0) {
432 1.1 cgd /* make sure catch_child does not snap it up */
433 1.1 cgd (void)signal(SIGCHLD, SIG_DFL);
434 1.1 cgd if (kill(child, SIGKILL) >= 0)
435 1.1 cgd while ((w = wait(&wstatus)) > 0 && w != child);
436 1.1 cgd }
437 1.1 cgd exit(status);
438 1.1 cgd }
439 1.1 cgd
440 1.1 cgd int dosigwinch;
441 1.1 cgd
442 1.1 cgd /*
443 1.1 cgd * This is called when the reader process gets the out-of-band (urgent)
444 1.1 cgd * request to turn on the window-changing protocol.
445 1.1 cgd */
446 1.1 cgd void
447 1.4 cgd writeroob(signo)
448 1.4 cgd int signo;
449 1.1 cgd {
450 1.1 cgd if (dosigwinch == 0) {
451 1.1 cgd sendwindow();
452 1.1 cgd (void)signal(SIGWINCH, sigwinch);
453 1.1 cgd }
454 1.1 cgd dosigwinch = 1;
455 1.1 cgd }
456 1.1 cgd
457 1.1 cgd void
458 1.4 cgd catch_child(signo)
459 1.4 cgd int signo;
460 1.1 cgd {
461 1.1 cgd union wait status;
462 1.1 cgd int pid;
463 1.1 cgd
464 1.1 cgd for (;;) {
465 1.4 cgd pid = wait3((int *)&status, WNOHANG|WUNTRACED, NULL);
466 1.1 cgd if (pid == 0)
467 1.1 cgd return;
468 1.1 cgd /* if the child (reader) dies, just quit */
469 1.4 cgd if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
470 1.1 cgd done((int)(status.w_termsig | status.w_retcode));
471 1.1 cgd }
472 1.1 cgd /* NOTREACHED */
473 1.1 cgd }
474 1.1 cgd
475 1.1 cgd /*
476 1.1 cgd * writer: write to remote: 0 -> line.
477 1.1 cgd * ~. terminate
478 1.1 cgd * ~^Z suspend rlogin process.
479 1.1 cgd * ~<delayed-suspend char> suspend rlogin process, but leave reader alone.
480 1.1 cgd */
481 1.4 cgd void
482 1.1 cgd writer()
483 1.1 cgd {
484 1.1 cgd register int bol, local, n;
485 1.1 cgd char c;
486 1.1 cgd
487 1.1 cgd bol = 1; /* beginning of line */
488 1.1 cgd local = 0;
489 1.1 cgd for (;;) {
490 1.1 cgd n = read(STDIN_FILENO, &c, 1);
491 1.1 cgd if (n <= 0) {
492 1.1 cgd if (n < 0 && errno == EINTR)
493 1.1 cgd continue;
494 1.1 cgd break;
495 1.1 cgd }
496 1.1 cgd /*
497 1.1 cgd * If we're at the beginning of the line and recognize a
498 1.1 cgd * command character, then we echo locally. Otherwise,
499 1.1 cgd * characters are echo'd remotely. If the command character
500 1.1 cgd * is doubled, this acts as a force and local echo is
501 1.1 cgd * suppressed.
502 1.1 cgd */
503 1.1 cgd if (bol) {
504 1.1 cgd bol = 0;
505 1.1 cgd if (!noescape && c == escapechar) {
506 1.1 cgd local = 1;
507 1.1 cgd continue;
508 1.1 cgd }
509 1.1 cgd } else if (local) {
510 1.1 cgd local = 0;
511 1.1 cgd if (c == '.' || c == deftc.t_eofc) {
512 1.1 cgd echo(c);
513 1.1 cgd break;
514 1.1 cgd }
515 1.1 cgd if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
516 1.1 cgd bol = 1;
517 1.1 cgd echo(c);
518 1.1 cgd stop(c);
519 1.1 cgd continue;
520 1.1 cgd }
521 1.1 cgd if (c != escapechar)
522 1.1 cgd #ifdef CRYPT
523 1.1 cgd #ifdef KERBEROS
524 1.1 cgd if (doencrypt)
525 1.4 cgd (void)des_write(rem,
526 1.4 cgd (char *)&escapechar, 1);
527 1.1 cgd else
528 1.1 cgd #endif
529 1.1 cgd #endif
530 1.1 cgd (void)write(rem, &escapechar, 1);
531 1.1 cgd }
532 1.1 cgd
533 1.1 cgd #ifdef CRYPT
534 1.1 cgd #ifdef KERBEROS
535 1.1 cgd if (doencrypt) {
536 1.1 cgd if (des_write(rem, &c, 1) == 0) {
537 1.1 cgd msg("line gone");
538 1.1 cgd break;
539 1.1 cgd }
540 1.1 cgd } else
541 1.1 cgd #endif
542 1.1 cgd #endif
543 1.1 cgd if (write(rem, &c, 1) == 0) {
544 1.1 cgd msg("line gone");
545 1.1 cgd break;
546 1.1 cgd }
547 1.1 cgd bol = c == defkill || c == deftc.t_eofc ||
548 1.1 cgd c == deftc.t_intrc || c == defltc.t_suspc ||
549 1.1 cgd c == '\r' || c == '\n';
550 1.1 cgd }
551 1.1 cgd }
552 1.1 cgd
553 1.4 cgd void
554 1.4 cgd #if __STDC__
555 1.4 cgd echo(register char c)
556 1.4 cgd #else
557 1.1 cgd echo(c)
558 1.4 cgd register char c;
559 1.4 cgd #endif
560 1.1 cgd {
561 1.1 cgd register char *p;
562 1.1 cgd char buf[8];
563 1.1 cgd
564 1.1 cgd p = buf;
565 1.1 cgd c &= 0177;
566 1.1 cgd *p++ = escapechar;
567 1.1 cgd if (c < ' ') {
568 1.1 cgd *p++ = '^';
569 1.1 cgd *p++ = c + '@';
570 1.1 cgd } else if (c == 0177) {
571 1.1 cgd *p++ = '^';
572 1.1 cgd *p++ = '?';
573 1.1 cgd } else
574 1.1 cgd *p++ = c;
575 1.1 cgd *p++ = '\r';
576 1.1 cgd *p++ = '\n';
577 1.1 cgd (void)write(STDOUT_FILENO, buf, p - buf);
578 1.1 cgd }
579 1.1 cgd
580 1.4 cgd void
581 1.4 cgd #if __STDC__
582 1.4 cgd stop(char cmdc)
583 1.4 cgd #else
584 1.1 cgd stop(cmdc)
585 1.1 cgd char cmdc;
586 1.4 cgd #endif
587 1.1 cgd {
588 1.1 cgd mode(0);
589 1.1 cgd (void)signal(SIGCHLD, SIG_IGN);
590 1.1 cgd (void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
591 1.1 cgd (void)signal(SIGCHLD, catch_child);
592 1.1 cgd mode(1);
593 1.4 cgd sigwinch(0); /* check for size changes */
594 1.1 cgd }
595 1.1 cgd
596 1.1 cgd void
597 1.4 cgd sigwinch(signo)
598 1.4 cgd int signo;
599 1.1 cgd {
600 1.1 cgd struct winsize ws;
601 1.1 cgd
602 1.1 cgd if (dosigwinch && get_window_size(0, &ws) == 0 &&
603 1.1 cgd bcmp(&ws, &winsize, sizeof(ws))) {
604 1.1 cgd winsize = ws;
605 1.1 cgd sendwindow();
606 1.1 cgd }
607 1.1 cgd }
608 1.1 cgd
609 1.1 cgd /*
610 1.1 cgd * Send the window size to the server via the magic escape
611 1.1 cgd */
612 1.4 cgd void
613 1.1 cgd sendwindow()
614 1.1 cgd {
615 1.1 cgd struct winsize *wp;
616 1.1 cgd char obuf[4 + sizeof (struct winsize)];
617 1.1 cgd
618 1.1 cgd wp = (struct winsize *)(obuf+4);
619 1.1 cgd obuf[0] = 0377;
620 1.1 cgd obuf[1] = 0377;
621 1.1 cgd obuf[2] = 's';
622 1.1 cgd obuf[3] = 's';
623 1.1 cgd wp->ws_row = htons(winsize.ws_row);
624 1.1 cgd wp->ws_col = htons(winsize.ws_col);
625 1.1 cgd wp->ws_xpixel = htons(winsize.ws_xpixel);
626 1.1 cgd wp->ws_ypixel = htons(winsize.ws_ypixel);
627 1.1 cgd
628 1.1 cgd #ifdef CRYPT
629 1.1 cgd #ifdef KERBEROS
630 1.1 cgd if(doencrypt)
631 1.1 cgd (void)des_write(rem, obuf, sizeof(obuf));
632 1.1 cgd else
633 1.1 cgd #endif
634 1.1 cgd #endif
635 1.1 cgd (void)write(rem, obuf, sizeof(obuf));
636 1.1 cgd }
637 1.1 cgd
638 1.1 cgd /*
639 1.1 cgd * reader: read from remote: line -> 1
640 1.1 cgd */
641 1.1 cgd #define READING 1
642 1.1 cgd #define WRITING 2
643 1.1 cgd
644 1.1 cgd jmp_buf rcvtop;
645 1.1 cgd int ppid, rcvcnt, rcvstate;
646 1.1 cgd char rcvbuf[8 * 1024];
647 1.1 cgd
648 1.1 cgd void
649 1.4 cgd oob(signo)
650 1.4 cgd int signo;
651 1.1 cgd {
652 1.1 cgd struct sgttyb sb;
653 1.1 cgd int atmark, n, out, rcvd;
654 1.1 cgd char waste[BUFSIZ], mark;
655 1.1 cgd
656 1.1 cgd out = O_RDWR;
657 1.1 cgd rcvd = 0;
658 1.4 cgd while (recv(rem, &mark, 1, MSG_OOB) < 0) {
659 1.1 cgd switch (errno) {
660 1.1 cgd case EWOULDBLOCK:
661 1.1 cgd /*
662 1.1 cgd * Urgent data not here yet. It may not be possible
663 1.1 cgd * to send it yet if we are blocked for output and
664 1.1 cgd * our input buffer is full.
665 1.1 cgd */
666 1.1 cgd if (rcvcnt < sizeof(rcvbuf)) {
667 1.1 cgd n = read(rem, rcvbuf + rcvcnt,
668 1.1 cgd sizeof(rcvbuf) - rcvcnt);
669 1.1 cgd if (n <= 0)
670 1.1 cgd return;
671 1.1 cgd rcvd += n;
672 1.1 cgd } else {
673 1.1 cgd n = read(rem, waste, sizeof(waste));
674 1.1 cgd if (n <= 0)
675 1.1 cgd return;
676 1.1 cgd }
677 1.1 cgd continue;
678 1.1 cgd default:
679 1.1 cgd return;
680 1.4 cgd }
681 1.1 cgd }
682 1.1 cgd if (mark & TIOCPKT_WINDOW) {
683 1.1 cgd /* Let server know about window size changes */
684 1.1 cgd (void)kill(ppid, SIGUSR1);
685 1.1 cgd }
686 1.1 cgd if (!eight && (mark & TIOCPKT_NOSTOP)) {
687 1.1 cgd (void)ioctl(0, TIOCGETP, (char *)&sb);
688 1.1 cgd sb.sg_flags &= ~CBREAK;
689 1.1 cgd sb.sg_flags |= RAW;
690 1.1 cgd (void)ioctl(0, TIOCSETN, (char *)&sb);
691 1.1 cgd notc.t_stopc = -1;
692 1.1 cgd notc.t_startc = -1;
693 1.1 cgd (void)ioctl(0, TIOCSETC, (char *)¬c);
694 1.1 cgd }
695 1.1 cgd if (!eight && (mark & TIOCPKT_DOSTOP)) {
696 1.1 cgd (void)ioctl(0, TIOCGETP, (char *)&sb);
697 1.1 cgd sb.sg_flags &= ~RAW;
698 1.1 cgd sb.sg_flags |= CBREAK;
699 1.1 cgd (void)ioctl(0, TIOCSETN, (char *)&sb);
700 1.1 cgd notc.t_stopc = deftc.t_stopc;
701 1.1 cgd notc.t_startc = deftc.t_startc;
702 1.1 cgd (void)ioctl(0, TIOCSETC, (char *)¬c);
703 1.1 cgd }
704 1.1 cgd if (mark & TIOCPKT_FLUSHWRITE) {
705 1.1 cgd (void)ioctl(1, TIOCFLUSH, (char *)&out);
706 1.1 cgd for (;;) {
707 1.1 cgd if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
708 1.1 cgd (void)fprintf(stderr, "rlogin: ioctl: %s.\n",
709 1.1 cgd strerror(errno));
710 1.1 cgd break;
711 1.1 cgd }
712 1.1 cgd if (atmark)
713 1.1 cgd break;
714 1.1 cgd n = read(rem, waste, sizeof (waste));
715 1.1 cgd if (n <= 0)
716 1.1 cgd break;
717 1.1 cgd }
718 1.1 cgd /*
719 1.1 cgd * Don't want any pending data to be output, so clear the recv
720 1.1 cgd * buffer. If we were hanging on a write when interrupted,
721 1.1 cgd * don't want it to restart. If we were reading, restart
722 1.1 cgd * anyway.
723 1.1 cgd */
724 1.1 cgd rcvcnt = 0;
725 1.1 cgd longjmp(rcvtop, 1);
726 1.1 cgd }
727 1.1 cgd
728 1.1 cgd /* oob does not do FLUSHREAD (alas!) */
729 1.1 cgd
730 1.1 cgd /*
731 1.1 cgd * If we filled the receive buffer while a read was pending, longjmp
732 1.1 cgd * to the top to restart appropriately. Don't abort a pending write,
733 1.1 cgd * however, or we won't know how much was written.
734 1.1 cgd */
735 1.1 cgd if (rcvd && rcvstate == READING)
736 1.1 cgd longjmp(rcvtop, 1);
737 1.1 cgd }
738 1.1 cgd
739 1.1 cgd /* reader: read from remote: line -> 1 */
740 1.4 cgd int
741 1.1 cgd reader(omask)
742 1.1 cgd int omask;
743 1.1 cgd {
744 1.4 cgd int pid, n, remaining;
745 1.4 cgd char *bufp;
746 1.1 cgd
747 1.4 cgd #if BSD >= 43 || defined(SUNOS4)
748 1.4 cgd pid = getpid(); /* modern systems use positives for pid */
749 1.1 cgd #else
750 1.4 cgd pid = -getpid(); /* old broken systems use negatives */
751 1.1 cgd #endif
752 1.1 cgd (void)signal(SIGTTOU, SIG_IGN);
753 1.1 cgd (void)signal(SIGURG, oob);
754 1.1 cgd ppid = getppid();
755 1.1 cgd (void)fcntl(rem, F_SETOWN, pid);
756 1.1 cgd (void)setjmp(rcvtop);
757 1.1 cgd (void)sigsetmask(omask);
758 1.4 cgd bufp = rcvbuf;
759 1.1 cgd for (;;) {
760 1.1 cgd while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
761 1.1 cgd rcvstate = WRITING;
762 1.1 cgd n = write(STDOUT_FILENO, bufp, remaining);
763 1.1 cgd if (n < 0) {
764 1.1 cgd if (errno != EINTR)
765 1.4 cgd return (-1);
766 1.1 cgd continue;
767 1.1 cgd }
768 1.1 cgd bufp += n;
769 1.1 cgd }
770 1.1 cgd bufp = rcvbuf;
771 1.1 cgd rcvcnt = 0;
772 1.1 cgd rcvstate = READING;
773 1.1 cgd
774 1.1 cgd #ifdef CRYPT
775 1.1 cgd #ifdef KERBEROS
776 1.1 cgd if (doencrypt)
777 1.1 cgd rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf));
778 1.1 cgd else
779 1.1 cgd #endif
780 1.1 cgd #endif
781 1.1 cgd rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
782 1.1 cgd if (rcvcnt == 0)
783 1.1 cgd return (0);
784 1.1 cgd if (rcvcnt < 0) {
785 1.1 cgd if (errno == EINTR)
786 1.1 cgd continue;
787 1.1 cgd (void)fprintf(stderr, "rlogin: read: %s.\n",
788 1.1 cgd strerror(errno));
789 1.4 cgd return (-1);
790 1.1 cgd }
791 1.1 cgd }
792 1.1 cgd }
793 1.1 cgd
794 1.4 cgd void
795 1.1 cgd mode(f)
796 1.4 cgd int f;
797 1.1 cgd {
798 1.1 cgd struct ltchars *ltc;
799 1.1 cgd struct sgttyb sb;
800 1.1 cgd struct tchars *tc;
801 1.1 cgd int lflags;
802 1.1 cgd
803 1.1 cgd (void)ioctl(0, TIOCGETP, (char *)&sb);
804 1.1 cgd (void)ioctl(0, TIOCLGET, (char *)&lflags);
805 1.1 cgd switch(f) {
806 1.1 cgd case 0:
807 1.1 cgd sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
808 1.1 cgd sb.sg_flags |= defflags|tabflag;
809 1.1 cgd tc = &deftc;
810 1.1 cgd ltc = &defltc;
811 1.1 cgd sb.sg_kill = defkill;
812 1.1 cgd sb.sg_erase = deferase;
813 1.1 cgd lflags = deflflags;
814 1.1 cgd break;
815 1.1 cgd case 1:
816 1.1 cgd sb.sg_flags |= (eight ? RAW : CBREAK);
817 1.1 cgd sb.sg_flags &= ~defflags;
818 1.1 cgd /* preserve tab delays, but turn off XTABS */
819 1.1 cgd if ((sb.sg_flags & TBDELAY) == XTABS)
820 1.1 cgd sb.sg_flags &= ~TBDELAY;
821 1.1 cgd tc = ¬c;
822 1.1 cgd ltc = &noltc;
823 1.1 cgd sb.sg_kill = sb.sg_erase = -1;
824 1.1 cgd if (litout)
825 1.1 cgd lflags |= LLITOUT;
826 1.1 cgd break;
827 1.1 cgd default:
828 1.1 cgd return;
829 1.1 cgd }
830 1.1 cgd (void)ioctl(0, TIOCSLTC, (char *)ltc);
831 1.1 cgd (void)ioctl(0, TIOCSETC, (char *)tc);
832 1.1 cgd (void)ioctl(0, TIOCSETN, (char *)&sb);
833 1.1 cgd (void)ioctl(0, TIOCLSET, (char *)&lflags);
834 1.1 cgd }
835 1.1 cgd
836 1.1 cgd void
837 1.4 cgd lostpeer(signo)
838 1.4 cgd int signo;
839 1.1 cgd {
840 1.1 cgd (void)signal(SIGPIPE, SIG_IGN);
841 1.1 cgd msg("\007connection closed.");
842 1.1 cgd done(1);
843 1.1 cgd }
844 1.1 cgd
845 1.1 cgd /* copy SIGURGs to the child process. */
846 1.1 cgd void
847 1.4 cgd copytochild(signo)
848 1.4 cgd int signo;
849 1.1 cgd {
850 1.1 cgd (void)kill(child, SIGURG);
851 1.1 cgd }
852 1.1 cgd
853 1.4 cgd void
854 1.1 cgd msg(str)
855 1.1 cgd char *str;
856 1.1 cgd {
857 1.1 cgd (void)fprintf(stderr, "rlogin: %s\r\n", str);
858 1.1 cgd }
859 1.1 cgd
860 1.1 cgd #ifdef KERBEROS
861 1.1 cgd /* VARARGS */
862 1.4 cgd void
863 1.4 cgd #if __STDC__
864 1.4 cgd warning(const char *fmt, ...)
865 1.4 cgd #else
866 1.4 cgd warning(fmt, va_alist)
867 1.4 cgd char *fmt;
868 1.4 cgd va_dcl
869 1.4 cgd #endif
870 1.1 cgd {
871 1.1 cgd va_list ap;
872 1.1 cgd
873 1.1 cgd (void)fprintf(stderr, "rlogin: warning, using standard rlogin: ");
874 1.4 cgd #ifdef __STDC__
875 1.4 cgd va_start(ap, fmt);
876 1.4 cgd #else
877 1.1 cgd va_start(ap);
878 1.4 cgd #endif
879 1.1 cgd vfprintf(stderr, fmt, ap);
880 1.1 cgd va_end(ap);
881 1.1 cgd (void)fprintf(stderr, ".\n");
882 1.1 cgd }
883 1.1 cgd #endif
884 1.1 cgd
885 1.4 cgd __dead void
886 1.1 cgd usage()
887 1.1 cgd {
888 1.1 cgd (void)fprintf(stderr,
889 1.1 cgd "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n",
890 1.1 cgd #ifdef KERBEROS
891 1.1 cgd #ifdef CRYPT
892 1.4 cgd "8EKLx", " [-k realm] ");
893 1.1 cgd #else
894 1.4 cgd "8EKL", " [-k realm] ");
895 1.1 cgd #endif
896 1.1 cgd #else
897 1.1 cgd "8EL", " ");
898 1.1 cgd #endif
899 1.1 cgd exit(1);
900 1.1 cgd }
901 1.1 cgd
902 1.1 cgd /*
903 1.4 cgd * The following routine provides compatibility (such as it is) between older
904 1.1 cgd * Suns and others. Suns have only a `ttysize', so we convert it to a winsize.
905 1.1 cgd */
906 1.4 cgd #ifdef OLDSUN
907 1.4 cgd int
908 1.1 cgd get_window_size(fd, wp)
909 1.1 cgd int fd;
910 1.1 cgd struct winsize *wp;
911 1.1 cgd {
912 1.1 cgd struct ttysize ts;
913 1.1 cgd int error;
914 1.1 cgd
915 1.1 cgd if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
916 1.4 cgd return (error);
917 1.1 cgd wp->ws_row = ts.ts_lines;
918 1.1 cgd wp->ws_col = ts.ts_cols;
919 1.1 cgd wp->ws_xpixel = 0;
920 1.1 cgd wp->ws_ypixel = 0;
921 1.4 cgd return (0);
922 1.1 cgd }
923 1.1 cgd #endif
924 1.1 cgd
925 1.4 cgd u_int
926 1.1 cgd getescape(p)
927 1.1 cgd register char *p;
928 1.1 cgd {
929 1.1 cgd long val;
930 1.1 cgd int len;
931 1.1 cgd
932 1.1 cgd if ((len = strlen(p)) == 1) /* use any single char, including '\' */
933 1.4 cgd return ((u_int)*p);
934 1.1 cgd /* otherwise, \nnn */
935 1.1 cgd if (*p == '\\' && len >= 2 && len <= 4) {
936 1.4 cgd val = strtol(++p, NULL, 8);
937 1.1 cgd for (;;) {
938 1.1 cgd if (!*++p)
939 1.4 cgd return ((u_int)val);
940 1.1 cgd if (*p < '0' || *p > '8')
941 1.1 cgd break;
942 1.1 cgd }
943 1.1 cgd }
944 1.1 cgd msg("illegal option value -- e");
945 1.1 cgd usage();
946 1.1 cgd /* NOTREACHED */
947 1.1 cgd }
948