1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 // Copyright (C) 2005-2013 Sourcefire, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License Version 2 as published
7 // by the Free Software Foundation.  You may not use, modify or distribute
8 // this program under any other version of the GNU General Public License.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 //--------------------------------------------------------------------------
19 
20 // detector_dns.cc author Sourcefire Inc.
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "detector_dns.h"
27 
28 #include "appid_config.h"
29 #include "appid_dns_session.h"
30 #include "app_info_table.h"
31 #include "application_ids.h"
32 
33 using namespace snort;
34 
35 #define MAX_DNS_HOST_NAME_LEN 253
36 
37 #define MAX_OPCODE     5
38 #define INVALID_OPCODE 3
39 
40 #define MAX_RCODE 10
41 
42 #define RCODE_NXDOMAIN 3
43 
44 #define DNS_LENGTH_FLAGS 0xC0
45 
46 #define PATTERN_A_REC      1
47 #define PATTERN_AAAA_REC  28
48 #define PATTERN_CNAME_REC  5
49 #define PATTERN_SRV_REC   33
50 #define PATTERN_TXT_REC   16
51 #define PATTERN_MX_REC    15
52 #define PATTERN_SOA_REC    6
53 #define PATTERN_NS_REC     2
54 #define PATTERN_ANY_REC  255
55 #define PATTERN_PTR_REC   12
56 
57 #pragma pack(1)
58 
59 struct DNSHeader
60 {
61     uint16_t id;
62 #if defined(SF_BIGENDIAN)
63     uint8_t QR : 1,
64         Opcode : 4,
65         AA : 1,
66         TC : 1,
67         RD : 1;
68     uint8_t RA : 1,
69         Z : 1,
70         AD : 1,
71         CD : 1,
72         RCODE : 4;
73 #else
74     uint8_t RD : 1,
75         TC : 1,
76         AA : 1,
77         Opcode : 4,
78         QR : 1;
79     uint8_t RCODE : 4,
80         CD : 1,
81         AD : 1,
82         Z : 1,
83         RA : 1;
84 #endif
85     uint16_t QDCount;
86     uint16_t ANCount;
87     uint16_t NSCount;
88     uint16_t ARCount;
89 };
90 
91 struct DNSTCPHeader
92 {
93     uint16_t length;
94 };
95 
96 struct DNSLabel
97 {
98     uint8_t len;
99     uint8_t name;
100 };
101 
102 struct DNSLabelPtr
103 {
104     uint16_t position;
105     uint8_t data;
106 };
107 
108 struct DNSLabelBitfield
109 {
110     uint8_t id;
111     uint8_t len;
112     uint8_t data;
113 };
114 
115 struct DNSQueryFixed
116 {
117     uint16_t QType;
118     uint16_t QClass;
119 };
120 
121 struct DNSAnswerData
122 {
123     uint16_t type;
124     uint16_t klass;
125     uint32_t ttl;
126     uint16_t r_len;
127 };
128 
129 #pragma pack()
130 
131 enum DNSState
132 {
133     DNS_STATE_QUERY,
134     DNS_STATE_RESPONSE
135 };
136 
137 struct ServiceDNSData
138 {
139     DNSState state;
140     uint16_t id;
141 };
142 
DnsTcpServiceDetector(ServiceDiscovery * sd)143 DnsTcpServiceDetector::DnsTcpServiceDetector(ServiceDiscovery* sd)
144 {
145     handler = sd;
146     name = "DNS-TCP";
147     proto = IpProtocol::TCP;
148     detectorType = DETECTOR_TYPE_DECODER;
149 
150     appid_registry =
151     {
152         { APP_ID_DNS, APPINFO_FLAG_SERVICE_UDP_REVERSED | APPINFO_FLAG_SERVICE_ADDITIONAL }
153     };
154 
155     service_ports =
156     {
157         { 53, IpProtocol::TCP, false },
158         { 5300, IpProtocol::TCP, false }
159     };
160 
161     handler->register_detector(name, this, proto);
162 }
163 
164 
DnsUdpServiceDetector(ServiceDiscovery * sd)165 DnsUdpServiceDetector::DnsUdpServiceDetector(ServiceDiscovery* sd)
166 {
167     handler = sd;
168     name = "DNS-UDP";
169     proto = IpProtocol::UDP;
170     detectorType = DETECTOR_TYPE_DECODER;
171 
172     appid_registry =
173     {
174         { APP_ID_DNS, APPINFO_FLAG_SERVICE_UDP_REVERSED | APPINFO_FLAG_SERVICE_ADDITIONAL }
175     };
176 
177     service_ports =
178     {
179         { 53, IpProtocol::UDP, false },
180         { 53, IpProtocol::UDP, true },
181         { 5300, IpProtocol::UDP, false }
182     };
183 
184     handler->register_detector(name, this, proto);
185 }
186 
187 
add_dns_query_info(AppIdSession & asd,uint16_t id,const uint8_t * host,uint8_t host_len,uint16_t host_offset,uint16_t record_type,AppidChangeBits & change_bits)188 APPID_STATUS_CODE DnsValidator::add_dns_query_info(AppIdSession& asd, uint16_t id,
189     const uint8_t* host, uint8_t host_len, uint16_t host_offset, uint16_t record_type,
190     AppidChangeBits& change_bits)
191 {
192     AppIdDnsSession* dsession = asd.get_dns_session();
193     if (!dsession)
194         dsession = asd.create_dns_session();
195     if ( ( dsession->get_state() != 0 ) && ( dsession->get_id() != id ) )
196         dsession->reset();
197 
198     if (dsession->get_state() & DNS_GOT_QUERY)
199         return APPID_SUCCESS;
200     dsession->set_state(dsession->get_state() | DNS_GOT_QUERY);
201 
202     dsession->set_id(id);
203     dsession->set_record_type(record_type);
204 
205     if (!dsession->get_host_len())
206     {
207         if ((host != nullptr) && (host_len > 0) && (host_offset > 0))
208         {
209             char* new_host = dns_parse_host(host, host_len);
210             if (!new_host)
211                 return APPID_NOMATCH;
212             dsession->set_host(new_host, change_bits);
213             dsession->set_host_offset(host_offset);
214             snort_free(new_host);
215        }
216     }
217 
218     return APPID_SUCCESS;
219 }
220 
add_dns_response_info(AppIdSession & asd,uint16_t id,const uint8_t * host,uint8_t host_len,uint16_t host_offset,uint8_t response_type,uint32_t ttl,AppidChangeBits & change_bits)221 APPID_STATUS_CODE DnsValidator::add_dns_response_info(AppIdSession& asd, uint16_t id,
222     const uint8_t* host, uint8_t host_len, uint16_t host_offset, uint8_t response_type, uint32_t ttl,
223     AppidChangeBits& change_bits)
224 {
225     AppIdDnsSession* dsession = asd.get_dns_session();
226     if (!dsession)
227         dsession = asd.create_dns_session();
228     if ( ( dsession->get_state() != 0 ) && ( dsession->get_id() != id ) )
229         dsession->reset();
230 
231     if (dsession->get_state() & DNS_GOT_RESPONSE)
232         return APPID_SUCCESS;
233     dsession->set_state(dsession->get_state() | DNS_GOT_RESPONSE);
234 
235     dsession->set_id(id);
236     dsession->set_ttl(ttl);
237     dsession->set_response_type(response_type);
238 
239     if (!dsession->get_host_len())
240     {
241         if ((host != nullptr) && (host_len > 0) && (host_offset > 0))
242         {
243             char* new_host = dns_parse_host(host, host_len);
244             if (!new_host)
245                 return APPID_NOMATCH;
246             dsession->set_host(new_host, change_bits);
247             dsession->set_host_offset(host_offset);
248             snort_free(new_host);
249         }
250     }
251 
252     return APPID_SUCCESS;
253 }
254 
dns_validate_label(const uint8_t * data,uint16_t & offset,uint16_t size,uint8_t & len,bool & len_valid)255 APPID_STATUS_CODE DnsValidator::dns_validate_label(const uint8_t* data, uint16_t& offset, uint16_t size,
256     uint8_t& len, bool& len_valid)
257 {
258     const DNSLabelPtr* lbl_ptr;
259     const DNSLabelBitfield* lbl_bit;
260     uint16_t tmp;
261 
262     len = 0;
263     len_valid = true;
264 
265     while ((size > offset) and (size - offset) >= (int)offsetof(DNSLabel, name))
266     {
267         const DNSLabel* lbl = (const DNSLabel*)(data + offset);
268 
269         switch (lbl->len & DNS_LENGTH_FLAGS)
270         {
271         case 0xC0:
272             len_valid = false;
273             lbl_ptr = (const DNSLabelPtr*)lbl;
274             offset += offsetof(DNSLabelPtr, data);
275             if (offset > size)
276                 return APPID_NOMATCH;
277             tmp = (uint16_t)(ntohs(lbl_ptr->position) & 0x3FFF);
278             if (tmp > size - offsetof(DNSLabel, name))
279                 return APPID_NOMATCH;
280             return APPID_SUCCESS;
281         case 0x00:
282             offset += offsetof(DNSLabel, name);
283             if (!lbl->len)
284             {
285                 len--;    // take off the extra '.' at the end
286                 return APPID_SUCCESS;
287             }
288             offset += lbl->len;
289             if ((len + lbl->len + 1) > MAX_DNS_HOST_NAME_LEN)
290             {
291                 len_valid = false;
292                 return APPID_NOMATCH;
293             }
294             len += lbl->len + 1;    // add 1 for '.'
295             break;
296         case 0x40:
297             len_valid = false;
298             if (lbl->len != 0x41)
299                 return APPID_NOMATCH;
300             offset += offsetof(DNSLabelBitfield, data);
301             if (offset >= size)
302                 return APPID_NOMATCH;
303             lbl_bit = (const DNSLabelBitfield*)lbl;
304             if (lbl_bit->len)
305             {
306                 offset += ((lbl_bit->len - 1) / 8) + 1;
307             }
308             else
309             {
310                 offset += 32;
311             }
312             break;
313         default:
314             len_valid = false;
315             return APPID_NOMATCH;
316         }
317     }
318     return APPID_NOMATCH;
319 }
320 
dns_validate_query(const uint8_t * data,uint16_t * offset,uint16_t size,uint16_t id,bool host_reporting,AppIdSession & asd,AppidChangeBits & change_bits)321 int DnsValidator::dns_validate_query(const uint8_t* data, uint16_t* offset, uint16_t size,
322     uint16_t id, bool host_reporting, AppIdSession& asd, AppidChangeBits& change_bits)
323 {
324     int ret;
325     const uint8_t* host;
326     uint8_t host_len;
327     bool host_len_valid;
328     uint16_t host_offset;
329 
330     host = data + *offset;
331     host_offset = *offset;
332     ret = dns_validate_label(data, *offset, size, host_len, host_len_valid);
333 
334     if (ret == APPID_SUCCESS)
335     {
336         const DNSQueryFixed* query = (const DNSQueryFixed*)(data + *offset);
337         *offset += sizeof(DNSQueryFixed);
338 
339         if (host_reporting)
340         {
341             uint16_t record_type = ntohs(query->QType);
342 
343             if ((host_len == 0) || (!host_len_valid))
344             {
345                 host        = nullptr;
346                 host_len    = 0;
347                 host_offset = 0;
348             }
349             switch (record_type)
350             {
351             case PATTERN_A_REC:
352             case PATTERN_AAAA_REC:
353             case PATTERN_CNAME_REC:
354             case PATTERN_SRV_REC:
355             case PATTERN_TXT_REC:
356             case PATTERN_MX_REC:
357             case PATTERN_SOA_REC:
358             case PATTERN_NS_REC:
359             case PATTERN_ANY_REC:
360                 ret = add_dns_query_info(asd, id, host, host_len, host_offset, record_type, change_bits);
361                 break;
362             case PATTERN_PTR_REC:
363                 ret = add_dns_query_info(asd, id, nullptr, 0, 0, record_type, change_bits);
364                 break;
365             default:
366                 break;
367             }
368         }
369     }
370     return ret;
371 }
372 
dns_validate_answer(const uint8_t * data,uint16_t * offset,uint16_t size,uint16_t id,uint8_t rcode,bool host_reporting,AppIdSession & asd,AppidChangeBits & change_bits)373 int DnsValidator::dns_validate_answer(const uint8_t* data, uint16_t* offset, uint16_t size,
374     uint16_t id, uint8_t rcode, bool host_reporting, AppIdSession& asd, AppidChangeBits& change_bits)
375 {
376     int ret;
377     uint8_t host_len;
378     bool host_len_valid;
379 
380     ret = dns_validate_label(data, *offset, size, host_len, host_len_valid);
381     if (ret == APPID_SUCCESS)
382     {
383         const DNSAnswerData* ad = (const DNSAnswerData*)(data + (*offset));
384         *offset += sizeof(DNSAnswerData);
385         if (*offset > size)
386             return APPID_NOMATCH;
387         uint16_t r_data_offset = *offset;
388         *offset += ntohs(ad->r_len);
389         if (*offset > size)
390             return APPID_NOMATCH;
391         if (host_reporting)
392         {
393             uint16_t record_type = ntohs(ad->type);
394             uint32_t ttl = ntohl(ad->ttl);
395 
396             switch (record_type)
397             {
398             case PATTERN_A_REC:
399             case PATTERN_AAAA_REC:
400             case PATTERN_CNAME_REC:
401             case PATTERN_SRV_REC:
402             case PATTERN_TXT_REC:
403             case PATTERN_MX_REC:
404             case PATTERN_SOA_REC:
405             case PATTERN_NS_REC:
406                 ret = add_dns_response_info(asd, id, nullptr, 0, 0, rcode, ttl, change_bits);
407                 break;
408             case PATTERN_PTR_REC:
409                 {
410                     const uint8_t* host = data + r_data_offset;
411                     uint16_t host_offset = r_data_offset;
412 
413                     ret = dns_validate_label(
414                         data, r_data_offset, size, host_len, host_len_valid);
415 
416                     if (ret != APPID_SUCCESS)
417                         return ret;
418 
419                     if ((host_len == 0) || (!host_len_valid))
420                     {
421                         host = nullptr;
422                         host_len = 0;
423                         host_offset = 0;
424                     }
425                     ret = add_dns_response_info(
426                         asd, id, host, host_len, host_offset, rcode, ttl, change_bits);
427                 }
428                 break;
429             default:
430                 break;
431             }
432         }
433     }
434     return ret;
435 }
436 
dns_validate_header(AppidSessionDirection dir,const DNSHeader * hdr,bool host_reporting,const AppIdSession & asd)437 int DnsValidator::dns_validate_header(AppidSessionDirection dir, const DNSHeader* hdr,
438     bool host_reporting, const AppIdSession& asd)
439 {
440     if (hdr->Opcode > MAX_OPCODE || hdr->Opcode == INVALID_OPCODE)
441         return APPID_NOMATCH;
442     else if (hdr->Z)
443         return APPID_NOMATCH;
444     else if (hdr->RCODE > MAX_RCODE)
445         return APPID_NOMATCH;
446     else if (!hdr->QR)        // Query.
447     {
448         if (host_reporting)
449         {
450             AppIdDnsSession* dsession = asd.get_dns_session();
451             if (dsession)
452                 dsession->reset();
453         }
454         return dir == APP_ID_FROM_INITIATOR ? APPID_SUCCESS : APPID_REVERSED;
455     }
456     else     // Response.
457         return dir == APP_ID_FROM_INITIATOR ? APPID_REVERSED : APPID_SUCCESS;
458 }
459 
validate_packet(const uint8_t * data,uint16_t size,const int,bool host_reporting,AppIdSession & asd,AppidChangeBits & change_bits)460 int DnsValidator::validate_packet(const uint8_t* data, uint16_t size, const int,
461     bool host_reporting, AppIdSession& asd, AppidChangeBits& change_bits)
462 {
463     uint16_t i;
464     uint16_t count;
465     const DNSHeader* hdr = (const DNSHeader*)data;
466     uint16_t offset;
467 
468     if (hdr->TC && size == 512)
469         return APPID_SUCCESS;
470 
471     offset = sizeof(DNSHeader);
472 
473     if (hdr->QDCount)
474     {
475         count = ntohs(hdr->QDCount);
476         for (i=0; i<count; i++)
477         {
478             if (dns_validate_query(data, &offset, size, ntohs(hdr->id), host_reporting, asd, change_bits) !=
479                 APPID_SUCCESS)
480             {
481                 return APPID_NOMATCH;
482             }
483         }
484     }
485 
486     if (hdr->ANCount)
487     {
488         count = ntohs(hdr->ANCount);
489         for (i=0; i<count; i++)
490         {
491             if (dns_validate_answer(data, &offset, size, ntohs(hdr->id), hdr->RCODE,
492                 host_reporting, asd, change_bits) != APPID_SUCCESS)
493             {
494                 return APPID_NOMATCH;
495             }
496         }
497     }
498 
499     if (hdr->NSCount)
500     {
501         count = ntohs(hdr->NSCount);
502         for (i=0; i<count; i++)
503         {
504             if (dns_validate_answer(data, &offset, size, ntohs(hdr->id), hdr->RCODE,
505                 host_reporting, asd, change_bits) != APPID_SUCCESS)
506             {
507                 return APPID_NOMATCH;
508             }
509         }
510     }
511 
512     if (hdr->ARCount)
513     {
514         count = ntohs(hdr->ARCount);
515         for (i=0; i<count; i++)
516         {
517             if (dns_validate_answer(data, &offset, size, ntohs(hdr->id), hdr->RCODE,
518                 host_reporting, asd, change_bits) != APPID_SUCCESS)
519             {
520                 return APPID_NOMATCH;
521             }
522         }
523     }
524 
525     if (hdr->QR && (hdr->RCODE != 0))    // error response
526         return add_dns_response_info(asd, ntohs(hdr->id), nullptr, 0, 0, hdr->RCODE, 0, change_bits);
527 
528     return APPID_SUCCESS;
529 }
530 
validate(AppIdDiscoveryArgs & args)531 int DnsUdpServiceDetector::validate(AppIdDiscoveryArgs& args)
532 {
533     int rval;
534 
535     if (!args.size)
536         return APPID_INPROCESS;
537 
538     if (args.size < sizeof(DNSHeader))
539     {
540         rval = (args.dir == APP_ID_FROM_INITIATOR) ? APPID_INVALID_CLIENT : APPID_NOMATCH;
541         goto udp_done;
542     }
543     if ((rval = dns_validate_header(args.dir, (const DNSHeader*)args.data,
544         args.asd.get_odp_ctxt().dns_host_reporting, args.asd)) != APPID_SUCCESS)
545     {
546         if (rval == APPID_REVERSED)
547         {
548             if (args.dir == APP_ID_FROM_RESPONDER)
549             {
550                 if (args.asd.get_session_flags(APPID_SESSION_UDP_REVERSED))
551                 {
552                     // To get here, we missed the initial query, got a
553                     // response, and now we've got another query.
554                     rval = validate_packet(args.data, args.size, args.dir,
555                         args.asd.get_odp_ctxt().dns_host_reporting, args.asd, args.change_bits);
556                     if (rval == APPID_SUCCESS)
557                         goto inprocess;
558                 }
559                 goto invalid;
560             }
561             else
562             {
563                 // To get here, we missed the initial query, but now we've got
564                 // a response.
565                 rval = validate_packet(args.data, args.size, args.dir,
566                     args.asd.get_odp_ctxt().dns_host_reporting, args.asd, args.change_bits);
567                 if (rval == APPID_SUCCESS)
568                 {
569                     args.asd.set_session_flags(APPID_SESSION_UDP_REVERSED);
570                     goto success;
571                 }
572                 goto nomatch;
573             }
574         }
575         rval = (args.dir == APP_ID_FROM_INITIATOR) ? APPID_INVALID_CLIENT : APPID_NOMATCH;
576         goto udp_done;
577     }
578 
579     rval = validate_packet(args.data, args.size, args.dir,
580         args.asd.get_odp_ctxt().dns_host_reporting, args.asd, args.change_bits);
581     if ((rval == APPID_SUCCESS) && (args.dir == APP_ID_FROM_INITIATOR))
582         goto inprocess;
583 
584 udp_done:
585     switch (rval)
586     {
587     case APPID_SUCCESS:
588 success:
589         args.asd.set_session_flags(APPID_SESSION_CONTINUE);
590         return add_service(args.change_bits, args.asd, args.pkt, args.dir, APP_ID_DNS);
591 
592     case APPID_INVALID_CLIENT:
593 invalid:
594         incompatible_data(args.asd, args.pkt, args.dir);
595         return APPID_NOT_COMPATIBLE;
596 
597     case APPID_NOMATCH:
598 nomatch:
599         fail_service(args.asd, args.pkt, args.dir);
600         return APPID_NOMATCH;
601 
602     case APPID_INPROCESS:
603 inprocess:
604         add_app(args.asd, APP_ID_NONE, APP_ID_DNS, nullptr, args.change_bits);
605         service_inprocess(args.asd, args.pkt, args.dir);
606         return APPID_INPROCESS;
607 
608     default:
609         return rval;
610     }
611 }
612 
validate(AppIdDiscoveryArgs & args)613 int DnsTcpServiceDetector::validate(AppIdDiscoveryArgs& args)
614 {
615     int rval;
616 
617     {
618         if (!args.size)
619             goto inprocess;
620         if (args.size < sizeof(DNSTCPHeader))
621         {
622             if (args.dir == APP_ID_FROM_INITIATOR)
623                 goto not_compatible;
624             else
625                 goto fail;
626         }
627         const DNSTCPHeader* hdr = (const DNSTCPHeader*)args.data;
628         const uint8_t* data = args.data + sizeof(DNSTCPHeader);
629         uint16_t size = args.size - sizeof(DNSTCPHeader);
630         uint16_t tmp = ntohs(hdr->length);
631         if (tmp < sizeof(DNSHeader) || dns_validate_header(args.dir, (const DNSHeader*)data,
632             args.asd.get_odp_ctxt().dns_host_reporting, args.asd))
633         {
634             if (args.dir == APP_ID_FROM_INITIATOR)
635                 goto not_compatible;
636             else
637                 goto fail;
638         }
639 
640         if (tmp > size)
641             goto not_compatible;
642         rval = validate_packet(data, size, args.dir,
643             args.asd.get_odp_ctxt().dns_host_reporting, args.asd, args.change_bits);
644         if (rval != APPID_SUCCESS)
645             goto tcp_done;
646 
647         ServiceDNSData* dd = static_cast<ServiceDNSData*>(data_get(args.asd));
648         if (!dd)
649         {
650             dd = static_cast<ServiceDNSData*>(snort_calloc(sizeof(ServiceDNSData)));
651             if (data_add(args.asd, dd, &snort_free))
652                 dd->state = DNS_STATE_QUERY;
653         }
654 
655         if (dd->state == DNS_STATE_QUERY)
656         {
657             if (args.dir != APP_ID_FROM_INITIATOR)
658                 goto fail;
659             dd->id = ((const DNSHeader*)data)->id;
660             dd->state = DNS_STATE_RESPONSE;
661             goto inprocess;
662         }
663         else
664         {
665             if (args.dir != APP_ID_FROM_RESPONDER || dd->id != ((const DNSHeader*)data)->id)
666                 goto fail;
667         }
668     }
669 tcp_done:
670     switch (rval)
671     {
672     case APPID_SUCCESS:
673         goto success;
674     case APPID_INVALID_CLIENT:
675         goto not_compatible;
676     case APPID_NOMATCH:
677         goto fail;
678     case APPID_INPROCESS:
679         goto inprocess;
680     default:
681         return rval;
682     }
683 
684 success:
685     args.asd.set_session_flags(APPID_SESSION_CONTINUE);
686     return add_service(args.change_bits, args.asd, args.pkt, args.dir, APP_ID_DNS);
687 
688 not_compatible:
689     incompatible_data(args.asd, args.pkt, args.dir);
690     return APPID_NOT_COMPATIBLE;
691 
692 fail:
693     fail_service(args.asd, args.pkt, args.dir);
694     return APPID_NOMATCH;
695 
696 inprocess:
697     add_app(args.asd, APP_ID_NONE, APP_ID_DNS, nullptr, args.change_bits);
698     service_inprocess(args.asd, args.pkt, args.dir);
699     return APPID_INPROCESS;
700 }
701 
dns_parse_host(const uint8_t * host,uint8_t host_len)702 char* dns_parse_host(const uint8_t* host, uint8_t host_len)
703 {
704     char* str = static_cast<char*>(snort_calloc(host_len + 1));    // plus '\0' at end
705     const uint8_t* src = host;
706     char* dst = str;
707 
708     uint32_t dstLen = 0;
709 
710     while (*src != 0)
711     {
712         uint8_t len = *src;
713         src++;
714 
715         if ((dstLen + len) <= host_len)
716             memcpy(dst, src, len);
717         else
718         {
719             // Malformed DNS host, return
720             snort_free(str);
721             return nullptr;
722         }
723         src += len;
724         dst += len;
725         *dst = '.';
726         dstLen += len + 1;
727         dst++;
728     }
729     str[host_len] = '\0';    // nullptr term
730     return str;
731 }
732