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
977 lines
31 KiB
C
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;
|
|
}
|