1 /*
2  * $Id: fcgi_buf.c,v 1.18 2003/02/03 23:07:37 robs Exp $
3  */
4 
5 #include "fcgi.h"
6 
7 #ifdef WIN32
8 #pragma warning( disable : 4127 )
9 #else
10 #ifdef APACHE2
11 #include <unistd.h>
12 #endif
13 #endif
14 
15 /*******************************************************************************
16  * Check buffer consistency with assertions.
17  */
18 #ifdef DEBUG
fcgi_buf_check(Buffer * buf)19 static void fcgi_buf_check(Buffer *buf)
20 {
21     ASSERT(buf->size > 0);
22     ASSERT(buf->length >= 0);
23     ASSERT(buf->length <= buf->size);
24 
25     ASSERT(buf->begin >= buf->data);
26     ASSERT(buf->begin < buf->data + buf->size);
27     ASSERT(buf->end >= buf->data);
28     ASSERT(buf->end < buf->data + buf->size);
29 
30     ASSERT(((buf->end - buf->begin + buf->size) % buf->size)
31             == (buf->length % buf->size));
32 }
33 #else
34 #define fcgi_buf_check(a) ((void) 0)
35 #endif
36 
37 /*******************************************************************************
38  * Reset buffer, losing any data that's in it.
39  */
fcgi_buf_reset(Buffer * buf)40 void fcgi_buf_reset(Buffer *buf)
41 {
42     buf->length = 0;
43     buf->begin = buf->end = buf->data;
44 }
45 
46 /*******************************************************************************
47  * Allocate and intialize a new buffer of the specified size.
48  */
fcgi_buf_new(pool * p,int size)49 Buffer *fcgi_buf_new(pool *p, int size)
50 {
51     Buffer *buf;
52 
53     buf = (Buffer *)ap_pcalloc(p, sizeof(Buffer) + size);
54     buf->size = size;
55     fcgi_buf_reset(buf);
56     return buf;
57 }
58 
fcgi_buf_removed(Buffer * const b,unsigned int len)59 void fcgi_buf_removed(Buffer * const b, unsigned int len)
60 {
61     b->length -= len;
62     b->begin += len;
63 
64     if (b->length == 0)
65     {
66         b->begin = b->end = b->data;
67     }
68     else if (b->begin >= b->data + b->size)
69     {
70         b->begin -= b->size;
71     }
72 }
73 
fcgi_buf_added(Buffer * const b,const unsigned int len)74 void fcgi_buf_added(Buffer * const b, const unsigned int len)
75 {
76     b->length += len;
77     b->end += len;
78 
79     if (b->end >= b->data + b->size)
80     {
81         b->end -= b->size;
82     }
83 }
84 
85 #ifdef WIN32
86 
socket_recv(SOCKET fd,char * buf,int len)87 static int socket_recv(SOCKET fd, char *buf, int len)
88 {
89     int bytes_read = recv(fd, buf, len, 0);
90 
91     if (bytes_read == SOCKET_ERROR)
92     {
93         return -1;
94     }
95     return bytes_read;
96 }
97 
socket_send(SOCKET fd,char * buf,int len)98 static int socket_send(SOCKET fd, char * buf, int len)
99 {
100     int bytes_sent = send(fd, buf, len, 0);
101 
102     if (bytes_sent == SOCKET_ERROR)
103     {
104         return -1;
105     }
106     return bytes_sent;
107 }
108 
109 #else /* !WIN32 */
110 
socket_recv(int fd,char * buf,int len)111 static int socket_recv(int fd, char * buf, int len)
112 {
113     int bytes_read;
114 
115     do {
116         bytes_read = read(fd, buf, len);
117 
118         if (bytes_read < 0)
119         {
120 #ifdef EWOULDBLOCK
121             ASSERT(errno != EWOULDBLOCK);
122 #endif
123 #ifdef EAGAIN
124             ASSERT(errno != EAGAIN);
125 #endif
126         }
127     } while (bytes_read == -1 && errno == EINTR);
128 
129     return bytes_read;
130 }
131 
socket_send(int fd,char * buf,int len)132 static int socket_send(int fd, char * buf, int len)
133 {
134     int bytes_sent;
135 
136     do {
137         bytes_sent = write(fd, buf, len);
138 
139         if (bytes_sent < 0)
140         {
141 #ifdef EWOULDBLOCK
142             ASSERT(errno != EWOULDBLOCK);
143 #endif
144 #ifdef EAGAIN
145             ASSERT(errno != EAGAIN);
146 #endif
147         }
148     }
149     while (bytes_sent == -1 && errno == EINTR);
150 
151     return bytes_sent;
152 }
153 
154 #endif /* !WIN32 */
155 
156 /*******************************************************************************
157  * Read from an open file descriptor into buffer.
158  *
159  * The caller should disable the default Apache SIGPIPE handler,
160  * otherwise a bad script could cause the request to abort and appear
161  * as though the client's fd caused it.
162  *
163  * Results:
164  *      <0 error, errno is set
165  *      =0 EOF reached
166  *      >0 successful read or no room in buffer (NOT # of bytes read)
167  */
fcgi_buf_socket_recv(Buffer * buf,SOCKET fd)168 int fcgi_buf_socket_recv(Buffer *buf, SOCKET fd)
169 {
170     int len;
171 
172     fcgi_buf_check(buf);
173 
174     if (buf->length == buf->size)
175         /* there's no room in the buffer, return "success" */
176         return 1;
177 
178     if (buf->length == 0)
179         /* the buffer is empty so defrag */
180         buf->begin = buf->end = buf->data;
181 
182     len = min(buf->size - buf->length, buf->data + buf->size - buf->end);
183 
184 #ifndef NO_WRITEV
185 
186     /* assume there is a readv() since there is a writev() */
187     if (len == buf->size - buf->length)
188     {
189 #endif
190 
191         len = socket_recv(fd, buf->end, len);
192 
193 #ifndef NO_WRITEV
194     }
195     else
196     {
197         /* the buffer is wrapped, use readv() */
198         struct iovec vec[2];
199 
200         vec[0].iov_base = buf->end;
201         vec[0].iov_len = len;
202         vec[1].iov_base = buf->data;
203         vec[1].iov_len = buf->size - buf->length - len;
204 
205         ASSERT(len);
206         ASSERT(vec[1].iov_len);
207 
208         do
209         {
210             len = readv(fd, vec, 2);
211         }
212         while (len == -1 && errno == EINTR);
213     }
214 #endif
215 
216     if (len <= 0) return len;
217 
218     fcgi_buf_added(buf, len);
219 
220     return len;     /* this may not contain the number of bytes read */
221 }
222 
223 
224 /*******************************************************************************
225  * Write from the buffer to an open file descriptor.
226  *
227  * The caller should disable the default Apache SIGPIPE handler,
228  * otherwise a bad script could cause the request to abort appearing
229  * as though the client's fd caused it.
230  *
231  * Results:
232  *      <0 if an error occured (bytes may or may not have been written)
233  *      =0 if no bytes were written
234  *      >0 successful write
235  */
fcgi_buf_socket_send(Buffer * buf,SOCKET fd)236 int fcgi_buf_socket_send(Buffer *buf, SOCKET fd)
237 {
238     int len;
239 
240     fcgi_buf_check(buf);
241 
242     if (buf->length == 0)
243         return 0;
244 
245     len = min(buf->length, buf->data + buf->size - buf->begin);
246 
247 #ifndef NO_WRITEV
248     if (len == buf->length)
249     {
250 #endif
251 
252         len = socket_send(fd, buf->begin, len);
253 
254 #ifndef NO_WRITEV
255     }
256     else
257     {
258         struct iovec vec[2];
259 
260         vec[0].iov_base = buf->begin;
261         vec[0].iov_len = len;
262         vec[1].iov_base = buf->data;
263         vec[1].iov_len = buf->length - len;
264 
265         do
266         {
267             len = writev(fd, vec, 2);
268         }
269         while (len == -1 && errno == EINTR);
270     }
271 #endif
272 
273     if (len <= 0) return len;
274 
275     fcgi_buf_removed(buf, len);
276 
277     return len;
278 }
279 
280 /*******************************************************************************
281  * Return the data block start address and the length of the block.
282  */
fcgi_buf_get_block_info(Buffer * buf,char ** beginPtr,int * countPtr)283 void fcgi_buf_get_block_info(Buffer *buf, char **beginPtr, int *countPtr)
284 {
285     fcgi_buf_check(buf);
286 
287     *beginPtr = buf->begin;
288     *countPtr = min(buf->length, buf->data + buf->size - buf->begin);
289 }
290 
291 /*******************************************************************************
292  * Throw away bytes from buffer.
293  */
fcgi_buf_toss(Buffer * buf,int count)294 void fcgi_buf_toss(Buffer *buf, int count)
295 {
296     fcgi_buf_check(buf);
297     ASSERT(count >= 0);
298     ASSERT(count <= buf->length);
299 
300     buf->length -= count;
301     buf->begin += count;
302     if(buf->begin >= buf->data + buf->size) {
303         buf->begin -= buf->size;
304     }
305 }
306 
307 /*******************************************************************************
308  * Return the free data block start address and the length of the block.
309  */
fcgi_buf_get_free_block_info(Buffer * buf,char ** endPtr,int * countPtr)310 void fcgi_buf_get_free_block_info(Buffer *buf, char **endPtr, int *countPtr)
311 {
312     fcgi_buf_check(buf);
313 
314     *endPtr = buf->end;
315     *countPtr = min(buf->size - buf->length,
316                     buf->data + buf->size - buf->end);
317 }
318 
319 /*******************************************************************************
320  * Updates the buf to reflect recently added data.
321  */
fcgi_buf_add_update(Buffer * buf,int count)322 void fcgi_buf_add_update(Buffer *buf, int count)
323 {
324     fcgi_buf_check(buf);
325     ASSERT(count >= 0);
326     ASSERT(count <= BufferFree(buf));
327 
328     buf->length += count;
329     buf->end += count;
330     if(buf->end >= buf->data + buf->size) {
331         buf->end -= buf->size;
332     }
333 
334     fcgi_buf_check(buf);
335 }
336 
337 /*******************************************************************************
338  * Adds a block of data to a buffer, returning the number of bytes added.
339  */
fcgi_buf_add_block(Buffer * buf,char * data,int datalen)340 int fcgi_buf_add_block(Buffer *buf, char *data, int datalen)
341 {
342     char *end;
343     int copied = 0;     /* Number of bytes actually copied. */
344     int canCopy;        /* Number of bytes to copy in a given op. */
345 
346     ASSERT(data != NULL);
347     ASSERT(datalen >= 0);
348 
349     if(datalen == 0) {
350         return 0;
351     }
352 
353     ASSERT(datalen > 0);
354     fcgi_buf_check(buf);
355     end = buf->data + buf->size;
356 
357     /*
358      * Copy the first part of the data:  from here to the end of the
359      * buffer, or the end of the data, whichever comes first.
360      */
361     datalen = min(BufferFree(buf), datalen);
362     canCopy = min(datalen, end - buf->end);
363     memcpy(buf->end, data, canCopy);
364     buf->length += canCopy;
365     buf->end += canCopy;
366     copied += canCopy;
367     if (buf->end >= end) {
368         buf->end = buf->data;
369     }
370     datalen -= canCopy;
371 
372     /*
373      * If there's more to go, copy the second part starting from the
374      * beginning of the buffer.
375      */
376     if (datalen > 0) {
377         data += canCopy;
378         memcpy(buf->end, data, datalen);
379         buf->length += datalen;
380         buf->end += datalen;
381         copied += datalen;
382     }
383     return(copied);
384 }
385 
386 /*******************************************************************************
387  * Add a string to a buffer, returning the number of bytes added.
388  */
fcgi_buf_add_string(Buffer * buf,char * str)389 int fcgi_buf_add_string(Buffer *buf, char *str)
390 {
391     return fcgi_buf_add_block(buf, str, strlen(str));
392 }
393 
394 /*******************************************************************************
395  * Gets a data block from a buffer, returning the number of bytes copied.
396  */
fcgi_buf_get_to_block(Buffer * buf,char * data,int datalen)397 int fcgi_buf_get_to_block(Buffer *buf, char *data, int datalen)
398 {
399     char *end;
400     int copied = 0;                /* Number of bytes actually copied. */
401     int canCopy;                   /* Number of bytes to copy in a given op. */
402 
403     ASSERT(data != NULL);
404     ASSERT(datalen > 0);
405     fcgi_buf_check(buf);
406 
407     end = buf->data + buf->size;
408 
409     /*
410      * Copy the first part out of the buffer: from here to the end
411      * of the buffer, or all of the requested data.
412      */
413     canCopy = min(buf->length, datalen);
414     canCopy = min(canCopy, end - buf->begin);
415 
416     memcpy(data, buf->begin, canCopy);
417 
418     buf->length -= canCopy;
419     buf->begin += canCopy;
420     copied += canCopy;
421     if (buf->begin >= end) {
422         buf->begin = buf->data;
423     }
424 
425     /*
426      * If there's more to go, copy the second part starting from the
427      * beginning of the buffer.
428      */
429     if (copied < datalen && buf->length > 0) {
430         data += copied;
431         canCopy = min(buf->length, datalen - copied);
432 
433         memcpy(data, buf->begin, canCopy);
434 
435         buf->length -= canCopy;
436         buf->begin += canCopy;
437         copied += canCopy;
438     }
439 
440     fcgi_buf_check(buf);
441     return(copied);
442 }
443 
444 /*******************************************************************************
445  * Move 'len' bytes from 'src' buffer to 'dest' buffer.  There must be at
446  * least 'len' bytes available in the source buffer and space for 'len'
447  * bytes in the destination buffer.
448  */
fcgi_buf_get_to_buf(Buffer * dest,Buffer * src,int len)449 void fcgi_buf_get_to_buf(Buffer *dest, Buffer *src, int len)
450 {
451     char *dest_end, *src_begin;
452     int dest_len, src_len, move_len;
453 
454     ASSERT(len > 0);
455     ASSERT(BufferLength(src) >= len);
456     ASSERT(BufferFree(dest) >= len);
457 
458     fcgi_buf_check(src);
459     fcgi_buf_check(dest);
460 
461     for (;;) {
462         if (len == 0)
463             return;
464 
465         fcgi_buf_get_free_block_info(dest, &dest_end, &dest_len);
466         fcgi_buf_get_block_info(src, &src_begin, &src_len);
467 
468         move_len = min(dest_len, src_len);
469         move_len = min(move_len, len);
470 
471         if (move_len == 0)
472             return;
473 
474         memcpy(dest_end, src_begin, move_len);
475         fcgi_buf_toss(src, move_len);
476         fcgi_buf_add_update(dest, move_len);
477         len -= move_len;
478     }
479 }
480 
array_grow(array_header * arr,int n)481 static void array_grow(array_header *arr, int n)
482 {
483     if (n <= 0)
484         return;
485 
486     if (arr->nelts + n > arr->nalloc) {
487         char *new_elts;
488         int new_nalloc = (arr->nalloc <= 0) ? n : arr->nelts + n;
489 
490         new_elts = ap_pcalloc(arr->pool, arr->elt_size * new_nalloc);
491         memcpy(new_elts, arr->elts, arr->nelts * arr->elt_size);
492 
493         arr->elts = new_elts;
494         arr->nalloc = new_nalloc;
495     }
496 }
497 
array_cat_block(array_header * arr,void * block,int n)498 static void array_cat_block(array_header *arr, void *block, int n)
499 {
500     array_grow(arr, n);
501     memcpy(arr->elts + arr->nelts * arr->elt_size, block, n * arr->elt_size);
502     arr->nelts += n;
503 }
504 
505 /*----------------------------------------------------------------------
506  * Append "len" bytes from "buf" into "arr".  Apache arrays are used
507  * whenever the data being handled is binary (may contain null chars).
508  */
fcgi_buf_get_to_array(Buffer * buf,array_header * arr,int len)509 void fcgi_buf_get_to_array(Buffer *buf, array_header *arr, int len)
510 {
511     int len1 = min(buf->length, buf->data + buf->size - buf->begin);
512 
513     fcgi_buf_check(buf);
514     ASSERT(len > 0);
515     ASSERT(len <= BufferLength(buf));
516 
517     array_grow(arr, len);
518 
519     len1 = min(len1, len);
520     array_cat_block(arr, buf->begin, len1);
521 
522     if (len1 < len)
523         array_cat_block(arr, buf->data, len - len1);
524 
525     fcgi_buf_toss(buf, len);
526 }
527