/** * 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 #include #include #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(); }