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