9769a3c7d81a89d66b6099c9cce585dd2fe59af6
[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         str ruri = STR_NULL;
412         str ruri_fixed = STR_NULL;
413
414         str contact = STR_NULL;
415         str tmp_contact = STR_NULL;
416         struct sip_uri curi;
417
418         int rc;
419
420         /* parse R-URI */
421         if (fixup_get_svalue(_msg, (gparam_t*)_ruri, &ruri_fixed)!=0) {
422                 LM_ERR("failed to convert r-uri parameter\n");
423                 return -1;
424         }
425
426         if (_ruri==NULL || strlen(_ruri) <= 0 || ruri_fixed.len <= 0) {
427                 LM_ERR("tsilo: invalid ruri parameter (empty or zero length).\n");
428                 return -1;
429         }
430
431         if (pkg_str_dup(&ruri, &ruri_fixed) < 0) {
432                 LM_ERR("failed to copy r-uri parameter\n");
433                 return -1;
434         }
435
436         if (ts_check_uri(&ruri) < 0) {
437                 LM_ERR("tsilo: failed to parse R-URI.\n");
438                 return -1;
439         }
440
441         /* parse Contact header */
442         if ((!_msg->contact && parse_headers(_msg, HDR_CONTACT_F, 0) != 0)
443                         || !_msg->contact) {
444                 LM_WARN("tsilo: missing contact header or the value is empty/malformed.\n");
445                 return -1;
446         }
447         if (_msg->contact) {
448                 if (parse_contact(_msg->contact) < 0) {
449                         LM_WARN("tsilo: failed to parse Contact header.\n");
450                         return -1;
451                 }
452                 if (parse_uri(
453                                                 ((struct contact_body*)_msg->contact->parsed)->contacts->uri.s,
454                                                 ((struct contact_body*)_msg->contact->parsed)->contacts->uri.len,
455                                                 &curi) != 0 ) {
456                         if (ts_check_uri(&_msg->contact->body) < 0) {   /* one more attempt */
457                                 LM_WARN("tsilo: failed to parse Contact header.\n");
458                                 return -1;
459                         }
460                 }
461
462                 tmp_contact.len = ((struct contact_body*)_msg->contact->parsed)->contacts->uri.len;
463                 tmp_contact.s = (char*)pkg_malloc(tmp_contact.len+1);
464                 if (tmp_contact.s == NULL) {
465                         PKG_MEM_ERROR;
466                         return -1;
467                 }
468                 memcpy(tmp_contact.s, ((struct contact_body*)_msg->contact->parsed)->contacts->uri.s, tmp_contact.len);
469                 tmp_contact.s[tmp_contact.len] = '\0';
470
471                 if (pkg_str_dup(&contact, &tmp_contact) < 0) {
472                         if (pkg_str_dup(&contact, &_msg->contact->body) < 0) { /* one more attempt */
473                                 LM_ERR("tsilo: problems when calling ts_append_contact(), cannot copy Contact parameter.\n");
474                                 return -1;
475                         }
476                 }
477         }
478
479         /* contact must be of syntax: sip:<user>@<host>:<port> with no parameters list */
480         rc = ts_append(_msg, &ruri, &contact, _table);
481
482         /* free previously used memory */
483         pkg_free(ruri.s);
484         pkg_free(contact.s);
485         pkg_free(tmp_contact.s);
486
487         return rc;
488 }
489
490 /**
491  *
492  */
493 static int ki_ts_append_by_contact(sip_msg_t* _msg, str *_table, str *_ruri) {
494         str ruri = STR_NULL;
495         str contact = STR_NULL;
496         str tmp_contact = STR_NULL;
497         struct sip_uri curi;
498         int rc;
499
500         /* parse R-URI */
501         if (ts_check_uri(_ruri) < 0)
502                 return -1;
503         if (pkg_str_dup(&ruri, _ruri) < 0)
504                 return -1;
505
506         /* parse Contact header */
507         if ((!_msg->contact && parse_headers(_msg, HDR_CONTACT_F, 0) != 0) || !_msg->contact)
508                 return -1;
509
510         if (_msg->contact) {
511                 if (parse_contact(_msg->contact) < 0)
512                         return -1;
513                 if (parse_uri(
514                                                 ((struct contact_body*)_msg->contact->parsed)->contacts->uri.s,
515                                                 ((struct contact_body*)_msg->contact->parsed)->contacts->uri.len,
516                                                 &curi) != 0 ) {
517                         if (ts_check_uri(&_msg->contact->body) < 0) /* one more attempt */
518                                 return -1;
519                 }
520
521                 tmp_contact.len = ((struct contact_body*)_msg->contact->parsed)->contacts->uri.len;
522                 tmp_contact.s = (char*)pkg_malloc(tmp_contact.len+1);
523                 if (tmp_contact.s == NULL) {
524                         PKG_MEM_ERROR;
525                         return -1;
526                 }
527                 memcpy(tmp_contact.s, ((struct contact_body*)_msg->contact->parsed)->contacts->uri.s, tmp_contact.len);
528                 tmp_contact.s[tmp_contact.len] = '\0';
529
530                 if (pkg_str_dup(&contact, &tmp_contact) < 0) {
531                         if (pkg_str_dup(&contact, &_msg->contact->body) < 0) /* one more attempt */
532                                 return -1;
533                 }
534         }
535
536         /* contact must be of syntax: sip:<user>@<host>:<port> with no parameters list */
537         rc = ts_append(_msg, &ruri, &contact, _table->s);
538
539         pkg_free(ruri.s);
540         pkg_free(contact.s);
541         pkg_free(tmp_contact.s);
542
543         return rc;
544 }
545
546 /**
547  *
548  */
549 static int w_ts_append_by_contact3(struct sip_msg* _msg, char *_table, char *_ruri, char *_contact) {
550         str ruri = STR_NULL;
551         str ruri_fixed = STR_NULL;
552
553         str contact = STR_NULL;
554         str contact_fixed = STR_NULL;
555
556         int rc;
557
558         /* parse R-URI */
559         if (fixup_get_svalue(_msg, (gparam_t*)_ruri, &ruri_fixed)!=0) {
560                 LM_ERR("failed to convert r-uri parameter\n");
561                 return -1;
562         }
563
564         if (_ruri==NULL || strlen(_ruri) <= 0 || ruri_fixed.len <= 0) {
565                 LM_ERR("tsilo: invalid ruri parameter.\n");
566                 return -1;
567         }
568
569         if (pkg_str_dup(&ruri, &ruri_fixed) < 0) {
570                 LM_ERR("failed to copy r-uri parameter\n");
571                 return -1;
572         }
573
574         if (ts_check_uri(&ruri) < 0) {
575                 LM_ERR("tsilo: failed to parse R-URI.\n");
576                 return -1;
577         }
578
579         /* parse Contact header */
580         if (fixup_get_svalue(_msg, (gparam_t*)_contact, &contact_fixed)!=0) {
581                 LM_ERR("failed to convert contact parameter\n");
582                 return -1;
583         }
584
585         if (_contact==NULL || strlen(_contact) <= 0 || contact_fixed.len <= 0) {
586                 LM_ERR("tsilo: invalid contact parameter.\n");
587                 return -1;
588         }
589
590         if (pkg_str_dup(&contact, &contact_fixed) < 0) {
591                 LM_ERR("failed to copy r-uri parameter\n");
592                 return -1;
593         }
594
595         if (ts_check_uri(&contact) < 0) {
596                 LM_ERR("tsilo: failed to parse Contact parameter.\n");
597                 return -1;
598         }
599
600         /* contact must be of syntax: sip:<user>@<host>:<port> with no parameters list */
601         rc = ts_append(_msg, &ruri, &contact, _table);
602
603         pkg_free(ruri.s);
604         pkg_free(contact.s);
605
606         return rc;
607 }
608
609 /**
610  *
611  */
612 static int ki_ts_append_by_contact_uri(sip_msg_t* _msg, str *_table, str *_ruri, str *_contact) {
613         str ruri = STR_NULL;
614         str contact = STR_NULL;
615
616         int rc;
617
618         /* parse R-URI */
619         if(ts_check_uri(_ruri) < 0)
620                 return -1;
621         if (pkg_str_dup(&ruri, _ruri) < 0)
622                 return -1;
623
624         /* parse Contact header */
625         if (ts_check_uri(_contact) < 0)
626                 return -1;
627         if (pkg_str_dup(&contact, _contact) < 0)
628                 return -1;
629
630         /* contact must be of syntax: sip:<user>@<host>:<port> with no parameters list */
631         rc = ts_append(_msg, &ruri, &contact, _table->s);
632
633         pkg_free(ruri.s);
634         pkg_free(contact.s);
635
636         return rc;
637 }
638
639 /**
640  *
641  */
642 static int w_ts_store(struct sip_msg* msg, char *p1, char *p2)
643 {
644         return ts_store(msg, 0);
645 }
646
647 /**
648  *
649  */
650 static int ki_ts_store(sip_msg_t* msg)
651 {
652         return ts_store(msg, 0);
653 }
654
655 /**
656  *
657  */
658 static int w_ts_store1(struct sip_msg* msg, char *_ruri, char *p2)
659 {
660         str suri;
661
662         if(fixup_get_svalue(msg, (gparam_t*)_ruri, &suri)!=0) {
663                 LM_ERR("failed to conert r-uri parameter\n");
664                 return -1;
665         }
666         return ts_store(msg, &suri);
667 }
668
669 /**
670  *
671  */
672 /* clang-format off */
673 static sr_kemi_t sr_kemi_tsilo_exports[] = {
674         { str_init("tsilo"), str_init("ts_store"),
675                 SR_KEMIP_INT, ki_ts_store,
676                 { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
677                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
678         },
679         { str_init("tsilo"), str_init("ts_store_uri"),
680                 SR_KEMIP_INT, ts_store,
681                 { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
682                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
683         },
684         { str_init("tsilo"), str_init("ts_append"),
685                 SR_KEMIP_INT, ki_ts_append,
686                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
687                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
688         },
689         { str_init("tsilo"), str_init("ts_append_to"),
690                 SR_KEMIP_INT, ki_ts_append_to,
691                 { SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_STR,
692                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
693         },
694         { str_init("tsilo"), str_init("ts_append_to_uri"),
695                 SR_KEMIP_INT, ki_ts_append_to_uri,
696                 { SR_KEMIP_INT, SR_KEMIP_INT, SR_KEMIP_STR,
697                         SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE }
698         },
699         { str_init("tsilo"), str_init("ts_append_by_contact"),
700                 SR_KEMIP_INT, ki_ts_append_by_contact,
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_by_contact_uri"),
705                 SR_KEMIP_INT, ki_ts_append_by_contact_uri,
706                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR,
707                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
708         },
709
710         { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
711 };
712 /* clang-format on */
713
714 int mod_register(char *path, int *dlflags, void *p1, void *p2)
715 {
716         sr_kemi_modules_add(sr_kemi_tsilo_exports);
717         return 0;
718 }