blob: aca2935e240144a363e1e9fc8375729d4605a446 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file sip/request.c SIP Request
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_fmt.h>
12#include <re_dns.h>
13#include <re_uri.h>
14#include <re_sys.h>
15#include <re_udp.h>
16#include <re_msg.h>
17#include <re_sip.h>
18#include "sip.h"
19
20
21struct sip_request {
22 struct le le;
23 struct list cachel;
24 struct list addrl;
25 struct list srvl;
26 struct sip_request **reqp;
27 struct sip_ctrans *ct;
28 struct dns_query *dnsq;
29 struct dns_query *dnsq2;
30 struct sip *sip;
31 char *met;
32 char *uri;
33 char *host;
34 struct mbuf *mb;
35 sip_send_h *sendh;
36 sip_resp_h *resph;
37 void *arg;
38 size_t sortkey;
39 enum sip_transp tp;
40 bool tp_selected;
41 bool stateful;
42 bool canceled;
43 bool provrecv;
44 uint16_t port;
45};
46
47
48static int request_next(struct sip_request *req);
49static bool rr_append_handler(struct dnsrr *rr, void *arg);
50static void srv_handler(int err, const struct dnshdr *hdr, struct list *ansl,
51 struct list *authl, struct list *addl, void *arg);
52static int srv_lookup(struct sip_request *req, const char *domain);
53static int addr_lookup(struct sip_request *req, const char *name);
54
55
56static int str_ldup(char **dst, const char *src, int len)
57{
58 struct pl pl;
59
60 pl.p = src;
61 pl.l = len < 0 ? str_len(src) : (size_t)len;
62
63 return pl_strdup(dst, &pl);
64}
65
66
67static void destructor(void *arg)
68{
69 struct sip_request *req = arg;
70
71 if (req->reqp && req->stateful) {
72 /* user does deref before request has completed */
73 *req->reqp = NULL;
74 req->reqp = NULL;
75 req->sendh = NULL;
76 req->resph = NULL;
77 sip_request_cancel(mem_ref(req));
78 return;
79 }
80
81 list_flush(&req->cachel);
82 list_flush(&req->addrl);
83 list_flush(&req->srvl);
84 list_unlink(&req->le);
85 mem_deref(req->dnsq);
86 mem_deref(req->dnsq2);
87 mem_deref(req->ct);
88 mem_deref(req->met);
89 mem_deref(req->uri);
90 mem_deref(req->host);
91 mem_deref(req->mb);
92}
93
94
95static void terminate(struct sip_request *req, int err,
96 const struct sip_msg *msg)
97{
98 if (req->reqp) {
99 *req->reqp = NULL;
100 req->reqp = NULL;
101 }
102
103 list_unlink(&req->le);
104 req->sendh = NULL;
105
106 if (req->resph) {
107 req->resph(err, msg, req->arg);
108 req->resph = NULL;
109 }
110}
111
112
113static bool close_handler(struct le *le, void *arg)
114{
115 struct sip_request *req = le->data;
116 (void)arg;
117
118 req->dnsq = mem_deref(req->dnsq);
119 req->dnsq2 = mem_deref(req->dnsq2);
120 req->ct = mem_deref(req->ct);
121
122 terminate(req, ECONNABORTED, NULL);
123 mem_deref(req);
124
125 return false;
126}
127
128
129static void response_handler(int err, const struct sip_msg *msg, void *arg)
130{
131 struct sip_request *req = arg;
132
133 if (msg && msg->scode < 200) {
134 if (!req->provrecv) {
135 req->provrecv = true;
136 if (req->canceled)
137 (void)sip_ctrans_cancel(req->ct);
138 }
139
140 if (req->resph)
141 req->resph(err, msg, req->arg);
142
143 return;
144 }
145
146 req->ct = NULL;
147
148 if (!req->canceled && (err || msg->scode == 503) &&
149 (req->addrl.head || req->srvl.head)) {
150
151 err = request_next(req);
152 if (!err)
153 return;
154 }
155
156 terminate(req, err, msg);
157 mem_deref(req);
158}
159
160
161static int request(struct sip_request *req, enum sip_transp tp,
162 const struct sa *dst)
163{
164 struct mbuf *mb = NULL;
165 char *branch = NULL;
166 int err = ENOMEM;
167 struct sa laddr;
168
169 req->provrecv = false;
170
171 branch = mem_alloc(24, NULL);
172 mb = mbuf_alloc(1024);
173
174 if (!branch || !mb)
175 goto out;
176
177 (void)re_snprintf(branch, 24, "z9hG4bK%016llx", rand_u64());
178
179 err = sip_transp_laddr(req->sip, &laddr, tp, dst);
180 if (err)
181 goto out;
182
183 err = mbuf_printf(mb, "%s %s SIP/2.0\r\n", req->met, req->uri);
184 err |= mbuf_printf(mb, "Via: SIP/2.0/%s %J;branch=%s;rport\r\n",
185 sip_transp_name(tp), &laddr, branch);
186 err |= req->sendh ? req->sendh(tp, &laddr, dst, mb, req->arg) : 0;
187 err |= mbuf_write_mem(mb, mbuf_buf(req->mb), mbuf_get_left(req->mb));
188 if (err)
189 goto out;
190
191 mb->pos = 0;
192
193 if (!req->stateful)
194 err = sip_send(req->sip, NULL, tp, dst, mb);
195 else
196 err = sip_ctrans_request(&req->ct, req->sip, tp, dst, req->met,
197 branch, mb, response_handler, req);
198 if (err)
199 goto out;
200
201 out:
202 mem_deref(branch);
203 mem_deref(mb);
204
205 return err;
206}
207
208
209static int request_next(struct sip_request *req)
210{
211 struct dnsrr *rr;
212 struct sa dst;
213 int err;
214
215 again:
216 rr = list_ledata(req->addrl.head);
217 if (!rr) {
218 rr = list_ledata(req->srvl.head);
219 if (!rr)
220 return ENOENT;
221
222 req->port = rr->rdata.srv.port;
223
224 dns_rrlist_apply2(&req->cachel, rr->rdata.srv.target,
225 DNS_TYPE_A, DNS_TYPE_AAAA, DNS_CLASS_IN,
226 true, rr_append_handler, &req->addrl);
227
228 list_unlink(&rr->le);
229
230 if (req->addrl.head) {
231 dns_rrlist_sort_addr(&req->addrl, req->sortkey);
232 mem_deref(rr);
233 goto again;
234 }
235
236 err = addr_lookup(req, rr->rdata.srv.target);
237 mem_deref(rr);
238
239 return err;
240 }
241
242 switch (rr->type) {
243
244 case DNS_TYPE_A:
245 sa_set_in(&dst, rr->rdata.a.addr, req->port);
246 break;
247
248 case DNS_TYPE_AAAA:
249 sa_set_in6(&dst, rr->rdata.aaaa.addr, req->port);
250 break;
251
252 default:
253 return EINVAL;
254 }
255
256 list_unlink(&rr->le);
257 mem_deref(rr);
258
259 err = request(req, req->tp, &dst);
260 if (err) {
261 if (req->addrl.head || req->srvl.head)
262 goto again;
263 }
264 else if (!req->stateful) {
265 req->resph = NULL;
266 terminate(req, 0, NULL);
267 mem_deref(req);
268 }
269
270 return err;
271}
272
273
274static bool transp_next(struct sip *sip, enum sip_transp *tp)
275{
276 enum sip_transp i;
277
278 for (i=(enum sip_transp)(*tp+1); i<SIP_TRANSPC; i++) {
279
280 if (!sip_transp_supported(sip, i, AF_UNSPEC))
281 continue;
282
283 *tp = i;
284 return true;
285 }
286
287 return false;
288}
289
290
291static bool transp_next_srv(struct sip *sip, enum sip_transp *tp)
292{
293 enum sip_transp i;
294
295 for (i=(enum sip_transp)(*tp-1); i>SIP_TRANSP_NONE; i--) {
296
297 if (!sip_transp_supported(sip, i, AF_UNSPEC))
298 continue;
299
300 *tp = i;
301 return true;
302 }
303
304 return false;
305}
306
307
308static bool rr_append_handler(struct dnsrr *rr, void *arg)
309{
310 struct list *lst = arg;
311
312 switch (rr->type) {
313
314 case DNS_TYPE_A:
315 case DNS_TYPE_AAAA:
316 case DNS_TYPE_SRV:
317 if (rr->le.list)
318 break;
319
320 list_append(lst, &rr->le, mem_ref(rr));
321 break;
322 }
323
324 return false;
325}
326
327
328static bool rr_cache_handler(struct dnsrr *rr, void *arg)
329{
330 struct sip_request *req = arg;
331
332 switch (rr->type) {
333
334 case DNS_TYPE_A:
335 if (!sip_transp_supported(req->sip, req->tp, AF_INET))
336 break;
337
338 list_unlink(&rr->le_priv);
339 list_append(&req->cachel, &rr->le_priv, rr);
340 break;
341
342#ifdef HAVE_INET6
343 case DNS_TYPE_AAAA:
344 if (!sip_transp_supported(req->sip, req->tp, AF_INET6))
345 break;
346
347 list_unlink(&rr->le_priv);
348 list_append(&req->cachel, &rr->le_priv, rr);
349 break;
350#endif
351
352 case DNS_TYPE_CNAME:
353 list_unlink(&rr->le_priv);
354 list_append(&req->cachel, &rr->le_priv, rr);
355 break;
356 }
357
358 return false;
359}
360
361
362static bool rr_naptr_handler(struct dnsrr *rr, void *arg)
363{
364 struct sip_request *req = arg;
365 enum sip_transp tp;
366
367 if (rr->type != DNS_TYPE_NAPTR)
368 return false;
369
370 if (!str_casecmp(rr->rdata.naptr.services, "SIP+D2U"))
371 tp = SIP_TRANSP_UDP;
372 else if (!str_casecmp(rr->rdata.naptr.services, "SIP+D2T"))
373 tp = SIP_TRANSP_TCP;
374 else if (!str_casecmp(rr->rdata.naptr.services, "SIPS+D2T"))
375 tp = SIP_TRANSP_TLS;
376 else
377 return false;
378
379 if (!sip_transp_supported(req->sip, tp, AF_UNSPEC))
380 return false;
381
382 req->tp = tp;
383 req->tp_selected = true;
384
385 return true;
386}
387
388
389static void naptr_handler(int err, const struct dnshdr *hdr, struct list *ansl,
390 struct list *authl, struct list *addl, void *arg)
391{
392 struct sip_request *req = arg;
393 struct dnsrr *rr;
394 (void)hdr;
395 (void)authl;
396
397 dns_rrlist_sort(ansl, DNS_TYPE_NAPTR, req->sortkey);
398
399 rr = dns_rrlist_apply(ansl, NULL, DNS_TYPE_NAPTR, DNS_CLASS_IN, false,
400 rr_naptr_handler, req);
401 if (!rr) {
402 req->tp = SIP_TRANSPC;
403 if (!transp_next_srv(req->sip, &req->tp)) {
404 err = EPROTONOSUPPORT;
405 goto fail;
406 }
407
408 err = srv_lookup(req, req->host);
409 if (err)
410 goto fail;
411
412 return;
413 }
414
415 dns_rrlist_apply(addl, rr->rdata.naptr.replace, DNS_TYPE_SRV,
416 DNS_CLASS_IN, true, rr_append_handler, &req->srvl);
417
418 if (!req->srvl.head) {
419 err = dnsc_query(&req->dnsq, req->sip->dnsc,
420 rr->rdata.naptr.replace, DNS_TYPE_SRV,
421 DNS_CLASS_IN, true, srv_handler, req);
422 if (err)
423 goto fail;
424
425 return;
426 }
427
428 dns_rrlist_sort(&req->srvl, DNS_TYPE_SRV, req->sortkey);
429
430 dns_rrlist_apply(addl, NULL, DNS_QTYPE_ANY, DNS_CLASS_IN, false,
431 rr_cache_handler, req);
432
433 err = request_next(req);
434 if (err)
435 goto fail;
436
437 return;
438
439 fail:
440 terminate(req, err, NULL);
441 mem_deref(req);
442}
443
444
445static void srv_handler(int err, const struct dnshdr *hdr, struct list *ansl,
446 struct list *authl, struct list *addl, void *arg)
447{
448 struct sip_request *req = arg;
449 (void)hdr;
450 (void)authl;
451
452 dns_rrlist_apply(ansl, NULL, DNS_TYPE_SRV, DNS_CLASS_IN, false,
453 rr_append_handler, &req->srvl);
454
455 if (!req->srvl.head) {
456 if (!req->tp_selected) {
457 if (transp_next_srv(req->sip, &req->tp)) {
458
459 err = srv_lookup(req, req->host);
460 if (err)
461 goto fail;
462
463 return;
464 }
465
466 req->tp = SIP_TRANSP_NONE;
467 if (!transp_next(req->sip, &req->tp)) {
468 err = EPROTONOSUPPORT;
469 goto fail;
470 }
471 }
472
473 req->port = sip_transp_port(req->tp, 0);
474
475 err = addr_lookup(req, req->host);
476 if (err)
477 goto fail;
478
479 return;
480 }
481
482 dns_rrlist_sort(&req->srvl, DNS_TYPE_SRV, req->sortkey);
483
484 dns_rrlist_apply(addl, NULL, DNS_QTYPE_ANY, DNS_CLASS_IN, false,
485 rr_cache_handler, req);
486
487 err = request_next(req);
488 if (err)
489 goto fail;
490
491 return;
492
493 fail:
494 terminate(req, err, NULL);
495 mem_deref(req);
496}
497
498
499static void addr_handler(int err, const struct dnshdr *hdr, struct list *ansl,
500 struct list *authl, struct list *addl, void *arg)
501{
502 struct sip_request *req = arg;
503 (void)hdr;
504 (void)authl;
505 (void)addl;
506
507 dns_rrlist_apply2(ansl, NULL, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_CLASS_IN,
508 false, rr_append_handler, &req->addrl);
509
510 /* wait for other (A/AAAA) query to complete */
511 if (req->dnsq || req->dnsq2)
512 return;
513
514 if (!req->addrl.head && !req->srvl.head) {
515 err = err ? err : EDESTADDRREQ;
516 goto fail;
517 }
518
519 dns_rrlist_sort_addr(&req->addrl, req->sortkey);
520
521 err = request_next(req);
522 if (err)
523 goto fail;
524
525 return;
526
527 fail:
528 terminate(req, err, NULL);
529 mem_deref(req);
530}
531
532
533static int srv_lookup(struct sip_request *req, const char *domain)
534{
535 char name[256];
536
537 if (re_snprintf(name, sizeof(name), "%s.%s",
538 sip_transp_srvid(req->tp), domain) < 0)
539 return ENOMEM;
540
541 return dnsc_query(&req->dnsq, req->sip->dnsc, name, DNS_TYPE_SRV,
542 DNS_CLASS_IN, true, srv_handler, req);
543}
544
545
546static int addr_lookup(struct sip_request *req, const char *name)
547{
548 int err;
549
550 if (sip_transp_supported(req->sip, req->tp, AF_INET)) {
551
552 err = dnsc_query(&req->dnsq, req->sip->dnsc, name,
553 DNS_TYPE_A, DNS_CLASS_IN, true,
554 addr_handler, req);
555 if (err)
556 return err;
557 }
558
559#ifdef HAVE_INET6
560 if (sip_transp_supported(req->sip, req->tp, AF_INET6)) {
561
562 err = dnsc_query(&req->dnsq2, req->sip->dnsc, name,
563 DNS_TYPE_AAAA, DNS_CLASS_IN, true,
564 addr_handler, req);
565 if (err)
566 return err;
567 }
568#endif
569
570 if (!req->dnsq && !req->dnsq2)
571 return EPROTONOSUPPORT;
572
573 return 0;
574}
575
576
577/**
578 * Send a SIP request
579 *
580 * @param reqp Pointer to allocated SIP request object
581 * @param sip SIP Stack
582 * @param stateful Stateful client transaction
583 * @param met SIP Method string
584 * @param metl Length of SIP Method string
585 * @param uri Request URI
586 * @param uril Length of Request URI string
587 * @param route Next hop route URI
588 * @param mb Buffer containing SIP request
589 * @param sortkey Key for DNS record sorting
590 * @param sendh Send handler
591 * @param resph Response handler
592 * @param arg Handler argument
593 *
594 * @return 0 if success, otherwise errorcode
595 */
596int sip_request(struct sip_request **reqp, struct sip *sip, bool stateful,
597 const char *met, int metl, const char *uri, int uril,
598 const struct uri *route, struct mbuf *mb, size_t sortkey,
599 sip_send_h *sendh, sip_resp_h *resph, void *arg)
600{
601 struct sip_request *req;
602 struct sa dst;
603 struct pl pl;
604 int err;
605
606 if (!sip || !met || !uri || !route || !mb)
607 return EINVAL;
608
609 if (pl_strcasecmp(&route->scheme, "sip"))
610 return ENOSYS;
611
612 req = mem_zalloc(sizeof(*req), destructor);
613 if (!req)
614 return ENOMEM;
615
616 list_append(&sip->reql, &req->le, req);
617
618 err = str_ldup(&req->met, met, metl);
619 if (err)
620 goto out;
621
622 err = str_ldup(&req->uri, uri, uril);
623 if (err)
624 goto out;
625
626 if (msg_param_decode(&route->params, "maddr", &pl))
627 pl = route->host;
628
629 err = pl_strdup(&req->host, &pl);
630 if (err)
631 goto out;
632
633 req->stateful = stateful;
634 req->sortkey = sortkey;
635 req->mb = mem_ref(mb);
636 req->sip = sip;
637 req->sendh = sendh;
638 req->resph = resph;
639 req->arg = arg;
640
641 if (!msg_param_decode(&route->params, "transport", &pl)) {
642
643 if (!pl_strcasecmp(&pl, "udp"))
644 req->tp = SIP_TRANSP_UDP;
645 else if (!pl_strcasecmp(&pl, "tcp"))
646 req->tp = SIP_TRANSP_TCP;
647 else if (!pl_strcasecmp(&pl, "tls"))
648 req->tp = SIP_TRANSP_TLS;
649 else {
650 err = EPROTONOSUPPORT;
651 goto out;
652 }
653
654 if (!sip_transp_supported(sip, req->tp, AF_UNSPEC)) {
655 err = EPROTONOSUPPORT;
656 goto out;
657 }
658
659 req->tp_selected = true;
660 }
661 else {
662 req->tp = SIP_TRANSP_NONE;
663 if (!transp_next(sip, &req->tp)) {
664 err = EPROTONOSUPPORT;
665 goto out;
666 }
667
668 req->tp_selected = false;
669 }
670
671 if (!sa_set_str(&dst, req->host,
672 sip_transp_port(req->tp, route->port))) {
673
674 err = request(req, req->tp, &dst);
675 if (!req->stateful) {
676 mem_deref(req);
677 return err;
678 }
679 }
680 else if (route->port) {
681
682 req->port = sip_transp_port(req->tp, route->port);
683 err = addr_lookup(req, req->host);
684 }
685 else if (req->tp_selected) {
686
687 err = srv_lookup(req, req->host);
688 }
689 else {
690 err = dnsc_query(&req->dnsq, sip->dnsc, req->host,
691 DNS_TYPE_NAPTR, DNS_CLASS_IN, true,
692 naptr_handler, req);
693 }
694
695 out:
696 if (err)
697 mem_deref(req);
698 else if (reqp) {
699 req->reqp = reqp;
700 *reqp = req;
701 }
702
703 return err;
704}
705
706
707/**
708 * Send a SIP request with formatted arguments
709 *
710 * @param reqp Pointer to allocated SIP request object
711 * @param sip SIP Stack
712 * @param stateful Stateful client transaction
713 * @param met Null-terminated SIP Method string
714 * @param uri Null-terminated Request URI string
715 * @param route Next hop route URI (optional)
716 * @param auth SIP authentication state
717 * @param sendh Send handler
718 * @param resph Response handler
719 * @param arg Handler argument
720 * @param fmt Formatted SIP headers and body
721 *
722 * @return 0 if success, otherwise errorcode
723 */
724int sip_requestf(struct sip_request **reqp, struct sip *sip, bool stateful,
725 const char *met, const char *uri, const struct uri *route,
726 struct sip_auth *auth, sip_send_h *sendh, sip_resp_h *resph,
727 void *arg, const char *fmt, ...)
728{
729 struct uri lroute;
730 struct mbuf *mb;
731 va_list ap;
732 int err;
733
734 if (!sip || !met || !uri || !fmt)
735 return EINVAL;
736
737 if (!route) {
738 struct pl uripl;
739
740 pl_set_str(&uripl, uri);
741
742 err = uri_decode(&lroute, &uripl);
743 if (err)
744 return err;
745
746 route = &lroute;
747 }
748
749 mb = mbuf_alloc(2048);
750 if (!mb)
751 return ENOMEM;
752
753 err = mbuf_write_str(mb, "Max-Forwards: 70\r\n");
754
755 if (auth)
756 err |= sip_auth_encode(mb, auth, met, uri);
757
758 if (err)
759 goto out;
760
761 va_start(ap, fmt);
762 err = mbuf_vprintf(mb, fmt, ap);
763 va_end(ap);
764
765 if (err)
766 goto out;
767
768 mb->pos = 0;
769
770 err = sip_request(reqp, sip, stateful, met, -1, uri, -1, route, mb,
771 (size_t)arg, sendh, resph, arg);
772 if (err)
773 goto out;
774
775 out:
776 mem_deref(mb);
777
778 return err;
779}
780
781
782/**
783 * Send a SIP dialog request with formatted arguments
784 *
785 * @param reqp Pointer to allocated SIP request object
786 * @param sip SIP Stack
787 * @param stateful Stateful client transaction
788 * @param met Null-terminated SIP Method string
789 * @param dlg SIP Dialog state
790 * @param cseq CSeq number
791 * @param auth SIP authentication state
792 * @param sendh Send handler
793 * @param resph Response handler
794 * @param arg Handler argument
795 * @param fmt Formatted SIP headers and body
796 *
797 * @return 0 if success, otherwise errorcode
798 */
799int sip_drequestf(struct sip_request **reqp, struct sip *sip, bool stateful,
800 const char *met, struct sip_dialog *dlg, uint32_t cseq,
801 struct sip_auth *auth, sip_send_h *sendh, sip_resp_h *resph,
802 void *arg, const char *fmt, ...)
803{
804 struct mbuf *mb;
805 va_list ap;
806 int err;
807
808 if (!sip || !met || !dlg || !fmt)
809 return EINVAL;
810
811 mb = mbuf_alloc(2048);
812 if (!mb)
813 return ENOMEM;
814
815 err = mbuf_write_str(mb, "Max-Forwards: 70\r\n");
816
817 if (auth)
818 err |= sip_auth_encode(mb, auth, met, sip_dialog_uri(dlg));
819
820 err |= sip_dialog_encode(mb, dlg, cseq, met);
821
822 if (sip->software)
823 err |= mbuf_printf(mb, "User-Agent: %s\r\n", sip->software);
824
825 if (err)
826 goto out;
827
828 va_start(ap, fmt);
829 err = mbuf_vprintf(mb, fmt, ap);
830 va_end(ap);
831
832 if (err)
833 goto out;
834
835 mb->pos = 0;
836
837 err = sip_request(reqp, sip, stateful, met, -1, sip_dialog_uri(dlg),
838 -1, sip_dialog_route(dlg), mb, sip_dialog_hash(dlg),
839 sendh, resph, arg);
840 if (err)
841 goto out;
842
843 out:
844 mem_deref(mb);
845
846 return err;
847}
848
849
850/**
851 * Cancel a pending SIP Request
852 *
853 * @param req SIP Request
854 */
855void sip_request_cancel(struct sip_request *req)
856{
857 if (!req || req->canceled)
858 return;
859
860 req->canceled = true;
861
862 if (!req->provrecv)
863 return;
864
865 (void)sip_ctrans_cancel(req->ct);
866}
867
868
869void sip_request_close(struct sip *sip)
870{
871 if (!sip)
872 return;
873
874 list_apply(&sip->reql, true, close_handler, NULL);
875}
876
877
878/**
879 * Check if a SIP request loops
880 *
881 * @param ls Loop state
882 * @param scode Status code from SIP response
883 *
884 * @return True if loops, otherwise false
885 */
886bool sip_request_loops(struct sip_loopstate *ls, uint16_t scode)
887{
888 bool loop = false;
889
890 if (!ls)
891 return false;
892
893 if (scode < 200) {
894 return false;
895 }
896 else if (scode < 300) {
897 ls->failc = 0;
898 }
899 else if (scode < 400) {
900 loop = (++ls->failc >= 16);
901 }
902 else {
903 switch (scode) {
904
905 default:
906 if (ls->last_scode == scode)
907 loop = true;
908 /*@fallthrough@*/
909 case 401:
910 case 407:
911 case 491:
912 if (++ls->failc >= 16)
913 loop = true;
914 break;
915 }
916 }
917
918 ls->last_scode = scode;
919
920 return loop;
921}
922
923
924/**
925 * Reset the loop state
926 *
927 * @param ls Loop state
928 */
929void sip_loopstate_reset(struct sip_loopstate *ls)
930{
931 if (!ls)
932 return;
933
934 ls->last_scode = 0;
935 ls->failc = 0;
936}