#define BUFFERS_PRIVATE
#include "orconfig.h"
#include <stddef.h>
#include "lib/buf/buffers.h"
#include "lib/tls/buffers_tls.h"
#include "lib/cc/torint.h"
#include "lib/log/log.h"
#include "lib/log/util_bug.h"
#include "lib/tls/tortls.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
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;
}
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_ONCE(buf->datalen > BUF_MAX_LEN)
return TOR_TLS_ERROR_MISC;
IF_BUG_ONCE(buf->datalen > BUF_MAX_LEN - at_most)
return TOR_TLS_ERROR_MISC;
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;
tor_assert(total_read+r <= BUF_MAX_LEN);
total_read += r;
if ((size_t)r < readlen)
break;
}
return (int)total_read;
}
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;
}
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);
IF_BUG_ONCE(*buf_flushlen > buf->datalen) {
*buf_flushlen = buf->datalen;
}
IF_BUG_ONCE(flushlen > *buf_flushlen) {
flushlen = *buf_flushlen;
}
sz = (ssize_t) flushlen;
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)
break;
} while (sz > 0);
tor_assert(flushed <= BUF_MAX_LEN);
return (int)flushed;
}