Merge commit 'origin/ser_modules'
[sip-router] / modules_s / iptrtpproxy / iptrtpproxy.c
1 /* $Id$
2  *
3  * Copyright (C) 2007 Tomas Mandys
4  *
5  * This file is part of ser, a free SIP server.
6  *
7  * ser 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  * For a license to use the ser software under conditions
13  * other than those described here, or to purchase support for this
14  * software, please contact iptel.org by e-mail at the following addresses:
15  *    info@iptel.org
16  *
17  * ser is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25  *
26  */
27
28 #include "../../sr_module.h"
29 #include "../../dprint.h"
30 #include "../../data_lump.h"
31 #include "../../data_lump_rpl.h"
32 #include "../../error.h"
33 #include "../../forward.h"
34 #include "../../mem/mem.h"
35 #include "../../parser/parse_content.h"
36 #include "../../parser/parse_uri.h"
37 #include "../../parser/parser_f.h"
38 #include "../../parser/parse_body.h"
39 #include "../../resolve.h"
40 #include "../../trim.h"
41 #include "../../ut.h"
42 #include "../../msg_translator.h"
43 #include "../../socket_info.h"
44 #include "../../select.h"
45 #include "../../select_buf.h"
46 #include "../../script_cb.h"
47 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <sys/time.h>
50 #include <netinet/in.h>
51 #include <arpa/inet.h>
52 #include <sys/uio.h>
53 #include <sys/un.h>
54 #include <ctype.h>
55 #include <errno.h>
56 #include <netdb.h>
57 #include <poll.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 #include <linux/netfilter/xt_RTPPROXY.h>
63 #include <arpa/inet.h>
64
65 MODULE_VERSION
66
67 #define MODULE_NAME "iptrtpproxy"
68
69 /* max.number of RTP streams per session */
70 #define MAX_MEDIA_NUMBER 20
71 #define MAX_SWITCHBOARD_NAME_LEN 20
72
73 struct switchboard_item {
74         str name;
75         int ringing_timeout;
76         struct xt_rtpproxy_sockopt_in_switchboard in_switchboard;
77         struct xt_rtpproxy_sockopt_in_alloc_session in_session;
78
79         struct switchboard_item* next;
80 };
81
82 static char* global_session_ids;
83 static str sdp_ip;
84 static struct xt_rtpproxy_handle handle = {.sockfd = 0};
85 static struct switchboard_item* switchboards = NULL;
86 static struct switchboard_item* found_switchboard;
87 static int found_direction;
88 static int switchboard_count = 0;
89
90
91 static struct switchboard_item* find_switchboard(str *name) {
92         struct switchboard_item* p;
93         for (p = switchboards; p; p=p->next) {
94                 if (name->len == p->name.len && strncasecmp(p->name.s, name->s, name->len)==0) break;
95         }
96         return p;
97 }
98
99 /** if succesfull allocated sessions available @rtpproxy.session_ids
100  */
101
102 static int rtpproxy_alloc_fixup(void** param, int param_no) {
103         switch (param_no) {
104                 case 1:
105                         return fixup_var_int_12(param, param_no);
106                 case 2:
107                         return fixup_var_str_12(param, param_no);
108                 default:
109                         return 0;
110         }
111 }
112
113 static int rtpproxy_update_fixup(void** param, int param_no) {
114         switch (param_no) {
115                 case 1:
116                         return rtpproxy_alloc_fixup(param, param_no);
117                 case 2:
118                         return fixup_var_str_12(param, param_no);
119                 default:
120                         return 0;
121         }
122 }
123
124 static int rtpproxy_delete_fixup(void** param, int param_no) {
125         return rtpproxy_update_fixup(param, 2);
126 }
127
128 static int rtpproxy_find_fixup(void** param, int param_no) {
129         return fixup_var_str_12(param, param_no);
130 }
131
132 struct sdp_session {
133         unsigned int media_count;
134         struct {
135                 int active;
136                 unsigned short port;
137                 unsigned int ip;
138                 str ip_s;
139                 str port_s;
140         } media[MAX_MEDIA_NUMBER];
141 };
142
143 struct ipt_session {
144         struct switchboard_item *switchboard;
145         unsigned int stream_count;
146         struct {
147                 int sess_id;
148                 int created;
149                 unsigned short proxy_port;
150         } streams[MAX_MEDIA_NUMBER];
151 };
152
153 static unsigned int s2ip4(str *s) {
154         struct in_addr res;
155         char c2;
156         c2 = s->s[s->len];
157         s->s[s->len] = '\0';
158         if (!inet_aton(s->s, &res)) {
159                 s->s[s->len] = c2;
160                 return 0;
161         }
162         s->s[s->len] = c2;
163         return res.s_addr;
164 }
165
166 static void ip42s(unsigned int ip, str *s) {
167         struct in_addr ip2 = { ip };
168         s->s = inet_ntoa(ip2);
169         s->len = strlen(s->s);
170 }
171
172 #define is_alpha(_c) (((_c) >= 'a' && (_c) <= 'z') || ((_c) >= 'A' && (_c) <= 'Z') || ((_c) >= '0' && (_c) <= '9') || ((_c) == '_') || ((_c) == '-'))
173
174 inline static int next_sdp_line(char** p, char* pend, char *ltype, str* line) {
175         char *cp;
176         while (*p < pend) {
177                 while (*p < pend && (**p == '\n' || **p == '\r')) (*p)++;
178                 for (cp = *p; cp < pend && *cp != '\n' && *cp != '\r'; cp++);
179
180                 if (cp-*p > 2 && (*p)[1] == '=') {
181                         *ltype = **p;
182                         line->s = (*p)+2;
183                         line->len = cp-line->s;
184                         *p = cp;
185                         return 0;
186                 }
187                 *p = cp;
188         }
189         return -1;
190 };
191
192 /* SDP RFC2327 */
193 static int parse_sdp_content(struct sip_msg* msg, struct sdp_session *sess) {
194         char *p, *pend, *cp, *cp2, *lend;
195         str line, cline_ip_s, body;
196         int sess_fl, i, cline_count;
197         char ltype, savec;
198         unsigned int cline_ip;
199
200         static str supported_media_types[] = {
201                 STR_STATIC_INIT("udp"),
202                 STR_STATIC_INIT("udptl"),
203                 STR_STATIC_INIT("rtp/avp"),
204                 STR_STATIC_INIT("rtp/savpf"),
205                 STR_NULL
206         };
207         memset(sess, 0, sizeof(*sess));
208         
209         
210         /* try to get the body part with application/sdp */
211         body.s = get_body_part(msg, TYPE_APPLICATION, SUBTYPE_SDP, &body.len);
212         if (!body.s) {
213                 ERR(MODULE_NAME": parse_sdp_content: failed to get the application/sdp body\n");
214                 return -1;
215         }
216         
217         #if 0
218         body.s = get_body(msg);
219         if (body.s==0) {
220                 ERR(MODULE_NAME": parse_sdp_content: failed to get the message body\n");
221                 return -1;
222         }
223         body.len = msg->len -(int)(body.s - msg->buf);
224         if (body.len==0) {
225                 ERR(MODULE_NAME": parse_sdp_content: message body has length zero\n");
226                 return -1;
227         }
228
229         /* no need for parse_headers(msg, EOH), get_body will parse everything */
230         if (!msg->content_type)
231         {
232                 WARN(MODULE_NAME": parse_sdp_content: Content-TYPE header absent!"
233                         "let's assume the content is text/plain\n");
234         }
235         else {
236                 trim_len(line.len, line.s, msg->content_type->body);
237                 if (line.len != sizeof("application/sdp")-1 || strncasecmp(line.s, "application/sdp", line.len) != 0) {
238                         ERR(MODULE_NAME": parse_sdp_content: bad content type '%.*s'\n", line.len, line.s);
239                         return -1;
240                 }
241         }
242         #endif
243         /*
244          * Parsing of SDP body.
245          * It can contain a few session descriptions (each starts with
246          * v-line), and each session may contain a few media descriptions
247          * (each starts with m-line).
248          * We have to change ports in m-lines, and also change IP addresses in
249          * c-lines which can be placed either in session header (fallback for
250          * all medias) or media description.
251          * Ports should be allocated for any media. IPs all should be changed
252          * to the same value (RTP proxy IP), so we can change all c-lines
253          * unconditionally.
254          * There are sendonly,recvonly modifiers which signalize one-way
255          * streaming, it probably won't work but it's handled the same way,
256          * RTCP commands are still bi-directional. "Inactive" modifier
257          * is not handled anyway. See RFC3264
258          */
259
260         p = body.s;
261         pend = body.s + body.len;
262         sess_fl = 0;
263         sess->media_count = 0;
264         cline_ip_s.s = NULL;  /* make gcc happy */
265         cline_ip_s.len = 0;
266         cline_ip = 0;
267         cline_count = 0;
268         while (p < pend) {
269                 if (next_sdp_line(&p, pend, &ltype, &line) < 0) break;
270                 switch (ltype) {
271                         case 'v':
272                                 /* Protocol Version: v=0 */
273                                 if (sess_fl != 0) {
274                                         ERR(MODULE_NAME": parse_sdp_content: only one session allowed\n");  /* RFC3264 */
275                                         return -1;
276                                 }
277                                 sess_fl = 1;
278                                 break;
279                         case 'c':
280                                 /* Connection Data: c=<network type> <address type> <connection address>, ex. c=IN IP4 224.2.17.12/127 */
281                                 switch (sess_fl) {
282                                         case 0:
283                                                 ERR(MODULE_NAME": parse_sdp_content: c= line is not in session section\n");
284                                                 return -1;
285                                         case 1:
286                                         case 2:
287                                                 cline_count++;
288                                                 if (cline_count > 1) {
289                                                         /* multicast not supported */
290                                                         if (sess_fl == 2) {
291                                                                 goto invalidate;
292                                                         }
293                                                         else {
294                                                                 cline_ip_s.len = 0;
295                                                         }
296                                                         break;
297                                                 }
298                                                 lend = line.s + line.len;
299                                                 cp = eat_token_end(line.s, lend);
300                                                 if (cp-line.s != 2 || memcmp(line.s, "IN", 2) != 0) {
301                                                         goto invalidate;
302                                                 }
303                                                 cp = eat_space_end(cp, lend);
304                                                 line.s = cp;
305                                                 cp = eat_token_end(cp, lend);
306                                                 if (cp-line.s != 3 || memcmp(line.s, "IP4", 3) != 0) {
307                                                         goto invalidate;
308                                                 }
309                                                 cp = eat_space_end(cp, lend);
310                                                 line.s = cp;
311                                                 cp = eat_token_end(cp, lend);
312                                                 line.len = cp-line.s;
313                                                 if (line.len == 0 || q_memchr(line.s, '/', line.len)) {
314                                                         /* multicast address not supported */
315                                                         goto invalidate;
316                                                 }
317                                                 if (sess_fl == 1) {
318                                                         cline_ip_s = line;
319                                                         cline_ip = s2ip4(&line);
320                                                 }
321                                                 else {
322                                                         sess->media[sess->media_count-1].ip = s2ip4(&line);
323                                                         sess->media[sess->media_count-1].active = 1;  /* IP may by specified by hostname */
324                                                         sess->media[sess->media_count-1].ip_s = line;
325                                                 }
326                                                 break;
327                                         default:
328                                                 ;
329                                 }
330                                 break;
331                         invalidate:
332                                 if (sess_fl == 2) {
333                                         sess->media[sess->media_count-1].active = 0;
334                                 }
335                                 break;
336                         case 'm':
337                                 /* Media Announcements: m=<media> <port>[/<number of ports>] <transport> <fmt list>, eg. m=audio 49170 RTP/AVP 0 */
338                                 switch (sess_fl) {
339                                         case 0:
340                                                 ERR(MODULE_NAME": parse_sdp_content: m= line is not in session section\n");
341                                                 return -1;
342                                         case 1:
343                                         case 2:
344                                                 if (sess->media_count >= MAX_MEDIA_NUMBER) {
345                                                         ERR(MODULE_NAME": parse_sdp_content: max.number of medias (%d) exceeded\n", MAX_MEDIA_NUMBER);
346                                                         return -1;
347                                                 }
348                                                 cline_count = 0;
349                                                 sess_fl = 2;
350                                                 sess->media_count++;
351                                                 sess->media[sess->media_count-1].active = 0;
352                                                 lend = line.s + line.len;
353                                                 cp = eat_token_end(line.s, lend);
354                                                 if (cp-line.s == 0) {
355                                                         break;
356                                                 }
357                                                 cp = eat_space_end(cp, lend);
358                                                 line.s = cp;
359                                                 cp = eat_token_end(cp, lend);
360                                                 line.len = cp-line.s;
361                                                 
362                                                 cp2 = q_memchr(line.s, '/', line.len);
363                                                 if (cp2) {
364                                                         /* strip optional number of ports, if present should be 2 */
365                                                         line.len = cp2-line.s;
366                                                 }
367                                                 sess->media[sess->media_count-1].port_s = line;
368                                                 if (line.len == 0) { /* invalid port? */
369                                                         break;
370                                                 }
371                                                 savec = line.s[line.len];
372                                                 line.s[line.len] = '\0';
373                                                 sess->media[sess->media_count-1].port = atol(line.s);
374                                                 line.s[line.len] = savec;
375                                                 if (sess->media[sess->media_count-1].port == 0) {
376                                                         break;
377                                                 }
378                                                 cp = eat_space_end(cp, lend);
379                                                 
380                                                 line.s = cp;
381                                                 cp = eat_token_end(cp, lend);
382                                                 line.len = cp-line.s;
383                                                 for (i = 0; supported_media_types[i].s != NULL; i++) {
384                                                         if (line.len == supported_media_types[i].len &&
385                                                                 strncasecmp(line.s, supported_media_types[i].s, line.len) == 0) {
386                                                                 sess->media[sess->media_count-1].active = cline_ip_s.len != 0;  /* IP may by specified by hostname */
387                                                                 sess->media[sess->media_count-1].ip_s = cline_ip_s;
388                                                                 sess->media[sess->media_count-1].ip = cline_ip;
389                                                                 break;
390                                                         }
391                                                 }
392                                                 break;
393                                         default:
394                                                 ;
395                                 }
396
397                                 break;
398                         default:
399                                 ;
400                 }
401         }
402         return 0;
403 }
404
405 static int prepare_lumps(struct sip_msg* msg, str* position, str* s) {
406         struct lump* anchor;
407         char *buf;
408
409 //ERR("'%.*s' --> '%.*s'\n", position->len, position->s, s->len, s->s); 
410         anchor = del_lump(msg, position->s - msg->buf, position->len, 0);
411         if (anchor == NULL) {
412                 ERR(MODULE_NAME": prepare_lumps: del_lump failed\n");
413                 return -1;
414         }
415         buf = pkg_malloc(s->len);
416         if (buf == NULL) {
417                 ERR(MODULE_NAME": prepare_lumps: out of memory\n");
418                 return -1;
419         }
420         memcpy(buf, s->s, s->len);
421         if (insert_new_lump_after(anchor, buf, s->len, 0) == 0) {
422                 ERR(MODULE_NAME": prepare_lumps: insert_new_lump_after failed\n");
423                 pkg_free(buf);
424                 return -1;
425         }
426         return 0;
427 }
428
429 static int update_sdp_content(struct sip_msg* msg, int gate_a_to_b, struct sdp_session *sdp_sess, struct ipt_session *ipt_sess) {
430         int i, j;
431         str s;
432         /* we must apply lumps for relevant c= and m= lines */
433         sdp_ip.len = 0;
434         for (i=0; i<sdp_sess->media_count; i++) {
435                 if (sdp_sess->media[i].active) {
436                         for (j=0; j<i; j++) {
437                                 if (sdp_sess->media[j].active && sdp_sess->media[i].ip_s.s == sdp_sess->media[j].ip_s.s) {
438                                         goto cline_fixed;
439                                 }
440                         }
441                         if (sdp_ip.len == 0) {
442                                 /* takes 1st ip to be rewritten, for aux purposes only */
443                                 sdp_ip = sdp_sess->media[i].ip_s;
444                         }
445                         /* apply lump for ip address in c= line */
446                         ip42s(ipt_sess->switchboard->in_switchboard.gate[!gate_a_to_b].ip, &s);
447                         if (prepare_lumps(msg, &sdp_sess->media[i].ip_s, &s) < 0)
448                                 return -1;
449         cline_fixed:
450                         /* apply lump for port in m= line */
451                         s.s = int2str(ipt_sess->streams[i].proxy_port, &s.len);
452                         if (prepare_lumps(msg, &sdp_sess->media[i].port_s, &s) < 0)
453                                 return -1;
454                 }
455         }
456         return 0;
457 }
458
459 /* null terminated result is allocated at static buffer */
460 static void serialize_ipt_session(struct ipt_session* sess, str* session_ids) {
461         static char buf[MAX_SWITCHBOARD_NAME_LEN+1+(5+1+1+10)*MAX_MEDIA_NUMBER+1];
462         char *p;
463         int i;
464         buf[0] = '\0';
465         p = buf;
466         memcpy(p, sess->switchboard->name.s, sess->switchboard->name.len);
467         p += sess->switchboard->name.len;
468         *p = ':';
469         p++;
470         for (i=0; i<sess->stream_count; i++) {
471                 if (sess->streams[i].sess_id >= 0) {
472                         p += sprintf(p, "%u/%u", sess->streams[i].sess_id, sess->streams[i].created);
473                 }
474                 *p = ',';
475                 p++;
476         }
477         p--;
478         *p = '\0';
479         session_ids->s = buf;
480         session_ids->len = p - buf;
481 }
482
483 /* switchboardname [":" [sess_id "/" created] [ * ( "," [sess_id "/" created] )] ] */
484 static int unserialize_ipt_session(str* session_ids, struct ipt_session* sess) {
485         char *p, *pend, savec;
486         str s;
487         memset(sess, 0, sizeof(*sess));
488         p = session_ids->s;
489         pend = session_ids->s+session_ids->len;
490         s.s = p;
491         while (p < pend && is_alpha(*p)) p++;
492         s.len = p-s.s;
493         sess->switchboard = find_switchboard(&s);
494         if (!sess->switchboard) {
495                 ERR(MODULE_NAME": unserialize_ipt_session: '%.*s', switchboard '%.*s' not found\n", session_ids->len, session_ids->s, s.len, s.s);
496                 return -1;
497         }
498         if (p == pend) return 0;
499         if (*p != ':') {
500                 ERR(MODULE_NAME": unserialize_ipt_session: '%.*s', colon expected near '%.*s'\n", session_ids->len, session_ids->s, pend-p, p);
501                 return -1;
502         }
503         do {
504                 if (sess->stream_count >= MAX_MEDIA_NUMBER) {
505                 ERR(MODULE_NAME": unserialize_ipt_session: '%.*s', max.media number (%d) exceeded\n", session_ids->len, session_ids->s, MAX_MEDIA_NUMBER);
506                         return -1;
507                 }
508                 p++;
509                 sess->stream_count++;
510                 sess->streams[sess->stream_count-1].sess_id = -1;
511                 sess->streams[sess->stream_count-1].created = 0;
512                 s.s = p;
513                 while (p < pend && (*p >= '0' && *p <= '9')) p++;
514                 if (p != pend && *p != '/') {
515                         ERR(MODULE_NAME": unserialize_ipt_session: '%.*s', '/' expected near '%.*s'\n", session_ids->len, session_ids->s, pend-p, p);
516                         return -1;
517                 }
518                 s.len = p-s.s;
519                 if (s.len > 0) {
520                         savec = s.s[s.len];
521                         s.s[s.len] = '\0';
522                         sess->streams[sess->stream_count-1].sess_id = atol(s.s);
523                         s.s[s.len] = savec;
524                 }
525                 p++;
526                 s.s = p;
527                 while (p < pend && (*p >= '0' && *p <= '9')) p++;
528                 if (p != pend && *p != ',') {
529                         sess->streams[sess->stream_count-1].sess_id = -1;
530                         ERR(MODULE_NAME": unserialize_ipt_session: '%.*s', comma expected near '%.*s'\n", session_ids->len, session_ids->s, pend-p, p);
531                         return -1;
532                 }
533                 s.len = p-s.s;
534                 if (s.len > 0) {
535                         savec = s.s[s.len];
536                         s.s[s.len] = '\0';
537                         sess->streams[sess->stream_count-1].created = atol(s.s);
538                         s.s[s.len] = savec;
539                 }
540         } while (p < pend);
541         return 0;
542 }
543
544 static void delete_ipt_sessions(struct ipt_session* ipt_sess) {
545         struct xt_rtpproxy_sockopt_in_sess_id in_sess_id;
546         int i, j;
547         for (i=0; i < ipt_sess->stream_count; i++) {
548                 if (ipt_sess->streams[i].sess_id >= 0) {
549                         j = i;
550                         in_sess_id.sess_id_min = ipt_sess->streams[i].sess_id;
551                         in_sess_id.sess_id_max = in_sess_id.sess_id_min;
552                         in_sess_id.created = ipt_sess->streams[i].created;
553                         /* group more sessions if possible */
554                         for (; i < ipt_sess->stream_count-1; i++) {
555                                 if (ipt_sess->streams[i+1].sess_id >= 0) {
556                                         if (ipt_sess->streams[i+1].sess_id == in_sess_id.sess_id_max+1) {
557                                                 in_sess_id.sess_id_max = ipt_sess->streams[i+1].sess_id;
558                                                 continue;
559                                         }
560                                         break;
561                                 }
562                         }
563                         if (xt_RTPPROXY_delete_session(&handle, &ipt_sess->switchboard->in_switchboard, &in_sess_id) < 0) {
564                                 ERR(MODULE_NAME": rtpproxy_delete: xt_RTPPROXY_delete_session error: %s (%d)\n", handle.err_str, handle.err_no);
565                                 /* what to do ? */
566                         }
567                         /* invalidate sessions including duplicates */
568                         for (; j<ipt_sess->stream_count; j++) {
569                                 if (ipt_sess->streams[j].sess_id >= in_sess_id.sess_id_min && ipt_sess->streams[j].sess_id <= in_sess_id.sess_id_max)
570                                         ipt_sess->streams[j].sess_id = -1;
571                         }
572                 }
573         }
574 }
575
576 #define GATE_FLAG 0x01
577 #define RINGING_TIMEOUT_FLAG 0x02
578
579 /* gate_a_to_b has index 0, gate_b_to_a 1 */
580 #define GATE_A_TO_B(flags) (((flags) & GATE_FLAG) == 0)
581
582 inline static void fill_in_session(int flags, int media_idx, struct sdp_session *sdp_sess, struct ipt_session *ipt_sess, struct xt_rtpproxy_sockopt_in_alloc_session *in_session) {
583         int j;
584         for (j=0; j<2; j++) {
585                 in_session->source[GATE_A_TO_B(flags)].stream[j].flags = 
586                         XT_RTPPROXY_SOCKOPT_FLAG_SESSION_ADDR |
587                         ipt_sess->switchboard->in_session.source[GATE_A_TO_B(flags)].stream[j].flags |
588                         ((flags & RINGING_TIMEOUT_FLAG) ? XT_RTPPROXY_SOCKOPT_FLAG_SESSION_LEARNING_TIMEOUT : 0);
589                 in_session->source[GATE_A_TO_B(flags)].stream[j].learning_timeout = (flags & RINGING_TIMEOUT_FLAG) ? 
590                         ipt_sess->switchboard->ringing_timeout :
591                         ipt_sess->switchboard->in_session.source[GATE_A_TO_B(flags)].stream[j].learning_timeout;
592                 in_session->source[GATE_A_TO_B(flags)].stream[j].addr.ip = sdp_sess->media[media_idx].ip;
593                 in_session->source[GATE_A_TO_B(flags)].stream[j].addr.port = sdp_sess->media[media_idx].port+j;
594         }
595         in_session->source[GATE_A_TO_B(flags)].always_learn = ipt_sess->switchboard->in_session.source[GATE_A_TO_B(flags)].always_learn;
596 }
597
598 static int rtpproxy_alloc(struct sip_msg* msg, char* _flags, char* _switchboard_id) {
599         int flags;
600         struct switchboard_item* si = 0;
601         struct sdp_session sdp_sess;
602         struct ipt_session ipt_sess;
603         struct xt_rtpproxy_sockopt_in_alloc_session in_session;
604         struct xt_rtpproxy_session out_session;
605         str s;
606         int i;
607
608         if (get_int_fparam(&flags, msg, (fparam_t*) _flags) < 0) {
609                 return -1;
610         }
611         if (get_str_fparam(&s, msg, (fparam_t*) _switchboard_id) < 0) {
612                 return -1;
613         }
614         if (s.len) {
615                 /* switchboard must be fully qualified, it simplifies helper because it's not necessary to store full identification to session_ids - name is sufficient */
616                 si = find_switchboard(&s);
617                 if (!si) {
618                         ERR(MODULE_NAME": rtpproxy_alloc: switchboard '%.*s' not found\n", s.len, s.s);
619                         return -1;
620                 }
621         }
622         else {
623                 if (!found_switchboard) {
624                         ERR(MODULE_NAME": rtpproxy_alloc: no implicit switchboard\n");
625                         return -1;
626                 }
627                 si = found_switchboard;
628         }
629         if (parse_sdp_content(msg, &sdp_sess) < 0)
630                 return -1;
631         memset(&ipt_sess, 0, sizeof(ipt_sess));
632         ipt_sess.switchboard = si;
633         memset(&in_session, 0, sizeof(in_session));
634         for (i = 0; i < sdp_sess.media_count; i++) {
635                 ipt_sess.streams[i].sess_id = -1;
636                 ipt_sess.stream_count = i+1;
637                 if (sdp_sess.media[i].active) {
638                         int j;
639                         for (j = 0; j < i; j++) {
640                                 /* if two media streams have equal source address than we will allocate only one ipt session */
641                                 if (sdp_sess.media[j].active && sdp_sess.media[i].ip == sdp_sess.media[j].ip && sdp_sess.media[i].port == sdp_sess.media[j].port) {
642                                         ipt_sess.streams[i].sess_id = ipt_sess.streams[j].sess_id;
643                                         ipt_sess.streams[i].proxy_port = ipt_sess.streams[j].proxy_port;
644                                         ipt_sess.streams[i].created = ipt_sess.streams[j].created;
645                                         goto cont;
646                                 }
647                         }
648                         fill_in_session(flags, i, &sdp_sess, &ipt_sess, &in_session);
649                         if (xt_RTPPROXY_alloc_session(&handle, &ipt_sess.switchboard->in_switchboard, &in_session, NULL, &out_session) < 0) {
650                                 ERR(MODULE_NAME": rtpproxy_alloc: xt_RTPPROXY_alloc_session error: %s (%d)\n", handle.err_str, handle.err_no);
651                                 delete_ipt_sessions(&ipt_sess);
652                                 return -1;
653                         }
654                         ipt_sess.streams[i].sess_id = out_session.sess_id;
655                         ipt_sess.streams[i].created = out_session.created;
656                         ipt_sess.streams[i].proxy_port = out_session.gate[!GATE_A_TO_B(flags)].stream[0].port;
657                 cont: ;
658                 }
659         }
660         if (update_sdp_content(msg, GATE_A_TO_B(flags), &sdp_sess, &ipt_sess) < 0) {
661                 delete_ipt_sessions(&ipt_sess);
662                 return -1;
663         }
664         serialize_ipt_session(&ipt_sess, &s);
665         global_session_ids = s.s; /* it's static and null terminated */
666         return 1;
667 }
668
669 static int rtpproxy_update(struct sip_msg* msg, char* _flags, char* _session_ids) {
670         str session_ids;
671         int flags, i;
672         struct sdp_session sdp_sess;
673         struct ipt_session ipt_sess;
674         struct xt_rtpproxy_sockopt_in_sess_id in_sess_id;
675         struct xt_rtpproxy_sockopt_in_alloc_session in_session;
676
677         if (get_int_fparam(&flags, msg, (fparam_t*) _flags) < 0) {
678                 return -1;
679         }
680         if (get_str_fparam(&session_ids, msg, (fparam_t*) _session_ids) < 0) {
681                 return -1;
682         }
683         if (unserialize_ipt_session(&session_ids, &ipt_sess) < 0) {
684                 return -1;
685         }
686         if (parse_sdp_content(msg, &sdp_sess) < 0)
687                 return -1;
688
689         if (ipt_sess.stream_count != sdp_sess.media_count) {
690                 ERR(MODULE_NAME": rtpproxy_update: number of m= item in offer (%d) and answer (%d) do not correspond\n", ipt_sess.stream_count, sdp_sess.media_count);
691                 return -1;
692         }
693         /* first we check for unexpected duplicate source ports */
694         for (i = 0; i < sdp_sess.media_count; i++) {
695                 if (ipt_sess.streams[i].sess_id >= 0 && sdp_sess.media[i].active) {
696                         int j;
697                         for (j = i+1; j < sdp_sess.media_count; j++) {
698                                 if (ipt_sess.streams[j].sess_id >= 0 && sdp_sess.media[j].active) {
699                                         /* if two media streams have equal source address XOR have equal session */
700                                         if ( (sdp_sess.media[i].ip == sdp_sess.media[j].ip && sdp_sess.media[i].port == sdp_sess.media[j].port) ^
701                                                  (ipt_sess.streams[i].sess_id == ipt_sess.streams[j].sess_id) ) {
702                                                 ERR(MODULE_NAME": rtpproxy_update: media (%d,%d) violation number\n", i, j);
703                                                 return -1;
704                                         }
705                                 }
706                         }
707                 }
708         }
709
710         memset(&in_session, 0, sizeof(in_session));
711         for (i = 0; i < sdp_sess.media_count; i++) {
712                 if (ipt_sess.streams[i].sess_id >= 0) {
713                         in_sess_id.sess_id_min = ipt_sess.streams[i].sess_id;
714                         in_sess_id.created = ipt_sess.streams[i].created;
715                         in_sess_id.sess_id_max = in_sess_id.sess_id_min;
716                         if (sdp_sess.media[i].active) {
717                                 fill_in_session(flags, i, &sdp_sess, &ipt_sess, &in_session);
718                                 if (xt_RTPPROXY_update_session(&handle, &ipt_sess.switchboard->in_switchboard, &in_sess_id, &in_session) < 0) {
719                                         ERR(MODULE_NAME": rtpproxy_alloc: xt_RTPPROXY_update_session error: %s (%d)\n", handle.err_str, handle.err_no);
720                                         /* delete all sessions ? */
721                                         return -1;
722                                 }
723                                 /* we don't know proxy port - it was known when being allocated so we got from switchboard - it's not too clear solution because it requires knowledge how ports are allocated */
724                                 ipt_sess.streams[i].proxy_port = ipt_sess.switchboard->in_switchboard.gate[!GATE_A_TO_B(flags)].port + 2*ipt_sess.streams[i].sess_id;
725                         }
726                         else {
727                                 /* can we delete any session allocated during offer? */
728                                 if (xt_RTPPROXY_delete_session(&handle, &ipt_sess.switchboard->in_switchboard, &in_sess_id) < 0) {
729                                         ERR(MODULE_NAME": rtpproxy_update: xt_RTPPROXY_delete_session error: %s (%d)\n", handle.err_str, handle.err_no);
730                                 }
731                                 ipt_sess.streams[i].sess_id = -1;
732                         }
733                 }
734         }
735         if (update_sdp_content(msg, GATE_A_TO_B(flags), &sdp_sess, &ipt_sess) < 0) {
736                 /* delete all sessions ? */
737                 return -1;
738         }
739         serialize_ipt_session(&ipt_sess, &session_ids);
740         global_session_ids = session_ids.s; /* it's static and null terminated */
741         return 1;
742 }
743
744 static int rtpproxy_adjust_timeout(struct sip_msg* msg, char* _flags, char* _session_ids) {
745         str session_ids;
746         int flags, i;
747         struct ipt_session ipt_sess;
748         struct xt_rtpproxy_sockopt_in_sess_id in_sess_id;
749         struct xt_rtpproxy_sockopt_in_alloc_session in_session;
750
751         if (get_int_fparam(&flags, msg, (fparam_t*) _flags) < 0) {
752                 return -1;
753         }
754         if (get_str_fparam(&session_ids, msg, (fparam_t*) _session_ids) < 0) {
755                 return -1;
756         }
757         if (unserialize_ipt_session(&session_ids, &ipt_sess) < 0) {
758                 return -1;
759         }
760
761         memset(&in_session, 0, sizeof(in_session));
762         for (i = 0; i < ipt_sess.stream_count; i++) {
763                 if (ipt_sess.streams[i].sess_id >= 0) {
764                         int j;
765                         in_sess_id.sess_id_min = ipt_sess.streams[i].sess_id;
766                         in_sess_id.created = ipt_sess.streams[i].created;
767                         in_sess_id.sess_id_max = in_sess_id.sess_id_min;
768
769
770                         for (j=0; j<2; j++) {
771
772                                 in_session.source[GATE_A_TO_B(flags)].stream[j].flags = 
773                                         (flags & RINGING_TIMEOUT_FLAG) ? 
774                                                 XT_RTPPROXY_SOCKOPT_FLAG_SESSION_LEARNING_TIMEOUT : 
775                                                 (ipt_sess.switchboard->in_session.source[GATE_A_TO_B(flags)].stream[j].flags & XT_RTPPROXY_SOCKOPT_FLAG_SESSION_LEARNING_TIMEOUT)
776                                         ;
777                                 in_session.source[GATE_A_TO_B(flags)].stream[j].learning_timeout = (flags & RINGING_TIMEOUT_FLAG) ? 
778                                         ipt_sess.switchboard->ringing_timeout :
779                                         ipt_sess.switchboard->in_session.source[GATE_A_TO_B(flags)].stream[j].learning_timeout;
780
781                         }
782
783                         if (xt_RTPPROXY_update_session(&handle, &ipt_sess.switchboard->in_switchboard, &in_sess_id, &in_session) < 0) {
784                                 ERR(MODULE_NAME": rtpproxy_alloc: xt_RTPPROXY_adjust_timeout error: %s (%d)\n", handle.err_str, handle.err_no);
785                                         return -1;
786                         }
787                 }
788         }
789         /* do not serialize sessions because it affect static buffer and more valuable values disappears */
790         return 1;
791 }
792
793 static int rtpproxy_delete(struct sip_msg* msg, char* _session_ids, char* dummy) {
794         str session_ids;
795         struct ipt_session ipt_sess;
796         if (get_str_fparam(&session_ids, msg, (fparam_t*) _session_ids) < 0) {
797                 return -1;
798         }
799         if (unserialize_ipt_session(&session_ids, &ipt_sess) < 0) {
800                 return -1;
801         }
802         delete_ipt_sessions(&ipt_sess);
803         /* do not serialize sessions because it affect static buffer and more valuable values disappears */
804         return 1;
805 }
806
807 static int rtpproxy_find(struct sip_msg* msg, char* _gate_a, char* _gate_b) {
808         unsigned int ip_a, ip_b;
809         str gate_a, gate_b;
810
811         if (get_str_fparam(&gate_a, msg, (fparam_t*) _gate_a) < 0) {
812                 return -1;
813         }
814         ip_a = s2ip4(&gate_a);
815         if (get_str_fparam(&gate_b, msg, (fparam_t*) _gate_b) < 0) {
816                 return -1;
817         }
818         ip_b = s2ip4(&gate_b);
819
820         found_direction = -1;
821         for (found_switchboard = switchboards; found_switchboard; found_switchboard=found_switchboard->next) {
822                 if (ip_a == found_switchboard->in_switchboard.gate[0].ip) {
823                         if (ip_b == found_switchboard->in_switchboard.gate[1].ip) {
824                                 found_direction = 1;
825                                 return 1;
826                                 break;
827                         }
828                 }
829                 else if (ip_a == found_switchboard->in_switchboard.gate[1].ip) {
830                         if (ip_b == found_switchboard->in_switchboard.gate[0].ip) {
831                                 found_direction = 0;
832                                 return 1;
833                         }
834                 }
835         }
836         return -1;
837 }
838
839 /* @select implementation */
840 static int sel_rtpproxy(str* res, select_t* s, struct sip_msg* msg) {  /* dummy */
841         return 0;
842 }
843
844 static int sel_sdp_ip(str* res, select_t* s, struct sip_msg* msg) {
845         *res = sdp_ip;
846         return 0;
847 }
848
849 static int sel_session_ids(str* res, select_t* s, struct sip_msg* msg) {
850         if (!global_session_ids)
851                 return 1;
852         res->s = global_session_ids;
853         res->len = strlen(res->s);
854         return 0;
855 }
856
857 static int sel_switchboard(str* res, select_t* s, struct sip_msg* msg) {
858         if (!found_switchboard)
859                 return 1;
860         *res = found_switchboard->name;
861         return 0;
862 }
863
864 static int sel_direction(str* res, select_t* s, struct sip_msg* msg) {
865         static char buf[2] = {'0', '1'};
866         if (!found_direction < 0)
867                 return 1;
868         res->s = buf+found_direction;
869         res->len = 1;
870         return 0;
871 }
872
873 select_row_t sel_declaration[] = {
874         { NULL, SEL_PARAM_STR, STR_STATIC_INIT(MODULE_NAME), sel_rtpproxy, SEL_PARAM_EXPECTED},
875         { sel_rtpproxy, SEL_PARAM_STR, STR_STATIC_INIT("sdp_ip"), sel_sdp_ip, 0 },
876         { sel_rtpproxy, SEL_PARAM_STR, STR_STATIC_INIT("session_ids"), sel_session_ids, 0 },
877         { sel_rtpproxy, SEL_PARAM_STR, STR_STATIC_INIT("switchboard"), sel_switchboard, 0 },
878         { sel_rtpproxy, SEL_PARAM_STR, STR_STATIC_INIT("direction"), sel_direction, 0 },
879
880         { NULL, SEL_PARAM_INT, STR_NULL, NULL, 0}
881 };
882
883 static int mod_pre_script_cb(struct sip_msg *msg, unsigned int flags, void *param) {
884         sdp_ip.s = "";
885         sdp_ip.len = 0;
886         found_switchboard = NULL;
887         found_direction = -1;
888         global_session_ids = NULL;
889         return 1;
890 }
891
892 /* module initialization */
893 static int mod_init(void) {
894         struct switchboard_item *si;
895         int i;
896         if (xt_RTPPROXY_open(&handle) < 0) goto err;
897         for (si = switchboards; si; si=si->next) {
898                 struct xt_rtpproxy_switchboard *out_switchboard;
899                 if (xt_RTPPROXY_get_switchboards(&handle, &si->in_switchboard, NULL, XT_RTPPROXY_SOCKOPT_FLAG_OUT_SWITCHBOARD, &out_switchboard) < 0) {
900                         goto err;
901                 }
902                 /* update switchboard info, we need real ports for rtpproxy_update, it may sometimes differ from in_switchboard when addr-a=addr-b. We'll take first switchboard returned, should be always only one */
903                 if (!out_switchboard) {
904                         ERR(MODULE_NAME": switchboard '%.*s' not found in iptables\n", si->name.len, si->name.s);
905                         goto err2;
906                 }
907                 if (si->in_switchboard.gate[0].ip == si->in_switchboard.gate[1].ip) {
908                         for (i=0; i<XT_RTPPROXY_MAX_GATE; i++) {
909                                 si->in_switchboard.gate[i].port = out_switchboard->so.gate[i].addr.port;
910                         }
911                 }
912                 xt_RTPPROXY_release_switchboards(&handle, out_switchboard);
913         }
914
915         register_script_cb(mod_pre_script_cb, REQUEST_CB | ONREPLY_CB | PRE_SCRIPT_CB, 0);
916         register_select_table(sel_declaration);
917         return 0;
918 err:
919         ERR(MODULE_NAME": %s (%d)\n", handle.err_str, handle.err_no);
920 err2:
921         if (handle.sockfd >= 0) {
922                 xt_RTPPROXY_close(&handle);
923         }
924         return -1;
925 }
926
927 static void mod_cleanup(void) {
928         if (handle.sockfd >= 0) {
929                 xt_RTPPROXY_close(&handle);
930         }
931 }
932
933 static int child_init(int rank) {
934
935         return 0;
936 }
937
938 #define eat_spaces(_p) \
939         while( *(_p)==' ' || *(_p)=='\t' ){\
940         (_p)++;}
941
942 #define DEF_PARAMS(_id,_s,_fld) \
943         if ( (param_ids & (_id)) && !(param_ids & ((_id) << par_GateB)) )  \
944                 si->_s[1]._fld = si->_s[0]._fld; \
945         if ( !(param_ids & (_id)) && (param_ids & ((_id) << par_GateB)) )  \
946                 si->_s[0]._fld = si->_s[1]._fld;
947
948
949 static int declare_switchboard(modparam_t type, void* val) {
950         char *s, *c;
951         int i;
952         struct switchboard_item *si = NULL;
953         enum param_id {
954                 par_GateB =             8,
955                 par_Name =              0x000001,
956                 par_RingingTimeout =    0x000002,
957                 par_Addr =              0x000100,
958                 par_Port =              0x000200,
959                 par_AlwaysLearn =       0x000400,
960                 par_LearningTimeout =   0x000800
961         };
962         #define IS_GATE_B(id) ((id &    0xFF0000)!=0)
963         static struct {
964                 char *name;
965                 unsigned int id;
966         } params[] = {
967                 {.name = "name", .id = par_Name},
968                 {.name = "addr-a", .id = par_Addr},
969                 {.name = "addr-b", .id = par_Addr << par_GateB},
970                 {.name = "port-a", .id = par_Port},
971                 {.name = "port-b", .id = par_Port << par_GateB},
972                 {.name = "always-learn-a", .id = par_AlwaysLearn},
973                 {.name = "always-learn-b", .id = par_AlwaysLearn << par_GateB},
974                 {.name = "learning-timeout-a", .id = par_LearningTimeout},
975                 {.name = "learning-timeout-b", .id = par_LearningTimeout << par_GateB},
976                 {.name = "ringing-timeout", .id = par_RingingTimeout},
977
978                 {.name = 0, .id = 0}
979         };
980         unsigned int param_ids = 0;
981
982         if (!val) return 0;
983         s = val;
984
985         eat_spaces(s);
986         if (!*s) return 0;
987         /* parse param: name=;addr-a=;addr-b=;port-a=;port-b=; */
988         si = pkg_malloc(sizeof(*si));
989         if (!si) goto err_E_OUT_OF_MEM;
990         memset(si, 0, sizeof(*si));
991         si->ringing_timeout = 60;
992         while (*s) {
993                 str p, val;
994                 unsigned int id;
995
996                 c = s;
997                 while ( is_alpha(*c) ) {
998                         c++;
999                 }
1000                 if (c == s) {
1001                         ERR(MODULE_NAME": declare_switchboard: param name expected near '%s'\n", s);
1002                         goto err_E_CFG;
1003                 }
1004                 p.s = s;
1005                 p.len = c-s;
1006                 eat_spaces(c);
1007                 s = c;
1008                 if (*c != '=') {
1009                         ERR(MODULE_NAME": declare_switchboard: equal char expected near '%s'\n", s);
1010                         goto err_E_CFG;
1011                 }
1012                 c++;
1013                 eat_spaces(c);
1014                 s = c;
1015                 while (*c && *c != ';') c++;
1016                 val.s = s;
1017                 val.len = c-s;
1018                 while (val.len > 0 && val.s[val.len-1]<=' ') val.len--;
1019                 if (*c) c++;
1020                 eat_spaces(c);
1021
1022                 id = 0;
1023                 for (i=0; params[i].id; i++) {
1024                         if (strlen(params[i].name)==p.len && strncasecmp(params[i].name, p.s, p.len) == 0) {
1025                                 id = params[i].id;
1026                                 break;
1027                         }
1028                 }
1029                 if (!id) {
1030                         ERR(MODULE_NAME": declare_switchboard: unknown param name '%.*s'\n", p.len, p.s);
1031                         goto err_E_CFG;
1032                 }
1033                 if (param_ids & id) {
1034                         ERR(MODULE_NAME": declare_switchboard: param '%.*s' used more than once\n", p.len, p.s);
1035                         goto err_E_CFG;
1036                 }
1037
1038                 switch (id) {
1039                         case par_Name:
1040                                 if (val.len > MAX_SWITCHBOARD_NAME_LEN) {
1041                                         ERR(MODULE_NAME": declare_switchboard: name is too long (%d>%d)\n", val.len, MAX_SWITCHBOARD_NAME_LEN);
1042                                         goto err_E_CFG;
1043                                 }
1044                                 si->name = val;
1045                                 break;
1046                         case par_Addr:
1047                         case par_Addr << par_GateB:
1048                                 si->in_switchboard.gate[IS_GATE_B(id)].ip = s2ip4(&val);
1049                                 if (si->in_switchboard.gate[IS_GATE_B(id)].ip == 0) {
1050                                         goto err_E_CFG2;
1051                                 }
1052                                 break;
1053                         case par_Port:
1054                         case par_Port << par_GateB: {
1055                                 unsigned int u;
1056                                 if (str2int(&val, &u) < 0) {
1057                                         goto err_E_CFG2;
1058                                 }
1059                                 si->in_switchboard.gate[IS_GATE_B(id)].port = u;
1060                                 break;
1061                         }
1062                         case par_AlwaysLearn:
1063                         case par_AlwaysLearn <<par_GateB: {
1064                                 unsigned int u;
1065                                 if (str2int(&val, &u) < 0) {
1066                                         goto err_E_CFG2;
1067                                 }
1068                                 si->in_session.source[IS_GATE_B(id)].always_learn = u != 0;
1069                                 break;
1070                         }
1071                         case par_LearningTimeout:
1072                         case par_LearningTimeout << par_GateB:{
1073                                 unsigned int u;
1074                                 if (str2int(&val, &u) < 0) {
1075                                         goto err_E_CFG2;
1076                                 }
1077                                 if (u) {
1078                                         for (i=0; i<2; i++) {
1079                                                 si->in_session.source[IS_GATE_B(id)].stream[i].learning_timeout = u;
1080                                                 si->in_session.source[IS_GATE_B(id)].stream[i].flags = XT_RTPPROXY_SOCKOPT_FLAG_SESSION_LEARNING_TIMEOUT;
1081                                         }
1082                                 }
1083                                 break;
1084                         }
1085                         case par_RingingTimeout: {
1086                                 unsigned int u;
1087                                 if (str2int(&val, &u) < 0) {
1088                                         goto err_E_CFG2;
1089                                 }
1090                                 if (u) {
1091                                         si->ringing_timeout = u;
1092                                 }
1093                                 break;
1094                         }
1095                         default:
1096                                 BUG(MODULE_NAME": declare_switchboard: unknown id '%x\n", id);
1097                                 goto err_E_CFG;
1098                 }
1099                 s = c;
1100                 param_ids |= id;
1101         }
1102
1103         if (find_switchboard(&si->name)) {
1104                 ERR(MODULE_NAME": declare_switchboard: name '%.*s' already declared\n", si->name.len, si->name.s);
1105                 goto err_E_CFG;
1106         }
1107         DEF_PARAMS(par_Addr,in_switchboard.gate,ip);
1108         DEF_PARAMS(par_Port,in_switchboard.gate,port);
1109
1110         DEF_PARAMS(par_AlwaysLearn,in_session.source,always_learn);
1111         for (i=0; i<2; i++) {
1112                 DEF_PARAMS(par_LearningTimeout,in_session.source,stream[i].learning_timeout);
1113                 DEF_PARAMS(par_LearningTimeout,in_session.source,stream[i].flags);
1114         }
1115         si->next = switchboards;
1116         switchboards = si;
1117         switchboard_count++;
1118
1119         return 0;
1120
1121 err_E_OUT_OF_MEM:
1122         ERR(MODULE_NAME": declare_switchboard(#%d): not enough pkg memory\n", switchboard_count);
1123         return E_OUT_OF_MEM;
1124
1125 err_E_CFG2:
1126         ERR(MODULE_NAME": declare_switchboard(#%d): parse error near \"%s\"\n", switchboard_count, s);
1127 err_E_CFG:
1128         if (si) pkg_free(si);
1129
1130         return E_CFG;
1131 }
1132
1133 static cmd_export_t cmds[] = {
1134         {MODULE_NAME "_alloc",     rtpproxy_alloc,         2, rtpproxy_alloc_fixup,       REQUEST_ROUTE | ONREPLY_ROUTE },
1135         {MODULE_NAME "_update",    rtpproxy_update,        2, rtpproxy_update_fixup,      REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE },
1136         {MODULE_NAME "_adjust_timeout", rtpproxy_adjust_timeout, 2, rtpproxy_update_fixup,      REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE },
1137         {MODULE_NAME "_delete",    rtpproxy_delete,        1, rtpproxy_delete_fixup,      REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE },
1138         {MODULE_NAME "_find",      rtpproxy_find,          2, rtpproxy_find_fixup,        REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE },
1139
1140         {0, 0, 0, 0, 0}
1141 };
1142
1143 static param_export_t params[] = {
1144         {"switchboard",           PARAM_STRING | PARAM_USE_FUNC, &declare_switchboard},
1145         {0, 0, 0}
1146 };
1147
1148 struct module_exports exports = {
1149         MODULE_NAME,
1150         cmds,
1151         0,       /* RPC methods */
1152         params,
1153         mod_init,
1154         0, /* reply processing */
1155         mod_cleanup, /* destroy function */
1156         0, /* on_break */
1157         child_init
1158 };
1159
1160
1161 #if !defined(NO_SHARED_LIBS) || NO_SHARED_LIBS==0
1162 /* make compiler happy and give it missing symbols */
1163 #include <iptables.h>
1164 #include <stdarg.h>
1165 void xtables_register_target(struct xtables_target *me) {
1166 }
1167
1168 void exit_error(enum exittype status, const char *msg, ...)
1169 {
1170         va_list args;
1171         
1172         va_start(args, msg);
1173 //      ERR(msg/*, args*/);  /* TODO: how to pass ... to macro? */
1174         ERR(MODULE_NAME": %s", msg);
1175         va_end(args);
1176 }
1177
1178 #ifndef TRUE
1179 #define TRUE 1
1180 #endif
1181 #ifndef FALSE
1182 #define FALSE 0
1183 #endif
1184
1185 int check_inverse(const char option[], int *invert, int *optind, int argc)
1186 {
1187         if (option && strcmp(option, "!") == 0) {
1188                 if (*invert)
1189                         exit_error(PARAMETER_PROBLEM, "Multiple `!' flags not allowed");
1190                 *invert = TRUE;
1191                 if (optind) {
1192                         *optind = *optind+1;
1193                         if (argc && *optind > argc)
1194                                 exit_error(PARAMETER_PROBLEM, "no argument following `!'");
1195                 }
1196                 return TRUE;
1197         }
1198         return FALSE;
1199 }
1200
1201 #endif
1202
1203