net/dns: Introduce the qname_copy routine

When a CNAME is part of the DNS answer RR, sometimes a label with
a pointer is found. The CNAME must be reused to create a new DNS
query and that CNAME will become the new DNS Query QNAME. This new
QNAME must not include pointers.

This patch introduces the qname_copy routine that "linearizes" a
given QNAME (perhaps with pointers).

The dns_read routine is also updated to reflect these changes.

Change-Id: I8e8f64e85e2cbf494fd589e2b7a67d470d34604b
Signed-off-by: Flavio Santes <flavio.santes@intel.com>
This commit is contained in:
Flavio Santes 2016-12-28 22:45:13 -06:00 committed by Tomasz Bursztyka
commit e86b6c23af
3 changed files with 101 additions and 9 deletions

View file

@ -73,8 +73,8 @@ int dns_write(struct dns_context *ctx, struct net_buf *dns_data,
uint16_t dns_id, struct net_buf *dns_qname);
static
int dns_read(struct dns_context *ctx, struct net_buf *dns_data,
uint16_t dns_id, uint8_t *cname, uint16_t *cname_len);
int dns_read(struct dns_context *ctx, struct net_buf *dns_data, uint16_t dns_id,
struct net_buf *cname);
/* net_context_recv callback */
static
@ -128,8 +128,7 @@ int dns_resolve(struct dns_context *ctx)
goto exit_resolve;
}
rc = dns_read(ctx, dns_data, dns_id,
dns_qname->data, &dns_qname->len);
rc = dns_read(ctx, dns_data, dns_id, dns_qname);
if (rc != 0) {
goto exit_resolve;
}
@ -222,8 +221,8 @@ void cb_recv(struct net_context *net_ctx, struct net_buf *buf, int status,
}
static
int dns_read(struct dns_context *ctx, struct net_buf *dns_data,
uint16_t dns_id, uint8_t *cname, uint16_t *cname_len)
int dns_read(struct dns_context *ctx, struct net_buf *dns_data, uint16_t dns_id,
struct net_buf *cname)
{
/* helper struct to track the dns msg received from the server */
struct dns_msg_t dns_msg;
@ -358,10 +357,14 @@ int dns_read(struct dns_context *ctx, struct net_buf *dns_data,
*/
if (ctx->items == 0) {
if (dns_msg.response_type == DNS_RESPONSE_CNAME_NO_IP) {
src = dns_msg.msg + dns_msg.response_position;
*cname_len = dns_msg.response_length;
uint16_t pos = dns_msg.response_position;
rc = dns_copy_qname(cname->data, &cname->len,
cname->size, &dns_msg, pos);
if (rc != 0) {
goto exit_error;
}
memcpy(cname, src, *cname_len);
}
}
@ -370,5 +373,6 @@ exit_ok:
exit_error:
net_nbuf_unref(ctx->rx_buf);
return rc;
}

View file

@ -17,6 +17,8 @@
#include "dns_pack.h"
#include <string.h>
/* This is the label's length octet, see 4.1.2. Question section format */
#define DNS_LABEL_LEN_SIZE 1
#define DNS_LABEL_MAX_SIZE 63
#define DNS_ANSWER_MIN_SIZE 12
#define DNS_COMMON_UINT_SIZE 2
@ -350,3 +352,67 @@ int dns_unpack_response_query(struct dns_msg_t *dns_msg)
return 0;
}
int dns_copy_qname(uint8_t *buf, uint16_t *len, uint16_t size,
struct dns_msg_t *dns_msg, uint16_t pos)
{
uint16_t msg_size = dns_msg->msg_size;
uint8_t *msg = dns_msg->msg;
uint16_t lb_size;
int rc = -EINVAL;
int i = 0;
*len = 0;
/* Iterate ANCOUNT + 1 to allow the Query's QNAME to be parsed.
* This is required to avoid 'alias loops'
*/
while (i++ < dns_header_ancount(dns_msg->msg) + 1) {
if (pos >= msg_size) {
rc = -ENOMEM;
break;
}
lb_size = msg[pos];
/* pointer */
if (lb_size > DNS_LABEL_MAX_SIZE) {
uint8_t mask = DNS_LABEL_MAX_SIZE;
if (pos + 1 >= msg_size) {
rc = -ENOMEM;
break;
}
/* See: RFC 1035, 4.1.4. Message compression */
pos = ((msg[pos] & mask) << 8) + msg[pos + 1];
continue;
}
/* validate that the label (i.e. size + elements),
* fits the current msg buffer
*/
if (DNS_LABEL_LEN_SIZE + lb_size > size - *len) {
rc = -ENOMEM;
break;
}
/* copy the lb_size value and label elements */
memcpy(buf + *len, msg + pos, DNS_LABEL_LEN_SIZE + lb_size);
/* update destination buffer len */
*len += DNS_LABEL_LEN_SIZE + lb_size;
/* update msg ptr position */
pos += DNS_LABEL_LEN_SIZE + lb_size;
/* The domain name terminates with the zero length octet
* for the null label of the root
*/
if (lb_size == 0) {
rc = 0;
break;
}
}
return rc;
}

View file

@ -361,4 +361,26 @@ int dns_msg_pack_query(uint8_t *buf, uint16_t *len, uint16_t size,
*/
int dns_unpack_response_query(struct dns_msg_t *dns_msg);
/**
* @brief dns_copy_qname Copies the qname from dns_msg to buf
*
* @details This routine applies 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 [out] buf Output buffer
* @param [out] len Output buffer's length
* @param [in] size Output buffer's size
* @param [in] dns_msg Structure containing the message
* @param [in] pos QNAME's position in dns_msg->msg
* @return 0 on success
* @return -EINVAL if an invalid parameter was passed as
* an argument
* @return -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);
#endif