- patch from Miklos Tirpak <miklos@iptel.org>:
[sip-router] / modules / tm / uac.c
1 /*
2  * $Id$
3  *
4  * simple UAC for things such as SUBSCRIBE or SMS gateway;
5  * no authentication and other UAC features -- just send
6  * a message, retransmit and await a reply; forking is not
7  * supported during client generation, in all other places
8  * it is -- adding it should be simple
9  *
10  * Copyright (C) 2001-2003 FhG Fokus
11  *
12  * This file is part of ser, a free SIP server.
13  *
14  * ser is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version
18  *
19  * For a license to use the ser software under conditions
20  * other than those described here, or to purchase support for this
21  * software, please contact iptel.org by e-mail at the following addresses:
22  *    info@iptel.org
23  *
24  * ser is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License 
30  * along with this program; if not, write to the Free Software 
31  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
32  *
33  * History:
34  * --------
35  *  2003-01-23  t_uac_dlg now uses get_out_socket (jiri)
36  *  2003-01-27  fifo:t_uac_dlg completed (jiri)
37  *  2003-01-29  scratchpad removed (jiri)
38  *  2003-02-13  t_uac, t _uac_dlg, gethfblock, uri2proxy changed to use 
39  *               proto & rb->dst (andrei)
40  *  2003-02-27  FIFO/UAC now dumps reply -- good for CTD (jiri)
41  *  2003-02-28  scratchpad compatibility abandoned (jiri)
42  *  2003-03-01  kr set through a function now (jiri)
43  *  2003-03-19  replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei)
44  *  2003-04-02  port_no_str does not contain a leading ':' anymore (andrei)
45  *  2003-07-08  appropriate log messages in check_params(...), 
46  *               call calculate_hooks if next_hop==NULL in t_uac (dcm) 
47  *  2003-10-24  updated to the new socket_info lists (andrei)
48  *  2003-12-03  completion filed removed from transaction and uac callbacks
49  *              merged in transaction callbacks as LOCAL_COMPLETED (bogdan)
50  *  2004-02-11  FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri)
51  *  2004-02-13  t->is_invite, t->local, t->noisy_ctimer replaced (bogdan)
52  *  2004-08-23  avp support in t_uac (bogdan)
53  *  2005-12-16  t_uac will set the new_cell timers to the default values,
54  *               fixes 0 fr_timer bug (andrei)
55  *  2006-08-11  t_uac uses dns failover until it finds a send socket (andrei)
56  */
57
58 #include <string.h>
59 #include "../../mem/shm_mem.h"
60 #include "../../dprint.h"
61 #include "../../globals.h"
62 #include "../../md5.h"
63 #include "../../crc.h"
64 #include "../../ip_addr.h"
65 #include "../../socket_info.h"
66 #include "ut.h"
67 #include "h_table.h"
68 #include "t_hooks.h"
69 #include "t_funcs.h"
70 #include "t_msgbuilder.h"
71 #include "callid.h"
72 #include "uac.h"
73
74
75 #define FROM_TAG_LEN (MD5_LEN + 1 /* - */ + CRC16_LEN) /* length of FROM tags */
76
77 static char from_tag[FROM_TAG_LEN + 1];
78
79 char* uac_from = "sip:foo@foo.bar"; /* Module parameter */
80
81 /* Enable/disable passing of provisional replies to FIFO applications */
82 int pass_provisional_replies = 0;
83
84 /*
85  * Initialize UAC
86  */
87 int uac_init(void) 
88 {
89         str src[3];
90         struct socket_info *si;
91
92         if (RAND_MAX < TABLE_ENTRIES) {
93                 LOG(L_WARN, "Warning: uac does not spread "
94                     "across the whole hash table\n");
95         }
96         /* on tcp/tls bind_address is 0 so try to get the first address we listen
97          * on no matter the protocol */
98         si=bind_address?bind_address:get_first_socket();
99         if (si==0){
100                 LOG(L_CRIT, "BUG: uac_init: null socket list\n");
101                 return -1;
102         }
103
104         /* calculate the initial From tag */
105         src[0].s = "Long live SER server";
106         src[0].len = strlen(src[0].s);
107         src[1].s = si->address_str.s;
108         src[1].len = strlen(src[1].s);
109         src[2].s = si->port_no_str.s;
110         src[2].len = strlen(src[2].s);
111
112         MDStringArray(from_tag, src, 3);
113         from_tag[MD5_LEN] = '-';
114         return 1;
115 }
116
117
118 /*
119  * Generate a From tag
120  */
121 void generate_fromtag(str* tag, str* callid)
122 {
123              /* calculate from tag from callid */
124         crcitt_string_array(&from_tag[MD5_LEN + 1], callid, 1);
125         tag->s = from_tag; 
126         tag->len = FROM_TAG_LEN;
127 }
128
129
130 /*
131  * Check value of parameters
132  */
133 static inline int check_params(str* method, str* to, str* from, dlg_t** dialog)
134 {
135         if (!method || !to || !from || !dialog) {
136                 LOG(L_ERR, "check_params(): Invalid parameter value\n");
137                 return -1;
138         }
139
140         if (!method->s || !method->len) {
141                 LOG(L_ERR, "check_params(): Invalid request method\n");
142                 return -2;
143         }
144
145         if (!to->s || !to->len) {
146                 LOG(L_ERR, "check_params(): Invalid To URI\n");
147                 return -4;
148         }
149
150         if (!from->s || !from->len) {
151                 LOG(L_ERR, "check_params(): Invalid From URI\n");
152                 return -5;
153         }
154         return 0;
155 }
156
157 static inline unsigned int dlg2hash( dlg_t* dlg )
158 {
159         str cseq_nr;
160         unsigned int hashid;
161
162         cseq_nr.s=int2str(dlg->loc_seq.value, &cseq_nr.len);
163         hashid=hash(dlg->id.call_id, cseq_nr);
164         DBG("DEBUG: dlg2hash: %d\n", hashid);
165         return hashid;
166 }
167
168 static inline int t_uac_prepare(str* method, str* headers, str* body, 
169                 dlg_t* dialog, transaction_cb cb, void* cbp, struct retr_buf **dst_req,
170                 struct cell **dst_cell)
171 {
172         struct dest_info dst;
173         struct cell *new_cell;
174         struct retr_buf *request;
175         char* buf;
176         int buf_len, ret, flags;
177         unsigned int hi;
178         int is_ack;
179 #ifdef USE_DNS_FAILOVER
180         struct dns_srv_handle dns_h;
181 #endif
182
183         ret=-1;
184         hi=0; /* make gcc happy */
185         /*if (dst_req) *dst_req = NULL;*/
186         is_ack = (((method->len == 3) && (memcmp("ACK", method->s, 3)==0)) ? 1 : 0);
187         
188         /*** added by dcm 
189          * - needed by external ua to send a request within a dlg
190          */
191         if (w_calculate_hooks(dialog)<0 && !dialog->hooks.next_hop)
192                 goto error2;
193
194         if (!dialog->loc_seq.is_set) {
195                 /* this is the first request in the dialog,
196                 set cseq to default value now - Miklos */
197                 dialog->loc_seq.value = DEFAULT_CSEQ;
198                 dialog->loc_seq.is_set = 1;
199         }
200
201         DBG("DEBUG:tm:t_uac: next_hop=<%.*s>\n",dialog->hooks.next_hop->len,
202                         dialog->hooks.next_hop->s);
203         /* it's a new message, so we will take the default socket */
204 #ifdef USE_DNS_FAILOVER
205         if (use_dns_failover){
206                 dns_srv_handle_init(&dns_h);
207                 if ((uri2dst(&dns_h, &dst, 0, dialog->hooks.next_hop, PROTO_NONE)==0)
208                                 || (dst.send_sock==0)){
209                         dns_srv_handle_put(&dns_h);
210                         ser_error = E_NO_SOCKET;
211                         ret=ser_error;
212                         LOG(L_ERR, "t_uac: no socket found\n");
213                         goto error2;
214                 }
215                 dns_srv_handle_put(&dns_h); /* not needed anymore */
216         }else{
217                 if ((uri2dst(0, &dst, 0, dialog->hooks.next_hop, PROTO_NONE)==0) ||
218                                 (dst.send_sock==0)){
219                         ser_error = E_NO_SOCKET;
220                         ret=ser_error;
221                         LOG(L_ERR, "t_uac: no socket found\n");
222                         goto error2;
223                 }
224         }
225 #else
226         if ((uri2dst(&dst, 0, dialog->hooks.next_hop, PROTO_NONE)==0) ||
227                         (dst.send_sock==0)){
228                 ser_error = E_NO_SOCKET;
229                 ret=ser_error;
230                 LOG(L_ERR, "t_uac: no socket found\n");
231                 goto error2;
232         }
233 #endif
234
235         new_cell = build_cell(0); 
236         if (!new_cell) {
237                 ret=E_OUT_OF_MEM;
238                 LOG(L_ERR, "t_uac: short of cell shmem\n");
239                 goto error2;
240         }
241         /* init timers hack, new_cell->fr_timer and new_cell->fr_inv_timer
242          * must be set, or else the fr will happen immediately
243          * we can't call init_new_t() because we don't have a sip msg
244          * => we'll ignore t_set_fr() or avp timer value and will use directly the
245          * module params fr_inv_timer and fr_timer -- andrei */
246         new_cell->fr_timeout=fr_timeout;
247         new_cell->fr_inv_timeout=fr_inv_timeout;
248
249         /* better reset avp list now - anyhow, it's useless from
250          * this point (bogdan) */
251         reset_avps();
252
253         /* add the callback the the transaction for LOCAL_COMPLETED event */
254  
255         flags = TMCB_LOCAL_COMPLETED;
256         /* Add also TMCB_LOCAL_REPLY_OUT if provisional replies are desired */
257         if (pass_provisional_replies) flags |= TMCB_LOCAL_RESPONSE_OUT;
258
259         if(cb && insert_tmcb(&(new_cell->tmcb_hl), flags, cb, cbp)!=1){
260                 ret=E_OUT_OF_MEM; 
261                 LOG(L_ERR, "t_uac: short of tmcb shmem\n");
262                 goto error2;
263         }
264
265         if (method->len==INVITE_LEN && memcmp(method->s, INVITE, INVITE_LEN)==0)
266                 new_cell->flags |= T_IS_INVITE_FLAG;
267         new_cell->flags |= T_IS_LOCAL_FLAG;
268         set_kr(REQ_FWDED);
269
270         request = &new_cell->uac[0].request;
271         
272         request->dst = dst;
273
274         if (!is_ack) {
275                 hi=dlg2hash(dialog);
276                 LOCK_HASH(hi);
277                 insert_into_hash_table_unsafe(new_cell, hi);
278                 UNLOCK_HASH(hi);
279         }
280
281         buf = build_uac_req(method, headers, body, dialog, 0, new_cell,
282                 &buf_len, &dst);
283         if (!buf) {
284                 LOG(L_ERR, "t_uac: Error while building message\n");
285                 ret=E_OUT_OF_MEM;
286                 goto error1;
287         }
288
289         new_cell->method.s = buf;
290         new_cell->method.len = method->len;
291
292         request->buffer = buf;
293         request->buffer_len = buf_len;
294         new_cell->nr_of_outgoings++;
295         
296         if (dst_req) *dst_req = request;
297         if (dst_cell) *dst_cell = new_cell;
298         
299         return 1;
300
301  error1:
302         if (!is_ack) {
303                 LOCK_HASH(hi);
304                 remove_from_hash_table_unsafe(new_cell);
305                 UNLOCK_HASH(hi);
306         }
307         free_cell(new_cell);
308 error2:
309         return ret;
310 }
311
312 /*
313  * Prepare a message within a dialog
314  */
315 int prepare_req_within(str* method, str* headers, 
316                 str* body, dlg_t* dialog, transaction_cb completion_cb, 
317                 void* cbp, struct retr_buf **dst_req)
318 {
319         if (!method || !dialog) {
320                 LOG(L_ERR, "req_within: Invalid parameter value\n");
321                 goto err;
322         }
323
324         if (dialog->state != DLG_CONFIRMED) {
325                 LOG(L_ERR, "req_within: Dialog is not confirmed yet\n");
326                 goto err;
327         }
328
329         if ((method->len == 3) && (!memcmp("ACK", method->s, 3))) goto send;
330         if ((method->len == 6) && (!memcmp("CANCEL", method->s, 6))) goto send;
331         dialog->loc_seq.value++; /* Increment CSeq */
332  send:
333         return t_uac_prepare(method, headers, body, dialog, completion_cb, cbp, dst_req, 0);
334
335  err:
336         /* if (cbp) shm_free(cbp); */
337         /* !! never free cbp here because if t_uac_prepare fails, cbp is not freed
338          * and thus caller has no chance to discover if it is freed or not !! */
339         return -1;
340 }
341
342 static inline void send_prepared_request_impl(struct retr_buf *request, int retransmit)
343 {
344         if (SEND_BUFFER(request) == -1) {
345                 LOG(L_ERR, "t_uac: Attempt to send to precreated request failed\n");
346         }
347         
348         if (retransmit && (start_retr(request)!=0))
349                 LOG(L_CRIT, "BUG: t_uac: failed to start retr. for %p\n", request);
350 }
351
352 void send_prepared_request(struct retr_buf *request)
353 {
354         send_prepared_request_impl(request, 1 /* retransmit */);
355 }
356
357 /*
358  * Send a request using data from the dialog structure
359  */
360 int t_uac(str* method, str* headers, str* body, dlg_t* dialog,
361           transaction_cb cb, void* cbp)
362 {
363         struct retr_buf *request;
364         struct cell *cell;
365         int ret;
366         int is_ack;
367
368         ret = t_uac_prepare(method, headers, body, dialog, cb, cbp, &request, &cell);
369         if (ret < 0) return ret;
370         is_ack = (method->len == 3) && (memcmp("ACK", method->s, 3)==0) ? 1 : 0;
371         send_prepared_request_impl(request, !is_ack /* retransmit */);
372         if (cell && is_ack)
373                 free_cell(cell);
374         return ret;
375 }
376
377 /*
378  * Send a request using data from the dialog structure
379  * ret_index and ret_label will identify the new cell
380  */
381 int t_uac_with_ids(str* method, str* headers, str* body, dlg_t* dialog,
382         transaction_cb cb, void* cbp,
383         unsigned int *ret_index, unsigned int *ret_label)
384 {
385         struct retr_buf *request;
386         struct cell *cell;
387         int ret;
388         int is_ack;
389
390         ret = t_uac_prepare(method, headers, body, dialog, cb, cbp, &request, &cell);
391         if (ret < 0) return ret;
392         is_ack = (method->len == 3) && (memcmp("ACK", method->s, 3)==0) ? 1 : 0;
393         send_prepared_request_impl(request, !is_ack /* retransmit */);
394         if (is_ack) {
395                 if (cell) free_cell(cell);
396                 if (ret_index && ret_label)
397                         *ret_index = *ret_label = 0;
398         } else {
399                 if (ret_index && ret_label) {
400                         *ret_index = cell->hash_index;
401                         *ret_label = cell->label;
402                 }
403         }
404         return ret;
405 }
406
407 /*
408  * Send a message within a dialog
409  */
410 int req_within(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb completion_cb, void* cbp)
411 {
412         if (!method || !dialog) {
413                 LOG(L_ERR, "req_within: Invalid parameter value\n");
414                 goto err;
415         }
416
417         if ((method->len == 3) && (!memcmp("ACK", method->s, 3))) goto send;
418         if ((method->len == 6) && (!memcmp("CANCEL", method->s, 6))) goto send;
419         dialog->loc_seq.value++; /* Increment CSeq */
420  send:
421         return t_uac(method, headers, body, dialog, completion_cb, cbp);
422
423  err:
424         if (cbp) shm_free(cbp);
425         return -1;
426 }
427
428
429 /*
430  * Send an initial request that will start a dialog
431  */
432 int req_outside(str* method, str* to, str* from, str* headers, str* body, dlg_t** dialog, transaction_cb cb, void* cbp)
433 {
434         str callid, fromtag;
435
436         if (check_params(method, to, from, dialog) < 0) goto err;
437         
438         generate_callid(&callid);
439         generate_fromtag(&fromtag, &callid);
440
441         if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, dialog) < 0) {
442                 LOG(L_ERR, "req_outside(): Error while creating new dialog\n");
443                 goto err;
444         }
445
446         return t_uac(method, headers, body, *dialog, cb, cbp);
447
448  err:
449         if (cbp) shm_free(cbp);
450         return -1;
451 }
452
453
454 /*
455  * Send a transactional request, no dialogs involved
456  */
457 int request(str* m, str* ruri, str* to, str* from, str* h, str* b, str *next_hop, transaction_cb c, void* cp)
458 {
459         str callid, fromtag;
460         dlg_t* dialog;
461         int res;
462
463         if (check_params(m, to, from, &dialog) < 0) goto err;
464
465         generate_callid(&callid);
466         generate_fromtag(&fromtag, &callid);
467
468         if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, &dialog) < 0) {
469                 LOG(L_ERR, "request(): Error while creating temporary dialog\n");
470                 goto err;
471         }
472
473         if (ruri) {
474                 dialog->rem_target.s = ruri->s;
475                 dialog->rem_target.len = ruri->len;
476                 dialog->hooks.request_uri = &dialog->rem_target;
477         }
478         w_calculate_hooks(dialog);
479
480         if (next_hop) 
481                 if ((next_hop->len > 0) && next_hop->s) dialog->hooks.next_hop = next_hop;
482
483         res = t_uac(m, h, b, dialog, c, cp);
484         dialog->rem_target.s = 0;
485         free_dlg(dialog);
486         return res;
487
488  err:
489         if (cp) shm_free(cp);
490         return -1;
491 }