1 /** @file
2
3 A brief file description
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 /*****************************************************************************
25 *
26 * SplitDNS.cc - Implementation of "split" DNS (as the name says)
27 *
28 *
29 ****************************************************************************/
30
31 #include "tscore/ink_platform.h"
32 #include "tscore/Tokenizer.h"
33 #include "tscore/Filenames.h"
34
35 #include <sys/types.h>
36 #include "P_SplitDNS.h"
37 #include "tscore/MatcherUtils.h"
38 #include "tscore/HostLookup.h"
39
40 /* --------------------------------------------------------------
41 this file is built using "ParentSelection.cc as a template.
42 ------------- ------------------------------------------------- */
43
44 /* --------------------------------------------------------------
45 globals
46 -------------------------------------------------------------- */
47 static const char modulePrefix[] = "[SplitDNS]";
48
49 ConfigUpdateHandler<SplitDNSConfig> *SplitDNSConfig::splitDNSUpdate = nullptr;
50
51 static ClassAllocator<DNSRequestData> DNSReqAllocator("DNSRequestDataAllocator");
52
53 /* --------------------------------------------------------------
54 used by a lot of protocols. We do not have dest ip in most
55 cases.
56 -------------------------------------------------------------- */
57 const matcher_tags sdns_dest_tags = {"dest_host", "dest_domain", nullptr, "url_regex", "url", nullptr, true};
58
59 /* --------------------------------------------------------------
60 config Callback Prototypes
61 -------------------------------------------------------------- */
62 enum SplitDNSCB_t {
63 SDNS_FILE_CB,
64 SDNS_ENABLE_CB,
65 };
66
67 static const char *SDNSResultStr[] = {"DNSServer_Undefined", "DNSServer_Specified", "DNSServer_Failed"};
68
69 int SplitDNSConfig::m_id = 0;
70 int SplitDNSConfig::gsplit_dns_enabled = 0;
71 int splitDNSFile_CB(const char *name, RecDataT data_type, RecData data, void *cookie);
72 Ptr<ProxyMutex> SplitDNSConfig::dnsHandler_mutex;
73
74 /* --------------------------------------------------------------
75 SplitDNSResult::SplitDNSResult()
76 -------------------------------------------------------------- */
SplitDNSResult()77 inline SplitDNSResult::SplitDNSResult() {}
78
79 /* --------------------------------------------------------------
80 SplitDNS::SplitDNS()
81 -------------------------------------------------------------- */
SplitDNS()82 SplitDNS::SplitDNS() {}
83
~SplitDNS()84 SplitDNS::~SplitDNS()
85 {
86 if (m_DNSSrvrTable) {
87 delete m_DNSSrvrTable;
88 }
89 }
90
91 /* --------------------------------------------------------------
92 SplitDNSConfig::acquire()
93 -------------------------------------------------------------- */
94 SplitDNS *
acquire()95 SplitDNSConfig::acquire()
96 {
97 return static_cast<SplitDNS *>(configProcessor.get(SplitDNSConfig::m_id));
98 }
99
100 /* --------------------------------------------------------------
101 SplitDNSConfig::release()
102 -------------------------------------------------------------- */
103 void
release(SplitDNS * params)104 SplitDNSConfig::release(SplitDNS *params)
105 {
106 configProcessor.release(SplitDNSConfig::m_id, params);
107 }
108
109 /* --------------------------------------------------------------
110 SplitDNSConfig::startup()
111 -------------------------------------------------------------- */
112 void
startup()113 SplitDNSConfig::startup()
114 {
115 // startup just check gsplit_dns_enabled
116 REC_ReadConfigInt32(gsplit_dns_enabled, "proxy.config.dns.splitDNS.enabled");
117 SplitDNSConfig::splitDNSUpdate = new ConfigUpdateHandler<SplitDNSConfig>();
118 SplitDNSConfig::splitDNSUpdate->attach("proxy.config.cache.splitdns.filename");
119 }
120
121 /* --------------------------------------------------------------
122 SplitDNSConfig::reconfigure()
123 -------------------------------------------------------------- */
124 void
reconfigure()125 SplitDNSConfig::reconfigure()
126 {
127 if (0 == gsplit_dns_enabled) {
128 return;
129 }
130
131 Note("%s loading ...", ts::filename::SPLITDNS);
132
133 SplitDNS *params = new SplitDNS;
134
135 params->m_SplitDNSlEnable = gsplit_dns_enabled;
136 params->m_DNSSrvrTable = new DNS_table("proxy.config.dns.splitdns.filename", modulePrefix, &sdns_dest_tags);
137
138 if (nullptr == params->m_DNSSrvrTable || (0 == params->m_DNSSrvrTable->getEntryCount())) {
139 Warning("Failed to load %s - No NAMEDs provided! Disabling SplitDNS", ts::filename::SPLITDNS);
140 gsplit_dns_enabled = 0;
141 delete params;
142 return;
143 }
144 params->m_numEle = params->m_DNSSrvrTable->getEntryCount();
145
146 if (nullptr != params->m_DNSSrvrTable->getHostMatcher() && nullptr == params->m_DNSSrvrTable->getReMatcher() &&
147 nullptr == params->m_DNSSrvrTable->getIPMatcher() && 4 >= params->m_numEle) {
148 HostLookup *pxHL = params->m_DNSSrvrTable->getHostMatcher()->getHLookup();
149 params->m_pxLeafArray = pxHL->get_leaf_array();
150 params->m_bEnableFastPath = true;
151 }
152
153 m_id = configProcessor.set(m_id, params);
154
155 if (is_debug_tag_set("splitdns_config")) {
156 SplitDNSConfig::print();
157 }
158
159 Note("%s finished loading", ts::filename::SPLITDNS);
160 }
161
162 /* --------------------------------------------------------------
163 SplitDNSConfig::print()
164 -------------------------------------------------------------- */
165 void
print()166 SplitDNSConfig::print()
167 {
168 SplitDNS *params = SplitDNSConfig::acquire();
169
170 Debug("splitdns_config", "DNS Server Selection Config");
171 Debug("splitdns_config", "\tEnabled=%d", params->m_SplitDNSlEnable);
172
173 params->m_DNSSrvrTable->Print();
174 SplitDNSConfig::release(params);
175 }
176
177 /* --------------------------------------------------------------
178 SplitDNS::getDNSRecord()
179 -------------------------------------------------------------- */
180 void *
getDNSRecord(const char * hostname)181 SplitDNS::getDNSRecord(const char *hostname)
182 {
183 Debug("splitdns", "Called SplitDNS::getDNSRecord(%s)", hostname);
184
185 DNSRequestData *pRD = DNSReqAllocator.alloc();
186 pRD->m_pHost = hostname;
187
188 SplitDNSResult res;
189 findServer(pRD, &res);
190
191 DNSReqAllocator.free(pRD);
192
193 if (DNS_SRVR_SPECIFIED == res.r) {
194 return (void *)&(res.m_rec->m_servers);
195 }
196
197 Debug("splitdns", "Fail to match a valid splitdns rule, fallback to default dns resolver");
198 return nullptr;
199 }
200
201 /* --------------------------------------------------------------
202 SplitDNS::findServer()
203 -------------------------------------------------------------- */
204 void
findServer(RequestData * rdata,SplitDNSResult * result)205 SplitDNS::findServer(RequestData *rdata, SplitDNSResult *result)
206 {
207 DNS_table *tablePtr = m_DNSSrvrTable;
208 SplitDNSRecord *rec;
209
210 ink_assert(result->r == DNS_SRVR_UNDEFINED);
211
212 if (m_SplitDNSlEnable == 0) {
213 result->r = DNS_SRVR_UNDEFINED;
214 return;
215 }
216
217 result->m_rec = nullptr;
218 result->m_line_number = 0xffffffff;
219
220 /* ---------------------------
221 the 'alleged' fast path ...
222 --------------------------- */
223 if (m_bEnableFastPath && m_pxLeafArray) {
224 SplitDNSRecord *data_ptr = nullptr;
225 char *pHost = const_cast<char *>(rdata->get_host());
226 if (nullptr == pHost) {
227 Warning("SplitDNS: No host to match !");
228 return;
229 }
230
231 int len = strlen(pHost);
232 int n = std::min(static_cast<size_t>(m_numEle), m_pxLeafArray->size());
233 for (int i = 0; i < n; i++) {
234 const HostLeaf &pxHL = m_pxLeafArray->at(i);
235
236 if (false == pxHL.isNot && static_cast<int>(pxHL.match.size()) > len) {
237 continue;
238 }
239
240 int idx = len - pxHL.match.size();
241 char *pH = &pHost[idx];
242 const char *pMatch = pxHL.match.data();
243 char cNot = *pMatch;
244
245 if ('!' == cNot) {
246 pMatch++;
247 }
248
249 int res = memcmp(pH, pMatch, pxHL.match.size());
250
251 if ((0 != res && '!' == cNot) || (0 == res && '!' != cNot)) {
252 data_ptr = static_cast<SplitDNSRecord *>(pxHL.opaque_data);
253 data_ptr->UpdateMatch(result, rdata);
254 break;
255 }
256 }
257 } else {
258 tablePtr->Match(rdata, result);
259 }
260
261 rec = result->m_rec;
262 if (rec == nullptr) {
263 result->r = DNS_SRVR_UNDEFINED;
264 return;
265 } else {
266 result->r = DNS_SRVR_SPECIFIED;
267 }
268
269 if (is_debug_tag_set("splitdns_config")) {
270 const char *host = rdata->get_host();
271
272 switch (result->r) {
273 case DNS_SRVR_FAIL:
274 Debug("splitdns_config", "Result for %s was %s", host, SDNSResultStr[result->r]);
275 break;
276 case DNS_SRVR_SPECIFIED:
277 Debug("splitdns_config", "Result for %s was dns servers", host);
278 result->m_rec->Print();
279 break;
280 default:
281 // DNS_SRVR_UNDEFINED
282 break;
283 }
284 }
285 }
286
287 /* --------------------------------------------------------------
288 SplitDNSRecord::ProcessDNSHosts()
289 -------------------------------------------------------------- */
290 const char *
ProcessDNSHosts(char * val)291 SplitDNSRecord::ProcessDNSHosts(char *val)
292 {
293 Tokenizer pTok(",; \t\r");
294 int numTok;
295 int port = 0;
296 int totsz = 0, sz = 0;
297
298 numTok = pTok.Initialize(val, SHARE_TOKS);
299 if (MAXNS < numTok) {
300 numTok = MAXNS;
301 Warning("Only first %d DNS servers are tracked", numTok);
302 }
303 if (numTok == 0) {
304 return "No servers specified";
305 }
306
307 /* ------------------------------------------------
308 Allocate the servers array and Loop through the
309 set of servers specified
310 ------------------------------------------------ */
311 for (int i = 0; i < numTok; i++) {
312 const char *current = pTok[i];
313 char *tmp = const_cast<char *>(strchr(current, ':'));
314 // coverity[secure_coding]
315 if (tmp != nullptr && sscanf(tmp + 1, "%d", &port) != 1) {
316 return "Malformed DNS port";
317 }
318
319 /* ----------------------------------------
320 Make sure that is no garbage beyond the
321 server port
322 ---------------------------------------- */
323 if (tmp) {
324 char *scan = tmp + 1;
325 for (; *scan != '\0' && ParseRules::is_digit(*scan); scan++) {
326 ;
327 }
328 for (; *scan != '\0' && ParseRules::is_wslfcr(*scan); scan++) {
329 ;
330 }
331
332 if (*scan != '\0') {
333 return "Garbage trailing entry or invalid separator";
334 }
335
336 if (tmp - current > (MAXDNAME - 1)) {
337 return "DNS server name (ip) is too long";
338 } else if (tmp - current == 0) {
339 return "server string is empty";
340 }
341 *tmp = 0;
342 }
343
344 if (0 != ats_ip_pton(current, &m_servers.x_server_ip[i].sa)) {
345 return "invalid IP address given for a DNS server";
346 }
347
348 ats_ip_port_cast(&m_servers.x_server_ip[i].sa) = htons(port ? port : NAMESERVER_PORT);
349
350 if ((MAXDNAME * 2 - 1) > totsz) {
351 sz = strlen(current);
352 memcpy((m_servers.x_dns_ip_line + totsz), current, sz);
353 totsz += sz;
354 }
355 }
356
357 m_dnsSrvr_cnt = numTok;
358 return nullptr;
359 }
360
361 /* --------------------------------------------------------------
362 SplitDNSRecord::ProcessDefDomain()
363 -------------------------------------------------------------- */
364 const char *
ProcessDefDomain(char * val)365 SplitDNSRecord::ProcessDefDomain(char *val)
366 {
367 Tokenizer pTok(",; \t\r");
368 int numTok;
369
370 numTok = pTok.Initialize(val, SHARE_TOKS);
371
372 if (numTok > 1) {
373 return "more than one default domain name specified";
374 }
375
376 if (numTok == 0) {
377 return "no default domain name specified";
378 }
379
380 int len = 0;
381 if (pTok[0] && 0 != (len = strlen(pTok[0]))) {
382 memcpy(&m_servers.x_def_domain[0], pTok[0], len);
383 m_servers.x_def_domain[len] = '\0';
384 }
385
386 return nullptr;
387 }
388
389 /* --------------------------------------------------------------
390 SplitDNSRecord::ProcessDomainSrchList()
391 -------------------------------------------------------------- */
392 const char *
ProcessDomainSrchList(char * val)393 SplitDNSRecord::ProcessDomainSrchList(char *val)
394 {
395 Tokenizer pTok(",; \t\r");
396 int numTok;
397 int sz = 0;
398 char *pSp = nullptr;
399
400 numTok = pTok.Initialize(val, SHARE_TOKS);
401
402 if (numTok == 0) {
403 return "No servers specified";
404 }
405
406 pSp = &m_servers.x_domain_srch_list[0];
407
408 for (int i = 0; i < numTok; i++) {
409 const char *current = pTok[i];
410 int cnt = sz += strlen(current);
411
412 if (MAXDNAME - 1 < sz) {
413 break;
414 }
415
416 memcpy(pSp, current, cnt);
417 pSp += (cnt + 1);
418 }
419
420 m_domain_srch_list = numTok;
421 return nullptr;
422 }
423
424 /* --------------------------------------------------------------
425 SplitDNSRecord::Init()
426
427 matcher_line* line_info - contains parsed label/value pairs
428 of the current split.config line
429 -------------------------------------------------------------- */
430 Result
Init(matcher_line * line_info)431 SplitDNSRecord::Init(matcher_line *line_info)
432 {
433 const char *errPtr = nullptr;
434
435 this->line_num = line_info->line_num;
436 for (int i = 0; i < MATCHER_MAX_TOKENS; i++) {
437 char *label = line_info->line[0][i];
438 char *val = line_info->line[1][i];
439
440 if (label == nullptr) {
441 continue;
442 }
443
444 if (strcasecmp(label, "def_domain") == 0) {
445 if (nullptr != (errPtr = ProcessDefDomain(val))) {
446 return Result::failure("%s %s at line %d", modulePrefix, errPtr, line_num);
447 }
448 line_info->line[0][i] = nullptr;
449 line_info->num_el--;
450 continue;
451 }
452
453 if (strcasecmp(label, "search_list") == 0) {
454 if (nullptr != (errPtr = ProcessDomainSrchList(val))) {
455 return Result::failure("%s %s at line %d", modulePrefix, errPtr, line_num);
456 }
457 line_info->line[0][i] = nullptr;
458 line_info->num_el--;
459 continue;
460 }
461
462 if (strcasecmp(label, "named") == 0) {
463 if (nullptr != (errPtr = ProcessDNSHosts(val))) {
464 return Result::failure("%s %s at line %d", modulePrefix, errPtr, line_num);
465 }
466 line_info->line[0][i] = nullptr;
467 line_info->num_el--;
468 continue;
469 }
470 }
471
472 if (!ats_is_ip(&m_servers.x_server_ip[0].sa)) {
473 return Result::failure("%s No server specified in %s at line %d", modulePrefix, ts::filename::SPLITDNS, line_num);
474 }
475
476 DNSHandler *dnsH = new DNSHandler;
477 ink_res_state res = new ts_imp_res_state;
478
479 memset(res, 0, sizeof(ts_imp_res_state));
480 if ((-1 == ink_res_init(res, m_servers.x_server_ip, m_dnsSrvr_cnt, dns_search, m_servers.x_def_domain,
481 m_servers.x_domain_srch_list, nullptr))) {
482 char ab[INET6_ADDRPORTSTRLEN];
483 return Result::failure("Failed to build res record for the servers %s ...",
484 ats_ip_ntop(&m_servers.x_server_ip[0].sa, ab, sizeof ab));
485 }
486
487 dnsH->m_res = res;
488 dnsH->mutex = SplitDNSConfig::dnsHandler_mutex;
489 ats_ip_invalidate(&dnsH->ip.sa); // Mark to use default DNS.
490
491 m_servers.x_dnsH = dnsH;
492
493 SET_CONTINUATION_HANDLER(dnsH, &DNSHandler::startEvent_sdns);
494 eventProcessor.thread_group[ET_DNS]._thread[0]->schedule_imm(dnsH);
495
496 /* -----------------------------------------------------
497 Process any modifiers to the directive, if they exist
498 ----------------------------------------------------- */
499 if (line_info->num_el > 0) {
500 const char *tmp = ProcessModifiers(line_info);
501 if (tmp != nullptr) {
502 return Result::failure("%s %s at line %d in %s", modulePrefix, tmp, line_num, ts::filename::SPLITDNS);
503 }
504 }
505
506 return Result::ok();
507 }
508
509 /* --------------------------------------------------------------
510 SplitDNSRecord::UpdateMatch()
511 -------------------------------------------------------------- */
512 void
UpdateMatch(SplitDNSResult * result,RequestData *)513 SplitDNSRecord::UpdateMatch(SplitDNSResult *result, RequestData * /* rdata ATS_UNUSED */)
514 {
515 int last_number = result->m_line_number;
516
517 if ((last_number < 0) || (last_number > this->line_num)) {
518 result->m_rec = this;
519 result->m_line_number = this->line_num;
520
521 Debug("splitdns_config", "Matched with %p dns node from line %d", this, this->line_num);
522 }
523 }
524
525 /* --------------------------------------------------------------
526 SplitDNSRecord::Print()
527 -------------------------------------------------------------- */
528 void
Print() const529 SplitDNSRecord::Print() const
530 {
531 for (int i = 0; i < m_dnsSrvr_cnt; i++) {
532 char ab[INET6_ADDRPORTSTRLEN];
533 Debug("splitdns_config", " %s", ats_ip_ntop(&m_servers.x_server_ip[i].sa, ab, sizeof ab));
534 }
535 }
536
537 void
ink_split_dns_init(ts::ModuleVersion v)538 ink_split_dns_init(ts::ModuleVersion v)
539 {
540 static int init_called = 0;
541
542 ink_release_assert(v.check(SPLITDNS_MODULE_INTERNAL_VERSION));
543 if (init_called) {
544 return;
545 }
546
547 init_called = 1;
548 }
549