Commit | Line | Data |
---|---|---|
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 | |
8 | char 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 |
5d78ef73 | 14 | static char sccsid[] = "@(#)telnetd.c 5.9 (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> |
5d78ef73 | 25 | #include <sys/time.h> |
de3b21e8 SL |
26 | |
27 | #include <netinet/in.h> | |
28 | ||
2b597a6b SL |
29 | #include <arpa/telnet.h> |
30 | ||
66b878f6 BJ |
31 | #include <stdio.h> |
32 | #include <signal.h> | |
33 | #include <errno.h> | |
34 | #include <sgtty.h> | |
9f005877 | 35 | #include <netdb.h> |
3f99c0f7 | 36 | #include <syslog.h> |
de3b21e8 | 37 | |
4898c5bf | 38 | #define BELL '\07' |
122f5efc | 39 | #define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r%s" |
66b878f6 BJ |
40 | |
41 | char hisopts[256]; | |
42 | char myopts[256]; | |
43 | ||
44 | char doopt[] = { IAC, DO, '%', 'c', 0 }; | |
45 | char dont[] = { IAC, DONT, '%', 'c', 0 }; | |
46 | char will[] = { IAC, WILL, '%', 'c', 0 }; | |
47 | char wont[] = { IAC, WONT, '%', 'c', 0 }; | |
48 | ||
49 | /* | |
50 | * I/O data buffers, pointers, and counters. | |
51 | */ | |
52 | char ptyibuf[BUFSIZ], *ptyip = ptyibuf; | |
53 | char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; | |
54 | char netibuf[BUFSIZ], *netip = netibuf; | |
9ef3087d | 55 | char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; |
5d78ef73 | 56 | char *neturg = 0; /* one past last bye of urgent data */ |
66b878f6 BJ |
57 | int pcc, ncc; |
58 | ||
59 | int pty, net; | |
60 | int inter; | |
fe5c5547 | 61 | extern char **environ; |
66b878f6 | 62 | extern int errno; |
c29f876c | 63 | char *line; |
5d78ef73 GM |
64 | int SYNCHing = 0; /* we are in TELNET SYNCH mode */ |
65 | /* | |
66 | * The following are some clocks used to decide how to interpret | |
67 | * the relationship between various variables. | |
68 | */ | |
66b878f6 | 69 | |
5d78ef73 GM |
70 | struct { |
71 | int | |
72 | system, /* what the current time is */ | |
73 | echotoggle, /* last time user entered echo character */ | |
74 | modenegotiated, /* last time operating mode negotiated */ | |
75 | didnetreceive, /* last time we read data from network */ | |
76 | gotDM; /* when did we last see a data mark */ | |
77 | } clocks; | |
78 | ||
79 | #define settimer(x) clocks.x = clocks.system++ | |
80 | \f | |
66b878f6 BJ |
81 | main(argc, argv) |
82 | char *argv[]; | |
83 | { | |
bb933cc2 | 84 | struct sockaddr_in from; |
bcb894cb | 85 | int on = 1, fromlen; |
bb933cc2 | 86 | |
5d78ef73 GM |
87 | #if defined(DEBUG) |
88 | { | |
89 | int s, ns, foo; | |
90 | struct servent *sp; | |
91 | static struct sockaddr_in sin = { AF_INET }; | |
92 | ||
93 | sp = getservbyname("telnet", "tcp"); | |
94 | if (sp == 0) { | |
95 | fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); | |
96 | exit(1); | |
97 | } | |
98 | sin.sin_port = sp->s_port; | |
99 | argc--, argv++; | |
100 | if (argc > 0) { | |
101 | sin.sin_port = atoi(*argv); | |
102 | sin.sin_port = htons((u_short)sin.sin_port); | |
103 | } | |
104 | ||
105 | s = socket(AF_INET, SOCK_STREAM, 0); | |
106 | if (s < 0) { | |
107 | perror("telnetd: socket");; | |
108 | exit(1); | |
109 | } | |
110 | if (bind(s, &sin, sizeof sin) < 0) { | |
111 | perror("bind"); | |
112 | exit(1); | |
113 | } | |
114 | if (listen(s, 1) < 0) { | |
115 | perror("listen"); | |
116 | exit(1); | |
117 | } | |
118 | foo = sizeof sin; | |
119 | ns = accept(s, &sin, &foo); | |
120 | if (ns < 0) { | |
121 | perror("accept"); | |
122 | exit(1); | |
123 | } | |
124 | dup2(ns, 0); | |
125 | close(s); | |
126 | } | |
127 | #endif /* defined(DEBUG) */ | |
076ae92c | 128 | openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); |
bb933cc2 MK |
129 | fromlen = sizeof (from); |
130 | if (getpeername(0, &from, &fromlen) < 0) { | |
131 | fprintf(stderr, "%s: ", argv[0]); | |
132 | perror("getpeername"); | |
133 | _exit(1); | |
de3b21e8 | 134 | } |
bcb894cb | 135 | if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { |
3f99c0f7 | 136 | syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); |
de3b21e8 | 137 | } |
bb933cc2 | 138 | doit(0, &from); |
f553aca8 SL |
139 | } |
140 | ||
fe5c5547 | 141 | char *envinit[] = { "TERM=network", 0 }; |
66b878f6 BJ |
142 | int cleanup(); |
143 | ||
144 | /* | |
145 | * Get a pty, scan input lines. | |
146 | */ | |
37c640e2 SL |
147 | doit(f, who) |
148 | int f; | |
149 | struct sockaddr_in *who; | |
66b878f6 | 150 | { |
c29f876c | 151 | char *host, *inet_ntoa(); |
1a33b848 | 152 | int i, p, t; |
66b878f6 | 153 | struct sgttyb b; |
37c640e2 | 154 | struct hostent *hp; |
c29f876c | 155 | char c; |
1a33b848 | 156 | |
c29f876c MK |
157 | for (c = 'p'; c <= 's'; c++) { |
158 | struct stat stb; | |
159 | ||
160 | line = "/dev/ptyXX"; | |
161 | line[strlen("/dev/pty")] = c; | |
162 | line[strlen("/dev/ptyp")] = '0'; | |
163 | if (stat(line, &stb) < 0) | |
164 | break; | |
1a33b848 | 165 | for (i = 0; i < 16; i++) { |
c29f876c MK |
166 | line[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; |
167 | p = open(line, 2); | |
1a33b848 SL |
168 | if (p > 0) |
169 | goto gotpty; | |
170 | } | |
66b878f6 | 171 | } |
8f2758db SL |
172 | fatal(f, "All network ports in use"); |
173 | /*NOTREACHED*/ | |
66b878f6 BJ |
174 | gotpty: |
175 | dup2(f, 0); | |
c29f876c | 176 | line[strlen("/dev/")] = 't'; |
1a33b848 | 177 | t = open("/dev/tty", O_RDWR); |
66b878f6 BJ |
178 | if (t >= 0) { |
179 | ioctl(t, TIOCNOTTY, 0); | |
180 | close(t); | |
181 | } | |
c29f876c | 182 | t = open(line, O_RDWR); |
8f2758db | 183 | if (t < 0) |
c29f876c | 184 | fatalperror(f, line, errno); |
66b878f6 | 185 | ioctl(t, TIOCGETP, &b); |
9ef3087d | 186 | b.sg_flags = CRMOD|XTABS|ANYP; |
66b878f6 | 187 | ioctl(t, TIOCSETP, &b); |
9ef3087d | 188 | ioctl(p, TIOCGETP, &b); |
da96b661 | 189 | b.sg_flags &= ~ECHO; |
9ef3087d | 190 | ioctl(p, TIOCSETP, &b); |
37c640e2 SL |
191 | hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), |
192 | who->sin_family); | |
193 | if (hp) | |
194 | host = hp->h_name; | |
195 | else | |
05fa5465 | 196 | host = inet_ntoa(who->sin_addr); |
8f2758db SL |
197 | if ((i = fork()) < 0) |
198 | fatalperror(f, "fork", errno); | |
66b878f6 BJ |
199 | if (i) |
200 | telnet(f, p); | |
201 | close(f); | |
202 | close(p); | |
203 | dup2(t, 0); | |
204 | dup2(t, 1); | |
205 | dup2(t, 2); | |
206 | close(t); | |
fe5c5547 | 207 | environ = envinit; |
0c285f22 | 208 | execl("/bin/login", "login", "-h", host, 0); |
8f2758db SL |
209 | fatalperror(f, "/bin/login", errno); |
210 | /*NOTREACHED*/ | |
211 | } | |
212 | ||
213 | fatal(f, msg) | |
214 | int f; | |
215 | char *msg; | |
216 | { | |
217 | char buf[BUFSIZ]; | |
218 | ||
1a33b848 | 219 | (void) sprintf(buf, "telnetd: %s.\r\n", msg); |
8f2758db | 220 | (void) write(f, buf, strlen(buf)); |
66b878f6 BJ |
221 | exit(1); |
222 | } | |
223 | ||
8f2758db SL |
224 | fatalperror(f, msg, errno) |
225 | int f; | |
226 | char *msg; | |
227 | int errno; | |
228 | { | |
229 | char buf[BUFSIZ]; | |
230 | extern char *sys_errlist[]; | |
231 | ||
1a33b848 | 232 | (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); |
8f2758db SL |
233 | fatal(f, buf); |
234 | } | |
235 | ||
5d78ef73 GM |
236 | |
237 | /* | |
238 | * Check a descriptor to see if out of band data exists on it. | |
239 | */ | |
240 | ||
241 | ||
242 | stilloob(s) | |
243 | int s; /* socket number */ | |
244 | { | |
245 | static struct timeval timeout = { 0 }; | |
246 | fd_set excepts; | |
247 | int value; | |
248 | ||
249 | do { | |
250 | FD_ZERO(&excepts); | |
251 | FD_SET(s, &excepts); | |
252 | value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); | |
253 | } while ((value == -1) && (errno = EINTR)); | |
254 | ||
255 | if (value < 0) { | |
256 | fatalperror(pty, "select", errno); | |
257 | } | |
258 | if (FD_ISSET(s, &excepts)) { | |
259 | return 1; | |
260 | } else { | |
261 | return 0; | |
262 | } | |
263 | } | |
264 | \f | |
66b878f6 BJ |
265 | /* |
266 | * Main loop. Select from pty and network, and | |
267 | * hand data to telnet receiver finite state machine. | |
268 | */ | |
269 | telnet(f, p) | |
270 | { | |
271 | int on = 1; | |
0c285f22 | 272 | char hostname[32]; |
66b878f6 BJ |
273 | |
274 | net = f, pty = p; | |
275 | ioctl(f, FIONBIO, &on); | |
276 | ioctl(p, FIONBIO, &on); | |
277 | signal(SIGTSTP, SIG_IGN); | |
8a53982e | 278 | signal(SIGCHLD, cleanup); |
f4c5d9f9 | 279 | setpgrp(0, 0); |
66b878f6 | 280 | |
da96b661 | 281 | /* |
5d78ef73 | 282 | * Request to do remote echo and to suppress go ahead. |
da96b661 SL |
283 | */ |
284 | dooption(TELOPT_ECHO); | |
5d78ef73 | 285 | dooption(TELOPT_SGA); |
0c285f22 SL |
286 | /* |
287 | * Show banner that getty never gave. | |
288 | */ | |
289 | gethostname(hostname, sizeof (hostname)); | |
290 | sprintf(nfrontp, BANNER, hostname, ""); | |
291 | nfrontp += strlen(nfrontp); | |
66b878f6 | 292 | for (;;) { |
5d78ef73 | 293 | fd_set ibits, obits, xbits; |
66b878f6 BJ |
294 | register int c; |
295 | ||
5d78ef73 GM |
296 | if (ncc < 0 && pcc < 0) |
297 | break; | |
298 | ||
299 | FD_ZERO(&ibits); | |
300 | FD_ZERO(&obits); | |
301 | FD_ZERO(&xbits); | |
66b878f6 BJ |
302 | /* |
303 | * Never look for input if there's still | |
304 | * stuff in the corresponding output buffer | |
305 | */ | |
5d78ef73 GM |
306 | if (nfrontp - nbackp || pcc > 0) { |
307 | FD_SET(f, &obits); | |
308 | } else { | |
309 | FD_SET(p, &ibits); | |
310 | } | |
311 | if (pfrontp - pbackp || ncc > 0) { | |
312 | FD_SET(p, &obits); | |
313 | } else { | |
314 | FD_SET(f, &ibits); | |
315 | } | |
316 | if (!SYNCHing) { | |
317 | FD_SET(f, &xbits); | |
318 | } | |
319 | if ((c = select(16, &ibits, &obits, &xbits, | |
320 | (struct timeval *)0)) < 1) { | |
321 | if (c == -1) { | |
322 | if (errno == EINTR) { | |
323 | continue; | |
324 | } | |
325 | } | |
66b878f6 BJ |
326 | sleep(5); |
327 | continue; | |
328 | } | |
329 | ||
5d78ef73 GM |
330 | /* |
331 | * Any urgent data? | |
332 | */ | |
333 | if (FD_ISSET(net, &xbits)) { | |
334 | SYNCHing = 1; | |
335 | } | |
336 | ||
66b878f6 BJ |
337 | /* |
338 | * Something to read from the network... | |
339 | */ | |
5d78ef73 GM |
340 | if (FD_ISSET(net, &ibits)) { |
341 | #if !defined(IOCTL_TO_DO_UNIX_OOB_IN_TCP_WAY) | |
342 | /* | |
343 | * In 4.2 (and some early 4.3) systems, the | |
344 | * OOB indication and data handling in the kernel | |
345 | * is such that if two separate TCP Urgent requests | |
346 | * come in, one byte of TCP data will be overlaid. | |
347 | * This is fatal for Telnet, but we try to live | |
348 | * with it. | |
349 | * | |
350 | * In addition, in 4.2 (and...), a special protocol | |
351 | * is needed to pick up the TCP Urgent data in | |
352 | * the correct sequence. | |
353 | * | |
354 | * What we do is: if we think we are in urgent | |
355 | * mode, we look to see if we are "at the mark". | |
356 | * If we are, we do an OOB receive. If we run | |
357 | * this twice, we will do the OOB receive twice, | |
358 | * but the second will fail, since the second | |
359 | * time we were "at the mark", but there wasn't | |
360 | * any data there (the kernel doesn't reset | |
361 | * "at the mark" until we do a normal read). | |
362 | * Once we've read the OOB data, we go ahead | |
363 | * and do normal reads. | |
364 | * | |
365 | * There is also another problem, which is that | |
366 | * since the OOB byte we read doesn't put us | |
367 | * out of OOB state, and since that byte is most | |
368 | * likely the TELNET DM (data mark), we would | |
369 | * stay in the TELNET SYNCH (SYNCHing) state. | |
370 | * So, clocks to the rescue. If we've "just" | |
371 | * received a DM, then we test for the | |
372 | * presence of OOB data when the receive OOB | |
373 | * fails (and AFTER we did the normal mode read | |
374 | * to clear "at the mark"). | |
375 | */ | |
376 | if (SYNCHing) { | |
377 | int atmark; | |
378 | ||
379 | ioctl(net, SIOCATMARK, (char *)&atmark); | |
380 | if (atmark) { | |
381 | ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); | |
382 | if ((ncc == -1) && (errno == EINVAL)) { | |
383 | ncc = read(net, netibuf, sizeof (netibuf)); | |
384 | if (clocks.didnetreceive < clocks.gotDM) { | |
385 | SYNCHing = stilloob(net); | |
386 | } | |
387 | } | |
388 | } else { | |
389 | ncc = read(net, netibuf, sizeof (netibuf)); | |
66b878f6 | 390 | } |
5d78ef73 GM |
391 | } else { |
392 | ncc = read(net, netibuf, sizeof (netibuf)); | |
393 | } | |
394 | settimer(didnetreceive); | |
395 | #else /* !defined(IOCTL_TO_DO_UNIX_OOB_IN_TCP_WAY) */ | |
396 | ncc = read(net, netibuf, sizeof (netibuf)); | |
397 | #endif /* !defined(IOCTL_TO_DO_UNIX_OOB_IN_TCP_WAY) */ | |
398 | if (ncc < 0 && errno == EWOULDBLOCK) | |
399 | ncc = 0; | |
400 | else { | |
401 | if (ncc <= 0) { | |
402 | break; | |
403 | } | |
404 | netip = netibuf; | |
405 | } | |
66b878f6 BJ |
406 | } |
407 | ||
408 | /* | |
409 | * Something to read from the pty... | |
410 | */ | |
5d78ef73 | 411 | if (FD_ISSET(p, &ibits)) { |
66b878f6 BJ |
412 | pcc = read(p, ptyibuf, BUFSIZ); |
413 | if (pcc < 0 && errno == EWOULDBLOCK) | |
414 | pcc = 0; | |
415 | else { | |
416 | if (pcc <= 0) | |
417 | break; | |
418 | ptyip = ptyibuf; | |
419 | } | |
420 | } | |
421 | ||
422 | while (pcc > 0) { | |
423 | if ((&netobuf[BUFSIZ] - nfrontp) < 2) | |
424 | break; | |
425 | c = *ptyip++ & 0377, pcc--; | |
426 | if (c == IAC) | |
427 | *nfrontp++ = c; | |
428 | *nfrontp++ = c; | |
9f515693 GM |
429 | if (c == '\r') { |
430 | if (pcc > 0 && ((*ptyip & 0377) == '\n')) { | |
431 | *nfrontp++ = *ptyip++ & 0377; | |
432 | pcc--; | |
433 | } else | |
434 | *nfrontp++ = '\0'; | |
435 | } | |
66b878f6 | 436 | } |
5d78ef73 | 437 | if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) |
66b878f6 BJ |
438 | netflush(); |
439 | if (ncc > 0) | |
440 | telrcv(); | |
5d78ef73 | 441 | if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) |
66b878f6 BJ |
442 | ptyflush(); |
443 | } | |
444 | cleanup(); | |
445 | } | |
446 | ||
447 | /* | |
448 | * State for recv fsm | |
449 | */ | |
450 | #define TS_DATA 0 /* base state */ | |
451 | #define TS_IAC 1 /* look for double IAC's */ | |
452 | #define TS_CR 2 /* CR-LF ->'s CR */ | |
453 | #define TS_BEGINNEG 3 /* throw away begin's... */ | |
454 | #define TS_ENDNEG 4 /* ...end's (suboption negotiation) */ | |
455 | #define TS_WILL 5 /* will option negotiation */ | |
456 | #define TS_WONT 6 /* wont " */ | |
457 | #define TS_DO 7 /* do " */ | |
458 | #define TS_DONT 8 /* dont " */ | |
459 | ||
460 | telrcv() | |
461 | { | |
462 | register int c; | |
463 | static int state = TS_DATA; | |
66b878f6 BJ |
464 | |
465 | while (ncc > 0) { | |
466 | if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) | |
467 | return; | |
468 | c = *netip++ & 0377, ncc--; | |
469 | switch (state) { | |
470 | ||
8356bfad GM |
471 | case TS_CR: |
472 | state = TS_DATA; | |
a6d8450f | 473 | if ((c == 0) || (c == '\n')) { |
8356bfad | 474 | break; |
a6d8450f | 475 | } |
8356bfad GM |
476 | /* FALL THROUGH */ |
477 | ||
66b878f6 BJ |
478 | case TS_DATA: |
479 | if (c == IAC) { | |
480 | state = TS_IAC; | |
481 | break; | |
482 | } | |
483 | if (inter > 0) | |
484 | break; | |
9f515693 GM |
485 | /* |
486 | * We map \r\n ==> \n, since \r\n says | |
487 | * that we want to be in column 1 of the next | |
488 | * printable line, and \n is the standard | |
489 | * unix way of saying that (\r is only good | |
490 | * if CRMOD is set, which it normally is). | |
491 | */ | |
a6d8450f | 492 | if (!myopts[TELOPT_BINARY] && c == '\r') { |
9f515693 GM |
493 | if ((ncc > 0) && ('\n' == *netip)) { |
494 | netip++; ncc--; | |
495 | c = '\n'; | |
496 | } else { | |
497 | state = TS_CR; | |
498 | } | |
a6d8450f GM |
499 | } |
500 | *pfrontp++ = c; | |
66b878f6 BJ |
501 | break; |
502 | ||
503 | case TS_IAC: | |
504 | switch (c) { | |
505 | ||
506 | /* | |
507 | * Send the process on the pty side an | |
508 | * interrupt. Do this with a NULL or | |
509 | * interrupt char; depending on the tty mode. | |
510 | */ | |
511 | case BREAK: | |
512 | case IP: | |
513 | interrupt(); | |
514 | break; | |
515 | ||
516 | /* | |
517 | * Are You There? | |
518 | */ | |
519 | case AYT: | |
1a33b848 SL |
520 | strcpy(nfrontp, "\r\n[Yes]\r\n"); |
521 | nfrontp += 9; | |
66b878f6 BJ |
522 | break; |
523 | ||
5d78ef73 GM |
524 | /* |
525 | * Abort Output | |
526 | */ | |
527 | case AO: { | |
528 | struct ltchars tmpltc; | |
529 | ||
530 | ptyflush(); /* half-hearted */ | |
531 | ioctl(pty, TIOCGLTC, &tmpltc); | |
532 | if (tmpltc.t_flushc != '\377') { | |
533 | *pfrontp++ = tmpltc.t_flushc; | |
534 | } | |
535 | *nfrontp++ = IAC; | |
536 | *nfrontp++ = DM; | |
537 | neturg = nfrontp; | |
538 | break; | |
539 | } | |
540 | ||
66b878f6 BJ |
541 | /* |
542 | * Erase Character and | |
543 | * Erase Line | |
544 | */ | |
545 | case EC: | |
5d78ef73 GM |
546 | case EL: { |
547 | struct sgttyb b; | |
548 | char ch; | |
549 | ||
550 | ptyflush(); /* half-hearted */ | |
551 | ioctl(pty, TIOCGETP, &b); | |
552 | ch = (c == EC) ? | |
553 | b.sg_erase : b.sg_kill; | |
554 | if (ch != '\377') { | |
555 | *pfrontp++ = ch; | |
556 | } | |
557 | break; | |
558 | } | |
66b878f6 BJ |
559 | |
560 | /* | |
561 | * Check for urgent data... | |
562 | */ | |
563 | case DM: | |
5d78ef73 GM |
564 | SYNCHing = stilloob(net); |
565 | settimer(gotDM); | |
66b878f6 BJ |
566 | break; |
567 | ||
5d78ef73 | 568 | |
66b878f6 BJ |
569 | /* |
570 | * Begin option subnegotiation... | |
571 | */ | |
572 | case SB: | |
573 | state = TS_BEGINNEG; | |
574 | continue; | |
575 | ||
576 | case WILL: | |
577 | case WONT: | |
578 | case DO: | |
579 | case DONT: | |
580 | state = TS_WILL + (c - WILL); | |
581 | continue; | |
582 | ||
583 | case IAC: | |
584 | *pfrontp++ = c; | |
585 | break; | |
586 | } | |
587 | state = TS_DATA; | |
588 | break; | |
589 | ||
590 | case TS_BEGINNEG: | |
591 | if (c == IAC) | |
592 | state = TS_ENDNEG; | |
593 | break; | |
594 | ||
595 | case TS_ENDNEG: | |
596 | state = c == SE ? TS_DATA : TS_BEGINNEG; | |
597 | break; | |
598 | ||
599 | case TS_WILL: | |
600 | if (!hisopts[c]) | |
601 | willoption(c); | |
602 | state = TS_DATA; | |
603 | continue; | |
604 | ||
605 | case TS_WONT: | |
606 | if (hisopts[c]) | |
607 | wontoption(c); | |
608 | state = TS_DATA; | |
609 | continue; | |
610 | ||
611 | case TS_DO: | |
612 | if (!myopts[c]) | |
613 | dooption(c); | |
614 | state = TS_DATA; | |
615 | continue; | |
616 | ||
617 | case TS_DONT: | |
618 | if (myopts[c]) { | |
619 | myopts[c] = 0; | |
620 | sprintf(nfrontp, wont, c); | |
da96b661 | 621 | nfrontp += sizeof (wont) - 2; |
66b878f6 BJ |
622 | } |
623 | state = TS_DATA; | |
624 | continue; | |
625 | ||
626 | default: | |
de3b21e8 | 627 | printf("telnetd: panic state=%d\n", state); |
66b878f6 BJ |
628 | exit(1); |
629 | } | |
630 | } | |
631 | } | |
632 | ||
633 | willoption(option) | |
634 | int option; | |
635 | { | |
636 | char *fmt; | |
637 | ||
638 | switch (option) { | |
639 | ||
640 | case TELOPT_BINARY: | |
641 | mode(RAW, 0); | |
642 | goto common; | |
643 | ||
644 | case TELOPT_ECHO: | |
645 | mode(0, ECHO|CRMOD); | |
646 | /*FALL THRU*/ | |
647 | ||
648 | case TELOPT_SGA: | |
649 | common: | |
650 | hisopts[option] = 1; | |
651 | fmt = doopt; | |
652 | break; | |
653 | ||
654 | case TELOPT_TM: | |
655 | fmt = dont; | |
656 | break; | |
657 | ||
658 | default: | |
659 | fmt = dont; | |
660 | break; | |
661 | } | |
13646f15 | 662 | sprintf(nfrontp, fmt, option); |
da96b661 | 663 | nfrontp += sizeof (dont) - 2; |
66b878f6 BJ |
664 | } |
665 | ||
666 | wontoption(option) | |
667 | int option; | |
668 | { | |
669 | char *fmt; | |
670 | ||
671 | switch (option) { | |
672 | ||
673 | case TELOPT_ECHO: | |
674 | mode(ECHO|CRMOD, 0); | |
675 | goto common; | |
676 | ||
677 | case TELOPT_BINARY: | |
678 | mode(0, RAW); | |
679 | /*FALL THRU*/ | |
680 | ||
681 | case TELOPT_SGA: | |
682 | common: | |
683 | hisopts[option] = 0; | |
684 | fmt = dont; | |
685 | break; | |
686 | ||
687 | default: | |
688 | fmt = dont; | |
689 | } | |
690 | sprintf(nfrontp, fmt, option); | |
da96b661 | 691 | nfrontp += sizeof (doopt) - 2; |
66b878f6 BJ |
692 | } |
693 | ||
694 | dooption(option) | |
695 | int option; | |
696 | { | |
697 | char *fmt; | |
698 | ||
699 | switch (option) { | |
700 | ||
701 | case TELOPT_TM: | |
702 | fmt = wont; | |
703 | break; | |
704 | ||
705 | case TELOPT_ECHO: | |
706 | mode(ECHO|CRMOD, 0); | |
707 | goto common; | |
708 | ||
709 | case TELOPT_BINARY: | |
710 | mode(RAW, 0); | |
711 | /*FALL THRU*/ | |
712 | ||
713 | case TELOPT_SGA: | |
714 | common: | |
715 | fmt = will; | |
716 | break; | |
717 | ||
718 | default: | |
719 | fmt = wont; | |
720 | break; | |
721 | } | |
722 | sprintf(nfrontp, fmt, option); | |
da96b661 | 723 | nfrontp += sizeof (doopt) - 2; |
66b878f6 BJ |
724 | } |
725 | ||
726 | mode(on, off) | |
727 | int on, off; | |
728 | { | |
729 | struct sgttyb b; | |
730 | ||
731 | ptyflush(); | |
732 | ioctl(pty, TIOCGETP, &b); | |
733 | b.sg_flags |= on; | |
734 | b.sg_flags &= ~off; | |
735 | ioctl(pty, TIOCSETP, &b); | |
736 | } | |
737 | ||
738 | /* | |
739 | * Send interrupt to process on other side of pty. | |
740 | * If it is in raw mode, just write NULL; | |
741 | * otherwise, write intr char. | |
742 | */ | |
743 | interrupt() | |
744 | { | |
745 | struct sgttyb b; | |
746 | struct tchars tchars; | |
747 | ||
748 | ptyflush(); /* half-hearted */ | |
749 | ioctl(pty, TIOCGETP, &b); | |
750 | if (b.sg_flags & RAW) { | |
751 | *pfrontp++ = '\0'; | |
752 | return; | |
753 | } | |
754 | *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? | |
755 | '\177' : tchars.t_intrc; | |
756 | } | |
757 | ||
758 | ptyflush() | |
759 | { | |
760 | int n; | |
761 | ||
762 | if ((n = pfrontp - pbackp) > 0) | |
763 | n = write(pty, pbackp, n); | |
9f005877 SL |
764 | if (n < 0) |
765 | return; | |
66b878f6 BJ |
766 | pbackp += n; |
767 | if (pbackp == pfrontp) | |
768 | pbackp = pfrontp = ptyobuf; | |
769 | } | |
770 | ||
5d78ef73 | 771 | #if 0 |
66b878f6 BJ |
772 | netflush() |
773 | { | |
774 | int n; | |
775 | ||
776 | if ((n = nfrontp - nbackp) > 0) | |
777 | n = write(net, nbackp, n); | |
9f005877 SL |
778 | if (n < 0) { |
779 | if (errno == EWOULDBLOCK) | |
780 | return; | |
781 | /* should blow this guy away... */ | |
782 | return; | |
783 | } | |
66b878f6 BJ |
784 | nbackp += n; |
785 | if (nbackp == nfrontp) | |
786 | nbackp = nfrontp = netobuf; | |
787 | } | |
5d78ef73 GM |
788 | #else /* 0 */ |
789 | ||
790 | ||
791 | /* | |
792 | * netflush | |
793 | * Send as much data as possible to the network, | |
794 | * handling requests for urgent data. | |
795 | */ | |
796 | ||
797 | ||
798 | netflush() | |
799 | { | |
800 | int n; | |
801 | ||
802 | if ((n = nfrontp - nbackp) > 0) { | |
803 | if (!neturg) { | |
804 | n = write(net, nbackp, n); /* normal write */ | |
805 | } else { | |
806 | n = neturg - nbackp; | |
807 | /* | |
808 | * In 4.2 (and 4.3) systems, there is some question about | |
809 | * what byte in a sendOOB operation is the "OOB" data. | |
810 | * To make ourselves compatible, we only send ONE byte | |
811 | * out of band, the one WE THINK should be OOB (though | |
812 | * we really have more the TCP philosophy of urgent data | |
813 | * rather than the Unix philosophy of OOB data). | |
814 | */ | |
815 | if (n > 1) { | |
816 | n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ | |
817 | } else { | |
818 | n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ | |
819 | } | |
820 | } | |
821 | } | |
822 | if (n < 0) { | |
823 | if (errno == EWOULDBLOCK) | |
824 | return; | |
825 | /* should blow this guy away... */ | |
826 | return; | |
827 | } | |
828 | nbackp += n; | |
829 | if (nbackp >= neturg) { | |
830 | neturg = 0; | |
831 | } | |
832 | if (nbackp == nfrontp) { | |
833 | nbackp = nfrontp = netobuf; | |
834 | } | |
835 | } | |
836 | #endif /* 0 */ | |
66b878f6 BJ |
837 | |
838 | cleanup() | |
839 | { | |
66b878f6 BJ |
840 | |
841 | rmut(); | |
a7f6263e | 842 | vhangup(); /* XXX */ |
ff24c640 | 843 | shutdown(net, 2); |
66b878f6 BJ |
844 | exit(1); |
845 | } | |
846 | ||
847 | #include <utmp.h> | |
848 | ||
849 | struct utmp wtmp; | |
850 | char wtmpf[] = "/usr/adm/wtmp"; | |
122f5efc JB |
851 | char utmpf[] = "/etc/utmp"; |
852 | #define SCPYN(a, b) strncpy(a, b, sizeof(a)) | |
853 | #define SCMPN(a, b) strncmp(a, b, sizeof(a)) | |
66b878f6 BJ |
854 | |
855 | rmut() | |
856 | { | |
857 | register f; | |
858 | int found = 0; | |
122f5efc JB |
859 | struct utmp *u, *utmp; |
860 | int nutmp; | |
861 | struct stat statbf; | |
66b878f6 | 862 | |
122f5efc | 863 | f = open(utmpf, O_RDWR); |
66b878f6 | 864 | if (f >= 0) { |
122f5efc JB |
865 | fstat(f, &statbf); |
866 | utmp = (struct utmp *)malloc(statbf.st_size); | |
867 | if (!utmp) | |
868 | syslog(LOG_ERR, "utmp malloc failed"); | |
869 | if (statbf.st_size && utmp) { | |
870 | nutmp = read(f, utmp, statbf.st_size); | |
871 | nutmp /= sizeof(struct utmp); | |
872 | ||
873 | for (u = utmp ; u < &utmp[nutmp] ; u++) { | |
874 | if (SCMPN(u->ut_line, line+5) || | |
875 | u->ut_name[0]==0) | |
876 | continue; | |
877 | lseek(f, ((long)u)-((long)utmp), L_SET); | |
878 | SCPYN(u->ut_name, ""); | |
879 | SCPYN(u->ut_host, ""); | |
880 | time(&u->ut_time); | |
881 | write(f, (char *)u, sizeof(wtmp)); | |
882 | found++; | |
883 | } | |
66b878f6 BJ |
884 | } |
885 | close(f); | |
886 | } | |
887 | if (found) { | |
1a33b848 | 888 | f = open(wtmpf, O_WRONLY|O_APPEND); |
66b878f6 BJ |
889 | if (f >= 0) { |
890 | SCPYN(wtmp.ut_line, line+5); | |
891 | SCPYN(wtmp.ut_name, ""); | |
37c640e2 | 892 | SCPYN(wtmp.ut_host, ""); |
66b878f6 | 893 | time(&wtmp.ut_time); |
122f5efc | 894 | write(f, (char *)&wtmp, sizeof(wtmp)); |
66b878f6 BJ |
895 | close(f); |
896 | } | |
897 | } | |
898 | chmod(line, 0666); | |
899 | chown(line, 0, 0); | |
900 | line[strlen("/dev/")] = 'p'; | |
901 | chmod(line, 0666); | |
902 | chown(line, 0, 0); | |
903 | } |