diff options
-rw-r--r-- | src/or/buffers.c | 181 | ||||
-rw-r--r-- | src/or/buffers.h | 27 | ||||
-rw-r--r-- | src/or/buffers_tls.c | 175 | ||||
-rw-r--r-- | src/or/buffers_tls.h | 19 | ||||
-rw-r--r-- | src/or/connection.c | 1 | ||||
-rw-r--r-- | src/or/include.am | 2 | ||||
-rw-r--r-- | src/or/main.c | 1 | ||||
-rw-r--r-- | src/test/test_buffers.c | 1 |
8 files changed, 222 insertions, 185 deletions
diff --git a/src/or/buffers.c b/src/or/buffers.c index ee888b42a4..1a9144e14b 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -27,7 +27,6 @@ #include "util.h" #include "torint.h" #include "torlog.h" -#include "tortls.h" #ifdef HAVE_UNISTD_H #include <unistd.h> #endif @@ -101,22 +100,6 @@ } while (0) #endif -/** 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. */ -static inline char * -CHUNK_WRITE_PTR(chunk_t *chunk) -{ - return chunk->data + chunk->datalen; -} - -/** 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) -{ - return (chunk->mem + chunk->memlen) - (chunk->data + chunk->datalen); -} - /** 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 @@ -183,9 +166,6 @@ chunk_grow(chunk_t *chunk, size_t sz) return chunk; } -/** 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 /** No chunk should take up more than this many bytes. */ @@ -479,7 +459,7 @@ buf_copy(const buf_t *buf) /** 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. */ -static chunk_t * +chunk_t * buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped) { chunk_t *chunk; @@ -564,23 +544,6 @@ read_to_chunk(buf_t *buf, chunk_t *chunk, tor_socket_t fd, size_t at_most, } } -/** As read_to_chunk(), but return (negative) error code on error, blocking, - * or TLS, and the number of bytes read otherwise. */ -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; -} - /** Read from socket <b>s</b>, writing onto end of <b>buf</b>. Read at most * <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 @@ -633,67 +596,6 @@ read_to_buf(tor_socket_t s, size_t at_most, buf_t *buf, int *reached_eof, return (int)total_read; } -/** As read_to_buf, but reads from a TLS connection, and returns a TLS - * status value rather than the number of bytes read. - * - * 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. - */ -int -read_to_buf_tls(tor_tls_t *tls, size_t at_most, buf_t *buf) -{ - int r = 0; - size_t total_read = 0; - - check_no_tls_errors(); - - check(); - - if (BUG(buf->datalen >= INT_MAX)) - return -1; - if (BUG(buf->datalen >= INT_MAX - at_most)) - return -1; - - while (at_most > total_read) { - size_t readlen = at_most - total_read; - chunk_t *chunk; - if (!buf->tail || CHUNK_REMAINING_CAPACITY(buf->tail) < MIN_READ_LEN) { - chunk = buf_add_chunk_with_capacity(buf, at_most, 1); - if (readlen > chunk->memlen) - readlen = chunk->memlen; - } 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); - check(); - if (r < 0) - return r; /* Error */ - tor_assert(total_read+r < INT_MAX); - total_read += r; - if ((size_t)r < readlen) /* eof, block, or no more to read. */ - break; - } - return (int)total_read; -} - /** 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 @@ -728,43 +630,6 @@ flush_chunk(tor_socket_t s, buf_t *buf, chunk_t *chunk, size_t sz, } } -/** 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 failure or blocking. - */ -static inline int -flush_chunk_tls(tor_tls_t *tls, buf_t *buf, chunk_t *chunk, - size_t sz, size_t *buf_flushlen) -{ - int r; - size_t forced; - char *data; - - forced = tor_tls_get_forced_write_size(tls); - if (forced > sz) - sz = forced; - if (chunk) { - data = chunk->data; - tor_assert(sz <= chunk->datalen); - } else { - data = NULL; - tor_assert(sz == 0); - } - r = tor_tls_write(tls, data, sz); - if (r < 0) - return r; - if (*buf_flushlen > (size_t)r) - *buf_flushlen -= r; - else - *buf_flushlen = 0; - 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; -} - /** Write data from <b>buf</b> to the socket <b>s</b>. Write at most * <b>sz</b> bytes, decrement *<b>buf_flushlen</b> by * the number of bytes actually written, and remove the written bytes @@ -806,50 +671,6 @@ flush_buf(tor_socket_t s, buf_t *buf, size_t sz, size_t *buf_flushlen) return (int)flushed; } -/** As flush_buf(), but writes data to a TLS connection. Can write more than - * <b>flushlen</b> bytes. - */ -int -flush_buf_tls(tor_tls_t *tls, buf_t *buf, size_t flushlen, - size_t *buf_flushlen) -{ - int r; - size_t flushed = 0; - ssize_t sz; - tor_assert(buf_flushlen); - tor_assert(*buf_flushlen <= buf->datalen); - tor_assert(flushlen <= *buf_flushlen); - sz = (ssize_t) flushlen; - - /* we want to let tls write even if flushlen is zero, because it might - * have a partial record pending */ - check_no_tls_errors(); - - check(); - do { - size_t flushlen0; - if (buf->head) { - if ((ssize_t)buf->head->datalen >= sz) - flushlen0 = sz; - else - flushlen0 = buf->head->datalen; - } else { - flushlen0 = 0; - } - - r = flush_chunk_tls(tls, buf, buf->head, flushlen0, buf_flushlen); - check(); - if (r < 0) - return r; - flushed += r; - sz -= r; - if (r == 0) /* Can't flush any more now. */ - break; - } while (sz > 0); - tor_assert(flushed < INT_MAX); - return (int)flushed; -} - /** Append <b>string_len</b> bytes from <b>string</b> to the end of * <b>buf</b>. * diff --git a/src/or/buffers.h b/src/or/buffers.h index 35fa46593b..77cc9ce0fb 100644 --- a/src/or/buffers.h +++ b/src/or/buffers.h @@ -19,9 +19,7 @@ typedef struct buf_t buf_t; -struct tor_tls_t; struct tor_compress_state_t; -struct ext_or_cmd_t; buf_t *buf_new(void); buf_t *buf_new_with_capacity(size_t size); @@ -39,11 +37,8 @@ size_t buf_get_total_allocation(void); int read_to_buf(tor_socket_t s, size_t at_most, buf_t *buf, int *reached_eof, int *socket_error); -int read_to_buf_tls(struct tor_tls_t *tls, size_t at_most, buf_t *buf); int flush_buf(tor_socket_t s, buf_t *buf, size_t sz, size_t *buf_flushlen); -int flush_buf_tls(struct tor_tls_t *tls, buf_t *buf, size_t sz, - size_t *buf_flushlen); int write_to_buf(const char *string, size_t string_len, buf_t *buf); int write_to_buf_compress(buf_t *buf, struct tor_compress_state_t *state, @@ -100,6 +95,28 @@ struct buf_t { chunk_t *head; /**< First chunk in the list, or NULL for none. */ chunk_t *tail; /**< Last chunk in the list, or NULL for none. */ }; + +chunk_t *buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped); +/** 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 + +/** 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) +{ + return (chunk->mem + chunk->memlen) - (chunk->data + chunk->datalen); +} + +/** 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. */ +static inline char * +CHUNK_WRITE_PTR(chunk_t *chunk) +{ + return chunk->data + chunk->datalen; +} + #endif #endif diff --git a/src/or/buffers_tls.c b/src/or/buffers_tls.c new file mode 100644 index 0000000000..e470738698 --- /dev/null +++ b/src/or/buffers_tls.c @@ -0,0 +1,175 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define BUFFERS_PRIVATE +#include "orconfig.h" +#include <stddef.h> +#include "buffers.h" +#include "buffers_tls.h" +#include "compat.h" +#include "compress.h" +#include "util.h" +#include "torint.h" +#include "torlog.h" +#include "tortls.h" +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +/** As read_to_chunk(), but return (negative) error code on error, blocking, + * or TLS, and the number of bytes read otherwise. */ +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; +} + +/** As read_to_buf, but reads from a TLS connection, and returns a TLS + * status value rather than the number of bytes read. + * + * 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. + */ +int +read_to_buf_tls(tor_tls_t *tls, size_t at_most, buf_t *buf) +{ + int r = 0; + size_t total_read = 0; + + check_no_tls_errors(); + + if (BUG(buf->datalen >= INT_MAX)) + return -1; + if (BUG(buf->datalen >= INT_MAX - at_most)) + return -1; + + while (at_most > total_read) { + size_t readlen = at_most - total_read; + chunk_t *chunk; + if (!buf->tail || CHUNK_REMAINING_CAPACITY(buf->tail) < MIN_READ_LEN) { + chunk = buf_add_chunk_with_capacity(buf, at_most, 1); + if (readlen > chunk->memlen) + readlen = chunk->memlen; + } 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); + if (r < 0) + return r; /* Error */ + tor_assert(total_read+r < INT_MAX); + total_read += r; + if ((size_t)r < readlen) /* eof, block, or no more to read. */ + break; + } + return (int)total_read; +} + +/** 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 failure or blocking. + */ +static inline int +flush_chunk_tls(tor_tls_t *tls, buf_t *buf, chunk_t *chunk, + size_t sz, size_t *buf_flushlen) +{ + int r; + size_t forced; + char *data; + + forced = tor_tls_get_forced_write_size(tls); + if (forced > sz) + sz = forced; + if (chunk) { + data = chunk->data; + tor_assert(sz <= chunk->datalen); + } else { + data = NULL; + tor_assert(sz == 0); + } + r = tor_tls_write(tls, data, sz); + if (r < 0) + return r; + if (*buf_flushlen > (size_t)r) + *buf_flushlen -= r; + else + *buf_flushlen = 0; + 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; +} + +/** As flush_buf(), but writes data to a TLS connection. Can write more than + * <b>flushlen</b> bytes. + */ +int +flush_buf_tls(tor_tls_t *tls, buf_t *buf, size_t flushlen, + size_t *buf_flushlen) +{ + int r; + size_t flushed = 0; + ssize_t sz; + tor_assert(buf_flushlen); + tor_assert(*buf_flushlen <= buf->datalen); + tor_assert(flushlen <= *buf_flushlen); + sz = (ssize_t) flushlen; + + /* we want to let tls write even if flushlen is zero, because it might + * have a partial record pending */ + check_no_tls_errors(); + + do { + size_t flushlen0; + if (buf->head) { + if ((ssize_t)buf->head->datalen >= sz) + flushlen0 = sz; + else + flushlen0 = buf->head->datalen; + } else { + flushlen0 = 0; + } + + r = flush_chunk_tls(tls, buf, buf->head, flushlen0, buf_flushlen); + if (r < 0) + return r; + flushed += r; + sz -= r; + if (r == 0) /* Can't flush any more now. */ + break; + } while (sz > 0); + tor_assert(flushed < INT_MAX); + return (int)flushed; +} + diff --git a/src/or/buffers_tls.h b/src/or/buffers_tls.h new file mode 100644 index 0000000000..4fce6c1130 --- /dev/null +++ b/src/or/buffers_tls.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_BUFFERS_TLS_H +#define TOR_BUFFERS_TLS_H + +struct buf_t; +struct tor_tls_t; + +int read_to_buf_tls(struct tor_tls_t *tls, size_t at_most, + struct buf_t *buf); +int flush_buf_tls(struct tor_tls_t *tls, struct buf_t *buf, size_t sz, + size_t *buf_flushlen); + +#endif + diff --git a/src/or/connection.c b/src/or/connection.c index 637b38cec7..866ce5e0b4 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -58,6 +58,7 @@ #include "or.h" #include "bridges.h" #include "buffers.h" +#include "buffers_tls.h" /* * Define this so we get channel internal functions, since we're implementing * part of a subclass (channel_tls_t). diff --git a/src/or/include.am b/src/or/include.am index fe860339fa..6dc43749b7 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -21,6 +21,7 @@ LIBTOR_A_SOURCES = \ src/or/addressmap.c \ src/or/bridges.c \ src/or/buffers.c \ + src/or/buffers_tls.c \ src/or/channel.c \ src/or/channelpadding.c \ src/or/channeltls.c \ @@ -156,6 +157,7 @@ ORHEADERS = \ src/or/addressmap.h \ src/or/bridges.h \ src/or/buffers.h \ + src/or/buffers_tls.h \ src/or/channel.h \ src/or/channelpadding.h \ src/or/channeltls.h \ diff --git a/src/or/main.c b/src/or/main.c index 7939f19845..a9d4bdb6ee 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -52,6 +52,7 @@ #include "backtrace.h" #include "bridges.h" #include "buffers.h" +#include "buffers_tls.h" #include "channel.h" #include "channeltls.h" #include "channelpadding.h" diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c index 26c0625978..d2d1223f31 100644 --- a/src/test/test_buffers.c +++ b/src/test/test_buffers.c @@ -7,6 +7,7 @@ #define PROTO_HTTP_PRIVATE #include "or.h" #include "buffers.h" +#include "buffers_tls.h" #include "ext_orport.h" #include "proto_cell.h" #include "proto_ext_or.h" |