Skip to content

Commit a4cda4e

Browse files
author
Miaoren Xia
committed
add domain except list for ipv6First mode
1 parent c0cd2d7 commit a4cda4e

File tree

5 files changed

+107
-45
lines changed

5 files changed

+107
-45
lines changed

README.md

+14-7
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@
4747
"su" : "nobody",
4848
"enableCache": true,
4949
"enableTcp": true,
50-
"ipv6First": 1,
50+
"ipv6First": "OnlyForLocal",
51+
"Ipv6FirstExcept": [ "www.qq.com."],
5152
"gfwMode": true,
5253
"daemonMode": false,
5354
"severity": "info",
@@ -78,7 +79,10 @@
7879
<su>nobody</su>
7980
<enableCache>true</enableCache>
8081
<enableTcp>true</enableTcp>
81-
<ipv6First>1</ipv6First>
82+
<ipv6First>OnlyForLocal</ipv6First>
83+
<ipv6FirstExcept>
84+
<domain>www.qq.com.</domain>
85+
</ipv6FirstExcept>
8286
<gfwMode>true</gfwMode>
8387
<daemonMode>false</daemonMode>
8488
<severity>info</severity>
@@ -103,11 +107,14 @@
103107
- su \[optinal] : change usr account after start up
104108
- enableCache \[optional] : enable internal dns cache, recommand, default is false
105109
- enableTcp \[optional] : enable tcp query support, both local and remote, defulat is false
106-
- ipv6First \[optional] : ipv6 mode, force return ipv6 address when available, default is 0<br>
107-
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;three levels supported:<br>
108-
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0 : turn off this feature<br>
109-
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1 : only for domains in gfwlist<br>
110-
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 : for all domains<br>
110+
- ipv6First \[optional] : ipv6 mode, force return ipv6 address when available, default is Off<br>
111+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;four levels supported:<br>
112+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Off : turn off this feature<br>
113+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OnlyForLocal : only for domains not in gfwlist<br>
114+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OnlyForRemote : only for domains in gfwlist<br>
115+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Full : for all domains<br>
116+
117+
- ipv6FirstExcept \[optional] : domain list not affected by above ipv6First policy (support wildcard)
111118
- daemonMode \[optional]: become daemon after start up, default is false
112119
- severity \[optional]: verbose level for logging facility, default is info:<br>
113120
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;can be one of the following value:<br>

config-schema.xsd

+16-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@
1717
<xs:element name="statisticsFile" type="xs:string" default=""/>
1818
<xs:element name="enableCache" type="xs:boolean" default="false"/>
1919
<xs:element name="enableTcp" type="xs:boolean" default="false"/>
20-
<xs:element name="ipv6First" type="xs:integer" default="0"/>
20+
<xs:element name="ipv6First" type="ipv6FirstMode" default="Off"/>
21+
<xs:element name="ipv6FirstExcept">
22+
<xs:complexType>
23+
<xs:sequence minOccurs="1">
24+
<xs:element name="domain" type="xs:string"/>
25+
</xs:sequence>
26+
</xs:complexType>
27+
</xs:element>
2128
<xs:element name="gfwMode" type="xs:boolean" default="false"/>
2229
<xs:element name="daemonMode" type="xs:boolean" default="false"/>
2330
<xs:element name="severity" type="severity_level" default="info"/>
@@ -68,4 +75,12 @@
6875
<xs:enumeration value="fatal"/>
6976
</xs:restriction>
7077
</xs:simpleType>
78+
<xs:simpleType name="ipv6FirstMode">
79+
<xs:restriction base="xs:string">
80+
<xs:enumeration value="Off"/>
81+
<xs:enumeration value="OnlyForLocal"/>
82+
<xs:enumeration value="OnlyForRemote"/>
83+
<xs:enumeration value="Full"/>
84+
</xs:restriction>
85+
</xs:simpleType>
7186
</xs:schema>

src/Config.cpp

+66-37
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ std::unordered_map<std::string, boost::log::trivial::severity_level> Config::sev
3737
{boost::log::trivial::to_string(boost::log::trivial::fatal), boost::log::trivial::fatal}
3838
};
3939

40+
std::unordered_map<std::string, Config::IPv6Mode> Config::ipv6mode_map = {
41+
{"Off", Config::IPv6Mode::Off},
42+
{"OnlyForLocal", Config::IPv6Mode::OnlyForLocal},
43+
{"OnlyForRemote", Config::IPv6Mode::OnlyForRemote},
44+
{"Full", Config::IPv6Mode::Full},
45+
};
46+
4047
void Config::trimAll() {
4148
for (auto &local : locals) {
4249
boost::trim(local.address);
@@ -62,9 +69,9 @@ Config *Config::load_config_file(const std::string &config_filename) {
6269
} else {
6370
std::cerr << "Error type of config file (either json "
6471

65-
#ifdef ENABLE_XML
72+
#ifdef ENABLE_XML
6673
"or xml"
67-
#endif
74+
#endif
6875
" format)" << std::endl;
6976
exit(1);
7077
}
@@ -119,7 +126,7 @@ void fill_scope(Config::DnsRecord &dnsRecord, const std::string &scope_str) {
119126
try {
120127
number = boost::lexical_cast<int>(results[1].c_str());
121128
} catch (boost::bad_lexical_cast &e) {
122-
std::cerr << "mask length must be a integer: " << e.what() << std::endl;
129+
std::cerr << "mask length must be a integer: " << e.what() << std::endl;
123130
exit(4);
124131
}
125132

@@ -141,8 +148,8 @@ void fill_scope(Config::DnsRecord &dnsRecord, const std::string &scope_str) {
141148
}
142149
number--;
143150
}
144-
*((uint64_t *) &scope.scope_mask.mask6.s6_addr) = htobe64(mask_number_high);
145-
*((uint64_t *) (scope.scope_mask.mask6.s6_addr + 8)) = htobe64(mask_number_low);
151+
*((uint64_t * ) & scope.scope_mask.mask6.s6_addr) = htobe64(mask_number_high);
152+
*((uint64_t * )(scope.scope_mask.mask6.s6_addr + 8)) = htobe64(mask_number_low);
146153
// std::cout << mask_number_high << " " << mask_number_low << std::endl;
147154

148155
} else {
@@ -167,6 +174,7 @@ void fill_scope(Config::DnsRecord &dnsRecord, const std::string &scope_str) {
167174
}
168175

169176
#ifdef ENABLE_XML
177+
170178
Config *Config::load_xml_config(const std::string &filename) {
171179

172180

@@ -202,7 +210,23 @@ Config *Config::load_xml_config(const std::string &filename) {
202210

203211
config->enableCache = root.child("enableCache").text().as_bool(false);
204212
config->enableTcp = root.child("enableTcp").text().as_bool(false);
205-
config->ipv6First = static_cast<IPv6Mode >(root.child("ipv6First").text().as_int(1));
213+
214+
try {
215+
config->ipv6First = Config::ipv6mode_map.at(boost::trim_copy(std::string(root.child("ipv6First").text().as_string("Off"))));
216+
} catch (std::out_of_range &e) {
217+
std::cerr << "Unkown ipv6First Mode : " << e.what() << std::endl;
218+
exit(EXIT_FAILURE);
219+
}
220+
221+
222+
if (auto ipv6FirstExceptIter = root.child("ipv6FirstExcept"); !ipv6FirstExceptIter.empty()) {
223+
for (auto &domain : ipv6FirstExceptIter.children("domain")) {
224+
std::string domain_str = domain.text().as_string();
225+
boost::trim(domain_str);
226+
if (!domain_str.empty()) config->ipv6FirstExcept.insert(domain_str);
227+
}
228+
}
229+
206230
config->gfwMode = root.child("gfwMode").text().as_bool(false);
207231
config->daemonMode = root.child("daemonMode").text().as_bool(false);
208232

@@ -237,6 +261,10 @@ Config *Config::load_xml_config(const std::string &filename) {
237261
for (auto scope : scopes.children("scope")) {
238262
std::string scope_str = scope.text().as_string();
239263
boost::trim(scope_str);
264+
if (scope_str.empty()) {
265+
std::cerr << "scope is empty\n";
266+
exit(EXIT_FAILURE);
267+
}
240268
fill_scope(dnsRecord, scope_str);
241269
}
242270
}
@@ -253,6 +281,7 @@ Config *Config::load_xml_config(const std::string &filename) {
253281
return config;
254282

255283
}
284+
256285
#endif
257286

258287
Config *Config::load_json_config(const std::string &filename) {
@@ -273,23 +302,22 @@ Config *Config::load_json_config(const std::string &filename) {
273302

274303
config->enableCache = j.value("enableCache", false);
275304
config->enableTcp = j.value("enableTcp", false);
276-
switch (j.value("ipv6First", 1)) {
277-
case 0:
278-
config->ipv6First = IPv6Mode::Off;
279-
break;
280-
case 1:
281-
config->ipv6First = IPv6Mode::OnlyForRemote;
282-
break;
283-
case 2:
284-
config->ipv6First = IPv6Mode::Full;
285-
break;
286-
case 3:
287-
config->ipv6First = IPv6Mode::OnlyForLocal;
288-
break;
289-
default:
290-
std::cerr << "err type number of ipv6First mode" << std::endl;
291-
return nullptr;
305+
try {
306+
config->ipv6First = Config::ipv6mode_map.at(boost::trim_copy(std::string(j.value("ipv6First", "Off"))));
307+
} catch (std::out_of_range &e) {
308+
std::cerr << "Unkown ipv6First Mode : " << e.what() << std::endl;
309+
exit(EXIT_FAILURE);
310+
}
311+
312+
auto it = j.find("ipv6FirstExcept");
313+
if (it != j.end()) {
314+
for (auto &domain : *it) {
315+
std::string domain_str = domain;
316+
config->ipv6FirstExcept.insert(domain_str);
317+
}
292318
}
319+
320+
293321
config->gfwMode = j.value("gfwMode", false);
294322
config->daemonMode = j.value("daemonMode", false);
295323

@@ -312,7 +340,7 @@ Config *Config::load_json_config(const std::string &filename) {
312340
config->localnet_server_port = localnet.value("port", 53);
313341

314342

315-
auto it = j.find("mappings");
343+
it = j.find("mappings");
316344
if (it != j.end()) {
317345
for (auto &item : *it) {
318346
std::string type_str = item["type"];
@@ -361,23 +389,24 @@ bool Config::DnsRecord::match(struct sockaddr_storage &client_addr) {
361389
for (auto &scope : scopes) {
362390
switch (scope.scope_ss_family) {
363391
case AF_INET:
364-
if (client_addr.ss_family == AF_INET6) {
365-
if (IN6_IS_ADDR_V4MAPPED(&reinterpret_cast<sockaddr_in6 *>(&client_addr)->sin6_addr)) {
366-
if ((*reinterpret_cast<uint32_t *>(reinterpret_cast<sockaddr_in6 *>(
367-
&client_addr)->sin6_addr.s6_addr + 12) &
368-
scope.scope_mask.mask.s_addr) == scope.scope_addr.addr.s_addr) {
369-
return true;
370-
}
371-
}
372-
} else if (client_addr.ss_family == AF_INET) {
373-
if ((reinterpret_cast<sockaddr_in *>(&client_addr)->sin_addr.s_addr & scope.scope_mask.mask.s_addr) ==
374-
scope.scope_addr.addr.s_addr)
375-
return true;
376-
}
392+
if (client_addr.ss_family == AF_INET6) {
393+
if (IN6_IS_ADDR_V4MAPPED(&reinterpret_cast<sockaddr_in6 *>(&client_addr)->sin6_addr)) {
394+
if ((*reinterpret_cast<uint32_t *>(reinterpret_cast<sockaddr_in6 *>(
395+
&client_addr)->sin6_addr.s6_addr + 12) &
396+
scope.scope_mask.mask.s_addr) == scope.scope_addr.addr.s_addr) {
397+
return true;
398+
}
399+
}
400+
} else if (client_addr.ss_family == AF_INET) {
401+
if ((reinterpret_cast<sockaddr_in *>(&client_addr)->sin_addr.s_addr & scope.scope_mask.mask.s_addr) ==
402+
scope.scope_addr.addr.s_addr)
403+
return true;
404+
}
377405
break;
378406

379407
case AF_INET6:
380-
if (client_addr.ss_family == AF_INET6 and ! IN6_IS_ADDR_V4MAPPED(&reinterpret_cast<sockaddr_in6 *>(&client_addr)->sin6_addr) ) {
408+
if (client_addr.ss_family == AF_INET6 and
409+
!IN6_IS_ADDR_V4MAPPED(&reinterpret_cast<sockaddr_in6 *>(&client_addr)->sin6_addr)) {
381410

382411
uint64_t high = *reinterpret_cast<uint64_t *>(reinterpret_cast<sockaddr_in6 *>(&client_addr)->sin6_addr.s6_addr);
383412
uint64_t low = *reinterpret_cast<uint64_t *>(

src/Config.h

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <boost/log/trivial.hpp>
1111
#include <boost/bimap.hpp>
1212
#include <netinet/in.h>
13+
#include <unordered_set>
1314

1415
#include "config.h"
1516

@@ -27,6 +28,8 @@ class Config {
2728
OnlyForLocal = 3
2829
};
2930

31+
std::unordered_set<std::string> ipv6FirstExcept;
32+
3033
struct Local {
3134
std::string address;
3235

@@ -108,6 +111,7 @@ class Config {
108111
void trimAll();
109112

110113
static std::unordered_map<std::string, boost::log::trivial::severity_level> severity_level_map;
114+
static std::unordered_map<std::string, IPv6Mode > ipv6mode_map;
111115
};
112116

113117

src/DnsHandler.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ bool add_upstream(char * /* buf */, ssize_t /* n */, Upstream *upstream) {
8787
}
8888

8989
bool use_ipv6_lookup(const Upstream *upstream) {
90+
for (auto domain : config->ipv6FirstExcept) {
91+
for (auto &q : upstream->dns1.questions) {
92+
if (fnmatch(domain.c_str(), q.name.c_str(), FNM_CASEFOLD) == 0) {
93+
return false;
94+
}
95+
}
96+
}
9097
return (Config::IPv6Mode::Full == config->ipv6First or
9198
(upstream->dns1.use_localnet_dns_server ?
9299
config->ipv6First == Config::IPv6Mode::OnlyForLocal

0 commit comments

Comments
 (0)