diff options
Diffstat (limited to 'net/ser/files/patch-modules__nathelper__moh.c')
-rw-r--r-- | net/ser/files/patch-modules__nathelper__moh.c | 434 |
1 files changed, 434 insertions, 0 deletions
diff --git a/net/ser/files/patch-modules__nathelper__moh.c b/net/ser/files/patch-modules__nathelper__moh.c new file mode 100644 index 000000000000..5c8ae4f33059 --- /dev/null +++ b/net/ser/files/patch-modules__nathelper__moh.c @@ -0,0 +1,434 @@ + +$FreeBSD$ + +--- modules/nathelper/moh.c ++++ modules/nathelper/moh.c +@@ -0,0 +1,428 @@ ++/* $Id: patch-modules::nathelper::moh.c,v 1.2 2005/04/05 13:10:07 netch Exp $ ++ * ++ * Copyright (C) 2005 Porta Software Ltd ++ * ++ * 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: ++ * info@iptel.org ++ * ++ * 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 <sys/types.h> ++#include <sys/uio.h> ++#include <stdio.h> ++#include <stdlib.h> ++#include "../../parser/parser_f.h" ++#include "../../ut.h" ++#include "nhelpr_funcs.h" ++#include "nathelper.h" ++ ++/* ++ * 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) ++ ++int ++is_hold_f(struct sip_msg* msg, char *str1, char *str2) ++{ ++ /* Look into body and find whether we see 0.0.0.0 as IP address. ++ * extract_mediaport() is designed to parse address from SDP. ++ * XXX Check all addresses or only first one? What if have some ++ * real addresses and some zero ones? ++ */ ++ str body, ip; ++ int pf; ++ ++ if (extract_body(msg, &body) == -1) { ++ LOG(L_ERR,"ERROR: is_hold: cannot extract body from msg!\n"); ++ return 0; ++ } ++ if (extract_mediaip(&body, &ip, &pf) == -1) { ++ LOG(L_ERR, "ERROR: is_hold: can't extract media IP from the SDP\n"); ++ return 0; ++ } ++ return isnulladdr(&ip, pf) ? 1 : -1; ++} ++ ++int ++start_moh_f(struct sip_msg* msg, char* str1, char* str2) ++{ ++ str callid, from_tag, to_tag; ++ int asymmetric, flookup, force, real; ++ int oidx, argc, medianum, c1p_altered, pf, pf1; ++ int seen_audio, seen_video; ++ str body, body1, tmpstr1, oldip, newip, oldport, newport; ++ str medianum_str; ++ char *cpend, *next, *bodylimit, *v1p, *v2p, *c1p, *c2p, *m1p, *m2p; ++ char *cp; ++ char medianum_buf[20], opts[16]; ++ char **ap, *argv[10]; ++ unsigned port; ++ struct rtpp_node *node; ++ struct iovec v_create[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 */ ++ }; ++ struct iovec v_play[14] = { ++ {NULL, 0}, /* command */ ++ {NULL, 0}, /* options */ ++ {" ", 1}, /* separator */ ++ {NULL, 0}, /* callid */ ++ {" ", 1}, /* separator */ ++ {NULL, 7}, /* pname */ ++ {" ", 1}, /* separator */ ++ {NULL, 1}, /* codecs */ ++ {" ", 1}, /* separator */ ++ {NULL, 0}, /* to_tag */ ++ {";", 1}, /* separator */ ++ {NULL, 0}, /* medianum */ ++ {" ", 1}, /* separator */ ++ {NULL, 0} /* from_tag */ ++ }; ++ ++ /* extract_body will also parse all the headers in the message as ++ * a side effect => don't move get_callid/get_to_tag in front of it ++ * -- andrei */ ++ if (extract_body(msg, &body) == -1) { ++ LOG(L_ERR, "ERROR: force_rtp_proxy2: can't extract body " ++ "from the message\n"); ++ return -1; ++ } ++ if (get_callid(msg, &callid) == -1 || callid.len == 0) { ++ LOG(L_ERR, "ERROR: start_moh: can't get Call-Id field\n"); ++ return -1; ++ } ++ if (get_to_tag(msg, &to_tag) == -1 || to_tag.len <= 0) { ++ LOG(L_ERR, "ERROR: start_moh: 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_moh: can't get From tag\n"); ++ return -1; ++ } ++ /* Setting specific options. XXX Do we really need this? */ ++ v_create[1].iov_base = opts; ++ asymmetric = flookup = force = real = 0; ++ force = 1; /* XXX we must force the proxying in this case */ ++ oidx = 2; /* 'UW' */ ++#if 0 ++ for (cp = str1; cp != NULL && *cp != '\0'; cp++) { ++ switch (*cp) { ++ case 'a': ++ case 'A': ++ opts[oidx++] = 'A'; ++ asymmetric = 1; ++ real = 1; ++ break; ++ ++ case 'i': ++ case 'I': ++ opts[oidx++] = 'I'; ++ break; ++ ++ case 'e': ++ case 'E': ++ opts[oidx++] = 'E'; ++ break; ++ ++ case 'l': ++ case 'L': ++ flookup = 1; ++ break; ++ ++ case 'f': ++ case 'F': ++ force = 1; ++ break; ++ ++ case 'r': ++ case 'R': ++ real = 1; ++ break; ++ ++ default: ++ LOG(L_ERR, "ERROR: force_rtp_proxy2: unknown option `%c'\n", *cp); ++ return -1; ++ } ++ } ++#endif ++ /* ++ * 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. ++ * ++ * Note start_moh() specifics: use only audio media or video media ++ * and stop after first of them of both kinds. But, medianum should ++ * reflect all of them. ++ */ ++ bodylimit = body.s + body.len; ++ v2p = v1p = find_sdp_line(body.s, bodylimit, 'v'); ++ if (v1p == NULL) { ++ LOG(L_ERR, "ERROR: start_moh: no sessions in SDP\n"); ++ return -1; ++ } ++ medianum = 0; ++ for(;;) { ++ /* 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: start_moh: no m= in session\n"); ++ 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'); ++ c1p_altered = 0; ++ /* Have session. Iterate media descriptions in session */ ++ seen_audio = seen_video = 0; ++ m2p = m1p; ++ while (!seen_audio || !seen_video) { ++ int is_audio, is_video; ++ /* We pass address to proxy and get some port from ++ * its resources. Then, if old address was empty ++ * (0.0.0.0), create a play stream for this media. ++ */ ++ 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'); ++ ++medianum; ++ /* ++ * start_moh() specifics: work only for audio/video ++ * media and apply to first of any in session. ++ */ ++ is_audio = (strncmp(m1p, "m=audio ", 8) == 0); ++ is_video = (strncmp(m1p, "m=video ", 8) == 0); ++ if ((is_audio && seen_audio) || ++ (is_video && seen_video) || ++ (!is_audio && !is_video)) ++ continue; ++ seen_audio = seen_audio || is_audio; ++ seen_video = seen_video || is_video; ++ /* Extract address and port */ ++ tmpstr1.s = c2p ? c2p : c1p; ++ if (tmpstr1.s == NULL) { ++ /* No "c=" */ ++ LOG(L_ERR, "ERROR: start_moh: 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: start_moh: 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: start_moh: can't extract media port " ++ "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); ++ } ++ /* XXX must compare address families in all addresses */ ++ if (pf == AF_INET6) { ++ opts[oidx] = '6'; ++ oidx++; ++ } ++ ++ /* ++ * If don't see NULL addr, this is not hold. ++ * So, skip to next one. ++ * XXX should also support "a=sendonly" ++ */ ++ if (!isnulladdr(&oldip, pf)) ++ continue; ++ ++ /* Prepare proxy command strings. */ ++ snprintf(medianum_buf, sizeof medianum_buf, "%d", medianum); ++ medianum_str.s = medianum_buf; ++ medianum_str.len = strlen(medianum_buf); ++ opts[0] = 'U'; opts[1] = 'W'; ++ v_create[1].iov_len = oidx; ++ STR2IOVEC(callid, v_create[3]); ++ STR2IOVEC(newip, v_create[5]); ++ STR2IOVEC(oldport, v_create[7]); ++ STR2IOVEC(from_tag, v_create[9]); ++ STR2IOVEC(medianum_str, v_create[11]); ++ STR2IOVEC(to_tag, v_create[13]); ++ STR2IOVEC(callid, v_play[3]); ++ if (is_audio) { ++ SZ2IOVEC(pname_audio, v_play[5]); ++ SZ2IOVEC(codecs_audio, v_play[7]); ++ } else { ++ SZ2IOVEC(pname_video, v_play[5]); ++ SZ2IOVEC(codecs_video, v_play[7]); ++ } ++ STR2IOVEC(to_tag, v_play[9]); ++ STR2IOVEC(medianum_str, v_play[11]); ++ STR2IOVEC(from_tag, v_play[13]); ++ SZ2IOVEC("P", v_play[1]); ++ /* Send command. */ ++ do { ++ node = select_rtpp_node(callid, 1); ++ if (!node) { ++ LOG(L_ERR, "ERROR: start_moh: no available proxies\n"); ++ return -1; ++ } ++ cp = send_rtpp_command(node, v_create, 14); ++ if (cp == NULL) ++ continue; ++ LOG(L_DBG, "start_moh: proxy reply to update: %s\n", cp); ++ PARSE_PROXY_REPLY; ++ if (argc < 1) { ++ LOG(L_ERR, "start_moh: no reply from rtp proxy\n"); ++ return -1; ++ } ++ port = atoi(argv[0]); ++ if (port <= 0 || port > 65535) { ++ LOG(L_ERR, "start_moh: incorrect port in reply from rtp proxy\n"); ++ return -1; ++ } ++ ++ pf1 = (argc >= 3 && argv[2][0] == '6') ? AF_INET6 : AF_INET; ++ 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) { ++ LOG(L_ERR, "start_mon: alter_mediaport() failed\n"); ++ return -1; ++ } ++ /* Alter IP. Don't alter IP common for the session ++ * more than once. ++ */ ++ if (c2p != NULL || !c1p_altered) { ++ body1.s = c2p ? c2p : c1p; ++ body1.len = m2p - body1.s; ++ if (alter_mediaip(msg, &body1, &oldip, pf, &newip, pf1, 2) == -1) { ++ LOG(L_ERR, "start_moh: alter_mediaip() failed\n"); ++ return -1; ++ } ++ if (!c2p) ++ c1p_altered = 1; ++ } ++ cp = send_rtpp_command(node, v_play, 14); ++ LOG(L_DBG, "start_moh: proxy reply to play: %s\n", cp); ++ if (cp == NULL) ++ continue; ++ PARSE_PROXY_REPLY; ++ if (argc < 1) { ++ LOG(L_ERR, "start_moh: no reply from rtp proxy\n"); ++ return -1; ++ } ++ } while(cp == NULL); ++ } /* Iterate medias in session */ ++ } /* Iterate sessions */ ++ return 1; ++} ++ ++int ++stop_moh_f(struct sip_msg* msg, char* str1, char* str2) ++{ ++ str callid, to_tag, from_tag; ++ struct rtpp_node *node; ++ struct iovec v_noplay[8] = {{NULL, 0}, {"S", 1}, {" ", 1}, ++ {NULL, 0}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}}; ++ struct iovec v_del[8] = {{NULL, 0}, {"DW", 2}, {" ", 1}, ++ {NULL, 0}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}}; ++ ++ if (get_callid(msg, &callid) == -1 || callid.len == 0) { ++ LOG(L_ERR, "ERROR: stop_moh: can't get Call-Id field\n"); ++ return -1; ++ } ++ if (get_to_tag(msg, &to_tag) == -1 || to_tag.len <= 0) { ++ LOG(L_ERR, "ERROR: stop_moh: can't get To tag\n"); ++ return -1; ++ } ++ if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) { ++ LOG(L_ERR, "ERROR: stop_moh: can't get From tag\n"); ++ return -1; ++ } ++ /* Ask RTP proxy to stop all plays for this tag. We don't iterate ++ * separate sessions; RTP proxy has knowledge of them. ++ */ ++ STR2IOVEC(callid, v_noplay[3]); ++ STR2IOVEC(to_tag, v_noplay[5]); ++ STR2IOVEC(from_tag, v_noplay[7]); ++ node = select_rtpp_node(callid, 1); ++ send_rtpp_command(node, v_noplay, 8); ++ /* Ask weak deletion for the session. The same as previous; ++ * RTP proxy knows all sessions. ++ */ ++ STR2IOVEC(callid, v_del[3]); ++ STR2IOVEC(to_tag, v_del[5]); ++ STR2IOVEC(from_tag, v_del[7]); ++ send_rtpp_command(node, v_del, 8); ++ return 1; ++} |