spcdns

Category: c library
Tags: C library

DNS resolving accourding to the spec.

History

Created by Sean Connor, who was borred on a Thanksgiving.

FAQ

What is it?

DNS Resolver

How cool is it?

Rather.

Competitors?

c-ares, atdns, udns

When to use it?

When you want dns resolving, and don't mind lgpl.

Is it dead?

No, it seems to be rather complete.

Where is it?

http://www.conman.org/software/spcdns/

How to use it

The documentation and the examples are rather good.

Download

file: 1_download.sh

#!/bin/bash

mkdir -p ./tmp
cd ./tmp
wget -q https://github.com/spc476/SPCDNS/archive/v1.0.10.tar.gz
tar -xaf v1.0.10.tar.gz
cd SPCDNS-1.0.10
make built/libspcdns.a

Init

The great thing is that that you can use name groups directly. Here is one examples, albeit a little modified.

file: main.c

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <errno.h>

#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <poll.h>

#include <dns.h>
#include <mappings.h>

typedef union sockaddr_all {
    struct sockaddr sa;
    struct sockaddr_in sin;
    struct sockaddr_in6 sin6;
} sockaddr_all;

int net_server( sockaddr_all *const restrict addr, const char *const restrict host) {
    memset(addr, 0, sizeof(sockaddr_all));
    if (inet_pton(AF_INET, host, &addr->sin.sin_addr.s_addr) < 0) {
        if (inet_pton(AF_INET6, host, &addr->sin6.sin6_addr.s6_addr) < 0) {
            return errno;
        }
        addr->sin6.sin6_family = AF_INET6;
        addr->sin6.sin6_port = htons(53);
    } else {
        addr->sin.sin_family = AF_INET;
        addr->sin.sin_port = htons(53);
    }

    return 0;
}

int net_request( sockaddr_all *const restrict srvaddr, dns_packet_t *const restrict dest, size_t *const restrict dsize, const dns_packet_t *const restrict src, const size_t ssize) {
    struct pollfd polldat;
    socklen_t asize;
    ssize_t bytes;
    int sock;
    int rc;
    int err;

    switch(srvaddr->sa.sa_family) {
        case AF_INET: asize = sizeof(struct sockaddr_in);  break;
        case AF_INET6: asize = sizeof(struct sockaddr_in6); break;
        default: return EPROTOTYPE;
    }

    sock = socket(srvaddr->sa.sa_family, SOCK_DGRAM, 0);
    if (sock < 0) {
        return errno;
    }

    bytes = sendto(sock, src, ssize, 0, &srvaddr->sa, asize);
    if (bytes < 0) {
        err = errno;
        close(sock);

        return err;
    }

    polldat.fd = sock;
    polldat.events = POLLIN;

    rc = poll(&polldat, 1, 15000);
    //this is where you would use a event loop
    if (rc < 0) {
        err = errno;
        close(sock);

        return err;
    }

    if (rc == 0) {
        close(sock);

        return ETIME;
    }

    bytes = recvfrom(sock, dest, *dsize, 0, NULL, NULL);
    if (bytes < 0) {
        int err = errno;
        close(sock);

        return err;
    }

    *dsize = bytes;
    close(sock);

    return 0;
}

int main(const int argc, const char **argv) {
    const char *serverhost = "8.8.8.8";
    const char *host = "www.groksome.com";
    const char *type = "A";
    bool fedns = 1;

    dns_question_t domain;
    dns_query_t    query;
    dns_packet_t   request[DNS_BUFFER_UDP];
    edns0_opt_t    opt;
    dns_answer_t   edns;

    domain.name  = host;
    domain.type  = dns_type_value(type);
    domain.class = CLASS_IN;

    query.id          = 1234;	/* should be a random value */
    query.query       = true;
    query.opcode      = OP_QUERY;
    query.aa          = false;
    query.tc          = false;
    query.rd          = true;
    query.ra          = false;
    query.ad          = false;
    query.cd          = false;
    query.rcode       = RCODE_OKAY;
    query.qdcount     = 1;
    query.questions   = &domain;
    query.ancount     = 0;
    query.answers     = NULL;
    query.nscount     = 0;
    query.nameservers = NULL;
    query.arcount     = 0;
    query.additional  = NULL;

    if (fedns) {
        opt.code = EDNS0RR_NSID;
        opt.data = (uint8_t *)"MY-DNS-SERVER-ID";
        opt.len  = strlen((char *)opt.data);

        edns.opt.name        = ".";
        edns.opt.type        = RR_OPT;
        edns.opt.class       = CLASS_UNKNOWN;
        edns.opt.ttl         = 0;
        edns.opt.udp_payload = 1464;
        edns.opt.version     = 0;
        edns.opt.fdo         = false;
        edns.opt.numopts     = 1;
        edns.opt.opts        = &opt;

        query.arcount    = 1;
        query.additional = &edns;
    }

    size_t reqsize = sizeof(request);
    int rc = dns_encode(request, &reqsize, &query);
    if (rc != RCODE_OKAY) {
        fprintf(stderr,"dns_encode() = (%d) %s\n", rc, dns_rcode_text(rc));
        return 1;
    }

    size_t replysize;
    sockaddr_all server;
    dns_packet_t reply[DNS_BUFFER_UDP];

    rc = net_server(&server, serverhost);
    if (rc != 0) {
        fprintf(stderr, "net_server() = %s", strerror(rc));

        return 1;
    }

    replysize = sizeof(reply);
    if (net_request(&server, reply, &replysize, request, reqsize) < 0) {
        fprintf(stderr,"failure\n");
        return 1;
    }
    dns_decoded_t bufresult[DNS_DECODEBUF_8K];
    dns_query_t * result = (dns_query_t *)bufresult;
    printf( "; Questions            = %lu\n"
            "; Answers              = %lu\n"
            "; Name Servers         = %lu\n"
            "; Additional Records   = %lu\n"
            "; Authoritative Result = %s\n"
            "; Truncated Result     = %s\n"
            "; Recursion Desired    = %s\n"
            "; Recursion Available  = %s\n"
            "; Result               = %s\n",
            (unsigned long)result->qdcount,
            (unsigned long)result->ancount,
            (unsigned long)result->nscount,
            (unsigned long)result->arcount,
            result->aa ? "true" : "false",
            result->tc ? "true" : "false",
            result->rd ? "true" : "false",
            result->ra ? "true" : "false",
            dns_rcode_text(result->rcode)
    );

    return 0;

}

file: 2_compile_and_run.sh

#!/bin/bash

gcc -std=c99 \
    -o ./tmp/main \
    main.c \
    -I./tmp/SPCDNS-1.0.10/src \
    ./tmp/SPCDNS-1.0.10/built/libspcdns.a  -lm
./tmp/main

Further reading:

We only tipped the top of the iceberg here. There is much more:

http://www.apps.ietf.org/rfc/rfc1035.txt