123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- /* 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
- buf_read_from_tls(buf_t *buf, tor_tls_t *tls, size_t at_most)
- {
- 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 buf_flush_to_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_drain(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 buf_flush_to_socket(), but writes data to a TLS connection. Can write
- * more than <b>flushlen</b> bytes.
- */
- int
- buf_flush_to_tls(buf_t *buf, tor_tls_t *tls, 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;
- }
|