Browse code

new module: connector to Portability number DataBase daemon

- new module, connector to portable number database daemon, pdbd
- the daemon implements a custom UDP protocol that allows the query of a custom
number portability database optimized for speed and memory efficieny based
on the 'cr' trie datastructure
- the daemon allows the storage of a number to carrier mapping for e.g. whole
germany in a few hundred megabytes of RAM, and their efficient retrival
- the connector module supports aggressive time outs to keep the post dial
delay low, it will return then a failure and default routing is possible
- TODO: port daemon and optimizing compiler as well, extend README
- this daemon (in its OpenSER version) has been used in production, but some
bugs could be slipped in the porting process
- credits for implementation: Hardy Kahl @ 1&1 Internet AG

Henning Westerholt authored on 08/09/2009 17:14:01
Showing 5 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,14 @@
1
+# WARNING: do not run this directly, it should be run by the master Makefile
2
+
3
+include ../../Makefile.defs
4
+
5
+auto_gen=
6
+NAME=pdb.so
7
+
8
+LIBS=
9
+DEFS+=-DOPENSER_MOD_INTERFACE
10
+
11
+SERLIBPATH=../../lib
12
+SER_LIBS+=$(SERLIBPATH)/kmi/kmi $(SERLIBPATH)/kcore/kcore
13
+
14
+include ../../Makefile.modules
0 15
new file mode 100644
... ...
@@ -0,0 +1,154 @@
1
+pdb Module
2
+
3
+Hardy Kahl
4
+
5
+   1&1 Internet AG
6
+
7
+Edited by
8
+
9
+Henning Westerholt
10
+
11
+   1&1 Internet AG
12
+   <henning.westerholt@1und1.de>
13
+
14
+   Copyright 2009 1&1 Internet AG
15
+   Revision History
16
+   Revision $Revision: 4863 $ $Date: 2008-09-05 13:11:33 +0200
17
+                              (Fri, 05 Sep 2008) $
18
+     __________________________________________________________
19
+
20
+   Table of Contents
21
+
22
+   1. Admin Guide
23
+
24
+        1.1. Overview
25
+        1.2. Dependencies
26
+
27
+              1.2.1. Kamailio Modules
28
+              1.2.2. External Libraries or Applications
29
+
30
+        1.3. Exported Parameters
31
+
32
+              1.3.1. timeout (integer)
33
+              1.3.2. server (string)
34
+
35
+        1.4. Exported Functions
36
+
37
+              1.4.1. pdb_query (string query, string dstavp)
38
+
39
+        1.5. MI Commands
40
+
41
+              1.5.1. pdb_status
42
+              1.5.2. pdb_activate
43
+              1.5.3. pdb_deactivate
44
+
45
+   List of Examples
46
+
47
+   1.1. Set timeout parameter
48
+   1.2. Set server parameter
49
+   1.3. pdb_query usage
50
+   1.4. pdb_status usage
51
+   1.5. pdb_activate usage
52
+   1.6. pdb_deactivate usage
53
+
54
+Chapter 1. Admin Guide
55
+
56
+1.1. Overview
57
+
58
+   The pdb module allows Kamailio to send queries to a list of
59
+   servers and store the answer in an AVP. The idea is to ask all
60
+   servers in parallel and use the first answer, that comes back.
61
+   A timeout for the query can be defined in milliseconds. The
62
+   queying can be activated and deactivated using FIFO commands.
63
+
64
+1.2. Dependencies
65
+
66
+1.2.1. Kamailio Modules
67
+
68
+   The module depends on the following modules (in the other words
69
+   the listed modules must be loaded before this module):
70
+     * none
71
+
72
+1.2.2. External Libraries or Applications
73
+
74
+   The following libraries or applications must be installed
75
+   before running Kamailio with this module loaded:
76
+     * none
77
+
78
+1.3. Exported Parameters
79
+
80
+1.3.1. timeout (integer)
81
+
82
+   This is the timeout in milliseconds for the pdb_query function.
83
+
84
+   Default value is "50".
85
+
86
+   Example 1.1. Set timeout parameter
87
+...
88
+modparam("pdb", "timeout", 10)
89
+...
90
+
91
+1.3.2. server (string)
92
+
93
+   This is the list of servers to be used by the pdb_query
94
+   function. Queries will be sent in parallel to all servers
95
+   configured in this list. This parameter is mandatory.
96
+
97
+   Example 1.2. Set server parameter
98
+...
99
+modparam("pdb", "server", "localhost:10001,host.name:10001,192.168.1.
100
+7:10002")
101
+...
102
+
103
+1.4. Exported Functions
104
+
105
+1.4.1.  pdb_query (string query, string dstavp)
106
+
107
+   Sends the query string to all configured servers and stores the
108
+   answer in dstavp. If it takes more than the configured timeout,
109
+   false is returned. Pseudo-variables or AVPs can be used for the
110
+   query string. The answer must consist of the null terminated
111
+   query string followed by a two byte integer value in network
112
+   byte order. The integer value will be stored in the given AVP.
113
+
114
+   Example 1.3. pdb_query usage
115
+...
116
+# query external service for routing information
117
+if (!pdb_query("$rU", "$avp(i:82)"))
118
+  $avp(i:82) = 0; # default routing
119
+}
120
+cr_route("$avp(i:82)", "$rd", "$rU", "$rU", "call_id");
121
+...
122
+
123
+1.5. MI Commands
124
+
125
+1.5.1.  pdb_status
126
+
127
+   Prints the status of the module. This can either be "active" or
128
+   "deactivated".
129
+
130
+   Example 1.4. pdb_status usage
131
+...
132
+kamctl fifo pdb_status
133
+...
134
+
135
+1.5.2.  pdb_activate
136
+
137
+   Activates the module. This is the default after loading the
138
+   module.
139
+
140
+   Example 1.5. pdb_activate usage
141
+...
142
+kamctl fifo pdb_activate
143
+...
144
+
145
+1.5.3.  pdb_deactivate
146
+
147
+   Deactivates the module. No more queries are performed until it
148
+   is activated again. As long as the module is deactivated, the
149
+   pdb_query function will return -1.
150
+
151
+   Example 1.6. pdb_deactivate usage
152
+...
153
+kamctl fifo pdb_deactivate
154
+...
0 155
new file mode 100644
... ...
@@ -0,0 +1,41 @@
1
+<?xml version="1.0" encoding='ISO-8859-1'?>
2
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
3
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
4
+
5
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
6
+%docentities;
7
+
8
+]>
9
+
10
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
11
+	<bookinfo>
12
+	<title>pdb Module</title>
13
+	<productname class="trade">&kamailioname;</productname>        
14
+	<authorgroup>
15
+		<author>
16
+		<firstname>Hardy</firstname>
17
+		<surname>Kahl</surname>
18
+		<affiliation><orgname>1&amp;1 Internet AG</orgname></affiliation>
19
+		</author>
20
+		<editor>
21
+		<firstname>Henning</firstname>
22
+		<surname>Westerholt</surname>
23
+		<affiliation><orgname>1&amp;1 Internet AG</orgname></affiliation>
24
+		<email>henning.westerholt@1und1.de</email>
25
+		</editor>
26
+	</authorgroup>
27
+	<copyright>
28
+		<year>2009</year>
29
+		<holder>1&amp;1 Internet AG</holder>
30
+	</copyright>
31
+	<revhistory>
32
+		<revision>
33
+		<revnumber>$Revision: 4863 $</revnumber>
34
+		<date>$Date: 2008-09-05 13:11:33 +0200 (Fri, 05 Sep 2008) $</date>
35
+		</revision>
36
+	</revhistory>
37
+	</bookinfo>
38
+	<toc></toc>
39
+
40
+	<xi:include href="pdb_admin.xml"/>
41
+</book>
0 42
new file mode 100644
... ...
@@ -0,0 +1,166 @@
1
+<chapter>
2
+	<title>&adminguide;</title>
3
+	
4
+	<section>
5
+	<title>Overview</title>
6
+	<para>
7
+	The pdb module allows Kamailio to send queries to a list of servers
8
+  and store the answer in an AVP. The idea is to ask all servers in
9
+  parallel and use the first answer, that comes back. A timeout for the
10
+	query can be defined in milliseconds. The queying can be activated and
11
+  deactivated using FIFO commands.
12
+	</para>
13
+	</section>
14
+
15
+	<section>
16
+		<title>Dependencies</title>
17
+		<section>
18
+			<title>&kamailio; Modules</title>
19
+			<para>
20
+			The module depends on the following modules (in the other words 
21
+			the listed modules must be loaded before this module):
22
+			</para>
23
+			<itemizedlist>
24
+			<listitem>
25
+				<para><emphasis>none</emphasis></para>
26
+			</listitem>
27
+			</itemizedlist>
28
+		</section>
29
+		<section>
30
+			<title>External Libraries or Applications</title>
31
+			<para>
32
+			The following libraries or applications must be installed 
33
+			before running &kamailio; with this module loaded:
34
+			</para>
35
+			<itemizedlist>
36
+				<listitem>
37
+				<para><emphasis>none</emphasis></para>
38
+				</listitem>
39
+			</itemizedlist>
40
+		</section>
41
+	</section>
42
+
43
+
44
+	<section>
45
+	<title>Exported Parameters</title>
46
+    <section>
47
+	    <title><varname>timeout</varname> (integer)</title>
48
+	    <para>
49
+			This is the timeout in milliseconds for the pdb_query function.
50
+	    </para>
51
+	    <para>
52
+		    <emphasis>
53
+			    Default value is <quote>50</quote>.
54
+		    </emphasis>
55
+	    </para>
56
+	    <example>
57
+		    <title>Set <varname>timeout</varname> parameter</title>
58
+		    <programlisting format="linespecific">
59
+...
60
+modparam("pdb", "timeout", 10)
61
+...
62
+		    </programlisting>
63
+	    </example>
64
+    </section>
65
+    <section>
66
+	    <title><varname>server</varname> (string)</title>
67
+	    <para>
68
+			This is the list of servers to be used by the pdb_query function.
69
+      Queries will be sent in parallel to all servers configured in this list.
70
+			This parameter is mandatory.
71
+	    </para>
72
+	    <example>
73
+		    <title>Set <varname>server</varname> parameter</title>
74
+		    <programlisting format="linespecific">
75
+...
76
+modparam("pdb", "server", "localhost:10001,host.name:10001,192.168.1.7:10002")
77
+...
78
+		    </programlisting>
79
+	    </example>
80
+    </section>
81
+	</section>
82
+	<section>
83
+		<title>Exported Functions</title>
84
+		<section>
85
+	    <title>
86
+				<function moreinfo="none">pdb_query (string query, string dstavp)</function>
87
+	    </title>
88
+	    <para>
89
+				Sends the query string to all configured servers and stores the answer in
90
+				dstavp. If it takes more than the configured timeout, false is returned.
91
+				
92
+				Pseudo-variables or AVPs can be used for the query string.
93
+				
94
+				The answer must consist of the null terminated query string followed by
95
+				a two byte integer value in network byte order. The integer value will
96
+				be stored in the given AVP.
97
+	    </para>
98
+			<example>
99
+				<title><function>pdb_query</function> usage</title>
100
+				<programlisting format="linespecific">
101
+...
102
+# query external service for routing information
103
+if (!pdb_query("$rU", "$avp(i:82)"))
104
+  $avp(i:82) = 0; # default routing
105
+}
106
+cr_route("$avp(i:82)", "$rd", "$rU", "$rU", "call_id");
107
+...
108
+				</programlisting>
109
+			</example>
110
+		</section>
111
+	</section>
112
+	<section>
113
+		<title><acronym>MI</acronym> Commands</title>
114
+		<section>
115
+	    <title>
116
+				<function moreinfo="none">pdb_status</function>
117
+	    </title>
118
+	    <para>
119
+				Prints the status of the module.
120
+				This can either be "active" or "deactivated".
121
+	    </para>
122
+			<example>
123
+				<title><function>pdb_status</function> usage</title>
124
+				<programlisting format="linespecific">
125
+...
126
+kamctl fifo pdb_status
127
+...
128
+				</programlisting>
129
+	    </example>
130
+		</section>
131
+		<section>
132
+	    <title>
133
+				<function moreinfo="none">pdb_activate</function>
134
+	    </title>
135
+	    <para>
136
+				Activates the module. This is the default after loading the module.
137
+	    </para>
138
+			<example>
139
+				<title><function>pdb_activate</function> usage</title>
140
+				<programlisting format="linespecific">
141
+...
142
+kamctl fifo pdb_activate
143
+...
144
+				</programlisting>
145
+	    </example>
146
+		</section>
147
+		<section>
148
+	    <title>
149
+				<function moreinfo="none">pdb_deactivate</function>
150
+	    </title>
151
+	    <para>
152
+				Deactivates the module. No more queries are performed until it is
153
+				activated again. As long as the module is deactivated, the
154
+				pdb_query function will return -1.
155
+	    </para>
156
+			<example>
157
+				<title><function>pdb_deactivate</function> usage</title>
158
+				<programlisting format="linespecific">
159
+...
160
+kamctl fifo pdb_deactivate
161
+...
162
+				</programlisting>
163
+	    </example>
164
+		</section>
165
+	</section>
166
+</chapter>
0 167
new file mode 100644
... ...
@@ -0,0 +1,690 @@
1
+/*
2
+ * $Id: carrierroute.c 4712 2008-08-22 17:05:16Z henningw $
3
+ *
4
+ * Copyright (C) 2009 1&1 Internet AG
5
+ *
6
+ * This file is part of sip-router, a free SIP server.
7
+ *
8
+ * sip-router 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
+ * sip-router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
+ */
22
+
23
+/*!
24
+ * @file pdb.c
25
+ * @brief Contains the functions exported by the module.
26
+ */
27
+
28
+
29
+#include "../../sr_module.h"
30
+#include "../../mem/mem.h"
31
+#include "../../mem/shm_mem.h"
32
+#include <sys/time.h>
33
+#include <poll.h>
34
+#include <stdlib.h>
35
+#include <unistd.h>
36
+#include <ctype.h>
37
+#include <sys/socket.h>
38
+#include <netinet/in.h>
39
+#include <arpa/inet.h>
40
+#include <errno.h>
41
+
42
+MODULE_VERSION
43
+
44
+
45
+#define NETBUFSIZE 200
46
+
47
+
48
+static char* modp_server = NULL;  /*!< format: <host>:<port>,... */
49
+static int timeout = 50;  /*!< timeout for queries in milliseconds */
50
+static int timeoutlogs = -10;  /*!< for aggregating timeout logs */
51
+static int *active = NULL;
52
+
53
+
54
+/*!
55
+ * Generic parameter that holds a string, an int or an pseudo-variable
56
+ * @todo replace this with gparam_t
57
+ */
58
+struct multiparam_t {
59
+	enum {
60
+		MP_INT,
61
+		MP_STR,
62
+		MP_AVP,
63
+		MP_PVE,
64
+	} type;
65
+	union {
66
+		int n;
67
+		str s;
68
+		struct {
69
+			unsigned short flags;
70
+			int_str name;
71
+		} a;
72
+		pv_elem_t *p;
73
+	} u;
74
+};
75
+
76
+
77
+/* ---- exported commands: */
78
+static int pdb_query(struct sip_msg *_msg, struct multiparam_t *_number, struct multiparam_t *_dstavp);
79
+
80
+/* ---- fixup functions: */
81
+static int pdb_query_fixup(void **arg, int arg_no);
82
+
83
+/* ---- module init functions: */
84
+static int mod_init(void);
85
+static int child_init(int rank);
86
+static int mi_child_init(void);
87
+static void mod_destroy();
88
+
89
+/* --- fifo functions */
90
+struct mi_root * mi_pdb_status(struct mi_root* cmd, void* param);  /* usage: kamctl fifo pdb_status */
91
+struct mi_root * mi_pdb_activate(struct mi_root* cmd, void* param);  /* usage: kamctl fifo pdb_activate */
92
+struct mi_root * mi_pdb_deactivate(struct mi_root* cmd, void* param);  /* usage: kamctl fifo pdb_deactivate */
93
+
94
+
95
+static cmd_export_t cmds[]={
96
+	{ "pdb_query", (cmd_function)pdb_query, 2, pdb_query_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
97
+	{0,0,0,0,0,0}
98
+};
99
+
100
+
101
+static param_export_t params[] = {
102
+	{"server",      STR_PARAM, &modp_server },
103
+	{"timeout",     INT_PARAM, &timeout },
104
+	{0, 0, 0 }
105
+};
106
+
107
+
108
+/* Exported MI functions */
109
+static mi_export_t mi_cmds[] = {
110
+	{ "pdb_status", mi_pdb_status, MI_NO_INPUT_FLAG, 0, mi_child_init },
111
+	{ "pdb_activate", mi_pdb_activate, MI_NO_INPUT_FLAG, 0, mi_child_init },
112
+	{ "pdb_deactivate", mi_pdb_deactivate, MI_NO_INPUT_FLAG, 0, mi_child_init },
113
+	{ 0, 0, 0, 0, 0}
114
+};
115
+
116
+
117
+struct module_exports exports = {
118
+	"pdb",
119
+	DEFAULT_DLFLAGS, /* dlopen flags */
120
+	cmds,       /* Exported functions */
121
+	params,     /* Export parameters */
122
+	0,          /* exported statistics */
123
+	mi_cmds,    /* exported MI functions */
124
+	0,          /* exported pseudo-variables */
125
+	0,          /* extra processes */
126
+	mod_init,   /* Module initialization function */
127
+	0,          /* Response function */
128
+	mod_destroy,/* Destroy function */
129
+	child_init  /* Child initialization function */
130
+};
131
+
132
+
133
+struct server_item_t {
134
+	struct server_item_t *next;
135
+	char *host;
136
+	unsigned short int port;
137
+	struct sockaddr_in dstaddr;
138
+	socklen_t dstaddrlen;
139
+	int sock;
140
+};
141
+
142
+
143
+struct server_list_t {
144
+	struct server_item_t *head;
145
+	int nserver;
146
+	struct pollfd *fds;
147
+};
148
+
149
+
150
+/*! global server list */
151
+static struct server_list_t *server_list;
152
+
153
+
154
+/*!
155
+ * \return 1 if query for the number succeded and the avp with the corresponding carrier id was set,
156
+ * -1 otherwise
157
+ */
158
+static int pdb_query(struct sip_msg *_msg, struct multiparam_t *_number, struct multiparam_t *_dstavp)
159
+{
160
+	struct timeval tstart, tnow;
161
+	struct server_item_t *server;
162
+	short int carrierid;
163
+	char buf[NETBUFSIZE+1+sizeof(carrierid)];
164
+	size_t reqlen;
165
+	int_str avp_val;
166
+	struct usr_avp *avp;
167
+	int i, ret, nflush;
168
+	long int td;
169
+	str number = { .len = 0, .s = NULL};
170
+
171
+	if ((active == NULL) || (*active == 0)) return -1;
172
+
173
+	switch (_number->type) {
174
+	case MP_STR:
175
+		number = _number->u.s;
176
+		break;
177
+	case MP_AVP:
178
+		avp = search_first_avp(_number->u.a.flags, _number->u.a.name, &avp_val, 0);
179
+		if (!avp) {
180
+			LM_ERR("cannot find AVP '%.*s'\n", _number->u.a.name.s.len, _number->u.a.name.s.s);
181
+			return -1;
182
+		}
183
+		if ((avp->flags&AVP_VAL_STR)==0) {
184
+			LM_ERR("cannot process integer value in AVP '%.*s'\n", _number->u.a.name.s.len, _number->u.a.name.s.s);
185
+			return -1;
186
+		}
187
+		else number = avp_val.s;
188
+		break;
189
+	case MP_PVE:
190
+		if (pv_printf_s(_msg, _number->u.p, &number)<0) {
191
+			LM_ERR("cannot print the number\n");
192
+			return -1;
193
+		}
194
+		break;
195
+	default:
196
+		LM_ERR("invalid number type\n");
197
+		return -1;
198
+	}
199
+
200
+	LM_DBG("querying '%.*s'...\n", number.len, number.s);
201
+	if (server_list == NULL) return -1;
202
+	if (server_list->fds == NULL) return -1;
203
+
204
+	if (gettimeofday(&tstart, NULL) != 0) {
205
+		LM_ERR("gettimeofday() failed with errno=%d (%s)\n", errno, strerror(errno));
206
+		return -1;
207
+	}
208
+
209
+	/* clear recv buffer */
210
+	server = server_list->head;
211
+	while (server) {
212
+		nflush = 0;
213
+		while (recv(server->sock, buf, NETBUFSIZE, MSG_DONTWAIT) > 0) {
214
+			nflush++;
215
+			if (gettimeofday(&tnow, NULL) != 0) {
216
+				LM_ERR("gettimeofday() failed with errno=%d (%s)\n", errno, strerror(errno));
217
+				return -1;
218
+			}
219
+			td=(tnow.tv_usec-tstart.tv_usec+(tnow.tv_sec-tstart.tv_sec)*1000000) / 1000;
220
+			if (td > timeout) {
221
+				LM_WARN("exceeded timeout while flushing recv buffer.\n");
222
+				return -1;
223
+			}
224
+		}
225
+		LM_DBG("flushed %d packets for '%s:%d'\n", nflush, server->host, server->port);
226
+		server = server ->next;
227
+	}
228
+
229
+	/* prepare request */
230
+	reqlen = number.len + 1; /* include null termination */
231
+	if (reqlen > NETBUFSIZE) {
232
+		LM_ERR("number too long '%.*s'.\n", number.len, number.s);
233
+		return -1;
234
+	}
235
+	strncpy(buf, number.s, number.len);
236
+	buf[number.len] = '\0';
237
+
238
+	/* send request to all servers */
239
+	server = server_list->head;
240
+	while (server) {
241
+		LM_DBG("sending request to '%s:%d'\n", server->host, server->port);
242
+		ret=sendto(server->sock, buf, reqlen, MSG_DONTWAIT, (struct sockaddr *)&(server->dstaddr), server->dstaddrlen);
243
+		if (ret < 0) {
244
+			LM_ERR("sendto() failed with errno=%d (%s)\n", errno, strerror(errno));
245
+		}
246
+		server = server->next;
247
+	}
248
+		
249
+	/* wait for response */
250
+	for (;;) {
251
+		if (gettimeofday(&tnow, NULL) != 0) {
252
+			LM_ERR("gettimeofday() failed with errno=%d (%s)\n", errno, strerror(errno));
253
+			return -1;
254
+		}
255
+		td=(tnow.tv_usec-tstart.tv_usec+(tnow.tv_sec-tstart.tv_sec)*1000000) / 1000;
256
+		if (td > timeout) {
257
+			timeoutlogs++;
258
+			if (timeoutlogs<0) {
259
+				LM_WARN("exceeded timeout while waiting for response.\n");
260
+			}
261
+			else if (timeoutlogs>1000) {
262
+				LM_WARN("exceeded timeout %d times while waiting for response.\n", timeoutlogs);
263
+				timeoutlogs=0;
264
+			}
265
+			return -1;
266
+		}
267
+		
268
+		ret=poll(server_list->fds, server_list->nserver, timeout-td);
269
+		for (i=0; i<server_list->nserver; i++) {
270
+			if (server_list->fds[i].revents & POLLIN) {
271
+				if (recv(server_list->fds[i].fd, buf, NETBUFSIZE, MSG_DONTWAIT) > 0) { /* do not block - just in case select/poll was wrong */
272
+					buf[NETBUFSIZE] = '\0';
273
+					if (strncmp(buf, number.s, number.len) == 0) {
274
+						carrierid=ntohs(*((short int *)&(buf[reqlen]))); /* convert to host byte order */
275
+						goto found;
276
+					}
277
+				}
278
+			}
279
+			server_list->fds[i].revents = 0;
280
+		}
281
+	}
282
+
283
+	found:
284
+	if (timeoutlogs>0) {
285
+		LM_WARN("exceeded timeout while waiting for response (buffered %d lines).\n", timeoutlogs);
286
+		timeoutlogs=-10;
287
+	}
288
+	if (gettimeofday(&tnow, NULL) == 0) {
289
+		LM_INFO("got an answer in %f ms\n", ((double)(tnow.tv_usec-tstart.tv_usec+(tnow.tv_sec-tstart.tv_sec)*1000000))/1000);
290
+	}
291
+	avp_val.n=carrierid;
292
+	/* set avp ! */
293
+	if (add_avp(_dstavp->u.a.flags, _dstavp->u.a.name, avp_val)<0) {
294
+		LM_ERR("add AVP failed\n");
295
+		return -1;
296
+	}
297
+
298
+	return 1;
299
+}
300
+
301
+
302
+/*!
303
+ * fixes the module functions' parameters if it is a phone number.
304
+ * supports string, pseudo-variables and AVPs.
305
+ *
306
+ * @param param the parameter
307
+ * @return 0 on success, -1 on failure
308
+ */
309
+static int mp_fixup(void ** param) {
310
+	pv_spec_t avp_spec;
311
+	struct multiparam_t *mp;
312
+	str s;
313
+
314
+	mp = (struct multiparam_t *)pkg_malloc(sizeof(struct multiparam_t));
315
+	if (mp == NULL) {
316
+		PKG_MEM_ERROR;
317
+		return -1;
318
+	}
319
+	memset(mp, 0, sizeof(struct multiparam_t));
320
+	
321
+	s.s = (char *)(*param);
322
+	s.len = strlen(s.s);
323
+
324
+	if (s.s[0]!='$') {
325
+		/* This is string */
326
+		mp->type=MP_STR;
327
+		mp->u.s=s;
328
+	}
329
+	else {
330
+		/* This is a pseudo-variable */
331
+		if (pv_parse_spec(&s, &avp_spec)==0) {
332
+			LM_ERR("pv_parse_spec failed for '%s'\n", (char *)(*param));
333
+			pkg_free(mp);
334
+			return -1;
335
+		}
336
+		if (avp_spec.type==PVT_AVP) {
337
+			/* This is an AVP - could be an id or name */
338
+			mp->type=MP_AVP;
339
+			if(pv_get_avp_name(0, &(avp_spec.pvp), &(mp->u.a.name), &(mp->u.a.flags))!=0) {
340
+				LM_ERR("Invalid AVP definition <%s>\n", (char *)(*param));
341
+				pkg_free(mp);
342
+				return -1;
343
+			}
344
+		} else {
345
+			mp->type=MP_PVE;
346
+			if(pv_parse_format(&s, &(mp->u.p))<0) {
347
+				LM_ERR("pv_parse_format failed for '%s'\n", (char *)(*param));
348
+				pkg_free(mp);
349
+				return -1;
350
+			}
351
+		}
352
+	}
353
+	*param = (void*)mp;
354
+
355
+	return 0;
356
+}
357
+
358
+
359
+/*!
360
+ * fixes the module functions' parameters in case of AVP names.
361
+ *
362
+ * @param param the parameter
363
+ * @return 0 on success, -1 on failure
364
+ */
365
+static int avp_name_fixup(void ** param) {
366
+	pv_spec_t avp_spec;
367
+	struct multiparam_t *mp;
368
+	str s;
369
+
370
+	s.s = (char *)(*param);
371
+	s.len = strlen(s.s);
372
+	if (s.len <= 0) return -1;
373
+	if (pv_parse_spec(&s, &avp_spec)==0 || avp_spec.type!=PVT_AVP) {
374
+		LM_ERR("Malformed or non AVP definition <%s>\n", (char *)(*param));
375
+		return -1;
376
+	}
377
+	
378
+	mp = (struct multiparam_t *)pkg_malloc(sizeof(struct multiparam_t));
379
+	if (mp == NULL) {
380
+		PKG_MEM_ERROR;
381
+		return -1;
382
+	}
383
+	memset(mp, 0, sizeof(struct multiparam_t));
384
+	
385
+	mp->type=MP_AVP;
386
+	if(pv_get_avp_name(0, &(avp_spec.pvp), &(mp->u.a.name), &(mp->u.a.flags))!=0) {
387
+		LM_ERR("Invalid AVP definition <%s>\n", (char *)(*param));
388
+		pkg_free(mp);
389
+		return -1;
390
+	}
391
+
392
+	*param = (void*)mp;
393
+	
394
+	return 0;
395
+}
396
+
397
+
398
+static int pdb_query_fixup(void **arg, int arg_no)
399
+{
400
+	if (arg_no == 1) {
401
+		/* phone number */
402
+		if (mp_fixup(arg) < 0) {
403
+			LM_ERR("cannot fixup parameter %d\n", arg_no);
404
+			return -1;
405
+		}
406
+	}
407
+	else if (arg_no == 2) {
408
+		/* destination avp name */
409
+		if (avp_name_fixup(arg) < 0) {
410
+			LM_ERR("cannot fixup parameter %d\n", arg_no);
411
+			return -1;
412
+		}
413
+	}
414
+
415
+	return 0;
416
+}
417
+
418
+
419
+/*!
420
+ * Adds new server structure to server list.
421
+ * \return 0 on success -1 otherwise
422
+ */
423
+static int add_server(char *host, char *port)
424
+{
425
+	long int ret;
426
+	struct server_item_t *server;
427
+
428
+	LM_DBG("adding server '%s:%s'\n", host, port);
429
+	server= pkg_malloc(sizeof(struct server_item_t));
430
+	if (server == NULL) {
431
+		PKG_MEM_ERROR;
432
+		return -1;
433
+	}
434
+	memset(server, 0, sizeof(struct server_item_t));
435
+
436
+	server->next = server_list->head;
437
+	server_list->head = server;
438
+
439
+	server->host = pkg_malloc(strlen(host)+1);
440
+	if (server->host == NULL) {
441
+		PKG_MEM_ERROR;
442
+		return -1;
443
+	}
444
+	strcpy(server->host, host);
445
+
446
+	ret=strtol(port, NULL, 10);
447
+	if ((ret<0) || (ret>65535)) {
448
+		LM_ERR("invalid port '%s'\n", port);
449
+		return -1;
450
+	}
451
+	server->port=ret;
452
+
453
+	return 0;
454
+}
455
+
456
+
457
+/*!
458
+ * Prepares data structures for all configured servers.
459
+ *  \return 0 on success, -1 otherwise
460
+ */
461
+static int prepare_server(void)
462
+{
463
+	char *p, *dst, *end, *sep, *host, *port;
464
+
465
+	if (modp_server == NULL) {
466
+		LM_ERR("server parameter missing.\n");
467
+		return -1;
468
+	}
469
+
470
+	/* Remove white space from db_sources */
471
+	for (p = modp_server, dst = modp_server; *p != '\0'; ++p, ++dst) {
472
+		while (isspace(*p)) ++p;
473
+		*dst = *p;
474
+	}
475
+	*dst = '\0';
476
+
477
+	p = modp_server;
478
+	end = p + strlen(p);
479
+
480
+	while (p < end) {
481
+		sep = strchr(p, ':');
482
+		if (sep == NULL) {
483
+			LM_ERR("syntax error in sources parameter.\n");
484
+			return -1;
485
+		}
486
+		host = p;
487
+		*sep = '\0';
488
+		p = sep + 1;
489
+
490
+		sep = strchr(p, ',');
491
+		if (sep == NULL) sep = end;
492
+		port = p;
493
+		*sep = '\0';
494
+		p = sep + 1;
495
+
496
+		if (add_server(host, port) != 0)  return -1;
497
+	}
498
+
499
+	return 0;
500
+}
501
+
502
+
503
+static void destroy_server_list(void)
504
+{
505
+	if (server_list) {
506
+		while (server_list->head) {
507
+			struct server_item_t *server = server_list->head;
508
+			server_list->head = server->next;
509
+			if (server->host) pkg_free(server->host);
510
+			pkg_free(server);
511
+		}
512
+		pkg_free(server_list);
513
+		server_list = NULL;
514
+	}
515
+}
516
+
517
+
518
+/*!
519
+ * Allocates memory and builds a list of all servers defined in module parameter.
520
+ * \return 0 on success, -1 otherwise
521
+ */
522
+static int init_server_list(void)
523
+{
524
+	server_list = pkg_malloc(sizeof(struct server_list_t));
525
+	if (server_list == NULL) {
526
+		PKG_MEM_ERROR;
527
+		return -1;
528
+	}
529
+	memset(server_list, 0, sizeof(struct server_list_t));
530
+
531
+	if (prepare_server() != 0) {
532
+		destroy_server_list();
533
+		return -1;
534
+	}
535
+
536
+  return 0;
537
+}
538
+
539
+
540
+/*!
541
+ * Initializes sockets for all servers in server list.
542
+ * \return 0 on success, -1 otherwise
543
+ */
544
+static int init_server_socket(void)
545
+{
546
+	struct server_item_t *server;
547
+	struct hostent *hp;
548
+	int i;
549
+
550
+	if (server_list) {
551
+		server_list->nserver=0;
552
+		server = server_list->head;
553
+		while (server) {
554
+			LM_DBG("initializing socket for '%s:%d'\n", server->host, server->port);
555
+			server->sock = socket(AF_INET, SOCK_DGRAM, 0);
556
+			if (server->sock<0) {
557
+				LM_ERR("socket() failed with errno=%d (%s).\n", errno, strerror(errno));
558
+				return -1;
559
+			}
560
+
561
+			memset(&(server->dstaddr), 0, sizeof(server->dstaddr));
562
+			server->dstaddr.sin_family = AF_INET;
563
+			server->dstaddr.sin_port = htons(server->port);
564
+			hp = gethostbyname(server->host);
565
+			if (hp == NULL) {
566
+				LM_ERR("gethostbyname(%s) failed with h_errno=%d.\n", server->host, h_errno);
567
+				close(server->sock);
568
+				server->sock=0;
569
+				return -1;
570
+			}
571
+			memcpy(&(server->dstaddr.sin_addr.s_addr), hp->h_addr, hp->h_length);
572
+			server->dstaddrlen=sizeof(server->dstaddr);
573
+
574
+			server = server->next;
575
+			server_list->nserver++;
576
+		}
577
+
578
+		LM_DBG("got %d server in list\n", server_list->nserver);
579
+		server_list->fds = pkg_malloc(sizeof(struct pollfd)*server_list->nserver);
580
+		if (server_list->fds == NULL) {
581
+			PKG_MEM_ERROR;
582
+			return -1;
583
+		}
584
+		memset(server_list->fds, 0, sizeof(struct pollfd)*server_list->nserver);
585
+
586
+		i=0;
587
+		server = server_list->head;
588
+		while (server) {
589
+			server_list->fds[i].fd=server->sock;
590
+			server_list->fds[i].events=POLLIN;
591
+			server = server->next;
592
+			i++;
593
+		}
594
+	}
595
+	return 0;
596
+}
597
+
598
+
599
+/*!
600
+ * Destroys sockets for all servers in server list.
601
+ */
602
+static void destroy_server_socket(void)
603
+{
604
+	if (server_list) {
605
+		struct server_item_t *server = server_list->head;
606
+		while (server) {
607
+			if (server->sock>0) close(server->sock);
608
+			server = server->next;
609
+		}
610
+		if (server_list->fds) pkg_free(server_list->fds);
611
+	}
612
+}
613
+
614
+
615
+struct mi_root * mi_pdb_status(struct mi_root* cmd, void* param)
616
+{
617
+	struct mi_root * root = NULL;
618
+	struct mi_node * node = NULL;
619
+
620
+	if (active == NULL) return init_mi_tree(500, "NULL pointer", 12);
621
+
622
+	root = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
623
+	if (root == NULL) return NULL;
624
+
625
+	if (*active) node = addf_mi_node_child(&root->node, 0, 0, 0, "pdb is active");
626
+	else node = addf_mi_node_child(&root->node, 0, 0, 0, "pdb is deactivated");
627
+	if (node == NULL) {
628
+		free_mi_tree(root);
629
+		return NULL;
630
+	}
631
+
632
+	return root;
633
+}
634
+
635
+
636
+struct mi_root * mi_pdb_deactivate(struct mi_root* cmd, void* param)
637
+{
638
+	if (active == NULL) return init_mi_tree(500, "NULL pointer", 12);
639
+
640
+	*active=0;
641
+	return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
642
+}
643
+
644
+
645
+struct mi_root * mi_pdb_activate(struct mi_root* cmd, void* param)
646
+{
647
+	if (active == NULL) return init_mi_tree(500, "NULL pointer", 12);
648
+
649
+	*active=1;
650
+	return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
651
+}
652
+
653
+
654
+static int mod_init(void)
655
+{
656
+	active = shm_malloc(sizeof(*active));
657
+	if (active == NULL) {
658
+		SHM_MEM_ERROR;
659
+		return -1;
660
+	}
661
+	*active=1;
662
+
663
+	if (init_server_list() != 0) {
664
+		shm_free(active);
665
+		return -1;
666
+	}
667
+	return 0;
668
+}
669
+
670
+
671
+static int child_init (int rank)
672
+{
673
+	if (init_server_socket() != 0) return -1;
674
+	return 0;
675
+}
676
+
677
+
678
+static int mi_child_init(void)
679
+{
680
+	if (init_server_socket() != 0) return -1;
681
+	return 0;
682
+}
683
+
684
+
685
+static void mod_destroy(void)
686
+{
687
+	destroy_server_socket();
688
+	destroy_server_list();
689
+	if (active) shm_free(active);
690
+}