Domain name random case results in the failure of the asynchronous DNS resolution of libevent2

The Libevent built-in asynchronous DNS parser uses a Hacking means called DNS-0x20 Encoding to prevent DNS from being poisoned. But this practice is problematic, which will cause DNS parsing in some environments to fail.

DNS protocol regulations on domain name sensation

In the database, the domain name should be regardless of case.

The RFC standard for the DNS protocol is There is a paragraph in its 3.5.

“Note That While Upper and Lower Case Letters Are Allowed in Domain Names, No Significance Is Attached to the Case. That IS, Two Names with the Same Spelling ButdingRerent Case Are To Be Treated As if Identical.

In the DNS reply, you should keep the domain name sensation. DNS messages have three types request, reply, update. What is the case where the domain name in the request package should be consistent in the reply. For example, I want to check, the domain name in the received reply message should also be or or And in the database,, and should be the same record.

In fact, some DNS Server does not comply with standards.

Here is the experiment I did. I launched a request for with NSlookup on my Windows 7, and I used Wireshark to capture the whatever.


000000 02 01 00 00 01 00 00 00 00 03 77 77 77 ………… .wwww

001005 42 61 69 64 75 03 63 6F 6D 00 00 01 00 01 … ..


000000 02 80 00 00 01 00 01 00 00 00 00 03 77 77 77 ………… .wwww

001005 62 61 69 64 75 03 63 6F 6D 00 00 01 00 01 c0 ……

00200c 00 01 00 01 00 00 01 F4 00 04 77 4B D9 38 ……….wk.8

It can be obviously seen in the package of the reply, and the domain name is written. According to my guess, it is not the issue of Unicom’s DNS Server, but the midway has been gone by my ISP.

What is DNS-0X20 ENCODING?

It is an unstuten anti-counterfeiting mechanism. DNS Although there is transactionID, it is easy to be forged. So I think, if we use the domain name in the request package, the DNS Server does comply with the RFC 1034 specification, and then return, then this increases the difficulty of poisoning in the middle. Because someone wants to tamper with tampering, we have to maintain a session to record that the current package is uppercase or lowercase. In the ASCII table, the uppercase letters start from 0x41, lowercase letters start from 0x61, just the difference 0x20. That is, ‘a’ | 0x20 ‘a’; so this random case is called 0x20 Encoding. But I think it is too stupid that you are too stupid or you think too stupid?

In fact, Google does use this method when building its public DNS Server ( But it knows that reality is inconsistent with ideals, so a set of white list mechanisms is used. Only 0x20 encoding is used in DNS Server in white list. Google said that such a request accounted for more than 70% of the traffic (“The WhiteListed Nameservers Comprise More Than 70% of Our Traffic.” from

Libevent How to achieve DNS-0x20 EncodingLibEvent is a very popular network library, such as Chrome, memcached relies on it to achieve non-blocking IO. When writing non-blocking IO programs, it is important to be a domain name resolution must also be asynchronous, that is, those domain name resolution functions in GLIBC cannot be used. To this end, libevent has a set of asynchronous DNS parsers called EVDNS. But the implementation of EVDNS is much simpler. It is forcibly all requests to use 0x20 encoding.

There is a field inside the EVDNS_BASE structure:

/ * True iff we will use the 0x20 Hack to Prevent Poisoning attics. * /

INT global_randomize_case;

This field is initialized to 1 in the EVDNS_BASE_NEW this function, so this feature is open by default.

Global_randomize_case This switch affects how to construct DNS Request.

In the request_new of evdns.c, this function,

if (base-> global_randomize_case) {unsigned i; char randbits [(sizeof (namebuf) +7) / 8]; strlcpy (namebuf, name, sizeof (namebuf)); evutil_secure_rng_get_bytes (randbits, (name_len + 7) / 8) ; for (i 0; i > 3] & (1

It finds that if this switch is open, it will randomly change the case of the domain name.

After receiving reply from DNS Server, the client calls the reply_parse function to resolve. Here is a function stack.

Reply_PARSE (EVDNS_BASE * BASE, Unsigned Char * Packet, INT Length)

Nameserver_read (Nameserver * NS)

Nameserver_Ready_Callback (int FD, Short Events, Void * arg)

Event_Persist_closure (Event_Base * Base, Event * EV)

EVENT_PROCESS_ACTIVE_SINGLE_QUEUE (Event_Base * Base, Event_List * Activeq)

Event_Process_Active (Event_Base * Base)

Event_base_loop (Event_Base * Base, INT FLAGS)

EVENT_BASE_DISPATCH (Event_Base * Event_base)

In Int reply_parse (struct evdns_base * base, u8 * packet, int length) This function, it is necessary to find match match in the request packet and reply package.

The following TMP_NAME comes from reply, cmp_name comes from Request.

int name_matches0; // … if (base-> global_randomize_case) {if (strcmp (tmp_name, cmp_name) 0) name_matches 1;} else {if (evutil_ascii_strcasecmp (tmp_name, cmp_name) 0) name_matches 1;}

If Name_Matches is 0 after it is finished. Then the DNS resolution failed.

There is a small program in the libevent’s Samples directory, you can try

# ./dns-example -v Parsing resolv.conf file /etc/resolv.conf

INFO: Added Nameserver as 0xBF86D0

Info: Added Nameserver AS 0xBF88A0


Resolving (fwd) …

Info: Resolve Requested for

Info: Setting Timeout for Request 0xBF8A70, SENT to Nameserver 0xBF86D0

Info: Removing Timeout for Request 0xBF8A70 No Answer (66)

Should not be no answser!

If you are curious, since Chrome uses to use Libevent, why don’t you have this problem?The answer is: Chrome does not use the Libevent built-in DNS parser, which only uses the foundation IO section.

Finally, I gave Libevent a Patch request to request them to modify the default behavior.I don’t know if I will be Merge.Https://