siproxd/src/sip_utils.c
Thomas Ries 52909f3faa - Hack to deal with Asterisks broken Alert-Info headers
(Asterisk does include Alert-Info header without '<>')
2007-04-21 18:56:49 +00:00

1148 lines
34 KiB
C

/* -*- Mode: C; c-basic-offset: 3 -*-
Copyright (C) 2002-2005 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 "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 (sip_ticket_t *ticket, int code) {
osip_message_t *request=ticket->sipmsg;
osip_message_t *response;
int pos;
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_message_init (&response);
if (response == NULL) {
ERROR("msg_make_template_reply: osip_message_init() failed");
return NULL;
}
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)));
osip_to_clone (request->to, &response->to);
osip_from_clone (request->from, &response->from);
/* if 3xx, also include 1st contact header */
if ((code==200) || ((code>=300) && (code<400))) {
osip_contact_t *req_contact = NULL;
osip_contact_t *res_contact = NULL;
osip_message_get_contact(request, 0, &req_contact);
if (req_contact) osip_contact_clone (req_contact, &res_contact);
if (res_contact) osip_list_add(response->contacts,res_contact,0);
}
/* 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 (sip_ticket_t *ticket) {
/*
!!! 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...
-> Might use a fixed unique part of branch parameter to identify
that it is MY via
*/
osip_message_t *my_msg=ticket->sipmsg;
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;
int port;
int i;
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<2; i++) {
/*
* search my in/outbound interfaces
*/
DEBUGC(DBCLASS_BABBLE,"resolving IP of interface %s",
(i==IF_INBOUND)? "inbound":"outbound");
if (get_interface_ip(i, &addr_myself) != STS_SUCCESS) {
continue;
}
/* check the extracted VIA against my own host addresses */
if (via->port) {
port=atoi(via->port);
if ((port<=0) || (port>65535)) port=SIP_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 {
DEBUGC(DBCLASS_PROXY, "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 (strcasecmp(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 proxy 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 (sip_ticket_t *ticket) {
osip_message_t *sip=ticket->sipmsg;
int found;
struct in_addr addr_uri, addr_myself;
int port;
int i, sts;
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 */
sts=get_ip_by_host(sip->req_uri->host, &addr_uri);
if (sts == STS_FAILURE) {
DEBUGC(DBCLASS_PROXY, "sip_gen_response: cannot resolve via [%s]",
sip->req_uri->host);
return STS_FALSE;
}
}
found=0;
for (i=0; i<2; i++) {
/*
* search my in/outbound interfaces
*/
DEBUGC(DBCLASS_BABBLE,"resolving IP of interface %s",
(i==IF_INBOUND)? "inbound":"outbound");
if (get_interface_ip(i, &addr_myself) != STS_SUCCESS) {
continue;
}
/* check the extracted HOST against my own host addresses */
if (sip->req_uri->port) {
port=atoi(sip->req_uri->port);
if ((port<=0) || (port>65535)) port=SIP_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;
}
/*
* 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(sip_ticket_t *ticket, int code) {
osip_message_t *response;
int sts;
osip_via_t *via;
int port;
char *buffer;
size_t buflen;
struct in_addr addr;
/* create the response template */
if ((response=msg_make_template_reply(ticket, code))==NULL) {
ERROR("sip_gen_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("sip_gen_response: Cannot send response - no via field");
osip_message_free(response);
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);
osip_message_free(response);
return STS_FAILURE;
}
}
sts = sip_message_to_str(response, &buffer, &buflen);
if (sts != 0) {
ERROR("sip_gen_response: msg_2char failed");
osip_message_free(response);
return STS_FAILURE;
}
if (via->port) {
port=atoi(via->port);
if ((port<=0) || (port>65535)) port=SIP_PORT;
} else {
port=SIP_PORT;
}
/* send to destination */
sipsock_send(addr, port, ticket->protocol, buffer, buflen);
/* 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 (sip_ticket_t *ticket, int interface) {
struct in_addr addr;
char tmp[URL_STRING_SIZE];
osip_via_t *via;
int sts;
char branch_id[VIA_BRANCH_SIZE];
char *myaddr;
if (get_interface_ip(interface, &addr) != STS_SUCCESS) {
return STS_FAILURE;
}
sts = sip_calculate_branch_id(ticket, branch_id);
myaddr=utils_inet_ntoa(addr);
sprintf(tmp, "SIP/2.0/UDP %s:%i;branch=%s",
myaddr, 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(ticket->sipmsg->vias,via,0);
return STS_SUCCESS;
}
/*
* SIP_DEL_MYVIA
*
* RETURNS
* STS_SUCCESS on success
* STS_FAILURE on error
*/
int sip_del_myvia (sip_ticket_t *ticket) {
osip_via_t *via;
int sts;
DEBUGC(DBCLASS_PROXY,"deleting topmost VIA");
via = osip_list_get (ticket->sipmsg->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(ticket->sipmsg->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 (sip_ticket_t *ticket, int direction) {
osip_message_t *sip_msg=ticket->sipmsg;
osip_contact_t *contact;
int i, j;
int replaced=0;
if (sip_msg == NULL) return STS_FAILURE;
/* at least one contact header present? */
osip_message_get_contact(sip_msg, 0, &contact);
if (contact == NULL) return STS_FAILURE;
/* loop for all existing contact headers in message */
for (j=0; contact != NULL; j++) {
osip_message_get_contact(sip_msg, j, &contact);
if (contact == NULL) break;
if (contact->url == NULL) continue;
/* search for an entry */
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;
if (direction == DIR_OUTGOING) {
DEBUGC(DBCLASS_PROXY, "rewriting 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);
} else {
DEBUGC(DBCLASS_PROXY, "rewriting Contact header %s@%s -> %s@%s",
(contact->url->username)? contact->url->username : "*NULL*",
(contact->url->host)? contact->url->host : "*NULL*",
urlmap[i].true_url->username, urlmap[i].true_url->host);
}
/* remove old entry */
osip_list_remove(sip_msg->contacts,j);
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,j);
replaced=1;
}
}
if (replaced == 0) {
DEBUGC(DBCLASS_PROXY, "no Contact header rewritten");
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 (sip_ticket_t *ticket, 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
*/
osip_message_t *sip_msg=ticket->sipmsg;
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", &param);
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, &param);
if (param && param->gvalue) {
MD5Update(&Md5Ctx, param->gvalue, strlen(param->gvalue));
}
/* Tag in From header */
osip_from_get_tag(sip_msg->from, &param);
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;
}
/*
* SIP_FIND_OUTBOUND_PROXY
*
* performs the lookup for an apropriate outbound proxy
*
* RETURNS
* STS_SUCCESS on successful lookup
* STS_FAILURE if no outbound proxy to be used
*/
int sip_find_outbound_proxy(sip_ticket_t *ticket, struct in_addr *addr,
int *port) {
int i, sts;
char *domain=NULL;
osip_message_t *sipmsg;
sipmsg=ticket->sipmsg;
if (!addr ||!port) {
ERROR("sip_find_outbound_proxy called with NULL addr or port");
return STS_FAILURE;
}
if (sipmsg && sipmsg->from && sipmsg->from->url) {
domain=sipmsg->from->url->host;
}
if (domain == NULL) {
WARN("sip_find_outbound_proxy called with NULL from->url->host");
return STS_FAILURE;
}
/*
* check consistency of configuration:
* outbound_domain_name, outbound_domain_host, outbound_domain_port
* must match up
*/
if ((configuration.outbound_proxy_domain_name.used !=
configuration.outbound_proxy_domain_host.used) ||
(configuration.outbound_proxy_domain_name.used !=
configuration.outbound_proxy_domain_port.used)) {
ERROR("configuration of outbound_domain_ inconsistent, check config");
} else {
/*
* perform the lookup for domain specific proxies
* first match wins
*/
for (i=0; i<configuration.outbound_proxy_domain_name.used; i++) {
if (strcasecmp(configuration.outbound_proxy_domain_name.string[i],
domain)==0) {
sts = get_ip_by_host(configuration.outbound_proxy_domain_host.string[i],
addr);
if (sts == STS_FAILURE) {
ERROR("sip_find_outbound_proxy: cannot resolve "
"outbound proxy host [%s], check config",
configuration.outbound_proxy_domain_host.string[i]);
return STS_FAILURE;
}
*port=atoi(configuration.outbound_proxy_domain_port.string[i]);
if ((*port<=0) || (*port>65535)) *port=SIP_PORT;
return STS_SUCCESS;
} /* strcmp */
} /* for i */
}
/*
* now check for a global outbound proxy
*/
if (configuration.outbound_proxy_host) {
/* I have a global outbound proxy configured */
sts = get_ip_by_host(configuration.outbound_proxy_host, 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;
}
DEBUGC(DBCLASS_PROXY, "proxy_request: have outbound proxy %s:%i",
configuration.outbound_proxy_host, *port);
return STS_SUCCESS;
}
return STS_FAILURE; /* no proxy */
}
/*
* SIP_IS_OUTGOING
*
* Figures out if this is an outgoing or incoming request/response.
* The direction is stored in the ticket->direction property.
*
* RETURNS
* STS_SUCCESS on success
* STS_FAILURE if unable to determine
*/
int sip_find_direction(sip_ticket_t *ticket, int *urlidx) {
int type;
int i, sts;
struct sockaddr_in *from;
osip_message_t *request;
osip_message_t *response;
from=&ticket->from;
request=ticket->sipmsg;
response=ticket->sipmsg;
type = 0;
ticket->direction = 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_SIP, "sip_find_direction: cannot resolve host [%s]",
urlmap[i].true_url->host);
} else {
DEBUGC(DBCLASS_SIP, "sip_find_direction: 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) {
if (MSG_IS_REQUEST(ticket->sipmsg)) {
type=REQTYP_OUTGOING;
} else {
type=RESTYP_OUTGOING;
}
break;
}
}
}
/*
* is the telegram directed to an internally registered host?
* -> it must be an INCOMING request
*/
if (type == 0) {
for (i=0; i<URLMAP_SIZE; i++) {
if (urlmap[i].active == 0) continue;
/* RFC3261:
* 'To' contains a display name (Bob) and a SIP or SIPS URI
* (sip:bob@biloxi.com) towards which the request was originally
* directed. Display names are described in RFC 2822 [3].
*/
/* So this means, that we must check the SIP URI supplied with the
* INVITE method, as this points to the real wanted target.
* Q: does there exist a situation where the SIP URI itself does
* point to "somewhere" but the To: points to the correct UA?
* So for now, we just look at both of them (SIP URI and To: header)
*/
if (MSG_IS_REQUEST(ticket->sipmsg)) {
/* REQUEST */
/* incoming request (SIP URI == 'masq') || ((SIP URI == 'reg') && !REGISTER)*/
if ((compare_url(request->req_uri, urlmap[i].masq_url)==STS_SUCCESS) ||
(!MSG_IS_REGISTER(request) &&
(compare_url(request->req_uri, urlmap[i].reg_url)==STS_SUCCESS))) {
type=REQTYP_INCOMING;
break;
}
/* 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;
}
} else {
/* RESPONSE */
/* 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;
}
} /* is request */
} /* for i */
} /* if type == 0 */
if (MSG_IS_RESPONSE(ticket->sipmsg)) {
/* &&&& Open Issue &&&&
it has been seen with cross-provider calls that the FROM may be 'garbled'
(e.g 1393xxx@proxy01.sipphone.com for calls made sipphone -> FWD)
How can we deal with this? Should I take into consideration the 'Via'
headers? This is the only clue I have, pointing to the *real* UA.
Maybe I should put in a 'siproxd' ftag value to recognize it as a header
inserted by myself
*/
if ((type == 0) && (!osip_list_eol(response->vias, 0))) {
osip_via_t *via;
struct in_addr addr_via, addr_myself;
int port_via, port_ua;
/* get the via address */
via = (osip_via_t *) osip_list_get (response->vias, 0);
DEBUGC(DBCLASS_SIP, "sip_find_direction: check via [%s] for "
"registered UA",via->host);
sts=get_ip_by_host(via->host, &addr_via);
if (sts == STS_FAILURE) {
DEBUGC(DBCLASS_SIP, "sip_find_direction: cannot resolve VIA [%s]",
via->host);
} else {
for (i=0; i<URLMAP_SIZE; i++) {
if (urlmap[i].active == 0) continue;
/* incoming response (1st via in list points to a registered UA) */
sts=get_ip_by_host(urlmap[i].true_url->host, &addr_myself);
if (sts == STS_FAILURE) {
DEBUGC(DBCLASS_SIP, "sip_find_direction: cannot resolve "
"true_url [%s]", via->host);
continue;
}
port_via=0;
if (via->port) port_via=atoi(via->port);
if ((port_via<=0) || (port_via>65535)) port_via=SIP_PORT;
port_ua=0;
if (urlmap[i].true_url->port)
port_ua=atoi(urlmap[i].true_url->port);
if ((port_ua<=0) || (port_ua>65535)) port_ua=SIP_PORT;
DEBUGC(DBCLASS_SIP, "sip_find_direction: checking for registered "
"host [%s:%i] <-> [%s:%i]",
urlmap[i].true_url->host, port_ua,
via->host, port_via);
if ((memcmp(&addr_myself, &addr_via, sizeof(addr_myself))==0) &&
(port_via == port_ua)) {
type=RESTYP_INCOMING;
break;
}
} /* for i */
}
} /* if type == 0 */
} /* is response */
if (type == 0) {
DEBUGC(DBCLASS_SIP, "sip_find_direction: unable to determine "
"direction of SIP packet");
return STS_FAILURE;
}
ticket->direction=type;
if (urlidx) *urlidx=i;
return STS_SUCCESS;
}
/*
* SIP_FIXUP_ALERT_INFO
*
* Asterisk Hack.
* Asterisk seems to include broken Alert-Info headers (without <>)
* that cause parsing to fail (libosip2).
* This function does try to guess if we have such a broken SIP
* message and does simply remove the offending Alert-Info header.
*
* RETURNS
* STS_SUCCESS on success
*/
int sip_fixup_asterisk(char *buff, int *buflen) {
char *alert_info_ptr=NULL;
/*
* Check for Asterisk UA string
* User-Agent: Asterisk PBX
*/
if (strstr(buff, "\r\nUser-Agent: Asterisk PBX\r\n")==NULL)
return STS_SUCCESS;
DEBUGC(DBCLASS_SIP, "sip_fixup_alert_info: SIP message is from Asterisk");
/*
* Look form Alert-Info: header
*/
alert_info_ptr=strstr(buff, "\r\nAlert-Info: ");
if (alert_info_ptr) {
char *eol_ptr=NULL;
DEBUGC(DBCLASS_SIP, "sip_fixup_alert_info: Asterisk message "
"with Alert-Info found");
eol_ptr=strstr((alert_info_ptr+2), "\r\n");
if (eol_ptr) {
int i;
// cut the Alert-Info header from the message
// (actually move the rest of the message up)
DEBUGC(DBCLASS_SIP, "sip_fixup_alert_info: removed malformed "
"Alert-Info header");
for (i=0; i<(*buflen - (eol_ptr - buff)); i++) {
alert_info_ptr[i]=eol_ptr[i];
} /* for */
*buflen=strlen(buff);
} /* if */
}
return STS_SUCCESS;
}