3d152bf67d3e61af5234e78fcfba55a8aeac6c19
[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  */
54
55 #include <string.h>
56 #include "../../mem/shm_mem.h"
57 #include "../../dprint.h"
58 #include "../../globals.h"
59 #include "../../md5.h"
60 #include "../../crc.h"
61 #include "../../ip_addr.h"
62 #include "../../socket_info.h"
63 #include "ut.h"
64 #include "h_table.h"
65 #include "t_hooks.h"
66 #include "t_funcs.h"
67 #include "t_msgbuilder.h"
68 #include "callid.h"
69 #include "uac.h"
70
71
72 #define FROM_TAG_LEN (MD5_LEN + 1 /* - */ + CRC16_LEN) /* length of FROM tags */
73
74 static char from_tag[FROM_TAG_LEN + 1];
75
76 char* uac_from = "sip:foo@foo.bar"; /* Module parameter */
77
78 /* Enable/disable passing of provisional replies to FIFO applications */
79 int pass_provisional_replies = 0;
80
81 /*
82  * Initialize UAC
83  */
84 int uac_init(void) 
85 {
86         str src[3];
87         struct socket_info *si;
88
89         if (RAND_MAX < TABLE_ENTRIES) {
90                 LOG(L_WARN, "Warning: uac does not spread "
91                     "across the whole hash table\n");
92         }
93         /* on tcp/tls bind_address is 0 so try to get the first address we listen
94          * on no matter the protocol */
95         si=bind_address?bind_address:get_first_socket();
96         if (si==0){
97                 LOG(L_CRIT, "BUG: uac_init: null socket list\n");
98                 return -1;
99         }
100
101         /* calculate the initial From tag */
102         src[0].s = "Long live SER server";
103         src[0].len = strlen(src[0].s);
104         src[1].s = si->address_str.s;
105         src[1].len = strlen(src[1].s);
106         src[2].s = si->port_no_str.s;
107         src[2].len = strlen(src[2].s);
108
109         MDStringArray(from_tag, src, 3);
110         from_tag[MD5_LEN] = '-';
111         return 1;
112 }
113
114
115 /*
116  * Generate a From tag
117  */
118 void generate_fromtag(str* tag, str* callid)
119 {
120              /* calculate from tag from callid */
121         crcitt_string_array(&from_tag[MD5_LEN + 1], callid, 1);
122         tag->s = from_tag; 
123         tag->len = FROM_TAG_LEN;
124 }
125
126
127 /*
128  * Check value of parameters
129  */
130 static inline int check_params(str* method, str* to, str* from, dlg_t** dialog)
131 {
132         if (!method || !to || !from || !dialog) {
133                 LOG(L_ERR, "check_params(): Invalid parameter value\n");
134                 return -1;
135         }
136
137         if (!method->s || !method->len) {
138                 LOG(L_ERR, "check_params(): Invalid request method\n");
139                 return -2;
140         }
141
142         if (!to->s || !to->len) {
143                 LOG(L_ERR, "check_params(): Invalid To URI\n");
144                 return -4;
145         }
146
147         if (!from->s || !from->len) {
148                 LOG(L_ERR, "check_params(): Invalid From URI\n");
149                 return -5;
150         }
151         return 0;
152 }
153
154 static inline unsigned int dlg2hash( dlg_t* dlg )
155 {
156         str cseq_nr;
157         unsigned int hashid;
158
159         cseq_nr.s=int2str(dlg->loc_seq.value, &cseq_nr.len);
160         hashid=hash(dlg->id.call_id, cseq_nr);
161         DBG("DEBUG: dlg2hash: %d\n", hashid);
162         return hashid;
163 }
164
165 /*
166  * Send a request using data from the dialog structure
167  */
168 int t_uac(str* method, str* headers, str* body, dlg_t* dialog,
169           transaction_cb cb, void* cbp)
170 {
171         struct socket_info* send_sock;
172         union sockaddr_union to_su;
173         struct cell *new_cell;
174         struct retr_buf *request;
175         char* buf;
176         int buf_len, ret, flags;
177         unsigned int hi;
178
179         ret=-1;
180         
181         /*** added by dcm 
182          * - needed by external ua to send a request within a dlg
183          */
184         if(!dialog->hooks.next_hop && w_calculate_hooks(dialog)<0)
185                 goto error2;
186
187         DBG("DEBUG:tm:t_uac: next_hop=<%.*s>\n",dialog->hooks.next_hop->len,
188                         dialog->hooks.next_hop->s);
189         /* it's a new message, so we will take the default socket */
190         send_sock = uri2sock(0, dialog->hooks.next_hop, &to_su, PROTO_NONE);
191         if (!send_sock) {
192                 ret=ser_error;
193                 LOG(L_ERR, "t_uac: no socket found\n");
194                 goto error2;
195         }       
196
197         new_cell = build_cell(0); 
198         if (!new_cell) {
199                 ret=E_OUT_OF_MEM;
200                 LOG(L_ERR, "t_uac: short of cell shmem\n");
201                 goto error2;
202         }
203
204         /* better reset avp list now - anyhow, it's useless from
205          * this point (bogdan) */
206         reset_avps();
207
208         /* add the callback the the transaction for LOCAL_COMPLETED event */
209  
210         flags = TMCB_LOCAL_COMPLETED;
211              /* Add also TMCB_LOCAL_REPLY_OUT if provisional replies are desired */
212         if (pass_provisional_replies) flags |= TMCB_LOCAL_RESPONSE_OUT;
213  
214         if(cb && insert_tmcb(&(new_cell->tmcb_hl), flags, cb, cbp)!=1){
215                 ret=E_OUT_OF_MEM; 
216                 LOG(L_ERR, "t_uac: short of tmcb shmem\n");
217                 goto error2;
218         }
219
220         if (method->len==INVITE_LEN && memcmp(method->s, INVITE, INVITE_LEN)==0)
221                 new_cell->flags |= T_IS_INVITE_FLAG;
222         new_cell->flags |= T_IS_LOCAL_FLAG;
223         set_kr(REQ_FWDED);
224
225         request = &new_cell->uac[0].request;
226         request->dst.to = to_su;
227         request->dst.send_sock = send_sock;
228         request->dst.proto = send_sock->proto;
229         request->dst.proto_reserved1 = 0;
230
231         hi=dlg2hash(dialog);
232         LOCK_HASH(hi);
233         insert_into_hash_table_unsafe(new_cell, hi);
234         UNLOCK_HASH(hi);
235
236         buf = build_uac_req(method, headers, body, dialog, 0, new_cell,
237                 &buf_len, send_sock);
238         if (!buf) {
239                 LOG(L_ERR, "t_uac: Error while building message\n");
240                 ret=E_OUT_OF_MEM;
241                 goto error1;
242         }
243
244         new_cell->method.s = buf;
245         new_cell->method.len = method->len;
246
247         request->buffer = buf;
248         request->buffer_len = buf_len;
249         new_cell->nr_of_outgoings++;
250         
251         if (SEND_BUFFER(request) == -1) {
252                 LOG(L_ERR, "t_uac: Attempt to send to '%.*s' failed\n", 
253                         dialog->hooks.next_hop->len,
254                         dialog->hooks.next_hop->s);
255         }
256         
257         if (start_retr(request)!=0)
258                 LOG(L_CRIT, "BUG: t_uac: failed to start retr. for %p\n", request);
259         return 1;
260
261  error1:
262         LOCK_HASH(hi);
263         remove_from_hash_table_unsafe(new_cell);
264         UNLOCK_HASH(hi);
265         free_cell(new_cell);
266 error2:
267         return ret;
268 }
269
270
271 /*
272  * Send a message within a dialog
273  */
274 int req_within(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb completion_cb, void* cbp)
275 {
276         if (!method || !dialog) {
277                 LOG(L_ERR, "req_within: Invalid parameter value\n");
278                 goto err;
279         }
280
281         if (dialog->state != DLG_CONFIRMED) {
282                 LOG(L_ERR, "req_within: Dialog is not confirmed yet\n");
283                 goto err;
284         }
285
286         if ((method->len == 3) && (!memcmp("ACK", method->s, 3))) goto send;
287         if ((method->len == 6) && (!memcmp("CANCEL", method->s, 6))) goto send;
288         dialog->loc_seq.value++; /* Increment CSeq */
289  send:
290         return t_uac(method, headers, body, dialog, completion_cb, cbp);
291
292  err:
293         if (cbp) shm_free(cbp);
294         return -1;
295 }
296
297
298 /*
299  * Send an initial request that will start a dialog
300  */
301 int req_outside(str* method, str* to, str* from, str* headers, str* body, dlg_t** dialog, transaction_cb cb, void* cbp)
302 {
303         str callid, fromtag;
304
305         if (check_params(method, to, from, dialog) < 0) goto err;
306         
307         generate_callid(&callid);
308         generate_fromtag(&fromtag, &callid);
309
310         if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, dialog) < 0) {
311                 LOG(L_ERR, "req_outside(): Error while creating new dialog\n");
312                 goto err;
313         }
314
315         return t_uac(method, headers, body, *dialog, cb, cbp);
316
317  err:
318         if (cbp) shm_free(cbp);
319         return -1;
320 }
321
322
323 /*
324  * Send a transactional request, no dialogs involved
325  */
326 int request(str* m, str* ruri, str* to, str* from, str* h, str* b, transaction_cb c, void* cp)
327 {
328         str callid, fromtag;
329         dlg_t* dialog;
330         int res;
331
332         if (check_params(m, to, from, &dialog) < 0) goto err;
333
334         generate_callid(&callid);
335         generate_fromtag(&fromtag, &callid);
336
337         if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, &dialog) < 0) {
338                 LOG(L_ERR, "request(): Error while creating temporary dialog\n");
339                 goto err;
340         }
341
342         if (ruri) {
343                 dialog->rem_target.s = ruri->s;
344                 dialog->rem_target.len = ruri->len;
345                 dialog->hooks.request_uri = &dialog->rem_target;
346         }
347         w_calculate_hooks(dialog);
348
349         res = t_uac(m, h, b, dialog, c, cp);
350         dialog->rem_target.s = 0;
351         free_dlg(dialog);
352         return res;
353
354  err:
355         if (cp) shm_free(cp);
356         return -1;
357 }