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