tinyproxy/src/buffer.c

309 lines
6.4 KiB
C
Raw Normal View History

/* $Id: buffer.c,v 1.1.1.1 2000-02-16 17:32:22 sdyoung 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 Robert James Kaes (rjkaes@flarenet.com)
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include <defines.h>
#endif
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <assert.h>
#include "utils.h"
#include "log.h"
#include "tinyproxy.h"
#include "buffer.h"
/*
* Take a string of data and a length and make a new line which can be added
* to the buffer
*/
static struct bufline_s *makenewline(unsigned char *data, unsigned int length)
{
struct bufline_s *newline;
assert(data);
assert(length > 0);
if (!(newline = xmalloc(sizeof(struct bufline_s))))
return NULL;
newline->string = data;
newline->next = NULL;
newline->length = length;
newline->pos = 0;
return newline;
}
/*
* Create a new buffer
*/
struct buffer_s *new_buffer(void)
{
struct buffer_s *buffptr;
if (!(buffptr = xmalloc(sizeof(struct buffer_s))))
return NULL;
buffptr->head = buffptr->tail = NULL;
buffptr->size = 0;
return buffptr;
}
/*
* Delete all the lines in the buffer and the buffer itself
*/
void delete_buffer(struct buffer_s *buffptr)
{
struct bufline_s *next;
assert(buffptr);
while (buffer_head(buffptr)) {
next = buffer_head(buffptr)->next;
free_line(buffer_head(buffptr));
buffer_head(buffptr) = next;
}
buffer_head(buffptr) = NULL;
buffer_tail(buffptr) = NULL;
safefree(buffptr);
}
/*
* Free the allocated buffer line
*/
void free_line(struct bufline_s *line)
{
if (!line)
return;
if (line->string) {
safefree(line->string);
}
safefree(line);
}
/*
* Push a new line on to the end of the buffer
*/
struct bufline_s *push_buffer(struct buffer_s *buffptr, unsigned char *data,
unsigned int length)
{
struct bufline_s *newline;
assert(buffptr);
assert(data);
assert(length > 0);
if (!(newline = makenewline(data, length)))
return NULL;
if (!buffer_head(buffptr) && !buffer_tail(buffptr))
buffer_head(buffptr) = buffer_tail(buffptr) = newline;
else
buffer_tail(buffptr) = (buffer_tail(buffptr)->next = newline);
buffptr->size += length;
return newline;
}
/*
* Pop a buffer line off the end of the buffer
*/
struct bufline_s *pop_buffer(struct buffer_s *buffptr)
{
struct bufline_s *line, *newend;
assert(buffptr);
if (buffer_head(buffptr) == buffer_tail(buffptr)) {
line = buffer_head(buffptr);
buffer_head(buffptr) = buffer_tail(buffptr) = NULL;
buffptr->size = 0;
return line;
}
line = buffer_tail(buffptr);
newend = buffer_head(buffptr);
while (newend->next != line && newend->next)
newend = newend->next;
buffer_tail(buffptr) = newend;
buffptr->size -= line->length;
return line;
}
/*
* Unshift a buffer line from the top of the buffer (meaning add a new line
* to the top of the buffer)
*/
struct bufline_s *unshift_buffer(struct buffer_s *buffptr, unsigned char *data,
unsigned int length)
{
struct bufline_s *newline;
assert(buffptr);
assert(data);
assert(length > 0);
if (!(newline = makenewline(data, length)))
return NULL;
if (!buffer_head(buffptr) && buffer_tail(buffptr)) {
buffer_head(buffptr) = buffer_tail(buffptr) = newline;
} else {
newline->next = buffer_head(buffptr);
buffer_head(buffptr) = newline;
if (!buffer_tail(buffptr))
buffer_tail(buffptr) = newline;
}
buffptr->size += length;
return newline;
}
/*
* Shift a line off the top of the buffer (remove the line from the top of
* the buffer)
*/
struct bufline_s *shift_buffer(struct buffer_s *buffptr)
{
struct bufline_s *line;
assert(buffptr);
if (!buffer_head(buffptr) && !buffer_tail(buffptr)) {
line = buffer_head(buffptr);
buffer_head(buffptr) = buffer_tail(buffptr) = NULL;
buffptr->size = 0;
return line;
}
line = buffer_head(buffptr);
buffer_head(buffptr) = line->next;
if (!buffer_head(buffptr))
buffer_tail(buffptr) = NULL;
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.
*/
int readbuff(int fd, struct buffer_s *buffptr)
{
int bytesin;
unsigned char inbuf[BUFFER];
unsigned char *buffer;
assert(fd >= 0);
assert(buffptr);
bytesin = recv(fd, inbuf, BUFFER, 0);
if (bytesin > 0) {
if (!(buffer = xmalloc(bytesin)))
return 0;
memcpy(buffer, inbuf, bytesin);
push_buffer(buffptr, buffer, bytesin);
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;
case ECONNRESET:
return -1;
default:
log("ERROR readbuff: recv (%s)", strerror(errno));
return -1;
}
}
}
/*
* Write the bytes in the buffer to the socket.
* Takes a connection and returns the number of bytes written.
*/
int writebuff(int fd, struct buffer_s *buffptr)
{
int bytessent;
struct bufline_s *line = buffer_head(buffptr);
assert(fd >= 0);
assert(buffptr);
bytessent = send(fd, line->string + line->pos,
(size_t) (line->length - line->pos), 0);
if (bytessent >= 0) {
/* bytes sent, adjust buffer */
line->pos += bytessent;
if (line->pos == line->length)
free_line(shift_buffer(buffptr));
return bytessent;
} else {
switch (errno) {
#ifdef EWOULDBLOCK
case EWOULDBLOCK:
#else
# ifdef EAGAIN
case EAGAIN:
# endif
#endif
case ENOBUFS:
case EINTR:
case ENOMEM:
return 0;
default:
log("ERROR writebuff: send (%s)", strerror(errno));
return -1;
}
}
}