proper error coding reintroduced to UAC (serweb did not display correct errors
[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  */
46
47 #include <string.h>
48 #include "../../mem/shm_mem.h"
49 #include "../../dprint.h"
50 #include "../../globals.h"
51 #include "../../md5.h"
52 #include "../../crc.h"
53 #include "../../ip_addr.h"
54 #include "ut.h"
55 #include "h_table.h"
56 #include "t_hooks.h"
57 #include "t_funcs.h"
58 #include "t_msgbuilder.h"
59 #include "callid.h"
60 #include "uac.h"
61
62
63 #define FROM_TAG_LEN (MD5_LEN + 1 /* - */ + CRC16_LEN) /* length of FROM tags */
64
65 static char from_tag[FROM_TAG_LEN + 1];
66
67 char* uac_from = "sip:foo@foo.bar"; /* Module parameter */
68
69
70 /*
71  * Initialize UAC
72  */
73 int uac_init(void) 
74 {
75         str src[3];
76
77         if (RAND_MAX < TABLE_ENTRIES) {
78                 LOG(L_WARN, "Warning: uac does not spread "
79                     "accross the whole hash table\n");
80         }
81
82         /* calculate the initial From tag */
83         src[0].s = "Long live SER server";
84         src[0].len = strlen(src[0].s);
85         src[1].s = sock_info[bind_idx].address_str.s;
86         src[1].len = strlen(src[1].s);
87         src[2].s = sock_info[bind_idx].port_no_str.s;
88         src[2].len = strlen(src[2].s);
89
90         MDStringArray(from_tag, src, 3);
91         from_tag[MD5_LEN] = '-';
92         return 1;
93 }
94
95
96 /*
97  * Generate a From tag
98  */
99 void generate_fromtag(str* tag, str* callid)
100 {
101              /* calculate from tag from callid */
102         crcitt_string_array(&from_tag[MD5_LEN + 1], callid, 1);
103         tag->s = from_tag; 
104         tag->len = FROM_TAG_LEN;
105 }
106
107
108 /*
109  * Check value of parameters
110  */
111 static inline int check_params(str* method, str* to, str* from, dlg_t** dialog)
112 {
113         if (!method || !to || !from || !dialog) {
114                 LOG(L_ERR, "check_params(): Invalid parameter value\n");
115                 return -1;
116         }
117
118         if (!method->s || !method->len) {
119                 LOG(L_ERR, "check_params(): Invalid request method\n");
120                 return -2;
121         }
122
123         if (!to->s || !to->len) {
124                 LOG(L_ERR, "check_params(): Invalid request method\n");
125                 return -4;
126         }
127
128         if (!from->s || !from->len) {
129                 LOG(L_ERR, "check_params(): Invalid request method\n");
130                 return -5;
131         }
132         return 0;
133 }
134
135
136 /*
137  * Send a request using data from the dialog structure
138  */
139 int t_uac(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb cb, void* cbp)
140 {
141         struct socket_info* send_sock;
142         union sockaddr_union to_su;
143         struct cell *new_cell;
144         struct retr_buf *request;
145         char* buf;
146         int buf_len;
147         int ret;
148
149         ret=-1;
150
151         send_sock = uri2sock(dialog->hooks.next_hop, &to_su, PROTO_NONE);
152         if (!send_sock) {
153                 ret=ser_error;
154                 LOG(L_ERR, "t_uac: no socket found\n");
155                 goto error2;
156         }       
157
158         new_cell = build_cell(0); 
159         if (!new_cell) {
160                 ret=E_OUT_OF_MEM;
161                 LOG(L_ERR, "t_uac: short of cell shmem\n");
162                 goto error2;
163         }
164
165         new_cell->completion_cb = cb;
166         new_cell->cbp = cbp;
167         
168              /* cbp is installed -- tell error handling bellow not to free it */
169         cbp = 0;
170
171         new_cell->is_invite = method->len == INVITE_LEN && memcmp(method->s, INVITE, INVITE_LEN) == 0;
172         new_cell->local= 1;
173         set_kr(REQ_FWDED);
174         
175         request = &new_cell->uac[0].request;
176         request->dst.to = to_su;
177         request->dst.send_sock = send_sock;
178         request->dst.proto = send_sock->proto;
179         request->dst.proto_reserved1 = 0;
180
181              /* need to put in table to calculate label which is needed for printing */
182         LOCK_HASH(new_cell->hash_index);
183         insert_into_hash_table_unsafe(new_cell);
184         UNLOCK_HASH(new_cell->hash_index);
185
186         buf = build_uac_req(method, headers, body, dialog, 0, new_cell, &buf_len, send_sock);
187         if (!buf) {
188                 LOG(L_ERR, "t_uac: Error while building message\n");
189                 ret=E_OUT_OF_MEM;
190                 goto error1;
191         }
192
193         new_cell->method.s = buf;
194         new_cell->method.len = method->len;
195
196         request->buffer = buf;
197         request->buffer_len = buf_len;
198         new_cell->nr_of_outgoings++;
199         
200         if (SEND_BUFFER(request) == -1) {
201                 LOG(L_ERR, "t_uac: Attempt to send to '%.*s' failed\n", 
202                     dialog->hooks.next_hop->len,
203                     dialog->hooks.next_hop->s
204                     );
205         }
206         
207         start_retr(request);
208         return 1;
209
210  error1:
211         LOCK_HASH(new_cell->hash_index);
212         remove_from_hash_table_unsafe(new_cell);
213         UNLOCK_HASH(new_cell->hash_index);
214         free_cell(new_cell);
215
216  error2:
217              /* if we did not install cbp, release it now */
218         if (cbp) shm_free(cbp);
219         return ret;
220 }
221
222
223 /*
224  * Send a message within a dialog
225  */
226 int req_within(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb completion_cb, void* cbp)
227 {
228         if (!method || !dialog) {
229                 LOG(L_ERR, "req_within: Invalid parameter value\n");
230                 goto err;
231         }
232
233         if (dialog->state != DLG_CONFIRMED) {
234                 LOG(L_ERR, "req_within: Dialog is not confirmed yet\n");
235                 goto err;
236         }
237
238         if ((method->len == 3) && (!memcmp("ACK", method->s, 3))) goto send;
239         if ((method->len == 6) && (!memcmp("CANCEL", method->s, 6))) goto send;
240         dialog->loc_seq.value++; /* Increment CSeq */
241  send:
242         return t_uac(method, headers, body, dialog, completion_cb, cbp);
243
244  err:
245         if (cbp) shm_free(cbp);
246         return -1;
247 }
248
249
250 /*
251  * Send an initial request that will start a dialog
252  */
253 int req_outside(str* method, str* to, str* from, str* headers, str* body, dlg_t** dialog, transaction_cb cb, void* cbp)
254 {
255         str callid, fromtag;
256
257         if (check_params(method, to, from, dialog) < 0) goto err;
258         
259         generate_callid(&callid);
260         generate_fromtag(&fromtag, &callid);
261
262         if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, dialog) < 0) {
263                 LOG(L_ERR, "req_outside(): Error while creating new dialog\n");
264                 goto err;
265         }
266
267         return t_uac(method, headers, body, *dialog, cb, cbp);
268
269  err:
270         if (cbp) shm_free(cbp);
271         return -1;
272 }
273
274
275 /*
276  * Send a transactional request, no dialogs involved
277  */
278 int request(str* m, str* ruri, str* to, str* from, str* h, str* b, transaction_cb c, void* cp)
279 {
280         str callid, fromtag;
281         dlg_t* dialog;
282         int res;
283
284         if (check_params(m, to, from, &dialog) < 0) goto err;
285
286         generate_callid(&callid);
287         generate_fromtag(&fromtag, &callid);
288
289         if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, &dialog) < 0) {
290                 LOG(L_ERR, "req_outside(): Error while creating temorary dialog\n");
291                 goto err;
292         }
293
294         if (ruri) {
295                 dialog->rem_target.s = ruri->s;
296                 dialog->rem_target.len = ruri->len;
297                 dialog->hooks.request_uri = &dialog->rem_target;
298         }
299
300         res = t_uac(m, h, b, dialog, c, cp);
301         dialog->rem_target.s = 0;
302         free_dlg(dialog);
303         return res;
304
305  err:
306         if (cp) shm_free(cp);
307         return -1;
308 }