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