spcdns

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