LCOV - code coverage report
Current view: top level - lib/tls - buffers_tls.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 27 66 40.9 %
Date: 2021-11-24 03:28:48 Functions: 2 4 50.0 %

          Line data    Source code
       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           3 : read_to_chunk_tls(buf_t *buf, chunk_t *chunk, tor_tls_t *tls,
      30             :                   size_t at_most)
      31             : {
      32           3 :   int read_result;
      33             : 
      34           3 :   tor_assert(CHUNK_REMAINING_CAPACITY(chunk) >= at_most);
      35           3 :   read_result = tor_tls_read(tls, CHUNK_WRITE_PTR(chunk), at_most);
      36           3 :   if (read_result < 0)
      37             :     return read_result;
      38           3 :   buf->datalen += read_result;
      39           3 :   chunk->datalen += read_result;
      40           3 :   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           2 : buf_read_from_tls(buf_t *buf, tor_tls_t *tls, size_t at_most)
      68             : {
      69           2 :   int r = 0;
      70           2 :   size_t total_read = 0;
      71             : 
      72           2 :   check_no_tls_errors();
      73             : 
      74           2 :   IF_BUG_ONCE(buf->datalen > BUF_MAX_LEN)
      75             :     return TOR_TLS_ERROR_MISC;
      76           2 :   IF_BUG_ONCE(buf->datalen > BUF_MAX_LEN - at_most)
      77             :     return TOR_TLS_ERROR_MISC;
      78             : 
      79           5 :   while (at_most > total_read) {
      80           3 :     size_t readlen = at_most - total_read;
      81           3 :     chunk_t *chunk;
      82           3 :     if (!buf->tail || CHUNK_REMAINING_CAPACITY(buf->tail) < MIN_READ_LEN) {
      83           2 :       chunk = buf_add_chunk_with_capacity(buf, at_most, 1);
      84           2 :       if (readlen > chunk->memlen)
      85             :         readlen = chunk->memlen;
      86             :     } else {
      87           1 :       size_t cap = CHUNK_REMAINING_CAPACITY(buf->tail);
      88           1 :       chunk = buf->tail;
      89           1 :       if (cap < readlen)
      90             :         readlen = cap;
      91             :     }
      92             : 
      93           3 :     r = read_to_chunk_tls(buf, chunk, tls, readlen);
      94           3 :     if (r < 0)
      95           0 :       return r; /* Error */
      96           3 :     tor_assert(total_read+r <= BUF_MAX_LEN);
      97             :     total_read += r;
      98             :   }
      99           2 :   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           0 : flush_chunk_tls(tor_tls_t *tls, buf_t *buf, chunk_t *chunk, size_t sz)
     110             : {
     111           0 :   int r;
     112           0 :   size_t forced;
     113           0 :   char *data;
     114             : 
     115           0 :   forced = tor_tls_get_forced_write_size(tls);
     116           0 :   if (forced > sz)
     117             :     sz = forced;
     118           0 :   if (chunk) {
     119           0 :     data = chunk->data;
     120           0 :     tor_assert(sz <= chunk->datalen);
     121             :   } else {
     122           0 :     data = NULL;
     123           0 :     tor_assert(sz == 0);
     124             :   }
     125           0 :   r = tor_tls_write(tls, data, sz);
     126           0 :   if (r < 0)
     127             :     return r;
     128           0 :   buf_drain(buf, r);
     129           0 :   log_debug(LD_NET,"flushed %d bytes, %d remain.",
     130             :             r,(int)buf->datalen);
     131           0 :   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           0 : buf_flush_to_tls(buf_t *buf, tor_tls_t *tls, size_t flushlen)
     139             : {
     140           0 :   int r;
     141           0 :   size_t flushed = 0;
     142           0 :   ssize_t sz;
     143           0 :   IF_BUG_ONCE(flushlen > buf->datalen) {
     144           0 :     flushlen = buf->datalen;
     145             :   }
     146           0 :   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           0 :   check_no_tls_errors();
     151             : 
     152           0 :   do {
     153           0 :     size_t flushlen0;
     154           0 :     if (buf->head) {
     155           0 :       if ((ssize_t)buf->head->datalen >= sz)
     156           0 :         flushlen0 = sz;
     157             :       else
     158             :         flushlen0 = buf->head->datalen;
     159             :     } else {
     160             :       flushlen0 = 0;
     161             :     }
     162             : 
     163           0 :     r = flush_chunk_tls(tls, buf, buf->head, flushlen0);
     164           0 :     if (r < 0)
     165           0 :       return r;
     166           0 :     flushed += r;
     167           0 :     sz -= r;
     168           0 :     if (r == 0) /* Can't flush any more now. */
     169             :       break;
     170           0 :   } while (sz > 0);
     171           0 :   tor_assert(flushed <= BUF_MAX_LEN);
     172           0 :   return (int)flushed;
     173             : }

Generated by: LCOV version 1.14