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