blob: fe6ba56280af4f22c38e8724193777ec730ae86e [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file subscribe.c SIP Event Subscribe
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <re_types.h>
7#include <re_mem.h>
8#include <re_mbuf.h>
9#include <re_sa.h>
10#include <re_list.h>
11#include <re_hash.h>
12#include <re_fmt.h>
13#include <re_uri.h>
14#include <re_sys.h>
15#include <re_tmr.h>
16#include <re_msg.h>
17#include <re_sip.h>
18#include <re_sipevent.h>
19#include "sipevent.h"
20
21
22enum {
23 DEFAULT_EXPIRES = 3600,
24 RESUB_FAIL_WAIT = 60000,
25 RESUB_FAILC_MAX = 7,
26 NOTIFY_TIMEOUT = 10000,
27};
28
29
30static int request(struct sipsub *sub, bool reset_ls);
31
32
33static void internal_notify_handler(struct sip *sip, const struct sip_msg *msg,
34 void *arg)
35{
36 (void)arg;
37
38 (void)sip_treply(NULL, sip, msg, 200, "OK");
39}
40
41
42static void internal_close_handler(int err, const struct sip_msg *msg,
43 const struct sipevent_substate *substate,
44 void *arg)
45{
46 (void)err;
47 (void)msg;
48 (void)substate;
49 (void)arg;
50}
51
52
53static bool terminate(struct sipsub *sub)
54{
55 sub->terminated = true;
56 sub->forkh = NULL;
57 sub->notifyh = internal_notify_handler;
58 sub->closeh = internal_close_handler;
59
60 if (sub->termwait) {
61 mem_ref(sub);
62 return true;
63 }
64
65 tmr_cancel(&sub->tmr);
66
67 if (sub->req) {
68 mem_ref(sub);
69 return true;
70 }
71
72 if (sub->expires && sub->subscribed && !request(sub, true)) {
73 mem_ref(sub);
74 return true;
75 }
76
77 return false;
78}
79
80
81static void destructor(void *arg)
82{
83 struct sipsub *sub = arg;
84
85 if (!sub->terminated) {
86
87 if (terminate(sub))
88 return;
89 }
90
91 tmr_cancel(&sub->tmr);
92 hash_unlink(&sub->he);
93 mem_deref(sub->req);
94 mem_deref(sub->dlg);
95 mem_deref(sub->auth);
96 mem_deref(sub->event);
97 mem_deref(sub->id);
98 mem_deref(sub->cuser);
99 mem_deref(sub->hdrs);
100 mem_deref(sub->refer_hdrs);
101 mem_deref(sub->sock);
102 mem_deref(sub->sip);
103}
104
105
106static void notify_timeout_handler(void *arg)
107{
108 struct sipsub *sub = arg;
109
110 sub->termwait = false;
111
112 if (sub->terminated)
113 mem_deref(sub);
114 else
115 sipsub_terminate(sub, ETIMEDOUT, NULL, NULL);
116}
117
118
119static void tmr_handler(void *arg)
120{
121 struct sipsub *sub = arg;
122 int err;
123
124 if (sub->req || sub->terminated)
125 return;
126
127 err = request(sub, true);
128 if (err) {
129 if (++sub->failc < RESUB_FAILC_MAX) {
130 sipsub_reschedule(sub, RESUB_FAIL_WAIT);
131 }
132 else {
133 sipsub_terminate(sub, err, NULL, NULL);
134 }
135 }
136}
137
138
139void sipsub_reschedule(struct sipsub *sub, uint64_t wait)
140{
141 tmr_start(&sub->tmr, wait, tmr_handler, sub);
142}
143
144
145void sipsub_terminate(struct sipsub *sub, int err, const struct sip_msg *msg,
146 const struct sipevent_substate *substate)
147{
148 sipsub_close_h *closeh;
149 void *arg;
150
151 closeh = sub->closeh;
152 arg = sub->arg;
153
154 (void)terminate(sub);
155
156 closeh(err, msg, substate, arg);
157}
158
159
160static void response_handler(int err, const struct sip_msg *msg, void *arg)
161{
162 const struct sip_hdr *minexp;
163 struct sipsub *sub = arg;
164
165 if (err || sip_request_loops(&sub->ls, msg->scode))
166 goto out;
167
168 if (msg->scode < 200) {
169 return;
170 }
171 else if (msg->scode < 300) {
172
173 uint32_t wait;
174
175 if (sub->forkh) {
176
177 struct sipsub *fsub;
178
179 fsub = sipsub_find(sub->sock, msg, NULL, true);
180 if (!fsub) {
181
182 err = sub->forkh(&fsub, sub, msg, sub->arg);
183 if (err)
184 return;
185 }
186 else {
187 (void)sip_dialog_update(fsub->dlg, msg);
188 }
189
190 sub = fsub;
191 }
192 else if (!sip_dialog_established(sub->dlg)) {
193
194 err = sip_dialog_create(sub->dlg, msg);
195 if (err) {
196 sub->subscribed = false;
197 goto out;
198 }
199 }
200 else {
201 /* Ignore 2xx responses for other dialogs
202 * if forking is disabled */
203 if (!sip_dialog_cmp(sub->dlg, msg))
204 return;
205
206 (void)sip_dialog_update(sub->dlg, msg);
207 }
208
209 if (!sub->termconf)
210 sub->subscribed = true;
211
212 sub->failc = 0;
213
214 if (!sub->expires && !sub->termconf) {
215
216 tmr_start(&sub->tmr, NOTIFY_TIMEOUT,
217 notify_timeout_handler, sub);
218 sub->termwait = true;
219 return;
220 }
221
222 if (sub->terminated)
223 goto out;
224
225 if (sub->refer) {
226 sub->refer = false;
227 return;
228 }
229
230 if (pl_isset(&msg->expires))
231 wait = pl_u32(&msg->expires);
232 else
233 wait = sub->expires;
234
235 sipsub_reschedule(sub, wait * 900);
236 return;
237 }
238 else {
239 if (sub->terminated && !sub->subscribed)
240 goto out;
241
242 switch (msg->scode) {
243
244 case 401:
245 case 407:
246 err = sip_auth_authenticate(sub->auth, msg);
247 if (err) {
248 err = (err == EAUTH) ? 0 : err;
249 break;
250 }
251
252 err = request(sub, false);
253 if (err)
254 break;
255
256 return;
257
258 case 403:
259 sip_auth_reset(sub->auth);
260 break;
261
262 case 423:
263 minexp = sip_msg_hdr(msg, SIP_HDR_MIN_EXPIRES);
264 if (!minexp || !pl_u32(&minexp->val) || !sub->expires)
265 break;
266
267 sub->expires = pl_u32(&minexp->val);
268
269 err = request(sub, false);
270 if (err)
271 break;
272
273 return;
274
275 case 481:
276 sub->subscribed = false;
277 break;
278 }
279 }
280
281 out:
282 sub->refer = false;
283
284 if (sub->terminated) {
285
286 if (!sub->expires || !sub->subscribed || request(sub, true))
287 mem_deref(sub);
288 }
289 else {
290 if (sub->subscribed && ++sub->failc < RESUB_FAILC_MAX)
291 sipsub_reschedule(sub, RESUB_FAIL_WAIT);
292 else
293 sipsub_terminate(sub, err, msg, NULL);
294 }
295}
296
297
298static int send_handler(enum sip_transp tp, const struct sa *src,
299 const struct sa *dst, struct mbuf *mb, void *arg)
300{
301 struct sip_contact contact;
302 struct sipsub *sub = arg;
303 (void)dst;
304
305 sip_contact_set(&contact, sub->cuser, src, tp);
306
307 return mbuf_printf(mb, "%H", sip_contact_print, &contact);
308}
309
310
311static int print_event(struct re_printf *pf, const struct sipsub *sub)
312{
313 if (sub->id)
314 return re_hprintf(pf, "%s;id=%s", sub->event, sub->id);
315 else
316 return re_hprintf(pf, "%s", sub->event);
317}
318
319
320static int request(struct sipsub *sub, bool reset_ls)
321{
322 if (reset_ls)
323 sip_loopstate_reset(&sub->ls);
324
325 if (sub->refer) {
326
327 sub->refer_cseq = sip_dialog_lseq(sub->dlg);
328
329 return sip_drequestf(&sub->req, sub->sip, true, "REFER",
330 sub->dlg, 0, sub->auth,
331 send_handler, response_handler, sub,
332 "%s"
333 "Content-Length: 0\r\n"
334 "\r\n",
335 sub->refer_hdrs);
336 }
337 else {
338 if (sub->terminated)
339 sub->expires = 0;
340
341 return sip_drequestf(&sub->req, sub->sip, true, "SUBSCRIBE",
342 sub->dlg, 0, sub->auth,
343 send_handler, response_handler, sub,
344 "Event: %H\r\n"
345 "Expires: %u\r\n"
346 "%s"
347 "Content-Length: 0\r\n"
348 "\r\n",
349 print_event, sub,
350 sub->expires,
351 sub->hdrs);
352 }
353}
354
355
356static int sipsub_alloc(struct sipsub **subp, struct sipevent_sock *sock,
357 bool refer, struct sip_dialog *dlg, const char *uri,
358 const char *from_name, const char *from_uri,
359 const char *event, const char *id, uint32_t expires,
360 const char *cuser,
361 const char *routev[], uint32_t routec,
362 sip_auth_h *authh, void *aarg, bool aref,
363 sipsub_fork_h *forkh, sipsub_notify_h *notifyh,
364 sipsub_close_h *closeh, void *arg,
365 const char *fmt, va_list ap)
366{
367 struct sipsub *sub;
368 int err;
369
370 if (!subp || !sock || !event || !cuser)
371 return EINVAL;
372
373 if (!dlg && (!uri || !from_uri))
374 return EINVAL;
375
376 sub = mem_zalloc(sizeof(*sub), destructor);
377 if (!sub)
378 return ENOMEM;
379
380 if (dlg) {
381 sub->dlg = mem_ref(dlg);
382 }
383 else {
384 err = sip_dialog_alloc(&sub->dlg, uri, uri, from_name,
385 from_uri, routev, routec);
386 if (err)
387 goto out;
388 }
389
390 hash_append(sock->ht_sub,
391 hash_joaat_str(sip_dialog_callid(sub->dlg)),
392 &sub->he, sub);
393
394 err = sip_auth_alloc(&sub->auth, authh, aarg, aref);
395 if (err)
396 goto out;
397
398 err = str_dup(&sub->event, event);
399 if (err)
400 goto out;
401
402 if (id) {
403 err = str_dup(&sub->id, id);
404 if (err)
405 goto out;
406 }
407
408 err = str_dup(&sub->cuser, cuser);
409 if (err)
410 goto out;
411
412 if (fmt) {
413 err = re_vsdprintf(refer ? &sub->refer_hdrs : &sub->hdrs,
414 fmt, ap);
415 if (err)
416 goto out;
417 }
418
419 sub->refer_cseq = -1;
420 sub->refer = refer;
421 sub->sock = mem_ref(sock);
422 sub->sip = mem_ref(sock->sip);
423 sub->expires = expires;
424 sub->forkh = forkh;
425 sub->notifyh = notifyh ? notifyh : internal_notify_handler;
426 sub->closeh = closeh ? closeh : internal_close_handler;
427 sub->arg = arg;
428
429 err = request(sub, true);
430 if (err)
431 goto out;
432
433 out:
434 if (err)
435 mem_deref(sub);
436 else
437 *subp = sub;
438
439 return err;
440}
441
442
443/**
444 * Allocate a SIP subscriber client
445 *
446 * @param subp Pointer to allocated SIP subscriber client
447 * @param sock SIP Event socket
448 * @param uri SIP Request URI
449 * @param from_name SIP From-header Name (optional)
450 * @param from_uri SIP From-header URI
451 * @param event SIP Event to subscribe to
452 * @param id SIP Event ID (optional)
453 * @param expires Subscription expires value
454 * @param cuser Contact username or URI
455 * @param routev Optional route vector
456 * @param routec Number of routes
457 * @param authh Authentication handler
458 * @param aarg Authentication handler argument
459 * @param aref True to ref argument
460 * @param forkh Fork handler
461 * @param notifyh Notify handler
462 * @param closeh Close handler
463 * @param arg Response handler argument
464 * @param fmt Formatted strings with extra SIP Headers
465 *
466 * @return 0 if success, otherwise errorcode
467 */
468int sipevent_subscribe(struct sipsub **subp, struct sipevent_sock *sock,
469 const char *uri, const char *from_name,
470 const char *from_uri, const char *event, const char *id,
471 uint32_t expires, const char *cuser,
472 const char *routev[], uint32_t routec,
473 sip_auth_h *authh, void *aarg, bool aref,
474 sipsub_fork_h *forkh, sipsub_notify_h *notifyh,
475 sipsub_close_h *closeh, void *arg,
476 const char *fmt, ...)
477{
478 va_list ap;
479 int err;
480
481 va_start(ap, fmt);
482 err = sipsub_alloc(subp, sock, false, NULL, uri, from_name, from_uri,
483 event, id, expires, cuser,
484 routev, routec, authh, aarg, aref, forkh, notifyh,
485 closeh, arg, fmt, ap);
486 va_end(ap);
487
488 return err;
489}
490
491
492/**
493 * Allocate a SIP subscriber client using an existing dialog
494 *
495 * @param subp Pointer to allocated SIP subscriber client
496 * @param sock SIP Event socket
497 * @param dlg Established SIP Dialog
498 * @param event SIP Event to subscribe to
499 * @param id SIP Event ID (optional)
500 * @param expires Subscription expires value
501 * @param cuser Contact username or URI
502 * @param authh Authentication handler
503 * @param aarg Authentication handler argument
504 * @param aref True to ref argument
505 * @param notifyh Notify handler
506 * @param closeh Close handler
507 * @param arg Response handler argument
508 * @param fmt Formatted strings with extra SIP Headers
509 *
510 * @return 0 if success, otherwise errorcode
511 */
512int sipevent_dsubscribe(struct sipsub **subp, struct sipevent_sock *sock,
513 struct sip_dialog *dlg, const char *event,
514 const char *id, uint32_t expires, const char *cuser,
515 sip_auth_h *authh, void *aarg, bool aref,
516 sipsub_notify_h *notifyh, sipsub_close_h *closeh,
517 void *arg, const char *fmt, ...)
518{
519 va_list ap;
520 int err;
521
522 va_start(ap, fmt);
523 err = sipsub_alloc(subp, sock, false, dlg, NULL, NULL, NULL,
524 event, id, expires, cuser,
525 NULL, 0, authh, aarg, aref, NULL, notifyh,
526 closeh, arg, fmt, ap);
527 va_end(ap);
528
529 return err;
530}
531
532
533/**
534 * Allocate a SIP refer client
535 *
536 * @param subp Pointer to allocated SIP subscriber client
537 * @param sock SIP Event socket
538 * @param uri SIP Request URI
539 * @param from_name SIP From-header Name (optional)
540 * @param from_uri SIP From-header URI
541 * @param cuser Contact username or URI
542 * @param routev Optional route vector
543 * @param routec Number of routes
544 * @param authh Authentication handler
545 * @param aarg Authentication handler argument
546 * @param aref True to ref argument
547 * @param forkh Fork handler
548 * @param notifyh Notify handler
549 * @param closeh Close handler
550 * @param arg Response handler argument
551 * @param fmt Formatted strings with extra SIP Headers
552 *
553 * @return 0 if success, otherwise errorcode
554 */
555int sipevent_refer(struct sipsub **subp, struct sipevent_sock *sock,
556 const char *uri, const char *from_name,
557 const char *from_uri, const char *cuser,
558 const char *routev[], uint32_t routec,
559 sip_auth_h *authh, void *aarg, bool aref,
560 sipsub_fork_h *forkh, sipsub_notify_h *notifyh,
561 sipsub_close_h *closeh, void *arg,
562 const char *fmt, ...)
563{
564 va_list ap;
565 int err;
566
567 va_start(ap, fmt);
568 err = sipsub_alloc(subp, sock, true, NULL, uri, from_name, from_uri,
569 "refer", NULL, DEFAULT_EXPIRES, cuser,
570 routev, routec, authh, aarg, aref, forkh, notifyh,
571 closeh, arg, fmt, ap);
572 va_end(ap);
573
574 return err;
575}
576
577
578/**
579 * Allocate a SIP refer client using an existing dialog
580 *
581 * @param subp Pointer to allocated SIP subscriber client
582 * @param sock SIP Event socket
583 * @param dlg Established SIP Dialog
584 * @param cuser Contact username or URI
585 * @param authh Authentication handler
586 * @param aarg Authentication handler argument
587 * @param aref True to ref argument
588 * @param notifyh Notify handler
589 * @param closeh Close handler
590 * @param arg Response handler argument
591 * @param fmt Formatted strings with extra SIP Headers
592 *
593 * @return 0 if success, otherwise errorcode
594 */
595int sipevent_drefer(struct sipsub **subp, struct sipevent_sock *sock,
596 struct sip_dialog *dlg, const char *cuser,
597 sip_auth_h *authh, void *aarg, bool aref,
598 sipsub_notify_h *notifyh, sipsub_close_h *closeh,
599 void *arg, const char *fmt, ...)
600{
601 va_list ap;
602 int err;
603
604 va_start(ap, fmt);
605 err = sipsub_alloc(subp, sock, true, dlg, NULL, NULL, NULL,
606 "refer", NULL, DEFAULT_EXPIRES, cuser,
607 NULL, 0, authh, aarg, aref, NULL, notifyh,
608 closeh, arg, fmt, ap);
609 va_end(ap);
610
611 return err;
612}
613
614
615int sipevent_fork(struct sipsub **subp, struct sipsub *osub,
616 const struct sip_msg *msg,
617 sip_auth_h *authh, void *aarg, bool aref,
618 sipsub_notify_h *notifyh, sipsub_close_h *closeh,
619 void *arg)
620{
621 struct sipsub *sub;
622 int err;
623
624 if (!subp || !osub || !msg)
625 return EINVAL;
626
627 sub = mem_zalloc(sizeof(*sub), destructor);
628 if (!sub)
629 return ENOMEM;
630
631 err = sip_dialog_fork(&sub->dlg, osub->dlg, msg);
632 if (err)
633 goto out;
634
635 hash_append(osub->sock->ht_sub,
636 hash_joaat_str(sip_dialog_callid(sub->dlg)),
637 &sub->he, sub);
638
639 err = sip_auth_alloc(&sub->auth, authh, aarg, aref);
640 if (err)
641 goto out;
642
643 sub->event = mem_ref(osub->event);
644 sub->id = mem_ref(osub->id);
645 sub->cuser = mem_ref(osub->cuser);
646 sub->hdrs = mem_ref(osub->hdrs);
647 sub->refer = osub->refer;
648 sub->sock = mem_ref(osub->sock);
649 sub->sip = mem_ref(osub->sip);
650 sub->expires = osub->expires;
651 sub->forkh = NULL;
652 sub->notifyh = notifyh ? notifyh : internal_notify_handler;
653 sub->closeh = closeh ? closeh : internal_close_handler;
654 sub->arg = arg;
655
656 if (!sub->expires) {
657 tmr_start(&sub->tmr, NOTIFY_TIMEOUT,
658 notify_timeout_handler, sub);
659 sub->termwait = true;
660 }
661
662 out:
663 if (err)
664 mem_deref(sub);
665 else
666 *subp = sub;
667
668 return err;
669}