Răsfoiți Sursa

o Use recently extended U command to submit list of supported payload types
to the RTPproxy. Only do this if the RTPproxy supports revision 20081102
of the protocol.

o Introduce 4 new commands:

rtpproxy_stream2uac(prompt_name, count)
rtpproxy_stream2uas(prompt_name, count)
rtpproxy_stop_stream2uac()
rtpproxy_stop_stream2uas()

Those commands allow to stream prompt/announcement pre-encoded with
the makeann command from the RTPproxy distribution. The uac/uas
suffix selects who will hear the announcement relatively to tha current
transaction, so that for example invoking the rtpproxy_stream2uac()
in the request processing block on ACK transaction will play the
prompt to the UA that has generated original INVITE and ACK while
rtpproxy_stop_stream2uas() on 183 in reply processing block will
play the prompt to the UA that has generated 183.

Another possible application of this functionality is implementing music
on hold (MOH) functionality. When count is -1, the streaming will be in
loop indefinitely until the appropriate rtpproxy_stop_stream2xxx()
is issued.

route {
if (method == "INVITE") {
rtpproxy_offer();
if (detect_hold()) {
rtpproxy_stream2uas("music_on_hold", -1);
} else {
rtpproxy_stop_stream2uas();
};
};
}

In order to work correctly, functions require that the session in the
RTPproxy already exists. Also those functions don't alted SDP, so that
they are not substitute for calling rtpproxy_offer/rtpproxy_answer or
force_rtp_proxy.

Sponsored by: Telio Telecom AS

Maxim Sobolev 17 ani în urmă
părinte
comite
bcd6e13df5

+ 83 - 25
modules_s/nathelper/nathelper.c

@@ -184,6 +184,7 @@
 
 #include "nhelpr_funcs.h"
 #include "nathelper.h"
+#include "rtpproxy_stream.h"
 #include "../../flags.h"
 #include "../../sr_module.h"
 #include "../../dprint.h"
@@ -245,6 +246,7 @@ MODULE_VERSION
 #define	REQ_CPROTOVER	"20050322"
 /* Additional version necessary for re-packetization support */
 #define	REP_CPROTOVER	"20071116"
+#define	PTL_CPROTOVER	"20081102"
 #define	CPORT		"22222"
 
 struct rtpp_head;
@@ -254,12 +256,11 @@ 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 extract_mediainfo(str *, 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 char *gencookie();
 static int rtpp_test(struct rtpp_node*, int, int);
-static char *send_rtpp_command(struct rtpp_node*, struct iovec *, int);
 static int unforce_rtp_proxy0_f(struct sip_msg *, char *, char *);
 static int unforce_rtp_proxy1_f(struct sip_msg *, char *, char *);
 static int start_recording0_f(struct sip_msg *, char *, char *);
@@ -295,15 +296,19 @@ static struct {
 	{NULL, 0, 0}
 };
 
-static str sup_ptypes[] = {
-	{.s = "udp", .len = 3},
-	{.s = "udptl", .len = 5},
-	{.s = "rtp/avp", .len = 7},
-	{.s = "rtp/avpf", .len = 8},
-	{.s = "rtp/savp", .len = 8},
-	{.s = "rtp/savpf", .len = 9},
-	{.s = "udp/bfcp", .len = 8},
-	{.s = NULL, .len = 0}
+static struct {
+	const char *s;
+	int len;
+	int is_rtp;
+} sup_ptypes[] = {
+	{.s = "udp",       .len = 3, .is_rtp = 0},
+	{.s = "udptl",     .len = 5, .is_rtp = 0},
+	{.s = "rtp/avp",   .len = 7, .is_rtp = 1},
+	{.s = "rtp/avpf",  .len = 8, .is_rtp = 1},
+	{.s = "rtp/savp",  .len = 8, .is_rtp = 1},
+	{.s = "rtp/savpf", .len = 9, .is_rtp = 1},
+	{.s = "udp/bfcp",  .len = 8, .is_rtp = 0},
+	{.s = NULL,        .len = 0, .is_rtp = 0}
 };
 
 static char *rtpproxy_sock = "unix:/var/run/rtpproxy.sock"; /* list */
@@ -330,6 +335,7 @@ struct rtpp_node {
 	unsigned		rn_weight;	/* for load balancing */
 	int			rn_recheck_ticks;
 	int			rn_rep_supported;
+	int			rn_ptl_supported;
 	struct rtpp_node	*rn_next;
 };
 
@@ -356,6 +362,10 @@ static cmd_export_t cmds[] = {
 	{"rtpproxy_answer",    rtpproxy_answer1_f,     0, NULL,             REQUEST_ROUTE | ONREPLY_ROUTE },
 	{"rtpproxy_answer",    rtpproxy_answer1_f,     1, fixup_var_str_1,             REQUEST_ROUTE | ONREPLY_ROUTE },
 	{"rtpproxy_answer",    rtpproxy_answer2_f,     2, fixup_var_str_12,            REQUEST_ROUTE | ONREPLY_ROUTE },
+	{"rtpproxy_stream2uac",rtpproxy_stream2uac2_f, 2, fixup_var_str_int,           REQUEST_ROUTE | ONREPLY_ROUTE },
+	{"rtpproxy_stream2uas",rtpproxy_stream2uas2_f, 2, fixup_var_str_int,           REQUEST_ROUTE | ONREPLY_ROUTE },
+	{"rtpproxy_stop_stream2uac",rtpproxy_stop_stream2uac2_f, 0, NULL,              REQUEST_ROUTE | ONREPLY_ROUTE },
+	{"rtpproxy_stop_stream2uas",rtpproxy_stop_stream2uas2_f, 0, NULL,              REQUEST_ROUTE | ONREPLY_ROUTE },
 	{0, 0, 0, 0, 0}
 };
 
@@ -1048,7 +1058,7 @@ extract_mediaip(str *body, str *mediaip, int *pf)
 }
 
 static int
-extract_mediaport(str *body, str *mediaport)
+extract_mediainfo(str *body, str *mediaport, str *payload_types)
 {
 	char *cp, *cp1;
 	int len, i;
@@ -1062,7 +1072,7 @@ extract_mediaport(str *body, str *mediaport)
 		cp = cp1 + 2;
 	}
 	if (cp1 == NULL) {
-		LOG(L_ERR, "ERROR: extract_mediaport: no `m=' in SDP\n");
+		LOG(L_ERR, "ERROR: extract_mediainfo: no `m=' in SDP\n");
 		return -1;
 	}
 	mediaport->s = cp1 + 2; /* skip `m=' */
@@ -1074,14 +1084,14 @@ extract_mediaport(str *body, str *mediaport)
 	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");
+		LOG(L_ERR, "ERROR: extract_mediainfo: no port in `m='\n");
 		return -1;
 	}
 	mediaport->s = cp;
 	cp = eat_space_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");
+		LOG(L_ERR, "ERROR: extract_mediainfo: no port in `m='\n");
 		return -1;
 	}
 	/* Extract port */
@@ -1089,7 +1099,7 @@ extract_mediaport(str *body, str *mediaport)
 	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");
+		LOG(L_ERR, "ERROR: extract_mediainfo: no port in `m='\n");
 		return -1;
 	}
 	ptype.s = cp;
@@ -1098,22 +1108,38 @@ extract_mediaport(str *body, str *mediaport)
 	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");
+		LOG(L_ERR, "ERROR: extract_mediainfo: 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");
+		LOG(L_ERR, "ERROR: extract_mediainfo: no protocol type in `m='\n");
 		return -1;
 	}
+	payload_types->len = ptype.len - (cp - ptype.s);
 	ptype.len = cp - ptype.s;
+	payload_types->s = cp;
 
-	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)
+	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)
+			continue;
+		if (sup_ptypes[i].is_rtp == 0) {
+			payload_types->len = 0;
 			return 0;
+		}
+		cp = eat_space_end(payload_types->s, payload_types->s +
+		    payload_types->len);
+		if (cp == payload_types->s) {
+			LOG(L_ERR, "ERROR: extract_mediainfo: no payload types in `m='\n");
+			return -1;
+		}
+		payload_types->len -= cp - payload_types->s;
+		payload_types->s = cp;
+		return 0;
+	}
 	/* Unproxyable protocol type. Generally it isn't error. */
 	return -1;
 }
@@ -1385,6 +1411,12 @@ rtpp_test(struct rtpp_node *node, int isdisabled, int force)
 		} else {
 			node->rn_rep_supported = 0;
 		}
+		rval = rtpp_checkcap(node, PTL_CPROTOVER, sizeof(PTL_CPROTOVER) - 1);
+		if (rval != -1) {
+			node->rn_ptl_supported = rval;
+		} else {
+			node->rn_ptl_supported = 0;
+		}
 		return 0;
 	} while(0);
 	LOG(L_WARN, "WARNING: rtpp_test: support for RTP proxy <%s>"
@@ -1396,7 +1428,7 @@ rtpp_test(struct rtpp_node *node, int isdisabled, int force)
 	return 1;
 }
 
-static char *
+char *
 send_rtpp_command(struct rtpp_node *node, struct iovec *v, int vcnt)
 {
 	struct sockaddr_un addr;
@@ -1510,7 +1542,7 @@ badproxy:
  * too rare. Otherwise we should implement "mature" HA clustering, which is
  * too expensive here.
  */
-static struct rtpp_node *
+struct rtpp_node *
 select_rtpp_node(str callid, int do_test, int node_idx)
 {
 	unsigned sum, sumcut, weight_sum;
@@ -1843,7 +1875,7 @@ static int
 force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 {
 	str body, body1, oldport, oldip, newport, newip, str1, str2, s;
-	str callid, from_tag, to_tag, tmp, c1_oldip;
+	str callid, from_tag, to_tag, tmp, c1_oldip, payload_types;
 	int create, port, len, asymmetric, flookup, argc, proxied, real, i;
 	int oidx, pf, pf1, force, c1_pf, rep_oidx;
 	unsigned int node_idx, oldport_i;
@@ -2102,7 +2134,8 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 			}
 			tmpstr1.s = m1p;
 			tmpstr1.len = m2p - m1p;
-			if (extract_mediaport(&tmpstr1, &oldport) == -1) {
+			if (extract_mediainfo(&tmpstr1, &oldport,
+			    &payload_types) == -1) {
 				LOG(L_ERR, "ERROR: force_rtp_proxy2: can't"
 				    " extract media port from the message\n");
 				return -1;
@@ -2162,6 +2195,31 @@ force_rtp_proxy(struct sip_msg *msg, char *param1, char *param2, int offer)
 						v[1].iov_len += rep_oidx;
 					}
 				}
+				if (payload_types.len > 0 && node->rn_ptl_supported != 0) {
+					cp1 = (char *)v[1].iov_base + v[1].iov_len;
+					*cp1 = 'c';
+					cp1++;
+					/*
+					 * Convert space-separated payload types list into
+					 * a comma-separated list.
+					 */
+					for (cp = payload_types.s;
+					    cp < payload_types.s + payload_types.len; cp++) {
+						if (isdigit(*cp)) {
+							*cp1 = *cp;
+							cp1++;
+							continue;
+						}
+						*cp1 = ',';
+						cp1++;
+						do {
+							cp++;
+						} while (!isdigit(*cp) &&
+						    cp < payload_types.s + payload_types.len);
+						cp--;
+					}
+					v[1].iov_len = cp1 - (char *)v[1].iov_base;
+				}
 				cp = send_rtpp_command(node, v, (to_tag.len > 0) ? 16 : 12);
 				v[1].iov_len = len;
 			} while (cp == NULL);

+ 4 - 0
modules_s/nathelper/nathelper.h

@@ -36,6 +36,10 @@
 /* Parameters from nathelper.c */
 extern struct socket_info* force_socket;
 
+/* Functions from nathelper */
+struct rtpp_node *select_rtpp_node(str, int, int);
+char *send_rtpp_command(struct rtpp_node *, struct iovec *, int);
+
 /* Functions from natping.c */
 int natpinger_init(void);
 int natpinger_child_init(int);

+ 204 - 0
modules_s/nathelper/rtpproxy_stream.c

@@ -0,0 +1,204 @@
+/* $Id$
+ *
+ * Copyright (C) 2008 Sippy Software, Inc., http://www.sippysoft.com
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+
+#include "../../ip_addr.h"
+#include "../../parser/msg_parser.h"
+#include "../../sr_module.h"
+#include "nathelper.h"
+#include "nhelpr_funcs.h"
+
+int
+fixup_var_str_int(void **param, int param_no)
+{
+    int ret;
+
+    if (param_no == 1) {
+        ret = fix_param(FPARAM_AVP, param);
+        if (ret <= 0)
+            return ret;
+        if (fix_param(FPARAM_STR, param) != 0)
+            return -1;
+    } else if (param_no == 2) {
+        if (fix_param(FPARAM_INT, param) != 0)
+            return -1;
+    }
+    return 0;
+}
+
+static int
+rtpproxy_stream(struct sip_msg* msg, str *pname, int count, int stream2uac)
+{
+    int nitems;
+    str callid, from_tag, to_tag;
+    struct rtpp_node *node;
+    char cbuf[16];
+    struct iovec v[] = {
+        {NULL,        0},
+        {cbuf,        0}, /* 1 P<count> */
+        {" ",         1},
+        {NULL,        0}, /* 3 callid */
+        {" ",         1},
+        {NULL,        0}, /* 5 pname */
+        {" session ", 9},
+        {NULL,        0}, /* 7 from tag */
+        {";1 ",       3},
+        {NULL,        0}, /* 9 to tag */
+        {";1",        2}
+    };
+
+    if (get_callid(msg, &callid) == -1 || callid.len == 0) {
+        LOG(L_ERR, "ERROR: rtpproxy_stream: can't get Call-Id field\n");
+        return -1;
+    }
+    if (get_to_tag(msg, &to_tag) == -1) {
+        LOG(L_ERR, "ERROR: rtpproxy_stream: can't get To tag\n");
+        return -1;
+    }
+    if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) {
+        LOG(L_ERR, "ERROR: rtpproxy_stream: can't get From tag\n");
+        return -1;
+    }
+    v[1].iov_len = sprintf(cbuf, "P%d", count);
+    STR2IOVEC(callid, v[3]);
+    STR2IOVEC(*pname, v[5]);
+    node = select_rtpp_node(callid, 1, -1);
+    if (!node) {
+        LOG(L_ERR, "ERROR: rtpproxy_stream: no available proxies\n");
+        return -1;
+    }
+    nitems = 11;
+    if (stream2uac == 0) {
+        if (to_tag.len == 0)
+            return -1;
+        STR2IOVEC(to_tag, v[7]);
+        STR2IOVEC(from_tag, v[9]);
+    } else {
+        STR2IOVEC(from_tag, v[7]);
+        STR2IOVEC(to_tag, v[9]);
+        if (to_tag.len <= 0)
+            nitems -= 2;
+    }
+    send_rtpp_command(node, v, nitems);
+
+    return 1;
+}
+
+static int
+rtpproxy_stream2_f(struct sip_msg *msg, char *str1, char *str2, int stream2uac)
+{
+    int count;
+    str pname;
+
+    if (get_str_fparam(&pname, msg, (fparam_t*) str1) < 0)
+        return -1;
+    if (get_int_fparam(&count, msg, (fparam_t*) str2) < 0)
+        return -1;
+    return rtpproxy_stream(msg, &pname, count, stream2uac);
+}
+
+int
+rtpproxy_stream2uac2_f(struct sip_msg* msg, char* str1, char* str2)
+{
+
+    return rtpproxy_stream2_f(msg, str1, str2, 1);
+}
+
+int
+rtpproxy_stream2uas2_f(struct sip_msg* msg, char* str1, char* str2)
+{
+
+    return rtpproxy_stream2_f(msg, str1, str2, 0);
+}
+
+static int
+rtpproxy_stop_stream(struct sip_msg* msg, int stream2uac)
+{
+    int nitems;
+    str callid, from_tag, to_tag;
+    struct rtpp_node *node;
+    struct iovec v[] = {
+        {NULL,        0},
+        {"S",         1}, /* 1 */
+        {" ",         1},
+        {NULL,        0}, /* 3 callid */
+        {" ",         1},
+        {NULL,        0}, /* 5 from tag */
+        {";1 ",       3},
+        {NULL,        0}, /* 7 to tag */
+        {";1",        2}
+    };
+
+    if (get_callid(msg, &callid) == -1 || callid.len == 0) {
+        LOG(L_ERR, "ERROR: rtpproxy_stop_stream: can't get Call-Id field\n");
+        return -1;
+    }
+    if (get_to_tag(msg, &to_tag) == -1) {
+        LOG(L_ERR, "ERROR: rtpproxy_stop_stream: can't get To tag\n");
+        return -1;
+    }
+    if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) {
+        LOG(L_ERR, "ERROR: rtpproxy_stop_stream: can't get From tag\n");
+        return -1;
+    }
+    STR2IOVEC(callid, v[3]);
+    node = select_rtpp_node(callid, 1, -1);
+    if (!node) {
+        LOG(L_ERR, "ERROR: rtpproxy_stop_stream: no available proxies\n");
+        return -1;
+    }
+    nitems = 9;
+    if (stream2uac == 0) {
+        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 -= 2;
+    }
+    send_rtpp_command(node, v, nitems);
+
+    return 1;
+}
+
+int
+rtpproxy_stop_stream2uac2_f(struct sip_msg* msg, char* str1, char* str2)
+{
+
+    return rtpproxy_stop_stream(msg, 1);
+}
+
+int
+rtpproxy_stop_stream2uas2_f(struct sip_msg* msg, char* str1, char* str2)
+{
+
+    return rtpproxy_stop_stream(msg, 0);
+}

+ 37 - 0
modules_s/nathelper/rtpproxy_stream.h

@@ -0,0 +1,37 @@
+/* $Id$
+ *
+ * Copyright (C) 2008 Sippy Software, Inc., http://www.sippysoft.com
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _RTPPROXY_STREAM_H
+#define  _RTPPROXY_STREAM_H
+
+int fixup_var_str_int(void **, int);
+int rtpproxy_stream2uac2_f(struct sip_msg *, char *, char *);
+int rtpproxy_stream2uas2_f(struct sip_msg *, char *, char *);
+int rtpproxy_stop_stream2uac2_f(struct sip_msg *, char *, char *);
+int rtpproxy_stop_stream2uas2_f(struct sip_msg *, char *, char *);
+
+#endif