xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: tio.c,v 1.2 2021/08/14 16:14:52 christos Exp $	*/
2 
3 /*
4    tio.c - timed io functions
5    This file is part of the nss-pam-ldapd library.
6 
7    Copyright (C) 2007-2014 Arthur de Jong
8 
9    This library is free software; you can redistribute it and/or
10    modify it under the terms of the GNU Lesser General Public
11    License as published by the Free Software Foundation; either
12    version 2.1 of the License, or (at your option) any later version.
13 
14    This library is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17    Lesser General Public License for more details.
18 
19    You should have received a copy of the GNU Lesser General Public
20    License along with this library; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22    02110-1301 USA
23 */
24 
25 #include <sys/cdefs.h>
26 __RCSID("$NetBSD: tio.c,v 1.2 2021/08/14 16:14:52 christos Exp $");
27 
28 #include "portable.h"
29 
30 #ifdef HAVE_STDINT_H
31 #include <stdint.h>
32 #endif /* HAVE_STDINT_H */
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <sys/time.h>
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <errno.h>
39 #include <string.h>
40 #include <signal.h>
41 #include <stdio.h>
42 #include <limits.h>
43 #include <poll.h>
44 #include <time.h>
45 
46 #include "tio.h"
47 
48 /* for platforms that don't have ETIME use ETIMEDOUT */
49 #ifndef ETIME
50 #define ETIME ETIMEDOUT
51 #endif /* ETIME */
52 
53 /* structure that holds a buffer
54    the buffer contains the data that is between the application and the
55    file descriptor that is used for efficient transfer
56    the buffer is built up as follows:
57    |.....********......|
58          ^start        ^size
59          ^--len--^           */
60 struct tio_buffer {
61   uint8_t *buffer;
62   size_t size;      /* the size of the buffer */
63   size_t maxsize;   /* the maximum size of the buffer */
64   size_t start;     /* the start of the data (before start is unused) */
65   size_t len;       /* size of the data (from the start) */
66 };
67 
68 /* structure that holds all the state for files */
69 struct tio_fileinfo {
70   int fd;
71   struct tio_buffer readbuffer;
72   struct tio_buffer writebuffer;
73   int readtimeout;
74   int writetimeout;
75   int read_resettable; /* whether the tio_reset() function can be called */
76 #ifdef DEBUG_TIO_STATS
77   /* this is used to collect statistics on the use of the streams
78      and can be used to tune the buffer sizes */
79   size_t byteswritten;
80   size_t bytesread;
81 #endif /* DEBUG_TIO_STATS */
82 };
83 
84 /* some older versions of Solaris don't provide CLOCK_MONOTONIC but do have
85    a CLOCK_HIGHRES that has the same properties we need */
86 #ifndef CLOCK_MONOTONIC
87 #ifdef CLOCK_HIGHRES
88 #define CLOCK_MONOTONIC CLOCK_HIGHRES
89 #endif /* CLOCK_HIGHRES */
90 #endif /* not CLOCK_MONOTONIC */
91 
92 /* update the timeout to the value that is remaining before the deadline
93    returns the number of milliseconds before the deadline (or a negative
94    value of the deadline has expired) */
tio_time_remaining(struct timespec * deadline,int timeout)95 static inline int tio_time_remaining(struct timespec *deadline, int timeout)
96 {
97   struct timespec tv;
98   /* if this is the first call, set the deadline and return the full time */
99   if ((deadline->tv_sec == 0) && (deadline->tv_nsec == 0))
100   {
101     if (clock_gettime(CLOCK_MONOTONIC, deadline) == 0)
102     {
103       deadline->tv_sec += timeout / 1000;
104       deadline->tv_nsec += (timeout % 1000) * 1000000;
105     }
106     return timeout;
107   }
108   /* get the current time (fall back to full time on error) */
109   if (clock_gettime(CLOCK_MONOTONIC, &tv))
110     return timeout;
111   /* calculate time remaining in milliseconds */
112   return (deadline->tv_sec - tv.tv_sec) * 1000 +
113          (deadline->tv_nsec - tv.tv_nsec) / 1000000;
114 }
115 
116 /* open a new TFILE based on the file descriptor */
tio_fdopen(int fd,int readtimeout,int writetimeout,size_t initreadsize,size_t maxreadsize,size_t initwritesize,size_t maxwritesize)117 TFILE *tio_fdopen(int fd, int readtimeout, int writetimeout,
118                   size_t initreadsize, size_t maxreadsize,
119                   size_t initwritesize, size_t maxwritesize)
120 {
121   struct tio_fileinfo *fp;
122   fp = (struct tio_fileinfo *)malloc(sizeof(struct tio_fileinfo));
123   if (fp == NULL)
124     return NULL;
125   fp->fd = fd;
126   /* initialize read buffer */
127   fp->readbuffer.buffer = (uint8_t *)malloc(initreadsize);
128   if (fp->readbuffer.buffer == NULL)
129   {
130     free(fp);
131     return NULL;
132   }
133   fp->readbuffer.size = initreadsize;
134   fp->readbuffer.maxsize = maxreadsize;
135   fp->readbuffer.start = 0;
136   fp->readbuffer.len = 0;
137   /* initialize write buffer */
138   fp->writebuffer.buffer = (uint8_t *)malloc(initwritesize);
139   if (fp->writebuffer.buffer == NULL)
140   {
141     free(fp->readbuffer.buffer);
142     free(fp);
143     return NULL;
144   }
145   fp->writebuffer.size = initwritesize;
146   fp->writebuffer.maxsize = maxwritesize;
147   fp->writebuffer.start = 0;
148   fp->writebuffer.len = 0;
149   /* initialize other attributes */
150   fp->readtimeout = readtimeout;
151   fp->writetimeout = writetimeout;
152   fp->read_resettable = 0;
153 #ifdef DEBUG_TIO_STATS
154   fp->byteswritten = 0;
155   fp->bytesread = 0;
156 #endif /* DEBUG_TIO_STATS */
157   return fp;
158 }
159 
160 /* wait for any activity on the specified file descriptor using
161    the specified deadline */
tio_wait(int fd,short events,int timeout,struct timespec * deadline)162 static int tio_wait(int fd, short events, int timeout,
163                     struct timespec *deadline)
164 {
165   int t;
166   struct pollfd fds[1];
167   int rv;
168   while (1)
169   {
170     fds[0].fd = fd;
171     fds[0].events = events;
172     /* figure out the time we need to wait */
173     if ((t = tio_time_remaining(deadline, timeout)) < 0)
174     {
175       errno = ETIME;
176       return -1;
177     }
178     /* sanity check for moving clock */
179     if (t > timeout)
180       t = timeout;
181     /* wait for activity */
182     rv = poll(fds, 1, t);
183     if (rv > 0)
184       return 0; /* we have activity */
185     else if (rv == 0)
186     {
187       /* no file descriptors were available within the specified time */
188       errno = ETIME;
189       return -1;
190     }
191     else if ((errno != EINTR) && (errno != EAGAIN))
192       /* some error occurred */
193       return -1;
194     /* we just try again on EINTR or EAGAIN */
195   }
196 }
197 
198 /* do a read on the file descriptor, returning the data in the buffer
199    if no data was read in the specified time an error is returned */
tio_read(TFILE * fp,void * buf,size_t count)200 int tio_read(TFILE *fp, void *buf, size_t count)
201 {
202   struct timespec deadline = {0, 0};
203   int rv;
204   uint8_t *tmp;
205   size_t newsz;
206   size_t len;
207   /* have a more convenient storage type for the buffer */
208   uint8_t *ptr = (uint8_t *)buf;
209   /* loop until we have returned all the needed data */
210   while (1)
211   {
212     /* check if we have enough data in the buffer */
213     if (fp->readbuffer.len >= count)
214     {
215       if (count > 0)
216       {
217         if (ptr != NULL)
218           memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start, count);
219         /* adjust buffer position */
220         fp->readbuffer.start += count;
221         fp->readbuffer.len -= count;
222       }
223       return 0;
224     }
225     /* empty what we have and continue from there */
226     if (fp->readbuffer.len > 0)
227     {
228       if (ptr != NULL)
229       {
230         memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start,
231                fp->readbuffer.len);
232         ptr += fp->readbuffer.len;
233       }
234       count -= fp->readbuffer.len;
235       fp->readbuffer.start += fp->readbuffer.len;
236       fp->readbuffer.len = 0;
237     }
238     /* after this point until the read fp->readbuffer.len is 0 */
239     if (!fp->read_resettable)
240     {
241       /* the stream is not resettable, re-use the buffer */
242       fp->readbuffer.start = 0;
243     }
244     else if (fp->readbuffer.start >= (fp->readbuffer.size - 4))
245     {
246       /* buffer is running empty, try to grow buffer */
247       if (fp->readbuffer.size < fp->readbuffer.maxsize)
248       {
249         newsz = fp->readbuffer.size * 2;
250         if (newsz > fp->readbuffer.maxsize)
251           newsz = fp->readbuffer.maxsize;
252         tmp = realloc(fp->readbuffer.buffer, newsz);
253         if (tmp != NULL)
254         {
255           fp->readbuffer.buffer = tmp;
256           fp->readbuffer.size = newsz;
257         }
258       }
259       /* if buffer still does not contain enough room, clear resettable */
260       if (fp->readbuffer.start >= (fp->readbuffer.size - 4))
261       {
262         fp->readbuffer.start = 0;
263         fp->read_resettable = 0;
264       }
265     }
266     /* wait until we have input */
267     if (tio_wait(fp->fd, POLLIN, fp->readtimeout, &deadline))
268       return -1;
269     /* read the input in the buffer */
270     len = fp->readbuffer.size - fp->readbuffer.start;
271 #ifdef SSIZE_MAX
272     if (len > SSIZE_MAX)
273       len = SSIZE_MAX;
274 #endif /* SSIZE_MAX */
275     rv = read(fp->fd, fp->readbuffer.buffer + fp->readbuffer.start, len);
276     /* check for errors */
277     if (rv == 0)
278     {
279       errno = ECONNRESET;
280       return -1;
281     }
282     else if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN))
283       return -1;        /* something went wrong with the read */
284     else if (rv > 0)
285       fp->readbuffer.len = rv;  /* skip the read part in the buffer */
286 #ifdef DEBUG_TIO_STATS
287     fp->bytesread += rv;
288 #endif /* DEBUG_TIO_STATS */
289   }
290 }
291 
292 /* Read and discard the specified number of bytes from the stream. */
tio_skip(TFILE * fp,size_t count)293 int tio_skip(TFILE *fp, size_t count)
294 {
295   return tio_read(fp, NULL, count);
296 }
297 
298 /* Read all available data from the stream and empty the read buffer. */
tio_skipall(TFILE * fp,int timeout)299 int tio_skipall(TFILE *fp, int timeout)
300 {
301   struct timespec deadline = {0, 0};
302   int rv;
303   size_t len;
304   /* clear the read buffer */
305   fp->readbuffer.start = 0;
306   fp->readbuffer.len = 0;
307   fp->read_resettable = 0;
308   /* read until we can't read no more */
309   len = fp->readbuffer.size;
310 #ifdef SSIZE_MAX
311   if (len > SSIZE_MAX)
312     len = SSIZE_MAX;
313 #endif /* SSIZE_MAX */
314   while (1)
315   {
316     /* wait until we have input */
317     if (tio_wait(fp->fd, POLLIN, timeout, &deadline))
318       return -1;
319     /* read data from the stream */
320     rv = read(fp->fd, fp->readbuffer.buffer, len);
321     if (rv == 0)
322       return 0; /* end-of-file */
323     if ((rv < 0) && (errno == EWOULDBLOCK))
324       return 0; /* we've ready everything we can without blocking */
325     if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN))
326       return -1; /* something went wrong with the read */
327   }
328 }
329 
330 /* the caller has assured us that we can write to the file descriptor
331    and we give it a shot */
tio_writebuf(TFILE * fp)332 static int tio_writebuf(TFILE *fp)
333 {
334   int rv;
335   /* write the buffer */
336 #ifdef MSG_NOSIGNAL
337   rv = send(fp->fd, fp->writebuffer.buffer + fp->writebuffer.start,
338             fp->writebuffer.len, MSG_NOSIGNAL);
339 #else /* not MSG_NOSIGNAL */
340   /* on platforms that cannot use send() with masked signals, we change the
341      signal mask and change it back after the write (note that there is a
342      race condition here) */
343   struct sigaction act, oldact;
344   /* set up sigaction */
345   memset(&act, 0, sizeof(struct sigaction));
346   act.sa_sigaction = NULL;
347   act.sa_handler = SIG_IGN;
348   sigemptyset(&act.sa_mask);
349   act.sa_flags = SA_RESTART;
350   /* ignore SIGPIPE */
351   if (sigaction(SIGPIPE, &act, &oldact) != 0)
352     return -1; /* error setting signal handler */
353   /* write the buffer */
354   rv = write(fp->fd, fp->writebuffer.buffer + fp->writebuffer.start,
355              fp->writebuffer.len);
356   /* restore the old handler for SIGPIPE */
357   if (sigaction(SIGPIPE, &oldact, NULL) != 0)
358     return -1; /* error restoring signal handler */
359 #endif
360   /* check for errors */
361   if ((rv == 0) || ((rv < 0) && (errno != EINTR) && (errno != EAGAIN)))
362     return -1; /* something went wrong with the write */
363   /* skip the written part in the buffer */
364   if (rv > 0)
365   {
366     fp->writebuffer.start += rv;
367     fp->writebuffer.len -= rv;
368 #ifdef DEBUG_TIO_STATS
369     fp->byteswritten += rv;
370 #endif /* DEBUG_TIO_STATS */
371     /* reset start if len is 0 */
372     if (fp->writebuffer.len == 0)
373       fp->writebuffer.start = 0;
374     /* move contents of the buffer to the front if it will save enough room */
375     if (fp->writebuffer.start >= (fp->writebuffer.size / 4))
376     {
377       memmove(fp->writebuffer.buffer,
378               fp->writebuffer.buffer + fp->writebuffer.start,
379               fp->writebuffer.len);
380       fp->writebuffer.start = 0;
381     }
382   }
383   return 0;
384 }
385 
386 /* write all the data in the buffer to the stream */
tio_flush(TFILE * fp)387 int tio_flush(TFILE *fp)
388 {
389   struct timespec deadline = {0, 0};
390   /* loop until we have written our buffer */
391   while (fp->writebuffer.len > 0)
392   {
393     /* wait until we can write */
394     if (tio_wait(fp->fd, POLLOUT, fp->writetimeout, &deadline))
395       return -1;
396     /* write one block */
397     if (tio_writebuf(fp))
398       return -1;
399   }
400   return 0;
401 }
402 
403 /* try a single write of data in the buffer if the file descriptor
404    will accept data */
tio_flush_nonblock(TFILE * fp)405 static int tio_flush_nonblock(TFILE *fp)
406 {
407   struct pollfd fds[1];
408   int rv;
409   /* see if we can write without blocking */
410   fds[0].fd = fp->fd;
411   fds[0].events = POLLOUT;
412   rv = poll(fds, 1, 0);
413   /* check if any file descriptors were ready (timeout) or we were
414      interrupted */
415   if ((rv == 0) || ((rv < 0) && ((errno == EINTR) || (errno == EAGAIN))))
416     return 0;
417   /* any other errors? */
418   if (rv < 0)
419     return -1;
420   /* so file descriptor will accept writes */
421   return tio_writebuf(fp);
422 }
423 
tio_write(TFILE * fp,const void * buf,size_t count)424 int tio_write(TFILE *fp, const void *buf, size_t count)
425 {
426   size_t fr;
427   uint8_t *tmp;
428   size_t newsz;
429   const uint8_t *ptr = (const uint8_t *)buf;
430   /* keep filling the buffer until we have buffered everything */
431   while (count > 0)
432   {
433     /* figure out free size in buffer */
434     fr = fp->writebuffer.size - (fp->writebuffer.start + fp->writebuffer.len);
435     if (count <= fr)
436     {
437       /* the data fits in the buffer */
438       memcpy(fp->writebuffer.buffer + fp->writebuffer.start +
439              fp->writebuffer.len, ptr, count);
440       fp->writebuffer.len += count;
441       return 0;
442     }
443     else if (fr > 0)
444     {
445       /* fill the buffer with data that will fit */
446       memcpy(fp->writebuffer.buffer + fp->writebuffer.start +
447              fp->writebuffer.len, ptr, fr);
448       fp->writebuffer.len += fr;
449       ptr += fr;
450       count -= fr;
451     }
452     /* try to flush some of the data that is in the buffer */
453     if (tio_flush_nonblock(fp))
454       return -1;
455     /* if we have room now, try again */
456     if (fp->writebuffer.size > (fp->writebuffer.start + fp->writebuffer.len))
457       continue;
458     /* try to grow the buffer */
459     if (fp->writebuffer.size < fp->writebuffer.maxsize)
460     {
461       newsz = fp->writebuffer.size * 2;
462       if (newsz > fp->writebuffer.maxsize)
463         newsz = fp->writebuffer.maxsize;
464       tmp = realloc(fp->writebuffer.buffer, newsz);
465       if (tmp != NULL)
466       {
467         fp->writebuffer.buffer = tmp;
468         fp->writebuffer.size = newsz;
469         continue; /* try again */
470       }
471     }
472     /* write the buffer to the stream */
473     if (tio_flush(fp))
474       return -1;
475   }
476   return 0;
477 }
478 
tio_close(TFILE * fp)479 int tio_close(TFILE *fp)
480 {
481   int retv;
482   /* write any buffered data */
483   retv = tio_flush(fp);
484 #ifdef DEBUG_TIO_STATS
485   /* dump statistics to stderr */
486   fprintf(stderr, "DEBUG_TIO_STATS READ=%d WRITTEN=%d\n", fp->bytesread,
487           fp->byteswritten);
488 #endif /* DEBUG_TIO_STATS */
489   /* close file descriptor */
490   if (close(fp->fd))
491     retv = -1;
492   /* free any allocated buffers */
493   memset(fp->readbuffer.buffer, 0, fp->readbuffer.size);
494   memset(fp->writebuffer.buffer, 0, fp->writebuffer.size);
495   free(fp->readbuffer.buffer);
496   free(fp->writebuffer.buffer);
497   /* free the tio struct itself */
498   free(fp);
499   /* return the result of the earlier operations */
500   return retv;
501 }
502 
tio_mark(TFILE * fp)503 void tio_mark(TFILE *fp)
504 {
505   /* move any data in the buffer to the start of the buffer */
506   if ((fp->readbuffer.start > 0) && (fp->readbuffer.len > 0))
507   {
508     memmove(fp->readbuffer.buffer,
509             fp->readbuffer.buffer + fp->readbuffer.start, fp->readbuffer.len);
510     fp->readbuffer.start = 0;
511   }
512   /* mark the stream as resettable */
513   fp->read_resettable = 1;
514 }
515 
tio_reset(TFILE * fp)516 int tio_reset(TFILE *fp)
517 {
518   /* check if the stream is (still) resettable */
519   if (!fp->read_resettable)
520     return -1;
521   /* reset the buffer */
522   fp->readbuffer.len += fp->readbuffer.start;
523   fp->readbuffer.start = 0;
524   return 0;
525 }
526