summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorAlfred E. Heggestad <aeh@db.org>2016-04-03 19:08:13 +0200
committerAlfred E. Heggestad <aeh@db.org>2016-04-03 19:08:13 +0200
commitf53ec36a768e41c47c72fac6ad5c3e672e291688 (patch)
tree327624a8155e6c2e849834b097b905b694ac5b69 /test
parenta80081cc08f65e3cd99d7b94cb1f849ee94dc47a (diff)
parent9e16ab5a1a79c1ee43cd3c07faa54be83d83e38b (diff)
Merge pull request #121 from alfredh/test_sip_dns
Test sip dns
Diffstat (limited to 'test')
-rw-r--r--test/call.c8
-rw-r--r--test/main.c4
-rw-r--r--test/mock/dnssrv.c245
-rw-r--r--test/mock/sipsrv.c146
-rw-r--r--test/sip/aor.c98
-rw-r--r--test/sip/auth.c91
-rw-r--r--test/sip/domain.c187
-rw-r--r--test/sip/location.c188
-rw-r--r--test/sip/sipsrv.c329
-rw-r--r--test/sip/sipsrv.h121
-rw-r--r--test/sip/user.c73
-rw-r--r--test/srcs.mk10
-rw-r--r--test/test.h25
-rw-r--r--test/ua.c288
14 files changed, 1646 insertions, 167 deletions
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..a03cf99 100644
--- a/test/main.c
+++ b/test/main.c
@@ -29,6 +29,8 @@ static const struct test tests[] = {
TEST(test_mos),
TEST(test_ua_alloc),
TEST(test_ua_register),
+ TEST(test_ua_register_dns),
+ TEST(test_ua_register_auth),
TEST(test_uag_find_param),
};
@@ -172,10 +174,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/mock/sipsrv.c b/test/mock/sipsrv.c
deleted file mode 100644
index c762f73..0000000
--- a/test/mock/sipsrv.c
+++ /dev/null
@@ -1,146 +0,0 @@
-/**
- * @file mock/sipsrv.c Mock SIP server
- *
- * Copyright (C) 2010 - 2015 Creytiv.com
- */
-#include <string.h>
-#include <re.h>
-#include "../test.h"
-
-
-#define DEBUG_MODULE "mock/sipsrv"
-#define DEBUG_LEVEL 5
-#include <re_dbg.h>
-
-
-#define LOCAL_PORT 0
-#define LOCAL_SECURE_PORT 0
-
-
-static bool sip_msg_handler(const struct sip_msg *msg, void *arg)
-{
- struct sip_server *srv = arg;
- int err;
-
-#if 0
- DEBUG_NOTICE("recv %r via %s\n", &msg->met, sip_transp_name(msg->tp));
-#endif
-
- if (0 == pl_strcmp(&msg->met, "REGISTER")) {
- ++srv->n_register_req;
- }
- else {
- DEBUG_NOTICE("method not handled (%r)\n", &msg->met);
- return false;
- }
-
- srv->tp_last = msg->tp;
-
- if (srv->terminate)
- err = sip_reply(srv->sip, msg, 503, "Server Error");
- else
- err = sip_reply(srv->sip, msg, 200, "OK");
-
- if (err) {
- DEBUG_WARNING("could not reply: %m\n", err);
- }
-
-#if 1
- if (srv->terminate)
- re_cancel();
-#endif
-
- return true;
-}
-
-
-static void destructor(void *arg)
-{
- struct sip_server *srv = arg;
-
- srv->terminate = true;
-
- sip_close(srv->sip, false);
- mem_deref(srv->sip);
-}
-
-
-int sip_server_alloc(struct sip_server **srvp)
-{
- struct sip_server *srv;
- struct sa laddr, laddrs;
- struct tls *tls = NULL;
- int err;
-
- if (!srvp)
- return EINVAL;
-
- srv = mem_zalloc(sizeof *srv, destructor);
- if (!srv)
- return ENOMEM;
-
- err = sa_set_str(&laddr, "127.0.0.1", LOCAL_PORT);
- err |= sa_set_str(&laddrs, "127.0.0.1", LOCAL_SECURE_PORT);
- if (err)
- goto out;
-
- err = sip_alloc(&srv->sip, NULL, 16, 16, 16,
- "mock SIP server", NULL, NULL);
- if (err)
- goto out;
-
- err |= sip_transp_add(srv->sip, SIP_TRANSP_UDP, &laddr);
- err |= sip_transp_add(srv->sip, SIP_TRANSP_TCP, &laddr);
- if (err)
- goto out;
-
-#ifdef USE_TLS
- err = tls_alloc(&tls, TLS_METHOD_SSLV23, NULL, NULL);
- if (err)
- goto out;
-
- err = tls_set_certificate(tls, test_certificate,
- strlen(test_certificate));
- if (err)
- goto out;
-
- err |= sip_transp_add(srv->sip, SIP_TRANSP_TLS, &laddrs, tls);
-#endif
- if (err)
- goto out;
-
- err = sip_listen(&srv->lsnr, srv->sip, true, sip_msg_handler, srv);
- if (err)
- goto out;
-
- out:
- mem_deref(tls);
- if (err)
- mem_deref(srv);
- else
- *srvp = srv;
-
- return err;
-}
-
-
-int sip_server_uri(struct sip_server *srv, char *uri, size_t sz,
- enum sip_transp tp)
-{
- struct sa laddr;
- int err;
-
- if (!srv || !uri || !sz)
- return EINVAL;
-
- err = sip_transp_laddr(srv->sip, &laddr, tp, NULL);
- if (err)
- return err;
-
- /* NOTE: angel brackets needed to parse ;transport parameter */
- if (re_snprintf(uri, sz, "<sip:x:x@%J%s>",
- &laddr, sip_transp_param(tp)) < 0)
- return ENOMEM;
-
- return 0;
-}
diff --git a/test/sip/aor.c b/test/sip/aor.c
new file mode 100644
index 0000000..0d8a317
--- /dev/null
+++ b/test/sip/aor.c
@@ -0,0 +1,98 @@
+/**
+ * @file sip/aor.c Mock SIP server -- SIP Address of Record
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+#include <string.h>
+#include <re.h>
+#include "sipsrv.h"
+
+
+static void destructor(void *arg)
+{
+ struct aor *aor = arg;
+
+ list_flush(&aor->locl);
+ hash_unlink(&aor->he);
+ mem_deref(aor->uri);
+}
+
+
+static int uri_canon(char **curip, const struct uri *uri)
+{
+ if (pl_isset(&uri->user))
+ return re_sdprintf(curip, "%r:%H@%r",
+ &uri->scheme,
+ uri_user_unescape, &uri->user,
+ &uri->host);
+ else
+ return re_sdprintf(curip, "%r:%r",
+ &uri->scheme,
+ &uri->host);
+}
+
+
+int aor_create(struct sip_server *srv, struct aor **aorp,
+ const struct uri *uri)
+{
+ struct aor *aor;
+ int err;
+
+ if (!aorp || !uri)
+ return EINVAL;
+
+ aor = mem_zalloc(sizeof(*aor), destructor);
+ if (!aor)
+ return ENOMEM;
+
+ err = uri_canon(&aor->uri, uri);
+ if (err)
+ goto out;
+
+ hash_append(srv->ht_aor, hash_joaat_str_ci(aor->uri), &aor->he, aor);
+
+ out:
+ if (err)
+ mem_deref(aor);
+ else
+ *aorp = aor;
+
+ return err;
+}
+
+
+int aor_find(struct sip_server *srv, struct aor **aorp, const struct uri *uri)
+{
+ struct list *lst;
+ struct aor *aor;
+ struct le *le;
+ char *curi;
+ int err;
+
+ if (!uri)
+ return EINVAL;
+
+ err = uri_canon(&curi, uri);
+ if (err)
+ return err;
+
+ lst = hash_list(srv->ht_aor, hash_joaat_str_ci(curi));
+
+ for (le=list_head(lst); le; le=le->next) {
+
+ aor = le->data;
+
+ if (!str_casecmp(curi, aor->uri))
+ break;
+ }
+
+ mem_deref(curi);
+
+ if (!le)
+ return ENOENT;
+
+ if (aorp)
+ *aorp = aor;
+
+ return 0;
+}
diff --git a/test/sip/auth.c b/test/sip/auth.c
new file mode 100644
index 0000000..5d3e224
--- /dev/null
+++ b/test/sip/auth.c
@@ -0,0 +1,91 @@
+/**
+ * @file sip/auth.c Mock SIP server -- authentication
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+#include <string.h>
+#include <time.h>
+#include <re.h>
+#include "sipsrv.h"
+
+
+enum {
+ NONCE_MIN_SIZE = 33,
+};
+
+
+int auth_print(struct re_printf *pf, const struct auth *auth)
+{
+ uint8_t key[MD5_SIZE];
+ uint64_t nv[2];
+
+ if (!auth)
+ return EINVAL;
+
+ nv[0] = time(NULL);
+ nv[1] = auth->srv->secret;
+
+ md5((uint8_t *)nv, sizeof(nv), key);
+
+ return re_hprintf(pf,
+ "Digest realm=\"%s\", nonce=\"%w%llx\", "
+ "qop=\"auth\"%s",
+ auth->realm,
+ key, sizeof(key), nv[0],
+ auth->stale ? ", stale=true" : "");
+}
+
+
+int auth_chk_nonce(struct sip_server *srv,
+ const struct pl *nonce, uint32_t expires)
+{
+ uint8_t nkey[MD5_SIZE], ckey[MD5_SIZE];
+ uint64_t nv[2];
+ struct pl pl;
+ int64_t age;
+ unsigned i;
+
+ if (!nonce || !nonce->p || nonce->l < NONCE_MIN_SIZE)
+ return EINVAL;
+
+ pl = *nonce;
+
+ for (i=0; i<sizeof(nkey); i++) {
+ nkey[i] = ch_hex(*pl.p++) << 4;
+ nkey[i] += ch_hex(*pl.p++);
+ pl.l -= 2;
+ }
+
+ nv[0] = pl_x64(&pl);
+ nv[1] = srv->secret;
+
+ md5((uint8_t *)nv, sizeof(nv), ckey);
+
+ if (memcmp(nkey, ckey, MD5_SIZE))
+ return EAUTH;
+
+ age = time(NULL) - nv[0];
+
+ if (age < 0 || age > expires)
+ return ETIMEDOUT;
+
+ return 0;
+}
+
+
+int auth_set_realm(struct auth *auth, const char *realm)
+{
+ size_t len;
+
+ if (!auth || !realm)
+ return EINVAL;
+
+ len = strlen(realm);
+ if (len >= sizeof(auth->realm))
+ return ENOMEM;
+
+ memcpy(auth->realm, realm, len);
+ auth->realm[len] = '\0';
+
+ return 0;
+}
diff --git a/test/sip/domain.c b/test/sip/domain.c
new file mode 100644
index 0000000..063e3b9
--- /dev/null
+++ b/test/sip/domain.c
@@ -0,0 +1,187 @@
+/**
+ * @file sip/domain.c Mock SIP server -- domain handling
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+#include <re.h>
+#include "sipsrv.h"
+
+
+#define DEBUG_MODULE "mock/sipsrv"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+enum {
+ NONCE_EXPIRES = 300,
+};
+
+
+static void destructor(void *arg)
+{
+ struct domain *dom = arg;
+
+ hash_unlink(&dom->he);
+ hash_flush(dom->ht_usr);
+ mem_deref(dom->ht_usr);
+ mem_deref(dom->name);
+}
+
+
+static struct domain *lookup(struct sip_server *srv, const struct pl *name)
+{
+ struct list *lst;
+ struct le *le;
+
+ lst = hash_list(srv->ht_dom, hash_joaat_ci(name->p, name->l));
+
+ for (le=list_head(lst); le; le=le->next) {
+
+ struct domain *dom = le->data;
+
+ if (pl_strcasecmp(name, dom->name))
+ continue;
+
+ return dom;
+ }
+
+ return NULL;
+}
+
+
+int domain_add(struct sip_server *srv, const char *name)
+{
+ struct domain *dom;
+ int err;
+
+ dom = mem_zalloc(sizeof(*dom), destructor);
+ if (!dom)
+ return ENOMEM;
+
+ err = str_dup(&dom->name, name);
+ if (err)
+ goto out;
+
+ err = hash_alloc(&dom->ht_usr, 32);
+ if (err)
+ return err;
+
+ hash_append(srv->ht_dom, hash_joaat_str_ci(name), &dom->he, dom);
+
+ out:
+ if (err)
+ mem_deref(dom);
+
+ return err;
+}
+
+
+int domain_find(struct sip_server *srv, const struct uri *uri)
+{
+ int err = ENOENT;
+ struct sa addr;
+
+ if (!uri)
+ return EINVAL;
+
+ if (!sa_set(&addr, &uri->host, uri->port)) {
+
+ if (!uri->port) {
+
+ uint16_t port = SIP_PORT;
+
+ if (!pl_strcasecmp(&uri->scheme, "sips"))
+ port = SIP_PORT_TLS;
+
+ sa_set_port(&addr, port);
+ }
+
+ if (sip_transp_isladdr(srv->sip, SIP_TRANSP_NONE, &addr))
+ return 0;
+
+ return ENOENT;
+ }
+
+ err = lookup(srv, &uri->host) ? 0 : ENOENT;
+
+ return err;
+}
+
+
+int domain_auth(struct sip_server *srv,
+ const struct uri *uri, bool user_match,
+ const struct sip_msg *msg, enum sip_hdrid hdrid,
+ struct auth *auth)
+{
+ struct domain *dom;
+ struct list *lst;
+ struct le *le;
+ int err = ENOENT;
+
+ if (!uri || !msg || !auth)
+ return EINVAL;
+
+ dom = lookup(srv, &uri->host);
+ if (!dom) {
+ DEBUG_WARNING("domain not found (%r)\n", &uri->host);
+ return ENOENT;
+ }
+
+ err = auth_set_realm(auth, dom->name);
+ if (err)
+ return err;
+
+ auth->stale = false;
+
+ lst = hash_list(msg->hdrht, hdrid);
+
+ for (le=list_head(lst); le; le=le->next) {
+
+ const struct sip_hdr *hdr = le->data;
+ struct httpauth_digest_resp resp;
+ const struct user *usr;
+
+ if (hdr->id != hdrid)
+ continue;
+
+ if (httpauth_digest_response_decode(&resp, &hdr->val))
+ continue;
+
+ if (pl_strcasecmp(&resp.realm, dom->name))
+ continue;
+
+ if (auth_chk_nonce(srv, &resp.nonce, NONCE_EXPIRES)) {
+ auth->stale = true;
+ continue;
+ }
+
+ auth->stale = false;
+
+ usr = user_find(dom->ht_usr, &resp.username);
+ if (!usr) {
+ DEBUG_WARNING("user not found (%r)\n", &resp.username);
+ break;
+ }
+
+ err = httpauth_digest_response_auth(&resp, &msg->met,usr->ha1);
+ if (err)
+ return err;
+
+ if (user_match && pl_cmp(&resp.username, &uri->user))
+ return EPERM;
+
+ return 0;
+ }
+
+ return EAUTH;
+}
+
+
+struct domain *domain_lookup(struct sip_server *srv, const char *name)
+{
+ struct pl pl;
+
+ pl_set_str(&pl, name);
+
+ return lookup(srv, &pl);
+}
diff --git a/test/sip/location.c b/test/sip/location.c
new file mode 100644
index 0000000..be2bd66
--- /dev/null
+++ b/test/sip/location.c
@@ -0,0 +1,188 @@
+/**
+ * @file sip/location.c Mock SIP server -- location handling
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+#include <time.h>
+#include <re.h>
+#include "sipsrv.h"
+
+
+struct loctmp {
+ struct sa src;
+ struct uri duri;
+ char *uri;
+ char *callid;
+ uint32_t expires;
+ uint32_t cseq;
+ double q;
+};
+
+
+static void destructor_loctmp(void *arg)
+{
+ struct loctmp *tmp = arg;
+
+ mem_deref(tmp->uri);
+ mem_deref(tmp->callid);
+}
+
+
+static void destructor_location(void *arg)
+{
+ struct location *loc = arg;
+
+ list_unlink(&loc->le);
+ mem_deref(loc->uri);
+ mem_deref(loc->callid);
+ mem_deref(loc->tmp);
+}
+
+
+static bool cmp_handler(struct le *le, void *arg)
+{
+ struct location *loc = le->data;
+
+ return uri_cmp(&loc->duri, arg);
+}
+
+
+int location_update(struct list *locl, const struct sip_msg *msg,
+ const struct sip_addr *contact, uint32_t expires)
+{
+ struct location *loc, *loc_new = NULL;
+ struct loctmp *tmp;
+ struct pl pl;
+ int err;
+
+ if (!locl || !msg || !contact)
+ return EINVAL;
+
+ loc = list_ledata(list_apply(locl, true, cmp_handler,
+ (void *)&contact->uri));
+ if (!loc) {
+ if (expires == 0)
+ return 0;
+
+ loc = loc_new = mem_zalloc(sizeof(*loc), destructor_location);
+ if (!loc)
+ return ENOMEM;
+
+ list_append(locl, &loc->le, loc);
+ }
+ else {
+ if (!pl_strcmp(&msg->callid, loc->callid) &&
+ msg->cseq.num <= loc->cseq)
+ return EPROTO;
+
+ if (expires == 0) {
+ loc->rm = true;
+ return 0;
+ }
+ }
+
+ tmp = mem_zalloc(sizeof(*tmp), destructor_loctmp);
+ if (!tmp) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ err = pl_strdup(&tmp->uri, &contact->auri);
+ if (err)
+ goto out;
+
+ pl_set_str(&pl, tmp->uri);
+
+ if (uri_decode(&tmp->duri, &pl)) {
+ err = EBADMSG;
+ goto out;
+ }
+
+ err = pl_strdup(&tmp->callid, &msg->callid);
+ if (err)
+ goto out;
+
+
+ if (!msg_param_decode(&contact->params, "q", &pl))
+ tmp->q = pl_float(&pl);
+ else
+ tmp->q = 1;
+
+ tmp->cseq = msg->cseq.num;
+ tmp->expires = expires;
+ tmp->src = msg->src;
+
+ out:
+ if (err) {
+ mem_deref(loc_new);
+ mem_deref(tmp);
+ }
+ else {
+ mem_deref(loc->tmp);
+ loc->tmp = tmp;
+ }
+
+ return err;
+}
+
+
+void location_commit(struct list *locl)
+{
+ time_t now = time(NULL);
+ struct le *le;
+
+ if (!locl)
+ return;
+
+ for (le=locl->head; le; ) {
+
+ struct location *loc = le->data;
+
+ le = le->next;
+
+ if (loc->rm) {
+ list_unlink(&loc->le);
+ mem_deref(loc);
+ }
+ else if (loc->tmp) {
+
+ mem_deref(loc->uri);
+ mem_deref(loc->callid);
+
+ loc->uri = mem_ref(loc->tmp->uri);
+ loc->callid = mem_ref(loc->tmp->callid);
+ loc->duri = loc->tmp->duri;
+ loc->cseq = loc->tmp->cseq;
+ loc->expires = loc->tmp->expires + now;
+ loc->src = loc->tmp->src;
+ loc->q = loc->tmp->q;
+
+ loc->tmp = mem_deref(loc->tmp);
+ }
+ }
+}
+
+
+void location_rollback(struct list *locl)
+{
+ struct le *le;
+
+ if (!locl)
+ return;
+
+ for (le=locl->head; le; ) {
+
+ struct location *loc = le->data;
+
+ le = le->next;
+
+ if (!loc->uri) {
+ list_unlink(&loc->le);
+ mem_deref(loc);
+ }
+ else {
+ loc->tmp = mem_deref(loc->tmp);
+ loc->rm = false;
+ }
+ }
+}
diff --git a/test/sip/sipsrv.c b/test/sip/sipsrv.c
new file mode 100644
index 0000000..686d587
--- /dev/null
+++ b/test/sip/sipsrv.c
@@ -0,0 +1,329 @@
+/**
+ * @file sip/sipsrv.c Mock SIP server
+ *
+ * Copyright (C) 2010 - 2015 Creytiv.com
+ */
+#include <string.h>
+#include <time.h>
+#include <re.h>
+#include <baresip.h>
+#include "../test.h"
+#include "sipsrv.h"
+
+
+#define DEBUG_MODULE "mock/sipsrv"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+#define LOCAL_PORT 0
+#define LOCAL_SECURE_PORT 0
+#define EXPIRES_MIN 60
+#define EXPIRES_MAX 3600
+
+
+static int print_contact(struct re_printf *pf, const struct aor *aor)
+{
+ const uint64_t now = (uint64_t)time(NULL);
+ struct le *le;
+ int err = 0;
+
+ for (le=aor->locl.head; le; le=le->next) {
+
+ const struct location *loc = le->data;
+
+ if (loc->expires < now)
+ continue;
+
+ err |= re_hprintf(pf, "Contact: <%s>;expires=%lli\r\n",
+ loc->uri, loc->expires - now);
+ }
+
+ return err;
+}
+
+
+static bool handle_register(struct sip_server *srv, const struct sip_msg *msg)
+{
+ struct auth auth = { srv, "", false };
+ struct sip *sip = srv->sip;
+ struct list *lst;
+ struct aor *aor;
+ struct le *le;
+ int err;
+
+ /* Request URI */
+ err = domain_find(srv, &msg->uri);
+ if (err) {
+ if (err == ENOENT) {
+ warning("domain not found\n");
+ return false;
+ }
+
+ sip_reply(sip, msg, 500, strerror(err));
+ error("domain find error: %s\n", strerror(err));
+ return true;
+ }
+
+ /* Authorize */
+ if (srv->auth_enabled)
+ err = domain_auth(srv, &msg->to.uri, true,
+ msg, SIP_HDR_AUTHORIZATION, &auth);
+ else
+ err = domain_find(srv, &msg->to.uri);
+
+ if (err && err != EAUTH) {
+ DEBUG_NOTICE("domain auth/find error: %m\n", err);
+ }
+
+ switch (err) {
+
+ case 0:
+ break;
+
+ case EAUTH:
+ sip_replyf(sip, msg, 401, "Unauthorized",
+ "WWW-Authenticate: %H\r\n"
+ "Content-Length: 0\r\n\r\n",
+ auth_print, &auth);
+ return true;
+
+ case EPERM:
+ sip_reply(sip, msg, 403, "Forbidden");
+ return true;
+
+ case ENOENT:
+ sip_reply(sip, msg, 404, "Not Found");
+ return true;
+
+ default:
+ sip_reply(sip, msg, 500, strerror(err));
+ error("domain error: %s\n", strerror(err));
+ return true;
+ }
+
+ /* Find AoR */
+ err = aor_find(srv, &aor, &msg->to.uri);
+ if (err) {
+ if (err != ENOENT) {
+ sip_reply(sip, msg, 500, strerror(err));
+ error("aor find error: %s\n", strerror(err));
+ return true;
+ }
+
+ err = aor_create(srv, &aor, &msg->to.uri);
+ if (err) {
+ sip_reply(sip, msg, 500, strerror(err));
+ error("aor create error: %s\n", strerror(err));
+ return true;
+ }
+ }
+
+ /* Process Contacts */
+ lst = hash_list(msg->hdrht, SIP_HDR_CONTACT);
+
+ for (le=list_head(lst); le; le=le->next) {
+
+ const struct sip_hdr *hdr = le->data;
+ struct sip_addr contact;
+ uint32_t expires;
+ struct pl pl;
+
+ if (hdr->id != SIP_HDR_CONTACT)
+ continue;
+
+ err = sip_addr_decode(&contact, &hdr->val);
+ if (err) {
+ sip_reply(sip, msg, 400, "Bad Contact");
+ goto fail;
+ }
+
+ if (!msg_param_decode(&contact.params, "expires", &pl))
+ expires = pl_u32(&pl);
+ else if (pl_isset(&msg->expires))
+ expires = pl_u32(&msg->expires);
+ else
+ expires = 3600;
+
+ if (expires > 0 && expires < EXPIRES_MIN) {
+ sip_replyf(sip, msg, 423, "Interval Too Brief",
+ "Min-Expires: %u\r\n"
+ "Content-Length: 0\r\n\r\n",
+ EXPIRES_MIN);
+ goto fail;
+ }
+
+ expires = min(expires, EXPIRES_MAX);
+
+ err = location_update(&aor->locl, msg, &contact, expires);
+ if (err) {
+ sip_reply(sip, msg, 500, strerror(err));
+ if (err != EPROTO)
+ error("location update error: %s\n",
+ strerror(err));
+ goto fail;
+ }
+ }
+
+ location_commit(&aor->locl);
+
+ sip_treplyf(NULL, NULL, sip, msg, false, 200, "OK",
+ "%H"
+ "Date: %H\r\n"
+ "Content-Length: 0\r\n\r\n",
+ print_contact, aor,
+ fmt_gmtime, NULL);
+
+ return true;
+
+ fail:
+ location_rollback(&aor->locl);
+
+ return true;
+}
+
+
+static bool sip_msg_handler(const struct sip_msg *msg, void *arg)
+{
+ struct sip_server *srv = arg;
+ int err = 0;
+
+#if 0
+ DEBUG_NOTICE("recv %r via %s\n", &msg->met, sip_transp_name(msg->tp));
+#endif
+
+ srv->tp_last = msg->tp;
+
+ if (0 == pl_strcmp(&msg->met, "REGISTER")) {
+ ++srv->n_register_req;
+ if (handle_register(srv, msg))
+ goto out;
+
+ sip_reply(srv->sip, msg, 503, "Server Error");
+ }
+ else {
+ DEBUG_NOTICE("method not handled (%r)\n", &msg->met);
+ return false;
+ }
+
+ if (srv->terminate)
+ err = sip_reply(srv->sip, msg, 503, "Server Error");
+
+ if (err) {
+ DEBUG_WARNING("could not reply: %m\n", err);
+ }
+
+ out:
+ if (srv->terminate)
+ re_cancel();
+
+ return true;
+}
+
+
+static void destructor(void *arg)
+{
+ struct sip_server *srv = arg;
+
+ srv->terminate = true;
+
+ sip_close(srv->sip, false);
+ mem_deref(srv->sip);
+
+ hash_flush(srv->ht_aor);
+ mem_deref(srv->ht_aor);
+
+ hash_flush(srv->ht_dom);
+ mem_deref(srv->ht_dom);
+}
+
+
+int sip_server_alloc(struct sip_server **srvp)
+{
+ struct sip_server *srv;
+ struct sa laddr, laddrs;
+ struct tls *tls = NULL;
+ int err;
+
+ if (!srvp)
+ return EINVAL;
+
+ srv = mem_zalloc(sizeof *srv, destructor);
+ if (!srv)
+ return ENOMEM;
+
+ err = sa_set_str(&laddr, "127.0.0.1", LOCAL_PORT);
+ err |= sa_set_str(&laddrs, "127.0.0.1", LOCAL_SECURE_PORT);
+ if (err)
+ goto out;
+
+ err = sip_alloc(&srv->sip, NULL, 16, 16, 16,
+ "mock SIP server", NULL, NULL);
+ if (err)
+ goto out;
+
+ err |= sip_transp_add(srv->sip, SIP_TRANSP_UDP, &laddr);
+ err |= sip_transp_add(srv->sip, SIP_TRANSP_TCP, &laddr);
+ if (err)
+ goto out;
+
+#ifdef USE_TLS
+ err = tls_alloc(&tls, TLS_METHOD_SSLV23, NULL, NULL);
+ if (err)
+ goto out;
+
+ err = tls_set_certificate(tls, test_certificate,
+ strlen(test_certificate));
+ if (err)
+ goto out;
+
+ err |= sip_transp_add(srv->sip, SIP_TRANSP_TLS, &laddrs, tls);
+#endif
+ if (err)
+ goto out;
+
+ err = sip_listen(&srv->lsnr, srv->sip, true, sip_msg_handler, srv);
+ if (err)
+ goto out;
+
+ srv->secret = rand_u64();
+
+ err = hash_alloc(&srv->ht_dom, 32);
+ if (err)
+ goto out;
+
+ err = hash_alloc(&srv->ht_aor, 32);
+ if (err)
+ goto out;
+
+ out:
+ mem_deref(tls);
+ if (err)
+ mem_deref(srv);
+ else
+ *srvp = srv;
+
+ return err;
+}
+
+
+int sip_server_uri(struct sip_server *srv, char *uri, size_t sz,
+ enum sip_transp tp)
+{
+ struct sa laddr;
+ int err;
+
+ if (!srv || !uri || !sz)
+ return EINVAL;
+
+ err = sip_transp_laddr(srv->sip, &laddr, tp, NULL);
+ if (err)
+ return err;
+
+ /* NOTE: angel brackets needed to parse ;transport parameter */
+ if (re_snprintf(uri, sz, "<sip:x:x@%J%s>",
+ &laddr, sip_transp_param(tp)) < 0)
+ return ENOMEM;
+
+ return 0;
+}
diff --git a/test/sip/sipsrv.h b/test/sip/sipsrv.h
new file mode 100644
index 0000000..aa136e0
--- /dev/null
+++ b/test/sip/sipsrv.h
@@ -0,0 +1,121 @@
+/**
+ * @file sip/sipsrv.h Mock SIP server -- interface
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+
+
+struct auth;
+
+
+/*
+ * SIP Server
+ */
+
+struct sip_server {
+ struct sip *sip;
+ struct sip_lsnr *lsnr;
+ bool auth_enabled;
+ bool terminate;
+
+ unsigned n_register_req;
+ enum sip_transp tp_last;
+
+ uint64_t secret;
+ struct hash *ht_dom;
+ struct hash *ht_aor;
+};
+
+int sip_server_alloc(struct sip_server **srvp);
+int sip_server_uri(struct sip_server *srv, char *uri, size_t sz,
+ enum sip_transp tp);
+
+
+/*
+ * AoR
+ */
+
+struct aor {
+ struct le he;
+ struct list locl;
+ char *uri;
+};
+
+int aor_create(struct sip_server *srv, struct aor **aorp,
+ const struct uri *uri);
+int aor_find(struct sip_server *srv, struct aor **aorp,
+ const struct uri *uri);
+
+
+/*
+ * Auth
+ */
+
+struct auth {
+ const struct sip_server *srv;
+ char realm[256];
+ bool stale;
+};
+
+int auth_print(struct re_printf *pf, const struct auth *auth);
+int auth_chk_nonce(struct sip_server *srv,
+ const struct pl *nonce, uint32_t expires);
+int auth_set_realm(struct auth *auth, const char *realm);
+
+
+/*
+ * Domain
+ */
+
+struct domain {
+ struct le he;
+ struct hash *ht_usr;
+ char *name;
+};
+
+
+int domain_add(struct sip_server *srv, const char *name);
+int domain_find(struct sip_server *srv, const struct uri *uri);
+int domain_auth(struct sip_server *srv,
+ const struct uri *uri, bool user_match,
+ const struct sip_msg *msg, enum sip_hdrid hdrid,
+ struct auth *auth);
+struct domain *domain_lookup(struct sip_server *srv, const char *name);
+
+
+/*
+ * Location
+ */
+
+struct location {
+ struct le le;
+ struct sa src;
+ struct uri duri;
+ char *uri;
+ char *callid;
+ struct loctmp *tmp;
+ uint64_t expires;
+ uint32_t cseq;
+ double q;
+ bool rm;
+};
+
+int location_update(struct list *locl, const struct sip_msg *msg,
+ const struct sip_addr *contact, uint32_t expires);
+void location_commit(struct list *locl);
+void location_rollback(struct list *locl);
+
+
+/*
+ * User
+ */
+
+struct user {
+ struct le he;
+ uint8_t ha1[MD5_SIZE];
+ char *name;
+};
+
+int user_add(struct hash *ht, const char *username, const char *password,
+ const char *realm);
+struct user *user_find(struct hash *ht, const struct pl *name);
diff --git a/test/sip/user.c b/test/sip/user.c
new file mode 100644
index 0000000..99fb47d
--- /dev/null
+++ b/test/sip/user.c
@@ -0,0 +1,73 @@
+/**
+ * @file sip/user.c Mock SIP server -- user handling
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+#include <re.h>
+#include "sipsrv.h"
+
+
+static void destructor(void *arg)
+{
+ struct user *usr = arg;
+
+ hash_unlink(&usr->he);
+ mem_deref(usr->name);
+}
+
+
+int user_add(struct hash *ht, const char *username, const char *password,
+ const char *realm)
+{
+ struct user *usr;
+ int err;
+
+ usr = mem_zalloc(sizeof(*usr), destructor);
+ if (!usr) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ err = str_dup(&usr->name, username);
+ if (err) {
+ goto out;
+ }
+
+ err = md5_printf(usr->ha1, "%s:%s:%s", username, realm, password);
+ if (err) {
+ goto out;
+ }
+
+ hash_append(ht, hash_joaat_str(username), &usr->he, usr);
+
+ out:
+ if (err) {
+ mem_deref(usr);
+ }
+
+ return err;
+}
+
+
+struct user *user_find(struct hash *ht, const struct pl *name)
+{
+ struct list *lst;
+ struct le *le;
+
+ if (!ht || !name)
+ return NULL;
+
+ lst = hash_list(ht, hash_joaat((uint8_t *)name->p, name->l));
+
+ for (le=list_head(lst); le; le=le->next) {
+
+ struct user *usr = le->data;
+
+ if (pl_strcmp(name, usr->name))
+ continue;
+
+ return usr;
+ }
+
+ return NULL;
+}
diff --git a/test/srcs.mk b/test/srcs.mk
index 332ba3f..a68ff94 100644
--- a/test/srcs.mk
+++ b/test/srcs.mk
@@ -18,7 +18,15 @@ TEST_SRCS += mos.c
#
# Mocks
#
-TEST_SRCS += mock/sipsrv.c
+TEST_SRCS += mock/dnssrv.c
+
+TEST_SRCS += sip/aor.c
+TEST_SRCS += sip/auth.c
+TEST_SRCS += sip/domain.c
+TEST_SRCS += sip/location.c
+TEST_SRCS += sip/sipsrv.c
+TEST_SRCS += sip/user.c
+
ifneq ($(USE_TLS),)
TEST_SRCS += mock/cert.c
endif
diff --git a/test/test.h b/test/test.h
index 20ccdcd..62904fe 100644
--- a/test/test.h
+++ b/test/test.h
@@ -66,21 +66,22 @@ extern const char test_certificate[];
/*
- * SIP Server
+ * Mock DNS-Server
*/
-struct sip_server {
- struct sip *sip;
- struct sip_lsnr *lsnr;
- bool terminate;
-
- unsigned n_register_req;
- enum sip_transp tp_last;
+struct dns_server {
+ struct udp_sock *us;
+ struct sa addr;
+ struct list rrl;
+ bool rotate;
};
-int sip_server_alloc(struct sip_server **srvp);
-int sip_server_uri(struct sip_server *srv, char *uri, size_t sz,
- enum sip_transp tp);
+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 */
@@ -89,6 +90,8 @@ 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_ua_register_auth(void);
int test_mos(void);
int test_call_answer(void);
diff --git a/test/ua.c b/test/ua.c
index c039705..2cfbce8 100644
--- a/test/ua.c
+++ b/test/ua.c
@@ -7,20 +7,35 @@
#include <re.h>
#include <baresip.h>
#include "test.h"
+#include "sip/sipsrv.h"
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;
@@ -32,15 +47,24 @@ static void ua_event_handler(struct ua *ua, enum ua_event ev,
if (ev == UA_EVENT_REGISTER_OK) {
+ re_printf("event: Register OK!\n");
+
++t->got_register_ok;
/* verify register success */
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);
}
+ else if (ev == UA_EVENT_REGISTER_FAIL) {
+
+ err = EAUTH;
+ re_cancel();
+ }
out:
if (err) {
@@ -58,13 +82,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 +106,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 +115,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 +125,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 +198,246 @@ 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 = domain_add(t.srvv[0], domain);
+ if (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;
+}
+
+
+#define USER "alfredh"
+#define PASS "password"
+#define DOMAIN "localhost"
+
+static int reg_auth(enum sip_transp tp)
+{
+ struct sa laddr;
+ struct test t;
+ char aor[256];
+ int err;
+
+ memset(&t, 0, sizeof t);
+
+ err = sip_server_alloc(&t.srvv[0]);
+ if (err) {
+ warning("failed to create sip server (%d/%m)\n", err, err);
+ goto out;
+ }
+
+ err = domain_add(t.srvv[0], DOMAIN);
+ TEST_ERR(err);
+
+ err = user_add(domain_lookup(t.srvv[0], DOMAIN)->ht_usr,
+ "alfredh", "password", DOMAIN);
+ TEST_ERR(err);
+
+ t.srvv[0]->auth_enabled = true;
+
+ err = sip_transp_laddr(t.srvv[0]->sip, &laddr, tp, NULL);
+ if (err)
+ return err;
+
+ /* NOTE: angel brackets needed to parse ;transport parameter */
+ if (re_snprintf(aor, sizeof(aor),
+ "<sip:%s:%s@%s>;outbound=\"sip:%J;transport=%s\"",
+ USER,
+ PASS,
+ DOMAIN,
+ &laddr,
+ sip_transp_name(tp)) < 0)
+ return ENOMEM;
+
+ 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;
+ goto out;
+ }
+
+ 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);
+
+
+ return err;
+}
+
+
+int test_ua_register_auth(void)
+{
+ int err;
+
+ err = ua_init("test", true, true, true, false);
+ TEST_ERR(err);
+
+ err |= reg_auth(SIP_TRANSP_UDP);
+ TEST_ERR(err);
+ err |= reg_auth(SIP_TRANSP_TCP);
+ TEST_ERR(err);
+#ifdef USE_TLS
+ err |= reg_auth(SIP_TRANSP_TLS);
+ TEST_ERR(err);
+#endif
+
+ out:
+ ua_stop_all(true);
+ ua_close();
+
+ return err;
+}