diff options
-rw-r--r-- | src/or/buffers.c | 80 | ||||
-rw-r--r-- | src/or/connection.c | 1 | ||||
-rw-r--r-- | src/or/control.c | 57 | ||||
-rw-r--r-- | src/or/or.h | 4 |
4 files changed, 72 insertions, 70 deletions
diff --git a/src/or/buffers.c b/src/or/buffers.c index d5a4e6836..56f5460ed 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -968,7 +968,7 @@ int fetch_from_buf_control(buf_t *buf, uint32_t *len_out, uint16_t *type_out, { uint32_t msglen; uint16_t type; - char tmp[10]; + char tmp[4]; tor_assert(buf); tor_assert(len_out); @@ -985,77 +985,17 @@ int fetch_from_buf_control(buf_t *buf, uint32_t *len_out, uint16_t *type_out, return 0; type = ntohs(get_uint16(tmp+2)); - if (type != CONTROL_CMD_FRAGMENTHEADER) { - *len_out = msglen; - *type_out = type; - buf_remove_from_front(buf, 4); - if (msglen) { - *body_out = tor_malloc(msglen+1); - fetch_from_buf(*body_out, msglen, buf); - (*body_out)[msglen] = '\0'; - } else { - *body_out = NULL; - } - return 1; + *len_out = msglen; + *type_out = type; + buf_remove_from_front(buf, 4); + if (msglen) { + *body_out = tor_malloc(msglen+1); + fetch_from_buf(*body_out, msglen, buf); + (*body_out)[msglen] = '\0'; } else { - uint32_t totallen, sofar; - char *cp, *endp, *outp; - - /* Okay, we have a fragmented message. Is it all here? */ - if (msglen < 6) - return -1; - peek_from_buf(tmp, 10, buf); - type = htons(get_uint16(tmp+4)); - totallen = htonl(get_uint32(tmp+6)); - if (totallen < 65536) - return -1; - - if (buf->datalen<4+6+totallen) - /* The data can't possibly be here yet, no matter how well it's packed.*/ - return 0; - - /* Count how much data is really here. */ - sofar = msglen-6; - cp = buf->cur+4+msglen; - endp = buf->cur+buf->datalen; - /* XXXXX!!!!!! This will not handle fragmented messages right now. */ - while (sofar < totallen) { - if ((endp-cp)<4) - return 0; /* Fragment header not all here. */ - msglen = ntohs(get_uint16(cp)); - if (ntohs(get_uint16(cp+2) != CONTROL_CMD_FRAGMENT)) - return -1; /* Missing fragment message; error. */ - if ((endp-cp) < (int)(4+msglen)) - return 0; /* Fragment not all here. */ - sofar += msglen; - cp += (4+msglen); - } - if (sofar > totallen) - return -1; /* Fragments add to more than expected; error. */ - - /* Okay, everything is here. */ - *len_out = totallen; - *type_out = type; - *body_out = tor_malloc(totallen+1); - - /* copy FRAGMENTED packet contents. */ - msglen = ntohs(get_uint16(buf->mem)); - if (msglen>6) - memcpy(*body_out,buf->mem+4+6,msglen-6); - sofar = msglen-6; - outp = *body_out+sofar; - cp = buf->mem+4+msglen; - while (sofar < totallen) { - msglen = ntohs(get_uint16(cp)); - memcpy(outp,cp+4,msglen); - outp += msglen; - cp += 4+msglen; - sofar -= msglen; - } - (*body_out)[totallen]='\0'; - - return 1; + *body_out = NULL; } + return 1; } /** Log an error and exit if <b>buf</b> is corrupted. diff --git a/src/or/connection.c b/src/or/connection.c index f1caeef86..55da6899d 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -209,6 +209,7 @@ _connection_free(connection_t *conn) { crypto_free_pk_env(conn->identity_pkey); tor_free(conn->nickname); tor_free(conn->socks_request); + tor_free(conn->incoming_cmd); tor_free(conn->read_event); /* Probably already freed by connection_free. */ tor_free(conn->write_event); /* Probably already freed by connection_free. */ diff --git a/src/or/control.c b/src/or/control.c index 59dc68a02..32ccbf596 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -923,6 +923,46 @@ handle_control_closecircuit(connection_t *conn, uint32_t len, return 0; } +static int +handle_control_fragments(connection_t *conn, uint16_t command_type, + uint32_t body_len, char *body) +{ + if (command_type == CONTROL_CMD_FRAGMENTHEADER) { + if (conn->incoming_cmd) { + log_fn(LOG_WARN, "Dropping incomplete fragmented command"); + tor_free(conn->incoming_cmd); + } + if (body_len < 6) { + send_control_error(conn, ERR_SYNTAX, "FRAGMENTHEADER too short."); + return 0; + } + conn->incoming_cmd_type = ntohs(get_uint16(body)); + conn->incoming_cmd_len = ntohl(get_uint32(body+2)); + conn->incoming_cmd_cur_len = 0; + conn->incoming_cmd = tor_malloc(conn->incoming_cmd_len); + body += 6; + body_len -= 6; + } else if (command_type == CONTROL_CMD_FRAGMENT) { + if (!conn->incoming_cmd) { + send_control_error(conn, ERR_SYNTAX, "Out-of-place FRAGMENT"); + return 0; + } + } else { + tor_assert(0); + } + + if (conn->incoming_cmd_cur_len + body_len > conn->incoming_cmd_len) { + tor_free(conn->incoming_cmd); + send_control_error(conn, ERR_SYNTAX, + "Fragmented data exceeds declared length"); + return 0; + } + memcpy(conn->incoming_cmd + conn->incoming_cmd_cur_len, + body, body_len); + conn->incoming_cmd_cur_len += body_len; + return 0; +} + /** Called when <b>conn</b> has no more bytes left on its outbuf. */ int connection_control_finished_flushing(connection_t *conn) { @@ -980,6 +1020,23 @@ connection_control_process_inbuf(connection_t *conn) { goto again; } + if (command_type == CONTROL_CMD_FRAGMENTHEADER || + command_type == CONTROL_CMD_FRAGMENT) { + if (handle_control_fragments(conn, command_type, body_len, body)) + return -1; + tor_free(body); + if (conn->incoming_cmd_cur_len != conn->incoming_cmd_len) + goto again; + + command_type = conn->incoming_cmd_type; + body_len = conn->incoming_cmd_len; + body = conn->incoming_cmd; + conn->incoming_cmd = NULL; + } else if (conn->incoming_cmd) { + log_fn(LOG_WARN, "Dropping incomplete fragmented command"); + tor_free(conn->incoming_cmd); + } + /* Okay, we're willing to process the command. */ switch (command_type) { diff --git a/src/or/or.h b/src/or/or.h index 1a31973e2..6df4996cd 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -639,6 +639,10 @@ struct connection_t { /* Used only by control connections */ uint32_t event_mask; + uint16_t incoming_cmd_type; + uint32_t incoming_cmd_len; + uint32_t incoming_cmd_cur_len; + char *incoming_cmd; }; typedef struct connection_t connection_t; |