Files
siproxd/src/proxy.c
Thomas Ries 2315f495d4 - Allow 2 of my vias in header to let 2 UA's sitting
behind the same siproxd to have conversation together
  UA1 -->--\       /-->--\
            siproxd       Registrar
  UA2 --<--/       \--<--/
- Redone code for evaluation if a received packet
  if coming from inbound or outbound interface
- RTP stream are now identified by call_id AND
  USERNAME of the contact header. This provides
  support for RTP proxying between 2 UAs sitting on the
  inbound network. -> Calls between local UAs going via
  siproxd should now work.
  UA1 -->--\
            siproxd
  UA2 --<--/
- Rewriting of SUBSCRIBE should now work.
- Removed obsolete prototypes from rtpproxy.h
- If the RTP stream in one direction is found to be
  stopped (sendto()) also stop the opposite direction
2004-02-01 01:44:42 +00:00

977 lines
31 KiB
C

/* -*- Mode: C; c-basic-offset: 3 -*-
Copyright (C) 2002 Thomas Ries <tries@gmx.net>
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 "config.h"
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <osipparser2/osip_parser.h>
#include <osipparser2/sdp_message.h>
#include "siproxd.h"
#include "log.h"
static char const ident[]="$Id: " __FILE__ ": " PACKAGE "-" VERSION "-"\
BUILDSTR " $";
/* configuration storage */
extern struct siproxd_config configuration;
extern int errno;
extern struct urlmap_s urlmap[]; /* URL mapping table */
extern struct lcl_if_s local_addresses;
extern int sip_socket; /* sending SIP datagrams */
/*
* PROXY_REQUEST
*
* RETURNS
* STS_SUCCESS on success
* STS_FAILURE on error
*
* RFC3261
* Section 16.3: Proxy Behavior - Request Validation
* 1. Reasonable Syntax
* 2. URI scheme
* 3. Max-Forwards
* 4. (Optional) Loop Detection
* 5. Proxy-Require
* 6. Proxy-Authorization
*
* Section 16.6: Proxy Behavior - Request Forwarding
* 1. Make a copy of the received request
* 2. Update the Request-URI
* 3. Update the Max-Forwards header field
* 4. Optionally add a Record-route header field value
* 5. Optionally add additional header fields
* 6. Postprocess routing information
* 7. Determine the next-hop address, port, and transport
* 8. Add a Via header field value
* 9. Add a Content-Length header field if necessary
* 10. Forward the new request
* 11. Set timer C
*/
int proxy_request (osip_message_t *request, struct sockaddr_in *from) {
int i;
int sts;
int type;
struct in_addr sendto_addr;
osip_uri_t *url;
int port;
char *buffer;
#define REQTYP_INCOMING 1
#define REQTYP_OUTGOING 2
DEBUGC(DBCLASS_PROXY,"proxy_request");
/*
* RFC 3261, Section 16.6 step 1
* Proxy Behavior - Request Forwarding - Make a copy
*/
/* nothing to do here, copy is ready in 'request'*/
/*
* RFC 3261, Section 16.6 step 3
* Proxy Behavior - Request Forwarding - Max-Forwards
* (if Max-Forward header exists, decrement by one, if it does not
* exist, add a new one with value SHOULD be 70)
*/
/* NOT IMPLEMENTED */
/*
* RFC 3261, Section 16.6 step 4
* Proxy Behavior - Request Forwarding - Add a Record-route header
*/
/* NOT IMPLEMENTED (optional) */
/*
* RFC 3261, Section 16.6 step 5
* Proxy Behavior - Request Forwarding - Add Additional Header Fields
*/
/* NOT IMPLEMENTED (optional) */
/*
* RFC 3261, Section 16.6 step 6
* Proxy Behavior - Request Forwarding - Postprocess routing information
*/
/* NOT IMPLEMENTED */
/*
* RFC 3261, Section 16.4
* Proxy Behavior - Route Information Preprocessing
* (process Record-Route header)
*/
/*
* Check if I am listed at the topmost Route header (if any Route
* header is existing at all). If so, remove it from the list.
*/
if (request->routes && !osip_list_eol(request->routes, 0)) {
struct in_addr addr1, addr2, addr3;
osip_route_t *route;
route = (osip_route_t *) osip_list_get(request->routes, 0);
sts = get_ip_by_host(route->url->host, &addr1);
get_ip_by_ifname(configuration.inbound_if, &addr2);
get_ip_by_ifname(configuration.outbound_if, &addr3);
if ((sts == STS_SUCCESS) &&
((memcmp(&addr1, &addr2, sizeof(addr1)) == 0) ||
(memcmp(&addr1, &addr3, sizeof(addr1)) == 0)) &&
(route->url->port ?
configuration.sip_listen_port == atoi(route->url->port):
configuration.sip_listen_port == SIP_PORT)) {
osip_list_remove(request->routes, 0);
osip_route_free(route);
/* possibly request->routes will be freed by osip_message_free() */
}
}
/*
* figure out whether this is an incoming or outgoing request
* by doing a lookup in the registration table.
*/
#define _OLD_DIRECTION_EVALUATION 0
#if _OLD_DIRECTION_EVALUATION
type = 0;
for (i=0; i<URLMAP_SIZE; i++) {
if (urlmap[i].active == 0) continue;
/* incoming request ('to' == 'masq') || (('to' == 'reg') && !REGISTER)*/
if ((compare_url(request->to->url, urlmap[i].masq_url)==STS_SUCCESS) ||
(!MSG_IS_REGISTER(request) &&
(compare_url(request->to->url, urlmap[i].reg_url)==STS_SUCCESS))) {
type=REQTYP_INCOMING;
DEBUGC(DBCLASS_PROXY,"incoming request from %s@%s from outbound",
request->from->url->username? request->from->url->username:"*NULL*",
request->from->url->host? request->from->url->host: "*NULL*");
break;
}
/* outgoing request ('from' == 'reg') */
if (compare_url(request->from->url, urlmap[i].reg_url)==STS_SUCCESS) {
type=REQTYP_OUTGOING;
DEBUGC(DBCLASS_PROXY,"outgoing request from %s@%s from inbound",
request->from->url->username? request->from->url->username:"*NULL*",
request->from->url->host? request->from->url->host: "*NULL*");
break;
}
}
#else
type = 0;
/*
* did I receive the telegram from a REGISTERED host?
* -> it must be an OUTGOING request
*/
for (i=0; i<URLMAP_SIZE; i++) {
struct in_addr tmp_addr;
if (urlmap[i].active == 0) continue;
if (get_ip_by_host(urlmap[i].true_url->host, &tmp_addr) == STS_FAILURE) {
DEBUGC(DBCLASS_PROXY, "proxy_request: cannot resolve host [%s]",
urlmap[i].true_url);
} else {
DEBUGC(DBCLASS_PROXY, "proxy_request: reghost:%s ip:%s",
urlmap[i].true_url->host, utils_inet_ntoa(from->sin_addr));
if (memcmp(&tmp_addr, &from->sin_addr, sizeof(tmp_addr)) == 0) {
type=REQTYP_OUTGOING;
break;
}
}
}
/*
* is the telegram directed to an internal registered host?
* -> it must be an INCOMING request
*/
if (type == 0) for (i=0; i<URLMAP_SIZE; i++) {
if (urlmap[i].active == 0) continue;
/* incoming request ('to' == 'masq') || (('to' == 'reg') && !REGISTER)*/
if ((compare_url(request->to->url, urlmap[i].masq_url)==STS_SUCCESS) ||
(!MSG_IS_REGISTER(request) &&
(compare_url(request->to->url, urlmap[i].reg_url)==STS_SUCCESS))) {
type=REQTYP_INCOMING;
break;
}
}
#endif
/*
* ok, we got a request that we are allowed to process.
*/
#ifdef HACK1
/* linphone-0.9.0pre4
take To address and place it into URI (at least the host part)
Linphone-0.9.0pre4 puts the proxy host in the request URI
if OUTBOUND proxy is activated!
This is only a hack to recreate the proper final request URI.
This issue has been fixed in 0.9.1pre1
*/
{
osip_header_t *header_ua;
osip_uri_t *url2;
url2=osip_message_get_uri(request);
osip_message_get_user_agent(request,0,&header_ua);
if ( header_ua && header_ua->hvalue &&
(strcmp(header_ua->hvalue,"oSIP/Linphone-0.8.0")==0) ) {
/* if an outgoing request, try to fix the SIP URI */
if (type == REQTYP_OUTGOING) {
WARN("broken linphone-0.8.0: restoring SIP URI");
free (url2->host);
url2->host=malloc(strlen(request->to->url->host));
strcpy(url2->host,request->to->url->host);
}
}
}
#endif
/*
* logging of passing calls
*/
if (configuration.log_calls) {
osip_uri_t *cont_url = NULL;
if (!osip_list_eol(request->contacts, 0))
cont_url = ((osip_contact_t*)(request->contacts->node->element))->url;
/* INVITE */
if (MSG_IS_INVITE(request)) {
if (cont_url) {
INFO("%s Call from: %s:%s",
(type==REQTYP_INCOMING) ? "Incoming":"Outgoing",
cont_url->username ? cont_url->username:"*NULL*",
cont_url->host ? cont_url->host : "*NULL*");
} else {
INFO("%s Call (w/o contact header) from: %s:%s",
(type==REQTYP_INCOMING) ? "Incoming":"Outgoing",
request->from->url->username ?
request->from->url->username:"*NULL*",
request->from->url->host ?
request->from->url->host : "*NULL*");
}
/* BYE / CANCEL */
} else if (MSG_IS_BYE(request) || MSG_IS_CANCEL(request)) {
if (cont_url) {
INFO("Ending Call from: %s:%s",
cont_url->username ? cont_url->username:"*NULL*",
cont_url->host ? cont_url->host : "*NULL*");
} else {
INFO("Ending Call (w/o contact header) from: %s:%s",
request->from->url->username ?
request->from->url->username:"*NULL*",
request->from->url->host ?
request->from->url->host : "*NULL*");
}
}
} /* log_calls */
/* get destination address */
url=osip_message_get_uri(request);
switch (type) {
/*
* from an external host to the internal masqueraded host
*/
case REQTYP_INCOMING:
DEBUGC(DBCLASS_PROXY,"incoming request from %s@%s from outbound",
request->from->url->username? request->from->url->username:"*NULL*",
request->from->url->host? request->from->url->host: "*NULL*");
/*
* RFC 3261, Section 16.6 step 2
* Proxy Behavior - Request Forwarding - Request-URI
* (rewrite request URI to point to the real host)
*/
/* 'i' still holds the valid index into the URLMAP table */
if (check_rewrite_rq_uri(request) == STS_TRUE) {
proxy_rewrite_request_uri(request, i);
}
/*
* RFC 3261, Section 16.6 step 8
* Proxy Behavior - Add a Via header field value
*/
/* add my Via header line (inbound interface)*/
sts = sip_add_myvia(request, IF_INBOUND);
if (sts == STS_FAILURE) {
ERROR("adding my inbound via failed!");
return STS_FAILURE;
}
/* if this is CANCEL/BYE request, stop RTP proxying */
if (MSG_IS_BYE(request) || MSG_IS_CANCEL(request)) {
/* stop the RTP proxying stream(s) */
rtp_stop_fwd(osip_message_get_call_id(request), DIR_INCOMING);
rtp_stop_fwd(osip_message_get_call_id(request), DIR_OUTGOING);
/* check for incoming request */
} else if (MSG_IS_INVITE(request)) {
/* First, rewrite the body */
if (configuration.rtp_proxy_enable == 1) {
sts = proxy_rewrite_invitation_body(request, DIR_INCOMING);
}
/*
* Note: Incoming requests have no need to rewrite Contact
* header - as we are not masquerading something there
*/
}
break;
/*
* from the internal masqueraded host to an external host
*/
case REQTYP_OUTGOING:
DEBUGC(DBCLASS_PROXY,"outgoing request from %s@%s from inbound",
request->from->url->username? request->from->url->username:"*NULL*",
request->from->url->host? request->from->url->host: "*NULL*");
/*
* RFC 3261, Section 16.6 step 2
* Proxy Behavior - Request Forwarding - Request-URI
*/
/* nothing to do for an outgoing request */
/* if it is addressed to myself, then it must be some request
* method that I as a proxy do not support. Reject */
#if 0
if (is_sipuri_local(request) == STS_TRUE) {
WARN("unsupported request [%s] directed to proxy from %s@%s -> %s@%s",
request->sip_method? request->sip_method:"*NULL*",
request->from->url->username? request->from->url->username:"*NULL*",
request->from->url->host? request->from->url->host : "*NULL*",
url->username? url->username : "*NULL*",
url->host? url->host : "*NULL*");
sip_gen_response(request, 403 /*forbidden*/);
return STS_FAILURE;
}
#endif
/* if an INVITE, rewrite body */
if (MSG_IS_INVITE(request)) {
sts = proxy_rewrite_invitation_body(request, DIR_OUTGOING);
}
/* rewrite Contact header to represent the masqued address */
sip_rewrite_contact(request, DIR_OUTGOING);
/*
* RFC 3261, Section 16.8
* Proxy Behavior - Add a Via header field value
*/
/* add my Via header line (outbound interface)*/
sts = sip_add_myvia(request, IF_OUTBOUND);
if (sts == STS_FAILURE) {
ERROR("adding my outbound via failed!");
}
/* if this is CANCEL/BYE request, stop RTP proxying */
if (MSG_IS_BYE(request) || MSG_IS_CANCEL(request)) {
/* stop the RTP proxying stream(s) */
rtp_stop_fwd(osip_message_get_call_id(request), DIR_INCOMING);
rtp_stop_fwd(osip_message_get_call_id(request), DIR_OUTGOING);
}
break;
default:
DEBUGC(DBCLASS_PROXY, "request [%s] from/to unregistered UA "
"(RQ: %s@%s -> %s@%s)",
request->sip_method? request->sip_method:"*NULL*",
request->from->url->username? request->from->url->username:"*NULL*",
request->from->url->host? request->from->url->host : "*NULL*",
url->username? url->username : "*NULL*",
url->host? url->host : "*NULL*");
/*
* we may end up here for two reasons:
* 1) An incomming request (from outbound) that is directed to
* an unknown (not registered) local UA
* 2) an outgoing request from a local UA that is not registered.
*
* Case 1) we should probably answer with "404 Not Found",
* case 2) more likely a "403 Forbidden"
*
* How about "408 Request Timeout" ?
*
*/
sip_gen_response(request, 408 /* Request Timeout */);
return STS_FAILURE;
}
/*
* RFC 3261, Section 16.6 step 7
* Proxy Behavior - Determine Next-Hop Address
*/
if ((type == REQTYP_OUTGOING) && (configuration.outbound_proxy_host)) {
/* I have an outbound proxy configured */
sts = get_ip_by_host(configuration.outbound_proxy_host, &sendto_addr);
if (sts == STS_FAILURE) {
DEBUGC(DBCLASS_PROXY, "proxy_request: cannot resolve outbound "
" proxy host [%s]", configuration.outbound_proxy_host);
return STS_FAILURE;
}
if (configuration.outbound_proxy_port) {
port=configuration.outbound_proxy_port;
} else {
port = SIP_PORT;
}
} else {
/* get the destination from the SIP URI */
sts = get_ip_by_host(url->host, &sendto_addr);
if (sts == STS_FAILURE) {
DEBUGC(DBCLASS_PROXY, "proxy_request: cannot resolve URI [%s]",
url->host);
return STS_FAILURE;
}
if (url->port) {
port=atoi(url->port);
} else {
port=SIP_PORT;
}
}
/*
* RFC 3261, Section 16.6 step 9
* Proxy Behavior - Add a Content-Length header field if necessary
*/
/* not necessary, already in message and we do not support TCP */
/*
* RFC 3261, Section 16.6 step 10
* Proxy Behavior - Forward the new request
*/
sts = osip_message_to_str(request, &buffer);
if (sts != 0) {
ERROR("proxy_request: osip_message_to_str failed");
return STS_FAILURE;
}
sipsock_send_udp(&sip_socket, sendto_addr, port, buffer, strlen(buffer), 1);
osip_free (buffer);
/*
* RFC 3261, Section 16.6 step 11
* Proxy Behavior - Set timer C
*/
/* NOT IMPLEMENTED - does this really apply for stateless proxies? */
return STS_SUCCESS;
}
/*
* PROXY_RESPONSE
*
* RETURNS
* STS_SUCCESS on success
* STS_FAILURE on error
*/
int proxy_response (osip_message_t *response, struct sockaddr_in *from) {
int i;
int sts;
int type;
struct in_addr sendto_addr;
osip_via_t *via;
int port;
char *buffer;
#define RESTYP_INCOMING 1
#define RESTYP_OUTGOING 2
DEBUGC(DBCLASS_PROXY,"proxy_response");
/*
* RFC 3261, Section 16.11
* Proxy Behavior - Remove my Via header field value
*/
/* remove my Via header line */
sts = sip_del_myvia(response);
if (sts == STS_FAILURE) {
DEBUGC(DBCLASS_PROXY,"not addressed to my VIA, ignoring response");
return STS_FAILURE;
}
/*
* figure out if this is an request coming from the outside
* world to one of our registered clients
*/
/* Ahhrghh...... a response seems to have NO contact information...
* so let's take FROM instead...
* the TO and FROM headers are EQUAL to the request - that means
* they are swapped in their meaning for a response...
*/
#if _OLD_DIRECTION_EVALUATION
type = 0;
for (i=0; i<URLMAP_SIZE; i++) {
if (urlmap[i].active == 0) continue;
/* incoming response ('from' == 'masq') || ('from' == 'reg') */
if ((compare_url(response->from->url, urlmap[i].reg_url)==STS_SUCCESS) ||
(compare_url(response->from->url, urlmap[i].masq_url)==STS_SUCCESS)) {
type=RESTYP_INCOMING;
DEBUGC(DBCLASS_PROXY,"incoming response for %s@%s from outbound",
response->from->url->username? response->from->url->username:"*NULL*",
response->from->url->host? response->from->url->host : "*NULL*");
break;
}
/* outgoing response ('to' == 'reg') || ('to' == 'masq' ) */
if ((compare_url(response->to->url, urlmap[i].masq_url)==STS_SUCCESS) ||
(compare_url(response->to->url, urlmap[i].reg_url)==STS_SUCCESS)){
type=RESTYP_OUTGOING;
DEBUGC(DBCLASS_PROXY,"outgoing response for %s@%s from inbound",
response->from->url->username ?
response->from->url->username : "*NULL*",
response->from->url->host ?
response->from->url->host : "*NULL*");
break;
}
}
#else
type = 0;
/*
* did I receive the telegram from a REGISTERED host?
* -> it must be an OUTGOING response
*/
for (i=0; i<URLMAP_SIZE; i++) {
struct in_addr tmp_addr;
if (urlmap[i].active == 0) continue;
if (get_ip_by_host(urlmap[i].true_url->host, &tmp_addr) == STS_FAILURE) {
DEBUGC(DBCLASS_PROXY, "proxy_request: cannot resolve host [%s]",
urlmap[i].true_url);
} else {
DEBUGC(DBCLASS_PROXY, "proxy_request: reghost:%s ip:%s",
urlmap[i].true_url->host, utils_inet_ntoa(from->sin_addr));
if (memcmp(&tmp_addr, &from->sin_addr, sizeof(tmp_addr)) == 0) {
type=RESTYP_OUTGOING;
break;
}
}
}
/*
* is the telegram directed to an internal registered host?
* -> it must be an INCOMING response
*/
if (type == 0) for (i=0; i<URLMAP_SIZE; i++) {
if (urlmap[i].active == 0) continue;
/* incoming response ('from' == 'masq') || ('from' == 'reg') */
if ((compare_url(response->from->url, urlmap[i].reg_url)==STS_SUCCESS) ||
(compare_url(response->from->url, urlmap[i].masq_url)==STS_SUCCESS)) {
type=RESTYP_INCOMING;
break;
}
}
#endif
/*
* ok, we got a response that we are allowed to process.
*/
switch (type) {
/*
* from an external host to the internal masqueraded host
*/
case RESTYP_INCOMING:
DEBUGC(DBCLASS_PROXY,"incoming response for %s@%s from outbound",
response->from->url->username? response->from->url->username:"*NULL*",
response->from->url->host? response->from->url->host : "*NULL*");
if ((MSG_IS_RESPONSE_FOR(response,"INVITE")) &&
((MSG_TEST_CODE(response, 200)) ||
(MSG_TEST_CODE(response, 183)))) {
/* This is an incoming response, therefore we need an incoming stream */
if (configuration.rtp_proxy_enable == 1) {
sts = proxy_rewrite_invitation_body(response, DIR_INCOMING);
}
}
if (MSG_IS_RESPONSE_FOR(response,"REGISTER")) {
/*
* REGISTER returns *my* Contact header information.
* Rewrite Contact header back to represent the true address.
* Other responses do return the Contact header of the sender.
*/
sip_rewrite_contact(response, DIR_INCOMING);
}
break;
/*
* from the internal masqueraded host to an external host
*/
case RESTYP_OUTGOING:
DEBUGC(DBCLASS_PROXY,"outgoing response for %s@%s from inbound",
response->from->url->username ?
response->from->url->username : "*NULL*",
response->from->url->host ?
response->from->url->host : "*NULL*");
#define satoi atoi /* used in MSG_TEST_CODE macro ... */
/* If an 200 OK or 183 Trying answer to an INVITE request,
* rewrite body */
if ((MSG_IS_RESPONSE_FOR(response,"INVITE")) &&
((MSG_TEST_CODE(response, 200)) ||
(MSG_TEST_CODE(response, 183)))) {
/* This is an outgoing response, therefore an outgoing stream */
sts = proxy_rewrite_invitation_body(response, DIR_OUTGOING);
}
/* rewrite Contact header to represent the masqued address */
sip_rewrite_contact(response, DIR_OUTGOING);
break;
default:
DEBUGC(DBCLASS_PROXY, "response from/to unregistered UA (%s@%s)",
response->from->url->username? response->from->url->username:"*NULL*",
response->from->url->host? response->from->url->host : "*NULL*");
return STS_FAILURE;
}
/*
* check if we need to send to an outbound proxy
*/
if ((type == RESTYP_OUTGOING) && (configuration.outbound_proxy_host)) {
/* have an outbound proxy - use it to send the packet */
sts = get_ip_by_host(configuration.outbound_proxy_host, &sendto_addr);
if (sts == STS_FAILURE) {
DEBUGC(DBCLASS_PROXY, "proxy_request: cannot resolve outbound "
" proxy host [%s]", configuration.outbound_proxy_host);
return STS_FAILURE;
}
if (configuration.outbound_proxy_port) {
port=configuration.outbound_proxy_port;
} else {
port = SIP_PORT;
}
} else {
/* get target address and port from VIA header */
via = (osip_via_t *) osip_list_get (response->vias, 0);
if (via == NULL) {
ERROR("proxy_response: list_get via failed");
return STS_FAILURE;
}
sts = get_ip_by_host(via->host, &sendto_addr);
if (sts == STS_FAILURE) {
DEBUGC(DBCLASS_PROXY, "proxy_response: cannot resolve VIA [%s]",
via->host);
return STS_FAILURE;
}
if (via->port) {
port=atoi(via->port);
} else {
port=SIP_PORT;
}
}
sts = osip_message_to_str(response, &buffer);
if (sts != 0) {
ERROR("proxy_response: osip_message_to_str failed");
return STS_FAILURE;
}
sipsock_send_udp(&sip_socket, sendto_addr, port, buffer, strlen(buffer), 1);
osip_free (buffer);
return STS_SUCCESS;
}
/*
* PROXY_REWRITE_INVITATION_BODY
*
* rewrites the outgoing INVITATION request or response packet
*
* RETURNS
* STS_SUCCESS on success
* STS_FAILURE on error
*/
int proxy_rewrite_invitation_body(osip_message_t *mymsg, int direction){
osip_body_t *body;
sdp_message_t *sdp;
struct in_addr map_addr, msg_addr, outside_addr, inside_addr;
int sts;
char *bodybuff;
char clen[8]; /* content length: probably never more than 7 digits !*/
int map_port, msg_port;
int media_stream_no;
sdp_connection_t *sdp_conn;
sdp_media_t *sdp_med;
/*
* get SDP structure
*/
sts = osip_message_get_body(mymsg, 0, &body);
if (sts != 0) {
if ((MSG_IS_RESPONSE_FOR(mymsg,"INVITE")) &&
(MSG_TEST_CODE(mymsg, 183))) {
/* 183 Trying *MAY* contain SDP data */
DEBUGC(DBCLASS_PROXY, "rewrite_invitation_body: "
"no body found in message");
return STS_SUCCESS;
} else {
/* INVITE request and 200 response *MUST* contain SDP data */
ERROR("rewrite_invitation_body: no body found in message");
return STS_FAILURE;
}
}
sts = osip_body_to_str(body, &bodybuff);
sts = sdp_message_init(&sdp);
sts = sdp_message_parse (sdp, bodybuff);
osip_free(bodybuff);
if (sts != 0) {
ERROR("rewrite_invitation_body: unable to sdp_message_parse body");
sdp_message_free(sdp);
return STS_FAILURE;
}
if (configuration.debuglevel)
{ /* just dump the buffer */
char *tmp, *tmp2;
sts = osip_message_get_body(mymsg, 0, &body);
sts = osip_body_to_str(body, &tmp);
osip_content_length_to_str(mymsg->content_length, &tmp2);
DEBUG("Body before rewrite (clen=%s, strlen=%i):\n%s\n----",
tmp2, strlen(tmp), tmp);
osip_free(tmp);
osip_free(tmp2);
}
/*
* RTP proxy: get ready and start forwarding
* start forwarding for each media stream ('m=' item in SIP message)
*/
sts = get_ip_by_host(sdp_message_c_addr_get(sdp,-1,0), &msg_addr);
if (sts == STS_FAILURE) {
DEBUGC(DBCLASS_PROXY, "proxy_rewrite_invitation_body: cannot resolve "
"m= (media) host [%s]", sdp_message_c_addr_get(sdp,-1,0));
sdp_message_free(sdp);
return STS_FAILURE;
}
sts = get_ip_by_ifname(configuration.outbound_if, &outside_addr);
if (sts == STS_FAILURE) {
ERROR("can't find outbound interface %s - configuration error?",
configuration.outbound_if);
sdp_message_free(sdp);
return STS_FAILURE;
}
sts = get_ip_by_ifname(configuration.inbound_if, &inside_addr);
if (sts == STS_FAILURE) {
ERROR("can't find inbound interface %s - configuration error?",
configuration.inbound_if);
sdp_message_free(sdp);
return STS_FAILURE;
}
/* figure out what address to use for RTP masquerading */
if (MSG_IS_REQUEST(mymsg)) {
if (direction == DIR_INCOMING)
map_addr = inside_addr;
else
map_addr = outside_addr;
} else /* MSG_IS_REPONSE(mymsg) */ {
if (direction == DIR_INCOMING)
map_addr = inside_addr;
else
map_addr = outside_addr;
}
/*
* rewrite c= address
* !! an IP address of 0.0.0.0 means *MUTE*, don't rewrite such one
*/
sdp_conn = sdp_message_connection_get (sdp, -1, 0);
if (sdp_conn && sdp_conn->c_addr) {
if (strcmp(sdp_conn->c_addr, "0.0.0.0") != 0) {
/* 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_PROXY, "proxy_rewrite_invitation_body: got a MUTE c= record");
}
} else {
ERROR("got NULL c= address record - can't rewrite");
}
/*
* loop through all m= descritions,
* start RTP proxy and rewrite them
*/
if (configuration.rtp_proxy_enable == 1) {
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;
/* 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) {
osip_uri_t *cont_url = NULL;
char *user=NULL;
if (!osip_list_eol(mymsg->contacts, 0))
cont_url = ((osip_contact_t*)(mymsg->contacts->node->element))->url;
if (cont_url) user=cont_url->username;
rtp_start_fwd(osip_message_get_call_id(mymsg),
user,
direction,
media_stream_no,
map_addr, &map_port,
msg_addr, msg_port);
/* and 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_PROXY, "proxy_rewrite_invitation_body: "
"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 */
} /* if rtp_proxy_enable */
/* remove old body */
sts = osip_list_remove(mymsg->bodies, 0);
osip_body_free(body);
/* dump new body */
sdp_message_to_str(sdp, &bodybuff);
/* free sdp structure */
sdp_message_free(sdp);
/* include new body */
osip_message_set_body(mymsg, bodybuff);
/* free content length resource and include new one*/
osip_content_length_free(mymsg->content_length);
mymsg->content_length=NULL;
sprintf(clen,"%i",strlen(bodybuff));
sts = osip_message_set_content_length(mymsg, clen);
/* free old body */
osip_free(bodybuff);
if (configuration.debuglevel)
{ /* just dump the buffer */
char *tmp, *tmp2;
sts = osip_message_get_body(mymsg, 0, &body);
sts = osip_body_to_str(body, &tmp);
osip_content_length_to_str(mymsg->content_length, &tmp2);
DEBUG("Body after rewrite (clen=%s, strlen=%i):\n%s\n----",
tmp2, strlen(tmp), tmp);
osip_free(tmp);
osip_free(tmp2);
}
return STS_SUCCESS;
}
/*
* PROXY_REWRITE_REQUEST_URI
*
* rewrites the incoming Request URI
*
* RETURNS
* STS_SUCCESS on success
*/
int proxy_rewrite_request_uri(osip_message_t *mymsg, int idx){
char *host;
char *port;
osip_uri_t *url;
if ((idx >= URLMAP_SIZE) || (idx < 0)) {
WARN("proxy_rewrite_request_uri: called with invalid index");
return STS_FAILURE;
}
DEBUGC(DBCLASS_PROXY,"rewriting incoming Request URI");
url=osip_message_get_uri(mymsg);
/* set the true host */
if(urlmap[idx].true_url->host) {
osip_free(url->host);url->host=NULL;
DEBUGC(DBCLASS_BABBLE,"proxy_rewrite_request_uri: host=%s",
urlmap[idx].true_url->host);
host = (char *)malloc(strlen(urlmap[idx].true_url->host)+1);
memcpy(host, urlmap[idx].true_url->host, strlen(urlmap[idx].true_url->host));
host[strlen(urlmap[idx].true_url->host)]='\0';
osip_uri_set_host(url, host);
}
/* set the true port */
if(urlmap[idx].true_url->port) {
osip_free(url->port);url->port=NULL;
DEBUGC(DBCLASS_BABBLE,"proxy_rewrite_request_uri: port=%s",
urlmap[idx].true_url->port);
port = (char *)malloc(strlen(urlmap[idx].true_url->port)+1);
memcpy(port, urlmap[idx].true_url->port, strlen(urlmap[idx].true_url->port));
port[strlen(urlmap[idx].true_url->port)]='\0';
osip_uri_set_port(url, port);
}
return STS_SUCCESS;
}