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