summaryrefslogtreecommitdiff
path: root/test/mock/dnssrv.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/mock/dnssrv.c')
-rw-r--r--test/mock/dnssrv.c245
1 files changed, 245 insertions, 0 deletions
diff --git a/test/mock/dnssrv.c b/test/mock/dnssrv.c
new file mode 100644
index 0000000..e5ac188
--- /dev/null
+++ b/test/mock/dnssrv.c
@@ -0,0 +1,245 @@
+/**
+ * @file mock/dnssrv.c Mock DNS server
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+#include <string.h>
+#include <re.h>
+#include "../test.h"
+
+
+#define DEBUG_MODULE "mock/dnssrv"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+#define LOCAL_PORT 0
+
+
+static void dns_server_match(struct dns_server *srv, struct list *rrl,
+ const char *name, uint16_t type)
+{
+ struct dnsrr *rr0 = NULL;
+ struct le *le;
+
+ le = srv->rrl.head;
+ while (le) {
+
+ struct dnsrr *rr = le->data;
+ le = le->next;
+
+ if (type == rr->type && 0==str_casecmp(name, rr->name)) {
+
+ if (!rr0)
+ rr0 = rr;
+ list_append(rrl, &rr->le_priv, rr);
+ }
+ }
+
+ /* If rotation is enabled, then rotate multiple entries
+ in a deterministic way (no randomness please) */
+ if (srv->rotate && rr0) {
+ list_unlink(&rr0->le);
+ list_append(&srv->rrl, &rr0->le, rr0);
+ }
+}
+
+
+static void decode_dns_query(struct dns_server *srv,
+ const struct sa *src, struct mbuf *mb)
+{
+ struct list rrl = LIST_INIT;
+ struct dnshdr hdr;
+ struct le *le;
+ char *qname = NULL;
+ size_t start, end;
+ uint16_t type, dnsclass;
+ int err = 0;
+
+ start = mb->pos;
+ end = mb->end;
+
+ if (dns_hdr_decode(mb, &hdr) || hdr.qr || hdr.nq != 1) {
+ DEBUG_WARNING("unable to decode query header\n");
+ return;
+ }
+
+ err = dns_dname_decode(mb, &qname, start);
+ if (err) {
+ DEBUG_WARNING("unable to decode query name\n");
+ goto out;
+ }
+
+ if (mbuf_get_left(mb) < 4) {
+ err = EBADMSG;
+ DEBUG_WARNING("unable to decode query type/class\n");
+ goto out;
+ }
+
+ type = ntohs(mbuf_read_u16(mb));
+ dnsclass = ntohs(mbuf_read_u16(mb));
+
+ DEBUG_INFO("dnssrv: type=%s query-name='%s'\n",
+ dns_rr_typename(type), qname);
+
+ if (dnsclass == DNS_CLASS_IN) {
+ dns_server_match(srv, &rrl, qname, type);
+ }
+
+ hdr.qr = true;
+ hdr.tc = false;
+ hdr.rcode = DNS_RCODE_OK;
+ hdr.nq = 1;
+ hdr.nans = list_count(&rrl);
+
+ mb->pos = start;
+
+ err = dns_hdr_encode(mb, &hdr);
+ if (err)
+ goto out;
+
+ mb->pos = end;
+
+ DEBUG_INFO("dnssrv: @@ found %u answers for %s\n",
+ list_count(&rrl), qname);
+
+ for (le = rrl.head; le; le = le->next) {
+ struct dnsrr *rr = le->data;
+
+ err = dns_rr_encode(mb, rr, 0, NULL, start);
+ if (err)
+ goto out;
+ }
+
+ mb->pos = start;
+
+ (void)udp_send(srv->us, src, mb);
+
+ out:
+ list_clear(&rrl);
+ mem_deref(qname);
+}
+
+
+static void udp_recv(const struct sa *src, struct mbuf *mb, void *arg)
+{
+ struct dns_server *srv = arg;
+
+ decode_dns_query(srv, src, mb);
+}
+
+
+static void destructor(void *arg)
+{
+ struct dns_server *srv = arg;
+
+ list_flush(&srv->rrl);
+ mem_deref(srv->us);
+}
+
+
+int dns_server_alloc(struct dns_server **srvp, bool rotate)
+{
+ struct dns_server *srv;
+ int err;
+
+ if (!srvp)
+ return EINVAL;
+
+ srv = mem_zalloc(sizeof(*srv), destructor);
+ if (!srv)
+ return ENOMEM;
+
+ err = sa_set_str(&srv->addr, "127.0.0.1", LOCAL_PORT);
+ if (err)
+ goto out;
+
+ err = udp_listen(&srv->us, &srv->addr, udp_recv, srv);
+ if (err)
+ goto out;
+
+ err = udp_local_get(srv->us, &srv->addr);
+ if (err)
+ goto out;
+
+ srv->rotate = rotate;
+
+ out:
+ if (err)
+ mem_deref(srv);
+ else
+ *srvp = srv;
+
+ return err;
+}
+
+
+int dns_server_add_a(struct dns_server *srv, const char *name, uint32_t addr)
+{
+ struct dnsrr *rr;
+ int err;
+
+ if (!srv || !name)
+ return EINVAL;
+
+ rr = dns_rr_alloc();
+ if (!rr)
+ return ENOMEM;
+
+ err = str_dup(&rr->name, name);
+ if (err)
+ goto out;
+
+ rr->type = DNS_TYPE_A;
+ rr->dnsclass = DNS_CLASS_IN;
+ rr->ttl = 3600;
+ rr->rdlen = 0;
+
+ rr->rdata.a.addr = addr;
+
+ list_append(&srv->rrl, &rr->le, rr);
+
+ out:
+ if (err)
+ mem_deref(rr);
+
+ return err;
+}
+
+
+int dns_server_add_srv(struct dns_server *srv, const char *name,
+ uint16_t pri, uint16_t weight, uint16_t port,
+ const char *target)
+{
+ struct dnsrr *rr;
+ int err;
+
+ if (!srv || !name || !port || !target)
+ return EINVAL;
+
+ rr = dns_rr_alloc();
+ if (!rr)
+ return ENOMEM;
+
+ err = str_dup(&rr->name, name);
+ if (err)
+ goto out;
+
+ rr->type = DNS_TYPE_SRV;
+ rr->dnsclass = DNS_CLASS_IN;
+ rr->ttl = 3600;
+ rr->rdlen = 0;
+
+ rr->rdata.srv.pri = pri;
+ rr->rdata.srv.weight = weight;
+ rr->rdata.srv.port = port;
+ str_dup(&rr->rdata.srv.target, target);
+
+ list_append(&srv->rrl, &rr->le, rr);
+
+ out:
+ if (err)
+ mem_deref(rr);
+
+ return err;
+}