466 lines
14 KiB
C
466 lines
14 KiB
C
/*
|
|
* Copyright (c) 2016 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#ifndef _DNS_PACK_H_
|
|
#define _DNS_PACK_H_
|
|
|
|
#include <zephyr/net/net_ip.h>
|
|
#include <zephyr/net/buf.h>
|
|
|
|
#include <zephyr/types.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
|
|
|
|
/* This is the label's length octet, see 4.1.2. Question section format */
|
|
#define DNS_LABEL_LEN_SIZE 1
|
|
#define DNS_POINTER_SIZE 2
|
|
#define DNS_LABEL_MIN_SIZE 1
|
|
#define DNS_LABEL_MAX_SIZE 63
|
|
#define DNS_NAME_MAX_SIZE 255
|
|
#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
|
|
#define DNS_TTL_LEN 4
|
|
#define DNS_RDLENGTH_LEN 2
|
|
|
|
#define NS_CMPRSFLGS 0xc0 /* DNS name compression */
|
|
|
|
/* 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 */
|
|
|
|
/**
|
|
* DNS message structure for DNS responses
|
|
*
|
|
* 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;
|
|
|
|
int response_type;
|
|
uint16_t response_position;
|
|
uint16_t response_length;
|
|
|
|
uint16_t query_offset;
|
|
uint16_t answer_offset;
|
|
uint16_t msg_size;
|
|
};
|
|
|
|
#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_PTR = 12, /* PTR */
|
|
DNS_RR_TYPE_TXT = 16, /* TXT */
|
|
DNS_RR_TYPE_AAAA = 28, /* IPv6 */
|
|
DNS_RR_TYPE_SRV = 33, /* SRV */
|
|
};
|
|
|
|
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,
|
|
DNS_CLASS_FLUSH = BIT(15)
|
|
};
|
|
|
|
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
|
|
};
|
|
|
|
struct dns_header {
|
|
/** Transaction ID */
|
|
uint16_t id;
|
|
/**
|
|
* | Name | Bit Position | Width | Description |
|
|
* |------|--------------|-------|-------------|
|
|
* | RCODE | 0 | 4 | Response / Error code |
|
|
* | CD | 4 | 1 | |
|
|
* | AD | 5 | 1 | Authenticated Data. 0 := Unacceptable, 1 := Acceptable |
|
|
* | Z | 6 | 1 | Reserved (WZ/RAZ) |
|
|
* | RA | 7 | 1 | Recursion Available. 0 := Unavailable, 1 := Available |
|
|
* | RD | 8 | 1 | Recursion Desired. 0 := No Recursion, 1 := Recursion |
|
|
* | TC | 9 | 1 | 0 := Not Truncated, 1 := Truncated |
|
|
* | AA | 10 | 1 | Answer Authenticated / Answer Authoritative. 0 := Not Authenticated, 1 := Authenticated|
|
|
* | Opcode | 11 | 4 | See @ref dns_opcode |
|
|
* | QR | 15 | 1 | 0 := Query, 1 := Response |
|
|
*/
|
|
uint16_t flags;
|
|
/** Query count */
|
|
uint16_t qdcount;
|
|
/** Answer count */
|
|
uint16_t ancount;
|
|
/** Authority count */
|
|
uint16_t nscount;
|
|
/** Additional information count */
|
|
uint16_t arcount;
|
|
/** Flexible array member for records */
|
|
uint8_t data[];
|
|
} __packed;
|
|
|
|
struct dns_query {
|
|
uint16_t type;
|
|
uint16_t class_;
|
|
} __packed;
|
|
|
|
struct dns_rr {
|
|
uint16_t type;
|
|
uint16_t class_;
|
|
uint32_t ttl;
|
|
uint16_t rdlength;
|
|
uint8_t rdata[];
|
|
} __packed;
|
|
|
|
struct dns_srv_rdata {
|
|
uint16_t priority;
|
|
uint16_t weight;
|
|
uint16_t port;
|
|
} __packed;
|
|
|
|
struct dns_a_rdata {
|
|
uint32_t address;
|
|
} __packed;
|
|
|
|
struct dns_aaaa_rdata {
|
|
uint8_t address[16];
|
|
} __packed;
|
|
|
|
/** It returns the ID field in the DNS msg header */
|
|
static inline int dns_header_id(uint8_t *header)
|
|
{
|
|
return htons(UNALIGNED_GET((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(UNALIGNED_GET((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(UNALIGNED_GET((uint16_t *)(header + 4)));
|
|
}
|
|
|
|
static inline int dns_unpack_header_qdcount(uint8_t *header)
|
|
{
|
|
return ntohs(UNALIGNED_GET((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(UNALIGNED_GET((uint16_t *)(header + 6)));
|
|
}
|
|
|
|
static inline int dns_unpack_header_ancount(uint8_t *header)
|
|
{
|
|
return ntohs(UNALIGNED_GET((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(UNALIGNED_GET((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(UNALIGNED_GET((uint16_t *)(header + 10)));
|
|
}
|
|
|
|
static inline int dns_query_qtype(uint8_t *question)
|
|
{
|
|
return htons(UNALIGNED_GET((uint16_t *)(question + 0)));
|
|
}
|
|
|
|
static inline int dns_unpack_query_qtype(const uint8_t *question)
|
|
{
|
|
return ntohs(UNALIGNED_GET((uint16_t *)(question + 0)));
|
|
}
|
|
|
|
static inline int dns_query_qclass(uint8_t *question)
|
|
{
|
|
return htons(UNALIGNED_GET((uint16_t *)(question + 2)));
|
|
}
|
|
|
|
static inline int dns_unpack_query_qclass(const uint8_t *question)
|
|
{
|
|
return ntohs(UNALIGNED_GET((uint16_t *)(question + 2)));
|
|
}
|
|
|
|
static inline int dns_answer_type(uint16_t dname_size, uint8_t *answer)
|
|
{
|
|
/* 4.1.3. Resource record format */
|
|
return ntohs(UNALIGNED_GET((uint16_t *)(answer + dname_size + 0)));
|
|
}
|
|
|
|
static inline int dns_answer_class(uint16_t dname_size, uint8_t *answer)
|
|
{
|
|
/* 4.1.3. Resource record format */
|
|
return ntohs(UNALIGNED_GET((uint16_t *)(answer + dname_size + 2)));
|
|
}
|
|
|
|
static inline int dns_answer_ttl(uint16_t dname_size, uint8_t *answer)
|
|
{
|
|
return ntohl(UNALIGNED_GET((uint32_t *)(answer + dname_size + 4)));
|
|
}
|
|
|
|
static inline int dns_answer_rdlength(uint16_t dname_size,
|
|
uint8_t *answer)
|
|
{
|
|
return ntohs(UNALIGNED_GET((uint16_t *)(answer + dname_size + 8)));
|
|
}
|
|
|
|
/**
|
|
* @brief 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
|
|
* @retval 0 on success
|
|
* @retval -ENOMEM if there is no enough space to store the resultant QNAME
|
|
* @retval -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 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.
|
|
* @param type Answer type parameter.
|
|
* @retval 0 on success
|
|
* @retval -ENOMEM on error
|
|
*/
|
|
int dns_unpack_answer(struct dns_msg_t *dns_msg, int dname_ptr, uint32_t *ttl,
|
|
enum dns_rr_type *type);
|
|
|
|
/**
|
|
* @brief 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.
|
|
* @retval 0 on success
|
|
* @retval -ENOMEM if the buffer in msg has no enough space to store the header.
|
|
* The header is always 12 bytes length.
|
|
* @retval -EINVAL if the src_id does not match the header's id, or if the
|
|
* header's QR value is not DNS_RESPONSE or if the header's OPCODE
|
|
* value is not DNS_QUERY, or if the header's Z value is not 0 or if
|
|
* the question counter is not 1 or the answer counter is less than 1.
|
|
* @retval RFC 1035 RCODEs (> 0) 1 Format error, 2 Server failure, 3 Name Error,
|
|
* 4 Not Implemented and 5 Refused.
|
|
*/
|
|
int dns_unpack_response_header(struct dns_msg_t *msg, int src_id);
|
|
|
|
/**
|
|
* @brief Packs the query message
|
|
*
|
|
* @param buf Buffer that will contain the resultant query
|
|
* @param len Number of bytes used to encode the query
|
|
* @param size Buffer size
|
|
* @param qname Domain name represented as a sequence of labels.
|
|
* See RFC 1035, 4.1.2. Question section format.
|
|
* @param qname_len Number of octets in qname.
|
|
* @param id Transaction Identifier
|
|
* @param qtype Query type: AA, AAAA. See enum dns_rr_type
|
|
* @retval 0 on success
|
|
* @retval On error, a negative value is returned.
|
|
* See: dns_msg_pack_query_header and 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 Unpacks the response's query.
|
|
*
|
|
* @details RFC 1035 states that the response's query comes after the first
|
|
* 12 bytes i.e., after the message's header. This function computes
|
|
* the answer_offset field.
|
|
*
|
|
* @param dns_msg Structure containing the message.
|
|
* @retval 0 on success
|
|
* @retval -ENOMEM if the null label is not found after traversing the buffer
|
|
* or if QCLASS and QTYPE are not found.
|
|
* @retval -EINVAL if QTYPE is not "A" (IPv4) or "AAAA" (IPv6) or if QCLASS
|
|
* is not "IN".
|
|
*/
|
|
int dns_unpack_response_query(struct dns_msg_t *dns_msg);
|
|
|
|
/**
|
|
* @brief Copies the qname from dns_msg to buf
|
|
*
|
|
* @details This routine implements the algorithm described in RFC 1035, 4.1.4.
|
|
* Message compression to copy the qname (perhaps containing pointers
|
|
* with offset) to the linear buffer buf. Pointers are removed and
|
|
* only the "true" labels are copied.
|
|
*
|
|
* @param buf Output buffer
|
|
* @param len Output buffer's length
|
|
* @param size Output buffer's size
|
|
* @param dns_msg Structure containing the message
|
|
* @param pos QNAME's position in dns_msg->msg
|
|
* @retval 0 on success
|
|
* @retval -EINVAL if an invalid parameter was passed as an argument
|
|
* @retval -ENOMEM if the label's size is corrupted
|
|
*/
|
|
int dns_copy_qname(uint8_t *buf, uint16_t *len, uint16_t size,
|
|
struct dns_msg_t *dns_msg, uint16_t pos);
|
|
|
|
/**
|
|
* @brief Unpacks the mDNS query. This is special version for multicast DNS
|
|
* as it skips checks to various fields as described in RFC 6762
|
|
* chapter 18.
|
|
*
|
|
* @param msg Structure containing the response.
|
|
* @param src_id Transaction id, this is returned to the caller.
|
|
* @retval 0 on success, <0 if error
|
|
* @retval -ENOMEM if the buffer in msg has no enough space to store the header.
|
|
* The header is always 12 bytes length.
|
|
* @retval -EINVAL if the src_id does not match the header's id, or if the
|
|
* header's QR value is not DNS_RESPONSE or if the header's OPCODE
|
|
* value is not DNS_QUERY, or if the header's Z value is not 0 or if
|
|
* the question counter is not 1 or the answer counter is less than 1.
|
|
* @retval RFC 1035 RCODEs (> 0) 1 Format error, 2 Server failure, 3 Name Error,
|
|
* 4 Not Implemented and 5 Refused.
|
|
*/
|
|
int mdns_unpack_query_header(struct dns_msg_t *msg, uint16_t *src_id);
|
|
|
|
static inline int llmnr_unpack_query_header(struct dns_msg_t *msg,
|
|
uint16_t *src_id)
|
|
{
|
|
return mdns_unpack_query_header(msg, src_id);
|
|
}
|
|
|
|
/**
|
|
* @brief Unpacks the query.
|
|
*
|
|
* @param dns_msg Structure containing the message.
|
|
* @param buf Result buf
|
|
* @param qtype Query type is returned to caller
|
|
* @param qclass Query class is returned to caller
|
|
* @retval 0 on success
|
|
* @retval -ENOMEM if the null label is not found after traversing the buffer
|
|
* or if QCLASS and QTYPE are not found.
|
|
* @retval -EINVAL if QTYPE is not "A" (IPv4) or "AAAA" (IPv6) or if QCLASS
|
|
* is not "IN".
|
|
*/
|
|
int dns_unpack_query(struct dns_msg_t *dns_msg, struct net_buf *buf,
|
|
enum dns_rr_type *qtype,
|
|
enum dns_class *qclass);
|
|
|
|
#endif
|