2005-08-15 11:54:31 +08:00
|
|
|
/* $Id: buffer.c,v 1.25 2005-08-15 03:54:31 rjkaes Exp $
|
2000-02-17 01:32:49 +08:00
|
|
|
*
|
|
|
|
* 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,
|
2002-05-24 12:45:32 +08:00
|
|
|
* by using this method we can increase the buffer size dynamically. However,
|
2000-02-17 01:32:49 +08:00
|
|
|
* 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. :)
|
|
|
|
*
|
2001-11-26 06:05:42 +08:00
|
|
|
* Copyright (C) 1999,2001 Robert James Kaes (rjkaes@users.sourceforge.net)
|
2000-02-17 01:32:49 +08:00
|
|
|
*
|
|
|
|
* 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"
|
2000-09-12 07:41:32 +08:00
|
|
|
|
2000-02-17 01:32:49 +08:00
|
|
|
#include "buffer.h"
|
2002-05-24 02:22:48 +08:00
|
|
|
#include "heap.h"
|
2000-09-12 07:41:32 +08:00
|
|
|
#include "log.h"
|
|
|
|
|
2001-11-05 23:23:05 +08:00
|
|
|
#define BUFFER_HEAD(x) (x)->head
|
|
|
|
#define BUFFER_TAIL(x) (x)->tail
|
2000-09-12 07:41:32 +08:00
|
|
|
|
|
|
|
struct bufline_s {
|
2005-08-15 11:54:31 +08:00
|
|
|
unsigned char *string; /* the actual string of data */
|
|
|
|
struct bufline_s *next; /* pointer to next in linked list */
|
|
|
|
size_t length; /* length of the string of data */
|
|
|
|
size_t pos; /* start sending from this offset */
|
2000-09-12 07:41:32 +08:00
|
|
|
};
|
|
|
|
|
2002-05-14 08:43:38 +08:00
|
|
|
/*
|
|
|
|
* The buffer structure points to the beginning and end of the buffer list
|
|
|
|
* (and includes the total size)
|
|
|
|
*/
|
|
|
|
struct buffer_s {
|
2005-08-15 11:54:31 +08:00
|
|
|
struct bufline_s *head; /* top of the buffer */
|
|
|
|
struct bufline_s *tail; /* bottom of the buffer */
|
|
|
|
size_t size; /* total size of the buffer */
|
2002-05-14 08:43:38 +08:00
|
|
|
};
|
|
|
|
|
2000-02-17 01:32:49 +08:00
|
|
|
/*
|
|
|
|
* Take a string of data and a length and make a new line which can be added
|
2001-11-26 06:05:42 +08:00
|
|
|
* 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.
|
2000-02-17 01:32:49 +08:00
|
|
|
*/
|
2001-11-22 08:31:10 +08:00
|
|
|
static struct bufline_s *
|
|
|
|
makenewline(unsigned char *data, size_t length)
|
2000-02-17 01:32:49 +08:00
|
|
|
{
|
2005-08-15 11:54:31 +08:00
|
|
|
struct bufline_s *newline;
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
assert(data != NULL);
|
|
|
|
assert(length > 0);
|
2001-05-24 02:01:23 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
if (!(newline = safemalloc(sizeof(struct bufline_s))))
|
|
|
|
return NULL;
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
if (!(newline->string = safemalloc(length))) {
|
|
|
|
safefree(newline);
|
|
|
|
return NULL;
|
|
|
|
}
|
2001-11-26 06:05:42 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
memcpy(newline->string, data, length);
|
2001-11-26 06:05:42 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
newline->next = NULL;
|
|
|
|
newline->length = length;
|
2001-11-05 23:23:05 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
/* Position our "read" pointer at the beginning of the data */
|
|
|
|
newline->pos = 0;
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
return newline;
|
2000-02-17 01:32:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free the allocated buffer line
|
|
|
|
*/
|
2001-11-22 08:31:10 +08:00
|
|
|
static void
|
|
|
|
free_line(struct bufline_s *line)
|
2000-02-17 01:32:49 +08:00
|
|
|
{
|
2005-08-15 11:54:31 +08:00
|
|
|
assert(line != NULL);
|
2001-05-24 02:01:23 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
if (!line)
|
|
|
|
return;
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
if (line->string)
|
|
|
|
safefree(line->string);
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
safefree(line);
|
2000-02-17 01:32:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2000-09-12 07:41:32 +08:00
|
|
|
* Create a new buffer
|
2000-02-17 01:32:49 +08:00
|
|
|
*/
|
2001-11-22 08:31:10 +08:00
|
|
|
struct buffer_s *
|
|
|
|
new_buffer(void)
|
2000-02-17 01:32:49 +08:00
|
|
|
{
|
2005-08-15 11:54:31 +08:00
|
|
|
struct buffer_s *buffptr;
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
if (!(buffptr = safemalloc(sizeof(struct buffer_s))))
|
|
|
|
return NULL;
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
buffptr->size = 0;
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
return buffptr;
|
2000-02-17 01:32:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2000-09-12 07:41:32 +08:00
|
|
|
* Delete all the lines in the buffer and the buffer itself
|
2000-02-17 01:32:49 +08:00
|
|
|
*/
|
2001-11-22 08:31:10 +08:00
|
|
|
void
|
|
|
|
delete_buffer(struct buffer_s *buffptr)
|
2000-02-17 01:32:49 +08:00
|
|
|
{
|
2005-08-15 11:54:31 +08:00
|
|
|
struct bufline_s *next;
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
assert(buffptr != NULL);
|
2001-05-24 02:01:23 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
while (BUFFER_HEAD(buffptr)) {
|
|
|
|
next = BUFFER_HEAD(buffptr)->next;
|
|
|
|
free_line(BUFFER_HEAD(buffptr));
|
|
|
|
BUFFER_HEAD(buffptr) = next;
|
|
|
|
}
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
safefree(buffptr);
|
2000-02-17 01:32:49 +08:00
|
|
|
}
|
|
|
|
|
2002-05-14 08:43:38 +08:00
|
|
|
/*
|
|
|
|
* Return the current size of the buffer.
|
|
|
|
*/
|
2005-08-15 11:54:31 +08:00
|
|
|
size_t
|
|
|
|
buffer_size(struct buffer_s *buffptr)
|
2002-05-14 08:43:38 +08:00
|
|
|
{
|
2005-08-15 11:54:31 +08:00
|
|
|
return buffptr->size;
|
2002-05-14 08:43:38 +08:00
|
|
|
}
|
|
|
|
|
2000-02-17 01:32:49 +08:00
|
|
|
/*
|
2001-11-26 06:05:42 +08:00
|
|
|
* Push a new line on to the end of the buffer.
|
2000-02-17 01:32:49 +08:00
|
|
|
*/
|
2001-11-26 06:05:42 +08:00
|
|
|
int
|
2001-11-22 08:31:10 +08:00
|
|
|
add_to_buffer(struct buffer_s *buffptr, unsigned char *data, size_t length)
|
2000-02-17 01:32:49 +08:00
|
|
|
{
|
2005-08-15 11:54:31 +08:00
|
|
|
struct bufline_s *newline;
|
|
|
|
|
|
|
|
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(buffptr->size == 0);
|
|
|
|
else
|
|
|
|
assert(buffptr->size > 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make a new line so we can add it to the buffer.
|
|
|
|
*/
|
|
|
|
if (!(newline = makenewline(data, length)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (buffptr->size == 0)
|
|
|
|
BUFFER_HEAD(buffptr) = BUFFER_TAIL(buffptr) = newline;
|
|
|
|
else {
|
|
|
|
BUFFER_TAIL(buffptr)->next = newline;
|
|
|
|
BUFFER_TAIL(buffptr) = newline;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffptr->size += length;
|
|
|
|
|
|
|
|
return 0;
|
2000-02-17 01:32:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2001-11-05 23:23:05 +08:00
|
|
|
* Remove the first line from the top of the buffer
|
2000-02-17 01:32:49 +08:00
|
|
|
*/
|
2001-11-22 08:31:10 +08:00
|
|
|
static struct bufline_s *
|
|
|
|
remove_from_buffer(struct buffer_s *buffptr)
|
2000-02-17 01:32:49 +08:00
|
|
|
{
|
2005-08-15 11:54:31 +08:00
|
|
|
struct bufline_s *line;
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
assert(buffptr != NULL);
|
|
|
|
assert(BUFFER_HEAD(buffptr) != NULL);
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
line = BUFFER_HEAD(buffptr);
|
|
|
|
BUFFER_HEAD(buffptr) = line->next;
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
buffptr->size -= line->length;
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2005-08-15 11:54:31 +08:00
|
|
|
return line;
|
2000-02-17 01:32:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reads the bytes from the socket, and adds them to the buffer.
|
|
|
|
* Takes a connection and returns the number of bytes read.
|
|
|
|
*/
|
2001-10-25 12:39:10 +08:00
|
|
|
#define READ_BUFFER_SIZE (1024 * 2)
|
2001-11-22 08:31:10 +08:00
|
|
|
ssize_t
|
2001-11-26 06:05:42 +08:00
|
|
|
read_buffer(int fd, struct buffer_s * buffptr)
|
2000-02-17 01:32:49 +08:00
|
|
|
{
|
2005-08-15 11:54:31 +08:00
|
|
|
ssize_t bytesin;
|
|
|
|
unsigned char buffer[READ_BUFFER_SIZE];
|
|
|
|
|
|
|
|
assert(fd >= 0);
|
|
|
|
assert(buffptr != NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't allow the buffer to grow larger than MAXBUFFSIZE
|
|
|
|
*/
|
|
|
|
if (buffptr->size >= MAXBUFFSIZE)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
bytesin = read(fd, buffer, READ_BUFFER_SIZE);
|
|
|
|
|
|
|
|
if (bytesin > 0) {
|
|
|
|
if (add_to_buffer(buffptr, buffer, bytesin) < 0) {
|
|
|
|
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) {
|
2000-02-17 01:32:49 +08:00
|
|
|
#ifdef EWOULDBLOCK
|
2005-08-15 11:54:31 +08:00
|
|
|
case EWOULDBLOCK:
|
2000-02-17 01:32:49 +08:00
|
|
|
#else
|
|
|
|
# ifdef EAGAIN
|
2005-08-15 11:54:31 +08:00
|
|
|
case EAGAIN:
|
2000-02-17 01:32:49 +08:00
|
|
|
# endif
|
|
|
|
#endif
|
2005-08-15 11:54:31 +08:00
|
|
|
case EINTR:
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
log_message(LOG_ERR,
|
|
|
|
"readbuff: recv() error \"%s\" on file descriptor %d",
|
|
|
|
strerror(errno), fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2000-02-17 01:32:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
2001-11-26 06:05:42 +08:00
|
|
|
write_buffer(int fd, struct buffer_s * buffptr)
|
2000-02-17 01:32:49 +08:00
|
|
|
{
|
2005-08-15 11:54:31 +08:00
|
|
|
ssize_t bytessent;
|
|
|
|
struct bufline_s *line;
|
|
|
|
|
|
|
|
assert(fd >= 0);
|
|
|
|
assert(buffptr != NULL);
|
|
|
|
|
|
|
|
if (buffptr->size == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Sanity check. It would be bad to be using a NULL pointer! */
|
|
|
|
assert(BUFFER_HEAD(buffptr) != NULL);
|
|
|
|
line = BUFFER_HEAD(buffptr);
|
|
|
|
|
|
|
|
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) {
|
2000-02-17 01:32:49 +08:00
|
|
|
#ifdef EWOULDBLOCK
|
2005-08-15 11:54:31 +08:00
|
|
|
case EWOULDBLOCK:
|
2000-02-17 01:32:49 +08:00
|
|
|
#else
|
|
|
|
# ifdef EAGAIN
|
2005-08-15 11:54:31 +08:00
|
|
|
case EAGAIN:
|
2000-02-17 01:32:49 +08:00
|
|
|
# endif
|
|
|
|
#endif
|
2005-08-15 11:54:31 +08:00
|
|
|
case EINTR:
|
|
|
|
return 0;
|
|
|
|
case ENOBUFS:
|
|
|
|
case ENOMEM:
|
|
|
|
log_message(LOG_ERR,
|
|
|
|
"writebuff: write() error [NOBUFS/NOMEM] \"%s\" on file descriptor %d",
|
|
|
|
strerror(errno), fd);
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
log_message(LOG_ERR,
|
|
|
|
"writebuff: write() error \"%s\" on file descriptor %d",
|
|
|
|
strerror(errno), fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2000-02-17 01:32:49 +08:00
|
|
|
}
|