blob: 5863e20e13c3997c53afaacc2f3bf4d9eebeb7c0 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file dialog.c SIP Dialog
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <string.h>
7#include <re_types.h>
8#include <re_mem.h>
9#include <re_mbuf.h>
10#include <re_sa.h>
11#include <re_list.h>
12#include <re_hash.h>
13#include <re_fmt.h>
14#include <re_uri.h>
15#include <re_sys.h>
16#include <re_tmr.h>
17#include <re_udp.h>
18#include <re_msg.h>
19#include <re_sip.h>
20#include "sip.h"
21
22
23enum {
24 ROUTE_OFFSET = 7,
25 X64_STRSIZE = 17,
26};
27
28struct sip_dialog {
29 struct uri route;
30 struct mbuf *mb;
31 char *callid;
32 char *ltag;
33 char *rtag;
34 char *uri;
35 uint32_t hash;
36 uint32_t lseq;
37 uint32_t rseq;
38 size_t cpos;
39};
40
41
42struct route_enc {
43 struct mbuf *mb;
44 size_t end;
45};
46
47
48static int x64_strdup(char **strp, uint64_t val)
49{
50 char *str;
51
52 str = mem_alloc(X64_STRSIZE, NULL);
53 if (!str)
54 return ENOMEM;
55
56 (void)re_snprintf(str, X64_STRSIZE, "%016llx", val);
57
58 *strp = str;
59
60 return 0;
61}
62
63
64static void destructor(void *arg)
65{
66 struct sip_dialog *dlg = arg;
67
68 mem_deref(dlg->callid);
69 mem_deref(dlg->ltag);
70 mem_deref(dlg->rtag);
71 mem_deref(dlg->uri);
72 mem_deref(dlg->mb);
73}
74
75
76/**
77 * Allocate a SIP Dialog
78 *
79 * @param dlgp Pointer to allocated SIP Dialog
80 * @param uri Target URI
81 * @param to_uri To URI
82 * @param from_name From displayname (optional)
83 * @param from_uri From URI
84 * @param routev Route vector
85 * @param routec Route count
86 *
87 * @return 0 if success, otherwise errorcode
88 */
89int sip_dialog_alloc(struct sip_dialog **dlgp,
90 const char *uri, const char *to_uri,
91 const char *from_name, const char *from_uri,
92 const char *routev[], uint32_t routec)
93{
94 const uint64_t ltag = rand_u64();
95 struct sip_dialog *dlg;
96 struct sip_addr addr;
97 size_t rend = 0;
98 struct pl pl;
99 uint32_t i;
100 int err;
101
102 if (!dlgp || !uri || !to_uri || !from_uri)
103 return EINVAL;
104
105 dlg = mem_zalloc(sizeof(*dlg), destructor);
106 if (!dlg)
107 return ENOMEM;
108
109 dlg->hash = hash_fast_str(from_uri);
110 dlg->lseq = rand_u16();
111
112 err = str_dup(&dlg->uri, uri);
113 if (err)
114 goto out;
115
116 err = x64_strdup(&dlg->callid, rand_u64());
117 if (err)
118 goto out;
119
120 err = x64_strdup(&dlg->ltag, ltag);
121 if (err)
122 goto out;
123
124 dlg->mb = mbuf_alloc(512);
125 if (!dlg->mb) {
126 err = ENOMEM;
127 goto out;
128 }
129
130 for (i=0; i<routec; i++) {
131 err |= mbuf_printf(dlg->mb, "Route: <%s;lr>\r\n", routev[i]);
132 if (i == 0)
133 rend = dlg->mb->pos - 2;
134 }
135 err |= mbuf_printf(dlg->mb, "To: <%s>\r\n", to_uri);
136 dlg->cpos = dlg->mb->pos;
137 err |= mbuf_printf(dlg->mb, "From: %s%s%s<%s>;tag=%016llx\r\n",
138 from_name ? "\"" : "", from_name,
139 from_name ? "\" " : "",
140 from_uri, ltag);
141 if (err)
142 goto out;
143
144 dlg->mb->pos = 0;
145
146 if (rend) {
147 pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET;
148 pl.l = rend - ROUTE_OFFSET;
149 err = sip_addr_decode(&addr, &pl);
150 dlg->route = addr.uri;
151 }
152 else {
153 pl_set_str(&pl, dlg->uri);
154 err = uri_decode(&dlg->route, &pl);
155 }
156
157 out:
158 if (err)
159 mem_deref(dlg);
160 else
161 *dlgp = dlg;
162
163 return err;
164}
165
166
167static bool record_route_handler(const struct sip_hdr *hdr,
168 const struct sip_msg *msg,
169 void *arg)
170{
171 struct route_enc *renc = arg;
172 (void)msg;
173
174 if (mbuf_printf(renc->mb, "Route: %r\r\n", &hdr->val))
175 return true;
176
177 if (!renc->end)
178 renc->end = renc->mb->pos - 2;
179
180 return false;
181}
182
183
184/**
185 * Accept and create a SIP Dialog from an incoming SIP Message
186 *
187 * @param dlgp Pointer to allocated SIP Dialog
188 * @param msg SIP Message
189 *
190 * @return 0 if success, otherwise errorcode
191 */
192int sip_dialog_accept(struct sip_dialog **dlgp, const struct sip_msg *msg)
193{
194 const struct sip_hdr *contact;
195 struct sip_dialog *dlg;
196 struct route_enc renc;
197 struct sip_addr addr;
198 struct pl pl;
199 int err;
200
201 if (!dlgp || !msg || !msg->req)
202 return EINVAL;
203
204 contact = sip_msg_hdr(msg, SIP_HDR_CONTACT);
205
206 if (!contact || !msg->callid.p)
207 return EBADMSG;
208
209 if (sip_addr_decode(&addr, &contact->val))
210 return EBADMSG;
211
212 dlg = mem_zalloc(sizeof(*dlg), destructor);
213 if (!dlg)
214 return ENOMEM;
215
216 dlg->hash = rand_u32();
217 dlg->lseq = rand_u16();
218 dlg->rseq = msg->cseq.num;
219
220 err = pl_strdup(&dlg->uri, &addr.auri);
221 if (err)
222 goto out;
223
224 err = pl_strdup(&dlg->callid, &msg->callid);
225 if (err)
226 goto out;
227
228 err = x64_strdup(&dlg->ltag, msg->tag);
229 if (err)
230 goto out;
231
232 err = pl_strdup(&dlg->rtag, &msg->from.tag);
233 if (err)
234 goto out;
235
236 dlg->mb = mbuf_alloc(512);
237 if (!dlg->mb) {
238 err = ENOMEM;
239 goto out;
240 }
241
242 renc.mb = dlg->mb;
243 renc.end = 0;
244
245 err |= sip_msg_hdr_apply(msg, true, SIP_HDR_RECORD_ROUTE,
246 record_route_handler, &renc) ? ENOMEM : 0;
247 err |= mbuf_printf(dlg->mb, "To: %r\r\n", &msg->from.val);
248 err |= mbuf_printf(dlg->mb, "From: %r;tag=%016llx\r\n", &msg->to.val,
249 msg->tag);
250 if (err)
251 goto out;
252
253 dlg->mb->pos = 0;
254
255 if (renc.end) {
256 pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET;
257 pl.l = renc.end - ROUTE_OFFSET;
258 err = sip_addr_decode(&addr, &pl);
259 dlg->route = addr.uri;
260 }
261 else {
262 pl_set_str(&pl, dlg->uri);
263 err = uri_decode(&dlg->route, &pl);
264 }
265
266 out:
267 if (err)
268 mem_deref(dlg);
269 else
270 *dlgp = dlg;
271
272 return err;
273}
274
275
276/**
277 * Initialize a SIP Dialog from an incoming SIP Message
278 *
279 * @param dlg SIP Dialog to initialize
280 * @param msg SIP Message
281 *
282 * @return 0 if success, otherwise errorcode
283 */
284int sip_dialog_create(struct sip_dialog *dlg, const struct sip_msg *msg)
285{
286 char *uri = NULL, *rtag = NULL;
287 const struct sip_hdr *contact;
288 struct route_enc renc;
289 struct sip_addr addr;
290 struct pl pl;
291 int err;
292
293 if (!dlg || dlg->rtag || !dlg->cpos || !msg)
294 return EINVAL;
295
296 contact = sip_msg_hdr(msg, SIP_HDR_CONTACT);
297
298 if (!contact)
299 return EBADMSG;
300
301 if (sip_addr_decode(&addr, &contact->val))
302 return EBADMSG;
303
304 renc.mb = mbuf_alloc(512);
305 if (!renc.mb)
306 return ENOMEM;
307
308 err = pl_strdup(&uri, &addr.auri);
309 if (err)
310 goto out;
311
312 err = pl_strdup(&rtag, msg->req ? &msg->from.tag : &msg->to.tag);
313 if (err)
314 goto out;
315
316 renc.end = 0;
317
318 err |= sip_msg_hdr_apply(msg, msg->req, SIP_HDR_RECORD_ROUTE,
319 record_route_handler, &renc) ? ENOMEM : 0;
320 err |= mbuf_printf(renc.mb, "To: %r\r\n",
321 msg->req ? &msg->from.val : &msg->to.val);
322
323 dlg->mb->pos = dlg->cpos;
324 err |= mbuf_write_mem(renc.mb, mbuf_buf(dlg->mb),
325 mbuf_get_left(dlg->mb));
326 dlg->mb->pos = 0;
327
328 if (err)
329 goto out;
330
331 renc.mb->pos = 0;
332
333 if (renc.end) {
334 pl.p = (const char *)mbuf_buf(renc.mb) + ROUTE_OFFSET;
335 pl.l = renc.end - ROUTE_OFFSET;
336 err = sip_addr_decode(&addr, &pl);
337 if (err)
338 goto out;
339
340 dlg->route = addr.uri;
341 }
342 else {
343 struct uri tmp;
344
345 pl_set_str(&pl, uri);
346 err = uri_decode(&tmp, &pl);
347 if (err)
348 goto out;
349
350 dlg->route = tmp;
351 }
352
353 mem_deref(dlg->mb);
354 mem_deref(dlg->uri);
355
356 dlg->mb = mem_ref(renc.mb);
357 dlg->rtag = mem_ref(rtag);
358 dlg->uri = mem_ref(uri);
359 dlg->rseq = msg->req ? msg->cseq.num : 0;
360 dlg->cpos = 0;
361
362 out:
363 mem_deref(renc.mb);
364 mem_deref(rtag);
365 mem_deref(uri);
366
367 return err;
368}
369
370
371/**
372 * Fork a SIP Dialog from an incoming SIP Message
373 *
374 * @param dlgp Pointer to allocated SIP Dialog
375 * @param odlg Original SIP Dialog
376 * @param msg SIP Message
377 *
378 * @return 0 if success, otherwise errorcode
379 */
380int sip_dialog_fork(struct sip_dialog **dlgp, struct sip_dialog *odlg,
381 const struct sip_msg *msg)
382{
383 const struct sip_hdr *contact;
384 struct sip_dialog *dlg;
385 struct route_enc renc;
386 struct sip_addr addr;
387 struct pl pl;
388 int err;
389
390 if (!dlgp || !odlg || !odlg->cpos || !msg)
391 return EINVAL;
392
393 contact = sip_msg_hdr(msg, SIP_HDR_CONTACT);
394
395 if (!contact || !msg->callid.p)
396 return EBADMSG;
397
398 if (sip_addr_decode(&addr, &contact->val))
399 return EBADMSG;
400
401 dlg = mem_zalloc(sizeof(*dlg), destructor);
402 if (!dlg)
403 return ENOMEM;
404
405 dlg->callid = mem_ref(odlg->callid);
406 dlg->ltag = mem_ref(odlg->ltag);
407 dlg->hash = odlg->hash;
408 dlg->lseq = odlg->lseq;
409 dlg->rseq = msg->req ? msg->cseq.num : 0;
410
411 err = pl_strdup(&dlg->uri, &addr.auri);
412 if (err)
413 goto out;
414
415 err = pl_strdup(&dlg->rtag, msg->req ? &msg->from.tag : &msg->to.tag);
416 if (err)
417 goto out;
418
419 dlg->mb = mbuf_alloc(512);
420 if (!dlg->mb) {
421 err = ENOMEM;
422 goto out;
423 }
424
425 renc.mb = dlg->mb;
426 renc.end = 0;
427
428 err |= sip_msg_hdr_apply(msg, msg->req, SIP_HDR_RECORD_ROUTE,
429 record_route_handler, &renc) ? ENOMEM : 0;
430 err |= mbuf_printf(dlg->mb, "To: %r\r\n",
431 msg->req ? &msg->from.val : &msg->to.val);
432
433 odlg->mb->pos = odlg->cpos;
434 err |= mbuf_write_mem(dlg->mb, mbuf_buf(odlg->mb),
435 mbuf_get_left(odlg->mb));
436 odlg->mb->pos = 0;
437
438 if (err)
439 goto out;
440
441 dlg->mb->pos = 0;
442
443 if (renc.end) {
444 pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET;
445 pl.l = renc.end - ROUTE_OFFSET;
446 err = sip_addr_decode(&addr, &pl);
447 dlg->route = addr.uri;
448 }
449 else {
450 pl_set_str(&pl, dlg->uri);
451 err = uri_decode(&dlg->route, &pl);
452 }
453
454 out:
455 if (err)
456 mem_deref(dlg);
457 else
458 *dlgp = dlg;
459
460 return err;
461}
462
463
464/**
465 * Update an existing SIP Dialog from a SIP Message
466 *
467 * @param dlg SIP Dialog to update
468 * @param msg SIP Message
469 *
470 * @return 0 if success, otherwise errorcode
471 */
472int sip_dialog_update(struct sip_dialog *dlg, const struct sip_msg *msg)
473{
474 const struct sip_hdr *contact;
475 struct sip_addr addr;
476 char *uri;
477 int err;
478
479 if (!dlg || !msg)
480 return EINVAL;
481
482 contact = sip_msg_hdr(msg, SIP_HDR_CONTACT);
483 if (!contact)
484 return EBADMSG;
485
486 if (sip_addr_decode(&addr, &contact->val))
487 return EBADMSG;
488
489 err = pl_strdup(&uri, &addr.auri);
490 if (err)
491 return err;
492
493 if (dlg->route.scheme.p == dlg->uri) {
494
495 struct uri tmp;
496 struct pl pl;
497
498 pl_set_str(&pl, uri);
499 err = uri_decode(&tmp, &pl);
500 if (err)
501 goto out;
502
503 dlg->route = tmp;
504 }
505
506 mem_deref(dlg->uri);
507 dlg->uri = mem_ref(uri);
508
509 out:
510 mem_deref(uri);
511
512 return err;
513}
514
515
516/**
517 * Check if a remote sequence number is valid
518 *
519 * @param dlg SIP Dialog
520 * @param msg SIP Message
521 *
522 * @return True if valid, False if invalid
523 */
524bool sip_dialog_rseq_valid(struct sip_dialog *dlg, const struct sip_msg *msg)
525{
526 if (!dlg || !msg || !msg->req)
527 return false;
528
529 if (msg->cseq.num < dlg->rseq)
530 return false;
531
532 dlg->rseq = msg->cseq.num;
533
534 return true;
535}
536
537
538int sip_dialog_encode(struct mbuf *mb, struct sip_dialog *dlg, uint32_t cseq,
539 const char *met)
540{
541 int err = 0;
542
543 if (!mb || !dlg || !met)
544 return EINVAL;
545
546 err |= mbuf_write_mem(mb, mbuf_buf(dlg->mb), mbuf_get_left(dlg->mb));
547 err |= mbuf_printf(mb, "Call-ID: %s\r\n", dlg->callid);
548 err |= mbuf_printf(mb, "CSeq: %u %s\r\n", strcmp(met, "ACK") ?
549 dlg->lseq++ : cseq, met);
550
551 return err;
552}
553
554
555const char *sip_dialog_uri(const struct sip_dialog *dlg)
556{
557 return dlg ? dlg->uri : NULL;
558}
559
560
561const struct uri *sip_dialog_route(const struct sip_dialog *dlg)
562{
563 return dlg ? &dlg->route : NULL;
564}
565
566
567uint32_t sip_dialog_hash(const struct sip_dialog *dlg)
568{
569 return dlg ? dlg->hash : 0;
570}
571
572
573/**
574 * Get the Call-ID from a SIP Dialog
575 *
576 * @param dlg SIP Dialog
577 *
578 * @return Call-ID string
579 */
580const char *sip_dialog_callid(const struct sip_dialog *dlg)
581{
582 return dlg ? dlg->callid : NULL;
583}
584
585
586/**
587 * Get the local sequence number from a SIP Dialog
588 *
589 * @param dlg SIP Dialog
590 *
591 * @return Local sequence number
592 */
593uint32_t sip_dialog_lseq(const struct sip_dialog *dlg)
594{
595 return dlg ? dlg->lseq : 0;
596}
597
598
599/**
600 * Check if a SIP Dialog is established
601 *
602 * @param dlg SIP Dialog
603 *
604 * @return True if established, False if not
605 */
606bool sip_dialog_established(const struct sip_dialog *dlg)
607{
608 return dlg && dlg->rtag;
609}
610
611
612/**
613 * Compare a SIP Dialog against a SIP Message
614 *
615 * @param dlg SIP Dialog
616 * @param msg SIP Message
617 *
618 * @return True if match, False if no match
619 */
620bool sip_dialog_cmp(const struct sip_dialog *dlg, const struct sip_msg *msg)
621{
622 if (!dlg || !msg)
623 return false;
624
625 if (pl_strcmp(&msg->callid, dlg->callid))
626 return false;
627
628 if (pl_strcmp(msg->req ? &msg->to.tag : &msg->from.tag, dlg->ltag))
629 return false;
630
631 if (pl_strcmp(msg->req ? &msg->from.tag : &msg->to.tag, dlg->rtag))
632 return false;
633
634 return true;
635}
636
637
638/**
639 * Compare a half SIP Dialog against a SIP Message
640 *
641 * @param dlg SIP Dialog
642 * @param msg SIP Message
643 *
644 * @return True if match, False if no match
645 */
646bool sip_dialog_cmp_half(const struct sip_dialog *dlg,
647 const struct sip_msg *msg)
648{
649 if (!dlg || !msg)
650 return false;
651
652 if (pl_strcmp(&msg->callid, dlg->callid))
653 return false;
654
655 if (pl_strcmp(msg->req ? &msg->to.tag : &msg->from.tag, dlg->ltag))
656 return false;
657
658 return true;
659}