/** * @file dialog.c SIP Dialog * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sip.h" enum { ROUTE_OFFSET = 7, X64_STRSIZE = 17, }; struct sip_dialog { struct uri route; struct mbuf *mb; char *callid; char *ltag; char *rtag; char *uri; uint32_t lseq; uint32_t rseq; size_t cpos; }; struct route_enc { struct mbuf *mb; size_t end; }; static int x64_strdup(char **strp, uint64_t val) { char *str; str = mem_alloc(X64_STRSIZE, NULL); if (!str) return ENOMEM; (void)re_snprintf(str, X64_STRSIZE, "%016llx", val); *strp = str; return 0; } static void destructor(void *arg) { struct sip_dialog *dlg = arg; mem_deref(dlg->callid); mem_deref(dlg->ltag); mem_deref(dlg->rtag); mem_deref(dlg->uri); mem_deref(dlg->mb); } int sip_dialog_alloc(struct sip_dialog **dlgp, const char *uri, const char *to_uri, const char *from_name, const char *from_uri, const char *routev[], uint32_t routec) { const uint64_t ltag = rand_u64(); struct sip_dialog *dlg; struct sip_addr addr; size_t rend = 0; struct pl pl; uint32_t i; int err; if (!dlgp || !uri || !to_uri || !from_uri) return EINVAL; dlg = mem_zalloc(sizeof(*dlg), destructor); if (!dlg) return ENOMEM; dlg->lseq = rand_u16(); err = str_dup(&dlg->uri, uri); if (err) goto out; err = x64_strdup(&dlg->callid, rand_u64()); if (err) goto out; err = x64_strdup(&dlg->ltag, ltag); if (err) goto out; dlg->mb = mbuf_alloc(512); if (!dlg->mb) { err = ENOMEM; goto out; } for (i=0; imb, "Route: <%s;lr>\r\n", routev[i]); if (i == 0) rend = dlg->mb->pos - 2; } err |= mbuf_printf(dlg->mb, "To: <%s>\r\n", to_uri); dlg->cpos = dlg->mb->pos; err |= mbuf_printf(dlg->mb, "From: %s%s%s<%s>;tag=%016llx\r\n", from_name ? "\"" : "", from_name, from_name ? "\" " : "", from_uri, ltag); if (err) goto out; dlg->mb->pos = 0; if (rend) { pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET; pl.l = rend - ROUTE_OFFSET; err = sip_addr_decode(&addr, &pl); dlg->route = addr.uri; } else { pl_set_str(&pl, dlg->uri); err = uri_decode(&dlg->route, &pl); } out: if (err) mem_deref(dlg); else *dlgp = dlg; return err; } static bool record_route_handler(const struct sip_hdr *hdr, const struct sip_msg *msg, void *arg) { struct route_enc *renc = arg; (void)msg; if (mbuf_printf(renc->mb, "Route: %r\r\n", &hdr->val)) return true; if (!renc->end) renc->end = renc->mb->pos - 2; return false; } int sip_dialog_accept(struct sip_dialog **dlgp, const struct sip_msg *msg) { const struct sip_hdr *contact; struct sip_dialog *dlg; struct route_enc renc; struct sip_addr addr; struct pl pl; int err; if (!dlgp || !msg || !msg->req) return EINVAL; contact = sip_msg_hdr(msg, SIP_HDR_CONTACT); if (!contact || !msg->callid.p) return EBADMSG; if (sip_addr_decode(&addr, &contact->val)) return EBADMSG; dlg = mem_zalloc(sizeof(*dlg), destructor); if (!dlg) return ENOMEM; dlg->lseq = rand_u16(); dlg->rseq = msg->cseq.num; err = pl_strdup(&dlg->uri, &addr.auri); if (err) goto out; err = pl_strdup(&dlg->callid, &msg->callid); if (err) goto out; err = x64_strdup(&dlg->ltag, msg->tag); if (err) goto out; err = pl_strdup(&dlg->rtag, &msg->from.tag); if (err) goto out; dlg->mb = mbuf_alloc(512); if (!dlg->mb) { err = ENOMEM; goto out; } renc.mb = dlg->mb; renc.end = 0; err |= sip_msg_hdr_apply(msg, true, SIP_HDR_RECORD_ROUTE, record_route_handler, &renc) ? ENOMEM : 0; err |= mbuf_printf(dlg->mb, "To: %r\r\n", &msg->from.val); err |= mbuf_printf(dlg->mb, "From: %r;tag=%016llx\r\n", &msg->to.val, msg->tag); if (err) goto out; dlg->mb->pos = 0; if (renc.end) { pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET; pl.l = renc.end - ROUTE_OFFSET; err = sip_addr_decode(&addr, &pl); dlg->route = addr.uri; } else { pl_set_str(&pl, dlg->uri); err = uri_decode(&dlg->route, &pl); } out: if (err) mem_deref(dlg); else *dlgp = dlg; return err; } int sip_dialog_create(struct sip_dialog *dlg, const struct sip_msg *msg) { char *uri = NULL, *rtag = NULL; const struct sip_hdr *contact; struct route_enc renc; struct sip_addr addr; struct pl pl; int err; if (!dlg || dlg->rtag || !dlg->cpos || !msg) return EINVAL; contact = sip_msg_hdr(msg, SIP_HDR_CONTACT); if (!contact) return EBADMSG; if (sip_addr_decode(&addr, &contact->val)) return EBADMSG; renc.mb = mbuf_alloc(512); if (!renc.mb) return ENOMEM; err = pl_strdup(&uri, &addr.auri); if (err) goto out; err = pl_strdup(&rtag, msg->req ? &msg->from.tag : &msg->to.tag); if (err) goto out; renc.end = 0; err |= sip_msg_hdr_apply(msg, msg->req, SIP_HDR_RECORD_ROUTE, record_route_handler, &renc) ? ENOMEM : 0; err |= mbuf_printf(renc.mb, "To: %r\r\n", msg->req ? &msg->from.val : &msg->to.val); dlg->mb->pos = dlg->cpos; err |= mbuf_write_mem(renc.mb, mbuf_buf(dlg->mb), mbuf_get_left(dlg->mb)); dlg->mb->pos = 0; if (err) goto out; renc.mb->pos = 0; if (renc.end) { pl.p = (const char *)mbuf_buf(renc.mb) + ROUTE_OFFSET; pl.l = renc.end - ROUTE_OFFSET; err = sip_addr_decode(&addr, &pl); if (err) goto out; dlg->route = addr.uri; } else { struct uri tmp; pl_set_str(&pl, uri); err = uri_decode(&tmp, &pl); if (err) goto out; dlg->route = tmp; } mem_deref(dlg->mb); mem_deref(dlg->uri); dlg->mb = mem_ref(renc.mb); dlg->rtag = mem_ref(rtag); dlg->uri = mem_ref(uri); dlg->rseq = msg->req ? msg->cseq.num : 0; dlg->cpos = 0; out: mem_deref(renc.mb); mem_deref(rtag); mem_deref(uri); return err; } int sip_dialog_fork(struct sip_dialog **dlgp, struct sip_dialog *odlg, const struct sip_msg *msg) { const struct sip_hdr *contact; struct sip_dialog *dlg; struct route_enc renc; struct sip_addr addr; struct pl pl; int err; if (!dlgp || !odlg || !odlg->cpos || !msg) return EINVAL; contact = sip_msg_hdr(msg, SIP_HDR_CONTACT); if (!contact || !msg->callid.p) return EBADMSG; if (sip_addr_decode(&addr, &contact->val)) return EBADMSG; dlg = mem_zalloc(sizeof(*dlg), destructor); if (!dlg) return ENOMEM; dlg->callid = mem_ref(odlg->callid); dlg->ltag = mem_ref(odlg->ltag); dlg->lseq = odlg->lseq; dlg->rseq = msg->req ? msg->cseq.num : 0; err = pl_strdup(&dlg->uri, &addr.auri); if (err) goto out; err = pl_strdup(&dlg->rtag, msg->req ? &msg->from.tag : &msg->to.tag); if (err) goto out; dlg->mb = mbuf_alloc(512); if (!dlg->mb) { err = ENOMEM; goto out; } renc.mb = dlg->mb; renc.end = 0; err |= sip_msg_hdr_apply(msg, msg->req, SIP_HDR_RECORD_ROUTE, record_route_handler, &renc) ? ENOMEM : 0; err |= mbuf_printf(dlg->mb, "To: %r\r\n", msg->req ? &msg->from.val : &msg->to.val); odlg->mb->pos = odlg->cpos; err |= mbuf_write_mem(dlg->mb, mbuf_buf(odlg->mb), mbuf_get_left(odlg->mb)); odlg->mb->pos = 0; if (err) goto out; dlg->mb->pos = 0; if (renc.end) { pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET; pl.l = renc.end - ROUTE_OFFSET; err = sip_addr_decode(&addr, &pl); dlg->route = addr.uri; } else { pl_set_str(&pl, dlg->uri); err = uri_decode(&dlg->route, &pl); } out: if (err) mem_deref(dlg); else *dlgp = dlg; return err; } int sip_dialog_update(struct sip_dialog *dlg, const struct sip_msg *msg) { const struct sip_hdr *contact; struct sip_addr addr; char *uri; int err; if (!dlg || !msg) return EINVAL; contact = sip_msg_hdr(msg, SIP_HDR_CONTACT); if (!contact) return EBADMSG; if (sip_addr_decode(&addr, &contact->val)) return EBADMSG; err = pl_strdup(&uri, &addr.auri); if (err) return err; if (dlg->route.scheme.p == dlg->uri) { struct uri tmp; struct pl pl; pl_set_str(&pl, uri); err = uri_decode(&tmp, &pl); if (err) goto out; dlg->route = tmp; } mem_deref(dlg->uri); dlg->uri = mem_ref(uri); out: mem_deref(uri); return err; } bool sip_dialog_rseq_valid(struct sip_dialog *dlg, const struct sip_msg *msg) { if (!dlg || !msg || !msg->req) return false; if (msg->cseq.num < dlg->rseq) return false; dlg->rseq = msg->cseq.num; return true; } int sip_dialog_encode(struct mbuf *mb, struct sip_dialog *dlg, uint32_t cseq, const char *met) { int err = 0; if (!mb || !dlg || !met) return EINVAL; err |= mbuf_write_mem(mb, mbuf_buf(dlg->mb), mbuf_get_left(dlg->mb)); err |= mbuf_printf(mb, "Call-ID: %s\r\n", dlg->callid); err |= mbuf_printf(mb, "CSeq: %u %s\r\n", strcmp(met, "ACK") ? dlg->lseq++ : cseq, met); return err; } const char *sip_dialog_uri(const struct sip_dialog *dlg) { return dlg ? dlg->uri : NULL; } const struct uri *sip_dialog_route(const struct sip_dialog *dlg) { return dlg ? &dlg->route : NULL; } const char *sip_dialog_callid(const struct sip_dialog *dlg) { return dlg ? dlg->callid : NULL; } uint32_t sip_dialog_lseq(const struct sip_dialog *dlg) { return dlg ? dlg->lseq : 0; } bool sip_dialog_established(const struct sip_dialog *dlg) { return dlg && dlg->rtag; } bool sip_dialog_cmp(const struct sip_dialog *dlg, const struct sip_msg *msg) { if (!dlg || !msg) return false; if (pl_strcmp(&msg->callid, dlg->callid)) return false; if (pl_strcmp(msg->req ? &msg->to.tag : &msg->from.tag, dlg->ltag)) return false; if (pl_strcmp(msg->req ? &msg->from.tag : &msg->to.tag, dlg->rtag)) return false; return true; } bool sip_dialog_cmp_half(const struct sip_dialog *dlg, const struct sip_msg *msg) { if (!dlg || !msg) return false; if (pl_strcmp(&msg->callid, dlg->callid)) return false; if (pl_strcmp(msg->req ? &msg->to.tag : &msg->from.tag, dlg->ltag)) return false; return true; }