xref: /original-bsd/libexec/telnetd/utility.c (revision da818fbb)
1 /*
2  * Copyright (c) 1989 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
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.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)utility.c	5.1 (Berkeley) 09/01/89";
20 #endif /* not lint */
21 
22 
23 #include "telnetd.h"
24 
25 /*
26  * utility functions performing io related tasks
27  */
28 
29 /*
30  * ttloop
31  *
32  *	A small subroutine to flush the network output buffer, get some data
33  * from the network, and pass it through the telnet state machine.  We
34  * also flush the pty input buffer (by dropping its data) if it becomes
35  * too full.
36  */
37 
38 void
39 ttloop()
40 {
41     void netflush();
42 
43     if (nfrontp-nbackp) {
44 	netflush();
45     }
46     ncc = read(net, netibuf, sizeof netibuf);
47     if (ncc < 0) {
48 	syslog(LOG_INFO, "ttloop:  read: %m\n");
49 	exit(1);
50     } else if (ncc == 0) {
51 	syslog(LOG_INFO, "ttloop:  peer died: %m\n");
52 	exit(1);
53     }
54     netip = netibuf;
55     telrcv();			/* state machine */
56     if (ncc > 0) {
57 	pfrontp = pbackp = ptyobuf;
58 	telrcv();
59     }
60 }  /* end of ttloop */
61 
62 /*
63  * Check a descriptor to see if out of band data exists on it.
64  */
65 stilloob(s)
66 int	s;		/* socket number */
67 {
68     static struct timeval timeout = { 0 };
69     fd_set	excepts;
70     int value;
71 
72     do {
73 	FD_ZERO(&excepts);
74 	FD_SET(s, &excepts);
75 	value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
76     } while ((value == -1) && (errno == EINTR));
77 
78     if (value < 0) {
79 	fatalperror(pty, "select");
80     }
81     if (FD_ISSET(s, &excepts)) {
82 	return 1;
83     } else {
84 	return 0;
85     }
86 }
87 
88 ptyflush()
89 {
90 	int n;
91 
92 	if ((n = pfrontp - pbackp) > 0)
93 		n = write(pty, pbackp, n);
94 	if (n < 0)
95 		return;
96 	pbackp += n;
97 	if (pbackp == pfrontp)
98 		pbackp = pfrontp = ptyobuf;
99 }
100 
101 /*
102  * nextitem()
103  *
104  *	Return the address of the next "item" in the TELNET data
105  * stream.  This will be the address of the next character if
106  * the current address is a user data character, or it will
107  * be the address of the character following the TELNET command
108  * if the current address is a TELNET IAC ("I Am a Command")
109  * character.
110  */
111 char *
112 nextitem(current)
113 char	*current;
114 {
115     if ((*current&0xff) != IAC) {
116 	return current+1;
117     }
118     switch (*(current+1)&0xff) {
119     case DO:
120     case DONT:
121     case WILL:
122     case WONT:
123 	return current+3;
124     case SB:		/* loop forever looking for the SE */
125 	{
126 	    register char *look = current+2;
127 
128 	    for (;;) {
129 		if ((*look++&0xff) == IAC) {
130 		    if ((*look++&0xff) == SE) {
131 			return look;
132 		    }
133 		}
134 	    }
135 	}
136     default:
137 	return current+2;
138     }
139 }  /* end of nextitem */
140 
141 
142 /*
143  * netclear()
144  *
145  *	We are about to do a TELNET SYNCH operation.  Clear
146  * the path to the network.
147  *
148  *	Things are a bit tricky since we may have sent the first
149  * byte or so of a previous TELNET command into the network.
150  * So, we have to scan the network buffer from the beginning
151  * until we are up to where we want to be.
152  *
153  *	A side effect of what we do, just to keep things
154  * simple, is to clear the urgent data pointer.  The principal
155  * caller should be setting the urgent data pointer AFTER calling
156  * us in any case.
157  */
158 netclear()
159 {
160     register char *thisitem, *next;
161     char *good;
162 #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
163 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
164 
165     thisitem = netobuf;
166 
167     while ((next = nextitem(thisitem)) <= nbackp) {
168 	thisitem = next;
169     }
170 
171     /* Now, thisitem is first before/at boundary. */
172 
173     good = netobuf;	/* where the good bytes go */
174 
175     while (nfrontp > thisitem) {
176 	if (wewant(thisitem)) {
177 	    int length;
178 
179 	    next = thisitem;
180 	    do {
181 		next = nextitem(next);
182 	    } while (wewant(next) && (nfrontp > next));
183 	    length = next-thisitem;
184 	    bcopy(thisitem, good, length);
185 	    good += length;
186 	    thisitem = next;
187 	} else {
188 	    thisitem = nextitem(thisitem);
189 	}
190     }
191 
192     nbackp = netobuf;
193     nfrontp = good;		/* next byte to be sent */
194     neturg = 0;
195 }  /* end of netclear */
196 
197 /*
198  *  netflush
199  *		Send as much data as possible to the network,
200  *	handling requests for urgent data.
201  */
202 void
203 netflush()
204 {
205     int n;
206     extern int not42;
207 
208     if ((n = nfrontp - nbackp) > 0) {
209 	/*
210 	 * if no urgent data, or if the other side appears to be an
211 	 * old 4.2 client (and thus unable to survive TCP urgent data),
212 	 * write the entire buffer in non-OOB mode.
213 	 */
214 	if ((neturg == 0) || (not42 == 0)) {
215 	    n = write(net, nbackp, n);	/* normal write */
216 	} else {
217 	    n = neturg - nbackp;
218 	    /*
219 	     * In 4.2 (and 4.3) systems, there is some question about
220 	     * what byte in a sendOOB operation is the "OOB" data.
221 	     * To make ourselves compatible, we only send ONE byte
222 	     * out of band, the one WE THINK should be OOB (though
223 	     * we really have more the TCP philosophy of urgent data
224 	     * rather than the Unix philosophy of OOB data).
225 	     */
226 	    if (n > 1) {
227 		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
228 	    } else {
229 		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
230 	    }
231 	}
232     }
233     if (n < 0) {
234 	if (errno == EWOULDBLOCK || errno == EINTR)
235 		return;
236 	cleanup();
237     }
238     nbackp += n;
239     if (nbackp >= neturg) {
240 	neturg = 0;
241     }
242     if (nbackp == nfrontp) {
243 	nbackp = nfrontp = netobuf;
244     }
245     return;
246 }  /* end of netflush */
247 
248 
249 /*
250  * writenet
251  *
252  * Just a handy little function to write a bit of raw data to the net.
253  * It will force a transmit of the buffer if necessary
254  *
255  * arguments
256  *    ptr - A pointer to a character string to write
257  *    len - How many bytes to write
258  */
259 writenet(ptr, len)
260 register char *ptr;
261 register int len;
262 {
263 	/* flush buffer if no room for new data) */
264 	if ((&netobuf[BUFSIZ] - nfrontp) < len) {
265 		/* if this fails, don't worry, buffer is a little big */
266 		netflush();
267 	}
268 
269 	bcopy(ptr, nfrontp, len);
270 	nfrontp += len;
271 
272 }  /* end of writenet */
273 
274 
275 /*
276  * miscellaneous functions doing a variety of little jobs follow ...
277  */
278 
279 
280 fatal(f, msg)
281 	int f;
282 	char *msg;
283 {
284 	char buf[BUFSIZ];
285 
286 	(void) sprintf(buf, "telnetd: %s.\r\n", msg);
287 	(void) write(f, buf, (int)strlen(buf));
288 	sleep(1);	/*XXX*/
289 	exit(1);
290 }
291 
292 fatalperror(f, msg)
293 	int f;
294 	char *msg;
295 {
296 	char buf[BUFSIZ];
297 	extern char *sys_errlist[];
298 
299 	(void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
300 	fatal(f, buf);
301 }
302 
303 char editedhost[32];
304 
305 edithost(pat, host)
306 	register char *pat;
307 	register char *host;
308 {
309 	register char *res = editedhost;
310 	char *strncpy();
311 
312 	if (!pat)
313 		pat = "";
314 	while (*pat) {
315 		switch (*pat) {
316 
317 		case '#':
318 			if (*host)
319 				host++;
320 			break;
321 
322 		case '@':
323 			if (*host)
324 				*res++ = *host++;
325 			break;
326 
327 		default:
328 			*res++ = *pat;
329 			break;
330 		}
331 		if (res == &editedhost[sizeof editedhost - 1]) {
332 			*res = '\0';
333 			return;
334 		}
335 		pat++;
336 	}
337 	if (*host)
338 		(void) strncpy(res, host,
339 				sizeof editedhost - (res - editedhost) -1);
340 	else
341 		*res = '\0';
342 	editedhost[sizeof editedhost - 1] = '\0';
343 }
344 
345 static char *putlocation;
346 
347 putstr(s)
348 register char *s;
349 {
350 
351 	while (*s)
352 		putchr(*s++);
353 }
354 
355 putchr(cc)
356 {
357 	*putlocation++ = cc;
358 }
359 
360 putf(cp, where)
361 register char *cp;
362 char *where;
363 {
364 	char *slash;
365 #ifndef	NO_GETTYTAB
366 	char datebuffer[60];
367 #endif	/* NO_GETTYTAB */
368 	extern char *rindex();
369 
370 	putlocation = where;
371 
372 	while (*cp) {
373 		if (*cp != '%') {
374 			putchr(*cp++);
375 			continue;
376 		}
377 		switch (*++cp) {
378 
379 		case 't':
380 			slash = rindex(line, '/');
381 			if (slash == (char *) 0)
382 				putstr(line);
383 			else
384 				putstr(&slash[1]);
385 			break;
386 
387 		case 'h':
388 			putstr(editedhost);
389 			break;
390 
391 #ifndef	NO_GETTYTAB
392 		case 'd':
393 			get_date(datebuffer);
394 			putstr(datebuffer);
395 			break;
396 #endif	/* NO_GETTYTAB */
397 
398 		case '%':
399 			putchr('%');
400 			break;
401 		}
402 		cp++;
403 	}
404 }
405 
406 /*ARGSUSED*/
407 #ifdef	NO_GETTYTAB
408 getent(cp, name)
409 char *cp, *name;
410 {
411 	return(0);
412 }
413 
414 /*ARGSUSED*/
415 char *
416 getstr(cp, cpp)
417 char *cp, **cpp;
418 {
419 	return(0);
420 }
421 #endif	/* NO_GETTYTAB */
422