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