samples/net: DNS client application

This sample code shows how to write a DNS client application.
Only client functionality is implemented in this sample.

The code is self-documented. Read the README file for more information
about the network setup.

See https://tools.ietf.org/html/rfc1035 for more information about the
DNS protocol.

Origin: Original

Jira: ZEP-500
Jira: ZEP-501

Change-Id: Ibcfa4e392143fdc2258971304d5296d70c1c9423
Signed-off-by: Flavio Santes <flavio.santes@intel.com>
This commit is contained in:
Flavio Santes 2016-07-29 17:32:48 -05:00 committed by Andrew Boie
commit e240f84c75
13 changed files with 1570 additions and 0 deletions

View file

@ -0,0 +1,21 @@
#
# 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.
#
KERNEL_TYPE = nano
BOARD = galileo
CONF_FILE = prj_galileo.conf
include $(ZEPHYR_BASE)/Makefile.inc

View file

@ -0,0 +1,102 @@
DNS Client Application
Requirements
------------
* Ethernet LAN for testing purposes.
* Galileo Gen 2 Development Board.
* USB-UART cable: CH340, FTDI 6-pin or equivalent for debugging.
* dnsmasq application. The dnsmasq version used in this sample is:
dnsmasq -v
Dnsmasq version 2.75 Copyright (c) 2000-2015 Simon Kelley
Building instructions
---------------------
* Read src/main.c, change the IP addresses according to the LAN
environment.
* make pristine && make are enough to build this sample.
* Follow the steps indicated here:
https://www.zephyrproject.org/doc/board/galileo.html
to load the binary into the Galileo Dev Board.
Usage
-----
* Open a terminal window and type:
dnsmasq -d
* Connect the USB-UART cable to the Galileo. Open a terminal and run:
screen /dev/ttyUSB0 115200
* Connect Galileo to the LAN, Turn on the board.
* The screen terminal window will show:
WARNING: no console will be available to OS
error: no suitable video mode found.
-----------------------------------------
Domain name: not_a_real_domain_name
[fiber:82] DNS Create Query: 0, ID: 1
[fiber:85] TX: 0
[fiber:88] RX: -5
-----------------------------------------
Domain name: oops!
[fiber:82] DNS Create Query: 0, ID: 2
[fiber:85] TX: 0
[fiber:88] RX: 0
[fiber:95] DNS response: 2
-----------------------------------------
Domain name: zephyrproject.org
[fiber:82] DNS Create Query: 0, ID: 3
[fiber:85] TX: 0
[fiber:88] RX: 0
****** DNS ANSWER: 0 ******
Response: IP address Size: 4: 140 211 169 8
[fiber:95] DNS response: 0
-----------------------------------------
Domain name: www.google.com
[fiber:82] DNS Create Query: 0, ID: 4
[fiber:85] TX: 0
[fiber:88] RX: 0
****** DNS ANSWER: 0 ******
Response: IP address Size: 4: 216 58 192 4
[fiber:95] DNS response: 0
-----------------------------------------
Domain name: mail.yahoo.com
[fiber:82] DNS Create Query: 0, ID: 5
[fiber:85] TX: 0
[fiber:88] RX: 0
****** DNS ANSWER: 0 ******
Response: CNAME NO IP address Size: 8: 5 108 111 103 105 110 192 17
CNAME: login.yahoo.com
****** DNS ANSWER: 1 ******
Response: CNAME NO IP address Size: 35: 9 102 111 45 100 115 45 97 116 115 6 109 101 109 98 101 114 3 103 48 50 8 121 97 104 111 111 100 110 115 3 110 101 116 0
CNAME: fo-ds-ats.member.g02.yahoodns.net
****** DNS ANSWER: 2 ******
Response: IP address Size: 4: 98 136 189 41
[fiber:95] DNS response: 0
-----------------------------------------
Bye!

View file

@ -0,0 +1,17 @@
CONFIG_STDOUT_CONSOLE=y
CONFIG_NETWORKING=y
CONFIG_ETHERNET=y
CONFIG_ETH_DW=y
CONFIG_NANO_TIMEOUTS=y
CONFIG_NETWORKING_WITH_TCP=y
CONFIG_NETWORKING_WITH_IPV4=y
CONFIG_NETWORKING_IPV6_NO_ND=y
CONFIG_IP_BUF_RX_SIZE=4
CONFIG_IP_BUF_TX_SIZE=4
#CONFIG_NETWORKING_WITH_LOGGING=y
#CONFIG_NETWORK_IP_STACK_DEBUG_NET_BUF=y
#CONFIG_NET_BUF_DEBUG=y

View file

@ -0,0 +1,25 @@
#
# 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.
#
ccflags-y +=-I${ZEPHYR_BASE}/net/ip/contiki
ccflags-y +=-I${ZEPHYR_BASE}/net/ip/contiki/os/lib
ccflags-y +=-I${ZEPHYR_BASE}/net/ip/contiki/os
ccflags-y +=-I${ZEPHYR_BASE}/net/ip
obj-y += dns_pack.o
obj-y += netz.o
obj-y += dns_utils.o
obj-y += main.o

View file

@ -0,0 +1,19 @@
#ifndef _APP_BUF_H_
#define _APP_BUF_H_
#include <zephyr.h> /* for __deprecated */
#include <stdint.h>
#include <stddef.h>
struct __deprecated app_buf_t;
struct app_buf_t {
uint8_t *buf;
size_t size;
size_t length;
};
#define APP_BUF_INIT(_buf, _size, _length) {.buf = _buf, .size = _size,\
.length = _length}
#endif

View file

@ -0,0 +1,280 @@
/*
* 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>
int dns_msg_pack_qname(int *len, uint8_t *buf, int size, char *domain_name)
{
int lb_index;
int lb_start;
int lb_size;
size_t i;
lb_start = 0;
lb_index = 1;
lb_size = 0;
/* traverse the domain name str, including the null-terminator :) */
for (i = 0; i < strlen(domain_name) + 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;
}
int dns_unpack_answer(struct dns_msg_t *dns_msg, int dname_ptr)
{
uint8_t *answer;
int buf_size;
int ptr;
answer = dns_msg->msg + dns_msg->answer_offset;
/* See RFC-1035 to find out why 63 */
if (answer[0] < 63) {
return -ENOMEM;
}
/* Recovery of the pointer value */
ptr = (((answer[0] & 63) << 8) + answer[1]);
if (ptr != dname_ptr) {
return -ENOMEM;
}
/*
* We need to be sure this buffer has enough space
* to parse 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 < 12) {
return -ENOMEM;
}
/* Only answers of type Internet.
* Here we use 2 as an offset because a ptr uses only 2 bytes.
*/
if (dns_answer_class(2, answer) != DNS_CLASS_IN) {
return -EINVAL;
}
switch (dns_response_type(2, answer)) {
case DNS_RR_TYPE_A:
case DNS_RR_TYPE_AAAA:
dns_msg->response_type = DNS_RESPONSE_IP;
dns_msg->response_position = dns_msg->answer_offset + 12;
dns_msg->response_length = dns_answer_rdlength(2, answer);
return 0;
case DNS_RR_TYPE_CNAME:
dns_msg->response_type = DNS_RESPONSE_CNAME_NO_IP;
dns_msg->response_position = dns_msg->answer_offset + 12;
dns_msg->response_length = dns_answer_rdlength(2, answer);
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;
int size;
int qdcount;
int ancount;
dns_header = msg->msg;
size = msg->msg_size;
if (size < DNS_MSG_HEADER_SIZE) {
return -ENOMEM;
}
if (dns_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;
}
switch (dns_header_rcode(dns_header)) {
case DNS_HEADER_NOERROR:
break;
default:
return dns_header_rcode(dns_header);
}
qdcount = dns_header_qdcount(dns_header);
ancount = dns_header_ancount(dns_header);
if (qdcount < 1 || ancount < 1) {
return -EINVAL;
}
return 0;
}
static int dns_msg_pack_query_header(uint8_t *buf, int size, uint16_t id)
{
if (size < DNS_MSG_HEADER_SIZE) {
return -ENOMEM;
}
*(uint16_t *)(buf + 0) = z_swap2(id);
/* RD = 1, TC = 0, AA = 0, Opcode = 0, QR = 0 <-> 0x01 (1B)
* RCode = 0, Z = 0, RA = 0 <-> 0x00 (1B)
*
* QDCOUNT = 1 <-> 0x0001 (2B)
*/
*(uint32_t *)(buf + 2) = 0x01000001;
*(uint32_t *)(buf + 6) = 0;
*(uint16_t *)(buf + 10) = 0;
return 0;
}
int dns_msg_pack_query(struct app_buf_t *buf, char *domain_name,
uint16_t id, enum dns_rr_type qtype)
{
int qname_len;
int offset;
int rc;
rc = dns_msg_pack_query_header(buf->buf, buf->size, id);
if (rc != 0) {
return rc;
}
offset = DNS_MSG_HEADER_SIZE;
rc = dns_msg_pack_qname(&qname_len, buf->buf + offset,
buf->size - offset, domain_name);
if (rc != 0) {
return rc;
}
offset += qname_len;
/* 4Bytes required: QType: (2B), QClass (2B) */
if (offset + 4 > buf->size) {
return rc;
}
/* QType */
*(uint16_t *)(buf->buf + offset + 0) = z_swap2(qtype);
/* QClass */
*(uint16_t *)(buf->buf + offset + 2) = z_swap2(DNS_CLASS_IN);
buf->length = offset + 4;
return 0;
}
int dns_find_null(int *qname_size, uint8_t *buf, int 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;
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 for qtype and qclass */
offset += 4;
if (offset > dns_msg->msg_size) {
return -ENOMEM;
}
if (dns_query_qtype(dns_query + qname_size) != DNS_RR_TYPE_A &&
dns_query_qtype(dns_query + qname_size) != DNS_RR_TYPE_AAAA) {
return -EINVAL;
}
if (dns_query_qclass(dns_query + qname_size) != DNS_CLASS_IN) {
return -EINVAL;
}
dns_msg->answer_offset = dns_msg->query_offset + qname_size + 4;
return 0;
}

View file

@ -0,0 +1,332 @@
/*
* 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 <stdint.h>
#include <stddef.h>
#include <errno.h>
#include <app_buf.h>
/**
* @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;
size_t msg_size;
int response_type;
int response_position;
int response_length;
int cname_offset;
int query_offset;
int 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
};
/* See 4.1.1 Header section format
* DNS Message Header is always 12 bytes
*/
#define DNS_MSG_HEADER_SIZE 12
static inline uint32_t z_swap4(uint32_t n)
{
return ((n&0x000000FF)<<24) + ((n&0x0000FF00)<<8)
+ ((n&0x00FF0000)>>8) + ((n&0xFF000000)>>24);
}
static inline uint16_t z_swap2(uint16_t n)
{
return (n>>8) + ((n & 0x00FF) << 8);
}
/** It returns the ID field in the DNS msg header */
static inline int dns_header_id(uint8_t *header)
{
return z_swap2(*(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 z_swap2(*(uint16_t *)(header + 4));
}
/** It returns the ANCOUNT field in the DNS msg header */
static inline int dns_header_ancount(uint8_t *header)
{
return z_swap2(*(uint16_t *)(header + 6));
}
/** It returns the NSCOUNT field in the DNS msg header */
static inline int dns_header_nscount(uint8_t *header)
{
return z_swap2(*(uint16_t *)(header + 8));
}
/** It returns the ARCOUNT field in the DNS msg header */
static inline int dns_header_arcount(uint8_t *header)
{
return z_swap2(*(uint16_t *)(header + 10));
}
static inline int dns_query_qtype(uint8_t *question)
{
return z_swap2(*((uint16_t *)(question + 0)));
}
static inline int dns_query_qclass(uint8_t *question)
{
return z_swap2(*((uint16_t *)(question + 2)));
}
static inline int dns_response_type(int dname_size, uint8_t *answer)
{
/** Future versions must consider byte 0
* *(answer + dname_size + 0);
*/
return *(answer + dname_size + 1);
}
static inline int dns_answer_class(int dname_size, uint8_t *answer)
{
/** Future versions must consider byte 2
* *(answer + dname_size + 2);
*/
return *(answer + dname_size + 3);
}
static inline int dns_answer_ttl(int dname_size, uint8_t *answer)
{
return z_swap4(*(uint32_t *)(answer + dname_size + 4));
}
static inline int dns_answer_rdlength(int dname_size, uint8_t *answer)
{
return z_swap2(*(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
*/
int dns_msg_pack_qname(int *len, uint8_t *buf, int 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.
* @return 0 on success
* @return -ENOMEM on error
*/
int dns_unpack_answer(struct dns_msg_t *dns_msg, int dname_ptr);
/**
* @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 buf Buffer that will contain the resultant query
* @param domain_name Something like: www.example.com
* @param id Transaction Identifier
* @param 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(struct app_buf_t *buf, char *domain_name,
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

View file

@ -0,0 +1,162 @@
/*
* 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 <stdio.h>
#include <errno.h>
int dns_print_msg_header(uint8_t *header, int size)
{
if (size < DNS_MSG_HEADER_SIZE) {
return -ENOMEM;
}
printf("\nHeader\n");
printf("\tFlags\n");
printf("\t\tTransaction ID:\t\t%d\n", dns_header_id(header));
printf("\t\tMessage type:\t\t%d (%s)\n", dns_header_qr(header),
(dns_header_qr(header) == DNS_QUERY ? "query" : "response"));
printf("\t\tOperation code:\t\t%d (%s)\n", dns_header_opcode(header),
(dns_header_opcode(header) == DNS_QUERY ? "query" : "other"));
printf("\t\tAuthoritative:\t\t%d (%s)\n", dns_header_aa(header),
(dns_header_aa(header) ? "true" : "false"));
printf("\t\tTruncated:\t\t%d (%s)\n", dns_header_tc(header),
(dns_header_tc(header) ? "true" : "false"));
printf("\t\tRecursion desired:\t%d (%s)\n", dns_header_rd(header),
(dns_header_rd(header) ? "true" : "false"));
printf("\t\tRecursion avaialable:\t%d (%s)\n", dns_header_ra(header),
(dns_header_ra(header) ? "true" : "false"));
printf("\t\tZ:\t\t\t%d\n", dns_header_z(header));
printf("\t\tResponse code:\t\t%d (%s)\n", dns_header_rcode(header),
(dns_header_rcode(header) == 0 ? "no error" : "error"));
printf("\tQuestion counter:\t%d\n", dns_header_qdcount(header));
printf("\tAnswer counter:\t\t%d\n", dns_header_ancount(header));
printf("\tNServer counter:\t%d\n", dns_header_nscount(header));
printf("\tAdditional counter:\t%d\n", dns_header_arcount(header));
return 0;
}
int dns_print_label(uint8_t *label, int size)
{
int n;
int i;
i = 0;
while (i < size) {
n = label[i];
if (n == 0) {
break;
} else if (n > 63) {
break;
} else if (i + n <= size) {
if (i) {
printf(".");
}
int j = i + 1; /* next char */
while (j < i + 1 + n) {
printf("%c", label[j]);
j++;
}
i += n + 1; /* content + octect size */
} else {
i = 0; /* no memory! */
break;
}
}
if (i == 0) {
return -ENOMEM;
}
return 0;
}
int dns_print_msg_query(uint8_t *qname, int qname_size, int qtype, int qclass)
{
printf("\nQuery\n");
printf("\tQuery name\n\t\tLabel size:\t%d\n\t\tDomain name:\t",
qname_size);
dns_print_label(qname, qname_size);
printf("\n");
printf("\tQuery type:\t\t%d\n", qtype);
printf("\tQuery class:\t\t%d\n", qclass);
return 0;
}
int dns_print_readable_msg_label(int offset, uint8_t *buf, int size)
{
int next;
int i;
int j;
for (i = offset; i < size;) {
if (buf[i] <= 63) {
/* +1 because the null label or a pointer */
if (i + buf[i] + 1 >= size) {
return -ENOMEM;
}
for (j = 1; j <= buf[i]; j++) {
printf("%c", buf[i + j]);
}
i += buf[i] + 1;
if (buf[i] == 0) {
break;
}
printf(".");
} else {
if (i + 1 >= size) {
return -ENOMEM;
}
next = ((buf[i] & 0x3F) << 8) + buf[i + 1];
if (next >= size) {
return -ENOMEM;
}
i = next;
offset = next;
}
}
printf("\n");
return 0;
}
int print_buf(uint8_t *buf, size_t size)
{
size_t i;
for (i = 0; i < size; i++) {
printf("%d ", buf[i]);
}
printf("\n");
return 0;
}
int print_app_buf(struct app_buf_t *buf)
{
print_buf(buf->buf, buf->length);
return 0;
}

View file

@ -0,0 +1,30 @@
/*
* 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_UTILS_H_
#define _DNS_UTILS_H_
#include <app_buf.h>
#include <stdint.h>
int dns_print_msg_header(uint8_t *header, int size);
int dns_print_msg_query(uint8_t *qname, int qname_size, int qtype, int qclass);
int dns_print_label(uint8_t *label, int size);
int dns_print_readable_msg_label(int offset, uint8_t *buf, int size);
int print_buf(uint8_t *buf, size_t size);
int print_app_buf(struct app_buf_t *buf);
#endif

View file

@ -0,0 +1,191 @@
/*
* 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 <zephyr.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "dns_pack.h"
#include "dns_utils.h"
#include "netz.h"
#define STACK_SIZE 1024
uint8_t stack[STACK_SIZE];
#define BUF_SIZE 1024
uint8_t tx_raw_buf[BUF_SIZE];
uint8_t rx_raw_buf[BUF_SIZE];
#define SLEEP_TIME 10
char *domains[] = {"not_a_real_domain_name", "oops!",
"zephyrproject.org", "www.google.com",
"mail.yahoo.com", NULL};
int dns_query(struct app_buf_t *buf, char *str, uint16_t id,
enum dns_rr_type qtype);
int dns_response(struct app_buf_t *_buf, int *response_type, int src_id);
void fiber(void)
{
struct app_buf_t tx_buf = APP_BUF_INIT(tx_raw_buf,
sizeof(tx_raw_buf), 0);
struct app_buf_t rx_buf = APP_BUF_INIT(rx_raw_buf,
sizeof(rx_raw_buf), 0);
struct netz_ctx_t netz_ctx = NETZ_CTX_INIT;
int response_type;
int counter;
char *name;
int rc;
netz_host_ipv4(&netz_ctx, 192, 168, 1, 101);
netz_remote_ipv4(&netz_ctx, 192, 168, 1, 10, 5353);
netz_netmask_ipv4(&netz_ctx, 255, 255, 255, 0);
netz_udp(&netz_ctx);
counter = 0;
do {
fiber_sleep(300);
printf("\n-----------------------------------------\n");
name = domains[counter];
if (name == NULL) {
counter = 0;
break;
}
counter += 1;
printf("Domain name: %s\n", name);
rc = dns_query(&tx_buf, name, counter, DNS_RR_TYPE_A);
printf("[%s:%d] DNS Create Query: %d, ID: %d\n",
__func__, __LINE__, rc, counter);
rc = netz_tx(&netz_ctx, &tx_buf);
printf("[%s:%d] TX: %d\n", __func__, __LINE__, rc);
rc = netz_rx(&netz_ctx, &rx_buf);
printf("[%s:%d] RX: %d\n", __func__, __LINE__, rc);
if (rc != 0) {
continue;
}
rc = dns_response(&rx_buf, &response_type, counter);
printf("[%s:%d] DNS response: %d\n", __func__, __LINE__, rc);
} while (1);
printf("Bye!\n");
}
void main(void)
{
net_init();
task_fiber_start(stack, STACK_SIZE, (nano_fiber_entry_t)fiber,
0, 0, 7, 0);
}
int dns_query(struct app_buf_t *buf, char *str, uint16_t id,
enum dns_rr_type qtype)
{
return dns_msg_pack_query(buf, str, id, qtype);
}
int dns_response(struct app_buf_t *_buf, int *response_type, int src_id)
{
struct dns_msg_t dns_msg = DNS_MSG_INIT(_buf->buf, _buf->size);
int ptr;
int rc;
int i;
rc = dns_unpack_response_header(&dns_msg, src_id);
if (rc != 0) {
return rc;
}
if (dns_header_qdcount(dns_msg.msg) != 1) {
return -EINVAL;
}
rc = dns_unpack_response_query(&dns_msg);
if (rc != 0) {
return rc;
}
i = 0;
/* the first dname is at 0x0c bytes */
ptr = 0x0c;
while (i < dns_header_ancount(dns_msg.msg)) {
printf("\n****** DNS ANSWER: %d ******\n", i);
rc = dns_unpack_answer(&dns_msg, ptr);
if (rc != 0) {
printf("[%s:%d]\n", __func__, __LINE__);
return rc;
}
switch (dns_msg.response_type) {
case DNS_RESPONSE_IP:
printf("Response: IP address\t\tSize: %d:\t",
dns_msg.response_length);
print_buf(dns_msg.msg + dns_msg.response_position,
dns_msg.response_length);
break;
case DNS_RESPONSE_CNAME_NO_IP:
printf("Response: CNAME NO IP address\t\tSize: %d:\t",
dns_msg.response_length);
print_buf(dns_msg.msg + dns_msg.response_position,
dns_msg.response_length);
printf("CNAME: ");
dns_print_readable_msg_label(dns_msg.response_position,
dns_msg.msg,
dns_msg.msg_size);
ptr = dns_msg.response_position;
break;
case DNS_RESPONSE_CNAME_WITH_IP:
printf("Response: CNAME WITH IP addr\t\tSize: %d:\t",
dns_msg.response_length);
break;
}
dns_msg.answer_offset = dns_msg.answer_offset + 12
+ dns_msg.response_length;
++i;
}
*response_type = dns_msg.response_type;
return 0;
}

View file

@ -0,0 +1,223 @@
/*
* 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 <netz.h>
#include <net/net_core.h>
#include <net/net_socket.h>
#include <string.h>
#include <errno.h>
void netz_host(struct netz_ctx_t *ctx, struct net_addr *host)
{
return netz_host_ipv4(ctx, host->in_addr.in4_u.u4_addr8[0],
host->in_addr.in4_u.u4_addr8[1],
host->in_addr.in4_u.u4_addr8[2],
host->in_addr.in4_u.u4_addr8[3]);
}
void netz_host_ipv4(struct netz_ctx_t *ctx, uint8_t a1, uint8_t a2,
uint8_t a3, uint8_t a4)
{
uip_ipaddr_t host_addr;
uip_ipaddr(&host_addr, a1, a2, a3, a4);
uip_sethostaddr(&host_addr);
ctx->host.in_addr.in4_u.u4_addr8[0] = a1;
ctx->host.in_addr.in4_u.u4_addr8[1] = a2;
ctx->host.in_addr.in4_u.u4_addr8[2] = a3;
ctx->host.in_addr.in4_u.u4_addr8[3] = a4;
ctx->host.family = AF_INET;
}
void netz_netmask(struct netz_ctx_t *ctx, struct net_addr *netmask)
{
return netz_netmask_ipv4(ctx, netmask->in_addr.in4_u.u4_addr8[0],
netmask->in_addr.in4_u.u4_addr8[1],
netmask->in_addr.in4_u.u4_addr8[2],
netmask->in_addr.in4_u.u4_addr8[3]);
}
void netz_netmask_ipv4(struct netz_ctx_t *ctx, uint8_t n1, uint8_t n2,
uint8_t n3, uint8_t n4)
{
ARG_UNUSED(ctx);
uip_ipaddr_t netmask;
uip_ipaddr(&netmask, n1, n2, n3, n4);
uip_setnetmask(&netmask);
}
void netz_remote(struct netz_ctx_t *ctx, struct net_addr *remote, int port)
{
return netz_remote_ipv4(ctx, remote->in_addr.in4_u.u4_addr8[0],
remote->in_addr.in4_u.u4_addr8[1],
remote->in_addr.in4_u.u4_addr8[2],
remote->in_addr.in4_u.u4_addr8[3], port);
}
void netz_remote_ipv4(struct netz_ctx_t *ctx, uint8_t a1, uint8_t a2,
uint8_t a3, uint8_t a4, int port)
{
ctx->remote.in_addr.in4_u.u4_addr8[0] = a1;
ctx->remote.in_addr.in4_u.u4_addr8[1] = a2;
ctx->remote.in_addr.in4_u.u4_addr8[2] = a3;
ctx->remote.in_addr.in4_u.u4_addr8[3] = a4;
ctx->remote.family = AF_INET;
ctx->remote_port = port;
}
static int netz_prepare(struct netz_ctx_t *ctx, enum ip_protocol proto)
{
#ifdef CONFIG_NETWORKING_WITH_TCP
struct app_buf_t buf = APP_BUF_INIT(NULL, 0, 0);
int rc;
#endif
ctx->connected = 0;
ctx->proto = proto;
ctx->net_ctx = net_context_get(ctx->proto,
&ctx->remote, ctx->remote_port,
&ctx->host, 0);
if (ctx->net_ctx == NULL) {
return -EINVAL;
}
#ifdef CONFIG_NETWORKING_WITH_TCP
/* workaround to activate the IP stack */
rc = netz_tx(ctx, &buf);
if (rc != 0) {
return rc;
}
#endif
ctx->connected = 1;
return 0;
}
int netz_tcp(struct netz_ctx_t *ctx)
{
return netz_prepare(ctx, IPPROTO_TCP);
}
int netz_udp(struct netz_ctx_t *ctx)
{
return netz_prepare(ctx, IPPROTO_UDP);
}
static void netz_sleep(int sleep_ticks)
{
struct nano_timer timer;
nano_timer_init(&timer, NULL);
nano_fiber_timer_start(&timer, sleep_ticks);
nano_fiber_timer_test(&timer, TICKS_UNLIMITED);
}
static int tcp_tx(struct net_context *ctx, uint8_t *buf, size_t size,
int tx_retry_timeout)
{
struct net_buf *nbuf;
uint8_t *ptr;
int rc;
nbuf = ip_buf_get_tx(ctx);
if (nbuf == NULL) {
return -EINVAL;
}
ptr = net_buf_add(nbuf, size);
memcpy(ptr, buf, size);
ip_buf_appdatalen(nbuf) = size;
do {
rc = net_send(nbuf);
if (rc >= 0) {
ip_buf_unref(nbuf);
return 0;
}
switch (rc) {
case -EINPROGRESS:
netz_sleep(tx_retry_timeout);
break;
case -EAGAIN:
case -ECONNRESET:
netz_sleep(tx_retry_timeout);
break;
default:
ip_buf_unref(nbuf);
return -EIO;
}
} while (1);
return 0;
}
static int tcp_rx(struct net_context *ctx, uint8_t *buf, size_t *read_bytes,
size_t size, int rx_timeout)
{
struct net_buf *nbuf;
int rc;
nbuf = net_receive(ctx, rx_timeout);
if (nbuf == NULL) {
return -EIO;
}
*read_bytes = ip_buf_appdatalen(nbuf);
if (*read_bytes > size) {
*read_bytes = size;
rc = -ENOMEM;
} else {
rc = 0;
}
memcpy(buf, ip_buf_appdata(nbuf), *read_bytes);
ip_buf_unref(nbuf);
return rc;
}
int netz_tx(struct netz_ctx_t *ctx, struct app_buf_t *buf)
{
int rc;
/* We don't evaluate if we are connected. */
rc = tcp_tx(ctx->net_ctx, buf->buf, buf->length,
ctx->tx_retry_timeout);
return rc;
}
int netz_rx(struct netz_ctx_t *ctx, struct app_buf_t *buf)
{
int rc;
if (ctx->connected != 1) {
return -ENOTCONN;
}
rc = tcp_rx(ctx->net_ctx, buf->buf, &buf->length, buf->size,
ctx->rx_timeout);
return rc;
}

View file

@ -0,0 +1,163 @@
/*
* 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 _NETZ_H_
#define _NETZ_H_
#include <net/ip_buf.h>
#include <net/net_core.h>
#include "app_buf.h"
struct __deprecated netz_ctx_t;
struct netz_ctx_t {
struct net_context *net_ctx;
int connected;
int rx_timeout;
int tx_retry_timeout;
struct net_addr host;
int host_port;
struct net_addr remote;
int remote_port;
enum ip_protocol proto;
};
#define TCP_COMMON_TIMEOUT 20
/**
* @brief NETZ_CTX_INIT Initializes a netz context with default values
*/
#define NETZ_CTX_INIT { .rx_timeout = TCP_COMMON_TIMEOUT, \
.tx_retry_timeout = TCP_COMMON_TIMEOUT, \
.net_ctx = NULL, \
.connected = 0, \
.proto = IPPROTO_UDP, \
.host_port = 0, \
.remote_port = 0}
/**
* @brief NET_ADDR_IPV4_INIT Initializes a net_addr structure with
* an IPv4 address specified by a1, a2, a3 and a4
*/
#define NET_ADDR_IPV4_INIT(a1, a2, a3, a4) {.in_addr.in4_u.u4_addr8[0] = (a1),\
.in_addr.in4_u.u4_addr8[1] = (a2),\
.in_addr.in4_u.u4_addr8[2] = (a3),\
.in_addr.in4_u.u4_addr8[3] = (a4),\
.family = AF_INET}
/**
* @brief netz_host Sets the host IPv4 address (no IPv6 support)
* @param ctx netz context structure
* @param host Network address
*/
void __deprecated netz_host(struct netz_ctx_t *ctx, struct net_addr *host);
/**
* @brief netz_host_ipv4 Sets the host IPv4 address (no IPv6 support)
* @param ctx netz context structure
* @param a1 Byte 0 of the IPv4 address
* @param a2 Byte 1 of the IPv4 address
* @param a3 Byte 2 of the IPv4 address
* @param a4 Byte 3 of the IPv4 address
*/
void __deprecated netz_host_ipv4(struct netz_ctx_t *ctx, uint8_t a1,
uint8_t a2, uint8_t a3, uint8_t a4);
/**
* @brief netz_netmask Sets the host's netmask address
* @param ctx netz context structure
* @param netmask Network address to be used as netmask
*/
void __deprecated netz_netmask(struct netz_ctx_t *ctx,
struct net_addr *netmask);
/**
* @brief netz_netmask_ipv4 Sets the host's netmask IPv4 address
* @param ctx netz context structure
* @param n1 Byte 0 of the IPv4 address
* @param n2 Byte 1 of the IPv4 address
* @param n3 Byte 2 of the IPv4 address
* @param n4 Byte 3 of the IPv4 address
*/
void __deprecated netz_netmask_ipv4(struct netz_ctx_t *ctx, uint8_t n1,
uint8_t n2, uint8_t n3, uint8_t n4);
/**
* @brief netz_remote Sets the address of the remote peer
* @param ctx netz context structure
* @param remote Network address of the remote peer
* @param port Port number of the remote peer
*/
void __deprecated netz_remote(struct netz_ctx_t *ctx, struct net_addr *remote,
int port);
/**
* @brief netz_remote_ipv4 Sets the IPv4 address of the remote peer
* @param ctx netz context structure
* @param a1 Byte 0 of the IPv4 address
* @param a2 Byte 1 of the IPv4 address
* @param a3 Byte 2 of the IPv4 address
* @param a4 Byte 3 of the IPv4 address
*/
void __deprecated netz_remote_ipv4(struct netz_ctx_t *ctx, uint8_t a1,
uint8_t a2, uint8_t a3, uint8_t a4,
int port);
/**
* @brief netz_tcp Initializes the netz context & connects
* to the remote peer
* @param ctx netz context structure
* @return 0 on success
* @return -EINVAL if a null context was obtained
* @return Read netz_tx return codes
*/
int __deprecated netz_tcp(struct netz_ctx_t *ctx);
/**
* @brief netz_udp Initializes the context for UDP transfers
* @param ctx netz context structure
* @return 0 on success
* @return -EINVAL if a null context was obtained
*/
int __deprecated netz_udp(struct netz_ctx_t *ctx);
/**
* @brief netz_tx TCP/UDP data transmission
* @param ctx netz context structure
* @param buf Buffer that contains the data to be sent
* @return 0 on success
* @return -EINVAL if no network buffer is available
* @return -EIO if a TCP error was detected
*/
int __deprecated netz_tx(struct netz_ctx_t *ctx, struct app_buf_t *buf);
/**
* @brief netz_rx TCP/UDP data reception
* @param ctx netz context structure
* @param buf Buffer that contains the received data
* @return 0 on success
* @return -EIO on TCP or network buffer error
* @return -ENOMEM if the space in buf is not enough
* to store the received data
*/
int __deprecated netz_rx(struct netz_ctx_t *ctx, struct app_buf_t *buf);
#endif

View file

@ -0,0 +1,5 @@
[test]
tags = net
build_only = true
arch_whitelist = x86
platform_whitelist = galileo