📄 dns.c
字号:
if (pend->conn == conn) {
resolve->pending_connections = pend->next;
tor_free(pend);
log_debug(LD_EXIT, "First connection (fd %d) no longer waiting "
"for resolve of %s",
conn->_base.s, escaped_safe_str(conn->_base.address));
return;
} else {
for ( ; pend->next; pend = pend->next) {
if (pend->next->conn == conn) {
victim = pend->next;
pend->next = victim->next;
tor_free(victim);
log_debug(LD_EXIT,
"Connection (fd %d) no longer waiting for resolve of %s",
conn->_base.s, escaped_safe_str(conn->_base.address));
return; /* more are pending */
}
}
tor_assert(0); /* not reachable unless onlyconn not in pending list */
}
}
/** Mark all connections waiting for <b>address</b> for close. Then cancel
* the resolve for <b>address</b> itself, and remove any cached results for
* <b>address</b> from the cache.
*/
void
dns_cancel_pending_resolve(const char *address)
{
pending_connection_t *pend;
cached_resolve_t search;
cached_resolve_t *resolve, *tmp;
edge_connection_t *pendconn;
circuit_t *circ;
strlcpy(search.address, address, sizeof(search.address));
resolve = HT_FIND(cache_map, &cache_root, &search);
if (!resolve)
return;
if (resolve->state != CACHE_STATE_PENDING) {
/* We can get into this state if we never actually created the pending
* resolve, due to finding an earlier cached error or something. Just
* ignore it. */
if (resolve->pending_connections) {
log_warn(LD_BUG,
"Address %s is not pending but has pending connections!",
escaped_safe_str(address));
tor_fragile_assert();
}
return;
}
if (!resolve->pending_connections) {
log_warn(LD_BUG,
"Address %s is pending but has no pending connections!",
escaped_safe_str(address));
tor_fragile_assert();
return;
}
tor_assert(resolve->pending_connections);
/* mark all pending connections to fail */
log_debug(LD_EXIT,
"Failing all connections waiting on DNS resolve of %s",
escaped_safe_str(address));
while (resolve->pending_connections) {
pend = resolve->pending_connections;
pend->conn->_base.state = EXIT_CONN_STATE_RESOLVEFAILED;
pendconn = pend->conn;
assert_connection_ok(TO_CONN(pendconn), 0);
tor_assert(pendconn->_base.s == -1);
if (!pendconn->_base.marked_for_close) {
connection_edge_end(pendconn, END_STREAM_REASON_RESOLVEFAILED);
}
circ = circuit_get_by_edge_conn(pendconn);
if (circ)
circuit_detach_stream(circ, pendconn);
if (!pendconn->_base.marked_for_close)
connection_free(TO_CONN(pendconn));
resolve->pending_connections = pend->next;
tor_free(pend);
}
tmp = HT_REMOVE(cache_map, &cache_root, resolve);
if (tmp != resolve) {
log_err(LD_BUG, "The cancelled resolve we purged didn't match any in"
" the cache. Tried to purge %s (%p); instead got %s (%p).",
resolve->address, (void*)resolve,
tmp ? tmp->address : "NULL", (void*)tmp);
}
tor_assert(tmp == resolve);
resolve->state = CACHE_STATE_DONE;
}
/** Helper: adds an entry to the DNS cache mapping <b>address</b> to the ipv4
* address <b>addr</b> (if is_reverse is 0) or the hostname <b>hostname</b> (if
* is_reverse is 1). <b>ttl</b> is a cache ttl; <b>outcome</b> is one of
* DNS_RESOLVE_{FAILED_TRANSIENT|FAILED_PERMANENT|SUCCEEDED}.
**/
static void
add_answer_to_cache(const char *address, uint8_t is_reverse, uint32_t addr,
const char *hostname, char outcome, uint32_t ttl)
{
cached_resolve_t *resolve;
if (outcome == DNS_RESOLVE_FAILED_TRANSIENT)
return;
//log_notice(LD_EXIT, "Adding to cache: %s -> %s (%lx, %s), %d",
// address, is_reverse?"(reverse)":"", (unsigned long)addr,
// hostname?hostname:"NULL",(int)outcome);
resolve = tor_malloc_zero(sizeof(cached_resolve_t));
resolve->magic = CACHED_RESOLVE_MAGIC;
resolve->state = (outcome == DNS_RESOLVE_SUCCEEDED) ?
CACHE_STATE_CACHED_VALID : CACHE_STATE_CACHED_FAILED;
strlcpy(resolve->address, address, sizeof(resolve->address));
resolve->is_reverse = is_reverse;
if (is_reverse) {
if (outcome == DNS_RESOLVE_SUCCEEDED) {
tor_assert(hostname);
resolve->result.hostname = tor_strdup(hostname);
} else {
tor_assert(! hostname);
resolve->result.hostname = NULL;
}
} else {
tor_assert(!hostname);
resolve->result.addr = addr;
}
resolve->ttl = ttl;
assert_resolve_ok(resolve);
HT_INSERT(cache_map, &cache_root, resolve);
set_expiry(resolve, time(NULL) + dns_get_expiry_ttl(ttl));
}
/** Return true iff <b>address</b> is one of the addresses we use to verify
* that well-known sites aren't being hijacked by our DNS servers. */
static INLINE int
is_test_address(const char *address)
{
or_options_t *options = get_options();
return options->ServerDNSTestAddresses &&
smartlist_string_isin_case(options->ServerDNSTestAddresses, address);
}
/** Called on the OR side when a DNS worker or the eventdns library tells us
* the outcome of a DNS resolve: tell all pending connections about the result
* of the lookup, and cache the value. (<b>address</b> is a NUL-terminated
* string containing the address to look up; <b>addr</b> is an IPv4 address in
* host order; <b>outcome</b> is one of
* DNS_RESOLVE_{FAILED_TRANSIENT|FAILED_PERMANENT|SUCCEEDED}.
*/
static void
dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
const char *hostname, char outcome, uint32_t ttl)
{
pending_connection_t *pend;
cached_resolve_t search;
cached_resolve_t *resolve, *removed;
edge_connection_t *pendconn;
circuit_t *circ;
assert_cache_ok();
strlcpy(search.address, address, sizeof(search.address));
resolve = HT_FIND(cache_map, &cache_root, &search);
if (!resolve) {
int is_test_addr = is_test_address(address);
if (!is_test_addr)
log_info(LD_EXIT,"Resolved unasked address %s; caching anyway.",
escaped_safe_str(address));
add_answer_to_cache(address, is_reverse, addr, hostname, outcome, ttl);
return;
}
assert_resolve_ok(resolve);
if (resolve->state != CACHE_STATE_PENDING) {
/* XXXX020 Maybe update addr? or check addr for consistency? Or let
* VALID replace FAILED? */
int is_test_addr = is_test_address(address);
if (!is_test_addr)
log_notice(LD_EXIT,
"Resolved %s which was already resolved; ignoring",
escaped_safe_str(address));
tor_assert(resolve->pending_connections == NULL);
return;
}
/* Removed this assertion: in fact, we'll sometimes get a double answer
* to the same question. This can happen when we ask one worker to resolve
* X.Y.Z., then we cancel the request, and then we ask another worker to
* resolve X.Y.Z. */
/* tor_assert(resolve->state == CACHE_STATE_PENDING); */
while (resolve->pending_connections) {
pend = resolve->pending_connections;
pendconn = pend->conn; /* don't pass complex things to the
connection_mark_for_close macro */
assert_connection_ok(TO_CONN(pendconn),time(NULL));
pendconn->_base.addr = addr;
pendconn->address_ttl = ttl;
if (outcome != DNS_RESOLVE_SUCCEEDED) {
/* prevent double-remove. */
pendconn->_base.state = EXIT_CONN_STATE_RESOLVEFAILED;
if (pendconn->_base.purpose == EXIT_PURPOSE_CONNECT) {
connection_edge_end(pendconn, END_STREAM_REASON_RESOLVEFAILED);
/* This detach must happen after we send the end cell. */
circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
} else {
send_resolved_cell(pendconn, outcome == DNS_RESOLVE_FAILED_PERMANENT ?
RESOLVED_TYPE_ERROR : RESOLVED_TYPE_ERROR_TRANSIENT);
/* This detach must happen after we send the resolved cell. */
circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
}
connection_free(TO_CONN(pendconn));
} else {
if (pendconn->_base.purpose == EXIT_PURPOSE_CONNECT) {
tor_assert(!is_reverse);
/* prevent double-remove. */
pend->conn->_base.state = EXIT_CONN_STATE_CONNECTING;
circ = circuit_get_by_edge_conn(pend->conn);
tor_assert(circ);
tor_assert(!CIRCUIT_IS_ORIGIN(circ));
/* unlink pend->conn from resolving_streams, */
circuit_detach_stream(circ, pend->conn);
/* and link it to n_streams */
pend->conn->next_stream = TO_OR_CIRCUIT(circ)->n_streams;
pend->conn->on_circuit = circ;
TO_OR_CIRCUIT(circ)->n_streams = pend->conn;
connection_exit_connect(pend->conn);
} else {
/* prevent double-remove. This isn't really an accurate state,
* but it does the right thing. */
pendconn->_base.state = EXIT_CONN_STATE_RESOLVEFAILED;
if (is_reverse)
send_resolved_hostname_cell(pendconn, hostname);
else
send_resolved_cell(pendconn, RESOLVED_TYPE_IPV4);
circ = circuit_get_by_edge_conn(pendconn);
tor_assert(circ);
circuit_detach_stream(circ, pendconn);
connection_free(TO_CONN(pendconn));
}
}
resolve->pending_connections = pend->next;
tor_free(pend);
}
resolve->state = CACHE_STATE_DONE;
removed = HT_REMOVE(cache_map, &cache_root, &search);
if (removed != resolve) {
log_err(LD_BUG, "The pending resolve we found wasn't removable from"
" the cache. Tried to purge %s (%p); instead got %s (%p).",
resolve->address, (void*)resolve,
removed ? removed->address : "NULL", (void*)removed);
}
assert_resolve_ok(resolve);
assert_cache_ok();
add_answer_to_cache(address, is_reverse, addr, hostname, outcome, ttl);
assert_cache_ok();
}
/** Eventdns helper: return true iff the eventdns result <b>err</b> is
* a transient failure. */
static int
evdns_err_is_transient(int err)
{
switch (err)
{
case DNS_ERR_SERVERFAILED:
case DNS_ERR_TRUNCATED:
case DNS_ERR_TIMEOUT:
return 1;
default:
return 0;
}
}
/** Configure eventdns nameservers if force is true, or if the configuration
* has changed since the last time we called this function. On Unix, this
* reads from options->ServerDNSResolvConfFile or /etc/resolv.conf; on
* Windows, this reads from options->ServerDNSResolvConfFile or the registry.
* Return 0 on success or -1 on failure. */
static int
configure_nameservers(int force)
{
or_options_t *options;
const char *conf_fname;
struct stat st;
int r;
options = get_options();
conf_fname = options->ServerDNSResolvConfFile;
#ifndef MS_WINDOWS
if (!conf_fname)
conf_fname = "/etc/resolv.conf";
#endif
evdns_set_log_fn(evdns_log_cb);
if (conf_fname) {
if (stat(conf_fname, &st)) {
log_warn(LD_EXIT, "Unable to stat resolver configuration in '%s': %s",
conf_fname, strerror(errno));
return options->ServerDNSAllowBrokenResolvConf ? 0 : -1;
}
if (!force && resolv_conf_fname && !strcmp(conf_fname,resolv_conf_fname)
&& st.st_mtime == resolv_conf_mtime) {
log_info(LD_EXIT, "No change to '%s'", conf_fname);
return 0;
}
if (nameservers_configured) {
evdns_search_clear();
evdns_clear_nameservers_and_suspend();
}
log_info(LD_EXIT, "Parsing resolver configuration in '%s'", conf_fname);
if ((r = evdns_resolv_conf_parse(DNS_OPTIONS_ALL, conf_fname))) {
log_warn(LD_EXIT, "Unable to parse '%s', or no nameservers in '%s' (%d)",
conf_fname, conf_fname, r);
return options->ServerDNSAllowBrokenResolvConf ? 0 : -1;
}
if (evdns_count_nameservers() == 0) {
log_warn(LD_EXIT, "Unable to find any nameservers in '%s'.", conf_fname);
return options->ServerDNSAllowBrokenResolvConf ? 0 : -1;
}
tor_free(resolv_conf_fname);
resolv_conf_fname = tor_strdup(conf_fname);
resolv_conf_mtime = st.st_mtime;
if (nameservers_configured)
evdns_resume();
}
#ifdef MS_WINDOWS
else {
if (nameservers_configured) {
evdns_search_clear();
evdns_clear_nameservers_and_suspend();
}
if (evdns_config_windows_nameservers()) {
log_warn(LD_EXIT,"Could not config nameservers.");
return options->ServerDNSAllowBrokenResolvConf ? 0 : -1;
}
if (evdns_count_nameservers() == 0) {
log_warn(LD_EXIT, "Unable to find any platform nameservers in "
"your Windows configuration. Perhaps you should list a "
"ServerDNSResolvConfFile file in your torrc?");
return options->ServerDNSAllowBrokenResolvConf ? 0 : -1;
}
if (nameservers_configured)
evdns_resume();
tor_free(resolv_conf_fname);
resolv_conf_mtime = 0;
}
#endif
if (evdns_count_nameservers() == 1) {
evdns_set_option("max-timeouts:", "16", DNS_OPTIONS_ALL);
evdns_set_option("timeout:", "10", DNS_OPTIONS_ALL);
} else {
evdns_set_option("max-timeouts:", "3", DNS_OPTIONS_ALL);
evdns_set_option("timeout:", "5", DNS_OPTIONS_ALL);
}
dns_servers_relaunch_checks();
nameservers_configured = 1;
return 0;
}
/** For eventdns: Called when we get an answer for a request we launched.
* See eventdns.h for arguments; 'arg' holds the address we tried to resolve.
*/
static void
evdns_callback(int result, char type, int count, int ttl, void *addresses,
void *arg)
{
char *string_address = arg;
uint8_t is_reverse = 0;
int status = DNS_RESOLVE_FAILED_PERMANENT;
uint32_t addr = 0;
const char *hostname = NULL;
int was_wildcarded = 0;
if (result == DNS_ERR_NONE) {
if (type == DNS_IPv4_A && count) {
char answer_buf[INET_NTOA_BUF_LEN+1];
struct in_addr in;
char *escaped_address;
uint32_t *addrs = addresses;
in.s_addr = addrs[0];
addr = ntohl(addrs[0]);
status = DNS_RESOLVE_SUCCEEDED;
tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf));
escaped_address = esc_for_log(string_address);
if (answer_is_wildcarded(answer_buf)) {
log_debug(LD_EXIT, "eventdns said that %s resolves to ISP-hijacked "
"address %s; treating as a failure.",
safe_str(escaped_address),
escaped_safe_str(answer_buf));
was_wildcarded = 1;
addr = 0;
status = DNS_RESOLVE_FAILED_PERMANENT;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -