1 /**
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 
22 enum {
23 	DEFAULT_EXPIRES = 3600,
24 	RESUB_FAIL_WAIT = 60000,
25 	RESUB_FAILC_MAX = 7,
26 	NOTIFY_TIMEOUT  = 10000,
27 };
28 
29 
30 static int request(struct sipsub *sub, bool reset_ls);
31 
32 
internal_notify_handler(struct sip * sip,const struct sip_msg * msg,void * arg)33 static 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 
internal_close_handler(int err,const struct sip_msg * msg,const struct sipevent_substate * substate,void * arg)42 static 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 
terminate(struct sipsub * sub)53 static 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 
destructor(void * arg)81 static 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 
notify_timeout_handler(void * arg)106 static 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 
tmr_handler(void * arg)119 static 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 
sipsub_reschedule(struct sipsub * sub,uint64_t wait)139 void sipsub_reschedule(struct sipsub *sub, uint64_t wait)
140 {
141 	tmr_start(&sub->tmr, wait, tmr_handler, sub);
142 }
143 
144 
sipsub_terminate(struct sipsub * sub,int err,const struct sip_msg * msg,const struct sipevent_substate * substate)145 void 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 
response_handler(int err,const struct sip_msg * msg,void * arg)160 static 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 
send_handler(enum sip_transp tp,const struct sa * src,const struct sa * dst,struct mbuf * mb,void * arg)298 static 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 
print_event(struct re_printf * pf,const struct sipsub * sub)311 static 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 
request(struct sipsub * sub,bool reset_ls)320 static 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 
sipsub_alloc(struct sipsub ** subp,struct sipevent_sock * sock,bool refer,struct sip_dialog * dlg,const char * uri,const char * from_name,const char * from_uri,const char * event,const char * id,uint32_t expires,const char * cuser,const char * routev[],uint32_t routec,sip_auth_h * authh,void * aarg,bool aref,sipsub_fork_h * forkh,sipsub_notify_h * notifyh,sipsub_close_h * closeh,void * arg,const char * fmt,va_list ap)356 static 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  */
sipevent_subscribe(struct sipsub ** subp,struct sipevent_sock * sock,const char * uri,const char * from_name,const char * from_uri,const char * event,const char * id,uint32_t expires,const char * cuser,const char * routev[],uint32_t routec,sip_auth_h * authh,void * aarg,bool aref,sipsub_fork_h * forkh,sipsub_notify_h * notifyh,sipsub_close_h * closeh,void * arg,const char * fmt,...)468 int 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  */
sipevent_dsubscribe(struct sipsub ** subp,struct sipevent_sock * sock,struct sip_dialog * dlg,const char * event,const char * id,uint32_t expires,const char * cuser,sip_auth_h * authh,void * aarg,bool aref,sipsub_notify_h * notifyh,sipsub_close_h * closeh,void * arg,const char * fmt,...)512 int 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  */
sipevent_refer(struct sipsub ** subp,struct sipevent_sock * sock,const char * uri,const char * from_name,const char * from_uri,const char * cuser,const char * routev[],uint32_t routec,sip_auth_h * authh,void * aarg,bool aref,sipsub_fork_h * forkh,sipsub_notify_h * notifyh,sipsub_close_h * closeh,void * arg,const char * fmt,...)555 int 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  */
sipevent_drefer(struct sipsub ** subp,struct sipevent_sock * sock,struct sip_dialog * dlg,const char * cuser,sip_auth_h * authh,void * aarg,bool aref,sipsub_notify_h * notifyh,sipsub_close_h * closeh,void * arg,const char * fmt,...)595 int 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 
sipevent_fork(struct sipsub ** subp,struct sipsub * osub,const struct sip_msg * msg,sip_auth_h * authh,void * aarg,bool aref,sipsub_notify_h * notifyh,sipsub_close_h * closeh,void * arg)615 int 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 }
670