6d93717db5d8d6154988de21b5109a2b20d5fd1d
[kamailio] / src / modules / usrloc / ul_keepalive.c
1 /*
2  * Usrloc module - keepalive
3  *
4  * Copyright (C) 2020 Asipto.com
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 #include <sys/time.h>
29
30 #include "../../core/dprint.h"
31 #include "../../core/ut.h"
32 #include "../../core/resolve.h"
33 #include "../../core/forward.h"
34 #include "../../core/globals.h"
35 #include "../../core/pvar.h"
36 #include "../../core/parser/parse_uri.h"
37 #include "../../core/parser/parse_from.h"
38 #include "../../core/parser/parse_to.h"
39 #include "../../core/parser/parse_rr.h"
40
41 #include "ul_keepalive.h"
42
43 static int ul_ka_send(str *kamsg, dest_info_t *kadst);
44
45 /**
46  *
47 _KAMETHOD_ _URI_ SIP/2.0\r\n
48 Via: SIP/2.0/_PROTO_ _IP_:_PORT_;branch=z9hG4bKx._GCNT_._BCNT_.0\r\n
49 __KAROUTES__
50 From: <_KAFROM_>;tag=_RUID_-_AORHASH_-_TIMESEC_-_TIMEUSEC_-_GCNT_._BCNT_\r\n
51 To: <sip:_AOR_>\r\n
52 Call-ID: _KACALLID_\r\n
53 CSeq: 1 _KAMETHOD_\r\n
54 Content-Length: 0\r\n\r\n"
55
56 */
57
58 #define ULKA_CALLID_PREFIX "ksrulka-"
59 #define ULKA_CALLID_PREFIX_LEN (sizeof(ULKA_CALLID_PREFIX) - 1)
60
61 #define ULKA_MSG "%.*s %.*s SIP/2.0\r\n" \
62   "Via: SIP/2.0/%.*s %.*s:%.*s;branch=z9hG4bKx.%u.%u.0\r\n" \
63   "%s%.*s%.*s" \
64   "From: <%.*s>;tag=%.*s-%x-%lx-%lx-%x.%x\r\n" \
65   "To: <sip:%.*s%s%.*s>\r\n" \
66   "Call-ID: " ULKA_CALLID_PREFIX "%u.%u\r\n" \
67   "CSeq: 80 %.*s\r\n" \
68   "Content-Length: 0\r\n\r\n"
69
70 extern str ul_ka_from;
71 extern str ul_ka_domain;
72 extern str ul_ka_method;
73 extern int ul_ka_mode;
74 extern int ul_ka_filter;
75 extern int ul_ka_loglevel;
76 extern pv_elem_t *ul_ka_logfmt;
77
78 extern unsigned int ul_nat_bflag;
79
80 static unsigned int _ul_ka_counter = 0;
81
82 /**
83  *
84  */
85 int ul_ka_urecord(urecord_t *ur)
86 {
87         ucontact_t *uc;
88 #define ULKA_BUF_SIZE 2048
89         char kabuf[ULKA_BUF_SIZE];
90         int kabuf_len;
91         str kamsg;
92         str vaddr;
93         str vport;
94         str sdst = STR_NULL;
95         str sproto = STR_NULL;
96         sip_uri_t duri;
97         char dproto;
98         struct hostent *he;
99         socket_info_t *ssock;
100         dest_info_t idst;
101         unsigned int bcnt = 0;
102         int aortype = 0;
103         int i;
104         struct timeval tv;
105
106         if (ul_ka_mode == ULKA_NONE) {
107                 return 0;
108         }
109         LM_DBG("keepalive for aor: %.*s\n", ur->aor.len, ur->aor.s);
110
111         for(i=0; i<ur->aor.len; i++) {
112                 if(ur->aor.s[i] == '@') {
113                         aortype = 1;
114                         break;
115                 }
116         }
117         _ul_ka_counter++;
118         for (uc = ur->contacts; uc != NULL; uc = uc->next) {
119                 if (uc->c.len <= 0) {
120                         continue;
121                 }
122                 if((ul_ka_filter&GAU_OPT_SERVER_ID) && (uc->server_id != server_id)) {
123                         continue;
124                 }
125                 if(ul_ka_mode & ULKA_NAT) {
126                         /* keepalive for natted contacts only */
127                         if (ul_nat_bflag == 0) {
128                                 continue;
129                         }
130                         if ((uc->cflags & ul_nat_bflag) != ul_nat_bflag) {
131                                 continue;
132                         }
133                 }
134                 if(uc->received.len > 0) {
135                         sdst = uc->received;
136                 } else {
137                         if (uc->path.len > 0) {
138                                 if(get_path_dst_uri(&uc->path, &sdst) < 0) {
139                                         LM_ERR("failed to get first uri for path\n");
140                                         continue;
141                                 }
142                         } else {
143                                 sdst = uc->c;
144                         }
145                 }
146                 if(parse_uri(sdst.s, sdst.len, &duri) < 0) {
147                         LM_ERR("cannot parse next hop uri\n");
148                         continue;
149                 }
150
151                 if(duri.port_no == 0) {
152                         duri.port_no = SIP_PORT;
153                 }
154                 dproto = duri.proto;
155                 he = sip_resolvehost(&duri.host, &duri.port_no, &dproto);
156                 if(he == NULL) {
157                         LM_ERR("cannot resolve destination\n");
158                         continue;
159                 }
160                 if(ul_ka_mode & ULKA_UDP) {
161                         if(dproto != PROTO_UDP) {
162                                 LM_DBG("skipping non-udp contact - proto %d\n", (int)dproto);
163                                 continue;
164                         }
165                 }
166                 init_dest_info(&idst);
167                 hostent2su(&idst.to, he, 0, duri.port_no);
168                 ssock = uc->sock;
169                 if(ssock == NULL) {
170                         ssock = get_send_socket(0, &idst.to, dproto);
171                 }
172                 if(ssock == NULL) {
173                         LM_ERR("cannot get sending socket\n");
174                         continue;
175                 }
176                 idst.proto = dproto;
177                 idst.send_sock = ssock;
178
179                 if(ssock->useinfo.name.len > 0) {
180                         vaddr = ssock->useinfo.name;
181                 } else {
182                         vaddr = ssock->address_str;
183                 }
184                 if(ssock->useinfo.port_no > 0) {
185                         vport = ssock->useinfo.port_no_str;
186                 } else {
187                         vport = ssock->port_no_str;
188                 }
189                 get_valid_proto_string(dproto, 1, 1, &sproto);
190
191                 bcnt++;
192                 gettimeofday(&tv, NULL);
193                 kabuf_len = snprintf(kabuf, ULKA_BUF_SIZE - 1, ULKA_MSG,
194                                 ul_ka_method.len, ul_ka_method.s,
195                                 uc->c.len, uc->c.s,
196                                 sproto.len, sproto.s,
197                                 vaddr.len, vaddr.s,
198                                 vport.len, vport.s,
199                                 _ul_ka_counter, bcnt,
200                                 (uc->path.len>0)?"Route: ":"",
201                                 (uc->path.len>0)?uc->path.len:0,
202                                 (uc->path.len>0)?uc->path.s:"",
203                                 (uc->path.len>0)?2:0,
204                                 (uc->path.len>0)?"\r\n":"",
205                                 ul_ka_from.len, ul_ka_from.s,
206                                 uc->ruid.len, uc->ruid.s, ur->aorhash,
207                                 (unsigned long)tv.tv_sec, (unsigned long)tv.tv_usec,
208                                 _ul_ka_counter, bcnt,
209                                 ur->aor.len, ur->aor.s,
210                                 (aortype==1)?"":"@",
211                                 (aortype==1)?0:ul_ka_domain.len, (aortype==1)?"":ul_ka_domain.s,
212                                 _ul_ka_counter, bcnt,
213                                 ul_ka_method.len, ul_ka_method.s);
214                 if(kabuf_len<=0 || kabuf_len>=ULKA_BUF_SIZE) {
215                         LM_ERR("failed to print the keepalive request\n");
216                 } else {
217                         LM_DBG("keepalive request (len: %d) [[\n%.*s]]\n",
218                                         kabuf_len, kabuf_len, kabuf);
219                         kamsg.s = kabuf;
220                         kamsg.len = kabuf_len;
221                         ul_ka_send(&kamsg, &idst);
222                 }
223
224         }
225         return 0;
226 }
227
228 /**
229  *
230  */
231 static int ul_ka_send(str *kamsg, dest_info_t *kadst)
232 {
233         if (kadst->proto == PROTO_UDP) {
234                 return udp_send(kadst, kamsg->s, kamsg->len);
235         }
236
237 #ifdef USE_TCP
238         else if(kadst->proto == PROTO_TCP) {
239                 /*tcp*/
240                 kadst->id=0;
241                 return tcp_send(kadst, 0, kamsg->s, kamsg->len);
242         }
243 #endif
244 #ifdef USE_TLS
245         else if(kadst->proto == PROTO_TLS) {
246                 /*tls*/
247                 kadst->id=0;
248                 return tcp_send(kadst, 0, kamsg->s, kamsg->len);
249         }
250 #endif
251 #ifdef USE_SCTP
252         else if(kadst->proto == PROTO_SCTP) {
253                 /*sctp*/
254                 return sctp_core_msg_send(kadst, kamsg->s, kamsg->len);
255         }
256 #endif
257         else {
258                 LM_ERR("unknown proto [%d] for sending keepalive\n",
259                                 kadst->proto);
260                 return -1;
261         }
262 }
263
264 /**
265  *
266  */
267 unsigned long ul_ka_fromhex(str *shex, int *err)
268 {
269     unsigned long v = 0;
270         int i;
271
272         *err = 0;
273     for (i=0; i<shex->len; i++) {
274         char b = shex->s[i];
275         if (b >= '0' && b <= '9') b = b - '0';
276         else if (b >= 'a' && b <='f') b = b - 'a' + 10;
277         else if (b >= 'A' && b <='F') b = b - 'A' + 10;
278                 else { *err = 1; return 0; };
279         v = (v << 4) | (b & 0xF);
280     }
281     return v;
282 }
283
284 /**
285  *
286  */
287 int ul_ka_reply_received(sip_msg_t *msg)
288 {
289         to_body_t *fb;
290         str ruid;
291         str tok;
292         int err;
293         unsigned int aorhash;
294         char *p;
295         struct timeval tvm;
296         struct timeval tvn;
297         unsigned int tvdiff;
298
299         if(msg->cseq == NULL) {
300                 if((parse_headers(msg, HDR_CSEQ_F, 0) == -1) || (msg->cseq == NULL)) {
301                         LM_ERR("invalid CSeq header\n");
302                         return -1;
303                 }
304         }
305
306         if(get_cseq(msg)->method.len != ul_ka_method.len) {
307                 return 1;
308         }
309         if(strncmp(get_cseq(msg)->method.s, ul_ka_method.s, ul_ka_method.len) != 0) {
310                 return 1;
311         }
312
313         /* there must be no second via */
314         if(!(parse_headers(msg, HDR_VIA2_F, 0) == -1 || (msg->via2 == 0)
315                            || (msg->via2->error != PARSE_OK))) {
316                 return 1;
317         }
318
319         /* from uri check */
320         if((parse_from_header(msg)) < 0) {
321                 LM_ERR("cannot parse From header\n");
322                 return -1;
323         }
324
325         fb = get_from(msg);
326         if(fb->uri.len != ul_ka_from.len
327                         || strncmp(fb->uri.s, ul_ka_from.s, ul_ka_from.len) != 0) {
328                 return 1;
329         }
330
331         /* from-tag is: ruid-aorhash-tmsec-tmusec-counter */
332         if(fb->tag_value.len <= 8) {
333                 return 1;
334         }
335
336         LM_DBG("checking keepalive reply [%.*s]\n", fb->tag_value.len,
337                         fb->tag_value.s);
338
339         /* todo: macro/function to tokenize */
340         /* skip counter */
341         p = q_memrchr(fb->tag_value.s, '-', fb->tag_value.len);
342         if(p == NULL) {
343                 LM_DBG("from tag format mismatch [%.*s]\n", fb->tag_value.len,
344                                 fb->tag_value.s);
345                 return 1;
346         }
347
348         /* tv_usec hash */
349         tok.len = p - fb->tag_value.s;
350         p = q_memrchr(fb->tag_value.s, '-', tok.len);
351         if(p == NULL) {
352                 LM_DBG("from tag format mismatch [%.*s]\n", fb->tag_value.len,
353                                 fb->tag_value.s);
354                 return 1;
355         }
356         tok.s = p + 1;
357         tok.len = fb->tag_value.s + tok.len - tok.s;
358         if(tok.len <= 0) {
359                 LM_DBG("empty token\n");
360                 return 1;
361         }
362         LM_DBG("tv usec string is [%.*s] (%d)\n", tok.len, tok.s, tok.len);
363         tvm.tv_usec = ul_ka_fromhex(&tok, &err);
364         if(err==1) {
365                 LM_DBG("invalid tv usec value\n");
366                 return 1;
367         }
368         LM_DBG("tv usec is [%lu]\n", (unsigned long)tvm.tv_usec);
369
370         /* tv_sec hash */
371         tok.len = p - fb->tag_value.s;
372         p = q_memrchr(fb->tag_value.s, '-', tok.len);
373         if(p == NULL) {
374                 LM_DBG("from tag format mismatch [%.*s]\n", fb->tag_value.len,
375                                 fb->tag_value.s);
376                 return 1;
377         }
378         tok.s = p + 1;
379         tok.len = fb->tag_value.s + tok.len - tok.s;
380         if(tok.len <= 0) {
381                 LM_DBG("empty token\n");
382                 return 1;
383         }
384         LM_DBG("tv sec string is [%.*s] (%d)\n", tok.len, tok.s, tok.len);
385         tvm.tv_sec = ul_ka_fromhex(&tok, &err);
386         if(err==1) {
387                 LM_DBG("invalid tv sec value\n");
388                 return 1;
389         }
390         LM_DBG("tv sec is [%lu]\n", (unsigned long)tvm.tv_sec);
391
392         /* aor hash */
393         tok.len = p - fb->tag_value.s;
394         p = q_memrchr(fb->tag_value.s, '-', tok.len);
395         if(p == NULL) {
396                 LM_DBG("from tag format mismatch [%.*s]\n", fb->tag_value.len,
397                                 fb->tag_value.s);
398                 return 1;
399         }
400         tok.s = p + 1;
401         tok.len = fb->tag_value.s + tok.len - tok.s;
402         if(tok.len <= 0) {
403                 LM_DBG("empty token\n");
404                 return 1;
405         }
406         LM_DBG("aor hash string is [%.*s] (%d)\n", tok.len, tok.s, tok.len);
407         aorhash = ul_ka_fromhex(&tok, &err);
408         if(err==1) {
409                 LM_DBG("invalid aor hash value\n");
410                 return 1;
411         }
412         LM_DBG("aor hash is [%u]\n", aorhash);
413
414         ruid.s = fb->tag_value.s;
415         ruid.len = tok.s - ruid.s - 1;
416
417         if(ruid.len <= 0) {
418                 LM_DBG("cannot get ruid in [%.*s]\n", fb->tag_value.len,
419                                 fb->tag_value.s);
420                 return 1;
421         }
422
423         gettimeofday(&tvn, NULL);
424         tvdiff = (tvn.tv_sec - tvm.tv_sec) * 1000000
425                                         + (tvn.tv_usec - tvm.tv_usec);
426         ul_update_keepalive(aorhash, &ruid, tvn.tv_sec, tvdiff);
427
428         if(ul_ka_loglevel != 255 && ul_ka_logfmt != NULL) {
429                 if (pv_printf_s(msg, ul_ka_logfmt, &tok) == 0) {
430                         LOG(ul_ka_loglevel, "keepalive roundtrip: %u.%06u sec - ruid [%.*s]%.*s\n",
431                                         tvdiff/1000000, tvdiff%1000000, ruid.len, ruid.s,
432                                         tok.len, tok.s);
433                         return 0;
434                 }
435         }
436
437         LM_DBG("response of keepalive for ruid [%.*s] aorhash [%u] roundtrip: %u.%06u secs\n",
438                         ruid.len, ruid.s, aorhash, tvdiff/1000000, tvdiff%1000000);
439
440         return 0;
441 }