2006-02-09 06:46:49 +01:00
|
|
|
/* Copyright (c) 2001 Matej Pfajfar.
|
|
|
|
* Copyright (c) 2001-2004, Roger Dingledine.
|
2007-12-12 22:09:01 +01:00
|
|
|
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
2008-02-07 06:31:47 +01:00
|
|
|
* Copyright (c) 2007-2008, The Tor Project, Inc. */
|
Implemented link padding and receiver token buckets
Each socket reads at most 'bandwidth' bytes per second sustained, but
can handle bursts of up to 10*bandwidth bytes.
Cells are now sent out at evenly-spaced intervals, with padding sent
out otherwise. Set Linkpadding=0 in the rc file to send cells as soon
as they're available (and to never send padding cells).
Added license/copyrights statements at the top of most files.
router->min and router->max have been merged into a single 'bandwidth'
value. We should make the routerinfo_t reflect this (want to do that,
Mat?)
As the bandwidth increases, and we want to stop sleeping more and more
frequently to send a single cell, cpu usage goes up. At 128kB/s we're
pretty much calling poll with a timeout of 1ms or even 0ms. The current
code takes a timeout of 0-9ms and makes it 10ms. prepare_for_poll()
handles everything that should have happened in the past, so as long as
our buffers don't get too full in that 10ms, we're ok.
Speaking of too full, if you run three servers at 100kB/s with -l debug,
it spends too much time printing debugging messages to be able to keep
up with the cells. The outbuf ultimately fills up and it kills that
connection. If you run with -l err, it works fine up through 500kB/s and
probably beyond. Down the road we'll want to teach it to recognize when
an outbuf is getting full, and back off.
svn:r50
2002-07-16 03:12:15 +02:00
|
|
|
/* See LICENSE for licensing information */
|
|
|
|
/* $Id$ */
|
2005-12-14 21:40:40 +01:00
|
|
|
const char buffers_c_id[] =
|
|
|
|
"$Id$";
|
2002-06-27 00:45:49 +02:00
|
|
|
|
2004-05-07 19:04:12 +02:00
|
|
|
/**
|
|
|
|
* \file buffers.c
|
2005-06-11 07:31:17 +02:00
|
|
|
* \brief Implements a generic buffer interface. Buffers are
|
|
|
|
* fairly opaque string holders that can read to or flush from:
|
|
|
|
* memory, file descriptors, or TLS connections.
|
2004-05-07 19:04:12 +02:00
|
|
|
**/
|
2008-02-21 00:20:36 +01:00
|
|
|
#define BUFFERS_PRIVATE
|
2002-06-27 00:45:49 +02:00
|
|
|
#include "or.h"
|
|
|
|
|
2007-12-26 19:09:36 +01:00
|
|
|
//#define PARANOIA
|
|
|
|
//#define NOINLINE
|
2005-04-26 22:53:22 +02:00
|
|
|
|
2005-04-27 02:53:44 +02:00
|
|
|
#ifdef PARANOIA
|
2008-02-12 21:20:52 +01:00
|
|
|
/** Helper: If PARANOIA is defined, assert that the buffer in local variable
|
|
|
|
* <b>buf</b> is well-formed. */
|
2007-06-17 20:22:39 +02:00
|
|
|
#define check() STMT_BEGIN assert_buf_ok(buf); STMT_END
|
2005-04-27 02:53:44 +02:00
|
|
|
#else
|
2007-06-17 20:22:39 +02:00
|
|
|
#define check() STMT_NIL
|
2005-04-27 02:53:44 +02:00
|
|
|
#endif
|
|
|
|
|
2005-04-27 21:40:31 +02:00
|
|
|
#ifdef NOINLINE
|
2005-04-27 02:53:44 +02:00
|
|
|
#undef INLINE
|
|
|
|
#define INLINE
|
2005-04-27 21:40:31 +02:00
|
|
|
#endif
|
2005-04-27 02:53:44 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
/* Implementation notes:
|
|
|
|
*
|
|
|
|
* After flirting with memmove, and dallying with ring-buffers, we're finally
|
|
|
|
* getting up to speed with the 1970s and implementing buffers as a linked
|
|
|
|
* list of small chunks. Each buffer has such a list; data is removed from
|
|
|
|
* the head of the list, and added at the tail. The list is singly linked,
|
|
|
|
* and the buffer keeps a pointer to the head and the tail.
|
|
|
|
*
|
|
|
|
* Every chunk, except the tail, contains at least one byte of data. Data in
|
|
|
|
* each chunk is contiguous.
|
|
|
|
*
|
|
|
|
* When you need to treat the first N characters on a buffer as a contiguous
|
|
|
|
* string, use the buf_pullup function to make them so. Don't do this more
|
|
|
|
* than necessary.
|
|
|
|
*
|
|
|
|
* The major free Unix kernels have handled buffers like this since, like,
|
|
|
|
* forever.
|
2005-06-08 19:27:11 +02:00
|
|
|
*/
|
2005-04-26 22:53:22 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
/* Chunk manipulation functions */
|
|
|
|
|
|
|
|
/** A single chunk on a buffer or in a freelist. */
|
|
|
|
typedef struct chunk_t {
|
|
|
|
struct chunk_t *next; /**< The next chunk on the buffer or freelist. */
|
|
|
|
size_t datalen; /**< The number of bytes stored in this chunk */
|
|
|
|
size_t memlen; /**< The number of usable bytes of storage in <b>mem</b>. */
|
|
|
|
char *data; /**< A pointer to the first byte of data stored in <b>mem</b>. */
|
|
|
|
char mem[1]; /**< The actual memory used for storage in this chunk. May be
|
|
|
|
* more than one byte long. */
|
|
|
|
} chunk_t;
|
|
|
|
|
|
|
|
/** Return the number of bytes needed to allocate a chunk to hold
|
|
|
|
* <b>memlen</b> bytes. */
|
|
|
|
#define CHUNK_ALLOC_SIZE(memlen) (sizeof(chunk_t) + (memlen) - 1)
|
|
|
|
/** Return the number of usable bytes in a chunk allocated with
|
|
|
|
* malloc(<b>memlen</b>). */
|
|
|
|
#define CHUNK_SIZE_WITH_ALLOC(memlen) ((memlen) - sizeof(chunk_t) + 1)
|
|
|
|
|
|
|
|
/** Return the next character in <b>chunk</b> onto which data can be appended.
|
|
|
|
* If the chunk is full, this might be off the end of chunk->mem. */
|
2005-06-11 20:52:12 +02:00
|
|
|
static INLINE char *
|
2007-12-26 01:12:08 +01:00
|
|
|
CHUNK_WRITE_PTR(chunk_t *chunk)
|
2005-04-26 22:53:22 +02:00
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
return chunk->data + chunk->datalen;
|
2005-04-26 22:53:22 +02:00
|
|
|
}
|
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
/** Return the number of bytes that can be written onto <b>chunk</b> without
|
|
|
|
* running out of space. */
|
|
|
|
static INLINE size_t
|
|
|
|
CHUNK_REMAINING_CAPACITY(const chunk_t *chunk)
|
2005-06-17 20:49:55 +02:00
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
return (chunk->mem + chunk->memlen) - (chunk->data + chunk->datalen);
|
2005-04-26 22:53:22 +02:00
|
|
|
}
|
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
/** Move all bytes stored in <b>chunk</b> to the front of <b>chunk</b>->mem,
|
|
|
|
* to free up space at the end. */
|
|
|
|
static INLINE void
|
|
|
|
chunk_repack(chunk_t *chunk)
|
2005-06-17 20:49:55 +02:00
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
if (chunk->datalen && chunk->data != &chunk->mem[0]) {
|
|
|
|
memmove(chunk->mem, chunk->data, chunk->datalen);
|
|
|
|
}
|
|
|
|
chunk->data = &chunk->mem[0];
|
2005-06-17 20:49:55 +02:00
|
|
|
}
|
|
|
|
|
2008-01-13 01:20:44 +01:00
|
|
|
#ifdef ENABLE_BUF_FREELISTS
|
2007-12-26 01:12:08 +01:00
|
|
|
/** A freelist of chunks. */
|
|
|
|
typedef struct chunk_freelist_t {
|
|
|
|
size_t alloc_size; /**< What size chunks does this freelist hold? */
|
|
|
|
int max_length; /**< Never allow more than this number of chunks in the
|
|
|
|
* freelist. */
|
|
|
|
int slack; /**< When trimming the freelist, leave this number of extra
|
|
|
|
* chunks beyond lowest_length.*/
|
|
|
|
int cur_length; /**< How many chunks on the freelist now? */
|
|
|
|
int lowest_length; /**< What's the smallest value of cur_length since the
|
|
|
|
* last time we cleaned this freelist? */
|
2007-12-27 15:20:30 +01:00
|
|
|
uint64_t n_alloc;
|
|
|
|
uint64_t n_free;
|
|
|
|
uint64_t n_hit;
|
2007-12-26 01:12:08 +01:00
|
|
|
chunk_t *head; /**< First chunk on the freelist. */
|
|
|
|
} chunk_freelist_t;
|
|
|
|
|
|
|
|
/** Macro to help define freelists. */
|
2007-12-27 15:20:30 +01:00
|
|
|
#define FL(a,m,s) { a, m, s, 0, 0, 0, 0, 0, NULL }
|
2007-12-26 01:12:08 +01:00
|
|
|
|
|
|
|
/** Static array of freelists, sorted by alloc_len, terminated by an entry
|
|
|
|
* with alloc_size of 0. */
|
|
|
|
static chunk_freelist_t freelists[] = {
|
2008-02-16 00:39:04 +01:00
|
|
|
FL(4096, 256, 8), FL(8192, 128, 4), FL(16384, 64, 4), FL(32768, 32, 2),
|
2007-12-29 06:16:30 +01:00
|
|
|
FL(0, 0, 0)
|
2007-12-26 01:12:08 +01:00
|
|
|
};
|
|
|
|
#undef FL
|
2008-02-12 21:20:52 +01:00
|
|
|
/** How many times have we looked for a chunk of a size that no freelist
|
|
|
|
* could help with? */
|
2007-12-27 15:20:30 +01:00
|
|
|
static uint64_t n_freelist_miss = 0;
|
2007-12-26 01:12:08 +01:00
|
|
|
|
|
|
|
static void assert_freelist_ok(chunk_freelist_t *fl);
|
|
|
|
|
|
|
|
/** Return the freelist to hold chunks of size <b>alloc</b>, or NULL if
|
|
|
|
* no freelist exists for that size. */
|
|
|
|
static INLINE chunk_freelist_t *
|
|
|
|
get_freelist(size_t alloc)
|
2005-04-26 22:53:22 +02:00
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
int i;
|
|
|
|
for (i=0; freelists[i].alloc_size <= alloc; ++i) {
|
|
|
|
if (freelists[i].alloc_size == alloc) {
|
|
|
|
return &freelists[i];
|
|
|
|
}
|
2005-04-26 22:53:22 +02:00
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
return NULL;
|
2005-04-26 22:53:22 +02:00
|
|
|
}
|
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
/** Deallocate a chunk or put it on a freelist */
|
|
|
|
static void
|
|
|
|
chunk_free(chunk_t *chunk)
|
2007-05-10 00:39:49 +02:00
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
size_t alloc = CHUNK_ALLOC_SIZE(chunk->memlen);
|
|
|
|
chunk_freelist_t *freelist = get_freelist(alloc);
|
|
|
|
if (freelist && freelist->cur_length < freelist->max_length) {
|
|
|
|
chunk->next = freelist->head;
|
|
|
|
freelist->head = chunk;
|
|
|
|
++freelist->cur_length;
|
2007-05-10 00:39:49 +02:00
|
|
|
} else {
|
2007-12-27 15:20:30 +01:00
|
|
|
if (freelist)
|
|
|
|
++freelist->n_free;
|
2007-12-26 01:12:08 +01:00
|
|
|
tor_free(chunk);
|
2007-05-10 00:39:49 +02:00
|
|
|
}
|
|
|
|
}
|
2007-04-23 16:42:27 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
/** Allocate a new chunk with a given allocation size, or get one from the
|
|
|
|
* freelist. Note that a chunk with allocation size A can actualy hold only
|
|
|
|
* CHUNK_SIZE_WITH_ALLOC(A) bytes in its mem field. */
|
|
|
|
static INLINE chunk_t *
|
|
|
|
chunk_new_with_alloc_size(size_t alloc)
|
2007-07-27 20:33:37 +02:00
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
chunk_t *ch;
|
|
|
|
chunk_freelist_t *freelist;
|
|
|
|
tor_assert(alloc >= sizeof(chunk_t));
|
|
|
|
freelist = get_freelist(alloc);
|
|
|
|
if (freelist && freelist->head) {
|
|
|
|
ch = freelist->head;
|
|
|
|
freelist->head = ch->next;
|
|
|
|
if (--freelist->cur_length < freelist->lowest_length)
|
|
|
|
freelist->lowest_length = freelist->cur_length;
|
2007-12-27 15:20:30 +01:00
|
|
|
++freelist->n_hit;
|
2007-12-26 01:12:08 +01:00
|
|
|
} else {
|
2008-02-16 00:39:04 +01:00
|
|
|
/* XXXX021 take advantage of tor_malloc_roundup, once we know how that
|
2008-02-06 06:31:21 +01:00
|
|
|
* affects freelists. */
|
2007-12-27 15:20:30 +01:00
|
|
|
if (freelist)
|
|
|
|
++freelist->n_alloc;
|
|
|
|
else
|
|
|
|
++n_freelist_miss;
|
2007-12-26 01:12:08 +01:00
|
|
|
ch = tor_malloc(alloc);
|
2007-07-27 20:33:37 +02:00
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
ch->next = NULL;
|
|
|
|
ch->datalen = 0;
|
|
|
|
ch->memlen = CHUNK_SIZE_WITH_ALLOC(alloc);
|
|
|
|
ch->data = &ch->mem[0];
|
|
|
|
return ch;
|
2007-07-27 20:33:37 +02:00
|
|
|
}
|
2008-01-13 01:20:44 +01:00
|
|
|
#else
|
|
|
|
static void
|
|
|
|
chunk_free(chunk_t *chunk)
|
|
|
|
{
|
|
|
|
tor_free(chunk);
|
|
|
|
}
|
|
|
|
static INLINE chunk_t *
|
|
|
|
chunk_new_with_alloc_size(size_t alloc)
|
|
|
|
{
|
|
|
|
chunk_t *ch;
|
|
|
|
ch = tor_malloc_roundup(&alloc);
|
|
|
|
ch->next = NULL;
|
|
|
|
ch->datalen = 0;
|
|
|
|
ch->memlen = CHUNK_SIZE_WITH_ALLOC(alloc);
|
|
|
|
ch->data = &ch->mem[0];
|
|
|
|
return ch;
|
|
|
|
}
|
|
|
|
#endif
|
2007-07-27 20:33:37 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
/** Allocate a new chunk with memory size of <b>sz</b>. */
|
|
|
|
#define chunk_new_with_capacity(sz) \
|
|
|
|
(chunk_new_with_alloc_size(CHUNK_ALLOC_SIZE(sz)))
|
2007-04-23 16:42:27 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
/** Expand <b>chunk</b> until it can hold <b>sz</b> bytes, and return a
|
|
|
|
* new pointer to <b>chunk</b>. Old pointers are no longer valid. */
|
|
|
|
static INLINE chunk_t *
|
|
|
|
chunk_grow(chunk_t *chunk, size_t sz)
|
|
|
|
{
|
|
|
|
off_t offset;
|
|
|
|
tor_assert(sz > chunk->memlen);
|
|
|
|
offset = chunk->data - chunk->mem;
|
|
|
|
chunk = tor_realloc(chunk, CHUNK_ALLOC_SIZE(sz));
|
|
|
|
chunk->memlen = sz;
|
|
|
|
chunk->data = chunk->mem + offset;
|
|
|
|
return chunk;
|
2007-04-23 16:42:27 +02:00
|
|
|
}
|
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
/** If a read onto the end of a chunk would be smaller than this number, then
|
|
|
|
* just start a new chunk. */
|
|
|
|
#define MIN_READ_LEN 8
|
|
|
|
/** Every chunk should take up at least this many bytes. */
|
|
|
|
#define MIN_CHUNK_ALLOC 256
|
2008-02-12 21:20:52 +01:00
|
|
|
/** No chunk should take up more than this many bytes. */
|
2007-12-26 01:12:08 +01:00
|
|
|
#define MAX_CHUNK_ALLOC 65536
|
|
|
|
|
|
|
|
/** Return the allocation size we'd like to use to hold <b>target</b>
|
|
|
|
* bytes. */
|
|
|
|
static INLINE size_t
|
|
|
|
preferred_chunk_size(size_t target)
|
2007-04-23 16:42:27 +02:00
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
size_t sz = MIN_CHUNK_ALLOC;
|
|
|
|
while (CHUNK_SIZE_WITH_ALLOC(sz) < target) {
|
|
|
|
sz <<= 1;
|
2007-04-23 16:42:27 +02:00
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
return sz;
|
2007-04-23 16:42:27 +02:00
|
|
|
}
|
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
/** Remove from the freelists most chunks that have not been used since the
|
|
|
|
* last call to buf_shrink_freelists(). */
|
2007-05-09 23:43:41 +02:00
|
|
|
void
|
2007-07-17 11:26:45 +02:00
|
|
|
buf_shrink_freelists(int free_all)
|
2007-05-09 23:43:41 +02:00
|
|
|
{
|
2008-01-13 01:20:44 +01:00
|
|
|
#ifdef ENABLE_BUF_FREELISTS
|
2007-12-26 01:12:08 +01:00
|
|
|
int i;
|
|
|
|
for (i = 0; freelists[i].alloc_size; ++i) {
|
|
|
|
int slack = freelists[i].slack;
|
|
|
|
assert_freelist_ok(&freelists[i]);
|
|
|
|
if (free_all || freelists[i].lowest_length > slack) {
|
|
|
|
int n_to_free = free_all ? freelists[i].cur_length :
|
|
|
|
(freelists[i].lowest_length - slack);
|
|
|
|
int n_to_skip = freelists[i].cur_length - n_to_free;
|
2007-12-27 04:27:48 +01:00
|
|
|
int new_length = n_to_skip;
|
2007-12-26 01:12:08 +01:00
|
|
|
chunk_t **chp = &freelists[i].head;
|
|
|
|
chunk_t *chunk;
|
2007-12-26 20:02:15 +01:00
|
|
|
log_info(LD_MM, "Cleaning freelist for %d-byte chunks: keeping %d, "
|
|
|
|
"dropping %d.",
|
|
|
|
(int)freelists[i].alloc_size, n_to_skip, n_to_free);
|
2007-12-26 01:12:08 +01:00
|
|
|
while (n_to_skip) {
|
|
|
|
tor_assert((*chp)->next);
|
|
|
|
chp = &(*chp)->next;
|
2007-12-27 04:23:57 +01:00
|
|
|
--n_to_skip;
|
2007-07-17 13:33:38 +02:00
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
chunk = *chp;
|
|
|
|
*chp = NULL;
|
|
|
|
while (chunk) {
|
|
|
|
chunk_t *next = chunk->next;
|
|
|
|
tor_free(chunk);
|
|
|
|
chunk = next;
|
|
|
|
--n_to_free;
|
2007-12-27 15:20:30 +01:00
|
|
|
++freelists[i].n_free;
|
2007-05-24 20:12:41 +02:00
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
tor_assert(!n_to_free);
|
2007-12-27 04:27:48 +01:00
|
|
|
freelists[i].cur_length = new_length;
|
2007-05-09 23:43:41 +02:00
|
|
|
}
|
2007-12-27 01:25:54 +01:00
|
|
|
freelists[i].lowest_length = freelists[i].cur_length;
|
2007-12-26 01:12:08 +01:00
|
|
|
assert_freelist_ok(&freelists[i]);
|
2007-05-09 23:43:41 +02:00
|
|
|
}
|
2008-01-13 01:20:44 +01:00
|
|
|
#else
|
|
|
|
(void) free_all;
|
|
|
|
#endif
|
2007-05-09 23:43:41 +02:00
|
|
|
}
|
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
/** Describe the current status of the freelists at log level <b>severity</b>.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
buf_dump_freelist_sizes(int severity)
|
2003-10-14 03:34:31 +02:00
|
|
|
{
|
2008-01-13 01:20:44 +01:00
|
|
|
#ifdef ENABLE_BUF_FREELISTS
|
2007-12-26 01:12:08 +01:00
|
|
|
int i;
|
|
|
|
log(severity, LD_MM, "====== Buffer freelists:");
|
|
|
|
for (i = 0; freelists[i].alloc_size; ++i) {
|
|
|
|
uint64_t total = ((uint64_t)freelists[i].cur_length) *
|
|
|
|
freelists[i].alloc_size;
|
|
|
|
log(severity, LD_MM,
|
2007-12-27 15:20:30 +01:00
|
|
|
U64_FORMAT" bytes in %d %d-byte chunks ["U64_FORMAT
|
|
|
|
" misses; "U64_FORMAT" frees; "U64_FORMAT" hits]",
|
|
|
|
U64_PRINTF_ARG(total),
|
|
|
|
freelists[i].cur_length, (int)freelists[i].alloc_size,
|
|
|
|
U64_PRINTF_ARG(freelists[i].n_alloc),
|
|
|
|
U64_PRINTF_ARG(freelists[i].n_free),
|
|
|
|
U64_PRINTF_ARG(freelists[i].n_hit));
|
2007-12-26 01:12:08 +01:00
|
|
|
}
|
2007-12-27 15:20:30 +01:00
|
|
|
log(severity, LD_MM, U64_FORMAT" allocations in non-freelist sizes",
|
|
|
|
U64_PRINTF_ARG(n_freelist_miss));
|
2008-01-13 01:20:44 +01:00
|
|
|
#else
|
|
|
|
(void)severity;
|
|
|
|
#endif
|
2007-12-26 01:12:08 +01:00
|
|
|
}
|
2005-04-26 22:53:22 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
/** Magic value for buf_t.magic, to catch pointer errors. */
|
|
|
|
#define BUFFER_MAGIC 0xB0FFF312u
|
|
|
|
/** A resizeable buffer, optimized for reading and writing. */
|
|
|
|
struct buf_t {
|
|
|
|
uint32_t magic; /**< Magic cookie for debugging: Must be set to
|
|
|
|
* BUFFER_MAGIC. */
|
|
|
|
size_t datalen; /**< How many bytes is this buffer holding right now? */
|
|
|
|
size_t default_chunk_size; /**< Don't allocate any chunks smaller than
|
|
|
|
* this for this buffer. */
|
|
|
|
chunk_t *head; /**< First chunk in the list, or NULL for none. */
|
|
|
|
chunk_t *tail; /**< Last chunk in the list, or NULL for none. */
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Collapse data from the first N chunks from <b>buf</b> into buf->head,
|
|
|
|
* growing it as necessary, until buf->head has the first <b>bytes</b> bytes
|
|
|
|
* of data from the buffer, or until buf->head has all the data in <b>buf</b>.
|
|
|
|
*
|
|
|
|
* If <b>nulterminate</b> is true, ensure that there is a 0 byte in
|
|
|
|
* buf->head->mem right after all the data. */
|
|
|
|
static void
|
|
|
|
buf_pullup(buf_t *buf, size_t bytes, int nulterminate)
|
|
|
|
{
|
|
|
|
chunk_t *dest, *src;
|
|
|
|
size_t capacity;
|
|
|
|
if (!buf->head)
|
2005-05-03 01:17:08 +02:00
|
|
|
return;
|
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
check();
|
|
|
|
if (buf->datalen < bytes)
|
|
|
|
bytes = buf->datalen;
|
|
|
|
|
|
|
|
if (nulterminate) {
|
|
|
|
capacity = bytes + 1;
|
|
|
|
if (buf->head->datalen >= bytes && CHUNK_REMAINING_CAPACITY(buf->head)) {
|
|
|
|
*CHUNK_WRITE_PTR(buf->head) = '\0';
|
|
|
|
return;
|
2005-04-26 22:53:22 +02:00
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
} else {
|
|
|
|
capacity = bytes;
|
|
|
|
if (buf->head->datalen >= bytes)
|
|
|
|
return;
|
2005-04-26 22:53:22 +02:00
|
|
|
}
|
2005-06-08 19:27:11 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
if (buf->head->memlen >= capacity) {
|
|
|
|
/* We don't need to grow the first chunk, but we might need to repack it.*/
|
|
|
|
if (CHUNK_REMAINING_CAPACITY(buf->head) < capacity-buf->datalen)
|
|
|
|
chunk_repack(buf->head);
|
|
|
|
tor_assert(CHUNK_REMAINING_CAPACITY(buf->head) >= capacity-buf->datalen);
|
2005-06-08 19:27:11 +02:00
|
|
|
} else {
|
2007-12-26 01:12:08 +01:00
|
|
|
chunk_t *newhead;
|
|
|
|
size_t newsize;
|
|
|
|
/* We need to grow the chunk. */
|
|
|
|
chunk_repack(buf->head);
|
|
|
|
newsize = CHUNK_SIZE_WITH_ALLOC(preferred_chunk_size(capacity));
|
|
|
|
newhead = chunk_grow(buf->head, newsize);
|
|
|
|
tor_assert(newhead->memlen >= capacity);
|
|
|
|
if (newhead != buf->head) {
|
|
|
|
if (buf->tail == buf->head)
|
|
|
|
buf->tail = newhead;
|
|
|
|
buf->head = newhead;
|
2007-04-23 16:42:27 +02:00
|
|
|
}
|
2005-06-08 19:27:11 +02:00
|
|
|
}
|
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
dest = buf->head;
|
|
|
|
while (dest->datalen < bytes) {
|
|
|
|
size_t n = bytes - dest->datalen;
|
|
|
|
src = dest->next;
|
|
|
|
tor_assert(src);
|
|
|
|
if (n > src->datalen) {
|
|
|
|
memcpy(CHUNK_WRITE_PTR(dest), src->data, src->datalen);
|
|
|
|
dest->datalen += src->datalen;
|
|
|
|
dest->next = src->next;
|
|
|
|
if (buf->tail == src)
|
|
|
|
buf->tail = dest;
|
|
|
|
chunk_free(src);
|
|
|
|
} else {
|
|
|
|
memcpy(CHUNK_WRITE_PTR(dest), src->data, n);
|
|
|
|
dest->datalen += n;
|
|
|
|
src->data += n;
|
|
|
|
src->datalen -= n;
|
|
|
|
tor_assert(dest->datalen == bytes);
|
|
|
|
}
|
2005-04-26 22:53:22 +02:00
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
|
|
|
|
if (nulterminate) {
|
|
|
|
tor_assert(CHUNK_REMAINING_CAPACITY(buf->head));
|
|
|
|
*CHUNK_WRITE_PTR(buf->head) = '\0';
|
2005-04-26 22:53:22 +02:00
|
|
|
}
|
2003-10-14 03:34:31 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
check();
|
2003-10-14 03:34:31 +02:00
|
|
|
}
|
|
|
|
|
2005-05-03 01:41:39 +02:00
|
|
|
/** Resize buf so it won't hold extra memory that we haven't been
|
2008-02-16 00:39:04 +01:00
|
|
|
* using lately.
|
2005-05-03 01:39:09 +02:00
|
|
|
*/
|
2005-05-03 00:49:24 +02:00
|
|
|
void
|
|
|
|
buf_shrink(buf_t *buf)
|
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
(void)buf;
|
2005-05-03 00:49:24 +02:00
|
|
|
}
|
|
|
|
|
2005-06-11 23:17:38 +02:00
|
|
|
/** Remove the first <b>n</b> bytes from buf. */
|
2005-06-11 20:52:12 +02:00
|
|
|
static INLINE void
|
2005-09-30 03:09:52 +02:00
|
|
|
buf_remove_from_front(buf_t *buf, size_t n)
|
|
|
|
{
|
2004-04-25 22:37:37 +02:00
|
|
|
tor_assert(buf->datalen >= n);
|
2007-12-26 01:12:08 +01:00
|
|
|
while (n) {
|
|
|
|
tor_assert(buf->head);
|
|
|
|
if (buf->head->datalen > n) {
|
|
|
|
buf->head->datalen -= n;
|
|
|
|
buf->head->data += n;
|
|
|
|
buf->datalen -= n;
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
chunk_t *victim = buf->head;
|
|
|
|
n -= victim->datalen;
|
|
|
|
buf->datalen -= victim->datalen;
|
|
|
|
buf->head = victim->next;
|
|
|
|
if (buf->tail == victim)
|
|
|
|
buf->tail = NULL;
|
|
|
|
chunk_free(victim);
|
2007-07-30 19:47:43 +02:00
|
|
|
}
|
2005-05-03 01:32:23 +02:00
|
|
|
}
|
2005-04-27 02:53:44 +02:00
|
|
|
check();
|
2003-10-14 03:34:31 +02:00
|
|
|
}
|
|
|
|
|
2008-02-16 00:39:04 +01:00
|
|
|
/** Create and return a new buf with default chunk capacity <b>size</b>.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
buf_t *
|
2005-09-30 03:09:52 +02:00
|
|
|
buf_new_with_capacity(size_t size)
|
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
buf_t *b = buf_new();
|
|
|
|
b->default_chunk_size = preferred_chunk_size(size);
|
|
|
|
return b;
|
2003-09-25 07:17:11 +02:00
|
|
|
}
|
2002-06-27 00:45:49 +02:00
|
|
|
|
2004-05-07 19:04:12 +02:00
|
|
|
/** Allocate and return a new buffer with default capacity. */
|
2005-06-11 20:52:12 +02:00
|
|
|
buf_t *
|
|
|
|
buf_new(void)
|
2003-09-25 07:17:11 +02:00
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
buf_t *buf = tor_malloc_zero(sizeof(buf_t));
|
|
|
|
buf->magic = BUFFER_MAGIC;
|
|
|
|
buf->default_chunk_size = 4096;
|
|
|
|
return buf;
|
2003-09-25 07:17:11 +02:00
|
|
|
}
|
2002-06-27 00:45:49 +02:00
|
|
|
|
2005-06-11 23:17:38 +02:00
|
|
|
/** Remove all data from <b>buf</b>. */
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
|
|
|
buf_clear(buf_t *buf)
|
2004-02-28 20:14:11 +01:00
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
chunk_t *chunk, *next;
|
2004-02-28 20:14:11 +01:00
|
|
|
buf->datalen = 0;
|
2007-12-26 01:12:08 +01:00
|
|
|
for (chunk = buf->head; chunk; chunk = next) {
|
|
|
|
next = chunk->next;
|
|
|
|
chunk_free(chunk);
|
|
|
|
}
|
|
|
|
buf->head = buf->tail = NULL;
|
2004-02-28 20:14:11 +01:00
|
|
|
}
|
2002-06-27 00:45:49 +02:00
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Return the number of bytes stored in <b>buf</b> */
|
2005-06-11 20:52:12 +02:00
|
|
|
size_t
|
|
|
|
buf_datalen(const buf_t *buf)
|
2003-09-25 07:17:11 +02:00
|
|
|
{
|
|
|
|
return buf->datalen;
|
2002-06-27 00:45:49 +02:00
|
|
|
}
|
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
/** Return the total length of all chunks used in <b>buf</b>. */
|
2005-06-11 20:52:12 +02:00
|
|
|
size_t
|
2007-12-26 01:12:08 +01:00
|
|
|
buf_allocation(const buf_t *buf)
|
2003-09-25 07:17:11 +02:00
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
size_t total = 0;
|
|
|
|
const chunk_t *chunk;
|
|
|
|
for (chunk = buf->head; chunk; chunk = chunk->next) {
|
|
|
|
total += chunk->memlen;
|
|
|
|
}
|
|
|
|
return total;
|
2003-09-25 07:17:11 +02:00
|
|
|
}
|
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
/** Return the number of bytes that can be added to <b>buf</b> without
|
|
|
|
* performing any additional allocation. */
|
|
|
|
size_t
|
|
|
|
buf_slack(const buf_t *buf)
|
2003-09-25 07:17:11 +02:00
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
if (!buf->tail)
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return CHUNK_REMAINING_CAPACITY(buf->tail);
|
2003-09-25 07:17:11 +02:00
|
|
|
}
|
|
|
|
|
2005-06-11 23:17:38 +02:00
|
|
|
/** Release storage held by <b>buf</b>. */
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
|
|
|
buf_free(buf_t *buf)
|
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
buf_clear(buf);
|
|
|
|
buf->magic = 0xdeadbeef;
|
|
|
|
tor_free(buf);
|
|
|
|
}
|
|
|
|
|
2008-01-06 01:54:22 +01:00
|
|
|
/** Append a new chunk with enough capacity to hold <b>capacity</b> bytes to
|
|
|
|
* the tail of <b>buf</b>. If <b>capped</b>, don't allocate a chunk bigger
|
|
|
|
* than MAX_CHUNK_ALLOC. */
|
2007-12-26 01:12:08 +01:00
|
|
|
static chunk_t *
|
2007-12-29 06:16:30 +01:00
|
|
|
buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped)
|
2007-12-26 01:12:08 +01:00
|
|
|
{
|
|
|
|
chunk_t *chunk;
|
2007-12-29 06:16:30 +01:00
|
|
|
if (CHUNK_ALLOC_SIZE(capacity) < buf->default_chunk_size) {
|
2007-12-26 01:12:08 +01:00
|
|
|
chunk = chunk_new_with_alloc_size(buf->default_chunk_size);
|
2007-12-29 06:16:30 +01:00
|
|
|
} else if (capped && CHUNK_ALLOC_SIZE(capacity) > MAX_CHUNK_ALLOC) {
|
|
|
|
chunk = chunk_new_with_alloc_size(MAX_CHUNK_ALLOC);
|
2007-12-26 01:12:08 +01:00
|
|
|
} else {
|
2007-12-29 06:16:30 +01:00
|
|
|
chunk = chunk_new_with_alloc_size(preferred_chunk_size(capacity));
|
2007-05-10 00:39:49 +02:00
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
if (buf->tail) {
|
|
|
|
tor_assert(buf->head);
|
|
|
|
buf->tail->next = chunk;
|
|
|
|
buf->tail = chunk;
|
|
|
|
} else {
|
|
|
|
tor_assert(!buf->head);
|
|
|
|
buf->head = buf->tail = chunk;
|
2007-04-23 16:42:27 +02:00
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
check();
|
|
|
|
return chunk;
|
2002-06-27 00:45:49 +02:00
|
|
|
}
|
|
|
|
|
2008-02-06 20:34:32 +01:00
|
|
|
/** Read up to <b>at_most</b> bytes from the socket <b>fd</b> into
|
2008-02-12 21:20:52 +01:00
|
|
|
* <b>chunk</b> (which must be on <b>buf</b>). If we get an EOF, set
|
2008-02-06 20:34:32 +01:00
|
|
|
* *<b>reached_eof</b> to 1. Return -1 on error, 0 on eof or blocking,
|
|
|
|
* and the number of bytes read otherwise. */
|
2005-06-11 20:52:12 +02:00
|
|
|
static INLINE int
|
2007-12-26 01:12:08 +01:00
|
|
|
read_to_chunk(buf_t *buf, chunk_t *chunk, int fd, size_t at_most,
|
|
|
|
int *reached_eof)
|
2005-04-26 22:53:22 +02:00
|
|
|
{
|
2008-02-22 04:44:36 +01:00
|
|
|
ssize_t read_result;
|
2005-04-26 22:53:22 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
tor_assert(CHUNK_REMAINING_CAPACITY(chunk) >= at_most);
|
|
|
|
read_result = tor_socket_recv(fd, CHUNK_WRITE_PTR(chunk), at_most, 0);
|
2005-04-26 22:53:22 +02:00
|
|
|
if (read_result < 0) {
|
2007-12-27 06:18:36 +01:00
|
|
|
int e = tor_socket_errno(fd);
|
2005-04-26 22:53:22 +02:00
|
|
|
if (!ERRNO_IS_EAGAIN(e)) { /* it's a real error */
|
2006-04-08 09:54:11 +02:00
|
|
|
#ifdef MS_WINDOWS
|
|
|
|
if (e == WSAENOBUFS)
|
|
|
|
log_warn(LD_NET,"recv() failed: WSAENOBUFS. Not enough ram?");
|
|
|
|
#endif
|
2005-04-26 22:53:22 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0; /* would block. */
|
|
|
|
} else if (read_result == 0) {
|
2007-12-26 01:12:08 +01:00
|
|
|
log_debug(LD_NET,"Encountered eof on fd %d", (int)fd);
|
2005-04-26 22:53:22 +02:00
|
|
|
*reached_eof = 1;
|
|
|
|
return 0;
|
2007-12-26 01:12:08 +01:00
|
|
|
} else { /* actually got bytes. */
|
2005-04-26 22:53:22 +02:00
|
|
|
buf->datalen += read_result;
|
2007-12-26 01:12:08 +01:00
|
|
|
chunk->datalen += read_result;
|
2008-02-22 04:44:36 +01:00
|
|
|
log_debug(LD_NET,"Read %ld bytes. %d on inbuf.", (long)read_result,
|
2006-02-13 09:28:42 +01:00
|
|
|
(int)buf->datalen);
|
2008-02-22 04:44:36 +01:00
|
|
|
tor_assert(read_result < INT_MAX);
|
|
|
|
return (int)read_result;
|
2005-04-26 22:53:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-06 20:34:32 +01:00
|
|
|
/** As read_to_chunk(), but return (negative) error code on error, blocking,
|
|
|
|
* or TLS, and the number of bytes read otherwise. */
|
2007-12-26 01:12:08 +01:00
|
|
|
static INLINE int
|
|
|
|
read_to_chunk_tls(buf_t *buf, chunk_t *chunk, tor_tls_t *tls,
|
|
|
|
size_t at_most)
|
|
|
|
{
|
|
|
|
int read_result;
|
|
|
|
|
|
|
|
tor_assert(CHUNK_REMAINING_CAPACITY(chunk) >= at_most);
|
|
|
|
read_result = tor_tls_read(tls, CHUNK_WRITE_PTR(chunk), at_most);
|
|
|
|
if (read_result < 0)
|
|
|
|
return read_result;
|
|
|
|
buf->datalen += read_result;
|
|
|
|
chunk->datalen += read_result;
|
|
|
|
return read_result;
|
|
|
|
}
|
|
|
|
|
2004-05-10 09:27:29 +02:00
|
|
|
/** Read from socket <b>s</b>, writing onto end of <b>buf</b>. Read at most
|
2008-02-06 20:34:32 +01:00
|
|
|
* <b>at_most</b> bytes, growing the buffer as necessary. If recv() returns 0
|
|
|
|
* (because of EOF), set *<b>reached_eof</b> to 1 and return 0. Return -1 on
|
|
|
|
* error; else return the number of bytes read.
|
2003-03-04 05:36:37 +01:00
|
|
|
*/
|
2008-02-16 00:39:04 +01:00
|
|
|
/* XXXX021 indicate "read blocked" somehow? */
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
|
|
|
read_to_buf(int s, size_t at_most, buf_t *buf, int *reached_eof)
|
2005-04-26 22:53:22 +02:00
|
|
|
{
|
2008-02-16 00:39:04 +01:00
|
|
|
/* XXXX021 It's stupid to overload the return values for these functions:
|
2008-02-06 20:34:32 +01:00
|
|
|
* "error status" and "number of bytes read" are not mutually exclusive.
|
|
|
|
*/
|
2007-12-26 01:12:08 +01:00
|
|
|
int r = 0;
|
|
|
|
size_t total_read = 0;
|
2002-06-27 00:45:49 +02:00
|
|
|
|
2005-04-27 02:53:44 +02:00
|
|
|
check();
|
2007-12-26 01:12:08 +01:00
|
|
|
tor_assert(reached_eof);
|
|
|
|
tor_assert(s >= 0);
|
|
|
|
|
2007-12-29 06:16:30 +01:00
|
|
|
while (at_most > total_read) {
|
|
|
|
size_t readlen = at_most - total_read;
|
2007-12-26 01:12:08 +01:00
|
|
|
chunk_t *chunk;
|
|
|
|
if (!buf->tail || CHUNK_REMAINING_CAPACITY(buf->tail) < MIN_READ_LEN) {
|
2007-12-29 06:16:30 +01:00
|
|
|
chunk = buf_add_chunk_with_capacity(buf, at_most, 1);
|
|
|
|
if (readlen > chunk->memlen)
|
|
|
|
readlen = chunk->memlen;
|
2005-04-26 22:53:22 +02:00
|
|
|
} else {
|
2007-12-26 01:12:08 +01:00
|
|
|
size_t cap = CHUNK_REMAINING_CAPACITY(buf->tail);
|
|
|
|
chunk = buf->tail;
|
|
|
|
if (cap < readlen)
|
|
|
|
readlen = cap;
|
2002-06-27 00:45:49 +02:00
|
|
|
}
|
2005-04-26 22:53:22 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
r = read_to_chunk(buf, chunk, s, readlen, reached_eof);
|
|
|
|
check();
|
|
|
|
if (r < 0)
|
|
|
|
return r; /* Error */
|
2008-03-02 23:29:04 +01:00
|
|
|
tor_assert(total_read+r < INT_MAX);
|
2007-12-26 01:12:08 +01:00
|
|
|
total_read += r;
|
2008-03-02 23:29:04 +01:00
|
|
|
if ((size_t)r < readlen) { /* eof, block, or no more to read. */
|
|
|
|
break;
|
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
}
|
2008-03-02 23:29:04 +01:00
|
|
|
return (int)total_read;
|
2002-06-27 00:45:49 +02:00
|
|
|
}
|
|
|
|
|
2008-02-06 20:34:32 +01:00
|
|
|
/** As read_to_buf, but reads from a TLS connection, and returns a TLS
|
|
|
|
* status value rather than the number of bytes read.
|
2005-06-11 08:07:22 +02:00
|
|
|
*
|
|
|
|
* Using TLS on OR connections complicates matters in two ways.
|
|
|
|
*
|
|
|
|
* First, a TLS stream has its own read buffer independent of the
|
|
|
|
* connection's read buffer. (TLS needs to read an entire frame from
|
|
|
|
* the network before it can decrypt any data. Thus, trying to read 1
|
|
|
|
* byte from TLS can require that several KB be read from the network
|
|
|
|
* and decrypted. The extra data is stored in TLS's decrypt buffer.)
|
|
|
|
* Because the data hasn't been read by Tor (it's still inside the TLS),
|
|
|
|
* this means that sometimes a connection "has stuff to read" even when
|
|
|
|
* poll() didn't return POLLIN. The tor_tls_get_pending_bytes function is
|
|
|
|
* used in connection.c to detect TLS objects with non-empty internal
|
|
|
|
* buffers and read from them again.
|
|
|
|
*
|
|
|
|
* Second, the TLS stream's events do not correspond directly to network
|
|
|
|
* events: sometimes, before a TLS stream can read, the network must be
|
|
|
|
* ready to write -- or vice versa.
|
2004-05-02 00:08:43 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
2005-10-06 06:33:40 +02:00
|
|
|
read_to_buf_tls(tor_tls_t *tls, size_t at_most, buf_t *buf)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
int r = 0;
|
|
|
|
size_t total_read = 0;
|
2005-04-27 02:53:44 +02:00
|
|
|
check();
|
2005-04-26 22:53:22 +02:00
|
|
|
|
2007-12-29 06:16:30 +01:00
|
|
|
while (at_most > total_read) {
|
|
|
|
size_t readlen = at_most - total_read;
|
2007-12-26 01:12:08 +01:00
|
|
|
chunk_t *chunk;
|
|
|
|
if (!buf->tail || CHUNK_REMAINING_CAPACITY(buf->tail) < MIN_READ_LEN) {
|
2007-12-29 06:16:30 +01:00
|
|
|
chunk = buf_add_chunk_with_capacity(buf, at_most, 1);
|
|
|
|
if (readlen > chunk->memlen)
|
|
|
|
readlen = chunk->memlen;
|
2007-12-26 01:12:08 +01:00
|
|
|
} else {
|
|
|
|
size_t cap = CHUNK_REMAINING_CAPACITY(buf->tail);
|
|
|
|
chunk = buf->tail;
|
|
|
|
if (cap < readlen)
|
|
|
|
readlen = cap;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = read_to_chunk_tls(buf, chunk, tls, readlen);
|
2005-04-27 02:53:44 +02:00
|
|
|
check();
|
2007-12-26 01:12:08 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r; /* Error */
|
2008-03-02 23:29:04 +01:00
|
|
|
tor_assert(total_read+r < INT_MAX);
|
|
|
|
total_read += r;
|
|
|
|
if ((size_t)r < readlen) /* eof, block, or no more to read. */
|
|
|
|
break;
|
2005-04-26 22:53:22 +02:00
|
|
|
}
|
2008-03-02 23:29:04 +01:00
|
|
|
return (int)total_read;
|
2003-12-17 22:09:31 +01:00
|
|
|
}
|
2003-09-04 18:05:08 +02:00
|
|
|
|
2008-02-06 20:34:32 +01:00
|
|
|
/** Helper for flush_buf(): try to write <b>sz</b> bytes from chunk
|
|
|
|
* <b>chunk</b> of buffer <b>buf</b> onto socket <b>s</b>. On success, deduct
|
|
|
|
* the bytes written from *<b>buf_flushlen</b>. Return the number of bytes
|
|
|
|
* written on success, 0 on blocking, -1 on failure.
|
2005-06-11 20:52:12 +02:00
|
|
|
*/
|
2005-04-26 22:53:22 +02:00
|
|
|
static INLINE int
|
2007-12-26 01:12:08 +01:00
|
|
|
flush_chunk(int s, buf_t *buf, chunk_t *chunk, size_t sz,
|
2008-02-06 20:34:32 +01:00
|
|
|
size_t *buf_flushlen)
|
2005-04-26 22:53:22 +02:00
|
|
|
{
|
2008-02-22 04:44:36 +01:00
|
|
|
ssize_t write_result;
|
2005-04-26 22:53:22 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
tor_assert(sz <= chunk->datalen);
|
|
|
|
write_result = tor_socket_send(s, chunk->data, sz, 0);
|
2005-04-26 22:53:22 +02:00
|
|
|
if (write_result < 0) {
|
|
|
|
int e = tor_socket_errno(s);
|
|
|
|
if (!ERRNO_IS_EAGAIN(e)) { /* it's a real error */
|
2006-04-08 09:54:11 +02:00
|
|
|
#ifdef MS_WINDOWS
|
|
|
|
if (e == WSAENOBUFS)
|
|
|
|
log_warn(LD_NET,"write() failed: WSAENOBUFS. Not enough ram?");
|
|
|
|
#endif
|
2005-04-26 22:53:22 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2006-02-13 09:28:42 +01:00
|
|
|
log_debug(LD_NET,"write() would block, returning.");
|
2005-04-26 22:53:22 +02:00
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
*buf_flushlen -= write_result;
|
|
|
|
buf_remove_from_front(buf, write_result);
|
2008-02-22 04:44:36 +01:00
|
|
|
tor_assert(write_result < INT_MAX);
|
|
|
|
return (int)write_result;
|
2005-04-26 22:53:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-08 22:13:15 +01:00
|
|
|
/** Helper for flush_buf_tls(): try to write <b>sz</b> bytes from chunk
|
|
|
|
* <b>chunk</b> of buffer <b>buf</b> onto socket <b>s</b>. (Tries to write
|
|
|
|
* more if there is a forced pending write size.) On success, deduct the
|
|
|
|
* bytes written from *<b>buf_flushlen</b>. Return the number of bytes
|
|
|
|
* written on success, and a TOR_TLS error code on failue or blocking.
|
|
|
|
*/
|
2007-12-26 01:12:08 +01:00
|
|
|
static INLINE int
|
|
|
|
flush_chunk_tls(tor_tls_t *tls, buf_t *buf, chunk_t *chunk,
|
2007-12-26 20:02:15 +01:00
|
|
|
size_t sz, size_t *buf_flushlen)
|
2007-12-26 01:12:08 +01:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
size_t forced;
|
2007-12-26 20:02:15 +01:00
|
|
|
char *data;
|
2007-12-26 01:12:08 +01:00
|
|
|
|
|
|
|
forced = tor_tls_get_forced_write_size(tls);
|
|
|
|
if (forced > sz)
|
|
|
|
sz = forced;
|
2007-12-26 20:02:15 +01:00
|
|
|
if (chunk) {
|
|
|
|
data = chunk->data;
|
|
|
|
tor_assert(sz <= chunk->datalen);
|
|
|
|
} else {
|
|
|
|
data = NULL;
|
|
|
|
tor_assert(sz == 0);
|
|
|
|
}
|
|
|
|
r = tor_tls_write(tls, data, sz);
|
2007-12-26 01:12:08 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2007-12-26 19:55:56 +01:00
|
|
|
if (*buf_flushlen > (size_t)r)
|
|
|
|
*buf_flushlen -= r;
|
|
|
|
else
|
|
|
|
*buf_flushlen = 0;
|
2007-12-26 01:12:08 +01:00
|
|
|
buf_remove_from_front(buf, r);
|
|
|
|
log_debug(LD_NET,"flushed %d bytes, %d ready to flush, %d remain.",
|
|
|
|
r,(int)*buf_flushlen,(int)buf->datalen);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Write data from <b>buf</b> to the socket <b>s</b>. Write at most
|
2005-10-29 20:19:37 +02:00
|
|
|
* <b>sz</b> bytes, decrement *<b>buf_flushlen</b> by
|
2004-05-09 18:47:25 +02:00
|
|
|
* the number of bytes actually written, and remove the written bytes
|
|
|
|
* from the buffer. Return the number of bytes written on success,
|
|
|
|
* -1 on failure. Return 0 if write() would block.
|
2004-05-02 00:08:43 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
2005-10-29 20:19:37 +02:00
|
|
|
flush_buf(int s, buf_t *buf, size_t sz, size_t *buf_flushlen)
|
2003-09-25 07:17:11 +02:00
|
|
|
{
|
2008-02-16 00:39:04 +01:00
|
|
|
/* XXXX021 It's stupid to overload the return values for these functions:
|
2008-02-06 20:34:32 +01:00
|
|
|
* "error status" and "number of bytes flushed" are not mutually exclusive.
|
|
|
|
*/
|
2005-04-26 22:53:22 +02:00
|
|
|
int r;
|
|
|
|
size_t flushed = 0;
|
2004-10-17 00:14:52 +02:00
|
|
|
tor_assert(buf_flushlen);
|
2007-12-26 01:12:08 +01:00
|
|
|
tor_assert(s >= 0);
|
2004-10-17 00:14:52 +02:00
|
|
|
tor_assert(*buf_flushlen <= buf->datalen);
|
2005-10-29 20:19:37 +02:00
|
|
|
tor_assert(sz <= *buf_flushlen);
|
2002-06-27 00:45:49 +02:00
|
|
|
|
2005-04-27 02:53:44 +02:00
|
|
|
check();
|
2007-12-26 01:12:08 +01:00
|
|
|
while (sz) {
|
|
|
|
size_t flushlen0;
|
|
|
|
tor_assert(buf->head);
|
|
|
|
if (buf->head->datalen >= sz)
|
|
|
|
flushlen0 = sz;
|
|
|
|
else
|
|
|
|
flushlen0 = buf->head->datalen;
|
2005-04-26 22:53:22 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
r = flush_chunk(s, buf, buf->head, flushlen0, buf_flushlen);
|
2005-04-27 02:53:44 +02:00
|
|
|
check();
|
2007-12-26 01:12:08 +01:00
|
|
|
if (r < 0)
|
2005-04-26 22:53:22 +02:00
|
|
|
return r;
|
|
|
|
flushed += r;
|
2007-12-26 01:12:08 +01:00
|
|
|
sz -= r;
|
2007-12-26 23:07:14 +01:00
|
|
|
if (r == 0 || (size_t)r < flushlen0) /* can't flush any more now. */
|
|
|
|
break;
|
2005-04-26 22:53:22 +02:00
|
|
|
}
|
2008-02-22 04:44:36 +01:00
|
|
|
tor_assert(flushed < INT_MAX);
|
|
|
|
return (int)flushed;
|
2005-04-26 22:53:22 +02:00
|
|
|
}
|
2003-10-14 03:34:31 +02:00
|
|
|
|
2008-02-08 22:13:15 +01:00
|
|
|
/** As flush_buf(), but writes data to a TLS connection. Can write more than
|
|
|
|
* <b>flushlen</b> bytes.
|
2004-05-02 00:08:43 +02:00
|
|
|
*/
|
2005-09-30 03:09:52 +02:00
|
|
|
int
|
2008-01-06 01:54:22 +01:00
|
|
|
flush_buf_tls(tor_tls_t *tls, buf_t *buf, size_t flushlen,
|
|
|
|
size_t *buf_flushlen)
|
2003-09-04 18:05:08 +02:00
|
|
|
{
|
|
|
|
int r;
|
2007-12-26 01:12:08 +01:00
|
|
|
size_t flushed = 0;
|
2007-12-26 19:55:56 +01:00
|
|
|
ssize_t sz;
|
2004-10-17 00:14:52 +02:00
|
|
|
tor_assert(buf_flushlen);
|
2005-10-29 20:19:37 +02:00
|
|
|
tor_assert(*buf_flushlen <= buf->datalen);
|
2007-12-26 19:55:56 +01:00
|
|
|
tor_assert(flushlen <= *buf_flushlen);
|
|
|
|
sz = (ssize_t) flushlen;
|
2003-09-07 12:24:40 +02:00
|
|
|
|
|
|
|
/* we want to let tls write even if flushlen is zero, because it might
|
|
|
|
* have a partial record pending */
|
2005-04-23 16:26:02 +02:00
|
|
|
check_no_tls_errors();
|
2005-04-26 22:53:22 +02:00
|
|
|
|
2005-04-27 02:53:44 +02:00
|
|
|
check();
|
2007-12-26 20:02:15 +01:00
|
|
|
do {
|
2007-12-26 01:12:08 +01:00
|
|
|
size_t flushlen0;
|
2007-12-26 01:36:05 +01:00
|
|
|
if (buf->head) {
|
2007-12-26 19:55:56 +01:00
|
|
|
if ((ssize_t)buf->head->datalen >= sz)
|
2007-12-26 01:36:05 +01:00
|
|
|
flushlen0 = sz;
|
|
|
|
else
|
|
|
|
flushlen0 = buf->head->datalen;
|
|
|
|
} else {
|
|
|
|
flushlen0 = 0;
|
|
|
|
}
|
2005-04-26 22:53:22 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
r = flush_chunk_tls(tls, buf, buf->head, flushlen0, buf_flushlen);
|
2005-04-27 02:53:44 +02:00
|
|
|
check();
|
2007-12-26 01:12:08 +01:00
|
|
|
if (r < 0)
|
2005-04-26 22:53:22 +02:00
|
|
|
return r;
|
|
|
|
flushed += r;
|
2007-12-26 01:12:08 +01:00
|
|
|
sz -= r;
|
2007-12-26 23:07:14 +01:00
|
|
|
if (r == 0) /* Can't flush any more now. */
|
|
|
|
break;
|
2007-12-26 20:02:15 +01:00
|
|
|
} while (sz > 0);
|
2008-02-22 04:44:36 +01:00
|
|
|
tor_assert(flushed < INT_MAX);
|
|
|
|
return (int)flushed;
|
2003-09-04 18:05:08 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Append <b>string_len</b> bytes from <b>string</b> to the end of
|
|
|
|
* <b>buf</b>.
|
|
|
|
*
|
2004-05-02 00:08:43 +02:00
|
|
|
* Return the new length of the buffer on success, -1 on failure.
|
|
|
|
*/
|
2005-04-26 22:53:22 +02:00
|
|
|
int
|
|
|
|
write_to_buf(const char *string, size_t string_len, buf_t *buf)
|
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
if (!string_len)
|
2008-02-22 04:44:36 +01:00
|
|
|
return (int)buf->datalen;
|
2007-12-26 01:12:08 +01:00
|
|
|
check();
|
2002-06-27 00:45:49 +02:00
|
|
|
|
2007-12-29 06:16:30 +01:00
|
|
|
while (string_len) {
|
|
|
|
size_t copy;
|
|
|
|
if (!buf->tail || !CHUNK_REMAINING_CAPACITY(buf->tail))
|
|
|
|
buf_add_chunk_with_capacity(buf, string_len, 1);
|
|
|
|
|
|
|
|
copy = CHUNK_REMAINING_CAPACITY(buf->tail);
|
2007-12-26 01:12:08 +01:00
|
|
|
if (copy > string_len)
|
|
|
|
copy = string_len;
|
|
|
|
memcpy(CHUNK_WRITE_PTR(buf->tail), string, copy);
|
|
|
|
string_len -= copy;
|
|
|
|
string += copy;
|
|
|
|
buf->datalen += copy;
|
|
|
|
buf->tail->datalen += copy;
|
2003-12-14 09:15:41 +01:00
|
|
|
}
|
2003-10-14 03:34:31 +02:00
|
|
|
|
2005-04-27 02:53:44 +02:00
|
|
|
check();
|
2008-02-22 04:44:36 +01:00
|
|
|
tor_assert(buf->datalen < INT_MAX);
|
|
|
|
return (int)buf->datalen;
|
2003-03-17 03:42:45 +01:00
|
|
|
}
|
|
|
|
|
2005-06-11 23:17:38 +02:00
|
|
|
/** Helper: copy the first <b>string_len</b> bytes from <b>buf</b>
|
|
|
|
* onto <b>string</b>.
|
2005-06-11 20:52:12 +02:00
|
|
|
*/
|
|
|
|
static INLINE void
|
2007-12-26 01:12:08 +01:00
|
|
|
peek_from_buf(char *string, size_t string_len, const buf_t *buf)
|
2005-04-26 22:53:22 +02:00
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
chunk_t *chunk;
|
2002-06-27 00:45:49 +02:00
|
|
|
|
2004-04-25 22:37:37 +02:00
|
|
|
tor_assert(string);
|
2005-12-14 21:40:40 +01:00
|
|
|
/* make sure we don't ask for too much */
|
|
|
|
tor_assert(string_len <= buf->datalen);
|
2005-11-30 23:48:58 +01:00
|
|
|
/* assert_buf_ok(buf); */
|
2002-06-27 00:45:49 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
chunk = buf->head;
|
|
|
|
while (string_len) {
|
|
|
|
size_t copy = string_len;
|
|
|
|
tor_assert(chunk);
|
|
|
|
if (chunk->datalen < copy)
|
|
|
|
copy = chunk->datalen;
|
|
|
|
memcpy(string, chunk->data, copy);
|
|
|
|
string_len -= copy;
|
|
|
|
string += copy;
|
|
|
|
chunk = chunk->next;
|
2005-04-26 22:53:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-12-14 21:40:40 +01:00
|
|
|
/** Remove <b>string_len</b> bytes from the front of <b>buf</b>, and store
|
|
|
|
* them into <b>string</b>. Return the new buffer size. <b>string_len</b>
|
|
|
|
* must be \<= the number of bytes on the buffer.
|
2005-04-26 22:53:22 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
|
|
|
fetch_from_buf(char *string, size_t string_len, buf_t *buf)
|
2005-04-26 22:53:22 +02:00
|
|
|
{
|
|
|
|
/* There must be string_len bytes in buf; write them onto string,
|
|
|
|
* then memmove buf back (that is, remove them from buf).
|
|
|
|
*
|
|
|
|
* Return the number of bytes still on the buffer. */
|
|
|
|
|
2005-04-27 02:53:44 +02:00
|
|
|
check();
|
2005-04-26 22:53:22 +02:00
|
|
|
peek_from_buf(string, string_len, buf);
|
2003-10-14 03:34:31 +02:00
|
|
|
buf_remove_from_front(buf, string_len);
|
2005-04-27 02:53:44 +02:00
|
|
|
check();
|
2008-02-22 04:44:36 +01:00
|
|
|
tor_assert(buf->datalen < INT_MAX);
|
|
|
|
return (int)buf->datalen;
|
2002-06-27 00:45:49 +02:00
|
|
|
}
|
|
|
|
|
2008-02-08 22:13:15 +01:00
|
|
|
/** Check <b>buf</b> for a variable-length cell according to the rules of link
|
|
|
|
* protocol version <b>linkproto</b>. If one is found, pull it off the buffer
|
|
|
|
* and assign a newly allocated var_cell_t to *<b>out</b>, and return 1.
|
|
|
|
* Return 0 if whatever is on the start of buf_t is not a variable-length
|
|
|
|
* cell. Return 1 and set *<b>out</b> to NULL if there seems to be the start
|
|
|
|
* of a variable-length cell on <b>buf</b>, but the whole thing isn't there
|
|
|
|
* yet. */
|
2007-11-05 22:46:35 +01:00
|
|
|
int
|
2008-02-08 22:13:15 +01:00
|
|
|
fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto)
|
2007-11-05 22:46:35 +01:00
|
|
|
{
|
|
|
|
char hdr[VAR_CELL_HEADER_SIZE];
|
|
|
|
var_cell_t *result;
|
|
|
|
uint8_t command;
|
|
|
|
uint16_t length;
|
2008-02-08 22:13:15 +01:00
|
|
|
/* If linkproto is unknown (0) or v2 (2), variable-length cells work as
|
|
|
|
* implemented here. If it's 1, there are no variable-length cells. Tor
|
|
|
|
* does not support other versions right now, and so can't negotiate them.
|
|
|
|
*/
|
|
|
|
if (linkproto == 1)
|
|
|
|
return 0;
|
2007-11-05 22:46:35 +01:00
|
|
|
check();
|
|
|
|
*out = NULL;
|
|
|
|
if (buf->datalen < VAR_CELL_HEADER_SIZE)
|
|
|
|
return 0;
|
|
|
|
peek_from_buf(hdr, sizeof(hdr), buf);
|
|
|
|
|
|
|
|
command = *(uint8_t*)(hdr+2);
|
|
|
|
if (!(CELL_COMMAND_IS_VAR_LENGTH(command)))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
length = ntohs(get_uint16(hdr+3));
|
|
|
|
if (buf->datalen < (size_t)(VAR_CELL_HEADER_SIZE+length))
|
|
|
|
return 1;
|
2007-11-06 00:34:39 +01:00
|
|
|
result = var_cell_new(length);
|
2007-11-05 22:46:35 +01:00
|
|
|
result->command = command;
|
|
|
|
result->circ_id = ntohs(*(uint16_t*)hdr);
|
|
|
|
|
|
|
|
buf_remove_from_front(buf, VAR_CELL_HEADER_SIZE);
|
|
|
|
peek_from_buf(result->payload, length, buf);
|
|
|
|
buf_remove_from_front(buf, length);
|
|
|
|
check();
|
|
|
|
|
|
|
|
*out = result;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2007-04-25 09:20:04 +02:00
|
|
|
/** Move up to *<b>buf_flushlen</b> bytes from <b>buf_in</b> to
|
|
|
|
* <b>buf_out</b>, and modify *<b>buf_flushlen</b> appropriately.
|
2007-04-21 19:26:12 +02:00
|
|
|
* Return the number of bytes actually copied.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
move_buf_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen)
|
|
|
|
{
|
2008-02-06 06:31:21 +01:00
|
|
|
/* XXXX we can do way better here, but this doesn't turn up in any
|
|
|
|
* profiles. */
|
2007-04-21 19:26:12 +02:00
|
|
|
char b[4096];
|
|
|
|
size_t cp, len;
|
|
|
|
len = *buf_flushlen;
|
|
|
|
if (len > buf_in->datalen)
|
|
|
|
len = buf_in->datalen;
|
|
|
|
|
|
|
|
cp = len; /* Remember the number of bytes we intend to copy. */
|
2008-02-22 04:44:36 +01:00
|
|
|
tor_assert(cp < INT_MAX);
|
2007-04-21 19:26:12 +02:00
|
|
|
while (len) {
|
|
|
|
/* This isn't the most efficient implementation one could imagine, since
|
|
|
|
* it does two copies instead of 1, but I kinda doubt that this will be
|
|
|
|
* critical path. */
|
|
|
|
size_t n = len > sizeof(b) ? sizeof(b) : len;
|
|
|
|
fetch_from_buf(b, n, buf_in);
|
|
|
|
write_to_buf(b, n, buf_out);
|
|
|
|
len -= n;
|
|
|
|
}
|
|
|
|
*buf_flushlen -= cp;
|
2008-02-22 04:44:36 +01:00
|
|
|
return (int)cp;
|
2007-04-21 19:26:12 +02:00
|
|
|
}
|
|
|
|
|
2007-12-30 01:13:07 +01:00
|
|
|
/** Internal structure: represents a position in a buffer. */
|
2007-12-29 18:36:03 +01:00
|
|
|
typedef struct buf_pos_t {
|
2007-12-30 01:13:07 +01:00
|
|
|
const chunk_t *chunk; /**< Which chunk are we pointing to? */
|
2008-02-21 22:15:31 +01:00
|
|
|
int pos;/**< Which character inside the chunk's data are we pointing to? */
|
2008-02-20 18:48:39 +01:00
|
|
|
size_t chunk_pos; /**< Total length of all previous chunks. */
|
2007-12-30 01:13:07 +01:00
|
|
|
} buf_pos_t;
|
2007-12-29 18:36:03 +01:00
|
|
|
|
2007-12-30 01:13:07 +01:00
|
|
|
/** Initialize <b>out</b> to point to the first character of <b>buf</b>.*/
|
2007-12-29 18:36:03 +01:00
|
|
|
static void
|
2007-12-30 01:13:07 +01:00
|
|
|
buf_pos_init(const buf_t *buf, buf_pos_t *out)
|
2007-12-29 18:36:03 +01:00
|
|
|
{
|
|
|
|
out->chunk = buf->head;
|
|
|
|
out->pos = 0;
|
2008-02-20 18:48:39 +01:00
|
|
|
out->chunk_pos = 0;
|
2007-12-29 18:36:03 +01:00
|
|
|
}
|
|
|
|
|
2007-12-30 01:13:07 +01:00
|
|
|
/** Advance <b>out</b> to the first appearance of <b>ch</b> at the current
|
|
|
|
* position of <b>out</b>, or later. Return -1 if no instances are found;
|
|
|
|
* otherwise returns the absolute position of the character. */
|
2008-02-22 04:44:36 +01:00
|
|
|
static off_t
|
2007-12-30 01:13:07 +01:00
|
|
|
buf_find_pos_of_char(char ch, buf_pos_t *out)
|
2007-12-29 18:36:03 +01:00
|
|
|
{
|
2007-12-30 01:13:07 +01:00
|
|
|
const chunk_t *chunk;
|
2008-02-20 17:57:39 +01:00
|
|
|
int pos;
|
2008-02-20 18:48:39 +01:00
|
|
|
tor_assert(out);
|
2008-02-21 00:38:57 +01:00
|
|
|
if (out->chunk) {
|
2008-02-21 00:58:48 +01:00
|
|
|
if (out->chunk->datalen) {
|
2008-02-21 00:38:57 +01:00
|
|
|
/*XXXX020 remove this once the bug it detects is fixed. */
|
2008-02-21 00:58:48 +01:00
|
|
|
if (!(out->pos < (off_t)out->chunk->datalen)) {
|
|
|
|
log_warn(LD_BUG, "About to assert. %p, %d, %d, %p, %d.",
|
|
|
|
out, (int)out->pos,
|
|
|
|
(int)out->chunk_pos, out->chunk,
|
|
|
|
out->chunk?(int)out->chunk->datalen : (int)-1
|
|
|
|
);
|
|
|
|
}
|
|
|
|
tor_assert(out->pos < (off_t)out->chunk->datalen);
|
|
|
|
} else {
|
|
|
|
tor_assert(out->pos == 0);
|
2008-02-21 00:38:57 +01:00
|
|
|
}
|
|
|
|
}
|
2008-02-20 17:57:39 +01:00
|
|
|
pos = out->pos;
|
2007-12-29 18:36:03 +01:00
|
|
|
for (chunk = out->chunk; chunk; chunk = chunk->next) {
|
2008-02-21 22:15:31 +01:00
|
|
|
char *cp = memchr(chunk->data+pos, ch, chunk->datalen - pos);
|
2007-12-29 18:36:03 +01:00
|
|
|
if (cp) {
|
|
|
|
out->chunk = chunk;
|
2008-02-22 04:44:36 +01:00
|
|
|
tor_assert(cp - chunk->data < INT_MAX);
|
|
|
|
out->pos = (int)(cp - chunk->data);
|
2008-02-20 18:48:39 +01:00
|
|
|
return out->chunk_pos + out->pos;
|
2007-12-29 18:36:03 +01:00
|
|
|
} else {
|
2008-02-20 18:48:39 +01:00
|
|
|
out->chunk_pos += chunk->datalen;
|
2007-12-29 18:36:03 +01:00
|
|
|
pos = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-12-30 01:13:07 +01:00
|
|
|
/** Advance <b>pos</b> by a single character, if there are any more characters
|
|
|
|
* in the buffer. Returns 0 on sucess, -1 on failure. */
|
2007-12-29 18:36:03 +01:00
|
|
|
static INLINE int
|
|
|
|
buf_pos_inc(buf_pos_t *pos)
|
|
|
|
{
|
2008-02-20 18:48:39 +01:00
|
|
|
++pos->pos;
|
2008-02-20 22:57:48 +01:00
|
|
|
if (pos->pos == (off_t)pos->chunk->datalen) {
|
2007-12-29 18:36:03 +01:00
|
|
|
if (!pos->chunk->next)
|
|
|
|
return -1;
|
2008-02-20 18:48:39 +01:00
|
|
|
pos->chunk_pos += pos->chunk->datalen;
|
2007-12-29 18:36:03 +01:00
|
|
|
pos->chunk = pos->chunk->next;
|
|
|
|
pos->pos = 0;
|
|
|
|
}
|
2007-12-30 01:13:07 +01:00
|
|
|
return 0;
|
2007-12-29 18:36:03 +01:00
|
|
|
}
|
|
|
|
|
2007-12-30 01:13:07 +01:00
|
|
|
/** Return true iff the <b>n</b>-character string in <b>s</b> appears
|
|
|
|
* (verbatim) at <b>pos</b>. */
|
2007-12-29 18:36:03 +01:00
|
|
|
static int
|
2008-02-19 23:52:50 +01:00
|
|
|
buf_matches_at_pos(const buf_pos_t *pos, const char *s, size_t n)
|
2007-12-29 18:36:03 +01:00
|
|
|
{
|
|
|
|
buf_pos_t p;
|
2008-02-21 03:10:38 +01:00
|
|
|
if (!n)
|
|
|
|
return 1;
|
|
|
|
|
2007-12-30 01:13:07 +01:00
|
|
|
memcpy(&p, pos, sizeof(p));
|
2007-12-29 18:36:03 +01:00
|
|
|
|
2008-02-21 03:10:38 +01:00
|
|
|
while (1) {
|
2007-12-30 01:13:07 +01:00
|
|
|
char ch = p.chunk->data[p.pos];
|
2007-12-29 18:36:03 +01:00
|
|
|
if (ch != *s)
|
|
|
|
return 0;
|
|
|
|
++s;
|
2008-02-21 03:10:38 +01:00
|
|
|
/* If we're out of characters that don't match, we match. Check this
|
|
|
|
* _before_ we test incrementing pos, in case we're at the end of the
|
|
|
|
* string. */
|
|
|
|
if (--n == 0)
|
|
|
|
return 1;
|
2007-12-30 01:13:07 +01:00
|
|
|
if (buf_pos_inc(&p)<0)
|
2007-12-29 18:36:03 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-12-30 01:13:07 +01:00
|
|
|
/** Return the first position in <b>buf</b> at which the <b>n</b>-character
|
|
|
|
* string <b>s</b> occurs, or -1 if it does not occur. */
|
2008-02-21 00:20:36 +01:00
|
|
|
/*private*/ int
|
2008-02-19 23:52:50 +01:00
|
|
|
buf_find_string_offset(const buf_t *buf, const char *s, size_t n)
|
2007-12-29 18:36:03 +01:00
|
|
|
{
|
|
|
|
buf_pos_t pos;
|
|
|
|
buf_pos_init(buf, &pos);
|
2007-12-30 01:13:07 +01:00
|
|
|
while (buf_find_pos_of_char(*s, &pos) >= 0) {
|
|
|
|
if (buf_matches_at_pos(&pos, s, n)) {
|
2008-02-22 04:44:36 +01:00
|
|
|
tor_assert(pos.chunk_pos + pos.pos < INT_MAX);
|
|
|
|
return (int)(pos.chunk_pos + pos.pos);
|
2007-12-29 18:36:03 +01:00
|
|
|
} else {
|
2007-12-30 01:13:07 +01:00
|
|
|
if (buf_pos_inc(&pos)<0)
|
2007-12-29 18:36:03 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** There is a (possibly incomplete) http statement on <b>buf</b>, of the
|
2004-05-07 19:04:12 +02:00
|
|
|
* form "\%s\\r\\n\\r\\n\%s", headers, body. (body may contain nuls.)
|
2003-09-17 22:09:06 +02:00
|
|
|
* If a) the headers include a Content-Length field and all bytes in
|
|
|
|
* the body are present, or b) there's no Content-Length field and
|
|
|
|
* all headers are present, then:
|
2004-03-31 07:01:30 +02:00
|
|
|
*
|
2004-05-09 18:47:25 +02:00
|
|
|
* - strdup headers into <b>*headers_out</b>, and nul-terminate it.
|
|
|
|
* - memdup body into <b>*body_out</b>, and nul-terminate it.
|
|
|
|
* - Then remove them from <b>buf</b>, and return 1.
|
2004-05-07 19:04:12 +02:00
|
|
|
*
|
|
|
|
* - If headers or body is NULL, discard that part of the buf.
|
|
|
|
* - If a headers or body doesn't fit in the arg, return -1.
|
2004-10-12 22:22:09 +02:00
|
|
|
* (We ensure that the headers or body don't exceed max len,
|
|
|
|
* _even if_ we're planning to discard them.)
|
2005-10-14 04:26:13 +02:00
|
|
|
* - If force_complete is true, then succeed even if not all of the
|
|
|
|
* content has arrived.
|
2003-12-17 22:09:31 +01:00
|
|
|
*
|
2003-09-17 22:09:06 +02:00
|
|
|
* Else, change nothing and return 0.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
|
|
|
fetch_from_buf_http(buf_t *buf,
|
|
|
|
char **headers_out, size_t max_headerlen,
|
2005-10-14 04:26:13 +02:00
|
|
|
char **body_out, size_t *body_used, size_t max_bodylen,
|
|
|
|
int force_complete)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2007-12-30 01:13:07 +01:00
|
|
|
char *headers, *p;
|
2004-10-14 04:47:09 +02:00
|
|
|
size_t headerlen, bodylen, contentlen;
|
2007-12-29 18:36:03 +01:00
|
|
|
int crlf_offset;
|
2003-09-17 22:09:06 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
check();
|
|
|
|
if (!buf->head)
|
|
|
|
return 0;
|
2003-09-17 22:09:06 +02:00
|
|
|
|
2007-12-29 18:36:03 +01:00
|
|
|
crlf_offset = buf_find_string_offset(buf, "\r\n\r\n", 4);
|
2007-12-30 01:13:07 +01:00
|
|
|
if (crlf_offset > (int)max_headerlen ||
|
2007-12-29 18:36:03 +01:00
|
|
|
(crlf_offset < 0 && buf->datalen > max_headerlen)) {
|
|
|
|
log_debug(LD_HTTP,"headers too long.");
|
|
|
|
return -1;
|
|
|
|
} else if (crlf_offset < 0) {
|
|
|
|
log_debug(LD_HTTP,"headers not all here yet.");
|
|
|
|
return 0;
|
|
|
|
}
|
2007-12-30 01:13:07 +01:00
|
|
|
/* Okay, we have a full header. Make sure it all appears in the first
|
|
|
|
* chunk. */
|
|
|
|
if ((int)buf->head->datalen < crlf_offset + 4)
|
2007-12-29 18:36:03 +01:00
|
|
|
buf_pullup(buf, crlf_offset+4, 0);
|
|
|
|
headerlen = crlf_offset + 4;
|
|
|
|
|
2008-02-21 03:10:38 +01:00
|
|
|
headers = buf->head->data;
|
2003-09-25 07:17:11 +02:00
|
|
|
bodylen = buf->datalen - headerlen;
|
2006-02-13 09:28:42 +01:00
|
|
|
log_debug(LD_HTTP,"headerlen %d, bodylen %d.", (int)headerlen, (int)bodylen);
|
2003-09-17 22:09:06 +02:00
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (max_headerlen <= headerlen) {
|
2006-02-13 09:28:42 +01:00
|
|
|
log_warn(LD_HTTP,"headerlen %d larger than %d. Failing.",
|
|
|
|
(int)headerlen, (int)max_headerlen-1);
|
2003-09-17 22:09:06 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-11-28 10:05:49 +01:00
|
|
|
if (max_bodylen <= bodylen) {
|
2006-02-13 09:28:42 +01:00
|
|
|
log_warn(LD_HTTP,"bodylen %d larger than %d. Failing.",
|
|
|
|
(int)bodylen, (int)max_bodylen-1);
|
2003-09-17 22:09:06 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2003-09-21 08:15:43 +02:00
|
|
|
#define CONTENT_LENGTH "\r\nContent-Length: "
|
2007-12-29 18:36:03 +01:00
|
|
|
p = (char*) tor_memstr(headers, headerlen, CONTENT_LENGTH);
|
2004-05-18 17:35:21 +02:00
|
|
|
if (p) {
|
2004-10-14 04:47:09 +02:00
|
|
|
int i;
|
|
|
|
i = atoi(p+strlen(CONTENT_LENGTH));
|
|
|
|
if (i < 0) {
|
2006-02-13 09:28:42 +01:00
|
|
|
log_warn(LD_PROTOCOL, "Content-Length is less than zero; it looks like "
|
|
|
|
"someone is trying to crash us.");
|
2004-10-12 20:38:36 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-10-14 04:47:09 +02:00
|
|
|
contentlen = i;
|
2004-03-31 07:01:30 +02:00
|
|
|
/* if content-length is malformed, then our body length is 0. fine. */
|
2006-02-13 09:28:42 +01:00
|
|
|
log_debug(LD_HTTP,"Got a contentlen of %d.",(int)contentlen);
|
2004-11-28 10:05:49 +01:00
|
|
|
if (bodylen < contentlen) {
|
2005-10-14 04:26:13 +02:00
|
|
|
if (!force_complete) {
|
2006-02-13 09:28:42 +01:00
|
|
|
log_debug(LD_HTTP,"body not all here yet.");
|
2005-10-14 04:26:13 +02:00
|
|
|
return 0; /* not all there yet */
|
|
|
|
}
|
2003-09-17 22:09:06 +02:00
|
|
|
}
|
2004-11-28 10:05:49 +01:00
|
|
|
if (bodylen > contentlen) {
|
2004-04-26 23:15:06 +02:00
|
|
|
bodylen = contentlen;
|
2006-02-13 09:28:42 +01:00
|
|
|
log_debug(LD_HTTP,"bodylen reduced to %d.",(int)bodylen);
|
2004-04-26 23:15:06 +02:00
|
|
|
}
|
2003-09-17 22:09:06 +02:00
|
|
|
}
|
|
|
|
/* all happy. copy into the appropriate places, and return 1 */
|
2004-11-28 10:05:49 +01:00
|
|
|
if (headers_out) {
|
2003-12-17 10:42:28 +01:00
|
|
|
*headers_out = tor_malloc(headerlen+1);
|
2007-12-26 01:12:08 +01:00
|
|
|
fetch_from_buf(*headers_out, headerlen, buf);
|
2006-07-15 21:21:30 +02:00
|
|
|
(*headers_out)[headerlen] = 0; /* nul terminate it */
|
2003-09-17 22:09:06 +02:00
|
|
|
}
|
2004-11-28 10:05:49 +01:00
|
|
|
if (body_out) {
|
2004-04-25 22:37:37 +02:00
|
|
|
tor_assert(body_used);
|
2004-03-31 07:01:30 +02:00
|
|
|
*body_used = bodylen;
|
2003-12-17 10:42:28 +01:00
|
|
|
*body_out = tor_malloc(bodylen+1);
|
2007-12-26 01:12:08 +01:00
|
|
|
fetch_from_buf(*body_out, bodylen, buf);
|
2006-07-15 21:21:30 +02:00
|
|
|
(*body_out)[bodylen] = 0; /* nul terminate it */
|
2003-09-17 22:09:06 +02:00
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
check();
|
2003-09-17 22:09:06 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** There is a (possibly incomplete) socks handshake on <b>buf</b>, of one
|
2003-10-04 03:37:01 +02:00
|
|
|
* of the forms
|
2004-05-07 19:04:12 +02:00
|
|
|
* - socks4: "socksheader username\\0"
|
|
|
|
* - socks4a: "socksheader username\\0 destaddr\\0"
|
|
|
|
* - socks5 phase one: "version #methods methods"
|
|
|
|
* - socks5 phase two: "version command 0 addresstype..."
|
2003-11-11 03:41:31 +01:00
|
|
|
* If it's a complete and valid handshake, and destaddr fits in
|
|
|
|
* MAX_SOCKS_ADDR_LEN bytes, then pull the handshake off the buf,
|
2004-05-09 18:47:25 +02:00
|
|
|
* assign to <b>req</b>, and return 1.
|
2004-05-07 19:04:12 +02:00
|
|
|
*
|
2003-09-18 10:11:31 +02:00
|
|
|
* If it's invalid or too big, return -1.
|
2004-05-07 19:04:12 +02:00
|
|
|
*
|
2003-10-04 03:37:01 +02:00
|
|
|
* Else it's not all there yet, leave buf alone and return 0.
|
2004-05-07 19:04:12 +02:00
|
|
|
*
|
2004-05-09 18:47:25 +02:00
|
|
|
* If you want to specify the socks reply, write it into <b>req->reply</b>
|
|
|
|
* and set <b>req->replylen</b>, else leave <b>req->replylen</b> alone.
|
2004-05-07 19:04:12 +02:00
|
|
|
*
|
2005-11-17 00:37:35 +01:00
|
|
|
* If <b>log_sockstype</b> is non-zero, then do a notice-level log of whether
|
|
|
|
* the connection is possibly leaking DNS requests locally or not.
|
|
|
|
*
|
2006-03-19 02:44:53 +01:00
|
|
|
* If <b>safe_socks</b> is true, then reject unsafe socks protocols.
|
|
|
|
*
|
2005-12-14 21:40:40 +01:00
|
|
|
* If returning 0 or -1, <b>req->address</b> and <b>req->port</b> are
|
|
|
|
* undefined.
|
2003-09-18 10:11:31 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
2006-03-19 02:44:53 +01:00
|
|
|
fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
|
|
|
|
int log_sockstype, int safe_socks)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2007-05-15 23:17:48 +02:00
|
|
|
unsigned int len;
|
2005-02-22 09:18:36 +01:00
|
|
|
char tmpbuf[INET_NTOA_BUF_LEN];
|
2003-10-04 03:37:01 +02:00
|
|
|
uint32_t destip;
|
2007-12-26 01:12:08 +01:00
|
|
|
uint8_t socksver;
|
2003-10-04 03:37:01 +02:00
|
|
|
enum {socks4, socks4a} socks4_prot = socks4a;
|
2003-09-18 10:11:31 +02:00
|
|
|
char *next, *startaddr;
|
2003-10-04 03:37:01 +02:00
|
|
|
struct in_addr in;
|
2003-09-18 10:11:31 +02:00
|
|
|
|
2004-08-04 03:08:10 +02:00
|
|
|
/* If the user connects with socks4 or the wrong variant of socks5,
|
|
|
|
* then log a warning to let him know that it might be unwise. */
|
|
|
|
static int have_warned_about_unsafe_socks = 0;
|
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (buf->datalen < 2) /* version and another byte */
|
2003-09-18 10:11:31 +02:00
|
|
|
return 0;
|
2005-04-26 22:53:22 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
buf_pullup(buf, 128, 0);
|
|
|
|
tor_assert(buf->head && buf->head->datalen >= 2);
|
|
|
|
|
|
|
|
socksver = *buf->head->data;
|
|
|
|
|
|
|
|
switch (socksver) { /* which version of socks? */
|
2003-10-04 03:37:01 +02:00
|
|
|
|
|
|
|
case 5: /* socks5 */
|
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (req->socks_version != 5) { /* we need to negotiate a method */
|
2007-12-26 01:12:08 +01:00
|
|
|
unsigned char nummethods = (unsigned char)*(buf->head->data+1);
|
2004-04-25 22:37:37 +02:00
|
|
|
tor_assert(!req->socks_version);
|
2004-11-28 10:05:49 +01:00
|
|
|
if (buf->datalen < 2u+nummethods)
|
2003-10-04 03:37:01 +02:00
|
|
|
return 0;
|
2007-12-26 01:12:08 +01:00
|
|
|
buf_pullup(buf, 2u+nummethods, 0);
|
|
|
|
if (!nummethods || !memchr(buf->head->data+2, 0, nummethods)) {
|
2006-02-13 09:28:42 +01:00
|
|
|
log_warn(LD_APP,
|
|
|
|
"socks5: offered methods don't include 'no auth'. "
|
|
|
|
"Rejecting.");
|
2003-11-11 03:41:31 +01:00
|
|
|
req->replylen = 2; /* 2 bytes of response */
|
2005-03-01 23:16:15 +01:00
|
|
|
req->reply[0] = 5;
|
2004-03-09 23:01:17 +01:00
|
|
|
req->reply[1] = '\xFF'; /* reject all methods */
|
2003-10-04 03:37:01 +02:00
|
|
|
return -1;
|
2003-11-16 18:00:02 +01:00
|
|
|
}
|
2006-08-24 06:51:55 +02:00
|
|
|
/* remove packet from buf. also remove any other extraneous
|
|
|
|
* bytes, to support broken socks clients. */
|
|
|
|
buf_clear(buf);
|
2003-10-04 03:37:01 +02:00
|
|
|
|
2003-11-11 03:41:31 +01:00
|
|
|
req->replylen = 2; /* 2 bytes of response */
|
|
|
|
req->reply[0] = 5; /* socks5 reply */
|
2008-05-04 11:36:48 +02:00
|
|
|
req->reply[1] = 0; /* tell client to use "none" auth method */
|
2005-12-14 21:40:40 +01:00
|
|
|
req->socks_version = 5; /* remember we've already negotiated auth */
|
2006-02-13 09:28:42 +01:00
|
|
|
log_debug(LD_APP,"socks5: accepted method 0");
|
2003-10-04 03:37:01 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* we know the method; read in the request */
|
2006-02-13 09:28:42 +01:00
|
|
|
log_debug(LD_APP,"socks5: checking request");
|
2004-11-28 10:05:49 +01:00
|
|
|
if (buf->datalen < 8) /* basic info plus >=2 for addr plus 2 for port */
|
2003-10-04 03:37:01 +02:00
|
|
|
return 0; /* not yet */
|
2007-12-26 01:12:08 +01:00
|
|
|
tor_assert(buf->head->datalen >= 8);
|
|
|
|
req->command = (unsigned char) *(buf->head->data+1);
|
2004-11-28 10:05:49 +01:00
|
|
|
if (req->command != SOCKS_COMMAND_CONNECT &&
|
2006-09-22 02:43:55 +02:00
|
|
|
req->command != SOCKS_COMMAND_RESOLVE &&
|
|
|
|
req->command != SOCKS_COMMAND_RESOLVE_PTR) {
|
|
|
|
/* not a connect or resolve or a resolve_ptr? we don't support it. */
|
2006-02-13 09:28:42 +01:00
|
|
|
log_warn(LD_APP,"socks5: command %d not recognized. Rejecting.",
|
|
|
|
req->command);
|
2003-10-04 03:37:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
switch (*(buf->head->data+3)) { /* address type */
|
2003-10-04 03:37:01 +02:00
|
|
|
case 1: /* IPv4 address */
|
2006-02-13 09:28:42 +01:00
|
|
|
log_debug(LD_APP,"socks5: ipv4 address type");
|
2004-11-28 10:05:49 +01:00
|
|
|
if (buf->datalen < 10) /* ip/port there? */
|
2003-10-04 03:37:01 +02:00
|
|
|
return 0; /* not yet */
|
2004-08-07 06:03:01 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
destip = ntohl(*(uint32_t*)(buf->head->data+4));
|
2003-10-04 03:37:01 +02:00
|
|
|
in.s_addr = htonl(destip);
|
2005-02-22 09:18:36 +01:00
|
|
|
tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
|
2004-11-28 10:05:49 +01:00
|
|
|
if (strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN) {
|
2006-02-13 09:28:42 +01:00
|
|
|
log_warn(LD_APP,
|
|
|
|
"socks5 IP takes %d bytes, which doesn't fit in %d. "
|
|
|
|
"Rejecting.",
|
|
|
|
(int)strlen(tmpbuf)+1,(int)MAX_SOCKS_ADDR_LEN);
|
2003-10-04 03:37:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-10-27 08:48:16 +02:00
|
|
|
strlcpy(req->address,tmpbuf,sizeof(req->address));
|
2007-12-26 01:12:08 +01:00
|
|
|
req->port = ntohs(*(uint16_t*)(buf->head->data+8));
|
2003-10-14 03:34:31 +02:00
|
|
|
buf_remove_from_front(buf, 10);
|
2006-09-22 02:43:55 +02:00
|
|
|
if (req->command != SOCKS_COMMAND_RESOLVE_PTR &&
|
|
|
|
!addressmap_have_mapping(req->address) &&
|
2005-09-24 23:56:04 +02:00
|
|
|
!have_warned_about_unsafe_socks) {
|
2006-02-13 09:28:42 +01:00
|
|
|
log_warn(LD_APP,
|
2007-10-10 17:44:34 +02:00
|
|
|
"Your application (using socks5 to port %d) is giving "
|
2006-02-13 09:28:42 +01:00
|
|
|
"Tor only an IP address. Applications that do DNS resolves "
|
|
|
|
"themselves may leak information. Consider using Socks4A "
|
2006-10-15 09:42:51 +02:00
|
|
|
"(e.g. via privoxy or socat) instead. For more information, "
|
2006-02-13 09:28:42 +01:00
|
|
|
"please see http://wiki.noreply.org/noreply/TheOnionRouter/"
|
2006-03-19 02:44:53 +01:00
|
|
|
"TorFAQ#SOCKSAndDNS.%s", req->port,
|
|
|
|
safe_socks ? " Rejecting." : "");
|
2004-08-07 06:03:01 +02:00
|
|
|
// have_warned_about_unsafe_socks = 1; // (for now, warn every time)
|
2007-01-06 06:42:31 +01:00
|
|
|
control_event_client_status(LOG_WARN,
|
|
|
|
"DANGEROUS_SOCKS PROTOCOL=SOCKS5 ADDRESS=%s:%d",
|
|
|
|
req->address, req->port);
|
2006-03-19 02:44:53 +01:00
|
|
|
if (safe_socks)
|
|
|
|
return -1;
|
2004-08-07 06:03:01 +02:00
|
|
|
}
|
2003-10-04 03:37:01 +02:00
|
|
|
return 1;
|
|
|
|
case 3: /* fqdn */
|
2006-02-13 09:28:42 +01:00
|
|
|
log_debug(LD_APP,"socks5: fqdn address type");
|
2007-05-15 23:17:48 +02:00
|
|
|
if (req->command == SOCKS_COMMAND_RESOLVE_PTR) {
|
|
|
|
log_warn(LD_APP, "socks5 received RESOLVE_PTR command with "
|
|
|
|
"hostname type. Rejecting.");
|
|
|
|
return -1;
|
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
len = (unsigned char)*(buf->head->data+4);
|
2007-05-15 23:17:48 +02:00
|
|
|
if (buf->datalen < 7+len) /* addr/port there? */
|
2003-10-04 03:37:01 +02:00
|
|
|
return 0; /* not yet */
|
2007-12-26 01:12:08 +01:00
|
|
|
buf_pullup(buf, 7+len, 0);
|
|
|
|
tor_assert(buf->head->datalen >= 7+len);
|
2004-11-28 10:05:49 +01:00
|
|
|
if (len+1 > MAX_SOCKS_ADDR_LEN) {
|
2006-02-13 09:28:42 +01:00
|
|
|
log_warn(LD_APP,
|
|
|
|
"socks5 hostname is %d bytes, which doesn't fit in "
|
|
|
|
"%d. Rejecting.", len+1,MAX_SOCKS_ADDR_LEN);
|
2003-10-04 03:37:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
memcpy(req->address,buf->head->data+5,len);
|
2003-11-16 18:00:02 +01:00
|
|
|
req->address[len] = 0;
|
2007-12-26 01:12:08 +01:00
|
|
|
req->port = ntohs(get_uint16(buf->head->data+5+len));
|
2003-10-14 03:34:31 +02:00
|
|
|
buf_remove_from_front(buf, 5+len+2);
|
2006-03-16 00:36:57 +01:00
|
|
|
if (!tor_strisprint(req->address) || strchr(req->address,'\"')) {
|
|
|
|
log_warn(LD_PROTOCOL,
|
2007-10-10 17:44:34 +02:00
|
|
|
"Your application (using socks5 to port %d) gave Tor "
|
2006-03-16 00:36:57 +01:00
|
|
|
"a malformed hostname: %s. Rejecting the connection.",
|
|
|
|
req->port, escaped(req->address));
|
|
|
|
return -1;
|
|
|
|
}
|
2005-11-17 00:37:35 +01:00
|
|
|
if (log_sockstype)
|
2006-02-13 09:28:42 +01:00
|
|
|
log_notice(LD_APP,
|
2007-10-10 17:44:34 +02:00
|
|
|
"Your application (using socks5 to port %d) gave "
|
2006-02-13 09:28:42 +01:00
|
|
|
"Tor a hostname, which means Tor will do the DNS resolve "
|
|
|
|
"for you. This is good.", req->port);
|
2003-10-04 03:37:01 +02:00
|
|
|
return 1;
|
|
|
|
default: /* unsupported */
|
2006-02-13 09:28:42 +01:00
|
|
|
log_warn(LD_APP,"socks5: unsupported address type %d. Rejecting.",
|
2007-12-26 01:12:08 +01:00
|
|
|
(int) *(buf->head->data+3));
|
2003-10-04 03:37:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-04-25 22:37:37 +02:00
|
|
|
tor_assert(0);
|
2003-10-04 03:37:01 +02:00
|
|
|
case 4: /* socks4 */
|
2004-03-08 00:50:15 +01:00
|
|
|
/* http://archive.socks.permeo.com/protocol/socks4.protocol */
|
|
|
|
/* http://archive.socks.permeo.com/protocol/socks4a.protocol */
|
2003-10-04 03:37:01 +02:00
|
|
|
|
2003-11-11 03:41:31 +01:00
|
|
|
req->socks_version = 4;
|
2004-11-28 10:05:49 +01:00
|
|
|
if (buf->datalen < SOCKS4_NETWORK_LEN) /* basic info available? */
|
2003-10-04 03:37:01 +02:00
|
|
|
return 0; /* not yet */
|
2007-12-26 01:12:08 +01:00
|
|
|
buf_pullup(buf, 1280, 0);
|
|
|
|
req->command = (unsigned char) *(buf->head->data+1);
|
2004-11-28 10:05:49 +01:00
|
|
|
if (req->command != SOCKS_COMMAND_CONNECT &&
|
2004-11-28 12:39:53 +01:00
|
|
|
req->command != SOCKS_COMMAND_RESOLVE) {
|
2006-09-22 02:43:55 +02:00
|
|
|
/* not a connect or resolve? we don't support it. (No resolve_ptr with
|
|
|
|
* socks4.) */
|
2006-02-13 09:28:42 +01:00
|
|
|
log_warn(LD_APP,"socks4: command %d not recognized. Rejecting.",
|
|
|
|
req->command);
|
2003-10-04 03:37:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
req->port = ntohs(*(uint16_t*)(buf->head->data+2));
|
|
|
|
destip = ntohl(*(uint32_t*)(buf->head->data+4));
|
2004-11-28 10:05:49 +01:00
|
|
|
if ((!req->port && req->command!=SOCKS_COMMAND_RESOLVE) || !destip) {
|
2006-02-13 09:28:42 +01:00
|
|
|
log_warn(LD_APP,"socks4: Port or DestIP is zero. Rejecting.");
|
2003-10-04 03:37:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-11-28 10:05:49 +01:00
|
|
|
if (destip >> 8) {
|
2006-02-13 09:28:42 +01:00
|
|
|
log_debug(LD_APP,"socks4: destip not in form 0.0.0.x.");
|
2003-10-04 03:37:01 +02:00
|
|
|
in.s_addr = htonl(destip);
|
2005-02-22 09:18:36 +01:00
|
|
|
tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
|
2004-11-28 10:05:49 +01:00
|
|
|
if (strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN) {
|
2006-02-13 09:28:42 +01:00
|
|
|
log_debug(LD_APP,"socks4 addr (%d bytes) too long. Rejecting.",
|
|
|
|
(int)strlen(tmpbuf));
|
2003-10-04 03:37:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2006-02-13 09:28:42 +01:00
|
|
|
log_debug(LD_APP,
|
2006-03-05 10:50:26 +01:00
|
|
|
"socks4: successfully read destip (%s)", safe_str(tmpbuf));
|
2003-10-04 03:37:01 +02:00
|
|
|
socks4_prot = socks4;
|
|
|
|
}
|
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
next = memchr(buf->head->data+SOCKS4_NETWORK_LEN, 0,
|
|
|
|
buf->head->datalen-SOCKS4_NETWORK_LEN);
|
2004-11-28 10:05:49 +01:00
|
|
|
if (!next) {
|
2007-12-26 01:12:08 +01:00
|
|
|
if (buf->head->datalen >= 1024) {
|
|
|
|
log_debug(LD_APP, "Socks4 user name too long; rejecting.");
|
|
|
|
return -1;
|
|
|
|
}
|
2006-02-13 09:28:42 +01:00
|
|
|
log_debug(LD_APP,"socks4: Username not here yet.");
|
2003-10-04 03:37:01 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
tor_assert(next < CHUNK_WRITE_PTR(buf->head));
|
2003-10-04 03:37:01 +02:00
|
|
|
|
2004-11-10 15:26:34 +01:00
|
|
|
startaddr = NULL;
|
2005-09-24 23:56:04 +02:00
|
|
|
if (socks4_prot != socks4a &&
|
2006-08-25 23:16:22 +02:00
|
|
|
!addressmap_have_mapping(tmpbuf) &&
|
2005-09-24 23:56:04 +02:00
|
|
|
!have_warned_about_unsafe_socks) {
|
2006-02-13 09:28:42 +01:00
|
|
|
log_warn(LD_APP,
|
2007-10-10 17:44:34 +02:00
|
|
|
"Your application (using socks4 to port %d) is giving Tor "
|
2006-02-13 09:28:42 +01:00
|
|
|
"only an IP address. Applications that do DNS resolves "
|
|
|
|
"themselves may leak information. Consider using Socks4A "
|
2006-03-19 02:44:53 +01:00
|
|
|
"(e.g. via privoxy or socat) instead. For more information, "
|
|
|
|
"please see http://wiki.noreply.org/noreply/TheOnionRouter/"
|
|
|
|
"TorFAQ#SOCKSAndDNS.%s", req->port,
|
|
|
|
safe_socks ? " Rejecting." : "");
|
2004-08-04 01:42:33 +02:00
|
|
|
// have_warned_about_unsafe_socks = 1; // (for now, warn every time)
|
2007-01-06 06:42:31 +01:00
|
|
|
control_event_client_status(LOG_WARN,
|
|
|
|
"DANGEROUS_SOCKS PROTOCOL=SOCKS4 ADDRESS=%s:%d",
|
|
|
|
tmpbuf, req->port);
|
2006-03-19 02:44:53 +01:00
|
|
|
if (safe_socks)
|
|
|
|
return -1;
|
2004-08-04 01:42:33 +02:00
|
|
|
}
|
2004-12-22 11:04:50 +01:00
|
|
|
if (socks4_prot == socks4a) {
|
2007-12-26 01:12:08 +01:00
|
|
|
if (next+1 == CHUNK_WRITE_PTR(buf->head)) {
|
2006-02-13 09:28:42 +01:00
|
|
|
log_debug(LD_APP,"socks4: No part of destaddr here yet.");
|
2004-12-22 11:04:50 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2004-11-10 15:26:34 +01:00
|
|
|
startaddr = next+1;
|
2007-12-26 01:12:08 +01:00
|
|
|
next = memchr(startaddr, 0, CHUNK_WRITE_PTR(buf->head)-startaddr);
|
2004-11-28 10:05:49 +01:00
|
|
|
if (!next) {
|
2007-12-26 01:12:08 +01:00
|
|
|
if (buf->head->datalen >= 1024) {
|
|
|
|
log_debug(LD_APP,"socks4: Destaddr too long.");
|
|
|
|
return -1;
|
|
|
|
}
|
2006-02-13 09:28:42 +01:00
|
|
|
log_debug(LD_APP,"socks4: Destaddr not all here yet.");
|
2003-10-04 03:37:01 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2004-11-28 10:05:49 +01:00
|
|
|
if (MAX_SOCKS_ADDR_LEN <= next-startaddr) {
|
2006-02-13 09:28:42 +01:00
|
|
|
log_warn(LD_APP,"socks4: Destaddr too long. Rejecting.");
|
2003-10-04 03:37:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
// tor_assert(next < buf->cur+buf->datalen);
|
2006-03-05 10:50:26 +01:00
|
|
|
|
2005-11-17 00:37:35 +01:00
|
|
|
if (log_sockstype)
|
2006-02-13 09:28:42 +01:00
|
|
|
log_notice(LD_APP,
|
2007-10-10 17:44:34 +02:00
|
|
|
"Your application (using socks4a to port %d) gave "
|
2006-02-13 09:28:42 +01:00
|
|
|
"Tor a hostname, which means Tor will do the DNS resolve "
|
|
|
|
"for you. This is good.", req->port);
|
2003-10-04 03:37:01 +02:00
|
|
|
}
|
2006-02-13 09:28:42 +01:00
|
|
|
log_debug(LD_APP,"socks4: Everything is here. Success.");
|
2004-11-10 15:26:34 +01:00
|
|
|
strlcpy(req->address, startaddr ? startaddr : tmpbuf,
|
2004-10-27 08:48:16 +02:00
|
|
|
sizeof(req->address));
|
2006-03-16 00:36:57 +01:00
|
|
|
if (!tor_strisprint(req->address) || strchr(req->address,'\"')) {
|
|
|
|
log_warn(LD_PROTOCOL,
|
2007-10-10 17:44:34 +02:00
|
|
|
"Your application (using socks4 to port %d) gave Tor "
|
2006-03-16 00:36:57 +01:00
|
|
|
"a malformed hostname: %s. Rejecting the connection.",
|
|
|
|
req->port, escaped(req->address));
|
|
|
|
return -1;
|
|
|
|
}
|
2005-12-14 21:40:40 +01:00
|
|
|
/* next points to the final \0 on inbuf */
|
2007-12-26 01:12:08 +01:00
|
|
|
buf_remove_from_front(buf, next - buf->head->data + 1);
|
2003-10-04 03:37:01 +02:00
|
|
|
return 1;
|
|
|
|
|
2004-02-26 23:02:22 +01:00
|
|
|
case 'G': /* get */
|
|
|
|
case 'H': /* head */
|
|
|
|
case 'P': /* put/post */
|
|
|
|
case 'C': /* connect */
|
2004-10-27 08:48:16 +02:00
|
|
|
strlcpy(req->reply,
|
2004-02-26 23:02:22 +01:00
|
|
|
"HTTP/1.0 501 Tor is not an HTTP Proxy\r\n"
|
2004-02-26 23:10:55 +01:00
|
|
|
"Content-Type: text/html; charset=iso-8859-1\r\n\r\n"
|
2004-02-26 23:02:22 +01:00
|
|
|
"<html>\n"
|
|
|
|
"<head>\n"
|
|
|
|
"<title>Tor is not an HTTP Proxy</title>\n"
|
|
|
|
"</head>\n"
|
|
|
|
"<body>\n"
|
2004-02-27 02:33:02 +01:00
|
|
|
"<h1>Tor is not an HTTP Proxy</h1>\n"
|
|
|
|
"<p>\n"
|
2005-12-14 21:40:40 +01:00
|
|
|
"It appears you have configured your web browser to use Tor as an HTTP proxy."
|
|
|
|
"\n"
|
|
|
|
"This is not correct: Tor is a SOCKS proxy, not an HTTP proxy.\n"
|
|
|
|
"Please configure your client accordingly.\n"
|
2004-02-27 02:33:02 +01:00
|
|
|
"</p>\n"
|
|
|
|
"<p>\n"
|
2007-12-07 00:56:36 +01:00
|
|
|
"See <a href=\"https://www.torproject.org/documentation.html\">"
|
2007-12-07 22:27:58 +01:00
|
|
|
"https://www.torproject.org/documentation.html</a> for more "
|
|
|
|
"information.\n"
|
2005-12-09 06:37:26 +01:00
|
|
|
"<!-- Plus this comment, to make the body response more than 512 bytes, so "
|
|
|
|
" IE will be willing to display it. Comment comment comment comment "
|
|
|
|
" comment comment comment comment comment comment comment comment.-->\n"
|
2004-02-27 02:33:02 +01:00
|
|
|
"</p>\n"
|
2004-02-26 23:02:22 +01:00
|
|
|
"</body>\n"
|
|
|
|
"</html>\n"
|
2004-10-27 08:48:16 +02:00
|
|
|
, MAX_SOCKS_REPLY_LEN);
|
2004-02-26 23:02:22 +01:00
|
|
|
req->replylen = strlen(req->reply)+1;
|
|
|
|
/* fall through */
|
2003-10-04 03:37:01 +02:00
|
|
|
default: /* version is not socks4 or socks5 */
|
2006-02-13 09:28:42 +01:00
|
|
|
log_warn(LD_APP,
|
|
|
|
"Socks version %d not recognized. (Tor is not an http proxy.)",
|
2007-12-26 01:12:08 +01:00
|
|
|
*(buf->head->data));
|
2007-01-06 06:42:31 +01:00
|
|
|
{
|
2007-12-26 01:12:08 +01:00
|
|
|
char *tmp = tor_strndup(buf->head->data, 8); /*XXXX what if longer?*/
|
2007-01-06 06:42:31 +01:00
|
|
|
control_event_client_status(LOG_WARN,
|
|
|
|
"SOCKS_UNKNOWN_PROTOCOL DATA=\"%s\"",
|
|
|
|
escaped(tmp));
|
|
|
|
tor_free(tmp);
|
|
|
|
}
|
2003-09-18 10:11:31 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-04 22:08:28 +01:00
|
|
|
/** Return 1 iff buf looks more like it has an (obsolete) v0 controller
|
|
|
|
* command on it than any valid v1 controller command. */
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
2007-03-04 22:08:28 +01:00
|
|
|
peek_buf_has_control0_command(buf_t *buf)
|
2004-11-03 02:32:26 +01:00
|
|
|
{
|
2007-03-04 22:08:28 +01:00
|
|
|
if (buf->datalen >= 4) {
|
|
|
|
char header[4];
|
|
|
|
uint16_t cmd;
|
|
|
|
peek_from_buf(header, sizeof(header), buf);
|
|
|
|
cmd = ntohs(get_uint16(header+2));
|
|
|
|
if (cmd <= 0x14)
|
|
|
|
return 1; /* This is definitely not a v1 control command. */
|
2005-03-02 21:22:10 +01:00
|
|
|
}
|
2007-03-04 22:08:28 +01:00
|
|
|
return 0;
|
2004-11-03 02:32:26 +01:00
|
|
|
}
|
|
|
|
|
2008-02-06 17:58:05 +01:00
|
|
|
/** Return the index within <b>buf</b> at which <b>ch</b> first appears,
|
|
|
|
* or -1 if <b>ch</b> does not appear on buf. */
|
2008-02-22 04:44:36 +01:00
|
|
|
static off_t
|
2007-12-29 03:33:42 +01:00
|
|
|
buf_find_offset_of_char(buf_t *buf, char ch)
|
|
|
|
{
|
|
|
|
chunk_t *chunk;
|
2008-02-22 04:44:36 +01:00
|
|
|
off_t offset = 0;
|
2007-12-29 03:33:42 +01:00
|
|
|
for (chunk = buf->head; chunk; chunk = chunk->next) {
|
|
|
|
char *cp = memchr(chunk->data, ch, chunk->datalen);
|
|
|
|
if (cp)
|
|
|
|
return offset + (cp - chunk->data);
|
|
|
|
else
|
|
|
|
offset += chunk->datalen;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2006-11-14 01:06:31 +01:00
|
|
|
/** Try to read a single LF-terminated line from <b>buf</b>, and write it,
|
|
|
|
* NUL-terminated, into the *<b>data_len</b> byte buffer at <b>data_out</b>.
|
|
|
|
* Set *<b>data_len</b> to the number of bytes in the line, not counting the
|
|
|
|
* terminating NUL. Return 1 if we read a whole line, return 0 if we don't
|
|
|
|
* have a whole line yet, and return -1 if the line length exceeds
|
2007-12-26 01:12:08 +01:00
|
|
|
* *<b>data_len</b>.
|
2006-11-14 01:06:31 +01:00
|
|
|
*/
|
|
|
|
int
|
2007-08-29 21:02:33 +02:00
|
|
|
fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len)
|
2006-11-14 01:06:31 +01:00
|
|
|
{
|
|
|
|
size_t sz;
|
2008-02-22 04:44:36 +01:00
|
|
|
off_t offset;
|
2006-11-14 01:06:31 +01:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
if (!buf->head)
|
|
|
|
return 0;
|
2007-12-29 03:33:42 +01:00
|
|
|
|
|
|
|
offset = buf_find_offset_of_char(buf, '\n');
|
|
|
|
if (offset < 0)
|
2006-11-14 01:06:31 +01:00
|
|
|
return 0;
|
2007-12-29 03:33:42 +01:00
|
|
|
sz = (size_t) offset;
|
2006-11-14 01:06:31 +01:00
|
|
|
if (sz+2 > *data_len) {
|
2007-12-29 03:33:42 +01:00
|
|
|
*data_len = sz + 2;
|
2006-11-14 01:06:31 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
fetch_from_buf(data_out, sz+1, buf);
|
|
|
|
data_out[sz+1] = '\0';
|
|
|
|
*data_len = sz+1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-08-28 05:16:02 +02:00
|
|
|
/** Compress on uncompress the <b>data_len</b> bytes in <b>data</b> using the
|
|
|
|
* zlib state <b>state</b>, appending the result to <b>buf</b>. If
|
|
|
|
* <b>done</b> is true, flush the data in the state and finish the
|
|
|
|
* compression/uncompression. Return -1 on failure, 0 on success. */
|
2006-06-18 09:27:47 +02:00
|
|
|
int
|
|
|
|
write_to_buf_zlib(buf_t *buf, tor_zlib_state_t *state,
|
|
|
|
const char *data, size_t data_len,
|
|
|
|
int done)
|
|
|
|
{
|
|
|
|
char *next;
|
|
|
|
size_t old_avail, avail;
|
2006-06-18 11:03:48 +02:00
|
|
|
int over = 0;
|
2006-06-24 04:10:21 +02:00
|
|
|
do {
|
2007-12-26 01:12:08 +01:00
|
|
|
int need_new_chunk = 0;
|
|
|
|
if (!buf->tail || ! CHUNK_REMAINING_CAPACITY(buf->tail)) {
|
|
|
|
size_t cap = data_len / 4;
|
2007-12-29 06:16:30 +01:00
|
|
|
buf_add_chunk_with_capacity(buf, cap, 1);
|
2007-12-26 01:12:08 +01:00
|
|
|
}
|
|
|
|
next = CHUNK_WRITE_PTR(buf->tail);
|
|
|
|
avail = old_avail = CHUNK_REMAINING_CAPACITY(buf->tail);
|
2006-06-18 09:27:47 +02:00
|
|
|
switch (tor_zlib_process(state, &next, &avail, &data, &data_len, done)) {
|
|
|
|
case TOR_ZLIB_DONE:
|
2006-06-18 11:03:48 +02:00
|
|
|
over = 1;
|
|
|
|
break;
|
2006-06-18 09:27:47 +02:00
|
|
|
case TOR_ZLIB_ERR:
|
|
|
|
return -1;
|
|
|
|
case TOR_ZLIB_OK:
|
|
|
|
if (data_len == 0)
|
2006-06-18 11:03:48 +02:00
|
|
|
over = 1;
|
2006-06-18 09:27:47 +02:00
|
|
|
break;
|
|
|
|
case TOR_ZLIB_BUF_FULL:
|
2007-12-26 01:12:08 +01:00
|
|
|
if (avail) {
|
|
|
|
/* Zlib says we need more room (ZLIB_BUF_FULL). Start a new chunk
|
|
|
|
* automatically, whether were going to or not. */
|
|
|
|
need_new_chunk = 1;
|
2006-06-18 09:27:47 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
buf->datalen += old_avail - avail;
|
2007-12-26 01:12:08 +01:00
|
|
|
buf->tail->datalen += old_avail - avail;
|
|
|
|
if (need_new_chunk) {
|
2007-12-29 06:16:30 +01:00
|
|
|
buf_add_chunk_with_capacity(buf, data_len/4, 1);
|
2007-12-26 01:12:08 +01:00
|
|
|
}
|
|
|
|
|
2006-06-24 04:10:21 +02:00
|
|
|
} while (!over);
|
2007-12-26 01:12:08 +01:00
|
|
|
check();
|
2006-06-18 22:39:46 +02:00
|
|
|
return 0;
|
2006-06-18 09:27:47 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Log an error and exit if <b>buf</b> is corrupted.
|
2004-05-02 00:08:43 +02:00
|
|
|
*/
|
2005-09-30 03:09:52 +02:00
|
|
|
void
|
|
|
|
assert_buf_ok(buf_t *buf)
|
2004-03-03 23:49:15 +01:00
|
|
|
{
|
2004-04-25 22:37:37 +02:00
|
|
|
tor_assert(buf);
|
|
|
|
tor_assert(buf->magic == BUFFER_MAGIC);
|
2007-04-23 16:42:27 +02:00
|
|
|
|
2007-12-26 01:12:08 +01:00
|
|
|
if (! buf->head) {
|
|
|
|
tor_assert(!buf->tail);
|
|
|
|
tor_assert(buf->datalen == 0);
|
2007-04-23 16:42:27 +02:00
|
|
|
} else {
|
2007-12-26 01:12:08 +01:00
|
|
|
chunk_t *ch;
|
|
|
|
size_t total = 0;
|
|
|
|
tor_assert(buf->tail);
|
|
|
|
for (ch = buf->head; ch; ch = ch->next) {
|
|
|
|
total += ch->datalen;
|
|
|
|
tor_assert(ch->datalen <= ch->memlen);
|
|
|
|
tor_assert(ch->data >= &ch->mem[0]);
|
|
|
|
tor_assert(ch->data < &ch->mem[0]+ch->memlen);
|
|
|
|
tor_assert(ch->data+ch->datalen <= &ch->mem[0] + ch->memlen);
|
|
|
|
if (!ch->next)
|
|
|
|
tor_assert(ch == buf->tail);
|
|
|
|
}
|
|
|
|
tor_assert(buf->datalen == total);
|
2007-04-23 16:42:27 +02:00
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
}
|
2007-04-23 16:42:27 +02:00
|
|
|
|
2008-01-13 01:20:44 +01:00
|
|
|
#ifdef ENABLE_BUF_FREELISTS
|
2007-12-26 01:12:08 +01:00
|
|
|
/** Log an error and exit if <b>fl</b> is corrupted.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
assert_freelist_ok(chunk_freelist_t *fl)
|
|
|
|
{
|
|
|
|
chunk_t *ch;
|
|
|
|
int n;
|
|
|
|
tor_assert(fl->alloc_size > 0);
|
|
|
|
n = 0;
|
|
|
|
for (ch = fl->head; ch; ch = ch->next) {
|
|
|
|
tor_assert(CHUNK_ALLOC_SIZE(ch->memlen) == fl->alloc_size);
|
|
|
|
++n;
|
2005-04-27 02:53:44 +02:00
|
|
|
}
|
2007-12-26 01:12:08 +01:00
|
|
|
tor_assert(n == fl->cur_length);
|
|
|
|
tor_assert(n >= fl->lowest_length);
|
|
|
|
tor_assert(n <= fl->max_length);
|
2004-03-03 23:49:15 +01:00
|
|
|
}
|
2008-01-13 01:20:44 +01:00
|
|
|
#endif
|
2005-06-09 21:03:31 +02:00
|
|
|
|