iptrtpproxy.c

00001 /* $Id: iptrtpproxy.c 30494 2010-07-20 15:05:24Z tma $
00002  *
00003  * Copyright (C) 2007 Tomas Mandys
00004  *
00005  * This file is part of ser, a free SIP server.
00006  *
00007  * ser is free software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License as published by
00009  * the Free Software Foundation; either version 2 of the License, or
00010  * (at your option) any later version
00011  *
00012  * For a license to use the ser software under conditions
00013  * other than those described here, or to purchase support for this
00014  * software, please contact iptel.org by e-mail at the following addresses:
00015  *    info@iptel.org
00016  *
00017  * ser is distributed in the hope that it will be useful,
00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020  * GNU General Public License for more details.
00021  *
00022  * You should have received a copy of the GNU General Public License
00023  * along with this program; if not, write to the Free Software
00024  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00025  *
00026  */
00027 
00028 // #include <linux/compiler.h>   will be needed to define __user macro
00029 #include "../../sr_module.h"
00030 #include "../../dprint.h"
00031 #include "../../data_lump.h"
00032 #include "../../data_lump_rpl.h"
00033 #include "../../error.h"
00034 #include "../../forward.h"
00035 #include "../../mem/mem.h"
00036 #include "../../mem/shm_mem.h"
00037 #include "../../atomic_ops.h"
00038 #include "../../parser/parse_content.h"
00039 #include "../../parser/parse_uri.h"
00040 #include "../../parser/parser_f.h"
00041 #include "../../parser/parse_body.h"
00042 #include "../../resolve.h"
00043 #include "../../trim.h"
00044 #include "../../ut.h"
00045 #include "../../msg_translator.h"
00046 #include "../../socket_info.h"
00047 #include "../../select.h"
00048 #include "../../select_buf.h"
00049 #include "../../script_cb.h"
00050 #include "../../cfg_parser.h"
00051 #include <sys/types.h>
00052 #include <sys/socket.h>
00053 #include <sys/time.h>
00054 #include <netinet/in.h>
00055 #include <arpa/inet.h>
00056 #include <sys/uio.h>
00057 #include <sys/un.h>
00058 #include <ctype.h>
00059 #include <errno.h>
00060 #include <netdb.h>
00061 #include <poll.h>
00062 #include <stdio.h>
00063 #include <stdlib.h>
00064 #include <string.h>
00065 #include <unistd.h>
00066 #include <linux/netfilter/xt_RTPPROXY.h>
00067 #include <arpa/inet.h>
00068 
00069 MODULE_VERSION
00070 
00071 #define MODULE_NAME "iptrtpproxy"
00072 
00073 /* max.number of RTP streams per session */
00074 #define MAX_MEDIA_NUMBER XT_RTPPROXY_MAX_ALLOC_SESSION
00075 #define MAX_CODEC_NUMBER MAX_MEDIA_NUMBER*5
00076 #define MAX_SWITCHBOARD_NAME_LEN 20
00077 #define MAX_AGGREGATED_NUMBER 30
00078 
00079 /* warningless cast at 64-bit */
00080 #define PTR2INT(v) (int)(long) (v)
00081 #define INT2PTR(v) (void*)(long) (v)
00082 
00083 struct host_item_stat {
00084         int last_error_stamp;
00085         int last_ok_stamp;
00086 };
00087 
00088 struct host_item {
00089         str name;
00090         struct xt_rtpproxy_connection_rpc_params rpc_params;
00091         int local;
00092         struct xt_rtpproxy_handle handle;
00093         int handle_is_opened;
00094 
00095         struct host_item_stat *stat;
00096         struct host_item *next;
00097 };
00098 
00099 struct switchboard_item_stat {
00100         atomic_t free;
00101         atomic_t alloc;
00102 };
00103 
00104 struct switchboard_item {
00105         str name;
00106         struct xt_rtpproxy_switchboard_id switchboard_addr;
00107         unsigned int sip_ip;
00108         str hostname;
00109         struct host_item *host;
00110         unsigned int weight;
00111 
00112         struct switchboard_item_stat *stat;
00113 
00114         struct switchboard_item *next;
00115 };
00116 
00117 enum sdp_media_type {
00118         sdpmtUnknown = 0, 
00119         sdpmtAudio, 
00120         sdpmtVideo, 
00121         sdpmtApplication, 
00122         sdpmtText, 
00123         sdpmtMessage,
00124         sdpmtData,  /* not recommended in RFC4566 */
00125         sdpmtControl   /* dtto */
00126 };
00127 
00128 #define NUM_MEDIA_TYPES (sdpmtControl+1)
00129 static str sdp_media_types_str[NUM_MEDIA_TYPES] = {
00130         STR_STATIC_INIT("unknown"),
00131         STR_STATIC_INIT("audio"),
00132         STR_STATIC_INIT("video"),
00133         STR_STATIC_INIT("application"),
00134         STR_STATIC_INIT("text"),
00135         STR_STATIC_INIT("message"),
00136         STR_STATIC_INIT("data"),
00137         STR_STATIC_INIT("control")
00138 };
00139 
00140 struct aggregation_item {
00141         str name;
00142         unsigned int switchboard_count;
00143         unsigned int sip_ip;
00144         struct switchboard_item *(*switchboards)[];
00145         struct aggregation_item *next;
00146 };
00147 
00148 struct codec_set_item_throttle {
00149         int max_streams;
00150         struct xt_rtpproxy_throttle_stat bandwidth[2];   /* RTP, RTCP */
00151 };
00152 
00153 struct codec_set_item {
00154         str name;
00155         struct {
00156                 struct codec_set_item_throttle throttle;
00157                 unsigned int (*codec_rights)[];
00158         } media_types[NUM_MEDIA_TYPES];
00159         struct codec_set_item *next;
00160 };
00161 
00162 struct ipt_session {
00163 
00164         unsigned int session_count;
00165         struct xt_rtpproxy_sockopt_session sessions[MAX_MEDIA_NUMBER];
00166         unsigned int sdp_media_count;
00167         int sdp_media[MAX_MEDIA_NUMBER];
00168 
00169         struct switchboard_item *switchboard;
00170 };
00171 
00172 static struct {
00173         str session_ids;
00174         str sdp_ip;
00175         str oline_user;
00176         str oline_addr;
00177         struct switchboard_item *switchboard[2];
00178         struct aggregation_item *aggregation[2];
00179         int learning_timeout;
00180         int expiration_timeout;
00181         int ttl;
00182         int always_learn;
00183         struct {
00184                 u_int32_t mark;
00185                 struct xt_rtpproxy_throttle_stat bandwidth[2];
00186         } throttle;
00187         struct codec_set_item *codec_set;
00188         int remove_codec_mask;
00189         unsigned int auth_rights;
00190         struct ipt_session protected_sess;
00191 } global_params;
00192 
00193 static struct switchboard_item *switchboards = NULL;
00194 static struct host_item *hosts = NULL;
00195 static struct aggregation_item *aggregations = NULL;
00196 static struct codec_set_item *codec_sets = NULL;
00197 static int switchboard_count = 0;
00198 static str iptrtpproxy_cfg_filename = STR_STATIC_INIT("/etc/iptrtpproxy.cfg");
00199 static int iptrtpproxy_cfg_flag = 0;
00200 static char* iptrtpproxy_cfg_hostname = NULL;
00201 static int rpc_heartbeat_timeout = 30;
00202 static int sdp_parsed = 999;   /* we need share parsed SDP between authorize_media & alloc/update * get_param */
00203 static struct sdp_session global_sdp_sess;
00204 
00205 #define declare_find_function(x) \
00206 static struct x##_item* find_##x(str *name, struct x##_item*** prev) { \
00207         struct x##_item* p;\
00208         struct x##_item** dummy;\
00209         if (!prev) \
00210                 prev = &dummy;\
00211         for (p = x##s, *prev = &x##s; p; *prev = &(**prev)->next, p=p->next) {\
00212                 int len, n;\
00213                 len = (name->len < p->name.len) ? name->len : p->name.len;\
00214                 n = strncasecmp(name->s, p->name.s, len);\
00215                 if (n == 0) {\
00216                         if (name->len == p->name.len) \
00217                                 return p;\
00218                         else if (name->len < p->name.len)\
00219                                 return NULL;\
00220                 }\
00221                 else if (n < 0) { \
00222                         return NULL;\
00223                 }\
00224         }\
00225         return NULL;\
00226 }
00227 
00228 declare_find_function(host)
00229 declare_find_function(switchboard)
00230 declare_find_function(aggregation)
00231 declare_find_function(codec_set)
00232 
00233 static struct switchboard_item* find_switchboard_by_addr(struct xt_rtpproxy_switchboard_id *addr) {
00234         struct switchboard_item* p;
00235         for (p = switchboards; p; p=p->next) {
00236                 if (addr->ip == p->switchboard_addr.ip && addr->port == p->switchboard_addr.port) break;
00237         }
00238         return p;
00239 }
00240 
00241 enum {
00242         PAR_EXPIRATION_TIMEOUT, 
00243         PAR_TTL, 
00244         PAR_LEARNING_TIMEOUT, 
00245         PAR_ALWAYS_LEARN,
00246         PAR_AGGREGATION_A, PAR_AGGREGATION_B, 
00247         PAR_SWITCHBOARD_A, PAR_SWITCHBOARD_B,
00248         PAR_AGGREGATION_BY_SIP_IP_A, PAR_AGGREGATION_BY_SIP_IP_B,
00249         PAR_SWITCHBOARD_BY_SIP_IP_A, PAR_SWITCHBOARD_BY_SIP_IP_B,
00250         PAR_SESSION_IDS, PAR_PROTECTED_SESSION_IDS,
00251         PAR_SDP_IP, PAR_ACTIVE_MEDIA_NUM,
00252         PAR_OLINE_USER, PAR_OLINE_ADDR,
00253         PAR_THROTTLE_MARK, 
00254         PAR_THROTTLE_RTP_MAX_BYTES, PAR_THROTTLE_RTP_MAX_PACKETS, 
00255         PAR_THROTTLE_RTCP_MAX_BYTES, PAR_THROTTLE_RTCP_MAX_PACKETS,
00256         PAR_CODEC_SET, PAR_AUTH_RIGHTS, PAR_REMOVE_CODEC_MASK
00257 };
00258 
00259 enum {
00260         PAR_READ=0x01, PAR_WRITE=0x02, PAR_INT=0x04, PAR_STR=0x08, PAR_DIR=0x10
00261 };
00262 
00263 static struct {
00264         char *name;
00265         int  id;
00266         int flags;
00267 } param_list[] = { 
00268         {"expiration_timeout", PAR_EXPIRATION_TIMEOUT, PAR_READ|PAR_WRITE|PAR_INT},
00269         {"ttl", PAR_TTL, PAR_READ|PAR_WRITE|PAR_INT},
00270         {"learning_timeout", PAR_LEARNING_TIMEOUT, PAR_READ|PAR_WRITE|PAR_INT},
00271         {"always_learn", PAR_ALWAYS_LEARN, PAR_READ|PAR_WRITE|PAR_INT},
00272         {"aggregation_a", PAR_AGGREGATION_A, PAR_READ|PAR_WRITE|PAR_STR},
00273         {"aggregation_b", PAR_AGGREGATION_B, PAR_READ|PAR_WRITE|PAR_STR|PAR_DIR},
00274         {"switchboard_a", PAR_SWITCHBOARD_A, PAR_READ|PAR_WRITE|PAR_STR},
00275         {"switchboard_b", PAR_SWITCHBOARD_B, PAR_READ|PAR_WRITE|PAR_STR|PAR_DIR},
00276         {"aggregation_by_sip_ip_a", PAR_AGGREGATION_BY_SIP_IP_A, PAR_WRITE|PAR_STR},
00277         {"aggregation_by_sip_ip_b", PAR_AGGREGATION_BY_SIP_IP_B, PAR_WRITE|PAR_STR|PAR_DIR},
00278         {"switchboard_by_sip_ip_a", PAR_SWITCHBOARD_BY_SIP_IP_A, PAR_WRITE|PAR_STR},
00279         {"switchboard_by_sip_ip_b", PAR_SWITCHBOARD_BY_SIP_IP_B, PAR_WRITE|PAR_STR|PAR_DIR},
00280         {"session_ids", PAR_SESSION_IDS, PAR_READ|PAR_STR},
00281         {"protected_session_ids", PAR_PROTECTED_SESSION_IDS, PAR_WRITE|PAR_STR},
00282         {"sdp_ip", PAR_SDP_IP, PAR_READ|PAR_INT},
00283         {"active_media_num", PAR_ACTIVE_MEDIA_NUM, PAR_READ|PAR_INT},
00284         {"o_user", PAR_OLINE_USER, PAR_READ|PAR_WRITE|PAR_STR},
00285         {"o_addr", PAR_OLINE_ADDR, PAR_READ|PAR_WRITE|PAR_STR},
00286         {"throttle_mark", PAR_THROTTLE_MARK, PAR_READ|PAR_WRITE|PAR_INT},
00287         {"throttle_rtp_max_bytes", PAR_THROTTLE_RTP_MAX_BYTES, PAR_READ|PAR_WRITE|PAR_INT},
00288         {"throttle_rtp_max_packets", PAR_THROTTLE_RTP_MAX_PACKETS, PAR_READ|PAR_WRITE|PAR_INT},
00289         {"throttle_rtcp_max_bytes", PAR_THROTTLE_RTCP_MAX_BYTES, PAR_READ|PAR_WRITE|PAR_INT},
00290         {"throttle_rtcp_max_packets", PAR_THROTTLE_RTCP_MAX_PACKETS, PAR_READ|PAR_WRITE|PAR_INT},
00291         {"codec_set", PAR_CODEC_SET, PAR_READ|PAR_WRITE|PAR_STR},
00292         {"remove_codec_mask", PAR_REMOVE_CODEC_MASK, PAR_READ|PAR_WRITE|PAR_INT},
00293         {"auth_rights", PAR_AUTH_RIGHTS, PAR_READ|PAR_INT},
00294         { NULL }
00295 };
00296 
00297 static int param2idx(str *name, int rw) {
00298         int i;
00299         for (i=0; param_list[i].name; i++) {
00300                 if (strlen(param_list[i].name)==name->len && 
00301                     strncasecmp(param_list[i].name, name->s, name->len) == 0 && 
00302                     (rw & param_list[i].flags)) {
00303                         return i;
00304                 }
00305         }
00306         ERR(MODULE_NAME": param2idx: unknown param '%.*s', rw:0x%0x\n", STR_FMT(name), rw);
00307         return -1;
00308 }
00309 
00313 static int rtpproxy_alloc_update_fixup(void** param, int param_no) {
00314         switch (param_no) {
00315                 case 1:
00316                         return fixup_var_int_12(param, param_no);
00317                 case 2:
00318                         return fixup_var_str_12(param, param_no);
00319                 default:
00320                         return 0;
00321         }
00322 }
00323 
00324 static int rtpproxy_delete_fixup(void** param, int param_no) {
00325         switch (param_no) {
00326                 case 1:
00327                         return fixup_var_str_12(param, param_no);
00328                 default:
00329                         return 0;
00330         }
00331 }
00332 
00333 static int rtpproxy_set_param_fixup(void** param, int param_no) {
00334         int idx;
00335         action_u_t *a;
00336         str s;
00337         switch (param_no) {
00338                 case 1:
00339                         s.s = (char*)*param;
00340                         s.len = strlen(s.s);
00341                         idx = param2idx(&s, PAR_WRITE);
00342                         if (idx < 0) {
00343                                 return E_CFG;
00344                         }
00345                         *param = INT2PTR(idx);
00346                         break;
00347                 case 2:
00348                         a = fixup_get_param(param, param_no, 1);
00349                         idx = a->u.number;
00350                         if (param_list[idx].flags & PAR_STR) {
00351                                 return fixup_var_str_12(param, param_no);
00352 
00353                         } else if (param_list[idx].flags & PAR_INT) {
00354                                 return fixup_var_int_12(param, param_no);
00355                         }
00356                         break;
00357         }
00358         return 0;
00359 }
00360 
00361 static int name2media_type(str *name) {
00362         int i;
00363         for (i = 1; i<NUM_MEDIA_TYPES; i++) {
00364                 if (name->len == sdp_media_types_str[i].len &&
00365                         strncasecmp(name->s, sdp_media_types_str[i].s, name->len) == 0) {
00366                                 return i;
00367                 }
00368         }
00369         return sdpmtUnknown;
00370 }
00371 
00372 struct codec_entry {
00373         str name;
00374         /* bandwidth */
00375         int payload_type;  /* -1 .. dynamic */
00376 };
00377 
00378 #define MAX_FIXED_PAYLOAD_TYPES 96
00379 
00380 static struct codec_entry(*reg_codecs)[] = NULL;
00381 static int reg_codec_count = 0;
00382 static int reg_codec_alloc_count = 0;
00383 static struct {
00384         int codec_id;
00385 } fixed_payload_types[MAX_FIXED_PAYLOAD_TYPES];
00386 
00387 
00388 /* unregistered codec .. 0 */
00389 static int name2codec_id(str *name, int *new_codec_id) {
00390         int i, j;
00391         i = 0;
00392         j = reg_codec_count - 1;
00393         while (i <= j) {        
00394                 int k, r;
00395                 k = (i + j)/2;
00396                 r = strncasecmp((*reg_codecs)[k].name.s, name->s, ((*reg_codecs)[k].name.len < name->len)?(*reg_codecs)[k].name.len:name->len);
00397                 if (r == 0 && (*reg_codecs)[k].name.len == name->len) {
00398                         return k+1;
00399                 } else if (r > 0 || (r == 0 && (*reg_codecs)[k].name.len > name->len)) {
00400                         j = k - 1;
00401                 }
00402                 else {
00403                         i = k + 1;
00404                 }
00405         }
00406         if (new_codec_id) {
00407                 *new_codec_id = i + 1;
00408         }
00409         return 0;
00410 }
00411 
00412 /* return <0 if error, otherwise codec_id */
00413 static int register_codec(str *name) {
00414         int codec_id, new_codec_id = 0;
00415         if (!(codec_id = name2codec_id(name, &new_codec_id))) {
00416                 int i;
00417                 if (reg_codec_count + 1 > reg_codec_alloc_count) {
00418                         void *p;
00419                         reg_codec_alloc_count += 10;
00420                         p = pkg_realloc(reg_codecs, sizeof((*reg_codecs)[0])*reg_codec_alloc_count);
00421                         if (!p) {
00422                                 return E_OUT_OF_MEM;
00423                         }
00424                         reg_codecs = p;
00425                 }
00426 
00427                 /* do not count codec_id == 0 (unknown) */
00428                 for (i=reg_codec_count-1; i >= new_codec_id-1; i--) {
00429                         (*reg_codecs)[i+1] = (*reg_codecs)[i];
00430                 }
00431                 reg_codec_count++;
00432                 (*reg_codecs)[new_codec_id-1].name = *name; 
00433                 codec_id = new_codec_id;
00434 
00435         }
00436         return codec_id;
00437 }
00438 
00439 enum send_rec_modifier {
00440         sdpaattr_sendonly = 1,
00441         sdpaattr_recvonly = 2,
00442         sdpaattr_sendrecv = 3,
00443         sdpaattr_inactive = 4
00444 };
00445 
00446 static str send_rec_modifiers[] = {
00447         STR_STATIC_INIT(""),
00448         STR_STATIC_INIT("sendonly"),
00449         STR_STATIC_INIT("recvonly"),
00450         STR_STATIC_INIT("sendrecv"),
00451         STR_STATIC_INIT("inactive"),
00452 };
00453 
00454 struct sdp_codec {
00455         unsigned int payload_type;
00456         unsigned int codec_id;
00457         str mline_payload_type_s;
00458         str a_rtpmap_line_s;
00459         str a_fmtp_line_s;
00460 };
00461 
00462 struct sdp_session {
00463         str oline_user_s;
00464         str oline_addr_s;
00465         unsigned int media_count;
00466         struct {
00467                 int active;  /* if SDP has been parsed correctly, has a IP (even 0.0.0.0), port!=0 and has supported params */
00468                 unsigned short port;
00469                 unsigned int ip;
00470                 str ip_s;
00471                 str port_s;
00472                 enum sdp_media_type media_type;
00473                 enum send_rec_modifier send_rec_modifier;
00474                 str send_rec_modifier_line_s;
00475                 int codec_count;
00476                 struct sdp_codec (*codecs)[];
00477         } media[MAX_MEDIA_NUMBER];
00478 };
00479 
00480 static unsigned int s2ip4(str *s) {
00481         struct in_addr res;
00482         char c2;
00483         c2 = s->s[s->len];
00484         s->s[s->len] = '\0';
00485         if (!inet_aton(s->s, &res)) {
00486                 s->s[s->len] = c2;
00487                 return 0;
00488         }
00489         s->s[s->len] = c2;
00490         return res.s_addr;
00491 }
00492 
00493 static void ip42s(unsigned int ip, str *s) {
00494         struct in_addr ip2 = { ip };
00495         s->s = inet_ntoa(ip2);
00496         s->len = strlen(s->s);
00497 }
00498 
00499 #define is_alpha(_c) (((_c) >= 'a' && (_c) <= 'z') || ((_c) >= 'A' && (_c) <= 'Z') || ((_c) >= '0' && (_c) <= '9') || ((_c) == '_') || ((_c) == '-'))
00500 
00501 inline static int next_sdp_line(char** p, char* pend, char *ltype, str* lvalue, str* line) {
00502         char *cp;
00503         while (*p < pend) {
00504                 while (*p < pend && (**p == '\n' || **p == '\r')) (*p)++;
00505                 for (cp = *p; cp < pend && *cp != '\n' && *cp != '\r'; cp++);
00506 
00507                 if (cp-*p > 2 && (*p)[1] == '=') {
00508                         *ltype = **p;
00509                         lvalue->s = (*p)+2;
00510                         lvalue->len = cp-lvalue->s;
00511                         while (cp < pend && (*cp == '\n' || *cp == '\r')) cp++;
00512                         line->s = (*p);
00513                         line->len = cp-line->s;
00514                         *p = cp;
00515                         return 0;
00516                 }
00517                 *p = cp;
00518         }
00519         return -1;
00520 };
00521 
00522 static int name2enum(str *name, str (*list)[]) {
00523         int i;
00524         for (i = 0; (*list)[i].s != NULL; i++) {
00525                 if (name->len == (*list)[i].len &&
00526                         strncasecmp(name->s, (*list)[i].s, name->len) == 0) {
00527                         return i;
00528                 }
00529         }
00530         return -1;
00531 }
00532 
00533 static int prefix2enum(str *line, str (*list)[]) {
00534         int i;
00535         for (i = 0; (*list)[i].s != NULL; i++) {
00536                 if (line->len > (*list)[i].len &&
00537                         strncmp(line->s, (*list)[i].s, (*list)[i].len) == 0) {
00538                         return i;
00539                 }
00540         }
00541         return -1;
00542 }
00543 
00544 /* SDP RFC2327 */
00545 static int parse_sdp_content(struct sip_msg* msg, struct sdp_session *sess) {
00546         char *p, *pend, *cp, *cp2, *lend;
00547         str line, lvalue, cline_ip_s, body;
00548         int sess_fl, i, cline_count, codec_count;
00549         char ltype, savec;
00550         unsigned int cline_ip;
00551         enum send_rec_modifier sess_send_rec_modifier;
00552 
00553         static struct sdp_codec codecs[MAX_CODEC_NUMBER];
00554 
00555         static str supported_protocols[] = {
00556                 STR_STATIC_INIT("rtp/avp"),
00557                 STR_STATIC_INIT("rtp/savp"),
00558                 STR_STATIC_INIT("rtp/avpf"),
00559                 STR_STATIC_INIT("rtp/savpf"),
00560                 STR_STATIC_INIT("udp"),
00561                 STR_STATIC_INIT("udptl"),
00562                 STR_NULL
00563         };
00564 
00565         enum a_attr {sdpaattr_rtpmap, sdpaattr_fmtp, sdpaattr_rtcp};
00566         static str a_attrs[] = {
00567                 STR_STATIC_INIT("rtpmap:"),
00568                 STR_STATIC_INIT("fmtp:"),
00569                 STR_STATIC_INIT("rtcp:"),
00570                 STR_NULL
00571         };
00572 
00573         memset(sess, 0, sizeof(*sess));
00574         /* try to get the body part with application/sdp */
00575         body.s = get_body_part(msg, TYPE_APPLICATION, SUBTYPE_SDP, &body.len);
00576         if (!body.s) {
00577                 ERR(MODULE_NAME": parse_sdp_content: failed to get the application/sdp body\n");
00578                 return -1;
00579         }
00580         
00581         #if 0
00582         body.s = get_body(msg);
00583         if (body.s==0) {
00584                 ERR(MODULE_NAME": parse_sdp_content: failed to get the message body\n");
00585                 return -1;
00586         }
00587         body.len = msg->len -(int)(body.s - msg->buf);
00588         if (body.len==0) {
00589                 ERR(MODULE_NAME": parse_sdp_content: message body has length zero\n");
00590                 return -1;
00591         }
00592 
00593         /* no need for parse_headers(msg, EOH), get_body will parse everything */
00594         if (!msg->content_type)
00595         {
00596                 WARN(MODULE_NAME": parse_sdp_content: Content-TYPE header absent!"
00597                         "let's assume the content is text/plain\n");
00598         }
00599         else {
00600                 trim_len(line.len, line.s, msg->content_type->body);
00601                 if (line.len != sizeof("application/sdp")-1 || strncasecmp(line.s, "application/sdp", line.len) != 0) {
00602                         ERR(MODULE_NAME": parse_sdp_content: bad content type '%.*s'\n", STR_FMT(&line));
00603                         return -1;
00604                 }
00605         }
00606         #endif
00607         /*
00608          * Parsing of SDP body.
00609          * It can contain a few session descriptions (each starts with
00610          * v-line), and each session may contain a few media descriptions
00611          * (each starts with m-line).
00612          * We have to change ports in m-lines, and also change IP addresses in
00613          * c-lines which can be placed either in session header (fallback for
00614          * all medias) or media description.
00615          * Ports should be allocated for any media. IPs all should be changed
00616          * to the same value (RTP proxy IP), so we can change all c-lines
00617          * unconditionally.
00618          * There are sendonly,recvonly modifiers which signalize one-way
00619          * streaming, it probably won't work but it's handled the same way,
00620          * RTCP commands are still bi-directional. "Inactive" modifier
00621          * is not handled anyway. See RFC3264
00622          */
00623 
00624         p = body.s;
00625         pend = body.s + body.len;
00626         sess_fl = 0;
00627         sess->media_count = 0;
00628         cline_ip_s.s = NULL;  /* make gcc happy */
00629         cline_ip_s.len = 0;
00630         cline_ip = 0;
00631         cline_count = 0;
00632         codec_count = 0;
00633         memset(&codecs, 0, sizeof(codecs));
00634         sess_send_rec_modifier = 0;
00635         while (p < pend) {
00636                 if (next_sdp_line(&p, pend, &ltype, &lvalue, &line) < 0) break;
00637                 lend = lvalue.s + lvalue.len;
00638                 switch (ltype) {
00639                         case 'v':
00640                                 /* Protocol Version: v=0 */
00641                                 if (sess_fl != 0) {
00642                                         ERR(MODULE_NAME": parse_sdp_content: only one session allowed\n");  /* RFC3264 */
00643                                         return -1;
00644                                 }
00645                                 sess_fl = 1;
00646                                 break;
00647                         case 'o': 
00648                                 /* originator & session description: o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address> */
00649                                 if (sess_fl != 1) {
00650                                         ERR(MODULE_NAME": parse_sdp_content: o= line is not in session section\n"); 
00651                                         return -1;
00652                                 }
00653                                 for (i=0; i<6; i++) 
00654                                 if (sess->oline_addr_s.s) {
00655                                         ERR(MODULE_NAME": parse_sdp_content: only one o= line allowed\n"); 
00656                                         return -1;
00657                                 }
00658                                 cp = eat_token_end(lvalue.s, lend);
00659                                 sess->oline_user_s.len = cp-lvalue.s;
00660                                 if (!sess->oline_user_s.len) goto invalid_o;
00661                                 sess->oline_user_s.s = lvalue.s;
00662                                 lvalue.s = eat_space_end(cp, lend);
00663                                 for (i=0; i<4; i++) {
00664                                                 cp = eat_token_end(lvalue.s, lend);
00665                                                 if (cp-lvalue.s == 0) goto invalid_o;
00666                                                 lvalue.s = eat_space_end(cp, lend);
00667                                 }
00668                                 cp = eat_token_end(lvalue.s, lend);
00669                                 sess->oline_addr_s.len = cp-lvalue.s;
00670                                 if (!sess->oline_addr_s.len) goto invalid_o;
00671                                 sess->oline_addr_s.s = lvalue.s;
00672                                 break;
00673                         invalid_o:
00674                                 ERR(MODULE_NAME": parse_sdp_content: invalid o= line '%.*s'\n", (int) (lend-line.s), line.s);
00675                                 return -1;
00676                         case 'c':
00677                                 /* Connection Data: c=<network type> <address type> <connection address>, ex. c=IN IP4 224.2.17.12/127 */
00678                                 switch (sess_fl) {
00679                                         case 0:
00680                                                 ERR(MODULE_NAME": parse_sdp_content: c= line is not in session section\n");
00681                                                 return -1;
00682                                         case 1:
00683                                         case 2:
00684                                                 cline_count++;
00685                                                 if (cline_count > 1) {
00686                                                         /* multicast not supported */
00687                                                         if (sess_fl == 2) {
00688                                                                 goto invalidate;
00689                                                         }
00690                                                         else {
00691                                                                 cline_ip_s.len = 0;
00692                                                         }
00693                                                         break;
00694                                                 }
00695                                                 cp = eat_token_end(lvalue.s, lend);
00696                                                 if (cp-lvalue.s != 2 || memcmp(lvalue.s, "IN", 2) != 0) {
00697                                                         goto invalidate;
00698                                                 }
00699                                                 cp = eat_space_end(cp, lend);
00700                                                 lvalue.s = cp;
00701                                                 cp = eat_token_end(cp, lend);
00702                                                 if (cp-lvalue.s != 3 || memcmp(lvalue.s, "IP4", 3) != 0) {
00703                                                         goto invalidate;
00704                                                 }
00705                                                 cp = eat_space_end(cp, lend);
00706                                                 lvalue.s = cp;
00707                                                 cp = eat_token_end(cp, lend);
00708                                                 lvalue.len = cp-lvalue.s;
00709                                                 if (lvalue.len == 0 || q_memchr(lvalue.s, '/', lvalue.len)) {
00710                                                         /* multicast address not supported */
00711                                                         goto invalidate;
00712                                                 }
00713                                                 if (sess_fl == 1) {
00714                                                         cline_ip_s = lvalue;
00715                                                         cline_ip = s2ip4(&lvalue);
00716                                                 }
00717                                                 else {
00718                                                         sess->media[sess->media_count-1].ip = s2ip4(&lvalue);
00719                                                         sess->media[sess->media_count-1].active = sess->media[sess->media_count-1].port != 0;  /* IP may by specified by hostname */
00720                                                         sess->media[sess->media_count-1].ip_s = lvalue;
00721                                                 }
00722                                                 break;
00723                                         default:
00724                                                 ;
00725                                 }
00726                                 break;
00727                         invalidate:
00728                                 if (sess_fl == 2) {
00729                                         sess->media[sess->media_count-1].active = 0;
00730                                 }
00731                                 break;
00732                         case 'm':
00733                                 /* Media Announcements: m=<media> <port>[/<number of ports>] <transport> <fmt list>, eg. m=audio 49170 RTP/AVP 0 */
00734                                 /* media: "audio", "video", "application", "data" and "control" */
00735                                 switch (sess_fl) {
00736                                         case 0:
00737                                                 ERR(MODULE_NAME": parse_sdp_content: m= line is not in session section\n");
00738                                                 return -1;
00739                                         case 1:
00740                                         case 2:
00741                                                 if (sess->media_count >= MAX_MEDIA_NUMBER) {
00742                                                         ERR(MODULE_NAME": parse_sdp_content: max.number of medias (%d) exceeded\n", MAX_MEDIA_NUMBER);
00743                                                         return -1;
00744                                                 }
00745                                                 cline_count = 0;
00746                                                 sess_fl = 2;
00747                                                 sess->media_count++;
00748                                                 sess->media[sess->media_count-1].active = 0;
00749                                                 sess->media[sess->media_count-1].port = 0;
00750                                                 sess->media[sess->media_count-1].send_rec_modifier = sess_send_rec_modifier;
00751                                                 cp = eat_token_end(lvalue.s, lend);
00752                                                 lvalue.len = cp-lvalue.s;
00753                                                 sess->media[sess->media_count-1].media_type = name2media_type(&lvalue);;
00754                                                 if (!lvalue.len) {
00755                                                         break;
00756                                                 }
00757                                                 cp = eat_space_end(cp, lend);
00758                                                 lvalue.s = cp;
00759                                                 cp = eat_token_end(cp, lend);
00760                                                 lvalue.len = cp-lvalue.s;
00761                                                 
00762                                                 cp2 = q_memchr(lvalue.s, '/', lvalue.len);
00763                                                 if (cp2) {
00764                                                         /* strip optional number of ports, if present should be 2 */
00765                                                         lvalue.len = cp2-lvalue.s;
00766                                                 }
00767                                                 sess->media[sess->media_count-1].port_s = lvalue;
00768                                                 if (lvalue.len == 0) { /* invalid port? */
00769                                                         break;
00770                                                 }
00771                                                 savec = lvalue.s[lvalue.len];
00772                                                 lvalue.s[lvalue.len] = '\0';
00773                                                 sess->media[sess->media_count-1].port = atol(lvalue.s);
00774                                                 lvalue.s[lvalue.len] = savec;
00775                                                 if (sess->media[sess->media_count-1].port == 0) {
00776                                                         break;
00777                                                 }
00778                                                 cp = eat_space_end(cp, lend);
00779                                                 
00780                                                 lvalue.s = cp;
00781                                                 cp = eat_token_end(cp, lend);
00782                                                 lvalue.len = cp-lvalue.s;
00783                                                 if (name2enum(&lvalue, &supported_protocols) >= 0) {
00784                                                         sess->media[sess->media_count-1].active = cline_ip_s.len != 0;  /* IP may by specified by hostname */
00785                                                         sess->media[sess->media_count-1].ip_s = cline_ip_s;
00786                                                         sess->media[sess->media_count-1].ip = cline_ip;
00787                                                 }
00788                                                 /* get payload types */
00789                                                 sess->media[sess->media_count-1].codecs = (struct sdp_codec (*)[]) (codecs + codec_count);
00790                                                 while (cp < lend) {
00791                                                         if (codec_count >= MAX_CODEC_NUMBER) {
00792                                                                 ERR(MODULE_NAME": parse_sdp_content: max.number of codecs (%d) exceeded\n", MAX_CODEC_NUMBER);
00793                                                                 return -1;
00794                                                         }
00795                                                         codecs[codec_count].mline_payload_type_s.s = cp;
00796                                                         cp = eat_space_end(cp, lend);
00797                                                         lvalue.s = cp;
00798                                                         cp = eat_token_end(cp, lend);
00799                                                         codecs[codec_count].mline_payload_type_s.len = cp - codecs[codec_count].mline_payload_type_s.s;
00800                                                         lvalue.len = cp-lvalue.s;
00801                                                         savec = lvalue.s[lvalue.len];
00802                                                         lvalue.s[lvalue.len] = '\0';
00803                                                         codecs[codec_count].payload_type = atol(lvalue.s);
00804                                                         if (codecs[codec_count].payload_type < MAX_FIXED_PAYLOAD_TYPES) {
00805                                                                 codecs[codec_count].codec_id = fixed_payload_types[codecs[codec_count].payload_type].codec_id;
00806                                                         }
00807                                                         lvalue.s[lvalue.len] = savec;
00808                                                         for (i=0; i < sess->media[sess->media_count-1].codec_count; i++) {
00809                                                                 if (codecs[codec_count].payload_type == (*sess->media[sess->media_count-1].codecs)[i].payload_type) {
00810                                                                         ERR(MODULE_NAME": parse_sdp_content: duplicate payload type in '%.*s'\n", (int) (lend-line.s), line.s);
00811                                                                         return -1;
00812                                                                 }
00813                                                         }
00814                                                         codec_count++;
00815                                                         sess->media[sess->media_count-1].codec_count++;                                                 
00816                                                 }
00817                                                 if (!sess->media[sess->media_count-1].codec_count) {
00818                                                         ERR(MODULE_NAME": parse_sdp_content: no codec declared '%.*s'\n", (int) (lend-line.s), line.s);
00819                                                         return -1;
00820                                                 }
00821                                                 break;
00822                                         default:
00823                                                 ;
00824                                 }
00825 
00826                                 break;
00827                         case 'a':
00828                                 i = name2enum(&lvalue, &send_rec_modifiers);
00829                                 if (i > 0) {
00830                                         switch (sess_fl) {
00831                                                 case 1:
00832                                                         if (sess_send_rec_modifier) {
00833                                                                 ERR(MODULE_NAME": parse_sdp_content: duplicate send/recv modifier in session '%.*s'\n", (int) (lend-line.s), line.s);
00834                                                                 return -1;
00835                                                         }
00836                                                         sess_send_rec_modifier = i;
00837                                                         break;
00838                                                 case 2:
00839                                                         if (sess->media[sess->media_count-1].send_rec_modifier_line_s.s) {
00840                                                                 ERR(MODULE_NAME": parse_sdp_content: duplicate send/recv modifier in stream '%.*s'\n", (int) (lend-line.s), line.s);
00841                                                                 return -1;
00842                                                         }
00843                                                         sess->media[sess->media_count-1].send_rec_modifier = i;
00844                                                         sess->media[sess->media_count-1].send_rec_modifier_line_s = line;
00845                                                 default:
00846                                                         ;
00847                                         }
00848                                 }
00849                                 else if (sess_fl == 2) {
00850                                         int payload_type;
00851                                         int a_attr;
00852                                         a_attr = prefix2enum(&lvalue, &a_attrs);
00853                                         if (a_attr < 0) {
00854                                                 break;
00855                                         }
00856                                         lend = lvalue.s + lvalue.len;
00857                                         lvalue.s += a_attrs[a_attr].len;
00858                                         switch (a_attr) {
00859                                                 case sdpaattr_rtpmap:
00860                                                         /* a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters>] , max.one a=rtpmap: per codec */
00861                                                 case sdpaattr_fmtp:
00862                                                         /* a=fmtp:<format/payload type> <format specific params>, max.one a=fmtp: per codec */
00863 
00864                                                         /* we validate only things important for us. Other thinkgs not important we leave up to UA. Not tested:
00865                                                            - payload order of a:rtpmap corresponds to m= line
00866                                                            - all dynamic payloads have cooresponding a:rtpmap line (for us it's unknown codec)
00867                                                            - if static payload type corresponds to codec, i.e. e.g. if 0 is PCMU
00868                                                          */
00869                                                         cp = eat_token_end(lvalue.s, lend);
00870                                                         lvalue.len = cp-lvalue.s;
00871                                                         savec = lvalue.s[lvalue.len];
00872                                                         lvalue.s[lvalue.len] = '\0';
00873                                                         payload_type = atol(lvalue.s);
00874                                                         lvalue.s[lvalue.len] = savec;
00875                                                         for (i=0; i < sess->media[sess->media_count-1].codec_count; i++) {
00876                                                                 if ((*sess->media[sess->media_count-1].codecs)[i].payload_type == payload_type) {
00877                                                                         goto found;
00878                                                                 }
00879                                                         }
00880                                                         ERR(MODULE_NAME": parse_sdp_content: '%.*s' payload type (%d) has not been mentioned at m= line\n", (int) (lend-line.s), line.s, payload_type);
00881                                                         return -1;
00882                                                 found:
00883                                                         cp = eat_space_end(cp, lend);
00884                                                         switch (a_attr) {
00885                                                                 case sdpaattr_rtpmap:
00886                                                                         if ((*sess->media[sess->media_count-1].codecs)[i].a_rtpmap_line_s.s) {
00887                                                                                 ERR(MODULE_NAME": parse_sdp_content: '%.*s' multiple a=rtpmap lines for payload type (%d)\n", (int) (lend-line.s), line.s, payload_type);
00888                                                                                 return -1;
00889 
00890                                                                         }
00891                                                                         (*sess->media[sess->media_count-1].codecs)[i].a_rtpmap_line_s = line;
00892                                                                         lvalue.s = cp;
00893                                                                         cp = eat_token2_end(cp, lend, '/');
00894                                                                         lvalue.len = cp-lvalue.s;
00895                                                                         (*sess->media[sess->media_count-1].codecs)[i].codec_id = name2codec_id(&lvalue, NULL);
00896                                                                         break;
00897                                                                 case sdpaattr_fmtp:
00898                                                                         if ((*sess->media[sess->media_count-1].codecs)[i].a_fmtp_line_s.s) {
00899                                                                                 ERR(MODULE_NAME": parse_sdp_content: '%.*s' multiple a=fmtp lines for payload type (%d)\n", (int) (lend-line.s), line.s, payload_type);
00900                                                                                 return -1;
00901                                                                         }
00902                                                                         (*sess->media[sess->media_count-1].codecs)[i].a_fmtp_line_s = line;
00903                                                                         break;
00904                                                                 default:
00905                                                                         break;
00906                                                         }
00907                                                         break;
00908                                                 case sdpaattr_rtcp:
00909                                                         /* a=rtcp: port [nettype space addrtype space connection-address] */
00910                                                         ERR(MODULE_NAME": parse_sdp_content: a=rtcp parameter is ignored '%.*s', RTCP relaying may fail\n", (int) (lend-line.s), line.s);
00911                                                         break;
00912                                                 default:
00913                                                         ;
00914                                         }
00915                                 }
00916                                 break;
00917 
00918                         default:
00919                                 ;
00920                 }
00921         }
00922         return 0;
00923 }
00924 
00925 /* simple wrapper to call parse_sdp_content() only once per request */
00926 static inline int check_parse_sdp_content(struct sip_msg* msg, struct sdp_session *sess) {
00927         switch (sdp_parsed) {
00928                 case -1:
00929                 case 0:
00930                         return sdp_parsed;
00931                 default:
00932                         sdp_parsed = parse_sdp_content(msg, sess);
00933                         return sdp_parsed;
00934         }
00935 }
00936 
00937 static int prepare_lumps(struct sip_msg* msg, str* position, str* s) {
00938         struct lump* anchor;
00939         char *buf;
00940 
00941         if (!position->s)
00942                 return 0;
00943 //ERR("'%.*s' --> '%.*s'\n", STR_FMT(position), STR_FMT(s));    
00944         anchor = del_lump(msg, position->s - msg->buf, position->len, 0);
00945         if (anchor == NULL) {
00946                 ERR(MODULE_NAME": prepare_lumps: del_lump failed\n");
00947                 return -1;
00948         }
00949         if (!s || !s->len) return 0;
00950 
00951         buf = pkg_malloc(s->len);
00952         if (buf == NULL) {
00953                 ERR(MODULE_NAME": prepare_lumps: out of memory\n");
00954                 return -1;
00955         }
00956         memcpy(buf, s->s, s->len);
00957         if (insert_new_lump_after(anchor, buf, s->len, 0) == 0) {
00958                 ERR(MODULE_NAME": prepare_lumps: insert_new_lump_after failed\n");
00959                 pkg_free(buf);
00960                 return -1;
00961         }
00962         return 0;
00963 }
00964 
00965 static int update_sdp_content(struct sip_msg* msg, int gate_a_to_b, struct sdp_session *sdp_sess, struct ipt_session *ipt_sess) {
00966         int i, j;
00967         str s;
00968         /* we must apply lumps for relevant c= and m= lines */
00969         global_params.sdp_ip.len = 0;
00970         for (i=0; i<sdp_sess->media_count; i++) {
00971                 if (sdp_sess->media[i].active) {
00972                         if (ipt_sess->sdp_media[i] < 0) {
00973                                 goto cline_fixed;
00974                         }
00975                         for (j=0; j<i; j++) {
00976                                 if (sdp_sess->media[j].active && sdp_sess->media[i].ip_s.s == sdp_sess->media[j].ip_s.s && ipt_sess->sdp_media[j] >= 0) {
00977                                         goto cline_fixed;
00978                                 }
00979                         }
00980                         if (global_params.sdp_ip.len == 0) {
00981                                 /* takes 1st ip to be rewritten, for aux purposes only */
00982                                 global_params.sdp_ip = sdp_sess->media[i].ip_s;
00983                         }
00984                         if (sdp_sess->media[i].ip != 0) {  /* we won't update 0.0.0.0 to anything because such a UA cannot receive. The session may be allocated and reused (unless expires) */
00985                                 /* apply lump for ip address in c= line */
00986                                 ip42s(ipt_sess->sessions[ipt_sess->sdp_media[i]].dir[!gate_a_to_b].switchboard.addr.ip, &s);
00987                                 if (prepare_lumps(msg, &sdp_sess->media[i].ip_s, &s) < 0)
00988                                         return -1;
00989                         }
00990         cline_fixed:
00991                         /* apply lump for port in m= line */
00992                         s.s = int2str((ipt_sess->sdp_media[i]<0)? 0/* disable stream */: ipt_sess->sessions[ipt_sess->sdp_media[i]].dir[!gate_a_to_b].stream[0].port, &s.len);
00993                         if (prepare_lumps(msg, &sdp_sess->media[i].port_s, &s) < 0)
00994                                 return -1;
00995                 }
00996         }
00997         /* do topo hiding if all media are disabled for c= line then set address 0.0.0.0 to hide UA location */
00998         for (i=0; i<sdp_sess->media_count; i++) {
00999                 if (sdp_sess->media[i].ip && (!sdp_sess->media[i].active || ipt_sess->sdp_media[i] < 0)) { /* not affected but previous loop */
01000                         for (j=0; j<i; j++) {
01001                                 if (sdp_sess->media[i].ip_s.s == sdp_sess->media[j].ip_s.s) {
01002                                         goto cline_fixed2;  /* must be already updated */
01003                                 }
01004                         }
01005                         for (j=i+1; j<sdp_sess->media_count; j++) {
01006                                 if (sdp_sess->media[i].ip_s.s == sdp_sess->media[j].ip_s.s && /* same c= line */
01007                                     sdp_sess->media[i].active && ipt_sess->sdp_media[i] >= 0) {  /* has media enabled */
01008                                         goto cline_fixed2;
01009                                 }
01010                         }
01011                         /* apply lump for ip address in c= line */
01012                         ip42s(0, &s);
01013                         if (prepare_lumps(msg, &sdp_sess->media[i].ip_s, &s) < 0)
01014                                 return -1;
01015                 cline_fixed2:
01016                         ;
01017                 }
01018         }
01019 
01020 
01021         if (sdp_sess->oline_addr_s.s) {  /* o= line exists */
01022                 if (global_params.oline_user.len) {
01023                         if (prepare_lumps(msg, &sdp_sess->oline_user_s, &global_params.oline_user) < 0)
01024                                 return -1;
01025                 }
01026                 if (global_params.oline_addr.len) {
01027                         if (prepare_lumps(msg, &sdp_sess->oline_addr_s, &global_params.oline_addr) < 0)
01028                                 return -1;
01029                 }
01030         }
01031         return 0;
01032 }
01033 
01034 /* null terminated result is allocated at static buffer */
01035 static void serialize_ipt_session(struct ipt_session* sess, str* session_ids) {
01036         static char buf[MAX_SWITCHBOARD_NAME_LEN+1+(5+1+1+10)*MAX_MEDIA_NUMBER+1];
01037         char *p;
01038         int i;
01039         buf[0] = '\0';
01040         p = buf;
01041         if (sess->sdp_media_count) {
01042                 if (sess->switchboard) {
01043                         memcpy(p, sess->switchboard->name.s, sess->switchboard->name.len);
01044                         p += sess->switchboard->name.len;
01045                 }
01046                 *p = ':';
01047                 p++;
01048                 for (i=0; i<sess->sdp_media_count; i++) {
01049                         if (sess->sdp_media[i] >= 0) {
01050                                 p += sprintf(p, "%u/%u", 
01051                                                 sess->sessions[sess->sdp_media[i]].dir[0].sess_id, 
01052                                                 sess->sessions[sess->sdp_media[i]].sh.created
01053                                         );
01054                         }
01055                         *p = ',';
01056                         p++;
01057                 }
01058                 p--;
01059                 *p = '\0';
01060         }
01061         session_ids->s = buf;
01062         session_ids->len = p - buf;
01063 }
01064 
01065 /* switchboardname [":" [sess_id "/" created] [ * ( "," [sess_id "/" created] )] ] */
01066 /* sessids are placed at dir[0] */
01067 static int unserialize_ipt_session(str* session_ids, struct ipt_session* sess) {
01068         char *p, *pend, savec;
01069         str s;
01070         unsigned int sess_id, created, i;
01071         
01072         memset(sess, 0, sizeof(*sess));
01073         if (session_ids->len == 0) {
01074                 return 0;
01075         }
01076         p = session_ids->s;
01077         pend = session_ids->s+session_ids->len;
01078         s.s = p;
01079         while (p < pend && is_alpha(*p)) p++;
01080         s.len = p-s.s;
01081         sess->switchboard = find_switchboard(&s, NULL);
01082         global_params.switchboard[0] = sess->switchboard;
01083         if (s.len && !sess->switchboard) {  /* empty switchboard is stored if all sessions are forced as disabled (port==0), SDP streams are empty but we need test medias later*/
01084                 ERR(MODULE_NAME": unserialize_ipt_session: '%.*s', switchboard '%.*s' not found\n", STR_FMT(session_ids), STR_FMT(&s));
01085                 return -1;
01086         }
01087         if (p == pend) return 0;
01088         if (*p != ':') {
01089                 ERR(MODULE_NAME": unserialize_ipt_session: '%.*s', colon expected near '%.*s'\n", STR_FMT(session_ids), PTR2INT(pend-p), p);
01090                 return -1;
01091         }
01092         do {
01093                 if (sess->sdp_media_count >= MAX_MEDIA_NUMBER) {
01094                 ERR(MODULE_NAME": unserialize_ipt_session: '%.*s', max.media number (%d) exceeded\n", STR_FMT(session_ids), MAX_MEDIA_NUMBER);
01095                         return -1;
01096                 }
01097                 sess->sdp_media[sess->sdp_media_count] = -1;
01098                 p++;            
01099                 if (p < pend && *p != ',') {
01100                         s.s = p;
01101                         while (p < pend && (*p >= '0' && *p <= '9')) p++;
01102                         s.len = p-s.s;
01103                         if (s.len == 0 || p == pend || *p != '/') {
01104                                 ERR(MODULE_NAME": unserialize_ipt_session: '%.*s', '/' expected near '%.*s'\n", STR_FMT(session_ids), PTR2INT(pend-p), p);
01105                                 return -1;
01106                         }
01107                         savec = s.s[s.len];
01108                         s.s[s.len] = '\0';
01109                         sess_id = atol(s.s);
01110                         s.s[s.len] = savec;
01111                         p++;
01112                         s.s = p;
01113                         while (p < pend && (*p >= '0' && *p <= '9')) p++;
01114                         s.len = p-s.s;
01115                         if (s.len == 0 || (p != pend && *p != ',')) {
01116                                 ERR(MODULE_NAME": unserialize_ipt_session: '%.*s', comma expected near '%.*s'\n", STR_FMT(session_ids), PTR2INT(pend-p), p);
01117                                 return -1;
01118                         }
01119                         savec = s.s[s.len];
01120                         s.s[s.len] = '\0';
01121                         created = atol(s.s);
01122                         s.s[s.len] = savec;
01123 
01124                         for (i=0; i<sess->sdp_media_count; i++) {
01125                                 if (sess->sdp_media[i] >= 0 && sess->sessions[sess->sdp_media[i]].dir[0].sess_id == sess_id) {
01126                                         if (sess->sessions[sess->sdp_media[i]].sh.created != created) {
01127                                                 ERR(MODULE_NAME": unserialize_ipt_session: '%.*s', sess-id/created mismatch '%u/(%u!=%u)'\n", 
01128                                                                 STR_FMT(session_ids), sess_id, sess->sessions[sess->sdp_media[i]].sh.created, created);
01129                                                 return -1;
01130                                         }
01131                                         sess->sdp_media[sess->sdp_media_count] = sess->sdp_media[i];
01132                                         goto cont;
01133                                 }
01134                         }
01135                         sess->sessions[sess->session_count].dir[0].switchboard.addr = sess->switchboard->switchboard_addr;
01136                         sess->sessions[sess->session_count].dir[0].sess_id = sess_id;
01137                         sess->sessions[sess->session_count].sh.created = created;
01138                         sess->sdp_media[sess->sdp_media_count] = sess->session_count;
01139                         sess->session_count++;
01140                 }
01141         cont:
01142                 sess->sdp_media_count++;
01143         } while (p < pend);
01144         return 0;
01145 }
01146 
01147 static inline int check_host_err(struct host_item *hi, int ret) {
01148         switch (hi->handle.err_no) {
01149                 case XT_RTPPROXY_ERR_CANNOT_OPEN_SOCKET:
01150                 case XT_RTPPROXY_ERR_RPC:
01151                         atomic_set_int(&hi->stat->last_error_stamp, (int) time(NULL));
01152                         break;
01153                 default:
01154                         atomic_set_int(&hi->stat->last_ok_stamp, (int) time(NULL));
01155         }
01156         return ret;
01157 }
01158 
01159 static inline int check_open_handle(struct host_item* hi) {
01160         if (!hi->handle_is_opened) {
01161                 if (hi->local) {
01162                         if (check_host_err(hi, xt_RTPPROXY_open(&hi->handle, xt_rtpproxy_LOCAL, NULL)) < 0) goto err;
01163                 } else {
01164                         if (check_host_err(hi, xt_RTPPROXY_open(&hi->handle, xt_rtpproxy_REMOTE, &hi->rpc_params)) < 0) goto err;
01165                 }
01166                 hi->handle_is_opened = 1;
01167         }
01168         return 0;
01169 err:
01170         ERR(MODULE_NAME": %s (%d)\n", hi->handle.err_str, hi->handle.err_no);
01171         return -1;
01172 }
01173 
01174 
01175 static void delete_ipt_sessions(struct host_item* hi, struct ipt_session* ipt_sess, struct ipt_session *ipt_surviving_sess) {
01176         int i;
01177         for (i=0; i < ipt_sess->session_count; i++) {
01178                 if (ipt_sess->switchboard == ipt_surviving_sess->switchboard) {                         
01179                         int j;
01180                         for (j=0; j < ipt_surviving_sess->session_count; j++) {
01181                                 if (ipt_sess->sessions[i].dir[0].sess_id == ipt_surviving_sess->sessions[j].dir[0].sess_id &&
01182                                         ipt_sess->sessions[i].sh.created == ipt_surviving_sess->sessions[j].sh.created  ) { /* we do non need test also created */
01183                                         goto skip_del;
01184                                 }
01185                         }
01186                 }
01187                 ipt_sess->sessions[i].sh.flags |= XT_RTPPROXY_SOCKOPT_FLAG_SESSION_DESTROY;
01188         skip_del: 
01189                 ;
01190         }
01191 
01192 //ERR("DEBUG_RTPPROXY: module: delete_ipt_sessions: xt_RTPPROXY_update_sessions(%d)\n", ipt_sess->session_count);
01193         if (check_host_err(hi, xt_RTPPROXY_update_sessions(&hi->handle, ipt_sess->session_count, &ipt_sess->sessions)) < 0) {
01194                 ERR(MODULE_NAME": delete_ipt_sessions: xt_RTPPROXY_update_session error: %s (%d)\n", hi->handle.err_str, hi->handle.err_no);
01195                 /* what to do ? */
01196         }
01197 }
01198 
01199 #define GATE_FLAG 0x01
01200 #define UPDATE_SDP_ONLY_FLAG 0x02
01201 
01202 /* gate_a_to_b has index 0, gate_b_to_a 1 */
01203 #define GATE_A_TO_B(flags) (((flags) & GATE_FLAG) == 0)
01204 
01205 /* SDP (sdp_session) -> ipt RTP proxy session [dir == 0] */
01206 inline static void fill_in_session(int flags, int media_idx, struct sdp_session *sdp_sess, struct xt_rtpproxy_sockopt_session *in_session) {
01207         int j;
01208         for (j=0; j<2; j++) {
01209                 if (sdp_sess) {
01210                         in_session->dir[GATE_A_TO_B(flags)].stream[j].flags |= XT_RTPPROXY_SOCKOPT_FLAG_SESSION_ADDR;
01211                         in_session->dir[GATE_A_TO_B(flags)].stream[j].source.ip = sdp_sess->media[media_idx].ip;
01212                         in_session->dir[GATE_A_TO_B(flags)].stream[j].source.port = sdp_sess->media[media_idx].port+j;
01213                 }
01214                 if (global_params.learning_timeout > 0) {
01215                         in_session->dir[GATE_A_TO_B(flags)].stream[j].flags |= XT_RTPPROXY_SOCKOPT_FLAG_SESSION_LEARNING_TIMEOUT;
01216                         in_session->dir[GATE_A_TO_B(flags)].stream[j].learning_timeout = global_params.learning_timeout; 
01217                 }
01218         }
01219         if (global_params.always_learn >= 0) {
01220                 in_session->dir[GATE_A_TO_B(flags)].always_learn = global_params.always_learn!=0;
01221                 in_session->dir[GATE_A_TO_B(flags)].flags |= XT_RTPPROXY_SOCKOPT_FLAG_ALWAYS_LEARN;
01222         }
01223         if (global_params.expiration_timeout > 0) {
01224                 in_session->sh.expires_timeout = global_params.expiration_timeout;
01225                 in_session->sh.flags |= XT_RTPPROXY_SOCKOPT_FLAG_SESSION_EXPIRES;
01226         }
01227         if (global_params.ttl >= 0) {
01228                 in_session->sh.ttl = global_params.ttl;
01229                 in_session->sh.flags |= XT_RTPPROXY_SOCKOPT_FLAG_SESSION_TTL;
01230         }
01231 }
01232 
01233 inline static void fill_in_session_throttle(int flags, int media_idx, struct xt_rtpproxy_sockopt_session *in_session) {
01234         int j;
01235         if (global_params.throttle.mark > 0) {
01236                 for (j=0; j<2; j++) {                   
01237                         in_session->dir[GATE_A_TO_B(flags)].stream[j].throttle.mark = global_params.throttle.mark;
01238                         in_session->dir[GATE_A_TO_B(flags)].stream[j].flags |= XT_RTPPROXY_SOCKOPT_FLAG_THROTTLE_MARK;
01239                 }
01240         }
01241         for (j=0; j<2; j++) {
01242                 if (global_params.codec_set && 
01243                     (global_params.codec_set->media_types[global_sdp_sess.media[media_idx].media_type].throttle.bandwidth[j].packets > 0 ||
01244                     global_params.codec_set->media_types[global_sdp_sess.media[media_idx].media_type].throttle.bandwidth[j].bytes > 0)
01245                    ) {
01246                         in_session->dir[GATE_A_TO_B(flags)].stream[j].throttle.max_bandwidth = global_params.codec_set->media_types[global_sdp_sess.media[media_idx].media_type].throttle.bandwidth[j];
01247                         in_session->dir[GATE_A_TO_B(flags)].stream[j].flags |= XT_RTPPROXY_SOCKOPT_FLAG_THROTTLE_BANDWIDTH;
01248                 } else if (global_params.throttle.bandwidth[j].bytes > 0 || global_params.throttle.bandwidth[j].packets > 0 ) {
01249                         in_session->dir[GATE_A_TO_B(flags)].stream[j].throttle.max_bandwidth = global_params.throttle.bandwidth[j];
01250                         in_session->dir[GATE_A_TO_B(flags)].stream[j].flags |= XT_RTPPROXY_SOCKOPT_FLAG_THROTTLE_BANDWIDTH;
01251 
01252                 }
01253         }
01254 }
01255 
01256 static int rtpproxy_alloc(struct sip_msg* msg, char* _flags, char* _dummy) {
01257         int flags;
01258         struct ipt_session ipt_sess;
01259         struct host_item* hi = NULL;
01260         struct xt_rtpproxy_switchboard_id aggregated_switchboards[MAX_AGGREGATED_NUMBER]; 
01261         time_t stamp;
01262         
01263         xt_rtpproxy_sockopt_count cnt[2];
01264         str s;
01265         int i, aggr_fl, reuse_existing_count;
01266 
01267         if (get_int_fparam(&flags, msg, (fparam_t*) _flags) < 0) {
01268                 return -1;
01269         }
01270         if (check_parse_sdp_content(msg, &global_sdp_sess) < 0) return -1;
01271 
01272 ERR("RTPPROXY_DEBUG: sdp.media_count: %d, flags: %d\n", global_sdp_sess.media_count, flags);
01273         if (global_params.protected_sess.switchboard) {  /* any protected ? */
01274                 /* get session source address from kernel module and compare with SDP content */
01275                 for (i = 0; i < global_params.protected_sess.session_count; i++) {
01276                         global_params.protected_sess.sessions[i].sh.flags |= XT_RTPPROXY_SOCKOPT_FLAG_SESSION_INFO;
01277                 }
01278                 if (check_open_handle(global_params.protected_sess.switchboard->host) < 0) {
01279                         return -1;
01280                 }
01281 ERR("RTPPROXY_DEBUG: xt_RTPPROXY_update_sessions(sess#:%d, sdp#:%d, XT_RTPPROXY_SOCKOPT_FLAG_SESSION_INFO)\n", global_params.protected_sess.session_count, global_params.protected_sess.sdp_media_count);
01282                 if (check_host_err(global_params.protected_sess.switchboard->host, xt_RTPPROXY_update_sessions(&global_params.protected_sess.switchboard->host->handle, global_params.protected_sess.session_count, &global_params.protected_sess.sessions)) < 0) {
01283                         ERR(MODULE_NAME": rtpproxy_alloc: xt_RTPPROXY_update_session error when retrieving sessions: %s (%d)\n", 
01284                                 global_params.protected_sess.switchboard->host->handle.err_str, 
01285                                 global_params.protected_sess.switchboard->host->handle.err_no
01286                         );
01287                         return -1;
01288                 }
01289         }
01290 
01291         reuse_existing_count = 0;
01292         memset(&ipt_sess, 0, sizeof(ipt_sess));
01293         for (i = 0; i < global_sdp_sess.media_count; i++) {
01294                 ipt_sess.sdp_media[i] = -1;
01295                 if (global_sdp_sess.media[i].active) {
01296                         int j;
01297                         for (j = 0; j < i; j++) {
01298                                 /* if two media streams have equal source address than we will allocate only one ipt session */
01299                                 if (global_sdp_sess.media[j].active) {
01300                                         if (global_sdp_sess.media[i].ip == global_sdp_sess.media[j].ip && global_sdp_sess.media[i].port == global_sdp_sess.media[j].port) {
01301                                                 if (global_sdp_sess.media[i].ip != 0) {
01302                                                         ipt_sess.sdp_media[i] = ipt_sess.sdp_media[j];
01303                                                         goto cont;                                              
01304                                                 } else if (i < global_params.protected_sess.sdp_media_count) {
01305                                                         int k, l;
01306                                                         k = global_params.protected_sess.sdp_media[i];
01307                                                         l = global_params.protected_sess.sdp_media[j];
01308                                                         if ((global_params.protected_sess.sessions[k].sh.flags & XT_RTPPROXY_SOCKOPT_FLAG_NOT_FOUND) == 0 &&
01309                                                             global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.ip == global_params.protected_sess.sessions[l].dir[GATE_A_TO_B(flags)].stream[0].source.ip &&
01310                                                         global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.port == global_params.protected_sess.sessions[l].dir[GATE_A_TO_B(flags)].stream[0].source.port) {
01311  
01312                                                                 /* if ip == 0, for example phone goes on-hold we'll take IP from protected sessions if possible */
01313                                                                 ipt_sess.sdp_media[i] = ipt_sess.sdp_media[j];
01314                                                                 goto cont;
01315                                                         }
01316                                                 }
01317                                         }
01318                                 }
01319                         }
01320                         /* if there are existing sessions then we take those instead of allocation new ones */
01321                         /* we can match 1:1 existing media streams against SDP sessions provided by SDP */
01322                         if (i < global_params.protected_sess.sdp_media_count) {
01323                                 int k;
01324                                 k = global_params.protected_sess.sdp_media[i];
01325 ERR("RTPPROXY_DEBUG: protected.sess media:%d -> sess:%d, flags: %d\n", i, k, global_params.protected_sess.sessions[k].sh.flags);
01326                                 if ((global_params.protected_sess.sessions[k].sh.flags & XT_RTPPROXY_SOCKOPT_FLAG_NOT_FOUND) == 0) {
01327                                         switch (global_params.protected_sess.sessions[k].sh.state) {
01328                                                 case xt_rtpproxy_INIT1:
01329                                                 case xt_rtpproxy_INIT2: 
01330                                                 case xt_rtpproxy_FORWARD1:
01331                                                 case xt_rtpproxy_FORWARD2:
01332                                                         /* is original ip:port of existion session equal to ip:port provided by SDP ? RTP test is sufficient */
01333                                                         /* Workaround: if a phone (Sipura, X-Lite, ...) goes on-hold then
01334                                                         c= line address is set to 0.0.0.0. It's not correct because RTCP media
01335                                                         can't tricle. It would not force new RTP session allocation. 
01336                                                         So we won't update SDP c= line but get sees_id from protected sess to reuse it when on-hold terminates.
01337                                                         But when on-hold is too long and session expires then new session will be allocated */
01338 ERR("DEBUG_RTPPROXY: module: CMP %x=%x & %d=%d\n", global_sdp_sess.media[i].ip, global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.ip, global_sdp_sess.media[i].port, global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.port);
01339                                                         if ((global_sdp_sess.media[i].ip == 0 || 
01340                                                              global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.ip == 0 ||
01341                                                              global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.ip == global_sdp_sess.media[i].ip) &&
01342                                                             /* global_sdp_sess.media[i].port always because active && */  
01343                                                             global_params.protected_sess.sessions[k].dir[GATE_A_TO_B(flags)].stream[0].source.port == global_sdp_sess.media[i].port) {
01344                                                                 /* keep all reused sess at the beginning of list, i.e. make slot */
01345 ERR("RTPPROXY_DEBUG: REUSE!\n");
01346                                                                 for (j=ipt_sess.session_count; j > 0; j--) {    
01347                                                                         ipt_sess.sessions[j] = ipt_sess.sessions[j-1];                                                                  
01348                                                                 }
01349                                                                 for (j=0; j < i; j++) {
01350                                                                         if (ipt_sess.sdp_media[j] >= 0) {
01351                                                                                 ipt_sess.sdp_media[j]++;
01352                                                                         }
01353                                                                 }
01354                                                                 /* put it at slot [0], copy data from existing session */
01355                                                                 for (j=0; j<2; j++) {
01356                                                                         ipt_sess.sessions[0].dir[j] = global_params.protected_sess.sessions[k].dir[j];
01357                                                                 }
01358                                                                 ipt_sess.sessions[0].sh = global_params.protected_sess.sessions[k].sh;
01359 
01360                                                                 ipt_sess.sdp_media[i] = 0;
01361                                                                 reuse_existing_count++;
01362                                                                 goto skip_fill;
01363                                                         }
01364                                                         break;
01365                                                 default:
01366                                                         ;
01367                                         }
01368                                 }
01369 
01370                         }
01371                         if (global_sdp_sess.media[i].ip == 0) {
01372                                 switch (global_sdp_sess.media[i].send_rec_modifier) {
01373                                 case sdpaattr_sendonly:
01374                                 case sdpaattr_sendrecv: /* it's error because it cannot receive anything but client are weird */
01375                                         break;  /* they can send RTP/RTCP, not recommended in RFC3264, maybe allow only when learning possible */
01376                                 default:
01377                                         /* do not allocate session for on-hold stream unless reused, disable stream (sdp_media[i]) < 0 */
01378 ERR("DEBUG_RTPPROXY: module: do not allocate session for on-hold stream unless reused\n");
01379                                         goto cont;
01380                                 }
01381                         }
01382                         fill_in_session(flags, i, &global_sdp_sess, ipt_sess.sessions+ipt_sess.session_count);
01383                         fill_in_session_throttle(flags, i, ipt_sess.sessions+ipt_sess.session_count);
01384                         ipt_sess.sdp_media[i] = ipt_sess.session_count;
01385                 skip_fill:
01386                         ipt_sess.session_count++;
01387                 }
01388         cont:
01389                 ;
01390         }
01391         ipt_sess.sdp_media_count = global_sdp_sess.media_count;
01392 
01393 ERR("RTPPROXY_DEBUG: session_count: %d, reuse_existing_count: %d\n", global_sdp_sess.media_count, reuse_existing_count);
01394 
01395         if (ipt_sess.session_count > reuse_existing_count) {
01396                 stamp = time(NULL);
01397                 if (reuse_existing_count > 0) {
01398                         /* we need allocate sessions at the same switchboard as already being existed */
01399                         aggr_fl = 0;
01400                         hi = global_params.protected_sess.switchboard->host;
01401                         ipt_sess.switchboard = global_params.protected_sess.switchboard;
01402                         for (i=reuse_existing_count; i < ipt_sess.session_count; i++) {
01403                                 int j;
01404                                 for (j=0; j<2; j++) {
01405                                         ipt_sess.sessions[i].dir[j].switchboard.addr = ipt_sess.sessions[0].dir[j].switchboard.addr;
01406                                 }
01407                         }
01408                 } else {
01409                         for (i=0; i<2; i++) {
01410                                 if (!global_params.switchboard[i] && !global_params.aggregation[i]) {
01411                                         ERR(MODULE_NAME": rtpproxy_alloc: aggregation/switchboard not set (dir:%d)\n", i);
01412                                         return -1;
01413                                 }
01414                         }
01415                         aggr_fl = global_params.aggregation[0] || global_params.aggregation[1];
01416                         if (aggr_fl) {
01417                                 struct switchboard_item *si;
01418                                 /* calculate switchboard weights. There is minor problem when weight are calculated some time before
01419                                    RPC commands are performed, i.e. if a remote RPC server become unavailable then more processes
01420                                    may spend time waiting for unresponsive machine even it's been discovered by parallel process.
01421                                 */
01422                                 for (si=switchboards; si; si=si->next) {
01423                                         unsigned int w;
01424                                         int a, f;
01425                                         time_t ok_stamp, err_stamp;
01426                                         a = f = 0;
01427                                         ok_stamp = atomic_get_int(&si->host->stat->last_ok_stamp);
01428                                         err_stamp = atomic_get_int(&si->host->stat->last_error_stamp);
01429                                         if (rpc_heartbeat_timeout > 0 && err_stamp > ok_stamp && (stamp-err_stamp) >= rpc_heartbeat_timeout) {
01430                                                 /* set max. priority to force remote rtpproxy rpc call, i.e. test if is alive or dead */
01431                                                 ok_stamp = err_stamp = 0;                               
01432                                         }
01433                                         if (err_stamp > ok_stamp) {
01434                                                 /* lowest priority */
01435                                                 /* prefer older error */
01436                                                 w = time(NULL) - err_stamp + 1;
01437                                                 if (w > 999) w = 999;
01438 
01439                                         } else if (ok_stamp == 0) {
01440                                                 /* not yet acquired, highest */
01441                                                 w = 100000000 + (rand() & 0xFFFF);  /* randomize not yet asked or being hartbeated */
01442                                         } else {
01443                                                 /* middle */
01444                                                 w = 1000;
01445                                                 a = atomic_get(&si->stat->alloc);
01446                                                 f = atomic_get(&si->stat->free);
01447                                                 if ((a + f) > 0) {
01448                                                         /* prefer switchboards having more free slots */
01449                                                         w += (1000*f)/(a+f);
01450                                                 }
01451                                         }
01452                                         si->weight = w;
01453                 //ERR(MODULE_NAME": rtpproxy_alloc: switchboard '%.*s' (ok_stamp: %u, err_stamp: %u, alloc: %u, free: %u, weight: %u)\n", STR_FMT(&si->name), (unsigned int) ok_stamp, (unsigned int) err_stamp, a, f, w);
01454                                 }
01455                                 hi = NULL;
01456                         }
01457                         else {
01458                                 if (global_params.switchboard[0]->host != global_params.switchboard[1]->host) {
01459                                         ERR(MODULE_NAME": rtpproxy_alloc: switchboard resides of different hosts '%.*s'!='%.*s'\n", 
01460                                                 STR_FMT(&global_params.switchboard[0]->host->name),
01461                                                 STR_FMT(&global_params.switchboard[1]->host->name)
01462                                         );
01463                                         return -1;
01464                                 }
01465                                 hi = global_params.switchboard[0]->host;
01466                                 for (i=0; i < ipt_sess.session_count; i++) {
01467                                         int j;
01468                                         for (j=0; j<2; j++) {
01469                                                 ipt_sess.sessions[i].dir[j].switchboard.addr = global_params.switchboard[j]->switchboard_addr;
01470                                         }
01471                                 }
01472                         }
01473                 }
01474         try_next_host:
01475                 cnt[0] = cnt[1] = 0;
01476                 if (aggr_fl) {
01477                         int j;
01478                         hi = NULL;
01479                         if (global_params.aggregation[0] && global_params.aggregation[1]) {
01480                                 int w = 0;
01481                                 /* find switchboard having max. weight */
01482                                 for (i=0; i<global_params.aggregation[0]->switchboard_count; i++) {
01483                                         if ((*global_params.aggregation[0]->switchboards)[i]->weight > w) { /* weight==0 is skipped */
01484                                                 time_t err_stamp;
01485                                                 err_stamp = atomic_get_int(&(*global_params.aggregation[0]->switchboards)[i]->host->stat->last_error_stamp);
01486                                                 if (err_stamp >= stamp) {
01487                                                         if (w > 0) continue;
01488                                                         /* decrease weight to minimum, parallel process meanwhile got error */
01489                                                         w = 1;
01490                                                 } else {
01491                                                         w = (*global_params.aggregation[0]->switchboards)[i]->weight;
01492                                                 }
01493                                                 hi = (*global_params.aggregation[0]->switchboards)[i]->host;
01494                                         }
01495                                 }
01496                         } else {
01497                                 for (j=0; j<2; j++) {
01498                                         if (!global_params.aggregation[j] && global_params.switchboard[j]->weight) {
01499                                                 hi = global_params.switchboard[j]->host;
01500                                         }
01501                                 }
01502                         }
01503                         if (!hi) {
01504                                 ERR(MODULE_NAME": rtpproxy_alloc: cannot allocate aggregated switchboard (#1)\n");
01505                                 return -1;
01506                         }
01507                         for (j=0; j<2; j++) {
01508                                 if (global_params.aggregation[j]) {                             
01509                                         struct switchboard_item *aggr_switchboards[MAX_AGGREGATED_NUMBER];
01510                                         for (i=0; i<global_params.aggregation[j]->switchboard_count; i++) {
01511                                                 if ((*global_params.aggregation[j]->switchboards)[i]->weight && 
01512                                                     (*global_params.aggregation[j]->switchboards)[i]->host == hi) {
01513                                                         int k, l;
01514                                                         if (cnt[0]+cnt[1] >= MAX_AGGREGATED_NUMBER) {
01515                                                                 ERR(MODULE_NAME": rtpproxy_alloc: number of aggregated switchboard exceeded limit %d\n", MAX_AGGREGATED_NUMBER);
01516                                                                 return -1;
01517                                                         }
01518                                                         /* put switchboard ordered by weight */
01519                                                         for (k=0; k<cnt[j] && aggr_switchboards[k]->weight >= (*global_params.aggregation[j]->switchboards)[i]->weight; k++);           
01520                                                         for (l=cnt[j]; l>k; l--) {
01521                                                                 aggr_switchboards[l] = aggr_switchboards[l-1];
01522                                                         }
01523                                                         aggr_switchboards[k] = (*global_params.aggregation[j]->switchboards)[i];
01524                                                         cnt[j]++;
01525                                                 }
01526                                         }
01527                                         if (!cnt[j]) {
01528                                                 ERR(MODULE_NAME": rtpproxy_alloc: cannot allocate aggregated switchboard (#2)\n");
01529                                                 return -1;
01530                                         }
01531                                         for (i = 0; i < cnt[j]; i++) {
01532                                                 aggregated_switchboards[j*cnt[0]+i] = aggr_switchboards[i]->switchboard_addr;
01533                                         }
01534                                 }
01535                                 else {
01536                                         if (cnt[0]+cnt[1] >= MAX_AGGREGATED_NUMBER) {
01537                                                 ERR(MODULE_NAME": rtpproxy_alloc: number of aggregated switchboard exceeded limit %d\n", MAX_AGGREGATED_NUMBER);
01538                                                 return -1;
01539                                         }
01540                                         aggregated_switchboards[cnt[0]+cnt[1]] = global_params.switchboard[j]->switchboard_addr;
01541                                         cnt[j]++;
01542                                 }
01543                         }
01544                         for (j=0; j<2; j++) {
01545                                 if (global_params.aggregation[j]) {                             
01546                                         for (i=0; i<global_params.aggregation[j]->switchboard_count; i++) {
01547                                                 if ((*global_params.aggregation[j]->switchboards)[i]->host == hi) {
01548                                                         /* done, do not process in next round again */
01549                                                         (*global_params.aggregation[j]->switchboards)[i]->weight = 0;
01550                                                 }
01551                                         }
01552                                 }
01553                                 else {
01554                                         global_params.switchboard[j]->weight = 0;
01555                                 }
01556                         }
01557                 }
01558 
01559                 if (reuse_existing_count < ipt_sess.session_count) {  /* allocation required ? */
01560                         if (check_open_handle(hi) < 0) {
01561                                 if (aggr_fl) {
01562                                         goto try_next_host;
01563                                 }
01564                                 return -1;
01565                         }
01566                 ERR("DEBUG_RTPPROXY: module: rtpproxy_alloc: xt_RTPPROXY_alloc_sessions(%d/%d/%d), host: '%.*s', flags: %d\n", cnt[0], cnt[1], ipt_sess.session_count, STR_FMT(&hi->name), flags);
01567                         if (check_host_err(hi, xt_RTPPROXY_alloc_sessions(&hi->handle,
01568                                                 cnt[0],
01569                                                 &aggregated_switchboards, 
01570                                                 cnt[1],
01571                                                 (void*) &aggregated_switchboards[cnt[0]],
01572                                                 ipt_sess.session_count-reuse_existing_count,  /* allocate only non-reused sessions */
01573                                                 &ipt_sess.sessions+reuse_existing_count
01574                                 )) < 0) {
01575                                 ERR(MODULE_NAME": rtpproxy_alloc: xt_RTPPROXY_alloc_session error: %s (%d)\n", hi->handle.err_str, hi->handle.err_no);
01576                                 if (aggr_fl) {
01577                                         goto try_next_host;
01578                                 }
01579                                 return -1;
01580                         }
01581                 }
01582         }
01583         if (update_sdp_content(msg, GATE_A_TO_B(flags), &global_sdp_sess, &ipt_sess) < 0) {
01584                 delete_ipt_sessions(hi, &ipt_sess, &global_params.protected_sess);
01585                 return -1;
01586         }
01587         if (ipt_sess.session_count) {
01588                 ipt_sess.switchboard = find_switchboard_by_addr(&ipt_sess.sessions[0].dir[0].switchboard.addr);
01589                 if (!ipt_sess.switchboard) {
01590                         BUG(MODULE_NAME": rtpproxy_alloc: switchboard-a definition not found\n");
01591                         return -1;
01592                 }
01593         //ERR("DEBUG_RTPPROXY: module: rtpproxy_alloc: switchboard-a '%.*s'\n", STR_FMT(&ipt_sess.switchboard->name));
01594                 global_params.switchboard[0] = ipt_sess.switchboard;
01595                 global_params.switchboard[1] = find_switchboard_by_addr(&ipt_sess.sessions[0].dir[1].switchboard.addr); 
01596                 if (!global_params.switchboard[1]) {
01597                         BUG(MODULE_NAME": rtpproxy_alloc: switchboard-b definition not found\n");
01598                         return -1;
01599                 }
01600         //ERR("DEBUG_RTPPROXY: module: rtpproxy_alloc: switchboard-b '%.*s'\n", STR_FMT(&global_params.switchboard[1]->name));
01601                 atomic_set(&global_params.switchboard[0]->stat->free, ipt_sess.sessions[0].dir[0].switchboard.free);
01602                 atomic_set(&global_params.switchboard[0]->stat->alloc, ipt_sess.sessions[0].dir[0].switchboard.alloc);
01603                 if (global_params.switchboard[0] != global_params.switchboard[1]) {
01604                         atomic_set(&global_params.switchboard[1]->stat->free, ipt_sess.sessions[0].dir[1].switchboard.free);
01605                         atomic_set(&global_params.switchboard[1]->stat->alloc, ipt_sess.sessions[0].dir[1].switchboard.alloc);
01606                 }
01607         }
01608         else {
01609                 ipt_sess.switchboard = global_params.protected_sess.switchboard; /* we need still keep the same switchboard */
01610                 global_params.switchboard[0] = ipt_sess.switchboard;
01611         }
01612         serialize_ipt_session(&ipt_sess, &s);
01613         global_params.session_ids = s; /* it's static and null terminated */
01614         return 1;
01615 }
01616 
01617 static int rtpproxy_update(struct sip_msg* msg, char* _flags, char* _session_ids) {
01618         str session_ids;
01619         int flags, i;
01620         struct ipt_session ipt_sess;
01621 
01622         if (get_int_fparam(&flags, msg, (fparam_t*) _flags) < 0) {
01623                 return -1;
01624         }
01625         if (get_str_fparam(&session_ids, msg, (fparam_t*) _session_ids) < 0) {
01626                 return -1;
01627         }
01628         if (unserialize_ipt_session(&session_ids, &ipt_sess) < 0) {
01629                 return -1;
01630         }
01631         if (check_parse_sdp_content(msg, &global_sdp_sess) < 0) return -1;
01632 
01633         if (ipt_sess.sdp_media_count != global_sdp_sess.media_count) {
01634                 ERR(MODULE_NAME": rtpproxy_update: number of m= item in offer (%d) and answer (%d) do not correspond\n", ipt_sess.sdp_media_count, global_sdp_sess.media_count);
01635                 return -1;
01636         }
01637         /* first we check for unexpected duplicate source ports */
01638         for (i = 0; i < global_sdp_sess.media_count; i++) {
01639                 if (ipt_sess.sdp_media[i] >= 0 && global_sdp_sess.media[i].active) {
01640                         int j;
01641                         for (j = i+1; j < global_sdp_sess.media_count; j++) {
01642                                 if (ipt_sess.sdp_media[j] >= 0 && global_sdp_sess.media[j].active) {
01643                                         /* if two media streams have equal source address XOR have equal session */
01644                                         if ( (global_sdp_sess.media[i].ip == global_sdp_sess.media[j].ip && global_sdp_sess.media[i].port == global_sdp_sess.media[j].port) ^
01645                                                  (ipt_sess.sdp_media[i] == ipt_sess.sdp_media[j]) ) {
01646                                                 ERR(MODULE_NAME": rtpproxy_update: media (%d,%d) violation number\n", i, j);
01647                                                 return -1;
01648                                         }
01649                                 }
01650                         }
01651                 }
01652         }
01653 
01654         if (flags & UPDATE_SDP_ONLY_FLAG) {
01655                 /* get session source address from kernel module, do not update RTP session, only updateSDP content */
01656                 for (i = 0; i < ipt_sess.session_count; i++) {
01657                         ipt_sess.sessions[i].sh.flags |= XT_RTPPROXY_SOCKOPT_FLAG_SESSION_INFO;
01658                 }
01659         } else {
01660                 /* first we check sessions to delete, the sessions can be "undeleted" if other media still uses session */
01661                 for (i = 0; i < global_sdp_sess.media_count; i++) {
01662                         if (ipt_sess.sdp_media[i] >= 0) {
01663                                 if (!global_sdp_sess.media[i].active) {
01664                                         ipt_sess.sessions[ipt_sess.sdp_media[i]].sh.flags |= XT_RTPPROXY_SOCKOPT_FLAG_SESSION_DESTROY;
01665                                         ipt_sess.sdp_media[i] = -1;
01666                                 }
01667                         }
01668                 }
01669 
01670                 for (i = 0; i < global_sdp_sess.media_count; i++) {
01671                         if (ipt_sess.sdp_media[i] >= 0) {
01672                                 if (global_sdp_sess.media[i].active) {
01673                                         fill_in_session(flags, i, &global_sdp_sess, ipt_sess.sessions+ipt_sess.sdp_media[i]);
01674                                         fill_in_session_throttle(flags, i, ipt_sess.sessions+ipt_sess.sdp_media[i]);
01675                                         ipt_sess.sessions[ipt_sess.sdp_media[i]].sh.flags &= ~XT_RTPPROXY_SOCKOPT_FLAG_SESSION_DESTROY;
01676                                 }
01677                         }
01678                 }
01679                 /* we cannot also delete sessions which have been reused from other session set */
01680                 for (i = 0; i < ipt_sess.session_count; i++) {
01681                         if (ipt_sess.sessions[i].sh.flags & XT_RTPPROXY_SOCKOPT_FLAG_SESSION_DESTROY) {
01682                                 if (ipt_sess.switchboard == global_params.protected_sess.switchboard) {
01683                                         int j;
01684                                         for (j=0; j < global_params.protected_sess.session_count; j++) {
01685                                                 if (ipt_sess.sessions[i].dir[0].sess_id == global_params.protected_sess.sessions[j].dir[0].sess_id &&
01686                                                         ipt_sess.sessions[i].sh.created == global_params.protected_sess.sessions[j].sh.created  ) {                                             
01687                                                         ipt_sess.sessions[i].sh.flags &= ~XT_RTPPROXY_SOCKOPT_FLAG_SESSION_DESTROY;
01688                                                         ipt_sess.sessions[i].sh.flags |= XT_RTPPROXY_SOCKOPT_FLAG_SESSION_INFO;
01689                                                         /* or we can remove from sessions TODO */
01690                                                         break;
01691                                                 }
01692                                         }
01693                                 }
01694                         }
01695                 }
01696         }
01697 
01698 //ERR("DEBUG_RTPPROXY: module: rtpproxy_update: xt_RTPPROXY_update_sessions(%d), flags:%d, switchboard:%p, sess:%.*s\n", ipt_sess.session_count, flags, ipt_sess.switchboard, STR_FMT(&session_ids));
01699         global_params.switchboard[0] = ipt_sess.switchboard;
01700         if (ipt_sess.switchboard) {
01701                 if (check_open_handle(ipt_sess.switchboard->host) < 0) {
01702                         return -1;
01703                 }
01704                 if (check_host_err(ipt_sess.switchboard->host, xt_RTPPROXY_update_sessions(&ipt_sess.switchboard->host->handle, ipt_sess.session_count, &ipt_sess.sessions)) < 0) {
01705                         ERR(MODULE_NAME": rtpproxy_update: xt_RTPPROXY_update_session error: %s (%d)\n", 
01706                                         ipt_sess.switchboard->host->handle.err_str, 
01707                                         ipt_sess.switchboard->host->handle.err_no
01708                         );
01709                                 /* delete all sessions ? */
01710                         return -1;
01711                 }
01712                 global_params.switchboard[1] = find_switchboard_by_addr(&ipt_sess.sessions[0].dir[1].switchboard.addr);
01713         } else {
01714                 /* disable media from answer too as we did it in request, port = 0 */
01715                 global_params.switchboard[1] = NULL;
01716         }
01717         if (update_sdp_content(msg, GATE_A_TO_B(flags), &global_sdp_sess, &ipt_sess) < 0) {
01718                 /* delete all sessions ? */
01719                 return -1;
01720         }
01721         serialize_ipt_session(&ipt_sess, &session_ids);
01722         global_params.session_ids = session_ids; /* it's static and null terminated */
01723         return 1;
01724 }
01725 
01726 static int rtpproxy_adjust_timeout(struct sip_msg* msg, char* _flags, char* _session_ids) {
01727         str session_ids;
01728         int flags, i;
01729         struct ipt_session ipt_sess;
01730 
01731         if (get_int_fparam(&flags, msg, (fparam_t*) _flags) < 0) {
01732                 return -1;
01733         }
01734         if (get_str_fparam(&session_ids, msg, (fparam_t*) _session_ids) < 0) {
01735                 return -1;
01736         }
01737         if (unserialize_ipt_session(&session_ids, &ipt_sess) < 0) {
01738                 return -1;
01739         }
01740         if (!ipt_sess.switchboard) {
01741                 return 1;
01742         }
01743         for (i = 0; i < ipt_sess.sdp_media_count; i++) {
01744                 if (ipt_sess.sdp_media[i] >= 0) {
01745                         fill_in_session(flags, i, NULL, ipt_sess.sessions+ipt_sess.sdp_media[i]);
01746                         /* throttle not affected */
01747                 }
01748         }
01749 //ERR("DEBUG_RTPPROXY: module: rtpproxy_adjust_timeout: xt_RTPPROXY_update_sessions(%d), flags:%d, sess:%.*s\n", ipt_sess.session_count, flags, STR_FMT(&session_ids));
01750         if (check_open_handle(ipt_sess.switchboard->host) < 0) {
01751                 return -1;
01752         }
01753         if (check_host_err(ipt_sess.switchboard->host, xt_RTPPROXY_update_sessions(&ipt_sess.switchboard->host->handle, ipt_sess.session_count, &ipt_sess.sessions)) < 0) {
01754                 ERR(MODULE_NAME": rtpproxy_adjust_timeout: xt_RTPPROXY_adjust_timeout error: %s (%d)\n", 
01755                                 ipt_sess.switchboard->host->handle.err_str, 
01756                                 ipt_sess.switchboard->host->handle.err_no
01757                 );
01758                 return -1;
01759         }
01760         /* do not serialize sessions because it affect static buffer and more valuable values disappears */
01761         return 1;
01762 }
01763 
01764 static int rtpproxy_delete(struct sip_msg* msg, char* _session_ids, char* _dummy) {
01765         str session_ids;
01766         struct ipt_session ipt_sess;
01767         if (get_str_fparam(&session_ids, msg, (fparam_t*) _session_ids) < 0) {
01768                 return -1;
01769         }
01770         if (!session_ids.len) return 1;
01771         if (unserialize_ipt_session(&session_ids, &ipt_sess) < 0) {
01772                 return -1;
01773         }
01774         
01775 //ERR("DEBUG_RTPPROXY: module: rtpproxy_delete: sess:%.*s\n", STR_FMT(&session_ids));
01776         if (!ipt_sess.switchboard) {
01777                 return 1;  /* nothing to delete */
01778         }
01779         if (check_open_handle(ipt_sess.switchboard->host) < 0) {
01780                 return -1;
01781         }
01782         delete_ipt_sessions(ipt_sess.switchboard->host, &ipt_sess, &global_params.protected_sess);
01783         /* do not serialize sessions because it affect static buffer and more valuable values disappears */
01784         return 1;
01785 }
01786 
01787 static int rtpproxy_authorize_media(struct sip_msg* msg, char* _dummy1, char* _dummy2) {
01788         unsigned int media_count[MAX_MEDIA_NUMBER];
01789         int i;
01790         if (!global_params.codec_set) return 1;
01791 
01792         if (check_parse_sdp_content(msg, &global_sdp_sess) < 0) return -1;
01793         global_params.auth_rights = 0;
01794         memset(&media_count, 0, sizeof(media_count));
01795 
01796         for (i=0; i<global_sdp_sess.media_count; i++) {
01797                 int j, n, fl;
01798                 if (global_sdp_sess.media[i].active != 1) continue;             
01799                 n = 0;
01800                 fl = media_count[global_sdp_sess.media[i].media_type] == global_params.codec_set->media_types[global_sdp_sess.media[i].media_type].throttle.max_streams;
01801                 if (fl) {
01802                         goto remove_stream;
01803                 }
01804                 for (j=0; j<global_sdp_sess.media[i].codec_count; j++) { 
01805                         unsigned int r;
01806                         struct sdp_codec *c;
01807                         c = &(*global_sdp_sess.media[i].codecs)[j];
01808                         /* codec has been already removed */
01809                         if (!c->mline_payload_type_s.s) continue;
01810                         r = (*global_params.codec_set->media_types[global_sdp_sess.media[i].media_type].codec_rights)[c->codec_id];
01811                         if (r) {
01812                                 if (r > global_params.auth_rights) {
01813                                         global_params.auth_rights = r;
01814                                 }
01815                                 if (r & global_params.remove_codec_mask) {
01816                                         /* remove codec */
01817                                         n++;
01818                                         if (n < global_sdp_sess.media[i].codec_count) {
01819                                                 /* if it's last remainng codec then leave it and remove stream */
01820                                                 if (prepare_lumps(msg, &c->mline_payload_type_s, NULL) < 0)
01821                                                         return -1;
01822                                                 c->mline_payload_type_s.s = NULL;  /* mark as removed */
01823                                                 if (prepare_lumps(msg, &c->a_rtpmap_line_s, NULL) < 0)
01824                                                         return -1;
01825                                                 if (prepare_lumps(msg, &c->a_fmtp_line_s, NULL) < 0)
01826                                                         return -1;
01827                                         }
01828                                 }                               
01829                         }
01830                 }
01831         remove_stream:
01832                 if (n == global_sdp_sess.media[i].codec_count || fl) {
01833                         /* remove the stream */
01834                         static str zero_s = STR_STATIC_INIT("0");
01835                         if (prepare_lumps(msg, & global_sdp_sess.media[i].port_s, &zero_s) < 0)
01836                                 return -1;
01837                         global_sdp_sess.media[i].active = 0;
01838                         continue;
01839                 }
01840                 media_count[global_sdp_sess.media[i].media_type]++;
01841         }
01842         return 1;
01843 }
01844 
01845 static int rtpproxy_set_param(struct sip_msg* msg, char* _idx, char* _value) {
01846         int idx, dir;
01847         unsigned int ip;
01848         idx = PTR2INT(_idx);
01849         union {
01850                 str s;
01851                 int i;
01852         } u;
01853         dir = (param_list[idx].flags & PAR_DIR) != 0;
01854         if (param_list[idx].flags & PAR_INT) {
01855                 if (get_int_fparam(&u.i, msg, (fparam_t*) _value) < 0) {
01856                         return -1;
01857                 }
01858         } else {
01859                 if (get_str_fparam(&u.s, msg, (fparam_t*) _value) < 0) {
01860                         return -1;
01861                 }
01862         }
01863         switch (param_list[idx].id) {
01864                 case PAR_EXPIRATION_TIMEOUT:
01865                         global_params.expiration_timeout = u.i;
01866                         break;
01867                 case PAR_TTL:
01868                         global_params.ttl = u.i;
01869                         break;
01870                 case PAR_LEARNING_TIMEOUT:
01871                         global_params.learning_timeout = u.i;
01872                         break;
01873                 case PAR_ALWAYS_LEARN:
01874                         global_params.always_learn = u.i;
01875                         break;
01876                 case PAR_SWITCHBOARD_A:
01877                 case PAR_SWITCHBOARD_B:
01878                         if (!(global_params.switchboard[dir] = find_switchboard(&u.s, NULL)))
01879                                 return -1;
01880                         break;
01881                 case PAR_SWITCHBOARD_BY_SIP_IP_A:
01882                 case PAR_SWITCHBOARD_BY_SIP_IP_B:
01883                         ip = s2ip4(&u.s);
01884                         for (global_params.switchboard[dir] = switchboards; 
01885                              global_params.switchboard[dir]; 
01886                              global_params.switchboard[dir] = global_params.switchboard[dir]->next) {
01887 
01888                                 if (ip == global_params.switchboard[dir]->sip_ip) {
01889                                         global_params.aggregation[dir] = NULL;  /* invalidate aggregation */
01890                                         return 1;
01891                                 }
01892                         }
01893                         return -1;
01894                 case PAR_AGGREGATION_A:
01895                 case PAR_AGGREGATION_B:
01896                         if (!(global_params.aggregation[dir] = find_aggregation(&u.s, NULL)))
01897                                 return -1;
01898                         break;
01899                 case PAR_AGGREGATION_BY_SIP_IP_A:
01900                 case PAR_AGGREGATION_BY_SIP_IP_B:
01901                         ip = s2ip4(&u.s);
01902                         for (global_params.aggregation[dir] = aggregations; 
01903                              global_params.aggregation[dir]; 
01904                              global_params.aggregation[dir] = global_params.aggregation[dir]->next) {
01905 
01906                                 if (ip == global_params.aggregation[dir]->sip_ip) {
01907                                         global_params.switchboard[dir] = NULL;  /* invalidate switchboard */
01908                                         return 1;
01909                                 }
01910                         }
01911                         return -1;
01912             case PAR_THROTTLE_MARK:
01913                         global_params.throttle.mark = u.i;
01914                         break;
01915                 case PAR_THROTTLE_RTP_MAX_BYTES:
01916                 case PAR_THROTTLE_RTCP_MAX_BYTES:
01917                         global_params.throttle.bandwidth[param_list[idx].id==PAR_THROTTLE_RTCP_MAX_BYTES].bytes = u.i;
01918                         break;
01919                 case PAR_THROTTLE_RTP_MAX_PACKETS:
01920                 case PAR_THROTTLE_RTCP_MAX_PACKETS:
01921                         global_params.throttle.bandwidth[param_list[idx].id==PAR_THROTTLE_RTCP_MAX_PACKETS].packets = u.i;
01922                         break;
01923 
01924                 case PAR_CODEC_SET:
01925                         if (!(global_params.codec_set = find_codec_set(&u.s, NULL)))
01926                                 return -1;
01927                         break;
01928                 case PAR_REMOVE_CODEC_MASK:
01929                         global_params.remove_codec_mask = u.i;
01930                         break;
01931 
01932                 case PAR_OLINE_USER: {
01933                                 static char buf[30];
01934                                 if (u.s.len > sizeof(buf)) {
01935                                                 return -1;
01936                                 }
01937                                 global_params.oline_user.len = u.s.len;
01938                                 if (u.s.len) {
01939                                         memcpy(buf, u.s.s, u.s.len);
01940                                         global_params.oline_user.s = buf;
01941                                 }
01942                                 break;
01943                         }                                               
01944                 case PAR_OLINE_ADDR: {
01945                                 static char buf[30];
01946                                 if (u.s.len > sizeof(buf)) {
01947                                                 return -1;
01948                                 }
01949                                 global_params.oline_addr.len = u.s.len;
01950                                 if (u.s.len) {
01951                                         memcpy(buf, u.s.s, u.s.len);
01952                                         global_params.oline_addr.s = buf;
01953                                 }
01954                                 break;
01955                         }
01956                 case PAR_PROTECTED_SESSION_IDS:
01957                         memset(&global_params.protected_sess, 0, sizeof(global_params.protected_sess));
01958                         if (!u.s.len) break;
01959                         if (unserialize_ipt_session(&u.s, &global_params.protected_sess) < 0) {
01960                                 return -1;
01961                         }
01962                         if (!global_params.protected_sess.session_count) {
01963                                 global_params.protected_sess.switchboard = NULL;
01964                         }
01965                         break;
01966 
01967                 default:
01968                         ;
01969         }
01970         return 1;
01971 }
01972 
01973 /* @select implementation */
01974 static int sel_rtpproxy(str* res, select_t* s, struct sip_msg* msg) {  /* dummy */
01975         union {int i; str s;} u;
01976         int dir, idx;
01977         u.s.len = 0;
01978         if (msg == NULL && res == NULL) {
01979                 /* fixup call */
01980                 int idx;
01981                 idx = param2idx(&s->params[1].v.s, PAR_READ);
01982                 if (idx < 0) {
01983                         return -1;
01984                 }
01985                 s->params[1].v.i = idx;
01986                 s->params[1].type = SEL_PARAM_DIV;
01987                 return 1;
01988         }
01989         idx = s->params[1].v.i;
01990         dir = (param_list[idx].flags & PAR_DIR) != 0;
01991         switch (param_list[idx].id) {
01992                 case PAR_EXPIRATION_TIMEOUT:
01993                         u.i = global_params.expiration_timeout;
01994                         break;
01995                 case PAR_TTL:
01996                         u.i = global_params.ttl;
01997                         break;
01998                 case PAR_LEARNING_TIMEOUT:
01999                         u.i = global_params.learning_timeout;
02000                         break;
02001                 case PAR_ALWAYS_LEARN:
02002                         u.i = global_params.always_learn;
02003                         break;
02004                 case PAR_SWITCHBOARD_A:
02005                 case PAR_SWITCHBOARD_B:
02006                         if (global_params.switchboard[dir])                             
02007                                 u.s = global_params.switchboard[dir]->name;
02008                         break;
02009                 case PAR_AGGREGATION_A:
02010                 case PAR_AGGREGATION_B:
02011                         if (global_params.aggregation[dir])
02012                                 u.s = global_params.aggregation[dir]->name;
02013                         break;
02014                 case PAR_SDP_IP:
02015                         u.s = global_params.sdp_ip;
02016                         break;
02017                 case PAR_ACTIVE_MEDIA_NUM: {
02018                         int i;
02019                         if (check_parse_sdp_content(msg, &global_sdp_sess) < 0) return -1;
02020                         u.i = 0;
02021                         for (i = 0; i < global_sdp_sess.media_count; i++) {
02022                                 if (global_sdp_sess.media[i].active) {
02023                                         u.i++;
02024                                 }
02025                         }
02026                         break;
02027                 }
02028                 case PAR_OLINE_USER:
02029                         if (check_parse_sdp_content(msg, &global_sdp_sess) < 0) return -1;
02030                         u.s = global_sdp_sess.oline_user_s;
02031                         break;
02032                 case PAR_OLINE_ADDR:
02033                         if (check_parse_sdp_content(msg, &global_sdp_sess) < 0) return -1;
02034                         u.s = global_sdp_sess.oline_addr_s;
02035                         break;
02036                 case PAR_SESSION_IDS:
02037                         u.s = global_params.session_ids;
02038                         break;
02039                 case PAR_THROTTLE_MARK:
02040                         u.i = global_params.throttle.mark;
02041                         break;
02042                 case PAR_THROTTLE_RTP_MAX_BYTES:
02043                 case PAR_THROTTLE_RTCP_MAX_BYTES:
02044                         u.i = global_params.throttle.bandwidth[param_list[idx].id==PAR_THROTTLE_RTCP_MAX_BYTES].bytes;
02045                         break;
02046                 case PAR_THROTTLE_RTP_MAX_PACKETS:
02047                 case PAR_THROTTLE_RTCP_MAX_PACKETS:
02048                         u.i = global_params.throttle.bandwidth[param_list[idx].id==PAR_THROTTLE_RTCP_MAX_PACKETS].packets;
02049                         break;
02050                 case PAR_CODEC_SET:
02051                         if (global_params.codec_set)
02052                                 u.s = global_params.codec_set->name;
02053                         break;
02054                 case PAR_AUTH_RIGHTS:
02055                         u.i = global_params.auth_rights;
02056                         break;
02057                 case PAR_REMOVE_CODEC_MASK:
02058                         u.i = global_params.remove_codec_mask;
02059                         break;
02060                 default:
02061                         ;
02062         }
02063         if (param_list[idx].flags & PAR_STR) {
02064                 if (!u.s.len) return 1;
02065                 *res = u.s;
02066         }
02067         else {
02068                 return uint_to_static_buffer(res, u.i);
02069         }
02070         return 0;
02071 }
02072 
02073 select_row_t sel_declaration[] = {
02074         { NULL, SEL_PARAM_STR, STR_STATIC_INIT(MODULE_NAME), sel_rtpproxy, CONSUME_NEXT_STR | FIXUP_CALL },
02075         { NULL, SEL_PARAM_INT, STR_NULL, NULL, 0}
02076 };
02077 
02078 static int mod_pre_script_cb(struct sip_msg *msg, unsigned int flags, void *param) {
02079         memset(&global_params, 0, sizeof(global_params));
02080         global_params.always_learn = -1;
02081         global_params.ttl = -1;
02082         sdp_parsed = 999; /* any number not in (-1,0) */
02083         return 1;
02084 }
02085 
02086 static struct {
02087         enum {iptrtpproxy_default=0x01, iptrtpproxy_switchboard=0x02, iptrtpproxy_host=0x04} flag;
02088         str name;
02089         struct {
02090                 struct {
02091                         struct xt_rtpproxy_switchboard_id addr;
02092                         str host;
02093                 } switchboard;
02094                 struct xt_rtpproxy_connection_rpc_params host;
02095         } dflt, parsed;
02096 
02097 } parse_config_vals;
02098 
02099 static int cfg_parse_addr(void* param, cfg_parser_t* st, unsigned int flags) {
02100         str val;
02101         char buff[50];
02102         val.s = buff;
02103         val.len = sizeof(buff)-1;
02104         if (cfg_parse_str(&val, st, CFG_EXTENDED_ALPHA|CFG_STR_STATIC) < 0) return -1;
02105         *(uint32_t*)param  = s2ip4(&val);
02106         if (*(uint32_t*)param == 0) {
02107                 ERR(MODULE_NAME": parse_addr: bad ip address '%.*s'\n", STR_FMT(&val));
02108                 return -1;
02109         }
02110         return 0;
02111 }
02112 
02113 static int cfg_parse_uint16(void* param, cfg_parser_t* st, unsigned int flags) {
02114         int val;
02115         if (cfg_parse_int(&val, st, 0) < 0) 
02116                 return -1;
02117         *(uint16_t *) param = val;
02118         return 0;
02119 }
02120 
02121 static int cfg_parse_default(void* param, cfg_parser_t* st, unsigned int flags) {
02122         int ret;
02123         cfg_token_t t;
02124         str val, tok;
02125         cfg_option_t* opt;
02126         char buf[MAX_TOKEN_LEN];
02127 
02128         tok.s = buf;
02129 
02130         if (st->cur_opt->val.len >= sizeof(buf)-1) goto skip;
02131         memcpy(tok.s, st->cur_opt->val.s, st->cur_opt->val.len);
02132         tok.len = st->cur_opt->val.len;
02133 
02134         /* we need process here options containing dash '-' because of too strict parser */
02135         while (1) {
02136                 ret = cfg_get_token(&t, st, 0);
02137                 if (ret < 0) return ret;
02138                 if (ret > 0) return 0;
02139                 if (t.type == '=')
02140                         break;
02141                 if (tok.len+t.val.len >= sizeof(buf)-1) goto skip;
02142                 memcpy(tok.s+tok.len, t.val.s, t.val.len);
02143                 tok.len += t.val.len;
02144                 
02145         }
02146         tok.s[tok.len] = '\0';
02147         if ((opt = cfg_lookup_token(st->options+1/*2nd pass*/, &tok)) && ((opt->flags & CFG_DEFAULT)== 0)) {
02148                 st->cur_opt = &t;
02149                 if (opt->f(opt->param, st, opt->flags) < 0) return -1;
02150                 return 0;
02151         }
02152 skip:
02153         if (cfg_parse_str(&val, st, CFG_EXTENDED_ALPHA) < 0) return -1;
02154         return 0;
02155 }
02156 
02157 static int safe_parsed_values() {
02158 #define PROC_DEFAULT(_f_, _def_) \
02159         if (!parse_config_vals.parsed._f_) {\
02160                 parse_config_vals.parsed._f_ = parse_config_vals.dflt._f_?parse_config_vals.dflt._f_:_def_; \
02161         }
02162         
02163         if (parse_config_vals.flag & iptrtpproxy_default) {
02164                 if (parse_config_vals.flag & iptrtpproxy_switchboard)
02165                         parse_config_vals.dflt.switchboard = parse_config_vals.parsed.switchboard;
02166                 else if (parse_config_vals.flag & iptrtpproxy_host)
02167                         parse_config_vals.dflt.host = parse_config_vals.parsed.host;
02168 
02169         } else if (parse_config_vals.flag) {
02170                 int fl, max_len, i;
02171                 struct switchboard_item **prev_si = NULL;
02172                 struct host_item **prev_hi = NULL;
02173                 char *s;
02174                 if (parse_config_vals.flag & iptrtpproxy_switchboard) {
02175                         fl = find_switchboard(&parse_config_vals.name, &prev_si) != NULL;
02176                         s = "switchboard";
02177                         max_len = MAX_SWITCHBOARD_NAME_LEN;
02178                 }
02179                 else {
02180                         struct host_item *p;
02181                         p = find_host(&parse_config_vals.name, &prev_hi);
02182                         fl = p != NULL;
02183                         s = "host";
02184                         max_len = HOST_NAME_MAX;
02185                         if (p && p->local) {
02186                                 pkg_free(parse_config_vals.name.s);
02187                                 goto local;
02188                         }       
02189                 }
02190                 if (fl) {
02191                         ERR(MODULE_NAME": safe_parsed_values: %s name '%.*s' already declared\n", s, STR_FMT(&parse_config_vals.name));
02192                         return -1;
02193                 }
02194                 for (i=0; i<parse_config_vals.name.len; i++) {
02195                         if (!is_alpha(parse_config_vals.name.s[i])) {
02196                                 ERR(MODULE_NAME": safe_parsed_values: bad %s name '%.*s'\n", s, STR_FMT(&parse_config_vals.name));
02197                                 return -1;
02198                         }
02199                 }
02200                 if (parse_config_vals.name.len > max_len) {
02201                         ERR(MODULE_NAME": safe_parsed_values: %s name '%.*s' is too long (%d>%d)\n", s, STR_FMT(&parse_config_vals.name), parse_config_vals.name.len, max_len);
02202                         return -1;
02203                 }
02204                 
02205                 if (parse_config_vals.flag & iptrtpproxy_switchboard) {
02206                         struct switchboard_item *si;
02207                         si = pkg_malloc(sizeof(*si));
02208                         if (!si) goto out_of_mem;
02209                         memset(si, 0, sizeof(*si));
02210                         si->name = parse_config_vals.name;                      
02211                         si->next = (*prev_si);
02212                         (*prev_si) = si;
02213                         PROC_DEFAULT(switchboard.addr.ip, 0);
02214                         PROC_DEFAULT(switchboard.addr.port, 0);
02215                         if (parse_config_vals.parsed.switchboard.host.len) {
02216                                 si->hostname = parse_config_vals.parsed.switchboard.host;
02217                         }
02218                         else {
02219                                 si->hostname = parse_config_vals.dflt.switchboard.host;
02220                         }
02221                         if (!si->hostname.len) {
02222                                 si->hostname.s = iptrtpproxy_cfg_hostname;
02223                                 si->hostname.len = strlen(si->hostname.s);
02224                         }
02225                         si->switchboard_addr = parse_config_vals.parsed.switchboard.addr;
02226                 }
02227                 else {
02228                         struct host_item *hi;
02229                         hi = pkg_malloc(sizeof(*hi));
02230                         if (!hi) goto out_of_mem;
02231                         memset(hi, 0, sizeof(*hi));
02232                         hi->name = parse_config_vals.name;
02233                         hi->next = (*prev_hi);
02234                         (*prev_hi) = hi;
02235                         PROC_DEFAULT(host.addr, 0);
02236                         PROC_DEFAULT(host.port, 0);
02237                         PROC_DEFAULT(host.request_size, XT_RTPPROXY_RPC_DEFAULT_REQUEST_SIZE);
02238                         PROC_DEFAULT(host.reply_size, XT_RTPPROXY_RPC_DEFAULT_REPLY_SIZE);
02239                         PROC_DEFAULT(host.total_timeout, XT_RTPPROXY_RPC_DEFAULT_TOTAL_TIMEOUT);
02240                         PROC_DEFAULT(host.udp_retry_timeout, XT_RTPPROXY_RPC_DEFAULT_UDP_REPLY_TIMEOUT);
02241                         hi->rpc_params = parse_config_vals.parsed.host;
02242                 }
02243         }
02244 local:
02245         memset(&parse_config_vals.parsed, 0, sizeof(parse_config_vals.parsed));
02246         memset(&parse_config_vals.name, 0, sizeof(parse_config_vals.name));
02247         return 0;
02248 out_of_mem:
02249         ERR(MODULE_NAME": safe_parsed_values: not enough pkg memory\n");
02250         return -1;
02251 }
02252 
02253 static cfg_option_t section_switchboard_options[] = {
02254 /* 1st pass */
02255         {NULL, .flags = CFG_DEFAULT, .f = cfg_parse_default},
02256 /* 2nd pass */
02257         {"addr", .f = cfg_parse_addr, .flags = CFG_CASE_SENSITIVE, .param = &parse_config_vals.parsed.switchboard.addr.ip},
02258         {"port", .f = cfg_parse_uint16, .flags = CFG_CASE_SENSITIVE, .param = &parse_config_vals.parsed.switchboard.addr.port},
02259         {"host", .f = cfg_parse_str, .flags = CFG_CASE_SENSITIVE|CFG_STR_PKGMEM, .param = &parse_config_vals.parsed.switchboard.host},
02260         {NULL, .flags = CFG_DEFAULT, .f = cfg_parse_default}
02261 };
02262 
02263 static cfg_option_t protos[] = {
02264         {"udp", .param = &parse_config_vals.parsed.host.proto, .val = xt_rtpproxy_connection_UDP},
02265         {"tcp", .param = &parse_config_vals.parsed.host.proto, .val = xt_rtpproxy_connection_TCP},
02266         {NULL, .flags = 0}
02267 };
02268 
02269 static cfg_option_t section_host_options[] = {
02270 /* 1st pass */
02271         {NULL, .flags = CFG_DEFAULT, .f = cfg_parse_default},
02272 /* 2nd pass */
02273         {"rpc-addr", .f = cfg_parse_addr, .flags = CFG_CASE_SENSITIVE, .param = &parse_config_vals.parsed.host.addr},
02274         {"rpc-port", .f = cfg_parse_uint16, .flags = CFG_CASE_SENSITIVE, .param = &parse_config_vals.parsed.host.port},
02275         {"rpc-proto", .f = cfg_parse_enum, .flags = CFG_CASE_SENSITIVE, .param = protos},
02276         {"rpc-request-size", .f = cfg_parse_int, .flags = CFG_CASE_SENSITIVE, .param = &parse_config_vals.parsed.host.request_size},
02277         {"rpc-reply-size", .f = cfg_parse_int, .flags = CFG_CASE_SENSITIVE, .param = &parse_config_vals.parsed.host.reply_size},
02278         {"rpc-total-timeout", .f = cfg_parse_int, .flags = CFG_CASE_SENSITIVE, .param = &parse_config_vals.parsed.host.total_timeout},
02279         {"rpc-udp-retry-timeout", .f = cfg_parse_int, .flags = CFG_CASE_SENSITIVE, .param = &parse_config_vals.parsed.host.udp_retry_timeout},
02280         {NULL, .flags = CFG_DEFAULT, .f = cfg_parse_default}
02281 };
02282 
02283 static cfg_option_t section_dummy_options[] = {
02284 /* 1st pass */
02285         {NULL, .flags = CFG_DEFAULT, .f = cfg_parse_default},
02286 /* 2nd pass */
02287         {NULL, .flags = CFG_DEFAULT, .f = cfg_parse_default}
02288 };
02289 
02290 #define DEFAULT_SECTION "default"
02291 #define SWITCHBOARD_PREFIX "switchboard"
02292 #define HOST_PREFIX "host"
02293 
02294 static int parse_section_name(void* param, cfg_parser_t* st, unsigned int flags) {
02295         cfg_token_t t;
02296         int ret, fl;
02297         ret = safe_parsed_values();
02298         if (ret != 0) return ret;
02299 
02300         cfg_set_options(st, section_dummy_options);
02301 
02302         ret = cfg_get_token(&t, st, 0);
02303         if (ret != 0) return ret;
02304         if (t.type != CFG_TOKEN_ALPHA) 
02305                 goto skip;
02306         if (t.val.len == (sizeof(DEFAULT_SECTION)-1) && strncmp(t.val.s, DEFAULT_SECTION, t.val.len) == 0) 
02307                 fl = iptrtpproxy_default;
02308         else if (t.val.len == (sizeof(SWITCHBOARD_PREFIX)-1) && strncmp(t.val.s, SWITCHBOARD_PREFIX, t.val.len) == 0) {
02309                 fl = iptrtpproxy_switchboard;
02310         } else if (t.val.len == (sizeof(HOST_PREFIX)-1) && strncmp(t.val.s, HOST_PREFIX, t.val.len) == 0) { 
02311                 fl = iptrtpproxy_host;
02312         }
02313         else
02314                 goto skip;
02315         ret = cfg_get_token(&t, st, 0);
02316         if (ret != 0) return ret;
02317         if (t.type != ':') 
02318                 goto skip;
02319         ret = cfg_parse_section(&parse_config_vals.name, st, CFG_STR_PKGMEM);
02320         if (ret != 0) return ret;
02321 
02322         if (fl==iptrtpproxy_default) {
02323                 if (parse_config_vals.name.len == (sizeof(SWITCHBOARD_PREFIX)-1) && strncmp(parse_config_vals.name.s, SWITCHBOARD_PREFIX, parse_config_vals.name.len) == 0) {
02324                         fl |= iptrtpproxy_switchboard;
02325                 }
02326                 else if (parse_config_vals.name.len == (sizeof(HOST_PREFIX)-1) && strncmp(parse_config_vals.name.s, HOST_PREFIX, parse_config_vals.name.len) == 0) {
02327                         fl |= iptrtpproxy_host;
02328                 }
02329                 else {
02330                         goto skip;
02331                 }
02332                 if (parse_config_vals.name.s) {
02333                         pkg_free(parse_config_vals.name.s);
02334                         parse_config_vals.name.s = NULL;
02335                 }
02336         }
02337         cfg_set_options(st, section_dummy_options);
02338         if (fl) {
02339                 if (fl & iptrtpproxy_switchboard) {
02340                         cfg_set_options(st, section_switchboard_options);
02341                 } else if (fl & iptrtpproxy_host) {
02342                         cfg_set_options(st, section_host_options);
02343                 }
02344                 parse_config_vals.flag = fl;
02345         }
02346         return 0;
02347 skip:
02348         while (t.type != ']') {
02349                 ret = cfg_get_token(&t, st, 0);
02350                 if (ret != 0) return ret;
02351         }
02352         return cfg_eat_eol(st, 0);
02353 }
02354 
02355 static int parse_iptrtpproxy_cfg() {
02356         cfg_parser_t* parser = NULL;
02357         static char buf[HOST_NAME_MAX+1];
02358         struct switchboard_item *si;
02359         if (!iptrtpproxy_cfg_hostname || !strlen(iptrtpproxy_cfg_hostname)) {
02360                 if (gethostname(buf, sizeof(buf)-1) < 0) {
02361                         ERR(MODULE_NAME"parse_iptrtpproxy_cfg: gethostname error\n");
02362                         return E_CFG;
02363                 }
02364                 iptrtpproxy_cfg_hostname = buf;
02365         }
02366         hosts = pkg_malloc(sizeof(*hosts));
02367         if (!hosts) return -1;
02368         memset(hosts, 0, sizeof(*hosts));
02369         hosts->name.s = iptrtpproxy_cfg_hostname;
02370         hosts->name.len = strlen(hosts->name.s);
02371         hosts->local = 1;
02372         
02373         if ((parser = cfg_parser_init(0, &iptrtpproxy_cfg_filename)) == NULL) {
02374                 ERR(MODULE_NAME"parse_iptrtpproxy_cfg: Error while initializing configuration file parser.\n");
02375                 return -1;
02376         }
02377         cfg_section_parser(parser, parse_section_name, NULL);
02378         memset(&parse_config_vals, 0, sizeof(parse_config_vals));
02379         if (sr_cfg_parse(parser)) {
02380                 return -1;
02381         }
02382         cfg_parser_close(parser);
02383         if (safe_parsed_values() < 0) {
02384                 return -1;
02385         }
02386         for (si = switchboards; si; si = si->next) {
02387                 si->host = find_host(&si->hostname, NULL);
02388                 if (!si->host) {
02389                         ERR(MODULE_NAME"parse_iptrtpproxy_cfg: host '%.*s' not found.\n", STR_FMT(&si->hostname));
02390                         return -1;
02391                 }
02392                 si->sip_ip = si->switchboard_addr.ip;
02393         }
02394         return 0;
02395 }
02396 
02397 static struct {
02398         char *name;
02399         int payload_type;
02400         unsigned int media_type;
02401         int clock_rate;
02402         int channels;
02403 } def_codecs [] = {
02404         {"PCMU", 0, 1<<sdpmtAudio, 8000, 1},
02405         {"GSM", 3, 1<<sdpmtAudio, 8000, 1},
02406         {"G723", 4, 1<<sdpmtAudio, 8000, 1},
02407         {"DVI4", 5, 1<<sdpmtAudio, 8000, 1},
02408         {"DVI4", 6, 1<<sdpmtAudio, 16000, 1},
02409         {"LPC", 7, 1<<sdpmtAudio, 8000, 1},
02410         {"PCMA", 8, 1<<sdpmtAudio, 8000, 1},
02411         {"G722", 9, 1<<sdpmtAudio, 8000, 1},
02412         {"L16", 10, 1<<sdpmtAudio, 44100, 2},
02413         {"L16", 11, 1<<sdpmtAudio, 44100, 1},
02414         {"QCELP", 12, 1<<sdpmtAudio, 8000, 1},
02415         {"CN", 13, 1<<sdpmtAudio, 8000, 1},
02416         {"MPA", 14, 1<<sdpmtAudio, 90000},
02417         {"G728", 15, 1<<sdpmtAudio, 8000, 1},
02418         {"DVI4", 16, 1<<sdpmtAudio, 11025, 1},
02419         {"DVI4", 17, 1<<sdpmtAudio, 22050, 1},
02420         {"G729", 18, 1<<sdpmtAudio, 8000, 1},
02421         {"CelB", 25, 1<<sdpmtVideo, 90000},
02422         {"JPEG", 26, 1<<sdpmtVideo, 90000},
02423         {"nv", 28, 1<<sdpmtVideo, 90000},
02424         {"H261", 31, 1<<sdpmtVideo, 90000},
02425         {"MPV", 32, 1<<sdpmtVideo, 90000},
02426         {"MP2T", 33, 1<<sdpmtAudio | 1<<sdpmtVideo, 90000},
02427         {"H263", 34, 1<<sdpmtVideo, 90000},
02428         {"telephone-event", -1, 1<<sdpmtAudio},
02429         {"tone", -1, 1<<sdpmtAudio},
02430         {"red", -1, 1<<sdpmtAudio|1<<sdpmtText},
02431         {"rtx", -1, 1<<sdpmtAudio|1<<sdpmtVideo|1<<sdpmtApplication|1<<sdpmtText},
02432         {"parityfec", -1, 1<<sdpmtAudio|1<<sdpmtVideo|1<<sdpmtApplication|1<<sdpmtText},
02433         {"t140c", -1, 1<<sdpmtAudio},
02434         {"t38", -1, 1<<sdpmtAudio},
02435         {"AMR", -1, 1<<sdpmtAudio, 8000},
02436         {"AMR-WB", -1, 1<<sdpmtAudio, 16000},
02437         {"L8", -1, 1<<sdpmtAudio},
02438         {"L20", -1, 1<<sdpmtAudio},
02439         {"L24", -1, 1<<sdpmtAudio},
02440         {"DAT12", -1, 1<<sdpmtAudio},
02441         
02442         {"raw", -1, 1<<sdpmtVideo},
02443         {"pointer", -1, 1<<sdpmtVideo},
02444         /* etc.*/       
02445 
02446         {NULL}
02447 };
02448 
02449 static int declare_def_codecs() {
02450         int codec_id, i;
02451         i = 0;
02452         while (def_codecs[i].name) {
02453                 str s;
02454                 s.s = def_codecs[i].name;
02455                 s.len = strlen(s.s);
02456                 codec_id = register_codec(&s);
02457                 if (codec_id < 0) return codec_id;
02458                 i++;
02459         }
02460         return 0;
02461 }
02462 
02463 /* module initialization */
02464 static int mod_init(void) {
02465         struct switchboard_item *si;
02466         struct aggregation_item *ai;
02467         struct host_item *hi;
02468         struct codec_set_item *ci;
02469         int i;
02470         if (iptrtpproxy_cfg_flag <= 1) {
02471                 if (parse_iptrtpproxy_cfg() < 0)
02472                         return E_CFG;
02473         }
02474 
02475         for (si = switchboards; si; si=si->next) {
02476                 str ips[2];
02477                 char buf1[17];
02478                 si->stat = shm_malloc(sizeof(*si->stat));
02479                 if (!si->stat) return E_OUT_OF_MEM;
02480                 memset(si->stat, 0, sizeof(*si->stat));
02481 
02482                 ip42s(si->switchboard_addr.ip, ips+0);
02483                 strncpy(buf1, ips[0].s, sizeof(buf1)-1);
02484                 ips[0].s = buf1;
02485                 ip42s(si->sip_ip, ips+1);
02486 
02487                 INFO(MODULE_NAME": mod_init: switchboard_name=%.*s;addr=%.*s;port=%d;sip-addr=%.*s;hostname=%.*s\n", 
02488                         STR_FMT(&si->name),
02489                         STR_FMT(ips+0),
02490                         si->switchboard_addr.port,
02491                         STR_FMT(ips+1),
02492                         STR_FMT(&si->hostname)
02493                 );
02494 
02495         }
02496         for (ai = aggregations; ai; ai=ai->next) {
02497                 str ips[1];
02498                 ip42s(ai->sip_ip, ips+0);
02499                 INFO(MODULE_NAME": mod_init: aggregation '%.*s';sip-addr=%.*s\n", 
02500                         STR_FMT(&ai->name),
02501                         STR_FMT(ips+0)
02502                 );
02503                 for (i=0; i<ai->switchboard_count; i++) {
02504                         ERR(MODULE_NAME": mod_init:   '%.*s'\n", STR_FMT(&(*ai->switchboards)[i]->name));
02505                 }
02506         }
02507         for (hi = hosts; hi; hi=hi->next) {
02508                 str ips;
02509                 hi->stat = shm_malloc(sizeof(*hi->stat));
02510                 if (!hi->stat) return E_OUT_OF_MEM;
02511                 memset(hi->stat, 0, sizeof(*hi->stat));
02512 
02513                 ip42s(hi->rpc_params.addr, &ips);
02514                 INFO(MODULE_NAME": mod_init: host_name=%.*s;rpc-addr=%.*s;rpc-port=%d;rpc-proto=%d,request-size=%d,reply-size=%d,total-timeout=%d,udp-retry-timeout=%d\n", 
02515                         STR_FMT(&hi->name),
02516                         STR_FMT(&ips),
02517                         hi->rpc_params.port,
02518                         hi->rpc_params.proto,
02519                         hi->rpc_params.request_size,
02520                         hi->rpc_params.reply_size,
02521                         hi->rpc_params.total_timeout,
02522                         hi->rpc_params.udp_retry_timeout
02523                 ); 
02524         }
02525 
02526                 if (!reg_codecs) {
02527                         int r;
02528                         if ((r = declare_def_codecs()) < 0) {
02529                                 return r;
02530                         }
02531         }
02532         memset(&fixed_payload_types, 0, sizeof(fixed_payload_types));
02533         i = 0;
02534         while (def_codecs[i].name && def_codecs[i].payload_type >= 0) {
02535                 str s;
02536                 int codec_id;
02537                 s.s = def_codecs[i].name;
02538                 s.len = strlen(s.s);            
02539                 codec_id = name2codec_id(&s, NULL);
02540                 if (!codec_id) {
02541                         BUG(MODULE_NAME":  mod_init: def.codec '%s' not found\n", s.s);
02542                         return -1;
02543                 }
02544                 fixed_payload_types[def_codecs[i].payload_type].codec_id = codec_id;
02545                 i++;
02546         }
02547 #if 0
02548         for (i=0; i<MAX_FIXED_PAYLOAD_TYPES; i++) {
02549                 if (fixed_payload_types[i].codec_id) {
02550                         INFO(MODULE_NAME": mod_init: payload_type=%d, codec_name='%.*s'\n", i, STR_FMT(&(*reg_codecs)[fixed_payload_types[i].codec_id-1].name));
02551                 }
02552         }
02553         for (i=0; i<reg_codec_count; i++) {
02554                 INFO(MODULE_NAME": mod_init: codec_id=%d, codec_name='%.*s'\n", i+1, STR_FMT(&(*reg_codecs)[i].name));
02555         }
02556 #endif
02557         for (ci = codec_sets; ci; ci=ci->next) {
02558                 int media_type;
02559                 INFO(MODULE_NAME": mod_init: codec_set='%.*s'\n", STR_FMT(&ci->name));
02560                 for (media_type=0; media_type<NUM_MEDIA_TYPES; media_type++) {
02561                         INFO(MODULE_NAME": mod_init:   media_type='%.*s';max_streams=%d;rtp_bytes=%d;rtcp_bytes=%d;rtp_packets=%d;rtcp_packets=%d\n", 
02562                                 STR_FMT(sdp_media_types_str+media_type),
02563                                 ci->media_types[media_type].throttle.max_streams,
02564                                 ci->media_types[media_type].throttle.bandwidth[0].bytes,
02565                                 ci->media_types[media_type].throttle.bandwidth[1].bytes,
02566                                 ci->media_types[media_type].throttle.bandwidth[0].packets,
02567                                 ci->media_types[media_type].throttle.bandwidth[1].packets
02568                         );
02569                         if (ci->media_types[media_type].throttle.max_streams != 0) {
02570                                 for (i=0; i<reg_codec_count; i++) {
02571                                         if ((*ci->media_types[media_type].codec_rights)[i] > 0) {
02572                                                 if (i > 0)
02573                                                         INFO(MODULE_NAME": mod_init:     codec='%.*s', right=%d\n", STR_FMT(&(*reg_codecs)[i-1].name), (*ci->media_types[media_type].codec_rights)[i]);
02574                                                 else
02575                                                         INFO(MODULE_NAME": mod_init:     codec='?', right=%d\n", (*ci->media_types[media_type].codec_rights)[i]);
02576                                         }
02577                                 }
02578                         }
02579                 }
02580         }
02581         /*register_script_cb(mod_pre_script_cb, REQ_TYPE_CB | RPL_TYPE_CB| PRE_SCRIPT_CB, 0);*/
02582         register_script_cb(mod_pre_script_cb, REQUEST_CB | ONREPLY_CB| PRE_SCRIPT_CB, 0);
02583         register_select_table(sel_declaration);
02584         return 0;
02585 }
02586 
02587 static void mod_cleanup(void) {
02588         struct host_item *hi;
02589         struct switchboard_item *si;
02590         for (si = switchboards; si; si = si->next) {
02591                 if (si->stat) {
02592                         shm_free(si->stat);
02593                         si->stat = NULL;
02594                 }
02595         }
02596         for (hi = hosts; hi; hi = hi->next) {
02597                 if (hi->handle_is_opened) {
02598                         xt_RTPPROXY_close(&hi->handle);
02599                         hi->handle_is_opened = 0;
02600                 }
02601                 if (hi->stat) {
02602                         shm_free(hi->stat);
02603                         hi->stat = NULL;
02604                 }
02605         }
02606 }
02607 
02608 static int child_init(int rank) {
02609 
02610         return 0;
02611 }
02612 
02613 
02614 #define eat_spaces(_p) \
02615         while( *(_p)==' ' || *(_p)=='\t' ){\
02616         (_p)++;}
02617 
02618 
02619 static int declare_config(modparam_t type, void* val) {
02620         if (!val) return 0;
02621         if (iptrtpproxy_cfg_flag <= 1) {
02622                 iptrtpproxy_cfg_flag = 2;
02623                 iptrtpproxy_cfg_filename = * (str*) val;
02624                 if (parse_iptrtpproxy_cfg() == 0)
02625                         return 0;
02626         }
02627         else {          
02628                 switch (iptrtpproxy_cfg_flag) {
02629                         case 2:
02630                                 ERR(MODULE_NAME": declare_config: 'config' param may be used only once\n");
02631                                 break;
02632                         case 3:
02633                                 ERR(MODULE_NAME": declare_config: 'config' param may not be used after 'switchboard'\n");
02634                                 break;
02635                         default:
02636                                 BUG(MODULE_NAME": declare_config: unexpected 'iptrtpproxy_cfg_flag' value %d\n", iptrtpproxy_cfg_flag);
02637                 }                               
02638         }
02639         return E_CFG;
02640 }
02641 
02642 static int declare_hostname(modparam_t type, void* val) {
02643         if (!val) return 0;
02644         if (iptrtpproxy_cfg_flag == 0) {
02645                 iptrtpproxy_cfg_hostname = (char*) val;
02646                 return 0;
02647         }
02648         else {
02649                 switch (iptrtpproxy_cfg_flag) {
02650                         case 1:
02651                                 ERR(MODULE_NAME": declare_hostname: 'hostname' param may be used only once\n");
02652                                 break;
02653                         case 2:
02654                         case 3:
02655                                 ERR(MODULE_NAME": declare_hostname: 'hostname' param may not be used after 'switchboard' or 'config'\n");
02656                                 break;
02657                         default:
02658                                 BUG(MODULE_NAME": declare_hostname: unexpected 'iptrtpproxy_cfg_flag' value %d\n", iptrtpproxy_cfg_flag);
02659                 }                               
02660         }
02661         return E_CFG;
02662 }
02663 
02664 struct parse_param_item {
02665         char *name;
02666         unsigned int id;
02667 };
02668 
02669 /* returns index to items, -1 if error or param not found */
02670 static int parse_next_param(char **s, struct parse_param_item (*params)[], str *val) {
02671                 str p;
02672                 int i;
02673                 char *c;
02674 
02675                 eat_spaces(*s);
02676                 c = *s;
02677                 while ( is_alpha(*c) ) {
02678                         c++;
02679                 }
02680                 if (c == *s) {
02681                         ERR(MODULE_NAME": parse_next_param: param name expected near '%s'\n", *s);
02682                         return -1;
02683                 }
02684                 p.s = *s;
02685                 p.len = c-*s;
02686                 eat_spaces(c);
02687                 *s = c;
02688                 if (*c != '=') {
02689                         ERR(MODULE_NAME": parse_next_param: equal char expected near '%s'\n", *s);
02690                         return -1;
02691                 }
02692                 c++;
02693                 eat_spaces(c);
02694                 *s = c;
02695                 while (*c && *c != ';') c++;
02696                 val->s = *s;
02697                 val->len = c-*s;
02698                 while (val->len > 0 && val->s[val->len-1]<=' ') val->len--;
02699                 if (*c) c++;
02700                 eat_spaces(c);
02701                 *s = c;
02702                 for (i=0; (*params)[i].name; i++) {
02703                         if (strlen((*params)[i].name)==p.len && strncasecmp((*params)[i].name, p.s, p.len) == 0) {
02704                                 return i;
02705                         }
02706                 }
02707                 ERR(MODULE_NAME": parse_next_param: unknown param name '%.*s'\n", STR_FMT(&p));
02708                 return -1;
02709 }
02710 
02711 static int declare_switchboard_param(modparam_t type, void* val) {
02712 
02713         char *s;
02714         int all_flag;
02715         struct switchboard_item *si = NULL;
02716         enum param_id {
02717                 par_Name =              0x000001,
02718                 par_Aggregation =       0x000002,
02719                 par_SipAddr =           0x000004
02720         };
02721         static struct parse_param_item params[] = {
02722                 {.name = "name", .id = par_Name},
02723                 {.name = "sip-addr", .id = par_SipAddr},
02724                 {.name = "aggregation", .id = par_Aggregation},
02725 
02726                 {.name = 0, .id = 0}
02727         };
02728 
02729         if (!val) return 0;
02730         if (iptrtpproxy_cfg_flag <= 1) {
02731                 iptrtpproxy_cfg_flag = 3;
02732                 if (parse_iptrtpproxy_cfg() < 0)
02733                         return E_CFG;
02734         }
02735 
02736         s = val;
02737         all_flag = -1;
02738 
02739         eat_spaces(s);
02740         if (!*s) return 0;
02741         /* parse param: name=;aggregation=;sip-addr= */
02742         while (*s) {
02743                 str val;
02744                 int idx;
02745 
02746                 idx = parse_next_param(&s, &params, &val);
02747                 if (idx < 0) goto err_E_CFG;
02748                 
02749                 if (all_flag >= 0 && params[idx].id == par_Name) {
02750                         ERR(MODULE_NAME": declare_switchboard_param: name must be the first param\n");
02751                         goto err_E_CFG;
02752                 }
02753                 if (params[idx].id == par_Name) {
02754                         all_flag = 0;
02755                         si = find_switchboard(&val, NULL);
02756                         if (!si) {
02757                                 if (val.len == 1 && val.s[0] == '*')
02758                                         all_flag = 1;
02759                                 else {
02760                                         ERR(MODULE_NAME": declare_switchboard_param: switchboard '%.*s' not found\n", STR_FMT(&val));
02761                                         goto err_E_CFG;
02762                                 }
02763                         }
02764                 }
02765                 else {
02766                         if (all_flag)
02767                                 si = switchboards;
02768                         while (si) {
02769 
02770                                 switch (params[idx].id) {
02771                                         case par_Name:
02772                                                 break;
02773                                         case par_Aggregation: {
02774                                                 struct aggregation_item *ai;
02775                                                 struct aggregation_item **prev_ai;
02776                                                 int i;
02777                                                 ai = find_aggregation(&val, &prev_ai);
02778                                                 if (!ai) {
02779                                                         ai = pkg_malloc(sizeof(*ai));
02780                                                         if (!ai) return E_OUT_OF_MEM;
02781                                                         memset(ai, 0, sizeof(*ai));
02782                                                         ai->name = val;
02783                                                         ai->next = (*prev_ai);
02784                                                         (*prev_ai) = ai;
02785                                                 }
02786                                                 for (i=0; i<ai->switchboard_count; i++) {
02787                                                         if ((*ai->switchboards)[i] == si) goto aggr_found;
02788                                                 }
02789                                                 if (!ai->sip_ip) {
02790                                                         ai->sip_ip = si->sip_ip;
02791                                                 }
02792                                                 ai->switchboards = pkg_realloc(ai->switchboards, sizeof((*ai->switchboards)[0])*(ai->switchboard_count+1));
02793                                                 if (!ai->switchboards) return E_OUT_OF_MEM;
02794                                                 (*ai->switchboards)[ai->switchboard_count] = si;
02795                                                 ai->switchboard_count++;
02796                                         aggr_found:
02797                                                 break;
02798                                         }
02799                                         case par_SipAddr:
02800                                                 si->sip_ip = s2ip4(&val);
02801                                                 if (si->sip_ip == 0) {
02802                                                         goto err_E_CFG;
02803                                                 }
02804                                                 break;  
02805 
02806                                         default:
02807                                                 BUG(MODULE_NAME": declare_switchboard_param: unknown id '%x\n", idx);
02808                                                 goto err_E_CFG;
02809                                 }
02810                                 if (!all_flag) break;
02811                                 si = si->next;
02812                         }
02813                 }
02814         }
02815         if (all_flag) {
02816                 return 0;
02817         }
02818 
02819         switchboard_count++;
02820 
02821         return 0;
02822 
02823 err_E_CFG:
02824         ERR(MODULE_NAME": declare_switchboard_param(#%d): parse error near \"%s\"\n", switchboard_count, s);
02825 
02826         return E_CFG;
02827 }
02828 
02829 static int declare_codec(modparam_t type, void* val) {
02830         int r;
02831         str *s;
02832         s = val;
02833         if (!s || !s->len) return 0;
02834         if (codec_sets) {
02835                 ERR(MODULE_NAME": declare_codec: codec declaration cannot follow codec set declaration\n");
02836                 return E_CFG;
02837         }
02838 
02839         if (!reg_codecs) {
02840                 int r;
02841                 if ((r = declare_def_codecs()) < 0) {
02842                         return r;
02843                 }
02844         }
02845         r = register_codec(s);
02846         if (r < 0) return r;
02847         return 0;
02848 }
02849 
02850 static int declare_codec_set(modparam_t type, void* val) {
02851         char *s;
02852         unsigned int cur_rights = 0;
02853         unsigned int cur_media_type = 0;
02854         struct codec_set_item *ci = NULL;
02855         enum param_id {
02856                 par_Name =                        0x000001,
02857                 par_Rights =              0x000002,
02858                 par_Codecs =              0x000003,
02859                 par_MediaType =           0x000004,             
02860                 par_MaxStreams =          0x000005,
02861                 par_ThrottleRTPBytes =    0x000100,
02862                 par_ThrottleRTCPBytes =   0x000101,
02863                 par_ThrottleRTPPackets =  0x000200,
02864                 par_ThrottleRTCPPackets = 0x000201
02865         };
02866         static struct parse_param_item params[] = {
02867                 {.name = "name", .id = par_Name},
02868                 {.name = "media_type", .id = par_MediaType},
02869                 {.name = "rights", .id = par_Rights},
02870                 {.name = "codecs", .id = par_Codecs},
02871                 {.name = "max_streams", .id = par_MaxStreams},
02872                 {.name = "rtp_bytes", .id = par_ThrottleRTPBytes},
02873                 {.name = "rtcp_bytes", .id = par_ThrottleRTCPBytes},
02874                 {.name = "rtp_packets", .id = par_ThrottleRTPPackets},
02875                 {.name = "rtcp_packets", .id = par_ThrottleRTCPPackets},
02876 
02877                 {.name = 0, .id = 0}
02878         };
02879 
02880         if (!val) return 0;
02881 
02882         if (!reg_codecs) {
02883                 int r;
02884                 if ((r = declare_def_codecs()) < 0) {
02885                         return r;
02886                 }
02887         }
02888         s = val;
02889         eat_spaces(s);
02890         if (!*s) return 0;
02891         /* parse param: name=;media_type=audio,video;rights=<int>;codecs=<codec1>,<codec2>,..;max_streams=2 */
02892         while (*s) {
02893                 str val;
02894                 int idx, media_type;
02895                 char *p, *pend;
02896                 struct codec_set_item **prev_ci;
02897 
02898                 idx = parse_next_param(&s, &params, &val);
02899                 if (idx < 0) return E_CFG;
02900 
02901                 if (params[idx].id != par_Name && !ci) {
02902                         ERR(MODULE_NAME": declare_codec_set: name must be the first param\n");
02903                         return E_CFG;
02904                 }
02905 
02906                 switch (params[idx].id) {
02907                         case par_Name:
02908                                 ci = find_codec_set(&val, &prev_ci);
02909                                 if (!ci) {
02910                                         int media_type;
02911                                         ci = pkg_malloc(sizeof(*ci));
02912                                         if (!ci) return E_OUT_OF_MEM;
02913                                         memset(ci, 0, sizeof(*ci));
02914                                         for (media_type=0; media_type<NUM_MEDIA_TYPES; media_type++) {
02915                                                 ci->media_types[media_type].codec_rights = pkg_malloc(sizeof((*ci->media_types[media_type].codec_rights)[0])*(reg_codec_count+1));
02916                                                 if (!ci->media_types[media_type].codec_rights) return E_OUT_OF_MEM;
02917                                                 memset(ci->media_types[media_type].codec_rights, 0, sizeof((*ci->media_types[media_type].codec_rights)[0])*(reg_codec_count+1));
02918                                                 ci->media_types[media_type].throttle.max_streams = -1;
02919                                         }                                       
02920                                         ci->name = val;
02921                                         ci->next = (*prev_ci);                                  
02922                                         (*prev_ci) = ci;
02923                                 }
02924                                 break;
02925                         case par_MediaType:
02926                                 cur_media_type = 0;
02927                                 if (val.len == 1 && val.s[0] == '*') {
02928                                         for (media_type=0; media_type<NUM_MEDIA_TYPES; media_type++) {
02929                                                 cur_media_type |= 1 << media_type;
02930                                         }
02931                                 }
02932                                 else {
02933                                         p = val.s;
02934                                         pend = p + val.len;
02935                                         while (p < pend) {
02936                                                 str s2;
02937                                                 s2.s = p;
02938                                                 while (p < pend && is_alpha(*p)) p++;
02939                                                 s2.len = p - s2.s;
02940                                                 cur_media_type |= 1 << name2media_type(&s2);
02941                                                 while (p < pend && !is_alpha(*p)) p++;
02942                                         }
02943                                 }
02944                                 break;
02945                         case par_Rights:
02946                                 val.s[val.len] = '\0'; /* we need not save value, it's already parsed */
02947                                 cur_rights = atol(val.s);
02948                                 break;
02949                         case par_MaxStreams:
02950                                 val.s[val.len] = '\0'; /* we need not save value, it's already parsed */
02951                                 for (media_type=0; media_type<NUM_MEDIA_TYPES; media_type++) {
02952                                         if (cur_media_type & (1<<media_type)) {
02953                                                 ci->media_types[media_type].throttle.max_streams = atol(val.s);
02954                                         }
02955                                 }
02956                                 break;
02957                         case par_ThrottleRTPBytes:
02958                         case par_ThrottleRTCPBytes:
02959                                 val.s[val.len] = '\0'; /* we need not save value, it's already parsed */
02960                                 for (media_type=0; media_type<NUM_MEDIA_TYPES; media_type++) {
02961                                         if (cur_media_type & (1<<media_type)) {
02962                                                 ci->media_types[media_type].throttle.bandwidth[params[idx].id&1].bytes = atol(val.s);
02963                                         }
02964                                 }
02965                                 break;
02966                         case par_ThrottleRTPPackets:
02967                         case par_ThrottleRTCPPackets:
02968                                 val.s[val.len] = '\0'; /* we need not save value, it's already parsed */
02969                                 for (media_type=0; media_type<NUM_MEDIA_TYPES; media_type++) {
02970                                         if (cur_media_type & (1<<media_type)) {
02971                                                 ci->media_types[media_type].throttle.bandwidth[params[idx].id&1].packets = atol(val.s);
02972                                         }
02973                                 }
02974                                 break;
02975                         case par_Codecs:
02976                                 if (val.len == 1 && val.s[0] == '*') {
02977                                         int codec_id;
02978                                         for (media_type=0; media_type<NUM_MEDIA_TYPES; media_type++) {
02979                                                 if (cur_media_type & (1<<media_type)) {
02980                                                         for (codec_id=0; codec_id<reg_codec_count; codec_id++) {
02981                                                                 (*ci->media_types[media_type].codec_rights)[codec_id] = cur_rights;
02982                                                         }
02983                                                 }
02984                                         }
02985                                 }
02986                                 else {
02987                                         p = val.s;
02988                                         pend = p + val.len;
02989                                         while (p < pend) {
02990                                                 str s2;
02991                                                 s2.s = p;
02992                                                 while (p < pend && is_alpha(*p)) p++;
02993                                                 s2.len = p - s2.s;
02994                                                 for (media_type=0; media_type<NUM_MEDIA_TYPES; media_type++) {
02995                                                         if (cur_media_type & (1<<media_type)) {
02996                                                                 (*ci->media_types[media_type].codec_rights)[name2codec_id(&s2, NULL)] = cur_rights;
02997                                                         }
02998                                                 }
02999                                                 while (p < pend && !is_alpha(*p)) p++;
03000                                         }
03001                                 }
03002                                 break;
03003                         default:
03004                                 BUG(MODULE_NAME": declare_codec_set: unknown id '%x\n", params[idx].id);
03005                                 return E_CFG;
03006                 }
03007 
03008         }
03009 
03010         return 0;
03011 }
03012 
03013 static cmd_export_t cmds[] = {
03014         {MODULE_NAME "_alloc",     rtpproxy_alloc,         1, rtpproxy_alloc_update_fixup,       REQUEST_ROUTE | ONREPLY_ROUTE },
03015         {MODULE_NAME "_update",    rtpproxy_update,        2, rtpproxy_alloc_update_fixup,      REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE },
03016         {MODULE_NAME "_adjust_timeout", rtpproxy_adjust_timeout, 2, rtpproxy_alloc_update_fixup,      REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE },
03017         {MODULE_NAME "_delete",    rtpproxy_delete,        1, rtpproxy_delete_fixup,      REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE },
03018         {MODULE_NAME "_set_param", rtpproxy_set_param,     2, rtpproxy_set_param_fixup,   REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE },
03019         {MODULE_NAME "_authorize_media", rtpproxy_authorize_media, 0, NULL,       REQUEST_ROUTE | ONREPLY_ROUTE },
03020 
03021         {0, 0, 0, 0, 0}
03022 };
03023 
03024 static param_export_t params[] = {
03025         {"config",                PARAM_STR | PARAM_USE_FUNC, &declare_config}, 
03026         {"switchboard",           PARAM_STRING | PARAM_USE_FUNC, &declare_switchboard_param},
03027         {"hostname",              PARAM_STRING | PARAM_USE_FUNC, &declare_hostname},
03028         {"codec_set",             PARAM_STRING | PARAM_USE_FUNC, &declare_codec_set},
03029         {"rpc_heartbeat_timeout", PARAM_INT, &rpc_heartbeat_timeout},
03030         {"declare_codec",         PARAM_STR | PARAM_USE_FUNC, &declare_codec}, 
03031         {0, 0, 0}
03032 };
03033 
03034 struct module_exports exports = {
03035         MODULE_NAME,
03036         cmds,
03037         0,       /* RPC methods */
03038         params,
03039         mod_init,
03040         0, /* reply processing */
03041         mod_cleanup, /* destroy function */
03042         0, /* on_break */
03043         child_init
03044 };
03045 
03046 
03047 #if !defined(NO_SHARED_LIBS) || NO_SHARED_LIBS==0
03048 /* make compiler happy and give it missing symbols */
03049 #ifdef IPT_RTPPROXY_IPTABLES_API
03050 #include <iptables.h>
03051 #undef IPT_RTPPROXY_IPTABLES_API
03052 #else
03053 #include <xtables.h>
03054 #endif
03055 
03056 #include <stdarg.h>
03057 
03058 #ifdef xtables_error
03059 /* iptables 1.4.8 */
03060 
03061 struct xtables_globals *xt_params = NULL;
03062 int xtables_check_inverse(const char option[], int *invert, int *optind, int argc, char **argv) {
03063         return FALSE;
03064 }
03065 void xtables_register_target(struct xtables_target *me) {
03066 }
03067 
03068 #else  /* xtables_error */
03069 
03070 #ifdef _IPTABLES_COMMON_H
03071 /* old iptables API, it uses iptables_common.h (instead of xtables.h) included from iptables.h */
03072 /* #ifndef XTABLES_VERSION ... optional test */
03073 #define IPT_RTPPROXY_IPTABLES_API 1
03074 #endif
03075 
03076 #ifdef IPT_RTPPROXY_IPTABLES_API
03077 void register_target(struct iptables_target *me) {
03078 }
03079 #else
03080 void xtables_register_target(struct xtables_target *me) {
03081 }
03082 #endif
03083 
03084 #if IPT_RTPPROXY_IPTABLES_API
03085 void exit_error(enum exittype status, char *msg, ...)
03086 #else
03087 void exit_error(enum exittype status, const char *msg, ...)
03088 #endif
03089 {
03090         va_list args;
03091         
03092         va_start(args, msg);
03093 //      ERR(msg/*, args*/);  /* TODO: how to pass ... to macro? */
03094         ERR(MODULE_NAME": %s", msg);
03095         va_end(args);
03096 }
03097 
03098 int check_inverse(const char option[], int *invert, int *optind, int argc) {
03099         return 0;
03100 }
03101 #endif  /* xtables_error */
03102 
03103 #endif
03104 
03105