This is with CMU mods for window size, terminal speed, and flow
[unix-history] / usr / src / libexec / telnetd / telnetd.c
CommitLineData
8c5eec2f 1/*
897ce52e
KB
2 * Copyright (c) 1983, 1986 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
b8c620d6
KB
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
8c5eec2f
DF
16 */
17
18#ifndef lint
19char copyright[] =
897ce52e 20"@(#) Copyright (c) 1983, 1986 Regents of the University of California.\n\
8c5eec2f 21 All rights reserved.\n";
897ce52e 22#endif /* not lint */
8c5eec2f 23
ac6e6727 24#ifndef lint
31004941 25static char sccsid[] = "@(#)telnetd.c 5.32 (Berkeley) %G%";
897ce52e 26#endif /* not lint */
ac6e6727 27
66b878f6 28/*
5eddff6d 29 * Telnet server.
66b878f6 30 */
5eddff6d 31#include <sys/param.h>
de3b21e8 32#include <sys/socket.h>
ce4fd43b 33#include <sys/wait.h>
1a33b848 34#include <sys/file.h>
c29f876c 35#include <sys/stat.h>
5d78ef73 36#include <sys/time.h>
de3b21e8
SL
37
38#include <netinet/in.h>
39
2b597a6b
SL
40#include <arpa/telnet.h>
41
66b878f6
BJ
42#include <stdio.h>
43#include <signal.h>
44#include <errno.h>
45#include <sgtty.h>
9f005877 46#include <netdb.h>
3f99c0f7 47#include <syslog.h>
affdaa4e 48#include <ctype.h>
de3b21e8 49
d8b5e42c
GM
50#define OPT_NO 0 /* won't do this option */
51#define OPT_YES 1 /* will do this option */
52#define OPT_YES_BUT_ALWAYS_LOOK 2
53#define OPT_NO_BUT_ALWAYS_LOOK 3
66b878f6
BJ
54char hisopts[256];
55char myopts[256];
56
57char doopt[] = { IAC, DO, '%', 'c', 0 };
58char dont[] = { IAC, DONT, '%', 'c', 0 };
59char will[] = { IAC, WILL, '%', 'c', 0 };
60char wont[] = { IAC, WONT, '%', 'c', 0 };
61
62/*
63 * I/O data buffers, pointers, and counters.
64 */
65char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
affdaa4e 66
66b878f6 67char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
affdaa4e 68
66b878f6 69char netibuf[BUFSIZ], *netip = netibuf;
affdaa4e
GM
70#define NIACCUM(c) { *netip++ = c; \
71 ncc++; \
72 }
73
9ef3087d 74char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
5d78ef73 75char *neturg = 0; /* one past last bye of urgent data */
affdaa4e
GM
76 /* the remote system seems to NOT be an old 4.2 */
77int not42 = 1;
78
d0a64c71 79#define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r"
10dc182f 80
d8b5e42c
GM
81 /* buffer for sub-options */
82char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
affdaa4e 83#define SB_CLEAR() subpointer = subbuffer;
d8b5e42c 84#define SB_TERM() { subend = subpointer; SB_CLEAR(); }
affdaa4e
GM
85#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
86 *subpointer++ = (c); \
87 }
d8b5e42c
GM
88#define SB_GET() ((*subpointer++)&0xff)
89#define SB_EOF() (subpointer >= subend)
affdaa4e 90
66b878f6
BJ
91int pcc, ncc;
92
93int pty, net;
94int inter;
fe5c5547 95extern char **environ;
66b878f6 96extern int errno;
c29f876c 97char *line;
5d78ef73
GM
98int SYNCHing = 0; /* we are in TELNET SYNCH mode */
99/*
100 * The following are some clocks used to decide how to interpret
101 * the relationship between various variables.
102 */
66b878f6 103
5d78ef73
GM
104struct {
105 int
106 system, /* what the current time is */
107 echotoggle, /* last time user entered echo character */
108 modenegotiated, /* last time operating mode negotiated */
109 didnetreceive, /* last time we read data from network */
d8b5e42c
GM
110 ttypeopt, /* ttype will/won't received */
111 ttypesubopt, /* ttype subopt is received */
112 getterminal, /* time started to get terminal information */
5d78ef73
GM
113 gotDM; /* when did we last see a data mark */
114} clocks;
115
d8b5e42c
GM
116#define settimer(x) (clocks.x = ++clocks.system)
117#define sequenceIs(x,y) (clocks.x < clocks.y)
5d78ef73 118\f
66b878f6
BJ
119main(argc, argv)
120 char *argv[];
121{
bb933cc2 122 struct sockaddr_in from;
bcb894cb 123 int on = 1, fromlen;
bb933cc2 124
31004941 125 if ((argc > 1) && (strcmp(argv[1], "-debug") == 0)) {
5d78ef73
GM
126 int s, ns, foo;
127 struct servent *sp;
128 static struct sockaddr_in sin = { AF_INET };
129
5d78ef73 130 argc--, argv++;
31004941
GM
131 if (argc > 1) {
132 sin.sin_port = atoi(argv[1]);
5d78ef73 133 sin.sin_port = htons((u_short)sin.sin_port);
31004941
GM
134 } else {
135 sp = getservbyname("telnet", "tcp");
136 if (sp == 0) {
137 fprintf(stderr,
138 "telnetd: tcp/telnet: unknown service\n");
139 exit(1);
140 }
141 sin.sin_port = sp->s_port;
5d78ef73
GM
142 }
143
144 s = socket(AF_INET, SOCK_STREAM, 0);
145 if (s < 0) {
146 perror("telnetd: socket");;
147 exit(1);
148 }
149 if (bind(s, &sin, sizeof sin) < 0) {
150 perror("bind");
151 exit(1);
152 }
153 if (listen(s, 1) < 0) {
154 perror("listen");
155 exit(1);
156 }
157 foo = sizeof sin;
158 ns = accept(s, &sin, &foo);
159 if (ns < 0) {
160 perror("accept");
161 exit(1);
162 }
163 dup2(ns, 0);
164 close(s);
165 }
076ae92c 166 openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
bb933cc2
MK
167 fromlen = sizeof (from);
168 if (getpeername(0, &from, &fromlen) < 0) {
169 fprintf(stderr, "%s: ", argv[0]);
170 perror("getpeername");
171 _exit(1);
de3b21e8 172 }
bcb894cb 173 if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
3f99c0f7 174 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
de3b21e8 175 }
bb933cc2 176 doit(0, &from);
f553aca8
SL
177}
178
d8b5e42c
GM
179char *terminaltype = 0;
180char *envinit[2];
181int cleanup();
affdaa4e
GM
182
183/*
d8b5e42c 184 * ttloop
affdaa4e 185 *
d8b5e42c
GM
186 * A small subroutine to flush the network output buffer, get some data
187 * from the network, and pass it through the telnet state machine. We
188 * also flush the pty input buffer (by dropping its data) if it becomes
189 * too full.
190 */
191
192void
193ttloop()
194{
195 if (nfrontp-nbackp) {
196 netflush();
197 }
198 ncc = read(net, netibuf, sizeof netibuf);
199 if (ncc < 0) {
200 syslog(LOG_INFO, "ttloop: read: %m\n");
10a3b37e
GM
201 exit(1);
202 } else if (ncc == 0) {
203 syslog(LOG_INFO, "ttloop: peer died: %m\n");
204 exit(1);
d8b5e42c
GM
205 }
206 netip = netibuf;
207 telrcv(); /* state machine */
208 if (ncc > 0) {
209 pfrontp = pbackp = ptyobuf;
210 telrcv();
211 }
212}
213
31004941
GM
214/*
215 * getterminalspeed
216 *
217 * Ask the other end to send along its terminal speed.
218 * subopt does the rest. Interlocked so it can't happen during
219 * getterminaltype.
220 */
221
222void
223getterminalspeed()
224{
225 static char sbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
226
227 bcopy(sbuf, nfrontp, sizeof sbuf);
228 nfrontp += sizeof sbuf;
229}
230
d8b5e42c
GM
231/*
232 * getterminaltype
affdaa4e 233 *
d8b5e42c
GM
234 * Ask the other end to send along its terminal type.
235 * Output is the variable terminaltype filled in.
affdaa4e
GM
236 */
237
d8b5e42c
GM
238void
239getterminaltype()
affdaa4e 240{
d8b5e42c 241 static char sbuf[] = { IAC, DO, TELOPT_TTYPE };
affdaa4e 242
d8b5e42c
GM
243 settimer(getterminal);
244 bcopy(sbuf, nfrontp, sizeof sbuf);
245 nfrontp += sizeof sbuf;
10a3b37e 246 hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
d8b5e42c
GM
247 while (sequenceIs(ttypeopt, getterminal)) {
248 ttloop();
affdaa4e 249 }
d8b5e42c
GM
250 if (hisopts[TELOPT_TTYPE] == OPT_YES) {
251 static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
affdaa4e 252
d8b5e42c
GM
253 bcopy(sbbuf, nfrontp, sizeof sbbuf);
254 nfrontp += sizeof sbbuf;
255 while (sequenceIs(ttypesubopt, getterminal)) {
256 ttloop();
257 }
258 }
259}
66b878f6
BJ
260
261/*
262 * Get a pty, scan input lines.
263 */
37c640e2
SL
264doit(f, who)
265 int f;
266 struct sockaddr_in *who;
66b878f6 267{
c29f876c 268 char *host, *inet_ntoa();
1a33b848 269 int i, p, t;
66b878f6 270 struct sgttyb b;
37c640e2 271 struct hostent *hp;
affdaa4e 272 int c;
1a33b848 273
c29f876c
MK
274 for (c = 'p'; c <= 's'; c++) {
275 struct stat stb;
276
277 line = "/dev/ptyXX";
278 line[strlen("/dev/pty")] = c;
279 line[strlen("/dev/ptyp")] = '0';
280 if (stat(line, &stb) < 0)
281 break;
1a33b848 282 for (i = 0; i < 16; i++) {
312b6ead
KB
283 line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
284 p = open(line, O_RDWR);
1a33b848
SL
285 if (p > 0)
286 goto gotpty;
287 }
66b878f6 288 }
8f2758db
SL
289 fatal(f, "All network ports in use");
290 /*NOTREACHED*/
66b878f6
BJ
291gotpty:
292 dup2(f, 0);
c29f876c 293 line[strlen("/dev/")] = 't';
1a33b848 294 t = open("/dev/tty", O_RDWR);
66b878f6
BJ
295 if (t >= 0) {
296 ioctl(t, TIOCNOTTY, 0);
297 close(t);
298 }
c29f876c 299 t = open(line, O_RDWR);
8f2758db 300 if (t < 0)
312b6ead
KB
301 fatalperror(f, line);
302 if (fchmod(t, 0))
303 fatalperror(f, line);
304 (void)signal(SIGHUP, SIG_IGN);
305 vhangup();
306 (void)signal(SIGHUP, SIG_DFL);
307 t = open(line, O_RDWR);
308 if (t < 0)
309 fatalperror(f, line);
66b878f6 310 ioctl(t, TIOCGETP, &b);
9ef3087d 311 b.sg_flags = CRMOD|XTABS|ANYP;
66b878f6 312 ioctl(t, TIOCSETP, &b);
9ef3087d 313 ioctl(p, TIOCGETP, &b);
da96b661 314 b.sg_flags &= ~ECHO;
31004941 315 b.sg_ospeed = b.sg_ispeed = B9600;
9ef3087d 316 ioctl(p, TIOCSETP, &b);
37c640e2
SL
317 hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
318 who->sin_family);
319 if (hp)
320 host = hp->h_name;
321 else
05fa5465 322 host = inet_ntoa(who->sin_addr);
d8b5e42c
GM
323
324 net = f;
325 pty = p;
326
327 /*
31004941 328 * get terminal type and size.
d8b5e42c
GM
329 */
330 getterminaltype();
331
8f2758db 332 if ((i = fork()) < 0)
312b6ead 333 fatalperror(f, "fork");
66b878f6
BJ
334 if (i)
335 telnet(f, p);
336 close(f);
337 close(p);
338 dup2(t, 0);
339 dup2(t, 1);
340 dup2(t, 2);
341 close(t);
d8b5e42c
GM
342 envinit[0] = terminaltype;
343 envinit[1] = 0;
fe5c5547 344 environ = envinit;
affdaa4e
GM
345 /*
346 * -h : pass on name of host.
d8b5e42c
GM
347 * WARNING: -h is accepted by login if and only if
348 * getuid() == 0.
affdaa4e
GM
349 * -p : don't clobber the environment (so terminal type stays set).
350 */
351 execl("/bin/login", "login", "-h", host,
d8b5e42c 352 terminaltype ? "-p" : 0, 0);
cdcb5dc6
MK
353 syslog(LOG_ERR, "/bin/login: %m\n");
354 fatalperror(2, "/bin/login");
8f2758db
SL
355 /*NOTREACHED*/
356}
357
358fatal(f, msg)
359 int f;
360 char *msg;
361{
362 char buf[BUFSIZ];
363
1a33b848 364 (void) sprintf(buf, "telnetd: %s.\r\n", msg);
8f2758db 365 (void) write(f, buf, strlen(buf));
66b878f6
BJ
366 exit(1);
367}
368
312b6ead 369fatalperror(f, msg)
8f2758db
SL
370 int f;
371 char *msg;
8f2758db
SL
372{
373 char buf[BUFSIZ];
374 extern char *sys_errlist[];
375
1a33b848 376 (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
8f2758db
SL
377 fatal(f, buf);
378}
379
5d78ef73
GM
380
381/*
382 * Check a descriptor to see if out of band data exists on it.
383 */
384
385
386stilloob(s)
387int s; /* socket number */
388{
389 static struct timeval timeout = { 0 };
390 fd_set excepts;
391 int value;
392
393 do {
394 FD_ZERO(&excepts);
395 FD_SET(s, &excepts);
396 value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
5eddff6d 397 } while ((value == -1) && (errno == EINTR));
5d78ef73
GM
398
399 if (value < 0) {
312b6ead 400 fatalperror(pty, "select");
5d78ef73
GM
401 }
402 if (FD_ISSET(s, &excepts)) {
403 return 1;
404 } else {
405 return 0;
406 }
407}
408\f
66b878f6
BJ
409/*
410 * Main loop. Select from pty and network, and
411 * hand data to telnet receiver finite state machine.
412 */
413telnet(f, p)
414{
415 int on = 1;
5eddff6d 416 char hostname[MAXHOSTNAMELEN];
d0a64c71
GM
417#define TABBUFSIZ 512
418 char defent[TABBUFSIZ];
419 char defstrs[TABBUFSIZ];
420#undef TABBUFSIZ
421 char *HE;
422 char *HN;
423 char *IM;
66b878f6 424
66b878f6
BJ
425 ioctl(f, FIONBIO, &on);
426 ioctl(p, FIONBIO, &on);
4701a1c1 427 ioctl(p, TIOCPKT, &on);
affdaa4e
GM
428#if defined(SO_OOBINLINE)
429 setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
430#endif /* defined(SO_OOBINLINE) */
66b878f6 431 signal(SIGTSTP, SIG_IGN);
9eebc521
GM
432 /*
433 * Ignoring SIGTTOU keeps the kernel from blocking us
434 * in ttioctl() in /sys/tty.c.
435 */
436 signal(SIGTTOU, SIG_IGN);
8a53982e 437 signal(SIGCHLD, cleanup);
f4c5d9f9 438 setpgrp(0, 0);
66b878f6 439
da96b661 440 /*
5d78ef73 441 * Request to do remote echo and to suppress go ahead.
da96b661 442 */
d8b5e42c
GM
443 if (!myopts[TELOPT_ECHO]) {
444 dooption(TELOPT_ECHO);
445 }
446 if (!myopts[TELOPT_SGA]) {
447 dooption(TELOPT_SGA);
448 }
31004941
GM
449 if (!hisopts[TELOPT_NAWS]) {
450 willoption(TELOPT_NAWS);
451 hisopts[TELOPT_NAWS] = OPT_NO;
452 }
453 if (!hisopts[TELOPT_TSPEED]) {
454 willoption(TELOPT_TSPEED);
455 hisopts[TELOPT_TSPEED] = OPT_NO;
456 }
457 if (!hisopts[TELOPT_LFLOW]) {
458 willoption(TELOPT_LFLOW);
459 hisopts[TELOPT_LFLOW] = OPT_NO;
460 }
461
affdaa4e
GM
462 /*
463 * Is the client side a 4.2 (NOT 4.3) system? We need to know this
464 * because 4.2 clients are unable to deal with TCP urgent data.
465 *
466 * To find out, we send out a "DO ECHO". If the remote system
467 * answers "WILL ECHO" it is probably a 4.2 client, and we note
468 * that fact ("WILL ECHO" ==> that the client will echo what
469 * WE, the server, sends it; it does NOT mean that the client will
470 * echo the terminal input).
471 */
9bd38ba8 472 (void) sprintf(nfrontp, doopt, TELOPT_ECHO);
affdaa4e 473 nfrontp += sizeof doopt-2;
d8b5e42c 474 hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
affdaa4e 475
0c285f22
SL
476 /*
477 * Show banner that getty never gave.
10dc182f 478 *
d0a64c71
GM
479 * We put the banner in the pty input buffer. This way, it
480 * gets carriage return null processing, etc., just like all
481 * other pty --> client data.
0c285f22 482 */
10dc182f 483
0c285f22 484 gethostname(hostname, sizeof (hostname));
d0a64c71
GM
485 if (getent(defent, "default") == 1) {
486 char *getstr();
487 char *p=defstrs;
488
489 HE = getstr("he", &p);
490 HN = getstr("hn", &p);
491 IM = getstr("im", &p);
492 if (HN && *HN)
493 strcpy(hostname, HN);
494 edithost(HE, hostname);
495 if (IM && *IM)
312b6ead 496 putf(IM, ptyibuf+1);
d0a64c71
GM
497 } else {
498 sprintf(ptyibuf+1, BANNER, hostname);
499 }
10dc182f 500
d0a64c71
GM
501 ptyip = ptyibuf+1; /* Prime the pump */
502 pcc = strlen(ptyip); /* ditto */
affdaa4e 503
4701a1c1
GM
504 /* Clear ptybuf[0] - where the packet information is received */
505 ptyibuf[0] = 0;
d0a64c71 506
affdaa4e
GM
507 /*
508 * Call telrcv() once to pick up anything received during
509 * terminal type negotiation.
510 */
511 telrcv();
512
66b878f6 513 for (;;) {
5d78ef73 514 fd_set ibits, obits, xbits;
66b878f6
BJ
515 register int c;
516
5d78ef73
GM
517 if (ncc < 0 && pcc < 0)
518 break;
519
520 FD_ZERO(&ibits);
521 FD_ZERO(&obits);
522 FD_ZERO(&xbits);
66b878f6
BJ
523 /*
524 * Never look for input if there's still
525 * stuff in the corresponding output buffer
526 */
5d78ef73
GM
527 if (nfrontp - nbackp || pcc > 0) {
528 FD_SET(f, &obits);
529 } else {
530 FD_SET(p, &ibits);
531 }
532 if (pfrontp - pbackp || ncc > 0) {
533 FD_SET(p, &obits);
534 } else {
535 FD_SET(f, &ibits);
536 }
537 if (!SYNCHing) {
538 FD_SET(f, &xbits);
539 }
31004941 540 FD_SET(p, &xbits);
5d78ef73
GM
541 if ((c = select(16, &ibits, &obits, &xbits,
542 (struct timeval *)0)) < 1) {
543 if (c == -1) {
544 if (errno == EINTR) {
545 continue;
546 }
547 }
66b878f6
BJ
548 sleep(5);
549 continue;
550 }
551
5d78ef73
GM
552 /*
553 * Any urgent data?
554 */
555 if (FD_ISSET(net, &xbits)) {
556 SYNCHing = 1;
557 }
558
66b878f6
BJ
559 /*
560 * Something to read from the network...
561 */
5d78ef73 562 if (FD_ISSET(net, &ibits)) {
affdaa4e 563#if !defined(SO_OOBINLINE)
5d78ef73 564 /*
5eddff6d 565 * In 4.2 (and 4.3 beta) systems, the
5d78ef73
GM
566 * OOB indication and data handling in the kernel
567 * is such that if two separate TCP Urgent requests
568 * come in, one byte of TCP data will be overlaid.
569 * This is fatal for Telnet, but we try to live
570 * with it.
571 *
572 * In addition, in 4.2 (and...), a special protocol
573 * is needed to pick up the TCP Urgent data in
574 * the correct sequence.
575 *
576 * What we do is: if we think we are in urgent
577 * mode, we look to see if we are "at the mark".
578 * If we are, we do an OOB receive. If we run
579 * this twice, we will do the OOB receive twice,
580 * but the second will fail, since the second
581 * time we were "at the mark", but there wasn't
582 * any data there (the kernel doesn't reset
583 * "at the mark" until we do a normal read).
584 * Once we've read the OOB data, we go ahead
585 * and do normal reads.
586 *
587 * There is also another problem, which is that
588 * since the OOB byte we read doesn't put us
589 * out of OOB state, and since that byte is most
590 * likely the TELNET DM (data mark), we would
591 * stay in the TELNET SYNCH (SYNCHing) state.
592 * So, clocks to the rescue. If we've "just"
593 * received a DM, then we test for the
594 * presence of OOB data when the receive OOB
595 * fails (and AFTER we did the normal mode read
596 * to clear "at the mark").
597 */
598 if (SYNCHing) {
599 int atmark;
600
601 ioctl(net, SIOCATMARK, (char *)&atmark);
602 if (atmark) {
603 ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
604 if ((ncc == -1) && (errno == EINVAL)) {
605 ncc = read(net, netibuf, sizeof (netibuf));
d8b5e42c 606 if (sequenceIs(didnetreceive, gotDM)) {
5d78ef73
GM
607 SYNCHing = stilloob(net);
608 }
609 }
610 } else {
611 ncc = read(net, netibuf, sizeof (netibuf));
66b878f6 612 }
5d78ef73
GM
613 } else {
614 ncc = read(net, netibuf, sizeof (netibuf));
615 }
616 settimer(didnetreceive);
affdaa4e 617#else /* !defined(SO_OOBINLINE)) */
5d78ef73 618 ncc = read(net, netibuf, sizeof (netibuf));
affdaa4e 619#endif /* !defined(SO_OOBINLINE)) */
5d78ef73
GM
620 if (ncc < 0 && errno == EWOULDBLOCK)
621 ncc = 0;
622 else {
623 if (ncc <= 0) {
624 break;
625 }
626 netip = netibuf;
627 }
66b878f6
BJ
628 }
629
630 /*
631 * Something to read from the pty...
632 */
31004941 633 if (FD_ISSET(p, &ibits) || FD_ISSET(p, &xbits)) {
66b878f6
BJ
634 pcc = read(p, ptyibuf, BUFSIZ);
635 if (pcc < 0 && errno == EWOULDBLOCK)
636 pcc = 0;
637 else {
638 if (pcc <= 0)
639 break;
31004941
GM
640 if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
641 netclear(); /* clear buffer back */
642 *nfrontp++ = IAC;
643 *nfrontp++ = DM;
644 neturg = nfrontp-1; /* off by one XXX */
645 }
646 if (hisopts[TELOPT_LFLOW] &&
647 (ptyibuf[0] &
648 (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
649 sprintf(nfrontp,"%c%c%c%c%c%c",
650 IAC, SB, TELOPT_LFLOW,
651 ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
652 IAC, SE);
653 nfrontp += 6;
654 }
4701a1c1
GM
655 pcc--;
656 ptyip = ptyibuf+1;
31004941 657 }
4701a1c1 658 }
66b878f6
BJ
659
660 while (pcc > 0) {
661 if ((&netobuf[BUFSIZ] - nfrontp) < 2)
662 break;
663 c = *ptyip++ & 0377, pcc--;
664 if (c == IAC)
665 *nfrontp++ = c;
666 *nfrontp++ = c;
bdb993e7
KB
667 /* Don't do CR-NUL if we are in binary mode */
668 if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
9f515693
GM
669 if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
670 *nfrontp++ = *ptyip++ & 0377;
671 pcc--;
672 } else
673 *nfrontp++ = '\0';
674 }
66b878f6 675 }
5d78ef73 676 if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
66b878f6
BJ
677 netflush();
678 if (ncc > 0)
679 telrcv();
5d78ef73 680 if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
66b878f6
BJ
681 ptyflush();
682 }
683 cleanup();
684}
685
686/*
687 * State for recv fsm
688 */
689#define TS_DATA 0 /* base state */
690#define TS_IAC 1 /* look for double IAC's */
691#define TS_CR 2 /* CR-LF ->'s CR */
affdaa4e
GM
692#define TS_SB 3 /* throw away begin's... */
693#define TS_SE 4 /* ...end's (suboption negotiation) */
66b878f6
BJ
694#define TS_WILL 5 /* will option negotiation */
695#define TS_WONT 6 /* wont " */
696#define TS_DO 7 /* do " */
697#define TS_DONT 8 /* dont " */
698
699telrcv()
700{
701 register int c;
702 static int state = TS_DATA;
66b878f6
BJ
703
704 while (ncc > 0) {
705 if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
706 return;
707 c = *netip++ & 0377, ncc--;
708 switch (state) {
709
8356bfad
GM
710 case TS_CR:
711 state = TS_DATA;
07f70d31 712 /* Strip off \n or \0 after a \r */
a6d8450f 713 if ((c == 0) || (c == '\n')) {
8356bfad 714 break;
a6d8450f 715 }
8356bfad
GM
716 /* FALL THROUGH */
717
66b878f6
BJ
718 case TS_DATA:
719 if (c == IAC) {
720 state = TS_IAC;
721 break;
722 }
723 if (inter > 0)
724 break;
9f515693 725 /*
07f70d31
GM
726 * We now map \r\n ==> \r for pragmatic reasons.
727 * Many client implementations send \r\n when
728 * the user hits the CarriageReturn key.
729 *
730 * We USED to map \r\n ==> \n, since \r\n says
9f515693
GM
731 * that we want to be in column 1 of the next
732 * printable line, and \n is the standard
733 * unix way of saying that (\r is only good
734 * if CRMOD is set, which it normally is).
735 */
bdb993e7 736 if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
07f70d31 737 state = TS_CR;
a6d8450f
GM
738 }
739 *pfrontp++ = c;
66b878f6
BJ
740 break;
741
742 case TS_IAC:
743 switch (c) {
744
745 /*
746 * Send the process on the pty side an
747 * interrupt. Do this with a NULL or
748 * interrupt char; depending on the tty mode.
749 */
66b878f6
BJ
750 case IP:
751 interrupt();
752 break;
753
a65453b7
GM
754 case BREAK:
755 sendbrk();
756 break;
757
66b878f6
BJ
758 /*
759 * Are You There?
760 */
761 case AYT:
1a33b848
SL
762 strcpy(nfrontp, "\r\n[Yes]\r\n");
763 nfrontp += 9;
66b878f6
BJ
764 break;
765
5d78ef73
GM
766 /*
767 * Abort Output
768 */
769 case AO: {
770 struct ltchars tmpltc;
771
772 ptyflush(); /* half-hearted */
773 ioctl(pty, TIOCGLTC, &tmpltc);
774 if (tmpltc.t_flushc != '\377') {
775 *pfrontp++ = tmpltc.t_flushc;
776 }
615dc3cd 777 netclear(); /* clear buffer back */
5d78ef73
GM
778 *nfrontp++ = IAC;
779 *nfrontp++ = DM;
c77c3bec 780 neturg = nfrontp-1; /* off by one XXX */
5d78ef73
GM
781 break;
782 }
783
66b878f6
BJ
784 /*
785 * Erase Character and
786 * Erase Line
787 */
788 case EC:
5d78ef73
GM
789 case EL: {
790 struct sgttyb b;
791 char ch;
792
793 ptyflush(); /* half-hearted */
794 ioctl(pty, TIOCGETP, &b);
795 ch = (c == EC) ?
796 b.sg_erase : b.sg_kill;
797 if (ch != '\377') {
798 *pfrontp++ = ch;
799 }
800 break;
801 }
66b878f6
BJ
802
803 /*
804 * Check for urgent data...
805 */
806 case DM:
5d78ef73
GM
807 SYNCHing = stilloob(net);
808 settimer(gotDM);
66b878f6
BJ
809 break;
810
5d78ef73 811
66b878f6
BJ
812 /*
813 * Begin option subnegotiation...
814 */
815 case SB:
affdaa4e 816 state = TS_SB;
31004941 817 SB_CLEAR();
66b878f6
BJ
818 continue;
819
820 case WILL:
0e376915
GM
821 state = TS_WILL;
822 continue;
823
66b878f6 824 case WONT:
0e376915
GM
825 state = TS_WONT;
826 continue;
827
66b878f6 828 case DO:
0e376915
GM
829 state = TS_DO;
830 continue;
831
66b878f6 832 case DONT:
0e376915 833 state = TS_DONT;
66b878f6
BJ
834 continue;
835
836 case IAC:
837 *pfrontp++ = c;
838 break;
839 }
840 state = TS_DATA;
841 break;
842
affdaa4e
GM
843 case TS_SB:
844 if (c == IAC) {
845 state = TS_SE;
846 } else {
847 SB_ACCUM(c);
848 }
66b878f6
BJ
849 break;
850
affdaa4e
GM
851 case TS_SE:
852 if (c != SE) {
853 if (c != IAC) {
854 SB_ACCUM(IAC);
855 }
856 SB_ACCUM(c);
857 state = TS_SB;
858 } else {
859 SB_TERM();
860 suboption(); /* handle sub-option */
861 state = TS_DATA;
862 }
66b878f6
BJ
863 break;
864
865 case TS_WILL:
d8b5e42c 866 if (hisopts[c] != OPT_YES)
66b878f6
BJ
867 willoption(c);
868 state = TS_DATA;
31004941
GM
869 if (c == TELOPT_TSPEED)
870 getterminalspeed();
66b878f6
BJ
871 continue;
872
873 case TS_WONT:
d8b5e42c 874 if (hisopts[c] != OPT_NO)
66b878f6
BJ
875 wontoption(c);
876 state = TS_DATA;
877 continue;
878
879 case TS_DO:
d8b5e42c 880 if (myopts[c] != OPT_YES)
66b878f6
BJ
881 dooption(c);
882 state = TS_DATA;
883 continue;
884
885 case TS_DONT:
d8b5e42c 886 if (myopts[c] != OPT_NO) {
affdaa4e 887 dontoption(c);
66b878f6
BJ
888 }
889 state = TS_DATA;
890 continue;
891
892 default:
5eddff6d 893 syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
de3b21e8 894 printf("telnetd: panic state=%d\n", state);
66b878f6
BJ
895 exit(1);
896 }
897 }
898}
899
900willoption(option)
901 int option;
902{
903 char *fmt;
904
905 switch (option) {
906
907 case TELOPT_BINARY:
908 mode(RAW, 0);
0e376915
GM
909 fmt = doopt;
910 break;
66b878f6
BJ
911
912 case TELOPT_ECHO:
affdaa4e
GM
913 not42 = 0; /* looks like a 4.2 system */
914 /*
915 * Now, in a 4.2 system, to break them out of ECHOing
916 * (to the terminal) mode, we need to send a "WILL ECHO".
917 * Kludge upon kludge!
918 */
d8b5e42c 919 if (myopts[TELOPT_ECHO] == OPT_YES) {
affdaa4e
GM
920 dooption(TELOPT_ECHO);
921 }
922 fmt = dont;
0e376915 923 break;
66b878f6 924
affdaa4e 925 case TELOPT_TTYPE:
d8b5e42c
GM
926 settimer(ttypeopt);
927 if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) {
928 hisopts[TELOPT_TTYPE] = OPT_YES;
929 return;
930 }
931 fmt = doopt;
932 break;
933
31004941
GM
934 case TELOPT_NAWS:
935 case TELOPT_TSPEED:
936 case TELOPT_LFLOW:
66b878f6 937 case TELOPT_SGA:
66b878f6
BJ
938 fmt = doopt;
939 break;
940
941 case TELOPT_TM:
942 fmt = dont;
943 break;
944
945 default:
946 fmt = dont;
947 break;
948 }
0e376915 949 if (fmt == doopt) {
d8b5e42c 950 hisopts[option] = OPT_YES;
0e376915 951 } else {
d8b5e42c 952 hisopts[option] = OPT_NO;
0e376915 953 }
9bd38ba8 954 (void) sprintf(nfrontp, fmt, option);
da96b661 955 nfrontp += sizeof (dont) - 2;
66b878f6
BJ
956}
957
958wontoption(option)
959 int option;
960{
961 char *fmt;
962
963 switch (option) {
66b878f6 964 case TELOPT_ECHO:
affdaa4e 965 not42 = 1; /* doesn't seem to be a 4.2 system */
0e376915 966 break;
66b878f6
BJ
967
968 case TELOPT_BINARY:
969 mode(0, RAW);
66b878f6 970 break;
10a3b37e
GM
971
972 case TELOPT_TTYPE:
973 settimer(ttypeopt);
974 break;
66b878f6 975 }
10a3b37e 976
0e376915 977 fmt = dont;
d8b5e42c 978 hisopts[option] = OPT_NO;
9bd38ba8 979 (void) sprintf(nfrontp, fmt, option);
da96b661 980 nfrontp += sizeof (doopt) - 2;
66b878f6
BJ
981}
982
983dooption(option)
984 int option;
985{
986 char *fmt;
987
988 switch (option) {
989
990 case TELOPT_TM:
991 fmt = wont;
992 break;
993
994 case TELOPT_ECHO:
995 mode(ECHO|CRMOD, 0);
0e376915
GM
996 fmt = will;
997 break;
66b878f6
BJ
998
999 case TELOPT_BINARY:
1000 mode(RAW, 0);
0e376915
GM
1001 fmt = will;
1002 break;
66b878f6
BJ
1003
1004 case TELOPT_SGA:
66b878f6
BJ
1005 fmt = will;
1006 break;
1007
1008 default:
1009 fmt = wont;
1010 break;
1011 }
0e376915 1012 if (fmt == will) {
d8b5e42c 1013 myopts[option] = OPT_YES;
0e376915 1014 } else {
d8b5e42c 1015 myopts[option] = OPT_NO;
0e376915 1016 }
9bd38ba8 1017 (void) sprintf(nfrontp, fmt, option);
da96b661 1018 nfrontp += sizeof (doopt) - 2;
66b878f6
BJ
1019}
1020
affdaa4e
GM
1021
1022dontoption(option)
1023int option;
1024{
1025 char *fmt;
1026
1027 switch (option) {
1028 case TELOPT_ECHO: /* we should stop echoing */
38260ba2 1029 mode(0, ECHO);
affdaa4e
GM
1030 fmt = wont;
1031 break;
d8b5e42c 1032
affdaa4e
GM
1033 default:
1034 fmt = wont;
1035 break;
1036 }
d8b5e42c 1037
affdaa4e 1038 if (fmt = wont) {
d8b5e42c 1039 myopts[option] = OPT_NO;
affdaa4e 1040 } else {
d8b5e42c 1041 myopts[option] = OPT_YES;
affdaa4e 1042 }
9bd38ba8 1043 (void) sprintf(nfrontp, fmt, option);
affdaa4e
GM
1044 nfrontp += sizeof (wont) - 2;
1045}
1046
31004941
GM
1047char *ttyspeeds[] = {
1048 "0", "50", "75", "110", "134", "150", "200", "300",
1049 "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" };
1050#define NUMSPEEDS sizeof ttyspeeds/sizeof ttyspeeds[0]
1051
1052string2speed(s)
1053 char *s;
1054{
1055 int i;
1056
1057 for (i = 0; i < NUMSPEEDS; i++)
1058 if (strcmp(s, ttyspeeds[i]) == 0)
1059 return(i);
1060
1061 return(0);
1062}
1063
affdaa4e
GM
1064/*
1065 * suboption()
1066 *
1067 * Look at the sub-option buffer, and try to be helpful to the other
1068 * side.
1069 *
1070 * Currently we recognize:
1071 *
d8b5e42c 1072 * Terminal type is
31004941
GM
1073 * Terminal size
1074 * Terminal speed is
affdaa4e
GM
1075 */
1076
1077suboption()
1078{
d8b5e42c
GM
1079 switch (SB_GET()) {
1080 case TELOPT_TTYPE: { /* Yaaaay! */
1081 static char terminalname[5+41] = "TERM=";
1082
1083 settimer(ttypesubopt);
1084
1085 if (SB_GET() != TELQUAL_IS) {
1086 return; /* ??? XXX but, this is the most robust */
1087 }
1088
1089 terminaltype = terminalname+strlen(terminalname);
1090
1091 while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
1092 !SB_EOF()) {
1093 register int c;
1094
1095 c = SB_GET();
1096 if (isupper(c)) {
1097 c = tolower(c);
1098 }
1099 *terminaltype++ = c; /* accumulate name */
1100 }
1101 *terminaltype = 0;
1102 terminaltype = terminalname;
1103 break;
1104 }
31004941
GM
1105 case TELOPT_NAWS: {
1106 struct winsize win;
1107 char c;
1108
1109#define SB_GETCHAR(c) \
1110 { if ((c = SB_GET()) == IAC && SB_GET() != IAC) return; }
1111
1112 ioctl(pty, TIOCGWINSZ, &win);
1113 settimer(ttypesubopt);
1114
1115 syslog(LOG_INFO, "%x %x %x %x",
1116 subpointer[0],subpointer[1],subpointer[2],subpointer[3]);
1117 SB_GETCHAR(c);
1118 win.ws_col = c << 8;
1119 SB_GETCHAR(c);
1120 win.ws_col |= c;
1121 SB_GETCHAR(c);
1122 win.ws_row = c << 8;
1123 SB_GETCHAR(c);
1124 win.ws_row |= c;
1125 syslog(LOG_INFO, "col %d row %d", win.ws_col, win.ws_row);
1126 ioctl(pty, TIOCSWINSZ, &win);
1127 break;
1128 }
1129 case TELOPT_TSPEED: {
1130 char speeds[41],*cp=speeds;
1131 struct sgttyb b;
1132 int ispeed,ospeed;
1133 char *ispeeds,*ospeeds;
1134
1135 if (SB_GET() != TELQUAL_IS) {
1136 return; /* ??? XXX but, this is the most robust */
1137 }
d8b5e42c 1138
31004941
GM
1139 ispeeds = NULL;
1140 ospeeds = speeds;
1141 ispeed = 0;
1142 ospeed = 0;
1143 while ((cp < (speeds + sizeof speeds-1)) && !SB_EOF()) {
1144 register int c;
1145
1146 c = SB_GET();
1147 if (c == ',') {
1148 c = 0;
1149 ispeeds = cp+1;
1150 }
1151 *cp++ = c; /* accumulate name */
1152 }
1153 *cp = 0;
1154
1155 if (ispeeds)
1156 ispeed = string2speed(ispeeds);
1157 if (ospeeds)
1158 ospeed = string2speed(ospeeds);
1159
1160 if (ispeed && ospeed) {
1161 ioctl(pty, TIOCGETP, &b);
1162 b.sg_ospeed = ospeed;
1163 b.sg_ispeed = ispeed;
1164 ioctl(pty, TIOCSETP, &b);
1165 }
1166
1167 break;
1168 }
affdaa4e
GM
1169 default:
1170 ;
1171 }
1172}
1173
66b878f6
BJ
1174mode(on, off)
1175 int on, off;
1176{
1177 struct sgttyb b;
1178
1179 ptyflush();
1180 ioctl(pty, TIOCGETP, &b);
1181 b.sg_flags |= on;
1182 b.sg_flags &= ~off;
1183 ioctl(pty, TIOCSETP, &b);
1184}
1185
1186/*
1187 * Send interrupt to process on other side of pty.
1188 * If it is in raw mode, just write NULL;
1189 * otherwise, write intr char.
1190 */
1191interrupt()
1192{
1193 struct sgttyb b;
1194 struct tchars tchars;
1195
1196 ptyflush(); /* half-hearted */
1197 ioctl(pty, TIOCGETP, &b);
1198 if (b.sg_flags & RAW) {
1199 *pfrontp++ = '\0';
1200 return;
1201 }
1202 *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
1203 '\177' : tchars.t_intrc;
1204}
1205
a65453b7
GM
1206/*
1207 * Send quit to process on other side of pty.
1208 * If it is in raw mode, just write NULL;
1209 * otherwise, write quit char.
1210 */
1211sendbrk()
1212{
1213 struct sgttyb b;
1214 struct tchars tchars;
1215
1216 ptyflush(); /* half-hearted */
1217 ioctl(pty, TIOCGETP, &b);
1218 if (b.sg_flags & RAW) {
1219 *pfrontp++ = '\0';
1220 return;
1221 }
1222 *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
1223 '\034' : tchars.t_quitc;
1224}
1225
66b878f6
BJ
1226ptyflush()
1227{
1228 int n;
1229
1230 if ((n = pfrontp - pbackp) > 0)
1231 n = write(pty, pbackp, n);
9f005877
SL
1232 if (n < 0)
1233 return;
66b878f6
BJ
1234 pbackp += n;
1235 if (pbackp == pfrontp)
1236 pbackp = pfrontp = ptyobuf;
1237}
615dc3cd
GM
1238\f
1239/*
1240 * nextitem()
1241 *
1242 * Return the address of the next "item" in the TELNET data
1243 * stream. This will be the address of the next character if
1244 * the current address is a user data character, or it will
1245 * be the address of the character following the TELNET command
1246 * if the current address is a TELNET IAC ("I Am a Command")
1247 * character.
1248 */
66b878f6 1249
615dc3cd
GM
1250char *
1251nextitem(current)
1252char *current;
66b878f6 1253{
615dc3cd
GM
1254 if ((*current&0xff) != IAC) {
1255 return current+1;
1256 }
1257 switch (*(current+1)&0xff) {
1258 case DO:
1259 case DONT:
1260 case WILL:
1261 case WONT:
1262 return current+3;
1263 case SB: /* loop forever looking for the SE */
1264 {
1265 register char *look = current+2;
66b878f6 1266
615dc3cd
GM
1267 for (;;) {
1268 if ((*look++&0xff) == IAC) {
1269 if ((*look++&0xff) == SE) {
1270 return look;
1271 }
1272 }
1273 }
9f005877 1274 }
615dc3cd
GM
1275 default:
1276 return current+2;
1277 }
66b878f6 1278}
5d78ef73
GM
1279
1280
615dc3cd
GM
1281/*
1282 * netclear()
1283 *
1284 * We are about to do a TELNET SYNCH operation. Clear
1285 * the path to the network.
1286 *
1287 * Things are a bit tricky since we may have sent the first
1288 * byte or so of a previous TELNET command into the network.
1289 * So, we have to scan the network buffer from the beginning
1290 * until we are up to where we want to be.
1291 *
1292 * A side effect of what we do, just to keep things
1293 * simple, is to clear the urgent data pointer. The principal
1294 * caller should be setting the urgent data pointer AFTER calling
1295 * us in any case.
1296 */
1297
1298netclear()
1299{
1300 register char *thisitem, *next;
1301 char *good;
1302#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
1303 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
1304
1305 thisitem = netobuf;
1306
1307 while ((next = nextitem(thisitem)) <= nbackp) {
1308 thisitem = next;
1309 }
1310
1311 /* Now, thisitem is first before/at boundary. */
1312
1313 good = netobuf; /* where the good bytes go */
1314
1315 while (nfrontp > thisitem) {
1316 if (wewant(thisitem)) {
1317 int length;
1318
1319 next = thisitem;
1320 do {
1321 next = nextitem(next);
1322 } while (wewant(next) && (nfrontp > next));
1323 length = next-thisitem;
1324 bcopy(thisitem, good, length);
1325 good += length;
1326 thisitem = next;
1327 } else {
1328 thisitem = nextitem(thisitem);
1329 }
1330 }
1331
1332 nbackp = netobuf;
1333 nfrontp = good; /* next byte to be sent */
1334 neturg = 0;
1335}
1336\f
5d78ef73
GM
1337/*
1338 * netflush
1339 * Send as much data as possible to the network,
1340 * handling requests for urgent data.
1341 */
1342
1343
1344netflush()
1345{
1346 int n;
1347
1348 if ((n = nfrontp - nbackp) > 0) {
affdaa4e
GM
1349 /*
1350 * if no urgent data, or if the other side appears to be an
1351 * old 4.2 client (and thus unable to survive TCP urgent data),
1352 * write the entire buffer in non-OOB mode.
1353 */
1354 if ((neturg == 0) || (not42 == 0)) {
5d78ef73
GM
1355 n = write(net, nbackp, n); /* normal write */
1356 } else {
1357 n = neturg - nbackp;
1358 /*
1359 * In 4.2 (and 4.3) systems, there is some question about
1360 * what byte in a sendOOB operation is the "OOB" data.
1361 * To make ourselves compatible, we only send ONE byte
1362 * out of band, the one WE THINK should be OOB (though
1363 * we really have more the TCP philosophy of urgent data
1364 * rather than the Unix philosophy of OOB data).
1365 */
1366 if (n > 1) {
1367 n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */
1368 } else {
1369 n = send(net, nbackp, n, MSG_OOB); /* URGENT data */
1370 }
1371 }
1372 }
1373 if (n < 0) {
1374 if (errno == EWOULDBLOCK)
1375 return;
1376 /* should blow this guy away... */
1377 return;
1378 }
1379 nbackp += n;
1380 if (nbackp >= neturg) {
1381 neturg = 0;
1382 }
1383 if (nbackp == nfrontp) {
1384 nbackp = nfrontp = netobuf;
1385 }
1386}
66b878f6
BJ
1387
1388cleanup()
1389{
c847f2e1
KB
1390 char *p;
1391
1392 p = line + sizeof("/dev/") - 1;
1393 if (logout(p))
1394 logwtmp(p, "", "");
1395 (void)chmod(line, 0666);
1396 (void)chown(line, 0, 0);
1397 *p = 'p';
1398 (void)chmod(line, 0666);
1399 (void)chown(line, 0, 0);
ff24c640 1400 shutdown(net, 2);
66b878f6
BJ
1401 exit(1);
1402}
1403
d0a64c71
GM
1404char editedhost[32];
1405
1406edithost(pat, host)
1407 register char *pat;
1408 register char *host;
1409{
1410 register char *res = editedhost;
1411
1412 if (!pat)
1413 pat = "";
1414 while (*pat) {
1415 switch (*pat) {
1416
1417 case '#':
1418 if (*host)
1419 host++;
1420 break;
1421
1422 case '@':
1423 if (*host)
1424 *res++ = *host++;
1425 break;
1426
1427 default:
1428 *res++ = *pat;
1429 break;
1430
1431 }
1432 if (res == &editedhost[sizeof editedhost - 1]) {
1433 *res = '\0';
1434 return;
1435 }
1436 pat++;
1437 }
1438 if (*host)
1439 strncpy(res, host, sizeof editedhost - (res - editedhost) - 1);
1440 else
1441 *res = '\0';
1442 editedhost[sizeof editedhost - 1] = '\0';
1443}
1444
1445static char *putlocation;
1446
1447puts(s)
1448register char *s;
1449{
1450
1451 while (*s)
1452 putchr(*s++);
1453}
1454
1455putchr(cc)
1456{
1457 *putlocation++ = cc;
1458}
1459
312b6ead 1460putf(cp, where)
d0a64c71
GM
1461register char *cp;
1462char *where;
d0a64c71
GM
1463{
1464 char *slash;
1465 char datebuffer[60];
1466 extern char *rindex();
1467
1468 putlocation = where;
1469
1470 while (*cp) {
1471 if (*cp != '%') {
1472 putchr(*cp++);
1473 continue;
1474 }
1475 switch (*++cp) {
1476
1477 case 't':
1478 slash = rindex(line, '/');
1479 if (slash == (char *) 0)
1480 puts(line);
1481 else
1482 puts(&slash[1]);
1483 break;
1484
1485 case 'h':
1486 puts(editedhost);
1487 break;
1488
1489 case 'd':
1490 get_date(datebuffer);
1491 puts(datebuffer);
1492 break;
1493
1494 case '%':
1495 putchr('%');
1496 break;
1497 }
1498 cp++;
1499 }
1500}