867 lines
24 KiB
C
867 lines
24 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 <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <net/if.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <pwd.h>
|
|
|
|
#include <osipparser2/osip_parser.h>
|
|
#include <osipparser2/osip_md5.h>
|
|
|
|
#include "siproxd.h"
|
|
#include "digcalc.h"
|
|
#include "rewrite_rules.h"
|
|
#include "log.h"
|
|
|
|
static char const ident[]="$Id$";
|
|
|
|
|
|
/* configuration storage */
|
|
extern struct siproxd_config configuration;
|
|
|
|
extern int h_errno;
|
|
|
|
extern struct urlmap_s urlmap[]; /* URL mapping table */
|
|
|
|
|
|
/*
|
|
* create a reply template from an given SIP request
|
|
*
|
|
* RETURNS a pointer to osip_message_t
|
|
*/
|
|
osip_message_t *msg_make_template_reply (osip_message_t * request, int code) {
|
|
osip_message_t *response;
|
|
int pos;
|
|
|
|
osip_message_init (&response);
|
|
response->message=NULL;
|
|
osip_message_set_version (response, osip_strdup ("SIP/2.0"));
|
|
osip_message_set_status_code (response, code);
|
|
osip_message_set_reason_phrase (response,
|
|
osip_strdup(osip_message_get_reason (code)));
|
|
|
|
if (request->to==NULL) {
|
|
ERROR("msg_make_template_reply: empty To in request header");
|
|
return NULL;
|
|
}
|
|
|
|
if (request->from==NULL) {
|
|
ERROR("msg_make_template_reply: empty From in request header");
|
|
return NULL;
|
|
}
|
|
|
|
osip_to_clone (request->to, &response->to);
|
|
osip_from_clone (request->from, &response->from);
|
|
|
|
|
|
/* via headers */
|
|
pos = 0;
|
|
while (!osip_list_eol (request->vias, pos)) {
|
|
char *tmp;
|
|
osip_via_t *via;
|
|
via = (osip_via_t *) osip_list_get (request->vias, pos);
|
|
osip_via_to_str (via, &tmp);
|
|
|
|
osip_message_set_via (response, tmp);
|
|
osip_free (tmp);
|
|
pos++;
|
|
}
|
|
|
|
osip_call_id_clone(request->call_id,&response->call_id);
|
|
|
|
osip_cseq_clone(request->cseq,&response->cseq);
|
|
|
|
return response;
|
|
}
|
|
|
|
|
|
/*
|
|
* check for a via loop.
|
|
* It checks for the presense of a via entry that holds one of
|
|
* my IP addresses and is *not* the topmost via.
|
|
*
|
|
* RETURNS
|
|
* STS_TRUE if loop detected
|
|
* STS_FALSE if no loop
|
|
*/
|
|
int check_vialoop (osip_message_t *my_msg) {
|
|
/*
|
|
!!! actually this is a problematic one.
|
|
1) for requests, I must search the whole VIA list
|
|
(topmost via is the previos station in the path)
|
|
|
|
2) for responses I must skip the topmost via, as this is mine
|
|
(and will be removed later on)
|
|
|
|
3) What happens if we have 'clashes' with private addresses??
|
|
From that point of view, siproxd *should* not try to
|
|
check against it's local IF addresses if they are private.
|
|
this then of course again can lead to a endless loop...
|
|
-> Use a fixed unique part of branch parameter to identify that it
|
|
is MY via
|
|
|
|
can we use something like a Tag in via headers?? (a veriy likely
|
|
to-be-unique ID)
|
|
|
|
*/
|
|
int sts;
|
|
int pos;
|
|
int found_own_via;
|
|
|
|
found_own_via=0;
|
|
pos = 1; /* for detecting a loop, don't check the first entry
|
|
as this is my own VIA! */
|
|
while (!osip_list_eol (my_msg->vias, pos)) {
|
|
osip_via_t *via;
|
|
via = (osip_via_t *) osip_list_get (my_msg->vias, pos);
|
|
sts = is_via_local (via);
|
|
if (sts == STS_TRUE) found_own_via+=1;
|
|
pos++;
|
|
}
|
|
|
|
/*
|
|
* what happens if a message is coming back to me legally?
|
|
* UA1 -->--\ /-->--\
|
|
* siproxd Registrar
|
|
* UA2 --<--/ \--<--/
|
|
*
|
|
* This may also lead to a VIA loop - so I probably must take the branch
|
|
* parameter into count (or a unique part of it) OR just allow at least 2
|
|
* vias of my own.
|
|
*/
|
|
return (found_own_via>2)? STS_TRUE : STS_FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* check if a given osip_via_t is local. I.e. its address is owned
|
|
* by my inbound or outbound interface
|
|
*
|
|
* RETURNS
|
|
* STS_TRUE if the given VIA is one of my interfaces
|
|
* STS_FALSE otherwise
|
|
*/
|
|
int is_via_local (osip_via_t *via) {
|
|
int sts, found;
|
|
struct in_addr addr_via, addr_myself;
|
|
char *my_interfaces[]=
|
|
{ configuration.inbound_if, configuration.outbound_if, (char*)-1 };
|
|
int port;
|
|
int i;
|
|
char *ptr;
|
|
|
|
if (via==NULL) {
|
|
ERROR("called is_via_local with NULL via");
|
|
return STS_FALSE;
|
|
}
|
|
|
|
DEBUGC(DBCLASS_BABBLE,"via name %s",via->host);
|
|
if (utils_inet_aton(via->host,&addr_via) == 0) {
|
|
/* need name resolution */
|
|
sts=get_ip_by_host(via->host, &addr_via);
|
|
if (sts == STS_FAILURE) {
|
|
DEBUGC(DBCLASS_DNS, "is_via_local: cannot resolve VIA [%s]",
|
|
via->host);
|
|
return STS_FAILURE;
|
|
}
|
|
}
|
|
|
|
found=0;
|
|
for (i=0; ; i++) {
|
|
/*
|
|
* try to search by interface name first
|
|
*/
|
|
ptr=my_interfaces[i];
|
|
if (ptr==(char*)-1) break; /* end of list mark */
|
|
|
|
if (ptr) {
|
|
DEBUGC(DBCLASS_BABBLE,"resolving IP of interface %s",ptr);
|
|
sts = get_ip_by_ifname(ptr, &addr_myself);
|
|
}
|
|
|
|
/* check the extracted VIA against my own host addresses */
|
|
if (via->port) port=atoi(via->port);
|
|
else port=SIP_PORT;
|
|
|
|
if ( (memcmp(&addr_myself, &addr_via, sizeof(addr_myself))==0) &&
|
|
(port == configuration.sip_listen_port) ) {
|
|
DEBUG("got address match [%s]", utils_inet_ntoa(addr_via));
|
|
found=1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (found)? STS_TRUE : STS_FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* compares two URLs
|
|
* (by now, only scheme, hostname and username are compared)
|
|
*
|
|
* RETURNS
|
|
* STS_SUCCESS if equal
|
|
* STS_FAILURE if non equal or error
|
|
*/
|
|
int compare_url(osip_uri_t *url1, osip_uri_t *url2) {
|
|
int sts1, sts2;
|
|
struct in_addr addr1, addr2;
|
|
|
|
/* sanity checks */
|
|
if ((url1 == NULL) || (url2 == NULL)) {
|
|
ERROR("compare_url: NULL ptr: url1=0x%p, url2=0x%p",url1, url2);
|
|
return STS_FAILURE;
|
|
}
|
|
|
|
/* sanity checks: host part is a MUST */
|
|
if ((url1->host == NULL) || (url2->host == NULL)) {
|
|
ERROR("compare_url: NULL ptr: url1->host=0x%p, url2->host=0x%p",
|
|
url1->host, url2->host);
|
|
return STS_FAILURE;
|
|
}
|
|
|
|
DEBUGC(DBCLASS_PROXY, "comparing urls: %s:%s@%s -> %s:%s@%s",
|
|
(url1->scheme) ? url1->scheme : "(null)",
|
|
(url1->username) ? url1->username : "(null)",
|
|
(url1->host) ? url1->host : "(null)",
|
|
(url2->scheme) ? url2->scheme : "(null)",
|
|
(url2->username) ? url2->username : "(null)",
|
|
(url2->host) ? url2->host : "(null)");
|
|
|
|
/* compare SCHEME (if present) case INsensitive */
|
|
if (url1->scheme && url2->scheme) {
|
|
if (osip_strcasecmp(url1->scheme, url2->scheme) != 0) {
|
|
DEBUGC(DBCLASS_PROXY, "compare_url: scheme mismatch");
|
|
return STS_FAILURE;
|
|
}
|
|
} else {
|
|
WARN("compare_url: NULL scheme - ignoring");
|
|
}
|
|
|
|
/* compare username (if present) case sensitive */
|
|
if (url1->username && url2->username) {
|
|
if (strcmp(url1->username, url2->username) != 0) {
|
|
DEBUGC(DBCLASS_PROXY, "compare_url: username mismatch");
|
|
return STS_FAILURE;
|
|
}
|
|
} else {
|
|
WARN("compare_url: NULL username - ignoring");
|
|
}
|
|
|
|
|
|
/*
|
|
* now, try to resolve the host. If resolveable, compare
|
|
* IP addresses - if not resolveable, compare the host names
|
|
* itselfes
|
|
*/
|
|
|
|
/* get the IP addresses from the (possible) hostnames */
|
|
sts1=get_ip_by_host(url1->host, &addr1);
|
|
if (sts1 == STS_FAILURE) {
|
|
DEBUGC(DBCLASS_PROXY, "compare_url: cannot resolve host [%s]",
|
|
url1->host);
|
|
}
|
|
|
|
sts2=get_ip_by_host(url2->host, &addr2);
|
|
if (sts2 == STS_FAILURE) {
|
|
DEBUGC(DBCLASS_PROXY, "compare_url: cannot resolve host [%s]",
|
|
url2->host);
|
|
}
|
|
|
|
if ((sts1 == STS_SUCCESS) && (sts2 == STS_SUCCESS)) {
|
|
/* compare IP addresses */
|
|
if (memcmp(&addr1, &addr2, sizeof(addr1))!=0) {
|
|
DEBUGC(DBCLASS_PROXY, "compare_url: IP mismatch");
|
|
return STS_FAILURE;
|
|
}
|
|
} else {
|
|
/* compare hostname strings case INsensitive */
|
|
if (osip_strcasecmp(url1->host, url2->host) != 0) {
|
|
DEBUGC(DBCLASS_PROXY, "compare_url: host name mismatch");
|
|
return STS_FAILURE;
|
|
}
|
|
}
|
|
|
|
/* the two URLs did pass all tests successfully - MATCH */
|
|
return STS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* compares two Call IDs
|
|
* (by now, only hostname and username are compared)
|
|
*
|
|
* RETURNS
|
|
* STS_SUCCESS if equal
|
|
* STS_FAILURE if non equal or error
|
|
*/
|
|
int compare_callid(osip_call_id_t *cid1, osip_call_id_t *cid2) {
|
|
|
|
if ((cid1==0) || (cid2==0)) {
|
|
ERROR("compare_callid: NULL ptr: cid1=0x%p, cid2=0x%p",cid1, cid2);
|
|
return STS_FAILURE;
|
|
}
|
|
|
|
/*
|
|
* Check number part: if present must be equal,
|
|
* if not present, must be not present in both cids
|
|
*/
|
|
if (cid1->number && cid2->number) {
|
|
/* have both numbers */
|
|
if (strcmp(cid1->number, cid2->number) != 0) goto mismatch;
|
|
} else {
|
|
/* at least one number missing, make sure that both are empty */
|
|
if ( (cid1->number && (cid1->number[0]!='\0')) ||
|
|
(cid2->number && (cid2->number[0]!='\0'))) {
|
|
goto mismatch;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check host part: if present must be equal,
|
|
* if not present, must be not present in both cids
|
|
*/
|
|
if (cid1->host && cid2->host) {
|
|
/* have both hosts */
|
|
if (strcmp(cid1->host, cid2->host) != 0) goto mismatch;
|
|
} else {
|
|
/* at least one host missing, make sure that both are empty */
|
|
if ( (cid1->host && (cid1->host[0]!='\0')) ||
|
|
(cid2->host && (cid2->host[0]!='\0'))) {
|
|
goto mismatch;
|
|
}
|
|
}
|
|
|
|
DEBUGC(DBCLASS_BABBLE, "comparing callid - matched: "
|
|
"%s@%s <-> %s@%s",
|
|
cid1->number, cid1->host, cid2->number, cid2->host);
|
|
return STS_SUCCESS;
|
|
|
|
mismatch:
|
|
DEBUGC(DBCLASS_BABBLE, "comparing callid - mismatch: "
|
|
"%s@%s <-> %s@%s",
|
|
cid1->number, cid1->host, cid2->number, cid2->host);
|
|
return STS_FAILURE;
|
|
}
|
|
|
|
|
|
/*
|
|
* check if a given request is addressed to local. I.e. it is addressed
|
|
* to the porxy itself (IP of my inbound or outbound interface, same port)
|
|
*
|
|
* RETURNS
|
|
* STS_TRUE if the request is addressed local
|
|
* STS_FALSE otherwise
|
|
*/
|
|
int is_sipuri_local (osip_message_t *sip) {
|
|
int sts, found;
|
|
struct in_addr addr_uri, addr_myself;
|
|
char *my_interfaces[]=
|
|
{ configuration.inbound_if, configuration.outbound_if, (char*)-1 };
|
|
int port;
|
|
int i;
|
|
char *ptr;
|
|
|
|
if (sip==NULL) {
|
|
ERROR("called is_sipuri_local with NULL sip");
|
|
return STS_FALSE;
|
|
}
|
|
|
|
if (!sip || !sip->req_uri) {
|
|
ERROR("is_sipuri_local: no request URI present");
|
|
return STS_FALSE;
|
|
}
|
|
|
|
DEBUGC(DBCLASS_DNS,"check for local SIP URI %s:%s",
|
|
sip->req_uri->host? sip->req_uri->host : "*NULL*",
|
|
sip->req_uri->port? sip->req_uri->port : "*NULL*");
|
|
|
|
if (utils_inet_aton(sip->req_uri->host, &addr_uri) == 0) {
|
|
/* need name resolution */
|
|
get_ip_by_host(sip->req_uri->host, &addr_uri);
|
|
}
|
|
|
|
found=0;
|
|
for (i=0; ; i++) {
|
|
/*
|
|
* try to search by interface name first
|
|
*/
|
|
ptr=my_interfaces[i];
|
|
if (ptr==(char*)-1) break; /* end of list mark */
|
|
|
|
if (ptr) {
|
|
DEBUGC(DBCLASS_BABBLE,"resolving IP of interface %s",ptr);
|
|
sts = get_ip_by_ifname(ptr, &addr_myself);
|
|
}
|
|
|
|
/* check the extracted HOST against my own host addresses */
|
|
if (sip->req_uri->port) {
|
|
port=atoi(sip->req_uri->port);
|
|
} else {
|
|
port=SIP_PORT;
|
|
}
|
|
|
|
if ( (memcmp(&addr_myself, &addr_uri, sizeof(addr_myself))==0) &&
|
|
(port == configuration.sip_listen_port) ) {
|
|
DEBUG("address match [%s]", utils_inet_ntoa(addr_uri));
|
|
found=1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
DEBUGC(DBCLASS_DNS, "SIP URI is %slocal", found? "":"not ");
|
|
return (found)? STS_TRUE : STS_FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* check if a given request (outbound -> inbound) shall its
|
|
* request URI get rewritten based upon our UA knowledge
|
|
*
|
|
* RETURNS
|
|
* STS_TRUE if to be rewritten
|
|
* STS_FALSE otherwise
|
|
*/
|
|
int check_rewrite_rq_uri (osip_message_t *sip) {
|
|
int i, j, sts;
|
|
int dflidx;
|
|
osip_header_t *ua_hdr;
|
|
|
|
/* get index of default entry */
|
|
dflidx=(sizeof(RQ_rewrite)/sizeof(RQ_rewrite[0])) - 1;
|
|
|
|
/* check fort existence of method */
|
|
if ((sip==NULL) ||
|
|
(sip->sip_method==NULL)) {
|
|
ERROR("check_rewrite_rq_uri: got NULL method");
|
|
return STS_FALSE;
|
|
}
|
|
|
|
/* extract UA string */
|
|
osip_message_get_user_agent (sip, 0, &ua_hdr);
|
|
if ((ua_hdr==NULL) || (ua_hdr->hvalue==NULL)) {
|
|
DEBUGC(DBCLASS_SIP, "check_rewrite_rq_uri: NULL UA in Header, "
|
|
"using default");
|
|
i=dflidx;
|
|
} else {
|
|
/* loop through the knowledge base */
|
|
for (i=0; RQ_rewrite[i].UAstring; i++) {
|
|
if (strncmp(RQ_rewrite[i].UAstring, ua_hdr->hvalue,
|
|
sizeof(RQ_rewrite[i].UAstring))==0) {
|
|
DEBUGC(DBCLASS_SIP, "got knowledge entry for [%s]",
|
|
ua_hdr->hvalue);
|
|
break;
|
|
}
|
|
} /* for i */
|
|
} /* if ua_hdr */
|
|
|
|
for (j=0; RQ_method[j].name; j++) {
|
|
if (strncmp(RQ_method[j].name,
|
|
sip->sip_method, RQ_method[j].size)==0) {
|
|
if (RQ_rewrite[i].action[j] >= 0) {
|
|
sts = (RQ_rewrite[i].action[j])? STS_TRUE: STS_FALSE;
|
|
} else {
|
|
sts = (RQ_rewrite[dflidx].action[j])? STS_TRUE: STS_FALSE;
|
|
}
|
|
DEBUGC(DBCLASS_SIP, "check_rewrite_rq_uri: [%s:%s, i=%i, j=%i] "
|
|
"got action %s",
|
|
(sip && sip->sip_method) ?
|
|
sip->sip_method : "*NULL*",
|
|
(ua_hdr && ua_hdr->hvalue)? ua_hdr->hvalue:"*NULL*",
|
|
i, j, (sts==STS_TRUE)? "rewrite":"norewrite");
|
|
return sts;
|
|
}
|
|
} /* for j */
|
|
|
|
WARN("check_rewrite_rq_uri: didn't get a hit of the method [%s]",
|
|
sip->sip_method);
|
|
return STS_FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* SIP_GEN_RESPONSE
|
|
*
|
|
* send an proxy generated response back to the client.
|
|
* Only errors are reported from the proxy itself.
|
|
* code = SIP result code to deliver
|
|
*
|
|
* RETURNS
|
|
* STS_SUCCESS on success
|
|
* STS_FAILURE on error
|
|
*/
|
|
int sip_gen_response(osip_message_t *request, int code) {
|
|
osip_message_t *response;
|
|
int sts;
|
|
osip_via_t *via;
|
|
int port;
|
|
char *buffer;
|
|
struct in_addr addr;
|
|
|
|
/* create the response template */
|
|
if ((response=msg_make_template_reply(request, code))==NULL) {
|
|
ERROR("proxy_response: error in msg_make_template_reply");
|
|
return STS_FAILURE;
|
|
}
|
|
|
|
/* we must check if first via has x.x.x.x address. If not, we must resolve it */
|
|
osip_message_get_via (response, 0, &via);
|
|
if (via == NULL)
|
|
{
|
|
ERROR("proxy_response: Cannot send response - no via field");
|
|
return STS_FAILURE;
|
|
}
|
|
|
|
|
|
/* name resolution */
|
|
if (utils_inet_aton(via->host, &addr) == 0)
|
|
{
|
|
/* need name resolution */
|
|
DEBUGC(DBCLASS_DNS,"resolving name:%s",via->host);
|
|
sts = get_ip_by_host(via->host, &addr);
|
|
if (sts == STS_FAILURE) {
|
|
DEBUGC(DBCLASS_PROXY, "sip_gen_response: cannot resolve via [%s]",
|
|
via->host);
|
|
return STS_FAILURE;
|
|
}
|
|
}
|
|
|
|
sts = osip_message_to_str(response, &buffer);
|
|
if (sts != 0) {
|
|
ERROR("proxy_response: msg_2char failed");
|
|
return STS_FAILURE;
|
|
}
|
|
|
|
|
|
if (via->port) {
|
|
port=atoi(via->port);
|
|
} else {
|
|
port=SIP_PORT;
|
|
}
|
|
|
|
/* send to destination */
|
|
sipsock_send(addr, port, buffer, strlen(buffer));
|
|
|
|
/* free the resources */
|
|
osip_message_free(response);
|
|
osip_free(buffer);
|
|
return STS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* SIP_ADD_MYVIA
|
|
*
|
|
* interface == IF_OUTBOUND, IF_INBOUND
|
|
*
|
|
* RETURNS
|
|
* STS_SUCCESS on success
|
|
* STS_FAILURE on error
|
|
*/
|
|
int sip_add_myvia (osip_message_t *request, int interface) {
|
|
struct in_addr addr;
|
|
char tmp[URL_STRING_SIZE];
|
|
osip_via_t *via;
|
|
int sts;
|
|
char branch_id[VIA_BRANCH_SIZE];
|
|
|
|
if (interface == IF_OUTBOUND) {
|
|
sts = get_ip_by_ifname(configuration.outbound_if, &addr);
|
|
if (sts == STS_FAILURE) {
|
|
ERROR("can't find outbound interface %s - configuration error?",
|
|
configuration.outbound_if);
|
|
return STS_FAILURE;
|
|
}
|
|
} else {
|
|
sts = get_ip_by_ifname(configuration.inbound_if, &addr);
|
|
if (sts == STS_FAILURE) {
|
|
ERROR("can't find inbound interface %s - configuration error?",
|
|
configuration.inbound_if);
|
|
return STS_FAILURE;
|
|
}
|
|
}
|
|
|
|
sts = sip_calculate_branch_id(request, branch_id);
|
|
|
|
sprintf(tmp, "SIP/2.0/UDP %s:%i;branch=%s;", utils_inet_ntoa(addr),
|
|
configuration.sip_listen_port, branch_id);
|
|
DEBUGC(DBCLASS_BABBLE,"adding VIA:%s",tmp);
|
|
|
|
sts = osip_via_init(&via);
|
|
if (sts!=0) return STS_FAILURE; /* allocation failed */
|
|
|
|
sts = osip_via_parse(via, tmp);
|
|
if (sts!=0) return STS_FAILURE;
|
|
|
|
osip_list_add(request->vias,via,0);
|
|
|
|
return STS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* SIP_DEL_MYVIA
|
|
*
|
|
* RETURNS
|
|
* STS_SUCCESS on success
|
|
* STS_FAILURE on error
|
|
*/
|
|
int sip_del_myvia (osip_message_t *response) {
|
|
osip_via_t *via;
|
|
int sts;
|
|
|
|
DEBUGC(DBCLASS_PROXY,"deleting topmost VIA");
|
|
via = osip_list_get (response->vias, 0);
|
|
|
|
if ( via == NULL ) {
|
|
ERROR("Got empty VIA list - is your UA configured properly?");
|
|
return STS_FAILURE;
|
|
}
|
|
|
|
if ( is_via_local(via) == STS_FALSE ) {
|
|
ERROR("I'm trying to delete a VIA but it's not mine! host=%s",via->host);
|
|
return STS_FAILURE;
|
|
}
|
|
|
|
sts = osip_list_remove(response->vias, 0);
|
|
osip_via_free (via);
|
|
return STS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* SIP_REWRITE_CONTACT
|
|
*
|
|
* rewrite the Contact header
|
|
*
|
|
* RETURNS
|
|
* STS_SUCCESS on success
|
|
* STS_FAILURE on error
|
|
*/
|
|
int sip_rewrite_contact (osip_message_t *sip_msg, int direction) {
|
|
osip_contact_t *contact;
|
|
int i;
|
|
|
|
if (sip_msg == NULL) return STS_FAILURE;
|
|
|
|
osip_message_get_contact(sip_msg, 0, &contact);
|
|
if (contact == NULL) return STS_FAILURE;
|
|
|
|
for (i=0;i<URLMAP_SIZE;i++){
|
|
if (urlmap[i].active == 0) continue;
|
|
if ((direction == DIR_OUTGOING) &&
|
|
(compare_url(contact->url, urlmap[i].true_url)==STS_SUCCESS)) break;
|
|
if ((direction == DIR_INCOMING) &&
|
|
(compare_url(contact->url, urlmap[i].masq_url)==STS_SUCCESS)) break;
|
|
}
|
|
|
|
/* found a mapping entry */
|
|
if (i<URLMAP_SIZE) {
|
|
char *tmp;
|
|
DEBUGC(DBCLASS_PROXY, "rewrote Contact header %s@%s -> %s@%s",
|
|
(contact->url->username)? contact->url->username : "*NULL*",
|
|
(contact->url->host)? contact->url->host : "*NULL*",
|
|
urlmap[i].masq_url->username, urlmap[i].masq_url->host);
|
|
|
|
/* remove old entry */
|
|
osip_list_remove(sip_msg->contacts,0);
|
|
osip_contact_to_str(contact, &tmp);
|
|
osip_contact_free(contact);
|
|
|
|
/* clone the url from urlmap*/
|
|
osip_contact_init(&contact);
|
|
osip_contact_parse(contact,tmp);
|
|
osip_free(tmp);
|
|
osip_uri_free(contact->url);
|
|
if (direction == DIR_OUTGOING) {
|
|
/* outgoing, use masqueraded url */
|
|
osip_uri_clone(urlmap[i].masq_url, &contact->url);
|
|
} else {
|
|
/* incoming, use true url */
|
|
osip_uri_clone(urlmap[i].true_url, &contact->url);
|
|
}
|
|
|
|
osip_list_add(sip_msg->contacts,contact,-1);
|
|
} else {
|
|
return STS_FAILURE;
|
|
}
|
|
|
|
return STS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* SIP_CALCULATE_BRANCH
|
|
*
|
|
* Calculates a branch parameter according to RFC3261 section 16.11
|
|
*
|
|
* The returned 'id' will be HASHHEXLEN + strlen(magic_cookie)
|
|
* characters (32 + 7) long. The caller must supply at least this
|
|
* amount of space in 'id'.
|
|
*
|
|
* RETURNS
|
|
* STS_SUCCESS on success
|
|
* STS_FAILURE on error
|
|
*/
|
|
int sip_calculate_branch_id (osip_message_t *sip_msg, char *id) {
|
|
/* RFC3261 section 16.11 recommends the following procedure:
|
|
* The stateless proxy MAY use any technique it likes to guarantee
|
|
* uniqueness of its branch IDs across transactions. However, the
|
|
* following procedure is RECOMMENDED. The proxy examines the
|
|
* branch ID in the topmost Via header field of the received
|
|
* request. If it begins with the magic cookie, the first
|
|
* component of the branch ID of the outgoing request is computed
|
|
* as a hash of the received branch ID. Otherwise, the first
|
|
* component of the branch ID is computed as a hash of the topmost
|
|
* Via, the tag in the To header field, the tag in the From header
|
|
* field, the Call-ID header field, the CSeq number (but not
|
|
* method), and the Request-URI from the received request. One of
|
|
* these fields will always vary across two different
|
|
* transactions.
|
|
*
|
|
* The branch value will consist of:
|
|
* - magic cookie "z9hG4bK"
|
|
* - 1st part (unique calculated ID
|
|
* - 2nd part (value for loop detection) <<- not yet used by siproxd
|
|
*/
|
|
static char *magic_cookie="z9hG4bK";
|
|
osip_via_t *via;
|
|
osip_uri_param_t *param=NULL;
|
|
osip_call_id_t *call_id=NULL;
|
|
HASHHEX hashstring;
|
|
|
|
hashstring[0]='\0';
|
|
|
|
/*
|
|
* Examine topmost via and look for a magic cookie.
|
|
* If it is there, I use THIS branch parameter as input for
|
|
* our hash calculation
|
|
*/
|
|
via = osip_list_get (sip_msg->vias, 0);
|
|
if (via == NULL) {
|
|
ERROR("have a SIP message without any via header");
|
|
return STS_FAILURE;
|
|
}
|
|
|
|
param=NULL;
|
|
osip_via_param_get_byname(via, "branch", ¶m);
|
|
if (param && param->gvalue) {
|
|
DEBUGC(DBCLASS_BABBLE, "looking for magic cookie [%s]",param->gvalue);
|
|
if (strncmp(param->gvalue, magic_cookie,
|
|
strlen(magic_cookie))==0) {
|
|
/* calculate MD5 hash */
|
|
MD5_CTX Md5Ctx;
|
|
HASH HA1;
|
|
|
|
MD5Init(&Md5Ctx);
|
|
MD5Update(&Md5Ctx, param->gvalue,
|
|
strlen(param->gvalue));
|
|
MD5Final(HA1, &Md5Ctx);
|
|
CvtHex(HA1, hashstring);
|
|
|
|
DEBUGC(DBCLASS_BABBLE, "existing branch -> branch hash [%s]",
|
|
hashstring);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If I don't have a branch parameter in the existing topmost via,
|
|
* then I need:
|
|
* - the topmost via
|
|
* - the tag in the To header field
|
|
* - the tag in the From header field
|
|
* - the Call-ID header field
|
|
* - the CSeq number (but not method)
|
|
* - the Request-URI from the received request
|
|
*/
|
|
if (hashstring[0] == '\0') {
|
|
/* calculate MD5 hash */
|
|
MD5_CTX Md5Ctx;
|
|
HASH HA1;
|
|
char *tmp;
|
|
|
|
MD5Init(&Md5Ctx);
|
|
|
|
/* topmost via */
|
|
osip_via_to_str(via, &tmp);
|
|
if (tmp) {
|
|
MD5Update(&Md5Ctx, tmp, strlen(tmp));
|
|
osip_free(tmp);
|
|
}
|
|
|
|
/* Tag in To header */
|
|
osip_to_get_tag(sip_msg->to, ¶m);
|
|
if (param && param->gvalue) {
|
|
MD5Update(&Md5Ctx, param->gvalue, strlen(param->gvalue));
|
|
}
|
|
|
|
/* Tag in From header */
|
|
osip_from_get_tag(sip_msg->from, ¶m);
|
|
if (param && param->gvalue) {
|
|
MD5Update(&Md5Ctx, param->gvalue, strlen(param->gvalue));
|
|
}
|
|
|
|
/* Call-ID */
|
|
call_id = osip_message_get_call_id(sip_msg);
|
|
osip_call_id_to_str(call_id, &tmp);
|
|
if (tmp) {
|
|
MD5Update(&Md5Ctx, tmp, strlen(tmp));
|
|
osip_free(tmp);
|
|
}
|
|
|
|
/* CSeq number (but not method) */
|
|
tmp = osip_cseq_get_number(sip_msg->cseq);
|
|
if (tmp) {
|
|
MD5Update(&Md5Ctx, tmp, strlen(tmp));
|
|
}
|
|
|
|
/* Request URI */
|
|
osip_uri_to_str(sip_msg->req_uri, &tmp);
|
|
if (tmp) {
|
|
MD5Update(&Md5Ctx, tmp, strlen(tmp));
|
|
osip_free(tmp);
|
|
}
|
|
|
|
MD5Final(HA1, &Md5Ctx);
|
|
CvtHex(HA1, hashstring);
|
|
|
|
DEBUGC(DBCLASS_BABBLE, "non-existing branch -> branch hash [%s]",
|
|
hashstring);
|
|
}
|
|
|
|
/* include the magic cookie */
|
|
sprintf(id, "%s%s", magic_cookie, hashstring);
|
|
|
|
return STS_SUCCESS;
|
|
}
|