summaryrefslogtreecommitdiff
path: root/src/or/directory.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/directory.c')
-rw-r--r--src/or/directory.c58
1 files changed, 53 insertions, 5 deletions
diff --git a/src/or/directory.c b/src/or/directory.c
index 76fc121909..bf963f1187 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -13,6 +13,7 @@
#include "config.h"
#include "connection.h"
#include "connection_edge.h"
+#include "consdiff.h"
#include "consdiffmgr.h"
#include "control.h"
#include "compat.h"
@@ -2417,6 +2418,10 @@ handle_response_fetch_consensus(dir_connection_t *conn,
const char *reason = args->reason;
const time_t now = approx_time();
+ const char *consensus;
+ char *new_consensus = NULL;
+ const char *sourcename;
+
int r;
const char *flavname = conn->requested_resource;
if (status_code != 200) {
@@ -2429,15 +2434,57 @@ handle_response_fetch_consensus(dir_connection_t *conn,
networkstatus_consensus_download_failed(status_code, flavname);
return -1;
}
- log_info(LD_DIR,"Received consensus directory (body size %d) from server "
- "'%s:%d'", (int)body_len, conn->base_.address, conn->base_.port);
- if ((r=networkstatus_set_current_consensus(body, flavname, 0,
+
+ if (looks_like_a_consensus_diff(body, body_len)) {
+ /* First find our previous consensus. Maybe it's in ram, maybe not. */
+ cached_dir_t *cd = dirserv_get_consensus(flavname);
+ const char *consensus_body;
+ char *owned_consensus = NULL;
+ if (cd) {
+ consensus_body = cd->dir;
+ } else {
+ owned_consensus = networkstatus_read_cached_consensus(flavname);
+ consensus_body = owned_consensus;
+ }
+ if (!consensus_body) {
+ log_warn(LD_DIR, "Received a consensus diff, but we can't find "
+ "any %s-flavored consensus in our current cache.",flavname);
+ networkstatus_consensus_download_failed(0, flavname);
+ // XXXX if this happens too much, see below
+ return -1;
+ }
+
+ new_consensus = consensus_diff_apply(consensus_body, body);
+ tor_free(owned_consensus);
+ if (new_consensus == NULL) {
+ log_warn(LD_DIR, "Could not apply consensus diff received from server "
+ "'%s:%d'", conn->base_.address, conn->base_.port);
+ // XXXX If this happens too many times, we should maybe not use
+ // XXXX this directory for diffs any more?
+ networkstatus_consensus_download_failed(0, flavname);
+ return -1;
+ }
+ log_info(LD_DIR, "Applied consensus diff (body size %d) from server "
+ "'%s:%d' resulted in a new consensus document (size %d).",
+ (int)body_len, conn->base_.address, conn->base_.port,
+ (int)strlen(new_consensus));
+ consensus = new_consensus;
+ sourcename = "generated based on a diff";
+ } else {
+ log_info(LD_DIR,"Received consensus directory (body size %d) from server "
+ "'%s:%d'", (int)body_len, conn->base_.address, conn->base_.port);
+ consensus = body;
+ sourcename = "downloaded";
+ }
+
+ if ((r=networkstatus_set_current_consensus(consensus, flavname, 0,
conn->identity_digest))<0) {
log_fn(r<-1?LOG_WARN:LOG_INFO, LD_DIR,
- "Unable to load %s consensus directory downloaded from "
+ "Unable to load %s consensus directory %s from "
"server '%s:%d'. I'll try again soon.",
- flavname, conn->base_.address, conn->base_.port);
+ flavname, sourcename, conn->base_.address, conn->base_.port);
networkstatus_consensus_download_failed(0, flavname);
+ tor_free(new_consensus);
return -1;
}
@@ -2455,6 +2502,7 @@ handle_response_fetch_consensus(dir_connection_t *conn,
}
log_info(LD_DIR, "Successfully loaded consensus.");
+ tor_free(new_consensus);
return 0;
}