diff --git a/changes/bug8596 b/changes/bug8596
new file mode 100644
index 0000000000000000000000000000000000000000..dd36bad85526fd85071e1bfe2ba0a27e67947f78
--- /dev/null
+++ b/changes/bug8596
@@ -0,0 +1,3 @@
+  o Minor features:
+    - Add CACHED keyword to ADDRMAP events in the control protocol to indicate
+      whether a DNS result will be cached or not.
diff --git a/src/or/addressmap.c b/src/or/addressmap.c
index 826eb301db27d87813e74e1c37f948cb7b828be3..79e4b7c5e299a898eaa3ff87c669c8fb9b3ce564 100644
--- a/src/or/addressmap.c
+++ b/src/or/addressmap.c
@@ -560,7 +560,7 @@ addressmap_register(const char *address, char *new_address, time_t expires,
   log_info(LD_CONFIG, "Addressmap: (re)mapped '%s' to '%s'",
            safe_str_client(address),
            safe_str_client(ent->new_address));
-  control_event_address_mapped(address, ent->new_address, expires, NULL);
+  control_event_address_mapped(address, ent->new_address, expires, NULL, 1);
 }
 
 /** An attempt to resolve <b>address</b> failed at some OR.
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 5075c474a35b2ee482bd83f9f9fa937a9dddfbe6..926fcab90cd87b3258d6a24b5733837a24e0917f 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -2040,25 +2040,21 @@ tell_controller_about_resolved_result(entry_connection_t *conn,
                                       int ttl,
                                       time_t expires)
 {
-
-  if (ttl >= 0 && (answer_type == RESOLVED_TYPE_IPV4 ||
-                   answer_type == RESOLVED_TYPE_HOSTNAME)) {
-    return; /* we already told the controller. */
-  } else if (answer_type == RESOLVED_TYPE_IPV4 && answer_len >= 4) {
+  expires = time(NULL) + ttl;
+  if (answer_type == RESOLVED_TYPE_IPV4 && answer_len >= 4) {
     char *cp = tor_dup_ip(ntohl(get_uint32(answer)));
     control_event_address_mapped(conn->socks_request->address,
-                                 cp, expires, NULL);
+                                 cp, expires, NULL, 0);
     tor_free(cp);
   } else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len < 256) {
     char *cp = tor_strndup(answer, answer_len);
     control_event_address_mapped(conn->socks_request->address,
-                                 cp, expires, NULL);
+                                 cp, expires, NULL, 0);
     tor_free(cp);
   } else {
     control_event_address_mapped(conn->socks_request->address,
-                                 "<error>",
-                                 time(NULL)+ttl,
-                                 "error=yes");
+                                 "<error>", time(NULL)+ttl,
+                                 "error=yes", 0);
   }
 }
 
@@ -2116,8 +2112,9 @@ connection_ap_handshake_socks_resolved(entry_connection_t *conn,
       conn->socks_request->has_finished = 1;
       return;
     } else {
-      /* This must be a request from the controller. We already sent
-       * a mapaddress if there's a ttl. */
+      /* This must be a request from the controller. Since answers to those
+       * requests are not cached, they do not generate an ADDRMAP event on
+       * their own. */
       tell_controller_about_resolved_result(conn, answer_type, answer_len,
                                             (char*)answer, ttl, expires);
       conn->socks_request->has_finished = 1;
diff --git a/src/or/control.c b/src/or/control.c
index 2a68464189d956bacada85fb86f5f4ed664b78a6..f50b87711c6be48497ccabaa3b65e537088170d0 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -2947,7 +2947,7 @@ handle_control_resolve(control_connection_t *conn, uint32_t len,
   send_control_done(conn);
   SMARTLIST_FOREACH(failed, const char *, arg, {
       control_event_address_mapped(arg, arg, time(NULL),
-                                   "internal");
+                                   "internal", 0);
   });
 
   SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
@@ -4024,15 +4024,17 @@ control_event_descriptors_changed(smartlist_t *routers)
  */
 int
 control_event_address_mapped(const char *from, const char *to, time_t expires,
-                             const char *error)
+                             const char *error, const int cached)
 {
   if (!EVENT_IS_INTERESTING(EVENT_ADDRMAP))
     return 0;
 
   if (expires < 3 || expires == TIME_MAX)
     send_control_event(EVENT_ADDRMAP, ALL_FORMATS,
-                                "650 ADDRMAP %s %s NEVER %s\r\n", from, to,
-                                error?error:"");
+                                "650 ADDRMAP %s %s NEVER %s%s"
+                                "CACHED=\"%s\"\r\n",
+                                  from, to, error?error:"", error?" ":"",
+                                cached?"YES":"NO");
   else {
     char buf[ISO_TIME_LEN+1];
     char buf2[ISO_TIME_LEN+1];
@@ -4040,10 +4042,10 @@ control_event_address_mapped(const char *from, const char *to, time_t expires,
     format_iso_time(buf2,expires);
     send_control_event(EVENT_ADDRMAP, ALL_FORMATS,
                                 "650 ADDRMAP %s %s \"%s\""
-                                " %s%sEXPIRES=\"%s\"\r\n",
+                                " %s%sEXPIRES=\"%s\" CACHED=\"%s\"\r\n",
                                 from, to, buf,
                                 error?error:"", error?" ":"",
-                                buf2);
+                                buf2, cached?"YES":"NO");
   }
 
   return 0;
diff --git a/src/or/control.h b/src/or/control.h
index 51ae230b09ee23f83ea45250af8caf994ecf28a4..0ea7941b13a71cb9a963207898f798987eb5f4c1 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -53,7 +53,8 @@ int control_event_stream_bandwidth_used(void);
 void control_event_logmsg(int severity, uint32_t domain, const char *msg);
 int control_event_descriptors_changed(smartlist_t *routers);
 int control_event_address_mapped(const char *from, const char *to,
-                                 time_t expires, const char *error);
+                                 time_t expires, const char *error,
+                                 const int cached);
 int control_event_or_authdir_new_descriptor(const char *action,
                                             const char *desc,
                                             size_t desclen,