summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlfred E. Heggestad <aeh@db.org>2016-04-02 14:44:27 +0200
committerAlfred E. Heggestad <aeh@db.org>2016-04-02 14:44:27 +0200
commit059b6a9f597419adc331ca7e1e2b40b090d907a1 (patch)
tree441eae974d945e1d3bcb89009ca9929e4d42d312
parenta80081cc08f65e3cd99d7b94cb1f849ee94dc47a (diff)
add test for SIP with DNS
-rw-r--r--src/net.c103
-rw-r--r--src/ua.c4
-rw-r--r--test/call.c8
-rw-r--r--test/main.c3
-rw-r--r--test/mock/dnssrv.c245
-rw-r--r--test/srcs.mk1
-rw-r--r--test/test.h20
-rw-r--r--test/ua.c177
8 files changed, 516 insertions, 45 deletions
diff --git a/src/net.c b/src/net.c
index 4829a5a..216ddf9 100644
--- a/src/net.c
+++ b/src/net.c
@@ -8,6 +8,9 @@
#include "core.h"
+#define MAX_NS 8
+
+
static struct {
struct config_net cfg;
struct sa laddr;
@@ -18,7 +21,7 @@ static struct {
#endif
struct tmr tmr;
struct dnsc *dnsc;
- struct sa nsv[4]; /**< Configured name servers */
+ struct sa nsv[MAX_NS];/**< Configured name servers */
uint32_t nsn; /**< Number of configured name servers */
uint32_t interval;
int af; /**< Preferred address family */
@@ -28,24 +31,64 @@ static struct {
} net;
+static int net_dns_srv_get(struct sa *srvv, uint32_t *n, bool *from_sys)
+{
+ struct sa nsv[MAX_NS];
+ uint32_t i, nsn = ARRAY_SIZE(nsv);
+ int err;
+
+ err = dns_srv_get(net.domain, sizeof(net.domain), nsv, &nsn);
+ if (err) {
+ nsn = 0;
+ }
+
+ if (net.nsn) {
+
+ if (net.nsn > *n)
+ return E2BIG;
+
+ /* Use any configured nameservers */
+ for (i=0; i<net.nsn; i++) {
+ srvv[i] = net.nsv[i];
+ }
+
+ *n = net.nsn;
+
+ if (from_sys)
+ *from_sys = false;
+ }
+ else {
+ if (nsn > *n)
+ return E2BIG;
+
+ for (i=0; i<nsn; i++)
+ srvv[i] = nsv[i];
+
+ *n = nsn;
+
+ if (from_sys)
+ *from_sys = true;
+ }
+
+ return 0;
+}
+
+
/**
* Check for DNS Server updates
*/
static void dns_refresh(void)
{
- struct sa nsv[8];
- uint32_t i, nsn;
+ struct sa nsv[MAX_NS];
+ uint32_t nsn;
int err;
nsn = ARRAY_SIZE(nsv);
- err = dns_srv_get(NULL, 0, nsv, &nsn);
+ err = net_dns_srv_get(nsv, &nsn, NULL);
if (err)
return;
- for (i=0; i<net.nsn; i++)
- sa_cpy(&nsv[nsn++], &net.nsv[i]);
-
(void)dnsc_srv_set(net.dnsc, nsv, nsn);
}
@@ -124,20 +167,13 @@ bool net_check(void)
static int dns_init(void)
{
- struct sa nsv[8];
- uint32_t i, nsn;
+ struct sa nsv[MAX_NS];
+ uint32_t nsn = ARRAY_SIZE(nsv);
int err;
- nsn = ARRAY_SIZE(nsv);
-
- err = dns_srv_get(net.domain, sizeof(net.domain), nsv, &nsn);
- if (err) {
- nsn = 0;
- }
-
- /* Add any configured nameservers */
- for (i=0; i<net.nsn && nsn < ARRAY_SIZE(nsv); i++)
- sa_cpy(&nsv[nsn++], &net.nsv[i]);
+ err = net_dns_srv_get(nsv, &nsn, NULL);
+ if (err)
+ return err;
return dnsc_alloc(&net.dnsc, NULL, nsv, nsn);
}
@@ -164,6 +200,7 @@ static bool check_ipv6(void)
*/
int net_init(const struct config_net *cfg, int af)
{
+ char buf4[128] = "", buf6[128] = "";
int err;
if (!cfg)
@@ -255,19 +292,18 @@ int net_init(const struct config_net *cfg, int af)
#endif
}
- (void)re_fprintf(stderr, "Local network address:");
-
if (sa_isset(&net.laddr, SA_ADDR)) {
- (void)re_fprintf(stderr, " IPv4=%s:%j",
- net.ifname, &net.laddr);
+ re_snprintf(buf4, sizeof(buf4), " IPv4=%s:%j",
+ net.ifname, &net.laddr);
}
#ifdef HAVE_INET6
if (sa_isset(&net.laddr6, SA_ADDR)) {
- (void)re_fprintf(stderr, " IPv6=%s:%j",
- net.ifname6, &net.laddr6);
+ re_snprintf(buf6, sizeof(buf6), " IPv6=%s:%j",
+ net.ifname6, &net.laddr6);
}
#endif
- (void)re_fprintf(stderr, "\n");
+ info("Local network address: %s %s\n",
+ buf4, buf6);
return err;
}
@@ -293,6 +329,7 @@ void net_close(void)
{
net.dnsc = mem_deref(net.dnsc);
tmr_cancel(&net.tmr);
+ net.nsn = 0;
}
@@ -336,23 +373,21 @@ void net_change(uint32_t interval, net_change_h *ch, void *arg)
static int dns_debug(struct re_printf *pf, void *unused)
{
- struct sa nsv[4];
- uint32_t i, nsn;
+ struct sa nsv[MAX_NS];
+ uint32_t i, nsn = ARRAY_SIZE(nsv);
+ bool from_sys;
int err;
(void)unused;
- nsn = ARRAY_SIZE(nsv);
-
- err = dns_srv_get(NULL, 0, nsv, &nsn);
+ err = net_dns_srv_get(nsv, &nsn, &from_sys);
if (err)
nsn = 0;
- err = re_hprintf(pf, " DNS Servers: (%u)\n", nsn);
+ err = re_hprintf(pf, " DNS Servers from %s: (%u)\n",
+ from_sys ? "System" : "Config", nsn);
for (i=0; i<nsn; i++)
err |= re_hprintf(pf, " %u: %J\n", i, &nsv[i]);
- for (i=0; i<net.nsn; i++)
- err |= re_hprintf(pf, " %u: %J\n", nsn+i, &net.nsv[i]);
return err;
}
diff --git a/src/ua.c b/src/ua.c
index 65834a6..575c752 100644
--- a/src/ua.c
+++ b/src/ua.c
@@ -163,7 +163,7 @@ int ua_register(struct ua *ua)
if (re_snprintf(reg_uri, sizeof(reg_uri), "%H", uri_encode, &uri) < 0)
return ENOMEM;
- if (str_isset(uag.cfg->uuid)) {
+ if (uag.cfg && str_isset(uag.cfg->uuid)) {
if (re_snprintf(params, sizeof(params),
";+sip.instance=\"<urn:uuid:%s>\"",
uag.cfg->uuid) < 0)
@@ -610,7 +610,7 @@ int ua_alloc(struct ua **uap, const char *aor)
}
/* Register clients */
- if (str_isset(uag.cfg->uuid))
+ if (uag.cfg && str_isset(uag.cfg->uuid))
add_extension(ua, "gruu");
if (0 == str_casecmp(ua->acc->sipnat, "outbound")) {
diff --git a/test/call.c b/test/call.c
index b597f3a..7bf3e26 100644
--- a/test/call.c
+++ b/test/call.c
@@ -48,6 +48,9 @@ struct fixture {
#define fixture_init(f) \
memset(f, 0, sizeof(*f)); \
\
+ err = ua_init("test", true, true, true, false); \
+ TEST_ERR(err); \
+ \
f->magic = MAGIC; \
aucodec_register(&dummy_pcma); \
\
@@ -75,7 +78,10 @@ struct fixture {
\
aucodec_unregister(&dummy_pcma); \
\
- uag_event_unregister(event_handler)
+ uag_event_unregister(event_handler); \
+ \
+ ua_stop_all(true); \
+ ua_close();
static struct aucodec dummy_pcma = {
diff --git a/test/main.c b/test/main.c
index 9ee4825..5503cbe 100644
--- a/test/main.c
+++ b/test/main.c
@@ -29,6 +29,7 @@ static const struct test tests[] = {
TEST(test_mos),
TEST(test_ua_alloc),
TEST(test_ua_register),
+ TEST(test_ua_register_dns),
TEST(test_uag_find_param),
};
@@ -172,10 +173,12 @@ int main(int argc, char *argv[])
}
str_ncpy(config->sip.local, "127.0.0.1:0", sizeof(config->sip.local));
+#if 0
/* XXX: needed for ua tests */
err = ua_init("test", true, true, true, false);
if (err)
goto out;
+#endif
if (argc >= (optind + 1)) {
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;
+}
diff --git a/test/srcs.mk b/test/srcs.mk
index 332ba3f..f2d4734 100644
--- a/test/srcs.mk
+++ b/test/srcs.mk
@@ -18,6 +18,7 @@ TEST_SRCS += mos.c
#
# Mocks
#
+TEST_SRCS += mock/dnssrv.c
TEST_SRCS += mock/sipsrv.c
ifneq ($(USE_TLS),)
TEST_SRCS += mock/cert.c
diff --git a/test/test.h b/test/test.h
index 20ccdcd..c88df56 100644
--- a/test/test.h
+++ b/test/test.h
@@ -83,12 +83,32 @@ int sip_server_uri(struct sip_server *srv, char *uri, size_t sz,
enum sip_transp tp);
+/*
+ * Mock DNS-Server
+ */
+
+struct dns_server {
+ struct udp_sock *us;
+ struct sa addr;
+ struct list rrl;
+ bool rotate;
+};
+
+int dns_server_alloc(struct dns_server **srvp, bool rotate);
+int dns_server_add_a(struct dns_server *srv,
+ const char *name, uint32_t addr);
+int dns_server_add_srv(struct dns_server *srv, const char *name,
+ uint16_t pri, uint16_t weight, uint16_t port,
+ const char *target);
+
+
/* test cases */
int test_cmd(void);
int test_ua_alloc(void);
int test_uag_find_param(void);
int test_ua_register(void);
+int test_ua_register_dns(void);
int test_mos(void);
int test_call_answer(void);
diff --git a/test/ua.c b/test/ua.c
index c039705..90bfe98 100644
--- a/test/ua.c
+++ b/test/ua.c
@@ -10,17 +10,31 @@
struct test {
- struct sip_server *srv;
+ struct sip_server *srvv[16];
+ size_t srvc;
struct ua *ua;
int err;
unsigned got_register_ok;
};
+static void test_reset(struct test *t)
+{
+ size_t i;
+
+ for (i=0; i<ARRAY_SIZE(t->srvv); i++)
+ mem_deref(t->srvv[i]);
+ mem_deref(t->ua);
+
+ memset(t, 0, sizeof(*t));
+}
+
+
static void ua_event_handler(struct ua *ua, enum ua_event ev,
struct call *call, const char *prm, void *arg)
{
struct test *t = arg;
+ size_t i;
int err = 0;
(void)call;
(void)prm;
@@ -38,7 +52,9 @@ static void ua_event_handler(struct ua *ua, enum ua_event ev,
ASSERT_TRUE(ua_isregistered(t->ua));
/* Terminate SIP Server, then De-REGISTER */
- t->srv->terminate = true;
+ for (i=0; i<t->srvc; i++)
+ t->srvv[i]->terminate = true;
+
t->ua = mem_deref(t->ua);
}
@@ -58,13 +74,13 @@ static int reg(enum sip_transp tp)
memset(&t, 0, sizeof t);
- err = sip_server_alloc(&t.srv);
+ err = sip_server_alloc(&t.srvv[0]);
if (err) {
warning("failed to create sip server (%d/%m)\n", err, err);
goto out;
}
- err = sip_server_uri(t.srv, aor, sizeof(aor), tp);
+ err = sip_server_uri(t.srvv[0], aor, sizeof(aor), tp);
TEST_ERR(err);
err = ua_alloc(&t.ua, aor);
@@ -82,8 +98,8 @@ static int reg(enum sip_transp tp)
if (t.err)
err = t.err;
- ASSERT_TRUE(t.srv->n_register_req > 0);
- ASSERT_EQ(tp, t.srv->tp_last);
+ ASSERT_TRUE(t.srvv[0]->n_register_req > 0);
+ ASSERT_EQ(tp, t.srvv[0]->tp_last);
ASSERT_TRUE(t.got_register_ok > 0);
out:
@@ -91,8 +107,7 @@ static int reg(enum sip_transp tp)
warning("selftest: ua_register test failed (%m)\n", err);
}
uag_event_unregister(ua_event_handler);
- mem_deref(t.srv);
- mem_deref(t.ua);
+ test_reset(&t);
return err;
}
@@ -102,12 +117,18 @@ int test_ua_register(void)
{
int err = 0;
+ err = ua_init("test", true, true, true, false);
+ TEST_ERR(err);
+
err |= reg(SIP_TRANSP_UDP);
err |= reg(SIP_TRANSP_TCP);
#ifdef USE_TLS
err |= reg(SIP_TRANSP_TLS);
#endif
+ ua_close();
+
+ out:
return err;
}
@@ -169,3 +190,143 @@ int test_uag_find_param(void)
return err;
}
+
+
+#define SERVER_COUNT 1
+
+
+static const char *_sip_transp_srvid(enum sip_transp tp)
+{
+ switch (tp) {
+
+ case SIP_TRANSP_UDP: return "_sip._udp";
+ case SIP_TRANSP_TCP: return "_sip._tcp";
+ case SIP_TRANSP_TLS: return "_sips._tcp";
+ default: return "???";
+ }
+}
+
+
+static int reg_dns(enum sip_transp tp)
+{
+ struct dns_server *dnssrv = NULL;
+ struct test t;
+ const char *domain = "test.invalid";
+ char aor[256];
+ char srv[256];
+ size_t i;
+ int err;
+
+ memset(&t, 0, sizeof t);
+
+ /*
+ * Setup server-side mocks:
+ */
+
+ err = dns_server_alloc(&dnssrv, true);
+ TEST_ERR(err);
+
+ info("| DNS-server on %J\n", &dnssrv->addr);
+
+ /* NOTE: must be done before ua_init() */
+ err = net_dnssrv_add(&dnssrv->addr);
+ TEST_ERR(err);
+
+ for (i=0; i<SERVER_COUNT; i++) {
+ struct sa sip_addr;
+ char arec[256];
+
+ err = sip_server_alloc(&t.srvv[i]);
+ if (err) {
+ warning("failed to create sip server (%d/%m)\n",
+ err, err);
+ goto out;
+ }
+
+ err = sip_transp_laddr(t.srvv[i]->sip, &sip_addr, tp, NULL);
+ TEST_ERR(err);
+
+ info("| SIP-server on %J\n", &sip_addr);
+
+ re_snprintf(arec, sizeof(arec),
+ "alpha%u.%s", i+1, domain);
+
+ re_snprintf(srv, sizeof(srv),
+ "%s.%s", _sip_transp_srvid(tp), domain);
+ err = dns_server_add_srv(dnssrv, srv,
+ 20, 0, sa_port(&sip_addr),
+ arec);
+ TEST_ERR(err);
+
+ err = dns_server_add_a(dnssrv, arec, sa_in(&sip_addr));
+ TEST_ERR(err);
+ }
+ t.srvc = SERVER_COUNT;
+
+ /* NOTE: angel brackets needed to parse ;transport parameter */
+ if (re_snprintf(aor, sizeof(aor), "<sip:x:x@%s;transport=%s>",
+ domain, sip_transp_name(tp)) < 0)
+ return ENOMEM;
+
+ /*
+ * Start SIP client:
+ */
+
+ err = ua_init("test", true, true, true, false);
+ TEST_ERR(err);
+
+ err = ua_alloc(&t.ua, aor);
+ TEST_ERR(err);
+
+ err = uag_event_register(ua_event_handler, &t);
+ if (err)
+ goto out;
+
+ /* run main-loop with timeout, wait for events */
+ err = re_main_timeout(5000);
+ if (err)
+ goto out;
+
+ if (t.err)
+ err = t.err;
+
+ /* verify that all SIP requests was sent to the first
+ * SIP-server.
+ */
+ ASSERT_TRUE(t.srvv[0]->n_register_req > 0);
+ ASSERT_EQ(tp, t.srvv[0]->tp_last);
+ ASSERT_TRUE(t.got_register_ok > 0);
+
+ out:
+ if (err) {
+ warning("selftest: ua_register test failed (%m)\n", err);
+ }
+ uag_event_unregister(ua_event_handler);
+
+ test_reset(&t);
+
+ ua_stop_all(true);
+ ua_close();
+
+ mem_deref(dnssrv);
+
+ return err;
+}
+
+
+int test_ua_register_dns(void)
+{
+ int err = 0;
+
+ err |= reg_dns(SIP_TRANSP_UDP);
+ TEST_ERR(err);
+ err |= reg_dns(SIP_TRANSP_TCP);
+ TEST_ERR(err);
+#ifdef USE_TLS
+ err |= reg_dns(SIP_TRANSP_TLS);
+ TEST_ERR(err);
+#endif
+
+ out:
+ return err;
+}