Skip to content

Internet of Threads (IoTh) domain name system tools/lib

Notifications You must be signed in to change notification settings

virtualsquare/iothdns

Repository files navigation

iothdns

Name Resolution support library for the Internet of Threads.

The domain name resolution functions provided by the C library use the TCP-IP stack implemented in the Linux kernel. They are thus unsuitable to support user level implemented stacks like those provided by libioth.

This library provides support for:

  • Client programs that need to query DNS servers
  • DNS servers, forwarders, filters that need to parse DNS queries, compose and send back appropriate replies

Compile and Install

Pre-requisites: libioth (and iothconf only for some examples, not for the library itself)

iothdns uses cmake. The standard building/installing procedure is:

mkdir build
cd build
cmake ..
make
sudo make install

An uninstaller is provided for your convenience. In the build directory run:

sudo make uninstall

The API

ioth configuration

The library must be initialized.The functions iothdns_init and iothdns_init_strcfg allow the user to choose the ioth stack to use and to set the configuration parameters using the same syntax of resolv.conf(5). The configuration can be provided as a pathname of a file (iothdns_init) or as a string (iothdns_init_strcfg).

struct iothdns *iothdns_init(struct ioth *stack, char *path_config);
struct iothdns *iothdns_init_strcfg(struct ioth *stack, char *config);
int iothdns_update(struct iothdns *iothdns, char *path_config);
int iothdns_update_strcfg(struct iothdns *iothdns, char *config);

void iothdns_fini(struct iothdns *iothdns);

(iothdns_init uses /etc/resolv.conf if path_config is NULL). iothdns_init and iothdns_init_strcfg return a iothdns descriptor used in many functions of this API. In case of error NULL is returned and errno provides a description of the error.

iothdns_fini closes the iothdns descriptor and deallocates its data structures.

iothdns_update and iothdns_update_strcfg update the DNS resolution configuration. These functions return 0 in case of success and -1 in case of error (using errno to identify the error encountered).

enum iothdns_pathtag {IOTHDNS_HOSTS, IOTHDNS_SERVICES, ...};
void iothdns_setpath(struct iothdns *iothdns, enum iothdns_pathtag pathtag, char *newvalue);
int iothdns_getpath(struct iothdns *iothdns, enum iothdns_pathtag pathtag, char *buf, size_t size);

The domain name resolution functions provided by the C library use some system provided files like /etc/hosts and /etc/services. iothdns_setpath allows users to redefine files to be used instead of the system provided ones.

ioth configuration examples

define the iothdns descriptor idd to use the kernel stack and /etc/resolv.conf

#include <iothdns.h>
struct iothdns *idd = iothdns_init(NULL, NULL);
...
iothdns_fini(idd);

define idd to use the kernel stack but a different configuration provided as a string

#include <iothdns.h>
struct iothdns *idd = iothdns_init_strcfg(NULL,
  "search my.domain.org\n"
  "nameserver 80.80.80.80");
...
iothdns_fini(idd);

define idd to use the kernel stack, simple string configuration (list of DNS IP addr)

#include <iothdns.h>
struct iothdns *idd = iothdns_init_strcfg(NULL, "8.8.8.8,80.80.80.80,2620:0:ccc::2");
...
iothdns_fini(idd);

idd uses a vdestack user level stack, whose virtual interface vde0 is connected to the VDE net vxvde://234.0.0.1.   vde0's IP address is 10.0.0.53/24, default gateway is 10.0.0.1. DNS is 1.1.1.1 (this example needs iothconf)

#include <ioth.h>
#include <iothconf.h>
#include <iothdns.h>
struct ioth *stack = ioth_newstack("vdestack", "vxvde://234.0.0.1");
ioth_config(stack, "eth,ip=10.0.0.53/24,gw=10.0.0.1");
struct iothdns *idd = iothdns_init_strcfg(stack, "nameserver 1.1.1.1");
...
iothdns_fini(idd);

user provided hosts/services files

iothdns_setpath(IOTHDNS_HOSTS, "~/.myetc/hosts");
iothdns_setpath(IOTHDNS_SERVICES, "~/.myetc/services");

high level API: client queries

iothdns_getaddrinfo and iothdns_getnameinfo are the iothdns counterparts of getaddrinfo(3) and getnameinfo(3). The only difference in the functions' signature is the heading iothdns descriptor.

int iothdns_getaddrinfo(struct iothdns *iothdns,
    const char *node, const char *service,
    const struct addrinfo *hints,
    struct addrinfo **res);

void iothdns_freeaddrinfo(struct addrinfo *res);

const char *iothdns_gai_strerror(int errcode);

int iothdns_getnameinfo(struct iothdns *iothdns,
    const struct sockaddr *addr, socklen_t addrlen,
    char *host, socklen_t hostlen,
    char *serv, socklen_t servlen, int flags);

Note: the implementation provides a support for the most common uses, not all the special cases of getaddrinfo(3) and getnameinfo(3) are supported yet.

mid level API: client queries

iothdns provides some functions to query for IPv4 or IPv6 addresses.

int iothdns_lookup_a(struct iothdns *iothdns, const char *name, struct in_addr *a, int n);
int iothdns_lookup_aaaa(struct iothdns *iothdns, const char *name, struct in6_addr *aaaa, int n);
int iothdns_lookup_aaaa_compat(struct iothdns *iothdns, const char *name, struct in6_addr *aaaa, int n);

where iothdns is the iothdns descriptor, name is the name of the host to query for, a or aaaa is the pointer to a buffer for one more IP (v4 or v6) addresses, n is the number of addresses that a or aaaa can host. The return value is:

  • -1: in case of error (e.g. errno is ENOENT in case of a non-existent name)
  • 0: if the name is valid but there is not an IP address defined for it
  • > 0: the return value is the number of IP addresses defined for the queried name. the heading n addresses are stored in the a or aaaa buffer (if the returned value is greater than n the remaining addresses are dropped).

iothdns_lookup_aaaa_compat returns IPv6 and IPv4 (compat mode, e.g. ::ffff:1.2.3.4) addresses, IPv6 addresses first, and then IPv4 addresses. (if n is 1, it gets an IPv6 address, if any. Otherwise it returns an IPv4 address.)

When iothdns_lookup_* functions get a numeric address no dns query is generated and the address is converted to struct in_addr or struct in6_addr.

low level API: client side

The structure of DNS protocol messages is defined by RFC 1035. Each message is composed by a header and 4 sections. Each section includes a sequence of resource records (RR):

    +---------------------+
    |        Header       |
    +---------------------+
    |       Question      | the question for the name server
    +---------------------+
    |        Answer       | RRs answering the question
    +---------------------+
    |      Authority      | RRs pointing toward an authority
    +---------------------+
    |      Additional     | RRs holding additional information
    +---------------------+

All sections can be empty except the Question which (in practice) always contains one element.

typedef int lookup_cb_t(int section, struct iothdns_rr *rr, struct iothdns_pkt *vpkt, void *arg);
int iothdns_lookup_cb(struct iothdns *iothdns, const char *name, int qtype,
    lookup_cb_t *lookup_cb, void *arg);
int iothdns_lookup_cb_tcp(struct iothdns *iothdns, const char *name, int qtype,
    lookup_cb_t *lookup_cb, void *arg);

These functions support general purpose queries. iothdns_lookup_cb and iothdns_lookup_cb_tcp perform a query using UDP or TCP respectively. If a server returns a reply message the callback function lookup_cb is called once for each RR in the Answer, Authority and Additional sections. The section number and the header of the resource record are passed to lookup_cb as arguments, specific payloads can be parsed using the packet parsing iothdns_get... functions on the vpkt parameter (see below). If the callback function returns 0 the scan continues otherwise terminates.

low level API: server side

typedef struct iothdns_pkt *parse_request_t(struct iothdns_header *h, void *arg);
int iothdns_udp_process_request(int fd, parse_request_t *parse_request, void *arg);
int iothdns_tcp_process_request(int fd, parse_request_t *parse_request, void *arg);

iothdns_udp_process_request and iothdns_tcp_process_request read the data available on the socket fd and call the callback parse_request for each query, then they send back the reply. The callback argument h includes all the data from the DNS query header and question section. The return value of parse_request is the message to be sent back as the reply, this message is created using the packet composing iothdns_put... functions (see below).

very low level API: parsing RFC1035 messages

struct iothdns_pkt *iothdns_get_header(struct iothdns_header *h, void *buf, size_t bufsize, char *qnamebuf);
int iothdns_get_rr(struct iothdns_pkt *vpkt, struct iothdns_rr *rr, char *namebuf);
uint8_t iothdns_get_int8(struct iothdns_pkt *vpkt);
uint16_t iothdns_get_int16(struct iothdns_pkt *vpkt);
uint32_t iothdns_get_int32(struct iothdns_pkt *vpkt);
void *iothdns_get_data(struct iothdns_pkt *vpkt, void *data, uint16_t len);
char *iothdns_get_name(struct iothdns_pkt *vpkt, char *name);
char *iothdns_get_string(struct iothdns_pkt *vpkt, char *name);
void *iothdns_get_a(struct iothdns_pkt *vpkt, void *addr_ipv4);
void *iothdns_get_aaaa(struct iothdns_pkt *vpkt, void *addr_ipv6);
void iothdns_free(struct iothdns_pkt *vpkt);

iothdns_get_header parses the header and question section of the message. qnamebuf is a temporary buffer (whose length is IOTHDNS_MAXNAME) to store the query name. iothdns_get_header returns a handler that can be used to get the other RRs (iothdns_get_rr) and the resource records' arguments (by the other iothdns_get... functions). All the names returned in iothdns_get_header, iothdns_get_rr and iothdns_get_name are strings containing fully qualified domain names. The parsing of the compression defined in section 4.1.4 of RFC1035 is automatic.

very low level API: composing RFC1035 messages

struct iothdns_pkt *iothdns_put_header(struct iothdns_header *h);
void iothdns_put_rr(int section, struct iothdns_pkt *vpkt, struct iothdns_rr *rr);
void iothdns_put_int8(struct iothdns_pkt *vpkt, uint8_t data);
void iothdns_put_int16(struct iothdns_pkt *vpkt, uint16_t data);
void iothdns_put_int32(struct iothdns_pkt *vpkt, uint32_t data);
void iothdns_put_data(struct iothdns_pkt *vpkt, void *data, uint16_t len);
void iothdns_put_name(struct iothdns_pkt *vpkt, const char *name);
void iothdns_put_name_uncompressed(struct iothdns_pkt *vpkt, const char *name);
void iothdns_put_string(struct iothdns_pkt *vpkt, char *string);
void iothdns_put_a(struct iothdns_pkt *vpkt, void *addr_ipv4);
void iothdns_put_aaaa(struct iothdns_pkt *vpkt, void *addr_ipv6);
struct iovec iothdns_getbuf(struct iothdns_pkt *vpkt);
void iothdns_free(struct iothdns_pkt *vpkt);

iothdns_put_header creates a message containing the header and the question section from the fields of h. It returns a handler that can be used to add all the other RRs (iothdns_put_rr) and the resource records' arguments (by the other iothdns_put...functions). Warning: RRs must be added in non-decreasing sorting of sections. A RR whose section is smaller than the section of the previous RR is silently discarded.

The length of the entire message as well as the length of each resource record are automatically computed and inserted in the correspodnent fields of the message. Moreover all the names added by iothdns_put_header, iothdns_put_rr and iothdns_put_name are automatically compressed using the method defined RFC1035, section 4.1.4. The function iothdns_getbuf has been designed to be used in functions like send(2), sendto(2), sendmsg(2), write(2) or writev(2) to send the composed message to the other end (to the server if it is a query, to the client if it is a reply).

Some tutorial examples

Simple Query:

test_query.c is a simple example: it uses the networking stack of the kernel but it redefines the configuration data. It tests iothdns_lookup_a, iothdns_lookup_aaaa and iothdns_lookup_cb_tcp.

$ gcc -o test_query test_query.c -liothdns
$ ./test_query mad.cs.unibo.it
1 130.136.5.6
1 2001:760:2e00:f005:226:b9ff:fe77:51b
section 1 qtype 5 mad.cs.unibo.it
cname maddalena.cs.unibo.it

A query using a user level stack

test_iothq.c is an evolution of the previous example. It uses a user level implementation of a stack. This example needs iothconf.

In the following usage example test_iothq uses a vdestack connected to a slirp emulator. The interface address is provided via dhcp.

$ gcc -o test_iothq test_iothq.c -liothdns -liothconf -lioth
$ ./test_iothq mad.cs.unibo.it vdestack slirp:// eth,dhcp
1 130.136.5.6
1 2001:760:2e00:f005:226:b9ff:fe77:51b
section 1 qtype 5 mad.cs.unibo.it
cname maddalena.cs.unibo.it

A minimal DNS server

test_server.c implements a minimal DNS server able to provide an A record (10.20.30.40) for the query test.fake. The server returns a name error for all the other queries. This server processes both UDP and TCP incoming queries.

The following usage example is based on vdens.

t$ gcc -o test_server test_server.c -liothdns -lioth
$ vdens
$# ip link set lo up
$# ./test_server &
[1] 705
$# host test.fake 127.0.0.1
Using domain server:
Name: 127.0.0.1
Address: 127.0.0.1#53
Aliases:

test.fake has address 10.20.30.40
$# klll %1
[1]+  Terminated              ./test_server
$# exit
$

getaddrinfo and getnameinfo

test_gai.c tests the iothdns implementation of getaddrinfo and getnameinfo.

The following command line interaction shows the usage banner of test_gai and some run. Each query is tested with and without '-x', i.e. comparing iothdns_get*info with glibc's get*info.

t$ gcc -o test_gai test_gai.c -liothdns
$ ./test_gai
Usage: ./test_gai [options] host port
        -r --rev
        -R resolvconf_file     --resolvconf resolvconf_file
        -x --native
        -4 --ipv4
        -6 --ipv6
        -d --dgram
        -s --stream
        -m --v4mapped
        -a --all
        -p --passive
        -c --canonname
        -n --numerichost
        -N --numericserv
        -e --errnoname
$ ./test_gai mad.cs.unibo.it domain
IPv4 SOCK_STREAM addr: 130.136.5.6 port 53 proto 6
IPv4 SOCK_DGRAM addr: 130.136.5.6 port 53 proto 17
IPv6 SOCK_STREAM addr: 2001:760:2e00:f005:226:b9ff:fe77:51b port 53 proto 6
IPv6 SOCK_DGRAM addr: 2001:760:2e00:f005:226:b9ff:fe77:51b port 53 proto 17
$ ./test_gai -x mad.cs.unibo.it domain
IPv4 SOCK_STREAM addr: 130.136.5.6 port 53 proto 6
IPv4 SOCK_DGRAM addr: 130.136.5.6 port 53 proto 17
IPv6 SOCK_STREAM addr: 2001:760:2e00:f005:226:b9ff:fe77:51b port 53 proto 6
IPv6 SOCK_DGRAM addr: 2001:760:2e00:f005:226:b9ff:fe77:51b port 53 proto 17
$ ./test_gai multiip.v2.cs.unibo.it ssh
IPv4 SOCK_STREAM addr: 130.136.200.90 port 22 proto 6
IPv4 SOCK_STREAM addr: 130.136.200.92 port 22 proto 6
IPv4 SOCK_STREAM addr: 130.136.200.91 port 22 proto 6
IPv6 SOCK_STREAM addr: 2001:760:2e00:ff00::5a port 22 proto 6
IPv6 SOCK_STREAM addr: 2001:760:2e00:ff00::5c port 22 proto 6
IPv6 SOCK_STREAM addr: 2001:760:2e00:ff00::5b port 22 proto 6
$ ./test_gai -x multiip.v2.cs.unibo.it ssh
IPv4 SOCK_STREAM addr: 130.136.200.90 port 22 proto 6
IPv4 SOCK_STREAM addr: 130.136.200.91 port 22 proto 6
IPv4 SOCK_STREAM addr: 130.136.200.92 port 22 proto 6
IPv6 SOCK_STREAM addr: 2001:760:2e00:ff00::5c port 22 proto 6
IPv6 SOCK_STREAM addr: 2001:760:2e00:ff00::5a port 22 proto 6
IPv6 SOCK_STREAM addr: 2001:760:2e00:ff00::5b port 22 proto 6
$ ./test_gai -r 130.136.5.6 22
host=maddalena.cs.unibo.it, serv=ssh
$ ./test_gai -rx 130.136.5.6 22
host=maddalena.cs.unibo.it, serv=ssh

About

Internet of Threads (IoTh) domain name system tools/lib

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published