From e0a4162a77cb44d23008c507ea74747416e29c64 Mon Sep 17 00:00:00 2001 From: Richard Aas Date: Mon, 14 Nov 2011 14:03:31 +0000 Subject: [PATCH] sipevent: initial sipsub framework --- Makefile | 2 +- include/re.h | 1 + include/re_sipevent.h | 15 +++ src/sipevent/mod.mk | 7 + src/sipevent/sub.c | 307 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 include/re_sipevent.h create mode 100644 src/sipevent/mod.mk create mode 100644 src/sipevent/sub.c diff --git a/Makefile b/Makefile index ac52c76..2c7ba73 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ MK := mk/re.mk include $(MK) # List of modules -MODULES += sip sipreg sipsess +MODULES += sip sipevent sipreg sipsess MODULES += uri httpauth MODULES += stun turn ice MODULES += natbd diff --git a/include/re.h b/include/re.h index ffca789..4aa80ef 100644 --- a/include/re.h +++ b/include/re.h @@ -36,6 +36,7 @@ extern "C" { #include "re_sdp.h" #include "re_uri.h" #include "re_sip.h" +#include "re_sipevent.h" #include "re_sipreg.h" #include "re_sipsess.h" #include "re_stun.h" diff --git a/include/re_sipevent.h b/include/re_sipevent.h new file mode 100644 index 0000000..cce9244 --- /dev/null +++ b/include/re_sipevent.h @@ -0,0 +1,15 @@ +/** + * @file re_sipevent.h SIP Event Framework + * + * Copyright (C) 2010 Creytiv.com + */ + +struct sipsub; + +int sipevent_subscribe(struct sipsub **subp, struct sip *sip, const char *uri, + const char *from_name, const char *from_uri, + const char *event, uint32_t expires, const char *cuser, + const char *routev[], uint32_t routec, + sip_auth_h *authh, void *aarg, bool aref, + sip_resp_h *resph, void *arg, + const char *fmt, ...); diff --git a/src/sipevent/mod.mk b/src/sipevent/mod.mk new file mode 100644 index 0000000..6291b76 --- /dev/null +++ b/src/sipevent/mod.mk @@ -0,0 +1,7 @@ +# +# mod.mk +# +# Copyright (C) 2010 Creytiv.com +# + +SRCS += sipevent/sub.c diff --git a/src/sipevent/sub.c b/src/sipevent/sub.c new file mode 100644 index 0000000..fdbac85 --- /dev/null +++ b/src/sipevent/sub.c @@ -0,0 +1,307 @@ +/** + * @file sub.c SIP Subscription + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +enum { + DEFAULT_EXPIRES = 3600, +}; + + +/** Defines a SIP subscriber client */ +struct sipsub { + struct sip_loopstate ls; + struct tmr tmr; + struct sip *sip; + struct sip_request *req; + struct sip_dialog *dlg; + struct sip_auth *auth; + char *event; + char *cuser; + char *hdrs; + sip_resp_h *resph; + void *arg; + uint32_t expires; + uint32_t failc; + bool subscribed; + bool terminated; +}; + + +static int request(struct sipsub *sub, bool reset_ls); + + +static void dummy_handler(int err, const struct sip_msg *msg, void *arg) +{ + (void)err; + (void)msg; + (void)arg; +} + + +static void destructor(void *arg) +{ + struct sipsub *sub = arg; + + tmr_cancel(&sub->tmr); + + if (!sub->terminated) { + + sub->resph = dummy_handler; + sub->terminated = true; + + if (sub->req) { + mem_ref(sub); + return; + } + + if (sub->subscribed && !request(sub, true)) { + mem_ref(sub); + return; + } + } + + mem_deref(sub->dlg); + mem_deref(sub->auth); + mem_deref(sub->event); + mem_deref(sub->cuser); + mem_deref(sub->sip); + mem_deref(sub->hdrs); +} + + +static uint32_t failwait(uint32_t failc) +{ + return min(1800, (30 * (1<tmr, failwait(++sub->failc), tmr_handler, sub); + sub->resph(err, NULL, sub->arg); + } +} + + +static void response_handler(int err, const struct sip_msg *msg, void *arg) +{ + const struct sip_hdr *minexp; + struct sipsub *sub = arg; + uint32_t wait; + + wait = failwait(sub->failc + 1); + + if (err || sip_request_loops(&sub->ls, msg->scode)) { + sub->failc++; + goto out; + } + + if (msg->scode < 200) { + return; + } + else if (msg->scode < 300) { + + sub->subscribed = true; + sub->failc = 0; + + if (pl_isset(&msg->expires)) + wait = pl_u32(&msg->expires); + else + wait = DEFAULT_EXPIRES; + + wait *= 900; + } + else { + if (sub->terminated && !sub->subscribed) + goto out; + + switch (msg->scode) { + + case 401: + case 407: + err = sip_auth_authenticate(sub->auth, msg); + if (err) { + err = (err == EAUTH) ? 0 : err; + break; + } + + err = request(sub, false); + if (err) + break; + + return; + + case 403: + sip_auth_reset(sub->auth); + break; + + case 423: + minexp = sip_msg_hdr(msg, SIP_HDR_MIN_EXPIRES); + if (!minexp || !pl_u32(&minexp->val) || !sub->expires) + break; + + sub->expires = pl_u32(&minexp->val); + + err = request(sub, false); + if (err) + break; + + return; + } + + ++sub->failc; + } + + out: + if (!sub->expires) { + mem_deref(sub); + } + else if (sub->terminated) { + if (!sub->subscribed || request(sub, true)) + mem_deref(sub); + } + else { + tmr_start(&sub->tmr, wait, tmr_handler, sub); + sub->resph(err, msg, sub->arg); + } +} + + +static int send_handler(enum sip_transp tp, const struct sa *src, + const struct sa *dst, struct mbuf *mb, void *arg) +{ + struct sipsub *sub = arg; + (void)dst; + + return mbuf_printf(mb, "Contact: \r\n", + sub->cuser, src, sip_transp_param(tp)); +} + + +static int request(struct sipsub *sub, bool reset_ls) +{ + if (sub->terminated) + sub->expires = 0; + + if (reset_ls) + sip_loopstate_reset(&sub->ls); + + return sip_drequestf(&sub->req, sub->sip, true, "SUBSCRIBE", sub->dlg, + 0, sub->auth, send_handler, response_handler, sub, + "Event: %s\r\n" + "Expires: %u\r\n" + "%s" + "Content-Length: 0\r\n" + "\r\n", + sub->event, + sub->expires, + sub->hdrs); +} + + +/** + * Allocate a SIP subscriber client + * + * @param subp Pointer to allocated SIP subscriber client + * @param sip SIP Stack instance + * @param uri SIP Request URI + * @param from_name SIP From-header Name (optional) + * @param from_uri SIP From-header URI + * @param event SIP Event to subscribe to + * @param expires Subscription expires value + * @param cuser Contact username + * @param routev Optional route vector + * @param routec Number of routes + * @param authh Authentication handler + * @param aarg Authentication handler argument + * @param aref True to ref argument + * @param resph Response handler + * @param arg Response handler argument + * @param fmt Formatted strings with extra SIP Headers + * + * @return 0 if success, otherwise errorcode + */ +int sipevent_subscribe(struct sipsub **subp, struct sip *sip, const char *uri, + const char *from_name, const char *from_uri, + const char *event, uint32_t expires, const char *cuser, + const char *routev[], uint32_t routec, + sip_auth_h *authh, void *aarg, bool aref, + sip_resp_h *resph, void *arg, + const char *fmt, ...) +{ + struct sipsub *sub; + int err; + + if (!subp || !sip || !uri || !from_uri || !event || !expires || !cuser) + return EINVAL; + + sub = mem_zalloc(sizeof(*sub), destructor); + if (!sub) + return ENOMEM; + + err = sip_dialog_alloc(&sub->dlg, uri, uri, from_name, from_uri, + routev, routec); + if (err) + goto out; + + err = sip_auth_alloc(&sub->auth, authh, aarg, aref); + if (err) + goto out; + + err = str_dup(&sub->event, event); + if (err) + goto out; + + err = str_dup(&sub->cuser, cuser); + if (err) + goto out; + + /* Custom SIP headers */ + if (fmt) { + va_list ap; + + va_start(ap, fmt); + err = re_vsdprintf(&sub->hdrs, fmt, ap); + va_end(ap); + + if (err) + goto out; + } + + sub->sip = mem_ref(sip); + sub->expires = expires; + sub->resph = resph ? resph : dummy_handler; + sub->arg = arg; + + err = request(sub, true); + if (err) + goto out; + + out: + if (err) + mem_deref(sub); + else + *subp = sub; + + return err; +}