diff --git a/.gitignore b/.gitignore index e6f86c4..37df1f2 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ opensips-1.6.3-tls_src.tar.gz /opensips-1.10.3_src.tar.gz /opensips-1.10.4_src.tar.gz /opensips-1.10.5_src.tar.gz +/opensips-1.11.6.tar.gz diff --git a/opensips-0001-Consistently-use-rtpproxy-switches.patch b/opensips-0001-Consistently-use-rtpproxy-switches.patch index ae8cb49..92fd29d 100644 --- a/opensips-0001-Consistently-use-rtpproxy-switches.patch +++ b/opensips-0001-Consistently-use-rtpproxy-switches.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Consistently use rtpproxy switches Signed-off-by: Peter Lemenkov diff --git a/modules/rtpproxy/doc/rtpproxy_admin.xml b/modules/rtpproxy/doc/rtpproxy_admin.xml -index 27b66b0..f671e49 100644 +index 08ce79b..1a73325 100644 --- a/modules/rtpproxy/doc/rtpproxy_admin.xml +++ b/modules/rtpproxy/doc/rtpproxy_admin.xml -@@ -581,7 +581,7 @@ rtpproxy_offer(); +@@ -523,7 +523,7 @@ modparam("rtpproxy", "rtpp_notify_socket", "tcp:10.10.10.10:9999") connection information. @@ -18,10 +18,10 @@ index 27b66b0..f671e49 100644 diff --git a/modules/rtpproxy/rtpproxy.c b/modules/rtpproxy/rtpproxy.c -index 691554d..39bcf6d 100644 +index 444263a..602d66b 100644 --- a/modules/rtpproxy/rtpproxy.c +++ b/modules/rtpproxy/rtpproxy.c -@@ -3153,8 +3153,6 @@ force_rtp_proxy_body(struct sip_msg* msg, struct force_rtpp_args *args) +@@ -3495,8 +3495,6 @@ force_rtp_proxy_body(struct sip_msg* msg, struct force_rtpp_args *args, pv_spec_ enable_notification = 1; break; diff --git a/opensips-0003-Removed-all-async-Oracle-operations-they-didn-t-work.patch b/opensips-0003-Removed-all-async-Oracle-operations-they-didn-t-work.patch index b844a22..0569c21 100644 --- a/opensips-0003-Removed-all-async-Oracle-operations-they-didn-t-work.patch +++ b/opensips-0003-Removed-all-async-Oracle-operations-they-didn-t-work.patch @@ -5,352 +5,8 @@ Subject: [PATCH] Removed all async Oracle operations - they didn't work well anyway -diff --git a/modules/db_oracle/asynch.c b/modules/db_oracle/asynch.c -deleted file mode 100644 -index d1bbe84..0000000 ---- a/modules/db_oracle/asynch.c -+++ /dev/null -@@ -1,270 +0,0 @@ --/* -- * $Id$ -- * -- * Oracle module interface -- * -- * Copyright (C) 2007,2008 TRUNK MOBILE -- * -- * This file is part of opensips, a free SIP server. -- * -- * opensips is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License as published by -- * the Free Software Foundation; either version 2 of the License, or -- * (at your option) any later version -- * -- * opensips is distributed in the hope that it will be useful, -- * but WITHOUT ANY WARRANTY; without even the implied warranty of -- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- * GNU General Public License for more details. -- * -- * You should have received a copy of the GNU General Public License -- * along with this program; if not, write to the Free Software -- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- */ --/* -- * History: -- * -------- -- */ -- --#include --#include --#include --#include --#include "../../dprint.h" --#include "../../sr_module.h" --#include "ora_con.h" --#include "asynch.h" -- --#define MAX_TIMEOUT_S 10 --#define MIN_TIMEOUT_MS 100 -- -- --/* Default is 3.0 second */ --static struct timeval request_tm = { .tv_sec = 3, .tv_usec = 0 }; -- --/* Default is 0.2 second */ --static struct timeval restore_tm = { .tv_sec = 0, .tv_usec = 200*1000 }; --static const struct timeval defrest_tm = { .tv_sec = 0, .tv_usec = 200*1000 }; -- --static int synch_mode; --static int cur_asynch_mode; --static struct timeval wtm; -- -- --static __inline__ int is_zero_tm(const struct timeval* tv) --{ -- return !tv->tv_usec && !tv->tv_sec; --} -- -- --/* -- * parse timeout value in syntax: nnn.mmm (sec/ms) -- */ --static int set_tv(unsigned type, const char* val, struct timeval* tv) --{ -- char *eptr; -- unsigned long s, ms; -- double dv; -- -- if (type != STR_PARAM) { -- LM_ERR("type of parameter is no STR\n"); -- return -1; -- } -- -- if (!val || !*val) { -- LM_ERR("empty parameter\n"); -- return -1; -- } -- -- errno = 0; -- dv = strtod(val, &eptr); -- -- if (*eptr) { -- LM_ERR("invalid parameter string\n"); -- return -2; -- } -- -- if ( errno -- || dv > (double)MAX_TIMEOUT_S -- || (dv && dv < ((double)MIN_TIMEOUT_MS)/1000)) -- { -- LM_ERR("value must be between 0.%u and %u.0\n", -- MIN_TIMEOUT_MS, MAX_TIMEOUT_S); -- return -3; -- } -- -- s = (unsigned)dv; -- dv -= (double)s; -- ms = (unsigned)(dv * 1000); -- tv->tv_sec = (time_t)s; -- tv->tv_usec = (suseconds_t)ms; -- return 0; --} -- -- --/* -- * set operation timeout -- */ --int set_timeout(unsigned type, const char* val) --{ -- int rc = set_tv(type, val, &request_tm); -- -- if (!rc) { -- synch_mode = is_zero_tm(&request_tm); -- if (!synch_mode && is_zero_tm(&restore_tm)) -- restore_tm = defrest_tm; -- } -- -- return rc; --} -- -- --/* -- * set (re)connect timeout -- */ --int set_reconnect(unsigned type, const char* val) --{ -- int rc = set_tv(type, val, &restore_tm); -- -- if (!synch_mode && is_zero_tm(&restore_tm)) { -- LM_WARN("in asyncronus mode reconnect time can't be zero. " -- "Set default value\n"); -- restore_tm = defrest_tm; -- } -- -- return rc; --} -- -- --static sword change_mode(ora_con_t* con) --{ -- return OCIAttrSet(con->svchp, OCI_HTYPE_SVCCTX, NULL, 0, -- OCI_ATTR_NONBLOCKING_MODE, con->errhp); --} -- -- --/* -- * start timelimited operation (if work in synch mode return SUCCESS) -- */ --sword begin_timelimit(ora_con_t* con, int connect) --{ -- struct timeval* tv; -- sword status; -- -- if (synch_mode) -- return OCI_SUCCESS; -- -- if (connect || cur_asynch_mode) { -- ub1 mode; -- -- status = OCIAttrGet(con->svchp, OCI_HTYPE_SVCCTX, &mode, NULL, -- OCI_ATTR_NONBLOCKING_MODE, con->errhp); -- if (status != OCI_SUCCESS) -- return status; -- -- if (mode) { -- status = change_mode(con); -- if (status != OCI_SUCCESS) -- return status; -- } -- cur_asynch_mode = 0; -- } -- -- status = change_mode(con); -- if (status != OCI_SUCCESS && connect >= 0) -- return status; -- -- cur_asynch_mode = 1; -- -- gettimeofday(&wtm, NULL); -- tv = &request_tm; -- if (connect) -- tv = &restore_tm; -- wtm.tv_sec += tv->tv_sec; -- wtm.tv_usec += tv->tv_usec; -- if (wtm.tv_usec >= 1000000) { -- wtm.tv_usec -= 1000000; -- ++wtm.tv_sec; -- } -- -- return OCI_SUCCESS; --} -- -- --static sword remap_status(ora_con_t* con, sword status) --{ -- sword code; -- -- if ( status == OCI_ERROR -- && OCIErrorGet(con->errhp, 1, NULL, &code, -- NULL, 0, OCI_HTYPE_ERROR) == OCI_SUCCESS -- && (code == 3123 /*|| code == 3127*/)) -- { -- status = OCI_STILL_EXECUTING; -- } -- return status; --} -- -- --/* -- * check completion of timelimited operation (if work in synch mode return 0) -- */ --int wait_timelimit(ora_con_t* con, sword status) --{ -- struct timeval cur; -- -- if (!cur_asynch_mode) -- return 0; -- -- if (remap_status(con, status) != OCI_STILL_EXECUTING) -- return 0; -- -- gettimeofday(&cur, NULL); -- return ( cur.tv_sec < wtm.tv_sec -- || (cur.tv_sec == wtm.tv_sec && cur.tv_usec < wtm.tv_usec)); --} -- -- --/* -- * close current timelimited operation and disconnect if timeout occured -- * return true only if work in asynch mode and timeout detect -- */ --int done_timelimit(ora_con_t* con, sword status) --{ -- int ret = 0; -- -- if (!cur_asynch_mode) -- return 0; -- -- if (remap_status(con, status) == OCI_STILL_EXECUTING) { -- sword code; -- -- status = OCIBreak(con->svchp, con->errhp); -- if (status != OCI_SUCCESS) -- LM_ERR("driver: %s\n", -- db_oracle_error(con, status)); -- -- status = OCIReset(con->svchp, con->errhp); -- if ( status == OCI_ERROR -- && OCIErrorGet(con->errhp, 1, NULL, &code, -- NULL, 0, OCI_HTYPE_ERROR) == OCI_SUCCESS -- && code == 1013) -- { -- status = OCI_SUCCESS; -- } -- if (status != OCI_SUCCESS) -- LM_ERR("driver: %s\n", -- db_oracle_error(con, status)); -- db_oracle_disconnect(con); -- ++ret; -- } else { -- status = change_mode(con); -- if (status != OCI_SUCCESS) { -- LM_ERR("driver: %s\n", db_oracle_error(con, status)); -- ++ret; -- } else { -- cur_asynch_mode = 0; -- } -- } -- return ret; --} -diff --git a/modules/db_oracle/asynch.h b/modules/db_oracle/asynch.h -deleted file mode 100644 -index 9694088..0000000 ---- a/modules/db_oracle/asynch.h -+++ /dev/null -@@ -1,62 +0,0 @@ --/* -- * $Id$ -- * -- * Oracle module interface -- * -- * Copyright (C) 2007,2008 TRUNK MOBILE -- * -- * This file is part of opensips, a free SIP server. -- * -- * opensips is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License as published by -- * the Free Software Foundation; either version 2 of the License, or -- * (at your option) any later version -- * -- * opensips is distributed in the hope that it will be useful, -- * but WITHOUT ANY WARRANTY; without even the implied warranty of -- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- * GNU General Public License for more details. -- * -- * You should have received a copy of the GNU General Public License -- * along with this program; if not, write to the Free Software -- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- * -- * History: -- * -------- -- */ -- --#ifndef ASYNCH_H --#define ASYNCH_H -- --#include --#include "ora_con.h" -- -- --/* -- * parse timeout value for operation in syntax: nnn.mmm (sec/ms) -- */ --int set_timeout(unsigned type, const char* val); -- --/* -- * parse timeout value for reconnect in syntax: nnn.mmm (sec/ms) -- */ --int set_reconnect(unsigned type, const char* val); -- -- --/* -- * start timelimited operation (if work in synch mode return SUCCESS) -- */ --sword begin_timelimit(ora_con_t* con, int connect); -- --/* -- * check completion of timelimited operation (if work in synch mode return 0) -- */ --int wait_timelimit(ora_con_t* con, sword status); -- --/* -- * close current timelimited operation and disconnect if timeout occured -- * return true only if work in asynch mode and timeout detect -- */ --int done_timelimit(ora_con_t* con, sword status); -- --#endif /* ASYNCH_H */ diff --git a/modules/db_oracle/db_oracle.c b/modules/db_oracle/db_oracle.c -index 70cfe40..40c7692 100644 +index e2f370a..6cc21c5 100644 --- a/modules/db_oracle/db_oracle.c +++ b/modules/db_oracle/db_oracle.c @@ -31,7 +31,7 @@ @@ -363,7 +19,7 @@ index 70cfe40..40c7692 100644 static int oracle_mod_init(void); static void destroy(void); diff --git a/modules/db_oracle/dbase.c b/modules/db_oracle/dbase.c -index d129e11..be2352e 100644 +index 32459c6..c136af0 100644 --- a/modules/db_oracle/dbase.c +++ b/modules/db_oracle/dbase.c @@ -36,7 +36,7 @@ @@ -383,11 +39,10 @@ index d129e11..be2352e 100644 ora_con_t* con = CON_ORA(_h); query_data_t* pqd = con->pqdata; size_t hc = pqd->_n + pqd->_nw; -@@ -223,32 +222,16 @@ static int db_oracle_submit_query(const db_con_t* _h, const str* _s) - (unsigned)hc); +@@ -224,31 +223,15 @@ static int db_oracle_submit_query(const db_con_t* _h, const str* _s) return -1; } -- + - if (!pqd->_rs) { - /* - * This method is at ~25% faster as set OCI_COMMIT_ON_SUCCESS @@ -401,7 +56,7 @@ index d129e11..be2352e 100644 - tmps.s = st_buf; - _s = &tmps; - } - +- - pass = 1; - if (!con->connected) { + if (con->connected != 2) { @@ -606,21 +261,19 @@ index 73ced27..f03f753 100644 return status; } diff --git a/modules/db_oracle/ora_con.h b/modules/db_oracle/ora_con.h -index 5835ffe..7267bee 100644 +index 6635ea0..7267bee 100644 --- a/modules/db_oracle/ora_con.h +++ b/modules/db_oracle/ora_con.h -@@ -50,7 +50,8 @@ struct ora_con { +@@ -50,6 +50,7 @@ struct ora_con { int connected; /* Authorized session started */ int bindpos; /* Last Bind handle position */ -- + pthread_t timer; -+ + query_data_t* pqdata; /* Temporary: cb data for submit_query/store_result */ - int uri_len; diff --git a/modules/db_oracle/res.c b/modules/db_oracle/res.c -index e9e6233..2c625f8 100644 +index d03aff8..35fd4d3 100644 --- a/modules/db_oracle/res.c +++ b/modules/db_oracle/res.c @@ -32,7 +32,7 @@ @@ -743,13 +396,13 @@ index e9e6233..2c625f8 100644 + memcpy(ROW_VALUES(&RES_ROWS(_r)[i]), ROW_VALUES(&orow_it->hdr), sizeof(db_val_t)*RES_COL_N(_r)); + ROW_N(&RES_ROWS(_r)[i]) = ROW_N(&orow_it->hdr); + ++i; -+ } + } + + for (orow_it = orow_list; orow_it; orow_it = orow_list) { + orow_list = orow_it->next; + pkg_free(orow_it); + orow_it = NULL; - } ++ } + return 0; + ora_err: diff --git a/opensips-0004-db_oracle-Fix-default-ret-result.patch b/opensips-0004-db_oracle-Fix-default-ret-result.patch index 30de5da..8b90367 100644 --- a/opensips-0004-db_oracle-Fix-default-ret-result.patch +++ b/opensips-0004-db_oracle-Fix-default-ret-result.patch @@ -5,7 +5,7 @@ Subject: [PATCH] [db_oracle] Fix default ret result Signed-off-by: Peter Lemenkov diff --git a/modules/db_oracle/res.c b/modules/db_oracle/res.c -index 2c625f8..22f10ee 100644 +index 35fd4d3..e56e2ad 100644 --- a/modules/db_oracle/res.c +++ b/modules/db_oracle/res.c @@ -215,8 +215,8 @@ set_int: diff --git a/opensips-0005-Don-t-modify-interim-return-value.patch b/opensips-0005-Don-t-modify-interim-return-value.patch new file mode 100644 index 0000000..ac3ff18 --- /dev/null +++ b/opensips-0005-Don-t-modify-interim-return-value.patch @@ -0,0 +1,136 @@ +From: Peter Lemenkov +Date: Fri, 11 Jan 2013 14:32:19 +0400 +Subject: [PATCH] Don't modify interim return value. + +Signed-off-by: Peter Lemenkov + +diff --git a/modules/sipmsgops/codecs.c b/modules/sipmsgops/codecs.c +index 3d3ff31..9270ecb 100644 +--- a/modules/sipmsgops/codecs.c ++++ b/modules/sipmsgops/codecs.c +@@ -349,8 +349,6 @@ static int do_for_all_streams(struct sip_msg* msg, str* str1,str * str2, + + } + +- if( rez <0 ) +- rez = 0; + return rez; + } + +@@ -688,7 +686,7 @@ int codec_find (struct sip_msg* msg, char* str1 ) + LM_DBG("searching for codec <%.*s> \n",res.len,res.s); + + if( do_for_all_streams( msg, &res, NULL, NULL, +- FIND, DESC_NAME) == 0) ++ FIND, DESC_NAME) <= 0) + return -1; + + return 1; +@@ -699,7 +697,7 @@ int codec_find_re (struct sip_msg* msg, char* str1 ) + { + + if( do_for_all_streams(msg, NULL, NULL, (regex_t*)str1, +- FIND, DESC_REGEXP) == 0) ++ FIND, DESC_REGEXP) <= 0) + return -1; + + return 1; +@@ -726,7 +724,7 @@ int codec_find_clock (struct sip_msg* msg, char* str1,char * str2 ) + codec.len,codec.s,clock.len,clock.s); + + if( do_for_all_streams( msg, &codec, &clock, NULL, +- FIND, DESC_NAME_AND_CLOCK) == 0) ++ FIND, DESC_NAME_AND_CLOCK) <= 0) + return -1; + + return 1; +@@ -746,7 +744,7 @@ int codec_delete (struct sip_msg* msg, char* str1 ) + LM_DBG("deleting codec <%.*s> \n",res.len,res.s); + + if( do_for_all_streams( msg, &res, NULL, NULL, +- DELETE, DESC_NAME) == 0) ++ DELETE, DESC_NAME) <= 0) + return -1; + return 1; + } +@@ -755,7 +753,7 @@ int codec_delete (struct sip_msg* msg, char* str1 ) + int codec_delete_re (struct sip_msg* msg, char* str1 ) + { + if( do_for_all_streams( msg, NULL, NULL, (regex_t*) str1, +- DELETE, DESC_REGEXP) == 0) ++ DELETE, DESC_REGEXP) <= 0) + return -1; + return 1; + } +@@ -764,7 +762,7 @@ int codec_delete_re (struct sip_msg* msg, char* str1 ) + int codec_delete_except_re (struct sip_msg* msg, char* str1 ) + { + if( do_for_all_streams( msg, NULL, NULL, (regex_t*) str1, +- DELETE, DESC_REGEXP_COMPLEMENT) == 0) ++ DELETE, DESC_REGEXP_COMPLEMENT) <= 0) + return -1; + return 1; + } +@@ -790,7 +788,7 @@ int codec_delete_clock (struct sip_msg* msg, char* str1 ,char * str2) + codec.len,codec.s,clock.len,clock.s); + + if( do_for_all_streams( msg, &codec, &clock, NULL, +- DELETE, DESC_NAME_AND_CLOCK) == 0) ++ DELETE, DESC_NAME_AND_CLOCK) <= 0) + return -1; + return 1; + } +@@ -809,7 +807,7 @@ int codec_move_up (struct sip_msg* msg, char* str1) + LM_DBG("moving up codec <%.*s> \n",res.len,res.s); + + if( do_for_all_streams( msg, &res, NULL, NULL, +- ADD_TO_FRONT, DESC_NAME) == 0) ++ ADD_TO_FRONT, DESC_NAME) <= 0) + return -1; + return 1; + } +@@ -818,7 +816,7 @@ int codec_move_up (struct sip_msg* msg, char* str1) + int codec_move_up_re (struct sip_msg* msg, char* str1) + { + if( do_for_all_streams( msg, NULL, NULL, (regex_t*)str1, +- ADD_TO_FRONT, DESC_REGEXP) == 0) ++ ADD_TO_FRONT, DESC_REGEXP) <= 0) + return -1; + return 1; + } +@@ -844,7 +842,7 @@ int codec_move_up_clock (struct sip_msg* msg, char* str1 ,char * str2) + codec.len,codec.s,clock.len,clock.s); + + if( do_for_all_streams( msg, &codec, &clock, NULL, +- ADD_TO_FRONT, DESC_NAME_AND_CLOCK) == 0) ++ ADD_TO_FRONT, DESC_NAME_AND_CLOCK) <= 0) + return -1; + return 1; + } +@@ -863,7 +861,7 @@ int codec_move_down (struct sip_msg* msg, char* str1) + LM_DBG("moving down codec <%.*s> \n",res.len,res.s); + + if( do_for_all_streams( msg, &res, NULL, NULL, +- ADD_TO_BACK, DESC_NAME) == 0) ++ ADD_TO_BACK, DESC_NAME) <= 0) + return -1; + return 1; + } +@@ -872,7 +870,7 @@ int codec_move_down (struct sip_msg* msg, char* str1) + int codec_move_down_re (struct sip_msg* msg, char* str1) + { + if( do_for_all_streams( msg, NULL, NULL, (regex_t*)str1, +- ADD_TO_BACK, DESC_REGEXP) == 0) ++ ADD_TO_BACK, DESC_REGEXP) <= 0) + return -1; + return 1; + } +@@ -898,7 +896,7 @@ int codec_move_down_clock (struct sip_msg* msg, char* str1 ,char * str2) + codec.len,codec.s,clock.len,clock.s); + + if( do_for_all_streams( msg, &codec, &clock, NULL, +- ADD_TO_BACK, DESC_NAME_AND_CLOCK) == 0) ++ ADD_TO_BACK, DESC_NAME_AND_CLOCK) <= 0) + return -1; + return 1; + } diff --git a/opensips-0005-Fix-for-old-Perl-in-RHEL5.patch b/opensips-0005-Fix-for-old-Perl-in-RHEL5.patch deleted file mode 100644 index 9eea5e6..0000000 --- a/opensips-0005-Fix-for-old-Perl-in-RHEL5.patch +++ /dev/null @@ -1,80 +0,0 @@ -From: Peter Lemenkov -Date: Tue, 19 Mar 2013 13:36:44 +0400 -Subject: [PATCH] Fix for old Perl in RHEL5 - -Partially revert a3cec180516b16ac84fc7e457cec2a03039e04bf - -Signed-off-by: Peter Lemenkov - -diff --git a/modules/db_perlvdb/perlvdb_conv.c b/modules/db_perlvdb/perlvdb_conv.c -index 511e076..ce1d45c 100644 ---- a/modules/db_perlvdb/perlvdb_conv.c -+++ b/modules/db_perlvdb/perlvdb_conv.c -@@ -215,7 +215,6 @@ SV *cond2perlcond(db_key_t key, db_op_t op, db_val_t* val) { - - int perlresult2dbres(SV *perlres, db_res_t **r) { - -- HV * result = NULL; - SV *colarrayref = NULL; - AV *colarray = NULL; - SV *acol = NULL; -@@ -253,14 +252,11 @@ int perlresult2dbres(SV *perlres, db_res_t **r) { - goto error; - } - -- result = (HV*)SvRV(perlres); -- - /* Memory allocation for C side result structure */ - *r = db_new_result(); - /* Fetch column definitions */ -- colarrayref = *hv_fetchs(result, PERL_VDB_COLDEFSMETHOD, 0); -- /* colarrayref = perlvdb_perlmethod(perlres, PERL_VDB_COLDEFSMETHOD, -- NULL, NULL, NULL, NULL); */ -+ colarrayref = perlvdb_perlmethod(perlres, PERL_VDB_COLDEFSMETHOD, -+ NULL, NULL, NULL, NULL); - if (!(SvROK(colarrayref))) goto error; - colarray = (AV *)SvRV(colarrayref); - -@@ -297,25 +293,9 @@ int perlresult2dbres(SV *perlres, db_res_t **r) { - - - } -- if(hv_exists(result, "rows", 4)){ -- rowarrayref =(SV*) hv_fetchs(result, "rows", 0); -- }else{ -- (*r)->n = 0; -- (*r)->res_rows = 0; -- (*r)->last_row = 0; -- goto end; -+ rowarrayref = perlvdb_perlmethod(perlres, PERL_VDB_ROWSMETHOD, -+ NULL, NULL, NULL, NULL); - -- } -- -- if(rowarrayref){ -- rowarrayref = *((SV**)rowarrayref); -- }else{ -- (*r)->n = 0; -- (*r)->res_rows = 0; -- (*r)->last_row = 0; -- goto end; -- -- } - if (!(SvROK(rowarrayref))) { /* Empty result set */ - (*r)->n = 0; - (*r)->res_rows = 0; -@@ -354,11 +334,10 @@ int perlresult2dbres(SV *perlres, db_res_t **r) { - cur_val.nul = 1; - continue; - } -- atypesv = *hv_fetchs((HV*)SvRV(aelement),PERL_VDB_TYPEMETHOD,0); /*aelement->{type} */ -- atype = SvIV(atypesv); -- /*atypesv = perlvdb_perlmethod(aelement, -+ atypesv = perlvdb_perlmethod(aelement, - PERL_VDB_TYPEMETHOD, -- NULL, NULL, NULL, NULL);*/ -+ NULL, NULL, NULL, NULL); -+ atype = SvIV(atypesv); - aval = perlvdb_perlmethod(aelement, PERL_VDB_DATAMETHOD, - NULL, NULL, NULL, NULL); - (*r)->rows[i].values[j].type = atype; diff --git a/opensips-0006-Don-t-modify-interim-return-value.patch b/opensips-0006-Don-t-modify-interim-return-value.patch deleted file mode 100644 index 2b73a20..0000000 --- a/opensips-0006-Don-t-modify-interim-return-value.patch +++ /dev/null @@ -1,136 +0,0 @@ -From: Peter Lemenkov -Date: Fri, 11 Jan 2013 14:32:19 +0400 -Subject: [PATCH] Don't modify interim return value. - -Signed-off-by: Peter Lemenkov - -diff --git a/modules/sipmsgops/codecs.c b/modules/sipmsgops/codecs.c -index ac6b14f..274bff7 100644 ---- a/modules/sipmsgops/codecs.c -+++ b/modules/sipmsgops/codecs.c -@@ -349,8 +349,6 @@ static int do_for_all_streams(struct sip_msg* msg, str* str1,str * str2, - - } - -- if( rez <0 ) -- rez = 0; - return rez; - } - -@@ -688,7 +686,7 @@ int codec_find (struct sip_msg* msg, char* str1 ) - LM_DBG("searching for codec <%.*s> \n",res.len,res.s); - - if( do_for_all_streams( msg, &res, NULL, NULL, -- FIND, DESC_NAME) == 0) -+ FIND, DESC_NAME) <= 0) - return -1; - - return 1; -@@ -699,7 +697,7 @@ int codec_find_re (struct sip_msg* msg, char* str1 ) - { - - if( do_for_all_streams(msg, NULL, NULL, (regex_t*)str1, -- FIND, DESC_REGEXP) == 0) -+ FIND, DESC_REGEXP) <= 0) - return -1; - - return 1; -@@ -726,7 +724,7 @@ int codec_find_clock (struct sip_msg* msg, char* str1,char * str2 ) - codec.len,codec.s,clock.len,clock.s); - - if( do_for_all_streams( msg, &codec, &clock, NULL, -- FIND, DESC_NAME_AND_CLOCK) == 0) -+ FIND, DESC_NAME_AND_CLOCK) <= 0) - return -1; - - return 1; -@@ -746,7 +744,7 @@ int codec_delete (struct sip_msg* msg, char* str1 ) - LM_DBG("deleting codec <%.*s> \n",res.len,res.s); - - if( do_for_all_streams( msg, &res, NULL, NULL, -- DELETE, DESC_NAME) == 0) -+ DELETE, DESC_NAME) <= 0) - return -1; - return 1; - } -@@ -755,7 +753,7 @@ int codec_delete (struct sip_msg* msg, char* str1 ) - int codec_delete_re (struct sip_msg* msg, char* str1 ) - { - if( do_for_all_streams( msg, NULL, NULL, (regex_t*) str1, -- DELETE, DESC_REGEXP) == 0) -+ DELETE, DESC_REGEXP) <= 0) - return -1; - return 1; - } -@@ -764,7 +762,7 @@ int codec_delete_re (struct sip_msg* msg, char* str1 ) - int codec_delete_except_re (struct sip_msg* msg, char* str1 ) - { - if( do_for_all_streams( msg, NULL, NULL, (regex_t*) str1, -- DELETE, DESC_REGEXP_COMPLEMENT) == 0) -+ DELETE, DESC_REGEXP_COMPLEMENT) <= 0) - return -1; - return 1; - } -@@ -790,7 +788,7 @@ int codec_delete_clock (struct sip_msg* msg, char* str1 ,char * str2) - codec.len,codec.s,clock.len,clock.s); - - if( do_for_all_streams( msg, &codec, &clock, NULL, -- DELETE, DESC_NAME_AND_CLOCK) == 0) -+ DELETE, DESC_NAME_AND_CLOCK) <= 0) - return -1; - return 1; - } -@@ -809,7 +807,7 @@ int codec_move_up (struct sip_msg* msg, char* str1) - LM_DBG("moving up codec <%.*s> \n",res.len,res.s); - - if( do_for_all_streams( msg, &res, NULL, NULL, -- ADD_TO_FRONT, DESC_NAME) == 0) -+ ADD_TO_FRONT, DESC_NAME) <= 0) - return -1; - return 1; - } -@@ -818,7 +816,7 @@ int codec_move_up (struct sip_msg* msg, char* str1) - int codec_move_up_re (struct sip_msg* msg, char* str1) - { - if( do_for_all_streams( msg, NULL, NULL, (regex_t*)str1, -- ADD_TO_FRONT, DESC_REGEXP) == 0) -+ ADD_TO_FRONT, DESC_REGEXP) <= 0) - return -1; - return 1; - } -@@ -844,7 +842,7 @@ int codec_move_up_clock (struct sip_msg* msg, char* str1 ,char * str2) - codec.len,codec.s,clock.len,clock.s); - - if( do_for_all_streams( msg, &codec, &clock, NULL, -- ADD_TO_FRONT, DESC_NAME_AND_CLOCK) == 0) -+ ADD_TO_FRONT, DESC_NAME_AND_CLOCK) <= 0) - return -1; - return 1; - } -@@ -863,7 +861,7 @@ int codec_move_down (struct sip_msg* msg, char* str1) - LM_DBG("moving down codec <%.*s> \n",res.len,res.s); - - if( do_for_all_streams( msg, &res, NULL, NULL, -- ADD_TO_BACK, DESC_NAME) == 0) -+ ADD_TO_BACK, DESC_NAME) <= 0) - return -1; - return 1; - } -@@ -872,7 +870,7 @@ int codec_move_down (struct sip_msg* msg, char* str1) - int codec_move_down_re (struct sip_msg* msg, char* str1) - { - if( do_for_all_streams( msg, NULL, NULL, (regex_t*)str1, -- ADD_TO_BACK, DESC_REGEXP) == 0) -+ ADD_TO_BACK, DESC_REGEXP) <= 0) - return -1; - return 1; - } -@@ -898,7 +896,7 @@ int codec_move_down_clock (struct sip_msg* msg, char* str1 ,char * str2) - codec.len,codec.s,clock.len,clock.s); - - if( do_for_all_streams( msg, &codec, &clock, NULL, -- ADD_TO_BACK, DESC_NAME_AND_CLOCK) == 0) -+ ADD_TO_BACK, DESC_NAME_AND_CLOCK) <= 0) - return -1; - return 1; - } diff --git a/opensips-0006-Return-actual-payload-ID-in-case-of-a-dynamic-payloa.patch b/opensips-0006-Return-actual-payload-ID-in-case-of-a-dynamic-payloa.patch new file mode 100644 index 0000000..7f5efb7 --- /dev/null +++ b/opensips-0006-Return-actual-payload-ID-in-case-of-a-dynamic-payloa.patch @@ -0,0 +1,67 @@ +From: Peter Lemenkov +Date: Fri, 11 Jan 2013 14:40:08 +0400 +Subject: [PATCH] Return actual payload ID in case of a dynamic payload + +Signed-off-by: Peter Lemenkov + +diff --git a/modules/sipmsgops/codecs.c b/modules/sipmsgops/codecs.c +index 9270ecb..0fe0a1c 100644 +--- a/modules/sipmsgops/codecs.c ++++ b/modules/sipmsgops/codecs.c +@@ -34,6 +34,7 @@ + #include "codecs.h" + #include "../../route.h" + #include "../../mod_fix.h" ++#include "../../ut.h" + + #define MAX_STREAMS 64 + +@@ -528,7 +529,9 @@ static int stream_process(struct sip_msg * msg, struct sdp_stream_cell *cell, + + if(op == FIND) + { +- ret = 1; ++ str2sint(&(payload->rtp_payload), &ret); ++ if( (ret >= 0) && (ret <= 34) ) /* if a fixed payload ID then just return 1 */ ++ ret = 1; + goto end; + } + +@@ -685,22 +688,12 @@ int codec_find (struct sip_msg* msg, char* str1 ) + + LM_DBG("searching for codec <%.*s> \n",res.len,res.s); + +- if( do_for_all_streams( msg, &res, NULL, NULL, +- FIND, DESC_NAME) <= 0) +- return -1; +- +- return 1; +- ++ return do_for_all_streams( msg, &res, NULL, NULL, FIND, DESC_NAME); + } + + int codec_find_re (struct sip_msg* msg, char* str1 ) + { +- +- if( do_for_all_streams(msg, NULL, NULL, (regex_t*)str1, +- FIND, DESC_REGEXP) <= 0) +- return -1; +- +- return 1; ++ return do_for_all_streams(msg, NULL, NULL, (regex_t*)str1, FIND, DESC_REGEXP); + } + + +@@ -723,11 +716,7 @@ int codec_find_clock (struct sip_msg* msg, char* str1,char * str2 ) + LM_DBG("searching for codec <%.*s> with clock <%.*s> \n", + codec.len,codec.s,clock.len,clock.s); + +- if( do_for_all_streams( msg, &codec, &clock, NULL, +- FIND, DESC_NAME_AND_CLOCK) <= 0) +- return -1; +- +- return 1; ++ return do_for_all_streams( msg, &codec, &clock, NULL, FIND, DESC_NAME_AND_CLOCK); + } + + diff --git a/opensips-0007-Return-actual-payload-ID-in-case-of-a-dynamic-payloa.patch b/opensips-0007-Return-actual-payload-ID-in-case-of-a-dynamic-payloa.patch deleted file mode 100644 index 4a7ef4d..0000000 --- a/opensips-0007-Return-actual-payload-ID-in-case-of-a-dynamic-payloa.patch +++ /dev/null @@ -1,67 +0,0 @@ -From: Peter Lemenkov -Date: Fri, 11 Jan 2013 14:40:08 +0400 -Subject: [PATCH] Return actual payload ID in case of a dynamic payload - -Signed-off-by: Peter Lemenkov - -diff --git a/modules/sipmsgops/codecs.c b/modules/sipmsgops/codecs.c -index 274bff7..de48765 100644 ---- a/modules/sipmsgops/codecs.c -+++ b/modules/sipmsgops/codecs.c -@@ -34,6 +34,7 @@ - #include "codecs.h" - #include "../../route.h" - #include "../../mod_fix.h" -+#include "../../ut.h" - - #define MAX_STREAMS 64 - -@@ -528,7 +529,9 @@ static int stream_process(struct sip_msg * msg, struct sdp_stream_cell *cell, - - if(op == FIND) - { -- ret = 1; -+ str2sint(&(payload->rtp_payload), &ret); -+ if( (ret >= 0) && (ret <= 34) ) /* if a fixed payload ID then just return 1 */ -+ ret = 1; - goto end; - } - -@@ -685,22 +688,12 @@ int codec_find (struct sip_msg* msg, char* str1 ) - - LM_DBG("searching for codec <%.*s> \n",res.len,res.s); - -- if( do_for_all_streams( msg, &res, NULL, NULL, -- FIND, DESC_NAME) <= 0) -- return -1; -- -- return 1; -- -+ return do_for_all_streams( msg, &res, NULL, NULL, FIND, DESC_NAME); - } - - int codec_find_re (struct sip_msg* msg, char* str1 ) - { -- -- if( do_for_all_streams(msg, NULL, NULL, (regex_t*)str1, -- FIND, DESC_REGEXP) <= 0) -- return -1; -- -- return 1; -+ return do_for_all_streams(msg, NULL, NULL, (regex_t*)str1, FIND, DESC_REGEXP); - } - - -@@ -723,11 +716,7 @@ int codec_find_clock (struct sip_msg* msg, char* str1,char * str2 ) - LM_DBG("searching for codec <%.*s> with clock <%.*s> \n", - codec.len,codec.s,clock.len,clock.s); - -- if( do_for_all_streams( msg, &codec, &clock, NULL, -- FIND, DESC_NAME_AND_CLOCK) <= 0) -- return -1; -- -- return 1; -+ return do_for_all_streams( msg, &codec, &clock, NULL, FIND, DESC_NAME_AND_CLOCK); - } - - diff --git a/opensips-0007-Use-additional-auth-field-Sip-Source-IP-Address.patch b/opensips-0007-Use-additional-auth-field-Sip-Source-IP-Address.patch new file mode 100644 index 0000000..f117ce8 --- /dev/null +++ b/opensips-0007-Use-additional-auth-field-Sip-Source-IP-Address.patch @@ -0,0 +1,59 @@ +From: Peter Lemenkov +Date: Wed, 10 Aug 2011 17:30:51 +0400 +Subject: [PATCH] Use additional auth field - Sip-Source-IP-Address + +Signed-off-by: Peter Lemenkov + +diff --git a/aaa/aaa_avp.h b/aaa/aaa_avp.h +index 2a7a1a8..4d61b6d 100644 +--- a/aaa/aaa_avp.h ++++ b/aaa/aaa_avp.h +@@ -58,7 +58,8 @@ + #define A_TIME_STAMP 28 + #define A_SIP_CALL_ID 29 + #define A_SIP_REQUEST_HASH 30 +-#define A_MAX 31 ++#define A_SIP_SOURCE_IP_ADDRESS 31 ++#define A_MAX 32 + + #define V_STATUS_START 0 + #define V_STATUS_STOP 1 +diff --git a/modules/auth_aaa/authaaa_mod.c b/modules/auth_aaa/authaaa_mod.c +index 9248f11..4f746f3 100644 +--- a/modules/auth_aaa/authaaa_mod.c ++++ b/modules/auth_aaa/authaaa_mod.c +@@ -126,6 +126,7 @@ static int mod_init(void) + memset(vals, 0, sizeof(vals)); + attrs[A_SERVICE_TYPE].name = "Service-Type"; + attrs[A_SIP_URI_USER].name = "Sip-URI-User"; ++ attrs[A_SIP_SOURCE_IP_ADDRESS].name = "Sip-Source-IP-Address"; + attrs[A_DIGEST_RESPONSE].name = "Digest-Response"; + attrs[A_DIGEST_ALGORITHM].name = "Digest-Algorithm"; + attrs[A_DIGEST_BODY_DIGEST].name = "Digest-Body-Digest"; +diff --git a/modules/auth_aaa/sterman.c b/modules/auth_aaa/sterman.c +index 2d32149..8b44f32 100644 +--- a/modules/auth_aaa/sterman.c ++++ b/modules/auth_aaa/sterman.c +@@ -89,6 +89,7 @@ int aaa_authorize_sterman(struct sip_msg* _msg, dig_cred_t* _cred, str* _method, + uint32_t service; + str method, user, user_name; + str *ruri; ++ str ip; + + send = received = NULL; + +@@ -231,6 +232,14 @@ int aaa_authorize_sterman(struct sip_msg* _msg, dig_cred_t* _cred, str* _method, + goto err; + } + ++ /* Add SIP source IP address as a check item */ ++ ip.s = ip_addr2a(&_msg->rcv.src_ip); ++ ip.len = strlen(ip.s); ++ if (proto.avp_add(conn, send, &attrs[A_SIP_SOURCE_IP_ADDRESS], ip.s,ip.len,0)) { ++ LM_ERR("unable to add Sip-URI-User attribute\n"); ++ goto err; ++ } ++ + /* Add CALL-ID in Acct-Session-Id Attribute */ + if ( _msg->callid==NULL && + (parse_headers(_msg, HDR_CALLID_F, 0)==-1 || _msg->callid==NULL) ) { diff --git a/opensips-0008-Don-t-remove-pthread-library-explicitly-from-mi_xmlr.patch b/opensips-0008-Don-t-remove-pthread-library-explicitly-from-mi_xmlr.patch new file mode 100644 index 0000000..4cdcf62 --- /dev/null +++ b/opensips-0008-Don-t-remove-pthread-library-explicitly-from-mi_xmlr.patch @@ -0,0 +1,28 @@ +From: Peter Lemenkov +Date: Wed, 26 Mar 2014 13:10:07 +0400 +Subject: [PATCH] Don't remove pthread library explicitly from mi_xmlrpc + +Signed-off-by: Peter Lemenkov + +diff --git a/modules/mi_xmlrpc/Makefile b/modules/mi_xmlrpc/Makefile +index 762c2dd..955528f 100644 +--- a/modules/mi_xmlrpc/Makefile ++++ b/modules/mi_xmlrpc/Makefile +@@ -76,7 +76,7 @@ ifeq ($(NEW),yes) + # remove the abyss library - we already have + # the code in the module + MY_DEFS+=-D_UNIX +- MY_LIBS:=$(filter-out %xmlrpc_abyss %xmlrpc_server_abyss %pthread, $(MY_LIBS)) ++ MY_LIBS:=$(filter-out %xmlrpc_abyss %xmlrpc_server_abyss, $(MY_LIBS)) + else + # remove from compiling the code we have in the + # module for abyss server +@@ -90,7 +90,7 @@ endif + + + DEFS+=$(MY_DEFS) +-LIBS=$(MY_LIBS) ++LIBS=$(MY_LIBS) -lpthread + + + include ../../Makefile.modules diff --git a/opensips-0008-Use-additional-auth-field-Sip-Source-IP-Address.patch b/opensips-0008-Use-additional-auth-field-Sip-Source-IP-Address.patch deleted file mode 100644 index d94901c..0000000 --- a/opensips-0008-Use-additional-auth-field-Sip-Source-IP-Address.patch +++ /dev/null @@ -1,59 +0,0 @@ -From: Peter Lemenkov -Date: Wed, 10 Aug 2011 17:30:51 +0400 -Subject: [PATCH] Use additional auth field - Sip-Source-IP-Address - -Signed-off-by: Peter Lemenkov - -diff --git a/aaa/aaa_avp.h b/aaa/aaa_avp.h -index 1211c26..6d95c72 100644 ---- a/aaa/aaa_avp.h -+++ b/aaa/aaa_avp.h -@@ -58,7 +58,8 @@ - #define A_TIME_STAMP 28 - #define A_SIP_CALL_ID 29 - #define A_SIP_REQUEST_HASH 30 --#define A_MAX 31 -+#define A_SIP_SOURCE_IP_ADDRESS 31 -+#define A_MAX 32 - - #define V_STATUS_START 0 - #define V_STATUS_STOP 1 -diff --git a/modules/auth_aaa/authaaa_mod.c b/modules/auth_aaa/authaaa_mod.c -index f63a485..c0a7235 100644 ---- a/modules/auth_aaa/authaaa_mod.c -+++ b/modules/auth_aaa/authaaa_mod.c -@@ -126,6 +126,7 @@ static int mod_init(void) - memset(vals, 0, sizeof(vals)); - attrs[A_SERVICE_TYPE].name = "Service-Type"; - attrs[A_SIP_URI_USER].name = "Sip-URI-User"; -+ attrs[A_SIP_SOURCE_IP_ADDRESS].name = "Sip-Source-IP-Address"; - attrs[A_DIGEST_RESPONSE].name = "Digest-Response"; - attrs[A_DIGEST_ALGORITHM].name = "Digest-Algorithm"; - attrs[A_DIGEST_BODY_DIGEST].name = "Digest-Body-Digest"; -diff --git a/modules/auth_aaa/sterman.c b/modules/auth_aaa/sterman.c -index e10deec..702af71 100644 ---- a/modules/auth_aaa/sterman.c -+++ b/modules/auth_aaa/sterman.c -@@ -89,6 +89,7 @@ int aaa_authorize_sterman(struct sip_msg* _msg, dig_cred_t* _cred, str* _method, - uint32_t service; - str method, user, user_name; - str *ruri; -+ str ip; - - send = received = NULL; - -@@ -231,6 +232,14 @@ int aaa_authorize_sterman(struct sip_msg* _msg, dig_cred_t* _cred, str* _method, - goto err; - } - -+ /* Add SIP source IP address as a check item */ -+ ip.s = ip_addr2a(&_msg->rcv.src_ip); -+ ip.len = strlen(ip.s); -+ if (proto.avp_add(conn, send, &attrs[A_SIP_SOURCE_IP_ADDRESS], ip.s,ip.len,0)) { -+ LM_ERR("unable to add Sip-URI-User attribute\n"); -+ goto err; -+ } -+ - /* Add CALL-ID in Acct-Session-Id Attribute */ - if ( _msg->callid==NULL && - (parse_headers(_msg, HDR_CALLID_F, 0)==-1 || _msg->callid==NULL) ) { diff --git a/opensips-0009-Don-t-remove-pthread-library-explicitly-from-mi_xmlr.patch b/opensips-0009-Don-t-remove-pthread-library-explicitly-from-mi_xmlr.patch deleted file mode 100644 index 4cdcf62..0000000 --- a/opensips-0009-Don-t-remove-pthread-library-explicitly-from-mi_xmlr.patch +++ /dev/null @@ -1,28 +0,0 @@ -From: Peter Lemenkov -Date: Wed, 26 Mar 2014 13:10:07 +0400 -Subject: [PATCH] Don't remove pthread library explicitly from mi_xmlrpc - -Signed-off-by: Peter Lemenkov - -diff --git a/modules/mi_xmlrpc/Makefile b/modules/mi_xmlrpc/Makefile -index 762c2dd..955528f 100644 ---- a/modules/mi_xmlrpc/Makefile -+++ b/modules/mi_xmlrpc/Makefile -@@ -76,7 +76,7 @@ ifeq ($(NEW),yes) - # remove the abyss library - we already have - # the code in the module - MY_DEFS+=-D_UNIX -- MY_LIBS:=$(filter-out %xmlrpc_abyss %xmlrpc_server_abyss %pthread, $(MY_LIBS)) -+ MY_LIBS:=$(filter-out %xmlrpc_abyss %xmlrpc_server_abyss, $(MY_LIBS)) - else - # remove from compiling the code we have in the - # module for abyss server -@@ -90,7 +90,7 @@ endif - - - DEFS+=$(MY_DEFS) --LIBS=$(MY_LIBS) -+LIBS=$(MY_LIBS) -lpthread - - - include ../../Makefile.modules diff --git a/opensips-0009-backport-rtpengine-up-to-64bc0c8226ccb6d531dfa230cce.patch b/opensips-0009-backport-rtpengine-up-to-64bc0c8226ccb6d531dfa230cce.patch new file mode 100644 index 0000000..f2547cd --- /dev/null +++ b/opensips-0009-backport-rtpengine-up-to-64bc0c8226ccb6d531dfa230cce.patch @@ -0,0 +1,5386 @@ +From: Peter Lemenkov +Date: Sat, 23 May 2015 14:25:35 +0300 +Subject: [PATCH] backport rtpengine up to + 64bc0c8226ccb6d531dfa230cce61d5ed5e6163e + + +diff --git a/modules/rtpengine/Makefile b/modules/rtpengine/Makefile +new file mode 100644 +index 0000000..33d6853 +--- /dev/null ++++ b/modules/rtpengine/Makefile +@@ -0,0 +1,14 @@ ++# $Id: Makefile 5901 2009-07-21 07:45:05Z bogdan_iancu $ ++# ++# print example module makefile ++# ++# ++# WARNING: do not run this directly, it should be run by the master Makefile ++ ++include ../../Makefile.defs ++auto_gen= ++NAME=rtpengine.so ++LIBS= ++ ++include ../../Makefile.modules ++ +diff --git a/modules/rtpengine/README b/modules/rtpengine/README +new file mode 100644 +index 0000000..0da096c +--- /dev/null ++++ b/modules/rtpengine/README +@@ -0,0 +1,661 @@ ++rtpengine Module ++ ++Maxim Sobolev ++ ++ Sippy Software, Inc. ++ ++Juha Heinanen ++ ++ TuTPro, Inc. ++ ++Edited by ++ ++Maxim Sobolev ++ ++Edited by ++ ++Bogdan-Andrei Iancu ++ ++Edited by ++ ++Juha Heinanen ++ ++Edited by ++ ++Sas Ovidiu ++ ++Edited by ++ ++Carsten Bock ++ ++ ng-voice GmbH ++ ++Edited by ++ ++Richard Fuchs ++ ++ Sipwise GmbH ++ ++ Copyright © 2003-2008 Sippy Software, Inc. ++ ++ Copyright © 2005 Voice Sistem SRL ++ ++ Copyright © 2009-2014 TuTPro Inc. ++ ++ Copyright © 2010 VoIPEmbedded Inc. ++ ++ Copyright © 2013-2014 Sipwise GmbH ++ __________________________________________________________ ++ ++ Table of Contents ++ ++ 1. Admin Guide ++ ++ 1.1. Overview ++ 1.2. Multiple RTP proxy usage ++ 1.3. Dependencies ++ ++ 1.3.1. OpenSIPS Modules ++ 1.3.2. External Libraries or Applications ++ ++ 1.4. Parameters ++ ++ 1.4.1. rtpengine_sock (string) ++ 1.4.2. rtpengine_disable_tout (integer) ++ 1.4.3. rtpengine_tout (integer) ++ 1.4.4. rtpengine_retr (integer) ++ 1.4.5. extra_id_pv (string) ++ 1.4.6. setid_avp (string) ++ ++ 1.5. Functions ++ ++ 1.5.1. rtpengine_use_set(setid) ++ 1.5.2. rtpengine_offer([flags]) ++ 1.5.3. rtpengine_answer([flags]) ++ 1.5.4. rtpengine_delete([flags]) ++ 1.5.5. rtpengine_manage([flags]) ++ 1.5.6. rtpengine_start_recording() ++ ++ 1.6. Exported Pseudo Variables ++ ++ 1.6.1. $rtpstat ++ ++ 1.7. MI Commands ++ ++ 1.7.1. rtpengine_enable ++ 1.7.2. rtpengine_show ++ ++ 2. Frequently Asked Questions ++ ++ List of Examples ++ ++ 1.1. Set rtpengine_sock parameter ++ 1.2. Set rtpengine_disable_tout parameter ++ 1.3. Set rtpengine_tout parameter ++ 1.4. Set rtpengine_retr parameter ++ 1.5. Set extra_id_pv parameter ++ 1.6. Set setid_avp parameter ++ 1.7. rtpengine_use_set usage ++ 1.8. rtpengine_offer usage ++ 1.9. rtpengine_answer usage ++ 1.10. rtpengine_delete usage ++ 1.11. rtpengine_manage usage ++ 1.12. rtpengine_start_recording usage ++ 1.13. $rtpstat Usage ++ 1.14. rtpengine_enable usage ++ 1.15. rtpengine_show usage ++ ++Chapter 1. Admin Guide ++ ++1.1. Overview ++ ++ This is a module that enables media streams to be proxied via ++ an RTP proxy. The only RTP proxy currently known to work with ++ this module is the Sipwise rtpengine ++ https://github.com/sipwise/rtpengine. The rtpengine module is a ++ modified version of the original rtpproxy module using a new ++ control protocol. The module is designed to be a drop-in ++ replacement for the old module from a configuration file point ++ of view, however due to the incompatible control protocol, it ++ only works with RTP proxies which specifically support it. ++ ++1.2. Multiple RTP proxy usage ++ ++ The rtpengine module can support multiple RTP proxies for ++ balancing/distribution and control/selection purposes. ++ ++ The module allows definition of several sets of rtpengines. ++ Load-balancing will be performed over a set and the admin has ++ the ability to choose what set should be used. The set is ++ selected via its id - the id being defined with the set. Refer ++ to the “rtpengine_sock” module parameter definition for syntax ++ description. ++ ++ The balancing inside a set is done automatically by the module ++ based on the weight of each RTP proxy from the set. ++ ++ The selection of the set is done from script prior using ++ rtpengine_delete(), rtpengine_offer() or rtpengine_answer() ++ functions - see the rtpengine_use_set() function. ++ ++ Another way to select the set is to define setid_avp module ++ parameter and assign setid to the defined avp before calling ++ rtpengine_offer() or rtpengine_manage() function. If forwarding ++ of the requests fails and there is another branch to try, ++ remember to unset the avp after calling rtpengine_delete() ++ function. ++ ++ For backward compatibility reasons, a set with no id take by ++ default the id 0. Also if no set is explicitly set before ++ rtpengine_delete(), rtpengine_offer() or rtpengine_answer() the ++ 0 id set will be used. ++ ++ IMPORTANT: if you use multiple sets, take care and use the same ++ set for both rtpengine_offer()/rtpengine_answer() and ++ rtpengine_delete()!! If the set was selected using setid_avp, ++ the avp needs to be set only once before rtpengine_offer() or ++ rtpengine_manage() call. ++ ++1.3. Dependencies ++ ++1.3.1. OpenSIPS Modules ++ ++ The following modules must be loaded before this module: ++ * tm module - (optional) if you want to have ++ rtpengine_manage() fully functional ++ ++1.3.2. External Libraries or Applications ++ ++ The following libraries or applications must be installed ++ before running OpenSIPS with this module loaded: ++ * None. ++ ++1.4. Parameters ++ ++1.4.1. rtpengine_sock (string) ++ ++ Definition of socket(s) used to connect to (a set) RTP proxy. ++ It may specify a UNIX socket or an IPv4/IPv6 UDP socket. ++ ++ Default value is “NONE” (disabled). ++ ++ Example 1.1. Set rtpengine_sock parameter ++... ++# single rtproxy ++modparam("rtpengine", "rtpengine_sock", "udp:localhost:12221") ++# multiple rtproxies for LB ++modparam("rtpengine", "rtpengine_sock", ++ "udp:localhost:12221 udp:localhost:12222") ++# multiple sets of multiple rtproxies ++modparam("rtpengine", "rtpengine_sock", ++ "1 == udp:localhost:12221 udp:localhost:12222") ++modparam("rtpengine", "rtpengine_sock", ++ "2 == udp:localhost:12225") ++... ++ ++1.4.2. rtpengine_disable_tout (integer) ++ ++ Once an RTP proxy was found unreachable and marked as disabled, ++ the rtpengine module will not attempt to establish ++ communication to that RTP proxy for rtpengine_disable_tout ++ seconds. ++ ++ Default value is “60”. ++ ++ Example 1.2. Set rtpengine_disable_tout parameter ++... ++modparam("rtpengine", "rtpengine_disable_tout", 20) ++... ++ ++1.4.3. rtpengine_tout (integer) ++ ++ Timeout value in waiting for reply from RTP proxy. ++ ++ Default value is “1”. ++ ++ Example 1.3. Set rtpengine_tout parameter ++... ++modparam("rtpengine", "rtpengine_tout", 2) ++... ++ ++1.4.4. rtpengine_retr (integer) ++ ++ How many times the module should retry to send and receive ++ after timeout was generated. ++ ++ Default value is “5”. ++ ++ Example 1.4. Set rtpengine_retr parameter ++... ++modparam("rtpengine", "rtpengine_retr", 2) ++... ++ ++1.4.5. extra_id_pv (string) ++ ++ The parameter sets the PV defination to use when the “b” ++ parameter is used on rtpengine_delete(), rtpengine_offer(), ++ rtpengine_answer() or rtpengine_manage() command. ++ ++ Default is empty, the “b” parameter may not be used then. ++ ++ Example 1.5. Set extra_id_pv parameter ++... ++modparam("rtpengine", "extra_id_pv", "$avp(extra_id)") ++... ++ ++1.4.6. setid_avp (string) ++ ++ The parameter defines an AVP that, if set, determines which RTP ++ proxy set rtpengine_offer(), rtpengine_answer(), ++ rtpengine_delete(), and rtpengine_manage() functions use. ++ ++ There is no default value. ++ ++ Example 1.6. Set setid_avp parameter ++... ++modparam("rtpengine", "setid_avp", "$avp(setid)") ++... ++ ++1.5. Functions ++ ++1.5.1. rtpengine_use_set(setid) ++ ++ Sets the ID of the RTP proxy set to be used for the next ++ rtpengine_delete(), rtpengine_offer(), rtpengine_answer() or ++ rtpengine_manage() command. The parameter can be an integer or ++ a config variable holding an integer. ++ ++ This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, ++ BRANCH_ROUTE. ++ ++ Example 1.7. rtpengine_use_set usage ++... ++rtpengine_use_set("2"); ++rtpengine_offer(); ++... ++ ++1.5.2. rtpengine_offer([flags]) ++ ++ Rewrites SDP body to ensure that media is passed through an RTP ++ proxy. To be invoked on INVITE for the cases the SDPs are in ++ INVITE and 200 OK and on 200 OK when SDPs are in 200 OK and ++ ACK. ++ ++ Meaning of the parameters is as follows: ++ * flags - flags to turn on some features. ++ The “flags” string is a list of space-separated items. Each ++ item is either an individual token, or a token in ++ “key=value” format. The possible tokens are described ++ below. ++ + via-branch=... - Include the “branch” value of one of ++ the “Via” headers in the request to the RTP proxy. ++ Possible values are: “1” - use the first “Via” header; ++ “2” - use the second “Via” header; “auto” - use the ++ first “Via” header if this is a request, or the second ++ one if this is a reply; “extra” - don't take the value ++ from a header, but instead use the value of the ++ “extra_id_pv” variable. This can be used to create one ++ media session per branch on the RTP proxy. When ++ sending a subsequent “delete” command to the RTP ++ proxy, you can then stop just the session for a ++ specific branch when passing the flag '1' or '2' in ++ the “rtpengine_delete”, or stop all sessions for a ++ call when not passing one of those two flags there. ++ This is especially useful if you have serially forked ++ call scenarios where the RTP proxy gets an “offer” ++ command for a new branch, and then a “delete” command ++ for the previous branch, which would otherwise delete ++ the full call, breaking the subsequent “answer” for ++ the new branch. This flag is only supported by the ++ Sipwise rtpengine RTP proxy at the moment! ++ + asymmetric - flags that UA from which message is ++ received doesn't support symmetric RTP. (automatically ++ sets the 'r' flag) ++ + force-answer - force “answer”, that is, only rewrite ++ SDP when corresponding session already exists in the ++ RTP proxy. By default is on when the session is to be ++ completed. ++ + internal, external - these flags specify the direction ++ of the SIP message. These flags only make sense when ++ the RTP proxy is running in bridge mode. “internal” ++ corresponds to the proxy's first interface, “external” ++ corresponds to the RTP proxy's second interface. You ++ always have to specify two flags to define the ++ incoming network and the outgoing network. For ++ example, “internal external” should be used for SIP ++ message received from the local interface and sent out ++ on the external interface, and “external internal” ++ vice versa. Other options are “internal internal” and ++ “external external”. So, for example if a SIP requests ++ is processed with “internal external” flags, the ++ corresponding response must be processed with ++ “internal external” flags. ++ + auto-bridge - this flag an alternative to the ++ “internal” and “external” flags in order to do ++ automatic bridging between IPv4 on the "internal ++ network" and IPv6 on the "external network". Instead ++ of explicitly instructing the RTP proxy to select a ++ particular address family, the distinction is done by ++ the given IP in the SDP body by the RTP proxy itself. ++ Not supported by Sipwise rtpengine. ++ + address-family=... - instructs the RTP proxy that the ++ recipient of this SDP body expects to see addresses of ++ a particular family. Possible values are “IP4” and ++ “IP6”. For example, if the SDP body contains IPv4 ++ addresses but the recipient only speaks IPv6, you ++ would use “address-family=IP6” to bridge between the ++ two address families. ++ Sipwise rtpengine remembers the address family ++ preference of each party after it has seen an SDP body ++ from them. This means that normally it is only ++ necessary to explicitly specify the address family in ++ the “offer”, but not in the “answer”. ++ Note: Please note, that this will only work properly ++ with non-dual-stack user-agents or with dual-stack ++ clients according to RFC6157 (which suggest ICE for ++ Dual-Stack implementations). This short-cut will not ++ work properly with RFC4091 (ANAT) compatible clients, ++ which suggests having different m-lines with different ++ IP-protocols grouped together. ++ + force - instructs the RTP proxy to ignore marks ++ inserted by another RTP proxy in transit to indicate ++ that the session is already goes through another ++ proxy. Allows creating a chain of proxies. Not ++ supported and ignored by Sipwise rtpengine. ++ + trust-address - flags that IP address in SDP should be ++ trusted. Without this flag, the RTP proxy ignores ++ address in the SDP and uses source address of the SIP ++ message as media address which is passed to the RTP ++ proxy. ++ + replace-origin - flags that IP from the origin ++ description (o=) should be also changed. ++ + replace-session-connection - flags to change the ++ session-level SDP connection (c=) IP if media ++ description also includes connection information. ++ + symmetric - flags that for the UA from which message ++ is received, support symmetric RTP must be forced. ++ + repacketize=NN - requests the RTP proxy to perform ++ re-packetization of RTP traffic coming from the UA ++ which has sent the current message to increase or ++ decrease payload size per each RTP packet forwarded if ++ possible. The NN is the target payload size in ms, for ++ the most codecs its value should be in 10ms ++ increments, however for some codecs the increment ++ could differ (e.g. 30ms for GSM or 20ms for G.723). ++ The RTP proxy would select the closest value supported ++ by the codec. This feature could be used for ++ significantly reducing bandwith overhead for low ++ bitrate codecs, for example with G.729 going from 10ms ++ to 100ms saves two thirds of the network bandwith. Not ++ supported by Sipwise rtpengine. ++ + ICE=... - controls the RTP proxy's behaviour regarding ++ ICE attributes within the SDP body. Possible values ++ are: “force” - discard any ICE attributes already ++ present in the SDP body and then generate and insert ++ new ICE data, leaving itself as the only ICE ++ candidates; “remove” instructs the RTP proxy to ++ discard any ICE attributes and not insert any new ones ++ into the SDP. The default (if no “ICE=...” is given at ++ all), new ICE data will only be generated if no ICE ++ was present in the SDP originally; otherwise the RTP ++ proxy will only insert itself as an additional ICE ++ candidate. Other SDP substitutions (c=, m=, etc) are ++ unaffected by this flag. ++ + RTP, SRTP, AVP, AVPF - These flags control the RTP ++ transport protocol that should be used towards the ++ recipient of the SDP. If none of them are specified, ++ the protocol given in the SDP is left untouched. ++ Otherwise, the “SRTP” flag indicates that SRTP should ++ be used, while “RTP” indicates that SRTP should not be ++ used. “AVPF” indicates that the advanced RTCP profile ++ with feedback messages should be used, and “AVP” ++ indicates that the regular RTCP profile should be ++ used. See also the next set of flags below. ++ + RTP/AVP, RTP/SAVP, RTP/AVPF, RTP/SAVPF - these serve ++ as an alternative, more explicit way to select between ++ the different RTP protocols and profiles supported by ++ the RTP proxy. For example, giving the flag ++ “RTP/SAVPF” has the same effect as giving the two ++ flags “SRTP AVPF”. ++ + to-tag - force inclusion of the “To” tag. Normally, ++ the “To” tag is always included when present, except ++ for “delete” messages. Including the “To” tag in a ++ “delete” messages allows you to be more selective ++ about which dialogues within a call are being torn ++ down. ++ + rtcp-mux-demux - if rtcp-mux (RFC 5761) was offered, ++ make the RTP proxy accept the offer, but not offer it ++ to the recipient of this message. ++ + rtcp-mux-reject - if rtcp-mux was offered, make the ++ RTP proxy reject the offer, but still offer it to the ++ recipient. Can be combined with “rtcp-mux-offer” to ++ always offer it. ++ + rtcp-mux-offer - make the RTP proxy offer rtcp-mux to ++ the recipient of this message, regardless of whether ++ it was offered originally or not. ++ + rtcp-mux-accept - if rtcp-mux was offered, make the ++ RTP proxy accept the offer and also offer it to the ++ recipient of this message. Can be combined with ++ “rtcp-mux-offer” to always offer it. ++ + media-address=... - force a particular media address ++ to be used in the SDP body. Address family is detected ++ automatically. ++ ++ This function can be used from ANY_ROUTE. ++ ++ Example 1.8. rtpengine_offer usage ++route { ++... ++ if (is_method("INVITE")) { ++ if (has_body("application/sdp")) { ++ if (rtpengine_offer()) ++ t_on_reply("1"); ++ } else { ++ t_on_reply("2"); ++ } ++ } ++ if (is_method("ACK") && has_body("application/sdp")) ++ rtpengine_answer(); ++... ++} ++ ++onreply_route[1] ++{ ++... ++ if (has_body("application/sdp")) ++ rtpengine_answer(); ++... ++} ++ ++onreply_route[2] ++{ ++... ++ if (has_body("application/sdp")) ++ rtpengine_offer(); ++... ++} ++ ++1.5.3. rtpengine_answer([flags]) ++ ++ Rewrites SDP body to ensure that media is passed through an RTP ++ proxy. To be invoked on 200 OK for the cases the SDPs are in ++ INVITE and 200 OK and on ACK when SDPs are in 200 OK and ACK. ++ ++ See rtpengine_offer() function description above for the ++ meaning of the parameters. ++ ++ This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, ++ FAILURE_ROUTE, BRANCH_ROUTE. ++ ++ Example 1.9. rtpengine_answer usage ++ ++ See rtpengine_offer() function example above for example. ++ ++1.5.4. rtpengine_delete([flags]) ++ ++ Tears down the RTPProxy session for the current call. ++ ++ See rtpengine_offer() function description above for the ++ meaning of the parameters. Note that not all flags make sense ++ for a “delete”. ++ ++ This function can be used from ANY_ROUTE. ++ ++ Example 1.10. rtpengine_delete usage ++... ++rtpengine_delete(); ++... ++ ++1.5.5. rtpengine_manage([flags]) ++ ++ Manage the RTPProxy session - it combines the functionality of ++ rtpengine_offer(), rtpengine_answer() and rtpengine_delete(), ++ detecting internally based on message type and method which one ++ to execute. ++ ++ It can take the same parameters as rtpengine_offer(). The flags ++ parameter to rtpengine_manage() can be a configuration variable ++ containing the flags as a string. ++ ++ Functionality: ++ * If INVITE with SDP, then do rtpengine_offer() ++ * If INVITE with SDP, when the tm module is loaded, mark ++ transaction with internal flag FL_SDP_BODY to know that the ++ 1xx and 2xx are for rtpengine_answer() ++ * If ACK with SDP, then do rtpengine_answer() ++ * If BYE or CANCEL, or called within a FAILURE_ROUTE[], then ++ do rtpengine_delete() ++ * If reply to INVITE with code >= 300 do rtpengine_delete() ++ * If reply with SDP to INVITE having code 1xx and 2xx, then ++ do rtpengine_answer() if the request had SDP or tm is not ++ loaded, otherwise do rtpengine_offer() ++ ++ This function can be used from ANY_ROUTE. ++ ++ Example 1.11. rtpengine_manage usage ++... ++rtpengine_manage(); ++... ++ ++1.5.6. rtpengine_start_recording() ++ ++ This function will send a signal to the RTP proxy to record the ++ RTP stream on the RTP proxy. This function is not supported by ++ Sipwise rtpengine at the moment! ++ ++ This function can be used from REQUEST_ROUTE and ONREPLY_ROUTE. ++ ++ Example 1.12. rtpengine_start_recording usage ++... ++rtpengine_start_recording(); ++... ++ ++1.6. Exported Pseudo Variables ++ ++1.6.1. $rtpstat ++ ++ Returns the RTP statistics from the RTP proxy. The RTP ++ statistics from the RTP proxy are provided as a string and it ++ does contain several packet counters. The statistics must be ++ retrieved before the session is deleted (before ++ rtpengine_delete()). ++ ++ Example 1.13. $rtpstat Usage ++... ++ append_hf("X-RTP-Statistics: $rtpstat\r\n"); ++... ++ ++1.7. MI Commands ++ ++1.7.1. rtpengine_enable ++ ++ Enables a RTP proxy if parameter value is greater than 0. ++ Disables it if a zero value is given. ++ ++ The first parameter is the RTP proxy url (exactly as defined in ++ the config file). ++ ++ The second parameter value must be a number in decimal. ++ ++ NOTE: if a RTP proxy is defined multiple times (in the same or ++ diferente sete), all of its instances will be enables/disabled. ++ ++ Example 1.14. rtpengine_enable usage ++... ++$ opensipsctl fifo rtpengine_enable udp:192.168.2.133:8081 0 ++... ++ ++1.7.2. rtpengine_show ++ ++ Displays all the RTP proxies and their information: set and ++ status (disabled or not, weight and recheck_ticks). ++ ++ No parameter. ++ ++ Example 1.15. rtpengine_show usage ++... ++$ opensipsctl fifo rtpengine_show ++... ++ ++Chapter 2. Frequently Asked Questions ++ ++ 2.1. ++ ++ How do I migrate from “rtpproxy” or “rtpproxy-ng” to ++ “rtpengine”? ++ ++ For the most part, only the names of the functions have ++ changed, with “rtpproxy” in each name replaced with ++ “rtpengine”. For example, “rtpproxy_manage()” has become ++ “rtpengine_manage()”. A few name duplications have also been ++ resolved, for example there is now a single ++ “rtpengine_delete()” instead of “unforce_rtp_proxy()” and the ++ identical “rtpproxy_destroy()”. ++ ++ The largest difference to the old module is how flags are ++ passed to “rtpengine_offer()”, “rtpengine_answer()”, ++ “rtpengine_manage()” and “rtpengine_delete()”. Instead of ++ having a string of single-letter flags, they now take a string ++ of space-separated items, with each item being either a single ++ token (word) or a “key=value” pair. ++ ++ For example, if you had a call “rtpproxy_offer("FRWOC+PS");”, ++ this would then become: ++rtpengine_offer("force trust-address symmetric replace-origin replace-se ++ssion-connection ICE=force RTP/SAVPF"); ++ ++ Finally, if you were using the second paramater (explicit media ++ address) to any of these functions, this has been replaced by ++ the “media-address=...” option within the first string of ++ flags. ++ ++ 2.2. ++ ++ Where can I find more about OpenSIPS? ++ ++ Take a look at http://www.opensips.org/. ++ ++ 2.3. ++ ++ Where can I post a question about this module? ++ ++ First at all check if your question was already answered on one ++ of our mailing lists: ++ * User Mailing List - ++ http://lists.opensips.org/cgi-bin/mailman/listinfo/users ++ * Developer Mailing List - ++ http://lists.opensips.org/cgi-bin/mailman/listinfo/devel ++ ++ E-mails regarding any stable OpenSIPS release should be sent to ++ and e-mails regarding development ++ versions should be sent to . ++ ++ If you want to keep the mail private, send it to ++ . ++ ++ 2.4. ++ ++ How can I report a bug? ++ ++ Please follow the guidelines provided at: ++ https://github.com/OpenSIPS/opensips/issues. +diff --git a/modules/rtpengine/bencode.c b/modules/rtpengine/bencode.c +new file mode 100644 +index 0000000..9de3f5e +--- /dev/null ++++ b/modules/rtpengine/bencode.c +@@ -0,0 +1,729 @@ ++/* ++ * Copyright ?? ++ * ++ * This file is part of opensips, a free SIP server. ++ * ++ * opensips is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version ++ * ++ * opensips is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * History: ++ * --------- ++ * 2014-06-17 Initial upload ++ */ ++ ++ ++ ++#include "bencode.h" ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* set to 0 for alloc debugging, e.g. through valgrind */ ++#define BENCODE_MIN_BUFFER_PIECE_LEN 512 ++ ++#define BENCODE_HASH_BUCKETS 31 /* prime numbers work best */ ++ ++struct __bencode_buffer_piece { ++ char *tail; ++ unsigned int left; ++ struct __bencode_buffer_piece *next; ++ char buf[0]; ++}; ++struct __bencode_free_list { ++ void *ptr; ++ free_func_t func; ++ struct __bencode_free_list *next; ++}; ++struct __bencode_hash { ++ struct bencode_item *buckets[BENCODE_HASH_BUCKETS]; ++}; ++ ++ ++ ++ ++ ++static bencode_item_t __bencode_end_marker = { ++ .type = BENCODE_END_MARKER, ++ .iov[0].iov_len = 1, ++ .iov[0].iov_base = "e", ++ .iov_cnt = 1, ++ .str_len = 1, ++}; ++ ++ ++ ++ ++static bencode_item_t *__bencode_decode(bencode_buffer_t *buf, const char *s, const char *end); ++ ++ ++ ++static void __bencode_item_init(bencode_item_t *item) { ++ item->last_child = item->parent = item->child = item->sibling = NULL; ++} ++ ++static void __bencode_container_init(bencode_item_t *cont) { ++ cont->iov[0].iov_len = 1; ++ cont->iov[1].iov_base = "e"; ++ cont->iov[1].iov_len = 1; ++ cont->iov_cnt = 2; ++ cont->str_len = 2; ++} ++ ++static void __bencode_dictionary_init(bencode_item_t *dict) { ++ dict->type = BENCODE_DICTIONARY; ++ dict->iov[0].iov_base = "d"; ++ dict->value = 0; ++ __bencode_container_init(dict); ++} ++ ++static void __bencode_list_init(bencode_item_t *list) { ++ list->type = BENCODE_LIST; ++ list->iov[0].iov_base = "l"; ++ __bencode_container_init(list); ++} ++ ++static struct __bencode_buffer_piece *__bencode_piece_new(unsigned int size) { ++ struct __bencode_buffer_piece *ret; ++ ++ if (size < BENCODE_MIN_BUFFER_PIECE_LEN) ++ size = BENCODE_MIN_BUFFER_PIECE_LEN; ++ ret = BENCODE_MALLOC(sizeof(*ret) + size); ++ if (!ret) ++ return NULL; ++ ++ ret->tail = ret->buf; ++ ret->left = size; ++ ret->next = NULL; ++ ++ return ret; ++} ++ ++int bencode_buffer_init(bencode_buffer_t *buf) { ++ buf->pieces = __bencode_piece_new(0); ++ if (!buf->pieces) ++ return -1; ++ buf->free_list = NULL; ++ buf->error = 0; ++ return 0; ++} ++ ++static void *__bencode_alloc(bencode_buffer_t *buf, unsigned int size) { ++ struct __bencode_buffer_piece *piece; ++ void *ret; ++ ++ if (!buf) ++ return NULL; ++ if (buf->error) ++ return NULL; ++ ++ piece = buf->pieces; ++ ++ if (size <= piece->left) ++ goto alloc; ++ ++ piece = __bencode_piece_new(size); ++ if (!piece) { ++ buf->error = 1; ++ return NULL; ++ } ++ piece->next = buf->pieces; ++ buf->pieces = piece; ++ ++ assert(size <= piece->left); ++ ++alloc: ++ piece->left -= size; ++ ret = piece->tail; ++ piece->tail += size; ++ return ret; ++} ++ ++void bencode_buffer_free(bencode_buffer_t *buf) { ++ struct __bencode_free_list *fl; ++ struct __bencode_buffer_piece *piece, *next; ++ ++ for (fl = buf->free_list; fl; fl = fl->next) ++ fl->func(fl->ptr); ++ ++ for (piece = buf->pieces; piece; piece = next) { ++ next = piece->next; ++ BENCODE_FREE(piece); ++ } ++} ++ ++static bencode_item_t *__bencode_item_alloc(bencode_buffer_t *buf, unsigned int payload) { ++ bencode_item_t *ret; ++ ++ ret = __bencode_alloc(buf, sizeof(struct bencode_item) + payload); ++ if (!ret) ++ return NULL; ++ ret->buffer = buf; ++ __bencode_item_init(ret); ++ return ret; ++} ++ ++bencode_item_t *bencode_dictionary(bencode_buffer_t *buf) { ++ bencode_item_t *ret; ++ ++ ret = __bencode_item_alloc(buf, 0); ++ if (!ret) ++ return NULL; ++ __bencode_dictionary_init(ret); ++ return ret; ++} ++ ++bencode_item_t *bencode_list(bencode_buffer_t *buf) { ++ bencode_item_t *ret; ++ ++ ret = __bencode_item_alloc(buf, 0); ++ if (!ret) ++ return NULL; ++ __bencode_list_init(ret); ++ return ret; ++} ++ ++static void __bencode_container_add(bencode_item_t *parent, bencode_item_t *child) { ++ if (!parent) ++ return; ++ if (!child) ++ return; ++ ++ assert(child->parent == NULL); ++ assert(child->sibling == NULL); ++ ++ child->parent = parent; ++ if (parent->last_child) ++ parent->last_child->sibling = child; ++ parent->last_child = child; ++ if (!parent->child) ++ parent->child = child; ++ ++ while (parent) { ++ parent->iov_cnt += child->iov_cnt; ++ parent->str_len += child->str_len; ++ parent = parent->parent; ++ } ++} ++ ++static bencode_item_t *__bencode_string_alloc(bencode_buffer_t *buf, const void *base, ++ int str_len, int iov_len, int iov_cnt, bencode_type_t type) ++{ ++ bencode_item_t *ret; ++ int len_len; ++ ++ assert((str_len <= 99999) && (str_len >= 0)); ++ ret = __bencode_item_alloc(buf, 7); ++ if (!ret) ++ return NULL; ++ len_len = sprintf(ret->__buf, "%d:", str_len); ++ ++ ret->type = type; ++ ret->iov[0].iov_base = ret->__buf; ++ ret->iov[0].iov_len = len_len; ++ ret->iov[1].iov_base = (void *) base; ++ ret->iov[1].iov_len = iov_len; ++ ret->iov_cnt = iov_cnt + 1; ++ ret->str_len = len_len + str_len; ++ ++ return ret; ++} ++ ++bencode_item_t *bencode_string_len_dup(bencode_buffer_t *buf, const char *s, int len) { ++ char *sd = __bencode_alloc(buf, len); ++ if (!sd) ++ return NULL; ++ memcpy(sd, s, len); ++ return bencode_string_len(buf, sd, len); ++} ++ ++bencode_item_t *bencode_string_len(bencode_buffer_t *buf, const char *s, int len) { ++ return __bencode_string_alloc(buf, s, len, len, 1, BENCODE_STRING); ++} ++ ++bencode_item_t *bencode_string_iovec(bencode_buffer_t *buf, const struct iovec *iov, int iov_cnt, int str_len) { ++ int i; ++ ++ if (iov_cnt < 0) ++ return NULL; ++ if (str_len < 0) { ++ str_len = 0; ++ for (i = 0; i < iov_cnt; i++) ++ str_len += iov[i].iov_len; ++ } ++ ++ return __bencode_string_alloc(buf, iov, str_len, iov_cnt, iov_cnt, BENCODE_IOVEC); ++} ++ ++bencode_item_t *bencode_integer(bencode_buffer_t *buf, long long int i) { ++ bencode_item_t *ret; ++ int alen, rlen; ++ ++ alen = 8; ++ while (1) { ++ ret = __bencode_item_alloc(buf, alen + 3); ++ if (!ret) ++ return NULL; ++ rlen = snprintf(ret->__buf, alen, "i%llde", i); ++ if (rlen < alen) ++ break; ++ alen <<= 1; ++ } ++ ++ ret->type = BENCODE_INTEGER; ++ ret->iov[0].iov_base = ret->__buf; ++ ret->iov[0].iov_len = rlen; ++ ret->iov[1].iov_base = NULL; ++ ret->iov[1].iov_len = 0; ++ ret->iov_cnt = 1; ++ ret->str_len = rlen; ++ ++ return ret; ++} ++ ++bencode_item_t *bencode_dictionary_add_len(bencode_item_t *dict, const char *key, int keylen, bencode_item_t *val) { ++ bencode_item_t *str; ++ ++ if (!dict || !val) ++ return NULL; ++ assert(dict->type == BENCODE_DICTIONARY); ++ ++ str = bencode_string_len(dict->buffer, key, keylen); ++ if (!str) ++ return NULL; ++ __bencode_container_add(dict, str); ++ __bencode_container_add(dict, val); ++ return val; ++} ++ ++bencode_item_t *bencode_list_add(bencode_item_t *list, bencode_item_t *item) { ++ if (!list || !item) ++ return NULL; ++ assert(list->type == BENCODE_LIST); ++ __bencode_container_add(list, item); ++ return item; ++} ++ ++static int __bencode_iovec_cpy(struct iovec *out, const struct iovec *in, int num) { ++ memcpy(out, in, num * sizeof(*out)); ++ return num; ++} ++ ++static int __bencode_str_cpy(char *out, const struct iovec *in, int num) { ++ char *orig = out; ++ ++ while (--num >= 0) { ++ memcpy(out, in->iov_base, in->iov_len); ++ out += in->iov_len; ++ in++; ++ } ++ return out - orig; ++} ++ ++static int __bencode_iovec_dump(struct iovec *out, bencode_item_t *item) { ++ bencode_item_t *child; ++ struct iovec *orig = out; ++ ++ assert(item->iov[0].iov_base != NULL); ++ out += __bencode_iovec_cpy(out, &item->iov[0], 1); ++ ++ child = item->child; ++ while (child) { ++ out += __bencode_iovec_dump(out, child); ++ child = child->sibling; ++ } ++ ++ if (item->type == BENCODE_IOVEC) ++ out += __bencode_iovec_cpy(out, item->iov[1].iov_base, item->iov[1].iov_len); ++ else if (item->iov[1].iov_base) ++ out += __bencode_iovec_cpy(out, &item->iov[1], 1); ++ ++ assert((out - orig) == item->iov_cnt); ++ return item->iov_cnt; ++} ++ ++static int __bencode_str_dump(char *out, bencode_item_t *item) { ++ char *orig = out; ++ bencode_item_t *child; ++ ++ assert(item->iov[0].iov_base != NULL); ++ out += __bencode_str_cpy(out, &item->iov[0], 1); ++ ++ child = item->child; ++ while (child) { ++ out += __bencode_str_dump(out, child); ++ child = child->sibling; ++ } ++ ++ if (item->type == BENCODE_IOVEC) ++ out += __bencode_str_cpy(out, item->iov[1].iov_base, item->iov[1].iov_len); ++ else if (item->iov[1].iov_base) ++ out += __bencode_str_cpy(out, &item->iov[1], 1); ++ ++ assert((out - orig) == item->str_len); ++ *out = '\0'; ++ return item->str_len; ++} ++ ++struct iovec *bencode_iovec(bencode_item_t *root, int *cnt, unsigned int head, unsigned int tail) { ++ struct iovec *ret; ++ ++ if (!root) ++ return NULL; ++ assert(cnt != NULL); ++ assert(root->iov_cnt > 0); ++ ++ ret = __bencode_alloc(root->buffer, sizeof(*ret) * (root->iov_cnt + head + tail)); ++ if (!ret) ++ return NULL; ++ *cnt = __bencode_iovec_dump(ret + head, root); ++ return ret; ++} ++ ++char *bencode_collapse(bencode_item_t *root, int *len) { ++ char *ret; ++ int l; ++ ++ if (!root) ++ return NULL; ++ assert(root->str_len > 0); ++ ++ ret = __bencode_alloc(root->buffer, root->str_len + 1); ++ if (!ret) ++ return NULL; ++ l = __bencode_str_dump(ret, root); ++ if (len) ++ *len = l; ++ return ret; ++} ++ ++char *bencode_collapse_dup(bencode_item_t *root, int *len) { ++ char *ret; ++ int l; ++ ++ if (!root) ++ return NULL; ++ assert(root->str_len > 0); ++ ++ ret = BENCODE_MALLOC(root->str_len + 1); ++ if (!ret) ++ return NULL; ++ ++ l = __bencode_str_dump(ret, root); ++ if (len) ++ *len = l; ++ return ret; ++} ++ ++static unsigned int __bencode_hash_str_len(const unsigned char *s, int len) { ++ unsigned long *ul; ++ unsigned int *ui; ++ unsigned short *us; ++ ++ if (len >= sizeof(*ul)) { ++ ul = (void *) s; ++ return *ul % BENCODE_HASH_BUCKETS; ++ } ++ if (len >= sizeof(*ui)) { ++ ui = (void *) s; ++ return *ui % BENCODE_HASH_BUCKETS; ++ } ++ if (len >= sizeof(*us)) { ++ us = (void *) s; ++ return *us % BENCODE_HASH_BUCKETS; ++ } ++ if (len >= sizeof(*s)) ++ return *s % BENCODE_HASH_BUCKETS; ++ ++ return 0; ++} ++ ++static unsigned int __bencode_hash_str(bencode_item_t *str) { ++ assert(str->type == BENCODE_STRING); ++ return __bencode_hash_str_len(str->iov[1].iov_base, str->iov[1].iov_len); ++} ++ ++static void __bencode_hash_insert(bencode_item_t *key, struct __bencode_hash *hash) { ++ unsigned int bucket, i; ++ ++ i = bucket = __bencode_hash_str(key); ++ ++ while (1) { ++ if (!hash->buckets[i]) { ++ hash->buckets[i] = key; ++ break; ++ } ++ i++; ++ if (i >= BENCODE_HASH_BUCKETS) ++ i = 0; ++ if (i == bucket) ++ break; ++ } ++} ++ ++static bencode_item_t *__bencode_decode_dictionary(bencode_buffer_t *buf, const char *s, const char *end) { ++ bencode_item_t *ret, *key, *value; ++ struct __bencode_hash *hash; ++ ++ if (*s != 'd') ++ return NULL; ++ s++; ++ ++ ret = __bencode_item_alloc(buf, sizeof(*hash)); ++ if (!ret) ++ return NULL; ++ __bencode_dictionary_init(ret); ++ ret->value = 1; ++ hash = (void *) ret->__buf; ++ memset(hash, 0, sizeof(*hash)); ++ ++ while (s < end) { ++ key = __bencode_decode(buf, s, end); ++ if (!key) ++ return NULL; ++ s += key->str_len; ++ if (key->type == BENCODE_END_MARKER) ++ break; ++ if (key->type != BENCODE_STRING) ++ return NULL; ++ __bencode_container_add(ret, key); ++ ++ if (s >= end) ++ return NULL; ++ value = __bencode_decode(buf, s, end); ++ if (!value) ++ return NULL; ++ s += value->str_len; ++ if (value->type == BENCODE_END_MARKER) ++ return NULL; ++ __bencode_container_add(ret, value); ++ ++ __bencode_hash_insert(key, hash); ++ } ++ ++ return ret; ++} ++ ++static bencode_item_t *__bencode_decode_list(bencode_buffer_t *buf, const char *s, const char *end) { ++ bencode_item_t *ret, *item; ++ ++ if (*s != 'l') ++ return NULL; ++ s++; ++ ++ ret = __bencode_item_alloc(buf, 0); ++ if (!ret) ++ return NULL; ++ __bencode_list_init(ret); ++ ++ while (s < end) { ++ item = __bencode_decode(buf, s, end); ++ if (!item) ++ return NULL; ++ s += item->str_len; ++ if (item->type == BENCODE_END_MARKER) ++ break; ++ __bencode_container_add(ret, item); ++ } ++ ++ return ret; ++} ++ ++static bencode_item_t *__bencode_decode_integer(bencode_buffer_t *buf, const char *s, const char *end) { ++ long long int i; ++ const char *orig = s; ++ char *convend; ++ bencode_item_t *ret; ++ ++ if (*s != 'i') ++ return NULL; ++ s++; ++ ++ if (s >= end) ++ return NULL; ++ ++ if (*s == '0') { ++ i = 0; ++ s++; ++ goto done; ++ } ++ ++ i = strtoll(s, &convend, 10); ++ if (convend == s) ++ return NULL; ++ s += (convend - s); ++ ++done: ++ if (s >= end) ++ return NULL; ++ if (*s != 'e') ++ return NULL; ++ s++; ++ ++ ret = __bencode_item_alloc(buf, 0); ++ if (!ret) ++ return NULL; ++ ret->type = BENCODE_INTEGER; ++ ret->iov[0].iov_base = (void *) orig; ++ ret->iov[0].iov_len = s - orig; ++ ret->iov[1].iov_base = NULL; ++ ret->iov[1].iov_len = 0; ++ ret->iov_cnt = 1; ++ ret->str_len = s - orig; ++ ret->value = i; ++ ++ return ret; ++} ++ ++static bencode_item_t *__bencode_decode_string(bencode_buffer_t *buf, const char *s, const char *end) { ++ unsigned long int sl; ++ char *convend; ++ const char *orig = s; ++ bencode_item_t *ret; ++ ++ if (*s == '0') { ++ sl = 0; ++ s++; ++ goto colon; ++ } ++ ++ sl = strtoul(s, &convend, 10); ++ if (convend == s) ++ return NULL; ++ s += (convend - s); ++ ++colon: ++ if (s >= end) ++ return NULL; ++ if (*s != ':') ++ return NULL; ++ s++; ++ ++ if (s + sl > end) ++ return NULL; ++ ++ ret = __bencode_item_alloc(buf, 0); ++ if (!ret) ++ return NULL; ++ ret->type = BENCODE_STRING; ++ ret->iov[0].iov_base = (void *) orig; ++ ret->iov[0].iov_len = s - orig; ++ ret->iov[1].iov_base = (void *) s; ++ ret->iov[1].iov_len = sl; ++ ret->iov_cnt = 2; ++ ret->str_len = s - orig + sl; ++ ++ return ret; ++} ++ ++static bencode_item_t *__bencode_decode(bencode_buffer_t *buf, const char *s, const char *end) { ++ if (s >= end) ++ return NULL; ++ ++ switch (*s) { ++ case 'd': ++ return __bencode_decode_dictionary(buf, s, end); ++ case 'l': ++ return __bencode_decode_list(buf, s, end); ++ case 'i': ++ return __bencode_decode_integer(buf, s, end); ++ case 'e': ++ return &__bencode_end_marker; ++ case '0': ++ case '1': ++ case '2': ++ case '3': ++ case '4': ++ case '5': ++ case '6': ++ case '7': ++ case '8': ++ case '9': ++ return __bencode_decode_string(buf, s, end); ++ default: ++ return NULL; ++ } ++} ++ ++bencode_item_t *bencode_decode(bencode_buffer_t *buf, const char *s, int len) { ++ assert(s != NULL); ++ return __bencode_decode(buf, s, s + len); ++} ++ ++ ++static int __bencode_dictionary_key_match(bencode_item_t *key, const char *keystr, int keylen) { ++ assert(key->type == BENCODE_STRING); ++ ++ if (keylen != key->iov[1].iov_len) ++ return 0; ++ if (memcmp(keystr, key->iov[1].iov_base, keylen)) ++ return 0; ++ ++ return 1; ++} ++ ++bencode_item_t *bencode_dictionary_get_len(bencode_item_t *dict, const char *keystr, int keylen) { ++ bencode_item_t *key; ++ unsigned int bucket, i; ++ struct __bencode_hash *hash; ++ ++ if (!dict) ++ return NULL; ++ if (dict->type != BENCODE_DICTIONARY) ++ return NULL; ++ ++ /* try hash lookup first if possible */ ++ if (dict->value == 1) { ++ hash = (void *) dict->__buf; ++ i = bucket = __bencode_hash_str_len((const unsigned char *) keystr, keylen); ++ while (1) { ++ key = hash->buckets[i]; ++ if (!key) ++ return NULL; /* would be there, but isn't */ ++ assert(key->sibling != NULL); ++ if (__bencode_dictionary_key_match(key, keystr, keylen)) ++ return key->sibling; ++ i++; ++ if (i >= BENCODE_HASH_BUCKETS) ++ i = 0; ++ if (i == bucket) ++ break; /* fall back to regular lookup */ ++ } ++ } ++ ++ for (key = dict->child; key; key = key->sibling->sibling) { ++ assert(key->sibling != NULL); ++ if (__bencode_dictionary_key_match(key, keystr, keylen)) ++ return key->sibling; ++ } ++ ++ return NULL; ++} ++ ++void bencode_buffer_destroy_add(bencode_buffer_t *buf, free_func_t func, void *p) { ++ struct __bencode_free_list *li; ++ ++ if (!p) ++ return; ++ li = __bencode_alloc(buf, sizeof(*li)); ++ if (!li) ++ return; ++ li->ptr = p; ++ li->func = func; ++ li->next = buf->free_list; ++ buf->free_list = li; ++} +diff --git a/modules/rtpengine/bencode.h b/modules/rtpengine/bencode.h +new file mode 100644 +index 0000000..ed1eb8c +--- /dev/null ++++ b/modules/rtpengine/bencode.h +@@ -0,0 +1,555 @@ ++/* ++ * Copyright ?? ++ * ++ * This file is part of opensips, a free SIP server. ++ * ++ * opensips is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version ++ * ++ * opensips is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * History: ++ * --------- ++ * 2014-06-17 Initial upload ++ */ ++ ++ ++#ifndef _BENCODE_H_ ++#define _BENCODE_H_ ++ ++#include ++#include ++ ++#if defined(PKG_MALLOC) || defined(pkg_malloc) ++/* opensips */ ++# include "../../mem/mem.h" ++# include "../../str.h" ++# ifndef BENCODE_MALLOC ++# define BENCODE_MALLOC pkg_malloc ++# define BENCODE_FREE pkg_free ++# endif ++#else ++/* mediaproxy-ng */ ++# include "str.h" ++# ifndef BENCODE_MALLOC ++# define BENCODE_MALLOC malloc ++# define BENCODE_FREE free ++# endif ++#endif ++ ++struct bencode_buffer; ++enum bencode_type; ++struct bencode_item; ++struct __bencode_buffer_piece; ++struct __bencode_free_list; ++ ++typedef enum bencode_type bencode_type_t; ++typedef struct bencode_buffer bencode_buffer_t; ++typedef struct bencode_item bencode_item_t; ++typedef void (*free_func_t)(void *); ++ ++enum bencode_type { ++ BENCODE_INVALID = 0, ++ BENCODE_STRING, /* byte string */ ++ BENCODE_INTEGER, /* long long int */ ++ BENCODE_LIST, /* flat list of other objects */ ++ BENCODE_DICTIONARY, /* dictionary of key/values pairs. keys are always strings */ ++ BENCODE_IOVEC, /* special case of a string, built through bencode_string_iovec() */ ++ BENCODE_END_MARKER, /* used internally only */ ++}; ++ ++struct bencode_item { ++ bencode_type_t type; ++ struct iovec iov[2]; /* when decoding, iov[1] contains the contents of a string object */ ++ unsigned int iov_cnt; ++ unsigned int str_len; /* length of the whole ENCODED object. NOT the length of a byte string */ ++ long long int value; /* when decoding an integer, contains the value; otherwise used internally */ ++ bencode_item_t *parent, *child, *last_child, *sibling; ++ bencode_buffer_t *buffer; ++ char __buf[0]; ++}; ++ ++struct bencode_buffer { ++ struct __bencode_buffer_piece *pieces; ++ struct __bencode_free_list *free_list; ++ int error:1; /* set to !0 if allocation failed at any point */ ++}; ++ ++ ++ ++ ++ ++/*** INIT & DESTROY ***/ ++ ++/* Initializes a bencode_buffer_t object. This object is used to group together all memory allocations ++ * made when encoding or decoding. Its memory usage is always growing, until it is freed, at which point ++ * all objects created through it become invalid. The actual object must be allocated separately, for ++ * example by being put on the stack. ++ * Returns 0 on success or -1 on failure (if no memory could be allocated). */ ++int bencode_buffer_init(bencode_buffer_t *buf); ++ ++/* Destroys a previously initialized bencode_buffer_t object. All memory used by the object is freed ++ * and all objects created through it become invalid. */ ++void bencode_buffer_free(bencode_buffer_t *buf); ++ ++/* Creates a new empty dictionary object. Memory will be allocated from the bencode_buffer_t object. ++ * Returns NULL if no memory could be allocated. */ ++bencode_item_t *bencode_dictionary(bencode_buffer_t *buf); ++ ++/* Creates a new empty list object. Memory will be allocated from the bencode_buffer_t object. ++ * Returns NULL if no memory could be allocated. */ ++bencode_item_t *bencode_list(bencode_buffer_t *buf); ++ ++/* Adds a pointer to the bencode_buffer_t object's internal free list. When the bencode_buffer_t ++ * object is destroyed, the specified function will be called on this pointer. */ ++void bencode_buffer_destroy_add(bencode_buffer_t *buf, free_func_t, void *); ++ ++/* Returns the buffer associated with an item, or NULL if pointer given is NULL */ ++static inline bencode_buffer_t *bencode_item_buffer(bencode_item_t *); ++ ++ ++ ++ ++ ++/*** DICTIONARY BUILDING ***/ ++ ++/* Adds a new key/value pair to a dictionary. Memory will be allocated from the same bencode_buffer_t ++ * object as the dictionary was allocated from. Returns NULL if no memory could be allocated, otherwise ++ * returns "val". ++ * The function does not check whether the key being added is already present in the dictionary. ++ * Also, the function does not reorder keys into lexicographical order; keys will be encoded in ++ * the same order as they've been added. The key must a null-terminated string. ++ * The value to be added must not have been previously linked into any other dictionary or list. */ ++static inline bencode_item_t *bencode_dictionary_add(bencode_item_t *dict, const char *key, bencode_item_t *val); ++ ++/* Identical to bencode_dictionary_add() but doesn't require the key string to be null-terminated */ ++bencode_item_t *bencode_dictionary_add_len(bencode_item_t *dict, const char *key, int keylen, bencode_item_t *val); ++ ++/* Convenience function to add a string value to a dictionary, possibly duplicated into the ++ * bencode_buffer_t object. */ ++static inline bencode_item_t *bencode_dictionary_add_string(bencode_item_t *dict, const char *key, const char *val); ++static inline bencode_item_t *bencode_dictionary_add_string_dup(bencode_item_t *dict, const char *key, const char *val); ++ ++/* Ditto, but for a "str" object */ ++static inline bencode_item_t *bencode_dictionary_add_str(bencode_item_t *dict, const char *key, const str *val); ++static inline bencode_item_t *bencode_dictionary_add_str_dup(bencode_item_t *dict, const char *key, const str *val); ++ ++/* Ditto, but adds a string created through an iovec array to the dictionary. See ++ * bencode_string_iovec(). */ ++static inline bencode_item_t *bencode_dictionary_add_iovec(bencode_item_t *dict, const char *key, ++ const struct iovec *iov, int iov_cnt, int str_len); ++ ++/* Convenience functions to add the respective (newly created) objects to a dictionary */ ++static inline bencode_item_t *bencode_dictionary_add_integer(bencode_item_t *dict, const char *key, long long int val); ++static inline bencode_item_t *bencode_dictionary_add_dictionary(bencode_item_t *dict, const char *key); ++static inline bencode_item_t *bencode_dictionary_add_list(bencode_item_t *dict, const char *key); ++ ++ ++ ++ ++ ++/*** LIST BUILDING ***/ ++ ++/* Adds a new item to a list. Returns "item". ++ * The item to be added must not have been previously linked into any other dictionary or list. */ ++bencode_item_t *bencode_list_add(bencode_item_t *list, bencode_item_t *item); ++ ++/* Convenience function to add the respective (newly created) objects to a list */ ++static inline bencode_item_t *bencode_list_add_string(bencode_item_t *list, const char *s); ++static inline bencode_item_t *bencode_list_add_list(bencode_item_t *list); ++static inline bencode_item_t *bencode_list_add_dictionary(bencode_item_t *list); ++ ++ ++ ++ ++ ++/*** STRING BUILDING & HANDLING ***/ ++ ++/* Creates a new byte-string object. The given string does not have to be null-terminated, instead ++ * the length of the string is specified by the "len" parameter. Returns NULL if no memory could ++ * be allocated. ++ * Strings are not copied or duplicated, so the string pointed to by "s" must remain valid until ++ * the complete document is finally encoded or sent out. */ ++bencode_item_t *bencode_string_len(bencode_buffer_t *buf, const char *s, int len); ++ ++/* Creates a new byte-string object. The given string must be null-terminated. Otherwise identical ++ * to bencode_string_len(). */ ++static inline bencode_item_t *bencode_string(bencode_buffer_t *buf, const char *s); ++ ++/* Creates a new byte-string object from a "str" object. The string does not have to be null- ++ * terminated. */ ++static inline bencode_item_t *bencode_str(bencode_buffer_t *buf, const str *s); ++ ++/* Identical to the above three functions, but copies the string into the bencode_buffer_t object. ++ * Thus, the given string doesn't have to remain valid and accessible afterwards. */ ++bencode_item_t *bencode_string_len_dup(bencode_buffer_t *buf, const char *s, int len); ++static inline bencode_item_t *bencode_string_dup(bencode_buffer_t *buf, const char *s); ++static inline bencode_item_t *bencode_str_dup(bencode_buffer_t *buf, const str *s); ++ ++/* Creates a new byte-string object from an iovec array. The created object has different internal ++ * semantics (not a BENCODE_STRING, but a BENCODE_IOVEC) and must not be treated like other string ++ * objects. The array pointer and contents must still be valid and accessible when the complete ++ * document is encoded. The full length of the string composed of the iovec array is given in the ++ * "str_len" parameter, which can be negative, in which case the array is iterated to calculate the ++ * length. */ ++bencode_item_t *bencode_string_iovec(bencode_buffer_t *buf, const struct iovec *iov, int iov_cnt, int str_len); ++ ++/* Convenience function to compare a string object to a regular C string. Returns 2 if object ++ * isn't a string object, otherwise returns according to strcmp(). */ ++static inline int bencode_strcmp(bencode_item_t *a, const char *b); ++ ++/* Converts the string object "in" into a str object "out". Returns "out" on success, or NULL on ++ * error ("in" was NULL or not a string object). */ ++static inline str *bencode_get_str(bencode_item_t *in, str *out); ++ ++ ++ ++ ++ ++/*** INTEGER BUILDING ***/ ++ ++/* Creates a new integer object. Returns NULL if no memory could be allocated. */ ++bencode_item_t *bencode_integer(bencode_buffer_t *buf, long long int i); ++ ++ ++ ++ ++ ++/*** COLLAPSING & ENCODING ***/ ++ ++/* Collapses and encodes the complete document structure under the "root" element (which normally ++ * is either a dictionary or a list) into an array of "iovec" structures. This array can then be ++ * passed to functions ala writev() or sendmsg() to output the encoded document as a whole. Memory ++ * is allocated from the same bencode_buffer_t object as the "root" object was allocated from. ++ * The "head" and "tail" parameters specify additional "iovec" structures that should be included ++ * in the allocated array before or after (respectively) the iovec structures used by the encoded ++ * document. Both parameters can be zero if no additional elements in the array are required. ++ * Returns a pointer to the allocated array or NULL if no memory could be allocated. The number of ++ * array elements is returned in "cnt" which must be a valid pointer to an int. This number does ++ * not include any additional elements allocated through the "head" or "tail" parameters. ++ * ++ * Therefore, the contents of the returned array are: ++ * [0 .. (head - 1)] = unused and uninitialized iovec structures ++ * [(head) .. (head + cnt - 1)] = the encoded document ++ * [(head + cnt) .. (head + cnt + tail - 1)] = unused and uninitialized iovec structures ++ * ++ * The returned array will be freed when the corresponding bencode_buffer_t object is destroyed. */ ++struct iovec *bencode_iovec(bencode_item_t *root, int *cnt, unsigned int head, unsigned int tail); ++ ++/* Similar to bencode_iovec(), but instead returns the encoded document as a null-terminated string. ++ * Memory for the string is allocated from the same bencode_buffer_t object as the "root" object ++ * was allocated from. If "len" is a non-NULL pointer, the length of the genrated string is returned ++ * in *len. This is important if the encoded document contains binary data, in which case null ++ * termination cannot be trusted. The returned string is freed when the corresponding ++ * bencode_buffer_t object is destroyed. */ ++char *bencode_collapse(bencode_item_t *root, int *len); ++ ++/* Identical to bencode_collapse() but fills in a "str" object. Returns "out". */ ++static str *bencode_collapse_str(bencode_item_t *root, str *out); ++ ++/* Identical to bencode_collapse(), but the memory for the returned string is not allocated from ++ * a bencode_buffer_t object, but instead using the function defined as BENCODE_MALLOC (normally ++ * malloc() or pkg_malloc()), similar to strdup(). Using this function, the bencode_buffer_t ++ * object can be destroyed, but the returned string remains valid and usable. */ ++char *bencode_collapse_dup(bencode_item_t *root, int *len); ++ ++ ++ ++ ++ ++/*** DECODING ***/ ++ ++/* Decodes an encoded document from a string into a tree of bencode_item_t objects. The string does ++ * not need to be null-terminated, instead the length of the string is given through the "len" ++ * parameter. Memory is allocated from the bencode_buffer_t object. Returns NULL if no memory could ++ * be allocated or if the document could not be successfully decoded. ++ * ++ * The returned element is the "root" of the document tree and normally is either a list object or ++ * a dictionary object, but can also be a single string or integer object with no other objects ++ * underneath or besides it (no childred and no siblings). The type of the object can be determined ++ * by its ->type property. ++ * ++ * The number of bytes that could successfully be decoded into an object tree can be accessed through ++ * the root element's ->str_len property. Normally, this number should be equal to the "len" parameter ++ * passed, in which case the full string could be decoded. If ->str_len is less than "len", then there ++ * was additional stray byte data after the end of the encoded document. ++ * ++ * The document tree can be traversed through the ->child and ->sibling pointers in each object. The ++ * ->child pointer will be NULL for string and integer objects, as they don't contain other objects. ++ * For lists and dictionaries, ->child will be a pointer to the first contained object. This first ++ * contained object's ->sibling pointer will point to the next (second) contained object of the list ++ * or the dictionary, and so on. The last contained element of a list of dictionary will have a ++ * NULL ->sibling pointer. ++ * ++ * Dictionaries are like lists with ordered key/value pairs. When traversing dictionaries like ++ * lists, the following applies: The first element in the list (where ->child points to) will be the ++ * key of the first key/value pair (guaranteed to be a string and guaranteed to be present). The ++ * next element (following one ->sibling) will be the value of the first key/value pair. Following ++ * another ->sibling will point to the key of the next (second) key/value pair, and so on. ++ * ++ * However, to access children objects of dictionaries, the special functions following the naming ++ * scheme bencode_dictionary_get_* below should be used. They perform key lookup through a simple ++ * hash built into the dictionary object and so perform the lookup much faster. Only dictionaries ++ * created through a decoding process (i.e. not ones created from bencode_dictionary()) have this ++ * property. The hash is efficient only up to a certain number of elements (BENCODE_HASH_BUCKETS ++ * in bencode.c) contained in the dictionary. If the number of children object exceeds this number, ++ * key lookup will be slower than simply linearily traversing the list. ++ * ++ * The decoding function for dictionary object does not check whether keys are unique within the ++ * dictionary. It also does not care about lexicographical order of the keys. ++ * ++ * Decoded string objects will contain the raw decoded byte string in ->iov[1] (including the correct ++ * length). Strings are NOT null-terminated. Decoded integer objects will contain the decoded value ++ * in ->value. ++ * ++ * All memory is freed when the bencode_buffer_t object is destroyed. ++ */ ++bencode_item_t *bencode_decode(bencode_buffer_t *buf, const char *s, int len); ++ ++/* Identical to bencode_decode(), but returns successfully only if the type of the decoded object match ++ * "expect". */ ++static inline bencode_item_t *bencode_decode_expect(bencode_buffer_t *buf, const char *s, int len, bencode_type_t expect); ++ ++/* Identical to bencode_decode_expect() but takes a "str" argument. */ ++static inline bencode_item_t *bencode_decode_expect_str(bencode_buffer_t *buf, const str *s, bencode_type_t expect); ++ ++ ++ ++ ++ ++/*** DICTIONARY LOOKUP & EXTRACTION ***/ ++ ++/* Searches the given dictionary object for the given key and returns the respective value. Returns ++ * NULL if the given object isn't a dictionary or if the key doesn't exist. The key must be a ++ * null-terminated string. */ ++static inline bencode_item_t *bencode_dictionary_get(bencode_item_t *dict, const char *key); ++ ++/* Identical to bencode_dictionary_get() but doesn't require the key to be null-terminated. */ ++bencode_item_t *bencode_dictionary_get_len(bencode_item_t *dict, const char *key, int key_len); ++ ++/* Identical to bencode_dictionary_get() but returns the value only if its type is a string, and ++ * returns it as a pointer to the string itself. Returns NULL if the value is of some other type. The ++ * returned string is NOT null-terminated. Length of the string is returned in *len, which must be a ++ * valid pointer. The returned string will be valid until dict's bencode_buffer_t object is destroyed. */ ++static inline char *bencode_dictionary_get_string(bencode_item_t *dict, const char *key, int *len); ++ ++/* Identical to bencode_dictionary_get_string() but fills in a "str" struct. Returns str->s, which ++ * may be NULL. */ ++static inline char *bencode_dictionary_get_str(bencode_item_t *dict, const char *key, str *str); ++ ++/* Looks up the given key in the dictionary and compares the corresponding value to the given ++ * null-terminated string. Returns 2 if the key isn't found or if the value isn't a string, otherwise ++ * returns according to strcmp(). */ ++static inline int bencode_dictionary_get_strcmp(bencode_item_t *dict, const char *key, const char *str); ++ ++/* Identical to bencode_dictionary_get() but returns the string in a newly allocated buffer (using the ++ * BENCODE_MALLOC function), which remains valid even after bencode_buffer_t is destroyed. */ ++static inline char *bencode_dictionary_get_string_dup(bencode_item_t *dict, const char *key, int *len); ++ ++/* Combines bencode_dictionary_get_str() and bencode_dictionary_get_string_dup(). Fills in a "str" ++ * struct, but copies the string into a newly allocated buffer. Returns str->s. */ ++static inline char *bencode_dictionary_get_str_dup(bencode_item_t *dict, const char *key, str *str); ++ ++/* Identical to bencode_dictionary_get_string() but expects an integer object. The parameter "defval" ++ * specified which value should be returned if the key is not found or if the value is not an integer. */ ++static inline long long int bencode_dictionary_get_integer(bencode_item_t *dict, const char *key, long long int defval); ++ ++/* Identical to bencode_dictionary_get(), but returns the object only if its type matches "expect". */ ++static inline bencode_item_t *bencode_dictionary_get_expect(bencode_item_t *dict, const char *key, bencode_type_t expect); ++ ++ ++ ++ ++ ++/**************************/ ++ ++static inline bencode_buffer_t *bencode_item_buffer(bencode_item_t *i) { ++ if (!i) ++ return NULL; ++ return i->buffer; ++} ++ ++static inline bencode_item_t *bencode_string(bencode_buffer_t *buf, const char *s) { ++ return bencode_string_len(buf, s, strlen(s)); ++} ++ ++static inline bencode_item_t *bencode_string_dup(bencode_buffer_t *buf, const char *s) { ++ return bencode_string_len_dup(buf, s, strlen(s)); ++} ++ ++static inline bencode_item_t *bencode_str(bencode_buffer_t *buf, const str *s) { ++ return bencode_string_len(buf, s->s, s->len); ++} ++ ++static inline bencode_item_t *bencode_str_dup(bencode_buffer_t *buf, const str *s) { ++ return bencode_string_len_dup(buf, s->s, s->len); ++} ++ ++static inline bencode_item_t *bencode_dictionary_add(bencode_item_t *dict, const char *key, bencode_item_t *val) { ++ if (!key) ++ return NULL; ++ return bencode_dictionary_add_len(dict, key, strlen(key), val); ++} ++ ++static inline bencode_item_t *bencode_dictionary_add_string(bencode_item_t *dict, const char *key, const char *val) { ++ if (!val) ++ return NULL; ++ return bencode_dictionary_add(dict, key, bencode_string(bencode_item_buffer(dict), val)); ++} ++ ++static inline bencode_item_t *bencode_dictionary_add_string_dup(bencode_item_t *dict, const char *key, const char *val) { ++ if (!val) ++ return NULL; ++ return bencode_dictionary_add(dict, key, bencode_string_dup(bencode_item_buffer(dict), val)); ++} ++ ++static inline bencode_item_t *bencode_dictionary_add_str(bencode_item_t *dict, const char *key, const str *val) { ++ if (!val) ++ return NULL; ++ return bencode_dictionary_add(dict, key, bencode_str(bencode_item_buffer(dict), val)); ++} ++ ++static inline bencode_item_t *bencode_dictionary_add_str_dup(bencode_item_t *dict, const char *key, const str *val) { ++ if (!val) ++ return NULL; ++ return bencode_dictionary_add(dict, key, bencode_str_dup(bencode_item_buffer(dict), val)); ++} ++ ++static inline bencode_item_t *bencode_dictionary_add_integer(bencode_item_t *dict, const char *key, long long int val) { ++ return bencode_dictionary_add(dict, key, bencode_integer(bencode_item_buffer(dict), val)); ++} ++ ++static inline bencode_item_t *bencode_dictionary_add_dictionary(bencode_item_t *dict, const char *key) { ++ return bencode_dictionary_add(dict, key, bencode_dictionary(bencode_item_buffer(dict))); ++} ++ ++static inline bencode_item_t *bencode_dictionary_add_list(bencode_item_t *dict, const char *key) { ++ return bencode_dictionary_add(dict, key, bencode_list(bencode_item_buffer(dict))); ++} ++ ++static inline bencode_item_t *bencode_list_add_string(bencode_item_t *list, const char *s) { ++ return bencode_list_add(list, bencode_string(bencode_item_buffer(list), s)); ++} ++ ++static inline bencode_item_t *bencode_list_add_list(bencode_item_t *list) { ++ return bencode_list_add(list, bencode_list(bencode_item_buffer(list))); ++} ++ ++static inline bencode_item_t *bencode_list_add_dictionary(bencode_item_t *list) { ++ return bencode_list_add(list, bencode_dictionary(bencode_item_buffer(list))); ++} ++ ++static inline bencode_item_t *bencode_dictionary_get(bencode_item_t *dict, const char *key) { ++ if (!key) ++ return NULL; ++ return bencode_dictionary_get_len(dict, key, strlen(key)); ++} ++ ++static inline char *bencode_dictionary_get_string(bencode_item_t *dict, const char *key, int *len) { ++ bencode_item_t *val; ++ val = bencode_dictionary_get(dict, key); ++ if (!val || val->type != BENCODE_STRING) ++ return NULL; ++ *len = val->iov[1].iov_len; ++ return val->iov[1].iov_base; ++} ++ ++static inline char *bencode_dictionary_get_str(bencode_item_t *dict, const char *key, str *str) { ++ str->s = bencode_dictionary_get_string(dict, key, &str->len); ++ if (!str->s) ++ str->len = 0; ++ return str->s; ++} ++ ++static inline char *bencode_dictionary_get_string_dup(bencode_item_t *dict, const char *key, int *len) { ++ const char *s; ++ char *ret; ++ s = bencode_dictionary_get_string(dict, key, len); ++ if (!s) ++ return NULL; ++ ret = BENCODE_MALLOC(*len); ++ if (!ret) ++ return NULL; ++ memcpy(ret, s, *len); ++ return ret; ++} ++ ++static inline char *bencode_dictionary_get_str_dup(bencode_item_t *dict, const char *key, str *str) { ++ str->s = bencode_dictionary_get_string_dup(dict, key, &str->len); ++ return str->s; ++} ++ ++static inline long long int bencode_dictionary_get_integer(bencode_item_t *dict, const char *key, long long int defval) { ++ bencode_item_t *val; ++ val = bencode_dictionary_get(dict, key); ++ if (!val || val->type != BENCODE_INTEGER) ++ return defval; ++ return val->value; ++} ++ ++static inline bencode_item_t *bencode_decode_expect(bencode_buffer_t *buf, const char *s, int len, bencode_type_t expect) { ++ bencode_item_t *ret; ++ ret = bencode_decode(buf, s, len); ++ if (!ret || ret->type != expect) ++ return NULL; ++ return ret; ++} ++ ++static inline bencode_item_t *bencode_decode_expect_str(bencode_buffer_t *buf, const str *s, bencode_type_t expect) { ++ return bencode_decode_expect(buf, s->s, s->len, expect); ++} ++ ++static inline bencode_item_t *bencode_dictionary_get_expect(bencode_item_t *dict, const char *key, bencode_type_t expect) { ++ bencode_item_t *ret; ++ ret = bencode_dictionary_get(dict, key); ++ if (!ret || ret->type != expect) ++ return NULL; ++ return ret; ++} ++static inline str *bencode_collapse_str(bencode_item_t *root, str *out) { ++ out->s = bencode_collapse(root, &out->len); ++ return out; ++} ++static inline int bencode_strcmp(bencode_item_t *a, const char *b) { ++ int len; ++ if (a->type != BENCODE_STRING) ++ return 2; ++ len = strlen(b); ++ if (a->iov[1].iov_len < len) ++ return -1; ++ if (a->iov[1].iov_len > len) ++ return 1; ++ return memcmp(a->iov[1].iov_base, b, len); ++} ++static inline int bencode_dictionary_get_strcmp(bencode_item_t *dict, const char *key, const char *str) { ++ bencode_item_t *i; ++ i = bencode_dictionary_get(dict, key); ++ if (!i) ++ return 2; ++ return bencode_strcmp(i, str); ++} ++ ++static inline str *bencode_get_str(bencode_item_t *in, str *out) { ++ if (!in || in->type != BENCODE_STRING) ++ return NULL; ++ out->s = in->iov[1].iov_base; ++ out->len = in->iov[1].iov_len; ++ return out; ++} ++ ++static inline bencode_item_t *bencode_dictionary_add_iovec(bencode_item_t *dict, const char *key, ++ const struct iovec *iov, int iov_cnt, int str_len) ++{ ++ return bencode_dictionary_add(dict, key, bencode_string_iovec(bencode_item_buffer(dict), iov, iov_cnt, str_len)); ++} ++ ++#endif +diff --git a/modules/rtpengine/doc/rtpengine.xml b/modules/rtpengine/doc/rtpengine.xml +new file mode 100644 +index 0000000..30ef34a +--- /dev/null ++++ b/modules/rtpengine/doc/rtpengine.xml +@@ -0,0 +1,108 @@ ++ ++ ++ ++ ++ ++ ++%docentities; ++ ++]> ++ ++ ++ ++ rtpengine Module ++ &osipsname; ++ ++ ++ Maxim ++ Sobolev ++ Sippy Software, Inc. ++
++ sobomax@sippysoft.com ++
++
++ ++ Juha ++ Heinanen ++ TuTPro, Inc. ++
++ jh@tutpro.com ++
++
++ ++ Maxim ++ Sobolev ++
++ sobomax@sippysoft.com ++
++
++ ++ Bogdan-Andrei ++ Iancu ++
++ bogdan@voice-system.ro ++
++
++ ++ Juha ++ Heinanen ++
++ jh@tutpro.com ++
++
++ ++ Sas ++ Ovidiu ++
++ osas@voipembedded.com ++
++
++ ++ Carsten ++ Bock ++ ng-voice GmbH ++
++ carsten@ng-voice.com ++
++
++ ++ Richard ++ Fuchs ++ Sipwise GmbH ++
++ rfuchs@sipwise.com ++
++
++
++ ++ 2003-2008 ++ Sippy Software, Inc. ++ ++ ++ 2005 ++ Voice Sistem SRL ++ ++ ++ 2009-2014 ++ TuTPro Inc. ++ ++ ++ 2010 ++ VoIPEmbedded Inc. ++ ++ ++ 2013-2014 ++ Sipwise GmbH ++ ++
++ ++ ++ &admin; ++ &faq; ++ ++ ++
+diff --git a/modules/rtpengine/doc/rtpengine_admin.xml b/modules/rtpengine/doc/rtpengine_admin.xml +new file mode 100644 +index 0000000..ee9e3a8 +--- /dev/null ++++ b/modules/rtpengine/doc/rtpengine_admin.xml +@@ -0,0 +1,715 @@ ++ ++ ++ ++ ++ &adminguide; ++ ++
++ Overview ++ ++ This is a module that enables media streams to be proxied ++ via an &rtp; proxy. The only &rtp; proxy currently known to work ++ with this module is the Sipwise rtpengine ++ . ++ The rtpengine module is a modified version of the original ++ rtpproxy module using a new control protocol. The module is ++ designed to be a drop-in replacement for the old module from ++ a configuration file point of view, however due to the ++ incompatible control protocol, it only works with &rtp; proxies ++ which specifically support it. ++ ++
++ ++
++ Multiple &rtp; proxy usage ++ ++ The rtpengine module can support multiple &rtp; proxies for ++ balancing/distribution and control/selection purposes. ++ ++ ++ The module allows definition of several sets of rtpengines. ++ Load-balancing will be performed over a set and the admin has the ++ ability to choose what set should be used. The set is selected via ++ its id - the id being defined with the set. Refer to the ++ rtpengine_sock module parameter definition for syntax ++ description. ++ ++ ++ The balancing inside a set is done automatically by the module based on ++ the weight of each &rtp; proxy from the set. ++ ++ ++ The selection of the set is done from script prior using ++ rtpengine_delete(), rtpengine_offer() or rtpengine_answer() ++ functions - see the rtpengine_use_set() function. ++ ++ ++ Another way to select the set is to define setid_avp ++ module parameter and assign setid to the defined avp ++ before calling rtpengine_offer() or rtpengine_manage() ++ function. If forwarding of the requests fails and ++ there is another branch to try, remember to unset the ++ avp after calling rtpengine_delete() function. ++ ++ ++ For backward compatibility reasons, a set with no id take by default ++ the id 0. Also if no set is explicitly set before ++ rtpengine_delete(), rtpengine_offer() or rtpengine_answer() ++ the 0 id set will be used. ++ ++ ++ IMPORTANT: if you use multiple sets, take care and use the same set for ++ both rtpengine_offer()/rtpengine_answer() and rtpengine_delete()!! ++ If the set was selected using setid_avp, the avp needs to be ++ set only once before rtpengine_offer() or rtpengine_manage() call. ++ ++
++ ++
++ Dependencies ++
++ &osips; Modules ++ ++ The following modules must be loaded before this module: ++ ++ ++ ++ tm module - (optional) if you want to ++ have rtpengine_manage() fully functional ++ ++ ++ ++ ++
++
++ External Libraries or Applications ++ ++ The following libraries or applications must be installed before ++ running &osips; with this module loaded: ++ ++ ++ ++ None. ++ ++ ++ ++ ++
++
++ ++
++ Parameters ++
++ <varname>rtpengine_sock</varname> (string) ++ ++ Definition of socket(s) used to connect to (a set) &rtp; proxy. It may ++ specify a UNIX socket or an IPv4/IPv6 UDP socket. ++ ++ ++ ++ Default value is NONE (disabled). ++ ++ ++ ++ Set <varname>rtpengine_sock</varname> parameter ++ ++... ++# single rtproxy ++modparam("rtpengine", "rtpengine_sock", "udp:localhost:12221") ++# multiple rtproxies for LB ++modparam("rtpengine", "rtpengine_sock", ++ "udp:localhost:12221 udp:localhost:12222") ++# multiple sets of multiple rtproxies ++modparam("rtpengine", "rtpengine_sock", ++ "1 == udp:localhost:12221 udp:localhost:12222") ++modparam("rtpengine", "rtpengine_sock", ++ "2 == udp:localhost:12225") ++... ++ ++ ++
++
++ <varname>rtpengine_disable_tout</varname> (integer) ++ ++ Once an &rtp; proxy was found unreachable and marked as disabled, the rtpengine ++ module will not attempt to establish communication to that &rtp; proxy for ++ rtpengine_disable_tout seconds. ++ ++ ++ ++ Default value is 60. ++ ++ ++ ++ Set <varname>rtpengine_disable_tout</varname> parameter ++ ++... ++modparam("rtpengine", "rtpengine_disable_tout", 20) ++... ++ ++ ++
++
++ <varname>rtpengine_tout</varname> (integer) ++ ++ Timeout value in waiting for reply from &rtp; proxy. ++ ++ ++ ++ Default value is 1. ++ ++ ++ ++ Set <varname>rtpengine_tout</varname> parameter ++ ++... ++modparam("rtpengine", "rtpengine_tout", 2) ++... ++ ++ ++
++
++ <varname>rtpengine_retr</varname> (integer) ++ ++ How many times the module should retry to send and receive after ++ timeout was generated. ++ ++ ++ ++ Default value is 5. ++ ++ ++ ++ Set <varname>rtpengine_retr</varname> parameter ++ ++... ++modparam("rtpengine", "rtpengine_retr", 2) ++... ++ ++ ++
++
++ <varname>extra_id_pv</varname> (string) ++ ++ The parameter sets the PV defination to use when the b ++ parameter is used on rtpengine_delete(), rtpengine_offer(), ++ rtpengine_answer() or rtpengine_manage() command. ++ ++ Default is empty, the b parameter may not be used then. ++ ++ ++ Set <varname>extra_id_pv</varname> parameter ++ ++... ++modparam("rtpengine", "extra_id_pv", "$avp(extra_id)") ++... ++ ++ ++
++ ++
++ <varname>setid_avp</varname> (string) ++ ++ The parameter defines an AVP that, if set, ++ determines which &rtp; proxy set ++ rtpengine_offer(), rtpengine_answer(), ++ rtpengine_delete(), and rtpengine_manage() ++ functions use. ++ ++ ++ There is no default value. ++ ++ ++ Set <varname>setid_avp</varname> parameter ++ ++... ++modparam("rtpengine", "setid_avp", "$avp(setid)") ++... ++ ++ ++
++ ++
++ ++
++ Functions ++
++ ++ <function moreinfo="none">rtpengine_use_set(setid)</function> ++ ++ ++ Sets the ID of the &rtp; proxy set to be used for the next ++ rtpengine_delete(), rtpengine_offer(), rtpengine_answer() ++ or rtpengine_manage() command. The parameter can be an integer or ++ a config variable holding an integer. ++ ++ ++ This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, ++ BRANCH_ROUTE. ++ ++ ++ <function>rtpengine_use_set</function> usage ++ ++... ++rtpengine_use_set("2"); ++rtpengine_offer(); ++... ++ ++ ++
++
++ ++ <function moreinfo="none">rtpengine_offer([flags])</function> ++ ++ ++ Rewrites &sdp; body to ensure that media is passed through ++ an &rtp; proxy. To be invoked ++ on INVITE for the cases the SDPs are in INVITE and 200 OK and on 200 OK ++ when SDPs are in 200 OK and ACK. ++ ++ Meaning of the parameters is as follows: ++ ++ ++ ++ flags - flags to turn on some features. ++ ++ The flags string is a list of space-separated items. Each item ++ is either an individual token, or a token in key=value format. The ++ possible tokens are described below. ++ ++ ++ via-branch=... - Include the branch ++ value of one of the Via headers in the request to the ++ &rtp; proxy. Possible values are: ++ 1 - use the first Via header; ++ 2 - use the second Via header; ++ auto - use the first Via header if this is ++ a request, or the second one if this is a reply; ++ extra - don't take the value from a header, but instead use ++ the value of the extra_id_pv variable. ++ This can be used to create one media session per branch ++ on the &rtp; proxy. When sending a subsequent delete command to ++ the &rtp; proxy, you can then stop just the session for a specific branch when ++ passing the flag '1' or '2' in the rtpengine_delete, or stop ++ all sessions for a call when not passing one of those two flags there. This is ++ especially useful if you have serially forked call scenarios where the &rtp; proxy ++ gets an offer command for a new branch, and then a ++ delete command for the previous branch, which would otherwise ++ delete the full call, breaking the subsequent answer for the ++ new branch. This flag is only supported by the Sipwise rtpengine ++ &rtp; proxy at the moment! ++ ++ ++ asymmetric - flags that UA from which message is ++ received doesn't support symmetric RTP. (automatically sets the 'r' flag) ++ ++ ++ force-answer - force answer, that is, ++ only rewrite &sdp; when corresponding session already exists ++ in the &rtp; proxy. By default is on when the session is to be ++ completed. ++ ++ ++ internal, external - these flags specify the direction of ++ the SIP message. These flags only make sense when the &rtp; proxy is running ++ in bridge mode. internal corresponds to the proxy's first ++ interface, external corresponds to the &rtp; proxy's ++ second interface. You always have to specify two flags to define ++ the incoming network and the outgoing network. For example, internal ++ external should be ++ used for SIP message received from the local interface and sent out on the ++ external interface, and external internal vice versa. Other ++ options are internal internal and external ++ external. ++ So, for example if a SIP requests is processed with internal ++ external flags, the corresponding ++ response must be processed with internal external flags. ++ ++ ++ auto-bridge - this flag an alternative to the ++ internal and external flags ++ in order to do automatic bridging between IPv4 on the ++ "internal network" and IPv6 on the "external network". Instead of ++ explicitly instructing the &rtp; proxy to select a particular address ++ family, the distinction is done by the given IP in the SDP body by ++ the RTP proxy itself. Not supported by Sipwise rtpengine. ++ ++ ++ address-family=... - instructs the &rtp; proxy that the ++ recipient of this &sdp; body expects to see addresses of a particular family. ++ Possible values are IP4 and IP6. For example, ++ if the &sdp; body contains IPv4 addresses but the recipient only speaks IPv6, ++ you would use address-family=IP6 to bridge between the two ++ address families. ++ ++ Sipwise rtpengine remembers the address family preference of each party after ++ it has seen an &sdp; body from them. This means that normally it is only ++ necessary to explicitly specify the address family in the offer, ++ but not in the answer. ++ ++ Note: Please note, that this will only work properly with non-dual-stack user-agents or with ++ dual-stack clients according to RFC6157 (which suggest ICE for Dual-Stack implementations). ++ This short-cut will not work properly with RFC4091 (ANAT) compatible clients, which suggests ++ having different m-lines with different IP-protocols grouped together. ++ ++ ++ force - instructs the &rtp; proxy to ignore marks ++ inserted by another &rtp; proxy in transit to indicate that the ++ session is already goes through another proxy. Allows creating ++ a chain of proxies. Not supported and ignored by Sipwise rtpengine. ++ ++ ++ trust-address - flags that IP address in SDP should ++ be trusted. Without this flag, the &rtp; proxy ignores address in ++ the SDP and uses source address of the SIP message as media ++ address which is passed to the RTP proxy. ++ ++ ++ replace-origin - flags that IP from the origin ++ description (o=) should be also changed. ++ ++ ++ replace-session-connection - flags to change the session-level ++ SDP connection (c=) IP if media description also includes ++ connection information. ++ ++ ++ symmetric - flags that for the UA from which ++ message is received, support symmetric RTP must be forced. ++ ++ ++ repacketize=NN - requests the &rtp; proxy to perform ++ re-packetization of RTP traffic coming from the UA which ++ has sent the current message to increase or decrease payload ++ size per each RTP packet forwarded if possible. The NN is the ++ target payload size in ms, for the most codecs its value should ++ be in 10ms increments, however for some codecs the increment ++ could differ (e.g. 30ms for GSM or 20ms for G.723). The ++ &rtp; proxy would select the closest value supported by the codec. ++ This feature could be used for significantly reducing bandwith ++ overhead for low bitrate codecs, for example with G.729 going ++ from 10ms to 100ms saves two thirds of the network bandwith. ++ Not supported by Sipwise rtpengine. ++ ++ ++ ICE=... - controls the &rtp; proxy's behaviour ++ regarding ICE attributes within the &sdp; body. Possible values ++ are: force - ++ discard any ICE attributes already present in the &sdp; body ++ and then generate and insert new ICE data, leaving itself ++ as the only ICE candidates; ++ remove instructs the &rtp; proxy to discard ++ any ICE attributes and not insert any new ones into the &sdp;. ++ The default (if no ICE=... is given at all), ++ new ICE data will only be generated ++ if no ICE was present in the &sdp; originally; otherwise ++ the &rtp; proxy will only insert itself as an ++ additional ICE candidate. Other ++ &sdp; substitutions (c=, m=, etc) are unaffected by this flag. ++ ++ ++ RTP, SRTP, AVP, AVPF - These flags control the &rtp; ++ transport protocol that should be used towards the recipient of ++ the &sdp;. If none of them are specified, the protocol given in ++ the &sdp; is left untouched. Otherwise, the SRTP flag indicates that ++ SRTP should be used, while RTP indicates that SRTP should not be used. ++ AVPF indicates that the advanced RTCP profile with feedback messages ++ should be used, and AVP indicates that the regular RTCP profile ++ should be used. See also the next set of flags below. ++ ++ ++ RTP/AVP, RTP/SAVP, RTP/AVPF, RTP/SAVPF - these serve as ++ an alternative, more explicit way to select between the different &rtp; protocols ++ and profiles supported by the &rtp; proxy. For example, giving the flag ++ RTP/SAVPF has the same effect as giving the two flags ++ SRTP AVPF. ++ ++ ++ to-tag - force inclusion of the To tag. ++ Normally, the To tag is always included when present, except ++ for delete messages. Including the To tag in ++ a delete messages allows you to be more selective about which ++ dialogues within a call are being torn down. ++ ++ ++ rtcp-mux-demux - if rtcp-mux (RFC 5761) was ++ offered, make the &rtp; proxy accept the offer, but not offer it to the ++ recipient of this message. ++ ++ ++ rtcp-mux-reject - if rtcp-mux was offered, make the ++ &rtp; proxy reject the offer, but still offer it to the recipient. Can be ++ combined with rtcp-mux-offer to always offer it. ++ ++ ++ rtcp-mux-offer - make the &rtp; proxy offer rtcp-mux ++ to the recipient of this message, regardless of whether it was offered ++ originally or not. ++ ++ ++ rtcp-mux-accept - if rtcp-mux was offered, make the ++ &rtp; proxy accept the offer and also offer it to the recipient of this ++ message. Can be combined with rtcp-mux-offer to always offer it. ++ ++ ++ media-address=... - force a particular media address to ++ be used in the &sdp; body. Address family is detected automatically. ++ ++ ++ ++ ++ ++ This function can be used from ANY_ROUTE. ++ ++ ++ <function>rtpengine_offer</function> usage ++ ++route { ++... ++ if (is_method("INVITE")) { ++ if (has_body("application/sdp")) { ++ if (rtpengine_offer()) ++ t_on_reply("1"); ++ } else { ++ t_on_reply("2"); ++ } ++ } ++ if (is_method("ACK") && has_body("application/sdp")) ++ rtpengine_answer(); ++... ++} ++ ++onreply_route[1] ++{ ++... ++ if (has_body("application/sdp")) ++ rtpengine_answer(); ++... ++} ++ ++onreply_route[2] ++{ ++... ++ if (has_body("application/sdp")) ++ rtpengine_offer(); ++... ++} ++ ++ ++
++
++ ++ <function moreinfo="none">rtpengine_answer([flags])</function> ++ ++ ++ Rewrites &sdp; body to ensure that media is passed through ++ an &rtp; proxy. To be invoked ++ on 200 OK for the cases the SDPs are in INVITE and 200 OK and on ACK ++ when SDPs are in 200 OK and ACK. ++ ++ ++ See rtpengine_offer() function description above for the meaning of the ++ parameters. ++ ++ ++ This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, ++ FAILURE_ROUTE, BRANCH_ROUTE. ++ ++ ++ <function>rtpengine_answer</function> usage ++ ++ See rtpengine_offer() function example above for example. ++ ++ ++
++
++ ++ <function moreinfo="none">rtpengine_delete([flags])</function> ++ ++ ++ Tears down the RTPProxy session for the current call. ++ ++ ++ See rtpengine_offer() function description above for the meaning of the ++ parameters. Note that not all flags make sense for a delete. ++ ++ ++ This function can be used from ANY_ROUTE. ++ ++ ++ <function>rtpengine_delete</function> usage ++ ++... ++rtpengine_delete(); ++... ++ ++ ++
++ ++
++ ++ <function moreinfo="none">rtpengine_manage([flags])</function> ++ ++ ++ Manage the RTPProxy session - it combines the functionality of ++ rtpengine_offer(), rtpengine_answer() and rtpengine_delete(), detecting ++ internally based on message type and method which one to execute. ++ ++ ++ It can take the same parameters as rtpengine_offer(). ++ The flags parameter to rtpengine_manage() can be a configuration variable ++ containing the flags as a string. ++ ++ ++ Functionality: ++ ++ ++ ++ ++ If INVITE with SDP, then do rtpengine_offer() ++ ++ ++ ++ ++ If INVITE with SDP, when the tm module is loaded, mark transaction with ++ internal flag FL_SDP_BODY to know that the 1xx and 2xx are for ++ rtpengine_answer() ++ ++ ++ ++ ++ If ACK with SDP, then do rtpengine_answer() ++ ++ ++ ++ ++ If BYE or CANCEL, or called within a FAILURE_ROUTE[], then do rtpengine_delete() ++ ++ ++ ++ ++ If reply to INVITE with code >= 300 do rtpengine_delete() ++ ++ ++ ++ ++ If reply with SDP to INVITE having code 1xx and 2xx, then ++ do rtpengine_answer() if the request had SDP or tm is not loaded, ++ otherwise do rtpengine_offer() ++ ++ ++ ++ ++ ++ This function can be used from ANY_ROUTE. ++ ++ ++ <function>rtpengine_manage</function> usage ++ ++... ++rtpengine_manage(); ++... ++ ++ ++
++ ++
++ ++ <function moreinfo="none">rtpengine_start_recording()</function> ++ ++ ++ This function will send a signal to the &rtp; proxy to record ++ the RTP stream on the &rtp; proxy. ++ This function is not supported by Sipwise rtpengine at the moment! ++ ++ ++ This function can be used from REQUEST_ROUTE and ONREPLY_ROUTE. ++ ++ ++ <function>rtpengine_start_recording</function> usage ++ ++... ++rtpengine_start_recording(); ++... ++ ++ ++
++ ++ ++
++ ++
++ Exported Pseudo Variables ++
++ <function moreinfo="none">$rtpstat</function> ++ ++ Returns the &rtp; statistics from the &rtp; proxy. The &rtp; statistics from the &rtp; proxy ++ are provided as a string and it does contain several packet counters. The statistics ++ must be retrieved before the session is deleted (before rtpengine_delete()). ++ ++ ++ ++ $rtpstat Usage ++ ++... ++ append_hf("X-RTP-Statistics: $rtpstat\r\n"); ++... ++ ++ ++
++ ++
++ ++
++ <acronym>MI</acronym> Commands ++
++ <function moreinfo="none">rtpengine_enable</function> ++ ++ Enables a &rtp; proxy if parameter value is greater than 0. ++ Disables it if a zero value is given. ++ ++ ++ The first parameter is the &rtp; proxy url (exactly as defined in ++ the config file). ++ ++ ++ The second parameter value must be a number in decimal. ++ ++ ++ NOTE: if a &rtp; proxy is defined multiple times (in the same or ++ diferente sete), all of its instances will be enables/disabled. ++ ++ ++ ++ <function moreinfo="none">rtpengine_enable</function> usage ++ ++... ++$ opensipsctl fifo rtpengine_enable udp:192.168.2.133:8081 0 ++... ++ ++ ++
++ ++
++ <function moreinfo="none">rtpengine_show</function> ++ ++ Displays all the &rtp; proxies and their information: set and ++ status (disabled or not, weight and recheck_ticks). ++ ++ ++ No parameter. ++ ++ ++ ++ <function moreinfo="none">rtpengine_show</function> usage ++ ++... ++$ opensipsctl fifo rtpengine_show ++... ++ ++ ++
++
++ ++
++ +diff --git a/modules/rtpengine/doc/rtpengine_faq.xml b/modules/rtpengine/doc/rtpengine_faq.xml +new file mode 100644 +index 0000000..8c821ca +--- /dev/null ++++ b/modules/rtpengine/doc/rtpengine_faq.xml +@@ -0,0 +1,94 @@ ++ ++ ++ ++ ++ &faqguide; ++ ++ ++ ++ How do I migrate from rtpproxy or rtpproxy-ng to ++ rtpengine? ++ ++ ++ ++ For the most part, only the names of the functions have changed, with ++ rtpproxy in each name replaced with rtpengine. ++ For example, rtpproxy_manage() has become ++ rtpengine_manage(). A few name duplications have also been resolved, ++ for example there is now a single rtpengine_delete() instead of ++ unforce_rtp_proxy() and the identical rtpproxy_destroy(). ++ ++ ++ The largest difference to the old module is how flags are passed to ++ rtpengine_offer(), rtpengine_answer(), ++ rtpengine_manage() and rtpengine_delete(). Instead of ++ having a string of single-letter flags, they now take a string of space-separated ++ items, with each item being either a single token (word) or a key=value ++ pair. ++ ++ ++ For example, if you had a call rtpproxy_offer("FRWOC+PS");, this would ++ then become: ++ ++ ++rtpengine_offer("force trust-address symmetric replace-origin replace-session-connection ICE=force RTP/SAVPF"); ++ ++ ++ Finally, if you were using the second paramater (explicit media address) to any of ++ these functions, this has been replaced by the media-address=... ++ option within the first string of flags. ++ ++ ++ ++ ++ ++ Where can I find more about OpenSIPS? ++ ++ ++ ++ Take a look at &osipshomelink;. ++ ++ ++ ++ ++ ++ Where can I post a question about this module? ++ ++ ++ ++ First at all check if your question was already answered on one of ++ our mailing lists: ++ ++ ++ ++ User Mailing List - &osipsuserslink; ++ ++ ++ Developer Mailing List - &osipsdevlink; ++ ++ ++ ++ E-mails regarding any stable &osips; release should be sent to ++ &osipsusersmail; and e-mails regarding development versions ++ should be sent to &osipsdevmail;. ++ ++ ++ If you want to keep the mail private, send it to ++ &osipshelpmail;. ++ ++ ++ ++ ++ ++ How can I report a bug? ++ ++ ++ ++ Please follow the guidelines provided at: ++ &osipsbugslink;. ++ ++ ++ ++ ++ ++ +diff --git a/modules/rtpengine/rtpengine.c b/modules/rtpengine/rtpengine.c +new file mode 100644 +index 0000000..cc86867 +--- /dev/null ++++ b/modules/rtpengine/rtpengine.c +@@ -0,0 +1,1908 @@ ++/* ++ * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com ++ * ++ * This file is part of opensips, a free SIP server. ++ * ++ * opensips is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version ++ * ++ * opensips is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * History: ++ * --------- ++ * 2014-06-17 Imported from rtpproxy module ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#ifndef __USE_BSD ++#define __USE_BSD ++#endif ++#include ++#ifndef __FAVOR_BSD ++#define __FAVOR_BSD ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../../str.h" ++#include "../../flags.h" ++#include "../../sr_module.h" ++#include "../../dprint.h" ++#include "../../data_lump.h" ++#include "../../data_lump_rpl.h" ++#include "../../error.h" ++#include "../../forward.h" ++#include "../../mem/mem.h" ++#include "../../parser/parse_from.h" ++#include "../../parser/parse_to.h" ++#include "../../parser/parse_uri.h" ++#include "../../parser/parser_f.h" ++#include "../../parser/sdp/sdp.h" ++#include "../../resolve.h" ++#include "../../timer.h" ++#include "../../trim.h" ++#include "../../ut.h" ++#include "../../pt.h" ++#include "../../pvar.h" ++#include "../../msg_translator.h" ++#include "../../usr_avp.h" ++#include "../../socket_info.h" ++#include "../../mod_fix.h" ++#include "../../dset.h" ++#include "../../route.h" ++#include "../../modules/tm/tm_load.h" ++#include "rtpengine.h" ++#include "rtpengine_funcs.h" ++#include "bencode.h" ++ ++#if !defined(AF_LOCAL) ++#define AF_LOCAL AF_UNIX ++#endif ++#if !defined(PF_LOCAL) ++#define PF_LOCAL PF_UNIX ++#endif ++ ++#define DEFAULT_RTPE_SET_ID 0 ++ ++#define MI_ENABLE_RTP_ENGINE "rtpengine_enable" ++#define MI_MIN_RECHECK_TICKS 0 ++#define MI_MAX_RECHECK_TICKS (unsigned int)-1 ++ ++#define MI_SHOW_RTP_ENGINES "rtpengine_show" ++ ++#define MI_RTP_ENGINE_NOT_FOUND "RTP engine not found" ++#define MI_RTP_ENGINE_NOT_FOUND_LEN (sizeof(MI_RTP_ENGINE_NOT_FOUND)-1) ++#define MI_SET "Set" ++#define MI_SET_LEN (sizeof(MI_SET)-1) ++#define MI_NODE "node" ++#define MI_NODE_LEN (sizeof(MI_NODE)-1) ++#define MI_INDEX "index" ++#define MI_INDEX_LEN (sizeof(MI_INDEX)-1) ++#define MI_DISABLED "disabled" ++#define MI_DISABLED_LEN (sizeof(MI_DISABLED)-1) ++#define MI_WEIGHT "weight" ++#define MI_WEIGHT_LEN (sizeof(MI_WEIGHT)-1) ++#define MI_RECHECK_TICKS "recheck_ticks" ++#define MI_RECHECK_T_LEN (sizeof(MI_RECHECK_TICKS)-1) ++ ++ ++ ++#define CPORT "22222" ++ ++enum rtpe_operation { ++ OP_OFFER = 1, ++ OP_ANSWER, ++ OP_DELETE, ++ OP_START_RECORDING, ++ OP_QUERY, ++}; ++ ++struct ng_flags_parse { ++ int via, to, packetize, transport; ++ bencode_item_t *dict, *flags, *direction, *replace, *rtcp_mux; ++}; ++ ++static const char *command_strings[] = { ++ [OP_OFFER] = "offer", ++ [OP_ANSWER] = "answer", ++ [OP_DELETE] = "delete", ++ [OP_START_RECORDING] = "start recording", ++ [OP_QUERY] = "query", ++}; ++ ++static char *gencookie(); ++static int rtpe_test(struct rtpe_node*, int, int); ++static int start_recording_f(struct sip_msg *, char *, char *); ++static int rtpengine_answer1_f(struct sip_msg *, char *, char *); ++static int rtpengine_offer1_f(struct sip_msg *, char *, char *); ++static int rtpengine_delete1_f(struct sip_msg *, char *, char *); ++static int rtpengine_manage1_f(struct sip_msg *, char *, char *); ++ ++static int parse_flags(struct ng_flags_parse *, struct sip_msg *, enum rtpe_operation *, const char *); ++ ++static int rtpengine_offer_answer(struct sip_msg *msg, const char *flags, int op); ++static int add_rtpengine_socks(struct rtpe_set * rtpe_list, char * rtpengine); ++static int fixup_set_id(void ** param, int param_no); ++static int set_rtpengine_set_f(struct sip_msg * msg, char * str1, char * str2); ++static struct rtpe_set * select_rtpe_set(int id_set); ++static struct rtpe_node *select_rtpe_node(str, int); ++static char *send_rtpe_command(struct rtpe_node *, bencode_item_t *, int *); ++static int get_extra_id(struct sip_msg* msg, str *id_str); ++ ++static int rtpengine_set_store(modparam_t type, void * val); ++static int rtpengine_add_rtpengine_set( char * rtp_proxies); ++ ++static int mod_init(void); ++static int child_init(int); ++static void mod_destroy(void); ++ ++/* Pseudo-Variables */ ++static int pv_get_rtpstat_f(struct sip_msg *, pv_param_t *, pv_value_t *); ++ ++/*mi commands*/ ++static struct mi_root* mi_enable_rtp_proxy(struct mi_root* cmd_tree, ++ void* param ); ++static struct mi_root* mi_show_rtpengines(struct mi_root* cmd_tree, ++ void* param); ++ ++ ++static int rtpengine_disable_tout = 60; ++static int rtpengine_retr = 5; ++static int rtpengine_tout = 1; ++static pid_t mypid; ++static unsigned int myseqn = 0; ++static str extra_id_pv_param = {NULL, 0}; ++static char *setid_avp_param = NULL; ++ ++static char ** rtpe_strings=0; ++static int rtpe_sets=0; /*used in rtpengine_set_store()*/ ++static int rtpe_set_count = 0; ++static unsigned int current_msg_id = (unsigned int)-1; ++/* RTP proxy balancing list */ ++struct rtpe_set_head * rtpe_set_list =0; ++struct rtpe_set * selected_rtpe_set =0; ++struct rtpe_set * default_rtpe_set=0; ++ ++/* array with the sockets used by rtpengine (per process)*/ ++static unsigned int rtpe_no = 0; ++static int *rtpe_socks = 0; ++ ++static int setid_avp_type; ++static int_str setid_avp; ++ ++typedef struct rtpe_set_link { ++ struct rtpe_set *rset; ++ pv_spec_t rpv; ++} rtpe_set_link_t; ++ ++/* tm */ ++static struct tm_binds tmb; ++ ++/*0-> disabled, 1 ->enabled*/ ++unsigned int *natping_state=0; ++ ++static pv_elem_t *extra_id_pv = NULL; ++ ++#define ANY_ROUTE (REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE) ++static cmd_export_t cmds[] = { ++ {"rtpengine_use_set", (cmd_function)set_rtpengine_set_f, 1, ++ fixup_set_id, 0, ++ ANY_ROUTE}, ++ {"rtpengine_start_recording", (cmd_function)start_recording_f, 0, ++ 0, 0, ++ ANY_ROUTE }, ++ {"rtpengine_offer", (cmd_function)rtpengine_offer1_f, 0, ++ 0, 0, ++ ANY_ROUTE}, ++ {"rtpengine_offer", (cmd_function)rtpengine_offer1_f, 1, ++ fixup_spve_null, 0, ++ ANY_ROUTE}, ++ {"rtpengine_answer", (cmd_function)rtpengine_answer1_f, 0, ++ 0, 0, ++ ANY_ROUTE}, ++ {"rtpengine_answer", (cmd_function)rtpengine_answer1_f, 1, ++ fixup_spve_null, 0, ++ ANY_ROUTE}, ++ {"rtpengine_manage", (cmd_function)rtpengine_manage1_f, 0, ++ 0, 0, ++ ANY_ROUTE}, ++ {"rtpengine_manage", (cmd_function)rtpengine_manage1_f, 1, ++ fixup_spve_null, 0, ++ ANY_ROUTE}, ++ {"rtpengine_delete", (cmd_function)rtpengine_delete1_f, 0, ++ 0, 0, ++ ANY_ROUTE}, ++ {"rtpengine_delete", (cmd_function)rtpengine_delete1_f, 1, ++ fixup_spve_null, 0, ++ ANY_ROUTE}, ++ {0, 0, 0, 0, 0, 0} ++}; ++ ++static pv_export_t mod_pvs[] = { ++ {{"rtpstat", (sizeof("rtpstat")-1)}, /* RTP-Statistics */ ++ 1000, pv_get_rtpstat_f, 0, 0, 0, 0, 0}, ++ {{0, 0}, 0, 0, 0, 0, 0, 0, 0} ++}; ++ ++static param_export_t params[] = { ++ {"rtpengine_sock", STR_PARAM|USE_FUNC_PARAM, ++ (void*)rtpengine_set_store }, ++ {"rtpengine_disable_tout", INT_PARAM, &rtpengine_disable_tout }, ++ {"rtpengine_retr", INT_PARAM, &rtpengine_retr }, ++ {"rtpengine_tout", INT_PARAM, &rtpengine_tout }, ++ {"extra_id_pv", STR_PARAM, &extra_id_pv_param.s }, ++ {"setid_avp", STR_PARAM, &setid_avp_param }, ++ {0, 0, 0} ++}; ++ ++static mi_export_t mi_cmds[] = { ++ {MI_ENABLE_RTP_ENGINE, 0, mi_enable_rtp_proxy, 0, 0, 0}, ++ {MI_SHOW_RTP_ENGINES, 0, mi_show_rtpengines, MI_NO_INPUT_FLAG, 0, 0}, ++ { 0, 0, 0, 0, 0, 0} ++}; ++ ++struct module_exports exports = { ++ "rtpengine", ++ MODULE_VERSION, ++ DEFAULT_DLFLAGS, /* dlopen flags */ ++ cmds, ++ params, ++ 0, /* exported statistics */ ++ mi_cmds, /* exported MI functions */ ++ mod_pvs, /* exported pseudo-variables */ ++ 0, /* extra processes */ ++ mod_init, ++ 0, /* reply processing */ ++ mod_destroy, /* destroy function */ ++ child_init ++}; ++ ++int msg_has_sdp(struct sip_msg *msg) ++{ ++ str body; ++ struct part *p; ++ struct multi_body *m; ++ ++ if(parse_headers(msg, HDR_CONTENTLENGTH_F,0) < 0) { ++ LM_ERR("cannot parse cseq header"); ++ return 0; ++ } ++ ++ body.len = get_content_length(msg); ++ if (!body.len) ++ return 0; ++ ++ m = get_all_bodies(msg); ++ if (!m) { ++ LM_DBG("cannot parse body\n"); ++ return 0; ++ } ++ ++ for (p = m->first; p; p = p->next) { ++ if (p->content_type == ((TYPE_APPLICATION << 16) + SUBTYPE_SDP)) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++ ++ ++static inline int str_eq(const str *p, const char *q) { ++ int l = strlen(q); ++ if (p->len != l) ++ return 0; ++ if (memcmp(p->s, q, l)) ++ return 0; ++ return 1; ++} ++ ++ ++static int rtpengine_set_store(modparam_t type, void * val){ ++ ++ char * p; ++ int len; ++ ++ p = (char* )val; ++ ++ if(p==0 || *p=='\0'){ ++ return 0; ++ } ++ ++ if(rtpe_sets==0){ ++ rtpe_strings = (char**)pkg_malloc(sizeof(char*)); ++ if(!rtpe_strings){ ++ LM_ERR("no pkg memory left\n"); ++ return -1; ++ } ++ } else {/*realloc to make room for the current set*/ ++ rtpe_strings = (char**)pkg_realloc(rtpe_strings, ++ (rtpe_sets+1)* sizeof(char*)); ++ if(!rtpe_strings){ ++ LM_ERR("no pkg memory left\n"); ++ return -1; ++ } ++ } ++ ++ /*allocate for the current set of urls*/ ++ len = strlen(p); ++ rtpe_strings[rtpe_sets] = (char*)pkg_malloc((len+1)*sizeof(char)); ++ ++ if(!rtpe_strings[rtpe_sets]){ ++ LM_ERR("no pkg memory left\n"); ++ return -1; ++ } ++ ++ memcpy(rtpe_strings[rtpe_sets], p, len); ++ rtpe_strings[rtpe_sets][len] = '\0'; ++ rtpe_sets++; ++ ++ return 0; ++} ++ ++ ++static int add_rtpengine_socks(struct rtpe_set * rtpe_list, ++ char * rtpengine){ ++ /* Make rtp proxies list. */ ++ char *p, *p1, *p2, *plim; ++ struct rtpe_node *pnode; ++ int weight; ++ ++ p = rtpengine; ++ plim = p + strlen(p); ++ ++ for(;;) { ++ weight = 1; ++ while (*p && isspace((int)*p)) ++ ++p; ++ if (p >= plim) ++ break; ++ p1 = p; ++ while (*p && !isspace((int)*p)) ++ ++p; ++ if (p <= p1) ++ break; /* may happen??? */ ++ /* Have weight specified? If yes, scan it */ ++ p2 = memchr(p1, '=', p - p1); ++ if (p2 != NULL) { ++ weight = strtoul(p2 + 1, NULL, 10); ++ } else { ++ p2 = p; ++ } ++ pnode = shm_malloc(sizeof(struct rtpe_node)); ++ if (pnode == NULL) { ++ LM_ERR("no shm memory left\n"); ++ return -1; ++ } ++ memset(pnode, 0, sizeof(*pnode)); ++ pnode->idx = rtpe_no++; ++ pnode->rn_recheck_ticks = 0; ++ pnode->rn_weight = weight; ++ pnode->rn_umode = 0; ++ pnode->rn_disabled = 0; ++ pnode->rn_url.s = shm_malloc(p2 - p1 + 1); ++ if (pnode->rn_url.s == NULL) { ++ shm_free(pnode); ++ LM_ERR("no shm memory left\n"); ++ return -1; ++ } ++ memmove(pnode->rn_url.s, p1, p2 - p1); ++ pnode->rn_url.s[p2 - p1] = 0; ++ pnode->rn_url.len = p2-p1; ++ ++ LM_DBG("url is %s, len is %i\n", pnode->rn_url.s, pnode->rn_url.len); ++ /* Leave only address in rn_address */ ++ pnode->rn_address = pnode->rn_url.s; ++ if (strncasecmp(pnode->rn_address, "udp:", 4) == 0) { ++ pnode->rn_umode = 1; ++ pnode->rn_address += 4; ++ } else if (strncasecmp(pnode->rn_address, "udp6:", 5) == 0) { ++ pnode->rn_umode = 6; ++ pnode->rn_address += 5; ++ } else if (strncasecmp(pnode->rn_address, "unix:", 5) == 0) { ++ pnode->rn_umode = 0; ++ pnode->rn_address += 5; ++ } ++ ++ if (rtpe_list->rn_first == NULL) { ++ rtpe_list->rn_first = pnode; ++ } else { ++ rtpe_list->rn_last->rn_next = pnode; ++ } ++ ++ rtpe_list->rn_last = pnode; ++ rtpe_list->rtpe_node_count++; ++ } ++ return 0; ++} ++ ++ ++/* 0-succes ++ * -1 - erorr ++ * */ ++static int rtpengine_add_rtpengine_set( char * rtp_proxies) ++{ ++ char *p,*p2; ++ struct rtpe_set * rtpe_list; ++ unsigned int my_current_id; ++ str id_set; ++ int new_list; ++ ++ /* empty definition? */ ++ p= rtp_proxies; ++ if(!p || *p=='\0'){ ++ return 0; ++ } ++ ++ for(;*p && isspace(*p);p++); ++ if(*p=='\0'){ ++ return 0; ++ } ++ ++ rtp_proxies = strstr(p, "=="); ++ if(rtp_proxies){ ++ if(*(rtp_proxies +2)=='\0'){ ++ LM_ERR("script error -invalid rtp proxy list!\n"); ++ return -1; ++ } ++ ++ *rtp_proxies = '\0'; ++ p2 = rtp_proxies-1; ++ for(;isspace(*p2); *p2 = '\0',p2--); ++ id_set.s = p; id_set.len = p2 - p+1; ++ ++ if(id_set.len <= 0 ||str2int(&id_set, &my_current_id)<0 ){ ++ LM_ERR("script error -invalid set_id value!\n"); ++ return -1; ++ } ++ ++ rtp_proxies+=2; ++ }else{ ++ rtp_proxies = p; ++ my_current_id = DEFAULT_RTPE_SET_ID; ++ } ++ ++ for(;*rtp_proxies && isspace(*rtp_proxies);rtp_proxies++); ++ ++ if(!(*rtp_proxies)){ ++ LM_ERR("script error -empty rtp_proxy list\n"); ++ return -1;; ++ } ++ ++ /*search for the current_id*/ ++ rtpe_list = rtpe_set_list ? rtpe_set_list->rset_first : 0; ++ while( rtpe_list != 0 && rtpe_list->id_set!=my_current_id) ++ rtpe_list = rtpe_list->rset_next; ++ ++ if(rtpe_list==NULL){ /*if a new id_set : add a new set of rtpe*/ ++ rtpe_list = shm_malloc(sizeof(struct rtpe_set)); ++ if(!rtpe_list){ ++ LM_ERR("no shm memory left\n"); ++ return -1; ++ } ++ memset(rtpe_list, 0, sizeof(struct rtpe_set)); ++ rtpe_list->id_set = my_current_id; ++ new_list = 1; ++ } else { ++ new_list = 0; ++ } ++ ++ if(add_rtpengine_socks(rtpe_list, rtp_proxies)!= 0){ ++ /*if this list will not be inserted, clean it up*/ ++ goto error; ++ } ++ ++ if (new_list) { ++ if(!rtpe_set_list){/*initialize the list of set*/ ++ rtpe_set_list = shm_malloc(sizeof(struct rtpe_set_head)); ++ if(!rtpe_set_list){ ++ LM_ERR("no shm memory left\n"); ++ return -1; ++ } ++ memset(rtpe_set_list, 0, sizeof(struct rtpe_set_head)); ++ } ++ ++ /*update the list of set info*/ ++ if(!rtpe_set_list->rset_first){ ++ rtpe_set_list->rset_first = rtpe_list; ++ }else{ ++ rtpe_set_list->rset_last->rset_next = rtpe_list; ++ } ++ ++ rtpe_set_list->rset_last = rtpe_list; ++ rtpe_set_count++; ++ ++ if(my_current_id == DEFAULT_RTPE_SET_ID){ ++ default_rtpe_set = rtpe_list; ++ } ++ } ++ ++ return 0; ++error: ++ return -1; ++} ++ ++ ++static int fixup_set_id(void ** param, int param_no) ++{ ++ int int_val, err; ++ struct rtpe_set* rtpe_list; ++ rtpe_set_link_t *rtpl = NULL; ++ str s; ++ ++ rtpl = (rtpe_set_link_t*)pkg_malloc(sizeof(rtpe_set_link_t)); ++ if(rtpl==NULL) { ++ LM_ERR("no more pkg memory\n"); ++ return -1; ++ } ++ memset(rtpl, 0, sizeof(rtpe_set_link_t)); ++ s.s = (char*)*param; ++ s.len = strlen(s.s); ++ ++ if(s.s[0] == PV_MARKER) { ++ if ( pv_parse_spec(&s, &rtpl->rpv) == NULL ) { ++ LM_ERR("invalid parameter %s\n", s.s); ++ return -1; ++ } ++ } else { ++ int_val = str2s(*param, strlen(*param), &err); ++ if (err == 0) { ++ pkg_free(*param); ++ if((rtpe_list = select_rtpe_set(int_val)) ==0){ ++ LM_ERR("rtpe_proxy set %i not configured\n", int_val); ++ return E_CFG; ++ } ++ rtpl->rset = rtpe_list; ++ } else { ++ LM_ERR("bad number <%s>\n", (char *)(*param)); ++ return E_CFG; ++ } ++ } ++ *param = (void*)rtpl; ++ return 0; ++} ++ ++static struct mi_root* mi_enable_rtp_proxy(struct mi_root* cmd_tree, ++ void* param ) ++{ struct mi_node* node; ++ str rtpe_url; ++ unsigned int enable; ++ struct rtpe_set * rtpe_list; ++ struct rtpe_node * crt_rtpe; ++ int found; ++ ++ found = 0; ++ ++ if(rtpe_set_list ==NULL) ++ goto end; ++ ++ node = cmd_tree->node.kids; ++ if(node == NULL) ++ return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); ++ ++ if(node->value.s == NULL || node->value.len ==0) ++ return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); ++ ++ rtpe_url = node->value; ++ ++ node = node->next; ++ if(node == NULL) ++ return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN); ++ ++ enable = 0; ++ if( strno2int( &node->value, &enable) <0) ++ goto error; ++ ++ for(rtpe_list = rtpe_set_list->rset_first; rtpe_list != NULL; ++ rtpe_list = rtpe_list->rset_next){ ++ ++ for(crt_rtpe = rtpe_list->rn_first; crt_rtpe != NULL; ++ crt_rtpe = crt_rtpe->rn_next){ ++ /*found a matching rtpe*/ ++ ++ if(crt_rtpe->rn_url.len == rtpe_url.len){ ++ ++ if(strncmp(crt_rtpe->rn_url.s, rtpe_url.s, rtpe_url.len) == 0){ ++ /*set the enabled/disabled status*/ ++ found = 1; ++ crt_rtpe->rn_recheck_ticks = ++ enable? MI_MIN_RECHECK_TICKS : MI_MAX_RECHECK_TICKS; ++ crt_rtpe->rn_disabled = enable?0:1; ++ } ++ } ++ } ++ } ++ ++end: ++ if(found) ++ return init_mi_tree( 200, MI_OK_S, MI_OK_LEN); ++ return init_mi_tree(404,MI_RTP_ENGINE_NOT_FOUND,MI_RTP_ENGINE_NOT_FOUND_LEN); ++error: ++ return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN); ++} ++ ++ ++ ++#define add_rtpe_node_int_info(_parent, _name, _name_len, _value, _attr,\ ++ _len, _string, _error)\ ++ do {\ ++ (_string) = int2str((_value), &(_len));\ ++ if((_string) == 0){\ ++ LM_ERR("cannot convert int value\n");\ ++ goto _error;\ ++ }\ ++ if(((_attr) = add_mi_attr((_parent), MI_DUP_VALUE, (_name), \ ++ (_name_len), (_string), (_len)) ) == 0)\ ++ goto _error;\ ++ }while(0); ++ ++static struct mi_root* mi_show_rtpengines(struct mi_root* cmd_tree, ++ void* param) ++{ ++ struct mi_node* node, *crt_node, *set_node; ++ struct mi_root* root; ++ struct mi_attr * attr; ++ struct rtpe_set * rtpe_list; ++ struct rtpe_node * crt_rtpe; ++ char * string, *id; ++ int id_len, len; ++ ++ string = id = 0; ++ ++ root = init_mi_tree(200, MI_OK_S, MI_OK_LEN); ++ if (!root) { ++ LM_ERR("the MI tree cannot be initialized!\n"); ++ return 0; ++ } ++ ++ if(rtpe_set_list ==NULL) ++ return root; ++ ++ node = &root->node; ++ node->flags |= MI_IS_ARRAY; ++ ++ for(rtpe_list = rtpe_set_list->rset_first; rtpe_list != NULL; ++ rtpe_list = rtpe_list->rset_next){ ++ ++ id = int2str(rtpe_list->id_set, &id_len); ++ if(!id){ ++ LM_ERR("cannot convert set id\n"); ++ goto error; ++ } ++ ++ if(!(set_node = add_mi_node_child(node, MI_IS_ARRAY|MI_DUP_VALUE, MI_SET, MI_SET_LEN, ++ id, id_len))) { ++ LM_ERR("cannot add the set node to the tree\n"); ++ goto error; ++ } ++ ++ for(crt_rtpe = rtpe_list->rn_first; crt_rtpe != NULL; ++ crt_rtpe = crt_rtpe->rn_next){ ++ ++ if(!(crt_node = add_mi_node_child(node, MI_DUP_VALUE, ++ MI_NODE, MI_NODE_LEN, ++ crt_rtpe->rn_url.s, crt_rtpe->rn_url.len)) ) { ++ LM_ERR("cannot add the child node to the tree\n"); ++ goto error; ++ } ++ ++ LM_DBG("adding node name %s \n",crt_rtpe->rn_url.s ); ++ ++ add_rtpe_node_int_info(crt_node, MI_INDEX, MI_INDEX_LEN, ++ crt_rtpe->idx, attr, len,string,error); ++ add_rtpe_node_int_info(crt_node, MI_DISABLED, MI_DISABLED_LEN, ++ crt_rtpe->rn_disabled, attr, len,string,error); ++ add_rtpe_node_int_info(crt_node, MI_WEIGHT, MI_WEIGHT_LEN, ++ crt_rtpe->rn_weight, attr, len, string,error); ++ add_rtpe_node_int_info(crt_node, MI_RECHECK_TICKS,MI_RECHECK_T_LEN, ++ crt_rtpe->rn_recheck_ticks, attr, len, string, error); ++ } ++ } ++ ++ return root; ++error: ++ if (root) ++ free_mi_tree(root); ++ return 0; ++} ++ ++ ++static int ++mod_init(void) ++{ ++ int i; ++ pv_spec_t avp_spec; ++ unsigned short avp_flags; ++ str s; ++ ++ if(register_mi_mod(exports.name, mi_cmds)!=0) ++ { ++ LM_ERR("failed to register MI commands\n"); ++ return -1; ++ } ++ ++ /* any rtpengine configured? */ ++ if(rtpe_set_list) ++ default_rtpe_set = select_rtpe_set(DEFAULT_RTPE_SET_ID); ++ ++ /* storing the list of rtp proxy sets in shared memory*/ ++ for(i=0;i\n", ++ setid_avp_param); ++ return -1; ++ } ++ if (pv_get_avp_name(0, &(avp_spec.pvp), &(setid_avp.n), ++ &avp_flags) != 0) { ++ LM_ERR("invalid AVP definition <%s>\n", setid_avp_param); ++ return -1; ++ } ++ setid_avp_type = avp_flags; ++ } ++ ++ if (rtpe_strings) ++ pkg_free(rtpe_strings); ++ ++ if (load_tm_api( &tmb ) < 0) ++ { ++ LM_DBG("could not load the TM-functions - answer-offer model" ++ " auto-detection is disabled\n"); ++ memset(&tmb, 0, sizeof(struct tm_binds)); ++ } ++ ++ return 0; ++} ++ ++ ++static int ++child_init(int rank) ++{ ++ int n; ++ char *cp; ++ struct addrinfo hints, *res; ++ struct rtpe_set *rtpe_list; ++ struct rtpe_node *pnode; ++ ++ if(rtpe_set_list==NULL ) ++ return 0; ++ ++ /* Iterate known RTP proxies - create sockets */ ++ mypid = getpid(); ++ ++ rtpe_socks = (int*)pkg_malloc( sizeof(int)*rtpe_no ); ++ if (rtpe_socks==NULL) { ++ LM_ERR("no more pkg memory\n"); ++ return -1; ++ } ++ ++ for(rtpe_list = rtpe_set_list->rset_first; rtpe_list != 0; ++ rtpe_list = rtpe_list->rset_next){ ++ ++ for (pnode=rtpe_list->rn_first; pnode!=0; pnode = pnode->rn_next){ ++ char *hostname; ++ ++ if (pnode->rn_umode == 0) { ++ rtpe_socks[pnode->idx] = -1; ++ goto rptest; ++ } ++ ++ /* ++ * This is UDP or UDP6. Detect host and port; lookup host; ++ * do connect() in order to specify peer address ++ */ ++ hostname = (char*)pkg_malloc(sizeof(char) * (strlen(pnode->rn_address) + 1)); ++ if (hostname==NULL) { ++ LM_ERR("no more pkg memory\n"); ++ return -1; ++ } ++ strcpy(hostname, pnode->rn_address); ++ ++ cp = strrchr(hostname, ':'); ++ if (cp != NULL) { ++ *cp = '\0'; ++ cp++; ++ } ++ if (cp == NULL || *cp == '\0') ++ cp = CPORT; ++ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_flags = 0; ++ hints.ai_family = (pnode->rn_umode == 6) ? AF_INET6 : AF_INET; ++ hints.ai_socktype = SOCK_DGRAM; ++ if ((n = getaddrinfo(hostname, cp, &hints, &res)) != 0) { ++ LM_ERR("%s\n", gai_strerror(n)); ++ pkg_free(hostname); ++ return -1; ++ } ++ pkg_free(hostname); ++ ++ rtpe_socks[pnode->idx] = socket((pnode->rn_umode == 6) ++ ? AF_INET6 : AF_INET, SOCK_DGRAM, 0); ++ if ( rtpe_socks[pnode->idx] == -1) { ++ LM_ERR("can't create socket\n"); ++ freeaddrinfo(res); ++ return -1; ++ } ++ ++ if (connect( rtpe_socks[pnode->idx], res->ai_addr, res->ai_addrlen) == -1) { ++ LM_ERR("can't connect to a RTP proxy\n"); ++ close( rtpe_socks[pnode->idx] ); ++ rtpe_socks[pnode->idx] = -1; ++ freeaddrinfo(res); ++ return -1; ++ } ++ freeaddrinfo(res); ++rptest: ++ pnode->rn_disabled = rtpe_test(pnode, 0, 1); ++ } ++ } ++ ++ return 0; ++} ++ ++ ++static void mod_destroy(void) ++{ ++ struct rtpe_set * crt_list, * last_list; ++ struct rtpe_node * crt_rtpe, *last_rtpe; ++ ++ /*free the shared memory*/ ++ if (natping_state) ++ shm_free(natping_state); ++ ++ if(rtpe_set_list == NULL) ++ return; ++ ++ for(crt_list = rtpe_set_list->rset_first; crt_list != NULL; ){ ++ ++ for(crt_rtpe = crt_list->rn_first; crt_rtpe != NULL; ){ ++ ++ if(crt_rtpe->rn_url.s) ++ shm_free(crt_rtpe->rn_url.s); ++ ++ last_rtpe = crt_rtpe; ++ crt_rtpe = last_rtpe->rn_next; ++ shm_free(last_rtpe); ++ } ++ ++ last_list = crt_list; ++ crt_list = last_list->rset_next; ++ shm_free(last_list); ++ } ++ ++ shm_free(rtpe_set_list); ++} ++ ++ ++ ++static char * gencookie(void) ++{ ++ static char cook[34]; ++ ++ sprintf(cook, "%d_%u ", (int)mypid, myseqn); ++ myseqn++; ++ return cook; ++} ++ ++ ++ ++static const char *transports[] = { ++ [0x00] = "RTP/AVP", ++ [0x01] = "RTP/SAVP", ++ [0x02] = "RTP/AVPF", ++ [0x03] = "RTP/SAVPF", ++}; ++ ++static int parse_flags(struct ng_flags_parse *ng_flags, struct sip_msg *msg, enum rtpe_operation *op, ++ const char *flags_str) ++{ ++ char *e; ++ const char *err; ++ str key, val; ++ ++ if (!flags_str) ++ return 0; ++ ++ while (1) { ++ while (*flags_str == ' ') ++ flags_str++; ++ ++ key.s = (void *) flags_str; ++ val.len = key.len = -1; ++ val.s = NULL; ++ ++ e = strpbrk(key.s, " ="); ++ if (!e) ++ e = key.s + strlen(key.s); ++ else if (*e == '=') { ++ key.len = e - key.s; ++ val.s = e + 1; ++ e = strchr(val.s, ' '); ++ if (!e) ++ e = val.s + strlen(val.s); ++ val.len = e - val.s; ++ } ++ ++ if (key.len == -1) ++ key.len = e - key.s; ++ if (!key.len) ++ break; ++ ++ /* XXX make this prettier */ ++ err = "unknown flag"; ++ switch (key.len) { ++ case 3: ++ if (str_eq(&key, "ICE")) { ++ err = "missing value"; ++ if (!val.s) ++ goto error; ++ err = "invalid value"; ++ if (str_eq(&val, "force") || str_eq(&val, "remove")) ++ bencode_dictionary_add_str(ng_flags->dict, "ICE", &val); ++ else ++ goto error; ++ } ++ else if (str_eq(&key, "RTP")) { ++ ng_flags->transport |= 0x100; ++ ng_flags->transport &= ~0x001; ++ } ++ else if (str_eq(&key, "AVP")) { ++ ng_flags->transport |= 0x100; ++ ng_flags->transport &= ~0x002; ++ } ++ else ++ goto error; ++ break; ++ ++ case 4: ++ if (str_eq(&key, "SRTP")) ++ ng_flags->transport |= 0x101; ++ else if (str_eq(&key, "AVPF")) ++ ng_flags->transport |= 0x102; ++ else ++ goto error; ++ break; ++ ++ case 5: ++ if (str_eq(&key, "force")) ++ bencode_list_add_string(ng_flags->flags, "force"); ++ else ++ goto error; ++ break; ++ ++ case 6: ++ if (str_eq(&key, "to-tag")) ++ ng_flags->to = 1; ++ else ++ goto error; ++ break; ++ ++ case 7: ++ if (str_eq(&key, "RTP/AVP")) ++ ng_flags->transport = 0x100; ++ else ++ goto error; ++ break; ++ ++ case 8: ++ if (str_eq(&key, "internal")) ++ bencode_list_add_string(ng_flags->direction, "internal"); ++ else if (str_eq(&key, "external")) ++ bencode_list_add_string(ng_flags->direction, "external"); ++ else if (str_eq(&key, "RTP/AVPF")) ++ ng_flags->transport = 0x102; ++ else if (str_eq(&key, "RTP/SAVP")) ++ ng_flags->transport = 0x101; ++ else ++ goto error; ++ break; ++ ++ case 9: ++ if (str_eq(&key, "symmetric")) ++ bencode_list_add_string(ng_flags->flags, "symmetric"); ++ else if (str_eq(&key, "RTP/SAVPF")) ++ ng_flags->transport = 0x103; ++ else ++ goto error; ++ break; ++ ++ case 10: ++ if (str_eq(&key, "via-branch")) { ++ err = "missing value"; ++ if (!val.s) ++ goto error; ++ err = "invalid value"; ++ if (*val.s == '1' || *val.s == '2') ++ ng_flags->via = *val.s - '0'; ++ else if (str_eq(&val, "auto")) ++ ng_flags->via = 3; ++ else if (str_eq(&val, "extra")) ++ ng_flags->via = -1; ++ else ++ goto error; ++ } ++ else if (str_eq(&key, "asymmetric")) ++ bencode_list_add_string(ng_flags->flags, "asymmetric"); ++ else ++ goto error; ++ break; ++ ++ case 11: ++ if (str_eq(&key, "auto-bridge")) ++ bencode_list_add_string(ng_flags->flags, "auto-bridge"); ++ else if (str_eq(&key, "repacketize")) { ++ err = "missing value"; ++ if (!val.s) ++ goto error; ++ ng_flags->packetize = 0; ++ while (isdigit(*val.s)) { ++ ng_flags->packetize *= 10; ++ ng_flags->packetize += *val.s - '0'; ++ val.s++; ++ } ++ err = "invalid value"; ++ if (!ng_flags->packetize) ++ goto error; ++ bencode_dictionary_add_integer(ng_flags->dict, "repacketize", ng_flags->packetize); ++ } ++ else ++ goto error; ++ break; ++ ++ case 12: ++ if (str_eq(&key, "force-answer")) { ++ err = "cannot force answer in non-offer command"; ++ if (*op != OP_OFFER) ++ goto error; ++ *op = OP_ANSWER; ++ } ++ else ++ goto error; ++ break; ++ case 13: ++ if (str_eq(&key, "trust-address")) ++ bencode_list_add_string(ng_flags->flags, "trust-address"); ++ else if (str_eq(&key, "media-address")) { ++ err = "missing value"; ++ if (!val.s) ++ goto error; ++ } ++ else ++ goto error; ++ break; ++ ++ case 14: ++ if (str_eq(&key, "replace-origin")) ++ bencode_list_add_string(ng_flags->replace, "origin"); ++ else if (str_eq(&key, "address-family")) { ++ err = "missing value"; ++ if (!val.s) ++ goto error; ++ err = "invalid value"; ++ if (str_eq(&val, "IP4") || str_eq(&val, "IP6")) ++ bencode_dictionary_add_str(ng_flags->dict, "address family", &val); ++ else ++ goto error; ++ } ++ else if (str_eq(&key, "rtcp-mux-demux")) ++ bencode_list_add_string(ng_flags->rtcp_mux, "demux"); ++ else if (str_eq(&key, "rtcp-mux-offer")) ++ bencode_list_add_string(ng_flags->rtcp_mux, "offer"); ++ else ++ goto error; ++ break; ++ ++ case 15: ++ if (str_eq(&key, "rtcp-mux-reject")) ++ bencode_list_add_string(ng_flags->rtcp_mux, "reject"); ++ else if (str_eq(&key, "rtcp-mux-accept")) ++ bencode_list_add_string(ng_flags->rtcp_mux, "accept"); ++ else ++ goto error; ++ break; ++ ++ case 26: ++ if (str_eq(&key, "replace-session-connection")) ++ bencode_list_add_string(ng_flags->replace, "session-connection"); ++ else ++ goto error; ++ break; ++ ++ default: ++ goto error; ++ } ++ ++ flags_str = e; ++ } ++ ++ return 0; ++ ++error: ++ if (val.s) ++ LM_ERR("error processing flag `%.*s' (value '%.*s'): %s\n", key.len, key.s, ++ val.len, val.s, err); ++ else ++ LM_ERR("error processing flag `%.*s': %s\n", key.len, key.s, err); ++ return -1; ++} ++ ++static bencode_item_t *rtpe_function_call(bencode_buffer_t *bencbuf, struct sip_msg *msg, ++ enum rtpe_operation op, const char *flags_str, str *body_out) ++{ ++ struct ng_flags_parse ng_flags; ++ bencode_item_t *item, *resp; ++ str callid, from_tag, to_tag, body, viabranch, error; ++ int ret; ++ struct rtpe_node *node; ++ char *cp; ++ ++ /*** get & init basic stuff needed ***/ ++ ++ memset(&ng_flags, 0, sizeof(ng_flags)); ++ ++ if (get_callid(msg, &callid) == -1 || callid.len == 0) { ++ LM_ERR("can't get Call-Id field\n"); ++ return NULL; ++ } ++ if (get_to_tag(msg, &to_tag) == -1) { ++ LM_ERR("can't get To tag\n"); ++ return NULL; ++ } ++ if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) { ++ LM_ERR("can't get From tag\n"); ++ return NULL; ++ } ++ if (bencode_buffer_init(bencbuf)) { ++ LM_ERR("could not initialize bencode_buffer_t\n"); ++ return NULL; ++ } ++ ng_flags.dict = bencode_dictionary(bencbuf); ++ ++ if (op == OP_OFFER || op == OP_ANSWER) { ++ ng_flags.flags = bencode_list(bencbuf); ++ ng_flags.direction = bencode_list(bencbuf); ++ ng_flags.replace = bencode_list(bencbuf); ++ ng_flags.rtcp_mux = bencode_list(bencbuf); ++ ++ if (extract_body(msg, &body) == -1) { ++ LM_ERR("can't extract body from the message\n"); ++ goto error; ++ } ++ bencode_dictionary_add_str(ng_flags.dict, "sdp", &body); ++ } ++ ++ /*** parse flags & build dictionary ***/ ++ ++ ng_flags.to = (op == OP_DELETE) ? 0 : 1; ++ ++ if (parse_flags(&ng_flags, msg, &op, flags_str)) ++ goto error; ++ ++ /* only add those if any flags were given at all */ ++ if (ng_flags.direction && ng_flags.direction->child) ++ bencode_dictionary_add(ng_flags.dict, "direction", ng_flags.direction); ++ if (ng_flags.flags && ng_flags.flags->child) ++ bencode_dictionary_add(ng_flags.dict, "flags", ng_flags.flags); ++ if (ng_flags.replace && ng_flags.replace->child) ++ bencode_dictionary_add(ng_flags.dict, "replace", ng_flags.replace); ++ if ((ng_flags.transport & 0x100)) ++ bencode_dictionary_add_string(ng_flags.dict, "transport-protocol", ++ transports[ng_flags.transport & 0x003]); ++ if (ng_flags.rtcp_mux && ng_flags.rtcp_mux->child) ++ bencode_dictionary_add(ng_flags.dict, "rtcp-mux", ng_flags.rtcp_mux); ++ ++ bencode_dictionary_add_str(ng_flags.dict, "call-id", &callid); ++ ++ if (ng_flags.via) { ++ if (ng_flags.via == 1 || ng_flags.via == 2) ++ ret = get_via_branch(msg, ng_flags.via, &viabranch); ++ else if (ng_flags.via == -1 && extra_id_pv) ++ ret = get_extra_id(msg, &viabranch); ++ else ++ ret = -1; ++ if (ret == -1 || viabranch.len == 0) { ++ LM_ERR("can't get Via branch/extra ID\n"); ++ goto error; ++ } ++ bencode_dictionary_add_str(ng_flags.dict, "via-branch", &viabranch); ++ } ++ ++ item = bencode_list(bencbuf); ++ bencode_dictionary_add(ng_flags.dict, "received-from", item); ++ bencode_list_add_string(item, (msg->rcv.src_ip.af == AF_INET) ? "IP4" : ( ++ (msg->rcv.src_ip.af == AF_INET6) ? "IP6" : ++ "?" ++ ) ); ++ bencode_list_add_string(item, ip_addr2a(&msg->rcv.src_ip)); ++ ++ if ((msg->first_line.type == SIP_REQUEST && op != OP_ANSWER) ++ || (msg->first_line.type == SIP_REPLY && op == OP_ANSWER)) ++ { ++ bencode_dictionary_add_str(ng_flags.dict, "from-tag", &from_tag); ++ if (ng_flags.to && to_tag.s && to_tag.len) ++ bencode_dictionary_add_str(ng_flags.dict, "to-tag", &to_tag); ++ } ++ else { ++ if (!to_tag.s || !to_tag.len) { ++ LM_ERR("No to-tag present\n"); ++ goto error; ++ } ++ bencode_dictionary_add_str(ng_flags.dict, "from-tag", &to_tag); ++ bencode_dictionary_add_str(ng_flags.dict, "to-tag", &from_tag); ++ } ++ ++ bencode_dictionary_add_string(ng_flags.dict, "command", command_strings[op]); ++ ++ /*** send it out ***/ ++ ++ if (bencbuf->error) { ++ LM_ERR("out of memory - bencode failed\n"); ++ goto error; ++ } ++ ++ if(msg->id != current_msg_id) ++ selected_rtpe_set = default_rtpe_set; ++ ++ do { ++ node = select_rtpe_node(callid, 1); ++ if (!node) { ++ LM_ERR("no available proxies\n"); ++ goto error; ++ } ++ ++ cp = send_rtpe_command(node, ng_flags.dict, &ret); ++ } while (cp == NULL); ++ LM_DBG("proxy reply: %.*s\n", ret, cp); ++ ++ /*** process reply ***/ ++ ++ resp = bencode_decode_expect(bencbuf, cp, ret, BENCODE_DICTIONARY); ++ if (!resp) { ++ LM_ERR("failed to decode bencoded reply from proxy: %.*s\n", ret, cp); ++ goto error; ++ } ++ if (!bencode_dictionary_get_strcmp(resp, "result", "error")) { ++ if (!bencode_dictionary_get_str(resp, "error-reason", &error)) ++ LM_ERR("proxy return error but didn't give an error reason: %.*s\n", ret, cp); ++ else ++ LM_ERR("proxy replied with error: %.*s\n", error.len, error.s); ++ goto error; ++ } ++ ++ if (body_out) ++ *body_out = body; ++ ++ return resp; ++ ++error: ++ bencode_buffer_free(bencbuf); ++ return NULL; ++} ++ ++static int rtpe_function_call_simple(struct sip_msg *msg, enum rtpe_operation op, const char *flags_str) ++{ ++ bencode_buffer_t bencbuf; ++ ++ if (!rtpe_function_call(&bencbuf, msg, op, flags_str, NULL)) ++ return -1; ++ ++ bencode_buffer_free(&bencbuf); ++ return 1; ++} ++ ++static bencode_item_t *rtpe_function_call_ok(bencode_buffer_t *bencbuf, struct sip_msg *msg, ++ enum rtpe_operation op, const char *flags_str, str *body) ++{ ++ bencode_item_t *ret; ++ ++ ret = rtpe_function_call(bencbuf, msg, op, flags_str, body); ++ if (!ret) ++ return NULL; ++ ++ if (bencode_dictionary_get_strcmp(ret, "result", "ok")) { ++ LM_ERR("proxy didn't return \"ok\" result\n"); ++ bencode_buffer_free(bencbuf); ++ return NULL; ++ } ++ ++ return ret; ++} ++ ++ ++ ++static int ++rtpe_test(struct rtpe_node *node, int isdisabled, int force) ++{ ++ bencode_buffer_t bencbuf; ++ bencode_item_t *dict; ++ char *cp; ++ int ret; ++ ++ if(node->rn_recheck_ticks == MI_MAX_RECHECK_TICKS){ ++ LM_DBG("rtpe %s disabled for ever\n", node->rn_url.s); ++ return 1; ++ } ++ if (force == 0) { ++ if (isdisabled == 0) ++ return 0; ++ if (node->rn_recheck_ticks > get_ticks()) ++ return 1; ++ } ++ ++ if (bencode_buffer_init(&bencbuf)) { ++ LM_ERR("could not initialized bencode_buffer_t\n"); ++ return 1; ++ } ++ dict = bencode_dictionary(&bencbuf); ++ bencode_dictionary_add_string(dict, "command", "ping"); ++ if (bencbuf.error) ++ goto benc_error; ++ ++ cp = send_rtpe_command(node, dict, &ret); ++ if (!cp) { ++ LM_ERR("proxy did not respond to ping\n"); ++ goto error; ++ } ++ ++ dict = bencode_decode_expect(&bencbuf, cp, ret, BENCODE_DICTIONARY); ++ if (!dict || bencode_dictionary_get_strcmp(dict, "result", "pong")) { ++ LM_ERR("proxy responded with invalid response\n"); ++ goto error; ++ } ++ ++ LM_INFO("rtp proxy <%s> found, support for it %senabled\n", ++ node->rn_url.s, force == 0 ? "re-" : ""); ++ ++ bencode_buffer_free(&bencbuf); ++ return 0; ++ ++benc_error: ++ LM_ERR("out of memory - bencode failed\n"); ++error: ++ bencode_buffer_free(&bencbuf); ++ return 1; ++} ++ ++static char * ++send_rtpe_command(struct rtpe_node *node, bencode_item_t *dict, int *outlen) ++{ ++ struct sockaddr_un addr; ++ int fd, len, i, vcnt; ++ char *cp; ++ static char buf[0x10000]; ++ struct pollfd fds[1]; ++ struct iovec *v; ++ ++ v = bencode_iovec(dict, &vcnt, 1, 0); ++ if (!v) { ++ LM_ERR("error converting bencode to iovec\n"); ++ return NULL; ++ } ++ ++ len = 0; ++ cp = buf; ++ if (node->rn_umode == 0) { ++ memset(&addr, 0, sizeof(addr)); ++ addr.sun_family = AF_LOCAL; ++ strncpy(addr.sun_path, node->rn_address, ++ sizeof(addr.sun_path) - 1); ++#ifdef HAVE_SOCKADDR_SA_LEN ++ addr.sun_len = strlen(addr.sun_path); ++#endif ++ ++ fd = socket(AF_LOCAL, SOCK_STREAM, 0); ++ if (fd < 0) { ++ LM_ERR("can't create socket\n"); ++ goto badproxy; ++ } ++ if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { ++ close(fd); ++ LM_ERR("can't connect to RTP proxy\n"); ++ goto badproxy; ++ } ++ ++ do { ++ len = writev(fd, v + 1, vcnt); ++ } while (len == -1 && errno == EINTR); ++ if (len <= 0) { ++ close(fd); ++ LM_ERR("can't send command to a RTP proxy\n"); ++ goto badproxy; ++ } ++ do { ++ len = read(fd, buf, sizeof(buf) - 1); ++ } while (len == -1 && errno == EINTR); ++ close(fd); ++ if (len <= 0) { ++ LM_ERR("can't read reply from a RTP proxy\n"); ++ goto badproxy; ++ } ++ } else { ++ fds[0].fd = rtpe_socks[node->idx]; ++ fds[0].events = POLLIN; ++ fds[0].revents = 0; ++ /* Drain input buffer */ ++ while ((poll(fds, 1, 0) == 1) && ++ ((fds[0].revents & POLLIN) != 0)) { ++ recv(rtpe_socks[node->idx], buf, sizeof(buf) - 1, 0); ++ fds[0].revents = 0; ++ } ++ v[0].iov_base = gencookie(); ++ v[0].iov_len = strlen(v[0].iov_base); ++ for (i = 0; i < rtpengine_retr; i++) { ++ do { ++ len = writev(rtpe_socks[node->idx], v, vcnt + 1); ++ } while (len == -1 && (errno == EINTR || errno == ENOBUFS)); ++ if (len <= 0) { ++ LM_ERR("can't send command to a RTP proxy\n"); ++ goto badproxy; ++ } ++ while ((poll(fds, 1, rtpengine_tout * 1000) == 1) && ++ (fds[0].revents & POLLIN) != 0) { ++ do { ++ len = recv(rtpe_socks[node->idx], buf, sizeof(buf)-1, 0); ++ } while (len == -1 && errno == EINTR); ++ if (len <= 0) { ++ LM_ERR("can't read reply from a RTP proxy\n"); ++ goto badproxy; ++ } ++ if (len >= (v[0].iov_len - 1) && ++ memcmp(buf, v[0].iov_base, (v[0].iov_len - 1)) == 0) { ++ len -= (v[0].iov_len - 1); ++ cp += (v[0].iov_len - 1); ++ if (len != 0) { ++ len--; ++ cp++; ++ } ++ goto out; ++ } ++ fds[0].revents = 0; ++ } ++ } ++ if (i == rtpengine_retr) { ++ LM_ERR("timeout waiting reply from a RTP proxy\n"); ++ goto badproxy; ++ } ++ } ++ ++out: ++ cp[len] = '\0'; ++ *outlen = len; ++ return cp; ++badproxy: ++ LM_ERR("proxy <%s> does not respond, disable it\n", node->rn_url.s); ++ node->rn_disabled = 1; ++ node->rn_recheck_ticks = get_ticks() + rtpengine_disable_tout; ++ ++ return NULL; ++} ++ ++/* ++ * select the set with the id_set id ++ */ ++ ++static struct rtpe_set * select_rtpe_set(int id_set ){ ++ ++ struct rtpe_set * rtpe_list; ++ /*is it a valid set_id?*/ ++ ++ if(!rtpe_set_list || !rtpe_set_list->rset_first){ ++ LM_ERR("no rtp_proxy configured\n"); ++ return 0; ++ } ++ ++ for(rtpe_list=rtpe_set_list->rset_first; rtpe_list!=0 && ++ rtpe_list->id_set!=id_set; rtpe_list=rtpe_list->rset_next); ++ if(!rtpe_list){ ++ LM_ERR(" script error-invalid id_set to be selected\n"); ++ } ++ ++ return rtpe_list; ++} ++/* ++ * Main balancing routine. This does not try to keep the same proxy for ++ * the call if some proxies were disabled or enabled; proxy death considered ++ * too rare. Otherwise we should implement "mature" HA clustering, which is ++ * too expensive here. ++ */ ++static struct rtpe_node * ++select_rtpe_node(str callid, int do_test) ++{ ++ unsigned sum, sumcut, weight_sum; ++ struct rtpe_node* node; ++ int was_forced; ++ ++ if(!selected_rtpe_set){ ++ LM_ERR("script error -no valid set selected\n"); ++ return NULL; ++ } ++ /* Most popular case: 1 proxy, nothing to calculate */ ++ if (selected_rtpe_set->rtpe_node_count == 1) { ++ node = selected_rtpe_set->rn_first; ++ if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks()) ++ node->rn_disabled = rtpe_test(node, 1, 0); ++ return node->rn_disabled ? NULL : node; ++ } ++ ++ /* XXX Use quick-and-dirty hashing algo */ ++ for(sum = 0; callid.len > 0; callid.len--) ++ sum += callid.s[callid.len - 1]; ++ sum &= 0xff; ++ ++ was_forced = 0; ++retry: ++ weight_sum = 0; ++ for (node=selected_rtpe_set->rn_first; node!=NULL; node=node->rn_next) { ++ ++ if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks()){ ++ /* Try to enable if it's time to try. */ ++ node->rn_disabled = rtpe_test(node, 1, 0); ++ } ++ if (!node->rn_disabled) ++ weight_sum += node->rn_weight; ++ } ++ if (weight_sum == 0) { ++ /* No proxies? Force all to be redetected, if not yet */ ++ if (was_forced) ++ return NULL; ++ was_forced = 1; ++ for(node=selected_rtpe_set->rn_first; node!=NULL; node=node->rn_next) { ++ node->rn_disabled = rtpe_test(node, 1, 1); ++ } ++ goto retry; ++ } ++ sumcut = sum % weight_sum; ++ /* ++ * sumcut here lays from 0 to weight_sum-1. ++ * Scan proxy list and decrease until appropriate proxy is found. ++ */ ++ for (node=selected_rtpe_set->rn_first; node!=NULL; node=node->rn_next) { ++ if (node->rn_disabled) ++ continue; ++ if (sumcut < node->rn_weight) ++ goto found; ++ sumcut -= node->rn_weight; ++ } ++ /* No node list */ ++ return NULL; ++found: ++ if (do_test) { ++ node->rn_disabled = rtpe_test(node, node->rn_disabled, 0); ++ if (node->rn_disabled) ++ goto retry; ++ } ++ return node; ++} ++ ++static int ++get_extra_id(struct sip_msg* msg, str *id_str) { ++ if(msg==NULL || extra_id_pv==NULL || id_str==NULL) { ++ LM_ERR("bad parameters\n"); ++ return -1; ++ } ++ if (pv_printf_s(msg, extra_id_pv, id_str)<0) { ++ LM_ERR("cannot print the additional id\n"); ++ return -1; ++ } ++ ++ return 1; ++ ++} ++ ++static int ++set_rtpengine_set_from_avp(struct sip_msg *msg) ++{ ++ struct usr_avp *avp; ++ int_str setid_val; ++ ++ if ((setid_avp_param == NULL) || ++ (avp = search_first_avp(setid_avp_type, setid_avp.n, &setid_val, 0)) ++ == NULL) ++ return 1; ++ ++ if (avp->flags&AVP_VAL_STR) { ++ LM_ERR("setid_avp must hold an integer value\n"); ++ return -1; ++ } ++ ++ selected_rtpe_set = select_rtpe_set(setid_val.n); ++ if(selected_rtpe_set == NULL) { ++ LM_ERR("could not locate rtpengine set %d\n", setid_val.n); ++ return -1; ++ } ++ ++ LM_DBG("using rtpengine set %d\n", setid_val.n); ++ ++ current_msg_id = msg->id; ++ ++ return 1; ++} ++ ++static int rtpengine_delete(struct sip_msg *msg, const char *flags) { ++ return rtpe_function_call_simple(msg, OP_DELETE, flags); ++} ++ ++static int ++rtpengine_delete1_f(struct sip_msg* msg, char* str1, char* str2) ++{ ++ str flags; ++ ++ if (set_rtpengine_set_from_avp(msg) == -1) ++ return -1; ++ ++ flags.s = NULL; ++ if (str1) ++ fixup_get_svalue(msg, (gparam_p)str1, &flags); ++ ++ return rtpengine_delete(msg, flags.s); ++} ++ ++/* This function assumes p points to a line of requested type. */ ++ ++static int ++set_rtpengine_set_f(struct sip_msg * msg, char * str1, char * str2) ++{ ++ rtpe_set_link_t *rtpl; ++ pv_value_t val; ++ ++ rtpl = (rtpe_set_link_t*)str1; ++ ++ current_msg_id = 0; ++ selected_rtpe_set = 0; ++ ++ if(rtpl->rset != NULL) { ++ current_msg_id = msg->id; ++ selected_rtpe_set = rtpl->rset; ++ } else { ++ if(pv_get_spec_value(msg, &rtpl->rpv, &val)<0) { ++ LM_ERR("cannot evaluate pv param\n"); ++ return -1; ++ } ++ if(!(val.flags & PV_VAL_INT)) { ++ LM_ERR("pv param must hold an integer value\n"); ++ return -1; ++ } ++ selected_rtpe_set = select_rtpe_set(val.ri); ++ if(selected_rtpe_set==NULL) { ++ LM_ERR("could not locate rtpengine set %d\n", val.ri); ++ return -1; ++ } ++ current_msg_id = msg->id; ++ } ++ return 1; ++} ++ ++static int ++rtpengine_manage(struct sip_msg *msg, const char *flags) ++{ ++ int method; ++ int nosdp; ++ ++ if(msg->cseq==NULL && ((parse_headers(msg, HDR_CSEQ_F, 0)==-1) ++ || (msg->cseq==NULL))) ++ { ++ LM_ERR("no CSEQ header\n"); ++ return -1; ++ } ++ ++ method = get_cseq(msg)->method_id; ++ ++ if(!(method==METHOD_INVITE || method==METHOD_ACK || method==METHOD_CANCEL ++ || method==METHOD_BYE || method==METHOD_UPDATE)) ++ return -1; ++ ++ if(method==METHOD_CANCEL || method==METHOD_BYE) ++ return rtpengine_delete(msg, flags); ++ ++ if(msg_has_sdp(msg)) ++ nosdp = 0; ++ else ++ nosdp = parse_sdp(msg); ++ ++ if(msg->first_line.type == SIP_REQUEST) { ++ if(method==METHOD_ACK && nosdp==0) ++ return rtpengine_offer_answer(msg, flags, OP_ANSWER); ++ if(method==METHOD_UPDATE && nosdp==0) ++ return rtpengine_offer_answer(msg, flags, OP_OFFER); ++ if(method==METHOD_INVITE && nosdp==0) { ++ if(route_type==FAILURE_ROUTE) ++ return rtpengine_delete(msg, flags); ++ return rtpengine_offer_answer(msg, flags, OP_OFFER); ++ } ++ } else if(msg->first_line.type == SIP_REPLY) { ++ if(msg->first_line.u.reply.statuscode>=300) ++ return rtpengine_delete(msg, flags); ++ if(nosdp==0) { ++ if(method==METHOD_UPDATE) ++ return rtpengine_offer_answer(msg, flags, OP_ANSWER); ++ if(tmb.t_gett==NULL || tmb.t_gett()==NULL ++ || tmb.t_gett()==T_UNDEFINED) ++ return rtpengine_offer_answer(msg, flags, OP_ANSWER); ++ return rtpengine_offer_answer(msg, flags, OP_OFFER); ++ } ++ } ++ return -1; ++} ++ ++static int ++rtpengine_manage1_f(struct sip_msg *msg, char *str1, char *str2) ++{ ++ str flags; ++ ++ if (set_rtpengine_set_from_avp(msg) == -1) ++ return -1; ++ ++ flags.s = NULL; ++ if (str1) ++ fixup_get_svalue(msg, (gparam_p)str1, &flags); ++ ++ return rtpengine_manage(msg, flags.s); ++} ++ ++static int ++rtpengine_offer1_f(struct sip_msg *msg, char *str1, char *str2) ++{ ++ str flags; ++ ++ if (set_rtpengine_set_from_avp(msg) == -1) ++ return -1; ++ ++ flags.s = NULL; ++ if (str1) ++ fixup_get_svalue(msg, (gparam_p)str1, &flags); ++ return rtpengine_offer_answer(msg, flags.s, OP_OFFER); ++} ++ ++static int ++rtpengine_answer1_f(struct sip_msg *msg, char *str1, char *str2) ++{ ++ str flags; ++ ++ if (set_rtpengine_set_from_avp(msg) == -1) ++ return -1; ++ ++ if (msg->first_line.type == SIP_REQUEST) ++ if (msg->first_line.u.request.method_value != METHOD_ACK) ++ return -1; ++ ++ flags.s = NULL; ++ if (str1) ++ fixup_get_svalue(msg, (gparam_p)str1, &flags); ++ return rtpengine_offer_answer(msg, flags.s, OP_ANSWER); ++} ++ ++static int ++rtpengine_offer_answer(struct sip_msg *msg, const char *flags, int op) ++{ ++ bencode_buffer_t bencbuf; ++ bencode_item_t *dict; ++ str body, newbody; ++ struct lump *anchor; ++ ++ dict = rtpe_function_call_ok(&bencbuf, msg, op, flags, &body); ++ if (!dict) ++ return -1; ++ ++ if (!bencode_dictionary_get_str_dup(dict, "sdp", &newbody)) { ++ LM_ERR("failed to extract sdp body from proxy reply\n"); ++ goto error; ++ } ++ ++ anchor = del_lump(msg, body.s - msg->buf, body.len, 0); ++ if (!anchor) { ++ LM_ERR("del_lump failed\n"); ++ goto error_free; ++ } ++ if (!insert_new_lump_after(anchor, newbody.s, newbody.len, 0)) { ++ LM_ERR("insert_new_lump_after failed\n"); ++ goto error_free; ++ } ++ ++ bencode_buffer_free(&bencbuf); ++ return 1; ++ ++error_free: ++ pkg_free(newbody.s); ++error: ++ bencode_buffer_free(&bencbuf); ++ return -1; ++} ++ ++ ++static int ++start_recording_f(struct sip_msg* msg, char *foo, char *bar) ++{ ++ return rtpe_function_call_simple(msg, OP_START_RECORDING, NULL); ++} ++ ++/* ++ * Returns the current RTP-Statistics from the RTP-Proxy ++ */ ++static int ++pv_get_rtpstat_f(struct sip_msg *msg, pv_param_t *param, ++ pv_value_t *res) ++{ ++ bencode_buffer_t bencbuf; ++ bencode_item_t *dict, *tot, *in, *out; ++ static char buf[256]; ++ str ret; ++ ++ dict = rtpe_function_call_ok(&bencbuf, msg, OP_QUERY, NULL, NULL); ++ if (!dict) ++ return -1; ++ ++ tot = bencode_dictionary_get_expect(dict, "totals", BENCODE_DICTIONARY); ++ in = bencode_dictionary_get_expect(tot, "input", BENCODE_DICTIONARY); ++ in = bencode_dictionary_get_expect(in, "rtp", BENCODE_DICTIONARY); ++ out = bencode_dictionary_get_expect(tot, "output", BENCODE_DICTIONARY); ++ out = bencode_dictionary_get_expect(out, "rtp", BENCODE_DICTIONARY); ++ ++ if (!in || !out) ++ goto error; ++ ++ ret.s = buf; ++ ret.len = snprintf(buf, sizeof(buf), ++ "Input: %lli bytes, %lli packets, %lli errors; " ++ "Output: %lli bytes, %lli packets, %lli errors", ++ bencode_dictionary_get_integer(in, "bytes", -1), ++ bencode_dictionary_get_integer(in, "packets", -1), ++ bencode_dictionary_get_integer(in, "errors", -1), ++ bencode_dictionary_get_integer(out, "bytes", -1), ++ bencode_dictionary_get_integer(out, "packets", -1), ++ bencode_dictionary_get_integer(out, "errors", -1)); ++ ++ bencode_buffer_free(&bencbuf); ++ return pv_get_strval(msg, param, res, &ret); ++ ++error: ++ bencode_buffer_free(&bencbuf); ++ return -1; ++} ++ +diff --git a/modules/rtpengine/rtpengine.h b/modules/rtpengine/rtpengine.h +new file mode 100644 +index 0000000..24d124b +--- /dev/null ++++ b/modules/rtpengine/rtpengine.h +@@ -0,0 +1,63 @@ ++/* ++ * Copyright (C) 2003 Porta Software Ltd ++ * ++ * This file is part of opensips, a free SIP server. ++ * ++ * opensips is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version ++ * ++ * opensips is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * History: ++ * --------- ++ * 2014-06-17 Imported from rtpproxy module ++*/ ++ ++ ++#ifndef _RTPENGINE_H ++#define _RTPENGINE_H ++ ++#include "bencode.h" ++#include "../../str.h" ++ ++struct rtpe_node { ++ unsigned int idx; /* overall index */ ++ str rn_url; /* unparsed, deletable */ ++ int rn_umode; ++ char *rn_address; /* substring of rn_url */ ++ int rn_disabled; /* found unaccessible? */ ++ unsigned rn_weight; /* for load balancing */ ++ unsigned int rn_recheck_ticks; ++ int rn_rep_supported; ++ int rn_ptl_supported; ++ struct rtpe_node *rn_next; ++}; ++ ++ ++struct rtpe_set{ ++ unsigned int id_set; ++ unsigned weight_sum; ++ unsigned int rtpe_node_count; ++ int set_disabled; ++ unsigned int set_recheck_ticks; ++ struct rtpe_node *rn_first; ++ struct rtpe_node *rn_last; ++ struct rtpe_set *rset_next; ++}; ++ ++ ++struct rtpe_set_head{ ++ struct rtpe_set *rset_first; ++ struct rtpe_set *rset_last; ++}; ++ ++#endif +diff --git a/modules/rtpengine/rtpengine_funcs.c b/modules/rtpengine/rtpengine_funcs.c +new file mode 100644 +index 0000000..8385552 +--- /dev/null ++++ b/modules/rtpengine/rtpengine_funcs.c +@@ -0,0 +1,429 @@ ++/* ++ * Copyright (C) 2001-2003 FhG Fokus ++ * ++ * This file is part of opensips, a free SIP server. ++ * ++ * opensips is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version ++ * ++ * opensips is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * History: ++ * -------- ++ * 2014-06-17 Imported from rtpproxy module ++ */ ++ ++#include ++#include ++#include ++#include ++#include "rtpengine_funcs.h" ++#include "../../dprint.h" ++#include "../../config.h" ++#include "../../ut.h" ++#include "../../forward.h" ++#include "../../resolve.h" ++#include "../../globals.h" ++#include "../../udp_server.h" ++#include "../../pt.h" ++#include "../../parser/msg_parser.h" ++#include "../../trim.h" ++#include "../../parser/parse_from.h" ++#include "../../parser/contact/parse_contact.h" ++#include "../../parser/parse_uri.h" ++#include "../../parser/parse_content.h" ++#include "../../parser/parser_f.h" ++#include "../../parser/sdp/sdp_helpr_funcs.h" ++ ++#define READ(val) \ ++ (*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24)) ++#define advance(_ptr,_n,_str,_error) \ ++ do{\ ++ if ((_ptr)+(_n)>(_str).s+(_str).len)\ ++ goto _error;\ ++ (_ptr) = (_ptr) + (_n);\ ++ }while(0); ++#define one_of_16( _x , _t ) \ ++ (_x==_t[0]||_x==_t[15]||_x==_t[8]||_x==_t[2]||_x==_t[3]||_x==_t[4]\ ++ ||_x==_t[5]||_x==_t[6]||_x==_t[7]||_x==_t[1]||_x==_t[9]||_x==_t[10]\ ++ ||_x==_t[11]||_x==_t[12]||_x==_t[13]||_x==_t[14]) ++#define one_of_8( _x , _t ) \ ++ (_x==_t[0]||_x==_t[7]||_x==_t[1]||_x==_t[2]||_x==_t[3]||_x==_t[4]\ ++ ||_x==_t[5]||_x==_t[6]) ++ ++ ++ ++/** ++ * return: ++ * -1: error ++ * 1: text or sdp ++ * 2: multipart ++ */ ++int check_content_type(struct sip_msg *msg) ++{ ++ static unsigned int appl[16] = { ++ 0x6c707061/*appl*/,0x6c707041/*Appl*/,0x6c705061/*aPpl*/, ++ 0x6c705041/*APpl*/,0x6c507061/*apPl*/,0x6c507041/*ApPl*/, ++ 0x6c505061/*aPPl*/,0x6c505041/*APPl*/,0x4c707061/*appL*/, ++ 0x4c707041/*AppL*/,0x4c705061/*aPpL*/,0x4c705041/*APpL*/, ++ 0x4c507061/*apPL*/,0x4c507041/*ApPL*/,0x4c505061/*aPPL*/, ++ 0x4c505041/*APPL*/}; ++ static unsigned int icat[16] = { ++ 0x74616369/*icat*/,0x74616349/*Icat*/,0x74614369/*iCat*/, ++ 0x74614349/*ICat*/,0x74416369/*icAt*/,0x74416349/*IcAt*/, ++ 0x74414369/*iCAt*/,0x74414349/*ICAt*/,0x54616369/*icaT*/, ++ 0x54616349/*IcaT*/,0x54614369/*iCaT*/,0x54614349/*ICaT*/, ++ 0x54416369/*icAT*/,0x54416349/*IcAT*/,0x54414369/*iCAT*/, ++ 0x54414349/*ICAT*/}; ++ static unsigned int ion_[8] = { ++ 0x006e6f69/*ion_*/,0x006e6f49/*Ion_*/,0x006e4f69/*iOn_*/, ++ 0x006e4f49/*IOn_*/,0x004e6f69/*ioN_*/,0x004e6f49/*IoN_*/, ++ 0x004e4f69/*iON_*/,0x004e4f49/*ION_*/}; ++ static unsigned int sdp_[8] = { ++ 0x00706473/*sdp_*/,0x00706453/*Sdp_*/,0x00704473/*sDp_*/, ++ 0x00704453/*SDp_*/,0x00506473/*sdP_*/,0x00506453/*SdP_*/, ++ 0x00504473/*sDP_*/,0x00504453/*SDP_*/}; ++ str str_type; ++ unsigned int x; ++ char *p; ++ ++ if (!msg->content_type) ++ { ++ LM_WARN("the header Content-TYPE is absent!" ++ "let's assume the content is text/plain ;-)\n"); ++ return 1; ++ } ++ ++ trim_len(str_type.len,str_type.s,msg->content_type->body); ++ if (str_type.len>=15 && (*str_type.s=='m' || *str_type.s=='M') ++ && strncasecmp(str_type.s, "multipart/mixed", 15) == 0) { ++ return 2; ++ } ++ p = str_type.s; ++ advance(p,4,str_type,error_1); ++ x = READ(p-4); ++ if (!one_of_16(x,appl)) ++ goto other; ++ advance(p,4,str_type,error_1); ++ x = READ(p-4); ++ if (!one_of_16(x,icat)) ++ goto other; ++ advance(p,3,str_type,error_1); ++ x = READ(p-3) & 0x00ffffff; ++ if (!one_of_8(x,ion_)) ++ goto other; ++ ++ /* skip spaces and tabs if any */ ++ while (*p==' ' || *p=='\t') ++ advance(p,1,str_type,error_1); ++ if (*p!='/') ++ { ++ LM_ERR("no / found after primary type\n"); ++ goto error; ++ } ++ advance(p,1,str_type,error_1); ++ while ((*p==' ' || *p=='\t') && p+1 found valid\n", (int)(p-str_type.s), str_type.s); ++ return 1; ++ } else { ++ LM_ERR("bad end for type!\n"); ++ return -1; ++ } ++ ++error_1: ++ LM_ERR("body ended :-(!\n"); ++error: ++ return -1; ++other: ++ LM_ERR("invalid type for a message\n"); ++ return -1; ++} ++ ++ ++/* ++ * Get message body and check Content-Type header field ++ */ ++int extract_body(struct sip_msg *msg, str *body ) ++{ ++ char c; ++ int ret; ++ str mpdel; ++ char *rest, *p1, *p2; ++ struct hdr_field hf; ++ unsigned int mime; ++ ++ if (get_body(msg,body)!=0 || body->len==0) { ++ LM_ERR("failed to get the message body\n"); ++ goto error; ++ } ++ ++ /* ++ * Better use the content-len value - no need of any explicit ++ * parcing as get_body() parsed all headers and Conten-Length ++ * body header is automaticaly parsed when found. ++ */ ++ if (msg->content_length==0) { ++ LM_ERR("failed to get the content length in message\n"); ++ goto error; ++ } ++ ++ body->len = get_content_length(msg); ++ if (body->len==0) { ++ LM_ERR("message body has length zero\n"); ++ goto error; ++ } ++ ++ if (body->len + body->s > msg->buf + msg->len) { ++ LM_ERR("content-length exceeds packet-length by %d\n", ++ (int)((body->len + body->s) - (msg->buf + msg->len))); ++ goto error; ++ } ++ ++ /* no need for parse_headers(msg, EOH), get_body will ++ * parse everything */ ++ /*is the content type correct?*/ ++ if((ret = check_content_type(msg))==-1) ++ { ++ LM_ERR("content type mismatching\n"); ++ goto error; ++ } ++ ++ if(ret!=2) ++ goto done; ++ ++ /* multipart body */ ++ if(get_mixed_part_delimiter(&msg->content_type->body,&mpdel) < 0) { ++ goto error; ++ } ++ p1 = find_sdp_line_delimiter(body->s, body->s+body->len, mpdel); ++ if (p1 == NULL) { ++ LM_ERR("empty multipart content\n"); ++ return -1; ++ } ++ p2=p1; ++ c = 0; ++ for(;;) ++ { ++ p1 = p2; ++ if (p1 == NULL || p1 >= body->s+body->len) ++ break; /* No parts left */ ++ p2 = find_next_sdp_line_delimiter(p1, body->s+body->len, ++ mpdel, body->s+body->len); ++ /* p2 is text limit for application parsing */ ++ rest = eat_line(p1 + mpdel.len + 2, p2 - p1 - mpdel.len - 2); ++ if ( rest > p2 ) { ++ LM_ERR("Unparsable <%.*s>\n", (int)(p1-p1), p1); ++ return -1; ++ } ++ while( rest>16) == TYPE_APPLICATION) ++ && ((mime&0x00ff) == SUBTYPE_SDP)) { ++ c = 1; ++ } ++ } ++ } /* end of while */ ++ if(c==1) ++ { ++ if (rest < p2 && *rest == '\r') rest++; ++ if (rest < p2 && *rest == '\n') rest++; ++ if (rest < p2 && p2[-1] == '\n') p2--; ++ if (rest < p2 && p2[-1] == '\r') p2--; ++ body->s = rest; ++ body->len = p2-rest; ++ goto done; ++ } ++ } ++ ++error: ++ return -1; ++ ++done: ++ /*LM_DBG("DEBUG:extract_body:=|%.*s|\n",body->len,body->s);*/ ++ return 1; ++} ++ ++/* ++ * ser_memmem() returns the location of the first occurrence of data ++ * pattern b2 of size len2 in memory block b1 of size len1 or ++ * NULL if none is found. Obtained from NetBSD. ++ */ ++void * ++ser_memmem(const void *b1, const void *b2, size_t len1, size_t len2) ++{ ++ /* Initialize search pointer */ ++ char *sp = (char *) b1; ++ ++ /* Initialize pattern pointer */ ++ char *pp = (char *) b2; ++ ++ /* Initialize end of search address space pointer */ ++ char *eos = sp + len1 - len2; ++ ++ /* Sanity check */ ++ if(!(b1 && b2 && len1 && len2)) ++ return NULL; ++ ++ while (sp <= eos) { ++ if (*sp == *pp) ++ if (memcmp(sp, pp, len2) == 0) ++ return sp; ++ ++ sp++; ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Some helper functions taken verbatim from tm module. ++ */ ++ ++/* ++ * Extract Call-ID value ++ * assumes the callid header is already parsed ++ * (so make sure it is, before calling this function or ++ * it might fail even if the message _has_ a callid) ++ */ ++int ++get_callid(struct sip_msg* _m, str* _cid) ++{ ++ ++ if ((parse_headers(_m, HDR_CALLID_F, 0) == -1)) { ++ LM_ERR("failed to parse call-id header\n"); ++ return -1; ++ } ++ ++ if (_m->callid == NULL) { ++ LM_ERR("call-id not found\n"); ++ return -1; ++ } ++ ++ _cid->s = _m->callid->body.s; ++ _cid->len = _m->callid->body.len; ++ trim(_cid); ++ return 0; ++} ++ ++/* ++ * Extract tag from To header field of a response ++ */ ++int ++get_to_tag(struct sip_msg* _m, str* _tag) ++{ ++ ++ if (!_m->to && ((parse_headers(_m, HDR_TO_F, 0) == -1) || (!_m->to))) { ++ LM_ERR("To header field missing\n"); ++ return -1; ++ } ++ ++ if (get_to(_m)->tag_value.len) { ++ _tag->s = get_to(_m)->tag_value.s; ++ _tag->len = get_to(_m)->tag_value.len; ++ } else { ++ _tag->s = NULL; /* fixes gcc 4.0 warnings */ ++ _tag->len = 0; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Extract tag from From header field of a request ++ */ ++int ++get_from_tag(struct sip_msg* _m, str* _tag) ++{ ++ ++ if (parse_from_header(_m)<0) { ++ LM_ERR("failed to parse From header\n"); ++ return -1; ++ } ++ ++ if (get_from(_m)->tag_value.len) { ++ _tag->s = get_from(_m)->tag_value.s; ++ _tag->len = get_from(_m)->tag_value.len; ++ } else { ++ _tag->s = NULL; /* fixes gcc 4.0 warnings */ ++ _tag->len = 0; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Extract URI from the Contact header field ++ */ ++int ++get_contact_uri(struct sip_msg* _m, struct sip_uri *uri, contact_t** _c) ++{ ++ ++ if ((parse_headers(_m, HDR_CONTACT_F, 0) == -1) || !_m->contact) ++ return -1; ++ if (!_m->contact->parsed && parse_contact(_m->contact) < 0) { ++ LM_ERR("failed to parse Contact body\n"); ++ return -1; ++ } ++ *_c = ((contact_body_t*)_m->contact->parsed)->contacts; ++ if (*_c == NULL) ++ /* no contacts found */ ++ return -1; ++ ++ if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri) < 0 || uri->host.len <= 0) { ++ LM_ERR("failed to parse Contact URI [%.*s]\n", ++ (*_c)->uri.len, ((*_c)->uri.s)?(*_c)->uri.s:""); ++ return -1; ++ } ++ return 0; ++} ++ ++/* ++ * Extract branch from Via header ++ */ ++int ++get_via_branch(struct sip_msg* msg, int vianum, str* _branch) ++{ ++ struct via_body *via; ++ struct via_param *p; ++ ++ if ((parse_headers(msg, (vianum == 1) ? HDR_VIA1_F : HDR_VIA2_F, 0) == -1) < 0) ++ return -1; ++ ++ via = (vianum == 1) ? msg->via1 : msg->via2; ++ for (p = via->param_lst; p; p = p->next) ++ { ++ if (p->name.len == strlen("branch") ++ && strncasecmp(p->name.s, "branch", strlen("branch")) == 0) { ++ _branch->s = p->value.s; ++ _branch->len = p->value.len; ++ return 0; ++ } ++ } ++ return -1; ++} +diff --git a/modules/rtpengine/rtpengine_funcs.h b/modules/rtpengine/rtpengine_funcs.h +new file mode 100644 +index 0000000..1f07f2d +--- /dev/null ++++ b/modules/rtpengine/rtpengine_funcs.h +@@ -0,0 +1,38 @@ ++/* ++ * Copyright (C) 2001-2003 FhG Fokus ++ * ++ * This file is part of opensips, a free SIP server. ++ * ++ * opensips is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version ++ * ++ * opensips is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++ ++#ifndef _RTPENGINE_FUNCS_H ++#define _RTPENGINE_FUNCS_H ++ ++#include "../../str.h" ++#include "../../parser/msg_parser.h" ++#include "../../parser/contact/contact.h" ++ ++int extract_body(struct sip_msg * , str *); ++int check_content_type(struct sip_msg * ); ++void *ser_memmem(const void *, const void *, size_t, size_t); ++int get_callid(struct sip_msg *, str *); ++int get_to_tag(struct sip_msg *, str *); ++int get_from_tag(struct sip_msg *, str *); ++int get_contact_uri(struct sip_msg *, struct sip_uri *, contact_t **); ++int get_via_branch(struct sip_msg *, int, str *); ++ ++#endif diff --git a/opensips-0010-Fix-fixing-hdr-names-shorter-than-3-chars.patch b/opensips-0010-Fix-fixing-hdr-names-shorter-than-3-chars.patch deleted file mode 100644 index e4d1a8b..0000000 --- a/opensips-0010-Fix-fixing-hdr-names-shorter-than-3-chars.patch +++ /dev/null @@ -1,53 +0,0 @@ -From: Bogdan-Andrei Iancu -Date: Thu, 11 Jun 2015 11:09:03 +0300 -Subject: [PATCH] Fix fixing hdr names shorter than 3 chars. - -The fixup function fails to identify header names shorter than 3 (like To or short formats). This affected script functions like is_present_hf() or remove_hf(). -Reported by Nick Altmann - -(cherry picked from commit 1f7bae0915ac93ca231ede55c5540560e5489a7b) - -Conflicts: - modules/sipmsgops/sipmsgops.c - -diff --git a/modules/sipmsgops/sipmsgops.c b/modules/sipmsgops/sipmsgops.c -index 6fc8f5d..4444f89 100644 ---- a/modules/sipmsgops/sipmsgops.c -+++ b/modules/sipmsgops/sipmsgops.c -@@ -780,6 +780,7 @@ static int is_method_f(struct sip_msg *msg, char *meth, char *str2 ) - static int hname_fixup(void** param, int param_no) - { - char *c; -+ int len; - struct hdr_field hdr; - gparam_p gp = NULL; - -@@ -793,21 +794,23 @@ static int hname_fixup(void** param, int param_no) - - if (gp->type == GPARAM_TYPE_STR) - { -- c = pkg_malloc(gp->v.sval.len + 1); -+ /* parse_hname2() accepts a minimum 4 bytes len buffer -+ * for parsing, so whatever is the len of the header name, -+ * fill it up to 4 */ -+ len = (gp->v.sval.len<3) ? (4) : (gp->v.sval.len+1) ; -+ c = pkg_malloc( len ); - if (!c) - return E_OUT_OF_MEM; - - memcpy(c, gp->v.sval.s, gp->v.sval.len); - c[gp->v.sval.len] = ':'; -- gp->v.sval.len++; - -- if (parse_hname2(c, c + gp->v.sval.len, &hdr) == 0) -+ if (parse_hname2(c, c + len, &hdr) == 0) - { - LM_ERR("error parsing header name\n"); - return E_UNSPEC; - } -- -- gp->v.sval.len--; -+ - pkg_free(c); - - if (hdr.type != HDR_OTHER_T && hdr.type != HDR_ERROR_T) diff --git a/opensips-0010-added-missing-force-relay-flag-option-for-ICE.patch b/opensips-0010-added-missing-force-relay-flag-option-for-ICE.patch new file mode 100644 index 0000000..599379c --- /dev/null +++ b/opensips-0010-added-missing-force-relay-flag-option-for-ICE.patch @@ -0,0 +1,18 @@ +From: Eric Tamme +Date: Thu, 18 Dec 2014 10:54:59 -0700 +Subject: [PATCH] added missing force-relay flag option for ICE + + +diff --git a/modules/rtpengine/rtpengine.c b/modules/rtpengine/rtpengine.c +index cc86867..3cb4a94 100644 +--- a/modules/rtpengine/rtpengine.c ++++ b/modules/rtpengine/rtpengine.c +@@ -984,7 +984,7 @@ static int parse_flags(struct ng_flags_parse *ng_flags, struct sip_msg *msg, enu + if (!val.s) + goto error; + err = "invalid value"; +- if (str_eq(&val, "force") || str_eq(&val, "remove")) ++ if (str_eq(&val, "force") || str_eq(&val, "force-relay") || str_eq(&val, "remove")) + bencode_dictionary_add_str(ng_flags->dict, "ICE", &val); + else + goto error; diff --git a/opensips-0011-added-more-rtp-transports-to-match-those-available-i.patch b/opensips-0011-added-more-rtp-transports-to-match-those-available-i.patch new file mode 100644 index 0000000..ea39815 --- /dev/null +++ b/opensips-0011-added-more-rtp-transports-to-match-those-available-i.patch @@ -0,0 +1,50 @@ +From: Eric Tamme +Date: Tue, 30 Dec 2014 11:48:51 -0700 +Subject: [PATCH] added more rtp transports to match those available in + rtpengine + + +diff --git a/modules/rtpengine/rtpengine.c b/modules/rtpengine/rtpengine.c +index 3cb4a94..ed4e2fe 100644 +--- a/modules/rtpengine/rtpengine.c ++++ b/modules/rtpengine/rtpengine.c +@@ -938,6 +938,9 @@ static const char *transports[] = { + [0x01] = "RTP/SAVP", + [0x02] = "RTP/AVPF", + [0x03] = "RTP/SAVPF", ++ [0x04] = "UDP/TLS/RTP/SAVP", ++ [0x05] = "UDP/TLS/RTP/SAVPF" ++ + }; + + static int parse_flags(struct ng_flags_parse *ng_flags, struct sip_msg *msg, enum rtpe_operation *op, +@@ -1148,6 +1151,20 @@ static int parse_flags(struct ng_flags_parse *ng_flags, struct sip_msg *msg, enu + goto error; + break; + ++ case 16: ++ if (str_eq(&key, "UDP/TLS/RTP/SAVP")) ++ ng_flags->transport = 0x104; ++ else ++ goto error; ++ break; ++ ++ case 17: ++ if (str_eq(&key, "UDP/TLS/RTP/SAVPF")) ++ ng_flags->transport = 0x105; ++ else ++ goto error; ++ break; ++ + case 26: + if (str_eq(&key, "replace-session-connection")) + bencode_list_add_string(ng_flags->replace, "session-connection"); +@@ -1234,7 +1251,7 @@ static bencode_item_t *rtpe_function_call(bencode_buffer_t *bencbuf, struct sip_ + bencode_dictionary_add(ng_flags.dict, "replace", ng_flags.replace); + if ((ng_flags.transport & 0x100)) + bencode_dictionary_add_string(ng_flags.dict, "transport-protocol", +- transports[ng_flags.transport & 0x003]); ++ transports[ng_flags.transport & 0x005]); + if (ng_flags.rtcp_mux && ng_flags.rtcp_mux->child) + bencode_dictionary_add(ng_flags.dict, "rtcp-mux", ng_flags.rtcp_mux); + diff --git a/opensips-0012-fixed-bitmask-to-cover-all-3-bits-vs.-1st-and-3rd.-T.patch b/opensips-0012-fixed-bitmask-to-cover-all-3-bits-vs.-1st-and-3rd.-T.patch new file mode 100644 index 0000000..babecff --- /dev/null +++ b/opensips-0012-fixed-bitmask-to-cover-all-3-bits-vs.-1st-and-3rd.-T.patch @@ -0,0 +1,19 @@ +From: Eric Tamme +Date: Wed, 18 Feb 2015 07:37:46 -0700 +Subject: [PATCH] fixed bitmask to cover all 3 bits vs. 1st and 3rd. Thanks to + eldonogithub. + + +diff --git a/modules/rtpengine/rtpengine.c b/modules/rtpengine/rtpengine.c +index ed4e2fe..3254850 100644 +--- a/modules/rtpengine/rtpengine.c ++++ b/modules/rtpengine/rtpengine.c +@@ -1251,7 +1251,7 @@ static bencode_item_t *rtpe_function_call(bencode_buffer_t *bencbuf, struct sip_ + bencode_dictionary_add(ng_flags.dict, "replace", ng_flags.replace); + if ((ng_flags.transport & 0x100)) + bencode_dictionary_add_string(ng_flags.dict, "transport-protocol", +- transports[ng_flags.transport & 0x005]); ++ transports[ng_flags.transport & 0x007]); + if (ng_flags.rtcp_mux && ng_flags.rtcp_mux->child) + bencode_dictionary_add(ng_flags.dict, "rtcp-mux", ng_flags.rtcp_mux); + diff --git a/opensips-0013-expose-the-DTLS-passive-flag-to-allow-for-passive-re.patch b/opensips-0013-expose-the-DTLS-passive-flag-to-allow-for-passive-re.patch new file mode 100644 index 0000000..9195a52 --- /dev/null +++ b/opensips-0013-expose-the-DTLS-passive-flag-to-allow-for-passive-re.patch @@ -0,0 +1,47 @@ +From: Eric Tamme +Date: Tue, 10 Mar 2015 13:07:00 -0600 +Subject: [PATCH] expose the DTLS passive flag to allow for passive relay + through rtpengine + + +diff --git a/modules/rtpengine/rtpengine.c b/modules/rtpengine/rtpengine.c +index 3254850..a1db547 100644 +--- a/modules/rtpengine/rtpengine.c ++++ b/modules/rtpengine/rtpengine.c +@@ -249,7 +249,7 @@ static pv_export_t mod_pvs[] = { + + static param_export_t params[] = { + {"rtpengine_sock", STR_PARAM|USE_FUNC_PARAM, +- (void*)rtpengine_set_store }, ++ (void*)rtpengine_set_store }, + {"rtpengine_disable_tout", INT_PARAM, &rtpengine_disable_tout }, + {"rtpengine_retr", INT_PARAM, &rtpengine_retr }, + {"rtpengine_tout", INT_PARAM, &rtpengine_tout }, +@@ -410,8 +410,8 @@ static int add_rtpengine_socks(struct rtpe_set * rtpe_list, + return -1; + } + memmove(pnode->rn_url.s, p1, p2 - p1); +- pnode->rn_url.s[p2 - p1] = 0; +- pnode->rn_url.len = p2-p1; ++ pnode->rn_url.s[p2 - p1] = 0; ++ pnode->rn_url.len = p2-p1; + + LM_DBG("url is %s, len is %i\n", pnode->rn_url.s, pnode->rn_url.len); + /* Leave only address in rn_address */ +@@ -1009,6 +1009,16 @@ static int parse_flags(struct ng_flags_parse *ng_flags, struct sip_msg *msg, enu + ng_flags->transport |= 0x101; + else if (str_eq(&key, "AVPF")) + ng_flags->transport |= 0x102; ++ else if (str_eq(&key, "DTLS")){ ++ err = "missing value"; ++ if (!val.s) ++ goto error; ++ err = "invalid value"; ++ if (str_eq(&val, "passive")) ++ bencode_dictionary_add_str(ng_flags->dict, "DTLS", &val); ++ else ++ goto error; ++ } + else + goto error; + break; diff --git a/opensips-0014-rtpengine-do-not-register-mi-functions-twice.patch b/opensips-0014-rtpengine-do-not-register-mi-functions-twice.patch new file mode 100644 index 0000000..279bbbf --- /dev/null +++ b/opensips-0014-rtpengine-do-not-register-mi-functions-twice.patch @@ -0,0 +1,24 @@ +From: Razvan Crainea +Date: Tue, 17 Mar 2015 14:33:53 +0200 +Subject: [PATCH] rtpengine: do not register mi functions twice + +Conflicts: + modules/rtpengine/rtpengine.c + +diff --git a/modules/rtpengine/rtpengine.c b/modules/rtpengine/rtpengine.c +index a1db547..794959b 100644 +--- a/modules/rtpengine/rtpengine.c ++++ b/modules/rtpengine/rtpengine.c +@@ -738,12 +738,6 @@ mod_init(void) + unsigned short avp_flags; + str s; + +- if(register_mi_mod(exports.name, mi_cmds)!=0) +- { +- LM_ERR("failed to register MI commands\n"); +- return -1; +- } +- + /* any rtpengine configured? */ + if(rtpe_set_list) + default_rtpe_set = select_rtpe_set(DEFAULT_RTPE_SET_ID); diff --git a/opensips-0015-fix-rtpstat-for-rtpengine-thanks-to-jarrod-for-track.patch b/opensips-0015-fix-rtpstat-for-rtpengine-thanks-to-jarrod-for-track.patch new file mode 100644 index 0000000..5773a63 --- /dev/null +++ b/opensips-0015-fix-rtpstat-for-rtpengine-thanks-to-jarrod-for-track.patch @@ -0,0 +1,55 @@ +From: Eric Tamme +Date: Wed, 18 Mar 2015 14:26:08 -0400 +Subject: [PATCH] fix rtpstat for rtpengine - thanks to jarrod for tracking + down the fix + + +diff --git a/modules/rtpengine/rtpengine.c b/modules/rtpengine/rtpengine.c +index 794959b..16e2fdc 100644 +--- a/modules/rtpengine/rtpengine.c ++++ b/modules/rtpengine/rtpengine.c +@@ -1891,7 +1891,7 @@ pv_get_rtpstat_f(struct sip_msg *msg, pv_param_t *param, + pv_value_t *res) + { + bencode_buffer_t bencbuf; +- bencode_item_t *dict, *tot, *in, *out; ++ bencode_item_t *dict, *tot, *rtp, *rtcp; + static char buf[256]; + str ret; + +@@ -1900,24 +1900,20 @@ pv_get_rtpstat_f(struct sip_msg *msg, pv_param_t *param, + return -1; + + tot = bencode_dictionary_get_expect(dict, "totals", BENCODE_DICTIONARY); +- in = bencode_dictionary_get_expect(tot, "input", BENCODE_DICTIONARY); +- in = bencode_dictionary_get_expect(in, "rtp", BENCODE_DICTIONARY); +- out = bencode_dictionary_get_expect(tot, "output", BENCODE_DICTIONARY); +- out = bencode_dictionary_get_expect(out, "rtp", BENCODE_DICTIONARY); +- +- if (!in || !out) ++ rtp = bencode_dictionary_get_expect(tot, "RTP", BENCODE_DICTIONARY); ++ rtcp = bencode_dictionary_get_expect(tot, "RTCP", BENCODE_DICTIONARY); ++ if (!rtp || !rtcp) + goto error; +- + ret.s = buf; + ret.len = snprintf(buf, sizeof(buf), +- "Input: %lli bytes, %lli packets, %lli errors; " +- "Output: %lli bytes, %lli packets, %lli errors", +- bencode_dictionary_get_integer(in, "bytes", -1), +- bencode_dictionary_get_integer(in, "packets", -1), +- bencode_dictionary_get_integer(in, "errors", -1), +- bencode_dictionary_get_integer(out, "bytes", -1), +- bencode_dictionary_get_integer(out, "packets", -1), +- bencode_dictionary_get_integer(out, "errors", -1)); ++ "RTP: %lli bytes, %lli packets, %lli errors; " ++ "RTCP: %lli bytes, %lli packets, %lli errors", ++ bencode_dictionary_get_integer(rtp, "bytes", -1), ++ bencode_dictionary_get_integer(rtp, "packets", -1), ++ bencode_dictionary_get_integer(rtp, "errors", -1), ++ bencode_dictionary_get_integer(rtcp, "bytes", -1), ++ bencode_dictionary_get_integer(rtcp, "packets", -1), ++ bencode_dictionary_get_integer(rtcp, "errors", -1)); + + bencode_buffer_free(&bencbuf); + return pv_get_strval(msg, param, res, &ret); diff --git a/opensips-0016-Fix-functions-to-match-their-documented-API-Change-f.patch b/opensips-0016-Fix-functions-to-match-their-documented-API-Change-f.patch new file mode 100644 index 0000000..8931002 --- /dev/null +++ b/opensips-0016-Fix-functions-to-match-their-documented-API-Change-f.patch @@ -0,0 +1,150 @@ +From: Rob Gagnon +Date: Thu, 19 Mar 2015 12:30:44 -0400 +Subject: [PATCH] Fix functions to match their documented API Change function + prototypes to use gparam_p instead of char * + + +diff --git a/modules/rtpengine/rtpengine.c b/modules/rtpengine/rtpengine.c +index 16e2fdc..efc3be2 100644 +--- a/modules/rtpengine/rtpengine.c ++++ b/modules/rtpengine/rtpengine.c +@@ -125,6 +125,11 @@ struct ng_flags_parse { + bencode_item_t *dict, *flags, *direction, *replace, *rtcp_mux; + }; + ++typedef struct rtpe_set_link { ++ struct rtpe_set *rset; ++ pv_spec_t rpv; ++} rtpe_set_link_t; ++ + static const char *command_strings[] = { + [OP_OFFER] = "offer", + [OP_ANSWER] = "answer", +@@ -135,18 +140,18 @@ static const char *command_strings[] = { + + static char *gencookie(); + static int rtpe_test(struct rtpe_node*, int, int); +-static int start_recording_f(struct sip_msg *, char *, char *); +-static int rtpengine_answer1_f(struct sip_msg *, char *, char *); +-static int rtpengine_offer1_f(struct sip_msg *, char *, char *); +-static int rtpengine_delete1_f(struct sip_msg *, char *, char *); +-static int rtpengine_manage1_f(struct sip_msg *, char *, char *); ++static int start_recording_f(struct sip_msg *); ++static int rtpengine_answer1_f(struct sip_msg *, gparam_p str1); ++static int rtpengine_offer1_f(struct sip_msg *, gparam_p str1); ++static int rtpengine_delete1_f(struct sip_msg *, gparam_p str1); ++static int rtpengine_manage1_f(struct sip_msg *, gparam_p str1); + + static int parse_flags(struct ng_flags_parse *, struct sip_msg *, enum rtpe_operation *, const char *); + + static int rtpengine_offer_answer(struct sip_msg *msg, const char *flags, int op); + static int add_rtpengine_socks(struct rtpe_set * rtpe_list, char * rtpengine); + static int fixup_set_id(void ** param, int param_no); +-static int set_rtpengine_set_f(struct sip_msg * msg, char * str1, char * str2); ++static int set_rtpengine_set_f(struct sip_msg * msg, rtpe_set_link_t *set_param); + static struct rtpe_set * select_rtpe_set(int id_set); + static struct rtpe_node *select_rtpe_node(str, int); + static char *send_rtpe_command(struct rtpe_node *, bencode_item_t *, int *); +@@ -193,11 +198,6 @@ static int *rtpe_socks = 0; + static int setid_avp_type; + static int_str setid_avp; + +-typedef struct rtpe_set_link { +- struct rtpe_set *rset; +- pv_spec_t rpv; +-} rtpe_set_link_t; +- + /* tm */ + static struct tm_binds tmb; + +@@ -1691,7 +1691,7 @@ static int rtpengine_delete(struct sip_msg *msg, const char *flags) { + } + + static int +-rtpengine_delete1_f(struct sip_msg* msg, char* str1, char* str2) ++rtpengine_delete1_f(struct sip_msg* msg, gparam_p str1) + { + str flags; + +@@ -1700,7 +1700,7 @@ rtpengine_delete1_f(struct sip_msg* msg, char* str1, char* str2) + + flags.s = NULL; + if (str1) +- fixup_get_svalue(msg, (gparam_p)str1, &flags); ++ fixup_get_svalue(msg, str1, &flags); + + return rtpengine_delete(msg, flags.s); + } +@@ -1708,12 +1708,12 @@ rtpengine_delete1_f(struct sip_msg* msg, char* str1, char* str2) + /* This function assumes p points to a line of requested type. */ + + static int +-set_rtpengine_set_f(struct sip_msg * msg, char * str1, char * str2) ++set_rtpengine_set_f(struct sip_msg * msg, rtpe_set_link_t *set_param) + { + rtpe_set_link_t *rtpl; + pv_value_t val; + +- rtpl = (rtpe_set_link_t*)str1; ++ rtpl = set_param; + + current_msg_id = 0; + selected_rtpe_set = 0; +@@ -1793,7 +1793,7 @@ rtpengine_manage(struct sip_msg *msg, const char *flags) + } + + static int +-rtpengine_manage1_f(struct sip_msg *msg, char *str1, char *str2) ++rtpengine_manage1_f(struct sip_msg *msg, gparam_p str1) + { + str flags; + +@@ -1802,13 +1802,13 @@ rtpengine_manage1_f(struct sip_msg *msg, char *str1, char *str2) + + flags.s = NULL; + if (str1) +- fixup_get_svalue(msg, (gparam_p)str1, &flags); ++ fixup_get_svalue(msg, str1, &flags); + + return rtpengine_manage(msg, flags.s); + } + + static int +-rtpengine_offer1_f(struct sip_msg *msg, char *str1, char *str2) ++rtpengine_offer1_f(struct sip_msg *msg, gparam_p str1) + { + str flags; + +@@ -1817,12 +1817,12 @@ rtpengine_offer1_f(struct sip_msg *msg, char *str1, char *str2) + + flags.s = NULL; + if (str1) +- fixup_get_svalue(msg, (gparam_p)str1, &flags); ++ fixup_get_svalue(msg, str1, &flags); + return rtpengine_offer_answer(msg, flags.s, OP_OFFER); + } + + static int +-rtpengine_answer1_f(struct sip_msg *msg, char *str1, char *str2) ++rtpengine_answer1_f(struct sip_msg *msg, gparam_p str1) + { + str flags; + +@@ -1835,7 +1835,7 @@ rtpengine_answer1_f(struct sip_msg *msg, char *str1, char *str2) + + flags.s = NULL; + if (str1) +- fixup_get_svalue(msg, (gparam_p)str1, &flags); ++ fixup_get_svalue(msg, str1, &flags); + return rtpengine_offer_answer(msg, flags.s, OP_ANSWER); + } + +@@ -1878,7 +1878,7 @@ error: + + + static int +-start_recording_f(struct sip_msg* msg, char *foo, char *bar) ++start_recording_f(struct sip_msg* msg) + { + return rtpe_function_call_simple(msg, OP_START_RECORDING, NULL); + } diff --git a/opensips-0017-Better-err-logging.patch b/opensips-0017-Better-err-logging.patch new file mode 100644 index 0000000..7265987 --- /dev/null +++ b/opensips-0017-Better-err-logging.patch @@ -0,0 +1,19 @@ +From: Bogdan-Andrei Iancu +Date: Thu, 2 Apr 2015 17:52:27 +0300 +Subject: [PATCH] Better err logging + +(cherry picked from commit a9ea0a142fc0e18f36e17e5129f522407edcab80) + +diff --git a/modules/rtpengine/rtpengine.c b/modules/rtpengine/rtpengine.c +index efc3be2..64662a1 100644 +--- a/modules/rtpengine/rtpengine.c ++++ b/modules/rtpengine/rtpengine.c +@@ -1474,7 +1474,7 @@ send_rtpe_command(struct rtpe_node *node, bencode_item_t *dict, int *outlen) + } while (len == -1 && errno == EINTR); + if (len <= 0) { + close(fd); +- LM_ERR("can't send command to a RTP proxy\n"); ++ LM_ERR("can't send command to a RTP proxy (%s)\n",strerror(errno)); + goto badproxy; + } + do { diff --git a/opensips-0018-Make-rtpengine-retry-sending-command-on-UDP-fragment.patch b/opensips-0018-Make-rtpengine-retry-sending-command-on-UDP-fragment.patch new file mode 100644 index 0000000..8e5380e --- /dev/null +++ b/opensips-0018-Make-rtpengine-retry-sending-command-on-UDP-fragment.patch @@ -0,0 +1,18 @@ +From: Oliver Severin Mulelid-Tynes +Date: Tue, 5 May 2015 09:47:46 +0200 +Subject: [PATCH] Make rtpengine retry sending command on UDP fragmentation + + +diff --git a/modules/rtpengine/rtpengine.c b/modules/rtpengine/rtpengine.c +index 64662a1..eaae544 100644 +--- a/modules/rtpengine/rtpengine.c ++++ b/modules/rtpengine/rtpengine.c +@@ -1500,7 +1500,7 @@ send_rtpe_command(struct rtpe_node *node, bencode_item_t *dict, int *outlen) + for (i = 0; i < rtpengine_retr; i++) { + do { + len = writev(rtpe_socks[node->idx], v, vcnt + 1); +- } while (len == -1 && (errno == EINTR || errno == ENOBUFS)); ++ } while (len == -1 && (errno == EINTR || errno == ENOBUFS || errno == EMSGSIZE)); + if (len <= 0) { + LM_ERR("can't send command to a RTP proxy\n"); + goto badproxy; diff --git a/opensips-0019-uri-fix-e164_check-overflow-checking.patch b/opensips-0019-uri-fix-e164_check-overflow-checking.patch new file mode 100644 index 0000000..41b5d2b --- /dev/null +++ b/opensips-0019-uri-fix-e164_check-overflow-checking.patch @@ -0,0 +1,22 @@ +From: Razvan Crainea +Date: Mon, 18 Jan 2016 19:30:48 +0200 +Subject: [PATCH] uri: fix e164_check overflow checking + +Reported by @ferrored on github +Closes #762 + +(cherry picked from commit 70e5a656c3cd4aa14757245dd710d3869575d3ca) + +diff --git a/modules/uri/checks.c b/modules/uri/checks.c +index 7fe5743..a895af6 100644 +--- a/modules/uri/checks.c ++++ b/modules/uri/checks.c +@@ -351,7 +351,7 @@ static inline int e164_check(str* _user) + char c; + + if ((_user->len > 2) && (_user->len < 17) && ((_user->s)[0] == '+')) { +- for (i = 1; i <= _user->len; i++) { ++ for (i = 1; i < _user->len; i++) { + c = (_user->s)[i]; + if (c < '0' || c > '9') return -1; + } diff --git a/opensips-0020-Fix-restoring-leg-route-set.patch b/opensips-0020-Fix-restoring-leg-route-set.patch new file mode 100644 index 0000000..8dff974 --- /dev/null +++ b/opensips-0020-Fix-restoring-leg-route-set.patch @@ -0,0 +1,45 @@ +From: Bogdan-Andrei Iancu +Date: Tue, 19 Jan 2016 14:47:34 +0200 +Subject: [PATCH] Fix restoring leg route set. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reported by Søren Andersen. + +(cherry picked from commit b602a7fb06a3658b71adedd69cf94eae814e93e5) + +diff --git a/modules/b2b_entities/b2b_entities.c b/modules/b2b_entities/b2b_entities.c +index 505b217..2f6eb10 100644 +--- a/modules/b2b_entities/b2b_entities.c ++++ b/modules/b2b_entities/b2b_entities.c +@@ -644,7 +644,7 @@ static inline int mi_print_b2be_dlg(struct mi_node *rpl, b2b_table htable, unsig + if(attr == NULL) goto error; + if(leg->route_set.len) + { +- attr = add_mi_attr(node1, MI_DUP_VALUE, "route_set", 8, ++ attr = add_mi_attr(node1, MI_DUP_VALUE, "route_set", 9, + leg->route_set.s, leg->route_set.len); + if(attr == NULL) goto error; + } +diff --git a/modules/b2b_entities/b2be_db.c b/modules/b2b_entities/b2be_db.c +index 64517fb..37e2ff7 100644 +--- a/modules/b2b_entities/b2be_db.c ++++ b/modules/b2b_entities/b2be_db.c +@@ -557,14 +557,13 @@ int b2b_entities_restore(void) + memset(&leg, 0, sizeof(dlg_leg_t)); + leg.tag.s= (char*)row_vals[21].val.string_val; + if(!leg.tag.s) +- { + continue; +- } + leg.tag.len = strlen(leg.tag.s); ++ leg.cseq = row_vals[22].val.int_val; + leg.contact.s = (char*)row_vals[23].val.string_val; + leg.contact.len = leg.contact.s?strlen(leg.contact.s):0; ++ leg.route_set.s = (char*)row_vals[24].val.string_val; + leg.route_set.len = leg.route_set.s?strlen(leg.route_set.s):0; +- leg.cseq = row_vals[22].val.int_val; + + new_leg = b2b_dup_leg(&leg, SHM_MEM_TYPE); + if(new_leg== NULL) diff --git a/opensips-0021-use-gnu89-for-newer-compilers-no-matter-what-arch-bu.patch b/opensips-0021-use-gnu89-for-newer-compilers-no-matter-what-arch-bu.patch new file mode 100644 index 0000000..3a893dc --- /dev/null +++ b/opensips-0021-use-gnu89-for-newer-compilers-no-matter-what-arch-bu.patch @@ -0,0 +1,30 @@ +From: Razvan Crainea +Date: Fri, 22 Jan 2016 12:34:35 +0200 +Subject: [PATCH] use gnu89 for newer compilers no matter what arch/build + system is used + + +diff --git a/Makefile.defs b/Makefile.defs +index 5f5c008..f83d744 100644 +--- a/Makefile.defs ++++ b/Makefile.defs +@@ -527,7 +527,7 @@ ifneq (,$(findstring gcc, $(CC_LONGVER))) + #transform gcc version into 2.9x, 3.x or 4.x + CC_CLASS=$(shell echo "$(CC_SHORTVER)" | \ + sed -e 's/2\.9.*/2.9x/' -e 's/3\.[0-9]/3.x/' \ +- -e 's/4\.[0-9]/4.x/') ++ -e 's/4\.[0-9]/4.x/' -e 's/5\.[0-9]/5.x/') + endif + + ifneq (, $(findstring Sun, $(CC_LONGVER))) +@@ -862,6 +862,10 @@ endif + CFLAGS=$(shell echo "$${CFLAGS}") + LDFLAGS+= + ++ifeq ($(CC_NAME)$(CC_CLASS), gcc5.x) ++ CFLAGS+=-std=gnu89 ++endif ++ + ifeq (,$(CFLAGS)) + + #common stuff diff --git a/opensips-0022-Fix-double-free-in-case-of-errors-when-running-MI-co.patch b/opensips-0022-Fix-double-free-in-case-of-errors-when-running-MI-co.patch new file mode 100644 index 0000000..7bf0136 --- /dev/null +++ b/opensips-0022-Fix-double-free-in-case-of-errors-when-running-MI-co.patch @@ -0,0 +1,41 @@ +From: Vlad Paiu +Date: Mon, 25 Jan 2016 13:32:57 +0200 +Subject: [PATCH] Fix double free in case of errors when running MI command + + +diff --git a/modules/mi_http/http_fnc.c b/modules/mi_http/http_fnc.c +index 56b84ff..384695c 100644 +--- a/modules/mi_http/http_fnc.c ++++ b/modules/mi_http/http_fnc.c +@@ -621,7 +621,6 @@ struct mi_root* mi_http_run_mi_cmd(int mod, int cmd, const str* arg, + (mi_flush_f *)mi_http_flush_tree, &html_page_data); + if (mi_rpl == NULL) { + LM_ERR("failed to process the command\n"); +- if (mi_cmd) free_mi_tree(mi_cmd); + goto error; + } else { + *page = html_page_data.page; +diff --git a/modules/mi_json/http_fnc.c b/modules/mi_json/http_fnc.c +index edf5b95..7cabb3e 100644 +--- a/modules/mi_json/http_fnc.c ++++ b/modules/mi_json/http_fnc.c +@@ -325,7 +325,6 @@ struct mi_root* mi_json_run_mi_cmd(const str* miCmd, const str* params, + NULL, &html_page_data); + if (mi_rpl == NULL) { + LM_ERR("failed to process the command\n"); +- if (mi_cmd) free_mi_tree(mi_cmd); + goto error; + } else { + *page = html_page_data.page; +diff --git a/modules/mi_xmlrpc_ng/http_fnc.c b/modules/mi_xmlrpc_ng/http_fnc.c +index ded5f12..caa50a4 100644 +--- a/modules/mi_xmlrpc_ng/http_fnc.c ++++ b/modules/mi_xmlrpc_ng/http_fnc.c +@@ -554,7 +554,6 @@ struct mi_root* mi_xmlrpc_http_run_mi_cmd(const str* arg, + (mi_flush_f *)mi_xmlrpc_http_flush_tree, &html_page_data); + if (mi_rpl == NULL) { + LM_ERR("failed to process the command\n"); +- if (mi_cmd) free_mi_tree(mi_cmd); + goto xml_error; + } else { + *page = html_page_data.page; diff --git a/opensips-0023-Fix-STANDARD-and-EXTRA-list-of-tables.patch b/opensips-0023-Fix-STANDARD-and-EXTRA-list-of-tables.patch new file mode 100644 index 0000000..7908133 --- /dev/null +++ b/opensips-0023-Fix-STANDARD-and-EXTRA-list-of-tables.patch @@ -0,0 +1,27 @@ +From: Bogdan-Andrei Iancu +Date: Mon, 25 Jan 2016 14:05:55 +0200 +Subject: [PATCH] Fix STANDARD and EXTRA list of tables. + +Permissions (in postgres) were not granted for the missing tables. +Reported by Nathaniel L. Keeling III on mailing list + +(cherry picked from commit aebf41f79274679bfc9a984a9d0c6871ac139462) + +Conflicts: + scripts/opensipsdbctl.base + +diff --git a/scripts/opensipsdbctl.base b/scripts/opensipsdbctl.base +index e7ab57a..cd7c726 100644 +--- a/scripts/opensipsdbctl.base ++++ b/scripts/opensipsdbctl.base +@@ -26,8 +26,8 @@ INSTALL_PRESENCE_TABLES=${INSTALL_PRESENCE_TABLES:-ask} + + # Used by dbtext and db_berkeley to define tables to be created, used by + # postgres to do the grants +-STANDARD_TABLES=${STANDARD_TABLES:-version acc dbaliases domain grp uri speed_dial pdt subscriber location re_grp address address missed_calls usr_preferences aliases silo dialog dispatcher dialplan dr_gateways dr_rules dr_groups rtpproxy_sockets load_balancer} +-EXTRA_TABLES=${EXTRA_TABLES:-imc_members imc_rooms cpl sip_trace domainpolicy carrierroute route_tree carrierfailureroute userblacklist globalblacklist cachedb registrant cc_flows cc_agents cc_cdrs cc_calls} ++STANDARD_TABLES=${STANDARD_TABLES:-version acc dbaliases domain grp uri speed_dial pdt subscriber location re_grp address missed_calls usr_preferences aliases silo dialog dispatcher dialplan dr_gateways dr_rules dr_groups dr_carriers rtpproxy_sockets load_balancer} ++EXTRA_TABLES=${EXTRA_TABLES:-imc_members imc_rooms cpl sip_trace domainpolicy carrierroute route_tree carrierfailureroute userblacklist globalblacklist b2b_entities b2b_logic cachedb registrant cc_flows cc_agents cc_cdrs cc_calls} + PRESENCE_TABLES=${PRESENCE_TABLES:-presentity active_watchers watchers xcap pua rls_presentity rls_watchers} + + # SQL definitions diff --git a/opensips-0024-Fixed-wrong-warning-on-memory-leak.patch b/opensips-0024-Fixed-wrong-warning-on-memory-leak.patch new file mode 100644 index 0000000..61c672a --- /dev/null +++ b/opensips-0024-Fixed-wrong-warning-on-memory-leak.patch @@ -0,0 +1,23 @@ +From: Bogdan-Andrei Iancu +Date: Tue, 26 Jan 2016 18:39:00 +0200 +Subject: [PATCH] Fixed wrong warning on memory leak. + +Set the auth_hdr buffer to NULL after moving the buffer into the lump. +Reported by Colin Martin on mailing list + +(cherry picked from commit bbaf8213ae9856d2345aa66d8aa002ec5f1f3fa5) + +diff --git a/modules/uac/auth.c b/modules/uac/auth.c +index c7c367d..69de12d 100644 +--- a/modules/uac/auth.c ++++ b/modules/uac/auth.c +@@ -183,6 +183,9 @@ int uac_auth( struct sip_msg *msg) + new_hdr->s = NULL; new_hdr->len = 0; + goto error; + } ++ /* the Authorization hdr was already pushed into the message as a lump ++ * along with the buffer, so detach the buffer from new_hdr var */ ++ new_hdr->s = NULL; new_hdr->len = 0; + + new_hdr->s = NULL; new_hdr->len = 0; + return 0; diff --git a/opensips-0025-Fix-calculation-of-timer-set-on-32b.patch b/opensips-0025-Fix-calculation-of-timer-set-on-32b.patch new file mode 100644 index 0000000..98e6131 --- /dev/null +++ b/opensips-0025-Fix-calculation-of-timer-set-on-32b.patch @@ -0,0 +1,23 @@ +From: Bogdan-Andrei Iancu +Date: Mon, 1 Feb 2016 09:52:13 +0200 +Subject: [PATCH] Fix calculation of timer set on 32b + +A bogus bitwise operation broke the calculation of the timer set on 32bits arch. +Reported by @mshary (Muhammad Shahzad Shafi) +Closes #732. + +(cherry picked from commit 5b7b5f78cb675d1d4126866307dc0caa22067d09) + +diff --git a/modules/tm/h_table.c b/modules/tm/h_table.c +index a0343e1..e14a305 100644 +--- a/modules/tm/h_table.c ++++ b/modules/tm/h_table.c +@@ -288,7 +288,7 @@ struct cell* build_cell( struct sip_msg* p_msg ) + /* get timer set id based on the transaction pointer, but + * devide by 64 to avoid issues because pointer are 64 bits + * aligned */ +- set = ( ((long)new_cell)>>tm_timer_shift ) % tm_table->timer_sets; ++ set = ( ((unsigned long)new_cell)>>tm_timer_shift ) % tm_table->timer_sets; + + /* UAS */ + #ifdef EXTRA_DEBUG diff --git a/opensips-0026-Fix-IPv6-insertion-in-fix_nated_contact.patch b/opensips-0026-Fix-IPv6-insertion-in-fix_nated_contact.patch new file mode 100644 index 0000000..14b2eb5 --- /dev/null +++ b/opensips-0026-Fix-IPv6-insertion-in-fix_nated_contact.patch @@ -0,0 +1,57 @@ +From: Bogdan-Andrei Iancu +Date: Fri, 5 Feb 2016 13:31:40 +0200 +Subject: [PATCH] Fix IPv6 insertion in fix_nated_contact() + +Add the [ ] around IPv6 in the newly built contact URI. +Reported by Jonathan Hunter on the mailing list + +(cherry picked from commit ce89e291e0616d80d555f1e386edde5bbd3922e9) + +diff --git a/modules/nathelper/nathelper.c b/modules/nathelper/nathelper.c +index d72abc2..67296c0 100644 +--- a/modules/nathelper/nathelper.c ++++ b/modules/nathelper/nathelper.c +@@ -601,6 +601,7 @@ fix_nated_contact_f(struct sip_msg* msg, char* str1, char* str2) + + cp = ip_addr2a(&msg->rcv.src_ip); + len = (hostport.s-c->uri.s) + strlen(cp) + 6 /* :port */ ++ + 2 /* just in case if IPv6 */ + + (params?params->len+(is_enclosed?0:2):0) + + 1 + left.len + left2.len; + buf = pkg_malloc(len); +@@ -610,15 +611,28 @@ fix_nated_contact_f(struct sip_msg* msg, char* str1, char* str2) + } + temp = hostport.s[0]; hostport.s[0] = '\0'; + if (params==NULL) { +- len1 = snprintf(buf, len, "%s%s:%d%.*s%.*s", c->uri.s, cp, +- msg->rcv.src_port,left.len,left.s,left2.len,left2.s); ++ if (msg->rcv.src_ip.af==AF_INET6) ++ len1 = snprintf(buf, len, "%s[%s]:%d%.*s%.*s", c->uri.s, cp, ++ msg->rcv.src_port,left.len,left.s,left2.len,left2.s); ++ else ++ len1 = snprintf(buf, len, "%s%s:%d%.*s%.*s", c->uri.s, cp, ++ msg->rcv.src_port,left.len,left.s,left2.len,left2.s); + } else if (!is_enclosed) { +- len1 = snprintf(buf, len, "<%s%s:%d%.*s>", c->uri.s, cp, +- msg->rcv.src_port,params->len,params->s); ++ if (msg->rcv.src_ip.af==AF_INET6) ++ len1 = snprintf(buf, len, "<%s[%s]:%d%.*s>", c->uri.s, cp, ++ msg->rcv.src_port,params->len,params->s); ++ else ++ len1 = snprintf(buf, len, "<%s%s:%d%.*s>", c->uri.s, cp, ++ msg->rcv.src_port,params->len,params->s); + } else { +- len1 = snprintf(buf, len, "%s%s:%d%.*s%.*s%.*s", c->uri.s, cp, +- msg->rcv.src_port,params->len,params->s, +- left.len,left.s,left2.len,left2.s); ++ if (msg->rcv.src_ip.af==AF_INET6) ++ len1 = snprintf(buf, len, "%s[%s]:%d%.*s%.*s%.*s", c->uri.s, cp, ++ msg->rcv.src_port,params->len,params->s, ++ left.len,left.s,left2.len,left2.s); ++ else ++ len1 = snprintf(buf, len, "%s%s:%d%.*s%.*s%.*s", c->uri.s, cp, ++ msg->rcv.src_port,params->len,params->s, ++ left.len,left.s,left2.len,left2.s); + } + if (len1 < len) + len = len1; diff --git a/opensips-0027-Don-t-push-throug-RTPProxy-streams-with-port-0-in-SD.patch b/opensips-0027-Don-t-push-throug-RTPProxy-streams-with-port-0-in-SD.patch new file mode 100644 index 0000000..12446c9 --- /dev/null +++ b/opensips-0027-Don-t-push-throug-RTPProxy-streams-with-port-0-in-SD.patch @@ -0,0 +1,23 @@ +From: Vlad Paiu +Date: Thu, 11 Feb 2016 16:58:19 +0200 +Subject: [PATCH] Don't push throug RTPProxy streams with port 0 in SDP + + +diff --git a/modules/rtpproxy/rtpproxy.c b/modules/rtpproxy/rtpproxy.c +index 602d66b..dbd3c70 100644 +--- a/modules/rtpproxy/rtpproxy.c ++++ b/modules/rtpproxy/rtpproxy.c +@@ -3710,6 +3710,13 @@ force_rtp_proxy_body(struct sip_msg* msg, struct force_rtpp_args *args, pv_spec_ + goto error; + } + ++medianum; ++ ++ /* If the callee wants to neither send nor receive a stream offered by ++ the caller, the callee sets the port number of that stream to zero in ++ its media description - don't engage rtpproxy for such streams */ ++ if (oldport.s[0] == '0' && oldport.len == 1) ++ continue; ++ + if (asymmetric != 0 || real != 0) { + newip = oldip; + } else { diff --git a/opensips-0028-Improve-qvalue-parsing.patch b/opensips-0028-Improve-qvalue-parsing.patch new file mode 100644 index 0000000..7780106 --- /dev/null +++ b/opensips-0028-Improve-qvalue-parsing.patch @@ -0,0 +1,174 @@ +From: Liviu Chircu +Date: Fri, 12 Feb 2016 17:12:57 +0200 +Subject: [PATCH] Improve qvalue parsing + +* accept qvalues with no decimal digits (e.g. "1." and "0.") + (this removes E_Q_DEC_MISSING from error.h) +* improve bad qvalue error reporting + +(cherry picked from commit ed60363d9d66bf230872c535e3fc0cf7f117b2c7) + +Conflicts: + cfg.y + +diff --git a/cfg.y b/cfg.y +index b969ce0..8b67517 100644 +--- a/cfg.y ++++ b/cfg.y +@@ -132,7 +132,7 @@ extern int yylex(); + static void yyerror(char* s); + static void yyerrorf(char* fmt, ...); + static char* tmp; +-static int i_tmp; ++static int i_tmp, rc; + static void* cmd_tmp; + static struct socket_id* lst_tmp; + static int rt; /* Type of route block for find_export */ +@@ -2751,13 +2751,18 @@ cmd: FORWARD LPAREN STRING RPAREN { mk_action2( $$, FORWARD_T, + | STRIP error { $$=0; yyerror("missing '(' or ')' ?"); } + | STRIP LPAREN error RPAREN { $$=0; yyerror("bad argument, " + "number expected"); } +- | APPEND_BRANCH LPAREN STRING COMMA STRING RPAREN { +- { qvalue_t q; +- if (str2q(&q, $5, strlen($5)) < 0) { +- yyerror("bad argument, q value expected"); +- } ++ | APPEND_BRANCH LPAREN STRING COMMA STRING RPAREN { ++ { ++ qvalue_t q; ++ ++ rc = str2q(&q, $5, strlen($5)); ++ if (rc < 0) ++ yyerrorf("bad qvalue (%.*s): %s", ++ strlen($5), $5, qverr2str(rc)); ++ + mk_action2( $$, APPEND_BRANCH_T, STR_ST, NUMBER_ST, $3, +- (void *)(long)q); } ++ (void *)(long)q); ++ } + } + | APPEND_BRANCH LPAREN STRING RPAREN { mk_action2( $$, APPEND_BRANCH_T, + STR_ST, NUMBER_ST, $3, (void *)Q_UNSPECIFIED) ; } +diff --git a/error.c b/error.c +index 9042be3..715863b 100644 +--- a/error.c ++++ b/error.c +@@ -105,10 +105,6 @@ int err2reason_phrase( + error_txt="q parameter too big"; + *sip_error=-E_BAD_REQ; + break; +- case E_Q_DEC_MISSING: +- error_txt="Decimal part missing in q"; +- *sip_error=-E_BAD_REQ; +- break; + case E_NO_DESTINATION: + error_txt="No destination available"; + *sip_error=-E_BAD_SERVER; +diff --git a/error.h b/error.h +index 87854ca..8e6f6a9 100644 +--- a/error.h ++++ b/error.h +@@ -46,8 +46,7 @@ + #define E_Q_INV_CHAR -15 /*!< Invalid character in q */ + #define E_Q_EMPTY -16 /*!< Empty q */ + #define E_Q_TOO_BIG -17 /*!< q too big (> 1) */ +-#define E_Q_DEC_MISSING -18 /*!< Decimal part missing */ +-#define E_NO_DESTINATION -19 /*!< No available destination */ ++#define E_NO_DESTINATION -18 /*!< No available destination */ + + /* opensips specific error codes */ + #define E_IP_BLOCKED -473 /*!< destination filtered */ +diff --git a/modules/registrar/sip_msg.c b/modules/registrar/sip_msg.c +index 28ad6bb..e5d895d 100644 +--- a/modules/registrar/sip_msg.c ++++ b/modules/registrar/sip_msg.c +@@ -281,12 +281,16 @@ void calc_contact_expires(struct sip_msg* _m, param_t* _ep, int* _e, struct save + */ + int calc_contact_q(param_t* _q, qvalue_t* _r) + { ++ int rc; ++ + if (!_q || (_q->body.len == 0)) { + *_r = default_q; + } else { +- if (str2q(_r, _q->body.s, _q->body.len) < 0) { ++ rc = str2q(_r, _q->body.s, _q->body.len); ++ if (rc < 0) { + rerrno = R_INV_Q; /* Invalid q parameter */ +- LM_ERR("invalid q parameter\n"); ++ LM_ERR("invalid qvalue (%.*s): %s\n", ++ _q->body.len, _q->body.s, qverr2str(rc)); + return -1; + } + } +diff --git a/modules/uac_redirect/rd_funcs.c b/modules/uac_redirect/rd_funcs.c +index ef6a923..329f2cb 100644 +--- a/modules/uac_redirect/rd_funcs.c ++++ b/modules/uac_redirect/rd_funcs.c +@@ -106,7 +106,7 @@ static void sort_contacts(contact_t *ct_list, str *ct_array, + { + param_t *q_para; + qvalue_t q; +- int i,j; ++ int i, j, rc; + char backup; + + for( ; ct_list ; ct_list = ct_list->next ) { +@@ -123,8 +123,10 @@ static void sort_contacts(contact_t *ct_list, str *ct_array, + if (q_para==0 || q_para->body.len==0) { + q = DEFAULT_Q_VALUE; + } else { +- if (str2q( &q, q_para->body.s, q_para->body.len)!=0) { +- LM_ERR("invalid q param\n"); ++ rc = str2q( &q, q_para->body.s, q_para->body.len); ++ if (rc != 0) { ++ LM_ERR("invalid qvalue (%.*s): %s\n", ++ q_para->body.len, q_para->body.s, qverr2str(rc)); + /* skip this contact */ + continue; + } +diff --git a/qvalue.c b/qvalue.c +index 5cd2cd9..34029f3 100644 +--- a/qvalue.c ++++ b/qvalue.c +@@ -75,6 +75,7 @@ int str2q(qvalue_t* q, char* s, int len) + break; + + case '.': ++ *q = 0; + state = ST_0_PT; + break; + +@@ -167,15 +168,8 @@ int str2q(qvalue_t* q, char* s, int len) + } + } + +- switch(state) { +- case ST_START: ++ if (state == ST_START) + return E_Q_EMPTY; + +- case ST_0_PT: +- case ST_1_PT: +- return E_Q_DEC_MISSING; +- +- default: +- return 0; +- } ++ return 0; + } +diff --git a/qvalue.h b/qvalue.h +index 96aa81f..717844c 100644 +--- a/qvalue.h ++++ b/qvalue.h +@@ -76,6 +76,10 @@ typedef int qvalue_t; + #define Q_PREFIX "0." + #define Q_PREFIX_LEN (sizeof(Q_PREFIX) - 1) + ++#define qverr2str(rc) \ ++ (rc == E_Q_INV_CHAR ? "bad characters" : \ ++ rc == E_Q_EMPTY ? "empty value" : \ ++ rc == E_Q_TOO_BIG ? "max value is 1.0" : "bad qvalue") + + + /*! \brief diff --git a/opensips-0029-Fix-contact-DB-delete-in-CONTACT_ONLY-macthing.patch b/opensips-0029-Fix-contact-DB-delete-in-CONTACT_ONLY-macthing.patch new file mode 100644 index 0000000..b947b65 --- /dev/null +++ b/opensips-0029-Fix-contact-DB-delete-in-CONTACT_ONLY-macthing.patch @@ -0,0 +1,78 @@ +From: Bogdan-Andrei Iancu +Date: Wed, 17 Feb 2016 18:28:51 +0200 +Subject: [PATCH] Fix contact DB delete in CONTACT_ONLY macthing. + +Do not use the callid as part of the key during the DB delete as it may changed (can be updated) in the meanwhile. +Reported by @ferrored. +Closing #764 +Many thanks to @ferrored for his support in troubleshooting this issue. + +diff --git a/modules/usrloc/ucontact.c b/modules/usrloc/ucontact.c +index 3986f67..af6da89 100644 +--- a/modules/usrloc/ucontact.c ++++ b/modules/usrloc/ucontact.c +@@ -805,6 +805,7 @@ int db_delete_ucontact(ucontact_t* _c) + char* dom; + db_key_t keys[4]; + db_val_t vals[4]; ++ int n; + + if (_c->flags & FL_MEM) { + return 0; +@@ -812,8 +813,6 @@ int db_delete_ucontact(ucontact_t* _c) + + keys[0] = &user_col; + keys[1] = &contact_col; +- keys[2] = &callid_col; +- keys[3] = &domain_col; + + vals[0].type = DB_STR; + vals[0].nul = 0; +@@ -823,22 +822,30 @@ int db_delete_ucontact(ucontact_t* _c) + vals[1].nul = 0; + vals[1].val.str_val = _c->c; + +- vals[2].type = DB_STR; +- vals[2].nul = 0; +- vals[2].val.str_val = _c->callid; ++ n = 2; ++ ++ if (matching_mode==CONTACT_CALLID) { ++ vals[n].type = DB_STR; ++ vals[n].nul = 0; ++ vals[n].val.str_val = _c->callid; ++ ++ keys[n++] = &callid_col; ++ } + + if (use_domain) { +- vals[3].type = DB_STR; +- vals[3].nul = 0; ++ vals[n].type = DB_STR; ++ vals[n].nul = 0; + dom = q_memchr(_c->aor->s, '@', _c->aor->len); + if (dom==0) { + vals[0].val.str_val.len = 0; +- vals[3].val.str_val = *_c->aor; ++ vals[n].val.str_val = *_c->aor; + } else { + vals[0].val.str_val.len = dom - _c->aor->s; +- vals[3].val.str_val.s = dom + 1; +- vals[3].val.str_val.len = _c->aor->s + _c->aor->len - dom - 1; ++ vals[n].val.str_val.s = dom + 1; ++ vals[n].val.str_val.len = _c->aor->s + _c->aor->len - dom - 1; + } ++ ++ keys[n++] = &domain_col; + } + + if (ul_dbf.use_table(ul_dbh, _c->domain) < 0) { +@@ -848,7 +855,7 @@ int db_delete_ucontact(ucontact_t* _c) + + CON_PS_REFERENCE(ul_dbh) = &my_ps; + +- if (ul_dbf.delete(ul_dbh, keys, 0, vals, (use_domain) ? (4) : (3)) < 0) { ++ if (ul_dbf.delete(ul_dbh, keys, 0, vals, n) < 0) { + LM_ERR("deleting from database failed\n"); + return -1; + } diff --git a/opensips.spec b/opensips.spec index 69fe5d6..a94d46c 100644 --- a/opensips.spec +++ b/opensips.spec @@ -1,33 +1,47 @@ %global _hardened_build 1 -%if 0%{?el6}%{?el7}%{?fedora} -%global opensips_user opensips -%global opensips_group opensips -%else -%global opensips_user root -%global opensips_group root -%endif - %global EXCLUDE_MODULES cachedb_cassandra cachedb_couchbase cachedb_mongodb %{!?_with_oracle:db_oracle} lua osp sngtc Summary: Open Source SIP Server Name: opensips -Version: 1.10.5 -Release: 9%{?dist} +Version: 1.11.6 +Release: 1%{?dist} License: GPLv2+ Group: System Environment/Daemons -Source0: https://opensips.org/pub/%{name}/%{version}/%{name}-%{version}_src.tar.gz +Source0: https://github.com/%{name}/%{name}/archive/%{version}/%{name}-%{version}.tar.gz Patch1: opensips-0001-Consistently-use-rtpproxy-switches.patch Patch2: opensips-0002-Cleanup-Oracle-s-makefiles.patch Patch3: opensips-0003-Removed-all-async-Oracle-operations-they-didn-t-work.patch Patch4: opensips-0004-db_oracle-Fix-default-ret-result.patch -# FIXME remove this outdated patch in the next major version (1.11.x) -Patch5: opensips-0005-Fix-for-old-Perl-in-RHEL5.patch -Patch6: opensips-0006-Don-t-modify-interim-return-value.patch -Patch7: opensips-0007-Return-actual-payload-ID-in-case-of-a-dynamic-payloa.patch -Patch8: opensips-0008-Use-additional-auth-field-Sip-Source-IP-Address.patch -Patch9: opensips-0009-Don-t-remove-pthread-library-explicitly-from-mi_xmlr.patch -Patch10: opensips-0010-Fix-fixing-hdr-names-shorter-than-3-chars.patch +Patch5: opensips-0005-Don-t-modify-interim-return-value.patch +Patch6: opensips-0006-Return-actual-payload-ID-in-case-of-a-dynamic-payloa.patch +Patch7: opensips-0007-Use-additional-auth-field-Sip-Source-IP-Address.patch +Patch8: opensips-0008-Don-t-remove-pthread-library-explicitly-from-mi_xmlr.patch +# These patches were backported from master branch and contains a technological +# preview of a new module - rtpengine +Patch9: opensips-0009-backport-rtpengine-up-to-64bc0c8226ccb6d531dfa230cce.patch +Patch10: opensips-0010-added-missing-force-relay-flag-option-for-ICE.patch +Patch11: opensips-0011-added-more-rtp-transports-to-match-those-available-i.patch +Patch12: opensips-0012-fixed-bitmask-to-cover-all-3-bits-vs.-1st-and-3rd.-T.patch +Patch13: opensips-0013-expose-the-DTLS-passive-flag-to-allow-for-passive-re.patch +Patch14: opensips-0014-rtpengine-do-not-register-mi-functions-twice.patch +Patch15: opensips-0015-fix-rtpstat-for-rtpengine-thanks-to-jarrod-for-track.patch +Patch16: opensips-0016-Fix-functions-to-match-their-documented-API-Change-f.patch +Patch17: opensips-0017-Better-err-logging.patch +Patch18: opensips-0018-Make-rtpengine-retry-sending-command-on-UDP-fragment.patch + +# These patches were taken from 1.11 branch (except ef82ca2368462c41f7115202e90f73d8ab16fd1f) +Patch19: opensips-0019-uri-fix-e164_check-overflow-checking.patch +Patch20: opensips-0020-Fix-restoring-leg-route-set.patch +Patch21: opensips-0021-use-gnu89-for-newer-compilers-no-matter-what-arch-bu.patch +Patch22: opensips-0022-Fix-double-free-in-case-of-errors-when-running-MI-co.patch +Patch23: opensips-0023-Fix-STANDARD-and-EXTRA-list-of-tables.patch +Patch24: opensips-0024-Fixed-wrong-warning-on-memory-leak.patch +Patch25: opensips-0025-Fix-calculation-of-timer-set-on-32b.patch +Patch26: opensips-0026-Fix-IPv6-insertion-in-fix_nated_contact.patch +Patch27: opensips-0027-Don-t-push-throug-RTPProxy-streams-with-port-0-in-SD.patch +Patch28: opensips-0028-Improve-qvalue-parsing.patch +Patch29: opensips-0029-Fix-contact-DB-delete-in-CONTACT_ONLY-macthing.patch URL: https://opensips.org BuildRequires: expat-devel @@ -36,7 +50,11 @@ BuildRequires: bison BuildRequires: flex BuildRequires: subversion # needed by snmpstats +%if 0%{?fedora} +BuildRequires: freeradius-client-devel +%else BuildRequires: radiusclient-ng-devel +%endif BuildRequires: mysql-devel BuildRequires: postgresql-devel @@ -135,11 +153,46 @@ server, namely DIameter Server Client (DISC). Summary: Back-2-Back User Agent Group: System Environment/Daemons Requires: %{name}%{?_isa} = %{version}-%{release} +Requires: %{name}-pua%{?_isa} = %{version}-%{release} %description b2bua B2BUA is an implementation of the behavior of a B2BUA as defined in RFC 3261 that offers the possibility to build certain services on top of it. +%package cachedb_memcached +Summary: Memcached connector for cache subsystem +Group: System Environment/Daemons +Requires: %{name}%{?_isa} = %{version}-%{release} +Provides: %{name}-memcached%{?_isa} = %{version}-%{release} +Obsoletes: %{name}-memcached +BuildRequires: libmemcached-devel + +%description cachedb_memcached +Memcached module is an implementation of a cache system designed to +work with a memcached server. + +%package cachedb_redis +Summary: Redis connector for cache subsystem +Group: System Environment/Daemons +Requires: %{name}%{?_isa} = %{version}-%{release} +Provides: %{name}-redis%{?_isa} = %{version}-%{release} +Obsoletes: %{name}-redis +BuildRequires: hiredis-devel + +%description cachedb_redis +This module is an implementation of a cache system designed to work +with a Redis server. + +%package call_center +Summary: An inbound call center system +Group: System Environment/Daemons +Requires: %{name}%{?_isa} = %{version}-%{release} +Requires: %{name}-b2bua%{?_isa} = %{version}-%{release} + +%description call_center +The Call Center module implements an inbound call center system with call flows +(for queueing the received calls) and agents (for answering the calls). + %package carrierroute Summary: Routing extension suitable for carriers Group: System Environment/Daemons @@ -176,6 +229,69 @@ Requires: %{name}%{?_isa} = %{version}-%{release} This module provides access to a database that is implemented as a HTTP server. +%package db_mysql +Summary: MySQL storage support for the OpenSIPS +Group: System Environment/Daemons +Requires: %{name}%{?_isa} = %{version}-%{release} +Provides: %{name}-mysql%{?_isa} = %{version}-%{release} +Obsoletes: %{name}-mysql + +%description db_mysql +The %{name}-mysql package contains the MySQL plugin for %{name}, which allows +a MySQL-Database to be used for persistent storage. + +%if 0%{?_with_oracle} +%package db_oracle +Summary: Oracle Storage Support for the OpenSIPS +Group: System Environment/Daemons +Requires: %{name}%{?_isa} = %{version}-%{release} +Provides: %{name}-oracle%{?_isa} = %{version}-%{release} +Obsoletes: %{name}-oracle +BuildRequires: oracle-instantclient-devel + +%description db_oracle +The %{name}-oracle package contains the Oracle plugin for %{name}, which allows +a Oracle-Database to be used for persistent storage. +%endif + +%package db_perlvdb +Summary: Perl virtual database engine +Group: System Environment/Daemons +# require perl-devel for >F7 and perl for <=F6 +BuildRequires: perl(ExtUtils::MakeMaker) +Requires: %{name}%{?_isa} = %{version}-%{release} +Requires: %{name}-perl%{?_isa} = %{version}-%{release} +Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) +Provides: %{name}-perlvdb%{?_isa} = %{version}-%{release} +Obsoletes: %{name}-perlvdb + +%description db_perlvdb +The Perl Virtual Database (VDB) provides a virtualization framework for +OpenSIPS's database access. It does not handle a particular database engine +itself but lets the user relay database requests to arbitrary Perl functions. + +%package db_postgresql +Summary: PostgreSQL Storage Support for the OpenSIPS +Group: System Environment/Daemons +Requires: %{name}%{?_isa} = %{version}-%{release} +Provides: %{name}-postgresql%{?_isa} = %{version}-%{release} +Obsoletes: %{name}-postgresql + +%description db_postgresql +The %{name}-postgresql package contains the PostgreSQL plugin for %{name}, +which allows a PostgreSQL-Database to be used for persistent storage. + +%package db_unixodbc +Summary: OpenSIPS unixODBC Storage support +Group: System Environment/Daemons +Requires: %{name}%{?_isa} = %{version}-%{release} +Provides: %{name}-unixodbc%{?_isa} = %{version}-%{release} +Obsoletes: %{name}-unixodbc + +%description db_unixodbc +The %{name}-unixodbc package contains the unixODBC plugin for %{name}, which +allows a unixODBC to be used for persistent storage + %package event_rabbitmq Summary: Event RabbitMQ module Group: System Environment/Daemons @@ -243,45 +359,47 @@ The LDAP module implements an LDAP search interface for OpenSIPS. #the features implemented in the modules. With this Lua module, you can #easily implement your own OpenSIPS extensions in Lua. -%package memcached -Summary: Memcached connector +%package mi_json +Summary: A JSON REST interface for the Management Interface Group: System Environment/Daemons Requires: %{name}%{?_isa} = %{version}-%{release} -BuildRequires: libmemcached-devel +Requires: %{name}-httpd%{?_isa} = %{version}-%{release} -%description memcached -Memcached module is an implementation of a cache system designed to -work with a memcached server. +%description mi_json +This module implements a JSON server for the Management Interface that handles +GET requests and generates JSON responses. -%package mmgeoip -Summary: Wrapper for the MaxMind GeoIP API +%package mi_xmlrpc +Summary: A xmlrpc server for the Management Interface Group: System Environment/Daemons Requires: %{name}%{?_isa} = %{version}-%{release} +Provides: %{name}-xmlrpc%{?_isa} = %{version}-%{release} +Obsoletes: %{name}-xmlrpc -%description mmgeoip -Mmgeoip is a lightweight wrapper for the MaxMind GeoIP API. It adds -IP address-to-location lookup capability to OpenSIPS scripts. +%description mi_xmlrpc +This module implements a xmlrpc server that handles xmlrpc requests and generates +xmlrpc responses. When a xmlrpc message is received a default method is executed. -%package mysql -Summary: MySQL storage support for the OpenSIPS +%package mi_xmlrpc_ng +Summary: A xmlrpc server for the Management Interface (new version) Group: System Environment/Daemons Requires: %{name}%{?_isa} = %{version}-%{release} +Requires: %{name}-httpd%{?_isa} = %{version}-%{release} +Provides: %{name}-xmlrpc_ng%{?_isa} = %{version}-%{release} +Obsoletes: %{name}-xmlrpc_ng -%description mysql -The %{name}-mysql package contains the MySQL plugin for %{name}, which allows -a MySQL-Database to be used for persistent storage. +%description mi_xmlrpc_ng +This module implements a xmlrpc server that handles xmlrpc requests and generates +xmlrpc responses. When a xmlrpc message is received a default method is executed. -%if 0%{?_with_oracle} -%package oracle -Summary: Oracle Storage Support for the OpenSIPS +%package mmgeoip +Summary: Wrapper for the MaxMind GeoIP API Group: System Environment/Daemons Requires: %{name}%{?_isa} = %{version}-%{release} -BuildRequires: oracle-instantclient-devel -%description oracle -The %{name}-oracle package contains the Oracle plugin for %{name}, which allows -a Oracle-Database to be used for persistent storage. -%endif +%description mmgeoip +Mmgeoip is a lightweight wrapper for the MaxMind GeoIP API. It adds +IP address-to-location lookup capability to OpenSIPS scripts. %package peering Summary: Radius peering @@ -315,20 +433,6 @@ simple access to the full world of CPAN modules. SIP URI rewriting could be implemented based on regular expressions; accessing arbitrary data backends, e.g. LDAP or Berkeley DB files, is now extremely simple. -%package perlvdb -Summary: Perl virtual database engine -Group: System Environment/Daemons -# require perl-devel for >F7 and perl for <=F6 -BuildRequires: perl(ExtUtils::MakeMaker) -Requires: %{name}%{?_isa} = %{version}-%{release} -Requires: %{name}-perl%{?_isa} = %{version}-%{release} -Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) - -%description perlvdb -The Perl Virtual Database (VDB) provides a virtualization framework for -OpenSIPS's database access. It does not handle a particular database engine -itself but lets the user relay database requests to arbitrary Perl functions. - %package pi_http Summary: A HTTP provisioning interface for OpenSIPS Group: System Environment/Daemons @@ -340,15 +444,6 @@ This module provides an HTTP provisioning interface for OpenSIPS. It is using the OpenSIPS's internal database API to provide a simple way of manipulating records inside OpenSIPS's tables. -%package postgresql -Summary: PostgreSQL Storage Support for the OpenSIPS -Group: System Environment/Daemons -Requires: %{name}%{?_isa} = %{version}-%{release} - -%description postgresql -The %{name}-postgresql package contains the PostgreSQL plugin for %{name}, -which allows a PostgreSQL-Database to be used for persistent storage. - %package presence Summary: Presence server Group: System Environment/Daemons @@ -498,16 +593,6 @@ Requires: %{name}%{?_isa} = %{version}-%{release} %description python Helps implement your own OpenSIPS extensions in Python -%package redis -Summary: Redis connector -Group: System Environment/Daemons -Requires: %{name}%{?_isa} = %{version}-%{release} -BuildRequires: hiredis-devel - -%description redis -This module is an implementation of a cache system designed to work -with a Redis server. - %package regex Summary: RegExp via PCRE library Group: System Environment/Daemons @@ -588,15 +673,6 @@ The %{name}-tlsops package implements TLS related functions to use in the routing script, and exports pseudo variables with certificate and TLS parameters. -%package unixodbc -Summary: OpenSIPS unixODBC Storage support -Group: System Environment/Daemons -Requires: %{name}%{?_isa} = %{version}-%{release} - -%description unixodbc -The %{name}-unixodbc package contains the unixODBC plugin for %{name}, which -allows a unixODBC to be used for persistent storage - %package xcap Summary: XCAP common functions Group: System Environment/Daemons @@ -617,25 +693,6 @@ It fetches XCAP elements, either documents or part of them, by sending HTTP GET requests. It also offers support for conditional queries. It uses libcurl library as a client-side HTTP transfer library. -%package xmlrpc -Summary: A xmlrpc server -Group: System Environment/Daemons -Requires: %{name}%{?_isa} = %{version}-%{release} - -%description xmlrpc -This module implements a xmlrpc server that handles xmlrpc requests and generates -xmlrpc responses. When a xmlrpc message is received a default method is executed. - -%package xmlrpc_ng -Summary: A xmlrpc server (new version) -Group: System Environment/Daemons -Requires: %{name}%{?_isa} = %{version}-%{release} -Requires: %{name}-httpd%{?_isa} = %{version}-%{release} - -%description xmlrpc_ng -This module implements a xmlrpc server that handles xmlrpc requests and generates -xmlrpc responses. When a xmlrpc message is received a default method is executed. - %package xmpp Summary: Gateway between OpenSIPS and a jabber server Group: System Environment/Daemons @@ -647,17 +704,40 @@ the exchange of instant messages between SIP clients and XMPP(jabber) clients. %prep -%setup -q -n %{name}-%{version}-tls +%setup -q %patch1 -p1 %patch2 -p1 %patch3 -p1 +rm -f modules/db_oracle/asynch.c +rm -f modules/db_oracle/asynch.h %patch4 -p1 -# Patch no.5 removed -%patch6 -p1 -b .dont_modify -%patch7 -p1 -b .return_actual_payload_id -%patch8 -p1 -b .additional_auth_field_src_ip -%patch9 -p1 -b .restore_pthread_linkage -%patch10 -p1 -b .short_headers +%patch5 -p1 -b .dont_modify +%patch6 -p1 -b .return_actual_payload_id +%patch7 -p1 -b .additional_auth_field_src_ip +%patch8 -p1 -b .restore_pthread_linkage + +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%patch17 -p1 +%patch18 -p1 + +%patch19 -p1 +%patch20 -p1 +%patch21 -p1 +%patch22 -p1 +%patch23 -p1 +%patch24 -p1 +%patch25 -p1 +%patch26 -p1 +%patch27 -p1 +%patch28 -p1 +%patch29 -p1 %build LOCALBASE=/usr NICER=0 CFLAGS="%{optflags} -fgnu89-inline" LDFLAGS="%{?__global_ldflags}" %{?_with_oracle:ORAHOME="$ORACLE_HOME"} %{__make} all %{?_smp_mflags} TLS=1 \ @@ -706,9 +786,7 @@ install -D -m 0644 -p packaging/fedora/%{name}.service %{buildroot}%{_unitdir}/% install -D -m 0644 -p packaging/fedora/%{name}.tmpfiles.conf %{buildroot}%{_tmpfilesdir}/%{name}.conf install -D -m 0755 -p packaging/fedora/%{name}.m4cfg %{buildroot}%{_sbindir}/opensips-m4cfg %endif -%if 0%{?el6}%{?el7}%{?fedora} mkdir -p %{buildroot}%{_localstatedir}/run/%{name} -%endif echo -e "\nETCDIR=\"%{_sysconfdir}/opensips\"\n" \ >> %{buildroot}%{_sysconfdir}/%{name}/opensipsctlrc @@ -717,12 +795,10 @@ install -D -p -m 644 packaging/fedora/%{name}.sysconfig %{buildroot}%{_sysconfdi %pre -%if 0%{?el6}%{?el7}%{?fedora} getent group %{name} >/dev/null || groupadd -r %{name} getent passwd %{name} >/dev/null || \ useradd -r -g %{name} -d %{_localstatedir}/run/%{name} -s /sbin/nologin \ -c "OpenSIPS SIP Server" %{name} 2>/dev/null || : -%endif %post %if 0%{?el6} @@ -749,12 +825,12 @@ fi %{_sbindir}/osipsconfig %{_sbindir}/osipsconsole -%attr(750,%{opensips_user},%{opensips_group}) %dir %{_sysconfdir}/opensips -%attr(750,%{opensips_user},%{opensips_group}) %dir %{_sysconfdir}/opensips/tls -%attr(750,%{opensips_user},%{opensips_group}) %dir %{_sysconfdir}/opensips/tls/rootCA -%attr(750,%{opensips_user},%{opensips_group}) %dir %{_sysconfdir}/opensips/tls/rootCA/certs -%attr(750,%{opensips_user},%{opensips_group}) %dir %{_sysconfdir}/opensips/tls/rootCA/private -%attr(750,%{opensips_user},%{opensips_group}) %dir %{_sysconfdir}/opensips/tls/user +%attr(750,%{name},%{name}) %dir %{_sysconfdir}/opensips +%attr(750,%{name},%{name}) %dir %{_sysconfdir}/opensips/tls +%attr(750,%{name},%{name}) %dir %{_sysconfdir}/opensips/tls/rootCA +%attr(750,%{name},%{name}) %dir %{_sysconfdir}/opensips/tls/rootCA/certs +%attr(750,%{name},%{name}) %dir %{_sysconfdir}/opensips/tls/rootCA/private +%attr(750,%{name},%{name}) %dir %{_sysconfdir}/opensips/tls/user %dir %{_libdir}/opensips/ %dir %{_libdir}/opensips/modules/ %dir %{_libdir}/opensips/opensipsctl/ @@ -767,15 +843,13 @@ fi %{_unitdir}/%{name}.service %{_tmpfilesdir}/%{name}.conf %endif -%if 0%{?el6}%{?el7}%{?fedora} %dir %attr(0755, opensips, opensips) %{_localstatedir}/run/%{name} -%endif %config(noreplace) %{_sysconfdir}/opensips/dictionary.opensips %config(noreplace) %{_sysconfdir}/sysconfig/%{name} -%attr(640,%{opensips_user},%{opensips_group}) %config(noreplace) %{_sysconfdir}/opensips/opensips.cfg -%attr(640,%{opensips_user},%{opensips_group}) %config(noreplace) %{_sysconfdir}/opensips/opensipsctlrc -%attr(640,%{opensips_user},%{opensips_group}) %config(noreplace) %{_sysconfdir}/opensips/osipsconsolerc +%attr(640,%{name},%{name}) %config(noreplace) %{_sysconfdir}/opensips/opensips.cfg +%attr(640,%{name},%{name}) %config(noreplace) %{_sysconfdir}/opensips/opensipsctlrc +%attr(640,%{name},%{name}) %config(noreplace) %{_sysconfdir}/opensips/osipsconsolerc # these files are just an examples so no need to restrict access to them %config(noreplace) %{_sysconfdir}/opensips/tls/ca.conf %config(noreplace) %{_sysconfdir}/opensips/tls/request.conf @@ -819,7 +893,7 @@ fi %doc docdir/README %doc docdir/README-MODULES %doc docdir/README.tls -%doc COPYING +%license COPYING %{_libdir}/opensips/modules/acc.so %{_libdir}/opensips/modules/alias_db.so @@ -830,17 +904,17 @@ fi %{_libdir}/opensips/modules/cachedb_local.so %{_libdir}/opensips/modules/cachedb_sql.so %{_libdir}/opensips/modules/call_control.so -%{_libdir}/opensips/modules/closeddial.so %{_libdir}/opensips/modules/cfgutils.so +%{_libdir}/opensips/modules/closeddial.so %{_libdir}/opensips/modules/db_cachedb.so %{_libdir}/opensips/modules/db_flatstore.so -%{_libdir}/opensips/modules/db_virtual.so %{_libdir}/opensips/modules/db_text.so -%{_libdir}/opensips/modules/dns_cache.so +%{_libdir}/opensips/modules/db_virtual.so %{_libdir}/opensips/modules/dialog.so %{_libdir}/opensips/modules/dialplan.so %{_libdir}/opensips/modules/dispatcher.so %{_libdir}/opensips/modules/diversion.so +%{_libdir}/opensips/modules/dns_cache.so %{_libdir}/opensips/modules/domain.so %{_libdir}/opensips/modules/domainpolicy.so %{_libdir}/opensips/modules/drouting.so @@ -858,13 +932,12 @@ fi %{_libdir}/opensips/modules/mathops.so %{_libdir}/opensips/modules/maxfwd.so %{_libdir}/opensips/modules/mediaproxy.so -%{_libdir}/opensips/modules/mi_fifo.so %{_libdir}/opensips/modules/mi_datagram.so +%{_libdir}/opensips/modules/mi_fifo.so %{_libdir}/opensips/modules/mi_http.so %{_libdir}/opensips/modules/msilo.so -%{_libdir}/opensips/modules/nathelper.so %{_libdir}/opensips/modules/nat_traversal.so -%{_libdir}/opensips/modules/rtpproxy.so +%{_libdir}/opensips/modules/nathelper.so %{_libdir}/opensips/modules/options.so %{_libdir}/opensips/modules/path.so %{_libdir}/opensips/modules/pdt.so @@ -874,6 +947,9 @@ fi %{_libdir}/opensips/modules/ratelimit.so %{_libdir}/opensips/modules/registrar.so %{_libdir}/opensips/modules/rr.so +%{_libdir}/opensips/modules/rtpengine.so +%{_libdir}/opensips/modules/rtpproxy.so +%{_libdir}/opensips/modules/script_helper.so %{_libdir}/opensips/modules/signaling.so %{_libdir}/opensips/modules/sipcapture.so %{_libdir}/opensips/modules/sipmsgops.so @@ -886,12 +962,12 @@ fi %{_libdir}/opensips/modules/textops.so %{_libdir}/opensips/modules/tm.so %{_libdir}/opensips/modules/uac.so +%{_libdir}/opensips/modules/uac_auth.so %{_libdir}/opensips/modules/uac_redirect.so -%{_libdir}/opensips/modules/userblacklist.so +%{_libdir}/opensips/modules/uac_registrant.so %{_libdir}/opensips/modules/uri.so +%{_libdir}/opensips/modules/userblacklist.so %{_libdir}/opensips/modules/usrloc.so -%{_libdir}/opensips/modules/uac_auth.so -%{_libdir}/opensips/modules/uac_registrant.so %doc docdir/README.acc %doc docdir/README.alias_db @@ -902,17 +978,17 @@ fi %doc docdir/README.cachedb_local %doc docdir/README.cachedb_sql %doc docdir/README.call_control -%doc docdir/README.closeddial %doc docdir/README.cfgutils +%doc docdir/README.closeddial %doc docdir/README.db_cachedb %doc docdir/README.db_flatstore -%doc docdir/README.db_virtual %doc docdir/README.db_text -%doc docdir/README.dns_cache +%doc docdir/README.db_virtual %doc docdir/README.dialog %doc docdir/README.dialplan %doc docdir/README.dispatcher %doc docdir/README.diversion +%doc docdir/README.dns_cache %doc docdir/README.domain %doc docdir/README.domainpolicy %doc docdir/README.drouting @@ -930,13 +1006,12 @@ fi %doc docdir/README.mathops %doc docdir/README.maxfwd %doc docdir/README.mediaproxy -%doc docdir/README.mi_fifo %doc docdir/README.mi_datagram +%doc docdir/README.mi_fifo %doc docdir/README.mi_http %doc docdir/README.msilo -%doc docdir/README.nathelper %doc docdir/README.nat_traversal -%doc docdir/README.rtpproxy +%doc docdir/README.nathelper %doc docdir/README.options %doc docdir/README.path %doc docdir/README.pdt @@ -946,6 +1021,9 @@ fi %doc docdir/README.ratelimit %doc docdir/README.registrar %doc docdir/README.rr +%doc docdir/README.rtpengine +%doc docdir/README.rtpproxy +%doc docdir/README.script_helper %doc docdir/README.signaling %doc docdir/README.sipcapture %doc docdir/README.sipmsgops @@ -958,12 +1036,12 @@ fi %doc docdir/README.textops %doc docdir/README.tm %doc docdir/README.uac +%doc docdir/README.uac_auth %doc docdir/README.uac_redirect -%doc docdir/README.userblacklist +%doc docdir/README.uac_registrant %doc docdir/README.uri +%doc docdir/README.userblacklist %doc docdir/README.usrloc -%doc docdir/README.uac_auth -%doc docdir/README.uac_registrant %files aaa_radius %{_libdir}/opensips/modules/aaa_radius.so @@ -984,8 +1062,22 @@ fi %files b2bua %{_libdir}/opensips/modules/b2b_entities.so %{_libdir}/opensips/modules/b2b_logic.so +%{_libdir}/opensips/modules/b2b_sca.so %doc docdir/README.b2b_entities %doc docdir/README.b2b_logic +%doc docdir/README.b2b_sca + +%files cachedb_memcached +%{_libdir}/opensips/modules/cachedb_memcached.so +%doc docdir/README.cachedb_memcached + +%files cachedb_redis +%{_libdir}/opensips/modules/cachedb_redis.so +%doc docdir/README.cachedb_redis + +%files call_center +%{_libdir}/opensips/modules/call_center.so +%doc docdir/README.call_center %files carrierroute %{_libdir}/opensips/modules/carrierroute.so @@ -1009,6 +1101,57 @@ fi %{_libdir}/opensips/modules/db_http.so %doc docdir/README.db_http +%files db_mysql +%{_libdir}/opensips/modules/db_mysql.so +%{_libdir}/opensips/opensipsctl/opensipsctl.mysql +%{_libdir}/opensips/opensipsctl/opensipsdbctl.mysql +%dir %{_datadir}/opensips/mysql +%{_datadir}/opensips/mysql/*.sql +%doc docdir/README.db_mysql + +%if 0%{?_with_oracle} +%files db_oracle +%{_sbindir}/opensips_orasel +%{_libdir}/opensips/modules/db_oracle.so +%{_libdir}/opensips/opensipsctl/opensipsctl.oracle +%{_libdir}/opensips/opensipsctl/opensipsdbctl.oracle +%{_libdir}/opensips/opensipsctl/opensipsdbfunc.oracle +%dir %{_datadir}/opensips/oracle +%{_datadir}/opensips/oracle/* +%doc docdir/README.db_oracle +%endif + +%files db_perlvdb +%dir %{perl_vendorlib}/OpenSIPS/VDB +%dir %{perl_vendorlib}/OpenSIPS/VDB/Adapter +%{_libdir}/opensips/modules/db_perlvdb.so +%{perl_vendorlib}/OpenSIPS/VDB.pm +%{perl_vendorlib}/OpenSIPS/VDB/Adapter/AccountingSIPtrace.pm +%{perl_vendorlib}/OpenSIPS/VDB/Adapter/Alias.pm +%{perl_vendorlib}/OpenSIPS/VDB/Adapter/Auth.pm +%{perl_vendorlib}/OpenSIPS/VDB/Adapter/Describe.pm +%{perl_vendorlib}/OpenSIPS/VDB/Adapter/Speeddial.pm +%{perl_vendorlib}/OpenSIPS/VDB/Adapter/TableVersions.pm +%{perl_vendorlib}/OpenSIPS/VDB/Column.pm +%{perl_vendorlib}/OpenSIPS/VDB/Pair.pm +%{perl_vendorlib}/OpenSIPS/VDB/ReqCond.pm +%{perl_vendorlib}/OpenSIPS/VDB/Result.pm +%{perl_vendorlib}/OpenSIPS/VDB/VTab.pm +%{perl_vendorlib}/OpenSIPS/VDB/Value.pm +%doc docdir/README.db_perlvdb + +%files db_postgresql +%{_libdir}/opensips/modules/db_postgres.so +%{_libdir}/opensips/opensipsctl/opensipsctl.pgsql +%{_libdir}/opensips/opensipsctl/opensipsdbctl.pgsql +%dir %{_datadir}/opensips/postgres +%{_datadir}/opensips/postgres/*.sql +%doc docdir/README.db_postgres + +%files db_unixodbc +%{_libdir}/opensips/modules/db_unixodbc.so +%doc docdir/README.db_unixodbc + %files event_rabbitmq %{_libdir}/opensips/modules/event_rabbitmq.so %doc docdir/README.event_rabbitmq @@ -1037,34 +1180,22 @@ fi #%{_libdir}/opensips/modules/lua.so #%doc docdir/README.lua -%files memcached -%{_libdir}/opensips/modules/cachedb_memcached.so -%doc docdir/README.cachedb_memcached +%files mi_json +%{_libdir}/opensips/modules/mi_json.so +%doc docdir/README.mi_json + +%files mi_xmlrpc +%{_libdir}/opensips/modules/mi_xmlrpc.so +%doc docdir/README.mi_xmlrpc + +%files mi_xmlrpc_ng +%{_libdir}/opensips/modules/mi_xmlrpc_ng.so +%doc docdir/README.mi_xmlrpc_ng %files mmgeoip %{_libdir}/opensips/modules/mmgeoip.so %doc docdir/README.mmgeoip -%files mysql -%{_libdir}/opensips/modules/db_mysql.so -%{_libdir}/opensips/opensipsctl/opensipsctl.mysql -%{_libdir}/opensips/opensipsctl/opensipsdbctl.mysql -%dir %{_datadir}/opensips/mysql -%{_datadir}/opensips/mysql/*.sql -%doc docdir/README.db_mysql - -%if 0%{?_with_oracle} -%files oracle -%{_sbindir}/opensips_orasel -%{_libdir}/opensips/modules/db_oracle.so -%{_libdir}/opensips/opensipsctl/opensipsctl.oracle -%{_libdir}/opensips/opensipsctl/opensipsdbctl.oracle -%{_libdir}/opensips/opensipsctl/opensipsdbfunc.oracle -%dir %{_datadir}/opensips/oracle -%{_datadir}/opensips/oracle/* -%doc docdir/README.db_oracle -%endif - %files peering %{_libdir}/opensips/modules/peering.so %doc docdir/README.peering @@ -1083,38 +1214,11 @@ fi %{perl_vendorlib}/OpenSIPS/Utils/Debug.pm %doc docdir/README.perl -%files perlvdb -%dir %{perl_vendorlib}/OpenSIPS/VDB -%dir %{perl_vendorlib}/OpenSIPS/VDB/Adapter -%{_libdir}/opensips/modules/db_perlvdb.so -%{perl_vendorlib}/OpenSIPS/VDB.pm -%{perl_vendorlib}/OpenSIPS/VDB/Adapter/AccountingSIPtrace.pm -%{perl_vendorlib}/OpenSIPS/VDB/Adapter/Alias.pm -%{perl_vendorlib}/OpenSIPS/VDB/Adapter/Auth.pm -%{perl_vendorlib}/OpenSIPS/VDB/Adapter/Describe.pm -%{perl_vendorlib}/OpenSIPS/VDB/Adapter/Speeddial.pm -%{perl_vendorlib}/OpenSIPS/VDB/Adapter/TableVersions.pm -%{perl_vendorlib}/OpenSIPS/VDB/Column.pm -%{perl_vendorlib}/OpenSIPS/VDB/Pair.pm -%{perl_vendorlib}/OpenSIPS/VDB/ReqCond.pm -%{perl_vendorlib}/OpenSIPS/VDB/Result.pm -%{perl_vendorlib}/OpenSIPS/VDB/VTab.pm -%{perl_vendorlib}/OpenSIPS/VDB/Value.pm -%doc docdir/README.db_perlvdb - %files pi_http %{_libdir}/opensips/modules/pi_http.so %{_datadir}/opensips/pi_http/ %doc docdir/README.pi_http -%files postgresql -%{_libdir}/opensips/modules/db_postgres.so -%{_libdir}/opensips/opensipsctl/opensipsctl.pgsql -%{_libdir}/opensips/opensipsctl/opensipsdbctl.pgsql -%dir %{_datadir}/opensips/postgres -%{_datadir}/opensips/postgres/*.sql -%doc docdir/README.db_postgres - %files presence %{_libdir}/opensips/modules/presence.so %doc docdir/README.presence @@ -1165,10 +1269,6 @@ fi %files python %{_libdir}/opensips/modules/python.so -%files redis -%{_libdir}/opensips/modules/cachedb_redis.so -%doc docdir/README.cachedb_redis - %files regex %{_libdir}/opensips/modules/regex.so %doc docdir/README.regex @@ -1204,10 +1304,6 @@ fi %{_libdir}/opensips/modules/tlsops.so %doc docdir/README.tlsops -%files unixodbc -%{_libdir}/opensips/modules/db_unixodbc.so -%doc docdir/README.db_unixodbc - %files xcap %{_libdir}/opensips/modules/xcap.so %doc docdir/README.xcap @@ -1216,19 +1312,20 @@ fi %{_libdir}/opensips/modules/xcap_client.so %doc docdir/README.xcap_client -%files xmlrpc -%{_libdir}/opensips/modules/mi_xmlrpc.so -%doc docdir/README.mi_xmlrpc - -%files xmlrpc_ng -%{_libdir}/opensips/modules/mi_xmlrpc_ng.so -%doc docdir/README.mi_xmlrpc_ng - %files xmpp %{_libdir}/opensips/modules/xmpp.so %doc docdir/README.xmpp %changelog +* Sun Feb 21 2016 Peter Lemenkov - 1.11.6-1 +- Ver. 1.11.6 +- Removed compatibility with EPEL5 +- New module - b2b_sca +- New module - call_center +- New module - mi_json +- New module - rtpengine (experimental!) +- New module - script_helper + * Thu Feb 04 2016 Fedora Release Engineering - 1.10.5-9 - Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild diff --git a/sources b/sources index c7f4c08..d13bd08 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -b260118c1c5ba91c1134141c0a5c6359 opensips-1.10.5_src.tar.gz +ca27d699368ed53b41848501072c3f59 opensips-1.11.6.tar.gz