Merge branch 'maint-0.3.5' into maint-0.4.1
[tor.git] / src / lib / tls / buffers_tls.c
blobed0f821ce851cbc5c64e9f1a6f3a2407fb5640f3
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-2019, The Tor Project, Inc. */
5 /* See LICENSE for licensing information */
7 /**
8 * \file buffers_tls.c
9 * \brief Read and write data on a tor_tls_t connection from a buf_t object.
10 **/
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"
22 #ifdef HAVE_UNISTD_H
23 #include <unistd.h>
24 #endif
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)
32 int read_result;
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;
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.
46 * Using TLS on OR connections complicates matters in two ways.
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.
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.
63 int
64 buf_read_from_tls(buf_t *buf, tor_tls_t *tls, size_t at_most)
66 int r = 0;
67 size_t total_read = 0;
69 check_no_tls_errors();
71 IF_BUG_ONCE(buf->datalen >= INT_MAX)
72 return -1;
73 IF_BUG_ONCE(buf->datalen >= INT_MAX - at_most)
74 return -1;
76 while (at_most > total_read) {
77 size_t readlen = at_most - total_read;
78 chunk_t *chunk;
79 if (!buf->tail || CHUNK_REMAINING_CAPACITY(buf->tail) < MIN_READ_LEN) {
80 chunk = buf_add_chunk_with_capacity(buf, at_most, 1);
81 if (readlen > chunk->memlen)
82 readlen = chunk->memlen;
83 } else {
84 size_t cap = CHUNK_REMAINING_CAPACITY(buf->tail);
85 chunk = buf->tail;
86 if (cap < readlen)
87 readlen = cap;
90 r = read_to_chunk_tls(buf, chunk, tls, readlen);
91 if (r < 0)
92 return r; /* Error */
93 tor_assert(total_read+r < INT_MAX);
94 total_read += r;
95 if ((size_t)r < readlen) /* eof, block, or no more to read. */
96 break;
98 return (int)total_read;
101 /** Helper for buf_flush_to_tls(): try to write <b>sz</b> bytes from chunk
102 * <b>chunk</b> of buffer <b>buf</b> onto socket <b>s</b>. (Tries to write
103 * more if there is a forced pending write size.) On success, deduct the
104 * bytes written from *<b>buf_flushlen</b>. Return the number of bytes
105 * written on success, and a TOR_TLS error code on failure or blocking.
107 static inline int
108 flush_chunk_tls(tor_tls_t *tls, buf_t *buf, chunk_t *chunk,
109 size_t sz, size_t *buf_flushlen)
111 int r;
112 size_t forced;
113 char *data;
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);
125 r = tor_tls_write(tls, data, sz);
126 if (r < 0)
127 return r;
128 if (*buf_flushlen > (size_t)r)
129 *buf_flushlen -= r;
130 else
131 *buf_flushlen = 0;
132 buf_drain(buf, r);
133 log_debug(LD_NET,"flushed %d bytes, %d ready to flush, %d remain.",
134 r,(int)*buf_flushlen,(int)buf->datalen);
135 return r;
138 /** As buf_flush_to_socket(), but writes data to a TLS connection. Can write
139 * more than <b>flushlen</b> bytes.
142 buf_flush_to_tls(buf_t *buf, tor_tls_t *tls, size_t flushlen,
143 size_t *buf_flushlen)
145 int r;
146 size_t flushed = 0;
147 ssize_t sz;
148 tor_assert(buf_flushlen);
149 IF_BUG_ONCE(*buf_flushlen > buf->datalen) {
150 *buf_flushlen = buf->datalen;
152 IF_BUG_ONCE(flushlen > *buf_flushlen) {
153 flushlen = *buf_flushlen;
155 sz = (ssize_t) flushlen;
157 /* we want to let tls write even if flushlen is zero, because it might
158 * have a partial record pending */
159 check_no_tls_errors();
161 do {
162 size_t flushlen0;
163 if (buf->head) {
164 if ((ssize_t)buf->head->datalen >= sz)
165 flushlen0 = sz;
166 else
167 flushlen0 = buf->head->datalen;
168 } else {
169 flushlen0 = 0;
172 r = flush_chunk_tls(tls, buf, buf->head, flushlen0, buf_flushlen);
173 if (r < 0)
174 return r;
175 flushed += r;
176 sz -= r;
177 if (r == 0) /* Can't flush any more now. */
178 break;
179 } while (sz > 0);
180 tor_assert(flushed < INT_MAX);
181 return (int)flushed;