diff options
Diffstat (limited to 'net/ser/files/patch-modules__nathelper__nathelper.c')
-rw-r--r-- | net/ser/files/patch-modules__nathelper__nathelper.c | 1786 |
1 files changed, 0 insertions, 1786 deletions
diff --git a/net/ser/files/patch-modules__nathelper__nathelper.c b/net/ser/files/patch-modules__nathelper__nathelper.c deleted file mode 100644 index d85865e0eaa0..000000000000 --- a/net/ser/files/patch-modules__nathelper__nathelper.c +++ /dev/null @@ -1,1786 +0,0 @@ - -$FreeBSD$ - ---- modules/nathelper/nathelper.c.orig -+++ modules/nathelper/nathelper.c -@@ -110,14 +110,45 @@ - * - * 2004-03-22 Fix get_body position (should be called before get_callid) - * (andrei) -+ * - * 2004-03-24 Fix newport for null ip address case (e.g onhold re-INVITE) - * (andrei) -- * 2004-09-30 added received port != via port test (andrei) -+ * -+ * 2004-09-30 added received port != via port test (andrei) -+ * - * 2004-10-10 force_socket option introduced (jiri) -+ * 2004-12-21 support for multiple medias added (netch) -+ * 2005-01-18 proxying on protocol type (RTP/AVP, udp, udptl), not -+ * media type. (sobomax,netch) -+ * -+ * 2005-02-24 Added support for using more than one rtp proxy, in which -+ * case traffic will be distributed evenly among them. In addition, -+ * each such proxy can be assigned a weight, which will specify -+ * which share of the traffic should be placed to this particular -+ * proxy. -+ * -+ * Introduce failover mechanism, so that if SER detects that one -+ * of many proxies is no longer available it temporarily decreases -+ * its weight to 0, so that no traffic will be assigned to it. -+ * Such "disabled" proxies are periodically checked to see if they -+ * are back to normal in which case respective weight is restored -+ * resulting in traffic being sent to that proxy again. - * -+ * Those features can be enabled by specifying more than one "URI" -+ * in the rtpproxy_sock parameter, optionally followed by the weight, -+ * which if absent is assumed to be 1, for example: -+ * -+ * rtpproxy_sock="unix:/foo/bar=4 udp:1.2.3.4:3456=3 udp:5.6.7.8:5432=1" -+ * -+ * 2005-03-24 music-on-hold implemented (netch) -+ * -+ * 2007-04-23 Do NAT pinging in the separate dedicated process. It provides much -+ * better scalability than doing it in the main one. - */ - - #include "nhelpr_funcs.h" -+#include "nathelper.h" -+#include "../../action.h" - #include "../../flags.h" - #include "../../sr_module.h" - #include "../../dprint.h" -@@ -127,6 +158,7 @@ - #include "../../forward.h" - #include "../../mem/mem.h" - #include "../../parser/parse_from.h" -+#include "../../parser/parse_hostport.h" - #include "../../parser/parse_to.h" - #include "../../parser/parse_uri.h" - #include "../../parser/parser_f.h" -@@ -171,39 +203,32 @@ - #define NAT_UAC_TEST_S_1918 0x08 - #define NAT_UAC_TEST_RPORT 0x10 - --/* Handy macros */ --#define STR2IOVEC(sx, ix) {(ix).iov_base = (sx).s; (ix).iov_len = (sx).len;} -- - /* Supported version of the RTP proxy command protocol */ - #define SUP_CPROTOVER 20040107 -+/* Required additional version of the RTP proxy command protocol */ -+#define REQ_CPROTOVER "20050322" - #define CPORT "22222" - - static int nat_uac_test_f(struct sip_msg* msg, char* str1, char* str2); - static int fix_nated_contact_f(struct sip_msg *, char *, char *); - static int fix_nated_sdp_f(struct sip_msg *, char *, char *); --static int extract_mediaip(str *, str *, int *); --static int extract_mediaport(str *, str *); --static int alter_mediaip(struct sip_msg *, str *, str *, int, str *, int, int); --static int alter_mediaport(struct sip_msg *, str *, str *, str *, int); -+static int fixate_sdp_f(struct sip_msg *, char *, char *); - static char *gencookie(); --static int rtpp_test(int, int); --static char *send_rtpp_command(struct iovec *, int); -+static int rtpp_test(struct rtpp_node*, int, int); - static int unforce_rtp_proxy_f(struct sip_msg *, char *, char *); -+static int start_recording_f(struct sip_msg *, char *, char *); - static int force_rtp_proxy0_f(struct sip_msg *, char *, char *); - static int force_rtp_proxy1_f(struct sip_msg *, char *, char *); - static int force_rtp_proxy2_f(struct sip_msg *, char *, char *); - static int fix_nated_register_f(struct sip_msg *, char *, char *); - static int add_rcv_param_f(struct sip_msg *, char *, char *); -+static int rewrite_from_from_f(struct sip_msg *, char *, char *); - --static void timer(unsigned int, void *); - inline static int fixup_str2int(void**, int); - static int mod_init(void); -+static void mod_cleanup(void); - static int child_init(int); - --static usrloc_api_t ul; -- --static int cblen = 0; --static int natping_interval = 0; - struct socket_info* force_socket = 0; - - -@@ -218,27 +243,51 @@ - {NULL, 0, 0} - }; - --/* -- * If this parameter is set then the natpinger will ping only contacts -- * that have the NAT flag set in user location database -- */ --static int ping_nated_only = 0; --static const char sbuf[4] = {0, 0, 0, 0}; --static char *rtpproxy_sock = "unix:/var/run/rtpproxy.sock"; -+static str sup_ptypes[] = { -+ {.s = "udp", .len = 3}, -+ {.s = "udptl", .len = 5}, -+ {.s = "rtp/avp", .len = 7}, -+ {.s = NULL, .len = 0} -+}; -+ -+static char *rtpproxy_sock = "unix:/var/run/rtpproxy.sock"; /* list */ - static char *force_socket_str = 0; - static int rtpproxy_disable = 0; - static int rtpproxy_disable_tout = 60; - static int rtpproxy_retr = 5; - static int rtpproxy_tout = 1; --static int umode = 0; --static int controlfd; - static pid_t mypid; - static unsigned int myseqn = 0; --static int rcv_avp_no=42; -+static int rcv_avp_no = 42; -+char *pname_audio = "-"; -+char *pname_video = "-"; -+char *codecs_audio = "-"; -+char *codecs_video = "-"; -+ -+struct rtpp_head { -+ struct rtpp_node *rn_first; -+ struct rtpp_node *rn_last; -+}; -+ -+struct rtpp_node { -+ char *rn_url; /* unparsed, deletable */ -+ int rn_umode; -+ char *rn_address; /* substring of rn_url */ -+ int rn_fd; /* control fd */ -+ int rn_disabled; /* found unaccessible? */ -+ unsigned rn_weight; /* for load balancing */ -+ int rn_recheck_ticks; -+ struct rtpp_node *rn_next; -+}; -+ -+/* RTP proxy balancing list */ -+static struct rtpp_head rtpp_list; -+static int rtpp_node_count = 0; - - static cmd_export_t cmds[] = { - {"fix_nated_contact", fix_nated_contact_f, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, - {"fix_nated_sdp", fix_nated_sdp_f, 1, fixup_str2int, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE }, -+ {"fixate_sdp", fixate_sdp_f, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, - {"unforce_rtp_proxy", unforce_rtp_proxy_f, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE }, - {"force_rtp_proxy", force_rtp_proxy0_f, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, - {"force_rtp_proxy", force_rtp_proxy1_f, 1, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, -@@ -246,11 +295,17 @@ - {"nat_uac_test", nat_uac_test_f, 1, fixup_str2int, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE }, - {"fix_nated_register", fix_nated_register_f, 0, 0, REQUEST_ROUTE }, - {"add_rcv_param", add_rcv_param_f, 0, 0, REQUEST_ROUTE }, -+ {"rewrite_from_from", rewrite_from_from_f, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, -+ {"is_hold", is_hold_f, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, -+ {"start_moh", start_moh_f, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, -+ {"stop_moh", stop_moh_f, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, -+ {"start_recording", start_recording_f, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE }, - {0, 0, 0, 0, 0} - }; - - static param_export_t params[] = { - {"natping_interval", INT_PARAM, &natping_interval }, -+ {"natping_method", STR_PARAM, &natping_method }, - {"ping_nated_only", INT_PARAM, &ping_nated_only }, - {"rtpproxy_sock", STR_PARAM, &rtpproxy_sock }, - {"rtpproxy_disable", INT_PARAM, &rtpproxy_disable }, -@@ -259,6 +314,10 @@ - {"rtpproxy_tout", INT_PARAM, &rtpproxy_tout }, - {"received_avp", INT_PARAM, &rcv_avp_no }, - {"force_socket", STR_PARAM, &force_socket_str }, -+ {"pname_audio", STR_PARAM, &pname_audio }, -+ {"pname_video", STR_PARAM, &pname_video }, -+ {"codecs_audio", STR_PARAM, &codecs_audio }, -+ {"codecs_video", STR_PARAM, &codecs_video }, - {0, 0, 0} - }; - -@@ -268,7 +327,7 @@ - params, - mod_init, - 0, /* reply processing */ -- 0, /* destroy function */ -+ mod_cleanup, /* destroy function */ - 0, /* on_break */ - child_init - }; -@@ -277,8 +336,6 @@ - mod_init(void) - { - int i; -- char *cp; -- bind_usrloc_t bind_usrloc; - struct in_addr addr; - str socket_str; - -@@ -288,18 +345,9 @@ - force_socket=grep_sock_info(&socket_str,0,0); - } - -- if (natping_interval > 0) { -- bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0); -- if (!bind_usrloc) { -- LOG(L_ERR, "nathelper: Can't find usrloc module\n"); -- return -1; -- } -- -- if (bind_usrloc(&ul) < 0) { -- return -1; -- } -- -- register_timer(timer, NULL, natping_interval); -+ if (natpinger_init() < 0) { -+ LOG(L_ERR, "nathelper: natpinger_init() failed\n"); -+ return -1; - } - - /* Prepare 1918 networks list */ -@@ -309,83 +357,154 @@ - nets_1918[i].netaddr = ntohl(addr.s_addr) & nets_1918[i].mask; - } - -+ memset(&rtpp_list, 0, sizeof(rtpp_list)); -+ rtpp_node_count = 0; - if (rtpproxy_disable == 0) { -- /* Make rtpproxy_sock writable */ -- cp = pkg_malloc(strlen(rtpproxy_sock) + 1); -- if (cp == NULL) { -- LOG(L_ERR, "nathelper: Can't allocate memory\n"); -- return -1; -- } -- strcpy(cp, rtpproxy_sock); -- rtpproxy_sock = cp; -+ /* Make rtp proxies list. */ -+ char *p, *p1, *p2, *plim; - -- if (strncmp(rtpproxy_sock, "udp:", 4) == 0) { -- umode = 1; -- rtpproxy_sock += 4; -- } else if (strncmp(rtpproxy_sock, "udp6:", 5) == 0) { -- umode = 6; -- rtpproxy_sock += 5; -- } else if (strncmp(rtpproxy_sock, "unix:", 5) == 0) { -- umode = 0; -- rtpproxy_sock += 5; -+ p = rtpproxy_sock; -+ plim = p + strlen(p); -+ for(;;) { -+ struct rtpp_node *pnode; -+ int weight; -+ -+ weight = 1; -+ while (*p && isspace(*p)) -+ ++p; -+ if (p >= plim) -+ break; -+ p1 = p; -+ while (*p && !isspace(*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 = pkg_malloc(sizeof(struct rtpp_node)); -+ if (pnode == NULL) { -+ LOG(L_ERR, "nathelper: Can't allocate memory\n"); -+ return -1; -+ } -+ memset(pnode, 0, sizeof(*pnode)); -+ pnode->rn_recheck_ticks = 0; -+ pnode->rn_weight = weight; -+ pnode->rn_umode = 0; -+ pnode->rn_fd = -1; -+ pnode->rn_disabled = 0; -+ pnode->rn_url = pkg_malloc(p2 - p1 + 1); -+ if (pnode->rn_url == NULL) { -+ LOG(L_ERR, "nathelper: Can't allocate memory\n"); -+ return -1; -+ } -+ memmove(pnode->rn_url, p1, p2 - p1); -+ pnode->rn_url[p2 - p1] = 0; -+ if (rtpp_list.rn_first == NULL) { -+ rtpp_list.rn_first = pnode; -+ } else { -+ rtpp_list.rn_last->rn_next = pnode; -+ } -+ rtpp_list.rn_last = pnode; -+ ++rtpp_node_count; -+ /* Leave only address in rn_address */ -+ pnode->rn_address = pnode->rn_url; -+ if (strncmp(pnode->rn_address, "udp:", 4) == 0) { -+ pnode->rn_umode = 1; -+ pnode->rn_address += 4; -+ } else if (strncmp(pnode->rn_address, "udp6:", 5) == 0) { -+ pnode->rn_umode = 6; -+ pnode->rn_address += 5; -+ } else if (strncmp(pnode->rn_address, "unix:", 5) == 0) { -+ pnode->rn_umode = 0; -+ pnode->rn_address += 5; -+ } - } - } - - return 0; - } - -+static void -+mod_cleanup(void) -+{ -+ -+ natpinger_cleanup(); -+} -+ - static int - child_init(int rank) - { - int n; - char *cp; - struct addrinfo hints, *res; -+ struct rtpp_node *pnode; - -- if (rtpproxy_disable == 0) { -- mypid = getpid(); -- if (umode != 0) { -- cp = strrchr(rtpproxy_sock, ':'); -- if (cp != NULL) { -- *cp = '\0'; -- cp++; -- } -- if (cp == NULL || *cp == '\0') -- cp = CPORT; -+ if (natpinger_child_init(rank) < 0) -+ return -1; - -- memset(&hints, 0, sizeof(hints)); -- hints.ai_flags = 0; -- hints.ai_family = (umode == 6) ? AF_INET6 : AF_INET; -- hints.ai_socktype = SOCK_DGRAM; -- if ((n = getaddrinfo(rtpproxy_sock, cp, &hints, &res)) != 0) { -- LOG(L_ERR, "nathelper: getaddrinfo: %s\n", gai_strerror(n)); -- return -1; -- } -+ /* Iterate known RTP proxies - create sockets */ -+ mypid = getpid(); -+ for (pnode = rtpp_list.rn_first; pnode != NULL; pnode = pnode->rn_next) { -+ char *old_colon; - -- controlfd = socket((umode == 6) ? AF_INET6 : AF_INET, SOCK_DGRAM, 0); -- if (controlfd == -1) { -- LOG(L_ERR, "nathelper: can't create socket\n"); -- freeaddrinfo(res); -- return -1; -- } -+ if (pnode->rn_umode == 0) -+ goto rptest; -+ /* -+ * This is UDP or UDP6. Detect host and port; lookup host; -+ * do connect() in order to specify peer address -+ */ -+ old_colon = cp = strrchr(pnode->rn_address, ':'); -+ if (cp != NULL) { -+ old_colon = cp; -+ *cp = '\0'; -+ cp++; -+ } -+ if (cp == NULL || *cp == '\0') -+ cp = CPORT; - -- if (connect(controlfd, res->ai_addr, res->ai_addrlen) == -1) { -- LOG(L_ERR, "nathelper: can't connect to a RTP proxy\n"); -- close(controlfd); -- freeaddrinfo(res); -- return -1; -- } -+ 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(pnode->rn_address, cp, &hints, &res)) != 0) { -+ LOG(L_ERR, "nathelper: getaddrinfo: %s\n", gai_strerror(n)); -+ return -1; -+ } -+ if (old_colon) -+ *old_colon = ':'; /* restore rn_address */ -+ -+ pnode->rn_fd = socket((pnode->rn_umode == 6) -+ ? AF_INET6 : AF_INET, SOCK_DGRAM, 0); -+ if (pnode->rn_fd == -1) { -+ LOG(L_ERR, "nathelper: can't create socket\n"); - freeaddrinfo(res); -+ return -1; - } - -- rtpproxy_disable = rtpp_test(0, 1); -- } else { -- rtpproxy_disable_tout = -1; -+ if (connect(pnode->rn_fd, res->ai_addr, res->ai_addrlen) == -1) { -+ LOG(L_ERR, "nathelper: can't connect to a RTP proxy\n"); -+ close(pnode->rn_fd); -+ pnode->rn_fd = -1; -+ freeaddrinfo(res); -+ return -1; -+ } -+ freeaddrinfo(res); -+rptest: -+ pnode->rn_disabled = rtpp_test(pnode, 0, 1); - } - -+ if (rtpproxy_disable) -+ rtpproxy_disable_tout = -1; -+ - return 0; - } - --static int -+int - isnulladdr(str *sx, int pf) - { - char *cp; -@@ -440,7 +559,7 @@ - * assumes the to header is already parsed, so - * make sure it really is before calling this function - */ --static inline int -+int - get_to_tag(struct sip_msg* _m, str* _tag) - { - -@@ -464,7 +583,7 @@ - /* - * Extract tag from From header field of a request - */ --static inline int -+int - get_from_tag(struct sip_msg* _m, str* _tag) - { - -@@ -489,7 +608,7 @@ - * (so make sure it is, before calling this function or - * it might fail even if the message _has_ a callid) - */ --static inline int -+int - get_callid(struct sip_msg* _m, str* _cid) - { - -@@ -563,9 +682,13 @@ - if (anchor == 0) - return -1; - -- hostport = uri.host; -- if (uri.port.len > 0) -- hostport.len = uri.port.s + uri.port.len - uri.host.s; -+ if (uri.maddr_val.len == 0) { -+ hostport = uri.host; -+ if (uri.port.len > 0) -+ hostport.len = uri.port.s + uri.port.len - uri.host.s; -+ } else { -+ hostport = uri.maddr_val; -+ } - - cp = ip_addr2a(&msg->rcv.src_ip); - len = c->uri.len + strlen(cp) + 6 /* :port */ - hostport.len + 1; -@@ -652,11 +775,22 @@ - { - struct sip_uri uri; - contact_t* c; -+ char t; -+ str host; -+ short int port; - - if (get_contact_uri(msg, &uri, &c) == -1) - return -1; - -- return (is1918addr(&(uri.host)) == 1) ? 1 : 0; -+ if (uri.maddr_val.len == 0) -+ return (is1918addr(&(uri.host)) == 1) ? 1 : 0; -+ t = uri.maddr_val.s[uri.maddr_val.len]; -+ uri.maddr_val.s[uri.maddr_val.len] = '\0'; -+ parse_hostport(uri.maddr_val.s, &host, &port); -+ uri.maddr_val.s[uri.maddr_val.len] = t; -+ if (host.len <= 0) -+ return 0; -+ return (is1918addr(&host) == 1) ? 1 : 0; - } - - /* -@@ -756,8 +890,8 @@ - static int - fix_nated_sdp_f(struct sip_msg* msg, char* str1, char* str2) - { -- str body, body1, oldip, oldip1, newip; -- int level, pf, pf1; -+ str body, body1, oldip, newip; -+ int level, pf; - char *buf; - struct lump* anchor; - -@@ -804,37 +938,43 @@ - } - - if (level & FIX_MEDIP) { -- if (extract_mediaip(&body, &oldip, &pf) == -1) { -- LOG(L_ERR, "ERROR: fix_nated_sdp: can't extract media IP from the SDP\n"); -- goto finalize; -- } -- if (pf != AF_INET) { -- LOG(L_ERR, "ERROR: fix_nated_sdp: " -- "not an IPv4 address in SDP\n"); -- goto finalize; -- } -- body1.s = oldip.s + oldip.len; -- body1.len = body.s + body.len - body1.s; -- if (extract_mediaip(&body1, &oldip1, &pf1) == -1) { -- oldip1.len = 0; -- } -- if (oldip1.len > 0 && pf != pf1) { -- LOG(L_ERR, "ERROR: fix_nated_sdp: mismatching " -- "address families in SDP\n"); -- return -1; -- } -- -+ /* Iterate all c= and replace ips in them. */ -+ unsigned hasreplaced = 0; -+ int pf1 = 0; -+ str body2; -+ char* bodylimit = body.s + body.len; - newip.s = ip_addr2a(&msg->rcv.src_ip); - newip.len = strlen(newip.s); -- if (alter_mediaip(msg, &body, &oldip, pf, &newip, pf, -- 1) == -1) { -- LOG(L_ERR, "ERROR: fix_nated_sdp: can't alter media IP"); -- return -1; -+ body1 = body; -+ for(;;) { -+ if (extract_mediaip(&body1, &oldip, &pf) == -1) -+ break; -+ if (pf != AF_INET) { -+ LOG(L_ERR, "ERROR: fix_nated_sdp: " -+ "not an IPv4 address in SDP\n"); -+ goto finalize; -+ } -+ if (!pf1) -+ pf1 = pf; -+ else if (pf != pf1) { -+ LOG(L_ERR, "ERROR: fix_nated_sdp: mismatching " -+ "address families in SDP\n"); -+ return -1; -+ } -+ body2.s = oldip.s + oldip.len; -+ body2.len = bodylimit - body2.s; -+ if (alter_mediaip(msg, &body1, &oldip, pf, &newip, pf, -+ 1) == -1) -+ { -+ LOG(L_ERR, "ERROR: fix_nated_sdp: can't alter media IP"); -+ return -1; -+ } -+ hasreplaced = 1; -+ body1 = body2; - } -- if (oldip1.len > 0 && alter_mediaip(msg, &body, &oldip1, pf1, -- &newip, pf, 0) == -1) { -- LOG(L_ERR, "ERROR: fix_nated_sdp: can't alter media IP"); -- return -1; -+ if (!hasreplaced) { -+ LOG(L_ERR, "ERROR: fix_nated_sdp: can't extract media IP from the SDP\n"); -+ goto finalize; - } - } - -@@ -842,7 +982,7 @@ - return 1; - } - --static int -+int - extract_mediaip(str *body, str *mediaip, int *pf) - { - char *cp, *cp1; -@@ -856,7 +996,7 @@ - cp = cp1 + 2; - } - if (cp1 == NULL) { -- LOG(L_DBG, "ERROR: extract_mediaip: no `c=' in SDP\n"); -+ LOG(L_ERR, "ERROR: extract_mediaip: no `c=' in SDP\n"); - return -1; - } - mediaip->s = cp1 + 2; -@@ -898,11 +1038,12 @@ - return 1; - } - --static int -+int - extract_mediaport(str *body, str *mediaport) - { - char *cp, *cp1; -- int len; -+ int len, i; -+ str ptype; - - cp1 = NULL; - for (cp = body->s; (len = body->s + body->len - cp) > 0;) { -@@ -915,32 +1056,62 @@ - LOG(L_ERR, "ERROR: extract_mediaport: no `m=' in SDP\n"); - return -1; - } -- mediaport->s = cp1 + 2; -+ mediaport->s = cp1 + 2; /* skip `m=' */ - mediaport->len = eat_line(mediaport->s, body->s + body->len - - mediaport->s) - mediaport->s; - trim_len(mediaport->len, mediaport->s, *mediaport); - -- if (mediaport->len > 6 && memcmp(mediaport->s, "audio", 5) == 0 && -- isspace((int)mediaport->s[5])) { -- mediaport->s += 5; -- mediaport->len -= 5; -- } else if (mediaport->len > 12 && memcmp(mediaport->s, "application", 11) == 0 && -- isspace((int)mediaport->s[11])) { -- mediaport->s += 11; -- mediaport->len -= 11; -- } else { -- LOG(L_ERR, "ERROR: extract_mediaport: can't parse `m=' in SDP\n"); -+ /* Skip media supertype and spaces after it */ -+ cp = eat_token_end(mediaport->s, mediaport->s + mediaport->len); -+ mediaport->len -= cp - mediaport->s; -+ if (mediaport->len <= 0 || cp == mediaport->s) { -+ LOG(L_ERR, "ERROR: extract_mediaport: no port in `m='\n"); - return -1; - } -+ mediaport->s = cp; - cp = eat_space_end(mediaport->s, mediaport->s + mediaport->len); -- mediaport->len = eat_token_end(cp, mediaport->s + mediaport->len) - cp; -+ mediaport->len -= cp - mediaport->s; -+ if (mediaport->len <= 0 || cp == mediaport->s) { -+ LOG(L_ERR, "ERROR: extract_mediaport: no port in `m='\n"); -+ return -1; -+ } -+ /* Extract port */ - mediaport->s = cp; -- return 1; -+ cp = eat_token_end(mediaport->s, mediaport->s + mediaport->len); -+ ptype.len = mediaport->len - (cp - mediaport->s); -+ if (ptype.len <= 0 || cp == mediaport->s) { -+ LOG(L_ERR, "ERROR: extract_mediaport: no port in `m='\n"); -+ return -1; -+ } -+ ptype.s = cp; -+ mediaport->len = cp - mediaport->s; -+ /* Skip spaces after port */ -+ cp = eat_space_end(ptype.s, ptype.s + ptype.len); -+ ptype.len -= cp - ptype.s; -+ if (ptype.len <= 0 || cp == ptype.s) { -+ LOG(L_ERR, "ERROR: extract_mediaport: no protocol type in `m='\n"); -+ return -1; -+ } -+ /* Extract protocol type */ -+ ptype.s = cp; -+ cp = eat_token_end(ptype.s, ptype.s + ptype.len); -+ if (cp == ptype.s) { -+ LOG(L_ERR, "ERROR: extract_mediaport: no protocol type in `m='\n"); -+ return -1; -+ } -+ ptype.len = cp - ptype.s; -+ -+ for (i = 0; sup_ptypes[i].s != NULL; i++) -+ if (ptype.len == sup_ptypes[i].len && -+ strncasecmp(ptype.s, sup_ptypes[i].s, ptype.len) == 0) -+ return 0; -+ /* Unproxyable protocol type. Generally it isn't error. */ -+ return -1; - } - --static int -+int - alter_mediaip(struct sip_msg *msg, str *body, str *oldip, int oldpf, -- str *newip, int newpf, int preserve) -+ str *newip, int newpf, int flags) - { - char *buf; - int offset; -@@ -948,7 +1119,7 @@ - str omip, nip, oip; - - /* check that updating mediaip is really necessary */ -- if (oldpf == newpf && isnulladdr(oldip, oldpf)) -+ if (oldpf == newpf && isnulladdr(oldip, oldpf) && !(flags & 2)) - return 0; - if (newip->len == oldip->len && - memcmp(newip->s, oldip->s, newip->len) == 0) -@@ -961,7 +1132,7 @@ - * another request comes. - */ - #if 0 -- /* disabled: -+ /* disabled: - * - alter_mediaip is called twice if 2 c= lines are present - * in the sdp (and we want to allow it) - * - the message flags are propagated in the on_reply_route -@@ -976,7 +1147,7 @@ - } - #endif - -- if (preserve != 0) { -+ if ((flags & 1) != 0) { - anchor = anchor_lump(msg, body->s + body->len - msg->buf, 0, 0); - if (anchor == NULL) { - LOG(L_ERR, "ERROR: alter_mediaip: anchor_lump failed\n"); -@@ -1052,7 +1223,7 @@ - return 0; - } - --static int -+int - alter_mediaport(struct sip_msg *msg, str *body, str *oldport, str *newport, - int preserve) - { -@@ -1128,6 +1299,161 @@ - return 0; - } - -+/* -+ * Finds specified text in area [*pp...bodylimit) at line beginning. -+ * Returns pointer to text, updates *pp to position after it. -+ */ -+ -+static char* -+find_sdp_text_bol(char **pp, char *plimit, char *text, size_t textlen) -+{ -+ /* Find text at beginning of line */ -+ if (*pp == NULL) -+ return NULL; -+ for(;;) { -+ char* p; -+ if (*pp >= plimit) -+ return NULL; -+ if (!(p = ser_memmem(*pp, text, plimit - *pp, textlen))) { -+ *pp = plimit; -+ return NULL; -+ } -+ *pp = p + 1; -+ if (p[-1] != '\n' && p[-1] != '\r') -+ continue; -+ return p; -+ } -+ /*UNREACHED*/ -+ return NULL; -+} -+ -+static int -+fixate_sdp_f(struct sip_msg* msg, char* str1, char* str2) -+{ -+ char *cp; -+ int newpf; -+ str body, newip, newport, dest, oldip, oldport; -+ struct sip_uri ruri; -+ struct hdr_field *hdr; -+ struct via_body *rvia; -+ char *bodylimit, *v1p, *v2p, *m1p, *m2p, *c1p, *c2p; -+ char *om_ip_pos, *om_port_pos; -+ int c1_altered; -+ -+ if (msg->first_line.type == SIP_REQUEST && -+ msg->first_line.u.request.method_value == METHOD_INVITE) { -+ LOG(L_ERR, "DEBUG: fixate_sdp: request\n"); -+ if (msg->parsed_uri_ok) { -+ dest = msg->parsed_uri.host; -+ } else { -+ if (parse_uri(msg->new_uri.s, msg->new_uri.len, &ruri) < 0) { -+ LOG(L_ERR, "ERROR: fixate_sdp: can't parse request uri\n"); -+ return -1; -+ } -+ dest = ruri.host; -+ } -+ } else if (msg->first_line.type == SIP_REPLY) { -+ LOG(L_ERR, "DEBUG: fixate_sdp: reply\n"); -+ rvia = NULL; -+ for (hdr=msg->headers; hdr; hdr=hdr->next) { -+ if (hdr->type == HDR_VIA) -+ rvia = hdr->parsed; -+ } -+ if (rvia == NULL) { -+ LOG(L_ERR, "ERROR: fixate_sdp: no or incorrect Via in reply\n"); -+ return -1; -+ } -+ if (rvia->received != NULL) { -+ dest = rvia->received->value; -+ } else { -+ dest = rvia->host; -+ } -+ } else { -+ return -1; -+ } -+ -+ if (extract_body(msg, &body) == -1 || body.len == 0) { -+ LOG(L_ERR, "ERROR: fixate_sdp: can't extract body " -+ "from the message\n"); -+ return -1; -+ } -+ bodylimit = body.s + body.len; -+ v2p = v1p = find_sdp_line(body.s, bodylimit, 'v'); -+ if (!v1p) { -+ LOG(L_ERR, "fixate_sdp: no sessions found\n"); -+ return -1; -+ } -+ om_ip_pos = body.s; -+ om_port_pos = body.s; -+ for(;;) { -+ v1p = v2p; -+ if (v1p == NULL || v1p >= bodylimit) -+ break; -+ v2p = find_next_sdp_line(v1p, bodylimit, 'v', bodylimit); -+ m2p = m1p = find_sdp_line(v1p, v2p, 'm'); -+ c1p = find_sdp_line(v1p, v2p, 'c'); -+ c1_altered = 0; -+ if (!m1p) { -+ LOG(L_ERR, "fixate_sdp: session without media\n"); -+ return -1; -+ } -+ for(;;) { -+ str tmpstr1; -+ m1p = m2p; -+ if (m1p == NULL || m1p >= v2p) -+ break; -+ m2p = find_next_sdp_line(m1p, v2p, 'm', v2p); -+ c2p = find_sdp_line(m1p, m2p, 'c'); -+ /* Set old port and IP. Order doesn't matter. */ -+ tmpstr1.s = m1p; -+ tmpstr1.len = bodylimit - m1p; -+ if (extract_mediaport(&tmpstr1, &newport) == -1) { -+ LOG(L_ERR, "ERROR: fixate_sdp: can't extract media port from the SDP\n"); -+ return -1; -+ } -+ cp = find_sdp_text_bol(&om_port_pos, bodylimit, AOLDMEDPRT, AOLDMEDPRT_LEN); -+ if (cp) { -+ oldport.s = cp + AOLDMEDPRT_LEN; -+ oldport.len = eat_line(oldport.s, bodylimit - oldport.s) - oldport.s; -+ trim_len(oldport.len, oldport.s, oldport); -+ if (oldport.len != 0 && -+ alter_mediaport(msg, &body, &newport, &oldport, 0) == -1) -+ return -1; -+ } -+ if (c2p || !c1_altered) { -+ tmpstr1.s = c2p ? c2p : c1p; -+ tmpstr1.len = bodylimit - tmpstr1.s; -+ if (extract_mediaip(&tmpstr1, &newip, &newpf) == -1) { -+ LOG(L_ERR, "ERROR: fixate_sdp: can't extract media IP from the SDP\n"); -+ return -1; -+ } -+ if (newip.len != dest.len || memcmp(newip.s, dest.s, dest.len) != 0 || -+ isnulladdr(&newip, newpf)) -+ return -1; -+ cp = find_sdp_text_bol(&om_ip_pos, bodylimit, AOLDMEDIP, AOLDMEDIP_LEN); -+ if (cp) { -+ oldip.s = cp + AOLDMEDIP_LEN; -+ oldip.len = eat_line(oldip.s, bodylimit - oldip.s) - oldip.s; -+ trim_len(oldip.len, oldip.s, oldip); -+ } -+ if (newip.len == oldip.len && -+ memcmp(newip.s, oldip.s, newip.len) == 0) -+ oldip.len = 0; -+ -+ if (oldip.len != 0) { -+ if (alter_mediaip(msg, &body, &newip, newpf, &oldip, AF_INET, -+ 0) == -1) -+ return -1; -+ } -+ if (!c2p) -+ c1_altered = 1; -+ } /* if rewrite IP */ -+ } /* medias */ -+ } /* sessions */ -+ -+ return 1; -+} -+ - static char * - gencookie() - { -@@ -1139,45 +1465,58 @@ - } - - static int --rtpp_test(int isdisabled, int force) -+rtpp_test(struct rtpp_node *node, int isdisabled, int force) - { - int rtpp_ver; -- static int recheck_ticks = 0; - char *cp; - struct iovec v[2] = {{NULL, 0}, {"V", 1}}; -+ struct iovec vf[4] = {{NULL, 0}, {"VF", 2}, {" ", 1}, -+ {REQ_CPROTOVER, 8}}; - - if (force == 0) { - if (isdisabled == 0) - return 0; -- if (recheck_ticks > get_ticks()) -+ if (node->rn_recheck_ticks > get_ticks()) - return 1; - } -- cp = send_rtpp_command(v, 2); -- if (cp == NULL) { -- LOG(L_WARN,"WARNING: rtpp_test: can't get version of " -- "the RTP proxy\n"); -- } else { -+ do { -+ cp = send_rtpp_command(node, v, 2); -+ if (cp == NULL) { -+ LOG(L_WARN,"WARNING: rtpp_test: can't get version of " -+ "the RTP proxy\n"); -+ break; -+ } - rtpp_ver = atoi(cp); -- if (rtpp_ver == SUP_CPROTOVER) { -- LOG(L_INFO, "rtpp_test: RTP proxy found, support for " -- "it %senabled\n", force == 0 ? "re-" : ""); -- return 0; -+ if (rtpp_ver != SUP_CPROTOVER) { -+ LOG(L_WARN, "WARNING: rtpp_test: unsupported " -+ "version of RTP proxy <%s> found: %d supported, " -+ "%d present\n", node->rn_url, -+ SUP_CPROTOVER, rtpp_ver); -+ break; - } -- LOG(L_WARN, "WARNING: rtpp_test: unsupported " -- "version of RTP proxy found: %d supported, " -- "%d present\n", SUP_CPROTOVER, rtpp_ver); -- } -- LOG(L_WARN, "WARNING: rtpp_test: support for RTP proxy " -- "has been disabled%s\n", -+ cp = send_rtpp_command(node, vf, 4); -+ if (cp[0] == 'E' || atoi(cp) != 1) { -+ LOG(L_WARN, "WARNING: rtpp_test: of RTP proxy <%s>" -+ "doesn't support required protocol version %s\n", -+ node->rn_url, REQ_CPROTOVER); -+ break; -+ } -+ LOG(L_INFO, "rtpp_test: RTP proxy <%s> found, support for " -+ "it %senabled\n", -+ node->rn_url, force == 0 ? "re-" : ""); -+ return 0; -+ } while(0); -+ LOG(L_WARN, "WARNING: rtpp_test: support for RTP proxy <%s>" -+ "has been disabled%s\n", node->rn_url, - rtpproxy_disable_tout < 0 ? "" : " temporarily"); - if (rtpproxy_disable_tout >= 0) -- recheck_ticks = get_ticks() + rtpproxy_disable_tout; -+ node->rn_recheck_ticks = get_ticks() + rtpproxy_disable_tout; - - return 1; - } - --static char * --send_rtpp_command(struct iovec *v, int vcnt) -+char * -+send_rtpp_command(struct rtpp_node *node, struct iovec *v, int vcnt) - { - struct sockaddr_un addr; - int fd, len, i; -@@ -1187,10 +1526,10 @@ - - len = 0; - cp = buf; -- if (umode == 0) { -+ if (node->rn_umode == 0) { - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_LOCAL; -- strncpy(addr.sun_path, rtpproxy_sock, -+ strncpy(addr.sun_path, node->rn_address, - sizeof(addr.sun_path) - 1); - #ifdef HAVE_SOCKADDR_SA_LEN - addr.sun_len = strlen(addr.sun_path); -@@ -1199,12 +1538,12 @@ - fd = socket(AF_LOCAL, SOCK_STREAM, 0); - if (fd < 0) { - LOG(L_ERR, "ERROR: send_rtpp_command: can't create socket\n"); -- return NULL; -+ goto badproxy; - } - if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - close(fd); - LOG(L_ERR, "ERROR: send_rtpp_command: can't connect to RTP proxy\n"); -- return NULL; -+ goto badproxy; - } - - do { -@@ -1213,7 +1552,7 @@ - if (len <= 0) { - close(fd); - LOG(L_ERR, "ERROR: send_rtpp_command: can't send command to a RTP proxy\n"); -- return NULL; -+ goto badproxy; - } - do { - len = read(fd, buf, sizeof(buf) - 1); -@@ -1221,38 +1560,38 @@ - close(fd); - if (len <= 0) { - LOG(L_ERR, "ERROR: send_rtpp_command: can't read reply from a RTP proxy\n"); -- return NULL; -+ goto badproxy; - } - } else { -- fds[0].fd = controlfd; -+ fds[0].fd = node->rn_fd; - fds[0].events = POLLIN; - fds[0].revents = 0; - /* Drain input buffer */ - while ((poll(fds, 1, 0) == 1) && - ((fds[0].revents & POLLIN) != 0)) { -- recv(controlfd, buf, sizeof(buf) - 1, 0); -+ recv(node->rn_fd, 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 < rtpproxy_retr; i++) { - do { -- len = writev(controlfd, v, vcnt); -+ len = writev(node->rn_fd, v, vcnt); - } while (len == -1 && (errno == EINTR || errno == ENOBUFS)); - if (len <= 0) { - LOG(L_ERR, "ERROR: send_rtpp_command: " - "can't send command to a RTP proxy\n"); -- return NULL; -+ goto badproxy; - } - while ((poll(fds, 1, rtpproxy_tout * 1000) == 1) && - (fds[0].revents & POLLIN) != 0) { - do { -- len = recv(controlfd, buf, sizeof(buf) - 1, 0); -+ len = recv(node->rn_fd, buf, sizeof(buf) - 1, 0); - } while (len == -1 && errno == EINTR); - if (len <= 0) { - LOG(L_ERR, "ERROR: send_rtpp_command: " - "can't read reply from a RTP proxy\n"); -- return NULL; -+ goto badproxy; - } - if (len >= (v[0].iov_len - 1) && - memcmp(buf, v[0].iov_base, (v[0].iov_len - 1)) == 0) { -@@ -1270,28 +1609,97 @@ - if (i == rtpproxy_retr) { - LOG(L_ERR, "ERROR: send_rtpp_command: " - "timeout waiting reply from a RTP proxy\n"); -- return NULL; -+ goto badproxy; - } - } - - out: - cp[len] = '\0'; - return cp; -+badproxy: -+ LOG(L_ERR, "send_rtpp_command(): proxy <%s> does not responding, disable it\n", node->rn_url); -+ node->rn_disabled = 1; -+ node->rn_recheck_ticks = get_ticks() + rtpproxy_disable_tout; -+ return NULL; -+} -+ -+/* -+ * 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. -+ */ -+struct rtpp_node * -+select_rtpp_node(str callid, int do_test) -+{ -+ unsigned sum, sumcut, weight_sum; -+ struct rtpp_node* node; -+ int was_forced; -+ -+ /* Most popular case: 1 proxy, nothing to calculate */ -+ if (rtpp_node_count == 1) { -+ node = rtpp_list.rn_first; -+ 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 = rtpp_list.rn_first; node != NULL; node = node->rn_next) { -+ if (node->rn_disabled) { -+ /* Try to enable if it's time to try. */ -+ if (node->rn_recheck_ticks <= get_ticks()) -+ node->rn_disabled = rtpp_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 = rtpp_list.rn_first; node != NULL; node = node->rn_next) { -+ node->rn_disabled = rtpp_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 = rtpp_list.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 = rtpp_test(node, node->rn_disabled, 0); -+ if (node->rn_disabled) -+ goto retry; -+ } -+ return node; - } - - static int - unforce_rtp_proxy_f(struct sip_msg* msg, char* str1, char* str2) - { - str callid, from_tag, to_tag; -+ struct rtpp_node *node; - struct iovec v[1 + 4 + 3] = {{NULL, 0}, {"D", 1}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}}; - /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 1 */ - -- rtpproxy_disable = rtpp_test(rtpproxy_disable, 0); -- if (rtpproxy_disable != 0) { -- LOG(L_ERR, "ERROR: unforce_rtp_proxy: support for RTP proxy " -- "is disabled\n"); -- return -1; -- } - if (get_callid(msg, &callid) == -1 || callid.len == 0) { - LOG(L_ERR, "ERROR: unforce_rtp_proxy: can't get Call-Id field\n"); - return -1; -@@ -1307,29 +1715,185 @@ - STR2IOVEC(callid, v[3]); - STR2IOVEC(from_tag, v[5]); - STR2IOVEC(to_tag, v[7]); -- send_rtpp_command(v, (to_tag.len > 0) ? 8 : 6); -+ node = select_rtpp_node(callid, 1); -+ if (!node) { -+ LOG(L_ERR, "ERROR: unforce_rtp_proxy: no available proxies\n"); -+ return -1; -+ } -+ send_rtpp_command(node, v, (to_tag.len > 0) ? 8 : 6); -+ -+ return 1; -+} -+ -+static int -+start_recording_f(struct sip_msg* msg, char* str1, char* str2) -+{ -+ int nitems; -+ str callid, from_tag, to_tag; -+ struct rtpp_node *node; -+ struct iovec v[1 + 4 + 3] = {{NULL, 0}, {"R", 1}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}}; -+ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */ /* 6 */ /* 1 */ -+ -+ if (get_callid(msg, &callid) == -1 || callid.len == 0) { -+ LOG(L_ERR, "ERROR: start_recording: can't get Call-Id field\n"); -+ return -1; -+ } -+ if (get_to_tag(msg, &to_tag) == -1) { -+ LOG(L_ERR, "ERROR: start_recording: can't get To tag\n"); -+ return -1; -+ } -+ if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) { -+ LOG(L_ERR, "ERROR: start_recording: can't get From tag\n"); -+ return -1; -+ } -+ STR2IOVEC(callid, v[3]); -+ STR2IOVEC(from_tag, v[5]); -+ STR2IOVEC(to_tag, v[7]); -+ node = select_rtpp_node(callid, 1); -+ if (!node) { -+ LOG(L_ERR, "ERROR: start_recording: no available proxies\n"); -+ return -1; -+ } -+ nitems = 8; -+ if (msg->first_line.type == SIP_REPLY) { -+ if (to_tag.len == 0) -+ return -1; -+ STR2IOVEC(to_tag, v[5]); -+ STR2IOVEC(from_tag, v[7]); -+ } else { -+ STR2IOVEC(from_tag, v[5]); -+ STR2IOVEC(to_tag, v[7]); -+ if (to_tag.len <= 0) -+ nitems = 6; -+ } -+ send_rtpp_command(node, v, nitems); -+ -+ return 1; -+} -+ -+/* -+ * Auxiliary for some functions. -+ * Returns pointer to first character of found line, or NULL if no such line. -+ */ -+ -+char* -+find_sdp_line(char *p, char *plimit, char linechar) -+{ -+ static char linehead[3] = "x="; -+ char *cp, *cp1; -+ linehead[0] = linechar; -+ /* Iterate thru body */ -+ cp = p; -+ for (;;) { -+ if (cp >= plimit) -+ return NULL; -+ cp1 = ser_memmem(cp, linehead, plimit-cp, 2); -+ if (cp1 == NULL) -+ return NULL; -+ /* -+ * As it is body, we assume it has previous line and we can -+ * lookup previous character. -+ */ -+ if (cp1[-1] == '\n' || cp1[-1] == '\r') -+ return cp1; -+ /* -+ * Having such data, but not at line beginning. -+ * Skip them and reiterate. ser_memmem() will find next -+ * occurrence. -+ */ -+ if (plimit - cp1 < 2) -+ return NULL; -+ cp = cp1 + 2; -+ } -+ /*UNREACHED*/ -+ return NULL; -+} -+ -+/* This function assumes p points to a line of requested type. */ - -+char* -+find_next_sdp_line(char *p, char *plimit, char linechar, char *defptr) -+{ -+ char* t; -+ if (p >= plimit || plimit - p < 3) -+ return defptr; -+ t = find_sdp_line(p + 2, plimit, linechar); -+ return t ? t : defptr; -+} -+ -+static int -+alter_line(struct sip_msg *msg, str *where, str *what) -+{ -+ struct lump *anchor; -+ anchor = del_lump(msg, where->s - msg->buf, where->len, 0); -+ if (!anchor) { -+ LOG(L_ERR, "del_lump() failed\n"); -+ return 0; -+ } -+ if (insert_new_lump_after(anchor, what->s, what->len, 0) == 0) { -+ LOG(L_ERR, "insert_new_lump_after() failed\n"); -+ return 0; -+ } - return 1; - } - -+/* -+ * The following macro is used in force_rtp_proxy2_f() and twice -+ * in start_moh() -+ */ -+ -+#define PARSE_PROXY_REPLY \ -+ do { \ -+ argc = 0; \ -+ memset(argv, 0, sizeof(argv)); \ -+ cpend=cp+strlen(cp); \ -+ next=eat_token_end(cp, cpend); \ -+ for (ap = argv; cp<cpend; cp=next+1, next=eat_token_end(cp, cpend)){ \ -+ *next=0; \ -+ if (*cp != '\0') { \ -+ *ap=cp; \ -+ argc++; \ -+ if ((char*)++ap >= ((char*)argv+sizeof(argv))) \ -+ break; \ -+ } \ -+ } \ -+ } while(0) -+ - static int - force_rtp_proxy2_f(struct sip_msg* msg, char* str1, char* str2) - { -- str body, body1, oldport, oldip, oldip1, newport, newip; -- str callid, from_tag, to_tag, tmp; -+ str body, body1, oldport, oldip, newport, newip; -+ str callid, from_tag, to_tag; - int create, port, len, asymmetric, flookup, argc, proxied, real; -+ int create1; - int oidx, pf, pf1, force; - char opts[16]; - char *cp, *cp1; - char *cpend, *next; - char **ap, *argv[10]; - struct lump* anchor; -- struct iovec v[1 + 6 + 5] = {{NULL, 0}, {NULL, 0}, {" ", 1}, {NULL, 0}, -- {" ", 1}, {NULL, 7}, {" ", 1}, {NULL, 1}, {" ", 1}, {NULL, 0}, -- {" ", 1}, {NULL, 0}}; -- /* 1 */ /* 2 */ /* 3 */ /* 4 */ -- /* 5 */ /* 6 */ /* 7 */ /* 8 */ /* 9 */ /* 10 */ -- /* 11 */ -+ struct rtpp_node *node; -+ struct iovec v[14] = { -+ {NULL, 0}, /* command */ -+ {NULL, 0}, /* options */ -+ {" ", 1}, /* separator */ -+ {NULL, 0}, /* callid */ -+ {" ", 1}, /* separator */ -+ {NULL, 7}, /* newip */ -+ {" ", 1}, /* separator */ -+ {NULL, 1}, /* oldport */ -+ {" ", 1}, /* separator */ -+ {NULL, 0}, /* from_tag */ -+ {";", 1}, /* separator */ -+ {NULL, 0}, /* medianum */ -+ {" ", 1}, /* separator */ -+ {NULL, 0} /* to_tag */ -+ }; -+ char *v1p, *v2p, *c1p, *c2p, *m1p, *m2p, *bodylimit; -+ char medianum_buf[20]; -+ int medianum, media_multi; -+ str medianum_str, tmpstr1; -+ int c1_altered; - - v[1].iov_base=opts; - asymmetric = flookup = force = real = 0; -@@ -1374,13 +1938,6 @@ - } - } - -- rtpproxy_disable = rtpp_test(rtpproxy_disable, 0); -- if (rtpproxy_disable != 0) { -- LOG(L_ERR, "ERROR: force_rtp_proxy2: support for RTP proxy " -- "is disabled\n"); -- return -1; -- } -- - if (msg->first_line.type == SIP_REQUEST && - msg->first_line.u.request.method_value == METHOD_INVITE) { - create = 1; -@@ -1409,14 +1966,7 @@ - LOG(L_ERR, "ERROR: force_rtp_proxy2: can't get From tag\n"); - return -1; - } -- if (flookup != 0) { -- if (create == 0 || to_tag.len == 0) -- return -1; -- create = 0; -- tmp = from_tag; -- from_tag = to_tag; -- to_tag = tmp; -- } -+ - proxied = 0; - for (cp = body.s; (len = body.s + body.len - cp) >= ANORTPPROXY_LEN;) { - cp1 = ser_memmem(cp, ANORTPPROXY, len, ANORTPPROXY_LEN); -@@ -1430,88 +1980,207 @@ - } - if (proxied != 0 && force == 0) - return -1; -- if (extract_mediaip(&body, &oldip, &pf) == -1) { -- LOG(L_ERR, "ERROR: force_rtp_proxy2: can't extract media IP " -- "from the message\n"); -- return -1; -- } -- if (asymmetric != 0 || real != 0) { -- newip = oldip; -- } else { -- newip.s = ip_addr2a(&msg->rcv.src_ip); -- newip.len = strlen(newip.s); -- } -- body1.s = oldip.s + oldip.len; -- body1.len = body.s + body.len - body1.s; -- if (extract_mediaip(&body1, &oldip1, &pf1) == -1) { -- oldip1.len = 0; -- } -- if (oldip1.len > 0 && pf != pf1) { -- LOG(L_ERR, "ERROR: force_rtp_proxy2: mismatching address " -- "families in SDP\n"); -- return -1; -- } -- if (extract_mediaport(&body, &oldport) == -1) { -- LOG(L_ERR, "ERROR: force_rtp_proxy2: can't extract media port " -- "from the message\n"); -+ /* -+ * Parsing of SDP body. -+ * It can contain a few session descriptions (each start with -+ * "v=" line), and each session may contain a few media descriptions -+ * (each start with "m=" line). -+ * We have to change ports in "m=", and also change IP addresses in -+ * "c=" which can be placed either in session header (fallback for -+ * all medias) or media description. -+ * Ports should be allocated for any media. IPs all should be changed -+ * to the same value (RTP proxy IP), so we can change all "c=" -+ * unconditionally. -+ */ -+ bodylimit = body.s + body.len; -+ v1p = find_sdp_line(body.s, bodylimit, 'v'); -+ if (v1p == NULL) { -+ LOG(L_ERR, "ERROR: force_rtp_proxy2: no sessions in SDP\n"); - return -1; - } -- if (pf == AF_INET6) { -- opts[oidx] = '6'; -- oidx++; -- } -- opts[0] = (create == 0) ? 'L' : 'U'; -- v[1].iov_len = oidx; -- STR2IOVEC(callid, v[3]); -- STR2IOVEC(newip, v[5]); -- STR2IOVEC(oldport, v[7]); -- STR2IOVEC(from_tag, v[9]); -- STR2IOVEC(to_tag, v[11]); -- cp = send_rtpp_command(v, (to_tag.len > 0) ? 12 : 10); -- if (cp == NULL) -- return -1; -- argc = 0; -- memset(argv, 0, sizeof(argv)); -- cpend=cp+strlen(cp); -- next=eat_token_end(cp, cpend); -- for (ap = argv; cp<cpend; cp=next+1, next=eat_token_end(cp, cpend)){ -- *next=0; -- if (*cp != '\0') { -- *ap=cp; -- argc++; -- if ((char*)++ap >= ((char*)argv+sizeof(argv))) -- break; -+ v2p = find_next_sdp_line(v1p, bodylimit, 'v', bodylimit); -+ media_multi = (v2p != bodylimit); -+ v2p = v1p; -+ medianum = 0; -+ for (;;) { -+ unsigned nmseen, nmchanged; -+ /* Per-session iteration. */ -+ v1p = v2p; -+ if (v1p == NULL || v1p >= bodylimit) -+ break; /* No sessions left */ -+ v2p = find_next_sdp_line(v1p, bodylimit, 'v', bodylimit); -+ /* v2p is text limit for session parsing. */ -+ m1p = find_sdp_line(v1p, v2p, 'm'); -+ /* Have this session media description? */ -+ if (m1p == NULL) { -+ LOG(L_ERR, "ERROR: force_rtp_proxy2: no m= in session\n"); -+ return -1; - } -- } -- if (argc < 1) -- return -1; -- port = atoi(argv[0]); -- if (port <= 0 || port > 65535) -- return -1; -+ /* -+ * Find c1p only between session begin and first media. -+ * c1p will give common c= for all medias. -+ */ -+ c1p = find_sdp_line(v1p, m1p, 'c'); -+ c1_altered = 0; -+ /* Have session. Iterate media descriptions in session */ -+ m2p = m1p; -+ nmseen = nmchanged = 0; -+ for (;;) { -+ create1 = create; -+ if (flookup != 0) { -+ if (!create || to_tag.len <= 0) { -+#if 0 -+ LOG(L_ERR, "force_rtp_proxy(): inappropriate 'l'\n"); -+#endif -+ return -1; -+ } -+ create1 = 0; -+ } -+ m1p = m2p; -+ if (m1p == NULL || m1p >= v2p) -+ break; -+ m2p = find_next_sdp_line(m1p, v2p, 'm', v2p); -+ /* c2p will point to per-media "c=" */ -+ c2p = find_sdp_line(m1p, m2p, 'c'); -+ /* Extract address and port */ -+ tmpstr1.s = c2p ? c2p : c1p; -+ ++nmseen; -+ if (tmpstr1.s == NULL) { -+ /* No "c=" */ -+ LOG(L_ERR, "ERROR: force_rtp_proxy2: can't find media IP " -+ "in the message\n"); -+ return -1; -+ } -+ tmpstr1.len = v2p - tmpstr1.s; /* limit is session limit text */ -+ if (extract_mediaip(&tmpstr1, &oldip, &pf) == -1) { -+ LOG(L_ERR, "ERROR: force_rtp_proxy2: can't extract media IP " -+ "from the message\n"); -+ return -1; -+ } -+ tmpstr1.s = m1p; -+ tmpstr1.len = m2p - m1p; -+ if (extract_mediaport(&tmpstr1, &oldport) == -1) { -+ LOG(L_ERR, "ERROR: force_rtp_proxy2: can't extract media port " -+ "from the message\n"); -+ return -1; -+ } -+ ++medianum; -+ if (asymmetric != 0 || real != 0) { -+ newip = oldip; -+ } else { -+ newip.s = ip_addr2a(&msg->rcv.src_ip); -+ newip.len = strlen(newip.s); -+ } -+ /* XXX must compare address families in all addresses */ -+ if (pf == AF_INET6) { -+ opts[oidx] = '6'; -+ oidx++; -+ } -+ snprintf(medianum_buf, sizeof medianum_buf, "%d", medianum); -+ medianum_str.s = medianum_buf; -+ medianum_str.len = strlen(medianum_buf); -+ opts[0] = (create1 == 0) ? 'L' : 'U'; -+ v[1].iov_len = oidx; -+ STR2IOVEC(callid, v[3]); -+ STR2IOVEC(newip, v[5]); -+ STR2IOVEC(oldport, v[7]); -+ /*assert(!flookup || to_tag.len > 0);*/ -+ STR2IOVEC(flookup ? to_tag : from_tag, v[9]); -+ if (1 || media_multi) /* XXX */ { -+ STR2IOVEC(medianum_str, v[11]); -+ } else { -+ v[10].iov_len = v[11].iov_len = 0; -+ } -+ STR2IOVEC(flookup ? from_tag : to_tag, v[13]); -+ do { -+ node = select_rtpp_node(callid, 1); -+ if (!node) { -+ LOG(L_ERR, "ERROR: force_rtp_proxy2: no available proxies\n"); -+ return -1; -+ } -+ cp = send_rtpp_command(node, v, -+ (to_tag.len > 0) ? 14 : 12); -+ } while (cp == NULL); -+ LOG(L_DBG, "force_rtp_proxy2: proxy reply: %s\n", cp); -+ PARSE_PROXY_REPLY; -+ if (argc < 1) { -+ LOG(L_ERR, "force_rtp_proxy2: no reply from rtp proxy\n"); -+ return -1; -+ } -+ port = atoi(argv[0]); -+ if (port <= 0 || port > 65535) { -+ /* -+ * In forced lookup mode absence of session indicated -+ * by port number being zero is not an error condition -+ * but merely of indication that there is no such -+ * session in the proxy exists. -+ */ -+ if (port != 0 || flookup == 0) -+ LOG(L_ERR, "force_rtp_proxy2: incorrect port in reply from rtp proxy\n"); -+ return -1; -+ } - -- pf1 = (argc >= 3 && argv[2][0] == '6') ? AF_INET6 : AF_INET; -+ pf1 = (argc >= 3 && argv[2][0] == '6') ? AF_INET6 : AF_INET; - -- if (isnulladdr(&oldip, pf)) { -- if (pf1 == AF_INET6) { -- newip.s = "::"; -- newip.len = 2; -- } else { -- newip.s = "0.0.0.0"; -- newip.len = 7; -+ if (isnulladdr(&oldip, pf)) { -+ if (pf1 == AF_INET6) { -+ newip.s = "::"; -+ newip.len = 2; -+ } else { -+ newip.s = "0.0.0.0"; -+ newip.len = 7; -+ } -+ } else { -+ newip.s = (argc < 2) ? str2 : argv[1]; -+ newip.len = strlen(newip.s); -+ } -+ newport.s = int2str(port, &newport.len); /* beware static buffer */ -+ /* Alter port. */ -+ body1.s = m1p; -+ body1.len = bodylimit - body1.s; -+ if (alter_mediaport(msg, &body1, &oldport, &newport, 0) == -1) -+ return -1; -+ /* -+ * Alter IP. Don't alter IP common for the session -+ * more than once. -+ */ -+ if (c2p != NULL || !c1_altered) { -+ body1.s = c2p ? c2p : c1p; -+ body1.len = bodylimit - body1.s; -+ if (alter_mediaip(msg, &body1, &oldip, pf, &newip, pf1, 0) == -1) -+ return -1; -+ if (!c2p) -+ c1_altered = 1; -+ } -+ ++nmchanged; -+ /* -+ * Gross hack: turn off flookup here, after first -+ * media stream. This is used to allow changing number -+ * of media streams during re-INVITE. -+ */ -+ flookup = 0; -+ } /* Iterate medias in session */ -+ if (c1p && nmseen == nmchanged && !c1_altered) { -+ /* Alter default c-line of this session. */ -+ str c1s, janus; -+ size_t newlen; -+ c1s.s = c1p; -+ c1s.len = eat_line(c1p, bodylimit - c1p) - c1p; -+ newlen = c1s.len + 6 + 1; -+ janus.s = pkg_malloc(c1s.len); -+ if (janus.s == NULL) { -+ LOG(L_ERR, "pkg_malloc failed\n"); -+ return 0; -+ } -+ sprintf(janus.s, "a=oldc:%*.*s", -+ (int) c1s.len, (int) c1s.len, c1s.s); -+ janus.len = strlen(janus.s); -+ if (alter_line(msg, &c1s, &janus) <= 0) { -+ LOG(L_ERR, "alter_line() failed\n"); -+ return 0; -+ } - } -- } else { -- newip.s = (argc < 2) ? str2 : argv[1]; -- newip.len = strlen(newip.s); -- } -- newport.s = int2str(port, &newport.len); /* beware static buffer */ -- -- if (alter_mediaip(msg, &body, &oldip, pf, &newip, pf1, 0) == -1) -- return -1; -- if (oldip1.len > 0 && -- alter_mediaip(msg, &body1, &oldip1, pf, &newip, pf1, 0) == -1) -- return -1; -- if (alter_mediaport(msg, &body, &oldport, &newport, 0) == -1) -- return -1; -+ } /* Iterate sessions */ - - if (proxied == 0) { - cp = pkg_malloc(ANORTPPROXY_LEN * sizeof(char)); -@@ -1555,75 +2224,41 @@ - return force_rtp_proxy1_f(msg, arg, NULL); - } - --static void --timer(unsigned int ticks, void *param) -+static int -+rewrite_from_from_f(struct sip_msg* msg, char* str1, char* str2) - { -- int rval; -- void *buf, *cp; -- str c; -- struct sip_uri curi; -- union sockaddr_union to; -- struct hostent* he; -- struct socket_info* send_sock; -+ struct action act; -+ struct sip_uri uri; -+ str ruri; - -- buf = NULL; -- if (cblen > 0) { -- buf = pkg_malloc(cblen); -- if (buf == NULL) { -- LOG(L_ERR, "ERROR: nathelper::timer: out of memory\n"); -- return; -- } -+ /* parsing from header */ -+ if (parse_from_header(msg) == -1) { -+ LOG(L_ERR, "rewrite_from_from(): cannot get FROM header\n"); -+ return -1; - } -- rval = ul.get_all_ucontacts(buf, cblen, (ping_nated_only ? FL_NAT : 0)); -- if (rval > 0) { -- if (buf != NULL) -- pkg_free(buf); -- cblen = rval * 2; -- buf = pkg_malloc(cblen); -- if (buf == NULL) { -- LOG(L_ERR, "ERROR: nathelper::timer: out of memory\n"); -- return; -- } -- rval = ul.get_all_ucontacts(buf, cblen, (ping_nated_only ? FL_NAT : 0)); -- if (rval != 0) { -- pkg_free(buf); -- return; -- } -+ ruri.len = get_from(msg)->uri.len; -+ ruri.s = pkg_malloc(ruri.len + 1); -+ memcpy(ruri.s, get_from(msg)->uri.s, ruri.len + 1); -+ if (parse_uri(ruri.s, ruri.len, &uri) < 0) { -+ LOG(L_ERR, "rewrite_from_from(): can't parse FROM URI\n"); -+ pkg_free(ruri.s); -+ return -1; -+ } -+ if (uri.user.len <= 0) { -+ uri.user.s = "Unknown"; -+ uri.user.len = 7; -+ } else { -+ uri.user.s[uri.user.len] = '\0'; - } - -- if (buf == NULL) -- return; -+ bzero(&act, sizeof(act)); -+ act.type = SET_USER_T; -+ act.p1_type = STRING_ST; -+ act.p1.string = uri.user.s; -+ do_action(&act, msg); - -- cp = buf; -- while (1) { -- memcpy(&(c.len), cp, sizeof(c.len)); -- if (c.len == 0) -- break; -- c.s = (char*)cp + sizeof(c.len); -- cp = (char*)cp + sizeof(c.len) + c.len; -- if (parse_uri(c.s, c.len, &curi) < 0) { -- LOG(L_ERR, "ERROR: nathelper::timer: can't parse contact uri\n"); -- continue; -- } -- if (curi.proto != PROTO_UDP && curi.proto != PROTO_NONE) -- continue; -- if (curi.port_no == 0) -- curi.port_no = SIP_PORT; -- he = sip_resolvehost(&curi.host, &curi.port_no, PROTO_UDP); -- if (he == NULL){ -- LOG(L_ERR, "ERROR: nathelper::timer: can't resolve_hos\n"); -- continue; -- } -- hostent2su(&to, he, 0, curi.port_no); -- send_sock=force_socket ? force_socket : -- get_send_socket(0, &to, PROTO_UDP); -- if (send_sock == NULL) { -- LOG(L_ERR, "ERROR: nathelper::timer: can't get sending socket\n"); -- continue; -- } -- udp_send(send_sock, (char *)sbuf, sizeof(sbuf), &to); -- } -- pkg_free(buf); -+ pkg_free(ruri.s); -+ return 1; - } - - |