diff --git a/ChangeLog b/ChangeLog
index 9c69b24780..736c997860 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -9,6 +9,8 @@ Changes in version 0.2.0.8-alpha - 2007-10-12
- Use annotations to record the purpose of each descriptor.
- Disable the SETROUTERPURPOSE controller command: it is now
obsolete.
+ - Controllers should now specify cache=no or cache=yes when using
+ the +POSTDESCRIPTOR command.
- Bridge authorities now write bridge descriptors to disk, meaning
we can export them to other programs and begin distributing them
to blocked users.
diff --git a/doc/spec/control-spec.txt b/doc/spec/control-spec.txt
index b57de5583c..69f72818b6 100644
--- a/doc/spec/control-spec.txt
+++ b/doc/spec/control-spec.txt
@@ -635,12 +635,18 @@ $Id$
3.14. POSTDESCRIPTOR
Sent from the client to the server. The syntax is:
- "+POSTDESCRIPTOR" [SP "purpose=" Purpose] CRLF Descriptor CRLF "." CRLF
+ "+POSTDESCRIPTOR" [SP "purpose=" Purpose] [SP "cache=" Cache]
+ CRLF Descriptor CRLF "." CRLF
This message informs the server about a new descriptor. If Purpose is
specified, it must be either "general" or "controller", else we
return a 552 error.
+ If Cache is specified, it must be either "no" or "yes", else we
+ return a 552 error. If Cache is not specified, Tor will decide for
+ itself whether it wants to cache the descriptor, and controllers
+ must not rely on its choice.
+
The descriptor, when parsed, must contain a number of well-specified
fields, including fields for its nickname and identity.
diff --git a/src/or/control.c b/src/or/control.c
index cbe3158a69..873d2f4b36 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -1878,35 +1878,19 @@ handle_control_getinfo(control_connection_t *conn, uint32_t len,
return 0;
}
-/** If *string contains a recognized purpose (for
- * circuits if for_circuits is 1, else for routers),
- * possibly prefaced with the string "purpose=", then assign it
- * and return 0. Otherwise return -1.
- *
- * If it's prefaced with "purpose=", then set *string to
- * the remainder of the string. */
-static int
-get_purpose(char **string, int for_circuits, uint8_t *purpose)
+/** Given a string, convert it to a circuit purpose. */
+static uint8_t
+circuit_purpose_from_string(const char *string)
{
- if (!strcmpstart(*string, "purpose="))
- *string += strlen("purpose=");
+ if (!strcmpstart(string, "purpose="))
+ string += strlen("purpose=");
- if (!for_circuits) {
- int r = router_purpose_from_string(*string);
- if (r == ROUTER_PURPOSE_UNKNOWN)
- return -1;
- *purpose = r;
- return 0;
- }
-
- if (!strcmp(*string, "general"))
- *purpose = CIRCUIT_PURPOSE_C_GENERAL;
- else if (!strcmp(*string, "controller"))
- *purpose = CIRCUIT_PURPOSE_CONTROLLER;
- else { /* not a recognized purpose */
- return -1;
- }
- return 0;
+ if (!strcmp(string, "general"))
+ return CIRCUIT_PURPOSE_C_GENERAL;
+ else if (!strcmp(string, "controller"))
+ return CIRCUIT_PURPOSE_CONTROLLER;
+ else
+ return CIRCUIT_PURPOSE_UNKNOWN;
}
/** Return a newly allocated smartlist containing the arguments to the command
@@ -1963,7 +1947,8 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
if (zero_circ && smartlist_len(args)>2) {
char *purp = smartlist_get(args,2);
- if (get_purpose(&purp, 1, &intended_purpose) < 0) {
+ intended_purpose = circuit_purpose_from_string(purp);
+ if (intended_purpose == CIRCUIT_PURPOSE_UNKNOWN) {
connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n", purp);
SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
smartlist_free(args);
@@ -2061,7 +2046,8 @@ handle_control_setcircuitpurpose(control_connection_t *conn,
{
char *purp = smartlist_get(args,1);
- if (get_purpose(&purp, 1, &new_purpose) < 0) {
+ new_purpose = circuit_purpose_from_string(purp);
+ if (new_purpose == CIRCUIT_PURPOSE_UNKNOWN) {
connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n", purp);
goto done;
}
@@ -2178,6 +2164,7 @@ handle_control_postdescriptor(control_connection_t *conn, uint32_t len,
char *desc;
const char *msg=NULL;
uint8_t purpose = ROUTER_PURPOSE_GENERAL;
+ int cache = 0; /* eventually, we may switch this to 1 */
char *cp = memchr(body, '\n', len);
smartlist_t *args = smartlist_create();
@@ -2186,21 +2173,37 @@ handle_control_postdescriptor(control_connection_t *conn, uint32_t len,
smartlist_split_string(args, body, " ",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- if (smartlist_len(args)) {
- char *purp = smartlist_get(args,0);
- if (get_purpose(&purp, 0, &purpose) < 0) {
- connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n",
- purp);
- SMARTLIST_FOREACH(args, char *, arg, tor_free(arg));
- smartlist_free(args);
- return 0;
+ SMARTLIST_FOREACH(args, char *, option,
+ {
+ if (!strcasecmpstart(option, "purpose=")) {
+ option += strlen("purpose=");
+ purpose = router_purpose_from_string(option);
+ if (purpose == ROUTER_PURPOSE_UNKNOWN) {
+ connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n",
+ option);
+ goto done;
+ }
+ } else if (!strcasecmpstart(option, "cache=")) {
+ option += strlen("cache=");
+ if (!strcmp(option, "no"))
+ cache = 0;
+ else if (!strcmp(option, "yes"))
+ cache = 1;
+ else {
+ connection_printf_to_buf(conn, "552 Unknown cache request \"%s\"\r\n",
+ option);
+ goto done;
+ }
+ } else { /* unrecognized argument? */
+ connection_printf_to_buf(conn,
+ "512 Unexpected argument \"%s\" to postdescriptor\r\n", option);
+ goto done;
}
- }
- SMARTLIST_FOREACH(args, char *, arg, tor_free(arg));
- smartlist_free(args);
+ });
+
read_escaped_data(cp, len-(cp-body), &desc);
- switch (router_load_single_router(desc, purpose, &msg)) {
+ switch (router_load_single_router(desc, purpose, cache, &msg)) {
case -1:
if (!msg) msg = "Could not parse descriptor";
connection_printf_to_buf(conn, "554 %s\r\n", msg);
@@ -2215,6 +2218,9 @@ handle_control_postdescriptor(control_connection_t *conn, uint32_t len,
}
tor_free(desc);
+ done:
+ SMARTLIST_FOREACH(args, char *, arg, tor_free(arg));
+ smartlist_free(args);
return 0;
}
diff --git a/src/or/or.h b/src/or/or.h
index ed5e6ec37c..fdc9bbc720 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -451,6 +451,9 @@ typedef enum {
/** A controller made this circuit and Tor should not use it. */
#define CIRCUIT_PURPOSE_CONTROLLER 18
#define _CIRCUIT_PURPOSE_MAX 18
+/** A catch-all for unrecognized purposes. Currently we don't expect
+ * to make or see any circuits with this purpose. */
+#define CIRCUIT_PURPOSE_UNKNOWN 255
/** True iff the circuit purpose p is for a circuit that
* originated at this node. */
@@ -3600,7 +3603,7 @@ int router_add_to_routerlist(routerinfo_t *router, const char **msg,
void router_add_extrainfo_to_routerlist(extrainfo_t *ei, const char **msg,
int from_cache, int from_fetch);
void routerlist_remove_old_routers(void);
-int router_load_single_router(const char *s, uint8_t purpose,
+int router_load_single_router(const char *s, uint8_t purpose, int cache,
const char **msg);
void router_load_routers_from_string(const char *s, const char *eos,
saved_location_t saved_location,
diff --git a/src/or/router.c b/src/or/router.c
index 29d44c33ec..105b1c11a3 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -1803,7 +1803,7 @@ router_purpose_to_string(uint8_t p)
return NULL;
}
-/** Given a string, convert it to a router purpose. */
+/** Given a string, convert it to a router purpose. */
uint8_t
router_purpose_from_string(const char *s)
{
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 501803310a..f7da316ce9 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -2991,7 +2991,8 @@ routerlist_descriptors_added(smartlist_t *sl)
* This is used only by the controller.
*/
int
-router_load_single_router(const char *s, uint8_t purpose, const char **msg)
+router_load_single_router(const char *s, uint8_t purpose, int cache,
+ const char **msg)
{
routerinfo_t *ri;
int r;
@@ -3017,6 +3018,9 @@ router_load_single_router(const char *s, uint8_t purpose, const char **msg)
return 0;
}
+ if (!cache) /* obey the preference of the controller */
+ ri->cache_info.do_not_cache = 1;
+
lst = smartlist_create();
smartlist_add(lst, ri);
routers_update_status_from_networkstatus(lst, 0);