restore sigint correctly, print 0 seconds if time == 0.
[unix-history] / usr / src / libexec / telnetd / telnetd.c
CommitLineData
ac6e6727 1#ifndef lint
bcb894cb 2static char sccsid[] = "@(#)telnetd.c 4.28 (Berkeley) %G%";
ac6e6727
BJ
3#endif
4
66b878f6
BJ
5/*
6 * Stripped-down telnet server.
7 */
de3b21e8
SL
8#include <sys/types.h>
9#include <sys/socket.h>
ce4fd43b 10#include <sys/wait.h>
de3b21e8
SL
11
12#include <netinet/in.h>
13
2b597a6b
SL
14#include <arpa/telnet.h>
15
66b878f6
BJ
16#include <stdio.h>
17#include <signal.h>
18#include <errno.h>
19#include <sgtty.h>
9f005877 20#include <netdb.h>
de3b21e8 21
4898c5bf
SL
22#define BELL '\07'
23#define BANNER "\r\n\r\n4.2 BSD UNIX (%s)\r\n\r\r\n\r%s"
66b878f6
BJ
24
25char hisopts[256];
26char myopts[256];
27
28char doopt[] = { IAC, DO, '%', 'c', 0 };
29char dont[] = { IAC, DONT, '%', 'c', 0 };
30char will[] = { IAC, WILL, '%', 'c', 0 };
31char wont[] = { IAC, WONT, '%', 'c', 0 };
32
33/*
34 * I/O data buffers, pointers, and counters.
35 */
36char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
37char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
38char netibuf[BUFSIZ], *netip = netibuf;
9ef3087d 39char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
66b878f6
BJ
40int pcc, ncc;
41
42int pty, net;
43int inter;
fe5c5547 44extern char **environ;
66b878f6
BJ
45extern int errno;
46char line[] = "/dev/ptyp0";
47
66b878f6
BJ
48main(argc, argv)
49 char *argv[];
50{
bb933cc2 51 struct sockaddr_in from;
bcb894cb 52 int on = 1, fromlen;
bb933cc2
MK
53
54 fromlen = sizeof (from);
55 if (getpeername(0, &from, &fromlen) < 0) {
56 fprintf(stderr, "%s: ", argv[0]);
57 perror("getpeername");
58 _exit(1);
de3b21e8 59 }
bcb894cb 60 if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
bb933cc2
MK
61 fprintf(stderr, "%s: ", argv[0]);
62 perror("setsockopt (SO_KEEPALIVE)");
de3b21e8 63 }
bb933cc2 64 doit(0, &from);
f553aca8
SL
65}
66
fe5c5547 67char *envinit[] = { "TERM=network", 0 };
66b878f6
BJ
68int cleanup();
69
70/*
71 * Get a pty, scan input lines.
72 */
37c640e2
SL
73doit(f, who)
74 int f;
75 struct sockaddr_in *who;
66b878f6 76{
37c640e2 77 char *cp = line, *host, *ntoa();
66b878f6
BJ
78 int i, p, cc, t;
79 struct sgttyb b;
37c640e2 80 struct hostent *hp;
66b878f6
BJ
81
82 for (i = 0; i < 16; i++) {
83 cp[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
84 p = open(cp, 2);
85 if (p > 0)
86 goto gotpty;
87 }
8f2758db
SL
88 fatal(f, "All network ports in use");
89 /*NOTREACHED*/
66b878f6
BJ
90gotpty:
91 dup2(f, 0);
92 cp[strlen("/dev/")] = 't';
93 t = open("/dev/tty", 2);
94 if (t >= 0) {
95 ioctl(t, TIOCNOTTY, 0);
96 close(t);
97 }
98 t = open(cp, 2);
8f2758db
SL
99 if (t < 0)
100 fatalperror(f, cp, errno);
66b878f6 101 ioctl(t, TIOCGETP, &b);
9ef3087d 102 b.sg_flags = CRMOD|XTABS|ANYP;
66b878f6 103 ioctl(t, TIOCSETP, &b);
9ef3087d 104 ioctl(p, TIOCGETP, &b);
da96b661 105 b.sg_flags &= ~ECHO;
9ef3087d 106 ioctl(p, TIOCSETP, &b);
37c640e2
SL
107 hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
108 who->sin_family);
109 if (hp)
110 host = hp->h_name;
111 else
112 host = ntoa(who->sin_addr);
8f2758db
SL
113 if ((i = fork()) < 0)
114 fatalperror(f, "fork", errno);
66b878f6
BJ
115 if (i)
116 telnet(f, p);
117 close(f);
118 close(p);
119 dup2(t, 0);
120 dup2(t, 1);
121 dup2(t, 2);
122 close(t);
fe5c5547 123 environ = envinit;
0c285f22 124 execl("/bin/login", "login", "-h", host, 0);
8f2758db
SL
125 fatalperror(f, "/bin/login", errno);
126 /*NOTREACHED*/
127}
128
129fatal(f, msg)
130 int f;
131 char *msg;
132{
133 char buf[BUFSIZ];
134
135 (void) sprintf(buf, "telnetd: %s.\n", msg);
136 (void) write(f, buf, strlen(buf));
66b878f6
BJ
137 exit(1);
138}
139
8f2758db
SL
140fatalperror(f, msg, errno)
141 int f;
142 char *msg;
143 int errno;
144{
145 char buf[BUFSIZ];
146 extern char *sys_errlist[];
147
148 (void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]);
149 fatal(f, buf);
150}
151
66b878f6
BJ
152/*
153 * Main loop. Select from pty and network, and
154 * hand data to telnet receiver finite state machine.
155 */
156telnet(f, p)
157{
158 int on = 1;
0c285f22 159 char hostname[32];
66b878f6
BJ
160
161 net = f, pty = p;
162 ioctl(f, FIONBIO, &on);
163 ioctl(p, FIONBIO, &on);
164 signal(SIGTSTP, SIG_IGN);
8a53982e 165 signal(SIGCHLD, cleanup);
66b878f6 166
da96b661
SL
167 /*
168 * Request to do remote echo.
169 */
170 dooption(TELOPT_ECHO);
171 myopts[TELOPT_ECHO] = 1;
0c285f22
SL
172 /*
173 * Show banner that getty never gave.
174 */
175 gethostname(hostname, sizeof (hostname));
176 sprintf(nfrontp, BANNER, hostname, "");
177 nfrontp += strlen(nfrontp);
66b878f6
BJ
178 for (;;) {
179 int ibits = 0, obits = 0;
180 register int c;
181
182 /*
183 * Never look for input if there's still
184 * stuff in the corresponding output buffer
185 */
186 if (nfrontp - nbackp)
187 obits |= (1 << f);
188 else
189 ibits |= (1 << p);
190 if (pfrontp - pbackp)
191 obits |= (1 << p);
192 else
193 ibits |= (1 << f);
194 if (ncc < 0 && pcc < 0)
195 break;
de3b21e8 196 select(16, &ibits, &obits, 0, 0);
66b878f6
BJ
197 if (ibits == 0 && obits == 0) {
198 sleep(5);
199 continue;
200 }
201
202 /*
203 * Something to read from the network...
204 */
205 if (ibits & (1 << f)) {
206 ncc = read(f, netibuf, BUFSIZ);
207 if (ncc < 0 && errno == EWOULDBLOCK)
208 ncc = 0;
209 else {
210 if (ncc <= 0)
211 break;
212 netip = netibuf;
213 }
214 }
215
216 /*
217 * Something to read from the pty...
218 */
219 if (ibits & (1 << p)) {
220 pcc = read(p, ptyibuf, BUFSIZ);
221 if (pcc < 0 && errno == EWOULDBLOCK)
222 pcc = 0;
223 else {
224 if (pcc <= 0)
225 break;
226 ptyip = ptyibuf;
227 }
228 }
229
230 while (pcc > 0) {
231 if ((&netobuf[BUFSIZ] - nfrontp) < 2)
232 break;
233 c = *ptyip++ & 0377, pcc--;
234 if (c == IAC)
235 *nfrontp++ = c;
236 *nfrontp++ = c;
237 }
238 if ((obits & (1 << f)) && (nfrontp - nbackp) > 0)
239 netflush();
240 if (ncc > 0)
241 telrcv();
242 if ((obits & (1 << p)) && (pfrontp - pbackp) > 0)
243 ptyflush();
244 }
245 cleanup();
246}
247
248/*
249 * State for recv fsm
250 */
251#define TS_DATA 0 /* base state */
252#define TS_IAC 1 /* look for double IAC's */
253#define TS_CR 2 /* CR-LF ->'s CR */
254#define TS_BEGINNEG 3 /* throw away begin's... */
255#define TS_ENDNEG 4 /* ...end's (suboption negotiation) */
256#define TS_WILL 5 /* will option negotiation */
257#define TS_WONT 6 /* wont " */
258#define TS_DO 7 /* do " */
259#define TS_DONT 8 /* dont " */
260
261telrcv()
262{
263 register int c;
264 static int state = TS_DATA;
265 struct sgttyb b;
266
267 while (ncc > 0) {
268 if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
269 return;
270 c = *netip++ & 0377, ncc--;
271 switch (state) {
272
273 case TS_DATA:
274 if (c == IAC) {
275 state = TS_IAC;
276 break;
277 }
278 if (inter > 0)
279 break;
280 *pfrontp++ = c;
281 if (!myopts[TELOPT_BINARY] && c == '\r')
282 state = TS_CR;
283 break;
284
285 case TS_CR:
286 if (c && c != '\n')
287 *pfrontp++ = c;
288 state = TS_DATA;
289 break;
290
291 case TS_IAC:
292 switch (c) {
293
294 /*
295 * Send the process on the pty side an
296 * interrupt. Do this with a NULL or
297 * interrupt char; depending on the tty mode.
298 */
299 case BREAK:
300 case IP:
301 interrupt();
302 break;
303
304 /*
305 * Are You There?
306 */
307 case AYT:
308 *pfrontp++ = BELL;
309 break;
310
311 /*
312 * Erase Character and
313 * Erase Line
314 */
315 case EC:
316 case EL:
317 ptyflush(); /* half-hearted */
318 ioctl(pty, TIOCGETP, &b);
319 *pfrontp++ = (c == EC) ?
320 b.sg_erase : b.sg_kill;
321 break;
322
323 /*
324 * Check for urgent data...
325 */
326 case DM:
327 break;
328
329 /*
330 * Begin option subnegotiation...
331 */
332 case SB:
333 state = TS_BEGINNEG;
334 continue;
335
336 case WILL:
337 case WONT:
338 case DO:
339 case DONT:
340 state = TS_WILL + (c - WILL);
341 continue;
342
343 case IAC:
344 *pfrontp++ = c;
345 break;
346 }
347 state = TS_DATA;
348 break;
349
350 case TS_BEGINNEG:
351 if (c == IAC)
352 state = TS_ENDNEG;
353 break;
354
355 case TS_ENDNEG:
356 state = c == SE ? TS_DATA : TS_BEGINNEG;
357 break;
358
359 case TS_WILL:
360 if (!hisopts[c])
361 willoption(c);
362 state = TS_DATA;
363 continue;
364
365 case TS_WONT:
366 if (hisopts[c])
367 wontoption(c);
368 state = TS_DATA;
369 continue;
370
371 case TS_DO:
372 if (!myopts[c])
373 dooption(c);
374 state = TS_DATA;
375 continue;
376
377 case TS_DONT:
378 if (myopts[c]) {
379 myopts[c] = 0;
380 sprintf(nfrontp, wont, c);
da96b661 381 nfrontp += sizeof (wont) - 2;
66b878f6
BJ
382 }
383 state = TS_DATA;
384 continue;
385
386 default:
de3b21e8 387 printf("telnetd: panic state=%d\n", state);
66b878f6
BJ
388 exit(1);
389 }
390 }
391}
392
393willoption(option)
394 int option;
395{
396 char *fmt;
397
398 switch (option) {
399
400 case TELOPT_BINARY:
401 mode(RAW, 0);
402 goto common;
403
404 case TELOPT_ECHO:
405 mode(0, ECHO|CRMOD);
406 /*FALL THRU*/
407
408 case TELOPT_SGA:
409 common:
410 hisopts[option] = 1;
411 fmt = doopt;
412 break;
413
414 case TELOPT_TM:
415 fmt = dont;
416 break;
417
418 default:
419 fmt = dont;
420 break;
421 }
13646f15 422 sprintf(nfrontp, fmt, option);
da96b661 423 nfrontp += sizeof (dont) - 2;
66b878f6
BJ
424}
425
426wontoption(option)
427 int option;
428{
429 char *fmt;
430
431 switch (option) {
432
433 case TELOPT_ECHO:
434 mode(ECHO|CRMOD, 0);
435 goto common;
436
437 case TELOPT_BINARY:
438 mode(0, RAW);
439 /*FALL THRU*/
440
441 case TELOPT_SGA:
442 common:
443 hisopts[option] = 0;
444 fmt = dont;
445 break;
446
447 default:
448 fmt = dont;
449 }
450 sprintf(nfrontp, fmt, option);
da96b661 451 nfrontp += sizeof (doopt) - 2;
66b878f6
BJ
452}
453
454dooption(option)
455 int option;
456{
457 char *fmt;
458
459 switch (option) {
460
461 case TELOPT_TM:
462 fmt = wont;
463 break;
464
465 case TELOPT_ECHO:
466 mode(ECHO|CRMOD, 0);
467 goto common;
468
469 case TELOPT_BINARY:
470 mode(RAW, 0);
471 /*FALL THRU*/
472
473 case TELOPT_SGA:
474 common:
475 fmt = will;
476 break;
477
478 default:
479 fmt = wont;
480 break;
481 }
482 sprintf(nfrontp, fmt, option);
da96b661 483 nfrontp += sizeof (doopt) - 2;
66b878f6
BJ
484}
485
486mode(on, off)
487 int on, off;
488{
489 struct sgttyb b;
490
491 ptyflush();
492 ioctl(pty, TIOCGETP, &b);
493 b.sg_flags |= on;
494 b.sg_flags &= ~off;
495 ioctl(pty, TIOCSETP, &b);
496}
497
498/*
499 * Send interrupt to process on other side of pty.
500 * If it is in raw mode, just write NULL;
501 * otherwise, write intr char.
502 */
503interrupt()
504{
505 struct sgttyb b;
506 struct tchars tchars;
507
508 ptyflush(); /* half-hearted */
509 ioctl(pty, TIOCGETP, &b);
510 if (b.sg_flags & RAW) {
511 *pfrontp++ = '\0';
512 return;
513 }
514 *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
515 '\177' : tchars.t_intrc;
516}
517
518ptyflush()
519{
520 int n;
521
522 if ((n = pfrontp - pbackp) > 0)
523 n = write(pty, pbackp, n);
9f005877
SL
524 if (n < 0)
525 return;
66b878f6
BJ
526 pbackp += n;
527 if (pbackp == pfrontp)
528 pbackp = pfrontp = ptyobuf;
529}
530
531netflush()
532{
533 int n;
534
535 if ((n = nfrontp - nbackp) > 0)
536 n = write(net, nbackp, n);
9f005877
SL
537 if (n < 0) {
538 if (errno == EWOULDBLOCK)
539 return;
540 /* should blow this guy away... */
541 return;
542 }
66b878f6
BJ
543 nbackp += n;
544 if (nbackp == nfrontp)
545 nbackp = nfrontp = netobuf;
546}
547
548cleanup()
549{
66b878f6
BJ
550
551 rmut();
a7f6263e 552 vhangup(); /* XXX */
ff24c640 553 shutdown(net, 2);
66b878f6
BJ
554 kill(0, SIGKILL);
555 exit(1);
556}
557
558#include <utmp.h>
559
560struct utmp wtmp;
561char wtmpf[] = "/usr/adm/wtmp";
562char utmp[] = "/etc/utmp";
da96b661
SL
563#define SCPYN(a, b) strncpy(a, b, sizeof (a))
564#define SCMPN(a, b) strncmp(a, b, sizeof (a))
66b878f6
BJ
565
566rmut()
567{
568 register f;
569 int found = 0;
570
571 f = open(utmp, 2);
572 if (f >= 0) {
da96b661 573 while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) {
66b878f6
BJ
574 if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0)
575 continue;
da96b661 576 lseek(f, -(long)sizeof (wtmp), 1);
66b878f6 577 SCPYN(wtmp.ut_name, "");
37c640e2 578 SCPYN(wtmp.ut_host, "");
66b878f6 579 time(&wtmp.ut_time);
da96b661 580 write(f, (char *)&wtmp, sizeof (wtmp));
66b878f6
BJ
581 found++;
582 }
583 close(f);
584 }
585 if (found) {
586 f = open(wtmpf, 1);
587 if (f >= 0) {
588 SCPYN(wtmp.ut_line, line+5);
589 SCPYN(wtmp.ut_name, "");
37c640e2 590 SCPYN(wtmp.ut_host, "");
66b878f6
BJ
591 time(&wtmp.ut_time);
592 lseek(f, (long)0, 2);
da96b661 593 write(f, (char *)&wtmp, sizeof (wtmp));
66b878f6
BJ
594 close(f);
595 }
596 }
597 chmod(line, 0666);
598 chown(line, 0, 0);
599 line[strlen("/dev/")] = 'p';
600 chmod(line, 0666);
601 chown(line, 0, 0);
602}
37c640e2
SL
603
604/*
605 * Convert network-format internet address
606 * to base 256 d.d.d.d representation.
607 */
608char *
609ntoa(in)
610 struct in_addr in;
611{
612 static char b[18];
613 register char *p;
614
615 p = (char *)&in;
616#define UC(b) (((int)b)&0xff)
617 sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
618 return (b);
619}