tinyproxy/src/buffer.c

295 lines
6.5 KiB
C
Raw Normal View History

/* $Id: buffer.c,v 1.19 2001-12-19 20:41:28 rjkaes Exp $
*
* The buffer used in each connection is a linked list of lines. As the lines
* are read in and written out the buffer expands and contracts. Basically,
* by using this method we can increase the buffer size dynamicly. However,
* we have a hard limit of 64 KB for the size of the buffer. The buffer can be
* thought of as a queue were we act on both the head and tail. The various
* functions act on each end (the names are taken from what Perl uses to act on
* the ends of an array. :)
*
* Copyright (C) 1999,2001 Robert James Kaes (rjkaes@users.sourceforge.net)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include "tinyproxy.h"
#include "buffer.h"
#include "log.h"
#include "utils.h"
#define BUFFER_HEAD(x) (x)->head
#define BUFFER_TAIL(x) (x)->tail
struct bufline_s {
unsigned char *string; /* the actual string of data */
struct bufline_s *next; /* pointer to next in linked list */
2001-11-22 08:31:10 +08:00
size_t length; /* length of the string of data */
size_t pos; /* start sending from this offset */
};
/*
* Take a string of data and a length and make a new line which can be added
* to the buffer. The data IS copied, so make sure if you allocated your
* data buffer on the heap, delete it because you now have TWO copies.
*/
2001-11-22 08:31:10 +08:00
static struct bufline_s *
makenewline(unsigned char *data, size_t length)
{
struct bufline_s *newline;
2001-05-24 02:01:23 +08:00
assert(data != NULL);
assert(length > 0);
2001-05-24 02:01:23 +08:00
if (!(newline = safemalloc(sizeof(struct bufline_s))))
return NULL;
if (!(newline->string = safemalloc(length))) {
safefree(newline);
return NULL;
}
memcpy(newline->string, data, length);
newline->next = NULL;
newline->length = length;
/* Position our "read" pointer at the beginning of the data */
newline->pos = 0;
return newline;
}
/*
* Free the allocated buffer line
*/
2001-11-22 08:31:10 +08:00
static void
free_line(struct bufline_s *line)
{
2001-05-24 02:01:23 +08:00
assert(line != NULL);
if (!line)
return;
2001-09-11 12:38:23 +08:00
if (line->string)
safefree(line->string);
safefree(line);
}
/*
* Create a new buffer
*/
2001-11-22 08:31:10 +08:00
struct buffer_s *
new_buffer(void)
{
struct buffer_s *buffptr;
if (!(buffptr = safemalloc(sizeof(struct buffer_s))))
return NULL;
/*
* Since the buffer is initially empty, set the HEAD and TAIL
* pointers to NULL since they can't possibly point anywhere at the
* moment.
*/
BUFFER_HEAD(buffptr) = BUFFER_TAIL(buffptr) = NULL;
BUFFER_SIZE(buffptr) = 0;
return buffptr;
}
/*
* Delete all the lines in the buffer and the buffer itself
*/
2001-11-22 08:31:10 +08:00
void
delete_buffer(struct buffer_s *buffptr)
{
struct bufline_s *next;
2001-05-24 02:01:23 +08:00
assert(buffptr != NULL);
while (BUFFER_HEAD(buffptr)) {
next = BUFFER_HEAD(buffptr)->next;
free_line(BUFFER_HEAD(buffptr));
BUFFER_HEAD(buffptr) = next;
}
safefree(buffptr);
}
/*
* Push a new line on to the end of the buffer.
*/
int
2001-11-22 08:31:10 +08:00
add_to_buffer(struct buffer_s *buffptr, unsigned char *data, size_t length)
{
struct bufline_s *newline;
2001-05-24 02:01:23 +08:00
assert(buffptr != NULL);
assert(data != NULL);
assert(length > 0);
/*
* Sanity check here. A buffer with a non-NULL head pointer must
* have a size greater than zero, and vice-versa.
*/
if (BUFFER_HEAD(buffptr) == NULL)
assert(BUFFER_SIZE(buffptr) == 0);
else
assert(BUFFER_SIZE(buffptr) > 0);
2001-05-24 02:01:23 +08:00
/*
* Make a new line so we can add it to the buffer.
*/
if (!(newline = makenewline(data, length)))
return -1;
if (BUFFER_SIZE(buffptr) == 0)
BUFFER_HEAD(buffptr) = BUFFER_TAIL(buffptr) = newline;
else
BUFFER_TAIL(buffptr) = (BUFFER_TAIL(buffptr)->next = newline);
BUFFER_SIZE(buffptr) += length;
return 0;
}
/*
* Remove the first line from the top of the buffer
*/
2001-11-22 08:31:10 +08:00
static struct bufline_s *
remove_from_buffer(struct buffer_s *buffptr)
{
struct bufline_s *line;
2001-05-24 02:01:23 +08:00
assert(buffptr != NULL);
assert(BUFFER_HEAD(buffptr) != NULL);
line = BUFFER_HEAD(buffptr);
BUFFER_HEAD(buffptr) = line->next;
buffptr->size -= line->length;
return line;
}
/*
* Reads the bytes from the socket, and adds them to the buffer.
* Takes a connection and returns the number of bytes read.
*/
#define READ_BUFFER_SIZE (1024 * 2)
2001-11-22 08:31:10 +08:00
ssize_t
read_buffer(int fd, struct buffer_s * buffptr)
{
ssize_t bytesin;
unsigned char buffer[READ_BUFFER_SIZE];
2001-05-24 02:01:23 +08:00
assert(fd >= 0);
assert(buffptr != NULL);
/*
* Don't allow the buffer to grow larger than MAXBUFFSIZE
*/
if (BUFFER_SIZE(buffptr) >= MAXBUFFSIZE)
return 0;
bytesin = read(fd, buffer, READ_BUFFER_SIZE);
if (bytesin > 0) {
if (add_to_buffer(buffptr, buffer, bytesin) < 0) {
2001-11-22 08:31:10 +08:00
log_message(LOG_ERR,
"readbuff: add_to_buffer() error.");
return -1;
}
return bytesin;
} else {
if (bytesin == 0) {
/* connection was closed by client */
return -1;
} else {
switch (errno) {
#ifdef EWOULDBLOCK
case EWOULDBLOCK:
#else
# ifdef EAGAIN
case EAGAIN:
# endif
#endif
case EINTR:
return 0;
default:
2001-11-22 08:31:10 +08:00
log_message(LOG_ERR,
"readbuff: recv() error \"%s\" on file descriptor %d",
strerror(errno), fd);
return -1;
}
}
}
}
/*
* Write the bytes in the buffer to the socket.
* Takes a connection and returns the number of bytes written.
*/
2001-11-22 08:31:10 +08:00
ssize_t
write_buffer(int fd, struct buffer_s * buffptr)
{
ssize_t bytessent;
2001-05-24 02:01:23 +08:00
struct bufline_s *line;
assert(fd >= 0);
assert(buffptr != NULL);
if (BUFFER_SIZE(buffptr) == 0)
return 0;
/* Sanity check. It would be bad to be using a NULL pointer! */
assert(BUFFER_HEAD(buffptr) != NULL);
line = BUFFER_HEAD(buffptr);
2001-11-22 08:31:10 +08:00
bytessent =
send(fd, line->string + line->pos, line->length - line->pos, MSG_NOSIGNAL);
if (bytessent >= 0) {
/* bytes sent, adjust buffer */
line->pos += bytessent;
if (line->pos == line->length)
free_line(remove_from_buffer(buffptr));
return bytessent;
} else {
switch (errno) {
#ifdef EWOULDBLOCK
case EWOULDBLOCK:
#else
# ifdef EAGAIN
case EAGAIN:
# endif
#endif
case EINTR:
return 0;
case ENOBUFS:
case ENOMEM:
2001-11-22 08:31:10 +08:00
log_message(LOG_ERR,
"writebuff: write() error [NOBUFS/NOMEM] \"%s\" on file descriptor %d",
strerror(errno), fd);
return 0;
default:
2001-11-22 08:31:10 +08:00
log_message(LOG_ERR,
"writebuff: write() error \"%s\" on file descriptor %d",
strerror(errno), fd);
return -1;
}
}
}