#include "evutil.h"
#include "log.h"
#include "mm-internal.h"
+#include "strlcpy-internal.h"
#ifdef WIN32
#include <winsock2.h>
#include <windows.h>
int global_max_retransmits; /* number of times we'll retransmit a request which timed out */
/* number of timeouts in a row before we consider this server to be down */
int global_max_nameserver_timeout;
+ /* true iff we will use the 0x20 hack to prevent poisoning attacks. */
+ int global_randomize_case;
struct search_state *global_search_state;
};
u16 _t; /* used by the macros */
u32 _t32; /* used by the macros */
char tmp_name[256], cmp_name[256]; /* used by the macros */
+ int name_matches = 0;
u16 trans_id, questions, answers, authority, additional, datalength;
u16 flags = 0;
goto err; \
if (name_parse(req->request, req->request_len, &k, cmp_name, sizeof(cmp_name))<0) \
goto err; \
- if (memcmp(tmp_name, cmp_name, strlen (tmp_name)) != 0) \
- return (-1); /* we ignore mismatching names */ \
+ if (base->global_randomize_case) { \
+ if (strcmp(tmp_name, cmp_name) == 0) \
+ name_matches = 1; \
+ } else { \
+ if (strcasecmp(tmp_name, cmp_name) == 0) \
+ name_matches = 1; \
+ } \
} while(0)
reply.type = req->request_type;
if (j > length) goto err;
}
+ if (!name_matches)
+ goto err;
+
/* now we have the answer section which looks like
* <label:name><u16:type><u16:class><u32:ttl><u16:len><data...>
*/
static ev_uint16_t (*trans_id_function)(void) = default_transaction_id_fn;
+static void
+default_random_bytes_fn(char *buf, size_t n)
+{
+ unsigned i;
+ for (i = 0; i < n-1; i += 2) {
+ u16 tid = trans_id_function();
+ buf[i] = (tid >> 8) & 0xff;
+ buf[i+1] = tid & 0xff;
+ }
+ if (i < n) {
+ u16 tid = trans_id_function();
+ buf[i] = tid & 0xff;
+ }
+}
+
+static void (*rand_bytes_function)(char *buf, size_t n) =
+ default_random_bytes_fn;
+
+static u16
+trans_id_from_random_bytes_fn(void)
+{
+ u16 tid;
+ rand_bytes_function((char*) &tid, sizeof(tid));
+ return tid;
+}
+
void
evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void))
{
trans_id_function = fn;
else
trans_id_function = default_transaction_id_fn;
+ rand_bytes_function = default_random_bytes_fn;
+}
+
+void
+evdns_set_random_bytes_fn(void (*fn)(char *, size_t))
+{
+ rand_bytes_function = fn;
+ trans_id_function = trans_id_from_random_bytes_fn;
}
/* Try to choose a strong transaction id which isn't already in flight */
return count;
}
+/* Helper: provide a working isalpha implementation on platforms with funny
+ * ideas about character types and isalpha behavior. */
+#define ISALPHA(c) isalpha((int)(unsigned char)(c))
+
static struct evdns_request *
request_new(struct evdns_base *base, int type, const char *name, int flags,
evdns_callback_type callback, void *user_ptr) {
struct evdns_request *const req =
(struct evdns_request *) mm_malloc(sizeof(struct evdns_request) + request_max_len);
int rlen;
+ char namebuf[256];
(void) flags;
if (!req) return NULL;
memset(req, 0, sizeof(struct evdns_request));
req->base = base;
+ if (base->global_randomize_case) {
+ unsigned i;
+ char randbits[(sizeof(namebuf)+7)/8];
+ strlcpy(namebuf, name, sizeof(namebuf));
+ rand_bytes_function(randbits, (name_len+7)/8);
+ for (i = 0; i < name_len; ++i) {
+ if (ISALPHA(namebuf[i])) {
+ if ((randbits[i >> 3] & (1<<(i & 7))))
+ namebuf[i] |= 0x20;
+ else
+ namebuf[i] &= ~0x20;
+ }
+ }
+ name = namebuf;
+ }
+
/* request data lives just after the header */
req->request = ((u8 *) req) + sizeof(struct evdns_request);
/* denotes that the request data shouldn't be free()ed */
type, CLASS_INET, req->request, request_max_len);
if (rlen < 0)
goto err1;
+
req->request_len = rlen;
req->trans_id = trans_id;
req->tx_count = 0;
if (!(flags & DNS_OPTION_MISC)) return 0;
log(EVDNS_LOG_DEBUG, "Setting retries to %d", retries);
base->global_max_retransmits = retries;
+ } else if (!strncmp(option, "randomize-case:", 15)) {
+ int randcase = strtoint(val);
+ if (!(flags & DNS_OPTION_MISC)) return 0;
+ base->global_randomize_case = randcase;
}
return 0;
}
base->global_max_retransmits = 3;
base->global_max_nameserver_timeout = 3;
base->global_search_state = NULL;
+ base->global_randomize_case = 1;
if (initialize_nameservers) {
int r;
#ifdef WIN32
The currently available configuration options are:
- ndots, timeout, max-timeouts, max-inflight, and attempts
+ ndots, timeout, max-timeouts, max-inflight, attempts, randomize-case.
@param base the evdns_base to which to apply this operation
@param option the name of the configuration option to be modified
/**
Set a callback that will be invoked to generate transaction IDs. By
- default, we pick transaction IDs based on the current clock time.
+ default, we pick transaction IDs based on the current clock time, which
+ is bad for security.
@param fn the new callback, or NULL to use the default.
*/
void evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void));
+/**
+ Set a callback used to generate random bytes. By default, we use
+ the same function as passed to evdns_set_transaction_id_fn to generate
+ bytes two at a time. If a function is provided here, it's also used
+ to generate transaction IDs.
+*/
+void evdns_set_random_bytes_fn(void (*fn)(char *, size_t));
+
#define DNS_NO_SEARCH 1
/*
ans.s_addr = htonl(0xc0a80b0bUL); /* 192.168.11.11 */
if (req->questions[i]->type == EVDNS_TYPE_A &&
req->questions[i]->dns_question_class == EVDNS_CLASS_INET &&
- !strcmp(req->questions[i]->name, "zz.example.com")) {
- r = evdns_server_request_add_a_reply(req, "zz.example.com",
+ !strcasecmp(req->questions[i]->name, "zz.example.com")) {
+ r = evdns_server_request_add_a_reply(req,
+ req->questions[i]->name,
1, &ans.s_addr, 12345);
if (r<0)
dns_ok = 0;
} else if (req->questions[i]->type == EVDNS_TYPE_AAAA &&
req->questions[i]->dns_question_class == EVDNS_CLASS_INET &&
- !strcmp(req->questions[i]->name, "zz.example.com")) {
+ !strcasecmp(req->questions[i]->name, "zz.example.com")) {
char addr6[17] = "abcdefghijklmnop";
- r = evdns_server_request_add_aaaa_reply(req, "zz.example.com",
+ r = evdns_server_request_add_aaaa_reply(req,
+ req->questions[i]->name,
1, addr6, 123);
if (r<0)
dns_ok = 0;
} else if (req->questions[i]->type == EVDNS_TYPE_PTR &&
req->questions[i]->dns_question_class == EVDNS_CLASS_INET &&
- !strcmp(req->questions[i]->name, TEST_ARPA)) {
- r = evdns_server_request_add_ptr_reply(req, NULL, TEST_ARPA,
- "ZZ.EXAMPLE.COM", 54321);
+ !strcasecmp(req->questions[i]->name, TEST_ARPA)) {
+ r = evdns_server_request_add_ptr_reply(req, NULL,
+ req->questions[i]->name,
+ "ZZ.EXAMPLE.COM", 54321);
if (r<0)
dns_ok = 0;
} else {