--- /dev/null
+/*
+ * ldnsd. Light-weight DNS daemon
+ *
+ * Tiny dns server to show how a real one could be built.
+ *
+ * (c) NLnet Labs, 2005
+ * See the file LICENSE for the license
+ */
+
+#include "config.h"
+#include <ldns/dns.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <netinet/igmp.h>
+
+#include <errno.h>
+
+#define INBUF_SIZE 4096
+
+void usage(FILE *output)
+{
+ fprintf(output, "Usage: ldnsd <port> <zone> <zonefile>\n");
+ fprintf(output, "Listens on the specified port and answers queries for the given zone\n");
+ fprintf(output, "This is NOT a full-fledged authoritative nameserver!\n");
+}
+
+static int udp_bind(int sock, int port, const char *my_address)
+{
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons((uint16_t)port);
+ addr.sin_addr.s_addr = INADDR_ANY;
+// if (join_mcast(sock, &addr) == -1) { return -1; }
+/*
+ if (join_group(sock, inet_addr(my_address), INADDR_ANY)) {
+ perror("setsockopt");
+ } else {
+ fprintf(stderr, "Ok.\n");
+ }
+*/
+ return bind(sock, (struct sockaddr *)&addr, (socklen_t) sizeof(addr));
+}
+
+/* this will probably be moved to a better place in the library itself */
+ldns_rr_list *
+get_rrset(const ldns_zone *zone, const ldns_rdf *owner_name, const ldns_rr_type qtype, const ldns_rr_class qclass)
+{
+ uint16_t i;
+ ldns_rr_list *rrlist = ldns_rr_list_new();
+ ldns_rr *cur_rr;
+ if (!zone || !owner_name) {
+ fprintf(stderr, "Warning: get_rrset called with NULL zone or owner name\n");
+ return rrlist;
+ }
+
+ for (i = 0; i < ldns_zone_rr_count(zone); i++) {
+ cur_rr = ldns_rr_list_rr(ldns_zone_rrs(zone), i);
+ if (ldns_dname_compare(ldns_rr_owner(cur_rr), owner_name) == 0 &&
+ ldns_rr_get_class(cur_rr) == qclass &&
+ ldns_rr_get_type(cur_rr) == qtype
+ ) {
+ ldns_rr_list_push_rr(rrlist, ldns_rr_clone(cur_rr));
+ }
+ }
+
+ printf("Found rrset of %u rrs\n", (unsigned int) ldns_rr_list_rr_count(rrlist));
+
+ return rrlist;
+}
+
+int
+main(int argc, char **argv)
+{
+ /* arguments */
+ int port;
+ const char *zone_name;
+ const char *zone_file;
+
+ /* network */
+ int sock;
+ size_t nb;
+ struct sockaddr addr_me;
+ struct sockaddr addr_him;
+ socklen_t hislen;
+ const char *my_address;
+ uint8_t inbuf[INBUF_SIZE];
+ uint8_t *outbuf;
+
+ /* dns */
+ ldns_status status;
+ ldns_pkt *query_pkt;
+ ldns_pkt *answer_pkt;
+ size_t answer_size;
+ ldns_rr *query_rr;
+ ldns_rr_list *answer_qr;
+ ldns_rr_list *answer_an;
+ ldns_rr_list *answer_ns;
+ ldns_rr_list *answer_ad;
+
+ /* zone */
+ ldns_zone *zone;
+ int line_nr;
+ FILE *zone_fp;
+
+ /* use this to listen on specified interfaces later? */
+ my_address = NULL;
+
+ if (argc < 4) {
+ usage(stdout);
+ exit(EXIT_FAILURE);
+ } else {
+ port = atoi(argv[1]);
+ if (port < 1) {
+ usage(stdout);
+ }
+ zone_name = argv[2];
+ zone_file = argv[3];
+ }
+
+ printf("Reading zone file %s\n", zone_file);
+ zone_fp = fopen(zone_file, "r");
+ if (!zone_fp) {
+ fprintf(stderr, "Unable to open %s: %s\n", zone_file, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ line_nr = 0;
+ zone = ldns_zone_new_frm_fp_l(zone_fp, NULL, 0, LDNS_RR_CLASS_IN, &line_nr);
+
+ if (!zone) {
+ printf("Zone reader failed, aborting\n");
+ exit(EXIT_FAILURE);
+ } else {
+ printf("Read %u resource records in zone file\n", (unsigned int) ldns_zone_rr_count(zone));
+ }
+
+ fclose(zone_fp);
+
+
+ printf("Listening on port %d\n", port);
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ fprintf(stderr, "%s: socket(): %s\n", argv[0], strerror(errno));
+ exit(1);
+ }
+
+ memset(&addr_me, 0, sizeof(addr_me));
+
+ /* bind: try all ports in that range */
+ if (udp_bind(sock, port, my_address)) {
+ fprintf(stderr, "%s: cannot bind(): %s\n", argv[0], strerror(errno));
+ }
+
+ /* Done. Now receive */
+ while (1) {
+ nb = (size_t) recvfrom(sock, inbuf, INBUF_SIZE, 0, &addr_him, &hislen);
+ if (nb < 1) {
+ fprintf(stderr, "%s: recvfrom(): %s\n",
+ argv[0], strerror(errno));
+ exit(1);
+ }
+
+ /*
+ show(inbuf, nb, nn, hp, sp, ip, bp);
+ */
+
+ printf("Got query of %u bytes\n", (unsigned int) nb);
+ status = ldns_wire2pkt(&query_pkt, inbuf, nb);
+ if (status != LDNS_STATUS_OK) {
+ printf("Got bad packet: %s\n", ldns_get_errorstr_by_id(status));
+ } else {
+ ldns_pkt_print(stdout, query_pkt);
+ }
+
+ query_rr = ldns_rr_list_rr(ldns_pkt_question(query_pkt), 0);
+ printf("QUERY RR: \n");
+ ldns_rr_print(stdout, query_rr);
+
+ answer_qr = ldns_rr_list_new();
+ ldns_rr_list_push_rr(answer_qr, ldns_rr_clone(query_rr));
+
+ answer_an = get_rrset(zone, ldns_rr_owner(query_rr), ldns_rr_get_type(query_rr), ldns_rr_get_class(query_rr));
+ answer_pkt = ldns_pkt_new();
+
+ answer_ns = ldns_rr_list_new();
+
+ answer_ad = ldns_rr_list_new();
+
+ ldns_pkt_set_qr(answer_pkt, 1);
+ ldns_pkt_set_aa(answer_pkt, 1);
+ ldns_pkt_set_id(answer_pkt, ldns_pkt_id(query_pkt));
+
+ /* aren't there push_rr(section) functions? */
+ /* and why isn't the count automatically updated? */
+ ldns_pkt_set_question(answer_pkt, answer_qr);
+ ldns_pkt_set_qdcount(answer_pkt, ldns_rr_list_rr_count(answer_qr));
+ ldns_pkt_set_answer(answer_pkt, answer_an);
+ ldns_pkt_set_ancount(answer_pkt, ldns_rr_list_rr_count(answer_an));
+ ldns_pkt_set_authority(answer_pkt, answer_ns);
+ ldns_pkt_set_nscount(answer_pkt, ldns_rr_list_rr_count(answer_ns));
+ ldns_pkt_set_additional(answer_pkt, answer_ad);
+ ldns_pkt_set_arcount(answer_pkt, ldns_rr_list_rr_count(answer_ad));
+
+ status = ldns_pkt2wire(&outbuf, answer_pkt, &answer_size);
+
+ printf("Answer packet size: %u bytes.\n", (unsigned int) answer_size);
+ if (status != LDNS_STATUS_OK) {
+ printf("Error creating answer: %s\n", ldns_get_errorstr_by_id(status));
+ } else {
+ nb = (size_t) sendto(sock, outbuf, answer_size, 0, &addr_him, hislen);
+ }
+
+
+
+ }
+
+ return 0;
+}