aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/dns6/dns6.c
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/dns6/dns6.c')
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/dns6/dns6.c903
1 files changed, 903 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/dns6/dns6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/dns6/dns6.c
new file mode 100644
index 0000000..6c90ce3
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/ipv6_stack/dns6/dns6.c
@@ -0,0 +1,903 @@
+/**
+ * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form, except as embedded into a Nordic
+ * Semiconductor ASA integrated circuit in a product or a software update for
+ * such product, must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * 4. This software, with or without modification, must only be used with a
+ * Nordic Semiconductor ASA integrated circuit.
+ *
+ * 5. Any software provided in binary form under this license must not be reverse
+ * engineered, decompiled, modified and/or disassembled.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include "sdk_errors.h"
+#include "sdk_os.h"
+#include "sdk_config.h"
+#include "iot_common.h"
+#include "iot_pbuffer.h"
+#include "mem_manager.h"
+#include "ipv6_api.h"
+#include "udp_api.h"
+#include "dns6_api.h"
+
+#if DNS6_CONFIG_LOG_ENABLED
+
+#define NRF_LOG_MODULE_NAME dns6
+
+#define NRF_LOG_LEVEL DNS6_CONFIG_LOG_LEVEL
+#define NRF_LOG_INFO_COLOR DNS6_CONFIG_INFO_COLOR
+#define NRF_LOG_DEBUG_COLOR DNS6_CONFIG_DEBUG_COLOR
+
+#include "nrf_log.h"
+NRF_LOG_MODULE_REGISTER();
+
+#define DNS6_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */
+#define DNS6_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */
+#define DNS6_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */
+
+#define DNS6_ENTRY() DNS6_TRC(">> %s", __func__)
+#define DNS6_EXIT() DNS6_TRC("<< %s", __func__)
+
+#else // DNS6_CONFIG_LOG_ENABLED
+
+#define DNS6_TRC(...) /**< Disables traces. */
+#define DNS6_DUMP(...) /**< Disables dumping of octet streams. */
+#define DNS6_ERR(...) /**< Disables error logs. */
+
+#define DNS6_ENTRY(...)
+#define DNS6_EXIT(...)
+
+#endif // DNS6_CONFIG_LOG_ENABLED
+
+/**
+ * @defgroup dns6_mutex_lock_unlock Module's Mutex Lock/Unlock Macros.
+ *
+ * @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but
+ * framework is provided in case need arises to use an alternative architecture.
+ * @{
+ */
+#define DNS6_MUTEX_LOCK() SDK_MUTEX_LOCK(m_dns6_mutex) /**< Lock module using mutex */
+#define DNS6_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_dns6_mutex) /**< Unlock module using mutex */
+/** @} */
+
+/**
+ * @defgroup api_param_check API Parameters check macros.
+ *
+ * @details Macros that verify parameters passed to the module in the APIs. These macros
+ * could be mapped to nothing in final versions of code to save execution and size.
+ * DNS6_DISABLE_API_PARAM_CHECK should be set to 0 to enable these checks.
+ *
+ * @{
+ */
+
+#if (DNS6_DISABLE_API_PARAM_CHECK == 0)
+
+/**@brief Macro to check is module is initialized before requesting one of the module procedures. */
+#define VERIFY_MODULE_IS_INITIALIZED() \
+ if (m_initialization_state == false) \
+ { \
+ return (SDK_ERR_MODULE_NOT_INITIALIZED | IOT_DNS6_ERR_BASE);\
+ }
+
+/**@brief Verify NULL parameters are not passed to API by application. */
+#define NULL_PARAM_CHECK(PARAM) \
+ if ((PARAM) == NULL) \
+ { \
+ return (NRF_ERROR_NULL | IOT_DNS6_ERR_BASE); \
+ }
+
+/**@brief Verify that empty parameters are not passed to API by application. */
+#define EMPTY_PARAM_CHECK(PARAM) \
+ if (*PARAM == 0) \
+ { \
+ return (NRF_ERROR_INVALID_DATA | IOT_DNS6_ERR_BASE); \
+ }
+
+#else // DNS6_DISABLE_API_PARAM_CHECK
+
+#define VERIFY_MODULE_IS_INITIALIZED()
+#define NULL_PARAM_CHECK(PARAM)
+#define EMPTY_PARAM_CHECK(PARAM)
+
+#endif // DNS6_DISABLE_API_PARAM_CHECK
+/** @} */
+
+/**@brief RFC1035 - DNS Header Fields and Values. */
+#define DNS_HEADER_FLAG1_QR_QUERY 0x00 /**< Bit specifies that message is a query. */
+#define DNS_HEADER_FLAG1_QR_RESPONSE 0x80 /**< Bit specifies that message is a response. */
+#define DNS_HEADER_FLAG1_OPCODE_STANDARD 0x00 /**< A standard type of query. */
+#define DNS_HEADER_FLAG1_OPCODE_INVERSE 0x08 /**< An inverse type of query. */
+#define DNS_HEADER_FLAG1_OPCODE_STATUS 0x10 /**< A server status request. */
+#define DNS_HEADER_FLAG1_AA 0x04 /**< Bit specifies that the responding name server is an authority for the domain name in question section. */
+#define DNS_HEADER_FLAG1_TC 0x02 /**< Bit specifies that message is truncated. */
+#define DNS_HEADER_FLAG1_RD 0x01 /**< Bit specifies that recursion is desired. */
+
+#define DNS_HEADER_FLAG2_RA 0x80 /**< Bit specifies if recursive query support is available in the name server. */
+#define DNS_HEADER_FLAG2_RCODE_NONE 0x00 /**< No error condition. */
+#define DNS_HEADER_FLAG2_RCODE_FORMAT_ERROR 0x01 /**< Error indicates that dns server is unable o interpret the query. */
+#define DNS_HEADER_FLAG2_RCODE_SERVER_FAILURE 0x02 /**< Error indicates that dns server has internal problem. */
+#define DNS_HEADER_FLAG2_RCODE_NAME_ERROR 0x03 /**< Error indicates that domain name referenced in the query does not exist. */
+#define DNS_HEADER_FLAG2_RCODE_NOT_IMPLEMENTED 0x04 /**< Error indicates that dns server does not support previously sent query. */
+#define DNS_HEADER_FLAG2_RCODE_REFUSED 0x05 /**< Error indicates that dns server refuses to perform operation. */
+#define DNS_HEADER_FLAG2_RCODE_MASK 0x0F /**< Bit mask of RCODE field. */
+
+#define DNS_QTYPE_A 0x0001 /**< QTYPE indicates IPv4 address. */
+#define DNS_QTYPE_CNAME 0x0005 /**< QTYPE indicates CNAME record. */
+#define DNS_QTYPE_AAAA 0x001C /**< QTYPE indicates IPv6 address. */
+
+#define DNS_QCLASS_IN 0x0001 /**< QCLASS indicates Internet type. */
+
+/**@brief DNS6 client module's defines. */
+#define DNS_LABEL_SEPARATOR '.' /**< Separator of hostname string. */
+#define DNS_LABEL_OFFSET 0xc0 /**< Byte indicates that offset is used to determine hostname. */
+
+#define DNS_HEADER_SIZE 12 /**< Size of DNS Header. */
+#define DNS_QUESTION_FOOTER_SIZE 4 /**< Size of DNS Question footer. */
+#define DNS_RR_BODY_SIZE 10 /**< Size of DNS Resource Record Body. */
+
+#define MESSAGE_ID_UNUSED 0 /**< Value indicates that record is unused and no request was performed yet. */
+#define MESSAGE_ID_INITIAL 0x0001 /**< Initial value of message id counter. */
+
+
+/**@brief DNS Header Format. */
+typedef struct
+{
+ uint16_t msg_id; /**< Query/Response message identifier. */
+ uint8_t flags_1; /**< Flags ( QR | Opcode | AA | TC | RD ). */
+ uint8_t flags_2; /**< Flags ( RA | Z | RCODE ). */
+ uint16_t qdcount; /**< The number of entries in the question section. */
+ uint16_t ancount; /**< The number of resource records in the answer section. */
+ uint16_t nscount; /**< The number of name server resource records in the authority records section. */
+ uint16_t arcount; /**< The number of resource records in the additional records section. */
+} dns_header_t;
+
+/**@brief DNS Question Footer Format. */
+typedef struct
+{
+ uint16_t qtype; /**< Type of the query. */
+ uint16_t qclass; /**< Class of the query. */
+} dns_question_footer_t;
+
+/**@brief DNS Resource AAAA Record Body Format. */
+typedef struct
+{
+ uint16_t rtype; /**< Type of the response. */
+ uint16_t rclass; /**< Class of the response. */
+ uint32_t rttl; /**< Time to Life field of the response. */
+ uint16_t rdlength; /**< Length of data in octets. */
+} dns_rr_body_t;
+
+/**@brief Structure holds pending query. */
+typedef struct
+{
+ uint16_t message_id; /**< Message id for DNS Query. */
+ uint8_t retries; /**< Number of already performed retries. */
+ uint8_t * p_hostname; /**< Pointer to hostname string in memory menager.*/
+ iot_timer_time_in_ms_t next_retransmission; /**< Time when next retransmission should be invoked. */
+ dns6_evt_handler_t evt_handler; /**< User registered callback. */
+} pending_query_t;
+
+SDK_MUTEX_DEFINE(m_dns6_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */
+static bool m_initialization_state = false; /**< Variable to maintain module initialization state. */
+static pending_query_t m_pending_queries[DNS6_MAX_PENDING_QUERIES]; /**< Queue contains pending queries. */
+static uint16_t m_message_id_counter; /**< Message ID counter, used to generate unique message IDs. */
+static udp6_socket_t m_socket; /**< Socket information provided by UDP. */
+
+
+/**@brief Function for freeing query entry in pending queue.
+ *
+ * @param[in] index Index of query.
+ *
+ * @retval None.
+ */
+static void query_init(uint32_t index)
+{
+ if (m_pending_queries[index].p_hostname)
+ {
+ UNUSED_VARIABLE(nrf_free(m_pending_queries[index].p_hostname));
+ }
+
+ m_pending_queries[index].message_id = MESSAGE_ID_UNUSED;
+ m_pending_queries[index].retries = 0;
+ m_pending_queries[index].p_hostname = NULL;
+ m_pending_queries[index].evt_handler = NULL;
+ m_pending_queries[index].next_retransmission = 0;
+}
+
+
+/**@brief Function for adding new query to pending queue.
+ *
+ * @param[in] p_hostname Pointer to hostname string.
+ * @param[in] evt_handler User defined event to handle given query.
+ *
+ * @retval Index of element in pending queries' table or DNS6_MAX_PENDING_QUERIES if no memory.
+ */
+static uint32_t query_add(uint8_t * p_hostname, dns6_evt_handler_t evt_handler)
+{
+ uint32_t index;
+
+ for (index = 0; index < DNS6_MAX_PENDING_QUERIES; index++)
+ {
+ if (m_pending_queries[index].message_id == MESSAGE_ID_UNUSED)
+ {
+ m_pending_queries[index].message_id = m_message_id_counter++;
+ m_pending_queries[index].retries = 0;
+ m_pending_queries[index].p_hostname = p_hostname;
+ m_pending_queries[index].evt_handler = evt_handler;
+ m_pending_queries[index].next_retransmission = 0;
+
+ break;
+ }
+ }
+
+ return index;
+}
+
+
+/**@brief Function for finding element in pending queue with specific message_id.
+ *
+ * @param[in] message_id Message identifier to find.
+ *
+ * @retval Index of element in pending queue or DNS6_MAX_PENDING_QUERIES if nothing found.
+ */
+static uint32_t query_find(uint32_t message_id)
+{
+ uint32_t index;
+
+ for (index = 0; index < DNS6_MAX_PENDING_QUERIES; index++)
+ {
+ if (m_pending_queries[index].message_id == message_id)
+ {
+ break;
+ }
+ }
+
+ return index;
+}
+
+
+/**@brief Function for checking if retransmission time of DNS query has been expired.
+ *
+ * @param[in] index Index of pending query.
+ *
+ * @retval True if timer has been expired, False otherwise.
+ */
+static bool query_timer_is_expired(uint32_t index)
+{
+ uint32_t err_code;
+ iot_timer_time_in_ms_t wall_clock_value;
+
+ // Get wall clock time.
+ err_code = iot_timer_wall_clock_get(&wall_clock_value);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ if (wall_clock_value >= m_pending_queries[index].next_retransmission)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/**@brief Function for setting retransmissions time of DNS query has been expired.
+ *
+ * @param[in] index Index of pending query.
+ *
+ * @retval None.
+ */
+static void query_timer_set(uint32_t index)
+{
+ uint32_t err_code;
+ iot_timer_time_in_ms_t wall_clock_value;
+
+ // Get wall clock time.
+ err_code = iot_timer_wall_clock_get(&wall_clock_value);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ m_pending_queries[index].next_retransmission =
+ wall_clock_value + (DNS6_RETRANSMISSION_INTERVAL * 1000);
+ }
+}
+
+
+/**@brief Function for creating compressed hostname from string.
+ *
+ * @param[inout] p_dest Pointer to place where hostname will be compressed.
+ * @param[in] p_hostname Pointer to hostname string.
+ *
+ * @retval Number of used bytes to compress a hostname.
+ */
+static uint32_t compress_hostname(uint8_t * p_dest, const uint8_t * p_hostname)
+{
+ uint32_t index = 0;
+ uint32_t label_pos = 0;
+ uint8_t * p_original = p_dest;
+
+ // Elide first byte in destination buffer to put label.
+ p_dest++;
+
+ // Parse until string termination is found.
+ for (index = 0; p_hostname[index] != 0; index++)
+ {
+ // Look for string separator.
+ if (p_hostname[index] == DNS_LABEL_SEPARATOR)
+ {
+ // Put number of subsequent string to last label.
+ p_original[label_pos] = index - label_pos;
+
+ // Protection to stop compressing after getting incorrect sequence.
+ if (index == label_pos)
+ {
+ return index + 1;
+ }
+
+ label_pos = index + 1;
+ }
+ else
+ {
+ // Copy character of hostname to destination buffer.
+ *p_dest = p_hostname[index];
+ }
+
+ p_dest++;
+ }
+
+ // Set last label.
+ p_original[label_pos] = index - label_pos;
+
+ // Terminate compressed hostname with 0.
+ *p_dest = 0;
+
+ // Return length of compressed string.
+ return index + 2;
+}
+
+
+/**@brief Function for finding end of compressed hostname.
+ *
+ * @param[in] p_hostname Pointer to compressed hostname string.
+ *
+ * @retval Pointer to the end of compressed hostname.
+ */
+static uint8_t * skip_compressed_hostname(uint8_t * p_hostname)
+{
+ while (*p_hostname != 0)
+ {
+ if ((*p_hostname & DNS_LABEL_OFFSET) == DNS_LABEL_OFFSET)
+ {
+ return p_hostname + 2;
+ }
+ else
+ {
+ p_hostname += *p_hostname + 1;
+ }
+ }
+
+ return p_hostname + 1;
+}
+
+
+/**@brief Function for sending DNS query.
+ *
+ * @param[in] index Index of query.
+ *
+ * @retval NRF_SUCCESS on successful execution of procedure, else an error code indicating reason
+ * for failure.
+ */
+static uint32_t query_send(uint32_t index)
+{
+ uint32_t length;
+ uint32_t err_code;
+ iot_pbuffer_t * p_buffer;
+ iot_pbuffer_alloc_param_t buffer_param;
+
+ buffer_param.type = UDP6_PACKET_TYPE;
+ buffer_param.flags = PBUFFER_FLAG_DEFAULT;
+ buffer_param.length = DNS_HEADER_SIZE + DNS_QUESTION_FOOTER_SIZE +
+ strlen((const char *)m_pending_queries[index].p_hostname) + 2;
+
+ // Allocate packet buffer.
+ err_code = iot_pbuffer_allocate(&buffer_param, &p_buffer);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ const dns_question_footer_t question_footer =
+ {
+ .qtype = HTONS(DNS_QTYPE_AAAA),
+ .qclass = HTONS(DNS_QCLASS_IN)
+ };
+
+ dns_header_t * p_dns_header = (dns_header_t *)p_buffer->p_payload;
+
+ // Fill DNS header fields.
+ p_dns_header->msg_id = HTONS(m_pending_queries[index].message_id);
+ p_dns_header->flags_1 = DNS_HEADER_FLAG1_QR_QUERY | DNS_HEADER_FLAG1_RD;
+ p_dns_header->flags_2 = DNS_HEADER_FLAG2_RCODE_NONE;
+
+ // Send only one question.
+ p_dns_header->qdcount = HTONS(1);
+ p_dns_header->ancount = HTONS(0);
+ p_dns_header->nscount = HTONS(0);
+ p_dns_header->arcount = HTONS(0);
+
+ // Start indexing from the end of the DNS header.
+ length = DNS_HEADER_SIZE;
+
+ // Compress and put hostname.
+ length += compress_hostname(&p_buffer->p_payload[length],
+ m_pending_queries[index].p_hostname);
+
+ // Add question footer.
+ memcpy(&p_buffer->p_payload[length], (uint8_t *)&question_footer, DNS_QUESTION_FOOTER_SIZE);
+
+ length += DNS_QUESTION_FOOTER_SIZE;
+
+ // Update packet buffer's data length.
+ p_buffer->length = length;
+
+ // Set retransmission timer.
+ query_timer_set(index);
+
+ // Send DNS query using UDP socket.
+ err_code = udp6_socket_send(&m_socket, p_buffer);
+
+ if (err_code != NRF_SUCCESS)
+ {
+ DNS6_ERR("Unable to send query on UDP socket. Reason %08lx.", err_code);
+
+ // Free the allocated buffer as send procedure has failed.
+ UNUSED_VARIABLE(iot_pbuffer_free(p_buffer, true));
+ }
+ }
+ else
+ {
+ DNS6_ERR("No memory to allocate packet buffer.");
+ }
+
+ return err_code;
+}
+
+
+/**@brief Function for notifying application of the DNS6 query status.
+ *
+ * @param[in] index Index of query.
+ * @param[in] process_result Variable indicates result of DNS query.
+ * @param[in] p_addr Pointer to memory that holds IPv6 addresses.
+ * @param[in] addr_count Number of found addresses.
+ *
+ * @retval None.
+ */
+static void app_notify(uint32_t index,
+ uint32_t process_result,
+ ipv6_addr_t * p_addr,
+ uint16_t addr_count)
+{
+ if (m_pending_queries[index].evt_handler)
+ {
+ DNS6_MUTEX_UNLOCK();
+
+ // Call handler of user request.
+ m_pending_queries[index].evt_handler(process_result,
+ (const char *)m_pending_queries[index].p_hostname,
+ p_addr,
+ addr_count);
+
+ DNS6_MUTEX_LOCK();
+ }
+}
+
+
+/**@brief Callback handler to receive data on the UDP port.
+ *
+ * @param[in] p_socket Socket identifier.
+ * @param[in] p_ip_header IPv6 header containing source and destination addresses.
+ * @param[in] p_udp_header UDP header identifying local and remote endpoints.
+ * @param[in] process_result Result of data reception, there could be possible errors like
+ * invalid checksum etc.
+ * @param[in] p_rx_packet Packet buffer containing the received data packet.
+ *
+ * @retval NRF_SUCCESS Indicates received data was handled successfully, else an an
+ * error code indicating reason for failure..
+ */
+static uint32_t server_response(const udp6_socket_t * p_socket,
+ const ipv6_header_t * p_ip_header,
+ const udp6_header_t * p_udp_header,
+ uint32_t process_result,
+ iot_pbuffer_t * p_rx_packet)
+{
+ uint32_t index;
+ uint32_t rr_index;
+ uint32_t err_code = NRF_SUCCESS;
+ ipv6_addr_t * p_addresses = NULL;
+ uint16_t addr_length = 0;
+
+ DNS6_MUTEX_LOCK();
+
+ DNS6_ENTRY();
+
+ // Check UDP process result and data length.
+ if ((process_result != NRF_SUCCESS) || p_rx_packet->length < DNS_HEADER_SIZE)
+ {
+ DNS6_ERR("Received erroneous response.");
+ err_code = (NRF_ERROR_INVALID_DATA | IOT_DNS6_ERR_BASE);
+ }
+ else
+ {
+ dns_header_t * p_dns_header = (dns_header_t *)p_rx_packet->p_payload;
+ uint8_t * p_data = &p_rx_packet->p_payload[DNS_HEADER_SIZE];
+ uint16_t qdcount = NTOHS(p_dns_header->qdcount);
+ uint16_t ancount = NTOHS(p_dns_header->ancount);
+
+ // Try to find a proper query for this response, else discard.
+ index = query_find(NTOHS(p_dns_header->msg_id));
+
+ if (index != DNS6_MAX_PENDING_QUERIES)
+ {
+ DNS6_TRC("Received response for hostname %s with %d answers.",
+ m_pending_queries[index].p_hostname, ancount);
+
+ // Check truncation error.
+ if (p_dns_header->flags_1 & DNS_HEADER_FLAG1_TC)
+ {
+ err_code = DNS6_RESPONSE_TRUNCATED;
+ }
+ else if (!(p_dns_header->flags_1 & DNS_HEADER_FLAG1_QR_RESPONSE))
+ {
+ err_code = (NRF_ERROR_INVALID_DATA | IOT_DNS6_ERR_BASE);
+ }
+ // Check response code.
+ else if (p_dns_header->flags_2 & DNS_HEADER_FLAG2_RCODE_MASK)
+ {
+ switch (p_dns_header->flags_2 & DNS_HEADER_FLAG2_RCODE_MASK)
+ {
+ case DNS_HEADER_FLAG2_RCODE_FORMAT_ERROR:
+ err_code = DNS6_FORMAT_ERROR;
+ break;
+
+ case DNS_HEADER_FLAG2_RCODE_SERVER_FAILURE:
+ err_code = DNS6_SERVER_FAILURE;
+ break;
+
+ case DNS_HEADER_FLAG2_RCODE_NAME_ERROR:
+ err_code = DNS6_HOSTNAME_NOT_FOUND;
+ break;
+
+ case DNS_HEADER_FLAG2_RCODE_NOT_IMPLEMENTED:
+ err_code = DNS6_NOT_IMPLEMENTED;
+ break;
+
+ case DNS_HEADER_FLAG2_RCODE_REFUSED:
+ err_code = DNS6_REFUSED_ERROR;
+ break;
+
+ default:
+ err_code = (NRF_ERROR_INVALID_DATA | IOT_DNS6_ERR_BASE);
+ break;
+ }
+ }
+ else if (ancount == 0)
+ {
+ // No answer found.
+ err_code = DNS6_HOSTNAME_NOT_FOUND;
+ }
+ else
+ {
+ dns_rr_body_t rr;
+
+ // Skip questions section.
+ for (rr_index = 0; rr_index < qdcount; rr_index++)
+ {
+ p_data = skip_compressed_hostname(p_data) + DNS_QUESTION_FOOTER_SIZE;
+ }
+
+ // Addresses are moved to beginning of the packet to ensure alignment is correct.
+ p_addresses = (ipv6_addr_t *)p_rx_packet->p_payload;
+
+ // Parse responses section.
+ for (rr_index = 0; rr_index < ancount; rr_index++)
+ {
+ p_data = skip_compressed_hostname(p_data);
+
+ // Fill resource record structure to fit alignment.
+ memcpy((uint8_t *)&rr, p_data, DNS_RR_BODY_SIZE);
+
+ if (NTOHS(rr.rtype) == DNS_QTYPE_AAAA && NTOHS(rr.rclass) == DNS_QCLASS_IN)
+ {
+ if (NTOHS(rr.rdlength) == IPV6_ADDR_SIZE)
+ {
+ DNS6_TRC("Found AAAA record with IPv6 address:");
+ DNS6_DUMP(p_data + DNS_RR_BODY_SIZE, IPV6_ADDR_SIZE);
+
+ // Move all addresses next to each other.
+ memmove(p_addresses[addr_length].u8,
+ p_data + DNS_RR_BODY_SIZE,
+ IPV6_ADDR_SIZE);
+
+ addr_length++;
+ }
+ }
+
+ p_data += DNS_RR_BODY_SIZE + NTOHS(rr.rdlength);
+ }
+
+ if (addr_length == 0)
+ {
+ DNS6_ERR("No IPv6 addresses was found.");
+
+ err_code = DNS6_HOSTNAME_NOT_FOUND;
+ }
+ }
+
+ // Notify application.
+ app_notify(index, err_code, p_addresses, addr_length);
+
+ // Initialize query entry.
+ query_init(index);
+ }
+ else
+ {
+ DNS6_ERR("Response with unknown message id.");
+ err_code = (NRF_ERROR_NOT_FOUND | IOT_DNS6_ERR_BASE);
+ }
+ }
+
+ DNS6_EXIT();
+
+ DNS6_MUTEX_UNLOCK();
+
+ return err_code;
+}
+
+
+uint32_t dns6_init(const dns6_init_t * p_dns_init)
+{
+ NULL_PARAM_CHECK(p_dns_init);
+
+ uint32_t index;
+ uint32_t err_code;
+
+ DNS6_ENTRY();
+
+ SDK_MUTEX_INIT(m_dns6_mutex);
+
+ DNS6_MUTEX_LOCK();
+
+ for (index = 0; index < DNS6_MAX_PENDING_QUERIES; index++)
+ {
+ query_init(index);
+ }
+
+ // Request new socket creation.
+ err_code = udp6_socket_allocate(&m_socket);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ // Bind the socket to the local port.
+ err_code = udp6_socket_bind(&m_socket, IPV6_ADDR_ANY, p_dns_init->local_src_port);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ // Connect to DNS server.
+ err_code = udp6_socket_connect(&m_socket,
+ &p_dns_init->dns_server.addr,
+ p_dns_init->dns_server.port);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ // Register data receive callback.
+ err_code = udp6_socket_recv(&m_socket, server_response);
+ }
+ }
+
+ if (err_code == NRF_SUCCESS)
+ {
+ DNS6_TRC("Module initialization is complete.");
+
+ // Set initialization state flag if all procedures succeeded.
+ m_initialization_state = true;
+ m_message_id_counter = 0x0001;
+ }
+ else
+ {
+ DNS6_ERR("UDP socket initialization failed. Reason %08lx.", err_code);
+
+ // Not all procedures succeeded with allocated socket, hence free it.
+ UNUSED_VARIABLE(udp6_socket_free(&m_socket));
+ }
+ }
+
+ DNS6_EXIT();
+
+ DNS6_MUTEX_UNLOCK();
+
+ return err_code;
+}
+
+
+uint32_t dns6_uninit(void)
+{
+ VERIFY_MODULE_IS_INITIALIZED();
+
+ uint32_t index;
+
+ DNS6_ENTRY();
+
+ DNS6_MUTEX_LOCK();
+
+ for (index = 0; index < DNS6_MAX_PENDING_QUERIES; index++)
+ {
+ query_init(index);
+ }
+
+ // Free UDP socket.
+ UNUSED_VARIABLE(udp6_socket_free(&m_socket));
+
+ // Clear initialization state flag.
+ m_initialization_state = false;
+
+ DNS6_EXIT();
+
+ DNS6_MUTEX_UNLOCK();
+
+ return NRF_SUCCESS;
+}
+
+
+uint32_t dns6_query(const char * p_hostname, dns6_evt_handler_t evt_handler)
+{
+ VERIFY_MODULE_IS_INITIALIZED();
+ NULL_PARAM_CHECK(evt_handler);
+ NULL_PARAM_CHECK(p_hostname);
+ EMPTY_PARAM_CHECK(p_hostname);
+
+ uint32_t index;
+ uint32_t err_code;
+ uint32_t hostname_length;
+ uint8_t * p_hostname_buff = NULL;
+
+ DNS6_ENTRY();
+
+ DNS6_MUTEX_LOCK();
+
+ // Calculate hostname length.
+ hostname_length = strlen(p_hostname) + 1;
+
+ // Allocate memory to make copy of hostname string.
+ err_code = nrf_mem_reserve(&p_hostname_buff, &hostname_length);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ // Copy hostname to cache buffer.
+ strcpy((char *)p_hostname_buff, p_hostname);
+
+ // Add query to pending queue.
+ index = query_add(p_hostname_buff, evt_handler);
+
+ if (index != DNS6_MAX_PENDING_QUERIES)
+ {
+ // Create and send DNS Query.
+ err_code = query_send(index);
+
+ if (err_code != NRF_SUCCESS)
+ {
+ // Remove query from pending queue immediately.
+ query_init(index);
+ }
+ }
+ else
+ {
+ DNS6_ERR("No place in pending queue.");
+
+ // No place in pending queue.
+ err_code = (NRF_ERROR_NO_MEM | IOT_DNS6_ERR_BASE);
+ }
+
+ // Not all procedures succeeded with sending query, hence free buffer for hostname.
+ if (err_code != NRF_SUCCESS)
+ {
+ UNUSED_VARIABLE(nrf_free(p_hostname_buff));
+ }
+ }
+ else
+ {
+ DNS6_ERR("No memory to allocate buffer for hostname.");
+ }
+
+ DNS6_EXIT();
+
+ DNS6_MUTEX_UNLOCK();
+
+ return err_code;
+}
+
+
+void dns6_timeout_process(iot_timer_time_in_ms_t wall_clock_value)
+{
+ uint32_t index;
+ uint32_t err_code;
+
+ UNUSED_PARAMETER(wall_clock_value);
+
+ DNS6_ENTRY();
+
+ DNS6_MUTEX_LOCK();
+
+ for (index = 0; index < DNS6_MAX_PENDING_QUERIES; index++)
+ {
+ if (m_pending_queries[index].message_id != MESSAGE_ID_UNUSED)
+ {
+ if (query_timer_is_expired(index))
+ {
+ err_code = NRF_SUCCESS;
+
+ if (m_pending_queries[index].retries < DNS6_MAX_RETRANSMISSION_COUNT)
+ {
+ DNS6_TRC("Query retransmission [%d] for hostname %s.",
+ m_pending_queries[index].retries, m_pending_queries[index].p_hostname);
+
+ // Increase retransmission number.
+ m_pending_queries[index].retries++;
+
+ // Send query again.
+ err_code = query_send(index);
+ }
+ else
+ {
+ DNS6_ERR("DNS server did not response on query for hostname %s.",
+ m_pending_queries[index].p_hostname);
+
+ // No response from server.
+ err_code = DNS6_SERVER_UNREACHABLE;
+ }
+
+ if (err_code != NRF_SUCCESS)
+ {
+ // Inform application that timeout occurs.
+ app_notify(index, err_code, NULL, 0);
+
+ // Remove query from pending queue.
+ query_init(index);
+ }
+ }
+ break;
+ }
+ }
+
+ DNS6_EXIT();
+
+ DNS6_MUTEX_UNLOCK();
+}