1 /*
2 ** fdbuf.c - Buffering functions for file descriptors
3 **
4 ** Copyright (c) 1997-2000 Peter Eriksson <pen@lysator.liu.se>
5 **
6 ** This program is free software; you can redistribute it and/or
7 ** modify it as you wish - as long as you don't claim that you wrote
8 ** it.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 */
14 
15 #include "plib/config.h"
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <errno.h>
20 #include <stdarg.h>
21 #include <string.h>
22 
23 #ifdef HAVE_UNISTD_H
24 #include <unistd.h>
25 #endif
26 
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/ioctl.h>
30 
31 #ifdef HAVE_SYS_FILIO_H
32 #include <sys/filio.h>
33 #endif
34 
35 #include <arpa/telnet.h>
36 
37 
38 #include "plib/safeio.h"
39 #include "plib/safestr.h"
40 #include "plib/fdbuf.h"
41 #include "plib/aalloc.h"
42 
43 #define inline
44 
45 
46 /*
47 ** Setup a new buffer. Never fails.
48 */
49 FDBUF *
fd_create(int fd,int flags)50 fd_create(int fd,
51 	  int flags)
52 {
53     FDBUF *fdp;
54 
55 
56     A_NEW(fdp);
57 
58     pthread_mutex_init(&fdp->refcnt_lock, NULL);
59     fdp->refcnt = 1;
60 
61     fdp->flags = flags;
62     fdp->fd = fd;
63 
64     pthread_mutex_init(&fdp->in_lock, NULL);
65     fdp->ungetc = -1;
66 
67     fdp->in_start = 0;
68     fdp->in_end = 0;
69     fdp->inbufsize = FDBUF_INBUFSIZE;
70     fdp->inbuf = a_malloc(fdp->inbufsize, "FDBUF inbuf");
71 
72     pthread_mutex_init(&fdp->out_lock, NULL);
73     fdp->lastc = -1;
74 
75     fdp->outbuflen = 0;
76     fdp->outbufsize = FDBUF_OUTBUFSIZE;
77     fdp->outbuf = a_malloc(fdp->outbufsize, "FDBUF outbuf");
78 
79     return fdp;
80 }
81 
82 
83 /*
84 ** Free and destroy the buffer. Never fails.
85 */
86 void
fd_destroy(FDBUF * fdp)87 fd_destroy(FDBUF *fdp)
88 {
89     if (fdp == NULL)
90 	return;
91 
92     fd_flush(fdp);
93 
94     pthread_mutex_lock(&fdp->refcnt_lock);
95     fdp->refcnt--;
96     if (fdp->refcnt > 0)
97     {
98 	pthread_mutex_unlock(&fdp->refcnt_lock);
99 	return;
100     }
101 
102     a_free(fdp->outbuf);
103     a_free(fdp->inbuf);
104     a_free(fdp);
105 }
106 
107 
108 
109 /*
110 ** Load more data into the buffer
111 */
112 static inline int
_fd_fill(FDBUF * fdp)113 _fd_fill(FDBUF *fdp)
114 {
115     int maxlen, len, avail, err;
116 
117 
118     if (fdp->in_start == fdp->in_end)
119 	fdp->in_start = fdp->in_end = 0;
120 
121     /* Free space in the input buffer */
122     maxlen = fdp->inbufsize - fdp->in_end;
123     if (maxlen == 0)
124 	return 0;
125 
126     avail = 0;
127     err = ioctl(fdp->fd, FIONREAD, &avail);
128 
129     if (err < 0 || avail == 0)
130 	avail = 1;
131 
132     if (avail > maxlen)
133 	avail = maxlen;
134 
135     len = s_read(fdp->fd, fdp->inbuf+fdp->in_start, avail);
136 
137     if (len == 0)
138 	return -1;
139     else if (len < 0)
140     {
141 	return errno;
142     }
143 
144     fdp->in_end += len;
145     return 0;
146 }
147 
148 int
fd_fill(FDBUF * fdp)149 fd_fill(FDBUF *fdp)
150 {
151     int err;
152 
153     pthread_mutex_lock(&fdp->in_lock);
154     err = _fd_fill(fdp);
155     pthread_mutex_unlock(&fdp->in_lock);
156 
157     return err;
158 }
159 
160 
161 /*
162 ** Send any buffered data
163 */
164 static inline int
_fd_flush(FDBUF * fdp)165 _fd_flush(FDBUF *fdp)
166 {
167     int len, rest;
168 
169 
170     if (fdp->outbuflen == 0)
171 	return 0;
172 
173     len = s_write(fdp->fd, fdp->outbuf, fdp->outbuflen);
174     if (len < 0)
175 	return errno;
176 
177     if (len == 0)
178 	return 0;
179 
180     rest = fdp->outbuflen - len;
181     if (rest > 0)
182 	memcpy(fdp->outbuf, fdp->outbuf+len, rest);
183     fdp->outbuflen -= len;
184     return 0;
185 }
186 
187 
188 int
fd_flush(FDBUF * fdp)189 fd_flush(FDBUF *fdp)
190 {
191     int err;
192 
193 
194     pthread_mutex_lock(&fdp->out_lock);
195     err = _fd_flush(fdp);
196     pthread_mutex_unlock(&fdp->out_lock);
197 
198     return err;
199 }
200 
201 
202 void
fd_purge(FDBUF * fdp)203 fd_purge(FDBUF *fdp)
204 {
205     pthread_mutex_lock(&fdp->out_lock);
206     fdp->outbuflen = 0;
207     fdp->lastc = -1;
208     pthread_mutex_unlock(&fdp->out_lock);
209 }
210 
211 
212 /*
213 ** Send one character
214 */
215 static inline int
_fd_putc(FDBUF * fdp,int c)216 _fd_putc(FDBUF *fdp,
217 	 int c)
218 {
219     int err;
220 
221 
222     if ((fdp->flags & FDF_CRLF) && c == '\n')
223 	if ((err = _fd_putc(fdp, '\r')) != 0)
224 	    return err;
225 
226     if (fdp->outbuflen == fdp->outbufsize)
227     {
228 	_fd_flush(fdp);
229 
230 	if (fdp->outbuflen == fdp->outbufsize)
231 	    return EAGAIN;
232     }
233 
234     fdp->outbuf[fdp->outbuflen++] = c;
235     if ((fdp->flags & FDF_LINEBUF) && c == '\n')
236 	_fd_flush(fdp);
237 
238     return 0;
239 }
240 
241 int
fd_putc(FDBUF * fdp,int c)242 fd_putc(FDBUF *fdp,
243 	int c)
244 {
245     int err;
246 
247     pthread_mutex_lock(&fdp->out_lock);
248     err = _fd_putc(fdp, c);
249     pthread_mutex_unlock(&fdp->out_lock);
250 
251     return err;
252 }
253 
254 
255 /*
256 ** Send a string
257 */
258 int
fd_puts(FDBUF * fdp,const char * str)259 fd_puts(FDBUF *fdp,
260 	const char *str)
261 {
262     int err = 0;
263 
264 
265     pthread_mutex_lock(&fdp->out_lock);
266     while (*str && err == 0)
267 	err = _fd_putc(fdp, *str++);
268     pthread_mutex_unlock(&fdp->out_lock);
269 
270     return err;
271 }
272 
273 
274 
275 /*
276 ** Send a formatted string
277 */
278 int
fd_printf(FDBUF * fdp,const char * fmt,...)279 fd_printf(FDBUF *fdp,
280 	  const char *fmt,
281 	  ...)
282 {
283     va_list ap;
284     char buf[8192];
285     int ecode;
286 
287 
288     va_start(ap, fmt);
289     ecode = s_vsnprintf(buf, sizeof(buf), fmt, ap);
290     va_end(ap);
291 
292     if (ecode < 0 || ecode >= sizeof(buf))
293 	return EINVAL;
294 
295     return fd_puts(fdp, buf);
296 }
297 
298 
299 
300 /*
301 ** Unget a character
302 */
303 int
fd_ungetc(FDBUF * fdp,int c)304 fd_ungetc(FDBUF *fdp,
305 	  int c)
306 {
307     if (fdp->ungetc != -1)
308 	return ENOMEM;
309 
310     fdp->ungetc = c;
311     return 0;
312 }
313 
314 /*
315 ** Get a character from the buffer
316 */
317 static inline int
_fd_rgetc(FDBUF * fdp)318 _fd_rgetc(FDBUF *fdp)
319 {
320     if (fdp->in_start == fdp->in_end && _fd_fill(fdp))
321 	return -1;
322 
323     return fdp->inbuf[fdp->in_start++];
324 }
325 
326 int
fd_rgetc(FDBUF * fdp)327 fd_rgetc(FDBUF *fdp)
328 {
329     int c;
330 
331     pthread_mutex_lock(&fdp->in_lock);
332     c = _fd_rgetc(fdp);
333     pthread_mutex_unlock(&fdp->in_lock);
334     return c;
335 }
336 
337 
338 static inline int
_fd_getc(FDBUF * fdp)339 _fd_getc(FDBUF *fdp)
340 {
341     int c;
342 
343 
344     if (fdp->ungetc != -1)
345     {
346 	c = fdp->ungetc;
347 	fdp->ungetc = -1;
348 	return c;
349     }
350 
351   Again:
352     c = _fd_rgetc(fdp);
353     if (c == EOF)
354 	return EOF;
355 
356     /* Simple, basic TELNET protocol handling */
357     if ((fdp->flags & FDF_TELNET) && c == IAC)
358     {
359 	c = _fd_rgetc(fdp);
360 	switch (c)
361 	{
362 	  case EOF:
363 	    return EOF;
364 
365 	  case AO:
366 	    fd_purge(fdp);
367 	    goto Again;
368 
369 	  case AYT:
370 	    fd_puts(fdp, "\n[Yes]\n");
371 	    fd_flush(fdp);
372 	    goto Again;
373 
374 	  case NOP:
375 	    goto Again;
376 
377 	  case IP: /* Interrupt process */
378 	    /* XXX: What should we do? */
379 	    goto Again;
380 
381 	  case SYNCH: /* Synchronize */
382 	    return -2;
383 
384 	  case WILL:
385 	  case WONT:
386 	    c = _fd_rgetc(fdp);
387 	    if (c == EOF)
388 		return EOF;
389 	    pthread_mutex_lock(&fdp->out_lock);
390 	    _fd_putc(fdp, IAC);
391 	    _fd_putc(fdp, DONT);
392 	    _fd_putc(fdp, c);
393 	    _fd_flush(fdp);
394 	    pthread_mutex_unlock(&fdp->out_lock);
395 	    goto Again;
396 
397 	  case DO:
398 	  case DONT:
399 	    c = _fd_rgetc(fdp);
400 	    if (c == EOF)
401 		return EOF;
402 	    pthread_mutex_lock(&fdp->out_lock);
403 	    _fd_putc(fdp, IAC);
404 	    _fd_putc(fdp, WONT);
405 	    _fd_putc(fdp, c);
406 	    _fd_flush(fdp);
407 	    pthread_mutex_unlock(&fdp->out_lock);
408 	    goto Again;
409 
410 	  default:
411 	    goto Again;
412 	}
413     }
414 
415     if ((fdp->flags & FDF_CRLF) && fdp->lastc == '\r' && c == '\n')
416 	goto Again;
417 
418     fdp->lastc = c;
419     if ((fdp->flags & FDF_CRLF) && c == '\r')
420 	c = '\n';
421 
422     return c;
423 }
424 
425 
426 int
fd_getc(FDBUF * fdp)427 fd_getc(FDBUF *fdp)
428 {
429     int c;
430 
431 
432     pthread_mutex_lock(&fdp->in_lock);
433 
434     while ((c = _fd_getc(fdp)) == -2)
435 	;
436 
437     pthread_mutex_unlock(&fdp->in_lock);
438 
439     return c;
440 }
441 
442 
443 /*
444 ** Get a line from the buffer
445 */
446 int
fd_gets(FDBUF * fdp,char * buf,int bufsize)447 fd_gets(FDBUF *fdp,
448 	char *buf,
449 	int bufsize)
450 {
451     int c, i;
452 
453 
454     if (bufsize == 0 || !buf)
455 	return -1;
456 
457     pthread_mutex_lock(&fdp->in_lock);
458 
459     i = 0;
460     while ((c = _fd_getc(fdp)) != -1 &&
461 	   c != '\n' && c != '\r' &&
462 	   (i < (bufsize-1)))
463     {
464 	if (c == -2) /* TELNET SYNCH */
465 	    i = 0;
466 	else
467 	    buf[i++] = c;
468     }
469     buf[i] = '\0';
470 
471     pthread_mutex_unlock(&fdp->in_lock);
472 
473     if (i == 0 && c == -1)
474 	return -1;
475 
476     if (i == (bufsize-1) && !(c == '\n' || c == '\r' || c == -1))
477 	return -1;
478 
479     return 0;
480 }
481