├── .gitmodules ├── checkbuild.sh ├── router.h ├── dns-query.sh ├── subnet_api.h ├── update_subnet.sh ├── subnet_api.c ├── Makefile ├── nslookup_wrapper.sh ├── dnsfix_wrt └── Makefile ├── dnsproto.h ├── router.c ├── dns_echo.c ├── dns_mod_access.c ├── subnet_gen.c ├── stunutil.c ├── dns_mod_trd.c ├── dns_split_daemon.c ├── dns_lookup.c ├── dnsproto.c ├── dns_res_trd.c ├── dns_mod_gfw.c └── dns_resolver_ng.c /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libtx"] 2 | path = libtx 3 | url = https://github.com/cachefiles/libtx 4 | -------------------------------------------------------------------------------- /checkbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p host-build 4 | echo "build host version" 5 | make -j5 -C host-build -f $(pwd)/Makefile BUILD_TARGET=$(uname) 6 | 7 | mkdir -p win32-build 8 | echo "build win32 version" 9 | # make -j5 -C win32-build -f $(pwd)/Makefile TARGET=i686-w64-mingw32 BUILD_TARGET=mingw 10 | -------------------------------------------------------------------------------- /router.h: -------------------------------------------------------------------------------- 1 | #ifndef _ROUTER_H_ 2 | #define _ROUTER_H_ 3 | #ifdef WIN32 4 | #define in_addr_t unsigned 5 | #endif 6 | 7 | struct route_item { 8 | in_addr_t prefix; 9 | in_addr_t submask; 10 | in_addr_t premask; 11 | in_addr_t nexthop; 12 | }; 13 | 14 | void route_restore(const char *route); 15 | 16 | int route_cmd(const char *route); 17 | const struct route_item *route_get(struct in_addr target); 18 | 19 | #endif 20 | 21 | -------------------------------------------------------------------------------- /dns-query.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | C=$(od -An --endian=big -d -N 2) 4 | C=${C//[[:blank:]]/} 5 | URL=https://blocked.cootail.com/dns-query 6 | # URL=https://one.one.one.one/dns-query 7 | # (dd bs=$C count=1 status=none) | curl -H "Content-Type: application/dns-message" -H "Content-Length: $C" --data-binary @- $URL > /tmp/dns.data 8 | 9 | rm /tmp/dns.data 10 | curl -H "Content-Type: application/dns-message" -H "Content-Length: $C" --data-binary @<(dd bs=$C count=1 status=none) $URL > /tmp/dns.data 11 | 12 | L=$(wc -c < /tmp/dns.data) 13 | printf "00: %02x %02x\n" $(($L/256)) $(($L%256)) | xxd -r 14 | 15 | cat /tmp/dns.data 16 | -------------------------------------------------------------------------------- /subnet_api.h: -------------------------------------------------------------------------------- 1 | #ifndef _SUBNET_H_ 2 | #define _SUBNET_H_ 3 | 4 | #include 5 | 6 | typedef struct subnet_s { 7 | uint8_t flags; 8 | uint8_t prefixlen; 9 | uint16_t zero; 10 | uint32_t keep; 11 | uint64_t network; 12 | } subnet_t; 13 | 14 | extern int _net4_count; 15 | extern subnet_t _net4_list[]; 16 | 17 | extern int _net6_count; 18 | extern subnet_t _net6_list[]; 19 | 20 | #ifdef __cplusplus 21 | extern "C" 22 | #endif 23 | 24 | subnet_t * lookupRoute4(uint64_t ip); 25 | subnet_t * lookupRoute6(uint64_t v6ip); 26 | 27 | uint64_t htonll(uint64_t val); 28 | uint64_t pton_val(const char *addr, int family); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /update_subnet.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test -f delegated-apnic-latest || wget -4 https://ftp.apnic.net/stats/apnic/delegated-apnic-latest 4 | 5 | grep "[A-Z][A-Z]|ipv4|" delegated-apnic-latest |grep -v CN|awk -F '|' '{print $4 "/" $5;}' > list-ipv4-without-cn.txt 6 | grep "[A-Z][A-Z]|ipv6|" delegated-apnic-latest |grep -v CN|awk -F '|' '{print $4 "/" $5;}' > list-ipv6-without-cn.txt 7 | 8 | ./subnet_gen -4 -i list-ipv4-without-cn.txt > subnet_data.c 9 | ./subnet_gen -6 -i list-ipv6-without-cn.txt >> subnet_data.c 10 | 11 | grep "[A-Z][A-Z]|ipv4|" delegated-apnic-latest |grep CN|awk -F '|' '{print $4 "/" $5;}' > list-ipv4-cn.txt 12 | grep "[A-Z][A-Z]|ipv6|" delegated-apnic-latest |grep CN|awk -F '|' '{print $4 "/" $5;}' > list-ipv6-cn.txt 13 | 14 | test -f ./subnet_gen && ./subnet_gen -4 -t -e list-ipv4-cn.txt > subnet_xdata.c 15 | test -f ./subnet_gen && ./subnet_gen -6 -t -e list-ipv6-cn.txt >> subnet_xdata.c 16 | -------------------------------------------------------------------------------- /subnet_api.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "subnet_api.h" 6 | 7 | subnet_t * lookupRoute6(uint64_t ip) 8 | { 9 | int low = 0, high = _net6_count -1; 10 | 11 | while (low <= high) { 12 | int mid = (low + high) / 2; 13 | int pref = _net6_list[mid].prefixlen; 14 | uint64_t msk1 = (~0ull >> pref); 15 | uint64_t net0 = (_net6_list[mid].network); 16 | 17 | if ((ip & ~msk1) == net0) 18 | return &_net6_list[mid]; 19 | 20 | if (net0 > ip) { 21 | high = mid - 1; 22 | } else if (net0 < ip) { 23 | low = mid + 1; 24 | } else { 25 | fprintf(stderr, "break, %lx %d\n", ip, _net6_count); 26 | break; 27 | } 28 | } 29 | 30 | return NULL; 31 | } 32 | 33 | subnet_t * lookupRoute4(uint64_t ip) 34 | { 35 | int low = 0, high = _net4_count -1; 36 | 37 | while (low <= high) { 38 | int mid = (low + high) / 2; 39 | int pref = _net4_list[mid].prefixlen; 40 | uint64_t msk1 = (~0ull >> pref); 41 | uint64_t net0 = (_net4_list[mid].network); 42 | 43 | if ((ip & ~msk1) == net0) 44 | return &_net4_list[mid]; 45 | 46 | if (net0 > ip) { 47 | high = mid - 1; 48 | } else if (net0 < ip) { 49 | low = mid + 1; 50 | } else { 51 | fprintf(stderr, "break, %llx %d\n", ip, _net4_count); 52 | break; 53 | } 54 | } 55 | 56 | return NULL; 57 | } 58 | 59 | uint64_t htonll(uint64_t val) 60 | { 61 | uint64_t data[2]; 62 | 63 | if (htons(0x1234) == 0x1234) { 64 | return val; 65 | } 66 | 67 | data[0] = htonl(val >> 32); 68 | data[1] = htonl(val & 0xffffffff); 69 | 70 | return (data[1] << 32) | data[0]; 71 | } 72 | 73 | uint64_t pton_val(const char *addr, int family) 74 | { 75 | uint64_t val[2] = {}; 76 | inet_pton(family, addr, val); 77 | return htonll(val[0]); 78 | } 79 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MODULE := dnsfix 2 | THIS_PATH := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) 3 | 4 | ifneq ($(TARGET),) 5 | CC := $(TARGET)-gcc 6 | LD := $(TARGET)-ld 7 | AR := $(TARGET)-ar 8 | CXX := $(TARGET)-g++ 9 | endif 10 | 11 | LOCAL_CXXFLAGS := -I$(THIS_PATH)/libtx/include -I$(THIS_PATH) -D_ENABLE_INET6_ 12 | LOCAL_CFLAGS := $(LOCAL_CXXFLAGS) 13 | LOCAL_LDLIBS := -lstdc++ 14 | 15 | ifeq ($(BUILD_TARGET), ) 16 | BUILD_TARGET:=$(shell uname) 17 | endif 18 | 19 | ifeq ($(BUILD_TARGET), mingw) 20 | LOCAL_LDFLAGS += -static 21 | LOCAL_LDLIBS += -lws2_32 22 | else 23 | LOCAL_LDLIBS += -lresolv 24 | endif 25 | 26 | ifeq ($(BUILD_TARGET), Linux) 27 | LOCAL_LDLIBS += -lrt -lresolv 28 | endif 29 | 30 | LOCAL_CFLAGS += -g -Wall -Wno-sign-compare -I. 31 | LOCAL_CXXFLAGS += -g -Wall -Wno-sign-compare -I. 32 | 33 | VPATH := $(THIS_PATH)/libtx:$(THIS_PATH) 34 | 35 | LOCAL_TARGETS = dns_lookup dnsfixd 36 | 37 | .PHONY: all 38 | all: $(LOCAL_TARGETS) stunc dns_res_trd dns_mod_trd dns_mod_gfw dns_echo dns_resolver_ng dns_mod_access udp_ixd 39 | LOCAL_TARGETS = 40 | 41 | CFLAGS := $(LOCAL_CFLAGS) 42 | CXXFLAGS := $(LOCAL_CXXFLAGS) 43 | 44 | LDLIBS := $(LOCAL_LDLIBS) 45 | LDFLAGS := $(LOCAL_LDFLAGS) 46 | OBJECTS := ncatutil.o dnsproto.o router.o subnet_data.o subnet_api.o 47 | 48 | dns_mod_trd: dns_mod_trd.o dnsproto.o subnet_api.o subnet_data.o 49 | $(CC) $(LDFLAGS) -o $@ $^ -lresolv 50 | 51 | udp_ixd: udp_ixd.o libtx.a 52 | 53 | dns_resolver_ng: dns_resolver_ng.o dnsproto.o subnet_api.o subnet_data.o tx_debug.o 54 | $(CC) $(LDFLAGS) -o $@ $^ -lresolv 55 | 56 | dns_mod_gfw: dns_mod_gfw.o subnet_api.o subnet_data.o dnsproto.o tx_debug.o 57 | $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) 58 | 59 | dns_mod_access: dns_mod_access.o subnet_api.o subnet_data.o dnsproto.o tx_debug.o 60 | $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) 61 | 62 | dns_echo: dns_echo.o tx_debug.o 63 | $(CC) $(LDFLAGS) -o $@ $^ -lresolv 64 | 65 | dns_res_trd: dns_res_trd.o dnsproto.o subnet_api.o subnet_data.o 66 | $(CC) $(LDFLAGS) -o $@ $^ -lresolv 67 | 68 | dns_lookup: dns_lookup.o dnsproto.o subnet_api.o subnet_data.o 69 | $(CC) $(LDFLAGS) -o $@ $^ -lresolv 70 | 71 | dnsfixd: dns_fixd.o dnsproto.o subnet_api.o subnet_data.o libtx.a 72 | $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) 73 | 74 | dnsfix.exe: dnsfix 75 | cp $< $@ 76 | 77 | dnsfix: OBJECTS := $(OBJECTS) 78 | dnsfix: $(OBJECTS) libtx.a 79 | $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) 80 | 81 | stunc: stunutil.o 82 | $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) 83 | 84 | subnet_gen: subnet_gen.o subnet_api.o 85 | $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) 86 | 87 | include $(THIS_PATH)/libtx/Makefile 88 | -------------------------------------------------------------------------------- /nslookup_wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 'A' + (cc - 'A' + 7) % 26 4 | export LC_CTYPE=C 5 | 6 | encrypt_once() { 7 | 8 | if [[ $1 =~ [^A-Za-z] ]] ; then 9 | echo -n $1; 10 | return; 11 | fi; 12 | 13 | alphaX=$(printf '%d' "'$1"); 14 | alphaA=$(printf '%d' "'a"); 15 | 16 | if [ $alphaA -gt $alphaX ]; then 17 | alphaA=$(printf '%d' "'A"); 18 | fi; 19 | 20 | ascii=$(($alphaA + ( $alphaX - $alphaA + 26 - 13) % 26)) 21 | echo -e -n $(printf "\\\\x%x" $ascii) 22 | } 23 | 24 | encrypt_domain() { 25 | DOMAIN=$1 26 | XDOMAIN="" 27 | 28 | n=0 29 | while [ $n -lt ${#DOMAIN} ]; do 30 | c=${DOMAIN:$n:1}; 31 | n=$(($n + 1)) 32 | XDOMAIN=$XDOMAIN$(encrypt_once $c) 33 | done; 34 | 35 | echo -n $XDOMAIN 36 | } 37 | 38 | load_func() { 39 | 40 | cat << EOF 41 | 42 | decrypt_once() { 43 | 44 | if [[ \$1 =~ [^A-Za-z] ]] ; then 45 | echo -n \$1; 46 | return; 47 | fi; 48 | 49 | alphaX=\$(printf '%d' "'\$1"); 50 | alphaA=\$(printf '%d' "'a"); 51 | 52 | if [ \$alphaA -gt \$alphaX ]; then 53 | alphaA=\$(printf '%d' "'A"); 54 | fi; 55 | 56 | ascii=\$((\$alphaA + ( \$alphaX - \$alphaA + 13) % 26)) 57 | echo -e -n \$(printf "\\\\\\\\x%x" \$ascii) 58 | } 59 | 60 | NEED_DECRYPT=0 61 | 62 | check_domain() { 63 | DOMAIN=\$1 64 | SUFFIX=.p.yrli.bid 65 | 66 | NEED_DECRYPT=0; 67 | if [[ \$DOMAIN =~ .*\\.p\\.yrli\\.bid ]]; then 68 | NEED_DECRYPT=1; 69 | else 70 | return; 71 | fi; 72 | } 73 | 74 | decrypt_domain() { 75 | DOMAIN=\$1 76 | XDOMAIN="" 77 | SUFFIX=.p.yrli.bid 78 | 79 | if ! [[ \$DOMAIN =~ .*\\.p\\.yrli\\.bid ]]; then 80 | echo -n \$DOMAIN; 81 | return; 82 | fi; 83 | 84 | n=0 85 | l=\${#DOMAIN} 86 | k=\${#SUFFIX} 87 | m=\$((\$l - \$k)) 88 | while [ \$n -lt \$m ]; do 89 | c=\${DOMAIN:\$n:1}; 90 | n=\$((\$n + 1)) 91 | XDOMAIN=\$XDOMAIN\$(decrypt_once \$c) 92 | done; 93 | 94 | echo -n \$XDOMAIN 95 | } 96 | 97 | decrypt_address() { 98 | SPLIT=""; 99 | ADDRESS=\$1 100 | 101 | if [ \$NEED_DECRYPT -eq 0 ]; then 102 | echo -n \$ADDRESS; 103 | return; 104 | fi; 105 | 106 | for num in \$(echo \$ADDRESS|sed "s/\./ /g"); do 107 | echo -n \$SPLIT\$((\$num ^ 0x5a)); 108 | SPLIT='.' 109 | done; 110 | } 111 | 112 | EOF 113 | } 114 | 115 | WRAPDOMAIN=$(encrypt_domain $1) 116 | shift 117 | echo $WRAPDOMAIN 118 | nslookup $WRAPDOMAIN.p.yrli.bid $@|( sed "/Server/{N; p; d;}; s/Name:\(.*\)/Name: \$(decrypt_domain \1)/; s/Address:\(.*\)/Address: \$(decrypt_address \1)/; ")|(load_func; sed "s/^/echo /;/decrypt_domain/{h; s/.*decrypt_domain\(.*\))/check_domain \1/p; g;}")|bash 119 | 120 | -------------------------------------------------------------------------------- /dnsfix_wrt/Makefile: -------------------------------------------------------------------------------- 1 | ############################################### 2 | #OpenWrt Makefile for toyclient program 3 | ############################################## 4 | 5 | include $(TOPDIR)/rules.mk 6 | 7 | # Name and release number of this package 8 | PKG_NAME := dnsfix 9 | PKG_RELEASE := 1 10 | 11 | 12 | # This specifies the directory where we're going to build the program. 13 | # The root build directory, $(BUILD_DIR), is by default the build_mipsel 14 | # directory in your OpenWrt SDK directory 15 | PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) 16 | include $(INCLUDE_DIR)/package.mk 17 | 18 | # Specify package information for this program. 19 | # The variables defined here should be self explanatory. 20 | # If you are running Kamikaze, delete the DESCRIPTION 21 | # variable below and uncomment the Kamikaze define 22 | # directive for the description below 23 | define Package/dnsfix 24 | TITLE := dnsfix -- do dns smart forward 25 | SECTION := utils 26 | CATEGORY := Utilities 27 | DEPENDS := +libstdcpp +librt 28 | endef 29 | 30 | # Uncomment portion below for Kamikaze and delete DESCRIPTION variable above 31 | define Package/dnsfix/description 32 | If you can't figure out what this program does, you're probably 33 | brain-dead and need immediate medical attention. 34 | endef 35 | 36 | # Specify what needs to be done to prepare for building the package. 37 | # In our case, we need to copy the source files to the build directory. 38 | # This is NOT the default. The default uses the PKG_SOURCE_URL and the 39 | # PKG_SOURCE which is not defined here to download the source from the web. 40 | # In order to just build a simple program that we have just written, it is 41 | # much easier to do it this way. 42 | define Build/Prepare 43 | mkdir -p $(PKG_BUILD_DIR) 44 | $(CP) ../* $(PKG_BUILD_DIR)/ 45 | endef 46 | 47 | # We do not need to define Build/Configure or Build/Compile directives 48 | # The defaults are appropriate for compiling a simple program such as this one 49 | # Specify where and how to install the program. Since we only have one file, 50 | # the toyclient executable, install it by copying it to the /bin directory on 51 | # the router. The $(1) variable represents the root directory on the router running 52 | # OpenWrt. The $(INSTALL_DIR) variable contains a command to prepare the install 53 | # directory if it does not already exist. Likewise $(INSTALL_BIN) contains the 54 | # command to copy the binary file from its current location (in our case the build 55 | # directory) to the install directory. 56 | define Package/dnsfix/install 57 | $(INSTALL_DIR) $(1)/bin 58 | $(INSTALL_BIN) $(PKG_BUILD_DIR)/stunc $(1)/bin/ 59 | $(INSTALL_BIN) $(PKG_BUILD_DIR)/dnsfix $(1)/bin/ 60 | endef 61 | 62 | # This line executes the necessary commands to compile our program. 63 | # The above define directives specify all the information needed, but this 64 | # line calls BuildPackage which in turn actually uses this information to build a package. 65 | $(eval $(call BuildPackage,dnsfix)) 66 | -------------------------------------------------------------------------------- /dnsproto.h: -------------------------------------------------------------------------------- 1 | #ifndef _DNSPROTO_H 2 | #define _DNSPROTO_H 3 | 4 | #define NSTYPE_A 1 5 | #define NSTYPE_NS 2 6 | #define NSTYPE_CNAME 5 7 | #define NSTYPE_SOA 6 8 | #define NSTYPE_PTR 12 9 | #define NSTYPE_MX 15 10 | #define NSTYPE_TXT 16 11 | #define NSTYPE_AAAA 28 12 | #define NSTYPE_SRV 33 13 | #define NSTYPE_DNAME 39 14 | #define NSTYPE_OPT 41 15 | #define NSTYPE_DS 43 16 | #define NSTYPE_RRSIG 46 17 | #define NSTYPE_NSEC 47 18 | #define NSTYPE_NSEC3 50 19 | #define NSTYPE_SVCB 64 20 | #define NSTYPE_HTTPS 65 21 | #define NSTYPE_ANY 0xffff 22 | 23 | #define NSSIG_SOA "ssuuuuu" 24 | #define NSSIG_MX "qs" 25 | #define NSSIG_CNAME "s" 26 | #define NSSIG_DNAME "s" 27 | #define NSSIG_NS "s" 28 | #define NSSIG_SRV "qqqs" 29 | #define NSSIG_PTR "s" 30 | #define NSSIG_A "A" 31 | #define NSSIG_TXT "B" 32 | #define NSSIG_AAAA "AAAA" 33 | #define NSSIG_NSEC "B" 34 | #define NSSIG_NSEC3 "B" 35 | #define NSSIG_RRSIG "B" 36 | #define NSSIG_OPT "B" 37 | #define NSSIG_DS "B" 38 | #define NSSIG_SVCB "B" 39 | #define NSSIG_HTTPS "B" 40 | 41 | #define MAX_RECORD_COUNT 64 42 | 43 | // #define DN_EXPANDED 0x8000 44 | 45 | #define NSFLAG_QR 0x8000 46 | #define NSFLAG_AA 0x0400 47 | #define NSFLAG_TC 0x0200 48 | #define NSFLAG_RD 0x0100 49 | #define NSFLAG_RA 0x0080 50 | #define NSFLAG_ZERO 0x0070 51 | #define NSFLAG_RCODE 0x000F 52 | 53 | #define RCODE_NXDOMAIN 3 54 | #define RCODE_SERVFAIL 2 55 | #define RCODE_REFUSED 5 56 | #define RCODE_NOTAUTH 9 57 | #define NSCLASS_INET 0x01 58 | 59 | struct dns_header { 60 | uint16_t ident; 61 | uint16_t flags; 62 | uint16_t question; 63 | uint16_t answer; 64 | uint16_t author; 65 | uint16_t addon; 66 | }; 67 | 68 | struct dns_question { 69 | uint16_t type; 70 | uint16_t klass; 71 | 72 | const char *domain; 73 | }; 74 | 75 | struct dns_resource { 76 | uint16_t type; 77 | uint16_t klass; 78 | uint32_t ttl; 79 | uint16_t len; 80 | 81 | short flags; 82 | const char *domain; 83 | uint8_t value[64]; 84 | }; 85 | 86 | struct dns_parser { 87 | int strcnt; 88 | char strtab[2048]; 89 | char *lastptr; 90 | const char *strptr[100]; 91 | uint8_t *comptr[MAX_RECORD_COUNT * 2]; 92 | 93 | struct dns_header head; 94 | struct dns_question question[4]; 95 | struct dns_resource *answer; 96 | struct dns_resource *author; 97 | struct dns_resource *addon; 98 | struct dns_resource records[MAX_RECORD_COUNT]; 99 | }; 100 | 101 | #define COUNTOF(arr) (sizeof(arr)/sizeof(arr[0])) 102 | 103 | #ifdef __cplusplus 104 | extern "C" { 105 | #endif 106 | 107 | int dns_build(struct dns_parser *parser, uint8_t *frame, size_t len); 108 | const char *add_domain(struct dns_parser *parser, const char *dn); 109 | const char *cache_get_name(const char *domain); 110 | int cache_put(struct dns_resource *res, size_t count); 111 | struct dns_parser * dns_parse(struct dns_parser *parser, const uint8_t *frame, size_t len); 112 | 113 | #ifdef __cplusplus 114 | } 115 | #endif 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /router.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #ifndef WIN32 7 | #include 8 | #include 9 | #include 10 | #else 11 | 12 | #include 13 | #include 14 | #endif 15 | 16 | typedef unsigned long u_long; 17 | 18 | #include 19 | 20 | #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) 21 | 22 | static int _route_size = 0; 23 | static struct route_item _route_table[2600]; 24 | static struct route_item *_route_get(in_addr_t prefix, in_addr_t _submask); 25 | 26 | static int binary_search(const struct route_item table[], int start, int end, in_addr_t khey) 27 | { 28 | int mid; 29 | 30 | while (start < end) { 31 | mid = start + (end - start) / 2; 32 | if (table[mid].prefix < khey) { 33 | start = mid + 1; 34 | } else if (table[mid].prefix > khey) { 35 | end = mid; 36 | } else { 37 | return mid; 38 | } 39 | } 40 | 41 | return start; 42 | } 43 | 44 | static int route_add(in_addr_t prefix, in_addr_t submask, in_addr_t nexthop) 45 | { 46 | int index; 47 | in_addr_t premask = 0; 48 | struct route_item *fib, *table = _route_table; 49 | 50 | assert(_route_size < ARRAY_SIZE(_route_table)); 51 | index = binary_search(_route_table, 0, _route_size, prefix); 52 | 53 | fib = _route_get(prefix, 0xffffffff); 54 | if (fib != NULL && fib->submask < submask) premask = fib->submask; 55 | 56 | fib = &table[index]; 57 | if (index < _route_size && fib->prefix == prefix) { 58 | while (index < _route_size) { 59 | fib = &table[index]; 60 | if (fib->prefix != prefix) { 61 | break; 62 | } else if (fib->submask == submask) { 63 | return 0; 64 | } else if (fib->submask < submask) { 65 | premask = fib->submask; 66 | index++; 67 | } else { 68 | break; 69 | } 70 | } 71 | 72 | while (index > 0) { 73 | fib = &table[index -1]; 74 | if (fib->prefix != prefix) { 75 | break; 76 | } else if (fib->submask == submask) { 77 | return 0; 78 | } else if (fib->submask > submask) { 79 | index--; 80 | } else { 81 | break; 82 | } 83 | } 84 | } 85 | 86 | memmove(&table[index + 1], &table[index], (_route_size - index) * sizeof(table[0])); 87 | table[index].submask = submask; 88 | table[index].nexthop = nexthop; 89 | table[index].premask = premask; 90 | table[index].prefix = prefix; 91 | _route_size++; 92 | 93 | for (index++; index < _route_size; index++) { 94 | fib = &table[index]; 95 | if (fib->submask < submask) { 96 | break; 97 | } else if ((fib->prefix & submask) != prefix) { 98 | break; 99 | } else if (fib->premask < submask) { 100 | fib->premask = submask; 101 | } 102 | } 103 | 104 | return 0; 105 | } 106 | 107 | void route_restore(const char *route) 108 | { 109 | int length = 0; 110 | char sprefix[16] = {}; 111 | const char *ptr = NULL; 112 | 113 | for (ptr = route - 1; ptr; ptr = strchr(ptr +1, ' ')) { 114 | if (2 == sscanf(ptr +1, "%[0-9.]/%d", sprefix, &length)) { 115 | in_addr_t prefix = inet_addr(sprefix); 116 | route_add(htonl(prefix), ~0 << (32 - length), INADDR_LOOPBACK); 117 | } 118 | } 119 | 120 | return; 121 | } 122 | 123 | static struct route_item *_route_get(in_addr_t prefix, in_addr_t _submask) 124 | { 125 | int index; 126 | in_addr_t submask; 127 | struct route_item *fib, *table = _route_table; 128 | 129 | index = binary_search(table, 0, _route_size, prefix & _submask); 130 | if (index < _route_size && 131 | (table[index].prefix == (prefix & table[index].submask))) { 132 | while (index + 1 < _route_size) { 133 | if (table[index +1].prefix 134 | != (prefix & table[index +1].submask)) { 135 | break; 136 | } 137 | index++; 138 | } 139 | return &_route_table[index]; 140 | } 141 | 142 | if (index-- == 0) { 143 | return NULL; 144 | } 145 | 146 | fib = &table[index]; 147 | submask = fib->submask; 148 | if ((submask & prefix) == fib->prefix) { 149 | return fib; 150 | } else if (fib->premask) { 151 | assert(fib->premask < _submask); 152 | return _route_get(prefix, fib->prefix); 153 | } 154 | 155 | return NULL; 156 | } 157 | 158 | const struct route_item *route_get(struct in_addr target) 159 | { 160 | #if 0 161 | printf("\n-------------------------\n"); 162 | for (int i = 0; i < _route_size; i++) { 163 | struct route_item *item = &_route_table[i]; 164 | printf("%d %08x %08x %08x %08x\n", i, item->prefix, item->submask, item->premask, item->nexthop); 165 | } 166 | printf("-------------------------\n"); 167 | #endif 168 | 169 | return _route_get(htonl(target.s_addr), ~0); 170 | } 171 | 172 | int route_cmd(const char *route) 173 | { 174 | int length = 0; 175 | char sprefix[16] = {}; 176 | char sgateway[16] = {}; 177 | 178 | if (3 == sscanf(route, "%16[0-9.]/%d@%16s", sprefix, &length, sgateway)) { 179 | in_addr_t prefix = inet_addr(sprefix); 180 | in_addr_t gateway = inet_addr(sgateway); 181 | route_add(htonl(prefix), ~0 << (32 - length), htonl(gateway)); 182 | return 0; 183 | } 184 | 185 | return -1; 186 | } 187 | 188 | static int _log_fake_init = 0; 189 | static const char *BLOCK_ROUTE = NULL; 190 | 191 | void log_fake_route(uint8_t *fakeip) 192 | { 193 | struct in_addr ip; 194 | u_long dest; 195 | 196 | if (_log_fake_init == 0) { 197 | BLOCK_ROUTE = getenv("BLOCK_ROUTE"); 198 | _log_fake_init = 1; 199 | } 200 | 201 | if (BLOCK_ROUTE == NULL) { 202 | return; 203 | } 204 | 205 | memcpy(&ip.s_addr, fakeip, sizeof(ip.s_addr)); 206 | dest = htonl(ip.s_addr); 207 | 208 | if (route_get(ip) != NULL) { 209 | return; 210 | } 211 | 212 | in_addr_t prefix = (dest & ~0xff); 213 | route_add(prefix, 0xffffff00, 0x08080808); 214 | 215 | FILE *froute = fopen(BLOCK_ROUTE, "a"); 216 | if (froute != NULL) { 217 | fprintf(froute, "%s/24\n", inet_ntoa(ip)); 218 | fclose(froute); 219 | } 220 | 221 | return; 222 | } 223 | 224 | -------------------------------------------------------------------------------- /dns_echo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include "tx_debug.h" 18 | 19 | #ifdef WIN32 20 | #include 21 | #else 22 | #include 23 | #include 24 | #include 25 | #include 26 | #define closesocket close 27 | #endif 28 | 29 | struct dns_context { 30 | int outfd; 31 | int sockfd; 32 | 33 | socklen_t dnslen; 34 | struct sockaddr *dnsaddr; 35 | 36 | socklen_t addrlen; 37 | struct sockaddr *last; 38 | struct sockaddr_in6 last6[0xffff + 1]; 39 | }; 40 | 41 | #define NSFLAG_QR 0x8000 42 | #define NSFLAG_AA 0x0400 43 | #define NSFLAG_TC 0x0200 44 | #define NSFLAG_RD 0x0100 45 | #define NSFLAG_RA 0x0080 46 | #define NSFLAG_ZERO 0x0070 47 | #define NSFLAG_RCODE 0x000F 48 | 49 | #define RCODE_NXDOMAIN 3 50 | #define RCODE_SERVFAIL 2 51 | #define RCODE_REFUSED 5 52 | #define NSCLASS_INET 0x01 53 | 54 | struct dns_header { 55 | uint16_t ident; 56 | uint16_t flags; 57 | uint16_t question; 58 | uint16_t answer; 59 | uint16_t author; 60 | uint16_t addon; 61 | }; 62 | 63 | int do_dns_forward(struct dns_context *ctx, void *buf, int count, struct sockaddr_in6 *from) 64 | { 65 | uint16_t ident; 66 | struct dns_header *h = (struct dns_header *)buf; 67 | h->flags |= htons(NSFLAG_RD); 68 | 69 | memcpy(&ident, buf, sizeof(ident)); 70 | memcpy(&ctx->last6[ident], from, sizeof(*from)); 71 | 72 | ctx->addrlen = sizeof(ctx->last6[0]); 73 | ctx->last = (struct sockaddr *)&ctx->last6[ident]; 74 | 75 | int len; 76 | char tmp[216]; 77 | 78 | len = sendto(ctx->outfd, buf, count, 0, ctx->dnsaddr, ctx->dnslen); 79 | 80 | LOG_DEBUG("%04x forward: [%s]:%d %d %d", ident, inet_ntop(AF_INET6, &from->sin6_addr, tmp, 216), 81 | htons(from->sin6_port), count, len); 82 | 83 | struct sockaddr_in6 *to = (struct sockaddr_in6 *)ctx->dnsaddr; 84 | LOG_DEBUG("to: [%s]:%d %d %d", inet_ntop(AF_INET6, &to->sin6_addr, tmp, 216), 85 | htons(to->sin6_port), count, len); 86 | return 0; 87 | } 88 | 89 | int do_dns_backward(struct dns_context *ctx, void *buf, int count, struct sockaddr_in6 *from) 90 | { 91 | char tmp[216]; 92 | uint16_t ident; 93 | memcpy(&ident, buf, sizeof(ident)); 94 | 95 | #define ADDR(s) (s->sin6_family == AF_INET6? &s->sin6_addr: (struct in6_addr *)&((struct sockaddr_in *)s)->sin_addr) 96 | 97 | LOG_DEBUG("%04x backward: af=%d/%d [%s]:%d %d", ident, from->sin6_family, AF_INET6, 98 | inet_ntop(from->sin6_family, ADDR(from), tmp, 216), htons(from->sin6_port), count); 99 | 100 | ctx->last = (struct sockaddr *)&ctx->last6[ident]; 101 | LOG_DEBUG("send: [%s]:%d %d", inet_ntop(AF_INET6, &ctx->last6[ident].sin6_addr, tmp, 216), 102 | htons(ctx->last6[ident].sin6_port), count); 103 | 104 | sendto(ctx->sockfd, buf, count, 0, ctx->last, ctx->addrlen); 105 | return 0; 106 | } 107 | 108 | int main(int argc, char *argv[]) 109 | { 110 | int retval; 111 | int outfd, sockfd; 112 | struct sockaddr_in6 myaddr; 113 | struct sockaddr * paddr = (struct sockaddr *)&myaddr; 114 | 115 | struct sockaddr_in6 myaddr6; 116 | struct sockaddr * paddr6 = (struct sockaddr *)&myaddr6; 117 | 118 | setenv("INCOMING", "::ffff:0.0.0.0", 0); 119 | setenv("OUTGOING", "::ffff:8.8.8.8", 0); 120 | 121 | outfd = socket(AF_INET6, SOCK_DGRAM, 0); 122 | assert(outfd != -1); 123 | 124 | myaddr.sin6_family = AF_INET6; 125 | myaddr.sin6_port = 0; 126 | myaddr.sin6_addr = in6addr_any; 127 | retval = bind(outfd, paddr, sizeof(myaddr)); 128 | assert(retval != -1); 129 | 130 | sockfd = socket(AF_INET6, SOCK_DGRAM, 0); 131 | assert(sockfd != -1); 132 | 133 | myaddr6.sin6_family = AF_INET6; 134 | myaddr6.sin6_port = htons(53); 135 | myaddr6.sin6_addr = in6addr_any; 136 | inet_pton(AF_INET6, getenv("INCOMING"), &myaddr6.sin6_addr); 137 | if (getenv("INCOMING_PORT")) { 138 | int port = atoi(getenv("INCOMING_PORT")); 139 | if (port) myaddr6.sin6_port = htons(port); 140 | } 141 | 142 | retval = bind(sockfd, paddr6, sizeof(myaddr6)); 143 | assert(retval != -1); 144 | 145 | int count; 146 | char buf[2048]; 147 | fd_set readfds = {}; 148 | socklen_t addrl = 0; 149 | struct sockaddr_in6 dnsaddr; 150 | 151 | struct dns_context c0 = { 152 | .outfd = outfd, 153 | .sockfd = sockfd, 154 | .dnslen = sizeof(dnsaddr), 155 | }; 156 | 157 | dnsaddr.sin6_family = AF_INET6; 158 | dnsaddr.sin6_port = htons(53); 159 | inet_pton(AF_INET6, getenv("OUTGOING"), &dnsaddr.sin6_addr); 160 | if (getenv("OUTGOING_PORT")) { 161 | int port = atoi(getenv("OUTGOING_PORT")); 162 | if (port) myaddr6.sin6_port = htons(port); 163 | } 164 | 165 | 166 | c0.dnsaddr = (struct sockaddr *)&dnsaddr; 167 | LOG_DEBUG("nsaddr %p pointer %p %d", c0.dnsaddr, &dnsaddr, htons(dnsaddr.sin6_port)); 168 | 169 | const struct sockaddr_in6 *inp = (const struct sockaddr_in6 *)&dnsaddr; 170 | LOG_DEBUG("dns_build bytes %d %d %d %s", 0, inp->sin6_family, htons(inp->sin6_port), getenv("NAMESERVER")); 171 | 172 | do { 173 | FD_ZERO(&readfds); 174 | FD_SET(outfd, &readfds); 175 | FD_SET(sockfd, &readfds); 176 | 177 | retval = select(sockfd + 1, &readfds, 0, 0, 0); 178 | if (retval == -1) { 179 | LOG_DEBUG("select failure: %s", strerror(errno)); 180 | break; 181 | } 182 | 183 | if (FD_ISSET(outfd, &readfds)) { 184 | // LOG_DEBUG("outfd is readable"); 185 | addrl = sizeof(myaddr); 186 | count = recvfrom(outfd, buf, sizeof(buf), 0, paddr, &addrl); 187 | assert(count > 0); 188 | do_dns_backward(&c0, buf, count, &myaddr); 189 | } 190 | 191 | if (FD_ISSET(sockfd, &readfds)) { 192 | // LOG_DEBUG("sockfd is readable"); 193 | addrl = sizeof(myaddr6); 194 | count = recvfrom(sockfd, buf, sizeof(buf), 0, paddr6, &addrl); 195 | assert(count > 0); 196 | do_dns_forward(&c0, buf, count, &myaddr6); 197 | } 198 | 199 | } while (retval >= 0); 200 | 201 | close(sockfd); 202 | close(outfd); 203 | 204 | return 0; 205 | } 206 | -------------------------------------------------------------------------------- /dns_mod_access.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include "tx_debug.h" 20 | #include "dnsproto.h" 21 | #include "subnet_api.h" 22 | 23 | #ifdef WIN32 24 | #include 25 | #else 26 | #include 27 | #include 28 | #include 29 | #include 30 | #define closesocket close 31 | #endif 32 | 33 | static char addrbuf[256]; 34 | #define ntop6(addr) inet_ntop(AF_INET6, &addr, addrbuf, sizeof(addrbuf)) 35 | #define ntop6p(addr) inet_ntop(AF_INET6, addr, addrbuf, sizeof(addrbuf)) 36 | 37 | #define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array)) 38 | 39 | struct dns_context { 40 | int sockfd; 41 | }; 42 | 43 | 44 | struct subnet_info { 45 | uint16_t tag; // 0x0008 46 | uint16_t len; 47 | uint16_t family; 48 | uint8_t source_netmask; 49 | uint8_t scope_netmask; 50 | uint8_t addr[16]; 51 | }; 52 | 53 | #define NS_IPV6 2 54 | #define NS_IPV4 1 55 | 56 | 57 | static int load_client_subnet(struct dns_parser *p0, struct sockaddr_in6 *from) 58 | { 59 | #ifndef DISABLE_SUBNET 60 | 61 | struct dns_resource *res = NULL; 62 | struct subnet_info *info = NULL; 63 | 64 | for (int i = 0; i < p0->head.addon; i++) { 65 | res = &p0->addon[i]; 66 | if (res->type != NSTYPE_OPT) { 67 | continue; 68 | } 69 | 70 | if (res->domain == NULL || *res->domain == 0) { 71 | size_t len = res->len; 72 | const uint8_t * valp = *(const uint8_t **)res->value; 73 | struct tagheader {uint16_t tag; uint16_t len; } tag0; 74 | 75 | while (len > sizeof(tag0)) { 76 | memcpy(&tag0, valp, sizeof(tag0)); 77 | if (len < sizeof(tag0) + htons(tag0.len)) break; 78 | const uint8_t *hold = valp; 79 | valp += sizeof(tag0) + htons(tag0.len); 80 | len -= (sizeof(tag0) + htons(tag0.len)); 81 | if (tag0.tag == htons(0x0008)) { 82 | info = (struct subnet_info *)hold; 83 | break; 84 | } 85 | } 86 | } 87 | } 88 | 89 | char cmd[1024]; 90 | if (info != NULL) { 91 | char buf[256], bytes[16] = {}; 92 | int prefixlen = info->source_netmask;//+ info->scope_netmask; 93 | size_t subnet_len = 8 + ((7 + prefixlen) >> 3); 94 | int family = htons(info->family); 95 | if (family == NS_IPV4) { 96 | family = AF_INET; 97 | if (prefixlen == 32) prefixlen = 24; 98 | } 99 | else if (family == NS_IPV6) { 100 | family = AF_INET6; 101 | if (prefixlen > 64) prefixlen = 48; 102 | } 103 | memcpy(bytes, info->addr, prefixlen >> 3); 104 | LOG_DEBUG("subnet family: %d sunet %s/%d", family, inet_ntop(family, bytes, buf, sizeof(buf)), prefixlen); 105 | if (family == AF_INET6) { 106 | sprintf(cmd, "ipset add bypass6 %s/%d", buf, prefixlen); 107 | system(cmd); 108 | } else { 109 | sprintf(cmd, "ipset add bypass %s/%d", buf, prefixlen); 110 | system(cmd); 111 | } 112 | } else { 113 | char buf[256], bytes[16] = {}; 114 | if (IN6_IS_ADDR_V4MAPPED(&from->sin6_addr)) { 115 | memcpy(bytes, &from->sin6_addr, sizeof(from->sin6_addr)); 116 | bytes[15] = 0; 117 | inet_ntop(AF_INET, bytes + 12, buf, sizeof(buf)); 118 | sprintf(cmd, "ipset add bypass %s/%d", buf, 24); 119 | system(cmd); 120 | } 121 | } 122 | #endif 123 | 124 | return 0; 125 | } 126 | 127 | static int dns_sendto(int outfd, struct dns_parser *parser, const struct sockaddr_in6 *inp, size_t tolen) 128 | { 129 | ssize_t len; 130 | uint8_t _hold[2048]; 131 | const struct sockaddr *to = (const struct sockaddr *)inp; 132 | 133 | len = dns_build(parser, _hold, sizeof(_hold)); 134 | assert(len > 0); 135 | 136 | if (len != -1) 137 | len = sendto(outfd, _hold, len, 0, to, tolen); 138 | 139 | LOG_DEBUG("dns_sendto %d af=%d %d %s %s", len, 140 | inp->sin6_family, htons(inp->sin6_port), ntop6(inp->sin6_addr), parser->question[0].domain); 141 | 142 | return len; 143 | } 144 | 145 | static int translate_domain(struct dns_parser *pp, char **store, const char *domain) 146 | { 147 | char buf[1024]; 148 | char *accessp = strcasestr(domain, ".access."); 149 | if (accessp == NULL) { 150 | return -1; 151 | } 152 | 153 | int first_len = accessp - domain; 154 | memcpy(buf, domain, first_len); 155 | strcpy(buf + first_len, domain + first_len + 7); 156 | *store = add_domain(pp, buf); 157 | 158 | return 0; 159 | } 160 | 161 | int do_dns_forward(struct dns_context *ctx, void *buf, int count, struct sockaddr_in6 *from) 162 | { 163 | struct dns_parser p0; 164 | struct dns_parser *pp; 165 | 166 | pp = dns_parse(&p0, buf, count); 167 | if (pp == NULL) { 168 | LOG_DEBUG("do_dns_forward parse failure"); 169 | return 0; 170 | } 171 | 172 | if (p0.head.flags & 0x8000) { 173 | LOG_DEBUG("FROM: %s this is not query", "nothing"); 174 | return -1; 175 | } 176 | 177 | if (p0.head.question == 0) { 178 | p0.head.flags |= RCODE_REFUSED; 179 | p0.head.flags |= NSFLAG_QR; 180 | dns_sendto(ctx->sockfd, &p0, from, sizeof(*from)); 181 | return 0; 182 | } 183 | 184 | load_client_subnet(&p0, from); 185 | 186 | p0.head.flags |= NSFLAG_QR; 187 | if (~p0.head.flags & NSFLAG_RD) 188 | p0.head.flags |= NSFLAG_AA; 189 | 190 | p0.answer[0].domain = p0.question[0].domain; 191 | p0.answer[0].type = NSTYPE_CNAME; 192 | p0.answer[0].ttl = 600; 193 | p0.answer[0].klass = NSCLASS_INET; 194 | if (translate_domain(&p0, (char **)p0.answer[0].value, p0.answer[0].domain) == 0) { 195 | p0.head.addon = 0; 196 | p0.head.answer++; 197 | } 198 | 199 | dns_sendto(ctx->sockfd, &p0, from, sizeof(*from)); 200 | 201 | return 0; 202 | } 203 | 204 | int main(int argc, char *argv[]) 205 | { 206 | int retval; 207 | int sockfd; 208 | 209 | struct sockaddr_in6 myaddr6; 210 | struct sockaddr * paddr1 = (struct sockaddr *)&myaddr6; 211 | 212 | setenv("LOCALADDR6", "::ffff:127.9.9.9", 0); 213 | 214 | sockfd = socket(AF_INET6, SOCK_DGRAM, 0); 215 | assert(sockfd != -1); 216 | 217 | myaddr6.sin6_family = AF_INET6; 218 | myaddr6.sin6_port = htons(53); 219 | // myaddr6.sin6_addr.s_addr = INADDR_ANY; 220 | inet_pton(AF_INET6, getenv("LOCALADDR6"), &myaddr6.sin6_addr); 221 | retval = bind(sockfd, paddr1, sizeof(myaddr6)); 222 | assert(retval != -1); 223 | 224 | int count; 225 | char buf[2048]; 226 | fd_set readfds = {}; 227 | socklen_t addrl = 0; 228 | 229 | struct dns_context c0 = { 230 | .sockfd = sockfd, 231 | }; 232 | 233 | do { 234 | FD_ZERO(&readfds); 235 | FD_SET(sockfd, &readfds); 236 | 237 | retval = select(sockfd + 2, &readfds, 0, 0, 0); 238 | if (retval == -1) { 239 | LOG_DEBUG("select failure: %s", strerror(errno)); 240 | break; 241 | } 242 | 243 | if (FD_ISSET(sockfd, &readfds)) { 244 | addrl = sizeof(myaddr6); 245 | count = recvfrom(sockfd, buf, sizeof(buf), 0, paddr1, &addrl); 246 | count > 0 || LOG_DEBUG("sockfd is readable: %d", count); 247 | assert(count > 0); 248 | do_dns_forward(&c0, buf, count, &myaddr6); 249 | } 250 | 251 | } while (retval >= 0); 252 | 253 | close(sockfd); 254 | 255 | return 0; 256 | } 257 | -------------------------------------------------------------------------------- /subnet_gen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "subnet_api.h" 10 | 11 | int _net_count = 0; 12 | subnet_t _net_list[100000]; 13 | static int _family = AF_INET; 14 | #define MAX_NET_SIZE (sizeof(_net_list)/sizeof(_net_list[0])) 15 | 16 | extern int __attribute__ ((alias ("_net_count"))) _net4_count; 17 | extern subnet_t __attribute__ ((alias ("_net_list"))) _net4_list[25600]; 18 | 19 | extern int __attribute__ ((alias ("_net_count"))) _net6_count; 20 | extern subnet_t __attribute__ ((alias ("_net_list"))) _net6_list[25600]; 21 | 22 | int includeNetwork(uint64_t network, uint8_t prefix) 23 | { 24 | int i, j = 0; 25 | /* super, exclude, subset, merge */ 26 | uint64_t mask = (~0ull >> prefix); 27 | 28 | /* step1: subset check */ 29 | for (i = 0; i < _net_count; i++) { 30 | subnet_t snet = _net_list[i]; 31 | uint64_t smask = (~0ull >> snet.prefixlen); 32 | 33 | if (snet.prefixlen <= prefix && 34 | snet.network == (~smask & network)) { 35 | return 0; 36 | } 37 | } 38 | 39 | /* step2: merge */ 40 | uint64_t test, ksam, scale = 0; 41 | subnet_t nnet = {.network = network & ~mask, .prefixlen = prefix}; 42 | 43 | for (i = 0; i < _net_count; i++) { 44 | subnet_t snet = _net_list[i]; 45 | 46 | if (snet.prefixlen > nnet.prefixlen) 47 | continue; 48 | 49 | ksam = (~0ull >> snet.prefixlen); 50 | test = snet.network ^ (nnet.network & ~ksam); 51 | 52 | if (test == ksam + 1) 53 | scale |= test; 54 | } 55 | 56 | scale >>= (64 - nnet.prefixlen); 57 | while (nnet.prefixlen > 0 && (scale & 1)) { 58 | nnet.prefixlen--; 59 | scale >>= 1; 60 | } 61 | 62 | mask = (~0ull >> nnet.prefixlen); 63 | nnet.network &= ~mask; 64 | 65 | /* step3: super check */ 66 | for (i = 0; i < _net_count; i++) { 67 | subnet_t snet = _net_list[i]; 68 | 69 | assert(j < MAX_NET_SIZE); 70 | if ((snet.network & ~mask) != nnet.network) 71 | _net_list[j++] = _net_list[i]; 72 | else if (snet.prefixlen < nnet.prefixlen) 73 | assert(0); 74 | } 75 | 76 | assert(j < MAX_NET_SIZE); 77 | _net_list[j++] = nnet; 78 | _net_count = j; 79 | 80 | return 0; 81 | } 82 | 83 | int excludeNetwork(uint64_t network, uint8_t prefixlen) 84 | { 85 | int i, j = 0; 86 | uint64_t net1, msk1, newnet; 87 | uint64_t net0, msk0 = (~0ull >> prefixlen); 88 | 89 | for (i = 0; i < _net_count; i++) { 90 | int pref = _net_list[i].prefixlen; 91 | 92 | if (pref <= prefixlen) { 93 | msk1 = (~0ull >> pref); 94 | net0 = (network & ~msk1); 95 | 96 | if (_net_list[i].network == net0) { 97 | assert(i == j); 98 | 99 | while (++i < _net_count) { 100 | assert(j < MAX_NET_SIZE); 101 | _net_list[j++] = _net_list[i]; 102 | } 103 | _net_count = j; 104 | 105 | for (int k = pref + 1; k <= prefixlen; k++) { 106 | msk1 = (~0ull >> k); 107 | newnet = network & ~msk1; 108 | includeNetwork(newnet ^ (msk1 + 1), (int)k); 109 | } 110 | 111 | return 0; 112 | } 113 | 114 | assert(j < MAX_NET_SIZE); 115 | _net_list[j++] = _net_list[i]; 116 | continue; 117 | } 118 | 119 | net1 = (_net_list[i].network & ~msk0); 120 | if (net1 == network) { 121 | continue; 122 | } 123 | 124 | assert(j < MAX_NET_SIZE); 125 | _net_list[j++] = _net_list[i]; 126 | } 127 | 128 | _net_count = j; 129 | return 0; 130 | } 131 | 132 | static inline int mylog2(int prefixlen) 133 | { 134 | int log = 0; 135 | 136 | if (prefixlen & 0xffff0000) { 137 | prefixlen >>= 16; 138 | log += 16; 139 | } 140 | 141 | if (prefixlen & 0xff00) { 142 | prefixlen >>= 8; 143 | log += 8; 144 | } 145 | 146 | if (prefixlen & 0xf0) { 147 | prefixlen >>= 4; 148 | log += 4; 149 | } 150 | 151 | if (prefixlen & 0xc) { 152 | prefixlen >>= 2; 153 | log += 2; 154 | } 155 | 156 | if (prefixlen & 0x2) { 157 | prefixlen >>= 1; 158 | log += 1; 159 | } 160 | 161 | return log; 162 | } 163 | 164 | #define COUNTOF(list) (sizeof(list)/sizeof(*list)) 165 | void initRoute6(const char *tag) 166 | { 167 | char * _include[] = {"2000::/3"}; 168 | char * _internal[] = {""}; 169 | 170 | char *_exclude[] = {"2002::/16"}; 171 | /* prebuilt: NONE INTERNAL EXTERNAL */ 172 | 173 | int prefixlen; 174 | uint64_t network; 175 | char sNetwork[128]; 176 | 177 | _net_count = 0; 178 | for (int i = 0; i < COUNTOF(_include); i++) { 179 | int nmatch = sscanf(_include[i], "%128[0-9.:]/%d%*s", sNetwork, &prefixlen); 180 | 181 | if (nmatch != 2) { 182 | fprintf(stderr, "match: %d %s\n", nmatch, _include[i]); 183 | continue; 184 | } 185 | 186 | network = pton_val(sNetwork, _family); 187 | includeNetwork(network, prefixlen); 188 | } 189 | 190 | for (int i = 0; i < COUNTOF(_exclude); i++) { 191 | int nmatch = sscanf(_exclude[i], "%128[0-9.:]/%d%*s", sNetwork, &prefixlen); 192 | 193 | if (nmatch != 2) { 194 | continue; 195 | } 196 | 197 | network = pton_val(sNetwork, _family); 198 | excludeNetwork(network, prefixlen); 199 | } 200 | 201 | return; 202 | } 203 | 204 | void initRoute(const char *tag) 205 | { 206 | char * _include[] = {"0.0.0.0/1", "128.0.0.0/2", "192.0.0.0/3"}; 207 | char * _internal[] = {"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"}; 208 | 209 | char *_exclude[] = {"0.0.0.0/8", "10.0.0.0/8", "127.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "169.254.0.0/16", "100.64.0.0/10", "192.0.0.0/24", "192.0.2.0/24", "192.88.99.0/24", "198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24"}; 210 | /* prebuilt: NONE INTERNAL EXTERNAL */ 211 | 212 | int prefixlen; 213 | uint64_t network; 214 | char sNetwork[128]; 215 | 216 | _net_count = 0; 217 | for (int i = 0; i < COUNTOF(_include); i++) { 218 | int nmatch = sscanf(_include[i], "%128[0-9a-f.:]/%d%*s", sNetwork, &prefixlen); 219 | 220 | if (nmatch != 2) { 221 | fprintf(stderr, "match: %d %s\n", nmatch, _include[i]); 222 | continue; 223 | } 224 | 225 | network = pton_val(sNetwork, _family); 226 | includeNetwork(network, prefixlen); 227 | } 228 | 229 | for (int i = 0; i < COUNTOF(_exclude); i++) { 230 | int nmatch = sscanf(_exclude[i], "%128[0-9a-f.:]/%d%*s", sNetwork, &prefixlen); 231 | 232 | if (nmatch != 2) { 233 | continue; 234 | } 235 | 236 | network = pton_val(sNetwork, _family); 237 | excludeNetwork(network, prefixlen); 238 | } 239 | 240 | return; 241 | } 242 | 243 | void loadRoute(const char *path, int (*callback)(uint64_t , uint8_t)) 244 | { 245 | 246 | int prefixlen; 247 | uint64_t network; 248 | char sNetwork[128]; 249 | 250 | FILE *fp = fopen(path, "r"); 251 | if (fp == NULL) { 252 | return; 253 | } 254 | 255 | char line[1024] = ""; 256 | while (fgets(line, sizeof(line) -1, fp) != NULL) { 257 | int nmatch = sscanf(line, "%123[^/]/%d", sNetwork, &prefixlen); 258 | 259 | if (prefixlen > 48) { 260 | prefixlen = 32 - mylog2(prefixlen); 261 | } 262 | 263 | if (nmatch == 2) { 264 | network = pton_val(sNetwork, _family); 265 | (*callback)(network, prefixlen); 266 | } else { 267 | // fscanf(fp, "%s", sNetwork); 268 | fprintf(stderr, "break nmatch %d %s\n", nmatch, sNetwork); 269 | break; 270 | } 271 | } 272 | fclose(fp); 273 | 274 | return; 275 | } 276 | 277 | int subnet_compare(const void *a, const void *b) 278 | { 279 | subnet_t *pa, *pb; 280 | pa = (subnet_t *)a, pb = (subnet_t *)b; 281 | return (pa->network > pb->network) - (pa->network < pb->network); 282 | } 283 | 284 | static void dumpRoute(void) 285 | { 286 | qsort(_net_list, _net_count, sizeof(_net_list[0]), subnet_compare); 287 | 288 | fprintf(stdout, "#include \"subnet_api.h\"\n"); 289 | fprintf(stdout, "\n"); 290 | 291 | if (_family == AF_INET) { 292 | fprintf(stdout, "int _net4_count = %d;\n", _net_count); 293 | fprintf(stdout, "\n"); 294 | fprintf(stdout, "subnet_t _net4_list[%d] = {", _net_count); 295 | } else { 296 | fprintf(stdout, "int _net6_count = %d;\n", _net_count); 297 | fprintf(stdout, "\n"); 298 | fprintf(stdout, "subnet_t _net6_list[%d] = {", _net_count); 299 | } 300 | 301 | for (int i = 0; i < _net_count; i++) { 302 | int slim = (i & 0x03); 303 | fprintf(stdout, "%s", slim? " ": "\n "); 304 | fprintf(stdout, "{0, %2d, 0, 0, 0x%lxull}%s", _net_list[i].prefixlen, 305 | _net_list[i].network, (i + 1 == _net_count? "\n": ",")); 306 | } 307 | fprintf(stdout, "};\n"); 308 | 309 | return; 310 | } 311 | 312 | int queryRoute(uint64_t ipv4, char *sTarget) 313 | { 314 | char sNetwork[128]; 315 | 316 | uint64_t target = (ipv4); 317 | subnet_t *subnet = lookupRoute6(target); 318 | 319 | uint64_t last_network = 0; 320 | for (int i = 0; i < _net_count; i++) { 321 | // fprintf(stderr, "%16llx/%d\n", _net_list[i].network, _net_list[i].prefixlen); 322 | assert(last_network < _net_list[i].network || last_network == 0); 323 | last_network = _net_list[i].network; 324 | } 325 | 326 | if (subnet != 0) { 327 | uint64_t network[2] = {htonll(subnet->network), 0}; 328 | inet_ntop(_family, network, sNetwork, sizeof(sNetwork)); 329 | fprintf(stderr, "ACTIVE network: %s/%d by %s\n", sNetwork, subnet->prefixlen, sTarget); 330 | } 331 | 332 | fprintf(stderr, "count %d\n", _net_count); 333 | return 0; 334 | } 335 | 336 | int main(int argc, char *argv[]) 337 | { 338 | int i, skip = 0, query = 0; 339 | 340 | for (i = 1; i < argc; i++) { 341 | if (skip-- > 0) 342 | continue; 343 | 344 | if (strcmp(argv[i], "-h") == 0) { 345 | fprintf(stderr, "%s -h -i -e -t -q query exclude-file-list\n", argv[0]); 346 | exit(0); 347 | } 348 | 349 | if (i + 1 < argc && 350 | strcmp(argv[i], "-i") == 0) { 351 | skip = 1; 352 | loadRoute(argv[i + skip], includeNetwork); 353 | continue; 354 | } 355 | 356 | if (i + 1 < argc && 357 | strcmp(argv[i], "-6") == 0) { 358 | _family = AF_INET6; 359 | continue; 360 | } 361 | 362 | if (i + 1 < argc && 363 | strcmp(argv[i], "-4") == 0) { 364 | _family = AF_INET; 365 | continue; 366 | } 367 | 368 | if (i + 1 < argc && 369 | strcmp(argv[i], "-e") == 0) { 370 | skip = 1; 371 | loadRoute(argv[i + skip], excludeNetwork); 372 | continue; 373 | } 374 | 375 | if (i + 1 < argc && 376 | strcmp(argv[i], "-t") == 0) { 377 | skip = 1; 378 | if (_family == AF_INET) 379 | initRoute(argv[i + skip]); 380 | else if (_family == AF_INET6) 381 | initRoute6(argv[i + skip]); 382 | continue; 383 | } 384 | 385 | if (i + 1 < argc && 386 | strcmp(argv[i], "-q") == 0) { 387 | skip = 1; 388 | if (query == 0) { 389 | qsort(_net_list, _net_count, sizeof(_net_list[0]), subnet_compare); 390 | query = 1; 391 | } 392 | 393 | queryRoute(pton_val(argv[i + 1], _family), argv[i + 1]); 394 | continue; 395 | } 396 | 397 | if (_net_count == 0) { 398 | if (_family == AF_INET) 399 | initRoute("DEFAULT"); 400 | else if (_family == AF_INET6) 401 | initRoute6("DEFAULT"); 402 | } 403 | 404 | loadRoute(argv[i], excludeNetwork); 405 | } 406 | 407 | if (query == 0) 408 | dumpRoute(); 409 | 410 | return 0; 411 | } 412 | -------------------------------------------------------------------------------- /stunutil.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef __MACH__ 8 | #include 9 | #include 10 | #endif 11 | 12 | typedef unsigned char u_char; 13 | typedef unsigned short u_short; 14 | typedef unsigned int u_int; 15 | 16 | #ifdef WIN32 17 | #include 18 | typedef int socklen_t; 19 | typedef unsigned long in_addr_t; 20 | typedef unsigned short in_port_t; 21 | #define MSG_DONTWAIT 0 22 | #else 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #endif 31 | 32 | /* 33 | * stun.l.google.com:19302 34 | * stun.ekiga.net:3478 35 | */ 36 | 37 | enum { 38 | BindingRequest = 0x0001, 39 | BindingResponse = 0x0101, 40 | BindingErrorResponse = 0x0111, 41 | 42 | MAPPED_ADDRESS = 0x0001, 43 | CHANGE_REQUEST = 0x0003, 44 | SOURCE_ADDRESS = 0x0004, 45 | CHANGED_ADDRESS = 0x0005 46 | }; 47 | 48 | #define STUN_MAX_REQUEST 64 49 | #define STUN_FLAG_CTREAT 0x01 50 | #define STUN_FLAG_FINISH 0x02 51 | #define STUN_FLAG_READED 0x04 52 | #define STUN_FLAG_TIMERD 0x08 53 | #define STUN_FLAG_ERROR 0x10 54 | 55 | struct request_context { 56 | int flags; 57 | size_t last_ticks; 58 | size_t retries_times; 59 | u_char request_ident[16]; 60 | u_char request_source[128]; 61 | struct sockaddr_in6 request_target; 62 | }; 63 | 64 | static int carrier = 0; 65 | // static int pending_timeout = 0; 66 | static int pending_incoming = 0; 67 | static struct request_context all_request_list[STUN_MAX_REQUEST]; 68 | 69 | static size_t utils_getticks(void) 70 | { 71 | #if defined(WIN32) 72 | DWORD ticks; 73 | 74 | ticks = GetTickCount(); 75 | 76 | return ticks; 77 | #elif defined(__APPLE__) 78 | clock_serv_t cclock; 79 | mach_timespec_t ts; 80 | host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); 81 | clock_get_time(cclock, &ts); 82 | mach_port_deallocate(mach_task_self(), cclock); 83 | return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000); 84 | #else 85 | int err; 86 | struct timespec ts; 87 | 88 | err = clock_gettime(CLOCK_MONOTONIC, &ts); 89 | assert(err == 0); 90 | 91 | return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000); 92 | #endif 93 | } 94 | 95 | #define MIN_PACKET_LEN sizeof(struct stun_request_args_base) 96 | 97 | struct stun_request_args_base { 98 | u_short binding_request, payload_length; 99 | u_char request_session_key[16]; 100 | }; 101 | 102 | static void clear_carrier(void) 103 | { 104 | while (carrier > 0) { 105 | fprintf(stderr, "\b"); 106 | carrier--; 107 | } 108 | 109 | return; 110 | } 111 | 112 | static void output_carrier(void) 113 | { 114 | fprintf(stderr, "."); 115 | carrier++; 116 | return; 117 | } 118 | 119 | static u_char *stun_pack(u_char *dst, u_short type, const void *src, size_t len) 120 | { 121 | * (u_short *)dst = htons(type); 122 | dst += sizeof(u_short); 123 | 124 | * (u_short *)dst = htons(len); 125 | dst += sizeof(u_short); 126 | 127 | memcpy(dst, src, len); 128 | dst += len; 129 | 130 | return dst; 131 | } 132 | 133 | static void output_debug_attrib(u_short attrib, u_char *but, u_short len) 134 | { 135 | struct { 136 | u_short family; 137 | u_short port; 138 | struct in_addr in1addr; 139 | } stun_addr; 140 | size_t cplen; 141 | 142 | cplen = sizeof(stun_addr); 143 | memcpy(&stun_addr, but, len > cplen? cplen: len); 144 | 145 | switch(attrib) { 146 | case CHANGED_ADDRESS: 147 | assert(len == 8); 148 | fprintf(stderr, "changed address: %s:%d\n", 149 | inet_ntoa(stun_addr.in1addr), htons(stun_addr.port)); 150 | break; 151 | 152 | case SOURCE_ADDRESS: 153 | assert(len == 8); 154 | fprintf(stderr, "source address: %s:%d\n", 155 | inet_ntoa(stun_addr.in1addr), htons(stun_addr.port)); 156 | break; 157 | 158 | case MAPPED_ADDRESS: 159 | assert(len == 8); 160 | fprintf(stderr, "mapped address: %s:%d\n", 161 | inet_ntoa(stun_addr.in1addr), htons(stun_addr.port)); 162 | break; 163 | } 164 | 165 | return; 166 | } 167 | 168 | static void extract_stun_packet(u_char *but, size_t len) 169 | { 170 | u_short attrib, length; 171 | u_char *limit = but + len; 172 | 173 | while (but + 4 <= limit) { 174 | attrib = *(u_short *)but; 175 | but += sizeof(u_short); 176 | attrib = htons(attrib); 177 | 178 | length = *(u_short *)but; 179 | but += sizeof(u_short); 180 | length = htons(length); 181 | 182 | if (but + length > limit) { 183 | fprintf(stderr, "extract stun attrib failure\n"); 184 | break; 185 | } 186 | 187 | output_debug_attrib(attrib, but, length); 188 | but += length; 189 | continue; 190 | } 191 | 192 | return; 193 | } 194 | 195 | static u_int stun_do_packet(int fildes, u_char *but, size_t len, struct sockaddr *from, socklen_t fromlen) 196 | { 197 | u_int flags = 0; 198 | size_t d_off = 0; 199 | struct stun_request_args_base args0; 200 | 201 | if (len > 0 && len < MIN_PACKET_LEN) { 202 | struct sockaddr_in so; 203 | clear_carrier(); 204 | memcpy(&so, from, fromlen); 205 | fprintf(stderr, "incoming: %s:%d\n", 206 | inet_ntoa(so.sin_addr), ntohs(so.sin_port)); 207 | return 0; 208 | } 209 | 210 | if (len < MIN_PACKET_LEN || len >= 1024) { 211 | clear_carrier(); 212 | #ifndef WIN32 213 | fprintf(stderr, "recv_len %ld, error %d", len, errno); 214 | #else 215 | fprintf(stderr, "recv_len %ld, error %d", len, GetLastError()); 216 | #endif 217 | return 0; 218 | } 219 | 220 | { 221 | struct sockaddr_in so; 222 | clear_carrier(); 223 | memcpy(&so, from, fromlen); 224 | fprintf(stderr, "incoming: %s:%d\n", 225 | inet_ntoa(so.sin_addr), ntohs(so.sin_port)); 226 | } 227 | 228 | d_off = sizeof(args0); 229 | memcpy(&args0, but, sizeof(args0)); 230 | 231 | if (args0.binding_request == htons(BindingErrorResponse) 232 | || args0.binding_request == htons(BindingResponse)) { 233 | extract_stun_packet(but + d_off, len - d_off); 234 | flags = STUN_FLAG_READED; 235 | return flags; 236 | } 237 | 238 | if (args0.binding_request == htons(BindingRequest)) { 239 | int error; 240 | size_t len; 241 | u_char *adj; 242 | u_char d_buf[2048]; 243 | struct sockaddr_in d_addr; 244 | socklen_t namelen = sizeof(d_addr); 245 | 246 | struct { 247 | u_short family; 248 | u_short port; 249 | u_int address; 250 | } attrib_addr; 251 | 252 | adj = (d_buf + d_off); 253 | memcpy(&d_addr, from, fromlen); 254 | 255 | attrib_addr.family = htons(1); 256 | attrib_addr.port = d_addr.sin_port; 257 | attrib_addr.address = d_addr.sin_addr.s_addr; 258 | adj = stun_pack(adj, MAPPED_ADDRESS, &attrib_addr, sizeof(attrib_addr)); 259 | 260 | error = getsockname(fildes, (struct sockaddr *)&d_addr, &namelen); 261 | assert(error == 0); 262 | 263 | attrib_addr.family = htons(1); 264 | attrib_addr.port = d_addr.sin_port; 265 | attrib_addr.address = d_addr.sin_addr.s_addr; 266 | adj = stun_pack(adj, SOURCE_ADDRESS, &attrib_addr, sizeof(attrib_addr)); 267 | 268 | len = adj - d_buf; 269 | args0.payload_length = htons(len - d_off); 270 | args0.binding_request = htons(BindingResponse); 271 | memcpy(d_buf, &args0, d_off); 272 | 273 | (void)sendto(fildes, (char *)d_buf, len, 0, from, fromlen); 274 | fprintf(stderr, "output packet\n"); 275 | return 0; 276 | } 277 | 278 | fprintf(stderr, "invalid packet\n"); 279 | return flags; 280 | } 281 | 282 | static void outgoing_stun_request(struct request_context *ctx, int fildes, unsigned change_type) 283 | { 284 | u_char *adj; 285 | size_t d_off; 286 | u_char d_buf[2048]; 287 | struct stun_request_args_base arg0; 288 | 289 | // u_char request_session_key[16]; 290 | 291 | d_off = sizeof(arg0); 292 | arg0.binding_request = htons(BindingRequest); 293 | arg0.payload_length = htons(0); 294 | 295 | adj = d_buf + d_off; 296 | 297 | if (change_type) { 298 | change_type = htonl(change_type); 299 | adj = stun_pack(adj, CHANGE_REQUEST, &change_type, sizeof(change_type)); 300 | } 301 | 302 | ctx->last_ticks = utils_getticks(); 303 | memcpy(arg0.request_session_key, ctx->request_ident, sizeof(ctx->request_ident)); 304 | memcpy(arg0.request_session_key + 12, &ctx->last_ticks, 4); 305 | arg0.request_session_key[11] = ctx->retries_times++; 306 | arg0.payload_length = htons(adj - d_buf - d_off); 307 | 308 | memcpy(d_buf, &arg0, d_off); 309 | (void)sendto(fildes, (char *)d_buf, adj - d_buf, 0, 310 | (const struct sockaddr *)&ctx->request_target, sizeof(ctx->request_target)); 311 | fprintf(stderr, "output request: %s\n", ctx->request_source); 312 | pending_incoming++; 313 | return; 314 | } 315 | 316 | static void stun_do_output(u_int flags, int fildes, size_t ticks, u_char *but, size_t len, u_int change_type) 317 | { 318 | int index; 319 | int receive = 0; 320 | int timeout = 0; 321 | struct request_context *ctx; 322 | struct stun_request_args_base arg0; 323 | 324 | receive = (flags & STUN_FLAG_READED); 325 | timeout = (flags & STUN_FLAG_TIMERD); 326 | 327 | if (receive) 328 | memcpy(&arg0, but, sizeof(arg0)); 329 | 330 | if (timeout && pending_incoming) 331 | pending_incoming--; 332 | 333 | index = 0; 334 | #define STUN_FLAG_DOING(flags) ((STUN_FLAG_CTREAT| STUN_FLAG_FINISH) & flags) 335 | for (; flags != 0 && index < STUN_MAX_REQUEST; index++) { 336 | ctx = all_request_list + index; 337 | if (STUN_FLAG_DOING(ctx->flags) != STUN_FLAG_CTREAT) { 338 | /* skip this context */ 339 | continue; 340 | } 341 | 342 | if (receive != 0 && 343 | 0 == memcmp(ctx->request_ident, arg0.request_session_key, 11)) { 344 | ctx->flags |= STUN_FLAG_FINISH; 345 | pending_incoming--; 346 | receive = 0; 347 | continue; 348 | } 349 | 350 | if (pending_incoming < 3 351 | && ctx->last_ticks + 1000 < ticks && ctx->retries_times < 3) { 352 | outgoing_stun_request(ctx, fildes, change_type); 353 | } 354 | 355 | if (pending_incoming >= 3 && receive == 0) { 356 | fprintf(stderr, "waiting for netxt retries\n"); 357 | break; 358 | } 359 | } 360 | 361 | return; 362 | } 363 | 364 | static int inet_p4ton6(struct in6_addr *in6a, const char *target) 365 | { 366 | char buf[256]; 367 | snprintf(buf, sizeof(buf), "::ffff:%s", target); 368 | return inet_pton(AF_INET6, buf, in6a); 369 | } 370 | 371 | static void load_stun_config(int argc, char *argv[]) 372 | { 373 | int i; 374 | int index = 0; 375 | char domainbuf[127]; 376 | 377 | for (i = 1; i < argc; i++) { 378 | int j; 379 | int d_port = 3478; 380 | char *port = 0; 381 | char *hostname = NULL; 382 | in_addr_t target = 0; 383 | struct request_context *ctx = NULL; 384 | 385 | if (argv[i] == NULL) { 386 | /* use by option */ 387 | continue; 388 | } 389 | 390 | strcpy(domainbuf, argv[i]); 391 | hostname = domainbuf; 392 | 393 | fprintf(stderr, "gethostname: %s\n", hostname); 394 | 395 | if (NULL != (port = strchr(domainbuf, ':'))) { 396 | *port++ = 0; 397 | d_port = atoi(port); 398 | if (d_port == -1) { 399 | fprintf(stderr, "get port failure: %s\n", argv[i]); 400 | continue; 401 | } 402 | } 403 | 404 | target = inet_addr(hostname); 405 | if (target == INADDR_ANY || target == INADDR_NONE) { 406 | struct hostent *phost = gethostbyname(hostname); 407 | if (phost == NULL) { 408 | fprintf(stderr, "get hostname failure: %s\n", argv[i]); 409 | continue; 410 | } 411 | memcpy(&target, phost->h_addr, sizeof(target)); 412 | } 413 | 414 | ctx = &all_request_list[index++]; 415 | strcpy((char *)ctx->request_source, argv[i]); 416 | ctx->last_ticks = 0; 417 | ctx->retries_times = 0; 418 | ctx->flags = STUN_FLAG_CTREAT; 419 | 420 | ctx->request_target.sin6_family = AF_INET6; 421 | ctx->request_target.sin6_port = htons(d_port); 422 | // inet_p4ton6(&ctx->request_target.sin6_addr, target); 423 | 424 | fprintf(stderr, "%s ", argv[i]); 425 | 426 | for (j = 0; j < 8; j++) { 427 | u_short *ident = (u_short *)ctx->request_ident; 428 | ident[j] = rand(); 429 | fprintf(stderr, "%04x", htons(ident[j])); 430 | } 431 | 432 | fprintf(stderr, "\n"); 433 | } 434 | 435 | return; 436 | } 437 | 438 | u_int stun_change_flag(const char *change_type) 439 | { 440 | u_int type = 0; 441 | 442 | if (strcmp(change_type, "port") == 0) { 443 | type |= 0x2; 444 | } else if (strcmp(change_type, "ip") == 0) { 445 | type |= 0x4; 446 | } else if (strcmp(change_type, "all") == 0) { 447 | type |= 0x4; 448 | type |= 0x2; 449 | } else if (strcmp(change_type, "ip:port") == 0) { 450 | type |= 0x4; 451 | type |= 0x2; 452 | } 453 | 454 | return type; 455 | } 456 | 457 | int main(int argc, char *argv[]) 458 | { 459 | int i; 460 | int error; 461 | int fildes; 462 | u_int change_type = 0; 463 | fd_set readfds; 464 | fd_set writefds; 465 | size_t last_idle; 466 | size_t last_ticks; 467 | struct sockaddr yours; 468 | struct sockaddr_in6 mime; 469 | 470 | #ifdef WIN32 471 | WSADATA data; 472 | WSAStartup(0x101, &data); 473 | #endif 474 | 475 | fildes = socket(AF_INET6, SOCK_DGRAM, 0); 476 | assert(fildes != -1); 477 | 478 | mime.sin6_family = AF_INET6; 479 | mime.sin6_port = htons(9000); 480 | mime.sin6_addr = in6addr_any; 481 | 482 | for (i = 1; i < argc; i++) { 483 | if (0 == strncmp(argv[i], "-p", 2)) { 484 | int d_port = 3478; 485 | const char *opt = argv[i] + 2; 486 | 487 | if (*opt != 0) { 488 | argv[i] = NULL; 489 | d_port = atoi(opt + (*opt == '=')); 490 | } else if (argc > i + 1) { 491 | argv[i] = NULL; 492 | d_port = atoi(argv[++i]); 493 | argv[i] = NULL; 494 | } 495 | 496 | mime.sin6_port = htons(d_port); 497 | } else if (0 == strncmp(argv[i], "-h", 2)) { 498 | fprintf(stderr, "%s [options] \n", argv[0]); 499 | fprintf(stderr, "\t-p source port\n"); 500 | fprintf(stderr, "\t-f port|all|ip|ip:port\n"); 501 | fprintf(stderr, "\tserver-list\n"); 502 | fprintf(stderr, "\n"); 503 | exit(0); 504 | } else if (0 == strncmp(argv[i], "-f", 2)) { 505 | const char *opt = argv[i] + 2; 506 | 507 | if (*opt != 0) { 508 | argv[i] = NULL; 509 | change_type = stun_change_flag(opt + (*opt == '=')); 510 | } else if (argc > i + 1) { 511 | argv[i] = NULL; 512 | change_type = stun_change_flag(argv[++i]); 513 | argv[i] = NULL; 514 | } 515 | } 516 | } 517 | 518 | error = bind(fildes, (const struct sockaddr *)&mime, sizeof(mime)); 519 | assert(error == 0); 520 | 521 | last_idle = utils_getticks(); 522 | last_ticks = utils_getticks(); 523 | load_stun_config(argc, argv); 524 | 525 | stun_do_output(STUN_FLAG_TIMERD, fildes, last_ticks, NULL, 0, change_type); 526 | while (pending_incoming > 0 || last_idle + 48000 > last_ticks) { 527 | u_char but[1024]; 528 | u_int stun_flags; 529 | size_t stun_ticks; 530 | struct timeval tval; 531 | 532 | tval.tv_sec = 1; 533 | tval.tv_usec = 0; 534 | 535 | FD_ZERO(&readfds); 536 | FD_SET(fildes, &readfds); 537 | 538 | FD_ZERO(&writefds); 539 | FD_SET(fildes, &writefds); 540 | error = select(fildes + 1, &readfds, NULL, NULL, &tval); 541 | assert(error != -1); 542 | 543 | stun_flags = 0; 544 | if (error > 0 && FD_ISSET(fildes, &readfds)) { 545 | socklen_t yourlen = sizeof(yours); 546 | error = recvfrom(fildes, (char *)but, sizeof(but), MSG_DONTWAIT, &yours, &yourlen); 547 | stun_flags = stun_do_packet(fildes, but, error, &yours, yourlen); 548 | last_idle = utils_getticks(); 549 | } 550 | 551 | stun_ticks = utils_getticks(); 552 | if (stun_ticks > last_ticks + 1000) { 553 | stun_flags |= STUN_FLAG_TIMERD; 554 | last_ticks = stun_ticks; 555 | output_carrier(); 556 | } 557 | 558 | stun_do_output(stun_flags, fildes, stun_ticks, but, error, change_type); 559 | continue; 560 | } 561 | 562 | 563 | #if defined(WIN32) 564 | closesocket(fildes); 565 | WSACleanup(); 566 | #else 567 | close(fildes); 568 | #endif 569 | 570 | clear_carrier(); 571 | return 0; 572 | } 573 | 574 | 575 | -------------------------------------------------------------------------------- /dns_mod_trd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include "dnsproto.h" 18 | #include "subnet_api.h" 19 | 20 | #define LOG_DEBUG(fmt, args...) fprintf(stderr, fmt"\n", ##args) 21 | 22 | #ifdef WIN32 23 | #include 24 | #else 25 | #include 26 | #include 27 | #include 28 | #include 29 | #define closesocket close 30 | #endif 31 | 32 | static char addrbuf[256]; 33 | #define ntop6(addr) inet_ntop(AF_INET6, &addr, addrbuf, sizeof(addrbuf)) 34 | #define ntop6p(addr) inet_ntop(AF_INET6, addr, addrbuf, sizeof(addrbuf)) 35 | 36 | struct dns_resource _predefine_resource_record[] = { 37 | { 38 | .type = NSTYPE_SOA, 39 | .klass = NSCLASS_INET, 40 | .ttl = 86400, 41 | .len = 4, 42 | .flags = 0, 43 | .domain = "_dummy", 44 | .value = {110, 42, 145, 164}}, 45 | { 46 | .type = NSTYPE_NS, 47 | .klass = NSCLASS_INET, 48 | .ttl = 86400, 49 | .len = 8, 50 | .flags = 0, 51 | .domain = "_dummy", 52 | .value = {110, 42, 145, 164}}, 53 | { 54 | .type = NSTYPE_A, 55 | .klass = NSCLASS_INET, 56 | .ttl = 360, 57 | .len = 4, 58 | .flags = 0, 59 | .domain = "cdn.855899.xyz", 60 | .value = {54, 192, 17, 115}}, 61 | { 62 | .type = NSTYPE_A, 63 | .klass = NSCLASS_INET, 64 | .ttl = 360, 65 | .len = 4, 66 | .flags = 0, 67 | .domain = "cdn.855899.xyz", 68 | .value = {172, 67, 165, 145}}, 69 | { 70 | .type = NSTYPE_A, 71 | .klass = NSCLASS_INET, 72 | .ttl = 36000, 73 | .len = 4, 74 | .flags = 0, 75 | .domain = "mtalk.oogleg.moc.cootail.com", 76 | .value = {10, 0, 3, 1}}, 77 | { 78 | .type = NSTYPE_A, 79 | .klass = NSCLASS_INET, 80 | .ttl = 36000, 81 | .len = 4, 82 | .flags = 0, 83 | .domain = "mtalk.google.com", 84 | .value = {10, 0, 3, 1}}, 85 | { 86 | .type = NSTYPE_A, 87 | .klass = NSCLASS_INET, 88 | .ttl = 36000, 89 | .len = 4, 90 | .flags = 0, 91 | .domain = "alt1-mtalk.google.com", 92 | .value = {110, 42, 145, 164}}, 93 | }; 94 | 95 | #define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array)) 96 | 97 | int fetch_predefine_resource_record(struct dns_parser *parser) 98 | { 99 | int found = 0; 100 | struct dns_resource *res; 101 | struct dns_question *que = &parser->question[0]; 102 | size_t domain_plen = strlen(que->domain); 103 | 104 | for (int i = 0; i < ARRAY_SIZE(_predefine_resource_record); i++) { 105 | 106 | if (MAX_RECORD_COUNT <= parser->head.answer) { 107 | break; 108 | } 109 | 110 | res = &_predefine_resource_record[i]; 111 | if ((res->type == que->type) && strcasecmp(res->domain, que->domain) == 0) { 112 | int index = parser->head.answer++; 113 | parser->answer[index].type = res->type; 114 | parser->answer[index].klass = res->klass; 115 | parser->answer[index].flags = res->flags; 116 | parser->answer[index].ttl = res->ttl; 117 | memcpy(parser->answer[index].value, res->value, sizeof(res->value)); 118 | parser->answer[index].domain = add_domain(parser, que->domain); 119 | } 120 | 121 | if (/* res->type == NSTYPE_ANY &&*/ strcasecmp(que->domain, res->domain) == 0) { 122 | found = 1; 123 | } 124 | } 125 | 126 | return (parser->head.answer > 0) || (found == 1); 127 | } 128 | 129 | 130 | #define NSCLASS_INET 0x01 131 | #define NSFLAG_RD 0x0100 132 | 133 | struct dns_context { 134 | int outfd; 135 | int sockfd; 136 | 137 | socklen_t dnslen; 138 | struct sockaddr_in6 *dnsaddr; 139 | }; 140 | 141 | struct dns_query_context { 142 | struct sockaddr_in6 from; 143 | struct dns_parser parser; 144 | }; 145 | 146 | static struct dns_query_context _orig_list[0xfff + 1]; 147 | static struct dns_query_context _orig_list_ipv4[0xfff + 1]; 148 | static struct dns_query_context _orig_list_ipv6[0xfff + 1]; 149 | 150 | static int dns_parser_copy(struct dns_parser *dst, struct dns_parser *src) 151 | { 152 | static uint8_t _qc_hold[2048]; 153 | size_t len = dns_build(src, _qc_hold, sizeof(_qc_hold)); 154 | return dns_parse(dst, _qc_hold, len) == NULL; 155 | } 156 | 157 | static int dns_contains(const char *domain) 158 | { 159 | int i; 160 | const char *_tld1[] = { 161 | "ten.", "ude.", "oc.", "gro.", "moc.", "vog.", NULL 162 | }; 163 | const char *_tld0[] = { 164 | "net.", "edu.", "co.", "org.", "com.", "gov.", NULL 165 | }; 166 | 167 | for (i = 0; _tld0[i]; i++) { 168 | if (strncasecmp(domain, _tld0[i], 4) == 0) { 169 | return 1; 170 | } 171 | } 172 | 173 | if (strncasecmp(domain, "co.", 3) == 0) { 174 | return 1; 175 | } 176 | 177 | return 0; 178 | } 179 | 180 | static int dns_rewrap(struct dns_parser *p1) 181 | { 182 | int num = p1->head.question; 183 | const char *domain = NULL; 184 | struct dns_question *que, *que1; 185 | 186 | que = &p1->question[0]; 187 | que1 = &p1->question[1]; 188 | 189 | int ndot = 0; 190 | char *limit, *optp; 191 | char *dots[8] = {}, title[256]; 192 | 193 | LOG_DEBUG("suffixes: %s %d", que->domain, que->type); 194 | 195 | *que1 = *que; 196 | 197 | optp = title; 198 | dots[ndot & 0x7] = title; 199 | for (domain = que->domain; *domain; domain++) { 200 | switch(*domain) { 201 | case '.': 202 | if (optp > dots[ndot & 0x7]) ndot++; 203 | *optp++ = *domain; 204 | dots[ndot & 0x7] = optp; 205 | break; 206 | 207 | default: 208 | *optp++ = *domain; 209 | break; 210 | } 211 | } 212 | 213 | *optp = 0; 214 | if (optp > dots[ndot & 0x7]) ndot++; 215 | 216 | if (ndot < 2) { 217 | return 0; 218 | } 219 | 220 | assert(ndot >= 2); 221 | if (!strcasecmp(dots[(ndot - 2) & 0x7], "cootail.com")) { 222 | return 0; 223 | } 224 | 225 | strcat(optp, ".cootail.com"); 226 | 227 | limit = optp - 1; 228 | ndot--; 229 | optp = dots[ndot & 0x7]; 230 | 231 | if (ndot < 1) { 232 | LOG_DEBUG("dns_unwrap warning %s XX", title); 233 | que1->domain = add_domain(p1, title); 234 | return 0; 235 | } 236 | 237 | int cc = 0; 238 | if (optp + 1 == limit) { 239 | limit = dots[ndot & 0x7] -2; 240 | ndot--; 241 | optp = dots[ndot & 0x7]; 242 | cc = 1; 243 | } 244 | 245 | if (cc == 0 || dns_contains(optp)) { 246 | for (; *optp && optp < limit; optp++) { 247 | char t = *optp; 248 | *optp = *limit; 249 | *limit-- = t; 250 | } 251 | 252 | if (ndot < 1) { 253 | LOG_DEBUG("dns_unwrap ork %s", title); 254 | que1->domain = add_domain(p1, title); 255 | return 0; 256 | } 257 | 258 | limit = dots[ndot & 0x7] -2; 259 | ndot--; 260 | optp = dots[ndot & 0x7]; 261 | } 262 | 263 | #if 0 264 | if (ndot < 1) { 265 | LOG_DEBUG("dns_unwrap warning %s", title); 266 | que1->domain = add_domain(p1, title); 267 | return 0; 268 | } 269 | #endif 270 | 271 | char t = *optp; 272 | memmove(optp, optp + 1, limit - optp); 273 | *limit = t; 274 | 275 | LOG_DEBUG("dns_unwrap title=%s cc=%d", title, cc); 276 | if (que1->type == NSTYPE_PTR) { 277 | que1->domain = add_domain(p1, que->domain); 278 | return 0; 279 | } 280 | 281 | que1->domain = add_domain(p1, title); 282 | return 0; 283 | } 284 | 285 | static int dns_sendto(int outfd, struct dns_parser *parser, const struct sockaddr *to, size_t tolen) 286 | { 287 | ssize_t len; 288 | uint8_t _hold[2048]; 289 | 290 | len = dns_build(parser, _hold, sizeof(_hold)); 291 | 292 | const struct sockaddr_in6 *inp = (const struct sockaddr_in6 *)to; 293 | if (len != -1) 294 | len = sendto(outfd, _hold, len, 0, to, tolen); 295 | else 296 | LOG_DEBUG("dns_build %d", len); 297 | 298 | LOG_DEBUG("dns_build bytes %d %d %d %s", len, inp->sin6_family, htons(inp->sin6_port), ntop6(inp->sin6_addr)); 299 | return len; 300 | } 301 | 302 | int do_dns_forward(struct dns_context *ctx, void *buf, int count, struct sockaddr_in6 *from) 303 | { 304 | struct dns_parser p0; 305 | struct dns_parser *pp; 306 | 307 | pp = dns_parse(&p0, buf, count); 308 | if (pp == NULL) { 309 | LOG_DEBUG("do_dns_forward parse failure"); 310 | return 0; 311 | } 312 | 313 | if (p0.head.flags & 0x8000) { 314 | LOG_DEBUG("FROM: %s this is not query", "nothing"); 315 | return -1; 316 | } 317 | 318 | if (fetch_predefine_resource_record(&p0)) { 319 | LOG_DEBUG("prefetch: %s", p0.question[0].domain); 320 | p0.head.flags |= NSFLAG_QR; 321 | dns_sendto(ctx->sockfd, &p0, (struct sockaddr *)from, sizeof(*from)); 322 | return 0; 323 | } 324 | 325 | int retval = 0; 326 | int offset = (p0.head.ident & 0xfff); 327 | 328 | struct dns_parser *p1 = NULL; 329 | struct dns_query_context *qc = &_orig_list[offset]; 330 | if (p0.question[0].type == NSTYPE_AAAA) { 331 | qc = &_orig_list_ipv6[offset]; 332 | } else if (p0.question[0].type == NSTYPE_A) { 333 | qc = &_orig_list_ipv4[offset]; 334 | } 335 | 336 | memset(qc, 0, sizeof(*qc)); 337 | qc->from = *from; 338 | 339 | dns_parser_copy(&qc->parser, &p0); 340 | p1 = &qc->parser; 341 | 342 | if (p0.question[0].type == NSTYPE_A 343 | && !!getenv("REFUSED_IPV4")) { 344 | p0.head.flags |= NSFLAG_QR; 345 | p0.head.flags &= ~NSFLAG_RCODE; 346 | p0.head.flags |= RCODE_NOTAUTH; 347 | dns_sendto(ctx->sockfd, &p0, (struct sockaddr *)from, sizeof(*from)); 348 | return -1; 349 | } 350 | 351 | if (getenv("FORWARD")) { 352 | p1->question[1] = p1->question[0]; 353 | p0.question[0] = p1->question[1]; 354 | } else if (dns_rewrap(p1) == -1) { 355 | LOG_DEBUG("FROM: %s this is not good", p1->question[0].domain); 356 | return -1; 357 | } 358 | 359 | if (p0.question[0].type != NSTYPE_PTR) { 360 | p0.question[0] = p1->question[1]; 361 | } 362 | 363 | p0.head.flags |= NSFLAG_RD; 364 | retval = dns_sendto(ctx->outfd, &p0, (struct sockaddr *)ctx->dnsaddr, ctx->dnslen); 365 | if (retval == -1) { 366 | LOG_DEBUG("dns_sendto failure: %s target %p", strerror(errno), ctx->dnsaddr); 367 | return 0; 368 | } 369 | 370 | return 0; 371 | } 372 | 373 | struct dns_soa { 374 | const char *nameserver; 375 | const char *email; 376 | }; 377 | 378 | static int dump_resource(const char *title, struct dns_resource *res) 379 | { 380 | 381 | if (res->type == NSTYPE_TXT) { 382 | LOG_DEBUG("%s %s TXT", title, res->domain); 383 | } else if (res->type == NSTYPE_A) { 384 | LOG_DEBUG("%s %s A %s", title, res->domain, inet_ntoa(*(struct in_addr *)res->value)); 385 | } else if (res->type == NSTYPE_NS) { 386 | LOG_DEBUG("%s %s NS %s", title, res->domain, *(const char **)res->value); 387 | } else if (res->type == NSTYPE_SRV) { 388 | LOG_DEBUG("%s %s SRV %p", title, res->domain, *(const char **)res->value); 389 | } else if (res->type == NSTYPE_SOA) { 390 | LOG_DEBUG("%s %s SOA %s", title, res->domain, *(const char **)res->value); 391 | } else if (res->type == NSTYPE_AAAA) { 392 | LOG_DEBUG("%s %s AAAA %s", title, res->domain, ntop6(res->value)); 393 | } else if (res->type == NSTYPE_CNAME) { 394 | LOG_DEBUG("%s %s CNAME %s", title, res->domain, *(const char **)res->value); 395 | } else { 396 | LOG_DEBUG("%s %s UNKOWN %d", title, res->domain, res->type); 397 | } 398 | 399 | return 0; 400 | } 401 | 402 | 403 | static int setup_route(const void* ip, int family) 404 | { 405 | uint64_t val = 0; 406 | subnet_t *subnet = NULL; 407 | char sTarget[128], sNetwork[128]; 408 | 409 | 410 | inet_ntop(family, ip, sTarget, sizeof(sTarget)); 411 | if (family == AF_INET6) { 412 | val = htonll(*(uint64_t *)ip); 413 | subnet = lookupRoute6(val); 414 | } else if (family == AF_INET) { 415 | val = htonll(*(uint64_t *)ip) & 0xffffffff00000000ull; 416 | subnet = lookupRoute4(val); 417 | } 418 | 419 | if (subnet != 0 && subnet->flags == 0) { 420 | char sCmd[1024]; 421 | uint64_t network = htonll(subnet->network); 422 | 423 | inet_ntop(family, &network, sNetwork, sizeof(sNetwork)); 424 | fprintf(stderr, "ACTIVE network: %s/%d by %s\n", sNetwork, subnet->prefixlen, sTarget); 425 | subnet->flags = 1; 426 | 427 | if (family == AF_INET) { 428 | sprintf(sCmd, "ipset add ipsec %s/%d", sNetwork, subnet->prefixlen); 429 | fprintf(stderr, "CMD=%s\n", sCmd); 430 | system(sCmd); 431 | } else { 432 | sprintf(sCmd, "ip -6 route add %s/%d dev tun0 mtu 1400 table 100", sNetwork, subnet->prefixlen); 433 | fprintf(stderr, "CMD=%s\n", sCmd); 434 | system(sCmd); 435 | } 436 | 437 | return 0; 438 | } 439 | 440 | return 0; 441 | } 442 | 443 | int do_dns_backward(struct dns_context *ctx, void *buf, int count, struct sockaddr_in6 *from) 444 | { 445 | struct dns_parser p0; 446 | struct dns_parser *pp; 447 | struct dns_resource *res; 448 | 449 | pp = dns_parse(&p0, buf, count); 450 | if (pp == NULL) { 451 | LOG_DEBUG("do_dns_backward parse failure"); 452 | return 0; 453 | } 454 | 455 | if (~p0.head.flags & 0x8000) { 456 | LOG_DEBUG("FROM: %s this is not response", ntop6(from->sin6_addr)); 457 | return -1; 458 | } 459 | 460 | int i, found = 0; 461 | int offset = (p0.head.ident & 0xfff); 462 | struct dns_query_context *qc = &_orig_list[offset]; 463 | if (p0.question[0].type == NSTYPE_AAAA) { 464 | qc = &_orig_list_ipv6[offset]; 465 | } else if (p0.question[0].type == NSTYPE_A) { 466 | qc = &_orig_list_ipv4[offset]; 467 | } 468 | 469 | pp = &qc->parser; 470 | int test = (p0.question[0].type != NSTYPE_PTR); 471 | if (strcmp(p0.question[0].domain, pp->question[test].domain) || p0.question[0].type != pp->question[0].type) { 472 | LOG_DEBUG("drop since name no expected: %s:%d %s:%d", p0.question[0].domain, p0.question[0].type, pp->question[test].domain, pp->question[0].type); 473 | return 0; 474 | } 475 | p0.question[0] = pp->question[0]; 476 | 477 | for (i = 0; i < p0.head.answer; i++) { 478 | res = &p0.answer[i]; 479 | if (res->type == NSTYPE_CNAME) { 480 | const char *alias = *(const char **)res->value; 481 | LOG_DEBUG("domain %s %s %s %s", res->domain, pp->question[0].domain, pp->question[1].domain, alias); 482 | if (strcasecmp(res->domain, pp->question[1].domain) == 0 && 483 | strcasecmp(alias, pp->question[0].domain) == 0) { 484 | memmove(p0.answer + i, p0.answer + i + 1, sizeof(p0.answer[0]) * (p0.head.answer - i -1)); 485 | p0.head.answer = p0.head.answer - 1; 486 | found = 1; 487 | break; 488 | } 489 | } else { 490 | if (strcasecmp(res->domain, pp->question[1].domain) == 0) { 491 | res->domain = pp->question[0].domain; 492 | } 493 | } 494 | } 495 | 496 | #if 0 497 | if (found == 0) { 498 | memmove(p0.answer + 1, p0.answer, sizeof(p0.answer[0]) * p0.head.answer); 499 | res = &p0.answer[0]; 500 | res->domain = add_domain(&p0, qc->parser.question[0].domain); 501 | res->type = NSTYPE_CNAME; 502 | res->klass = NSCLASS_INET; 503 | res->ttl = 3600; 504 | *(const char **)res->value = add_domain(&p0, qc->parser.question[1].domain); 505 | p0.head.answer++; 506 | } 507 | #endif 508 | 509 | #if 0 510 | for (i = 0; i < p0.head.answer; i++) { 511 | res = &p0.answer[i]; 512 | if (res->type == NSTYPE_A) { 513 | setup_route(res->value, AF_INET); 514 | } else if (res->type == NSTYPE_AAAA) { 515 | setup_route(res->value, AF_INET6); 516 | } 517 | } 518 | #endif 519 | 520 | // p0.head.addon = 0; 521 | char buf0[256]; 522 | p0.head.ident = qc->parser.head.ident; 523 | LOG_DEBUG("dns_sendto %s:%d", inet_ntop(AF_INET6, &qc->from.sin6_addr, buf0, sizeof(buf0)), htons(qc->from.sin6_port)); 524 | dns_sendto(ctx->sockfd, &p0, (struct sockaddr *)&qc->from, sizeof(qc->from)); 525 | 526 | return 0; 527 | } 528 | 529 | int main(int argc, char *argv[]) 530 | { 531 | int retval; 532 | int outfd, sockfd; 533 | struct sockaddr_in6 myaddr; 534 | struct sockaddr * paddr = (struct sockaddr *)&myaddr; 535 | 536 | struct sockaddr_in6 myaddr6; 537 | struct sockaddr * paddr6 = (struct sockaddr *)&myaddr6; 538 | 539 | outfd = socket(AF_INET6, SOCK_DGRAM, 0); 540 | assert(outfd != -1); 541 | 542 | myaddr.sin6_family = AF_INET6; 543 | myaddr.sin6_port = 0; 544 | myaddr.sin6_addr = in6addr_any; 545 | retval = bind(outfd, paddr, sizeof(myaddr)); 546 | assert(retval != -1); 547 | 548 | sockfd = socket(AF_INET6, SOCK_DGRAM, 0); 549 | assert(sockfd != -1); 550 | 551 | myaddr6.sin6_family = AF_INET6; 552 | myaddr6.sin6_port = htons(53); 553 | myaddr6.sin6_addr = in6addr_loopback; 554 | #if 0 555 | myaddr6.sin6_addr = in6addr_any; 556 | #endif 557 | setenv("BINDLOCAL", "::ffff:127.0.0.111", 0); 558 | inet_pton(AF_INET6, getenv("BINDLOCAL"), &myaddr6.sin6_addr); 559 | retval = bind(sockfd, paddr6, sizeof(myaddr6)); 560 | assert(retval != -1); 561 | 562 | int count; 563 | char buf[2048]; 564 | fd_set readfds = {}; 565 | socklen_t addrl = 0; 566 | struct sockaddr_in6 dnsaddr; 567 | 568 | struct dns_context c0 = { 569 | .outfd = outfd, 570 | .sockfd = sockfd, 571 | .dnslen = sizeof(dnsaddr), 572 | }; 573 | 574 | setenv("NAMESERVER", "::ffff:8.8.8.8", 0); 575 | 576 | dnsaddr.sin6_family = AF_INET6; 577 | dnsaddr.sin6_port = htons(53); 578 | inet_pton(AF_INET6, getenv("NAMESERVER"), &dnsaddr.sin6_addr); 579 | // dnsaddr.sin_addr.s_addr = inet_addr("223.5.5.5"); 580 | 581 | c0.dnsaddr = (struct sockaddr_in6 *)&dnsaddr; 582 | LOG_DEBUG("nsaddr %p pointer %p %d", c0.dnsaddr, &dnsaddr, htons(dnsaddr.sin6_port)); 583 | 584 | const struct sockaddr_in6 *inp = (const struct sockaddr_in6 *)&dnsaddr; 585 | LOG_DEBUG("dns_build bytes %d %d %d %s", 0, inp->sin6_family, htons(inp->sin6_port), ntop6(inp->sin6_addr)); 586 | 587 | do { 588 | FD_ZERO(&readfds); 589 | FD_SET(outfd, &readfds); 590 | FD_SET(sockfd, &readfds); 591 | 592 | retval = select(sockfd + 1, &readfds, 0, 0, 0); 593 | if (retval == -1) { 594 | LOG_DEBUG("select failure: %s", strerror(errno)); 595 | break; 596 | } 597 | 598 | if (FD_ISSET(outfd, &readfds)) { 599 | addrl = sizeof(myaddr); 600 | count = recvfrom(outfd, buf, sizeof(buf), 0, paddr, &addrl); 601 | count > 0 || LOG_DEBUG("outfd is readable"); 602 | assert(count > 0); 603 | do_dns_backward(&c0, buf, count, &myaddr); 604 | } 605 | 606 | if (FD_ISSET(sockfd, &readfds)) { 607 | addrl = sizeof(myaddr6); 608 | count = recvfrom(sockfd, buf, sizeof(buf), 0, paddr6, &addrl); 609 | count > 0 || LOG_DEBUG("sockfd is readable"); 610 | assert(count > 0); 611 | do_dns_forward(&c0, buf, count, &myaddr6); 612 | } 613 | 614 | } while (retval >= 0); 615 | 616 | close(sockfd); 617 | close(outfd); 618 | 619 | return 0; 620 | } 621 | -------------------------------------------------------------------------------- /dns_split_daemon.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "dnsproto.h" 20 | #include "subnet_api.h" 21 | 22 | #define LOG_DEBUG(fmt, args...) fprintf(stderr, fmt"\n", ##args) 23 | 24 | #ifdef WIN32 25 | #include 26 | #else 27 | #include 28 | #include 29 | #include 30 | #include 31 | #define closesocket close 32 | #endif 33 | 34 | static char addrbuf[256]; 35 | #define ntop6(addr) inet_ntop(AF_INET6, &addr, addrbuf, sizeof(addrbuf)) 36 | #define ARRAY_COUNT(array) (sizeof(array)/sizeof(array[0])) 37 | struct data_t { 38 | void *buf; 39 | size_t len; 40 | }; 41 | 42 | struct pair_t { 43 | struct data_t key; 44 | struct data_t value; 45 | }; 46 | 47 | struct cache_t { 48 | size_t total; 49 | struct pair_t list[0x1000]; 50 | }; 51 | 52 | static struct cache_t _g_pool; 53 | 54 | struct dns_context { 55 | int outfd; 56 | int sockfd; 57 | 58 | socklen_t dnslen; 59 | struct sockaddr *dnsaddr; 60 | struct sockaddr *qualaaddr; 61 | }; 62 | 63 | struct zip_parser { 64 | char buf[1500]; 65 | int len; 66 | }; 67 | 68 | struct dns_query_context { 69 | int is_china_domain; 70 | int is_nonchina_domain; 71 | struct data_t key; 72 | struct sockaddr_in6 from; 73 | struct zip_parser parser, ecs_parser, def_parser; 74 | }; 75 | 76 | static struct dns_query_context _orig_list[0x1000]; 77 | static int cache_reset(struct cache_t *pool) 78 | { 79 | int i; 80 | struct pair_t *item; 81 | 82 | for (i = 0; i < pool->total; i++) { 83 | item = &pool->list[i]; 84 | free(item->value.buf); 85 | item->value.buf = NULL; 86 | 87 | free(item->key.buf); 88 | item->key.buf = NULL; 89 | } 90 | pool->total = 0; 91 | 92 | return 0; 93 | } 94 | 95 | static int cache_lookup(struct cache_t *pool, const struct data_t *key, struct data_t *value) 96 | { 97 | int i; 98 | struct pair_t item; 99 | 100 | for (i = 0; i < pool->total; i++) { 101 | item = pool->list[i]; 102 | if ((key->len == item.key.len) && 103 | (key->buf == item.key.buf || 104 | memcmp(key->buf, item.key.buf, key->len) == 0)) { 105 | value->len = item.value.len; 106 | value->buf = item.value.buf; 107 | return i; 108 | } 109 | } 110 | 111 | return -1; 112 | } 113 | 114 | static int cache_update(struct cache_t *pool, const struct data_t *key, const struct data_t *value) 115 | { 116 | struct data_t item; 117 | struct pair_t *pair; 118 | 119 | int index = cache_lookup(pool, key, &item); 120 | 121 | { 122 | const uint16_t *src = (uint16_t *)key->buf; 123 | const uint16_t *dst = (uint16_t *)value->buf; 124 | LOG_DEBUG("cache_lookup: TODO:XXX %d ", memcmp(dst + 6, src + 5, key->len -10)); 125 | LOG_DEBUG("cache_lookup: TODO:XXX %d %d %d %d", htons(src[1]), htons(src[2]), htons(src[3]), htons(src[4])); 126 | LOG_DEBUG("cache_lookup: TODO:XXX %d %d %d %d", htons(dst[2]), htons(dst[3]), htons(dst[4]), htons(dst[5])); 127 | } 128 | 129 | if (index >= 0) { 130 | pair = &pool->list[index]; 131 | if (pair->value.buf == value->buf) { 132 | assert(pair->value.len == value->len); 133 | return 0; 134 | } else if (pair->value.buf != NULL) { 135 | free(pair->value.buf); 136 | } else { 137 | assert(pair->value.buf); 138 | } 139 | pair->value.buf = malloc(value->len); 140 | memcpy(pair->value.buf, value->buf, value->len); 141 | pair->value.len = value->len; 142 | return 0; 143 | } else if (pool->total + 1 < ARRAY_COUNT(pool->list)) { 144 | index = pool->total++; 145 | pair = &pool->list[index]; 146 | 147 | pair->value.buf = malloc(value->len); 148 | assert(pair->value.buf != NULL); 149 | 150 | memcpy(pair->value.buf, value->buf, value->len); 151 | pair->value.len = value->len; 152 | 153 | pair->key.buf = malloc(key->len); 154 | assert(pair->key.buf != NULL); 155 | 156 | memcpy(pair->key.buf, key->buf, key->len); 157 | pair->key.len = key->len; 158 | return 0; 159 | } 160 | 161 | return -1; 162 | } 163 | 164 | static int dns_parser_copy(struct dns_parser *dst, struct dns_parser *src) 165 | { 166 | static uint8_t _qc_hold[2048]; 167 | size_t len = dns_build(src, _qc_hold, sizeof(_qc_hold)); 168 | return dns_parse(dst, _qc_hold, len) == NULL; 169 | } 170 | 171 | struct subnet_info { 172 | uint16_t tag; // 0x0008 173 | uint16_t len; 174 | uint16_t family; 175 | uint8_t source_netmask; 176 | uint8_t scope_netmask; 177 | uint8_t addr[16]; 178 | }; 179 | 180 | #define NS_IPV6 2 181 | #define NS_IPV4 1 182 | 183 | // china mobile 117.143.102.0/24 184 | const static struct subnet_info subnet4_data = { 185 | 0x08, sizeof(subnet4_data), NS_IPV4, 24, 0, {117, 143, 102, 0} 186 | }; 187 | 188 | // vn he-ipv6 prefix 2001:470:35:639::/56 189 | const static struct subnet_info subnet6_data = { 190 | 0x08, sizeof(subnet6_data), NS_IPV6, 56, 53, {0x20, 0x01, 0x04, 0x70, 0x00, 0x35, 0x06, 0x39} 191 | }; 192 | 193 | static int contains_subnet(struct dns_parser *p0) 194 | { 195 | struct dns_resource *res = NULL; 196 | struct subnet_info *info = NULL; 197 | 198 | for (int i = 0; i < p0->head.addon; i++) { 199 | res = &p0->addon[i]; 200 | if (res->type != NSTYPE_OPT) { 201 | continue; 202 | } 203 | 204 | if (res->domain == NULL || *res->domain == 0) { 205 | size_t len = res->len; 206 | const uint8_t * valp = *(const uint8_t **)res->value; 207 | struct tagheader {uint16_t tag; uint16_t len; } tag0; 208 | 209 | while (len > sizeof(tag0)) { 210 | memcpy(&tag0, valp, sizeof(tag0)); 211 | if (len < sizeof(tag0) + htons(tag0.len)) break; 212 | const uint8_t *hold = valp; 213 | valp += sizeof(tag0) + htons(tag0.len); 214 | len -= (sizeof(tag0) + htons(tag0.len)); 215 | if (tag0.tag == htons(0x0008)) { 216 | info = (struct subnet_info *)hold; 217 | LOG_DEBUG("taglen=%d family=%d", htons(tag0.len), htons(info->family)); 218 | return htons(info->family) == NS_IPV4; 219 | } 220 | } 221 | } 222 | } 223 | 224 | return 0; 225 | } 226 | 227 | static int add_client_subnet(struct dns_parser *p0, uint8_t *optbuf, const struct subnet_info *info) 228 | { 229 | #ifndef DISABLE_SUBNET 230 | 231 | int have_edns = 0; 232 | struct dns_resource *res = NULL; 233 | struct subnet_info info0 = *info; 234 | 235 | int prefixlen = info->source_netmask;//+ info->scope_netmask; 236 | size_t subnet_len = 8 + ((7 + prefixlen) >> 3); 237 | 238 | info0.tag = htons(info->tag); 239 | info0.family = htons(info->family); 240 | info0.len = htons(4 + ((7 + prefixlen) >> 3)); 241 | 242 | for (int i = 0; i < p0->head.addon; i++) { 243 | res = &p0->addon[i]; 244 | if (res->type != NSTYPE_OPT) { 245 | continue; 246 | } 247 | 248 | if (res->domain == NULL || *res->domain == 0) { 249 | size_t len = res->len; 250 | const uint8_t * valp = *(const uint8_t **)res->value; 251 | struct tagheader {uint16_t tag; uint16_t len; } tag0; 252 | 253 | while (len > sizeof(tag0)) { 254 | memcpy(&tag0, valp, sizeof(tag0)); 255 | if (len < sizeof(tag0) + htons(tag0.len)) break; 256 | const uint8_t *hold = valp; 257 | valp += sizeof(tag0) + htons(tag0.len); 258 | len -= (sizeof(tag0) + htons(tag0.len)); 259 | if (tag0.tag == htons(0x0008)) { 260 | const uint8_t * valp0 = *(const uint8_t **)res->value; 261 | memcpy(optbuf, valp0, (hold - valp0)); 262 | memcpy(optbuf + (hold - valp0), valp, len); 263 | 264 | memcpy(optbuf + (hold - valp0) + len, &info0, subnet_len); 265 | *(void **)res->value = optbuf; 266 | res->len = len + (hold - valp0) + subnet_len; 267 | have_edns = 1; 268 | break; 269 | } 270 | } 271 | 272 | if (have_edns == 0) { 273 | const uint8_t * valp = *(const uint8_t **)res->value; 274 | 275 | memcpy(optbuf, &info0, subnet_len); 276 | memcpy(optbuf + subnet_len, valp, res->len); 277 | 278 | *(void **)res->value = optbuf; 279 | res->len += subnet_len; 280 | have_edns = 1; 281 | } 282 | } 283 | } 284 | 285 | if (p0->head.addon < MAX_RECORD_COUNT && have_edns == 0) { 286 | res = &p0->addon[p0->head.addon++]; 287 | 288 | res->domain = ""; 289 | res->klass = 0x1000; 290 | res->type = NSTYPE_OPT; 291 | res->ttl = 0; 292 | res->len = subnet_len; 293 | memcpy(optbuf, &info0, subnet_len); 294 | *(const void **)res->value = optbuf; 295 | } 296 | #endif 297 | 298 | return 0; 299 | } 300 | 301 | static int dns_contains(const char *domain) 302 | { 303 | int i; 304 | const char *_tld0[] = { 305 | "ten.", "ude.", "oc.", "gro.", "moc.", "vog.", NULL 306 | }; 307 | const char *_tld1[] = { 308 | "net.", "edu.", "co.", "org.", "com.", "gov.", NULL 309 | }; 310 | 311 | (void)_tld1; 312 | for (i = 0; _tld0[i]; i++) { 313 | if (strncasecmp(domain, _tld0[i], 4) == 0) { 314 | return 1; 315 | } 316 | } 317 | 318 | if (strncasecmp(domain, "oc.", 3) == 0) { 319 | return 1; 320 | } 321 | 322 | if (strncmp(domain, "co.", 3) == 0) { 323 | return 1; 324 | } 325 | 326 | return 0; 327 | } 328 | 329 | static int dns_sendto(int outfd, struct dns_parser *parser, const struct sockaddr *to, size_t tolen) 330 | { 331 | ssize_t len; 332 | uint8_t _hold[2048]; 333 | 334 | len = dns_build(parser, _hold, sizeof(_hold)); 335 | 336 | const struct sockaddr_in6 *inp = (const struct sockaddr_in6 *)to; 337 | LOG_DEBUG("dns_build bytes %ld %d %d %s", len, inp->sin6_family, htons(inp->sin6_port), ntop6(inp->sin6_addr)); 338 | if (len != -1) 339 | len = sendto(outfd, _hold, len, 0, to, tolen); 340 | else 341 | LOG_DEBUG("dns_build %ld", len); 342 | 343 | return len; 344 | } 345 | 346 | static void xxdump(const char *title, const void *buf, size_t len) 347 | { 348 | int i; 349 | const uint8_t *data = (const uint8_t *)buf; 350 | const char MAP[17] = "0123456789abcdef"; 351 | char buf1[11111]; 352 | char *ptr = buf1; 353 | 354 | for (i = 0; i < len; i++) { 355 | assert(ptr - buf1 + 2 < sizeof(buf1)); 356 | uint8_t d = data[i]; 357 | *ptr++ = MAP[d >> 4]; 358 | *ptr++ = MAP[d & 0xf]; 359 | } 360 | *ptr = 0; 361 | 362 | LOG_DEBUG("%s: %s", title, buf1); 363 | 364 | return ; 365 | } 366 | 367 | int do_dns_forward(struct dns_context *ctx, void *buf, int count, struct sockaddr_in6 *from) 368 | { 369 | struct dns_parser p0; 370 | struct dns_parser *pp; 371 | 372 | pp = dns_parse(&p0, buf, count); 373 | if (pp == NULL) { 374 | LOG_DEBUG("do_dns_forward parse failure"); 375 | return 0; 376 | } 377 | 378 | size_t datalen = dns_build(&p0, buf, 2048); 379 | if (datalen == -1 || datalen > 1500) { 380 | LOG_DEBUG("do_dns_forward refill failure"); 381 | return 0; 382 | }; 383 | 384 | if (p0.head.question == 0 || p0.head.flags & 0x8000) { 385 | LOG_DEBUG("FROM: %s this is not query", "nothing"); 386 | return -1; 387 | } 388 | 389 | { 390 | struct data_t item, value; 391 | char *keybuf = (char *)buf; 392 | struct cache_t *pool = &_g_pool; 393 | 394 | item.buf = keybuf + 2; // skip ident 395 | item.len = count - 2; 396 | xxdump("data", item.buf, item.len); 397 | 398 | int index = cache_lookup(pool, &item, &value); 399 | LOG_DEBUG("cache is lookup: %d %d vallen %d", count, index, value.len); 400 | if (index >= 0 && value.len > count) { 401 | memcpy(value.buf, keybuf, 2); 402 | sendto(ctx->sockfd, value.buf, value.len, 0, (struct sockaddr *)from, sizeof(*from)); 403 | return 0; 404 | } 405 | 406 | } 407 | 408 | int retval = 0; 409 | int offset = (p0.head.ident & 0xfff); 410 | 411 | struct dns_query_context *qc = &_orig_list[offset]; 412 | if (qc->key.len > 0) { 413 | qc->key.len = 0; 414 | free(qc->key.buf); 415 | qc->key.buf = NULL; 416 | } 417 | 418 | struct sockaddr *dnsaddr = ctx->qualaaddr; 419 | size_t dnslen = ctx->dnslen; 420 | 421 | if (p0.question[0].type == NSTYPE_AAAA) { 422 | dnsaddr = ctx->qualaaddr; 423 | } else if (p0.question[0].type == NSTYPE_A) { 424 | dnsaddr = ctx->dnsaddr; 425 | } 426 | 427 | retval = dns_sendto(ctx->outfd, &p0, dnsaddr, dnslen); 428 | if (retval == -1) { 429 | LOG_DEBUG("dns_sendto failure"); 430 | return 0; 431 | } 432 | 433 | memset(qc, 0, sizeof(*qc)); 434 | qc->from = *from; 435 | qc->key.len = count; 436 | qc->key.buf = malloc(count); 437 | memcpy(qc->key.buf, buf, count); 438 | 439 | return 0; 440 | } 441 | 442 | int do_dns_backward(struct dns_context *ctx, void *buf, int count, struct sockaddr_in6 *from) 443 | { 444 | struct dns_parser p0; 445 | struct dns_parser *pp; 446 | 447 | LOG_DEBUG("count %d", count); 448 | 449 | pp = dns_parse(&p0, buf, count); 450 | if (pp == NULL) { 451 | LOG_DEBUG("do_dns_backward parse failure"); 452 | return 0; 453 | } 454 | 455 | if (~p0.head.flags & htons(0x80)) { 456 | LOG_DEBUG("FROM: %s this is not response", ntop6(from->sin6_addr)); 457 | return -1; 458 | } 459 | 460 | int offset = (p0.head.ident & 0xfff); 461 | struct dns_query_context *qc = &_orig_list[offset]; 462 | LOG_DEBUG("cache_update: %d\n", qc->key.len); 463 | if (qc->key.len > 0) { 464 | struct data_t key, value; 465 | char *keybuf = (char *)qc->key.buf; 466 | key.len = qc->key.len - 2; 467 | key.buf = keybuf + 2; 468 | 469 | value.buf = buf; 470 | value.len = count; 471 | 472 | cache_update(&_g_pool, &key, &value); 473 | free(qc->key.buf); 474 | qc->key.buf = NULL; 475 | qc->key.len = 0; 476 | } 477 | 478 | dns_sendto(ctx->sockfd, &p0, (struct sockaddr *)&qc->from, sizeof(qc->from)); 479 | 480 | return 0; 481 | } 482 | 483 | static void parse_sockaddr(const char *src, struct sockaddr_in6 *dst) 484 | { 485 | char *data = strdup(src); 486 | char *per = data; 487 | int nalpha = 0; 488 | int colon = 0; 489 | int ndot = 0; 490 | int ndiv = -1; 491 | int i; 492 | 493 | for (i = 0; src[i] && colon < 2; i++) { 494 | if (src[i] == ':') colon++; 495 | if (src[i] == '.') ndot++; 496 | if (src[i] == '%') ndiv = i; 497 | if (isalpha(src[i])) nalpha++; 498 | } 499 | 500 | // [ipv6] [ipv6]:port 501 | if (*per == '[') { 502 | char *ipv6 = ++per; 503 | 504 | while (*per != ']' && *per != 0) per++; 505 | if (*per == ']') 506 | *per++ = 0; 507 | 508 | if (ndiv != -1) { 509 | data[ndiv] = 0; 510 | dst->sin6_scope_id = if_nametoindex(data + ndiv + 1); 511 | } 512 | 513 | inet_pton(AF_INET6, ipv6, &dst->sin6_addr); 514 | if (*per == ':') 515 | dst->sin6_port = htons(atoi(per + 1)); 516 | return; 517 | } 518 | 519 | // ipv6: x::x 520 | if (colon >= 2) { 521 | LOG_DEBUG("IPV6: %s", src); 522 | inet_pton(AF_INET6, src, &dst->sin6_addr); 523 | if (ndiv != -1) { 524 | data[ndiv] = 0; 525 | dst->sin6_scope_id = if_nametoindex(data + ndiv + 1); 526 | } 527 | return; 528 | } 529 | 530 | // domain domain:port 531 | if (nalpha) { 532 | return; 533 | } 534 | 535 | // xx.xx.xx.xx xx.xx.xx.xx:port 536 | if (ndot) { 537 | char buf[56]; 538 | while (*per != ':' && *per != 0) per++; 539 | if (*per == ':') 540 | *per++ = 0; 541 | sprintf(buf, "::ffff:%s", data); 542 | inet_pton(AF_INET6, buf, &dst->sin6_addr); 543 | if (*per) dst->sin6_port == htons(atoi(per)); 544 | return; 545 | } 546 | 547 | // port 548 | dst->sin6_port == htons(atoi(src)); 549 | } 550 | 551 | #define get_score_id(ifname) if_nametoindex(ifname) 552 | // #define get_score_id(ifname) 0 553 | 554 | int main(int argc, char *argv[]) 555 | { 556 | int retval; 557 | int outfd, sockfd; 558 | struct sockaddr_in6 myaddr; 559 | struct sockaddr * paddr = (struct sockaddr *)&myaddr; 560 | 561 | struct sockaddr_in6 myaddr6; 562 | struct sockaddr * paddr6 = (struct sockaddr *)&myaddr6; 563 | setenv("BINDLOCAL", "::ffff:127.0.0.111", 0); 564 | LOG_DEBUG("memory: %lu %lu %lu\n", sizeof(_orig_list), sizeof(_orig_list[0]), sizeof(_orig_list[0].parser)); 565 | 566 | outfd = socket(AF_INET6, SOCK_DGRAM, 0); 567 | assert(outfd != -1); 568 | 569 | myaddr.sin6_family = AF_INET6; 570 | myaddr.sin6_port = 0; 571 | myaddr.sin6_addr = in6addr_any; 572 | retval = bind(outfd, paddr, sizeof(myaddr)); 573 | assert(retval != -1); 574 | 575 | sockfd = socket(AF_INET6, SOCK_DGRAM, 0); 576 | assert(sockfd != -1); 577 | 578 | myaddr6.sin6_family = AF_INET6; 579 | myaddr6.sin6_port = htons(53); 580 | myaddr6.sin6_addr = in6addr_any; 581 | 582 | char _dummy[256], *ifp; 583 | strcpy(_dummy, getenv("BINDLOCAL")); 584 | if (NULL != (ifp = strchr(_dummy, '%'))) { 585 | *ifp ++ = 0; 586 | myaddr6.sin6_scope_id = get_score_id(ifp); 587 | inet_pton(AF_INET6, _dummy, &myaddr6.sin6_addr); 588 | } else { 589 | myaddr6.sin6_scope_id = 0; 590 | inet_pton(AF_INET6, _dummy, &myaddr6.sin6_addr); 591 | } 592 | 593 | retval = bind(sockfd, paddr6, sizeof(myaddr6)); 594 | assert(retval != -1); 595 | 596 | int count; 597 | char buf[2048]; 598 | fd_set readfds = {}; 599 | socklen_t addrl = 0; 600 | struct sockaddr_in6 dnsaddr; 601 | struct sockaddr_in6 qualaaddr; 602 | 603 | struct dns_context c0 = { 604 | .outfd = outfd, 605 | .sockfd = sockfd, 606 | .dnslen = sizeof(dnsaddr), 607 | }; 608 | 609 | setenv("NAMESERVER", "[::ffff:8.8.8.8]:53", 0); 610 | setenv("QUALASERVER", "[::ffff:8.8.8.8]:53", 0); 611 | 612 | dnsaddr.sin6_family = AF_INET6; 613 | dnsaddr.sin6_port = htons(53); 614 | dnsaddr.sin6_addr = in6addr_loopback; 615 | parse_sockaddr(getenv("NAMESERVER"), &dnsaddr); 616 | 617 | qualaaddr.sin6_family = AF_INET6; 618 | qualaaddr.sin6_port = htons(53); 619 | qualaaddr.sin6_addr = in6addr_loopback; 620 | parse_sockaddr(getenv("QUALASERVER"), &qualaaddr); 621 | 622 | char i6buf[64]; 623 | LOG_DEBUG("NAMESERVER: %s :%d", inet_ntop(AF_INET6, &dnsaddr.sin6_addr, i6buf, sizeof(i6buf)), htons(dnsaddr.sin6_port)); 624 | LOG_DEBUG("QUALASERVER: %s :%d", inet_ntop(AF_INET6, &qualaaddr.sin6_addr, i6buf, sizeof(i6buf)), htons(dnsaddr.sin6_port)); 625 | 626 | c0.dnsaddr = (struct sockaddr *)&dnsaddr; 627 | c0.qualaaddr = (struct sockaddr *)&qualaaddr; 628 | LOG_DEBUG("nsaddr %p pointer %p %d", c0.dnsaddr, &dnsaddr, htons(dnsaddr.sin6_port)); 629 | 630 | const struct sockaddr_in6 *inp = (const struct sockaddr_in6 *)&dnsaddr; 631 | LOG_DEBUG("dns_build bytes %d %d %d %s", 0, inp->sin6_family, htons(inp->sin6_port), ntop6(inp->sin6_addr)); 632 | 633 | time_t uptime = time(NULL); 634 | 635 | do { 636 | FD_ZERO(&readfds); 637 | FD_SET(outfd, &readfds); 638 | FD_SET(sockfd, &readfds); 639 | 640 | retval = select(sockfd + 1, &readfds, 0, 0, 0); 641 | if (retval == -1) { 642 | LOG_DEBUG("select failure: %s", strerror(errno)); 643 | break; 644 | } 645 | 646 | if (FD_ISSET(outfd, &readfds)) { 647 | addrl = sizeof(myaddr); 648 | count = recvfrom(outfd, buf, sizeof(buf), 0, paddr, &addrl); 649 | assert(count > 0); 650 | count > 0 || LOG_DEBUG("outfd is readable"); 651 | do_dns_backward(&c0, buf, count, &myaddr); 652 | } 653 | 654 | if (FD_ISSET(sockfd, &readfds)) { 655 | addrl = sizeof(myaddr6); 656 | count = recvfrom(sockfd, buf, sizeof(buf), 0, paddr6, &addrl); 657 | assert(count > 0); 658 | count > 0 || LOG_DEBUG("sockfd is readable"); 659 | do_dns_forward(&c0, buf, count, &myaddr6); 660 | } 661 | 662 | if (uptime > time(NULL) || uptime + 600 < time(NULL) || _g_pool.total + 2 > ARRAY_COUNT(_g_pool.list)) { 663 | cache_reset(&_g_pool); 664 | uptime = time(NULL); 665 | } 666 | 667 | } while (retval >= 0); 668 | 669 | close(sockfd); 670 | close(outfd); 671 | 672 | return 0; 673 | } 674 | -------------------------------------------------------------------------------- /dns_lookup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include "dnsproto.h" 18 | #include "subnet_api.h" 19 | 20 | #ifdef WIN32 21 | #include 22 | #else 23 | #include 24 | #include 25 | #include 26 | #include 27 | #define closesocket close 28 | #endif 29 | 30 | static char addrbuf[256]; 31 | #define ntop6(addr) inet_ntop(AF_INET6, &addr, addrbuf, sizeof(addrbuf)) 32 | 33 | #define NS_PTR(p) *(const char **)p 34 | #define LOG_DEBUG(fmt...) 35 | 36 | struct root_server { 37 | char domain[32]; 38 | int ttl; 39 | char ipv4[32]; 40 | char ipv6[132]; 41 | }; 42 | 43 | static struct root_server _root_servers[]= { 44 | {"a.root-servers.net", 518400, "198.41.0.4", "2001:503:ba3e::2:30"}, 45 | {"b.root-servers.net", 518400, "199.9.14.201", "2001:500:200::b"}, 46 | {"c.root-servers.net", 518400, "192.33.4.12", "2001:500:2::c"}, 47 | {"d.root-servers.net", 518400, "199.7.91.13", "2001:500:2d::d"}, 48 | {"e.root-servers.net", 518400, "192.203.230.10", "2001:500:a8::e"}, 49 | {"f.root-servers.net", 518400, "192.5.5.241", "2001:500:2f::f"}, 50 | {"g.root-servers.net", 518400, "192.112.36.4", "2001:500:12::d0d"}, 51 | {"h.root-servers.net", 518400, "198.97.190.53", "2001:500:1::53"}, 52 | {"i.root-servers.net", 518400, "192.36.148.17", "2001:7fe::53"}, 53 | {"j.root-servers.net", 518400, "192.58.128.30", "2001:503:c27::2:30"}, 54 | {"k.root-servers.net", 518400, "193.0.14.129", "2001:7fd::1"}, 55 | {"l.root-servers.net", 518400, "199.7.83.42", "2001:500:9f::42"}, 56 | {"m.root-servers.net", 518400, "202.12.27.33", "2001:dc3::35"} 57 | }; 58 | 59 | #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) 60 | static int ncache = 0; 61 | static struct dns_resource caches[1024] = {}; 62 | 63 | static int npendings = 0; 64 | static struct dns_question pendings[1024] = {}; 65 | int is_query_pending(const char *domain, int type) 66 | { 67 | int i; 68 | struct dns_question *que; 69 | 70 | for (i = 0; i < npendings; i++) { 71 | que = pendings + i; 72 | if (!strcasecmp(domain, que->domain) && type == que->type) { 73 | return 1; 74 | } 75 | 76 | } 77 | 78 | return 0; 79 | 80 | } 81 | 82 | int set_query_pending(const char *domain, int type) 83 | { 84 | int i; 85 | struct dns_question que; 86 | 87 | if (is_query_pending(domain, type)) { 88 | return 0; 89 | } 90 | 91 | que.domain = domain; 92 | que.type = type; 93 | pendings[npendings++] = que; 94 | 95 | return 0; 96 | 97 | } 98 | 99 | int unset_query_pending(const char *domain, int type) 100 | { 101 | int i; 102 | struct dns_question *que; 103 | 104 | for (i = 0; i < npendings; i++) { 105 | que = pendings + i; 106 | if (!strcasecmp(domain, que->domain) && type == que->type) { 107 | pendings[i] = pendings[--npendings]; 108 | return 1; 109 | } 110 | 111 | } 112 | 113 | return 0; 114 | 115 | } 116 | 117 | int contains(const char *domain, const char *suffix) 118 | { 119 | int full, part; 120 | full = strlen(domain); 121 | part = strlen(suffix); 122 | if (full == part) return !strcasecmp(domain, suffix); 123 | return full > part && !strcasecmp(domain + full - part, suffix) && domain[full - part - 1] == '.'; 124 | } 125 | 126 | 127 | static int build_name_server(const char *domain, struct dns_resource p[], size_t l) 128 | { 129 | int i; 130 | int nserver = ARRAY_SIZE(_root_servers); 131 | 132 | if (nserver > l) 133 | nserver = l; 134 | 135 | struct dns_resource tpl = { 136 | .type = NSTYPE_NS, 137 | .klass = NSCLASS_INET, 138 | .ttl = 0, 139 | .len = 8, 140 | .flags = 0, 141 | .domain = "", 142 | .value = {} 143 | }; 144 | 145 | for (i = 0; i < nserver; i++) { 146 | p[i] = tpl; 147 | const char *ptr = _root_servers[i].domain; 148 | memcpy(p[i].value, &ptr, sizeof(ptr)); 149 | } 150 | 151 | for (int j = 0; j < ncache; j++) { 152 | struct dns_resource *res = &caches[j]; 153 | if (res->type == NSTYPE_NS && 154 | contains(domain, res->domain)) { 155 | p[nserver++] = caches[j]; 156 | } 157 | } 158 | 159 | return nserver; 160 | } 161 | 162 | static int lookup_cache(const char *domain, int type, struct dns_resource p[], size_t l) 163 | { 164 | int count = 0; 165 | 166 | for (int j = 0; j < ncache; j++) { 167 | struct dns_resource *res = &caches[j]; 168 | if (res->type == type && strcasecmp(domain, res->domain) == 0) { 169 | p[count] = *res; 170 | count++; 171 | } 172 | 173 | if (res->type == NSTYPE_CNAME && strcasecmp(domain, res->domain) == 0) { 174 | p[0] = *res; 175 | return 1; 176 | } 177 | } 178 | 179 | if (count > 0) { 180 | LOG_DEBUG("lookup_cache domain %s type %d\n", domain, type); 181 | return count; 182 | } 183 | 184 | return 0; 185 | } 186 | 187 | static int hold_to_cache(struct dns_resource *res, size_t count) 188 | { 189 | int i, j; 190 | struct dns_resource *f, *t; 191 | 192 | cache_put(res, count); 193 | 194 | for (i = 0; i < count; i++) { 195 | f = res + i; 196 | 197 | int src = 0, dst = 0; 198 | for (j = 0; j < ncache; j++) { 199 | t = &caches[j]; 200 | if (t->domain == f->domain && t->type == f->type) 201 | continue; 202 | if (src < j) 203 | caches[src] = caches[j]; 204 | src++; 205 | } 206 | ncache = src; 207 | } 208 | 209 | for (i = 0; i < count; i++) { 210 | f = res + i; 211 | caches[ncache] = *f; 212 | ncache++; 213 | } 214 | 215 | return 0; 216 | } 217 | 218 | static int search(const char *domain, struct dns_resource p[], size_t l) 219 | { 220 | int i; 221 | struct dns_resource *res; 222 | 223 | for (i = 0; i < l; i++) { 224 | res = &p[i]; 225 | // const char *ns = NS_PTR(res->value); 226 | if (res->type == NSTYPE_NS && !strcasecmp(res->domain, domain)) { 227 | return 1; 228 | } 229 | } 230 | 231 | return 0; 232 | } 233 | 234 | #define NSFLAG_QR 0x8000 235 | #define NSFLAG_AA 0x0400 236 | 237 | static const char *inet_4to6(void *v6ptr, const void *v4ptr) 238 | { 239 | uint8_t *v4 = (uint8_t *)v4ptr; 240 | uint8_t *v6 = (uint8_t *)v6ptr; 241 | 242 | memset(v6, 0, 10); 243 | v6[10] = 0xff; 244 | v6[11] = 0xff; 245 | 246 | v6[12] = v4[0]; 247 | v6[13] = v4[1]; 248 | v6[14] = v4[2]; 249 | v6[15] = v4[3]; 250 | return ""; 251 | } 252 | 253 | int wait_readable(int sockfd, int millsec) 254 | { 255 | int check; 256 | fd_set readfds; 257 | struct timeval timeout = { 258 | .tv_sec = millsec/1000, 259 | .tv_usec = (millsec % 1000) * 1000 260 | }; 261 | 262 | FD_ZERO(&readfds); 263 | FD_SET(sockfd, &readfds); 264 | 265 | check = select(sockfd + 1, &readfds, NULL, NULL, &timeout); 266 | return check > 0; 267 | } 268 | 269 | static int fetch_resource(const char *domain, int type, const struct in6_addr *server, struct dns_resource p[], size_t start, size_t l, const char *server_name, int *got_author) 270 | { 271 | int i; 272 | int len; 273 | int sockfd; 274 | uint8_t buf[2048]; 275 | struct dns_question *que; 276 | struct dns_resource *res; 277 | struct dns_parser parser = {}; 278 | struct sockaddr_in6 dest = {}; 279 | 280 | parser.head.flags = 0; 281 | parser.head.question = 1; 282 | que = &parser.question[0]; 283 | que->domain = add_domain(&parser, domain); 284 | que->type = type; 285 | que->klass = NSCLASS_INET; 286 | 287 | sockfd = socket(AF_INET6, SOCK_DGRAM, 0); 288 | 289 | parser.head.ident = random(); 290 | len = dns_build(&parser, buf, sizeof(buf)); 291 | 292 | dest.sin6_family = AF_INET6; 293 | dest.sin6_port = htons(53); 294 | dest.sin6_addr = *server; 295 | 296 | len = sendto(sockfd, buf, len, 0, (struct sockaddr *)&dest, sizeof(dest)); 297 | LOG_DEBUG("domain=%s send=%d to=%s %s\n", domain, len, ntop6(dest.sin6_addr), server_name); 298 | if (len > 0 && !wait_readable(sockfd, 400)) { 299 | len = sendto(sockfd, buf, len, 0, (struct sockaddr *)&dest, sizeof(dest)); 300 | LOG_DEBUG("retry domain=%s send=%d to=%s %s\n", domain, len, ntop6(dest.sin6_addr), server_name); 301 | } 302 | 303 | if (len <= 0 || !wait_readable(sockfd, 1000)) { 304 | LOG_DEBUG("failure or timeout\n"); 305 | printf("failure or timeout\n"); 306 | return start; 307 | } 308 | 309 | socklen_t destlen = sizeof(dest); 310 | len = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&dest, &destlen); 311 | LOG_DEBUG("domain=%s recv=%d\n", domain, len); 312 | printf("domain=%s send=%d to=%s %s len=%d\n", domain, len, ntop6(dest.sin6_addr), server_name, len); 313 | 314 | close(sockfd); 315 | 316 | if (len < 12) 317 | return 0; 318 | 319 | memset(&parser, 0, sizeof(parser)); 320 | if (NULL == dns_parse(&parser, buf, len)) { 321 | LOG_DEBUG("dns_parse failure\n"); 322 | return 0; 323 | } 324 | 325 | if (~parser.head.flags & 0x8000) { 326 | LOG_DEBUG("not response\n"); 327 | return 0; 328 | } 329 | 330 | if (parser.head.question != 1 || parser.head.answer + parser.head.author == 0) { 331 | LOG_DEBUG("not response correct\n"); 332 | return 0; 333 | } 334 | 335 | que = &parser.question[0]; 336 | 337 | int ans = start; 338 | const char * origin = que->domain; 339 | 340 | for (i = 0; i < parser.head.answer; i++) { 341 | res = &parser.answer[i]; 342 | 343 | // fprintf(stderr, "anser: %s, domain: %s type %d\n", origin, res->domain, res->type); 344 | if (strcasecmp(origin, res->domain) == 0 && res->type == type) { 345 | if (ans < l) { 346 | p[ans] = *res; 347 | ans++; 348 | } 349 | } else if (strcasecmp(origin, res->domain) == 0 && 350 | res->type == NSTYPE_CNAME) { 351 | char **ptr = (char **)res->value; 352 | // fprintf(stderr, "cname %s -> %s\n", domain, *ptr); 353 | domain = *ptr; 354 | if (ans < l) { 355 | p[ans] = *res; 356 | ans++; 357 | } 358 | #if 0 359 | } else if (contains(origin, res->domain) && 360 | res->type == NSTYPE_NS) { 361 | const char **ptr = (const char **)res->value; 362 | fprintf(stderr, "NS: %s\n", *ptr); 363 | if (ans < l) { 364 | p[ans] = *res; 365 | ans++; 366 | } 367 | #endif 368 | } 369 | } 370 | 371 | *got_author = ans > start || !!(parser.head.flags & NSFLAG_AA); 372 | if (ans > start || (parser.head.flags & NSFLAG_AA)) { 373 | // fprintf(stderr, "ans: %d\n", ans); 374 | hold_to_cache(p, ans); 375 | return ans > start? ans: 0; 376 | } 377 | 378 | 379 | for (i = 0; i < parser.head.author; i++) { 380 | res = &parser.author[i]; 381 | 382 | // fprintf(stderr, "author: %s, domain: %s type %d type %d\n", origin, res->domain, res->type, type); 383 | #if 0 384 | if (strcasecmp(origin, res->domain) == 0 && res->type == type) { 385 | if (ans < l) { 386 | p[ans] = *res; 387 | ans++; 388 | } 389 | } else if (strcasecmp(origin, res->domain) == 0 && 390 | res->type == NSTYPE_CNAME) { 391 | char **ptr = (char **)res->value; 392 | domain = *ptr; 393 | if (ans < l) { 394 | p[ans] = *res; 395 | ans++; 396 | } 397 | } else 398 | #endif 399 | if (contains(origin, res->domain) && 400 | res->type == NSTYPE_NS) { 401 | const char *ptr = NS_PTR(res->value); 402 | LOG_DEBUG("NS: %s zone: %s\n", ptr, res->domain); 403 | if (ans < l && !search(res->domain, p, start)) { 404 | p[ans] = *res; 405 | ans++; 406 | } 407 | } 408 | } 409 | 410 | int oldans = ans; 411 | for (i = 0; i < parser.head.addon; i++) { 412 | res = &parser.addon[i]; 413 | 414 | // fprintf(stderr, "addon: %s, domain: %s type %d\n", origin, res->domain, res->type); 415 | if (res->type == NSTYPE_A) { 416 | if (ans < l) { 417 | p[ans] = *res; 418 | ans++; 419 | } 420 | } 421 | if (res->type == NSTYPE_AAAA) { 422 | if (ans < l) { 423 | p[ans] = *res; 424 | ans++; 425 | } 426 | } 427 | } 428 | 429 | // fprintf(stderr, "ans=%d\n", ans); 430 | hold_to_cache(p, ans); 431 | return oldans; 432 | } 433 | 434 | int filter(struct dns_resource p[], size_t l, size_t count, const char *domain, int type) 435 | { 436 | int i, save = 0; 437 | struct dns_resource *res = NULL; 438 | 439 | // fprintf(stderr, "filter start\n"); 440 | for (i = 0; i < l; i++) { 441 | res = p + i; 442 | if (strcasecmp(domain, res->domain) == 0 && type == res->type) { 443 | // fprintf(stderr, "%s %s %d %d\n", domain, res->domain, save, count); 444 | p[save++] = *res; 445 | } 446 | } 447 | 448 | // fprintf(stderr, "%s %d %d\n", domain, save, count); 449 | 450 | assert(save == count); 451 | return count; 452 | } 453 | 454 | static int get_max_suffix(struct dns_resource *item, size_t len, const char *domain, int type) 455 | { 456 | int nsuffix; 457 | int ndot = 0; 458 | 459 | while (len-- > 0) { 460 | if (item->type == NSTYPE_NS) { 461 | nsuffix = strlen(item->domain); 462 | if (nsuffix > ndot) 463 | ndot = nsuffix; 464 | } 465 | 466 | if ((type == item->type || item->type == NSTYPE_CNAME) && 467 | !strcasecmp(domain, item->domain)) { 468 | ndot = 1000; 469 | break; 470 | } 471 | 472 | item++; 473 | } 474 | 475 | return ndot; 476 | } 477 | 478 | static int query_resource(const char *domain, int type, struct dns_resource p[], size_t l); 479 | 480 | static int query_resource_alias(const char *domain, int type, struct dns_resource p[], size_t l) 481 | { 482 | struct dns_resource *iter; 483 | struct dns_resource *origin_p = p; 484 | 485 | for ( ; ; ) { 486 | int c = query_resource(domain, type, p, l); 487 | 488 | if (c == 1 && p[0].type == NSTYPE_CNAME && type != NSTYPE_CNAME) { 489 | domain = NS_PTR(p->value); 490 | for (iter = origin_p; iter < p; iter++) 491 | if (!strcasecmp(domain, iter->domain)) 492 | goto next; 493 | p++, l--; 494 | continue; 495 | } 496 | 497 | next: 498 | p += c; 499 | break; 500 | } 501 | 502 | return p - origin_p; 503 | } 504 | 505 | static int query_resource(const char *domain, int type, struct dns_resource p[], size_t l) 506 | { 507 | int c, i, j, ndot = 0; 508 | struct dns_resource * res = NULL; 509 | 510 | LOG_DEBUG("query_resource: %s type %d\n", domain, type); 511 | printf("query_resource: %s type %d\n", domain, type); 512 | assert(l > 100); 513 | c = lookup_cache(domain, type, p, l); 514 | if (c == 1 && type != NSTYPE_CNAME && p->type == NSTYPE_CNAME) c = 0; 515 | if (c > 0) 516 | return c; 517 | 518 | if (is_query_pending(domain, type)) { 519 | printf("query_pending: %s type %d\n", domain, type); 520 | return 0; 521 | } 522 | 523 | c = build_name_server(domain, p, l); 524 | 525 | ndot = get_max_suffix(p, c, domain, type); 526 | 527 | int stage = 0; 528 | int got_author = 0; 529 | set_query_pending(domain, type); 530 | for (stage = 0; stage < 2; stage++) 531 | for (i = 0; i < c && !got_author; i++) { 532 | res = p + i; 533 | if (res->type != NSTYPE_NS) 534 | continue; 535 | 536 | if (ndot > strlen(res->domain)) 537 | continue; 538 | 539 | LOG_DEBUG("prepare query_resource_alias: zone %s type %d ndot %d ns %s i=%d c=%d\n", res->domain, type, ndot, "XXX", i, c); 540 | const char * ns = NS_PTR(res->value); 541 | if (search(ns, p, c)) { 542 | /* avoid lookup loop */ 543 | // fprintf(stderr, "ns: %s\n", ns); 544 | continue; 545 | } 546 | 547 | if (strcmp(domain, ns) == 0) { 548 | /* avoid lookup loop */ 549 | // fprintf(stderr, "ns: %s domain: %s\n", ns, domain); 550 | continue; 551 | } 552 | 553 | struct in6_addr dest_addr; 554 | struct dns_resource * server = p + c; 555 | 556 | int types[] = {NSTYPE_A, NSTYPE_AAAA}; 557 | 558 | if (type == NSTYPE_AAAA) { 559 | types[0] = NSTYPE_AAAA; 560 | types[1] = NSTYPE_A; 561 | } 562 | 563 | 564 | if (stage == 0) { 565 | int count = lookup_cache(ns, types[0], server, l - c); 566 | for (j = 0; j < count; j++) { 567 | res = server + j; 568 | if (res->type != types[0]) 569 | continue; 570 | if (res->type == NSTYPE_A) 571 | inet_4to6(&dest_addr, res->value); 572 | else 573 | memcpy(&dest_addr, res->value, 16); 574 | int newc = fetch_resource(domain, type, &dest_addr, p, c, l, ns, &got_author); 575 | if (newc > c || got_author) { 576 | c = newc; 577 | break; 578 | } 579 | } 580 | goto next; 581 | } 582 | 583 | int count1 = query_resource_alias(ns, types[0], server, l - c); 584 | LOG_DEBUG("query_resource_alias: ipv4 %s type %d ndot %d ns %s i=%d c=%d\n", domain, type, ndot, ns, i, c); 585 | for (j = 0; j < count1; j++) { 586 | res = server + j; 587 | if (res->type != types[0]) 588 | continue; 589 | if (res->type == NSTYPE_A) 590 | inet_4to6(&dest_addr, res->value); 591 | else 592 | memcpy(&dest_addr, res->value, 16); 593 | int newc = fetch_resource(domain, type, &dest_addr, p, c, l, ns, &got_author); 594 | if (newc > c || got_author) { 595 | c = newc; 596 | goto next; 597 | } 598 | } 599 | 600 | int count2 = query_resource_alias(ns, types[1], server, l - c); 601 | LOG_DEBUG("query_resource_alias: ipv6 %s type %d ndot %d ns %s i=%d c=%d\n", domain, type, ndot, ns, i, c); 602 | for (j = 0; j < count2; j++) { 603 | res = server + j; 604 | if (res->type != types[1]) 605 | continue; 606 | if (res->type == NSTYPE_A) 607 | inet_4to6(&dest_addr, res->value); 608 | else 609 | memcpy(&dest_addr, res->value, 16); 610 | int newc = fetch_resource(domain, type, &dest_addr, p, c, l, ns, &got_author); 611 | if (newc > c || got_author) { 612 | c = newc; 613 | goto next; 614 | } 615 | } 616 | next: 617 | ndot = get_max_suffix(p, c, domain, type); 618 | } 619 | 620 | // unset_query_pending(domain, type); 621 | c = lookup_cache(domain, type, p, l); 622 | 623 | return c; 624 | } 625 | 626 | int main(int argc, char *argv[]) 627 | { 628 | int c, i, j; 629 | struct dns_resource *res; 630 | struct dns_resource answser[2560]; 631 | 632 | 633 | int nserver = ARRAY_SIZE(_root_servers); 634 | 635 | struct dns_resource tpl = { 636 | .type = NSTYPE_A, 637 | .klass = NSCLASS_INET, 638 | .ttl = 0, 639 | .len = 4, 640 | .flags = 0, 641 | .domain = ".", 642 | .value = {} 643 | }; 644 | 645 | for (i = 0; i < nserver; i++) { 646 | answser[i] = tpl; 647 | answser[i].ttl = _root_servers[i].ttl; 648 | answser[i].domain = _root_servers[i].domain; 649 | inet_pton(AF_INET, _root_servers[i].ipv4, answser[i].value); 650 | } 651 | 652 | for (i = 0; i < nserver; i++) { 653 | answser[i + nserver] = tpl; 654 | answser[i + nserver].type = NSTYPE_AAAA; 655 | answser[i + nserver].ttl = _root_servers[i].ttl; 656 | answser[i + nserver].domain = _root_servers[i].domain; 657 | inet_pton(AF_INET6, _root_servers[i].ipv6, answser[i + nserver].value); 658 | } 659 | hold_to_cache(answser, nserver * 2); 660 | 661 | 662 | for (i = 1; i < argc; i++) { 663 | c = query_resource_alias(argv[i], NSTYPE_A, answser, 2560); 664 | fprintf(stderr, "main c=%d\n", c); 665 | 666 | for (j = 0; j < c; j++) { 667 | res = &answser[j]; 668 | if (res->type == NSTYPE_CNAME) { 669 | fprintf(stderr, "CNAME %s -> %s\n", res->domain, *(char **)res->value); 670 | } else if (res->type == NSTYPE_A) { 671 | fprintf(stderr, "A %s -> %s\n", res->domain, inet_ntoa(*(struct in_addr *)res->value)); 672 | } else if (res->type == NSTYPE_AAAA) { 673 | fprintf(stderr, "AAAA %s -> %s\n", res->domain, ntop6(res->value)); 674 | } 675 | } 676 | 677 | c = query_resource_alias(argv[i], NSTYPE_AAAA, answser, 2560); 678 | fprintf(stderr, "main c=%d\n", c); 679 | 680 | for (j = 0; j < c; j++) { 681 | res = &answser[j]; 682 | if (res->type == NSTYPE_CNAME) { 683 | fprintf(stderr, "CNAME %s -> %s\n", res->domain, *(char **)res->value); 684 | } else if (res->type == NSTYPE_AAAA) { 685 | fprintf(stderr, "AAAA %s -> %s\n", res->domain, ntop6(res->value)); 686 | } else if (res->type == NSTYPE_A) { 687 | fprintf(stderr, "A %s -> %s\n", res->domain, inet_ntoa(*(struct in_addr *)res->value)); 688 | } 689 | } 690 | } 691 | 692 | return 0; 693 | } 694 | -------------------------------------------------------------------------------- /dnsproto.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "dnsproto.h" 11 | 12 | #ifdef __ANDROID__ 13 | #define dn_comp __dn_comp 14 | #endif 15 | 16 | #define NTOH_PTR_SET(x, y, z) { z _t; memcpy(&_t, y, sizeof(_t)); switch (sizeof(_t)) { case 4: _t = htonl(_t); break; case 2: _t = htons(_t); break; default: break; } memcpy(x, &_t, sizeof(_t)); } 17 | 18 | union dns_res_value { 19 | uint32_t u32; 20 | uint16_t u16; 21 | char *str; 22 | void *ptr; 23 | }; 24 | 25 | struct dns_rsc_fixed { 26 | uint16_t type; 27 | uint16_t klass; 28 | uint32_t ttl; 29 | uint16_t len; 30 | } __attribute__ ((packed)); 31 | 32 | static int get_rsc_fixed(struct dns_resource *res, struct dns_rsc_fixed *pf, const void *s, size_t len) 33 | { 34 | *pf = *(const struct dns_rsc_fixed *)s; 35 | 36 | assert(len == sizeof(*pf)); 37 | res->type = pf->type = ntohs(pf->type); 38 | res->klass = pf->klass = ntohs(pf->klass); 39 | res->ttl = pf->ttl = ntohl(pf->ttl); 40 | res->len = pf->len = ntohs(pf->len); 41 | 42 | return 0; 43 | } 44 | 45 | static const char * rsrc_verify_signature[256] = { 46 | [NSTYPE_A] = NSSIG_A, 47 | [NSTYPE_NS] = NSSIG_NS, 48 | [NSTYPE_CNAME] = NSSIG_CNAME, 49 | [NSTYPE_SOA] = NSSIG_SOA, 50 | [NSTYPE_PTR] = NSSIG_PTR, 51 | [NSTYPE_MX] = NSSIG_MX, 52 | [NSTYPE_TXT] = NSSIG_TXT, 53 | [NSTYPE_AAAA] = NSSIG_AAAA, 54 | [NSTYPE_SRV] = NSSIG_SRV, 55 | [NSTYPE_DNAME] = NSSIG_DNAME, 56 | [NSTYPE_OPT] = NSSIG_OPT, 57 | [NSTYPE_DS] = NSSIG_DS, 58 | [NSTYPE_RRSIG] = NSSIG_RRSIG, 59 | [NSTYPE_NSEC] = NSSIG_NSEC, 60 | [NSTYPE_NSEC3] = NSSIG_NSEC3, 61 | [NSTYPE_SVCB] = NSSIG_SVCB, 62 | [NSTYPE_HTTPS] = NSSIG_HTTPS, 63 | }; 64 | 65 | #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) 66 | 67 | void *add_value(struct dns_parser *parser, const void *dn, size_t len) 68 | { 69 | int i; 70 | int l; 71 | int n = parser->strcnt; 72 | 73 | for (i = 0; i < parser->strcnt; i++) { 74 | if (memcmp(parser->strptr[i], dn, len) == 0) { 75 | return parser->strptr[i]; 76 | } 77 | } 78 | 79 | if (parser->strcnt >= ARRAY_SIZE(parser->strptr)) { 80 | fprintf(stderr, "str index is full\n"); 81 | return NULL; 82 | } 83 | 84 | if (parser->lastptr == NULL) { 85 | assert (parser->strcnt == 0); 86 | parser->lastptr = parser->strtab; 87 | } 88 | 89 | l = len; 90 | if (parser->lastptr + l + 1 91 | >= parser->strtab + sizeof(parser->strtab)) { 92 | fprintf(stderr, "str buf is full\n"); 93 | return NULL; 94 | } 95 | 96 | parser->strcnt++; 97 | memcpy(parser->lastptr, dn, l + 1); 98 | parser->strptr[n] = parser->lastptr; 99 | parser->lastptr[l] = 0; 100 | parser->lastptr += (l + 1); 101 | parser->lastptr += (l + 1); 102 | return parser->strptr[n]; 103 | } 104 | 105 | const char *add_domain(struct dns_parser *parser, const char *dn) 106 | { 107 | int i; 108 | int l; 109 | int n = parser->strcnt; 110 | 111 | for (i = 0; i < parser->strcnt; i++) { 112 | if (strcmp(parser->strptr[i], dn) == 0) { 113 | return parser->strptr[i]; 114 | } 115 | } 116 | 117 | if (parser->strcnt >= ARRAY_SIZE(parser->strptr)) { 118 | fprintf(stderr, "str index is full\n"); 119 | return NULL; 120 | } 121 | 122 | if (parser->lastptr == NULL) { 123 | assert (parser->strcnt == 0); 124 | parser->lastptr = parser->strtab; 125 | } 126 | 127 | l = strlen(dn); 128 | if (parser->lastptr + l + 1 129 | >= parser->strtab + sizeof(parser->strtab)) { 130 | fprintf(stderr, "str buf is full\n"); 131 | return NULL; 132 | } 133 | 134 | parser->strcnt++; 135 | memcpy(parser->lastptr, dn, l + 1); 136 | parser->strptr[n] = parser->lastptr; 137 | parser->lastptr += (l + 1); 138 | return parser->strptr[n]; 139 | } 140 | 141 | const uint8_t * rsc_verify_handle(struct dns_resource *res, struct dns_parser *parse, const uint8_t *buf, const uint8_t *frame, size_t msglen) 142 | { 143 | int len; 144 | char dn[256]; 145 | const char *dnp = NULL; 146 | const uint8_t *dopt = buf; 147 | 148 | if (res->type < 256 && rsrc_verify_signature[res->type]) { 149 | uint8_t *valptr = res->value; 150 | uint8_t *vallimit = res->value + sizeof(res->value); 151 | const char *signature = rsrc_verify_signature[res->type]; 152 | 153 | while (*signature && dopt < &frame[msglen]) { 154 | void *btr = valptr; 155 | switch (*signature++) { 156 | case 'B': 157 | valptr += sizeof(dopt); 158 | assert(valptr < vallimit); 159 | assert(buf + res->len >= dopt); 160 | res->len > 500 && printf("res->len=%d: type=%d\n", res->len, res->type); 161 | assert(res->len <= 500); 162 | void * dotp0 = add_value(parse, dopt, buf + res->len - dopt); 163 | assert(dotp0 != NULL); 164 | memcpy(btr, &dotp0, sizeof(dotp0)); 165 | dopt += buf + res->len - dopt; 166 | break; 167 | 168 | case 'u': 169 | valptr += 4; 170 | assert(valptr < vallimit); 171 | NTOH_PTR_SET(btr, dopt, uint32_t); 172 | dopt += 4; 173 | break; 174 | 175 | case 'A': 176 | valptr += 4; 177 | assert(valptr < vallimit); 178 | memcpy(btr, dopt, sizeof(uint32_t)); 179 | dopt += 4; 180 | break; 181 | 182 | case 'q': 183 | valptr += 2; 184 | assert(valptr < vallimit); 185 | NTOH_PTR_SET(btr, dopt, uint16_t); 186 | dopt += 2; 187 | break; 188 | 189 | case 'S': 190 | valptr += sizeof(char *); 191 | assert(valptr < vallimit); 192 | 193 | len = *dopt++; 194 | snprintf(dn, len + 1, "%s", dopt); 195 | if (len > 0 && (dnp = add_domain(parse, dn))) { 196 | memcpy(btr, &dnp, sizeof(dnp)); 197 | dopt += len; 198 | break; 199 | } 200 | assert(0); 201 | 202 | case 's': 203 | valptr += sizeof(char *); 204 | assert(valptr < vallimit); 205 | len = dn_expand(frame, &frame[msglen], dopt, dn, sizeof(dn)); 206 | if (len > 0 && (dnp = add_domain(parse, dn))) { 207 | memcpy(btr, &dnp, sizeof(dnp)); 208 | dopt += len; 209 | break; 210 | } 211 | 212 | default: 213 | return &frame[msglen + 1]; 214 | } 215 | } 216 | 217 | if (dopt == buf + res->len) { 218 | return buf; 219 | } 220 | } 221 | 222 | fprintf(stderr, "unkown resource type: %d\n", res->type); 223 | return &frame[msglen + 1]; 224 | } 225 | 226 | #define GET_SHORT(v, p) v = ntohs(*(uint16_t *)p) 227 | 228 | struct dns_parser * dns_parse(struct dns_parser *parser, const uint8_t *frame, size_t len) 229 | { 230 | const struct dns_header *phead = (const struct dns_header *)frame; 231 | const uint8_t *limit = NULL; 232 | const uint8_t *dotp = NULL; 233 | 234 | char dn[256]; 235 | int complen = 0; 236 | int num = 0; 237 | 238 | int16_t nstype, nsclass; 239 | struct dns_rsc_fixed f0; 240 | struct dns_question *nsq; 241 | struct dns_resource *res; 242 | memset(parser, 0, sizeof(*parser)); 243 | 244 | parser->head.ident = phead->ident; 245 | parser->head.flags = ntohs(phead->flags); 246 | parser->head.question = ntohs(phead->question); 247 | parser->head.answer = ntohs(phead->answer); 248 | parser->head.author = ntohs(phead->author); 249 | parser->head.addon = ntohs(phead->addon); 250 | 251 | dotp = (const uint8_t *)(phead + 1); 252 | limit = (const uint8_t *)(frame + len); 253 | 254 | if (parser->head.question >= COUNTOF(parser->question) || 255 | parser->head.question == 0 || 256 | parser->head.answer + parser->head.author + parser->head.addon >= MAX_RECORD_COUNT) { 257 | fprintf(stderr, "H: %d/%d/%d/%d\n", 258 | parser->head.question, parser->head.answer, 259 | parser->head.author, parser->head.addon); 260 | return NULL; 261 | } 262 | 263 | for (num = 0; dotp < limit && num < parser->head.question; num ++) { 264 | nsq = &parser->question[num]; 265 | complen = dn_expand(frame, limit, dotp, dn, sizeof(dn)); 266 | if (complen <= 0) { 267 | return NULL; 268 | } 269 | 270 | dotp += complen; 271 | nsq->domain = add_domain(parser, dn); 272 | if (nsq->domain == NULL) { 273 | return NULL; 274 | } 275 | 276 | GET_SHORT(nsq->type, dotp); 277 | dotp += sizeof(nstype); 278 | 279 | GET_SHORT(nsq->klass, dotp); 280 | dotp += sizeof(nsclass); 281 | } 282 | 283 | if (parser->head.question != num) { 284 | return NULL; 285 | } 286 | 287 | 288 | int nrecord = 0; 289 | parser->answer = parser->records + nrecord; 290 | 291 | for (num = 0; dotp < limit && num < parser->head.answer; num ++) { 292 | res = &parser->answer[num]; 293 | 294 | complen = dn_expand(frame, limit, dotp, dn, sizeof(dn)); 295 | if (complen <= 0) { 296 | return NULL; 297 | } 298 | 299 | dotp += complen; 300 | res->domain = add_domain(parser, dn); 301 | if (res->domain == NULL) { 302 | return NULL; 303 | } 304 | 305 | get_rsc_fixed(res, &f0, dotp, sizeof(f0)); 306 | dotp += sizeof(f0); 307 | 308 | dotp = rsc_verify_handle(res, parser, dotp, frame, len); 309 | dotp += f0.len; 310 | } 311 | 312 | if (parser->head.answer != num) { 313 | return NULL; 314 | } 315 | 316 | nrecord += num; 317 | parser->author = parser->records + nrecord; 318 | for (num = 0; dotp < limit && num < parser->head.author; num ++) { 319 | res = &parser->author[num]; 320 | 321 | complen = dn_expand(frame, limit, dotp, dn, sizeof(dn)); 322 | if (complen <= 0) { 323 | return NULL; 324 | } 325 | 326 | dotp += complen; 327 | res->domain = add_domain(parser, dn); 328 | if (res->domain == NULL) { 329 | return NULL; 330 | } 331 | 332 | get_rsc_fixed(res, &f0, dotp, sizeof(f0)); 333 | dotp += sizeof(f0); 334 | 335 | dotp = rsc_verify_handle(res, parser, dotp, frame, len); 336 | dotp += f0.len; 337 | } 338 | 339 | if (parser->head.author != num) { 340 | return NULL; 341 | } 342 | 343 | nrecord += num; 344 | parser->addon = parser->records + nrecord; 345 | for (num = 0; dotp < limit && num < parser->head.addon; num ++) { 346 | res = &parser->addon[num]; 347 | 348 | complen = dn_expand(frame, limit, dotp, dn, sizeof(dn)); 349 | if (complen <= 0) { 350 | return NULL; 351 | } 352 | 353 | dotp += complen; 354 | res->domain = add_domain(parser, dn); 355 | if (res->domain == NULL) { 356 | return NULL; 357 | } 358 | 359 | get_rsc_fixed(res, &f0, dotp, sizeof(f0)); 360 | dotp += sizeof(f0); 361 | 362 | dotp = rsc_verify_handle(res, parser, dotp, frame, len); 363 | dotp += f0.len; 364 | 365 | } 366 | 367 | if (parser->head.addon != num) { 368 | return NULL; 369 | } 370 | 371 | if (dotp > limit) { 372 | #if 0 373 | uint8_t *test = (uint8_t*)frame; 374 | for (int i = 0; i < len; i++) 375 | fprintf(stderr, (i&0xf)? "%02x ": "\n%02x ", test[i]); 376 | #endif 377 | return NULL; 378 | } 379 | 380 | return parser; 381 | } 382 | 383 | uint8_t * dn_put_domain(uint8_t *buf, uint8_t *limit, const char *domain, uint8_t **ptr, size_t count) 384 | { 385 | int ret; 386 | 387 | if (buf < limit) { 388 | domain = domain == NULL? "": domain; 389 | ret = dn_comp(domain, buf, limit - buf, ptr, ptr + count); 390 | if (ret > 0) { 391 | return buf + ret; 392 | } 393 | } 394 | 395 | return limit; 396 | } 397 | 398 | uint8_t * dn_put_short(uint8_t *buf, uint8_t *limit, uint16_t val) 399 | { 400 | if (buf + sizeof(val) < limit) { 401 | val = htons(val); 402 | memcpy(buf, &val, sizeof(val)); 403 | return buf + sizeof(val); 404 | } 405 | 406 | return limit; 407 | } 408 | 409 | uint8_t * dn_put_long(uint8_t *buf, uint8_t *limit, uint32_t val) 410 | { 411 | if (buf + sizeof(val) < limit) { 412 | val = htonl(val); 413 | memcpy(buf, &val, sizeof(val)); 414 | return buf + sizeof(val); 415 | } 416 | 417 | return limit; 418 | } 419 | 420 | uint8_t * dn_put_resource(uint8_t *dotp, uint8_t *limit, const struct dns_resource *res, struct dns_parser *parse) 421 | { 422 | int ret; 423 | int len; 424 | uint8_t *mark = NULL; 425 | 426 | if (res->type < 256 && rsrc_verify_signature[res->type]) { 427 | const uint8_t *right_val = res->value; 428 | const char *signature = rsrc_verify_signature[res->type]; 429 | 430 | ret = dn_comp(res->domain, dotp, limit - dotp, parse->comptr, parse->comptr + ARRAY_SIZE(parse->comptr)); 431 | if (ret <= 0 || dotp + ret >= limit) { 432 | return limit; 433 | } 434 | 435 | dotp += ret; 436 | dotp = dn_put_short(dotp, limit, res->type); 437 | dotp = dn_put_short(dotp, limit, res->klass); 438 | dotp = dn_put_long(dotp, limit, res->ttl); 439 | 440 | mark = dotp; 441 | dotp = dn_put_short(dotp, limit, res->len); 442 | 443 | while (*signature && dotp < limit) { 444 | union dns_res_value * drvp = (union dns_res_value *)right_val; 445 | switch (*signature++) { 446 | case 'B': 447 | memcpy(dotp, drvp->ptr, res->len); 448 | right_val += sizeof(void *); 449 | dotp += res->len; 450 | break; 451 | 452 | case 'u': 453 | NTOH_PTR_SET(dotp, &drvp->u32, uint32_t); 454 | right_val += 4; 455 | dotp += 4; 456 | break; 457 | 458 | case 'A': 459 | memcpy(dotp, &drvp->u32, sizeof(uint32_t)); 460 | right_val += 4; 461 | dotp += 4; 462 | break; 463 | 464 | case 'q': 465 | NTOH_PTR_SET(dotp, &drvp->u16, uint16_t); 466 | right_val += 2; 467 | dotp += 2; 468 | break; 469 | 470 | case 'S': 471 | len = strlen(drvp->str); 472 | if (len < 256 && dotp + len + 4 < limit) { 473 | right_val += sizeof(void *); 474 | *dotp++ = strlen(drvp->str); 475 | memcpy(dotp, drvp->str, len); 476 | dotp += len; 477 | break; 478 | } 479 | break; 480 | 481 | case 's': 482 | ret = dn_comp(drvp->str, dotp, limit - dotp, parse->comptr, parse->comptr + ARRAY_SIZE(parse->comptr)); 483 | if (ret > 0 && dotp + ret + 4 < limit) { 484 | right_val += sizeof(void *); 485 | dotp += ret; 486 | break; 487 | } 488 | 489 | default: 490 | return limit; 491 | } 492 | } 493 | 494 | if (dotp < limit && mark + res->len + 2 != dotp) { 495 | dn_put_short(mark, limit, dotp - mark - 2); 496 | } 497 | 498 | return dotp; 499 | } 500 | 501 | return limit; 502 | } 503 | 504 | int dns_build(struct dns_parser *parser, uint8_t *frame, size_t len) 505 | { 506 | struct dns_header *phead = (struct dns_header *)frame; 507 | uint8_t *dotp = NULL; 508 | int num = 0; 509 | 510 | struct dns_resource *res; 511 | struct dns_question *nsq; 512 | 513 | uint8_t *limit = &frame[len]; 514 | 515 | phead->ident = parser->head.ident; 516 | phead->flags = htons(parser->head.flags); 517 | phead->question = htons(parser->head.question); 518 | phead->answer = htons(parser->head.answer); 519 | phead->author = htons(parser->head.author); 520 | phead->addon = htons(parser->head.addon); 521 | 522 | dotp = (uint8_t *)(phead + 1); 523 | memset(parser->comptr, 0, sizeof(parser->comptr)); 524 | parser->comptr[0] = frame; 525 | 526 | assert(parser->head.question < COUNTOF(parser->question)); 527 | for (num = 0; dotp < limit && num < parser->head.question; num ++) { 528 | nsq = &parser->question[num]; 529 | dotp = dn_put_domain(dotp, limit, nsq->domain, parser->comptr, ARRAY_SIZE(parser->comptr)); 530 | dotp = dn_put_short(dotp, limit, nsq->type); 531 | dotp = dn_put_short(dotp, limit, nsq->klass); 532 | } 533 | 534 | assert(parser->head.answer + parser->head.addon + parser->head.author < MAX_RECORD_COUNT); 535 | for (num = 0; dotp < limit && num < parser->head.answer; num ++) { 536 | res = &parser->answer[num]; 537 | char ** cname = (char **)res->value; 538 | if (res->type == NSTYPE_CNAME && res->domain == *cname) { 539 | phead->answer = htons(htons(phead->answer) - 1); 540 | continue; 541 | } 542 | 543 | dotp = dn_put_resource(dotp, limit, res, parser); 544 | } 545 | 546 | for (num = 0; dotp < limit && num < parser->head.author; num ++) { 547 | res = &parser->author[num]; 548 | dotp = dn_put_resource(dotp, limit, res, parser); 549 | } 550 | 551 | for (num = 0; dotp < limit && num < parser->head.addon; num ++) { 552 | res = &parser->addon[num]; 553 | dotp = dn_put_resource(dotp, limit, res, parser); 554 | } 555 | 556 | if (dotp >= limit) { 557 | return -1; 558 | } 559 | 560 | return dotp - frame; 561 | } 562 | 563 | static int nstrtab = 0; 564 | static char *pstrtab[10240]; 565 | static char _strbuf[1024 * 1024]; 566 | static char *lastsym = _strbuf + 1; 567 | 568 | extern int cache_verify(const void *ptr) 569 | { 570 | assert(ptr >= _strbuf); 571 | assert(ptr < lastsym); 572 | return 0; 573 | } 574 | 575 | static int cache_bound(const char *domain, int *lowp, int *highp) 576 | { 577 | int cmp = 1, mid = -1; 578 | int low = 0, high = nstrtab -1; 579 | 580 | while (low <= high) { 581 | mid = (low + high) >> 1; 582 | 583 | cmp = strcasecmp(pstrtab[mid], domain); 584 | if (cmp == 0) 585 | break; 586 | 587 | if (cmp > 0) { 588 | high = mid - 1; 589 | } else if (cmp < 0) { 590 | low = mid + 1; 591 | } else { 592 | assert(0); 593 | } 594 | } 595 | 596 | if (highp) 597 | *highp = high; 598 | 599 | if (lowp) 600 | *lowp = low; 601 | 602 | return cmp == 0? mid: -1; 603 | } 604 | 605 | const char *cache_get_name(const char *domain) 606 | { 607 | int index = -1; 608 | 609 | if (*domain == 0) { 610 | fprintf(stderr, "start %p end %p\n", _strbuf, _strbuf + sizeof(_strbuf)); 611 | return _strbuf; 612 | } else { 613 | index = cache_bound(domain, NULL, NULL); 614 | } 615 | 616 | return (index != -1)? pstrtab[index]: NULL; 617 | } 618 | 619 | static const char *cache_add_domain(const char *domain) 620 | { 621 | int low, high, n; 622 | char *self = NULL; 623 | char *limit = _strbuf + sizeof(_strbuf); 624 | 625 | if (*domain == 0) { 626 | // fprintf(stderr, "dummy\n"); 627 | return _strbuf; 628 | } 629 | 630 | if (cache_bound(domain, &low, &high) != -1) { 631 | return pstrtab[(low + high) >> 1]; 632 | } 633 | 634 | if (lastsym >= limit || nstrtab >= 10240) { 635 | assert(0); 636 | return NULL; 637 | } 638 | 639 | n = snprintf(lastsym, limit - lastsym, "%s", domain); 640 | self = lastsym; 641 | lastsym = &lastsym[n + 1]; 642 | 643 | memmove(pstrtab + high + 2, pstrtab + high + 1, (nstrtab - high - 1) * sizeof(char *)); 644 | pstrtab[high + 1] = self; 645 | 646 | nstrtab++; 647 | return self; 648 | } 649 | 650 | struct dns_mx { 651 | uint16_t prio; 652 | const char *server; 653 | } __attribute__((packed)); 654 | 655 | struct dns_soa { 656 | const char *server; 657 | const char *email; 658 | } __attribute__((packed)); 659 | 660 | struct dns_srv { 661 | uint16_t prio; 662 | uint16_t weight; 663 | uint16_t key; 664 | const char *server; 665 | } __attribute__((packed)); 666 | 667 | int cache_put(struct dns_resource *ress, size_t count) 668 | { 669 | int i; 670 | const char ** server; 671 | struct dns_resource *res; 672 | 673 | struct dns_mx *mx; 674 | struct dns_srv *srv; 675 | struct dns_soa *soa; 676 | 677 | for (i = 0; i < count; i++) { 678 | res = &ress[i]; 679 | res->domain = cache_add_domain(res->domain); 680 | switch (res->type) { 681 | case NSTYPE_CNAME: 682 | case NSTYPE_DNAME: 683 | case NSTYPE_PTR: 684 | case NSTYPE_NS: 685 | server = (const char **)res->value; 686 | *server = cache_add_domain(*server); 687 | break; 688 | 689 | case NSTYPE_MX: 690 | mx = (struct dns_mx *)res->value; 691 | mx->server = cache_add_domain(mx->server); 692 | break; 693 | 694 | case NSTYPE_SOA: 695 | soa = (struct dns_soa *)res->value; 696 | soa->server = cache_add_domain(soa->server); 697 | soa->email = cache_add_domain(soa->email); 698 | break; 699 | 700 | case NSTYPE_SRV: 701 | srv = (struct dns_srv *)res->value; 702 | srv->server = cache_add_domain(srv->server); 703 | break; 704 | 705 | default: 706 | break; 707 | } 708 | } 709 | 710 | return 0; 711 | } 712 | 713 | #if 0 714 | static int nstrtab = 0; 715 | static char *pstrtab[10240]; 716 | static char _strbuf[1024 * 1024]; 717 | static char *lastsym = _strbuf; 718 | 719 | static char *cache_lookup_domain(const char *domain) 720 | { 721 | int i; 722 | 723 | for (i = 0; i < nstrtab; i++) { 724 | if (strcasecmp(domain, pstrtab[i]) == 0) 725 | return pstrtab[i]; 726 | } 727 | 728 | return NULL; 729 | } 730 | 731 | static const char *cache_add_domain(const char *domain) 732 | { 733 | int i, n; 734 | char *self = NULL; 735 | char *limit = _strbuf + sizeof(_strbuf); 736 | 737 | for (i = 0; i < nstrtab; i++) { 738 | if (strcasecmp(domain, pstrtab[i]) == 0) 739 | return pstrtab[i]; 740 | } 741 | 742 | if (lastsym == limit || nstrtab >= 10240) { 743 | assert(0); 744 | return NULL; 745 | } 746 | 747 | n = snprintf(lastsym, limit - lastsym, "%s", domain); 748 | pstrtab[nstrtab++] = self = lastsym; 749 | lastsym += (n + 1); 750 | 751 | return self; 752 | } 753 | #endif 754 | 755 | int move_to_cache(struct dns_resource *ress, size_t count) 756 | { 757 | int i; 758 | const char ** server; 759 | struct dns_resource *res; 760 | 761 | for (i = 0; i < count; i++) { 762 | res = &ress[i]; 763 | res->domain = cache_add_domain(res->domain); 764 | switch (res->type) { 765 | case NSTYPE_CNAME: 766 | case NSTYPE_NS: 767 | server = (const char **)res->value; 768 | *server = cache_add_domain(*server); 769 | break; 770 | 771 | default: 772 | break; 773 | } 774 | } 775 | 776 | return 0; 777 | } 778 | -------------------------------------------------------------------------------- /dns_res_trd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include "dnsproto.h" 19 | #include "subnet_api.h" 20 | 21 | #define LOG_DEBUG(fmt, args...) fprintf(stderr, fmt"\n", ##args) 22 | 23 | #ifdef WIN32 24 | #include 25 | #else 26 | #include 27 | #include 28 | #include 29 | #include 30 | #define closesocket close 31 | #endif 32 | 33 | static char addrbuf[256]; 34 | #define ntop6(addr) inet_ntop(AF_INET6, &addr, addrbuf, sizeof(addrbuf)) 35 | 36 | struct dns_context { 37 | int outfd; 38 | int sockfd; 39 | 40 | socklen_t dnslen; 41 | struct sockaddr *dnsaddr; 42 | struct sockaddr *ecsaddr; 43 | }; 44 | 45 | struct zip_parser { 46 | char buf[1500]; 47 | int len; 48 | }; 49 | 50 | struct dns_query_context { 51 | int is_china_domain; 52 | int is_nonchina_domain; 53 | struct sockaddr_in6 from; 54 | struct zip_parser parser, ecs_parser, def_parser; 55 | }; 56 | 57 | static struct dns_query_context _orig_list[0x1000]; 58 | 59 | static int dns_parser_copy(struct dns_parser *dst, struct dns_parser *src) 60 | { 61 | static uint8_t _qc_hold[2048]; 62 | size_t len = dns_build(src, _qc_hold, sizeof(_qc_hold)); 63 | return dns_parse(dst, _qc_hold, len) == NULL; 64 | } 65 | 66 | struct subnet_info { 67 | uint16_t tag; // 0x0008 68 | uint16_t len; 69 | uint16_t family; 70 | uint8_t source_netmask; 71 | uint8_t scope_netmask; 72 | uint8_t addr[16]; 73 | }; 74 | 75 | #define NS_IPV6 2 76 | #define NS_IPV4 1 77 | 78 | // china mobile 117.143.102.0/24 79 | const static struct subnet_info subnet4_data = { 80 | 0x08, sizeof(subnet4_data), NS_IPV4, 24, 0, {117, 143, 102, 0} 81 | }; 82 | 83 | // vn he-ipv6 prefix 2001:470:35:639::/56 84 | const static struct subnet_info subnet6_data = { 85 | 0x08, sizeof(subnet6_data), NS_IPV6, 56, 53, {0x20, 0x01, 0x04, 0x70, 0x00, 0x35, 0x06, 0x39} 86 | }; 87 | 88 | static int contains_subnet(struct dns_parser *p0) 89 | { 90 | struct dns_resource *res = NULL; 91 | struct subnet_info *info = NULL; 92 | 93 | for (int i = 0; i < p0->head.addon; i++) { 94 | res = &p0->addon[i]; 95 | if (res->type != NSTYPE_OPT) { 96 | continue; 97 | } 98 | 99 | if (res->domain == NULL || *res->domain == 0) { 100 | size_t len = res->len; 101 | const uint8_t * valp = *(const uint8_t **)res->value; 102 | struct tagheader {uint16_t tag; uint16_t len; } tag0; 103 | 104 | while (len > sizeof(tag0)) { 105 | memcpy(&tag0, valp, sizeof(tag0)); 106 | if (len < sizeof(tag0) + htons(tag0.len)) break; 107 | const uint8_t *hold = valp; 108 | valp += sizeof(tag0) + htons(tag0.len); 109 | len -= (sizeof(tag0) + htons(tag0.len)); 110 | if (tag0.tag == htons(0x0008)) { 111 | info = (struct subnet_info *)hold; 112 | LOG_DEBUG("taglen=%d family=%d", htons(tag0.len), htons(info->family)); 113 | return htons(info->family) == NS_IPV4; 114 | } 115 | } 116 | } 117 | } 118 | 119 | return 0; 120 | } 121 | 122 | static int add_client_subnet(struct dns_parser *p0, uint8_t *optbuf, const struct subnet_info *info) 123 | { 124 | #ifndef DISABLE_SUBNET 125 | 126 | int have_edns = 0; 127 | struct dns_resource *res = NULL; 128 | struct subnet_info info0 = *info; 129 | 130 | int prefixlen = info->source_netmask;//+ info->scope_netmask; 131 | size_t subnet_len = 8 + ((7 + prefixlen) >> 3); 132 | 133 | info0.tag = htons(info->tag); 134 | info0.family = htons(info->family); 135 | info0.len = htons(4 + ((7 + prefixlen) >> 3)); 136 | 137 | for (int i = 0; i < p0->head.addon; i++) { 138 | res = &p0->addon[i]; 139 | if (res->type != NSTYPE_OPT) { 140 | continue; 141 | } 142 | 143 | if (res->domain == NULL || *res->domain == 0) { 144 | size_t len = res->len; 145 | const uint8_t * valp = *(const uint8_t **)res->value; 146 | struct tagheader {uint16_t tag; uint16_t len; } tag0; 147 | 148 | while (len > sizeof(tag0)) { 149 | memcpy(&tag0, valp, sizeof(tag0)); 150 | if (len < sizeof(tag0) + htons(tag0.len)) break; 151 | const uint8_t *hold = valp; 152 | valp += sizeof(tag0) + htons(tag0.len); 153 | len -= (sizeof(tag0) + htons(tag0.len)); 154 | if (tag0.tag == htons(0x0008)) { 155 | const uint8_t * valp0 = *(const uint8_t **)res->value; 156 | memcpy(optbuf, valp0, (hold - valp0)); 157 | memcpy(optbuf + (hold - valp0), valp, len); 158 | 159 | memcpy(optbuf + (hold - valp0) + len, &info0, subnet_len); 160 | *(void **)res->value = optbuf; 161 | res->len = len + (hold - valp0) + subnet_len; 162 | have_edns = 1; 163 | break; 164 | } 165 | } 166 | 167 | if (have_edns == 0) { 168 | const uint8_t * valp = *(const uint8_t **)res->value; 169 | 170 | memcpy(optbuf, &info0, subnet_len); 171 | memcpy(optbuf + subnet_len, valp, res->len); 172 | 173 | *(void **)res->value = optbuf; 174 | res->len += subnet_len; 175 | have_edns = 1; 176 | } 177 | } 178 | } 179 | 180 | if (p0->head.addon < MAX_RECORD_COUNT && have_edns == 0) { 181 | res = &p0->addon[p0->head.addon++]; 182 | 183 | res->domain = ""; 184 | res->klass = 0x1000; 185 | res->type = NSTYPE_OPT; 186 | res->ttl = 0; 187 | res->len = subnet_len; 188 | memcpy(optbuf, &info0, subnet_len); 189 | *(const void **)res->value = optbuf; 190 | } 191 | #endif 192 | 193 | return 0; 194 | } 195 | 196 | static int dns_contains(const char *domain) 197 | { 198 | int i; 199 | const char *_tld0[] = { 200 | "ten.", "ude.", "oc.", "gro.", "moc.", "vog.", NULL 201 | }; 202 | const char *_tld1[] = { 203 | "net.", "edu.", "co.", "org.", "com.", "gov.", NULL 204 | }; 205 | 206 | (void)_tld1; 207 | for (i = 0; _tld0[i]; i++) { 208 | if (strncasecmp(domain, _tld0[i], 4) == 0) { 209 | return 1; 210 | } 211 | } 212 | 213 | if (strncasecmp(domain, "oc.", 3) == 0) { 214 | return 1; 215 | } 216 | 217 | if (strncmp(domain, "co.", 3) == 0) { 218 | return 1; 219 | } 220 | 221 | return 0; 222 | } 223 | 224 | static int dns_unwrap(struct dns_parser *p1) 225 | { 226 | char *domain = NULL; 227 | struct dns_question *que, *que1; 228 | 229 | que = &p1->question[0]; 230 | que1 = &p1->question[1]; 231 | *que1 = *que; 232 | 233 | int ndot = 0; 234 | char *limit, *optp; 235 | char *dots[8], title[256]; 236 | 237 | LOG_DEBUG("suffixes: %s", que->domain); 238 | if (que->domain == NULL) { 239 | return -1; 240 | } 241 | 242 | title[sizeof(title) -1] = 0; 243 | strncpy(title, que->domain, sizeof(title) -1); 244 | 245 | dots[ndot++] = title; 246 | for (domain = title; *domain; domain++) 247 | if (*domain == '.') dots[ndot++&0x7] = domain + 1; 248 | 249 | // mail.oogleg.moc.cooltail.com 250 | if (ndot < 3) { 251 | LOG_DEBUG("dns_unwrap failure"); 252 | return -1; 253 | } 254 | 255 | ndot -= 2; 256 | dots[ndot & 0x7][-1] = 0; 257 | LOG_DEBUG("suffixes: %s %s", dots[ndot & 0x7], title); 258 | 259 | assert(ndot > 0); 260 | limit = dots[ndot & 0x7] -2; 261 | ndot--; 262 | optp = dots[ndot & 0x7]; 263 | 264 | int cc = 0; 265 | if (ndot < 1) { 266 | LOG_DEBUG("dns_unwrap warning %s", title); 267 | que1->domain = add_domain(p1, title); 268 | p1->head.question++; 269 | return 0; 270 | } 271 | 272 | if (optp + 1 == limit) { 273 | limit = dots[ndot & 0x7] -2; 274 | ndot--; 275 | optp = dots[ndot & 0x7]; 276 | cc = 1; 277 | } 278 | 279 | if (cc == 0 || dns_contains(optp)) { 280 | LOG_DEBUG("o %s %s %s", title, optp, limit); 281 | for (; *optp && optp < limit; optp++) { 282 | char t = *optp; 283 | *optp = *limit; 284 | *limit-- = t; 285 | } 286 | 287 | LOG_DEBUG("o %s", title); 288 | if (ndot < 1) { 289 | LOG_DEBUG("dns_unwrap ork %s", title); 290 | que1->domain = add_domain(p1, title); 291 | p1->head.question++; 292 | return 0; 293 | } 294 | 295 | LOG_DEBUG("xx dns_unwrap %s %d", title, cc); 296 | limit = dots[ndot & 0x7] -2; 297 | ndot--; 298 | optp = dots[ndot & 0x7]; 299 | } 300 | 301 | #if 0 302 | if (ndot < 1) { 303 | LOG_DEBUG("dns_unwrap warning %s", title); 304 | que1->domain = add_domain(p1, title); 305 | p1->head.question++; 306 | return 0; 307 | } 308 | #endif 309 | 310 | char t = *limit; 311 | LOG_DEBUG("zz dns_unwrap %s %c %s", title, t, optp); 312 | memmove(optp + 1, optp, limit - optp); 313 | *optp = t; 314 | 315 | LOG_DEBUG("zz dns_unwrap %s %d", title, cc); 316 | que1->domain = add_domain(p1, title); 317 | p1->head.question++; 318 | return 0; 319 | } 320 | 321 | static int dns_sendto(int outfd, struct dns_parser *parser, const struct sockaddr *to, size_t tolen) 322 | { 323 | ssize_t len; 324 | uint8_t _hold[2048]; 325 | 326 | len = dns_build(parser, _hold, sizeof(_hold)); 327 | 328 | const struct sockaddr_in6 *inp = (const struct sockaddr_in6 *)to; 329 | LOG_DEBUG("dns_build bytes %ld %d %d %s", len, inp->sin6_family, htons(inp->sin6_port), ntop6(inp->sin6_addr)); 330 | if (len != -1) 331 | len = sendto(outfd, _hold, len, 0, to, tolen); 332 | else 333 | LOG_DEBUG("dns_build %ld", len); 334 | 335 | return len; 336 | } 337 | 338 | int do_dns_forward(struct dns_context *ctx, void *buf, int count, struct sockaddr_in6 *from) 339 | { 340 | struct dns_parser p0; 341 | struct dns_parser *pp; 342 | 343 | pp = dns_parse(&p0, buf, count); 344 | if (pp == NULL) { 345 | LOG_DEBUG("do_dns_forward parse failure"); 346 | return 0; 347 | } 348 | 349 | if (p0.head.flags & 0x8000) { 350 | LOG_DEBUG("FROM: %s this is not query", "nothing"); 351 | return -1; 352 | } 353 | 354 | int retval = 0; 355 | int offset = (p0.head.ident & 0xfff); 356 | 357 | struct dns_parser *p1 = NULL; 358 | struct dns_parser parser = {}; 359 | struct dns_query_context *qc = &_orig_list[offset]; 360 | memset(qc, 0, sizeof(*qc)); 361 | qc->from = *from; 362 | 363 | dns_parser_copy(&parser, &p0); 364 | p1 = &parser; 365 | if (dns_unwrap(p1) == -1) { 366 | LOG_DEBUG("FROM: %s this is not good", p1->question[0].domain); 367 | return -1; 368 | } 369 | p0.question[0] = p1->question[1]; 370 | qc->parser.len = dns_build(&parser, qc->parser.buf, sizeof(qc->parser.buf)); 371 | assert(qc->parser.len > 0); 372 | 373 | uint8_t optbuf[256]; 374 | add_client_subnet(&p0, optbuf, &subnet4_data); 375 | 376 | p0.head.flags |= NSFLAG_RD; 377 | retval = dns_sendto(ctx->outfd, &p0, ctx->ecsaddr, ctx->dnslen); 378 | if (retval == -1) { 379 | LOG_DEBUG("dns_sendto failure"); 380 | return 0; 381 | } 382 | 383 | if (p0.head.addon > 0 384 | && p0.addon[0].len > 0 385 | && p0.addon[0].type == NSTYPE_OPT) 386 | p0.addon[0].len = 0; 387 | #if 0 388 | if (p0.question[0].type == NSTYPE_AAAA) 389 | add_client_subnet(&p0, optbuf, &subnet6_data); 390 | #endif 391 | 392 | p0.head.ident += 0x1000; 393 | retval = dns_sendto(ctx->outfd, &p0, ctx->dnsaddr, ctx->dnslen); 394 | if (retval == -1) { 395 | LOG_DEBUG("dns_sendto failure: %s %p", strerror(errno), ctx->dnsaddr); 396 | return 0; 397 | } 398 | 399 | if (strchr(p1->question[1].domain, '_') != NULL) { 400 | LOG_DEBUG("dns_sendto contains _"); 401 | qc->is_nonchina_domain = 1; 402 | return 0; 403 | } 404 | 405 | char _domain[256]; 406 | snprintf(_domain, sizeof(_domain), "_.%s", p1->question[1].domain); 407 | p0.question[0].domain = add_domain(&p0, _domain); 408 | p0.question[0].type = NSTYPE_TXT; 409 | p0.head.addon = 0; 410 | p0.head.answer = 0; 411 | p0.head.author = 0; 412 | p0.head.question = 1; 413 | 414 | p0.head.ident += 0x1000; 415 | retval = dns_sendto(ctx->outfd, &p0, ctx->dnsaddr, ctx->dnslen); 416 | if (retval == -1) { 417 | LOG_DEBUG("dns_sendto failure"); 418 | return 0; 419 | } 420 | 421 | return 0; 422 | } 423 | 424 | struct dns_soa { 425 | const char *nameserver; 426 | const char *email; 427 | }; 428 | 429 | static int dump_resource(const char *title, struct dns_resource *res) 430 | { 431 | 432 | char inet6[256]; 433 | if (res->type == NSTYPE_TXT) { 434 | LOG_DEBUG("%s %s TXT", title, res->domain); 435 | } else if (res->type == NSTYPE_A) { 436 | LOG_DEBUG("%s %s A %s", title, res->domain, inet_ntoa(*(struct in_addr *)res->value)); 437 | } else if (res->type == NSTYPE_NS) { 438 | LOG_DEBUG("%s %s NS %s", title, res->domain, *(const char **)res->value); 439 | } else if (res->type == NSTYPE_SRV) { 440 | LOG_DEBUG("%s %s SRV %p", title, res->domain, *(const char **)res->value); 441 | } else if (res->type == NSTYPE_SOA) { 442 | LOG_DEBUG("%s %s SOA %s", title, res->domain, *(const char **)res->value); 443 | } else if (res->type == NSTYPE_AAAA) { 444 | LOG_DEBUG("%s %s AAAA %s", title, res->domain, inet_ntop(AF_INET6, res->value, inet6, sizeof(inet6))); 445 | } else if (res->type == NSTYPE_CNAME) { 446 | LOG_DEBUG("%s %s CNAME %s", title, res->domain, *(const char **)res->value); 447 | } else { 448 | LOG_DEBUG("%s %s UNKOWN %d", title, res->domain, res->type); 449 | } 450 | 451 | return 0; 452 | } 453 | 454 | int dns_answer_diff(struct dns_parser *p1, struct dns_parser *p2) 455 | { 456 | int i, j = 0, d = 0; 457 | int type = NSTYPE_CNAME; 458 | struct dns_resource *f, *t; 459 | 460 | int ncname = 0; 461 | for (i = 0; i < p1->head.answer && d == 0; i++) { 462 | f = &p1->answer[i]; 463 | if (f->type != type) { 464 | continue; 465 | } 466 | 467 | while (j < p2->head.answer && 468 | p2->answer[j].type != type) { 469 | j++; 470 | } 471 | 472 | if (j >= p2->head.answer) { 473 | return 1; 474 | } 475 | 476 | t = &p2->answer[j++]; 477 | d = !!strcasecmp(*(const char **)f->value, *(const char **)t->value); 478 | LOG_DEBUG("alias: %s %s %d %d", *(const char **)f->value, *(const char **)t->value, ncname, d); 479 | ncname ++; 480 | } 481 | 482 | while (j < p2->head.answer && d == 0) { 483 | t = &p2->answer[j++]; 484 | if (t->type == type) return 1; 485 | j++; 486 | } 487 | 488 | return d || ncname < 2; 489 | } 490 | 491 | static const char * get_suffix(const char *domain, int ndot) 492 | { 493 | assert (ndot < 256); 494 | 495 | int offset = 0; 496 | const char *dots[256] = {}; 497 | 498 | dots[offset] = domain; 499 | for (; *domain; domain++) { 500 | switch(*domain) { 501 | case '.': 502 | if (domain > dots[offset]) offset++; 503 | dots[offset] = domain + 1; 504 | break; 505 | 506 | default: 507 | break; 508 | } 509 | } 510 | 511 | if (domain > dots[offset]) offset++; 512 | 513 | if (offset > ndot) { 514 | LOG_DEBUG("offset = %d ndot=%d %s", offset, ndot, dots[offset - ndot]); 515 | return dots[offset - ndot]; 516 | } 517 | 518 | return 0; 519 | } 520 | 521 | static subnet_t *lookupRoute(const void *block, int type) 522 | { 523 | uint64_t network; 524 | 525 | memcpy(&network, block, sizeof(network)); 526 | if (type == NSTYPE_AAAA) { 527 | return lookupRoute6(htonll(network)); 528 | } 529 | 530 | if (type == NSTYPE_A) { 531 | return lookupRoute4(htonll(network)); 532 | } 533 | 534 | return NULL; 535 | } 536 | 537 | int cdn_is_akamai(const char *domain) 538 | { 539 | // www.apple.com.edgekey.net.globalredir.akadns.net. 3388 IN CNAME e6858.dscx.akamaiedge.net. 540 | // e6858.dscx.akamaiedge.net. 20 IN A 23.59.247.25 541 | 542 | const char *suffixies = strstr(domain, "akamaiedge.net"); 543 | if (suffixies && strcmp(suffixies, "akamaiedge.net") == 0) { 544 | return 1; 545 | } 546 | 547 | suffixies = strstr(domain, "akadns.net"); 548 | if (suffixies && strcmp(suffixies, "akadns.net") == 0) { 549 | return 1; 550 | } 551 | 552 | suffixies = strstr(domain, "akamai.net"); 553 | if (suffixies && strcmp(suffixies, "akamai.net") == 0) { 554 | return 1; 555 | } 556 | 557 | return 0; 558 | } 559 | 560 | int do_dns_backward(struct dns_context *ctx, void *buf, int count, struct sockaddr_in6 *from) 561 | { 562 | struct dns_parser p0; 563 | struct dns_parser *pp; 564 | struct dns_resource *res; 565 | 566 | LOG_DEBUG("count %d", count); 567 | pp = dns_parse(&p0, buf, count); 568 | if (pp == NULL) { 569 | LOG_DEBUG("do_dns_backward parse failure"); 570 | return 0; 571 | } 572 | 573 | if (~p0.head.flags & 0x8000) { 574 | LOG_DEBUG("FROM: %s this is not response", ntop6(from->sin6_addr)); 575 | return -1; 576 | } 577 | 578 | int i; 579 | int offset = (p0.head.ident & 0xfff); 580 | struct dns_query_context *qc = &_orig_list[offset]; 581 | struct dns_parser parser, def_parser, ecs_parser; 582 | 583 | if (p0.question[0].type == NSTYPE_PTR) { 584 | p0.head.flags &= ~NSFLAG_RCODE; 585 | qc->is_china_domain = 1; 586 | } 587 | 588 | assert (p0.question[0].domain); 589 | dns_parse(&parser, qc->parser.buf, qc->parser.len); 590 | if (strcasecmp(parser.question[1].domain, p0.question[0].domain)) { 591 | struct dns_parser p1 = {}; 592 | const char *domain = p0.question[0].domain; 593 | LOG_DEBUG("query soa: %s %s ", parser.question[1].domain, p0.question[0].domain); 594 | 595 | for (i = 0; i < p0.head.answer; i++) { 596 | res = &p0.answer[i]; 597 | dump_resource("answer ", res); 598 | } 599 | 600 | for (i = 0; i < p0.head.author; i++) { 601 | res = &p0.author[i]; 602 | dump_resource("author ", res); 603 | } 604 | 605 | for (i = 0; i < p0.head.addon; i++) { 606 | res = &p0.addon[i]; 607 | dump_resource("addon ", res); 608 | } 609 | 610 | if (*domain == '_' && domain[1] == '.') { 611 | const char *soa_nameserver = NULL; 612 | for (i = 0; i < p0.head.author; i++) { 613 | res = &p0.author[i]; 614 | if (res->type == NSTYPE_SOA) { 615 | soa_nameserver = *(const char **)res->value; 616 | if (cdn_is_akamai(soa_nameserver)) 617 | qc->is_china_domain = 1; 618 | break; 619 | } 620 | } 621 | p1.head.ident = p0.head.ident; 622 | p1.head.flags = NSFLAG_RD; 623 | p1.head.question = 1; 624 | p1.question[0].type = NSTYPE_A; 625 | p1.question[0].klass = NSCLASS_INET; 626 | 627 | if (soa_nameserver && strcasecmp(soa_nameserver, parser.question[1].domain)) { 628 | p1.head.ident += 0x1000; 629 | p1.question[0].domain = add_domain(&p1, soa_nameserver); 630 | dns_sendto(ctx->outfd, &p1, ctx->dnsaddr, ctx->dnslen); 631 | p1.question[0].type = NSTYPE_AAAA; 632 | dns_sendto(ctx->outfd, &p1, ctx->dnsaddr, ctx->dnslen); 633 | return 0; 634 | } 635 | } 636 | 637 | for (i = 0; i < p0.head.answer; i++) { 638 | res = &p0.answer[i]; 639 | if (res->type == NSTYPE_CNAME && (cdn_is_akamai(res->domain) 640 | || cdn_is_akamai(*(const char **)res->value))) { 641 | qc->is_china_domain = 1; 642 | break; 643 | } 644 | 645 | if (res->type == NSTYPE_A || res->type == NSTYPE_AAAA) { 646 | if (NULL == lookupRoute(res->value, res->type)) { 647 | qc->is_china_domain = 1; 648 | break; 649 | } else { 650 | qc->is_nonchina_domain = 1; 651 | } 652 | } 653 | } 654 | 655 | if (qc->is_china_domain == 0 && p0.head.answer > 0) { 656 | qc->is_nonchina_domain = 1; 657 | } 658 | 659 | dns_parse(&def_parser, qc->def_parser.buf, qc->def_parser.len); 660 | dns_parse(&ecs_parser, qc->ecs_parser.buf, qc->ecs_parser.len); 661 | goto check_flush; 662 | } 663 | 664 | if (contains_subnet(&p0)) { 665 | dns_parser_copy(&ecs_parser, &p0); 666 | dns_parse(&def_parser, qc->def_parser.buf, qc->def_parser.len); 667 | } else { 668 | dns_parse(&ecs_parser, qc->ecs_parser.buf, qc->ecs_parser.len); 669 | dns_parser_copy(&def_parser, &p0); 670 | } 671 | 672 | for (i = 0; i < p0.head.answer; i++) { 673 | res = &p0.answer[i]; 674 | if (res->type == NSTYPE_CNAME && (cdn_is_akamai(res->domain) 675 | || cdn_is_akamai(*(const char **)res->value))) { 676 | // qc->is_china_domain = 1; 677 | break; 678 | } 679 | 680 | if ((res->type == NSTYPE_A || res->type == NSTYPE_AAAA) 681 | && NULL == lookupRoute(res->value, res->type)) { 682 | dns_parser_copy(&ecs_parser, &p0); 683 | // qc->is_china_domain = 1; 684 | break; 685 | } 686 | } 687 | 688 | qc->ecs_parser.len = dns_build(&ecs_parser, qc->ecs_parser.buf, sizeof(qc->ecs_parser.buf)); 689 | qc->def_parser.len = dns_build(&def_parser, qc->def_parser.buf, sizeof(qc->def_parser.buf)); 690 | 691 | check_flush: 692 | if (qc->is_china_domain && ecs_parser.head.question) { 693 | dns_parser_copy(&p0, &ecs_parser); 694 | 695 | p0.question[0] = parser.question[0]; 696 | memmove(p0.answer + 1, p0.answer, sizeof(p0.answer[0]) * p0.head.answer); 697 | 698 | res = &p0.answer[0]; 699 | res->domain = add_domain(&p0, parser.question[0].domain); 700 | res->type = NSTYPE_CNAME; 701 | res->klass = NSCLASS_INET; 702 | res->ttl = 3600; 703 | *(const char **)res->value = add_domain(&p0, parser.question[1].domain); 704 | p0.head.answer++; 705 | 706 | const char *ptr = get_suffix(parser.question[0].domain, 3); 707 | 708 | for (i = 0; i < p0.head.author && ptr; i++) { 709 | res = &p0.author[i]; 710 | if (res->type == NSTYPE_SOA) { 711 | res->domain = add_domain(&p0, ptr); 712 | res->ttl = 7200; 713 | } 714 | } 715 | 716 | p0.head.addon = 0; 717 | p0.head.ident = parser.head.ident; 718 | p0.head.flags |= NSFLAG_AA; 719 | dns_sendto(ctx->sockfd, &p0, (struct sockaddr *)&qc->from, sizeof(qc->from)); 720 | LOG_DEBUG("RETURN: china domain: %s type=%d answer=%d", p0.question[0].domain, p0.question[0].type, p0.head.answer); 721 | } 722 | 723 | if (qc->is_nonchina_domain && def_parser.head.question && ecs_parser.head.question) { 724 | struct dns_question *que; 725 | dns_parser_copy(&p0, &def_parser); 726 | 727 | que = &p0.question[0]; 728 | p0.question[0] = parser.question[0]; 729 | 730 | if (dns_answer_diff(&def_parser, &ecs_parser) == 0 && 0) { 731 | for (i = 0; i < p0.head.answer; i++) { 732 | res = &p0.answer[i]; 733 | if (strcasecmp(res->domain, parser.question[1].domain) == 0) { 734 | res->domain = add_domain(&p0, parser.question[0].domain); 735 | } 736 | } 737 | LOG_DEBUG("ecs is ko, will return cname"); 738 | } else { 739 | int nanswer = 0; 740 | for (i = 0; i < p0.head.answer; i++) { 741 | res = &p0.answer[i]; 742 | if (que->type == res->type) { 743 | res->domain = que->domain; 744 | p0.answer[nanswer++] = *res; 745 | } 746 | } 747 | LOG_DEBUG("RETURN: ecs is ok, do not return %s cname: %d type=%d originally: %d/%d/%d", 748 | p0.question[0].domain, nanswer, p0.question[0].type, def_parser.head.answer, ecs_parser.head.answer, p0.head.answer); 749 | p0.head.answer = nanswer; 750 | } 751 | 752 | const char *ptr = get_suffix(parser.question[0].domain, 3); 753 | 754 | for (i = 0; i < p0.head.author && ptr; i++) { 755 | res = &p0.author[i]; 756 | if (res->type == NSTYPE_SOA) { 757 | res->domain = add_domain(&p0, ptr); 758 | res->ttl = 7200; 759 | } 760 | } 761 | 762 | p0.head.ident = parser.head.ident; 763 | p0.head.flags |= NSFLAG_AA; 764 | dns_sendto(ctx->sockfd, &p0, (struct sockaddr *)&qc->from, sizeof(qc->from)); 765 | } 766 | 767 | LOG_DEBUG("%s is_nonchina_domain %d is_china_domain %d question ecs=%d def=%d", 768 | parser.question[1].domain, 769 | qc->is_nonchina_domain, qc->is_china_domain, 770 | ecs_parser.head.question, def_parser.head.question); 771 | // dns_build(&ecs_parser, qc->ecs_parser.buf, qc->ecs_parser.len); 772 | // dns_build(&def_parser, qc->def_parser.buf, qc->def_parser.len); 773 | // dns_build(&parser, qc->parser.buf, qc->parser.len); 774 | return 0; 775 | } 776 | 777 | // #define get_score_id(ifname) if_nametoindex(ifname) 778 | #define get_score_id(ifname) 0 779 | 780 | int main(int argc, char *argv[]) 781 | { 782 | int retval; 783 | int outfd, sockfd; 784 | struct sockaddr_in6 myaddr; 785 | struct sockaddr * paddr = (struct sockaddr *)&myaddr; 786 | 787 | struct sockaddr_in6 myaddr6; 788 | struct sockaddr * paddr6 = (struct sockaddr *)&myaddr6; 789 | setenv("BINDLOCAL", "::ffff:127.0.0.111", 0); 790 | LOG_DEBUG("memory: %d %d %d\n", sizeof(_orig_list), sizeof(_orig_list[0]), sizeof(_orig_list[0].parser)); 791 | 792 | outfd = socket(AF_INET6, SOCK_DGRAM, 0); 793 | assert(outfd != -1); 794 | 795 | myaddr.sin6_family = AF_INET6; 796 | myaddr.sin6_port = 0; 797 | myaddr.sin6_addr = in6addr_any; 798 | retval = bind(outfd, paddr, sizeof(myaddr)); 799 | assert(retval != -1); 800 | 801 | sockfd = socket(AF_INET6, SOCK_DGRAM, 0); 802 | assert(sockfd != -1); 803 | 804 | myaddr6.sin6_family = AF_INET6; 805 | myaddr6.sin6_port = htons(53); 806 | myaddr6.sin6_addr = in6addr_any; 807 | 808 | char _dummy[256], *ifp; 809 | strcpy(_dummy, getenv("BINDLOCAL")); 810 | if (NULL != (ifp = strchr(_dummy, '%'))) { 811 | *ifp ++ = 0; 812 | myaddr6.sin6_scope_id = get_score_id(ifp); 813 | inet_pton(AF_INET6, _dummy, &myaddr6.sin6_addr); 814 | } else { 815 | myaddr6.sin6_scope_id = 0; 816 | inet_pton(AF_INET6, _dummy, &myaddr6.sin6_addr); 817 | } 818 | 819 | retval = bind(sockfd, paddr6, sizeof(myaddr6)); 820 | assert(retval != -1); 821 | 822 | int count; 823 | char buf[2048]; 824 | fd_set readfds = {}; 825 | socklen_t addrl = 0; 826 | struct sockaddr_in6 dnsaddr; 827 | struct sockaddr_in6 ecsaddr; 828 | 829 | struct dns_context c0 = { 830 | .outfd = outfd, 831 | .sockfd = sockfd, 832 | .dnslen = sizeof(dnsaddr), 833 | }; 834 | 835 | setenv("NAMESERVER", "::ffff:8.8.8.8", 0); 836 | setenv("ECS_SERVER", "::ffff:8.8.8.8", 0); 837 | 838 | dnsaddr.sin6_family = AF_INET6; 839 | dnsaddr.sin6_port = htons(53); 840 | inet_pton(AF_INET6, getenv("NAMESERVER"), &dnsaddr.sin6_addr); 841 | 842 | ecsaddr.sin6_family = AF_INET6; 843 | ecsaddr.sin6_port = htons(53); 844 | inet_pton(AF_INET6, getenv("ECS_SERVER"), &ecsaddr.sin6_addr); 845 | 846 | c0.dnsaddr = (struct sockaddr *)&dnsaddr; 847 | c0.ecsaddr = (struct sockaddr *)&ecsaddr; 848 | LOG_DEBUG("nsaddr %p pointer %p %d", c0.dnsaddr, &dnsaddr, htons(dnsaddr.sin6_port)); 849 | 850 | const struct sockaddr_in6 *inp = (const struct sockaddr_in6 *)&dnsaddr; 851 | LOG_DEBUG("dns_build bytes %d %d %d %s", 0, inp->sin6_family, htons(inp->sin6_port), ntop6(inp->sin6_addr)); 852 | 853 | do { 854 | FD_ZERO(&readfds); 855 | FD_SET(outfd, &readfds); 856 | FD_SET(sockfd, &readfds); 857 | 858 | retval = select(sockfd + 1, &readfds, 0, 0, 0); 859 | if (retval == -1) { 860 | LOG_DEBUG("select failure: %s", strerror(errno)); 861 | break; 862 | } 863 | 864 | if (FD_ISSET(outfd, &readfds)) { 865 | addrl = sizeof(myaddr); 866 | count = recvfrom(outfd, buf, sizeof(buf), 0, paddr, &addrl); 867 | assert(count > 0); 868 | count > 0 || LOG_DEBUG("outfd is readable"); 869 | do_dns_backward(&c0, buf, count, &myaddr); 870 | } 871 | 872 | if (FD_ISSET(sockfd, &readfds)) { 873 | addrl = sizeof(myaddr6); 874 | count = recvfrom(sockfd, buf, sizeof(buf), 0, paddr6, &addrl); 875 | assert(count > 0); 876 | count > 0 || LOG_DEBUG("sockfd is readable"); 877 | do_dns_forward(&c0, buf, count, &myaddr6); 878 | } 879 | 880 | } while (retval >= 0); 881 | 882 | close(sockfd); 883 | close(outfd); 884 | 885 | return 0; 886 | } 887 | -------------------------------------------------------------------------------- /dns_mod_gfw.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include "tx_debug.h" 20 | #include "dnsproto.h" 21 | #include "subnet_api.h" 22 | 23 | #ifdef WIN32 24 | #include 25 | #else 26 | #include 27 | #include 28 | #include 29 | #include 30 | #define closesocket close 31 | #endif 32 | 33 | static char addrbuf[256]; 34 | #define ntop6(addr) inet_ntop(AF_INET6, &addr, addrbuf, sizeof(addrbuf)) 35 | #define ntop6p(addr) inet_ntop(AF_INET6, addr, addrbuf, sizeof(addrbuf)) 36 | 37 | struct dns_soa { 38 | const char *name_server; 39 | const char *admin_email; 40 | uint32_t serial; 41 | uint32_t day2; 42 | uint32_t day3; 43 | uint32_t day4; 44 | uint32_t day5; 45 | }; 46 | 47 | static struct dns_soa _rr_soa = { 48 | .name_server = "ns2.603030.xyz", 49 | .admin_email = "admin.603030.xyz", 50 | .serial = 20231523, 51 | .day2 = 7200, 52 | .day3 = 1800, 53 | .day4 = 1209600, 54 | .day5 = 1600 55 | }; 56 | 57 | struct dns_resource _predefine_resource_record[] = { 58 | { 59 | .type = NSTYPE_SOA, 60 | .klass = NSCLASS_INET, 61 | .ttl = 86400, 62 | .len = 4, 63 | .flags = 0, 64 | .domain = "_dummy", 65 | .value = {110, 42, 145, 164}}, 66 | { 67 | .type = NSTYPE_NS, 68 | .klass = NSCLASS_INET, 69 | .ttl = 86400, 70 | .len = 8, 71 | .flags = 0, 72 | .domain = "_dummy", 73 | .value = {110, 42, 145, 164}}, 74 | { 75 | .type = NSTYPE_A, 76 | .klass = NSCLASS_INET, 77 | .ttl = 360, 78 | .len = 4, 79 | .flags = 0, 80 | .domain = "cdn.855899.xyz", 81 | .value = {54, 192, 17, 115}}, 82 | { 83 | .type = NSTYPE_A, 84 | .klass = NSCLASS_INET, 85 | .ttl = 360, 86 | .len = 4, 87 | .flags = 0, 88 | .domain = "cdn.855899.xyz", 89 | .value = {172, 67, 165, 145}}, 90 | { 91 | .type = NSTYPE_A, 92 | .klass = NSCLASS_INET, 93 | .ttl = 36000, 94 | .len = 4, 95 | .flags = 0, 96 | .domain = "mtalk.oogleg.moc.603030.xyz", 97 | .value = {10, 0, 3, 1}}, 98 | { 99 | .type = NSTYPE_A, 100 | .klass = NSCLASS_INET, 101 | .ttl = 36000, 102 | .len = 4, 103 | .flags = 0, 104 | .domain = "example.com", 105 | .value = {93, 184, 215, 14}}, 106 | { 107 | .type = NSTYPE_A, 108 | .klass = NSCLASS_INET, 109 | .ttl = 36000, 110 | .len = 4, 111 | .flags = 0, 112 | .domain = "alt1-mtalk.google.com", 113 | .value = {110, 42, 145, 164}}, 114 | }; 115 | 116 | #define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array)) 117 | 118 | int fetch_predefine_resource_record(struct dns_parser *parser) 119 | { 120 | int found = 0; 121 | struct dns_resource *res; 122 | struct dns_question *que = &parser->question[0]; 123 | 124 | for (int i = 0; i < ARRAY_SIZE(_predefine_resource_record); i++) { 125 | 126 | if (MAX_RECORD_COUNT <= parser->head.answer) { 127 | break; 128 | } 129 | 130 | res = &_predefine_resource_record[i]; 131 | if ((res->type == que->type) && strcasecmp(res->domain, que->domain) == 0) { 132 | int index = parser->head.answer++; 133 | parser->answer[index].type = res->type; 134 | parser->answer[index].klass = res->klass; 135 | parser->answer[index].flags = res->flags; 136 | parser->answer[index].ttl = res->ttl; 137 | memcpy(parser->answer[index].value, res->value, sizeof(res->value)); 138 | parser->answer[index].domain = add_domain(parser, que->domain); 139 | } 140 | 141 | if (/* res->type == NSTYPE_ANY &&*/ strcasecmp(que->domain, res->domain) == 0) { 142 | found = 1; 143 | } 144 | } 145 | 146 | return (parser->head.answer > 0) || (found == 1); 147 | } 148 | 149 | struct dns_context { 150 | int outfd; 151 | int sockfd; 152 | int echofd; 153 | 154 | socklen_t dnslen; 155 | struct sockaddr_in6 *dnsaddr; 156 | }; 157 | 158 | struct dns_query_context { 159 | uint32_t digest; 160 | int server_status; 161 | char cname[256]; 162 | struct sockaddr_in6 from; 163 | struct dns_parser parser; 164 | }; 165 | 166 | static struct dns_query_context _orig_list[0x1000]; 167 | 168 | static struct sockaddr_in6 _ain6 = {}; 169 | static struct sockaddr_in6 _cin6 = {}; 170 | 171 | static int dns_parser_copy(struct dns_parser *dst, struct dns_parser *src) 172 | { 173 | static uint8_t _qc_hold[2048]; 174 | size_t len = dns_build(src, _qc_hold, sizeof(_qc_hold)); 175 | assert(len > 0); 176 | int isok = dns_parse(dst, _qc_hold, len) == NULL; 177 | assert(isok == 0); 178 | return isok; 179 | } 180 | 181 | static int dns_contains(const char *domain) 182 | { 183 | int i; 184 | const char *_tld1[] = { 185 | "ten.", "ude.", "oc.", "gro.", "moc.", "vog.", NULL 186 | }; 187 | const char *_tld0[] = { 188 | "net.", "edu.", "co.", "org.", "com.", "gov.", NULL 189 | }; 190 | 191 | (void)_tld1; 192 | for (i = 0; _tld0[i]; i++) { 193 | if (strncasecmp(domain, _tld0[i], 4) == 0) { 194 | return 1; 195 | } 196 | } 197 | 198 | if (strncasecmp(domain, "oc.", 3) == 0) { 199 | return 1; 200 | } 201 | 202 | return 0; 203 | } 204 | 205 | static int dns_rewrap(struct dns_parser *p1) 206 | { 207 | const char *domain = NULL; 208 | struct dns_question *que, *que1; 209 | 210 | que = &p1->question[0]; 211 | que1 = &p1->question[1]; 212 | 213 | int ndot = 0; 214 | char *optp, *limit; 215 | char *dots[8] = {}, title[256]; 216 | 217 | // LOG_DEBUG("suffixes: %s %d", que->domain, que->type); 218 | if (p1->head.question != 1 || que->domain == NULL) { 219 | return -1; 220 | } 221 | 222 | optp = title; 223 | limit = title + sizeof(title); 224 | dots[ndot & 0x7] = title; 225 | for (domain = que->domain; *domain; domain++) { 226 | switch(*domain) { 227 | case '.': 228 | if (optp > dots[ndot & 0x7]) ndot++; 229 | assert(optp < limit); 230 | *optp++ = *domain; 231 | dots[ndot & 0x7] = optp; 232 | break; 233 | 234 | default: 235 | assert(optp < limit); 236 | *optp++ = *domain; 237 | break; 238 | } 239 | } 240 | 241 | *optp = 0; 242 | if (optp > dots[ndot & 0x7]) ndot++; 243 | 244 | if (ndot <= 3) { 245 | return -1; 246 | } 247 | 248 | if (ndot > 3 && !strcasecmp(dots[(ndot - 3) & 0x7], "oil.603030.xyz")) { 249 | *que1 = *que; 250 | p1->addon[0].domain = add_domain(p1, "oil.603030.xyz"); 251 | p1->addon[0].klass = NSCLASS_INET; 252 | p1->addon[0].type = NSTYPE_A; 253 | p1->addon[0].ttl = 3600; 254 | dots[(ndot - 3) & 0x7][-1] = 0; 255 | que1->domain = add_domain(p1, title); 256 | assert(que1->domain); 257 | p1->head.question = 2; 258 | return 0; 259 | } 260 | 261 | return -1; 262 | } 263 | 264 | static int dns_sendto(int outfd, struct dns_parser *parser, const struct sockaddr_in6 *inp, size_t tolen) 265 | { 266 | ssize_t len; 267 | uint8_t _hold[2048]; 268 | const struct sockaddr *to = (const struct sockaddr *)inp; 269 | 270 | len = dns_build(parser, _hold, sizeof(_hold)); 271 | assert(len > 0); 272 | 273 | if (len != -1) 274 | len = sendto(outfd, _hold, len, 0, to, tolen); 275 | 276 | LOG_DEBUG("dns_sendto %d af=%d %d %s %s", len, 277 | inp->sin6_family, htons(inp->sin6_port), ntop6(inp->sin6_addr), parser->question[0].domain); 278 | 279 | return len; 280 | } 281 | 282 | int off_primary = 0; 283 | static char cache_primary[1024 * 1024] = {}; 284 | 285 | int off_secondary = 0; 286 | static char cache_secondary[1024 * 1024] = {}; 287 | 288 | enum {STATUS_ALLOW, STATUS_REJECT}; 289 | static char * cache_current = cache_primary; 290 | 291 | static int cache_get(const char *host) 292 | { 293 | int off = 0; 294 | 295 | for (off = 0; off < off_primary; off++) { 296 | int length = cache_primary[off] & 0xff; 297 | 298 | if (strncasecmp(cache_primary + off + 1, host, length) == 0) { 299 | int type = cache_primary[off + length]; 300 | LOG_DEBUG("get cache host: %s type %d", host, type); 301 | return type | 0x80; 302 | } 303 | 304 | off += length; 305 | } 306 | 307 | for (off = 0; off < off_secondary; off++) { 308 | int length = cache_secondary[off] & 0xff; 309 | 310 | if (strncasecmp(cache_secondary + off + 1, host, length) == 0) { 311 | int type = cache_secondary[off + length]; 312 | LOG_DEBUG("get cache host: %s type %d", host, type); 313 | return type | 0x80; 314 | } 315 | 316 | off += length; 317 | } 318 | 319 | return 0; 320 | } 321 | 322 | 323 | static int cache_add(const char *host, int atype) 324 | { 325 | int off = 0; 326 | 327 | if (cache_get(host)) 328 | return 0; 329 | 330 | int hostlen = strlen(host); 331 | 332 | if (atype == STATUS_ALLOW) 333 | hostlen++; 334 | 335 | assert(hostlen < 256); 336 | int *off_current = 0; 337 | if (cache_current == cache_primary) { 338 | off_current = &off_primary; 339 | if (off_primary + hostlen >= sizeof(cache_primary)) { 340 | cache_current = cache_secondary; 341 | off_current = &off_secondary; 342 | off_secondary = 0; 343 | } 344 | } else if (cache_current == cache_secondary) { 345 | off_current = &off_secondary; 346 | if (off_secondary + hostlen >= sizeof(cache_secondary)) { 347 | cache_current = cache_primary; 348 | off_current = &off_primary; 349 | off_primary = 0; 350 | } 351 | } 352 | 353 | assert(off_current); 354 | 355 | off = *off_current; 356 | 357 | cache_current[off++] = hostlen; 358 | memcpy(cache_current + off, host, hostlen); 359 | off += hostlen; 360 | 361 | *off_current = off; 362 | 363 | LOG_DEBUG("cache_add %s %d", host, atype); 364 | return 0; 365 | } 366 | 367 | int do_dns_forward(struct dns_context *ctx, void *buf, int count, struct sockaddr_in6 *from) 368 | { 369 | struct dns_parser p0; 370 | struct dns_parser *pp; 371 | 372 | pp = dns_parse(&p0, buf, count); 373 | if (pp == NULL) { 374 | LOG_DEBUG("do_dns_forward parse failure"); 375 | return 0; 376 | } 377 | 378 | if (p0.head.flags & 0x8000) { 379 | LOG_DEBUG("FROM: %s this is not query", "nothing"); 380 | return -1; 381 | } 382 | 383 | if (p0.head.question && p0.question[0].type == NSTYPE_A 384 | && !strcmp(p0.question[0].domain, "example.com")) { 385 | LOG_DEBUG("FROM: %s keepalive %s", ntop6p(&from->sin6_addr), p0.question[0].domain); 386 | // memcpy(ctx->dnsaddr, from, sizeof(*from)); 387 | // ctx->dnslen = sizeof(*from); 388 | // ctx->echofd = ctx->sockfd; 389 | } 390 | 391 | if (p0.head.question && fetch_predefine_resource_record(&p0)) { 392 | LOG_DEBUG("prefetch: %s", p0.question[0].domain); 393 | p0.head.flags |= NSFLAG_QR; 394 | dns_sendto(ctx->sockfd, &p0, from, sizeof(*from)); 395 | return 0; 396 | } 397 | 398 | int type = cache_get(p0.question[0].domain); 399 | if (type && (p0.question[0].type == NSTYPE_A || p0.question[0].type == NSTYPE_AAAA)) { 400 | LOG_DEBUG("cache_get: %s %d", p0.question[0].domain, type); 401 | p0.head.flags |= NSFLAG_QR; 402 | if (p0.head.flags & NSFLAG_RD) p0.head.flags |= NSFLAG_RA; 403 | p0.head.flags |= NSFLAG_AA; 404 | p0.head.flags &= ~NSFLAG_ZERO; 405 | 406 | p0.head.answer = 1; 407 | p0.head.addon = 0; 408 | p0.answer[0].domain = p0.question[0].domain; 409 | p0.answer[0].type = p0.question[0].type; 410 | p0.answer[0].ttl = 7200; 411 | p0.answer[0].klass = p0.question[0].klass; 412 | 413 | if (type & 0x7f) { 414 | uint32_t fakeip = rand(); 415 | if (fakeip == 0x7f7f7f7f) fakeip = 0x1010101; 416 | uint32_t list[4] = {fakeip, fakeip, fakeip, fakeip}; 417 | memcpy(p0.answer[0].value, list, 16); 418 | } else if (p0.question[0].type == NSTYPE_AAAA) { 419 | memset(p0.answer[0].value, 0xfe, 16); 420 | } else { 421 | memset(p0.answer[0].value, 127, 16); 422 | } 423 | 424 | dns_sendto(ctx->sockfd, &p0, from, sizeof(*from)); 425 | return 0; 426 | } 427 | 428 | if (p0.head.question == 0) { 429 | p0.head.flags |= RCODE_REFUSED; 430 | p0.head.flags |= NSFLAG_QR; 431 | dns_sendto(ctx->sockfd, &p0, from, sizeof(*from)); 432 | return 0; 433 | } 434 | 435 | const char *myzone = strcasestr(p0.question[0].domain, "oil.603030.xyz"); 436 | if (myzone == NULL || strcasecmp(myzone, "oil.603030.xyz") || p0.question[0].type == NSTYPE_CNAME) { 437 | // p0.head.flags |= RCODE_NXDOMAIN; 438 | p0.head.flags |= RCODE_REFUSED; 439 | p0.head.flags |= NSFLAG_QR; 440 | dns_sendto(ctx->sockfd, &p0, from, sizeof(*from)); 441 | return 0; 442 | } 443 | 444 | int retval = 0; 445 | int offset = (p0.head.ident & 0xfff); 446 | 447 | struct dns_parser *p1 = NULL; 448 | struct dns_query_context qc0 = {}; 449 | struct dns_query_context *qc = &qc0; 450 | struct dns_query_context *qc1 = &_orig_list[offset]; 451 | 452 | memset(qc, 0, sizeof(qc0)); 453 | qc->from = *from; 454 | 455 | dns_parser_copy(&qc->parser, &p0); 456 | p1 = &qc->parser; 457 | 458 | if (dns_rewrap(p1) == -1) { 459 | p0.head.flags |= RCODE_REFUSED; 460 | p0.head.flags |= NSFLAG_QR; 461 | dns_sendto(ctx->sockfd, &p0, from, sizeof(*from)); 462 | LOG_DEBUG("FROM: %s this is not good %d", p0.question[0].domain, p0.question[0].type); 463 | return -1; 464 | } 465 | 466 | const char *zone = p1->addon[0].domain; 467 | LOG_DEBUG("%04x: FROM: %s to %s, zone %s", p1->head.ident, p1->question[0].domain, p1->question[1].domain, zone); 468 | if (zone != NULL && *p0.question[0].domain == '_') { 469 | struct dns_resource *res; 470 | p0.head.flags |= (RCODE_NXDOMAIN| NSFLAG_AA); 471 | p0.head.flags |= (NSFLAG_RA| NSFLAG_QR); 472 | 473 | res = &p0.author[0]; 474 | res->domain = add_domain(&p0, zone); 475 | res->type = NSTYPE_SOA; 476 | res->klass = NSCLASS_INET; 477 | res->ttl = 7200; 478 | memcpy(res->value , &_rr_soa, sizeof(_rr_soa)); 479 | p0.head.author = 1; 480 | 481 | retval = dns_sendto(ctx->sockfd, &p0, &qc->from, ctx->dnslen); 482 | if (retval == -1) { 483 | LOG_DEBUG("dns_sendto failure: %s %p", strerror(errno), ctx->dnsaddr); 484 | return 0; 485 | } 486 | return 0; 487 | } 488 | 489 | if (zone != NULL && strcasecmp(zone, "oil.603030.xyz") == 0 && (p0.question[0].type == NSTYPE_AAAA || p0.question[0].type == NSTYPE_A)) { 490 | p0.question[0] = p1->question[1]; 491 | p0.head.flags &= ~NSFLAG_QR; 492 | p0.head.flags &= ~NSFLAG_RD; 493 | p0.head.flags &= ~NSFLAG_RCODE; 494 | p0.head.question = 1; 495 | p0.head.answer = 0; 496 | p0.head.author = 0; 497 | 498 | p0.addon[0].domain = ""; 499 | p0.addon[0].ttl = 0; 500 | p0.addon[0].klass = 1320; 501 | p0.addon[0].type = NSTYPE_OPT; 502 | p0.addon[0].len = 0; 503 | p0.head.addon = 1; 504 | 505 | int outfd = ctx->echofd == -1? ctx->outfd: ctx->echofd; 506 | retval = dns_sendto(outfd, &p0, ctx->dnsaddr, ctx->dnslen); 507 | if (retval == -1) { 508 | LOG_DEBUG("dns_sendto failure: %s %p", strerror(errno), ctx->dnsaddr); 509 | return 0; 510 | } 511 | 512 | retval = dns_sendto(ctx->outfd, &p0, &_ain6, sizeof(_ain6)); 513 | if (retval == -1) { 514 | LOG_DEBUG("dns_sendto failure: %s %p", strerror(errno), ctx->dnsaddr); 515 | return 0; 516 | } 517 | 518 | p0.head.flags |= NSFLAG_RD; 519 | retval = dns_sendto(ctx->outfd, &p0, &_cin6, sizeof(_cin6)); 520 | if (retval == -1) { 521 | LOG_DEBUG("dns_sendto failure: %s %p", strerror(errno), ctx->dnsaddr); 522 | return 0; 523 | } 524 | 525 | *qc1 = qc0; 526 | qc1->digest = 0; 527 | dns_parser_copy(&qc1->parser, &qc0.parser); 528 | } else { 529 | p0.head.flags |= RCODE_REFUSED; 530 | p0.head.flags |= NSFLAG_QR; 531 | dns_sendto(ctx->sockfd, &p0, from, sizeof(*from)); 532 | LOG_DEBUG("refused FROM: %s this is not good %d", p0.question[0].domain, p0.question[0].type); 533 | } 534 | 535 | return 0; 536 | } 537 | 538 | static int dump_resource(const char *title, struct dns_resource *res) 539 | { 540 | 541 | if (res->type == NSTYPE_TXT) { 542 | LOG_DEBUG("%s %s TXT", title, res->domain); 543 | } else if (res->type == NSTYPE_A) { 544 | LOG_DEBUG("%s %s A %s", title, res->domain, inet_ntoa(*(struct in_addr *)res->value)); 545 | } else if (res->type == NSTYPE_NS) { 546 | LOG_DEBUG("%s %s NS %s", title, res->domain, *(const char **)res->value); 547 | } else if (res->type == NSTYPE_SRV) { 548 | LOG_DEBUG("%s %s SRV %p", title, res->domain, *(const char **)res->value); 549 | } else if (res->type == NSTYPE_SOA) { 550 | LOG_DEBUG("%s %s SOA %s", title, res->domain, *(const char **)res->value); 551 | } else if (res->type == NSTYPE_AAAA) { 552 | LOG_DEBUG("%s %s AAAA %s", title, res->domain, ntop6p(res->value)); 553 | } else if (res->type == NSTYPE_CNAME) { 554 | LOG_DEBUG("%s %s CNAME %s", title, res->domain, *(const char **)res->value); 555 | } else { 556 | LOG_DEBUG("%s %s UNKOWN %d", title, res->domain, res->type); 557 | } 558 | 559 | return 0; 560 | } 561 | 562 | static uint32_t get_check_sum(void *buf, size_t count) 563 | { 564 | uint32_t cksum = 0; 565 | uint32_t *dataptr = (uint32_t*)buf; 566 | uint32_t flags = *dataptr; 567 | 568 | #define NSFLAG_AA 0x0400 569 | #define NSFLAG_RD 0x0100 570 | #define NSFLAG_RA 0x0080 571 | 572 | flags &= ~htonl(NSFLAG_AA|NSFLAG_RD|NSFLAG_RA); 573 | 574 | LOG_DEBUG("flags=%08x %08x", flags, *dataptr); 575 | while (count >= 4) { 576 | cksum += flags; 577 | count -= 4; 578 | flags = *++dataptr; 579 | } 580 | 581 | return cksum; 582 | } 583 | 584 | struct dns_alias { 585 | char *name; 586 | }; 587 | 588 | #define SS_CHINA 1 589 | #define SS_ONEED 2 590 | #define SS_REJECTED 8 591 | 592 | int do_dns_backward(struct dns_context *ctx, void *buf, int count, struct sockaddr_in6 *from) 593 | { 594 | int i; 595 | struct dns_parser p0; 596 | struct dns_parser *pp; 597 | struct dns_resource *res; 598 | 599 | pp = dns_parse(&p0, buf, count); 600 | if (pp == NULL) { 601 | LOG_DEBUG("do_dns_backward parse failure"); 602 | return 0; 603 | } 604 | 605 | #if 0 606 | if (~p0.head.flags & 0x8000) { 607 | LOG_DEBUG("FROM: %s this is not response", inet_ntoa(from->sin_addr)); 608 | return -1; 609 | } 610 | #endif 611 | if (~p0.head.flags & 0x8000) { 612 | if (p0.head.question && p0.question[0].type == NSTYPE_A 613 | && !strcmp(p0.question[0].domain, "example.com")) { 614 | LOG_DEBUG("FROM: %s keepalive %s", ntop6p(&from->sin6_addr), p0.question[0].domain); 615 | memcpy(ctx->dnsaddr, from, sizeof(*from)); 616 | ctx->dnslen = sizeof(*from); 617 | ctx->echofd = ctx->outfd; 618 | } 619 | 620 | if (p0.head.question && fetch_predefine_resource_record(&p0)) { 621 | LOG_DEBUG("prefetch: %s", p0.question[0].domain); 622 | p0.head.flags |= NSFLAG_QR; 623 | dns_sendto(ctx->outfd, &p0, from, sizeof(*from)); 624 | return 0; 625 | } 626 | } 627 | 628 | int offset = (p0.head.ident & 0xfff); 629 | struct dns_query_context *qc = &_orig_list[offset]; 630 | 631 | pp = &qc->parser; 632 | if (pp->head.ident != p0.head.ident || pp->head.question != 2) { 633 | LOG_DEBUG("FROM: %s XX unexpected response: %d", ntop6(from->sin6_addr), pp->head.question); 634 | return -1; 635 | } 636 | 637 | if (IN6_ARE_ADDR_EQUAL(&_cin6.sin6_addr, &from->sin6_addr)) { 638 | for (i = 0; i < p0.head.answer; i++) { 639 | res = &p0.answer[i]; 640 | if (res->type == NSTYPE_CNAME) { 641 | struct dns_alias * alias = (struct dns_alias *)res->value; 642 | strcpy(qc->cname, alias->name); 643 | break; 644 | } 645 | } 646 | 647 | qc->server_status |= SS_ONEED; 648 | LOG_DEBUG("oneed server_status: %x :%s\n", qc->server_status, qc->cname); 649 | if (qc->server_status == SS_ONEED) return 0; 650 | 651 | p0.question[0] = pp->question[0]; 652 | 653 | p0.head.flags |= NSFLAG_QR; 654 | p0.head.flags |= NSFLAG_RA; 655 | p0.head.flags |= NSFLAG_RD; 656 | p0.head.flags &= ~NSFLAG_RCODE; 657 | p0.answer[0].klass = NSCLASS_INET; 658 | 659 | p0.head.answer = 1; 660 | p0.head.addon = 0; 661 | p0.answer[0].domain = p0.question[0].domain; 662 | p0.answer[0].type = p0.question[0].type; 663 | p0.answer[0].ttl = 7200; 664 | p0.answer[0].klass = p0.question[0].klass; 665 | 666 | if (p0.answer[0].type != NSTYPE_AAAA && 667 | p0.answer[0].type != NSTYPE_A) { 668 | p0.head.answer = 0; 669 | } else if (qc->server_status & SS_REJECTED) { 670 | uint32_t fakeip = rand(); 671 | if (fakeip == 0x7f7f7f7f) fakeip = 0x1010101; 672 | uint32_t list[4] = {fakeip, fakeip, fakeip, fakeip}; 673 | memcpy(p0.answer[0].value, list, 16); 674 | } else if (*qc->cname) { 675 | int type; 676 | char buf[356]; 677 | snprintf(buf, sizeof(buf), "%s.oil.603030.xyz", qc->cname); 678 | type = cache_get(buf); 679 | if (type == 0) { 680 | p0.answer[0].type = NSTYPE_CNAME; 681 | struct dns_alias *alias = (struct dns_alias *)p0.answer[0].value; 682 | alias->name = add_domain(&p0, buf); 683 | } else if (type & 0x7f) { 684 | uint32_t fakeip = rand(); 685 | if (fakeip == 0x7f7f7f7f) fakeip = 0x1010101; 686 | uint32_t list[4] = {fakeip, fakeip, fakeip, fakeip}; 687 | memcpy(p0.answer[0].value, list, 16); 688 | cache_add(p0.question[0].domain, STATUS_REJECT); 689 | } else if (p0.question[0].type == NSTYPE_AAAA) { 690 | cache_add(p0.question[0].domain, STATUS_ALLOW); 691 | memset(p0.answer[0].value, 0xfe, 16); 692 | } else { 693 | cache_add(p0.question[0].domain, STATUS_ALLOW); 694 | memset(p0.answer[0].value, 127, 16); 695 | } 696 | } else { 697 | if (p0.question[0].type == NSTYPE_AAAA) { 698 | memset(p0.answer[0].value, 0xfe, 16); 699 | } else { 700 | memset(p0.answer[0].value, 127, 16); 701 | } 702 | cache_add(p0.question[0].domain, STATUS_ALLOW); 703 | } 704 | 705 | dns_sendto(ctx->sockfd, &p0, &qc->from, sizeof(qc->from)); 706 | return 0; 707 | } else if (IN6_ARE_ADDR_EQUAL(&_ain6.sin6_addr, &from->sin6_addr)) { 708 | int found = 0; 709 | uint64_t val = 0; 710 | 711 | for (i = 0; 3 != found && i < p0.head.addon; i++) { 712 | res = &p0.addon[i]; 713 | if (res->type == NSTYPE_AAAA) { 714 | val = htonll(*(uint64_t *)res->value); 715 | found = 2 + !lookupRoute6(val); 716 | } else if (res->type == NSTYPE_A) { 717 | val = htonll(*(uint64_t *)res->value) & 0xffffffff00000000ull; 718 | found = 2 + !lookupRoute4(val); 719 | } 720 | } 721 | 722 | LOG_DEBUG("found=%d %s", found, ntop6(from->sin6_addr)); 723 | if (found != 3) { 724 | p0.question[0] = pp->question[0]; 725 | p0.head.answer = 0; 726 | p0.head.author = 0; 727 | p0.head.addon = 0; 728 | 729 | p0.head.flags |= NSFLAG_QR; 730 | p0.head.flags &= ~NSFLAG_RCODE; 731 | p0.head.flags |= RCODE_REFUSED; 732 | 733 | if (NULL != getenv("REFUSED")) 734 | dns_sendto(ctx->sockfd, &p0, &qc->from, sizeof(qc->from)); 735 | 736 | return -1; 737 | } 738 | qc->server_status = (SS_ONEED| SS_CHINA); 739 | qc->cname[0] = 0; 740 | } else { 741 | qc->server_status |= SS_CHINA; 742 | } 743 | 744 | uint32_t digest = get_check_sum(buf, 6 * 2); 745 | if (digest != qc->digest && qc->digest != 0) { 746 | LOG_DEBUG("FROM: %s digest %08x %08x %d", ntop6(from->sin6_addr), digest, qc->digest, count); 747 | return -1; 748 | } 749 | 750 | qc->digest = digest; 751 | if (p0.head.author != 0 && p0.head.answer > 0) { 752 | LOG_DEBUG("FROM: %s %d/%d/%d unexpected response", ntop6(from->sin6_addr), p0.head.author, p0.head.answer, p0.head.addon); 753 | return -1; 754 | } 755 | 756 | if (pp->head.answer > p0.head.answer) { 757 | LOG_DEBUG("FROM: %s should not overwrite response", ntop6(from->sin6_addr)); 758 | return -1; 759 | } 760 | 761 | if (strcasecmp(pp->question[1].domain, p0.question[0].domain)) { 762 | LOG_DEBUG("FROM: %s should not overwrite response since question not ok", ntop6(from->sin6_addr)); 763 | LOG_DEBUG("FROM: domain %s %s", pp->question[1].domain, p0.question[0].domain); 764 | return -1; 765 | } 766 | 767 | LOG_DEBUG("%04x do_dns_backward parse copy: %d/%d/%d/%d", 768 | p0.head.ident, p0.head.question, p0.head.answer, p0.head.author, p0.head.addon); 769 | 770 | p0.question[0] = pp->question[0]; 771 | 772 | p0.head.flags |= NSFLAG_QR; 773 | p0.head.flags |= NSFLAG_RA; 774 | p0.head.flags |= NSFLAG_RD; 775 | p0.head.flags &= ~NSFLAG_RCODE; 776 | p0.answer[0].klass = NSCLASS_INET; 777 | 778 | if (p0.head.answer > 0) { 779 | p0.answer[0].domain = p0.question[0].domain; 780 | int allow_mask = 0; 781 | for (i = 0; i < p0.head.answer; i++) { 782 | p0.answer[i].ttl = 7200; 783 | if (p0.head.addon) { 784 | allow_mask = 1; 785 | if (p0.answer[i].type == NSTYPE_A) 786 | memset(p0.answer[i].value, 127, 4); 787 | if (p0.answer[i].type == NSTYPE_AAAA) 788 | memset(p0.answer[i].value, 0xfe, 16); 789 | if (p0.answer[i].type == NSTYPE_CNAME) 790 | *(char **)p0.answer[i].value = "chinazone.603030.xyz"; 791 | } 792 | } 793 | if (*qc->cname && allow_mask) { 794 | char buf[356]; 795 | p0.answer[0].type = NSTYPE_CNAME; 796 | snprintf(buf, sizeof(buf), "%s.oil.603030.xyz", qc->cname); 797 | struct dns_alias *alias = (struct dns_alias *)p0.answer[0].value; 798 | alias->name = add_domain(&p0, buf); 799 | p0.head.answer = 1; 800 | } 801 | 802 | if (p0.head.flags & NSFLAG_RD) p0.head.flags |= NSFLAG_RA; 803 | p0.head.flags |= NSFLAG_AA; 804 | p0.head.flags &= ~NSFLAG_ZERO; 805 | 806 | if (!allow_mask) { 807 | qc->server_status |= SS_REJECTED; 808 | // cache_add(p0.question[0].domain, allow_mask? STATUS_ALLOW: STATUS_REJECT); 809 | cache_add(p0.question[0].domain, STATUS_REJECT); 810 | } 811 | } else if (p0.question[0].type == NSTYPE_AAAA 812 | || p0.question[0].type == NSTYPE_A) { 813 | p0.answer[0].domain = p0.question[0].domain; 814 | p0.answer[0].type = p0.question[0].type; 815 | p0.answer[0].ttl = 7200; 816 | if (p0.question[0].type == NSTYPE_A) 817 | memset(p0.answer[0].value, 127, 4); 818 | if (p0.question[0].type == NSTYPE_AAAA) 819 | memset(p0.answer[0].value, 0xfe, 16); 820 | 821 | if (*qc->cname) { 822 | char buf[356]; 823 | p0.answer[0].type = NSTYPE_CNAME; 824 | snprintf(buf, sizeof(buf), "%s.oil.603030.xyz", qc->cname); 825 | struct dns_alias *alias = (struct dns_alias *)p0.answer[0].value; 826 | alias->name = add_domain(&p0, buf); 827 | } 828 | 829 | // cache_add(p0.question[0].domain, STATUS_ALLOW); 830 | 831 | if (p0.head.flags & NSFLAG_RD) p0.head.flags |= NSFLAG_RA; 832 | p0.head.flags |= NSFLAG_AA; 833 | p0.head.flags &= ~NSFLAG_ZERO; 834 | p0.head.answer = 1; 835 | } 836 | 837 | for (i = 0; i < p0.head.author; i++) { 838 | if (p0.author[i].type == NSTYPE_SOA) { 839 | p0.author[i].domain = "oil.603030.xyz"; 840 | p0.author[i].ttl = 7200; 841 | } 842 | } 843 | 844 | p0.head.addon = 0; 845 | p0.head.author = 0; 846 | 847 | int all_flasgs = (SS_ONEED| SS_CHINA); 848 | LOG_DEBUG("server_status: %x :%s\n", qc->server_status, qc->cname); 849 | if ((qc->server_status & all_flasgs) == all_flasgs) { 850 | dns_sendto(ctx->sockfd, &p0, &qc->from, sizeof(qc->from)); 851 | if ((~qc->server_status & SS_REJECTED) && 0 == *qc->cname) 852 | cache_add(p0.question[0].domain, STATUS_ALLOW); 853 | } 854 | 855 | return 0; 856 | } 857 | 858 | int main(int argc, char *argv[]) 859 | { 860 | int retval; 861 | int outfd, sockfd; 862 | struct sockaddr_in6 myaddr; 863 | struct sockaddr * paddr = (struct sockaddr *)&myaddr; 864 | 865 | struct sockaddr_in6 myaddr6; 866 | struct sockaddr * paddr1 = (struct sockaddr *)&myaddr6; 867 | 868 | setenv("NAMESERVER", "2408:4009:501::2", 0); 869 | setenv("LOCALADDR6", "2001:470:66:22a::2", 0); 870 | setenv("ROOTSERVER", "::ffff:192.41.162.30", 0); 871 | setenv("FOURONE", "::ffff:101:101", 0); 872 | 873 | _ain6.sin6_family = AF_INET6; 874 | _ain6.sin6_port = htons(53); 875 | inet_pton(AF_INET6, getenv("ROOTSERVER"), &_ain6.sin6_addr); 876 | 877 | _cin6.sin6_family = AF_INET6; 878 | _cin6.sin6_port = htons(53); 879 | inet_pton(AF_INET6, getenv("FOURONE"), &_cin6.sin6_addr); 880 | 881 | outfd = socket(AF_INET6, SOCK_DGRAM, 0); 882 | assert(outfd != -1); 883 | 884 | myaddr.sin6_family = AF_INET6; 885 | myaddr.sin6_port = htons(51623); 886 | myaddr.sin6_addr = in6addr_any; 887 | retval = bind(outfd, paddr, sizeof(myaddr)); 888 | assert(retval != -1); 889 | 890 | sockfd = socket(AF_INET6, SOCK_DGRAM, 0); 891 | assert(sockfd != -1); 892 | 893 | myaddr6.sin6_family = AF_INET6; 894 | myaddr6.sin6_port = htons(53); 895 | // myaddr6.sin6_addr.s_addr = INADDR_ANY; 896 | inet_pton(AF_INET6, getenv("LOCALADDR6"), &myaddr6.sin6_addr); 897 | retval = bind(sockfd, paddr1, sizeof(myaddr6)); 898 | assert(retval != -1); 899 | 900 | int count; 901 | char buf[2048]; 902 | fd_set readfds = {}; 903 | socklen_t addrl = 0; 904 | struct sockaddr_in6 dnsaddr; 905 | 906 | struct dns_context c0 = { 907 | .outfd = outfd, 908 | .sockfd = sockfd, 909 | .echofd = -1, 910 | .dnslen = sizeof(dnsaddr), 911 | }; 912 | 913 | dnsaddr.sin6_family = AF_INET6; 914 | dnsaddr.sin6_port = htons(53); 915 | inet_pton(AF_INET6, getenv("NAMESERVER"), &dnsaddr.sin6_addr); 916 | 917 | c0.dnsaddr = &dnsaddr; 918 | LOG_DEBUG("nsaddr %p pointer %p %d", c0.dnsaddr, &dnsaddr, htons(dnsaddr.sin6_port)); 919 | 920 | const struct sockaddr_in6 *inp = &dnsaddr; 921 | LOG_DEBUG("dns_build af=%d port=%d %s", inp->sin6_family, htons(inp->sin6_port), inet_ntop(AF_INET6, &inp->sin6_addr, buf, sizeof(buf))); 922 | 923 | _predefine_resource_record[0].domain = "oil.603030.xyz"; 924 | _predefine_resource_record[0].type = NSTYPE_NS; 925 | *(char **)_predefine_resource_record[0].value = "ns2.603030.xyz"; 926 | 927 | do { 928 | FD_ZERO(&readfds); 929 | FD_SET(outfd, &readfds); 930 | FD_SET(sockfd, &readfds); 931 | 932 | retval = select(sockfd + 2, &readfds, 0, 0, 0); 933 | if (retval == -1) { 934 | LOG_DEBUG("select failure: %s", strerror(errno)); 935 | break; 936 | } 937 | 938 | if (FD_ISSET(outfd, &readfds)) { 939 | addrl = sizeof(myaddr); 940 | count = recvfrom(outfd, buf, sizeof(buf), 0, paddr, &addrl); 941 | count > 0 || LOG_DEBUG("outfd is readable count=%d", count); 942 | if (count > 0) do_dns_backward(&c0, buf, count, &myaddr); 943 | } 944 | 945 | if (FD_ISSET(sockfd, &readfds)) { 946 | addrl = sizeof(myaddr6); 947 | count = recvfrom(sockfd, buf, sizeof(buf), 0, paddr1, &addrl); 948 | count > 0 || LOG_DEBUG("sockfd is readable: %d", count); 949 | if (count > 0) do_dns_forward(&c0, buf, count, &myaddr6); 950 | } 951 | 952 | } while (retval >= 0); 953 | 954 | close(sockfd); 955 | close(outfd); 956 | 957 | return 0; 958 | } 959 | -------------------------------------------------------------------------------- /dns_resolver_ng.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "dnsproto.h" 20 | #include "tx_debug.h" 21 | #include "subnet_api.h" 22 | 23 | #ifdef WIN32 24 | #include 25 | #else 26 | #include 27 | #include 28 | #include 29 | #include 30 | #define closesocket close 31 | #endif 32 | 33 | static char addrbuf[256]; 34 | #define ntop6(addr) inet_ntop(AF_INET6, &addr, addrbuf, sizeof(addrbuf)) 35 | #define ntop6p(addr) inet_ntop(AF_INET6, addr, addrbuf, sizeof(addrbuf)) 36 | #define MIN(a, b) ((a) < (b)? (a): (b)) 37 | 38 | struct dns_resource _predefine_resource_record[] = { 39 | { 40 | .type = NSTYPE_SOA, 41 | .klass = NSCLASS_INET, 42 | .ttl = 86400, 43 | .len = 4, 44 | .flags = 0, 45 | .domain = "_dummy", 46 | .value = {110, 42, 145, 164}}, 47 | { 48 | .type = NSTYPE_NS, 49 | .klass = NSCLASS_INET, 50 | .ttl = 86400, 51 | .len = 8, 52 | .flags = 0, 53 | .domain = "_dummy", 54 | .value = {110, 42, 145, 164}}, 55 | { 56 | .type = NSTYPE_A, 57 | .klass = NSCLASS_INET, 58 | .ttl = 36000, 59 | .len = 4, 60 | .flags = 0, 61 | .domain = "mtalk.google.com", 62 | .value = {110, 42, 145, 164}}, 63 | { 64 | .type = NSTYPE_AAAA, 65 | .klass = NSCLASS_INET, 66 | .ttl = 36000, 67 | .len = 4, 68 | .flags = 0, 69 | .domain = "mtalk.google.com", 70 | .value = {0x24, 04, 0x68, 0, 0x40, 8, 0xc, 0x02, 0, 0, 0, 0, 0, 0, 0, 0xbc}}, 71 | { 72 | .type = NSTYPE_AAAA, 73 | .klass = NSCLASS_INET, 74 | .ttl = 36000, 75 | .len = 4, 76 | .flags = 0, 77 | .domain = "ipv4only.arpa", 78 | .value = {110, 42, 145, 164}}, 79 | }; 80 | 81 | #define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array)) 82 | const char _nat64_prefix[] = "2002:1769:c6bd:ffff::8.8.8.8"; 83 | 84 | int fetch_predefine_resource_record(struct dns_parser *parser) 85 | { 86 | int found = 0; 87 | struct dns_resource *res; 88 | struct dns_question *que = &parser->question[0]; 89 | size_t domain_plen = strlen(que->domain); 90 | 91 | for (int i = 0; i < ARRAY_SIZE(_predefine_resource_record); i++) { 92 | 93 | if (MAX_RECORD_COUNT <= parser->head.answer) { 94 | break; 95 | } 96 | 97 | res = &_predefine_resource_record[i]; 98 | if ((res->type == que->type) && strcasecmp(res->domain, que->domain) == 0) { 99 | int index = parser->head.answer++; 100 | parser->answer[index].type = res->type; 101 | parser->answer[index].klass = res->klass; 102 | parser->answer[index].flags = res->flags; 103 | parser->answer[index].ttl = res->ttl; 104 | memcpy(parser->answer[index].value, res->value, sizeof(res->value)); 105 | parser->answer[index].domain = add_domain(parser, que->domain); 106 | } 107 | 108 | if (/* res->type == NSTYPE_ANY &&*/ strcasecmp(que->domain, res->domain) == 0) { 109 | found = 1; 110 | } 111 | } 112 | 113 | return (parser->head.answer > 0) || (found == 1); 114 | } 115 | 116 | struct dns_cname { 117 | const char *alias; 118 | }; 119 | 120 | #define NSCLASS_INET 0x01 121 | #define NSFLAG_RD 0x0100 122 | 123 | struct dns_context { 124 | int outfd; 125 | int sockfd; 126 | 127 | socklen_t dnslen; 128 | struct sockaddr_in6 *dnslocal; 129 | 130 | socklen_t dnslen1; 131 | struct sockaddr_in6 *dnsremote; 132 | }; 133 | 134 | #define FLAG_REMOTE 0x1 135 | #define FLAG_LOCAL 0x2 136 | #define FLAG_ALL 0x3 137 | #define FLAG_BLOCK_IPV4 0x4 138 | #define FLAG_ZERO_IDENT 0x8 139 | 140 | struct dns_incoming { 141 | uint16_t index; 142 | uint8_t flags; 143 | uint8_t length; 144 | uint8_t body[96]; 145 | }; 146 | 147 | #if 0 148 | struct dns_outgoing { 149 | }; 150 | #endif 151 | 152 | struct dns_switcher { 153 | char *domain; 154 | uint8_t near_out_A, near_got_A; 155 | uint8_t pure_out_A, pure_got_A; 156 | uint8_t near_out_AAAA, near_got_AAAA; 157 | uint8_t pure_out_AAAA, pure_got_AAAA; 158 | 159 | struct dns_incoming incomings[4]; 160 | }; 161 | 162 | struct dns_query_context { 163 | int flags; 164 | int updated; 165 | int preference; 166 | int score_board_id; 167 | 168 | int nat64_pref; 169 | uint8_t oil_addr[16]; 170 | 171 | struct sockaddr_in6 from; 172 | struct dns_parser parser; 173 | }; 174 | 175 | static struct dns_query_context _orig_list[0x1000]; 176 | 177 | #define PREFERENCE_LOCAL_NAT64 100 178 | #define PREFERENCE_REMOTE_NAT64 4 179 | #define PREFERENCE_NON_LOCAL_NAT64 6 180 | #define PREFERENCE_NON_REMOTE_NAT64 6 181 | 182 | #define PREFERENCE_LOCAL_IPV4 1 183 | #define PREFERENCE_LOCAL_IPV6 20 184 | 185 | #define PREFERENCE_REMOTE_IPV6 5 186 | #define PREFERENCE_REMOTE_IPV4 4 187 | 188 | #define PREFERENCE_NON_LOCAL_IPV4 5 189 | #define PREFERENCE_NON_LOCAL_IPV6 50 190 | #define PREFERENCE_NON_REMOTE_IPV6 50 191 | #define PREFERENCE_NON_REMOTE_IPV4 50 192 | 193 | struct dns_score_board { 194 | int checking; 195 | uint16_t ipv6_offset; 196 | uint16_t ipv4_offset; 197 | 198 | time_t ipv4_atime; 199 | time_t ipv6_atime; 200 | 201 | int updated; 202 | int preference; 203 | }; 204 | 205 | static struct dns_score_board _hash_src[0x10000]; 206 | 207 | static int dns_parser_copy(struct dns_parser *dst, const struct dns_parser *src) 208 | { 209 | static uint8_t _qc_hold[2048]; 210 | size_t len = dns_build(src, _qc_hold, sizeof(_qc_hold)); 211 | return dns_parse(dst, _qc_hold, len) == NULL; 212 | } 213 | 214 | static int dns_sendto(int outfd, struct dns_parser *parser, const struct sockaddr *to, size_t tolen) 215 | { 216 | ssize_t len; 217 | uint8_t _hold[2048]; 218 | 219 | len = dns_build(parser, _hold, sizeof(_hold)); 220 | 221 | const struct sockaddr_in6 *inp = (const struct sockaddr_in6 *)to; 222 | if (len != -1) 223 | len = sendto(outfd, _hold, len, 0, to, tolen); 224 | else 225 | LOG_DEBUG("dns_build %d", len); 226 | 227 | LOG_DEBUG("dns_build bytes %d %d %d %s", len, inp->sin6_family, htons(inp->sin6_port), ntop6(inp->sin6_addr)); 228 | return len; 229 | } 230 | 231 | struct subnet_info { 232 | uint16_t tag; // 0x0008 233 | uint16_t len; 234 | uint16_t family; 235 | uint8_t source_netmask; 236 | uint8_t scope_netmask; 237 | uint8_t addr[16]; 238 | }; 239 | 240 | #define NS_IPV6 2 241 | #define NS_IPV4 1 242 | 243 | // china mobile 117.143.102.0/24 244 | const static struct subnet_info subnet4_data = { 245 | 0x08, sizeof(subnet4_data), NS_IPV4, 24, 0, {117, 143, 102, 0} 246 | }; 247 | 248 | static int add_client_subnet(struct dns_parser *p0, uint8_t *optbuf, const struct subnet_info *info) 249 | { 250 | #ifndef DISABLE_SUBNET 251 | 252 | int have_edns = 0; 253 | struct dns_resource *res = NULL; 254 | struct subnet_info info0 = *info; 255 | 256 | int prefixlen = info->source_netmask;//+ info->scope_netmask; 257 | size_t subnet_len = 8 + ((7 + prefixlen) >> 3); 258 | 259 | info0.tag = htons(info->tag); 260 | info0.family = htons(info->family); 261 | info0.len = htons(4 + ((7 + prefixlen) >> 3)); 262 | 263 | for (int i = 0; i < p0->head.addon; i++) { 264 | res = &p0->addon[i]; 265 | if (res->type != NSTYPE_OPT) { 266 | continue; 267 | } 268 | 269 | if (res->domain == NULL || *res->domain == 0) { 270 | size_t len = res->len; 271 | const uint8_t * valp = *(const uint8_t **)res->value; 272 | struct tagheader {uint16_t tag; uint16_t len; } tag0; 273 | 274 | while (len > sizeof(tag0)) { 275 | memcpy(&tag0, valp, sizeof(tag0)); 276 | if (len < sizeof(tag0) + htons(tag0.len)) break; 277 | const uint8_t *hold = valp; 278 | valp += sizeof(tag0) + htons(tag0.len); 279 | len -= (sizeof(tag0) + htons(tag0.len)); 280 | if (tag0.tag == htons(0x0008)) { 281 | const uint8_t * valp0 = *(const uint8_t **)res->value; 282 | memcpy(optbuf, valp0, (hold - valp0)); 283 | memcpy(optbuf + (hold - valp0), valp, len); 284 | 285 | memcpy(optbuf + (hold - valp0) + len, &info0, subnet_len); 286 | *(void **)res->value = optbuf; 287 | res->len = len + (hold - valp0) + subnet_len; 288 | have_edns = 1; 289 | break; 290 | } 291 | } 292 | 293 | if (have_edns == 0) { 294 | const uint8_t * valp = *(const uint8_t **)res->value; 295 | 296 | memcpy(optbuf, &info0, subnet_len); 297 | memcpy(optbuf + subnet_len, valp, res->len); 298 | 299 | *(void **)res->value = optbuf; 300 | res->len += subnet_len; 301 | have_edns = 1; 302 | } 303 | } 304 | } 305 | 306 | if (p0->head.addon < MAX_RECORD_COUNT && have_edns == 0) { 307 | res = &p0->addon[p0->head.addon++]; 308 | 309 | res->domain = ""; 310 | res->klass = 0x1000; 311 | res->type = NSTYPE_OPT; 312 | res->ttl = 0; 313 | res->len = subnet_len; 314 | memcpy(optbuf, &info0, subnet_len); 315 | *(const void **)res->value = optbuf; 316 | } 317 | #endif 318 | 319 | return 0; 320 | } 321 | 322 | static uint16_t dns_hash(const void *up, const char *domain) 323 | { 324 | int i; 325 | uint32_t total = 0; 326 | const uint16_t *ptr = (const uint16_t *)up; 327 | 328 | for (i = 0; i < 8; i ++) { 329 | total += ptr[i]; 330 | } 331 | 332 | ptr = (const uint16_t *)domain; 333 | while ((*ptr & 0xff00) && (*ptr & 0xff)) { 334 | total += *ptr++; 335 | } 336 | 337 | if (*ptr & htons(0xff00)) { 338 | total += *ptr++; 339 | } 340 | 341 | total = (total & 0xffff) + (total >> 16); 342 | return (total & 0xffff) + (total >> 16); 343 | } 344 | 345 | int update_preference(struct dns_score_board *, struct dns_query_context *, int ); 346 | 347 | int do_dns_forward(struct dns_context *ctx, void *buf, int count, struct sockaddr_in6 *from) 348 | { 349 | struct dns_parser p0; 350 | struct dns_parser *pp; 351 | 352 | pp = dns_parse(&p0, buf, count); 353 | if (pp == NULL) { 354 | LOG_DEBUG("do_dns_forward parse failure"); 355 | return 0; 356 | } 357 | 358 | if (p0.head.flags & 0x8000) { 359 | LOG_DEBUG("FROM: %s this is not query", "nothing"); 360 | return -1; 361 | } 362 | 363 | if (fetch_predefine_resource_record(&p0)) { 364 | LOG_DEBUG("prefetch: %s", p0.question[0].domain); 365 | p0.head.flags |= NSFLAG_QR; 366 | dns_sendto(ctx->sockfd, &p0, (struct sockaddr *)from, sizeof(*from)); 367 | return 0; 368 | } 369 | 370 | int retval = 0; 371 | int offset = (p0.head.ident & 0xfff); 372 | 373 | struct dns_parser *p1 = NULL; 374 | struct dns_query_context *qc = &_orig_list[offset]; 375 | 376 | p1 = &qc->parser; 377 | if (memcmp(from, &qc->from, sizeof(from)) || 378 | (qc->flags & FLAG_ALL) == FLAG_ALL|| 379 | (qc->preference == MIN(PREFERENCE_LOCAL_IPV4, PREFERENCE_LOCAL_IPV6)) || 380 | memcmp(&p1->head, &p0.head, sizeof(p0.head)) || 381 | strcmp(p0.question[0].domain, p1->question[0].domain) || 382 | p0.question[0].type != p1->question[0].type) { 383 | memset(qc, 0, sizeof(*qc)); 384 | qc->from = *from; 385 | qc->preference = 100; 386 | qc->nat64_pref = 100; 387 | qc->score_board_id = -1; 388 | 389 | dns_parser_copy(&qc->parser, &p0); 390 | } 391 | 392 | p0.head.flags |= NSFLAG_RD; 393 | 394 | char optbuf[124]; 395 | struct dns_parser u1 = {}; 396 | dns_parser_copy(&u1, &p0); 397 | add_client_subnet(&u1, optbuf, &subnet4_data); 398 | 399 | retval = dns_sendto(ctx->outfd, &u1, (struct sockaddr *)ctx->dnslocal, ctx->dnslen1); 400 | if (retval == -1) { 401 | LOG_DEBUG("dns_sendto failure: %s %p", strerror(errno), ctx->dnslocal); 402 | return 0; 403 | } 404 | 405 | if (ctx->dnsremote == NULL) { 406 | return 0; 407 | } 408 | 409 | retval = sendto(ctx->outfd, buf, count, 0, (struct sockaddr *)ctx->dnsremote, ctx->dnslen); 410 | if (retval == -1) { 411 | LOG_DEBUG("dns_sendto failure: %s %p", strerror(errno), ctx->dnsremote); 412 | return 0; 413 | } 414 | 415 | if (p0.question[0].type != NSTYPE_AAAA && p0.question[0].type != NSTYPE_A) { 416 | LOG_DEBUG("skip dns type: %s type:%d", p0.question[0].domain, p0.question[0].type); 417 | return 0; 418 | } 419 | 420 | int hashid = dns_hash(&from->sin6_addr, p0.question[0].domain); 421 | LOG_DEBUG("FROM %s:%d QUERY: %s type:%d hashid:%04x ident:%04x", 422 | ntop6(from->sin6_addr), htons(from->sin6_port), 423 | p0.question[0].domain, p0.question[0].type, hashid, p0.head.ident); 424 | 425 | struct dns_score_board *sb = &_hash_src[hashid]; 426 | 427 | if (p0.question[0].type == NSTYPE_A) { 428 | sb->ipv4_offset = p0.head.ident; 429 | sb->ipv4_atime = time(NULL); 430 | } else if (p0.question[0].type == NSTYPE_AAAA) { 431 | sb->ipv6_offset = p0.head.ident; 432 | sb->ipv6_atime = time(NULL); 433 | } 434 | 435 | int i; 436 | struct dns_parser *pp4, *pp6; 437 | struct dns_query_context *qc6, *qc4; 438 | 439 | qc4 = &_orig_list[sb->ipv4_offset & 0xfff]; 440 | qc6 = &_orig_list[sb->ipv6_offset & 0xfff]; 441 | 442 | pp4 = &qc4->parser; 443 | pp6 = &qc6->parser; 444 | if (sb->ipv4_atime + 7 < time(NULL) || sb->ipv6_atime + 7 < time(NULL) || 445 | memcmp(&qc4->from.sin6_addr, &qc6->from.sin6_addr, sizeof(from->sin6_addr))) { 446 | LOG_DEBUG("time expire detected: %s", p0.question[0].domain); 447 | sb->preference = 100; 448 | sb->checking = 0; 449 | } else { 450 | assert(pp4->head.question > 0 && pp6->head.question > 0); 451 | if (strcasecmp(pp4->question[0].domain, pp6->question[0].domain) || 452 | pp4->question[0].type != NSTYPE_A || pp6->question[0].type != NSTYPE_AAAA) { 453 | LOG_DEBUG("getaddrinfo failure domain: %s %s", pp4->question[0].domain, pp6->question[0].domain); 454 | sb->preference = 100; 455 | sb->checking = 0; 456 | } else { 457 | LOG_DEBUG("getaddrinfo detected: %s %d", pp4->question[0].domain, qc4->nat64_pref); 458 | qc4->score_board_id = hashid; 459 | qc6->score_board_id = hashid; 460 | sb->checking = 1; 461 | update_preference(sb, qc4, qc4->preference); 462 | update_preference(sb, qc6, qc6->preference); 463 | 464 | struct dns_resource *res; 465 | pp4 = &qc4->parser; 466 | if (qc4->nat64_pref < qc6->preference && 467 | !strcmp(pp4->question[0].domain, pp6->question[0].domain)) { 468 | update_preference(sb, qc6, qc4->nat64_pref); 469 | dns_parser_copy(pp6, pp4); 470 | pp6->head.ident = sb->ipv6_offset; 471 | pp6->question[0].type = NSTYPE_AAAA; 472 | 473 | uint32_t ipv4 = 0; 474 | for (i = 0; i < pp6->head.answer; i++) { 475 | res = &pp6->answer[i]; 476 | if (res->type == NSTYPE_A) { 477 | res->type = NSTYPE_AAAA; 478 | memcpy(&ipv4, res->value, 4); 479 | inet_pton(AF_INET6, getenv("NAT64_PREFIX"), res->value); 480 | memcpy(pp6->answer[i].value + 12, &ipv4, 4); 481 | } 482 | } 483 | } 484 | } 485 | } 486 | 487 | return 0; 488 | } 489 | 490 | struct dns_soa { 491 | const char *nameserver; 492 | const char *email; 493 | }; 494 | 495 | static int dump_resource(const char *title, struct dns_resource *res) 496 | { 497 | 498 | if (res->type == NSTYPE_TXT) { 499 | LOG_DEBUG("%s %s TXT", title, res->domain); 500 | } else if (res->type == NSTYPE_A) { 501 | LOG_DEBUG("%s %s A %s", title, res->domain, inet_ntoa(*(struct in_addr *)res->value)); 502 | } else if (res->type == NSTYPE_NS) { 503 | LOG_DEBUG("%s %s NS %s", title, res->domain, *(const char **)res->value); 504 | } else if (res->type == NSTYPE_SRV) { 505 | LOG_DEBUG("%s %s SRV %p", title, res->domain, *(const char **)res->value); 506 | } else if (res->type == NSTYPE_SOA) { 507 | LOG_DEBUG("%s %s SOA %s", title, res->domain, *(const char **)res->value); 508 | } else if (res->type == NSTYPE_AAAA) { 509 | LOG_DEBUG("%s %s AAAA %s", title, res->domain, ntop6(res->value)); 510 | } else if (res->type == NSTYPE_CNAME) { 511 | LOG_DEBUG("%s %s CNAME %s", title, res->domain, *(const char **)res->value); 512 | } else { 513 | LOG_DEBUG("%s %s UNKOWN %d", title, res->domain, res->type); 514 | } 515 | 516 | return 0; 517 | } 518 | 519 | 520 | static int setup_route(const void* ip, int family) 521 | { 522 | uint64_t val = 0; 523 | subnet_t *subnet = NULL; 524 | char sTarget[128], sNetwork[128]; 525 | 526 | 527 | inet_ntop(family, ip, sTarget, sizeof(sTarget)); 528 | if (family == AF_INET6) { 529 | val = htonll(*(uint64_t *)ip); 530 | subnet = lookupRoute6(val); 531 | } else if (family == AF_INET) { 532 | val = htonll(*(uint64_t *)ip) & 0xffffffff00000000ull; 533 | subnet = lookupRoute4(val); 534 | } 535 | 536 | if (subnet != 0 && subnet->flags == 0) { 537 | char sCmd[1024]; 538 | uint64_t network = htonll(subnet->network); 539 | 540 | inet_ntop(family, &network, sNetwork, sizeof(sNetwork)); 541 | fprintf(stderr, "ACTIVE network: %s/%d by %s\n", sNetwork, subnet->prefixlen, sTarget); 542 | subnet->flags = 1; 543 | 544 | if (family == AF_INET) { 545 | sprintf(sCmd, "ipset add ipsec %s/%d", sNetwork, subnet->prefixlen); 546 | fprintf(stderr, "CMD=%s\n", sCmd); 547 | system(sCmd); 548 | } else { 549 | sprintf(sCmd, "ip -6 route add %s/%d dev tun0 mtu 1400 table 100", sNetwork, subnet->prefixlen); 550 | fprintf(stderr, "CMD=%s\n", sCmd); 551 | system(sCmd); 552 | } 553 | 554 | return 0; 555 | } 556 | 557 | return 0; 558 | } 559 | 560 | int update_preference(struct dns_score_board *sb, struct dns_query_context *qc, int preference) 561 | { 562 | assert(qc); 563 | assert(preference > 0); 564 | 565 | if (preference <= qc->preference) { 566 | qc->preference = preference; 567 | qc->updated = 1; 568 | } 569 | 570 | if (sb == NULL) 571 | return 0; 572 | 573 | if (preference <= sb->preference) { 574 | sb->preference = preference; 575 | sb->updated = 1; 576 | } 577 | 578 | assert(qc->preference >= sb->preference); 579 | return 0; 580 | } 581 | 582 | int do_dns_backward(struct dns_context *ctx, void *buf, int count, struct sockaddr_in6 *from) 583 | { 584 | struct dns_parser p0; 585 | struct dns_parser *pp; 586 | struct dns_resource *res; 587 | 588 | pp = dns_parse(&p0, buf, count); 589 | if (pp == NULL || p0.head.question == 0) { 590 | LOG_DEBUG("do_dns_backward parse failure: %s", ntop6(from->sin6_addr)); 591 | return 0; 592 | } 593 | 594 | if (~p0.head.flags & 0x8000) { 595 | LOG_DEBUG("FROM: %s this is not response", ntop6(from->sin6_addr)); 596 | return -1; 597 | } 598 | 599 | int i, found = 0, nat64_pref = 100; 600 | int offset = (p0.head.ident & 0xfff); 601 | struct dns_query_context *qc = &_orig_list[offset]; 602 | 603 | pp = &qc->parser; 604 | 605 | LOG_DEBUG("record: %s %d %s %x", ntop6(from->sin6_addr), p0.head.answer, p0.question[0].domain, p0.question[0].type); 606 | 607 | const char *suffixes = strstr(p0.question[0].domain, ".oil.cootail.com"); 608 | 609 | size_t domainlen = strlen(pp->question[0].domain); 610 | if (strncmp(p0.question[0].domain, pp->question[0].domain, domainlen)) { 611 | LOG_DEBUG("reponse out of day: %s %s type=%d %s", ntop6(qc->from), pp->question[0].domain, p0.question[0].type, p0.question[0].domain); 612 | return 0; 613 | } 614 | 615 | if (p0.question[0].type != NSTYPE_AAAA && p0.question[0].type != NSTYPE_A) { 616 | LOG_DEBUG("skip domain: %s %s type=%d", ntop6(qc->from), pp->question[0].domain, p0.question[0].type); 617 | dns_sendto(ctx->sockfd, &p0, (struct sockaddr *)&qc->from, sizeof(qc->from)); 618 | return 0; 619 | } 620 | 621 | if (suffixes == NULL && strcmp(pp->question[0].domain, p0.question[0].domain)) { 622 | LOG_DEBUG("skip domain: %s %s %s type=%d", ntop6(qc->from), pp->question[0].domain, p0.question[0].domain, p0.question[0].type); 623 | return 0; 624 | } 625 | 626 | struct dns_score_board *sb = NULL; 627 | if (qc->score_board_id != -1) { 628 | sb = &_hash_src[qc->score_board_id]; 629 | 630 | struct dns_query_context *qc4, *qc6; 631 | qc4 = &_orig_list[sb->ipv4_offset & 0xfff]; 632 | qc6 = &_orig_list[sb->ipv6_offset & 0xfff]; 633 | 634 | sb->updated = 0; 635 | if (sb->ipv4_offset != p0.head.ident 636 | && sb->ipv6_offset != p0.head.ident) { 637 | sb = NULL; 638 | } else if (!sb->checking || qc4->score_board_id != qc6->score_board_id) { 639 | LOG_DEBUG("checking %d qc4_score_board_id %x qc6_score_board_id %x", 640 | sb->checking, qc4->score_board_id, qc6->score_board_id); 641 | sb = NULL; 642 | } else { 643 | int type = p0.question[0].type; 644 | int ipv4 = sb->ipv4_offset == p0.head.ident && type == NSTYPE_A; 645 | int ipv6 = sb->ipv6_offset == p0.head.ident && type == NSTYPE_AAAA; 646 | assert(ipv4 || ipv6 || suffixes); 647 | } 648 | 649 | } 650 | 651 | qc->updated = 0; 652 | if (suffixes && strcmp(suffixes, ".oil.cootail.com") == 0) { 653 | if (!strcmp(p0.question[0].domain, pp->question[0].domain)) { 654 | LOG_DEBUG("skip fake domain: %s %s", ntop6(qc->from), pp->question[0].domain); 655 | dns_sendto(ctx->sockfd, &p0, (struct sockaddr *)&qc->from, sizeof(qc->from)); 656 | return 0; 657 | } 658 | 659 | if (pp->head.answer < 0) { 660 | LOG_DEBUG("something wrong: %s", ntop6(qc->from), pp->question[0].domain); 661 | assert(0); 662 | return 0; 663 | } 664 | 665 | char buf[256]; 666 | uint32_t type = 0; 667 | for (i = 0; i < p0.head.answer; i++) { 668 | res = &p0.answer[i]; 669 | if (res->type == NSTYPE_A) { 670 | memcpy(&type, res->value, 4); 671 | break; 672 | } 673 | } 674 | 675 | dns_parser_copy(&p0, pp); 676 | memcpy(p0.answer[0].value, qc->oil_addr, 16); 677 | p0.answer[0].domain = p0.question[0].domain; 678 | p0.answer[0].type = p0.question[0].type; 679 | p0.answer[0].klass = NSCLASS_INET; 680 | p0.answer[0].ttl = 3600; 681 | p0.head.answer = 0; 682 | 683 | if (type == 0x7f7f7f7f) { 684 | p0.head.answer = 1; 685 | } else { 686 | qc->flags |= FLAG_BLOCK_IPV4; 687 | } 688 | 689 | from->sin6_addr = ctx->dnslocal->sin6_addr; 690 | LOG_DEBUG("oil detect finish: %x %s\n", type, pp->question[0].domain); 691 | } else if (ctx->dnsremote != NULL && p0.head.answer == 1 && 692 | IN6_ARE_ADDR_EQUAL(&from->sin6_addr, &ctx->dnslocal->sin6_addr)) { 693 | uint64_t val; 694 | subnet_t *subnet = NULL; 695 | int preference = 100; 696 | char temp[256]; 697 | 698 | res = &p0.answer[0]; 699 | val = htonll(*(uint64_t *)res->value); 700 | memcpy(qc->oil_addr, res->value, 16); 701 | 702 | if (res->type == NSTYPE_A) { 703 | subnet = lookupRoute4(val); 704 | preference = subnet? PREFERENCE_NON_LOCAL_IPV4: PREFERENCE_LOCAL_IPV4; 705 | nat64_pref = subnet? PREFERENCE_NON_LOCAL_NAT64: PREFERENCE_LOCAL_NAT64; 706 | } else if (res->type == NSTYPE_AAAA) { 707 | subnet = lookupRoute6(val); 708 | preference = subnet? PREFERENCE_NON_LOCAL_IPV6: PREFERENCE_LOCAL_IPV6; 709 | } 710 | 711 | preference = MIN(preference, nat64_pref); 712 | if ((sb && preference <= sb->preference) || (!sb && preference <= qc->preference)) { 713 | snprintf(temp, sizeof(temp), "%s.oil.cootail.com", p0.question[0].domain); 714 | LOG_DEBUG("start oil detect %s %x %x %x %s", temp, p0.head.ident, pp->head.ident, p0.head.flags, temp); 715 | memset(&p0, 0, sizeof(p0)); 716 | 717 | p0.head.flags |= NSFLAG_RD; 718 | p0.head.ident = pp->head.ident; 719 | p0.head.question = 1; 720 | p0.question[0].domain = add_domain(&p0, temp); 721 | p0.question[0].klass = NSCLASS_INET; 722 | p0.question[0].type = NSTYPE_A; 723 | 724 | dns_sendto(ctx->outfd, &p0, (struct sockaddr *)ctx->dnslocal, ctx->dnslen1); 725 | return 0; 726 | } 727 | 728 | LOG_DEBUG("ignore oil detect %s %x %x %x", p0.question[0].domain, p0.head.ident, pp->head.ident, p0.head.flags); 729 | p0.head.answer = 0; 730 | nat64_pref = 100; 731 | } 732 | 733 | if (IN6_ARE_ADDR_EQUAL(&from->sin6_addr, &ctx->dnslocal->sin6_addr) && ctx->dnsremote) { 734 | uint64_t val; 735 | subnet_t *subnet = 0; 736 | 737 | for (i = 0; i < p0.head.answer; i++) { 738 | res = &p0.answer[i]; 739 | 740 | val = htonll(*(uint64_t *)res->value); 741 | if (res->type == NSTYPE_A) { 742 | subnet = lookupRoute4(val); 743 | update_preference(sb, qc, subnet? PREFERENCE_NON_LOCAL_IPV4: PREFERENCE_LOCAL_IPV4); 744 | nat64_pref = subnet? PREFERENCE_NON_LOCAL_NAT64: PREFERENCE_LOCAL_NAT64; 745 | } else if (res->type == NSTYPE_AAAA) { 746 | subnet = lookupRoute6(val); 747 | update_preference(sb, qc, subnet? PREFERENCE_NON_LOCAL_IPV6: PREFERENCE_LOCAL_IPV6); 748 | } 749 | } 750 | 751 | qc->flags |= FLAG_LOCAL; 752 | } 753 | 754 | if (ctx->dnsremote && IN6_ARE_ADDR_EQUAL(&from->sin6_addr, &ctx->dnsremote->sin6_addr)) { 755 | uint64_t val; 756 | subnet_t *subnet = 0; 757 | 758 | for (i = 0; i < p0.head.answer; i++) { 759 | res = &p0.answer[i]; 760 | 761 | val = htonll(*(uint64_t *)res->value); 762 | if (res->type == NSTYPE_A) { 763 | subnet = lookupRoute4(val); 764 | update_preference(sb, qc, subnet? PREFERENCE_REMOTE_IPV4: PREFERENCE_NON_REMOTE_IPV4); 765 | nat64_pref = subnet? PREFERENCE_REMOTE_NAT64: PREFERENCE_NON_REMOTE_NAT64; 766 | } else if (res->type == NSTYPE_AAAA) { 767 | subnet = lookupRoute6(val); 768 | update_preference(sb, qc, subnet? PREFERENCE_REMOTE_IPV6: PREFERENCE_NON_LOCAL_IPV6); 769 | } 770 | } 771 | 772 | qc->flags |= FLAG_REMOTE; 773 | } 774 | 775 | check_finish: 776 | 777 | if (sb != NULL && !sb->updated) { 778 | LOG_DEBUG("ignore this response %s type:%d answer:%d", p0.question[0].domain, p0.question[0].type, p0.head.answer); 779 | } 780 | 781 | if (sb != NULL) { 782 | struct dns_parser *pp4, *pp6; 783 | struct dns_query_context *qc4, *qc6; 784 | qc4 = &_orig_list[sb->ipv4_offset & 0xfff]; 785 | qc6 = &_orig_list[sb->ipv6_offset & 0xfff]; 786 | 787 | pp6 = &qc6->parser; 788 | pp4 = &qc4->parser; 789 | assert(qc4->score_board_id == qc6->score_board_id); 790 | assert(suffixes || !strcasecmp(pp4->question[0].domain, pp6->question[0].domain)); 791 | 792 | assert(sb->updated <= qc->updated); 793 | if (qc->updated) { 794 | assert(!sb || sb->preference <= qc->preference); 795 | dns_parser_copy(pp, &p0); 796 | } 797 | 798 | if (nat64_pref < qc6->preference) { 799 | pp6 = &qc6->parser; 800 | pp4 = &qc4->parser; 801 | for (i = 0; i < p0.head.answer; i++) { 802 | pp6->answer[i] = p0.answer[i]; 803 | if (p0.question[0].domain == p0.answer[i].domain) { 804 | pp6->answer[i].domain = pp6->question[0].domain; 805 | } else { 806 | pp6->answer[i].domain = add_domain(pp6, p0.answer[i].domain); 807 | } 808 | 809 | struct dns_cname *cname = (struct dns_cname *)pp6->answer[i].value; 810 | if (pp6->answer[i].type == NSTYPE_CNAME) { 811 | cname->alias = add_domain(pp6, cname->alias); 812 | } else { 813 | pp6->answer[i].type = NSTYPE_AAAA; 814 | inet_pton(AF_INET6, getenv("NAT64_PREFIX"), cname); 815 | memcpy(pp6->answer[i].value + 12, p0.answer[i].value, 4); 816 | } 817 | } 818 | LOG_DEBUG("update %s nat64 answer: %d pref64:%d", p0.question[0].domain, p0.head.answer, nat64_pref); 819 | pp6->head.answer = p0.head.answer; 820 | update_preference(sb, qc6, nat64_pref); 821 | if (nat64_pref < 100) qc4->nat64_pref = nat64_pref; 822 | } 823 | 824 | LOG_DEBUG("pref:%d flags4:%x flags6:%x pref4:%d pref6:%d ", sb->preference, qc4->flags, qc6->flags, qc4->preference, qc6->preference); 825 | if (sb->preference == MIN(PREFERENCE_LOCAL_IPV4, PREFERENCE_LOCAL_IPV6) || 826 | ((qc4->flags & FLAG_ALL) == FLAG_ALL && (qc6->flags & FLAG_ALL) == FLAG_ALL)) { 827 | 828 | if (qc4->preference == sb->preference || (qc4->flags & FLAG_ALL) == FLAG_ALL) { 829 | pp = &qc4->parser; 830 | pp->head.flags |= NSFLAG_QR; 831 | int save = pp->head.answer; 832 | pp->head.answer = (!(qc4->flags & FLAG_BLOCK_IPV4) && (qc4->preference <= sb->preference))? pp->head.answer: 0; 833 | LOG_DEBUG("ipv4: d=%s n=%d nat64_pref=%d", pp->question[0].domain, pp->head.answer, nat64_pref); 834 | dns_sendto(ctx->sockfd, pp, (struct sockaddr *)&qc4->from, sizeof(qc4->from)); 835 | pp->head.answer = save; 836 | if (nat64_pref < 100) qc4->nat64_pref = nat64_pref; 837 | } 838 | 839 | if (qc6->preference == sb->preference || (qc6->flags & FLAG_ALL) == FLAG_ALL) { 840 | pp = &qc6->parser; 841 | pp->head.flags |= NSFLAG_QR; 842 | pp->head.answer = (qc6->preference <= sb->preference)? pp->head.answer: 0; 843 | LOG_DEBUG("ipv6: d=%s n=%d", pp->question[0].domain, pp->head.answer); 844 | dns_sendto(ctx->sockfd, pp, (struct sockaddr *)&qc6->from, sizeof(qc6->from)); 845 | } 846 | } 847 | 848 | return 0; 849 | } 850 | 851 | pp = &qc->parser; 852 | if (qc->updated) { 853 | pp = &p0; 854 | } 855 | 856 | if ((qc->flags & FLAG_ALL) == FLAG_ALL || (qc->preference == MIN(PREFERENCE_LOCAL_IPV4, PREFERENCE_LOCAL_IPV6))) { 857 | LOG_DEBUG("dns_sendto %s %s:%d d=%s n=%d type:%d pref:%d", pp->question[0].type==NSTYPE_AAAA?"ipv6":"ipv4", ntop6(qc->from.sin6_addr), 858 | htons(qc->from.sin6_port), pp->question[0].domain, pp->head.answer, pp->question[0].type, qc->preference); 859 | pp->head.flags |= NSFLAG_QR; 860 | struct dns_parser modp0; 861 | dns_parser_copy(&modp0, pp); 862 | 863 | if (qc->flags & FLAG_BLOCK_IPV4 && pp->question[0].type == NSTYPE_A) 864 | modp0.head.answer = 0; 865 | 866 | modp0.head.flags &= ~(NSFLAG_RA|NSFLAG_RD); 867 | for (int i = 0; i < modp0.head.answer; i++) { 868 | struct dns_resource *res = &modp0.answer[i]; 869 | res->ttl = 5; 870 | } 871 | 872 | dns_sendto(ctx->sockfd, &modp0, (struct sockaddr *)&qc->from, sizeof(qc->from)); 873 | pp = NULL; 874 | } 875 | 876 | if ((nat64_pref <= qc->nat64_pref) && (qc->updated || pp == NULL)) { 877 | dns_parser_copy(&qc->parser, &p0); 878 | if (nat64_pref < 100) qc->nat64_pref = nat64_pref; 879 | } else if (qc->updated) { 880 | dns_parser_copy(&qc->parser, &p0); 881 | } else { 882 | assert(nat64_pref >= qc->nat64_pref); 883 | assert(!qc->updated || pp == NULL); 884 | } 885 | 886 | return 0; 887 | } 888 | 889 | #define get_score_id(ifname) if_nametoindex(ifname) 890 | 891 | int main(int argc, char *argv[]) 892 | { 893 | int retval; 894 | int outfd, sockfd; 895 | struct sockaddr_in6 myaddr; 896 | struct sockaddr * paddr = (struct sockaddr *)&myaddr; 897 | 898 | struct sockaddr_in6 myaddr6; 899 | struct sockaddr * paddr6 = (struct sockaddr *)&myaddr6; 900 | 901 | outfd = socket(AF_INET6, SOCK_DGRAM, 0); 902 | assert(outfd != -1); 903 | 904 | myaddr.sin6_family = AF_INET6; 905 | myaddr.sin6_port = 0; 906 | myaddr.sin6_addr = in6addr_any; 907 | retval = bind(outfd, paddr, sizeof(myaddr)); 908 | assert(retval != -1); 909 | 910 | sockfd = socket(AF_INET6, SOCK_DGRAM, 0); 911 | assert(sockfd != -1); 912 | 913 | myaddr6.sin6_family = AF_INET6; 914 | myaddr6.sin6_port = htons(53); 915 | myaddr6.sin6_addr = in6addr_loopback; 916 | #if 0 917 | myaddr6.sin6_addr = in6addr_any; 918 | #endif 919 | setenv("NAT64_PREFIX", _nat64_prefix, 0); 920 | setenv("BINDLOCAL", "::ffff:127.0.0.111", 0); 921 | 922 | char _dummy[256], *ifp; 923 | strcpy(_dummy, getenv("BINDLOCAL")); 924 | if (NULL != (ifp = strchr(_dummy, '%'))) { 925 | *ifp ++ = 0; 926 | myaddr6.sin6_scope_id = get_score_id(ifp); 927 | inet_pton(AF_INET6, _dummy, &myaddr6.sin6_addr); 928 | } else { 929 | myaddr6.sin6_scope_id = 0; 930 | inet_pton(AF_INET6, _dummy, &myaddr6.sin6_addr); 931 | } 932 | 933 | retval = bind(sockfd, paddr6, sizeof(myaddr6)); 934 | assert(retval != -1); 935 | 936 | int count; 937 | char buf[2048]; 938 | fd_set readfds = {}; 939 | socklen_t addrl = 0; 940 | struct sockaddr_in6 dnslocal; 941 | struct sockaddr_in6 dnsremote; 942 | 943 | struct dns_context c0 = { 944 | .outfd = outfd, 945 | .sockfd = sockfd, 946 | .dnslen = sizeof(dnslocal), 947 | .dnslen1 = sizeof(dnsremote), 948 | }; 949 | 950 | setenv("NAMESERVER", "::ffff:8.8.8.8", 0); 951 | 952 | dnslocal.sin6_family = AF_INET6; 953 | dnslocal.sin6_port = htons(53); 954 | inet_pton(AF_INET6, getenv("NAMESERVER"), &dnslocal.sin6_addr); 955 | c0.dnslocal = &dnslocal; 956 | 957 | dnsremote.sin6_family = AF_INET6; 958 | dnsremote.sin6_port = htons(53); 959 | 960 | c0.dnsremote = NULL; 961 | if (getenv("REMOTESERVER") != NULL) { 962 | inet_pton(AF_INET6, getenv("REMOTESERVER"), &dnsremote.sin6_addr); 963 | if (!IN6_ARE_ADDR_EQUAL(&dnsremote.sin6_addr, &dnslocal.sin6_addr)) 964 | c0.dnsremote = &dnsremote; 965 | } 966 | 967 | const struct sockaddr_in6 *inp = (const struct sockaddr_in6 *)&dnslocal; 968 | LOG_DEBUG("dns_build bytes %d %d %d %s", 0, inp->sin6_family, htons(inp->sin6_port), ntop6(inp->sin6_addr)); 969 | 970 | const char *ipv4only = "ipv4only.arpa"; 971 | for (int i = 0; i < ARRAY_SIZE(_predefine_resource_record); i++) { 972 | 973 | struct dns_resource * res = &_predefine_resource_record[i]; 974 | if ((res->type == NSTYPE_AAAA) && strcasecmp(res->domain, ipv4only) == 0) { 975 | inet_pton(AF_INET6, getenv("NAT64_PREFIX"), res->value); 976 | break; 977 | } 978 | } 979 | 980 | do { 981 | FD_ZERO(&readfds); 982 | FD_SET(outfd, &readfds); 983 | FD_SET(sockfd, &readfds); 984 | 985 | retval = select(sockfd + 1, &readfds, 0, 0, 0); 986 | if (retval == -1) { 987 | LOG_DEBUG("select failure: %s", strerror(errno)); 988 | break; 989 | } 990 | 991 | if (FD_ISSET(sockfd, &readfds)) { 992 | addrl = sizeof(myaddr6); 993 | count = recvfrom(sockfd, buf, sizeof(buf), 0, paddr6, &addrl); 994 | count > 0 || LOG_DEBUG("sockfd is readable"); 995 | assert(count > 0); 996 | do_dns_forward(&c0, buf, count, &myaddr6); 997 | continue; 998 | } 999 | 1000 | if (FD_ISSET(outfd, &readfds)) { 1001 | addrl = sizeof(myaddr); 1002 | count = recvfrom(outfd, buf, sizeof(buf), 0, paddr, &addrl); 1003 | count > 0 || LOG_DEBUG("outfd is readable"); 1004 | assert(count > 0); 1005 | do_dns_backward(&c0, buf, count, &myaddr); 1006 | } 1007 | 1008 | } while (retval >= 0); 1009 | 1010 | close(sockfd); 1011 | close(outfd); 1012 | 1013 | return 0; 1014 | } 1015 | --------------------------------------------------------------------------------