[dns-operations] Source code to identify the fake DNS packets from China Re: Odd behaviour on one node in I root-server (facebook, youtube & twitter)

崔莺莺 yingyingcui.scholarzhang at gmail.com
Sat Mar 27 14:07:53 UTC 2010


These wrong DNS replies are sent by the notorious Great Firewall of
China. Checked by this program, the phenomenon is GFW's DNS poisoning
without doubt.

There is one version of the matching function in
http://code.google.com/p/scholarzhang/source/browse/trunk/west-chamber/extensions/xt_gfw.c
. And in the google code project mentioned, the xt_gfw.c provided a
iptables module to match the certain packets.

Here is my own codes to listen to every DNS packets and show whether
it is a GFW's Poisoning packet in userspace. Normally it prints one
256 or 512 whenever it see a DNS poisoning packet from GFW.

You may want to modify the code about LINK_OFF, DLT_EN10MB and device
"eth0" before compile it.

*** /dev/null   2010-01-01 00:00:00.000000000 +0000
--- dns_gfwcheck.c      2010-01-01 00:00:00.000000000 +0000
***************
*** 0 ****
--- 1,111 ----
+ #include "dns_gfwcheck.h"
+ #include <arpa/inet.h>
+
+ __u16 gfw_dns_type(struct iphdr *iph)
+ {
+       __u8 *ipp = (__u8 *)iph;
+       __u8 *boundry = ipp + ntohs(iph->tot_len), *pos, *pos2, *pos3;
+
+ #define __DNS_FLAG_OFF 30
+ #define __DNS_FLAG *(__be16 *)(ipp + __DNS_FLAG_OFF)
+ #define __DNS_QDCOUNT_OFF 32
+ #define __DNS_QDCOUNT *(__be16 *)(ipp + __DNS_QDCOUNT_OFF)
+ #define __DNS_ANCOUNT_OFF 34
+ #define __DNS_ANCOUNT *(__be16 *)(ipp + __DNS_ANCOUNT_OFF)
+ #define __DNS_NSCOUNT_OFF 36
+ #define __DNS_NSCOUNT *(__be16 *)(ipp + __DNS_NSCOUNT_OFF)
+ #define __DNS_ARCOUNT_OFF 38
+ #define __DNS_ARCOUNT *(__be16 *)(ipp + __DNS_ARCOUNT_OFF)
+ #define __DNS_QUERIES_OFF 40
+ #define __DNS_RR_TTL_OFF 4
+ #define __DNS_RR_TTL *(__be32 *)(pos3 + __DNS_RR_TTL_OFF)
+ #define __DNS_RR_DATA_LENGTH_OFF 8
+ #define __DNS_RR_DATA_LENGTH *(__be16 *)(pos3 + __DNS_RR_DATA_LENGTH_OFF)
+ #define __DNS_RR_DATA_OFF 10
+ #define GFW_DNS_QDCOUNT htons(1)
+ #define GFW_DNS_ANCOUNT htons(1)
+ #define GFW_DNS_NSCOUNT 0
+ #define GFW_DNS_ARCOUNT 0
+ #define GFW_DNS_TYPE1_ID htons(0x7110)
+ #define GFW_DNS_TYPE1_FLAG htons(0x8180)
+ #define GFW_DNS_TYPE2_FLAG htons(0x8580)
+ #define GFW_DNS_TYPE1_HOST htons(0xc00c)
+ #define GFW_DNS_TYPE1_TYPEnCLASS 0x01000100
+ #define GFW_DNS_TYPE1_TTL htonl(300)
+ #define GFW_DNS_TYPE2_TTL htonl(86400)
+ #define GFW_POISON1 inet_addr("93.46.8.89")
+ #define GFW_POISON2 inet_addr("203.98.7.65")
+ #define GFW_POISON3 inet_addr("8.7.198.45")
+ #define GFW_POISON4 inet_addr("78.16.49.15")
+ #define GFW_POISON5 inet_addr("46.82.174.68")
+ #define GFW_POISON6 inet_addr("243.185.187.39")
+ #define GFW_POISON7 inet_addr("159.106.121.75")
+ #define GFW_POISON8 inet_addr("37.61.54.158")
+ #define GFW_POISON9 inet_addr("59.24.3.173")
+ #define __IHL 5
+ #define __RR_IP *(__be32 *)pos3
+
+       /* Let's ignore iph->tos here since this field may be modified
+        * by the intermediate routers. And I can't believe router may
+        * modify options of the ip packet.
+        */
+       if ( iph->ihl != __IHL || iph->frag_off != 0 /*|| iph->tos != 0*/
+            || __DNS_QDCOUNT != GFW_DNS_QDCOUNT
+            || __DNS_ANCOUNT != GFW_DNS_ANCOUNT
+            || __DNS_NSCOUNT != GFW_DNS_NSCOUNT
+            || __DNS_ARCOUNT != GFW_DNS_ARCOUNT )
+               return GFW_DNS_FALSE;
+
+       if ( iph->id == GFW_DNS_TYPE1_ID
+            && __DNS_FLAG == GFW_DNS_TYPE1_FLAG ) {
+               for (pos = ipp + __DNS_QUERIES_OFF;
+                    pos < boundry && *pos != 0; pos += *pos + 1);
+               pos += (1 + 2 + 2);
+               if ( pos + (2 + __DNS_RR_DATA_OFF + 4) == boundry
+                    && *(__be16 *)pos == GFW_DNS_TYPE1_HOST ) {
+                       pos3 = pos + 2;
+                       if ( *(__be32 *)pos3 == GFW_DNS_TYPE1_TYPEnCLASS
+                            && __DNS_RR_TTL == GFW_DNS_TYPE1_TTL
+                            && __DNS_RR_DATA_LENGTH == 0x0400 ) {
+                               pos3 += __DNS_RR_DATA_OFF;
+                               if ( __RR_IP == GFW_POISON1
+                                    || __RR_IP == GFW_POISON2
+                                    || __RR_IP == GFW_POISON3
+                                    || __RR_IP == GFW_POISON4
+                                    || __RR_IP == GFW_POISON5
+                                    || __RR_IP == GFW_POISON6
+                                    || __RR_IP == GFW_POISON7
+                                    || __RR_IP == GFW_POISON8
+                                    || __RR_IP == GFW_POISON9 )
+                                       return GFW_DNS_TYPE1;
+                       }
+               }
+       }
+       else if ( (ntohs(iph->id) % 79 == 27)
+                 && (__DNS_FLAG == GFW_DNS_TYPE2_FLAG) ) {
+               for (pos = ipp + __DNS_QUERIES_OFF; (pos < boundry) &&
*pos != 0;
+                    pos += *pos + 1);
+
+               pos2 = pos + 1;
+               pos3 = pos + (1 + 2 + 2);
+               pos = ipp + __DNS_QUERIES_OFF;
+               if ( pos2 - pos + pos3 + (__DNS_RR_DATA_OFF + 4) == boundry ) {
+                       for (;(*pos == *pos3) && (pos < pos2); ++pos, ++pos3);
+                       if ( pos == pos2 && __DNS_RR_TTL == GFW_DNS_TYPE2_TTL
+                            && __DNS_RR_DATA_LENGTH == 0x0400 ) {
+                               pos3 += __DNS_RR_DATA_OFF;
+                               if ( __RR_IP == GFW_POISON1
+                                    || __RR_IP == GFW_POISON2
+                                    || __RR_IP == GFW_POISON3
+                                    || __RR_IP == GFW_POISON4
+                                    || __RR_IP == GFW_POISON5
+                                    || __RR_IP == GFW_POISON6
+                                    || __RR_IP == GFW_POISON7
+                                    || __RR_IP == GFW_POISON8
+                                    || __RR_IP == GFW_POISON9 )
+                                       return GFW_DNS_TYPE2;
+                       }
+               }
+       }
+       return GFW_DNS_FALSE;
+ }
*** /dev/null   2010-01-01 00:00:00.000000000 +0000
--- dns_gfwcheck.h      2010-01-01 00:00:00.000000000 +0000
***************
*** 0 ****
--- 1,18 ----
+ #ifndef DNS_ISGFW_H
+ #define DNS_ISGFW_H
+
+ #include <linux/ip.h>
+ #include <linux/udp.h>
+
+ #define GFW_DNS_TYPE1 0x0100
+ #define GFW_DNS_TYPE2 0x0200
+ #define GFW_DNS_FALSE 0
+
+ /**
+  * User MUST guarantee that ipp is a UDP/IP packet.
+  * @param ipp: pointer to the FULL ip packet
+  * @return if the packet is sent by GFW, return GFW_DNS_TYPE{1,2};
otherwise return GFW_DNS_FALSE
+  */
+ __u16 gfw_dns_type(struct iphdr *iph);
+
+ #endif
*** /dev/null   2010-01-01 00:00:00.000000000 +0000
--- test.c      2010-01-01 00:00:00.000000000 +0000
***************
*** 0 ****
--- 1,65 ----
+ #include "dns_gfwcheck.h"
+ #include <stdio.h>
+ #include <unistd.h>
+ #include <pcap.h>
+
+ pcap_t *handle;
+
+ void print_unpoisoned() {
+       struct pcap_pkthdr *pkt_header;
+       const __u8 *pkt_data;
+ #define LINK_OFF 14
+       int ret;
+
+       while (1) {
+               ret = pcap_next_ex(handle, &pkt_header, &pkt_data);
+               if (ret == 0)
+                       continue;
+               if (ret == -2)
+                       break;
+               ret = gfw_dns_type( (struct iphdr *)(pkt_data + LINK_OFF) );
+               printf("%d\n", ret);
+       }
+ }
+
+ int main (int argc, char **argv) {
+       char filter_exp[] = "udp src port 53";
+       char pcap_dev[] = "eth0";
+       int pcap_snaplen = 65535;
+       int pcap_timeout = 1;
+       int pcap_promisc = 0;
+       char pcap_errbuf[PCAP_ERRBUF_SIZE];
+       struct bpf_program fp;
+       int opt;
+       char live = 1;
+
+       while ((opt = getopt(argc, argv, "r:")) != -1) {
+               switch(opt) {
+               case 'r':
+                       live = 0;
+                       handle = pcap_open_offline(optarg, pcap_errbuf);
+               }
+       }
+
+       if (live)
+               handle = pcap_open_live(pcap_dev, pcap_snaplen, pcap_promisc,
+                                       pcap_timeout, pcap_errbuf);
+
+       if (handle == NULL) {
+               fprintf(stderr, "Cannot open device/file for capture\n");
+               return 2;
+       }
+       if ( pcap_compile(handle, &fp, filter_exp, 1, 0) == -1
+            || pcap_setfilter(handle, &fp) == -1 ) {
+               pcap_perror(handle, "pcap");
+               return 2;
+       }
+
+       if (pcap_datalink(handle) != DLT_EN10MB ) {
+               fprintf(stderr, "Unsupported DLT\n");
+               return 2;
+       }
+
+       print_unpoisoned();
+       return 0;
+ }
*** /dev/null   2010-01-01 00:00:00.000000000 +0000
--- Makefile    2010-01-01 00:00:00.000000000 +0000
***************
*** 0 ****
--- 1,15 ----
+ CC = cc
+ INCLUDES = -I.
+ LIBS = -lpcap
+ CFLAGS = -Wall -Wextra -g
+
+ lib: dns_gfwcheck.o
+
+ test: test.c dns_gfwcheck.o
+       $(CC) $(LIBS) $(CFLAGS) $(INCLUDES) -o $@ $< dns_gfwcheck.o
+
+ dns_isgfw.o: dns_gfwcheck.c dns_gfwcheck.h
+       $(CC) $(CFLAGS) $(INCLUDES) -c $<
+
+ clean:
+       rm -f *.o test



More information about the dns-operations mailing list