1/* **************************************************************************** 2 * PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. 3 * See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage. 4 * . 5 * Authors: Toon Stegen, Jelle De Vleeschouwer 6 * ****************************************************************************/ 7#include "pico_config.h" 8#include "pico_protocol.h" 9#include "pico_stack.h" 10#include "pico_addressing.h" 11#include "pico_ipv4.h" 12#include "pico_ipv6.h" 13#include "pico_dns_common.h" 14#include "pico_tree.h" 15 16#ifdef DEBUG_DNS 17 #define dns_dbg dbg 18#else 19 #define dns_dbg(...) do {} while(0) 20#endif 21 22/* MARK: v NAME & IP FUNCTIONS */ 23#define dns_name_foreach_label_safe(label, name, next, maxlen) \ 24 for ((label) = (name), (next) = (char *)((name) + *(unsigned char*)(name) + 1); \ 25 (*(label) != '\0') && ((uint16_t)((label) - (name)) < (maxlen)); \ 26 (label) = (next), (next) = (char *)((next) + *(unsigned char*)(next) + 1)) 27 28/* **************************************************************************** 29 * Checks if the DNS name doesn't exceed 256 bytes including zero-byte. 30 * 31 * @param namelen Length of the DNS name-string including zero-byte 32 * @return 0 when the length is correct 33 * ****************************************************************************/ 34int 35pico_dns_check_namelen( uint16_t namelen ) 36{ 37 return ((namelen > 2u) && (namelen < 256u)) ? (0) : (-1); 38} 39 40/* **************************************************************************** 41 * Returns the length of a name in a DNS-packet as if DNS name compression 42 * would be applied to the packet. If there's no compression present 43 * 44 * @param name Compressed name you want the calculate the strlen from 45 * @return Returns strlen of a compressed name, takes the first byte of compr- 46 * ession pointer into account but not the second byte, which acts 47 * like a trailing zero-byte 48 * ****************************************************************************/ 49uint16_t 50pico_dns_namelen_comp( char *name ) 51{ 52 uint16_t len = 0; 53 char *label = NULL, *next = NULL; 54 55 /* Check params */ 56 if (!name) { 57 pico_err = PICO_ERR_EINVAL; 58 return 0; 59 } 60 61 /* Just count until the zero-byte or a pointer */ 62 dns_name_foreach_label_safe(label, name, next, 255) { 63 if ((0xC0 & *label)) 64 break; 65 } 66 67 /* Calculate the length */ 68 len = (uint16_t)(label - name); 69 if(*label != '\0') 70 len++; 71 72 return len; 73} 74 75/* **************************************************************************** 76 * Returns the uncompressed name in DNS name format when DNS name compression 77 * is applied to the packet-buffer. 78 * 79 * @param name Compressed name, should be in the bounds of the actual packet 80 * @param packet Packet that contains the compressed name 81 * @return Returns the decompressed name, NULL on failure. 82 * ****************************************************************************/ 83char * 84pico_dns_decompress_name( char *name, pico_dns_packet *packet ) 85{ 86 char decompressed_name[PICO_DNS_NAMEBUF_SIZE] = { 87 0 88 }; 89 char *return_name = NULL; 90 uint8_t *dest_iterator = NULL; 91 uint8_t *iterator = NULL; 92 uint16_t ptr = 0, nslen = 0; 93 94 /* Initialise iterators */ 95 iterator = (uint8_t *) name; 96 dest_iterator = (uint8_t *) decompressed_name; 97 while (*iterator != '\0') { 98 if ((*iterator) & 0xC0) { 99 /* We have a pointer */ 100 ptr = (uint16_t)((((uint16_t) *iterator) & 0x003F) << 8); 101 ptr = (uint16_t)(ptr | (uint16_t) *(iterator + 1)); 102 iterator = (uint8_t *)((uint8_t *)packet + ptr); 103 } else { 104 /* We want to keep the label lengths */ 105 *dest_iterator = (uint8_t) *iterator; 106 /* Copy the label */ 107 memcpy(dest_iterator + 1, iterator + 1, *iterator); 108 /* Move to next length label */ 109 dest_iterator += (*iterator) + 1; 110 iterator += (*iterator) + 1; 111 } 112 } 113 /* Append final zero-byte */ 114 *dest_iterator = (uint8_t) '\0'; 115 116 /* Provide storage for the name to return */ 117 nslen = (uint16_t)(pico_dns_strlen(decompressed_name) + 1); 118 if(!(return_name = PICO_ZALLOC((size_t)nslen))) { 119 pico_err = PICO_ERR_ENOMEM; 120 return NULL; 121 } 122 123 memcpy((void *)return_name, (void *)decompressed_name, (size_t)nslen); 124 125 return return_name; 126} 127 128/* **************************************************************************** 129 * Determines the length of a given url as if it where a DNS name in reverse 130 * resolution format. 131 * 132 * @param url URL wanted to create a reverse resolution name from. 133 * @param arpalen Will get filled with the length of the ARPA-suffix depending 134 * on the proto-parameter. 135 * @param proto The protocol to create a ARPA-suffix for. Can be either 136 * 'PICO_PROTO_IPV4' or 'PICO_PROTO_IPV6' 137 * @return Returns the length of the reverse name 138 * ****************************************************************************/ 139static uint16_t 140pico_dns_url_get_reverse_len( const char *url, 141 uint16_t *arpalen, 142 uint16_t proto ) 143{ 144 uint16_t slen = (uint16_t)(pico_dns_strlen(url) + 2u); 145 146 /* Check if pointers given are not NULL */ 147 if (pico_dns_check_namelen(slen) && !arpalen) { 148 pico_err = PICO_ERR_EINVAL; 149 return 0; 150 } 151 152 /* Get the length of arpa-suffix if needed */ 153 if (proto == PICO_PROTO_IPV4) 154 *arpalen = (uint16_t) pico_dns_strlen(PICO_ARPA_IPV4_SUFFIX); 155 156#ifdef PICO_SUPPORT_IPV6 157 else if (proto == PICO_PROTO_IPV6) 158 { 159 *arpalen = (uint16_t) pico_dns_strlen(PICO_ARPA_IPV6_SUFFIX); 160 slen = STRLEN_PTR_IP6 + 2u; 161 } 162#endif 163 return slen; 164} 165 166/* **************************************************************************** 167 * Converts a DNS name in URL format to a reverse name in DNS name format. 168 * Provides space for the DNS name as well. PICO_FREE() should be called on the 169 * returned string buffer that contains the reverse DNS name. 170 * 171 * @param url DNS name in URL format to convert to reverse name 172 * @param proto Depending on the protocol given the ARPA-suffix will be added. 173 * @return Returns a pointer to a string-buffer with the reverse DNS name. 174 * ****************************************************************************/ 175static char * 176pico_dns_url_to_reverse_qname( const char *url, uint8_t proto ) 177{ 178 char *reverse_qname = NULL; 179 uint16_t arpalen = 0; 180 uint16_t slen = pico_dns_url_get_reverse_len(url, &arpalen, proto); 181 182 /* Check namelen */ 183 if (pico_dns_check_namelen(slen)) { 184 pico_err = PICO_ERR_EINVAL; 185 return NULL; 186 } 187 188 /* Provide space for the reverse name */ 189 if (!(reverse_qname = PICO_ZALLOC((size_t)(slen + arpalen)))) { 190 pico_err = PICO_ERR_ENOMEM; 191 return NULL; 192 } 193 194 /* If reverse IPv4 address resolving, convert to IPv4 arpa-format */ 195 if (PICO_PROTO_IPV4 == proto) { 196 memcpy(reverse_qname + 1u, url, slen - 1u); 197 pico_dns_mirror_addr(reverse_qname + 1u); 198 memcpy(reverse_qname + slen - 1, PICO_ARPA_IPV4_SUFFIX, arpalen); 199 } 200 201 /* If reverse IPv6 address resolving, convert to IPv6 arpa-format */ 202#ifdef PICO_SUPPORT_IPV6 203 else if (proto == PICO_PROTO_IPV6) { 204 pico_dns_ipv6_set_ptr(url, reverse_qname + 1u); 205 memcpy(reverse_qname + 1u + STRLEN_PTR_IP6, 206 PICO_ARPA_IPV6_SUFFIX, arpalen); 207 } 208#endif 209 else { /* This shouldn't happen */ 210 PICO_FREE(reverse_qname); 211 return NULL; 212 } 213 214 pico_dns_name_to_dns_notation(reverse_qname, (uint16_t)(slen + arpalen)); 215 return reverse_qname; 216} 217 218/* **************************************************************************** 219 * Converts a DNS name in DNS name format to a name in URL format. Provides 220 * space for the name in URL format as well. PICO_FREE() should be called on 221 * the returned string buffer that contains the name in URL format. 222 * 223 * @param qname DNS name in DNS name format to convert 224 * @return Returns a pointer to a string-buffer with the URL name on success. 225 * ****************************************************************************/ 226char * 227pico_dns_qname_to_url( const char *qname ) 228{ 229 char *url = NULL; 230 char temp[256] = { 231 0 232 }; 233 uint16_t namelen = pico_dns_strlen(qname); 234 235 /* Check if qname is not a NULL-pointer and if the length is OK */ 236 if (pico_dns_check_namelen(namelen)) { 237 pico_err = PICO_ERR_EINVAL; 238 return NULL; 239 } 240 241 /* Provide space for the URL */ 242 if (!(url = PICO_ZALLOC(namelen))) { 243 pico_err = PICO_ERR_ENOMEM; 244 return NULL; 245 } 246 247 /* Convert qname to an URL */ 248 memcpy(temp, qname, namelen); 249 pico_dns_notation_to_name(temp, namelen); 250 memcpy((void *)url, (void *)(temp + 1), (size_t)(namelen - 1)); 251 252 return url; 253} 254 255/* **************************************************************************** 256 * Converts a DNS name in URL format to a name in DNS name format. Provides 257 * space for the DNS name as well. PICO_FREE() should be called on the returned 258 * string buffer that contains the DNS name. 259 * 260 * @param url DNS name in URL format to convert 261 * @return Returns a pointer to a string-buffer with the DNS name on success. 262 * ****************************************************************************/ 263char * 264pico_dns_url_to_qname( const char *url ) 265{ 266 char *qname = NULL; 267 uint16_t namelen = (uint16_t)(pico_dns_strlen(url) + 2u); 268 269 /* Check if url or qname_addr is not a NULL-pointer */ 270 if (pico_dns_check_namelen(namelen)) { 271 pico_err = PICO_ERR_EINVAL; 272 return NULL; 273 } 274 275 /* Provide space for the qname */ 276 if (!(qname = PICO_ZALLOC(namelen))) { 277 pico_err = PICO_ERR_ENOMEM; 278 return NULL; 279 } 280 281 /* Copy in the URL (+1 to leave space for leading '.') */ 282 memcpy(qname + 1, url, (size_t)(namelen - 1)); 283 pico_dns_name_to_dns_notation(qname, namelen); 284 return qname; 285} 286 287/* **************************************************************************** 288 * @param url String-buffer 289 * @return Length of string-buffer in an uint16_t 290 * ****************************************************************************/ 291uint16_t 292pico_dns_strlen( const char *url ) 293{ 294 if (!url) 295 return 0; 296 297 return (uint16_t) strlen(url); 298} 299 300/* **************************************************************************** 301 * Replaces .'s in a DNS name in URL format by the label lengths. So it 302 * actually converts a name in URL format to a name in DNS name format. 303 * f.e. "*www.google.be" => "3www6google2be0" 304 * 305 * @param url Location to buffer with name in URL format. The URL needs to 306 * be +1 byte offset in the actual buffer. Size is should be 307 * pico_dns_strlen(url) + 2. 308 * @param maxlen Maximum length of buffer so it doesn't cause a buffer overflow 309 * @return 0 on success, something else on failure. 310 * ****************************************************************************/ 311int pico_dns_name_to_dns_notation( char *url, uint16_t maxlen ) 312{ 313 char c = '\0'; 314 char *lbl = url, *i = url; 315 316 /* Check params */ 317 if (!url || pico_dns_check_namelen(maxlen)) { 318 pico_err = PICO_ERR_EINVAL; 319 return -1; 320 } 321 322 /* Iterate over url */ 323 while ((c = *++i) != '\0') { 324 if ('.' == c) { 325 *lbl = (char)(i - lbl - 1); 326 lbl = i; 327 } 328 329 if ((uint16_t)(i - url) > (uint16_t)maxlen) break; 330 } 331 *lbl = (char)(i - lbl - 1); 332 333 return 0; 334} 335 336/* **************************************************************************** 337 * Replaces the label lengths in a DNS-name by .'s. So it actually converts a 338 * name in DNS format to a name in URL format. 339 * f.e. 3www6google2be0 => .www.google.be 340 * 341 * @param ptr Location to buffer with name in DNS name format 342 * @param maxlen Maximum length of buffer so it doesn't cause a buffer overflow 343 * @return 0 on success, something else on failure. 344 * ****************************************************************************/ 345int pico_dns_notation_to_name( char *ptr, uint16_t maxlen ) 346{ 347 char *label = NULL, *next = NULL; 348 349 /* Iterate safely over the labels and update each label */ 350 dns_name_foreach_label_safe(label, ptr, next, maxlen) { 351 *label = '.'; 352 } 353 354 return 0; 355} 356 357/* **************************************************************************** 358 * Determines the length of the first label of a DNS name in URL-format 359 * 360 * @param url DNS name in URL-format 361 * @return Length of the first label of DNS name in URL-format 362 * ****************************************************************************/ 363uint16_t 364pico_dns_first_label_length( const char *url ) 365{ 366 const char *i = NULL; 367 uint16_t len = 0; 368 369 /* Check params */ 370 if (!url) return 0; 371 372 /* Count */ 373 i = url; 374 while (*i != '.' && *i != '\0') { 375 ++i; 376 ++len; 377 } 378 return len; 379} 380 381/* **************************************************************************** 382 * Mirrors a dotted IPv4-address string. 383 * f.e. 192.168.0.1 => 1.0.168.192 384 * 385 * @param ptr 386 * @return 0 on success, something else on failure. 387 * ****************************************************************************/ 388int 389pico_dns_mirror_addr( char *ip ) 390{ 391 uint32_t addr = 0; 392 393 /* Convert IPv4-string to network-order 32-bit number */ 394 if (pico_string_to_ipv4(ip, &addr) < 0) 395 return -1; 396 397 /* Mirror the 32-bit number */ 398 addr = (uint32_t)((uint32_t)((addr & (uint32_t)0xFF000000u) >> 24) | 399 (uint32_t)((addr & (uint32_t)0xFF0000u) >> 8) | 400 (uint32_t)((addr & (uint32_t)0xFF00u) << 8) | 401 (uint32_t)((addr & (uint32_t)0xFFu) << 24)); 402 403 return pico_ipv4_to_string(ip, addr); 404} 405 406#ifdef PICO_SUPPORT_IPV6 407/* **************************************************************************** 408 * Get the ASCII value of the Most Significant Nibble of a byte 409 * 410 * @param byte Byte you want to extract the MSN from. 411 * @return The ASCII value of the Most Significant Nibble of the byte 412 * ****************************************************************************/ 413static inline char 414dns_ptr_ip6_nibble_lo( uint8_t byte ) 415{ 416 uint8_t nibble = byte & 0x0f; 417 if (nibble < 10) 418 return (char)(nibble + '0'); 419 else 420 return (char)(nibble - 0xa + 'a'); 421} 422 423/* **************************************************************************** 424 * Get the ASCII value of the Least Significant Nibble of a byte 425 * 426 * @param byte Byte you want to extract the LSN from. 427 * @return The ASCII value of the Least Significant Nibble of the byte 428 * ****************************************************************************/ 429static inline char 430dns_ptr_ip6_nibble_hi( uint8_t byte ) 431{ 432 uint8_t nibble = (byte & 0xf0u) >> 4u; 433 if (nibble < 10u) 434 return (char)(nibble + '0'); 435 else 436 return (char)(nibble - 0xa + 'a'); 437} 438 439/* **************************************************************************** 440 * Convert an IPv6-address in string-format to a IPv6-address in nibble-format. 441 * Doesn't add a IPv6 ARPA-suffix though. 442 * 443 * @param ip IPv6-address stored as a string 444 * @param dst Destination to store IPv6-address in nibble-format 445 * ****************************************************************************/ 446void 447pico_dns_ipv6_set_ptr( const char *ip, char *dst ) 448{ 449 int i = 0, j = 0; 450 struct pico_ip6 ip6; 451 memset(&ip6, 0, sizeof(struct pico_ip6)); 452 pico_string_to_ipv6(ip, ip6.addr); 453 for (i = 15; i >= 0; i--) { 454 if ((j + 3) > 64) return; /* Don't want j to go out of bounds */ 455 456 dst[j++] = dns_ptr_ip6_nibble_lo(ip6.addr[i]); 457 dst[j++] = '.'; 458 dst[j++] = dns_ptr_ip6_nibble_hi(ip6.addr[i]); 459 dst[j++] = '.'; 460 } 461} 462#endif 463 464/* MARK: ^ NAME & IP FUNCTIONS */ 465/* MARK: v QUESTION FUNCTIONS */ 466 467/* **************************************************************************** 468 * Calculates the size of a single DNS Question. Void-pointer allows this 469 * function to be used with pico_tree_size. 470 * 471 * @param question Void-point to DNS Question 472 * @return Size in bytes of single DNS Question if it was copied flat. 473 * ****************************************************************************/ 474static uint16_t pico_dns_question_size( void *question ) 475{ 476 uint16_t size = 0; 477 struct pico_dns_question *q = (struct pico_dns_question *)question; 478 if (!q) 479 return 0; 480 481 size = q->qname_length; 482 size = (uint16_t)(size + sizeof(struct pico_dns_question_suffix)); 483 return size; 484} 485 486/* **************************************************************************** 487 * Deletes a single DNS Question. 488 * 489 * @param question Void-pointer to DNS Question. Can be used with pico_tree_- 490 * destroy. 491 * @return Returns 0 on success, something else on failure. 492 * ****************************************************************************/ 493int 494pico_dns_question_delete( void **question ) 495{ 496 struct pico_dns_question **q = (struct pico_dns_question **)question; 497 498 /* Check params */ 499 if ((!q) || !(*q)) { 500 pico_err = PICO_ERR_EINVAL; 501 return -1; 502 } 503 504 if ((*q)->qname) 505 PICO_FREE(((*q)->qname)); 506 507 if ((*q)->qsuffix) 508 PICO_FREE((*q)->qsuffix); 509 510 PICO_FREE((*q)); 511 *question = NULL; 512 513 return 0; 514} 515 516/* **************************************************************************** 517 * Fills in the DNS question suffix-fields with the correct values. 518 * 519 * todo: Update pico_dns_client to make the same mechanism possible like with 520 * filling DNS Resource Record-suffixes. 521 * 522 * @param suf Pointer to the suffix member of the DNS question. 523 * @param qtype DNS type of the DNS question to be. 524 * @param qclass DNS class of the DNS question to be. 525 * @return Returns 0 on success, something else on failure. 526 * ****************************************************************************/ 527int 528pico_dns_question_fill_suffix( struct pico_dns_question_suffix *suf, 529 uint16_t qtype, 530 uint16_t qclass ) 531{ 532 if (!suf) 533 return -1; 534 535 suf->qtype = short_be(qtype); 536 suf->qclass = short_be(qclass); 537 return 0; 538} 539 540/* **************************************************************************** 541 * Fills in the name of the DNS question. 542 * 543 * @param qname Pointer-pointer to the name-member of the DNS-question 544 * @param url Name in URL format you want to convert to a name in DNS name 545 * format. When reverse resolving, only the IP, either IPV4 or 546 * IPV6, should be given in string format. 547 * f.e. => for IPv4: "192.168.2.1" 548 * => for IPv6: "2001:0db8:85a3:0042:1000:8a2e:0370:7334" 549 * @param qtype DNS type type of the DNS question to be. 550 * @param proto When reverse is true the reverse resolution name will be 551 * generated depending on the protocol. Can be either 552 * PICO_PROTO_IPV4 or PICO_PROTO_IPV6. 553 * @param reverse When this is true a reverse resolution name will be generated 554 * from the URL. 555 * @return The eventual length of the generated name, 0 on failure. 556 * ****************************************************************************/ 557static uint16_t 558pico_dns_question_fill_name( char **qname, 559 const char *url, 560 uint16_t qtype, 561 uint8_t proto, 562 uint8_t reverse ) 563{ 564 uint16_t slen = 0; 565 566 /* Try to convert the URL to an FQDN */ 567 if (reverse && qtype == PICO_DNS_TYPE_PTR) 568 *qname = pico_dns_url_to_reverse_qname(url, proto); 569 else { 570 (*qname) = pico_dns_url_to_qname(url); 571 } 572 573 if (!(*qname)) { 574 return 0; 575 } 576 577 slen = (uint16_t)(pico_dns_strlen(*qname) + 1u); 578 return (pico_dns_check_namelen(slen)) ? ((uint16_t)0) : (slen); 579} 580 581/* **************************************************************************** 582 * Creates a standalone DNS Question with a given name and type. 583 * 584 * @param url DNS question name in URL format. Will be converted to DNS 585 * name notation format. 586 * @param len Will be filled with the total length of the DNS question. 587 * @param proto Protocol for which you want to create a question. Can be 588 * either PICO_PROTO_IPV4 or PICO_PROTO_IPV6. 589 * @param qtype DNS type of the question to be. 590 * @param qclass DNS class of the question to be. 591 * @param reverse When this is true, a reverse resolution name will be 592 * generated from the URL 593 * @return Returns pointer to the created DNS Question on success, NULL on 594 * failure. 595 * ****************************************************************************/ 596struct pico_dns_question * 597pico_dns_question_create( const char *url, 598 uint16_t *len, 599 uint8_t proto, 600 uint16_t qtype, 601 uint16_t qclass, 602 uint8_t reverse ) 603{ 604 struct pico_dns_question *question = NULL; 605 uint16_t slen = 0; 606 int ret = 0; 607 608 /* Check if valid arguments are provided */ 609 if (!url || !len) { 610 pico_err = PICO_ERR_EINVAL; 611 return NULL; 612 } 613 614 /* Allocate space for the question and the subfields */ 615 if (!(question = PICO_ZALLOC(sizeof(struct pico_dns_question)))) { 616 pico_err = PICO_ERR_ENOMEM; 617 return NULL; 618 } 619 620 /* Fill name field */ 621 slen = pico_dns_question_fill_name(&(question->qname), url, 622 qtype, proto, reverse); 623 question->qname_length = (uint8_t)(slen); 624 question->proto = proto; 625 626 /* Provide space for the question suffix & try to fill in */ 627 question->qsuffix = PICO_ZALLOC(sizeof(struct pico_dns_question_suffix)); 628 ret = pico_dns_question_fill_suffix(question->qsuffix, qtype, qclass); 629 if (ret || pico_dns_check_namelen(slen)) { 630 pico_dns_question_delete((void **)&question); 631 return NULL; 632 } 633 634 /* Determine the entire length of the question */ 635 *len = (uint16_t)(slen + (uint16_t)sizeof(struct pico_dns_question_suffix)); 636 637 return question; 638} 639 640/* **************************************************************************** 641 * Decompresses the name of a single DNS question. 642 * 643 * @param question Question you want to decompress the name of 644 * @param packet Packet in which the DNS question is contained. 645 * @return Pointer to original name of the DNS question before decompressing. 646 * ****************************************************************************/ 647char * 648pico_dns_question_decompress( struct pico_dns_question *question, 649 pico_dns_packet *packet ) 650{ 651 char *qname_original = question->qname; 652 653 /* Try to decompress the question name */ 654 if (!(question->qname = pico_dns_decompress_name(question->qname, packet))) { 655 question->qname = qname_original; 656 } 657 658 return qname_original; 659} 660 661 662/* MARK: ^ QUESTION FUNCTIONS */ 663/* MARK: v RESOURCE RECORD FUNCTIONS */ 664 665/* **************************************************************************** 666 * Copies the contents of DNS Resource Record to a single flat memory-buffer. 667 * 668 * @param record Pointer to DNS record you want to copy flat. 669 * @param destination Pointer-pointer to flat memory buffer to copy DNS record 670 * to. When function returns, this will point to location 671 * right after the flat copied DNS Resource Record. 672 * @return Returns 0 on success, something else on failure. 673 * ****************************************************************************/ 674static int 675pico_dns_record_copy_flat( struct pico_dns_record *record, 676 uint8_t **destination ) 677{ 678 char *dest_rname = NULL; /* rname destination location */ 679 struct pico_dns_record_suffix *dest_rsuffix = NULL; /* rsuffix destin. */ 680 uint8_t *dest_rdata = NULL; /* rdata destination location */ 681 682 /* Check if there are no NULL-pointers given */ 683 if (!record || !destination || !(*destination)) { 684 pico_err = PICO_ERR_EINVAL; 685 return -1; 686 } 687 688 /* Initialise the destination pointers to the right locations */ 689 dest_rname = (char *) *destination; 690 dest_rsuffix = (struct pico_dns_record_suffix *) 691 (dest_rname + record->rname_length); 692 dest_rdata = ((uint8_t *)dest_rsuffix + 693 sizeof(struct pico_dns_record_suffix)); 694 695 /* Copy the rname of the resource record into the flat location */ 696 strcpy(dest_rname, record->rname); 697 698 /* Copy the question suffix fields */ 699 dest_rsuffix->rtype = record->rsuffix->rtype; 700 dest_rsuffix->rclass = record->rsuffix->rclass; 701 dest_rsuffix->rttl = record->rsuffix->rttl; 702 dest_rsuffix->rdlength = record->rsuffix->rdlength; 703 704 /* Copy the rdata of the resource */ 705 memcpy(dest_rdata, record->rdata, short_be(dest_rsuffix->rdlength)); 706 707 /* Point to location right after flat resource record */ 708 *destination = (uint8_t *)(dest_rdata + 709 short_be(record->rsuffix->rdlength)); 710 return 0; 711} 712 713/* **************************************************************************** 714 * Calculates the size of a single DNS Resource Record. Void-pointer allows 715 * this function to be used with pico_tree_size. 716 * 717 * @param record void-pointer to DNS record you want to know the size of. 718 * @return Size of single DNS record if it was copied flat. 719 * ****************************************************************************/ 720static uint16_t 721pico_dns_record_size( void *record ) 722{ 723 uint16_t size = 0; 724 struct pico_dns_record *rr = (struct pico_dns_record *)record; 725 726 if (!rr || !(rr->rsuffix)) 727 return 0; 728 729 size = rr->rname_length; 730 size = (uint16_t)(size + sizeof(struct pico_dns_record_suffix)); 731 size = (uint16_t)(size + short_be(rr->rsuffix->rdlength)); 732 return size; 733} 734 735/* **************************************************************************** 736 * Deletes a single DNS resource record. Void-pointer-pointer allows this 737 * function to be used with pico_tree_destroy. 738 * 739 * @param record void-pointer-pointer to DNS record you want to delete. 740 * @return Returns 0 on success, something else on failure. 741 * ****************************************************************************/ 742int 743pico_dns_record_delete( void **record ) 744{ 745 struct pico_dns_record **rr = (struct pico_dns_record **)record; 746 747 if ((!rr) || !(*rr)) 748 return 0; 749 750 if ((*rr)->rname) 751 PICO_FREE((*rr)->rname); 752 753 if ((*rr)->rsuffix) 754 PICO_FREE((*rr)->rsuffix); 755 756 if ((*rr)->rdata) 757 PICO_FREE((*rr)->rdata); 758 759 PICO_FREE((*rr)); 760 *record = NULL; 761 762 return 0; 763} 764 765/* **************************************************************************** 766 * Just copies a resource record hard. 767 * 768 * @param record DNS record you want to copy 769 * @return Pointer to copy of DNS record. 770 * ****************************************************************************/ 771struct pico_dns_record * 772pico_dns_record_copy( struct pico_dns_record *record ) 773{ 774 struct pico_dns_record *copy = NULL; 775 776 /* Check params */ 777 if (!record || !(record->rname) || !(record->rdata) || !(record->rsuffix)) { 778 pico_err = PICO_ERR_EINVAL; 779 return NULL; 780 } 781 782 /* Provide space for the copy */ 783 if (!(copy = PICO_ZALLOC(sizeof(struct pico_dns_record)))) { 784 pico_err = PICO_ERR_ENOMEM; 785 return NULL; 786 } 787 788 /* Provide space for the subfields */ 789 copy->rname = PICO_ZALLOC((size_t)record->rname_length); 790 copy->rsuffix = PICO_ZALLOC(sizeof(struct pico_dns_record_suffix)); 791 copy->rdata = PICO_ZALLOC((size_t)short_be(record->rsuffix->rdlength)); 792 if (!(copy->rname) || !(copy->rsuffix) || !(copy->rdata)) { 793 pico_dns_record_delete((void **)©); 794 pico_err = PICO_ERR_ENOMEM; 795 return NULL; 796 } 797 798 /* Fill in the rname field */ 799 memcpy((void *)(copy->rname), (void *)(record->rname), 800 (size_t)(record->rname_length)); 801 copy->rname_length = record->rname_length; 802 803 /* Fill in the rsuffix fields */ 804 copy->rsuffix->rtype = record->rsuffix->rtype; 805 copy->rsuffix->rclass = record->rsuffix->rclass; 806 copy->rsuffix->rttl = record->rsuffix->rttl; 807 copy->rsuffix->rdlength = record->rsuffix->rdlength; 808 809 /* Fill in the rdata field */ 810 memcpy(copy->rdata, record->rdata, short_be(record->rsuffix->rdlength)); 811 812 return copy; 813} 814 815/* **************************************************************************** 816 * Fills in the DNS resource record suffix-fields with the correct values. 817 * 818 * @param suf Pointer-pointer to rsuffix-member of struct pico_dns_record. 819 * @param rtype DNS type of the resource record to be. 820 * @param rclass DNS class of the resource record to be. 821 * @param rttl DNS ttl of the resource record to be. 822 * @param rdlength DNS rdlength of the resource record to be. 823 * @return Returns 0 on success, something else on failure. 824 * ****************************************************************************/ 825static int 826pico_dns_record_fill_suffix( struct pico_dns_record_suffix **suf, 827 uint16_t rtype, 828 uint16_t rclass, 829 uint32_t rttl, 830 uint16_t rdlength ) 831{ 832 /* Try to provide space for the rsuffix */ 833 if (!(*suf = PICO_ZALLOC(sizeof(struct pico_dns_record_suffix)))) { 834 pico_err = PICO_ERR_ENOMEM; 835 return -1; 836 } 837 838 /* Fill in the fields */ 839 (*suf)->rtype = short_be(rtype); 840 (*suf)->rclass = short_be(rclass); 841 (*suf)->rttl = long_be(rttl); 842 (*suf)->rdlength = short_be(rdlength); 843 844 return 0; 845} 846 847/* **************************************************************************** 848 * Fills the data-buffer of a DNS resource record. 849 * 850 * @param rdata Pointer-pointer to rdata-member of struct pico_dns_record. 851 * @param _rdata Memory buffer with data to insert in the resource record. If 852 * data should contain a DNS name, the name in the databuffer 853 * needs to be in URL-format. 854 * @param datalen The exact length in bytes of the _rdata-buffer. If data of 855 * record should contain a DNS name, datalen needs to be 856 * pico_dns_strlen(_rdata). 857 * @param rtype DNS type of the resource record to be 858 * @return Returns 0 on failure, length of filled in rdata-member on success. 859 * Can differ from datalen-param because of URL to DNS Name conversion. 860 * ****************************************************************************/ 861static uint16_t 862pico_dns_record_fill_rdata( uint8_t **rdata, 863 void *_rdata, 864 uint16_t datalen, 865 uint16_t rtype ) 866{ 867 uint16_t _datalen = 0; 868 869 /* If type is PTR, rdata will be a DNS name in URL format */ 870 if (rtype == PICO_DNS_TYPE_PTR) { 871 _datalen = (uint16_t)(datalen + 2u); 872 if (!(*rdata = (uint8_t *)pico_dns_url_to_qname(_rdata))) { 873 pico_err = PICO_ERR_ENOMEM; 874 return 0; 875 } 876 } else { 877 /* Otherwise just copy in the databuffer */ 878 if (datalen == 0) { 879 return datalen; 880 } 881 882 _datalen = datalen; 883 if (!(*rdata = (uint8_t *)PICO_ZALLOC((size_t)datalen))) { 884 pico_err = PICO_ERR_ENOMEM; 885 return 0; 886 } 887 888 memcpy((void *)*rdata, (void *)_rdata, datalen); 889 } 890 891 return _datalen; 892} 893 894/* **************************************************************************** 895 * Create a standalone DNS Resource Record with a given name. 896 * 897 * @param url DNS rrecord name in URL format. Will be converted to DNS 898 * name notation format. 899 * @param _rdata Memory buffer with data to insert in the resource record. If 900 * data should contain a DNS name, the name in the databuffer 901 * needs to be in URL-format. 902 * @param datalen The exact length in bytes of the _rdata-buffer. If data of 903 * record should contain a DNS name, datalen needs to be 904 * pico_dns_strlen(_rdata). 905 * @param len Will be filled with the total length of the DNS rrecord. 906 * @param rtype DNS type of the resource record to be. 907 * @param rclass DNS class of the resource record to be. 908 * @param rttl DNS ttl of the resource record to be. 909 * @return Returns pointer to the created DNS Resource Record 910 * ****************************************************************************/ 911struct pico_dns_record * 912pico_dns_record_create( const char *url, 913 void *_rdata, 914 uint16_t datalen, 915 uint16_t *len, 916 uint16_t rtype, 917 uint16_t rclass, 918 uint32_t rttl ) 919{ 920 struct pico_dns_record *record = NULL; 921 uint16_t slen = (uint16_t)(pico_dns_strlen(url) + 2u); 922 int ret = 0; 923 924 /* Check params */ 925 if (pico_dns_check_namelen(slen) || !_rdata || !len) { 926 pico_err = PICO_ERR_EINVAL; 927 return NULL; 928 } 929 930 /* Allocate space for the record and subfields */ 931 if (!(record = PICO_ZALLOC(sizeof(struct pico_dns_record)))) { 932 pico_err = PICO_ERR_ENOMEM; 933 return NULL; 934 } 935 936 /* Provide space and convert the URL to a DNS name */ 937 record->rname = pico_dns_url_to_qname(url); 938 record->rname_length = slen; 939 940 /* Provide space & fill in the rdata field */ 941 datalen = pico_dns_record_fill_rdata(&(record->rdata), _rdata, 942 datalen, rtype); 943 944 /* Provide space & fill in the rsuffix */ 945 ret = pico_dns_record_fill_suffix(&(record->rsuffix), rtype, rclass, rttl, 946 datalen); 947 948 /* Check if everything succeeded */ 949 if (!(record->rname) || ret) { 950 pico_dns_record_delete((void **)&record); 951 return NULL; 952 } 953 954 /* Determine the complete length of resource record */ 955 *len = (uint16_t)(slen + sizeof(struct pico_dns_record_suffix) + datalen); 956 return record; 957} 958 959/* **************************************************************************** 960 * Decompresses the name of single DNS record. 961 * 962 * @param record DNS record to decompress the name of. 963 * @param packet Packet in which is DNS record is present 964 * @return Pointer to original name of the DNS record before decompressing. 965 * ****************************************************************************/ 966char * 967pico_dns_record_decompress( struct pico_dns_record *record, 968 pico_dns_packet *packet ) 969{ 970 char *rname_original = record->rname; 971 972 /* Try to decompress the record name */ 973 if (!(record->rname = pico_dns_decompress_name(record->rname, packet))) { 974 record->rname = rname_original; 975 } 976 977 return rname_original; 978} 979 980static int pico_tolower(int c) 981{ 982 if ((c >= 'A') && (c <= 'Z')) 983 c += 'a' - 'A'; 984 985 return c; 986} 987 988/* MARK: ^ RESOURCE RECORD FUNCTIONS */ 989/* MARK: v COMPARING */ 990 991/* **************************************************************************** 992 * Compares two databuffers against each other. 993 * 994 * @param a 1st Memory buffer to compare 995 * @param b 2nd Memory buffer to compare 996 * @param rdlength_a Length of 1st memory buffer 997 * @param rdlength_b Length of 2nd memory buffer 998 * @param caseinsensitive Whether or not the bytes are compared 999 * case-insensitive. Should be either 1000 * PICO_DNS_CASE_SENSITIVE or PICO_DNS_CASE_INSENSITIVE 1001 * @return 0 when the buffers are equal, returns difference when they're not. 1002 * ****************************************************************************/ 1003int 1004pico_dns_rdata_cmp( uint8_t *a, uint8_t *b, 1005 uint16_t rdlength_a, uint16_t rdlength_b, uint8_t caseinsensitive ) 1006{ 1007 uint16_t i = 0; 1008 uint16_t slen = 0; 1009 int dif = 0; 1010 1011 /* Check params */ 1012 if (!a || !b) { 1013 if (!a && !b) 1014 return 0; 1015 1016 pico_err = PICO_ERR_EINVAL; 1017 return -1; 1018 } 1019 1020 /* Determine the smallest length */ 1021 slen = rdlength_a; 1022 if (rdlength_b < slen) 1023 slen = rdlength_b; 1024 1025 /* loop over slen */ 1026 if(caseinsensitive) { 1027 for (i = 0; i < slen; i++) { 1028 if ((dif = pico_tolower((int)a[i]) - pico_tolower((int)b[i]))) { 1029 return dif; 1030 } 1031 } 1032 }else{ 1033 for (i = 0; i < slen; i++) { 1034 if ((dif = (int)a[i] - (int)b[i])) { 1035 return dif; 1036 } 1037 } 1038 } 1039 1040 /* Return difference of buffer lengths */ 1041 return (int)((int)rdlength_a - (int)rdlength_b); 1042} 1043 1044/* **************************************************************************** 1045 * Compares 2 DNS questions 1046 * 1047 * @param qa DNS question A as a void-pointer (for pico_tree) 1048 * @param qb DNS question A as a void-pointer (for pico_tree) 1049 * @return 0 when questions are equal, returns difference when they're not. 1050 * ****************************************************************************/ 1051int 1052pico_dns_question_cmp( void *qa, 1053 void *qb ) 1054{ 1055 int dif = 0; 1056 uint16_t at = 0, bt = 0; 1057 struct pico_dns_question *a = (struct pico_dns_question *)qa; 1058 struct pico_dns_question *b = (struct pico_dns_question *)qb; 1059 1060 /* Check params */ 1061 if (!a || !b) { 1062 pico_err = PICO_ERR_EINVAL; 1063 return -1; 1064 } 1065 1066 /* First, compare the qtypes */ 1067 at = short_be(a->qsuffix->qtype); 1068 bt = short_be(b->qsuffix->qtype); 1069 if ((dif = (int)((int)at - (int)bt))) 1070 return dif; 1071 1072 /* Then compare qnames */ 1073 return pico_dns_rdata_cmp((uint8_t *)a->qname, (uint8_t *)b->qname, 1074 pico_dns_strlen(a->qname), 1075 pico_dns_strlen(b->qname), PICO_DNS_CASE_INSENSITIVE); 1076} 1077 1078/* **************************************************************************** 1079 * Compares 2 DNS records by type and name only 1080 * 1081 * @param ra DNS record A as a void-pointer (for pico_tree) 1082 * @param rb DNS record B as a void-pointer (for pico_tree) 1083 * @return 0 when name and type of records are equal, returns difference when 1084 * they're not. 1085 * ****************************************************************************/ 1086int 1087pico_dns_record_cmp_name_type( void *ra, 1088 void *rb ) 1089{ 1090 int dif; 1091 uint16_t at = 0, bt = 0; 1092 struct pico_dns_record *a = (struct pico_dns_record *)ra; 1093 struct pico_dns_record *b = (struct pico_dns_record *)rb; 1094 1095 /* Check params */ 1096 if (!a || !b) { 1097 pico_err = PICO_ERR_EINVAL; 1098 return -1; 1099 } 1100 1101 /* First, compare the rrtypes */ 1102 at = short_be(a->rsuffix->rtype); 1103 bt = short_be(b->rsuffix->rtype); 1104 if ((dif = (int)((int)at - (int)bt))) 1105 return dif; 1106 1107 /* Then compare names */ 1108 return pico_dns_rdata_cmp((uint8_t *)(a->rname), (uint8_t *)(b->rname), 1109 (uint16_t)strlen(a->rname), 1110 (uint16_t)strlen(b->rname), PICO_DNS_CASE_INSENSITIVE); 1111} 1112 1113/* **************************************************************************** 1114 * Compares 2 DNS records by type, name AND rdata for a truly unique result 1115 * 1116 * @param ra DNS record A as a void-pointer (for pico_tree) 1117 * @param rb DNS record B as a void-pointer (for pico_tree) 1118 * @return 0 when records are equal, returns difference when they're not 1119 * ****************************************************************************/ 1120int 1121pico_dns_record_cmp( void *ra, 1122 void *rb ) 1123{ 1124 int dif = 0; 1125 struct pico_dns_record *a = (struct pico_dns_record *)ra; 1126 struct pico_dns_record *b = (struct pico_dns_record *)rb; 1127 1128 /* Check params */ 1129 if (!a || !b) { 1130 pico_err = PICO_ERR_EINVAL; 1131 return -1; 1132 } 1133 1134 /* Compare type and name */ 1135 if ((dif = pico_dns_record_cmp_name_type(a, b))) 1136 return dif; 1137 1138 /* Then compare rdata */ 1139 return pico_dns_rdata_cmp(a->rdata, b->rdata, 1140 short_be(a->rsuffix->rdlength), 1141 short_be(b->rsuffix->rdlength), PICO_DNS_CASE_SENSITIVE); 1142} 1143 1144/* MARK: ^ COMPARING */ 1145/* MARK: v PICO_TREE */ 1146 1147/* **************************************************************************** 1148 * Erases a pico_tree entirely. 1149 * 1150 * @param tree Pointer to a pico_tree-instance 1151 * @param node_delete Helper-function for type-specific deleting. 1152 * @return Returns 0 on success, something else on failure. 1153 * ****************************************************************************/ 1154int 1155pico_tree_destroy( struct pico_tree *tree, int (*node_delete)(void **)) 1156{ 1157 struct pico_tree_node *node = NULL, *next = NULL; 1158 void *item = NULL; 1159 1160 /* Check params */ 1161 if (!tree) { 1162 pico_err = PICO_ERR_EINVAL; 1163 return -1; 1164 } 1165 1166 pico_tree_foreach_safe(node, tree, next) { 1167 item = node->keyValue; 1168 pico_tree_delete(tree, node->keyValue); 1169 if (item && node_delete) { 1170 node_delete((void **)&item); 1171 } 1172 } 1173 1174 return 0; 1175} 1176 1177/* **************************************************************************** 1178 * Calculates the size in bytes of all the nodes contained in the tree summed 1179 * up. And gets the amount of items in the tree as well. 1180 * 1181 * @param tree Pointer to pico_tree-instance 1182 * @param size Will get filled with the size of all the nodes summed up. 1183 * Make sure you clear out (set to 0) this param before you 1184 * call this function because it doesn't happen inside and 1185 * each size will be added to the initial value. 1186 * @param node_size Helper-function for type-specific size-determination 1187 * @return Amount of items in the tree. 1188 * ****************************************************************************/ 1189static uint16_t 1190pico_tree_size( struct pico_tree *tree, 1191 uint16_t *size, 1192 uint16_t (*node_size)(void *)) 1193{ 1194 struct pico_tree_node *node = NULL; 1195 void *node_item = NULL; 1196 uint16_t count = 0; 1197 1198 /* Check params */ 1199 if (!tree || !size) { 1200 pico_err = PICO_ERR_EINVAL; 1201 return 0; 1202 } 1203 1204 /* Add up the node sizes */ 1205 pico_tree_foreach(node, tree) { 1206 if ((node_item = node->keyValue)) { 1207 *size = (uint16_t)((*size) + node_size(node_item)); 1208 count++; 1209 } 1210 } 1211 1212 return count; 1213} 1214 1215/* **************************************************************************** 1216 * Determines the amount of nodes in a pico_tere 1217 * 1218 * @param tree Pointer to pico_tree-instance 1219 * @return Amount of items in the tree. 1220 * ****************************************************************************/ 1221uint16_t 1222pico_tree_count( struct pico_tree *tree ) 1223{ 1224 struct pico_tree_node *node = NULL; 1225 uint16_t count = 0; 1226 1227 pico_tree_foreach(node, tree) { 1228 if (node->keyValue) 1229 count++; 1230 } 1231 1232 return count; 1233} 1234 1235/* **************************************************************************** 1236 * Deletes all the questions with given DNS name from a pico_tree 1237 * 1238 * @param qtree Pointer to pico_tree-instance which contains DNS questions 1239 * @param name Name of the questions you want to delete 1240 * @return Returns 0 on success, something else on failure. 1241 * ****************************************************************************/ 1242int 1243pico_dns_qtree_del_name( struct pico_tree *qtree, 1244 const char *name ) 1245{ 1246 struct pico_tree_node *node = NULL, *next = NULL; 1247 struct pico_dns_question *question = NULL; 1248 1249 /* Check params */ 1250 if (!qtree || !name) { 1251 pico_err = PICO_ERR_EINVAL; 1252 return -1; 1253 } 1254 1255 /* Iterate over tree and delete every node with given name */ 1256 pico_tree_foreach_safe(node, qtree, next) { 1257 question = (struct pico_dns_question *)node->keyValue; 1258 if ((question) && (strcasecmp(question->qname, name) == 0)) { 1259 question = pico_tree_delete(qtree, (void *)question); 1260 pico_dns_question_delete((void **)&question); 1261 } 1262 } 1263 1264 return 0; 1265} 1266 1267/* **************************************************************************** 1268 * Checks whether a question with given name is in the tree or not. 1269 * 1270 * @param qtree Pointer to pico_tree-instance which contains DNS questions 1271 * @param name Name you want to check for 1272 * @return 1 when the name is present in the qtree, 0 when it's not. 1273 * ****************************************************************************/ 1274int 1275pico_dns_qtree_find_name( struct pico_tree *qtree, 1276 const char *name ) 1277{ 1278 struct pico_tree_node *node = NULL; 1279 struct pico_dns_question *question = NULL; 1280 1281 /* Check params */ 1282 if (!qtree || !name) { 1283 pico_err = PICO_ERR_EINVAL; 1284 return 0; 1285 } 1286 1287 /* Iterate over tree and compare names */ 1288 pico_tree_foreach(node, qtree) { 1289 question = (struct pico_dns_question *)node->keyValue; 1290 if ((question) && (strcasecmp(question->qname, name) == 0)) 1291 return 1; 1292 } 1293 1294 return 0; 1295} 1296 1297/* MARK: ^ PICO_TREE */ 1298/* MARK: v DNS PACKET FUNCTIONS */ 1299 1300/* **************************************************************************** 1301 * Fills the header section of a DNS packet with the correct flags and section 1302 * -counts. 1303 * 1304 * @param hdr Header to fill in. 1305 * @param qdcount Amount of questions added to the packet 1306 * @param ancount Amount of answer records added to the packet 1307 * @param nscount Amount of authority records added to the packet 1308 * @param arcount Amount of additional records added to the packet 1309 * ****************************************************************************/ 1310void 1311pico_dns_fill_packet_header( struct pico_dns_header *hdr, 1312 uint16_t qdcount, 1313 uint16_t ancount, 1314 uint16_t nscount, 1315 uint16_t arcount ) 1316{ 1317 /* ID should be filled by caller */ 1318 1319 if(qdcount > 0) { /* Questions present? Make it a query */ 1320 hdr->qr = PICO_DNS_QR_QUERY; 1321 hdr->aa = PICO_DNS_AA_NO_AUTHORITY; 1322 } else { /* No questions present? Make it an answer*/ 1323 hdr->qr = PICO_DNS_QR_RESPONSE; 1324 hdr->aa = PICO_DNS_AA_IS_AUTHORITY; 1325 } 1326 1327 /* Fill in the flags and the fields */ 1328 hdr->opcode = PICO_DNS_OPCODE_QUERY; 1329 hdr->tc = PICO_DNS_TC_NO_TRUNCATION; 1330 hdr->rd = PICO_DNS_RD_IS_DESIRED; 1331 hdr->ra = PICO_DNS_RA_NO_SUPPORT; 1332 hdr->z = 0; /* Z, AD, CD are 0 */ 1333 hdr->rcode = PICO_DNS_RCODE_NO_ERROR; 1334 hdr->qdcount = short_be(qdcount); 1335 hdr->ancount = short_be(ancount); 1336 hdr->nscount = short_be(nscount); 1337 hdr->arcount = short_be(arcount); 1338} 1339 1340/* **************************************************************************** 1341 * Fills a single DNS resource record section of a DNS packet. 1342 * 1343 * @param rtree Tree that contains the DNS resource records. 1344 * @param dest Pointer-pointer to location where you want to insert records. 1345 * Will point to location after current section on return. 1346 * @return 0 on success, something else on failure. 1347 * ****************************************************************************/ 1348static int 1349pico_dns_fill_packet_rr_section( struct pico_tree *rtree, 1350 uint8_t **dest ) 1351{ 1352 struct pico_tree_node *node = NULL; 1353 struct pico_dns_record *record = NULL; 1354 1355 pico_tree_foreach(node, rtree) { 1356 record = node->keyValue; 1357 if ((record) && pico_dns_record_copy_flat(record, dest)) { 1358 dns_dbg("Could not copy record into Answer Section!\n"); 1359 return -1; 1360 } 1361 } 1362 return 0; 1363} 1364 1365/* **************************************************************************** 1366 * Fills the resource record sections of a DNS packet with provided record- 1367 * trees. 1368 * 1369 * @param packet Packet you want to fill 1370 * @param qtree Question tree to determine where the rrsections begin. 1371 * @param antree DNS records to put in Answer section 1372 * @param nstree DNS records to put in Authority section 1373 * @param artree DNS records to put in Additional section 1374 * @return 0 on success, something else on failure. 1375 * ****************************************************************************/ 1376static int 1377pico_dns_fill_packet_rr_sections( pico_dns_packet *packet, 1378 struct pico_tree *qtree, 1379 struct pico_tree *antree, 1380 struct pico_tree *nstree, 1381 struct pico_tree *artree ) 1382{ 1383 int anret = 0, nsret = 0, arret = 0; 1384 uint16_t temp = 0; 1385 uint8_t *destination = NULL; 1386 1387 /* Check params */ 1388 if (!packet || !qtree || !antree || !nstree || !artree) { 1389 pico_err = PICO_ERR_EINVAL; 1390 return -1; 1391 } 1392 1393 /* Initialise the destination pointers before iterating */ 1394 destination = (uint8_t *)packet + sizeof(struct pico_dns_header); 1395 pico_tree_size(qtree, &temp, &pico_dns_question_size); 1396 destination = destination + temp; 1397 1398 /* Iterate over ANSWERS */ 1399 anret = pico_dns_fill_packet_rr_section(antree, &destination); 1400 1401 /* Iterate over AUTHORITIES */ 1402 nsret = pico_dns_fill_packet_rr_section(nstree, &destination); 1403 1404 /* Iterate over ADDITIONALS */ 1405 arret = pico_dns_fill_packet_rr_section(artree, &destination); 1406 1407 if (anret || nsret || arret) 1408 return -1; 1409 1410 return 0; 1411} 1412 1413/* **************************************************************************** 1414 * Fills the question section of a DNS packet with provided questions in the 1415 * tree. 1416 * 1417 * @param packet Packet you want to fill 1418 * @param qtree Question tree with question you want to insert 1419 * @return 0 on success, something else on failure. 1420 * ****************************************************************************/ 1421static int 1422pico_dns_fill_packet_question_section( pico_dns_packet *packet, 1423 struct pico_tree *qtree ) 1424{ 1425 struct pico_tree_node *node = NULL; 1426 struct pico_dns_question *question = NULL; 1427 struct pico_dns_question_suffix *dest_qsuffix = NULL; 1428 char *dest_qname = NULL; 1429 1430 /* Check params */ 1431 if (!packet || !qtree) { 1432 pico_err = PICO_ERR_EINVAL; 1433 return -1; 1434 } 1435 1436 /* Initialise pointer */ 1437 dest_qname = (char *)((char *)packet + sizeof(struct pico_dns_header)); 1438 1439 pico_tree_foreach(node, qtree) { 1440 question = node->keyValue; 1441 if (question) { 1442 /* Copy the name */ 1443 memcpy(dest_qname, question->qname, question->qname_length); 1444 1445 /* Copy the suffix */ 1446 dest_qsuffix = (struct pico_dns_question_suffix *) 1447 (dest_qname + question->qname_length); 1448 dest_qsuffix->qtype = question->qsuffix->qtype; 1449 dest_qsuffix->qclass = question->qsuffix->qclass; 1450 1451 /* Move to next question */ 1452 dest_qname = (char *)((char *)dest_qsuffix + 1453 sizeof(struct pico_dns_question_suffix)); 1454 } 1455 } 1456 return 0; 1457} 1458 1459/* **************************************************************************** 1460 * Looks for a name somewhere else in packet, more specifically between the 1461 * beginning of the data buffer and the name itself. 1462 * ****************************************************************************/ 1463static uint8_t * 1464pico_dns_packet_compress_find_ptr( uint8_t *name, 1465 uint8_t *data, 1466 uint16_t len ) 1467{ 1468 uint8_t *iterator = NULL; 1469 1470 /* Check params */ 1471 if (!name || !data || !len) 1472 return NULL; 1473 1474 if ((name < data) || (name > (data + len))) 1475 return NULL; 1476 1477 iterator = data; 1478 1479 /* Iterate from the beginning of data up until the name-ptr */ 1480 while (iterator < name) { 1481 /* Compare in each iteration of current name is equal to a section of 1482 the DNS packet and if so return the pointer to that section */ 1483 if (memcmp((void *)iterator++, (void *)name, 1484 pico_dns_strlen((char *)name) + 1u) == 0) 1485 return (iterator - 1); 1486 } 1487 return NULL; 1488} 1489 1490/* **************************************************************************** 1491 * Compresses a single name by looking for the same name somewhere else in the 1492 * packet-buffer. 1493 * ****************************************************************************/ 1494static int 1495pico_dns_packet_compress_name( uint8_t *name, 1496 uint8_t *packet, 1497 uint16_t *len) 1498{ 1499 uint8_t *lbl_iterator = NULL; /* To iterate over labels */ 1500 uint8_t *compression_ptr = NULL; /* PTR to somewhere else in the packet */ 1501 uint8_t *offset = NULL; /* PTR after compression pointer */ 1502 uint8_t *ptr_after_str = NULL; 1503 uint8_t *last_byte = NULL; 1504 uint8_t *i = NULL; 1505 uint16_t ptr = 0; 1506 uint16_t difference = 0; 1507 1508 /* Check params */ 1509 if (!name || !packet || !len) { 1510 pico_err = PICO_ERR_EINVAL; 1511 return -1; 1512 } 1513 1514 if ((name < packet) || (name > (packet + *len))) { 1515 dns_dbg("Name ptr OOB. name: %p max: %p\n", name, packet + *len); 1516 pico_err = PICO_ERR_EINVAL; 1517 return -1; 1518 } 1519 1520 /* Try to compress name */ 1521 lbl_iterator = name; 1522 while (lbl_iterator != '\0') { 1523 /* Try to find a compression pointer with current name */ 1524 compression_ptr = pico_dns_packet_compress_find_ptr(lbl_iterator, 1525 packet + 12, *len); 1526 /* If name can be compressed */ 1527 if (compression_ptr) { 1528 /* Point to place after current string */ 1529 ptr_after_str = lbl_iterator + strlen((char *)lbl_iterator) + 1u; 1530 1531 /* Calculate the compression pointer value */ 1532 ptr = (uint16_t)(compression_ptr - packet); 1533 1534 /* Set the compression pointer in the packet */ 1535 *lbl_iterator = (uint8_t)(0xC0 | (uint8_t)(ptr >> 8)); 1536 *(lbl_iterator + 1) = (uint8_t)(ptr & 0xFF); 1537 1538 /* Move up the rest of the packet data to right after the pointer */ 1539 offset = lbl_iterator + 2; 1540 1541 /* Move up left over data */ 1542 difference = (uint16_t)(ptr_after_str - offset); 1543 last_byte = packet + *len; 1544 for (i = ptr_after_str; i < last_byte; i++) { 1545 *((uint8_t *)(i - difference)) = *i; 1546 } 1547 /* Update length */ 1548 *len = (uint16_t)(*len - difference); 1549 break; 1550 } 1551 1552 /* Move to next length label */ 1553 lbl_iterator = lbl_iterator + *(lbl_iterator) + 1; 1554 } 1555 return 0; 1556} 1557 1558/* **************************************************************************** 1559 * Utility function compress a record section 1560 * ****************************************************************************/ 1561static int 1562pico_dns_compress_record_sections( uint16_t qdcount, uint16_t count, 1563 uint8_t *buf, uint8_t **iterator, 1564 uint16_t *len ) 1565{ 1566 struct pico_dns_record_suffix *rsuffix = NULL; 1567 uint8_t *_iterator = NULL; 1568 uint16_t i = 0; 1569 1570 /* Check params */ 1571 if (!iterator || !(*iterator) || !buf || !len) { 1572 pico_err = PICO_ERR_EINVAL; 1573 return -1; 1574 } 1575 1576 _iterator = *iterator; 1577 1578 for (i = 0; i < count; i++) { 1579 if (qdcount || i) 1580 pico_dns_packet_compress_name(_iterator, buf, len); 1581 1582 /* To get rdlength */ 1583 rsuffix = (struct pico_dns_record_suffix *) 1584 (_iterator + pico_dns_namelen_comp((char *)_iterator) + 1u); 1585 1586 /* Move to next res record */ 1587 _iterator = ((uint8_t *)rsuffix + 1588 sizeof(struct pico_dns_record_suffix) + 1589 short_be(rsuffix->rdlength)); 1590 } 1591 *iterator = _iterator; 1592 return 0; 1593} 1594 1595/* **************************************************************************** 1596 * Applies DNS name compression to an entire DNS packet 1597 * ****************************************************************************/ 1598static int 1599pico_dns_packet_compress( pico_dns_packet *packet, uint16_t *len ) 1600{ 1601 uint8_t *packet_buf = NULL; 1602 uint8_t *iterator = NULL; 1603 uint16_t qdcount = 0, rcount = 0, i = 0; 1604 1605 /* Check params */ 1606 if (!packet || !len) { 1607 pico_err = PICO_ERR_EINVAL; 1608 return -1; 1609 } 1610 1611 packet_buf = (uint8_t *)packet; 1612 1613 /* Temporarily store the question & record counts */ 1614 qdcount = short_be(packet->qdcount); 1615 rcount = (uint16_t)(rcount + short_be(packet->ancount)); 1616 rcount = (uint16_t)(rcount + short_be(packet->nscount)); 1617 rcount = (uint16_t)(rcount + short_be(packet->arcount)); 1618 1619 /* Move past the DNS packet header */ 1620 iterator = (uint8_t *)((uint8_t *) packet + 12u); 1621 1622 /* Start with the questions */ 1623 for (i = 0; i < qdcount; i++) { 1624 if(i) { /* First question can't be compressed */ 1625 pico_dns_packet_compress_name(iterator, packet_buf, len); 1626 } 1627 1628 /* Move to next question */ 1629 iterator = (uint8_t *)(iterator + 1630 pico_dns_namelen_comp((char *)iterator) + 1631 sizeof(struct pico_dns_question_suffix) + 1u); 1632 } 1633 /* Then onto the answers */ 1634 pico_dns_compress_record_sections(qdcount, rcount, packet_buf, &iterator, 1635 len); 1636 return 0; 1637} 1638 1639/* **************************************************************************** 1640 * Calculates how big a packet needs be in order to store all the questions & 1641 * records in the tree. Also determines the amount of questions and records. 1642 * 1643 * @param qtree Tree with Questions. 1644 * @param antree Tree with Answer Records. 1645 * @param nstree Tree with Authority Records. 1646 * @param artree Tree with Additional Records. 1647 * @param qdcount Pointer to var to store amount of questions 1648 * @param ancount Pointer to var to store amount of answers. 1649 * @param nscount Pointer to var to store amount of authorities. 1650 * @param arcount Pointer to var to store amount of additionals. 1651 * @return Returns the total length that the DNS packet needs to be. 1652 * ****************************************************************************/ 1653static uint16_t 1654pico_dns_packet_len( struct pico_tree *qtree, 1655 struct pico_tree *antree, 1656 struct pico_tree *nstree, 1657 struct pico_tree *artree, 1658 uint8_t *qdcount, uint8_t *ancount, 1659 uint8_t *nscount, uint8_t *arcount ) 1660{ 1661 uint16_t len = (uint16_t) sizeof(pico_dns_packet); 1662 1663 /* Check params */ 1664 if (!qtree || !antree || !nstree || !artree) { 1665 pico_err = PICO_ERR_EINVAL; 1666 return 0; 1667 } 1668 1669 *qdcount = (uint8_t)pico_tree_size(qtree, &len, &pico_dns_question_size); 1670 *ancount = (uint8_t)pico_tree_size(antree, &len, &pico_dns_record_size); 1671 *nscount = (uint8_t)pico_tree_size(nstree, &len, &pico_dns_record_size); 1672 *arcount = (uint8_t)pico_tree_size(artree, &len, &pico_dns_record_size); 1673 return len; 1674} 1675 1676/* **************************************************************************** 1677 * Generic packet creation utility that just creates a DNS packet with given 1678 * questions and resource records to put in the Resource Record Sections. If a 1679 * NULL-pointer is provided for a certain tree, no records will be added to 1680 * that particular section of the packet. 1681 * 1682 * @param qtree DNS Questions to put in the Question Section. 1683 * @param antree DNS Records to put in the Answer Section. 1684 * @param nstree DNS Records to put in the Authority Section. 1685 * @param artree DNS Records to put in the Additional Section. 1686 * @param len Will get fill with the entire size of the packet 1687 * @return Pointer to created DNS packet 1688 * ****************************************************************************/ 1689static pico_dns_packet * 1690pico_dns_packet_create( struct pico_tree *qtree, 1691 struct pico_tree *antree, 1692 struct pico_tree *nstree, 1693 struct pico_tree *artree, 1694 uint16_t *len ) 1695{ 1696 PICO_DNS_QTREE_DECLARE(_qtree); 1697 PICO_DNS_RTREE_DECLARE(_antree); 1698 PICO_DNS_RTREE_DECLARE(_nstree); 1699 PICO_DNS_RTREE_DECLARE(_artree); 1700 pico_dns_packet *packet = NULL; 1701 uint8_t qdcount = 0, ancount = 0, nscount = 0, arcount = 0; 1702 1703 /* Set default vector, if arguments are NULL-pointers */ 1704 _qtree = (qtree) ? (*qtree) : (_qtree); 1705 _antree = (antree) ? (*antree) : (_antree); 1706 _nstree = (nstree) ? (*nstree) : (_nstree); 1707 _artree = (artree) ? (*artree) : (_artree); 1708 1709 /* Get the size of the entire packet and determine the header counters */ 1710 *len = pico_dns_packet_len(&_qtree, &_antree, &_nstree, &_artree, 1711 &qdcount, &ancount, &nscount, &arcount); 1712 1713 /* Provide space for the entire packet */ 1714 if (!(packet = PICO_ZALLOC(*len))) { 1715 pico_err = PICO_ERR_ENOMEM; 1716 return NULL; 1717 } 1718 1719 /* Fill the Question Section with questions */ 1720 if (qtree && pico_tree_count(&_qtree) != 0) { 1721 if (pico_dns_fill_packet_question_section(packet, &_qtree)) { 1722 dns_dbg("Could not fill Question Section correctly!\n"); 1723 PICO_FREE(packet); 1724 return NULL; 1725 } 1726 } 1727 1728 /* Fill the Resource Record Sections with resource records */ 1729 if (pico_dns_fill_packet_rr_sections(packet, &_qtree, &_antree, 1730 &_nstree, &_artree)) { 1731 dns_dbg("Could not fill Resource Record Sections correctly!\n"); 1732 PICO_FREE(packet); 1733 return NULL; 1734 } 1735 1736 /* Fill the DNS packet header and try to compress */ 1737 pico_dns_fill_packet_header(packet, qdcount, ancount, nscount, arcount); 1738 pico_dns_packet_compress(packet, len); 1739 1740 return packet; 1741} 1742 1743/* **************************************************************************** 1744 * Creates a DNS Query packet with given question and resource records to put 1745 * the Resource Record Sections. If a NULL-pointer is provided for a certain 1746 * tree, no records will be added to that particular section of the packet. 1747 * 1748 * @param qtree DNS Questions to put in the Question Section 1749 * @param antree DNS Records to put in the Answer Section 1750 * @param nstree DNS Records to put in the Authority Section 1751 * @param artree DNS Records to put in the Additional Section 1752 * @param len Will get filled with the entire size of the packet 1753 * @return Pointer to created DNS packet 1754 * ****************************************************************************/ 1755pico_dns_packet * 1756pico_dns_query_create( struct pico_tree *qtree, 1757 struct pico_tree *antree, 1758 struct pico_tree *nstree, 1759 struct pico_tree *artree, 1760 uint16_t *len ) 1761{ 1762 return pico_dns_packet_create(qtree, antree, nstree, artree, len); 1763} 1764 1765/* **************************************************************************** 1766 * Creates a DNS Answer packet with given resource records to put in the 1767 * Resource Record Sections. If a NULL-pointer is provided for a certain tree, 1768 * no records will be added to that particular section of the packet. 1769 * 1770 * @param antree DNS Records to put in the Answer Section 1771 * @param nstree DNS Records to put in the Authority Section 1772 * @param artree DNS Records to put in the Additional Section 1773 * @param len Will get filled with the entire size of the packet 1774 * @return Pointer to created DNS packet. 1775 * ****************************************************************************/ 1776pico_dns_packet * 1777pico_dns_answer_create( struct pico_tree *antree, 1778 struct pico_tree *nstree, 1779 struct pico_tree *artree, 1780 uint16_t *len ) 1781{ 1782 return pico_dns_packet_create(NULL, antree, nstree, artree, len); 1783} 1784/* MARK: ^ DNS PACKET FUNCTIONS */ 1785