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 : }
|