tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

buffers_tls.c (5154B)


      1 /* Copyright (c) 2001 Matej Pfajfar.
      2 * Copyright (c) 2001-2004, Roger Dingledine.
      3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
      4 * Copyright (c) 2007-2021, The Tor Project, Inc. */
      5 /* See LICENSE for licensing information */
      6 
      7 /**
      8 * \file buffers_tls.c
      9 * \brief Read and write data on a tor_tls_t connection from a buf_t object.
     10 **/
     11 
     12 #define BUFFERS_PRIVATE
     13 #include "orconfig.h"
     14 #include <stddef.h>
     15 #include "lib/buf/buffers.h"
     16 #include "lib/tls/buffers_tls.h"
     17 #include "lib/cc/torint.h"
     18 #include "lib/log/log.h"
     19 #include "lib/log/util_bug.h"
     20 #include "lib/tls/tortls.h"
     21 
     22 #ifdef HAVE_UNISTD_H
     23 #include <unistd.h>
     24 #endif
     25 
     26 /** As read_to_chunk(), but return (negative) error code on error, blocking,
     27 * or TLS, and the number of bytes read otherwise. */
     28 static inline int
     29 read_to_chunk_tls(buf_t *buf, chunk_t *chunk, tor_tls_t *tls,
     30                  size_t at_most)
     31 {
     32  int read_result;
     33 
     34  tor_assert(CHUNK_REMAINING_CAPACITY(chunk) >= at_most);
     35  read_result = tor_tls_read(tls, CHUNK_WRITE_PTR(chunk), at_most);
     36  if (read_result < 0)
     37    return read_result;
     38  buf->datalen += read_result;
     39  chunk->datalen += read_result;
     40  return read_result;
     41 }
     42 
     43 /** As read_to_buf, but reads from a TLS connection, and returns a TLS
     44 * status value rather than the number of bytes read.
     45 *
     46 * Using TLS on OR connections complicates matters in two ways.
     47 *
     48 * First, a TLS stream has its own read buffer independent of the
     49 * connection's read buffer.  (TLS needs to read an entire frame from
     50 * the network before it can decrypt any data.  Thus, trying to read 1
     51 * byte from TLS can require that several KB be read from the network
     52 * and decrypted.  The extra data is stored in TLS's decrypt buffer.)
     53 * Because the data hasn't been read by Tor (it's still inside the TLS),
     54 * this means that sometimes a connection "has stuff to read" even when
     55 * poll() didn't return POLLIN. The tor_tls_get_pending_bytes function is
     56 * used in connection.c to detect TLS objects with non-empty internal
     57 * buffers and read from them again.
     58 *
     59 * Second, the TLS stream's events do not correspond directly to network
     60 * events: sometimes, before a TLS stream can read, the network must be
     61 * ready to write -- or vice versa.
     62 *
     63 * On success, return the number of bytes read. On error, a TOR_TLS_* negative
     64 * code is returned (expect any of them except TOR_TLS_DONE).
     65 */
     66 int
     67 buf_read_from_tls(buf_t *buf, tor_tls_t *tls, size_t at_most)
     68 {
     69  int r = 0;
     70  size_t total_read = 0;
     71 
     72  check_no_tls_errors();
     73 
     74  IF_BUG_ONCE(buf->datalen > BUF_MAX_LEN)
     75    return TOR_TLS_ERROR_MISC;
     76  IF_BUG_ONCE(buf->datalen > BUF_MAX_LEN - at_most)
     77    return TOR_TLS_ERROR_MISC;
     78 
     79  while (at_most > total_read) {
     80    size_t readlen = at_most - total_read;
     81    chunk_t *chunk;
     82    if (!buf->tail || CHUNK_REMAINING_CAPACITY(buf->tail) < MIN_READ_LEN) {
     83      chunk = buf_add_chunk_with_capacity(buf, at_most, 1);
     84      if (readlen > chunk->memlen)
     85        readlen = chunk->memlen;
     86    } else {
     87      size_t cap = CHUNK_REMAINING_CAPACITY(buf->tail);
     88      chunk = buf->tail;
     89      if (cap < readlen)
     90        readlen = cap;
     91    }
     92 
     93    r = read_to_chunk_tls(buf, chunk, tls, readlen);
     94    if (r < 0)
     95      return r; /* Error */
     96    tor_assert(total_read+r <= BUF_MAX_LEN);
     97    total_read += r;
     98  }
     99  return (int)total_read;
    100 }
    101 
    102 /** Helper for buf_flush_to_tls(): try to write <b>sz</b> bytes from chunk
    103 * <b>chunk</b> of buffer <b>buf</b> onto socket <b>s</b>.  (Tries to write
    104 * more if there is a forced pending write size.)  On success, deduct the
    105 * bytes written from *<b>buf_flushlen</b>.  Return the number of bytes
    106 * written on success, and a TOR_TLS error code on failure or blocking.
    107 */
    108 static inline int
    109 flush_chunk_tls(tor_tls_t *tls, buf_t *buf, chunk_t *chunk, size_t sz)
    110 {
    111  int r;
    112  size_t forced;
    113  char *data;
    114 
    115  forced = tor_tls_get_forced_write_size(tls);
    116  if (forced > sz)
    117    sz = forced;
    118  if (chunk) {
    119    data = chunk->data;
    120    tor_assert(sz <= chunk->datalen);
    121  } else {
    122    data = NULL;
    123    tor_assert(sz == 0);
    124  }
    125  r = tor_tls_write(tls, data, sz);
    126  if (r < 0)
    127    return r;
    128  buf_drain(buf, r);
    129  log_debug(LD_NET,"flushed %d bytes, %d remain.",
    130            r,(int)buf->datalen);
    131  return r;
    132 }
    133 
    134 /** As buf_flush_to_socket(), but writes data to a TLS connection.  Can write
    135 * more than <b>flushlen</b> bytes.
    136 */
    137 int
    138 buf_flush_to_tls(buf_t *buf, tor_tls_t *tls, size_t flushlen)
    139 {
    140  int r;
    141  size_t flushed = 0;
    142  ssize_t sz;
    143  IF_BUG_ONCE(flushlen > buf->datalen) {
    144    flushlen = buf->datalen;
    145  }
    146  sz = (ssize_t) flushlen;
    147 
    148  /* we want to let tls write even if flushlen is zero, because it might
    149   * have a partial record pending */
    150  check_no_tls_errors();
    151 
    152  do {
    153    size_t flushlen0;
    154    if (buf->head) {
    155      if ((ssize_t)buf->head->datalen >= sz)
    156        flushlen0 = sz;
    157      else
    158        flushlen0 = buf->head->datalen;
    159    } else {
    160      flushlen0 = 0;
    161    }
    162 
    163    r = flush_chunk_tls(tls, buf, buf->head, flushlen0);
    164    if (r < 0)
    165      return r;
    166    flushed += r;
    167    sz -= r;
    168    if (r == 0) /* Can't flush any more now. */
    169      break;
    170  } while (sz > 0);
    171  tor_assert(flushed <= BUF_MAX_LEN);
    172  return (int)flushed;
    173 }