Re: [Siproxd-users] Siproxd and patch
Status: Beta
Brought to you by:
tries
From: Tristan <tr...@te...> - 2008-02-22 14:47:46
|
diff -rdEbU3 -ruN siproxd-0.7.0.orig/src/Makefile siproxd-0.7.0/src/Makefile --- siproxd-0.7.0.orig/src/Makefile 2008-02-22 15:27:13.000000000 +0100 +++ siproxd-0.7.0/src/Makefile 2008-02-20 15:44:04.000000000 +0100 @@ -34,6 +34,14 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +# PJSIP stuff +PJBASE=/root/src/pjproject-0.8.0 +include $(PJBASE)/build.mak +# +# LDFLAGS = ${APP_LDFLAGS} +# LDLIBS = ${APP_LDLIBS} +# CFLAGS = ${APP_CFLAGS} +# CPPFLAGS= ${CFLAGS} srcdir = . @@ -88,7 +96,7 @@ accessctl.$(OBJEXT) route_processing.$(OBJEXT) \ security.$(OBJEXT) auth.$(OBJEXT) fwapi.$(OBJEXT) \ resolve.$(OBJEXT) plugin_shortdial.$(OBJEXT) \ - dejitter.$(OBJEXT) + dejitter.$(OBJEXT) plugin_mediarelay.$(OBJEXT) siproxd_OBJECTS = $(am_siproxd_OBJECTS) siproxd_LDADD = $(LDADD) DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) @@ -131,10 +139,11 @@ INSTALL_STRIP_PROGRAM = ${SHELL} $(install_sh) -c -s LDFLAGS = LIBOBJS = -LIBS = -lresolv -lresolv -losipparser2 -losip2 +# PATCH PJLIB +LIBS = -lresolv -lresolv -losipparser2 -losip2 ${APP_LDFLAGS} $(APP_LDLIBS) LN_S = ln -s LTLIBOBJS = -MAKEINFO = ${SHELL} /root/src/siproxd-0.7.0.orig/scripts/missing --run makeinfo +MAKEINFO = ${SHELL} /root/src/siproxd-0.7.0/scripts/missing --run makeinfo OBJEXT = o PACKAGE = siproxd PACKAGE_BUGREPORT = @@ -204,12 +213,11 @@ target_vendor = redhat AM_CFLAGS = -Wall -D_GNU_SOURCE \ -DBUILDSTR="\"`cat .buildno`\"" - siproxd_SOURCES = siproxd.c proxy.c register.c sock.c utils.c \ sip_utils.c sip_layer.c log.c readconf.c rtpproxy.c \ rtpproxy_relay.c accessctl.c route_processing.c \ security.c auth.c fwapi.c resolve.c \ - plugin_shortdial.c dejitter.c + plugin_shortdial.c dejitter.c plugin_mediarelay.c # addrcache.c diff -rdEbU3 -ruN siproxd-0.7.0.orig/src/plugin_mediarelay.c siproxd-0.7.0/src/plugin_mediarelay.c --- siproxd-0.7.0.orig/src/plugin_mediarelay.c 1970-01-01 01:00:00.000000000 +0100 +++ siproxd-0.7.0/src/plugin_mediarelay.c 2008-02-22 15:16:32.000000000 +0100 @@ -0,0 +1,960 @@ +/* + Copyright (C) 2008 Tristan Mahé <tri...@gm...> + + This file is part of Siproxd. + + Siproxd 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. + + Siproxd 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 Siproxd; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "plugin_mediarelay.h" + +static char const ident[]="$Id: plugin_mediarelay.c,v 0.2$"; + +/* RTP proxy with advanced functions + * Process INVITE, 200/ok, BYE, CANCEL... + * don't forget to NEVER activate proxy_rtp in siproxd configuration as it will mess up everything + */ + +int plugin_mediarelay(sip_ticket_t *ticket) +{ + int sts=STS_SUCCESS; + int direction;int tableidx;int idx;int status_code; + + if (!ticket || !ticket->sipmsg) return STS_FAILURE; + + status_code = osip_message_get_status_code(ticket->sipmsg); + + /* We have an sdp, create or start relay */ + if ( MSG_IS_INVITE(ticket->sipmsg) + || ( MSG_IS_RESPONSE_FOR(ticket->sipmsg,"INVITE") && status_code == 200) ) + { + direction = rtp_find_direction(ticket); + tableidx = is_in_mediatable(ticket); + if (tableidx < -1) return STS_FAILURE; // something wrong happened ?! should be logged + + if ( tableidx == -1 && MSG_IS_INVITE(ticket->sipmsg) ) + { + DEBUGC(DBCLASS_PLUGIN, "plugin_mediarelay: " + "INVITE callid is not in mediatable, filling table"); + idx = add_mediatable_t(ticket); + { + pthread_mutex_lock(&mediatable_mutex); + sts = media_rewrite_sdp(ticket,direction,idx); + pthread_mutex_unlock(&mediatable_mutex); + } + } + else if ( tableidx > -1 && mediatable[tableidx].local_port_dst == 0 + && MSG_IS_RESPONSE_FOR(ticket->sipmsg,"INVITE") ) + { + DEBUGC(DBCLASS_PLUGIN, "plugin_mediarelay: " + "200/OK received [%d]",tableidx); + { + pthread_mutex_lock(&mediatable_mutex); + sts = media_rewrite_sdp(ticket,direction,tableidx); + pthread_mutex_unlock(&mediatable_mutex); + } + launch_rtp_thread(tableidx); + } + return sts; + } /* We have to delete created conference */ + else if ( MSG_IS_BYE(ticket->sipmsg) || MSG_IS_CANCEL(ticket->sipmsg) ) + { + DEBUGC(DBCLASS_PLUGIN, "plugin_mediarelay: " + "BYE|CANCEL received, stopping forward"); + while ( (tableidx = is_in_mediatable(ticket)) > -1 ) + { + { + pthread_mutex_lock(&mediatable_mutex); + memset(&mediatable[tableidx], 0, sizeof(mediatable[0])); + pthread_mutex_unlock(&mediatable_mutex); + } + } + } /* catch all errors corresponding to an invite */ + else if (MSG_IS_RESPONSE_FOR(ticket->sipmsg,"INVITE") && + (status_code >= 300 && status_code <= 699 ) + ) + { + DEBUGC(DBCLASS_PLUGIN, "plugin_mediarelay: " + "ERROR for invite received, deleting entries"); + while ( (tableidx = is_in_mediatable(ticket)) > -1 ) + { + { + pthread_mutex_lock(&mediatable_mutex); + memset(&mediatable[tableidx], 0, sizeof(mediatable[0])); + pthread_mutex_unlock(&mediatable_mutex); + } + } + } + return sts; +} + +/* is called by media_rewrite_sdp which is within a mutex so no problems */ +static int get_local_port() { + int local_port;int i; + for (local_port=configuration.rtp_port_low;local_port<configuration.rtp_port_high;local_port+=2) + { + for (i=0; i<RTPPROXY_SIZE; i++) + { + if (local_port == mediatable[i].local_port_orig || local_port == mediatable[i].local_port_dst) + break; + } /* for i */ + if (i<RTPPROXY_SIZE) + continue; + else + break; + } /* for (local_port)*/ + + if (local_port >= configuration.rtp_port_high) + local_port = 0; + DEBUGC(DBCLASS_PLUGIN, "plugin_mediarelay: " + "local port allocated: %d",local_port); + return local_port; +} + +static int is_local_uri(osip_uri_t *url) { + int i; + /* search for an entry */ + { + for (i=0;i<URLMAP_SIZE;i++){ + if (urlmap[i].active == 0) continue; + if ( compare_url(url, urlmap[i].true_url)==STS_SUCCESS) break; + if ( compare_url(url, urlmap[i].reg_url)==STS_SUCCESS) break; + if ( compare_url(url, urlmap[i].masq_url)==STS_SUCCESS) break; + } + } + + /* found a mapping entry */ + if (i<URLMAP_SIZE) + return 1; + else + return 0; +} + + +static int add_mediatable_t(sip_ticket_t *ticket) +{ + int freeidx=-1;int j; + osip_call_id_t *callid=NULL; + + pthread_mutex_lock(&mediatable_mutex); + + for (j=0; j<RTPPROXY_SIZE; j++) + { + if (mediatable[j].active==0) + { + freeidx=j; + break; + } + } + if (freeidx == -1) + { + ERROR("plugin_mediarelay: mediatable is full!"); + } + else + { + callid = osip_message_get_call_id(ticket->sipmsg); + if (!callid) + freeidx = -1; + else + { + mediatable[freeidx].active=1; + + if (callid->number) { + strcpy(mediatable[freeidx].callid_number, callid->number); + } else { + mediatable[freeidx].callid_number[0]='\0'; + } + + if (callid->host) { + strcpy(mediatable[freeidx].callid_host, callid->host); + } else { + mediatable[freeidx].callid_host[0]='\0'; + } + } + } + pthread_mutex_unlock(&mediatable_mutex); + + return freeidx; +} + +static int is_in_mediatable(sip_ticket_t *ticket) +{ + osip_call_id_t *callid=NULL; + osip_call_id_t cid; + int ret = -1;int i; + callid = osip_message_get_call_id(ticket->sipmsg); + + /* very important sanity checks */ + if ( ! callid ) + return -2; + if (callid->number && (strlen(callid->number) >= CALLIDNUM_SIZE)) { + ERROR("media_rewrite_sdp: received callid number [%s] " + "has too many characters (%ld, max=%i)", + callid->number, (long)strlen(callid->number),CALLIDNUM_SIZE); + return -2; + } + if (callid->host && (strlen(callid->host) >= CALLIDHOST_SIZE)) { + ERROR("media_rewrite_sdp: received callid host [%s] " + "has too many characters (%ld, max=%i)", + callid->host, (long)strlen(callid->host),CALLIDHOST_SIZE); + return -2; + } + { + pthread_mutex_lock(&mediatable_mutex); + for (i=0; i<RTPPROXY_SIZE; i++) + { + cid.number = mediatable[i].callid_number; + cid.host = mediatable[i].callid_host; + if (compare_callid(callid, &cid) == STS_SUCCESS ) + { + ret = i; + break; + } + } + pthread_mutex_unlock(&mediatable_mutex); + } + return ret; +} + +void destroy_mediatable() +{ + int i; + pthread_mutex_lock(&mediatable_mutex); + for (i=0; i<RTPPROXY_SIZE; i++) + { + if (mediatable[i].active==1) + { + memset(&mediatable[i], 0, sizeof(mediatable[0])); + } + } + pthread_mutex_unlock(&mediatable_mutex); +} + +static int rtp_find_direction(sip_ticket_t *ticket) +{ + int direction; + direction = sip_find_direction(ticket, NULL); + + if ( is_local_uri(ticket->sipmsg->to->url) && is_local_uri(ticket->sipmsg->from->url) ) + { + DEBUGC(DBCLASS_PLUGIN, "plugin_mediarelay: " + "%s to local from local",MSG_IS_INVITE(ticket->sipmsg) ? "INVITE" :"200/OK" ); + direction = DIR_INCOMING; + } + else if ( is_local_uri(ticket->sipmsg->to->url) ) // && ! is_local(ticket->sipmsg->from->url) ) + { + if ( MSG_IS_INVITE(ticket->sipmsg) ) + { + DEBUGC(DBCLASS_PLUGIN, "plugin_mediarelay: " + "INVITE to local from ext"); + direction = DIR_INCOMING; + } + else // 200/OK + { + DEBUGC(DBCLASS_PLUGIN, "plugin_mediarelay: " + "200/OK to local from ext"); + direction = DIR_OUTGOING; + } + } + else + { + if ( MSG_IS_INVITE(ticket->sipmsg) ) + { + DEBUGC(DBCLASS_PLUGIN, "plugin_mediarelay: " + "INVITE to ext from local"); + direction = DIR_OUTGOING; + } + else // 200/OK + { + DEBUGC(DBCLASS_PLUGIN, "plugin_mediarelay: " + "200/OK to ext from local"); + direction = DIR_INCOMING; + } + } + return direction; +} + +static pj_status_t create_stream( pj_pool_t *pool, + pjmedia_endpt *med_endpt, + const pjmedia_codec_info *codec_info, + pjmedia_dir dir, + pj_uint16_t local_port, + char *remote_addr, + pj_uint16_t remote_port, + pjmedia_stream **p_stream ) +{ + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "entering create_stream()"); + pjmedia_stream_info info; + pjmedia_transport *transport; + pj_status_t status; + + /* Reset stream info. */ + pj_bzero(&info, sizeof(info)); + + /* Initialize stream info formats */ + info.type = PJMEDIA_TYPE_AUDIO; +// info.proto = PJMEDIA_TP_PROTO_RTP_AVP; + info.dir = dir; + pj_memcpy(&info.fmt, codec_info, sizeof(pjmedia_codec_info)); + info.tx_pt = codec_info->pt; + info.ssrc = pj_rand(); + + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "create_stream() copying remote_addr now [%s:%d]",remote_addr,remote_port); + + /* Copy remote address */ + pj_str_t ip = pj_str(remote_addr); + status = pj_sockaddr_in_init ( &info.rem_addr,&ip,remote_port ); + if (status != PJ_SUCCESS) + ERROR("Invalid remote address [%s:%d] [%d]",remote_addr,remote_port, status); + + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "create_stream() creating udp transport"); + /* Create media transport */ + status = pjmedia_transport_udp_create(med_endpt, NULL, local_port, + 0, &transport); + if (status != PJ_SUCCESS) { + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "create_stream() FAILURE creating udp transport"); + return status; + } + /* Now that the stream info is initialized, we can create the + * stream. + */ + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "create_stream() stream creation final step"); + status = pjmedia_stream_create( med_endpt, pool, &info, + transport, NULL, p_stream); + + if (status != PJ_SUCCESS) { + ERROR("Error creating stream [%d]", status); + pjmedia_transport_udp_close(transport); + return status; + } + + return PJ_SUCCESS; +} + + +int media_rewrite_sdp(sip_ticket_t *ticket, int direction, int url_map_idx) { + int map_port = 0; /* Default */ + osip_message_t *mymsg=ticket->sipmsg; + osip_body_t *body; + sdp_message_t *sdp; + struct in_addr map_addr, addr_sess, addr_media, outside_addr, inside_addr; + int sts; + char *buff; + size_t buflen; + char clen[8]; /* content length: probably never more than 7 digits !*/ + int msg_port; + int media_stream_no; + sdp_connection_t *sdp_conn; + sdp_media_t *sdp_med; + int rtp_direction=0; + int have_c_media=0; + int isrtp = 0 ; + + map_port = get_local_port(); + if (map_port == 0) + return STS_FAILURE; + + /* + * get SDP structure + */ + sts = osip_message_get_body(mymsg, 0, &body); + if (sts != 0) { + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "no body found in message"); + return STS_FAILURE; + } + + sts = sip_body_to_str(body, &buff, &buflen); + if (sts != 0) { + ERROR("media_rewrite_sdp: unable to sip_body_to_str"); + return STS_FAILURE; + } + + DEBUGC(-1, "media_rewrite_sdp: payload %ld bytes", (long)buflen); + DUMP_BUFFER(-1, buff, buflen); + + + sts = sdp_message_init(&sdp); + sts = sdp_message_parse (sdp, buff); + if (sts != 0) { + ERROR("media_rewrite_sdp: unable to sdp_message_parse body"); + DUMP_BUFFER(-1, buff, buflen); + osip_free(buff); + sdp_message_free(sdp); + return STS_FAILURE; + } + osip_free(buff); + + /* + * RTP proxy: get ready and process + * rewrite for each media stream ('m=' item in SIP message) + */ + + /* get outbound address */ + if (get_interface_ip(IF_OUTBOUND, &outside_addr) != STS_SUCCESS) { + sdp_message_free(sdp); + return STS_FAILURE; + } + + /* get inbound address */ + if (get_interface_ip(IF_INBOUND, &inside_addr) != STS_SUCCESS) { + sdp_message_free(sdp); + return STS_FAILURE; + } + + /* figure out what address to use for RTP masquerading TO REWRITE !!! */ + /* + if (MSG_IS_INVITE && ticket->direction == DIR_INCOMING) + // rewrite using internal iface. ( vl16 ) + else if (MSG_IS_RESPONSE_FOR(ticket->sipmsg,"INVITE") && ticket->direction == DIR_OUTGOING) + // 200/OK + */ + if (MSG_IS_REQUEST(mymsg)) { + if (direction == DIR_INCOMING) { + memcpy(&map_addr, &inside_addr, sizeof (map_addr)); + rtp_direction = DIR_OUTGOING; + } else { + memcpy(&map_addr, &outside_addr, sizeof (map_addr)); + rtp_direction = DIR_INCOMING; + } + } else /* MSG_IS_REPONSE(mymsg) */ { + if (direction == DIR_INCOMING) { + memcpy(&map_addr, &inside_addr, sizeof (map_addr)); + rtp_direction = DIR_OUTGOING; + } else { + memcpy(&map_addr, &outside_addr, sizeof (map_addr)); + rtp_direction = DIR_INCOMING; + } + } + + if ( MSG_IS_INVITE(ticket->sipmsg) ) + { + memcpy(&mediatable[url_map_idx].local_ipaddr_orig, &map_addr, sizeof (mediatable[url_map_idx].local_ipaddr_orig)); + mediatable[url_map_idx].local_port_orig = map_port; + } + else + { + memcpy(&mediatable[url_map_idx].local_ipaddr_dst, &map_addr, sizeof (mediatable[url_map_idx].local_ipaddr_dst)); + mediatable[url_map_idx].local_port_dst = map_port; + } + + DEBUGC(DBCLASS_PLUGIN, "proxy_media_rewrite_sdp: SIP[%s %s] RTP[%s %s]", + MSG_IS_REQUEST(mymsg)? "RQ" : "RS", + (direction==DIR_INCOMING)? "IN" : "OUT", + (rtp_direction==DIR_INCOMING)? "IN" : "OUT", + utils_inet_ntoa(map_addr)); + + + /* + * first, check presence of a 'c=' item on session level + */ + if (sdp->c_connection==NULL || sdp->c_connection->c_addr==NULL) { + /* + * No 'c=' on session level, search on media level now + * + * According to RFC2327, ALL media description must + * include a 'c=' item now: + */ + media_stream_no=0; + while (!sdp_message_endof_media(sdp, media_stream_no)) { + /* check if n'th media stream is present */ + if (sdp_message_c_addr_get(sdp, media_stream_no, 0) == NULL) { + ERROR("SDP: have no 'c=' on session level and neither " + "on media level (media=%i)",media_stream_no); + sdp_message_free(sdp); + return STS_FAILURE; + } + media_stream_no++; + } /* while */ + } + + /* Required 'c=' items ARE present */ + + /* + * rewrite 'c=' item on session level if present and not yet done. + * remember the original address in addr_sess + */ + memset(&addr_sess, 0, sizeof(addr_sess)); + if (sdp->c_connection && sdp->c_connection->c_addr) { + sts = get_ip_by_host(sdp->c_connection->c_addr, &addr_sess); + if (sts == STS_FAILURE) { + ERROR("SDP: cannot resolve session 'c=' host [%s]", + sdp->c_connection->c_addr); + sdp_message_free(sdp); + return STS_FAILURE; + } + if ( MSG_IS_INVITE(ticket->sipmsg) ) + { + sprintf(mediatable[url_map_idx].remote_ipaddr_orig,"%s", sdp->c_connection->c_addr); + } + else + { + sprintf(mediatable[url_map_idx].remote_ipaddr_dst,"%s", sdp->c_connection->c_addr); + } + /* + * Rewrite + * an IP address of 0.0.0.0 means *MUTE*, don't rewrite such + */ + if (strcmp(sdp->c_connection->c_addr, "0.0.0.0") != 0) { + osip_free(sdp->c_connection->c_addr); + sdp->c_connection->c_addr=osip_malloc(HOSTNAME_SIZE); + sprintf(sdp->c_connection->c_addr, "%s", utils_inet_ntoa(map_addr)); + } else { + /* 0.0.0.0 - don't rewrite */ + DEBUGC(DBCLASS_PLUGIN, "proxy_mdia_rewrite_sdp: " + "got a MUTE c= record (on session level - legal?)"); + } + } + + + /* + * rewrite 'o=' item (originator) on session level if present. + */ + if (sdp->o_addrtype && sdp->o_addr) { + if (strcmp(sdp->o_addrtype, "IP4") != 0) { + ERROR("got IP6 in SDP originator - not yet suported by siproxd"); + sdp_message_free(sdp); + return STS_FAILURE; + } + + osip_free(sdp->o_addr); + sdp->o_addr=osip_malloc(HOSTNAME_SIZE); + sprintf(sdp->o_addr, "%s", utils_inet_ntoa(map_addr)); + } + + /* + * loop through all media descritions,rewrite them + */ + for (media_stream_no=0;;media_stream_no++) { + /* check if n'th media stream is present */ + if (sdp_message_m_port_get(sdp, media_stream_no) == NULL) break; + + /* + * check if a 'c=' item is present in this media description, + * if so -> rewrite it + */ + memset(&addr_media, 0, sizeof(addr_media)); + have_c_media=0; + sdp_conn=sdp_message_connection_get(sdp, media_stream_no, 0); + if (sdp_conn && sdp_conn->c_addr) { + if (strcmp(sdp_conn->c_addr, "0.0.0.0") != 0) { + sts = get_ip_by_host(sdp_conn->c_addr, &addr_media); + have_c_media=1; + /* have a valid address */ + osip_free(sdp_conn->c_addr); + sdp_conn->c_addr=osip_malloc(HOSTNAME_SIZE); + sprintf(sdp_conn->c_addr, "%s", utils_inet_ntoa(map_addr)); + } else { + /* 0.0.0.0 - don't rewrite */ + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: got a " + "MUTE c= record (media level)"); + } + } + + /* start an RTP proxying stream */ + if (sdp_message_m_port_get(sdp, media_stream_no)) { + msg_port=atoi(sdp_message_m_port_get(sdp, media_stream_no)); + if ((msg_port > 0) && (msg_port <= 65535)) { + if ( MSG_IS_INVITE(ticket->sipmsg) ) + { + mediatable[url_map_idx].remote_port_orig = msg_port; + } + else + { + mediatable[url_map_idx].remote_port_dst = msg_port; + } + client_id_t client_id; + osip_contact_t *contact = NULL; + char *tmp=NULL; + char *protocol=NULL; + + /* try to get some additional UA specific unique ID. + * This Client-ID should be guaranteed persistent + * and not depend on if a UA/Server does include a + * particular Header (Contact) or not. + * I should just go for the remote IP address here, no? + */ + /* &&& NO, using the sender IP will cause troubles if + * the remote peer (Proxy/registrar) is thrown out of + * the signalling path. Then suddenly the IP address changes. + * + * I should probably go back to the Contact Header based idea, + * with fallback to IP. + * I must use a n-item structure, including all things that + * can be used for comparing. Then, in the RTP proxy, comparison + * must take place according to priorities with the item + * with highest priority present in the RTP table *and* + * extracted from the SIP message. + * To start with, this can be just 2 things, the Contact header + * and the Layer 3 IP address. As long as the UAs include the + * Contact header, we survive IP changes (e.g. when the SIP + * Registrar/Proxy is removed from signalling path within an + * ongoing call. This may happen if the Registrar/Proxy does + * not explicitely ask to stay in the signalling path using + * Record-Route headers - which is perfectly legal + * + */ + + memset(&client_id, 0, sizeof(client_id)); + + /* get the Contact Header if present */ + osip_message_get_contact(mymsg, 0, &contact); + if (contact) osip_contact_to_str(contact, &tmp); + if (tmp) strncpy(client_id.contact, tmp, CLIENT_ID_SIZE-1); + + /* store the IP address of the sender */ + memcpy(&client_id.from_ip, &ticket->from.sin_addr, + sizeof(client_id.from_ip)); + + /* + * is this an RTP stream ? If yes, set 'isrtp=1' + */ + protocol = sdp_message_m_proto_get (sdp, media_stream_no); + if (protocol == NULL) { + DEBUGC(DBCLASS_PLUGIN, "no protocol definition found!"); + } else { + char *check; + char *cmp; + isrtp = 1; + check = protocol ; + cmp = "RTP/" ; + while (*cmp && (isrtp = isrtp && *check) && + (isrtp = isrtp && (*cmp++ == toupper(*check++))) ) {} ; + if (isrtp) { + DEBUGC(DBCLASS_PLUGIN, "found RTP protocol [%s]!", protocol); + } else { + DEBUGC(DBCLASS_PLUGIN, "found non RTP protocol [%s]!", protocol); + } + } + + /* + * do we have a 'c=' item on media level? + * if not, use the same as on session level + */ + if (have_c_media == 0) { + memcpy(&addr_media, &addr_sess, sizeof(addr_sess)); + } + + /* + * Am I running in front of the routing device? Then I cannot + * use the external IP to bind a listen socket to, but should + * use my real IP on the outbound interface (which may legally + * be the same as the inbound interface if only one interface + * is used). + */ + if ((rtp_direction == DIR_INCOMING) && + (configuration.outbound_host) && + (strcmp(configuration.outbound_host, "")!=0)) { + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "in-front-of-NAT-Router, use real outboud IP"); + if (get_interface_real_ip(IF_OUTBOUND, &map_addr) + != STS_SUCCESS) { + ERROR("cannot get my real outbound interface address"); + /* as we do not know better, take the internal address */ + memcpy(&map_addr, &inside_addr, sizeof (map_addr)); + } + } + + /* rewrite the port */ + sdp_med=osip_list_get(&(sdp->m_medias), media_stream_no); + if (sdp_med && sdp_med->m_port) { + osip_free(sdp_med->m_port); + sdp_med->m_port=osip_malloc(8); /* 5 digits, \0 + align */ + sprintf(sdp_med->m_port, "%i", map_port); + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "m= rewrote port to [%i]",map_port); + } else { + ERROR("rewriting port in m= failed sdp_med=%p, " + "m_number_of_port=%p", sdp_med, sdp_med->m_port); + } + } /* if msg_port > 0 */ + } else { + /* no port defined - skip entry */ + WARN("no port defined in m=(media) stream_no=%i", media_stream_no); + continue; + } + } /* for media_stream_no */ + + + + /* remove old body */ + sts = osip_list_remove(&(mymsg->bodies), 0); + osip_body_free(body); + + /* dump new body */ + sdp_message_to_str(sdp, &buff); + buflen=strlen(buff); + + /* free sdp structure */ + sdp_message_free(sdp); + + /* include new body */ + sip_message_set_body(mymsg, buff, buflen); + if (sts != 0) { + ERROR("media_rewrite_sdp: unable to sip_message_set_body body"); + } + + /* free content length resource and include new one*/ + osip_content_length_free(mymsg->content_length); + mymsg->content_length=NULL; + sprintf(clen,"%ld",(long)buflen); + sts = osip_message_set_content_length(mymsg, clen); + + /* free old body */ + osip_free(buff); + + return STS_SUCCESS; +} + +static void launch_rtp_thread(int tableidx) { + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: launch_rtp_thread [%d]",tableidx); + pthread_t thread; + int ret; + ret = pthread_create( &thread, NULL, run_rtp_proxy, (void*) &tableidx); /* is not unallocated */ + pj_thread_sleep(500); /* 500 ms to wait for run_rtp_proxy to get tableidx value */ +} + +/* Main thread for running conference bridge. + - We should export conf to enable other relay to start + - we should add record support +*/ +static void *run_rtp_proxy(void *ptr) +{ + /* thread stuff */ + pj_thread_desc tdesc; + pj_thread_t *thread; + + int url_map_idx; + url_map_idx = (int) *((int *) ptr); + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: starting rtp_proxy with index [%d]",url_map_idx); + + /* REGISTER new thread into PJLIB*/ + pj_thread_register(NULL,tdesc,&thread); + + /* PJMEDIA declarations */ + pjmedia_dir dir = PJMEDIA_DIR_ENCODING_DECODING; + pj_status_t status;pjmedia_transport *tp; + + pjmedia_conf *conf; + pjmedia_port *conf_port; + pjmedia_port *null; /* null port for the master to operate correctly */ + pjmedia_master_port *master_port; /* Global master port for the conf */ + const pjmedia_codec_info *codec_info; + pj_pool_t *pool; + + /* for streams */ + pjmedia_stream *stream_orig; /* Unique RTP stream */ + pjmedia_port *stream_port_orig; + unsigned media_slot_orig; /* slot index of the port in the conference bridge */ + + pjmedia_stream *stream_dst; /* Unique RTP stream */ + pjmedia_port *stream_port_dst; + unsigned media_slot_dst; /* slot index of the port in the conference bridge */ + + /* + * start PJMEDIA relay by creating a conf, easier to extend than just a buffer based relay + */ + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: get codec mgr for alaw"); + pjmedia_codec_mgr_get_codec_info( pjmedia_endpt_get_codec_mgr(med_endpt), + 8, &codec_info); + + /* Create memory pool for application purpose */ + pool = pj_pool_create( &pjPoolFactory.factory, /* pool factory */ + mediatable[url_map_idx].callid_number, /* pool name. */ + 4000, /* init size */ + 4000, /* increment size */ + NULL /* callback on error */ + ); + + status = pjmedia_conf_create( pool, /* pool to use */ + NCONFPORTS, /* number of ports */ + CLOCK_RATE, + NCHANNELS, + NSAMPLES, + NBITS, + PJMEDIA_CONF_NO_DEVICE, /* options */ + &conf /* result */ + ); + if (status != PJ_SUCCESS) + { + conf = NULL; + ERROR("Unable to create conference bridge: %d",status); + goto on_exit; + } + + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "conference ready"); + conf_port = pjmedia_conf_get_master_port(conf); + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "creating null port"); + status = pjmedia_null_port_create(pool, CLOCK_RATE, + NCHANNELS, NSAMPLES, NBITS, &null); + if (status != PJ_SUCCESS) + { + ERROR("Unable to create NULL port: %d",status); + } + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "creating master port"); + status = pjmedia_master_port_create(pool, null, conf_port, 0, &master_port); + if (status != PJ_SUCCESS) + { + ERROR("Unable to create MASTER port: %d",status); + } + /* INIT RTP STREAMS */ + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "now creating orig stream [%d] [%s:%d]", + mediatable[url_map_idx].local_port_orig, + mediatable[url_map_idx].remote_ipaddr_orig, + mediatable[url_map_idx].remote_port_orig); + status = create_stream( pool, + med_endpt, + codec_info, + dir, + mediatable[url_map_idx].local_port_orig, + mediatable[url_map_idx].remote_ipaddr_orig, + mediatable[url_map_idx].remote_port_orig, + &stream_orig + ); + if (status != PJ_SUCCESS) + { + ERROR("Unable to create stream: %d",status); + } + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "getting stream port"); + status = pjmedia_stream_get_port( stream_orig, &stream_port_orig); + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "now creating dst stream [%d] [%s:%d]", + mediatable[url_map_idx].local_port_dst, + mediatable[url_map_idx].remote_ipaddr_dst, + mediatable[url_map_idx].remote_port_dst); + status = create_stream( pool, + med_endpt, + codec_info, + dir, + mediatable[url_map_idx].local_port_dst, + mediatable[url_map_idx].remote_ipaddr_dst, + mediatable[url_map_idx].remote_port_dst, + &stream_dst + ); + if (status != PJ_SUCCESS) + { + ERROR("Unable to create stream: %d",status); + } + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "getting stream port"); + status = pjmedia_stream_get_port( stream_dst, &stream_port_dst); + + if (status != PJ_SUCCESS) + { + ERROR("Unable to get stream port: %d",status); +// return STS_FAILURE; + } + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "adding stream port to conf"); + status = pjmedia_conf_add_port( conf, + pool, + stream_port_orig, + NULL, + &media_slot_orig + ); + status = pjmedia_conf_add_port( conf, + pool, + stream_port_dst, + NULL, + &media_slot_dst + ); + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: " + "starting stream"); + pjmedia_stream_start(stream_orig); + pjmedia_stream_start(stream_dst); + + status = pjmedia_master_port_start(master_port); + // Connect the two streams in unidirectionnal way + pjmedia_conf_connect_port(conf,media_slot_orig,media_slot_dst,0); + pjmedia_conf_connect_port(conf,media_slot_dst,media_slot_orig,0); + + /* wait there for BYE */ + for (;;) + { + pthread_mutex_lock(&mediatable_mutex); + if ( mediatable[url_map_idx].active == 0 ) + break; + pthread_mutex_unlock(&mediatable_mutex); + pj_thread_sleep(500); /* sleep for 500ms waiting for bye */ + } + pthread_mutex_unlock(&mediatable_mutex); + + /* Deinit right now */ +on_exit: + DEBUGC(DBCLASS_PLUGIN, "plugin_mediarelay: " + "disconnecting conf ports"); + pjmedia_conf_disconnect_port(conf,media_slot_orig,media_slot_dst); + pjmedia_conf_disconnect_port(conf,media_slot_dst,media_slot_orig); + + DEBUGC(DBCLASS_PLUGIN, "plugin_mediarelay: " + "destroying streams now"); + if (media_slot_orig) + status = pjmedia_conf_remove_port( conf, media_slot_orig); + if (stream_port_orig) + pjmedia_port_destroy(stream_port_orig); + if (stream_orig) + { + tp = pjmedia_stream_get_transport(stream_orig); + pjmedia_stream_destroy(stream_orig); + pjmedia_transport_udp_close(tp); + } + if (media_slot_dst) + status = pjmedia_conf_remove_port( conf, media_slot_dst); + if (stream_port_dst) + pjmedia_port_destroy(stream_port_dst); + if (stream_dst) + { + tp = pjmedia_stream_get_transport(stream_dst); + pjmedia_stream_destroy(stream_dst); + pjmedia_transport_udp_close(tp); + } + + DEBUGC(DBCLASS_PLUGIN, "plugin_mediarelay: " + "destroying master port"); + if (master_port) + { + pjmedia_master_port_stop(master_port); + pjmedia_master_port_destroy(master_port, PJ_FALSE); + } + DEBUGC(DBCLASS_PLUGIN, "plugin_mediarelay: " + "destroying conference"); + if (conf) + status = pjmedia_conf_destroy( conf ); + + DEBUGC(DBCLASS_PLUGIN, "plugin_mediarelay: " + "destroying null port"); + if (null) + { + pjmedia_port_destroy(null); + } + DEBUGC(DBCLASS_PLUGIN, "plugin_mediarelay: " + "destroying pool"); + if (pool) + pj_pool_release( pool ); + + pj_thread_destroy(thread); + + pthread_exit(NULL); +} diff -rdEbU3 -ruN siproxd-0.7.0.orig/src/plugin_mediarelay.h siproxd-0.7.0/src/plugin_mediarelay.h --- siproxd-0.7.0.orig/src/plugin_mediarelay.h 1970-01-01 01:00:00.000000000 +0100 +++ siproxd-0.7.0/src/plugin_mediarelay.h 2008-02-22 15:11:31.000000000 +0100 @@ -0,0 +1,125 @@ +/* + Copyright (C) 2008 Tristan Mahé <tri...@gm...> + + This file is part of Siproxd. + + Siproxd 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. + + Siproxd 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 Siproxd; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/in.h> +#include <osipparser2/osip_parser.h> +#include <osipparser2/sdp_message.h> +#include "siproxd.h" +#include "log.h" +#include <pthread.h> + +#define PJMEDIA_SOUND_IMPLEMENTATION 0 +#define PJMEDIA_CODEC_MAX_SILENCE_PERIOD 16000 +#define PJMEDIA_MAX_MTU 1500 +#define PJMEDIA_PASOUND_MAX_LATENCY 100 + +/*Pjsip stuff */ +#include <pjlib.h> +#include <pjlib-util.h> +#include <pjmedia.h> +#include <pjmedia-codec.h> + +#include <pjmedia/session.h> +#include <pjmedia/errno.h> +#include <pjmedia/endpoint.h> +#include <pjmedia/stream.h> +#include <pjmedia/sdp.h> +#include <pj/log.h> +#include <pj/os.h> +#include <pj/pool.h> +#include <pj/string.h> +#include <pj/assert.h> +#include <pj/ctype.h> +#include <pj/rand.h> + +#include <pjsua-lib/pjsua.h> + +#define CALLIDNUM_SIZE 256 +#define CALLIDHOST_SIZE 128 + +/* Constants */ +#define CLOCK_RATE 8000 +#define NSAMPLES (CLOCK_RATE * 20 / 1000) +#define NCHANNELS 1 +#define NBITS 16 +/*Maximum ports in the conf (1 for master, 2 for streams, 1 for recorder and 1 for listener) */ +#define NCONFPORTS 5 + +extern pj_caching_pool pjPoolFactory; +extern pjmedia_endpt *med_endpt; + +extern struct siproxd_config configuration; +extern struct urlmap_s urlmap[]; /* URL mapping table */ + +/* contains the stream for ONE phone. opposite_entry contains the index for the nated relay */ +typedef struct { + int active; /* (0 -> free slot)*/ + char callid_number[CALLIDNUM_SIZE]; /* call ID */ + char callid_host[CALLIDHOST_SIZE]; /* --"-- */ + + /* For call originator*/ + struct in_addr local_ipaddr_orig; /* local IP */ + int local_port_orig; /* local allocated port */ + char remote_ipaddr_orig[32]; /* remote IP */ + int remote_port_orig; /* remote port */ + + /* For call destination */ + struct in_addr local_ipaddr_dst; /* local IP */ + int local_port_dst; /* local allocated port */ + char remote_ipaddr_dst[32]; /* remote IP */ + int remote_port_dst; /* remote port */ +} mediatable_t; + +mediatable_t mediatable[RTPPROXY_SIZE]; + +static pthread_mutex_t mediatable_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void *run_rtp_proxy(void *ptr); + +static void launch_rtp_thread(int tableidx); + +/* is called by media_rewrite_sdp which is within a mutex so no problems */ +static int get_local_port(); + +static int is_local_uri(osip_uri_t *url); + +static int add_mediatable_t(sip_ticket_t *ticket); + +static int is_in_mediatable(sip_ticket_t *ticket); + +void destroy_mediatable(); + +static int rtp_find_direction(sip_ticket_t *ticket); + +static pj_status_t create_stream( pj_pool_t *pool, + pjmedia_endpt *med_endpt, + const pjmedia_codec_info *codec_info, + pjmedia_dir dir, + pj_uint16_t local_port, + char *remote_addr, + pj_uint16_t remote_port, + pjmedia_stream **p_stream ); +/* + * is called within a mediatable mutex, so no problem to update mediatable entry + */ +int media_rewrite_sdp(sip_ticket_t *ticket, int direction, int url_map_idx); diff -rdEbU3 -ruN siproxd-0.7.0.orig/src/plugins.h siproxd-0.7.0/src/plugins.h --- siproxd-0.7.0.orig/src/plugins.h 2005-12-26 17:39:12.000000000 +0100 +++ siproxd-0.7.0/src/plugins.h 2008-02-22 14:34:49.000000000 +0100 @@ -24,3 +24,9 @@ /* short Dial plugin */ int plugin_shortdial(sip_ticket_t *ticket); + +/* replace RTP proxy with full media control */ +int plugin_mediarelay(sip_ticket_t *ticket); + +/* current media rtp destroyer */ +void destroy_mediatable(); diff -rdEbU3 -ruN siproxd-0.7.0.orig/src/siproxd.c siproxd-0.7.0/src/siproxd.c --- siproxd-0.7.0.orig/src/siproxd.c 2007-11-28 21:51:45.000000000 +0100 +++ siproxd-0.7.0/src/siproxd.c 2008-02-22 14:41:47.000000000 +0100 @@ -40,6 +40,15 @@ #include "plugins.h" #include "log.h" + +// #include <pjsua-lib/pjsua.h> +#include <pjlib.h> +#include <pjmedia.h> +#include <pjmedia-codec.h> +// #include <pjlib-util.h> + +#define PJMEDIA_SOUND_IMPLEMENTATION PJMEDIA_SOUND_NULL_SOUND + static char const ident[]="$Id: siproxd.c,v 1.72 2007/11/28 20:51:45 hb9xar Exp $"; /* configuration storage */ @@ -73,6 +82,8 @@ */ static int dmalloc_dump=0; static int exit_program=0; +struct pj_caching_pool pjPoolFactory; +struct pjmedia_endpt *med_endpt; /* * local prototypes @@ -80,6 +91,7 @@ static void sighandler(int sig); + int main (int argc, char *argv[]) { int sts; @@ -97,6 +109,18 @@ int cmdline_debuglevel=0; char *pidfilename=NULL; struct sigaction act; + pj_status_t pj_status; + + pj_status = pj_init(); + pj_caching_pool_init(&pjPoolFactory, &pj_pool_factory_default_policy, 0); + pj_status = pjmedia_endpt_create(&pjPoolFactory.factory, NULL, 2, &med_endpt); + if (pj_status != PJ_SUCCESS) + ERROR("Unable to create media endpoint: %d",pj_status); + + DEBUGC(DBCLASS_PLUGIN, "media_rewrite_sdp: init codec"); + pj_status = pjmedia_codec_g711_init(med_endpt); +// status = init_codecs( med_endpt); +// pjsua_set_no_snd_dev(); log_init(); @@ -456,7 +480,8 @@ sts = plugin_shortdial(&ticket); if (sts == STS_SIP_SENT) goto end_loop; } - + sts = plugin_mediarelay(&ticket); + if (sts == STS_SIP_SENT) goto end_loop; /********************************* * finally proxy the message. @@ -583,6 +608,17 @@ } } + /* Destroy mediatable going on to remove objects */ + destroy_mediatable(); + sleep(1); + DEBUGC(DBCLASS_PLUGIN, "PJMEDIA: " + "destroying endpoint"); + pjmedia_endpt_destroy( med_endpt ); + DEBUGC(DBCLASS_PLUGIN, "PJMEDIA: " + "destroying pool factory"); + pj_caching_pool_destroy( &pjPoolFactory ); + pj_shutdown(); + /* END */ log_end(); return 0; |