summaryrefslogtreecommitdiff
path: root/src/or/buffers.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/buffers.c')
-rw-r--r--src/or/buffers.c254
1 files changed, 207 insertions, 47 deletions
diff --git a/src/or/buffers.c b/src/or/buffers.c
index 710374638b..2a88382501 100644
--- a/src/or/buffers.c
+++ b/src/or/buffers.c
@@ -12,6 +12,14 @@
**/
#define BUFFERS_PRIVATE
#include "or.h"
+#include "buffers.h"
+#include "config.h"
+#include "connection_edge.h"
+#include "connection_or.h"
+#include "control.h"
+#include "reasons.h"
+#include "../common/util.h"
+#include "../common/torlog.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -145,10 +153,13 @@ get_freelist(size_t alloc)
/** Deallocate a chunk or put it on a freelist */
static void
-chunk_free(chunk_t *chunk)
+chunk_free_unchecked(chunk_t *chunk)
{
- size_t alloc = CHUNK_ALLOC_SIZE(chunk->memlen);
- chunk_freelist_t *freelist = get_freelist(alloc);
+ size_t alloc;
+ chunk_freelist_t *freelist;
+
+ alloc = CHUNK_ALLOC_SIZE(chunk->memlen);
+ freelist = get_freelist(alloc);
if (freelist && freelist->cur_length < freelist->max_length) {
chunk->next = freelist->head;
freelist->head = chunk;
@@ -193,7 +204,7 @@ chunk_new_with_alloc_size(size_t alloc)
}
#else
static void
-chunk_free(chunk_t *chunk)
+chunk_free_unchecked(chunk_t *chunk)
{
tor_free(chunk);
}
@@ -259,13 +270,22 @@ buf_shrink_freelists(int free_all)
int n_to_free = free_all ? freelists[i].cur_length :
(freelists[i].lowest_length - slack);
int n_to_skip = freelists[i].cur_length - n_to_free;
+ int orig_length = freelists[i].cur_length;
int orig_n_to_free = n_to_free, n_freed=0;
int orig_n_to_skip = n_to_skip;
int new_length = n_to_skip;
chunk_t **chp = &freelists[i].head;
chunk_t *chunk;
while (n_to_skip) {
- tor_assert((*chp)->next);
+ if (! (*chp)->next) {
+ log_warn(LD_BUG, "I wanted to skip %d chunks in the freelist for "
+ "%d-byte chunks, but only found %d. (Length %d)",
+ orig_n_to_skip, (int)freelists[i].alloc_size,
+ orig_n_to_skip-n_to_skip, freelists[i].cur_length);
+ assert_freelist_ok(&freelists[i]);
+ goto done;
+ }
+ // tor_assert((*chp)->next);
chp = &(*chp)->next;
--n_to_skip;
}
@@ -290,13 +310,15 @@ buf_shrink_freelists(int free_all)
}
// tor_assert(!n_to_free);
freelists[i].cur_length = new_length;
- log_info(LD_MM, "Cleaned freelist for %d-byte chunks: kept %d, "
- "dropped %d.",
- (int)freelists[i].alloc_size, orig_n_to_skip, orig_n_to_free);
+ log_info(LD_MM, "Cleaned freelist for %d-byte chunks: original "
+ "length %d, kept %d, dropped %d.",
+ (int)freelists[i].alloc_size, orig_length,
+ orig_n_to_skip, orig_n_to_free);
}
freelists[i].lowest_length = freelists[i].cur_length;
assert_freelist_ok(&freelists[i]);
}
+ done:
enable_control_logging();
#else
(void) free_all;
@@ -404,7 +426,7 @@ buf_pullup(buf_t *buf, size_t bytes, int nulterminate)
dest->next = src->next;
if (buf->tail == src)
buf->tail = dest;
- chunk_free(src);
+ chunk_free_unchecked(src);
} else {
memcpy(CHUNK_WRITE_PTR(dest), src->data, n);
dest->datalen += n;
@@ -450,7 +472,7 @@ buf_remove_from_front(buf_t *buf, size_t n)
buf->head = victim->next;
if (buf->tail == victim)
buf->tail = NULL;
- chunk_free(victim);
+ chunk_free_unchecked(victim);
}
}
check();
@@ -484,7 +506,7 @@ buf_clear(buf_t *buf)
buf->datalen = 0;
for (chunk = buf->head; chunk; chunk = next) {
next = chunk->next;
- chunk_free(chunk);
+ chunk_free_unchecked(chunk);
}
buf->head = buf->tail = NULL;
}
@@ -523,6 +545,8 @@ buf_slack(const buf_t *buf)
void
buf_free(buf_t *buf)
{
+ if (!buf)
+ return;
buf_clear(buf);
buf->magic = 0xdeadbeef;
tor_free(buf);
@@ -1275,6 +1299,43 @@ fetch_from_buf_http(buf_t *buf,
return 1;
}
+/**
+ * Wait this many seconds before warning the user about using SOCKS unsafely
+ * again (requires that WarnUnsafeSocks is turned on). */
+#define SOCKS_WARN_INTERVAL 5
+
+/** Warn that the user application has made an unsafe socks request using
+ * protocol <b>socks_protocol</b> on port <b>port</b>. Don't warn more than
+ * once per SOCKS_WARN_INTERVAL, unless <b>safe_socks</b> is set. */
+static void
+log_unsafe_socks_warning(int socks_protocol, const char *address,
+ uint16_t port, int safe_socks)
+{
+ static ratelim_t socks_ratelim = RATELIM_INIT(SOCKS_WARN_INTERVAL);
+
+ or_options_t *options = get_options();
+ char *m = NULL;
+ if (! options->WarnUnsafeSocks)
+ return;
+ if (safe_socks || (m = rate_limit_log(&socks_ratelim, approx_time()))) {
+ log_warn(LD_APP,
+ "Your application (using socks%d to port %d) is giving "
+ "Tor only an IP address. Applications that do DNS resolves "
+ "themselves may leak information. Consider using Socks4A "
+ "(e.g. via privoxy or socat) instead. For more information, "
+ "please see https://wiki.torproject.org/TheOnionRouter/"
+ "TorFAQ#SOCKSAndDNS.%s%s",
+ socks_protocol,
+ (int)port,
+ safe_socks ? " Rejecting." : "",
+ m ? m : "");
+ tor_free(m);
+ }
+ control_event_client_status(LOG_WARN,
+ "DANGEROUS_SOCKS PROTOCOL=SOCKS%d ADDRESS=%s:%d",
+ socks_protocol, address, (int)port);
+}
+
/** There is a (possibly incomplete) socks handshake on <b>buf</b>, of one
* of the forms
* - socks4: "socksheader username\\0"
@@ -1313,10 +1374,6 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
char *next, *startaddr;
struct in_addr in;
- /* If the user connects with socks4 or the wrong variant of socks5,
- * then log a warning to let him know that it might be unwise. */
- static int have_warned_about_unsafe_socks = 0;
-
if (buf->datalen < 2) /* version and another byte */
return 0;
@@ -1396,21 +1453,8 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
req->port = ntohs(get_uint16(buf->head->data+4+addrlen));
buf_remove_from_front(buf, 6+addrlen);
if (req->command != SOCKS_COMMAND_RESOLVE_PTR &&
- !addressmap_have_mapping(req->address,0) &&
- !have_warned_about_unsafe_socks) {
- log_warn(LD_APP,
- "Your application (using socks5 to port %d) is giving "
- "Tor only an IP address. Applications that do DNS resolves "
- "themselves may leak information. Consider using Socks4A "
- "(e.g. via privoxy or socat) instead. For more information, "
- "please see http://wiki.noreply.org/noreply/TheOnionRouter/"
- "TorFAQ#SOCKSAndDNS.%s", req->port,
- safe_socks ? " Rejecting." : "");
- /*have_warned_about_unsafe_socks = 1;*/
- /*(for now, warn every time)*/
- control_event_client_status(LOG_WARN,
- "DANGEROUS_SOCKS PROTOCOL=SOCKS5 ADDRESS=%s:%d",
- req->address, req->port);
+ !addressmap_have_mapping(req->address,0)) {
+ log_unsafe_socks_warning(5, req->address, req->port, safe_socks);
if (safe_socks)
return -1;
}
@@ -1475,8 +1519,8 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
return -1;
}
- req->port = ntohs(*(uint16_t*)(buf->head->data+2));
- destip = ntohl(*(uint32_t*)(buf->head->data+4));
+ req->port = ntohs(get_uint16(buf->head->data+2));
+ destip = ntohl(get_uint32(buf->head->data+4));
if ((!req->port && req->command!=SOCKS_COMMAND_RESOLVE) || !destip) {
log_warn(LD_APP,"socks4: Port or DestIP is zero. Rejecting.");
return -1;
@@ -1491,7 +1535,8 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
return -1;
}
log_debug(LD_APP,
- "socks4: successfully read destip (%s)", safe_str(tmpbuf));
+ "socks4: successfully read destip (%s)",
+ safe_str_client(tmpbuf));
socks4_prot = socks4;
}
@@ -1509,20 +1554,9 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
startaddr = NULL;
if (socks4_prot != socks4a &&
- !addressmap_have_mapping(tmpbuf,0) &&
- !have_warned_about_unsafe_socks) {
- log_warn(LD_APP,
- "Your application (using socks4 to port %d) is giving Tor "
- "only an IP address. Applications that do DNS resolves "
- "themselves may leak information. Consider using Socks4A "
- "(e.g. via privoxy or socat) instead. For more information, "
- "please see http://wiki.noreply.org/noreply/TheOnionRouter/"
- "TorFAQ#SOCKSAndDNS.%s", req->port,
- safe_socks ? " Rejecting." : "");
- /*have_warned_about_unsafe_socks = 1;*/ /*(for now, warn every time)*/
- control_event_client_status(LOG_WARN,
- "DANGEROUS_SOCKS PROTOCOL=SOCKS4 ADDRESS=%s:%d",
- tmpbuf, req->port);
+ !addressmap_have_mapping(tmpbuf,0)) {
+ log_unsafe_socks_warning(4, tmpbuf, req->port, safe_socks);
+
if (safe_socks)
return -1;
}
@@ -1614,6 +1648,132 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
}
}
+/** Inspect a reply from SOCKS server stored in <b>buf</b> according
+ * to <b>state</b>, removing the protocol data upon success. Return 0 on
+ * incomplete response, 1 on success and -1 on error, in which case
+ * <b>reason</b> is set to a descriptive message (free() when finished
+ * with it).
+ *
+ * As a special case, 2 is returned when user/pass is required
+ * during SOCKS5 handshake and user/pass is configured.
+ */
+int
+fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
+{
+ unsigned char *data;
+ size_t addrlen;
+
+ if (buf->datalen < 2)
+ return 0;
+
+ buf_pullup(buf, 128, 0);
+ tor_assert(buf->head && buf->head->datalen >= 2);
+
+ data = (unsigned char *) buf->head->data;
+
+ switch (state) {
+ case PROXY_SOCKS4_WANT_CONNECT_OK:
+ /* Wait for the complete response */
+ if (buf->head->datalen < 8)
+ return 0;
+
+ if (data[1] != 0x5a) {
+ *reason = tor_strdup(socks4_response_code_to_string(data[1]));
+ return -1;
+ }
+
+ /* Success */
+ buf_remove_from_front(buf, 8);
+ return 1;
+
+ case PROXY_SOCKS5_WANT_AUTH_METHOD_NONE:
+ /* we don't have any credentials */
+ if (data[1] != 0x00) {
+ *reason = tor_strdup("server doesn't support any of our "
+ "available authentication methods");
+ return -1;
+ }
+
+ log_info(LD_NET, "SOCKS 5 client: continuing without authentication");
+ buf_clear(buf);
+ return 1;
+
+ case PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929:
+ /* we have a username and password. return 1 if we can proceed without
+ * providing authentication, or 2 otherwise. */
+ switch (data[1]) {
+ case 0x00:
+ log_info(LD_NET, "SOCKS 5 client: we have auth details but server "
+ "doesn't require authentication.");
+ buf_clear(buf);
+ return 1;
+ case 0x02:
+ log_info(LD_NET, "SOCKS 5 client: need authentication.");
+ buf_clear(buf);
+ return 2;
+ /* fall through */
+ }
+
+ *reason = tor_strdup("server doesn't support any of our available "
+ "authentication methods");
+ return -1;
+
+ case PROXY_SOCKS5_WANT_AUTH_RFC1929_OK:
+ /* handle server reply to rfc1929 authentication */
+ if (data[1] != 0x00) {
+ *reason = tor_strdup("authentication failed");
+ return -1;
+ }
+
+ log_info(LD_NET, "SOCKS 5 client: authentication successful.");
+ buf_clear(buf);
+ return 1;
+
+ case PROXY_SOCKS5_WANT_CONNECT_OK:
+ /* response is variable length. BND.ADDR, etc, isn't needed
+ * (don't bother with buf_pullup()), but make sure to eat all
+ * the data used */
+
+ /* wait for address type field to arrive */
+ if (buf->datalen < 4)
+ return 0;
+
+ switch (data[3]) {
+ case 0x01: /* ip4 */
+ addrlen = 4;
+ break;
+ case 0x04: /* ip6 */
+ addrlen = 16;
+ break;
+ case 0x03: /* fqdn (can this happen here?) */
+ if (buf->datalen < 5)
+ return 0;
+ addrlen = 1 + data[4];
+ break;
+ default:
+ *reason = tor_strdup("invalid response to connect request");
+ return -1;
+ }
+
+ /* wait for address and port */
+ if (buf->datalen < 6 + addrlen)
+ return 0;
+
+ if (data[1] != 0x00) {
+ *reason = tor_strdup(socks5_response_code_to_string(data[1]));
+ return -1;
+ }
+
+ buf_remove_from_front(buf, 6 + addrlen);
+ return 1;
+ }
+
+ /* shouldn't get here... */
+ tor_assert(0);
+
+ return -1;
+}
+
/** Return 1 iff buf looks more like it has an (obsolete) v0 controller
* command on it than any valid v1 controller command. */
int