siptrace: replaced sequeces of string concatenations with safer formatted print
[sip-router] / src / modules / siptrace / siptrace_send.c
1 /*
2  * siptrace module - helper module to trace sip messages
3  *
4  * Copyright (C) 2017 kamailio.org
5  *
6  * This file is part of Kamailio, a free SIP server.
7  *
8  * Kamailio is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * Kamailio is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28
29 #include "../../core/dprint.h"
30 #include "../../core/proxy.h"
31 #include "../../core/forward.h"
32 #include "../../core/parser/parse_content.h"
33 #include "../../core/parser/parse_from.h"
34 #include "../../core/parser/parse_cseq.h"
35
36 #include "siptrace_send.h"
37
38
39 extern int *xheaders_write_flag;
40 extern int *xheaders_read_flag;
41 extern str dup_uri_str;
42 extern sip_uri_t *dup_uri;
43
44 /**
45  *
46  */
47 int sip_trace_prepare(sip_msg_t *msg)
48 {
49         if(parse_from_header(msg) == -1 || msg->from == NULL
50                         || get_from(msg) == NULL) {
51                 LM_ERR("cannot parse FROM header\n");
52                 goto error;
53         }
54
55         if(parse_to_header(msg) == -1 || msg->to == NULL || get_to(msg) == NULL) {
56                 LM_ERR("cannot parse To header\n");
57                 goto error;
58         }
59
60         if(parse_headers(msg, HDR_CALLID_F, 0) != 0 || msg->callid == NULL
61                         || msg->callid->body.s == NULL) {
62                 LM_ERR("cannot parse call-id\n");
63                 goto error;
64         }
65
66         if(msg->cseq == NULL && ((parse_headers(msg, HDR_CSEQ_F, 0) == -1)
67                                                                         || (msg->cseq == NULL))) {
68                 LM_ERR("cannot parse cseq\n");
69                 goto error;
70         }
71
72         return 0;
73 error:
74         return -1;
75 }
76
77 /**
78  * Appends x-headers to the message in sto->body containing data from sto
79  */
80 int sip_trace_xheaders_write(struct _siptrace_data *sto)
81 {
82         char *buf = NULL;
83         int bytes_written = 0;
84         char *eoh = NULL;
85         int eoh_offset = 0;
86         char *new_eoh = NULL;
87
88         if(xheaders_write_flag == NULL || *xheaders_write_flag == 0)
89                 return 0;
90
91         // Memory for the message with some additional headers.
92         // It gets free()ed in sip_trace_xheaders_free().
93         buf = pkg_malloc(sto->body.len + XHEADERS_BUFSIZE);
94         if(buf == NULL) {
95                 LM_ERR("sip_trace_xheaders_write: out of memory\n");
96                 return -1;
97         }
98
99         // Copy the whole message to buf first; it must be \0-terminated for
100         // strstr() to work. Then search for the end-of-header sequence.
101         memcpy(buf, sto->body.s, sto->body.len);
102         buf[sto->body.len] = '\0';
103         eoh = strstr(buf, "\r\n\r\n");
104         if(eoh == NULL) {
105                 LM_ERR("sip_trace_xheaders_write: malformed message\n");
106                 goto error;
107         }
108         eoh += 2; // the first \r\n belongs to the last header => skip it
109
110         // Write the new headers a the end-of-header position. This overwrites
111         // the \r\n terminating the old headers and the beginning of the message
112         // body. Both will be recovered later.
113         bytes_written =
114                         snprintf(eoh, XHEADERS_BUFSIZE, "X-Siptrace-Fromip: %.*s\r\n"
115                                                                                         "X-Siptrace-Toip: %.*s\r\n"
116                                                                                         "X-Siptrace-Time: %llu %llu\r\n"
117                                                                                         "X-Siptrace-Method: %.*s\r\n"
118                                                                                         "X-Siptrace-Dir: %s\r\n",
119                                         sto->fromip.len, sto->fromip.s, sto->toip.len, sto->toip.s,
120                                         (unsigned long long)sto->tv.tv_sec,
121                                         (unsigned long long)sto->tv.tv_usec, sto->method.len,
122                                         sto->method.s, sto->dir);
123         if(bytes_written >= XHEADERS_BUFSIZE) {
124                 LM_ERR("sip_trace_xheaders_write: string too long\n");
125                 goto error;
126         }
127
128         // Copy the \r\n terminating the old headers and the message body from the
129         // old buffer in sto->body.s to the new end-of-header in buf.
130         eoh_offset = eoh - buf;
131         new_eoh = eoh + bytes_written;
132         memcpy(new_eoh, sto->body.s + eoh_offset, sto->body.len - eoh_offset);
133
134         // Change sto to point to the new buffer.
135         sto->body.s = buf;
136         sto->body.len += bytes_written;
137         return 0;
138 error:
139         if(buf != NULL)
140                 pkg_free(buf);
141         return -1;
142 }
143
144 /**
145  * Parses x-headers, saves the data back to sto, and removes the x-headers
146  * from the message in sto->buf
147  */
148 int sip_trace_xheaders_read(struct _siptrace_data *sto)
149 {
150         char *searchend = NULL;
151         char *eoh = NULL;
152         char *xheaders = NULL;
153         long long unsigned int tv_sec, tv_usec;
154
155         if(xheaders_read_flag == NULL || *xheaders_read_flag == 0)
156                 return 0;
157
158         // Find the end-of-header marker \r\n\r\n
159         searchend = sto->body.s + sto->body.len - 3;
160         eoh = memchr(sto->body.s, '\r', searchend - eoh);
161         while(eoh != NULL && eoh < searchend) {
162                 if(memcmp(eoh, "\r\n\r\n", 4) == 0)
163                         break;
164                 eoh = memchr(eoh + 1, '\r', searchend - eoh);
165         }
166         if(eoh == NULL) {
167                 LM_ERR("sip_trace_xheaders_read: malformed message\n");
168                 return -1;
169         }
170
171         // Find x-headers: eoh will be overwritten by \0 to allow the use of
172         // strstr(). The byte at eoh will later be recovered, when the
173         // message body is shifted towards the beginning of the message
174         // to remove the x-headers.
175         *eoh = '\0';
176         xheaders = strstr(sto->body.s, "\r\nX-Siptrace-Fromip: ");
177         if(xheaders == NULL) {
178                 LM_ERR("sip_trace_xheaders_read: message without x-headers "
179                            "from %.*s, callid %.*s\n",
180                                 sto->fromip.len, sto->fromip.s, sto->callid.len, sto->callid.s);
181                 return -1;
182         }
183
184         // Allocate memory for new strings in sto
185         // (gets free()ed in sip_trace_xheaders_free() )
186         sto->fromip.s = pkg_malloc(51);
187         sto->toip.s = pkg_malloc(51);
188         sto->method.s = pkg_malloc(51);
189         sto->dir = pkg_malloc(4);
190         if(!(sto->fromip.s && sto->toip.s && sto->method.s && sto->dir)) {
191                 LM_ERR("sip_trace_xheaders_read: out of memory\n");
192                 goto erroraftermalloc;
193         }
194
195         // Parse the x-headers: scanf()
196         if(sscanf(xheaders, "\r\n"
197                                                 "X-Siptrace-Fromip: %50s\r\n"
198                                                 "X-Siptrace-Toip: %50s\r\n"
199                                                 "X-Siptrace-Time: %llu %llu\r\n"
200                                                 "X-Siptrace-Method: %50s\r\n"
201                                                 "X-Siptrace-Dir: %3s",
202                            sto->fromip.s, sto->toip.s, &tv_sec, &tv_usec, sto->method.s,
203                            sto->dir)
204                         == EOF) {
205                 LM_ERR("sip_trace_xheaders_read: malformed x-headers\n");
206                 goto erroraftermalloc;
207         }
208         sto->fromip.len = strlen(sto->fromip.s);
209         sto->toip.len = strlen(sto->toip.s);
210         sto->tv.tv_sec = (time_t)tv_sec;
211         sto->tv.tv_usec = (suseconds_t)tv_usec;
212         sto->method.len = strlen(sto->method.s);
213
214         // Remove the x-headers: the message body is shifted towards the beginning
215         // of the message, overwriting the x-headers. Before that, the byte at eoh
216         // is recovered.
217         *eoh = '\r';
218         memmove(xheaders, eoh, sto->body.len - (eoh - sto->body.s));
219         sto->body.len -= eoh - xheaders;
220
221         return 0;
222
223 erroraftermalloc:
224         if(sto->fromip.s)
225                 pkg_free(sto->fromip.s);
226         if(sto->toip.s)
227                 pkg_free(sto->toip.s);
228         if(sto->method.s)
229                 pkg_free(sto->method.s);
230         if(sto->dir)
231                 pkg_free(sto->dir);
232         return -1;
233 }
234
235 /**
236  * Frees the memory allocated by sip_trace_xheaders_{write,read}
237  */
238 int sip_trace_xheaders_free(struct _siptrace_data *sto)
239 {
240         if(xheaders_write_flag != NULL && *xheaders_write_flag != 0) {
241                 if(sto->body.s)
242                         pkg_free(sto->body.s);
243         }
244
245         if(xheaders_read_flag != NULL && *xheaders_read_flag != 0) {
246                 if(sto->fromip.s)
247                         pkg_free(sto->fromip.s);
248                 if(sto->toip.s)
249                         pkg_free(sto->toip.s);
250                 if(sto->dir)
251                         pkg_free(sto->dir);
252         }
253
254         return 0;
255 }
256
257
258 /**
259  *
260  */
261 int trace_send_duplicate(char *buf, int len, struct dest_info *dst2)
262 {
263         struct dest_info dst;
264         struct proxy_l *p = NULL;
265
266         if(buf == NULL || len <= 0)
267                 return -1;
268
269         if(dup_uri_str.s == 0 || dup_uri == NULL)
270                 return 0;
271
272         init_dest_info(&dst);
273
274         if(!dst2) {
275                 /* create a temporary proxy from dst param */
276                 dst.proto = PROTO_UDP;
277                 p = mk_proxy(&dup_uri->host,
278                                 (dup_uri->port_no) ? dup_uri->port_no : SIP_PORT, dst.proto);
279                 if(p == 0) {
280                         LM_ERR("bad host name in uri\n");
281                         return -1;
282                 }
283                 hostent2su(
284                                 &dst.to, &p->host, p->addr_idx, (p->port) ? p->port : SIP_PORT);
285
286                 dst.send_sock = get_send_socket(0, &dst.to, dst.proto);
287                 if(dst.send_sock == 0) {
288                         LM_ERR("can't forward to af %d, proto %d no corresponding"
289                                    " listening socket\n",
290                                         dst.to.s.sa_family, dst.proto);
291                         goto error;
292                 }
293         } else {
294                 /* create a temporary proxy to dup uri */
295                 dst.proto = PROTO_UDP;
296                 p = mk_proxy(&dup_uri->host,
297                                 (dup_uri->port_no) ? dup_uri->port_no : SIP_PORT, dst.proto);
298                 if(p == 0) {
299                         LM_ERR("bad host name in uri\n");
300                         return -1;
301                 }
302         }
303
304         if(msg_send((dst2) ? dst2 : &dst, buf, len) < 0) {
305                 LM_ERR("cannot send duplicate message\n");
306                 goto error;
307         }
308
309         if(p) {
310                 free_proxy(p); /* frees only p content, not p itself */
311                 pkg_free(p);
312         }
313         return 0;
314 error:
315         if(p) {
316                 free_proxy(p); /* frees only p content, not p itself */
317                 pkg_free(p);
318         }
319         return -1;
320 }
321
322 /**
323  *
324  */
325 char* siptrace_proto_name(int vproto)
326 {
327         switch(vproto) {
328                 case PROTO_TCP:
329                         return "tcp";
330                 case PROTO_TLS:
331                         return "tls";
332                 case PROTO_SCTP:
333                         return "sctp";
334                 case PROTO_WS:
335                         return "ws";
336                 case PROTO_WSS:
337                         return "wss";
338                 default:
339                         return "udp";
340         }
341 }