diff options
-rw-r--r-- | src/net.c | 103 | ||||
-rw-r--r-- | src/ua.c | 4 | ||||
-rw-r--r-- | test/call.c | 8 | ||||
-rw-r--r-- | test/main.c | 3 | ||||
-rw-r--r-- | test/mock/dnssrv.c | 245 | ||||
-rw-r--r-- | test/srcs.mk | 1 | ||||
-rw-r--r-- | test/test.h | 20 | ||||
-rw-r--r-- | test/ua.c | 177 |
8 files changed, 516 insertions, 45 deletions
@@ -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; } @@ -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); @@ -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; +} |