Index: src/or/control.c
===================================================================
--- src/or/control.c (revision 10779)
+++ src/or/control.c (working copy)
@@ -43,7 +43,8 @@
#define EVENT_STATUS_GENERAL 0x0012
#define EVENT_GUARD 0x0013
#define EVENT_STREAM_BANDWIDTH_USED 0x0014
-#define _EVENT_MAX 0x0014
+#define EVENT_RESOLVE_RESPONSE 0x0015
+#define _EVENT_MAX 0x0015
/* If _EVENT_MAX ever hits 0x0020, we need to make the mask wider. */
/** Bitfield: The bit 1<<e is set if any open control
@@ -133,6 +134,8 @@
static int handle_control_closecircuit(control_connection_t *conn,
uint32_t len,
const char *body);
+static int handle_control_resolve(control_connection_t *conn, uint32_t len,
+ const char *body);
static int handle_control_usefeature(control_connection_t *conn,
uint32_t len,
const char *body);
@@ -495,6 +498,15 @@
continue;
}
if (control_conn->event_mask & (1<resolve_cmd_pending)
+ continue;
+ if (strncasecmp(control_conn->resolve_cmd_pending, msg,
+ strlen(control_conn->resolve_cmd_pending)))
+ continue;
+ tor_free(control_conn->resolve_cmd_pending);
+ }
int is_err = 0;
connection_write_to_buf(msg, strlen(msg), TO_CONN(control_conn));
if (event == EVENT_ERR_MSG)
@@ -2212,6 +2224,51 @@
return 0;
}
+static int
+handle_control_resolve(control_connection_t *conn, uint32_t len,
+ const char *body)
+{
+ smartlist_t *args;
+ (void) len; /* body is nul-terminated; it's safe to ignore the length */
+ char msg[512];
+
+ if (conn->resolve_cmd_pending){
+ connection_printf_to_buf(conn, "251 Resolve Already In Progress\r\n");
+ return 0;
+ }
+
+ args = smartlist_create();
+ smartlist_split_string(args, body, " ",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ SMARTLIST_FOREACH(args, const char *, arg, {
+ tor_snprintf(msg, sizeof(msg),"650 RESOLVE %s", arg);
+ conn->resolve_cmd_pending = tor_strdup(msg);
+ dnsserv_server_control(arg);
+ break;
+ });
+
+ conn->event_mask |= (1 << EVENT_RESOLVE_RESPONSE);
+ control_update_global_event_mask();
+
+ SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
+ smartlist_free(args);
+
+ send_control_done(conn);
+ return 0;
+}
+
+int
+control_event_resolve_response(const char *question,const char *answer)
+{
+ if (!EVENT_IS_INTERESTING(EVENT_RESOLVE_RESPONSE))
+ return 0;
+
+ send_control_event(EVENT_RESOLVE_RESPONSE, ALL_NAMES,
+ "650 RESOLVE %s:%s\r\n",
+ question, answer);
+ return 0;
+}
+
/** Called when we get a USEFEATURE command: parse the feature list, and
* set up the control_connection's options properly. */
static int
@@ -2442,6 +2499,9 @@
} else if (!strcasecmp(conn->incoming_cmd, "USEFEATURE")) {
if (handle_control_usefeature(conn, data_len, args))
return -1;
+ } else if (!strcasecmp(conn->incoming_cmd, "RESOLVE")) {
+ if (handle_control_resolve(conn, data_len, args))
+ return -1;
} else {
connection_printf_to_buf(conn, "510 Unrecognized command \"%s\"\r\n",
conn->incoming_cmd);
Index: src/or/or.h
===================================================================
--- src/or/or.h (revision 10779)
+++ src/or/or.h (working copy)
@@ -914,6 +914,9 @@
* request that we're going to try to answer. */
struct evdns_server_request *dns_server_request;
+ /** Is this a DNS request from a controller? */
+ unsigned int control_resolve_request:1;
+
} edge_connection_t;
/** Subtype of connection_t for an "directory connection" -- that is, an HTTP
@@ -970,6 +973,7 @@
uint32_t incoming_cmd_len;
uint32_t incoming_cmd_cur_len;
char *incoming_cmd;
+ char *resolve_cmd_pending;
/* Used only by control v0 connections */
uint16_t incoming_cmd_type;
} control_connection_t;
@@ -2605,6 +2609,7 @@
int control_event_stream_status(edge_connection_t *conn,
stream_status_event_t e,
int reason);
+int control_event_resolve_response(const char *question, const char *answer);
int control_tls_error_to_reason(int e);
int control_event_or_conn_status(or_connection_t *conn,
or_conn_status_event_t e, int reason);
@@ -2801,6 +2806,8 @@
int ttl);
void dnsserv_reject_request(edge_connection_t *conn);
+void dnsserv_server_control(const char *name);
+
/********************************* hibernate.c **********************/
int accounting_parse_options(or_options_t *options, int validate_only);
Index: src/or/dnsserv.c
===================================================================
--- src/or/dnsserv.c (revision 10779)
+++ src/or/dnsserv.c (working copy)
@@ -145,6 +145,79 @@
tor_free(q_name);
}
+/* Helper function: called whenever the client sends a resolve request to our
+ * controller. We need to eventually answer the request req.
+ */
+void
+dnsserv_server_control(const char *name)
+{
+ edge_connection_t *conn;
+ struct evdns_server_request *server_req;
+ struct in_addr in;
+ char *q_name;
+ int i;
+ int is_ip_address;
+
+ /* Make a new dummy AP connection, and attach the request to it. */
+ conn = TO_EDGE_CONN(connection_new(CONN_TYPE_AP, AF_INET));
+ conn->_base.state = AP_CONN_STATE_RESOLVE_WAIT;
+
+ is_ip_address = tor_inet_aton(name, &in);
+
+ if (!is_ip_address)
+ conn->socks_request->command = SOCKS_COMMAND_RESOLVE;
+ else
+ conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;
+
+ strlcpy(conn->socks_request->address, name,
+ sizeof(conn->socks_request->address));
+
+ server_req = malloc(sizeof(struct evdns_server_request));
+ if (server_req == NULL) return;
+ memset(server_req, 0, sizeof(struct evdns_server_request));
+
+ server_req->flags = 0;
+ server_req->nquestions = 0;
+
+ server_req->questions = malloc(sizeof(struct evdns_server_question *) * 1);
+ if (server_req->questions == NULL)
+ return;
+
+ for ( i = 0; i < 1; ++i) {
+ struct evdns_server_question *q;
+ int namelen;
+ namelen = strlen(name);
+ q = malloc(sizeof(struct evdns_server_question) + namelen);
+ if (!q)
+ return;
+ if (!is_ip_address)
+ q->type = EVDNS_TYPE_A;
+ else
+ q->type = EVDNS_TYPE_PTR;
+ q->class = EVDNS_CLASS_INET;
+ memcpy(q->name, name, namelen+1);
+ server_req->questions[server_req->nquestions++] = q;
+ }
+
+ conn->dns_server_request = server_req;
+ conn->control_resolve_request = 1;
+
+ connection_add(TO_CONN(conn));
+
+ /* Now, throw the connection over to get rewritten (which will answer it
+ * immediately if it's in the cache, or completely bogus, or automapped),
+ * and then attached to a circuit. */
+ log_info(LD_APP, "Passing request for %s to rewrite_and_attach.",
+ escaped_safe_str(name));
+ q_name = tor_strdup(name); /* q could be freed in rewrite_and_attach */
+ connection_ap_handshake_rewrite_and_attach(conn, NULL, NULL);
+ /* Now, the connection is marked if it was bad. */
+
+ log_info(LD_APP, "Passed request for %s to rewrite_and_attach.",
+ escaped_safe_str(q_name));
+ tor_free(q_name);
+}
+
/** If there is a pending request on conn that's waiting for an answer,
* send back an error and free the request. */
void
@@ -181,27 +254,49 @@
* or more of the questions in the request); then, call
* evdns_server_request_respond. */
if (answer_type == RESOLVED_TYPE_IPV6) {
- log_info(LD_APP, "Got an IPv6 answer; that's not implemented.");
- err = DNS_ERR_NOTIMPL;
+ if (conn->control_resolve_request)
+ control_event_resolve_response(req->questions[0]->name,
+ "IPv6 not implemented");
+ else {
+ log_info(LD_APP, "Got an IPv6 answer; that's not implemented.");
+ err = DNS_ERR_NOTIMPL;
+ }
} else if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4 &&
conn->socks_request->command == SOCKS_COMMAND_RESOLVE) {
- evdns_server_request_add_a_reply(req,
- conn->socks_request->address,
- 1, (char*)answer, ttl);
+ if (conn->control_resolve_request)
+ control_event_resolve_response(req->questions[0]->name,
+ tor_dup_addr(ntohl(get_uint32(answer))));
+ else
+ evdns_server_request_add_a_reply(req,
+ conn->socks_request->address,
+ 1, (char*)answer, ttl);
} else if (answer_type == RESOLVED_TYPE_HOSTNAME &&
conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR) {
- char *ans = tor_strndup(answer, answer_len);
- evdns_server_request_add_ptr_reply(req, NULL,
+ if (conn->control_resolve_request)
+ control_event_resolve_response(req->questions[0]->name, answer);
+ else {
+ char *ans = tor_strndup(answer, answer_len);
+ evdns_server_request_add_ptr_reply(req, NULL,
conn->socks_request->address,
(char*)answer, ttl);
- tor_free(ans);
+ tor_free(ans);
+ }
} else if (answer_type == RESOLVED_TYPE_ERROR) {
- err = DNS_ERR_NOTEXIST;
+ if (conn->control_resolve_request)
+ control_event_resolve_response(req->questions[0]->name, "Unknown Host");
+ else
+ err = DNS_ERR_NOTEXIST;
} else { /* answer_type == RESOLVED_TYPE_ERROR_TRANSIENT */
- err = DNS_ERR_SERVERFAILED;
+ if (conn->control_resolve_request)
+ control_event_resolve_response(req->questions[0]->name,
+ "Temporary Error");
+ else
+ err = DNS_ERR_SERVERFAILED;
}
- evdns_server_request_respond(req, err);
+ if (!conn->control_resolve_request)
+ evdns_server_request_respond(req, err);
+
conn->dns_server_request = NULL;
}