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