aboutsummaryrefslogtreecommitdiff
path: root/src/or/buffers.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/buffers.c')
-rw-r--r--src/or/buffers.c141
1 files changed, 128 insertions, 13 deletions
diff --git a/src/or/buffers.c b/src/or/buffers.c
index 89382d1d8e..201778e301 100644
--- a/src/or/buffers.c
+++ b/src/or/buffers.c
@@ -281,6 +281,7 @@ buf_pullup(buf_t *buf, size_t bytes)
}
#ifdef TOR_UNIT_TESTS
+/* Return the data from the first chunk of buf in cp, and its length in sz. */
void
buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz)
{
@@ -292,6 +293,53 @@ buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz)
*sz = buf->head->datalen;
}
}
+
+/* Write sz bytes from cp into a newly allocated buffer buf.
+ * Returns NULL when passed a NULL cp or zero sz.
+ * Asserts on failure: only for use in unit tests.
+ * buf must be freed using buf_free(). */
+buf_t *
+buf_new_with_data(const char *cp, size_t sz)
+{
+ /* Validate arguments */
+ if (!cp || sz <= 0) {
+ return NULL;
+ }
+
+ tor_assert(sz < SSIZE_T_CEILING);
+
+ /* Allocate a buffer */
+ buf_t *buf = buf_new_with_capacity(sz);
+ tor_assert(buf);
+ assert_buf_ok(buf);
+ tor_assert(!buf->head);
+
+ /* Allocate a chunk that is sz bytes long */
+ buf->head = chunk_new_with_alloc_size(CHUNK_ALLOC_SIZE(sz));
+ buf->tail = buf->head;
+ tor_assert(buf->head);
+ assert_buf_ok(buf);
+ tor_assert(buf_allocation(buf) >= sz);
+
+ /* Copy the data and size the buffers */
+ tor_assert(sz <= buf_slack(buf));
+ tor_assert(sz <= CHUNK_REMAINING_CAPACITY(buf->head));
+ memcpy(&buf->head->mem[0], cp, sz);
+ buf->datalen = sz;
+ buf->head->datalen = sz;
+ buf->head->data = &buf->head->mem[0];
+ assert_buf_ok(buf);
+
+ /* Make sure everything is large enough */
+ tor_assert(buf_allocation(buf) >= sz);
+ tor_assert(buf_allocation(buf) >= buf_datalen(buf) + buf_slack(buf));
+ /* Does the buffer implementation allocate more than the requested size?
+ * (for example, by rounding up). If so, these checks will fail. */
+ tor_assert(buf_datalen(buf) == sz);
+ tor_assert(buf_slack(buf) == 0);
+
+ return buf;
+}
#endif
/** Remove the first <b>n</b> bytes from buf. */
@@ -562,6 +610,11 @@ read_to_buf(tor_socket_t s, size_t at_most, buf_t *buf, int *reached_eof,
tor_assert(reached_eof);
tor_assert(SOCKET_OK(s));
+ if (BUG(buf->datalen >= INT_MAX))
+ return -1;
+ if (BUG(buf->datalen >= INT_MAX - at_most))
+ return -1;
+
while (at_most > total_read) {
size_t readlen = at_most - total_read;
chunk_t *chunk;
@@ -619,6 +672,11 @@ read_to_buf_tls(tor_tls_t *tls, size_t at_most, buf_t *buf)
check();
+ if (BUG(buf->datalen >= INT_MAX))
+ return -1;
+ if (BUG(buf->datalen >= INT_MAX - at_most))
+ return -1;
+
while (at_most > total_read) {
size_t readlen = at_most - total_read;
chunk_t *chunk;
@@ -813,6 +871,11 @@ write_to_buf(const char *string, size_t string_len, buf_t *buf)
return (int)buf->datalen;
check();
+ if (BUG(buf->datalen >= INT_MAX))
+ return -1;
+ if (BUG(buf->datalen >= INT_MAX - string_len))
+ return -1;
+
while (string_len) {
size_t copy;
if (!buf->tail || !CHUNK_REMAINING_CAPACITY(buf->tail))
@@ -962,6 +1025,12 @@ move_buf_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen)
/* We can do way better here, but this doesn't turn up in any profiles. */
char b[4096];
size_t cp, len;
+
+ if (BUG(buf_out->datalen >= INT_MAX))
+ return -1;
+ if (BUG(buf_out->datalen >= INT_MAX - *buf_flushlen))
+ return -1;
+
len = *buf_flushlen;
if (len > buf_in->datalen)
len = buf_in->datalen;
@@ -1090,6 +1159,52 @@ buf_find_string_offset(const buf_t *buf, const char *s, size_t n)
return -1;
}
+/**
+ * Scan the HTTP headers in the <b>headerlen</b>-byte memory range at
+ * <b>headers</b>, looking for a "Content-Length" header. Try to set
+ * *<b>result_out</b> to the numeric value of that header if possible.
+ * Return -1 if the header was malformed, 0 if it was missing, and 1 if
+ * it was present and well-formed.
+ */
+STATIC int
+buf_http_find_content_length(const char *headers, size_t headerlen,
+ size_t *result_out)
+{
+ const char *p, *newline;
+ char *len_str, *eos=NULL;
+ size_t remaining, result;
+ int ok;
+ *result_out = 0; /* The caller shouldn't look at this unless the
+ * return value is 1, but let's prevent confusion */
+
+#define CONTENT_LENGTH "\r\nContent-Length: "
+ p = (char*) tor_memstr(headers, headerlen, CONTENT_LENGTH);
+ if (p == NULL)
+ return 0;
+
+ tor_assert(p >= headers && p < headers+headerlen);
+ remaining = (headers+headerlen)-p;
+ p += strlen(CONTENT_LENGTH);
+ remaining -= strlen(CONTENT_LENGTH);
+
+ newline = memchr(p, '\n', remaining);
+ if (newline == NULL)
+ return -1;
+
+ len_str = tor_memdup_nulterm(p, newline-p);
+ /* We limit the size to INT_MAX because other parts of the buffer.c
+ * code don't like buffers to be any bigger than that. */
+ result = (size_t) tor_parse_uint64(len_str, 10, 0, INT_MAX, &ok, &eos);
+ if (eos && !tor_strisspace(eos)) {
+ ok = 0;
+ } else {
+ *result_out = result;
+ }
+ tor_free(len_str);
+
+ return ok ? 1 : -1;
+}
+
/** There is a (possibly incomplete) http statement on <b>buf</b>, of the
* form "\%s\\r\\n\\r\\n\%s", headers, body. (body may contain NULs.)
* If a) the headers include a Content-Length field and all bytes in
@@ -1115,9 +1230,10 @@ fetch_from_buf_http(buf_t *buf,
char **body_out, size_t *body_used, size_t max_bodylen,
int force_complete)
{
- char *headers, *p;
- size_t headerlen, bodylen, contentlen;
+ char *headers;
+ size_t headerlen, bodylen, contentlen=0;
int crlf_offset;
+ int r;
check();
if (!buf->head)
@@ -1153,17 +1269,12 @@ fetch_from_buf_http(buf_t *buf,
return -1;
}
-#define CONTENT_LENGTH "\r\nContent-Length: "
- p = (char*) tor_memstr(headers, headerlen, CONTENT_LENGTH);
- if (p) {
- int i;
- i = atoi(p+strlen(CONTENT_LENGTH));
- if (i < 0) {
- log_warn(LD_PROTOCOL, "Content-Length is less than zero; it looks like "
- "someone is trying to crash us.");
- return -1;
- }
- contentlen = i;
+ r = buf_http_find_content_length(headers, headerlen, &contentlen);
+ if (r == -1) {
+ log_warn(LD_PROTOCOL, "Content-Length is bogus; maybe "
+ "someone is trying to crash us.");
+ return -1;
+ } else if (r == 1) {
/* if content-length is malformed, then our body length is 0. fine. */
log_debug(LD_HTTP,"Got a contentlen of %d.",(int)contentlen);
if (bodylen < contentlen) {
@@ -1176,7 +1287,11 @@ fetch_from_buf_http(buf_t *buf,
bodylen = contentlen;
log_debug(LD_HTTP,"bodylen reduced to %d.",(int)bodylen);
}
+ } else {
+ tor_assert(r == 0);
+ /* Leave bodylen alone */
}
+
/* all happy. copy into the appropriate places, and return 1 */
if (headers_out) {
*headers_out = tor_malloc(headerlen+1);