blob: 0ff326600dd5184d12555cea4c7e8c743b67405c [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file sip/ctrans.c SIP Client Transaction
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 state {
24 TRYING,
25 CALLING,
26 PROCEEDING,
27 COMPLETED,
28};
29
30
31enum {
32 COMPLETE_WAIT = 32000,
33};
34
35
36struct sip_ctrans {
37 struct le he;
38 struct sa dst;
39 struct tmr tmr;
40 struct tmr tmre;
41 struct sip *sip;
42 struct mbuf *mb;
43 struct mbuf *mb_ack;
44 struct sip_msg *req;
45 struct sip_connqent *qent;
46 char *met;
47 char *branch;
48 sip_resp_h *resph;
49 void *arg;
50 enum sip_transp tp;
51 enum state state;
52 uint32_t txc;
53 bool invite;
54};
55
56
57static bool route_handler(const struct sip_hdr *hdr, const struct sip_msg *msg,
58 void *arg)
59{
60 (void)msg;
61 return 0 != mbuf_printf(arg, "Route: %r\r\n", &hdr->val);
62}
63
64
65static int request_copy(struct mbuf **mbp, struct sip_ctrans *ct,
66 const char *met, const struct sip_msg *resp)
67{
68 struct mbuf *mb;
69 int err;
70
71 if (!ct->req) {
72 err = sip_msg_decode(&ct->req, ct->mb);
73 if (err)
74 return err;
75 }
76
77 mb = mbuf_alloc(1024);
78 if (!mb)
79 return ENOMEM;
80
81 err = mbuf_printf(mb, "%s %r SIP/2.0\r\n", met, &ct->req->ruri);
82 err |= mbuf_printf(mb, "Via: %r\r\n", &ct->req->via.val);
83 err |= mbuf_write_str(mb, "Max-Forwards: 70\r\n");
84 err |= sip_msg_hdr_apply(ct->req, true, SIP_HDR_ROUTE,
85 route_handler, mb) ? ENOMEM : 0;
86 err |= mbuf_printf(mb, "To: %r\r\n",
87 resp ? &resp->to.val : &ct->req->to.val);
88 err |= mbuf_printf(mb, "From: %r\r\n", &ct->req->from.val);
89 err |= mbuf_printf(mb, "Call-ID: %r\r\n", &ct->req->callid);
90 err |= mbuf_printf(mb, "CSeq: %u %s\r\n", ct->req->cseq.num, met);
91 if (ct->sip->software)
92 err |= mbuf_printf(mb, "User-Agent: %s\r\n",ct->sip->software);
93 err |= mbuf_write_str(mb, "Content-Length: 0\r\n\r\n");
94
95 mb->pos = 0;
96
97 if (err)
98 mem_deref(mb);
99 else
100 *mbp = mb;
101
102 return err;
103}
104
105
106static void destructor(void *arg)
107{
108 struct sip_ctrans *ct = arg;
109
110 hash_unlink(&ct->he);
111 tmr_cancel(&ct->tmr);
112 tmr_cancel(&ct->tmre);
113 mem_deref(ct->met);
114 mem_deref(ct->branch);
115 mem_deref(ct->qent);
116 mem_deref(ct->req);
117 mem_deref(ct->mb);
118 mem_deref(ct->mb_ack);
119}
120
121
122static bool cmp_handler(struct le *le, void *arg)
123{
124 struct sip_ctrans *ct = le->data;
125 const struct sip_msg *msg = arg;
126
127 if (pl_strcmp(&msg->via.branch, ct->branch))
128 return false;
129
130 if (pl_strcmp(&msg->cseq.met, ct->met))
131 return false;
132
133 return true;
134}
135
136
137static void dummy_handler(int err, const struct sip_msg *msg, void *arg)
138{
139 (void)err;
140 (void)msg;
141 (void)arg;
142}
143
144
145static void terminate(struct sip_ctrans *ct, int err)
146{
147 switch (ct->state) {
148
149 case TRYING:
150 case CALLING:
151 case PROCEEDING:
152 ct->resph(err, NULL, ct->arg);
153 break;
154
155 default:
156 break;
157 }
158}
159
160
161static void transport_handler(int err, void *arg)
162{
163 struct sip_ctrans *ct = arg;
164
165 terminate(ct, err);
166 mem_deref(ct);
167}
168
169
170static void tmr_handler(void *arg)
171{
172 struct sip_ctrans *ct = arg;
173
174 terminate(ct, ETIMEDOUT);
175 mem_deref(ct);
176}
177
178
179static void retransmit_handler(void *arg)
180{
181 struct sip_ctrans *ct = arg;
182 uint32_t timeout;
183 int err;
184
185 ct->txc++;
186
187 switch (ct->state) {
188
189 case TRYING:
190 timeout = MIN(SIP_T1<<ct->txc, SIP_T2);
191 break;
192
193 case CALLING:
194 timeout = SIP_T1<<ct->txc;
195 break;
196
197 case PROCEEDING:
198 timeout = SIP_T2;
199 break;
200
201 default:
202 return;
203 }
204
205 tmr_start(&ct->tmre, timeout, retransmit_handler, ct);
206
207 err = sip_transp_send(&ct->qent, ct->sip, NULL, ct->tp, &ct->dst,
208 ct->mb, transport_handler, ct);
209 if (err) {
210 terminate(ct, err);
211 mem_deref(ct);
212 }
213}
214
215
216static void invite_response(struct sip_ctrans *ct, const struct sip_msg *msg)
217{
218 switch (ct->state) {
219
220 case CALLING:
221 tmr_cancel(&ct->tmr);
222 tmr_cancel(&ct->tmre);
223 /*@fallthrough@*/
224 case PROCEEDING:
225 if (msg->scode < 200) {
226 ct->state = PROCEEDING;
227 ct->resph(0, msg, ct->arg);
228 }
229 else if (msg->scode < 300) {
230 ct->resph(0, msg, ct->arg);
231 mem_deref(ct);
232 }
233 else {
234 ct->state = COMPLETED;
235
236 (void)request_copy(&ct->mb_ack, ct, "ACK", msg);
237 (void)sip_send(ct->sip, NULL, ct->tp, &ct->dst,
238 ct->mb_ack);
239
240 ct->resph(0, msg, ct->arg);
241
242 if (sip_transp_reliable(ct->tp)) {
243 mem_deref(ct);
244 break;
245 }
246
247 tmr_start(&ct->tmr, COMPLETE_WAIT, tmr_handler, ct);
248 }
249 break;
250
251 case COMPLETED:
252 if (msg->scode < 300)
253 break;
254
255 (void)sip_send(ct->sip, NULL, ct->tp, &ct->dst, ct->mb_ack);
256 break;
257
258 default:
259 break;
260 }
261}
262
263
264static bool response_handler(const struct sip_msg *msg, void *arg)
265{
266 struct sip_ctrans *ct;
267 struct sip *sip = arg;
268
269 ct = list_ledata(hash_lookup(sip->ht_ctrans,
270 hash_joaat_pl(&msg->via.branch),
271 cmp_handler, (void *)msg));
272 if (!ct)
273 return false;
274
275 if (ct->invite) {
276 invite_response(ct, msg);
277 return true;
278 }
279
280 switch (ct->state) {
281
282 case TRYING:
283 case PROCEEDING:
284 if (msg->scode < 200) {
285 ct->state = PROCEEDING;
286 ct->resph(0, msg, ct->arg);
287 }
288 else {
289 ct->state = COMPLETED;
290 ct->resph(0, msg, ct->arg);
291
292 if (sip_transp_reliable(ct->tp)) {
293 mem_deref(ct);
294 break;
295 }
296
297 tmr_start(&ct->tmr, SIP_T4, tmr_handler, ct);
298 tmr_cancel(&ct->tmre);
299 }
300 break;
301
302 default:
303 break;
304 }
305
306 return true;
307}
308
309
310int sip_ctrans_request(struct sip_ctrans **ctp, struct sip *sip,
311 enum sip_transp tp, const struct sa *dst, char *met,
312 char *branch, struct mbuf *mb,
313 sip_resp_h *resph, void *arg)
314{
315 struct sip_ctrans *ct;
316 int err;
317
318 if (!sip || !dst || !met || !branch || !mb)
319 return EINVAL;
320
321 ct = mem_zalloc(sizeof(*ct), destructor);
322 if (!ct)
323 return ENOMEM;
324
325 hash_append(sip->ht_ctrans, hash_joaat_str(branch), &ct->he, ct);
326
327 ct->invite = !strcmp(met, "INVITE");
328 ct->branch = mem_ref(branch);
329 ct->met = mem_ref(met);
330 ct->mb = mem_ref(mb);
331 ct->dst = *dst;
332 ct->tp = tp;
333 ct->sip = sip;
334 ct->state = ct->invite ? CALLING : TRYING;
335 ct->resph = resph ? resph : dummy_handler;
336 ct->arg = arg;
337
338 err = sip_transp_send(&ct->qent, sip, NULL, tp, dst, mb,
339 transport_handler, ct);
340 if (err)
341 goto out;
342
343 tmr_start(&ct->tmr, 64 * SIP_T1, tmr_handler, ct);
344
345 if (!sip_transp_reliable(ct->tp))
346 tmr_start(&ct->tmre, SIP_T1, retransmit_handler, ct);
347
348 out:
349 if (err)
350 mem_deref(ct);
351 else if (ctp)
352 *ctp = ct;
353
354 return err;
355}
356
357
358int sip_ctrans_cancel(struct sip_ctrans *ct)
359{
360 struct mbuf *mb = NULL;
361 char *cancel = NULL;
362 int err;
363
364 if (!ct)
365 return EINVAL;
366
367 if (!ct->invite)
368 return 0;
369
370 switch (ct->state) {
371
372 case PROCEEDING:
373 tmr_start(&ct->tmr, 64 * SIP_T1, tmr_handler, ct);
374 break;
375
376 default:
377 return EPROTO;
378 }
379
380 err = str_dup(&cancel, "CANCEL");
381 if (err)
382 goto out;
383
384 err = request_copy(&mb, ct, cancel, NULL);
385 if (err)
386 goto out;
387
388 err = sip_ctrans_request(NULL, ct->sip, ct->tp, &ct->dst, cancel,
389 ct->branch, mb, NULL, NULL);
390 if (err)
391 goto out;
392
393 out:
394 mem_deref(cancel);
395 mem_deref(mb);
396
397 return err;
398}
399
400
401int sip_ctrans_init(struct sip *sip, uint32_t sz)
402{
403 int err;
404
405 err = sip_listen(NULL, sip, false, response_handler, sip);
406 if (err)
407 return err;
408
409 return hash_alloc(&sip->ht_ctrans, sz);
410}
411
412
413static const char *statename(enum state state)
414{
415 switch (state) {
416
417 case TRYING: return "TRYING";
418 case CALLING: return "CALLING";
419 case PROCEEDING: return "PROCEEDING";
420 case COMPLETED: return "COMPLETED";
421 default: return "???";
422 }
423}
424
425
426static bool debug_handler(struct le *le, void *arg)
427{
428 struct sip_ctrans *ct = le->data;
429 struct re_printf *pf = arg;
430
431 (void)re_hprintf(pf, " %-10s %-10s %2llus (%s)\n",
432 ct->met,
433 statename(ct->state),
434 tmr_get_expire(&ct->tmr)/1000,
435 ct->branch);
436
437 return false;
438}
439
440
441int sip_ctrans_debug(struct re_printf *pf, const struct sip *sip)
442{
443 int err;
444
445 err = re_hprintf(pf, "client transactions:\n");
446 hash_apply(sip->ht_ctrans, debug_handler, pf);
447
448 return err;
449}