iot/dns: Add DNS Client support for Zephyr
This commit adds support the DNS client API on top of the new native IP stack. Some features of this implementation are: - Support for IPv4 and IPv6 - Support for multiple concurrent queries. A net_buf structure is required per context. See the DNS_RESOLVER_ADDITIONAL_BUF_CTR configuration variable Origin: Original Jira: ZEP-793 Jira: ZEP-855 Jira: ZEP-975 Change-Id: I351a636462a1b78a412c9bce1ef3cd0fa6223a52 Signed-off-by: Flavio Santes <flavio.santes@intel.com>
This commit is contained in:
parent
eb03404bce
commit
36bbd7a935
184
include/iot/dns_client.h
Normal file
184
include/iot/dns_client.h
Normal file
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _DNS_CLIENT_H_
|
||||
#define _DNS_CLIENT_H_
|
||||
|
||||
enum dns_query_type {
|
||||
DNS_QUERY_TYPE_A = 1, /* IPv4 */
|
||||
DNS_QUERY_TYPE_AAAA = 28 /* IPv6 */
|
||||
};
|
||||
|
||||
#include <net/net_context.h>
|
||||
#include <net/net_ip.h>
|
||||
|
||||
/**
|
||||
* @brief dns_init DNS resolver initialization routine
|
||||
* @details This routine must be called before any other
|
||||
* dns routine.
|
||||
* @return 0, always.
|
||||
* Note: new versions may return error codes.
|
||||
*/
|
||||
int dns_init(void);
|
||||
|
||||
/**
|
||||
* @brief dns4_resolve Retrieves the IPv4 addresses associated to the
|
||||
* domain name 'name'.
|
||||
* @details This routine obtains the IPv4 addresses
|
||||
* associated to the domain name 'name'.
|
||||
* The DNS server is specified by the sockaddr
|
||||
* structure.
|
||||
* Depending on the DNS server used, one or more
|
||||
* IP addresses may be recovered by this routine.
|
||||
* NOTE: You can use an IPv6 DNS server to look-up
|
||||
* for IPv4 addresses or an IPv4 server to look-up
|
||||
* for IPv6 address. Domain name services are not
|
||||
* tied to any specific routing or transport
|
||||
* technology.
|
||||
* @param [in] ctx Previously initialized network context.
|
||||
* @param [out] addresses An array of IPv4 addresses.
|
||||
* @param [out] items Number of IPv4 addresses stored in 'addresses'.
|
||||
* @param [in] elements Available positions in the addresses array.
|
||||
* @param [in] name C-string containing the Domain Name to resolve,
|
||||
* i.e. 'example.com'.
|
||||
* @param [in] dns_server IP address and port number of the DNS server.
|
||||
* @param [in] timeout RX/TX timeout, for example: TICKS_UNLIMITED.
|
||||
* This timeout is also used when a buffer is
|
||||
* required from the pool.
|
||||
* @return 0 on success
|
||||
*
|
||||
* Number of returned addresses may be less than
|
||||
* the one reported by the DNS server. So, it is
|
||||
* considered a success because we are 'resolving'
|
||||
* the 'name'.
|
||||
* @return -EIO on network error.
|
||||
* @return -EINVAL if an invalid parameter was passed as
|
||||
* an argument to this routine. This value is also
|
||||
* returned if the application received a malformed
|
||||
* packet from the DNS server.
|
||||
* @return -ENOMEM if there are no buffers available.
|
||||
*/
|
||||
int dns4_resolve(struct net_context *ctx, struct in_addr *addresses,
|
||||
int *items, int elements, char *name,
|
||||
struct sockaddr *dns_server, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* @brief dns6_resolve Retrieves the IPv6 addresses associated to the
|
||||
* domain name 'name'.
|
||||
* @details See 'details' at the 'dns4_resolve' routine.
|
||||
* @param [in] ctx Previously initialized network context.
|
||||
* @param [out] addresses An array of IPv6 addresses.
|
||||
* @param [out] items Number of IPv6 addresses stored in 'addresses'.
|
||||
* @param [in] elements Available positions in the addresses array.
|
||||
* @param [in] name C-string containing the Domain Name to resolve,
|
||||
* i.e. 'example.com'.
|
||||
* @param [in] dns_server IP address and port number of the DNS server.
|
||||
* @param [in] timeout RX/TX timeout, for example: TICKS_UNLIMITED.
|
||||
* This timeout is also used when a buffer is
|
||||
* required from the pool.
|
||||
* @return 0 on success
|
||||
*
|
||||
* Number of returned addresses may be less than
|
||||
* the one reported by the DNS server. So, it is
|
||||
* considered a success because we are 'resolving'
|
||||
* the 'name'.
|
||||
* @return -EIO on network error
|
||||
* @return -EINVAL if an invalid parameter was passed as
|
||||
* an argument to this routine. This value is also
|
||||
* returned if the application received a malformed
|
||||
* packet from the DNS server.
|
||||
* @return -ENOMEM if there are no buffers available
|
||||
*/
|
||||
int dns6_resolve(struct net_context *ctx, struct in6_addr *addresses,
|
||||
int *items, int elements, char *name,
|
||||
struct sockaddr *dns_server, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* @brief dns4_resolve_quick Retrives one IPv4 address associated to the
|
||||
* domain name 'name'.
|
||||
* @details See 'details' at the 'dns4_resolve' routine.
|
||||
* @param [in] ctx Previously initialized network context.
|
||||
* @param [out] address IPv4 address.
|
||||
* @param [in] name C-string containing the Domain Name to resolve,
|
||||
* i.e. 'example.com'.
|
||||
* @param [in] dns_server IP address and port number of the DNS server.
|
||||
* @param [in] timeout RX/TX timeout, for example: TICKS_UNLIMITED.
|
||||
* This timeout is also used when a buffer is
|
||||
* required from the pool.
|
||||
* @return 0 on success
|
||||
*
|
||||
* Number of returned addresses may be less than
|
||||
* the one reported by the DNS server. So, it is
|
||||
* considered a success because we are 'resolving'
|
||||
* the 'name'.
|
||||
* @return -EIO on network error
|
||||
* @return -EINVAL if an invalid parameter was passed as
|
||||
* an argument to this routine. This value is also
|
||||
* returned if the application received a malformed
|
||||
* packet from the DNS server.
|
||||
* @return -ENOMEM if there are no buffers available
|
||||
*/
|
||||
static inline
|
||||
int dns4_resolve_quick(struct net_context *ctx, struct in_addr *address,
|
||||
char *name, struct sockaddr *dns_server,
|
||||
uint32_t timeout)
|
||||
{
|
||||
int elements = 1;
|
||||
int items;
|
||||
|
||||
return dns4_resolve(ctx, address, &items, elements, name,
|
||||
dns_server, timeout);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief dns6_resolve_quick Retrives one IPv6 address associated to the
|
||||
* domain name 'name'.
|
||||
* @details See 'details' at the 'dns4_resolve' routine.
|
||||
* @param [in] ctx Previously initialized network context.
|
||||
* @param [out] address IPv6 address.
|
||||
* @param [in] name C-string containing the Domain Name to resolve,
|
||||
* i.e. 'example.com'.
|
||||
* @param [in] dns_server IP address and port number of the DNS server.
|
||||
* @param [in] timeout RX/TX timeout, for example: TICKS_UNLIMITED.
|
||||
* This timeout is also used when a buffer is
|
||||
* required from the pool.
|
||||
* @return 0 on success
|
||||
*
|
||||
* Number of returned addresses may be less than
|
||||
* the one reported by the DNS server. So, it is
|
||||
* considered a success because we are 'resolving'
|
||||
* the 'name'.
|
||||
* @return -EIO on network error
|
||||
* @return -EINVAL if an invalid parameter was passed as
|
||||
* an argument to this routine. This value is also
|
||||
* returned if the application received a malformed
|
||||
* packet from the DNS server.
|
||||
* @return -ENOMEM if there are no buffers available
|
||||
*/
|
||||
static inline
|
||||
int dns6_resolve_quick(struct net_context *ctx, struct in6_addr *address,
|
||||
char *name, struct sockaddr *dns_server,
|
||||
uint32_t timeout)
|
||||
{
|
||||
int elements = 1;
|
||||
int items;
|
||||
|
||||
return dns6_resolve(ctx, address, &items, elements, name,
|
||||
dns_server, timeout);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1 +1,2 @@
|
|||
obj-$(CONFIG_ZOAP) += zoap/
|
||||
obj-$(CONFIG_ZOAP) += zoap/
|
||||
obj-$(CONFIG_DNS_RESOLVER) += dns/
|
||||
|
|
|
@ -18,4 +18,6 @@ menu "IoT Protocols"
|
|||
|
||||
source "lib/iot/zoap/Kconfig"
|
||||
|
||||
source "lib/iot/dns/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
ifdef CONFIG_ZOAP
|
||||
include $(srctree)/lib/iot/zoap/Makefile
|
||||
endif
|
||||
|
||||
ifdef CONFIG_DNS_RESOLVER
|
||||
include $(srctree)/lib/iot/dns/Makefile
|
||||
endif
|
||||
|
|
45
lib/iot/dns/Kconfig
Normal file
45
lib/iot/dns/Kconfig
Normal file
|
@ -0,0 +1,45 @@
|
|||
#
|
||||
# Copyright (c) 2016 Intel Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
config DNS_RESOLVER
|
||||
bool
|
||||
prompt "DNS resolver"
|
||||
default n
|
||||
help
|
||||
This option enables the DNS client side support for Zephyr
|
||||
|
||||
config DNS_RESOLVER_ADDITIONAL_BUF_CTR
|
||||
int
|
||||
prompt "Additional DNS buffers"
|
||||
depends on DNS_RESOLVER
|
||||
default 0
|
||||
help
|
||||
Number of additional buffers available for the DNS resolver.
|
||||
The DNS resolver requires at least one buffer. This option
|
||||
enables additional buffers required for multiple concurrent
|
||||
DNS connections.
|
||||
|
||||
config DNS_RESOLVER_ADDITIONAL_QUERIES
|
||||
int
|
||||
prompt "Additional DNS queries"
|
||||
depends on DNS_RESOLVER
|
||||
range 0 2
|
||||
default 1
|
||||
help
|
||||
Number of additional DNS queries that the DNS resolver may
|
||||
generate when the RR ANSWER only contains CNAME(s).
|
||||
The maximum value of this variable is constrained to avoid
|
||||
'alias loops'.
|
5
lib/iot/dns/Makefile
Normal file
5
lib/iot/dns/Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
ccflags-y += -I$(srctree)/lib/iot/dns
|
||||
|
||||
obj-y := dns_pack.o
|
||||
obj-y += dns_client.o
|
||||
|
22
lib/iot/dns/README
Normal file
22
lib/iot/dns/README
Normal file
|
@ -0,0 +1,22 @@
|
|||
DNS Client API for Zephyr
|
||||
=========================
|
||||
|
||||
|
||||
Known limitations:
|
||||
|
||||
- Synchronous queries
|
||||
- Only IPv4 and IPv6 records can be handled
|
||||
- Minimal protocol validation. If you do not trust your DNS server,
|
||||
it is time to change it :)
|
||||
|
||||
|
||||
Usage:
|
||||
|
||||
Before calling the resolver, it must be initialized via the 'dns_init'
|
||||
routine.
|
||||
|
||||
'dnsX_resolve_quick' routines just return the first IP address.
|
||||
However, domain names may be served by more IP addresses, so
|
||||
'dnsX_resolve' routines may be more useful.
|
||||
|
||||
See samples/net/dns_client/src/main.c.
|
424
lib/iot/dns/dns_client.c
Normal file
424
lib/iot/dns/dns_client.c
Normal file
|
@ -0,0 +1,424 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <iot/dns_client.h>
|
||||
#include "dns_pack.h"
|
||||
|
||||
#include <drivers/rand32.h>
|
||||
#include <net/buf.h>
|
||||
#include <net/nbuf.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* RFC 1035, 3.1. Name space definitions
|
||||
* To simplify implementations, the total length of a domain name (i.e.,
|
||||
* label octets and label length octets) is restricted to 255 octets or
|
||||
* less.
|
||||
*/
|
||||
#define DNS_MAX_NAME_LEN 255
|
||||
|
||||
#define DNS_QUERY_MAX_SIZE (DNS_MSG_HEADER_SIZE + DNS_MAX_NAME_LEN + \
|
||||
DNS_QTYPE_LEN + DNS_QCLASS_LEN)
|
||||
|
||||
/* This value is recommended by RFC 1035 */
|
||||
#define DNS_RESOLVER_MAX_BUF_SIZE 512
|
||||
#define DNS_RESOLVER_MIN_BUF 1
|
||||
#define DNS_RESOLVER_BUF_CTR (DNS_RESOLVER_MIN_BUF + \
|
||||
CONFIG_DNS_RESOLVER_ADDITIONAL_BUF_CTR)
|
||||
#define DNS_RESOLVER_QUERIES (1 + CONFIG_DNS_RESOLVER_ADDITIONAL_QUERIES)
|
||||
|
||||
/* Compressed RR uses a pointer to another RR. So, min size is 12 bytes without
|
||||
* considering RR payload.
|
||||
* See https://tools.ietf.org/html/rfc1035#section-4.1.4
|
||||
*/
|
||||
#define DNS_ANSWER_PTR_LEN 12
|
||||
|
||||
/* See dns_unpack_answer, and also see:
|
||||
* https://tools.ietf.org/html/rfc1035#section-4.1.2
|
||||
*/
|
||||
#define DNS_QUERY_POS 0x0c
|
||||
|
||||
#define DNS_IPV4_LEN 4
|
||||
#define DNS_IPV6_LEN 16
|
||||
|
||||
static struct nano_fifo dns_msg_fifo;
|
||||
static NET_BUF_POOL(dns_msg_pool, DNS_RESOLVER_BUF_CTR,
|
||||
DNS_RESOLVER_MAX_BUF_SIZE, &dns_msg_fifo, NULL, 0);
|
||||
|
||||
static struct nano_fifo dns_qname_fifo;
|
||||
static NET_BUF_POOL(dns_qname_pool, DNS_RESOLVER_BUF_CTR,
|
||||
DNS_MAX_NAME_LEN, &dns_qname_fifo, NULL, 0);
|
||||
|
||||
int dns_init(void)
|
||||
{
|
||||
net_buf_pool_init(dns_msg_pool);
|
||||
net_buf_pool_init(dns_qname_pool);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int dns_write(struct net_context *ctx, struct net_buf *dns_data,
|
||||
uint32_t timeout, uint16_t dns_id, enum dns_query_type type,
|
||||
struct net_buf *dns_qname, struct sockaddr *dns_server);
|
||||
|
||||
static
|
||||
int dns_read(struct net_context *ctx, struct net_buf *dns_data,
|
||||
uint32_t timeout, uint16_t dns_id, enum dns_query_type type,
|
||||
uint8_t *addresses, int *items, int elements,
|
||||
uint8_t *cname, uint16_t *cname_len);
|
||||
|
||||
/*
|
||||
* Note about the DNS transaction identifier:
|
||||
* The transaction identifier is randomized according to:
|
||||
* http://www.cisco.com/c/en/us/about/security-center/dns-best-practices.html#3
|
||||
* Here we assume that even after the cast, dns_id = sys_rand32_get(), there is
|
||||
* enough entropy :)
|
||||
*/
|
||||
static
|
||||
int dns_resolve(struct net_context *ctx, uint8_t *addresses, int *items,
|
||||
int elements, char *name, enum dns_query_type type,
|
||||
struct sockaddr *dns_server, uint32_t timeout)
|
||||
{
|
||||
struct net_buf *dns_data = NULL;
|
||||
struct net_buf *dns_qname = NULL;
|
||||
uint16_t dns_id;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
dns_id = sys_rand32_get();
|
||||
|
||||
dns_data = net_buf_get_timeout(&dns_msg_fifo, 0, timeout);
|
||||
if (dns_data == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto exit_resolve;
|
||||
}
|
||||
|
||||
dns_qname = net_buf_get_timeout(&dns_qname_fifo, 0, timeout);
|
||||
if (dns_qname == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto exit_resolve;
|
||||
}
|
||||
|
||||
rc = dns_msg_pack_qname(&dns_qname->len, dns_qname->data,
|
||||
DNS_MAX_NAME_LEN, name);
|
||||
if (rc != 0) {
|
||||
rc = -EINVAL;
|
||||
goto exit_resolve;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
rc = dns_write(ctx, dns_data, timeout, dns_id, type, dns_qname,
|
||||
dns_server);
|
||||
if (rc != 0) {
|
||||
goto exit_resolve;
|
||||
}
|
||||
|
||||
rc = dns_read(ctx, dns_data, timeout, dns_id, type,
|
||||
addresses, items, elements, dns_qname->data,
|
||||
&dns_qname->len);
|
||||
if (rc != 0) {
|
||||
goto exit_resolve;
|
||||
}
|
||||
|
||||
/* Server response includes at least one IP address */
|
||||
if (*items > 0) {
|
||||
break;
|
||||
}
|
||||
} while (++i < DNS_RESOLVER_QUERIES);
|
||||
|
||||
rc = 0;
|
||||
if (*items <= 0) {
|
||||
rc = -EINVAL;
|
||||
}
|
||||
|
||||
exit_resolve:
|
||||
/* dns_data may be NULL, however net_nbuf_unref supports that */
|
||||
net_nbuf_unref(dns_data);
|
||||
net_nbuf_unref(dns_qname);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dns4_resolve(struct net_context *ctx, struct in_addr *addresses,
|
||||
int *items, int elements, char *name,
|
||||
struct sockaddr *dns_server, uint32_t timeout)
|
||||
{
|
||||
return dns_resolve(ctx, (uint8_t *)addresses, items, elements,
|
||||
name, DNS_QUERY_TYPE_A, dns_server, timeout);
|
||||
}
|
||||
|
||||
int dns6_resolve(struct net_context *ctx, struct in6_addr *addresses,
|
||||
int *items, int elements, char *name,
|
||||
struct sockaddr *dns_server, uint32_t timeout)
|
||||
{
|
||||
return dns_resolve(ctx, (uint8_t *)addresses, items, elements,
|
||||
name, DNS_QUERY_TYPE_AAAA, dns_server, timeout);
|
||||
}
|
||||
|
||||
static
|
||||
int dns_write(struct net_context *ctx, struct net_buf *dns_data,
|
||||
uint32_t timeout, uint16_t dns_id, enum dns_query_type type,
|
||||
struct net_buf *dns_qname, struct sockaddr *dns_server)
|
||||
{
|
||||
struct net_buf *tx;
|
||||
|
||||
int server_addr_len;
|
||||
int rc;
|
||||
|
||||
rc = dns_msg_pack_query(dns_data->data, &dns_data->len, dns_data->size,
|
||||
dns_qname->data, dns_qname->len, dns_id,
|
||||
(enum dns_rr_type)type);
|
||||
if (rc != 0) {
|
||||
rc = -EINVAL;
|
||||
goto exit_write;
|
||||
}
|
||||
|
||||
tx = net_nbuf_get_tx(ctx);
|
||||
if (tx == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto exit_write;
|
||||
}
|
||||
|
||||
rc = net_nbuf_write(tx, dns_data->len, dns_data->data);
|
||||
if (rc != true) {
|
||||
rc = -ENOMEM;
|
||||
goto exit_write;
|
||||
}
|
||||
|
||||
if (dns_server->family == AF_INET) {
|
||||
server_addr_len = sizeof(struct sockaddr_in);
|
||||
} else {
|
||||
server_addr_len = sizeof(struct sockaddr_in6);
|
||||
}
|
||||
|
||||
/* tx and dns_data buffers will be dereferenced after this call */
|
||||
rc = net_context_sendto(tx, dns_server, server_addr_len, NULL,
|
||||
timeout, NULL, NULL);
|
||||
if (rc != 0) {
|
||||
rc = -EIO;
|
||||
goto exit_write;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
exit_write:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static
|
||||
void cb_recv(struct net_context *context, struct net_buf *buf, int status,
|
||||
void *user_data)
|
||||
{
|
||||
struct net_buf **data;
|
||||
|
||||
ARG_UNUSED(context);
|
||||
|
||||
if (status != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
data = (struct net_buf **)user_data;
|
||||
*data = buf;
|
||||
}
|
||||
|
||||
static
|
||||
int dns_recv(struct net_context *ctx, struct net_buf **buf, uint32_t timeout)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = net_context_recv(ctx, cb_recv, timeout, buf);
|
||||
if (rc != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int nbuf_copy(struct net_buf *dst, struct net_buf *src, int offset,
|
||||
int len);
|
||||
|
||||
static
|
||||
int dns_read(struct net_context *ctx, struct net_buf *dns_data,
|
||||
uint32_t timeout, uint16_t dns_id, enum dns_query_type type,
|
||||
uint8_t *addresses, int *items, int elements,
|
||||
uint8_t *cname, uint16_t *cname_len)
|
||||
{
|
||||
struct net_buf *rx = NULL;
|
||||
/* helper struct to track the dns msg received from the server */
|
||||
struct dns_msg_t dns_msg;
|
||||
/* RR ttl, so far it is not passed to caller */
|
||||
uint32_t ttl;
|
||||
uint8_t *src;
|
||||
uint8_t *dst;
|
||||
int address_size;
|
||||
/* index that points to the current answer being analyzed */
|
||||
int answer_ptr;
|
||||
int data_len;
|
||||
int offset;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
if (elements <= 0) {
|
||||
rc = -EINVAL;
|
||||
goto exit_error;
|
||||
}
|
||||
|
||||
rc = dns_recv(ctx, &rx, timeout);
|
||||
if (rc != 0) {
|
||||
rc = -EIO;
|
||||
goto exit_error;
|
||||
}
|
||||
|
||||
data_len = min(net_nbuf_appdatalen(rx), DNS_RESOLVER_MAX_BUF_SIZE);
|
||||
offset = net_buf_frags_len(rx) - data_len;
|
||||
|
||||
if (nbuf_copy(dns_data, rx, offset, data_len) != 0) {
|
||||
rc = -ENOMEM;
|
||||
goto exit_error;
|
||||
}
|
||||
|
||||
dns_msg.msg = dns_data->data;
|
||||
dns_msg.msg_size = data_len;
|
||||
|
||||
rc = dns_unpack_response_header(&dns_msg, dns_id);
|
||||
if (rc != 0) {
|
||||
rc = -EINVAL;
|
||||
goto exit_error;
|
||||
}
|
||||
|
||||
if (dns_header_qdcount(dns_msg.msg) != 1) {
|
||||
rc = -EINVAL;
|
||||
goto exit_error;
|
||||
}
|
||||
|
||||
rc = dns_unpack_response_query(&dns_msg);
|
||||
if (rc != 0) {
|
||||
rc = -EINVAL;
|
||||
goto exit_error;
|
||||
}
|
||||
|
||||
if (type == DNS_QUERY_TYPE_A) {
|
||||
address_size = DNS_IPV4_LEN;
|
||||
} else {
|
||||
address_size = DNS_IPV6_LEN;
|
||||
}
|
||||
|
||||
/* while loop to traverse the response */
|
||||
answer_ptr = DNS_QUERY_POS;
|
||||
*items = 0;
|
||||
i = 0;
|
||||
while (i < dns_header_ancount(dns_msg.msg)) {
|
||||
rc = dns_unpack_answer(&dns_msg, answer_ptr, &ttl);
|
||||
if (rc != 0) {
|
||||
rc = -EINVAL;
|
||||
goto exit_error;
|
||||
}
|
||||
|
||||
switch (dns_msg.response_type) {
|
||||
case DNS_RESPONSE_IP:
|
||||
if (dns_msg.response_length < address_size) {
|
||||
/* it seems this is a malformed message */
|
||||
rc = -EINVAL;
|
||||
goto exit_error;
|
||||
}
|
||||
|
||||
src = dns_msg.msg + dns_msg.response_position;
|
||||
dst = (uint8_t *)addresses + *items * address_size;
|
||||
memcpy(dst, src, address_size);
|
||||
|
||||
*items += 1;
|
||||
if (*items >= elements) {
|
||||
/* elements is always >= 1, so it is assumed
|
||||
* that at least one address was returned.
|
||||
*/
|
||||
goto exit_ok;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DNS_RESPONSE_CNAME_NO_IP:
|
||||
/* Instead of using the QNAME at DNS_QUERY_POS,
|
||||
* we will use this CNAME
|
||||
*/
|
||||
answer_ptr = dns_msg.response_position;
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
goto exit_error;
|
||||
}
|
||||
|
||||
/* Update the answer offset to point to the next RR (answer) */
|
||||
dns_msg.answer_offset += DNS_ANSWER_PTR_LEN;
|
||||
dns_msg.answer_offset += dns_msg.response_length;
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
/* No IP addresses were found, so we take the last CNAME to generate
|
||||
* another query. Number of additional queries is controlled via Kconfig
|
||||
*/
|
||||
if (*items == 0 && dns_msg.response_type == DNS_RESPONSE_CNAME_NO_IP) {
|
||||
src = dns_msg.msg + dns_msg.response_position;
|
||||
*cname_len = dns_msg.response_length;
|
||||
|
||||
memcpy(cname, src, *cname_len);
|
||||
}
|
||||
|
||||
exit_ok:
|
||||
rc = 0;
|
||||
|
||||
exit_error:
|
||||
net_nbuf_unref(rx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static
|
||||
int nbuf_copy(struct net_buf *dst, struct net_buf *src, int offset,
|
||||
int len)
|
||||
{
|
||||
int copied;
|
||||
int to_copy;
|
||||
|
||||
/* find the right fragment to start copying from */
|
||||
while (src && offset >= src->len) {
|
||||
offset -= src->len;
|
||||
src = src->frags;
|
||||
}
|
||||
|
||||
/* traverse the fragment chain until len bytes are copied */
|
||||
copied = 0;
|
||||
while (src && len > 0) {
|
||||
to_copy = min(len, src->len - offset);
|
||||
memcpy(dst->data + copied, src->data + offset, to_copy);
|
||||
|
||||
copied = to_copy;
|
||||
len -= to_copy;
|
||||
src = src->frags;
|
||||
/* after the first iteration, this value will be 0 */
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
352
lib/iot/dns/dns_pack.c
Normal file
352
lib/iot/dns/dns_pack.c
Normal file
|
@ -0,0 +1,352 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "dns_pack.h"
|
||||
#include <string.h>
|
||||
|
||||
#define DNS_LABEL_MAX_SIZE 63
|
||||
#define DNS_ANSWER_MIN_SIZE 12
|
||||
#define DNS_COMMON_UINT_SIZE 2
|
||||
|
||||
#define DNS_HEADER_ID_LEN 2
|
||||
#define DNS_HEADER_FLAGS_LEN 2
|
||||
#define DNS_QTYPE_LEN 2
|
||||
#define DNS_QCLASS_LEN 2
|
||||
#define DNS_QDCOUNT_LEN 2
|
||||
#define DNS_ANCOUNT_LEN 2
|
||||
#define DNS_NSCOUNT_LEN 2
|
||||
#define DNS_ARCOUNT_LEN 2
|
||||
|
||||
/* RFC 1035 '4.1.1. Header section format' defines the following flags:
|
||||
* QR, Opcode, AA, TC, RD, RA, Z and RCODE.
|
||||
* This implementation only uses RD (Recursion Desired).
|
||||
*/
|
||||
#define DNS_RECURSION 1
|
||||
|
||||
/* These two defines represent the 3rd and 4th bytes of the DNS msg header.
|
||||
* See RFC 1035, 4.1.1. Header section format.
|
||||
*/
|
||||
#define DNS_FLAGS1 DNS_RECURSION /* QR, Opcode, AA, and TC = 0 */
|
||||
#define DNS_FLAGS2 0 /* RA, Z and RCODE = 0 */
|
||||
|
||||
static inline
|
||||
uint16_t dns_strlen(char *str)
|
||||
{
|
||||
if (str == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return (uint16_t)strlen(str);
|
||||
}
|
||||
|
||||
int dns_msg_pack_qname(uint16_t *len, uint8_t *buf, uint16_t size,
|
||||
char *domain_name)
|
||||
{
|
||||
uint16_t dn_size;
|
||||
uint16_t lb_start;
|
||||
uint16_t lb_index;
|
||||
uint16_t lb_size;
|
||||
uint16_t i;
|
||||
|
||||
lb_start = 0;
|
||||
lb_index = 1;
|
||||
lb_size = 0;
|
||||
|
||||
dn_size = dns_strlen(domain_name);
|
||||
if (dn_size == 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* traverse the domain name str, including the null-terminator :) */
|
||||
for (i = 0; i < dn_size + 1; i++) {
|
||||
if (lb_index >= size) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
switch (domain_name[i]) {
|
||||
default:
|
||||
buf[lb_index] = domain_name[i];
|
||||
lb_size += 1;
|
||||
break;
|
||||
case '.':
|
||||
buf[lb_start] = lb_size;
|
||||
lb_size = 0;
|
||||
lb_start = lb_index;
|
||||
break;
|
||||
case '\0':
|
||||
buf[lb_start] = lb_size;
|
||||
buf[lb_index] = 0;
|
||||
break;
|
||||
}
|
||||
lb_index += 1;
|
||||
}
|
||||
|
||||
*len = lb_index;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline
|
||||
void set_dns_msg_response(struct dns_msg_t *dns_msg, int type, uint16_t pos,
|
||||
uint16_t len)
|
||||
{
|
||||
dns_msg->response_type = type;
|
||||
dns_msg->response_position = pos;
|
||||
dns_msg->response_length = len;
|
||||
}
|
||||
|
||||
int dns_unpack_answer(struct dns_msg_t *dns_msg, int dname_ptr, uint32_t *ttl)
|
||||
{
|
||||
uint16_t buf_size;
|
||||
uint16_t pos;
|
||||
uint16_t len;
|
||||
uint8_t *answer;
|
||||
int ptr;
|
||||
|
||||
answer = dns_msg->msg + dns_msg->answer_offset;
|
||||
|
||||
if (answer[0] < DNS_LABEL_MAX_SIZE) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Recovery of the pointer value */
|
||||
ptr = (((answer[0] & DNS_LABEL_MAX_SIZE) << 8) + answer[1]);
|
||||
if (ptr != dname_ptr) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to be sure this buffer has enough space
|
||||
* to contain the answer.
|
||||
*
|
||||
* size: dname_size + type + class + ttl + rdlength + rdata
|
||||
* 2 + 2 + 2 + 4 + 2 + ?
|
||||
*
|
||||
* So, answer size >= 12
|
||||
*
|
||||
* See RFC-1035 4.1.3. Resource record format
|
||||
*/
|
||||
buf_size = dns_msg->msg_size - dns_msg->answer_offset;
|
||||
if (buf_size < DNS_ANSWER_MIN_SIZE) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Only DNS_CLASS_IN answers
|
||||
* Here we use 2 as an offset because a ptr uses only 2 bytes.
|
||||
*/
|
||||
if (dns_answer_class(DNS_COMMON_UINT_SIZE, answer) != DNS_CLASS_IN) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* TTL value */
|
||||
*ttl = dns_answer_ttl(DNS_COMMON_UINT_SIZE, answer);
|
||||
pos = dns_msg->answer_offset + DNS_ANSWER_MIN_SIZE;
|
||||
len = dns_unpack_answer_rdlength(DNS_COMMON_UINT_SIZE, answer);
|
||||
|
||||
switch (dns_response_type(DNS_COMMON_UINT_SIZE, answer)) {
|
||||
case DNS_RR_TYPE_A:
|
||||
case DNS_RR_TYPE_AAAA:
|
||||
set_dns_msg_response(dns_msg, DNS_RESPONSE_IP, pos, len);
|
||||
return 0;
|
||||
|
||||
case DNS_RR_TYPE_CNAME:
|
||||
set_dns_msg_response(dns_msg, DNS_RESPONSE_CNAME_NO_IP,
|
||||
pos, len);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
/* malformed dns answer */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_unpack_response_header(struct dns_msg_t *msg, int src_id)
|
||||
{
|
||||
uint8_t *dns_header;
|
||||
uint16_t size;
|
||||
int qdcount;
|
||||
int ancount;
|
||||
int rc;
|
||||
|
||||
dns_header = msg->msg;
|
||||
size = msg->msg_size;
|
||||
|
||||
if (size < DNS_MSG_HEADER_SIZE) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (dns_unpack_header_id(dns_header) != src_id) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dns_header_qr(dns_header) != DNS_RESPONSE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dns_header_opcode(dns_header) != DNS_QUERY) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dns_header_z(dns_header) != 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = dns_header_rcode(dns_header);
|
||||
switch (rc) {
|
||||
case DNS_HEADER_NOERROR:
|
||||
break;
|
||||
default:
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
qdcount = dns_unpack_header_qdcount(dns_header);
|
||||
ancount = dns_unpack_header_ancount(dns_header);
|
||||
if (qdcount < 1 || ancount < 1) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dns_msg_pack_query_header(uint8_t *buf, uint16_t size, uint16_t id)
|
||||
{
|
||||
uint16_t offset;
|
||||
|
||||
if (size < DNS_MSG_HEADER_SIZE) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
UNALIGNED_PUT(htons(id), (uint16_t *)(buf));
|
||||
|
||||
/* RD = 1, TC = 0, AA = 0, Opcode = 0, QR = 0 <-> 0x01 (1B)
|
||||
* RCode = 0, Z = 0, RA = 0 <-> 0x00 (1B)
|
||||
*
|
||||
* QDCOUNT = 1 <-> 0x0001 (2B)
|
||||
*/
|
||||
|
||||
offset = DNS_HEADER_ID_LEN;
|
||||
/* Split the following assignements just in case we need to alter
|
||||
* the flags in future releases
|
||||
*/
|
||||
*(buf + offset) = DNS_FLAGS1; /* QR, Opcode, AA, TC and RD */
|
||||
*(buf + offset + 1) = DNS_FLAGS2; /* RA, Z and RCODE */
|
||||
|
||||
offset += DNS_HEADER_FLAGS_LEN;
|
||||
/* set question counter */
|
||||
UNALIGNED_PUT(htons(1), (uint16_t *)(buf + offset));
|
||||
|
||||
offset += DNS_QDCOUNT_LEN;
|
||||
/* set answer and ns rr */
|
||||
UNALIGNED_PUT(0, (uint32_t *)(buf + offset));
|
||||
|
||||
offset += DNS_ANCOUNT_LEN + DNS_NSCOUNT_LEN;
|
||||
/* set the additional records */
|
||||
UNALIGNED_PUT(0, (uint16_t *)(buf + offset));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_msg_pack_query(uint8_t *buf, uint16_t *len, uint16_t size,
|
||||
uint8_t *qname, uint16_t qname_len, uint16_t id,
|
||||
enum dns_rr_type qtype)
|
||||
{
|
||||
uint16_t msg_size;
|
||||
uint16_t offset;
|
||||
int rc;
|
||||
|
||||
msg_size = DNS_MSG_HEADER_SIZE + DNS_QTYPE_LEN + DNS_QCLASS_LEN;
|
||||
if (msg_size + qname_len > size) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rc = dns_msg_pack_query_header(buf, size, id);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
offset = DNS_MSG_HEADER_SIZE;
|
||||
memcpy(buf + offset, qname, qname_len);
|
||||
|
||||
offset += qname_len;
|
||||
|
||||
/* QType */
|
||||
UNALIGNED_PUT(htons(qtype), (uint16_t *)(buf + offset + 0));
|
||||
offset += DNS_QTYPE_LEN;
|
||||
|
||||
/* QClass */
|
||||
UNALIGNED_PUT(htons(DNS_CLASS_IN), (uint16_t *)(buf + offset));
|
||||
|
||||
*len = offset + DNS_QCLASS_LEN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_find_null(int *qname_size, uint8_t *buf, uint16_t size)
|
||||
{
|
||||
*qname_size = 0;
|
||||
while (*qname_size < size) {
|
||||
if (buf[(*qname_size)++] == 0x00) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int dns_unpack_response_query(struct dns_msg_t *dns_msg)
|
||||
{
|
||||
uint8_t *dns_query;
|
||||
uint8_t *buf;
|
||||
int remaining_size;
|
||||
int qname_size;
|
||||
int offset;
|
||||
int rc;
|
||||
|
||||
dns_msg->query_offset = DNS_MSG_HEADER_SIZE;
|
||||
dns_query = dns_msg->msg + dns_msg->query_offset;
|
||||
remaining_size = dns_msg->msg_size - dns_msg->query_offset;
|
||||
|
||||
rc = dns_find_null(&qname_size, dns_query, remaining_size);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* header already parsed + qname size */
|
||||
offset = dns_msg->query_offset + qname_size;
|
||||
|
||||
/* 4 bytes more due to qtype and qclass */
|
||||
offset += DNS_QTYPE_LEN + DNS_QCLASS_LEN;
|
||||
if (offset > dns_msg->msg_size) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
buf = dns_query + qname_size;
|
||||
if (dns_unpack_query_qtype(buf) != DNS_RR_TYPE_A &&
|
||||
dns_unpack_query_qtype(buf) != DNS_RR_TYPE_AAAA) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dns_unpack_query_qclass(buf) != DNS_CLASS_IN) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dns_msg->answer_offset = dns_msg->query_offset + qname_size +
|
||||
DNS_QTYPE_LEN + DNS_QCLASS_LEN;
|
||||
|
||||
return 0;
|
||||
}
|
364
lib/iot/dns/dns_pack.h
Normal file
364
lib/iot/dns/dns_pack.h
Normal file
|
@ -0,0 +1,364 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _DNS_PACK_H_
|
||||
#define _DNS_PACK_H_
|
||||
|
||||
#include <net/net_ip.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* See RFC 1035, 4.1.1 Header section format
|
||||
* DNS Message Header is always 12 bytes
|
||||
*/
|
||||
#define DNS_MSG_HEADER_SIZE 12
|
||||
|
||||
/**
|
||||
* @brief dns_msg_t
|
||||
*
|
||||
* @details Structure that points to the buffer containing the
|
||||
* DNS message. It also contains some decodified
|
||||
* message's properties that can not be recovered easily:
|
||||
*
|
||||
* - cname_offset
|
||||
*
|
||||
* - query_offset
|
||||
*
|
||||
* - answer_offset:
|
||||
*
|
||||
* - response_type
|
||||
* It indicates the response's content type. It could be
|
||||
* an IP address, a CNAME with IP (two answers), a CNAME
|
||||
* with no IP address. See enum dns_response_type for
|
||||
* more details.
|
||||
*
|
||||
* - response_position: this is an offset. It holds the
|
||||
* starting byte of the field containing the desired
|
||||
* info. For example an IPv4 address.
|
||||
*
|
||||
* - response_length: this is an offset. It holds the
|
||||
* response's length.
|
||||
*/
|
||||
struct dns_msg_t {
|
||||
uint8_t *msg;
|
||||
uint16_t msg_size;
|
||||
|
||||
int response_type;
|
||||
uint16_t response_position;
|
||||
uint16_t response_length;
|
||||
|
||||
uint16_t query_offset;
|
||||
uint16_t answer_offset;
|
||||
};
|
||||
|
||||
#define DNS_MSG_INIT(b, s) {.msg = b, .msg_size = s, \
|
||||
.response_type = -EINVAL}
|
||||
|
||||
|
||||
enum dns_rr_type {
|
||||
DNS_RR_TYPE_INVALID = 0,
|
||||
DNS_RR_TYPE_A = 1, /* IPv4 */
|
||||
DNS_RR_TYPE_CNAME = 5, /* CNAME */
|
||||
DNS_RR_TYPE_AAAA = 28 /* IPv6 */
|
||||
};
|
||||
|
||||
enum dns_response_type {
|
||||
DNS_RESPONSE_INVALID = -EINVAL,
|
||||
DNS_RESPONSE_IP,
|
||||
DNS_RESPONSE_CNAME_WITH_IP,
|
||||
DNS_RESPONSE_CNAME_NO_IP
|
||||
};
|
||||
|
||||
enum dns_class {
|
||||
DNS_CLASS_INVALID = 0,
|
||||
DNS_CLASS_IN,
|
||||
};
|
||||
|
||||
enum dns_msg_type {
|
||||
DNS_QUERY = 0,
|
||||
DNS_RESPONSE
|
||||
};
|
||||
|
||||
enum dns_header_rcode {
|
||||
DNS_HEADER_NOERROR = 0,
|
||||
DNS_HEADER_FORMATERROR,
|
||||
DNS_HEADER_SERVERFAILURE,
|
||||
DNS_HEADER_NAMEERROR,
|
||||
DNS_HEADER_NOTIMPLEMENTED,
|
||||
DNS_HEADER_REFUSED
|
||||
};
|
||||
|
||||
/** It returns the ID field in the DNS msg header */
|
||||
static inline int dns_header_id(uint8_t *header)
|
||||
{
|
||||
return htons(*(uint16_t *)header);
|
||||
}
|
||||
|
||||
/* inline unpack routines are used to unpack data from network
|
||||
* order to cpu. Similar routines without the unpack prefix are
|
||||
* used for cpu to network order.
|
||||
*/
|
||||
static inline int dns_unpack_header_id(uint8_t *header)
|
||||
{
|
||||
return ntohs(*(uint16_t *)header);
|
||||
}
|
||||
|
||||
/** It returns the QR field in the DNS msg header */
|
||||
static inline int dns_header_qr(uint8_t *header)
|
||||
{
|
||||
return ((*(header + 2)) & 0x80) ? 1 : 0;
|
||||
}
|
||||
|
||||
/** It returns the OPCODE field in the DNS msg header */
|
||||
static inline int dns_header_opcode(uint8_t *header)
|
||||
{
|
||||
return ((*(header + 2)) & 0x70) >> 1;
|
||||
}
|
||||
|
||||
/** It returns the AA field in the DNS msg header */
|
||||
static inline int dns_header_aa(uint8_t *header)
|
||||
{
|
||||
return ((*(header + 2)) & 0x04) ? 1 : 0;
|
||||
}
|
||||
|
||||
/** It returns the TC field in the DNS msg header */
|
||||
static inline int dns_header_tc(uint8_t *header)
|
||||
{
|
||||
return ((*(header + 2)) & 0x02) ? 1 : 0;
|
||||
}
|
||||
|
||||
/** It returns the RD field in the DNS msg header */
|
||||
static inline int dns_header_rd(uint8_t *header)
|
||||
{
|
||||
return ((*(header + 2)) & 0x01) ? 1 : 0;
|
||||
}
|
||||
|
||||
/** It returns the RA field in the DNS msg header */
|
||||
static inline int dns_header_ra(uint8_t *header)
|
||||
{
|
||||
return ((*(header + 3)) & 0x80) >> 7;
|
||||
}
|
||||
|
||||
/** It returns the Z field in the DNS msg header */
|
||||
static inline int dns_header_z(uint8_t *header)
|
||||
{
|
||||
return ((*(header + 3)) & 0x70) >> 4;
|
||||
}
|
||||
|
||||
/** It returns the RCODE field in the DNS msg header */
|
||||
static inline int dns_header_rcode(uint8_t *header)
|
||||
{
|
||||
return ((*(header + 3)) & 0x0F);
|
||||
}
|
||||
|
||||
/** It returns the QDCOUNT field in the DNS msg header */
|
||||
static inline int dns_header_qdcount(uint8_t *header)
|
||||
{
|
||||
return htons(*(uint16_t *)(header + 4));
|
||||
}
|
||||
|
||||
static inline int dns_unpack_header_qdcount(uint8_t *header)
|
||||
{
|
||||
return ntohs(*(uint16_t *)(header + 4));
|
||||
}
|
||||
|
||||
/** It returns the ANCOUNT field in the DNS msg header */
|
||||
static inline int dns_header_ancount(uint8_t *header)
|
||||
{
|
||||
return htons(*(uint16_t *)(header + 6));
|
||||
}
|
||||
|
||||
static inline int dns_unpack_header_ancount(uint8_t *header)
|
||||
{
|
||||
return ntohs(*(uint16_t *)(header + 6));
|
||||
}
|
||||
|
||||
/** It returns the NSCOUNT field in the DNS msg header */
|
||||
static inline int dns_header_nscount(uint8_t *header)
|
||||
{
|
||||
return htons(*(uint16_t *)(header + 8));
|
||||
}
|
||||
|
||||
/** It returns the ARCOUNT field in the DNS msg header */
|
||||
static inline int dns_header_arcount(uint8_t *header)
|
||||
{
|
||||
return htons(*(uint16_t *)(header + 10));
|
||||
}
|
||||
|
||||
static inline int dns_query_qtype(uint8_t *question)
|
||||
{
|
||||
return htons(*((uint16_t *)(question + 0)));
|
||||
}
|
||||
|
||||
static inline int dns_unpack_query_qtype(uint8_t *question)
|
||||
{
|
||||
return ntohs(*((uint16_t *)(question + 0)));
|
||||
}
|
||||
|
||||
static inline int dns_query_qclass(uint8_t *question)
|
||||
{
|
||||
return htons(*((uint16_t *)(question + 2)));
|
||||
}
|
||||
|
||||
static inline int dns_unpack_query_qclass(uint8_t *question)
|
||||
{
|
||||
return ntohs(*((uint16_t *)(question + 2)));
|
||||
}
|
||||
|
||||
static inline int dns_response_type(uint16_t dname_size, uint8_t *answer)
|
||||
{
|
||||
/** Future versions must consider byte 0
|
||||
* 4.1.3. Resource record format
|
||||
* *(answer + dname_size + 0);
|
||||
*/
|
||||
return *(answer + dname_size + 1);
|
||||
}
|
||||
|
||||
static inline int dns_answer_class(uint16_t dname_size, uint8_t *answer)
|
||||
{
|
||||
/** Future versions must consider byte 2
|
||||
* 4.1.3. Resource record format
|
||||
* *(answer + dname_size + 2);
|
||||
*/
|
||||
return *(answer + dname_size + 3);
|
||||
}
|
||||
|
||||
static inline int dns_answer_ttl(uint16_t dname_size, uint8_t *answer)
|
||||
{
|
||||
return htonl(*(uint32_t *)(answer + dname_size + 4));
|
||||
}
|
||||
|
||||
static inline int dns_answer_rdlength(uint16_t dname_size, uint8_t *answer)
|
||||
{
|
||||
return htons(*(uint16_t *)(answer + dname_size + 8));
|
||||
}
|
||||
|
||||
static inline
|
||||
int dns_unpack_answer_rdlength(uint16_t dname_size, uint8_t *answer)
|
||||
{
|
||||
return ntohs(*(uint16_t *)(answer + dname_size + 8));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief dns_msg_pack_qname Packs a QNAME
|
||||
* @param len Bytes used by this function
|
||||
* @param buf Buffer
|
||||
* @param sizeof Buffer's size
|
||||
* @param domain_name Something like www.example.com
|
||||
* @return 0 on success
|
||||
* @return -ENOMEM if there is no enough space to store
|
||||
* the resultant QNAME
|
||||
* @return -EINVAL if an invalid parameter was passed as
|
||||
* an argument
|
||||
*/
|
||||
int dns_msg_pack_qname(uint16_t *len, uint8_t *buf, uint16_t size,
|
||||
char *domain_name);
|
||||
|
||||
/**
|
||||
* @brief dns_unpack_answer Unpacks an answer message
|
||||
* @param dns_msg Structure
|
||||
* @param dname_ptr An index to the previous CNAME. For example
|
||||
* for the first answer, ptr must be 0x0c, the
|
||||
* DNAME at the question.
|
||||
* @param ttl TTL answer parameter.
|
||||
* @return 0 on success
|
||||
* @return -ENOMEM on error
|
||||
*/
|
||||
int dns_unpack_answer(struct dns_msg_t *dns_msg, int dname_ptr, uint32_t *ttl);
|
||||
|
||||
/**
|
||||
* @brief dns_unpack_response_header
|
||||
*
|
||||
* @details Unpacks the header's response.
|
||||
*
|
||||
* @param msg Structure containing the response.
|
||||
*
|
||||
* @param src_id Transaction id, it must match the id
|
||||
* used in the query datagram sent to the
|
||||
* DNS server.
|
||||
* @return 0 on success
|
||||
*
|
||||
* @return -ENOMEM if the buffer in msg has no
|
||||
* enough space to store the header.
|
||||
* The header is always 12 bytes length.
|
||||
*
|
||||
* @return -EINVAL:
|
||||
* * if the src_id does not match the
|
||||
* header's id.
|
||||
* * if the header's QR value is
|
||||
* not DNS_RESPONSE.
|
||||
* * if the header's OPCODE value is not
|
||||
* DNS_QUERY.
|
||||
* * if the header's Z value is not 0.
|
||||
* * if the question counter is not 1 or
|
||||
* the answer counter is less than 1.
|
||||
*
|
||||
* RFC 1035 RCODEs (> 0):
|
||||
*
|
||||
* 1 Format error
|
||||
* 2 Server failure
|
||||
* 3 Name Error
|
||||
* 4 Not Implemented
|
||||
* 5 Refused
|
||||
*
|
||||
*/
|
||||
int dns_unpack_response_header(struct dns_msg_t *msg, int src_id);
|
||||
|
||||
/**
|
||||
* @brief dns_msg_pack_query Packs the query message
|
||||
* @param [out] buf Buffer that will contain the resultant query
|
||||
* @param [out] len Number of bytes used to encode the query
|
||||
* @param [in] size Buffer size
|
||||
* @param [in] qname Domain name represented as a sequence of labels.
|
||||
* See RFC 1035, 4.1.2. Question section format.
|
||||
* @param [in] qname_len Number of octects in qname.
|
||||
* @param [in] id Transaction Identifier
|
||||
* @param [in] qtype Query type: AA, AAAA. See enum dns_rr_type
|
||||
* @return 0 on success
|
||||
* @return On error, a negative value is returned. See:
|
||||
* - dns_msg_pack_query_header
|
||||
* - dns_msg_pack_qname
|
||||
*/
|
||||
int dns_msg_pack_query(uint8_t *buf, uint16_t *len, uint16_t size,
|
||||
uint8_t *qname, uint16_t qname_len, uint16_t id,
|
||||
enum dns_rr_type qtype);
|
||||
|
||||
/**
|
||||
* @brief dns_unpack_response_query
|
||||
*
|
||||
* @details Unpacks the response's query. RFC 1035 states that the
|
||||
* response's query comes after the first 12 bytes,
|
||||
* i.e. afther the message's header.
|
||||
*
|
||||
* This function computes the answer_offset field.
|
||||
*
|
||||
* @param dns_msg Structure containing the message.
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -ENOMEM:
|
||||
* * if the null label is not found after
|
||||
* traversing the buffer.
|
||||
* * if QCLASS and QTYPE are not found.
|
||||
* @return -EINVAL:
|
||||
* * if QTYPE is not "A" (IPv4) or "AAAA" (IPv6).
|
||||
* * if QCLASS is not "IN".
|
||||
*
|
||||
*/
|
||||
int dns_unpack_response_query(struct dns_msg_t *dns_msg);
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue