/** * @file sip/sipsrv.c Mock SIP server * * Copyright (C) 2010 - 2015 Creytiv.com */ #include #include #include #include #include "../test.h" #include "sipsrv.h" #define DEBUG_MODULE "mock/sipsrv" #define DEBUG_LEVEL 5 #include #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, "", &laddr, sip_transp_param(tp)) < 0) return ENOMEM; return 0; }