zephyr/lib/iot/dns/dns_pack.h

365 lines
9.3 KiB
C

/*
* 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,
const 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