tsilo: reworked checking ruri and contact parameters
[sip-router] / src / modules / tsilo / tsilo.c
1 /**
2  *
3  * Copyright (C) 2015 Federico Cabiddu (federico.cabiddu@gmail.com)
4  *
5  * This file is part of Kamailio, a free SIP server.
6  *
7  * Kamailio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version
11  *
12  * Kamailio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25
26 #include "../../core/sr_module.h"
27 #include "../../core/dprint.h"
28 #include "../../core/mod_fix.h"
29 #include "../../core/route.h"
30 #include "../../core/script_cb.h"
31 #include "../../modules/tm/tm_load.h"
32 #include "../../modules/registrar/api.h"
33 #include "../../core/dset.h"
34 #include "../../core/rpc_lookup.h"
35 #include "../../core/kemi.h"
36
37 #include "../../core/parser/contact/parse_contact.h"
38
39 #include "ts_hash.h"
40 #include "ts_handlers.h"
41 #include "ts_append.h"
42 #include "ts_store.h"
43 #include "ts_rpc.h"
44
45 MODULE_VERSION
46
47
48 /** TM bind **/
49 struct tm_binds _tmb;
50 /** REGISTRAR bind **/
51 registrar_api_t _regapi;
52
53 /** parameters */
54 static int hash_size = 2048;
55 int use_domain = 0;
56
57 /** module functions */
58 static int mod_init(void);
59 static void destroy(void);
60
61 static int w_ts_append_to(struct sip_msg* msg, char *idx, char *lbl, char *d);
62 static int w_ts_append_to2(struct sip_msg* msg, char *idx, char *lbl, char *d, char *ruri);
63 static int fixup_ts_append_to(void** param, int param_no);
64 static int w_ts_append(struct sip_msg* _msg, char *_table, char *_ruri);
65 static int fixup_ts_append(void** param, int param_no);
66 static int w_ts_append_by_contact2(struct sip_msg* _msg, char *_table, char *_ruri);
67 static int w_ts_append_by_contact3(struct sip_msg* _msg, char *_table, char *_ruri, char *_contact);
68 static int fixup_ts_append_by_contact(void** param, int param_no);
69 static int w_ts_store(struct sip_msg* msg, char *p1, char *p2);
70 static int w_ts_store1(struct sip_msg* msg, char *_ruri, char *p2);
71
72 stat_var *stored_ruris;
73 stat_var *stored_transactions;
74 stat_var *total_ruris;
75 stat_var *total_transactions;
76 stat_var *added_branches;
77
78 static cmd_export_t cmds[]={
79         {"ts_append_to", (cmd_function)w_ts_append_to,  3,
80                 fixup_ts_append_to, 0, REQUEST_ROUTE | FAILURE_ROUTE },
81         {"ts_append_to", (cmd_function)w_ts_append_to2,  4,
82                 fixup_ts_append_to, 0, REQUEST_ROUTE | FAILURE_ROUTE },
83         {"ts_append", (cmd_function)w_ts_append,  2,
84                 fixup_ts_append, 0, REQUEST_ROUTE | FAILURE_ROUTE },
85         {"ts_append_by_contact", (cmd_function)w_ts_append_by_contact2,  2,     /* for two parameters */
86                 fixup_ts_append_by_contact, 0, REQUEST_ROUTE | FAILURE_ROUTE },
87         {"ts_append_by_contact", (cmd_function)w_ts_append_by_contact3,  3,     /* for three parameters */
88                 fixup_ts_append_by_contact, 0, REQUEST_ROUTE | FAILURE_ROUTE },
89         {"ts_store", (cmd_function)w_ts_store,  0,
90                 0 , 0, REQUEST_ROUTE | FAILURE_ROUTE },
91         {"ts_store", (cmd_function)w_ts_store1,  1,
92                 fixup_spve_null , 0, REQUEST_ROUTE | FAILURE_ROUTE },
93         {0,0,0,0,0,0}
94 };
95
96 static param_export_t params[]={
97         {"hash_size",   INT_PARAM,      &hash_size},
98         {"use_domain",  INT_PARAM,      &use_domain},
99         {0,0,0}
100 };
101
102 #ifdef STATISTICS
103 /*! \brief We expose internal variables via the statistic framework below.*/
104 stat_export_t mod_stats[] = {
105         {"stored_ruris",        STAT_NO_RESET,  &stored_ruris  },
106         {"stored_transactions", STAT_NO_RESET,  &stored_transactions  },
107         {"total_ruris",         STAT_NO_RESET,  &total_ruris  },
108         {"total_transactions",  STAT_NO_RESET,  &total_transactions  },
109         {"added_branches",      STAT_NO_RESET,  &added_branches  },
110         {0, 0, 0}
111 };
112 #endif
113
114 /** module exports */
115 struct module_exports exports = {
116         "tsilo",         /* module name */
117         DEFAULT_DLFLAGS, /* dlopen flags */
118         cmds,            /* exported functions */
119         params,          /* exported parameters */
120         0,               /* exported RPC methods */
121         0,               /* exported pseudo-variables */
122         0,               /* response handling function */
123         mod_init,        /* module initialization function */
124         0,               /* per-child init function */
125         destroy          /* destroy function */
126 };
127
128 /**
129  * init module function
130  */
131 static int mod_init(void)
132 {
133         unsigned int n;
134
135         /* register the RPC methods */
136         if(rpc_register_array(rpc_methods)!=0)
137         {
138         LM_ERR("failed to register RPC commands\n");
139         return -1;
140         }
141         /* load the TM API */
142         if (load_tm_api(&_tmb)!=0) {
143                 LM_ERR("can't load TM API\n");
144                 return -1;
145         }
146
147         /* load the REGISTRAR API */
148         if (registrar_load_api(&_regapi) != 0) {
149                 LM_ERR("cannot load REGISTRAR API\n");
150                 return -1;
151         }
152
153         /* sanitize hash_size */
154         if (hash_size < 1){
155                 LM_WARN("hash_size is smaller "
156                         "than 1  -> rounding from %d to 1\n",
157                         hash_size);
158                 hash_size = 1;
159         }
160
161         /* initialize the hash table */
162         for( n=0 ; n<(8*sizeof(n)) ; n++) {
163                 if (hash_size==(1<<n))
164                         break;
165                 if (n && hash_size<(1<<n)) {
166                         LM_WARN("hash_size is not a power "
167                                 "of 2 as it should be -> rounding from %d to %d (n=%d)\n",
168                                 hash_size, 1<<(n-1), n);
169                         hash_size = 1<<(n-1);
170                         break;
171                 }
172         }
173
174         LM_DBG("creating table with size %d", hash_size);
175         if ( init_ts_table(hash_size)<0 ) {
176                 LM_ERR("failed to create hash table\n");
177                 return -1;
178         }
179
180 #ifdef STATISTICS
181         /* register statistics */
182         if (register_module_stats(exports.name, mod_stats)!=0 ) {
183                 LM_ERR("failed to register core statistics\n");
184                 return -1;
185         }
186         return 0;
187 #endif
188
189 }
190
191 /**
192  * destroy function
193  */
194 static void destroy(void)
195 {
196         destroy_ts_table();
197         return;
198 }
199
200 static int ts_check_uri(str *uri)
201 {
202         struct sip_uri ruri;
203         if (parse_uri(uri->s, uri->len, &ruri)!=0)
204         {
205                 LM_ERR("bad uri [%.*s]\n",
206                                 uri->len,
207                                 uri->s);
208                 return -1;
209         }
210         return 0;
211 }
212 /**
213  *
214  */
215 static int fixup_ts_append_to(void** param, int param_no)
216 {
217         if (param_no==1 || param_no==2) {
218                 return fixup_igp_null(param, 1);
219         }
220
221         if (param_no==3) {
222                 if(strlen((char*)*param)<=1 && (*(char*)(*param)==0 || *(char*)(*param)=='0')) {
223                         *param = (void*)0;
224                         LM_ERR("empty table name\n");
225                         return -1;
226                 }
227         }
228
229         if (param_no==4) {
230                 return fixup_spve_null(param, 1);
231         }
232         return 0;
233 }
234
235 static int fixup_ts_append(void** param, int param_no)
236 {
237         if (param_no==1) {
238                 if(strlen((char*)*param)<=1 && (*(char*)(*param)==0 || *(char*)(*param)=='0')) {
239                         *param = (void*)0;
240                         LM_ERR("empty table name\n");
241                         return -1;
242                 }
243         }
244
245         if (param_no==2 || param_no==3) {
246                 return fixup_spve_null(param, 1);
247         }
248
249         return 0;
250 }
251
252 static int fixup_ts_append_by_contact(void** param, int param_no)
253 {
254         if (param_no==1) {
255                 if(strlen((char*)*param)<=1 && (*(char*)(*param)==0 || *(char*)(*param)=='0')) {
256                         *param = (void*)0;
257                         LM_ERR("empty table name\n");
258                         return -1;
259                 }
260         }
261
262         if (param_no==2 || param_no==3) {
263                 return fixup_spve_null(param, 1);
264         }
265
266         return 0;
267 }
268
269 /**
270  *
271  */
272 static int w_ts_append(struct sip_msg* _msg, char *_table, char *_ruri)
273 {
274         str tmp  = STR_NULL;
275         str ruri = STR_NULL;
276         int rc;
277
278         /* we do not want to do append by particular location */
279         str contact = STR_NULL;
280
281         if(_ruri==NULL || (fixup_get_svalue(_msg, (gparam_p)_ruri, &tmp)!=0 || tmp.len<=0)) {
282                 LM_ERR("invalid ruri parameter\n");
283                 return -1;
284         }
285         if(ts_check_uri(&tmp)<0)
286                 return -1;
287
288         if (pkg_str_dup(&ruri, &tmp) < 0)
289                 return -1;
290
291         rc = ts_append(_msg, &ruri, &contact, _table);
292
293         pkg_free(ruri.s);
294
295         return rc;
296 }
297
298 /**
299  *
300  */
301 static int ki_ts_append(sip_msg_t* _msg, str *_table, str *_ruri)
302 {
303         str ruri = STR_NULL;
304         int rc;
305
306         /* we do not want to do append by particular location */
307         str contact = STR_NULL;
308
309         if(ts_check_uri(_ruri)<0)
310                 return -1;
311
312         if (pkg_str_dup(&ruri, _ruri) < 0)
313                 return -1;
314
315         rc = ts_append(_msg, &ruri, &contact, _table->s);
316
317         pkg_free(ruri.s);
318
319         return rc;
320 }
321
322 /**
323  *
324  */
325 static int w_ts_append_to(struct sip_msg* msg, char *idx, char *lbl, char *table)
326 {
327         unsigned int tindex;
328         unsigned int tlabel;
329
330         /* we do not want to do append by particular location */
331         str contact = STR_NULL;
332
333         if(fixup_get_ivalue(msg, (gparam_p)idx, (int*)&tindex)<0) {
334                 LM_ERR("cannot get transaction index\n");
335                 return -1;
336         }
337
338         if(fixup_get_ivalue(msg, (gparam_p)lbl, (int*)&tlabel)<0) {
339                 LM_ERR("cannot get transaction label\n");
340                 return -1;
341         }
342
343         /* we do not want to do append by particular location here */
344         return ts_append_to(msg, tindex, tlabel, table, 0, &contact);
345 }
346
347 /**
348  *
349  */
350 static int ki_ts_append_to(sip_msg_t* _msg, int tindex, int tlabel, str *_table)
351 {
352         /* we do not want to do append by particular location */
353         str contact = STR_NULL;
354
355         /* we do not want to do append by particular location here */
356         return ts_append_to(_msg, (unsigned int)tindex, (unsigned int)tlabel,
357                         _table->s, 0, &contact);
358 }
359
360 /**
361  *
362  */
363 static int w_ts_append_to2(struct sip_msg* msg, char *idx, char *lbl, char *table, char *ruri)
364 {
365         unsigned int tindex;
366         unsigned int tlabel;
367         str suri;
368
369         /* we do not want to do append by particular location */
370         str contact = STR_NULL;
371
372         if(fixup_get_ivalue(msg, (gparam_p)idx, (int*)&tindex)<0) {
373                 LM_ERR("cannot get transaction index\n");
374                 return -1;
375         }
376
377         if(fixup_get_ivalue(msg, (gparam_p)lbl, (int*)&tlabel)<0) {
378                 LM_ERR("cannot get transaction label\n");
379                 return -1;
380         }
381
382         if(fixup_get_svalue(msg, (gparam_t*)ruri, &suri)!=0) {
383                 LM_ERR("failed to conert r-uri parameter\n");
384                 return -1;
385         }
386         if(ts_check_uri(&suri)<0)
387                 return -1;
388
389         /* we do not want to do append by particular location here */
390         return ts_append_to(msg, tindex, tlabel, table, &suri, &contact);
391 }
392
393 /**
394  *
395  */
396 static int ki_ts_append_to_uri(sip_msg_t* _msg, int tindex, int tlabel,
397                 str *_table, str *_uri)
398 {
399         /* we do not want to do append by particular location */
400         str contact = STR_NULL;
401
402         /* we do not want to do append by particular location here */
403         return ts_append_to(_msg, (unsigned int)tindex, (unsigned int)tlabel,
404                         _table->s, _uri, &contact);
405 }
406
407 /**
408  *
409  */
410 static int w_ts_append_by_contact2(struct sip_msg* _msg, char *_table, char *_ruri)
411 {
412         str ruri = STR_NULL;
413         str ruri_fixed = STR_NULL;
414
415         str contact = STR_NULL;
416         str tmp_contact = STR_NULL;
417         struct sip_uri curi;
418
419         int rc;
420
421         if (_ruri==NULL) {
422                 LM_ERR("invalid ruri parameter\n");
423                 return -1;
424         }
425
426         /* parse R-URI */
427         if (fixup_get_svalue(_msg, (gparam_t*)_ruri, &ruri_fixed)!=0) {
428                 LM_ERR("failed to convert r-uri parameter\n");
429                 return -1;
430         }
431
432         if (ruri_fixed.s == NULL || ruri_fixed.len <= 0) {
433                 LM_ERR("invalid ruri parameter - empty or negative length\n");
434                 return -1;
435         }
436
437         if (pkg_str_dup(&ruri, &ruri_fixed) < 0) {
438                 LM_ERR("failed to copy r-uri parameter\n");
439                 return -1;
440         }
441
442         if (ts_check_uri(&ruri) < 0) {
443                 LM_ERR("failed to parse R-URI.\n");
444                 return -1;
445         }
446
447         /* parse Contact header */
448         if ((!_msg->contact && parse_headers(_msg, HDR_CONTACT_F, 0) != 0)
449                         || !_msg->contact) {
450                 LM_WARN("missing contact header or the value is empty/malformed.\n");
451                 return -1;
452         }
453         if (_msg->contact) {
454                 if (parse_contact(_msg->contact) < 0) {
455                         LM_WARN("failed to parse Contact header.\n");
456                         return -1;
457                 }
458                 if (parse_uri(
459                                                 ((struct contact_body*)_msg->contact->parsed)->contacts->uri.s,
460                                                 ((struct contact_body*)_msg->contact->parsed)->contacts->uri.len,
461                                                 &curi) != 0 ) {
462                         if (ts_check_uri(&_msg->contact->body) < 0) {   /* one more attempt */
463                                 LM_WARN("failed to parse Contact header.\n");
464                                 return -1;
465                         }
466                 }
467
468                 tmp_contact.len = ((struct contact_body*)_msg->contact->parsed)->contacts->uri.len;
469                 tmp_contact.s = (char*)pkg_malloc(tmp_contact.len+1);
470                 if (tmp_contact.s == NULL) {
471                         PKG_MEM_ERROR;
472                         return -1;
473                 }
474                 memcpy(tmp_contact.s, ((struct contact_body*)_msg->contact->parsed)->contacts->uri.s, tmp_contact.len);
475                 tmp_contact.s[tmp_contact.len] = '\0';
476
477                 if (pkg_str_dup(&contact, &tmp_contact) < 0) {
478                         if (pkg_str_dup(&contact, &_msg->contact->body) < 0) { /* one more attempt */
479                                 LM_ERR("problems when calling ts_append_contact(), cannot copy Contact parameter.\n");
480                                 return -1;
481                         }
482                 }
483         }
484
485         /* contact must be of syntax: sip:<user>@<host>:<port> with no parameters list */
486         rc = ts_append(_msg, &ruri, &contact, _table);
487
488         /* free previously used memory */
489         pkg_free(ruri.s);
490         pkg_free(contact.s);
491         pkg_free(tmp_contact.s);
492
493         return rc;
494 }
495
496 /**
497  *
498  */
499 static int ki_ts_append_by_contact(sip_msg_t* _msg, str *_table, str *_ruri)
500 {
501         str ruri = STR_NULL;
502         str contact = STR_NULL;
503         str tmp_contact = STR_NULL;
504         struct sip_uri curi;
505         int rc;
506
507         /* parse R-URI */
508         if (ts_check_uri(_ruri) < 0)
509                 return -1;
510         if (pkg_str_dup(&ruri, _ruri) < 0)
511                 return -1;
512
513         /* parse Contact header */
514         if ((!_msg->contact && parse_headers(_msg, HDR_CONTACT_F, 0) != 0) || !_msg->contact)
515                 return -1;
516
517         if (_msg->contact) {
518                 if (parse_contact(_msg->contact) < 0)
519                         return -1;
520                 if (parse_uri(
521                                                 ((struct contact_body*)_msg->contact->parsed)->contacts->uri.s,
522                                                 ((struct contact_body*)_msg->contact->parsed)->contacts->uri.len,
523                                                 &curi) != 0 ) {
524                         if (ts_check_uri(&_msg->contact->body) < 0) /* one more attempt */
525                                 return -1;
526                 }
527
528                 tmp_contact.len = ((struct contact_body*)_msg->contact->parsed)->contacts->uri.len;
529                 tmp_contact.s = (char*)pkg_malloc(tmp_contact.len+1);
530                 if (tmp_contact.s == NULL) {
531                         PKG_MEM_ERROR;
532                         return -1;
533                 }
534                 memcpy(tmp_contact.s, ((struct contact_body*)_msg->contact->parsed)->contacts->uri.s, tmp_contact.len);
535                 tmp_contact.s[tmp_contact.len] = '\0';
536
537                 if (pkg_str_dup(&contact, &tmp_contact) < 0) {
538                         if (pkg_str_dup(&contact, &_msg->contact->body) < 0) /* one more attempt */
539                                 return -1;
540                 }
541         }
542
543         /* contact must be of syntax: sip:<user>@<host>:<port> with no parameters list */
544         rc = ts_append(_msg, &ruri, &contact, _table->s);
545
546         pkg_free(ruri.s);
547         pkg_free(contact.s);
548         pkg_free(tmp_contact.s);
549
550         return rc;
551 }
552
553 /**
554  *
555  */
556 static int w_ts_append_by_contact3(struct sip_msg* _msg, char *_table,
557                 char *_ruri, char *_contact)
558 {
559         str ruri = STR_NULL;
560         str ruri_fixed = STR_NULL;
561
562         str contact = STR_NULL;
563         str contact_fixed = STR_NULL;
564
565         int rc;
566
567         if (_ruri==NULL || _contact==NULL) {
568                 LM_ERR("invalid ruri or contact parameters\n");
569                 return -1;
570         }
571         /* parse R-URI */
572         if (fixup_get_svalue(_msg, (gparam_t*)_ruri, &ruri_fixed)!=0) {
573                 LM_ERR("failed to convert r-uri parameter\n");
574                 return -1;
575         }
576
577         if (ruri_fixed.s == NULL || ruri_fixed.len <= 0) {
578                 LM_ERR("invalid ruri parameter - empty or negative length\n");
579                 return -1;
580         }
581
582         if (pkg_str_dup(&ruri, &ruri_fixed) < 0) {
583                 LM_ERR("failed to copy r-uri parameter\n");
584                 return -1;
585         }
586
587         if (ts_check_uri(&ruri) < 0) {
588                 LM_ERR("failed to parse R-URI.\n");
589                 return -1;
590         }
591
592         /* parse Contact header */
593         if (fixup_get_svalue(_msg, (gparam_t*)_contact, &contact_fixed)!=0) {
594                 LM_ERR("failed to convert contact parameter\n");
595                 return -1;
596         }
597
598         if (contact_fixed.s == NULL || contact_fixed.len <= 0) {
599                 LM_ERR("invalid contact parameter value\n");
600                 return -1;
601         }
602
603         if (pkg_str_dup(&contact, &contact_fixed) < 0) {
604                 LM_ERR("failed to copy r-uri parameter\n");
605                 return -1;
606         }
607
608         if (ts_check_uri(&contact) < 0) {
609                 LM_ERR("failed to parse Contact parameter.\n");
610                 return -1;
611         }
612
613         /* contact must be of syntax: sip:<user>@<host>:<port> with no parameters list */
614         rc = ts_append(_msg, &ruri, &contact, _table);
615
616         pkg_free(ruri.s);
617         pkg_free(contact.s);
618
619         return rc;
620 }
621
622 /**
623  *
624  */
625 static int ki_ts_append_by_contact_uri(sip_msg_t* _msg, str *_table, str *_ruri,
626                 str *_contact)
627 {
628         str ruri = STR_NULL;
629         str contact = STR_NULL;
630
631         int rc;
632
633         /* parse R-URI */
634         if(ts_check_uri(_ruri) < 0)
635                 return -1;
636         if (pkg_str_dup(&ruri, _ruri) < 0)
637                 return -1;
638
639         /* parse Contact header */
640         if (ts_check_uri(_contact) < 0)
641                 return -1;
642         if (pkg_str_dup(&contact, _contact) < 0)
643                 return -1;
644
645         /* contact must be of syntax: sip:<user>@<host>:<port> with no parameters list */
646         rc = ts_append(_msg, &ruri, &contact, _table->s);
647
648         pkg_free(ruri.s);
649         pkg_free(contact.s);
650
651         return rc;
652 }
653
654 /**
655  *
656  */
657 static int w_ts_store(struct sip_msg* msg, char *p1, char *p2)
658 {
659         return ts_store(msg, 0);
660 }
661
662 /**
663  *
664  */
665 static int ki_ts_store(sip_msg_t* msg)
666 {
667         return ts_store(msg, 0);
668 }
669
670 /**
671  *
672  */
673 static int w_ts_store1(struct sip_msg* msg, char *_ruri, char *p2)
674 {
675         str suri;
676
677         if(fixup_get_svalue(msg, (gparam_t*)_ruri, &suri)!=0) {
678                 LM_ERR("failed to conert r-uri parameter\n");
679                 return -1;
680         }
681         return ts_store(msg, &suri);
682 }
683
684 /**
685  *
686  */
687 /* clang-format off */
688 static sr_kemi_t sr_kemi_tsilo_exports[] = {
689         { str_init("tsilo"), str_init("ts_store"),
690                 SR_KEMIP_INT, ki_ts_store,
691                 { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
692                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
693         },
694         { str_init("tsilo"), str_init("ts_store_uri"),
695                 SR_KEMIP_INT, ts_store,
696                 { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
697                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
698         },
699         { str_init("tsilo"), str_init("ts_append"),
700                 SR_KEMIP_INT, ki_ts_append,
701                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
702                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
703         },
704         { str_init("tsilo"), str_init("ts_append_to"),
705                 SR_KEMIP_INT, ki_ts_append_to,
706                 { SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_STR,
707                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
708         },
709         { str_init("tsilo"), str_init("ts_append_to_uri"),
710                 SR_KEMIP_INT, ki_ts_append_to_uri,
711                 { SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_STR,
712                         SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE }
713         },
714         { str_init("tsilo"), str_init("ts_append_by_contact"),
715                 SR_KEMIP_INT, ki_ts_append_by_contact,
716                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
717                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
718         },
719         { str_init("tsilo"), str_init("ts_append_by_contact_uri"),
720                 SR_KEMIP_INT, ki_ts_append_by_contact_uri,
721                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR,
722                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
723         },
724
725         { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
726 };
727 /* clang-format on */
728
729 int mod_register(char *path, int *dlflags, void *p1, void *p2)
730 {
731         sr_kemi_modules_add(sr_kemi_tsilo_exports);
732         return 0;
733 }