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, 1786 insertions, 0 deletions
diff --git a/net/ser/files/patch-modules__nathelper__nathelper.c b/net/ser/files/patch-modules__nathelper__nathelper.c new file mode 100644 index 000000000000..d85865e0eaa0 --- /dev/null +++ b/net/ser/files/patch-modules__nathelper__nathelper.c @@ -0,0 +1,1786 @@ + +$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; + } + + |