407 lines
13 KiB
C
407 lines
13 KiB
C
/* -*- Mode: C; c-basic-offset: 3 -*-
|
|
Copyright (C) 2002-2008 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$";
|
|
|
|
/* configuration storage */
|
|
extern struct siproxd_config configuration; /* defined in siproxd.c */
|
|
|
|
|
|
/*
|
|
* PROXY_PREPROCESS_ROUTE
|
|
*
|
|
* Route Information Preprocessing
|
|
*
|
|
* RETURNS
|
|
* STS_SUCCESS on success
|
|
*/
|
|
int route_preprocess(sip_ticket_t *ticket){
|
|
osip_message_t *mymsg=ticket->sipmsg;
|
|
int sts;
|
|
osip_uri_t *url;
|
|
int dest_port;
|
|
int last;
|
|
struct in_addr addr1, addr2, addr3;
|
|
osip_route_t *route;
|
|
osip_uri_param_t *param=NULL;
|
|
|
|
if (osip_list_size(&(mymsg->routes))<=0) {
|
|
DEBUGC(DBCLASS_PROXY, "route_preprocess: no Route header present");
|
|
return STS_SUCCESS;
|
|
}
|
|
|
|
if (get_interface_ip(IF_INBOUND, &addr2) != STS_SUCCESS) {
|
|
return STS_FAILURE;
|
|
}
|
|
if (get_interface_ip(IF_OUTBOUND, &addr3)!= STS_SUCCESS) {
|
|
return STS_FAILURE;
|
|
}
|
|
|
|
/*
|
|
* 16.4 Route Information Preprocessing:
|
|
* The proxy MUST inspect the Request-URI of the request. If the
|
|
* Request-URI of the request contains a value this proxy previously
|
|
* placed into a Record-Route header field (see Section 16.6 item 4),
|
|
* the proxy MUST replace the Request-URI in the request with the last
|
|
* value from the Route header field, and remove that value from the
|
|
* Route header field. The proxy MUST then proceed as if it received
|
|
* this modified request.
|
|
*/
|
|
url = osip_message_get_uri(mymsg);
|
|
dest_port= (url->port)?atoi(url->port):SIP_PORT;
|
|
if ((dest_port <=0) || (dest_port >65535)) dest_port=SIP_PORT;
|
|
|
|
if (get_ip_by_host(url->host, &addr1) == STS_SUCCESS) {
|
|
if ((configuration.sip_listen_port == dest_port) &&
|
|
(url->username && (strcmp(url->username,"siproxd")==0)) &&
|
|
((memcmp(&addr1, &addr2, sizeof(addr1)) == 0) ||
|
|
(memcmp(&addr1, &addr3, sizeof(addr1)) == 0))) {
|
|
/* Request URI points does to myself */
|
|
DEBUGC(DBCLASS_PROXY, "request URI [%s@%s:%i] points to myself",
|
|
(url->username)?url->username:"*NULL*",
|
|
(url->host)?url->host:"*NULL*",
|
|
(url->port)?atoi(url->port):SIP_PORT);
|
|
/* get last route in list */
|
|
last=osip_list_size(&(mymsg->routes))-1;
|
|
route = (osip_route_t *) osip_list_get(&(mymsg->routes), last);
|
|
DEBUGC(DBCLASS_PROXY, "moving last Route [%s@%s:%i] to URI",
|
|
(route->url->username)?route->url->username:"*NULL*",
|
|
(route->url->host)?route->url->host:"*NULL*",
|
|
(route->url->port)?atoi(route->url->port):SIP_PORT);
|
|
|
|
/* issue warning if the Route I'm going to fetch is NOT
|
|
a strict router (lr parameter not present) - something is fishy */
|
|
if (osip_uri_uparam_get_byname(route->url, "lr", ¶m) == 0) {
|
|
WARN("Fixup Strict Router: Route entry [%s@%s:%i] is not "
|
|
"a strict Router!",
|
|
(route->url->username)?route->url->username:"*NULL*",
|
|
(route->url->host)?route->url->host:"*NULL*",
|
|
(route->url->port)?atoi(route->url->port):SIP_PORT);
|
|
}
|
|
|
|
/* rewrite request URI */
|
|
osip_uri_free(url);
|
|
osip_uri_clone(route->url, &(mymsg->req_uri));
|
|
|
|
/* remove from list */
|
|
osip_list_remove(&(mymsg->routes), last);
|
|
osip_route_free(route);
|
|
}
|
|
} else {
|
|
WARN("cannot resolve host in Request URI [%s]", url->host);
|
|
}
|
|
|
|
|
|
/*
|
|
* 16.4 Route Information Preprocessing:
|
|
* If the first value in the Route header field indicates this proxy,
|
|
* the proxy MUST remove that value from the request.
|
|
*/
|
|
DEBUGC(DBCLASS_PROXY, "route_preprocess: checking topmost "
|
|
"Route header");
|
|
route = (osip_route_t *) osip_list_get(&(mymsg->routes), 0);
|
|
if ((route != NULL) && (route->url != NULL) &&
|
|
(route->url->host != NULL)) {
|
|
sts = get_ip_by_host(route->url->host, &addr1);
|
|
|
|
/* my own route header? */
|
|
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(&(mymsg->routes), 0);
|
|
osip_route_free(route);
|
|
DEBUGC(DBCLASS_PROXY, "removed Route header pointing to myself");
|
|
}
|
|
}
|
|
|
|
return STS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* PROXY_POSTPROCESS_ROUTE
|
|
*
|
|
* Route Information Postprocessing
|
|
*
|
|
* RETURNS
|
|
* STS_SUCCESS on success
|
|
*/
|
|
int route_postprocess(sip_ticket_t *ticket){
|
|
osip_message_t *mymsg=ticket->sipmsg;
|
|
osip_uri_t *url;
|
|
osip_route_t *route=NULL;
|
|
osip_uri_param_t *param=NULL;
|
|
|
|
/*
|
|
* RFC 3261, Section 16.6 step 6
|
|
* Proxy Behavior - Request Forwarding - Postprocess routing information
|
|
*
|
|
* If the copy contains a Route header field, the proxy MUST
|
|
* inspect the URI in its first value. If that URI does not
|
|
* contain an lr parameter, the proxy MUST modify the copy as
|
|
* follows:
|
|
*
|
|
* - The proxy MUST place the Request-URI into the Route header
|
|
* field as the last value.
|
|
*
|
|
* - The proxy MUST then place the first Route header field value
|
|
* into the Request-URI and remove that value from the Route
|
|
* header field.
|
|
*/
|
|
|
|
if (!osip_list_eol(&(mymsg->routes), 0)) {
|
|
|
|
route = (osip_route_t *) osip_list_get(&(mymsg->routes), 0);
|
|
if (route->url) {
|
|
/* check for non existing lr parameter */
|
|
if (osip_uri_uparam_get_byname(route->url, "lr", ¶m) != 0) {
|
|
osip_route_t *new_route=NULL;
|
|
url=osip_message_get_uri(mymsg);
|
|
|
|
/* push Request URI into Route header list at the last position */
|
|
osip_route_init(&new_route);
|
|
osip_uri_clone(url, &new_route->url);
|
|
osip_list_add(&(mymsg->routes), new_route, -1);
|
|
|
|
/* rewrite request URI to now topmost Route header */
|
|
DEBUGC(DBCLASS_PROXY, "Route header w/o 'lr': rewriting request "
|
|
"URI from %s to %s", url->host, route->url->host);
|
|
osip_uri_free(url);
|
|
url=NULL;
|
|
osip_uri_clone(route->url, &url);
|
|
/* remove first Route header from list & free */
|
|
osip_list_remove(&(mymsg->routes), 0);
|
|
osip_route_free(route);
|
|
route = NULL;
|
|
}
|
|
}
|
|
}
|
|
return STS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* PROXY_ADD_RECORDROUTE
|
|
*
|
|
* Add a Record-route header
|
|
*
|
|
* RETURNS
|
|
* STS_SUCCESS on success
|
|
*/
|
|
int route_add_recordroute(sip_ticket_t *ticket){
|
|
osip_message_t *mymsg=ticket->sipmsg;
|
|
int sts;
|
|
int position;
|
|
struct in_addr addr;
|
|
osip_record_route_t *r_route;
|
|
osip_uri_t *uri_of_proxy;
|
|
|
|
/*
|
|
* RFC 3261, Section 16.6 step 4
|
|
* Proxy Behavior - Request Forwarding - Add a Record-route header
|
|
*/
|
|
|
|
/*
|
|
* get the IP address of the interface where I'm going to
|
|
* send out this request
|
|
*/
|
|
switch (ticket->direction) {
|
|
case REQTYP_INCOMING:
|
|
case RESTYP_INCOMING:
|
|
if (get_interface_ip(IF_INBOUND, &addr) != STS_SUCCESS) {
|
|
return STS_FAILURE;
|
|
}
|
|
break;
|
|
case REQTYP_OUTGOING:
|
|
case RESTYP_OUTGOING:
|
|
if (get_interface_ip(IF_OUTBOUND, &addr) != STS_SUCCESS) {
|
|
return STS_FAILURE;
|
|
}
|
|
break;
|
|
default:
|
|
ERROR("Oops, never should end up here (direction=%i)", ticket->direction);
|
|
return STS_FAILURE;
|
|
}
|
|
|
|
sts = osip_record_route_init(&r_route);
|
|
if (sts == 0) {
|
|
sts = osip_uri_init(&uri_of_proxy);
|
|
if (sts == 0) {
|
|
char tmp[8];
|
|
|
|
/* host name / IP */
|
|
osip_uri_set_host(uri_of_proxy, osip_strdup(utils_inet_ntoa(addr)));
|
|
osip_uri_set_username(uri_of_proxy, osip_strdup("siproxd"));
|
|
|
|
/* port number */
|
|
sprintf(tmp, "%i", configuration.sip_listen_port);
|
|
osip_uri_set_port(uri_of_proxy, osip_strdup(tmp));
|
|
|
|
/* 'lr' parameter */
|
|
osip_uri_uparam_add(uri_of_proxy, osip_strdup("lr"),NULL);
|
|
|
|
osip_record_route_set_url(r_route, uri_of_proxy);
|
|
|
|
position=0;
|
|
/* if it is a response, add in to the end of the list
|
|
* (reverse order as in request!) */
|
|
if ((ticket->direction==RESTYP_INCOMING) ||
|
|
(ticket->direction==RESTYP_OUTGOING)) {
|
|
position = -1;
|
|
}
|
|
|
|
/* insert into record-route list*/
|
|
osip_list_add (&(mymsg->record_routes), r_route, position);
|
|
|
|
} else {
|
|
osip_record_route_free(r_route);
|
|
} /* if url_init */
|
|
} /* if record route init */
|
|
|
|
|
|
return STS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* PROXY_PURGE_RECORDROUTE
|
|
*
|
|
* Purge Record-Route headers pointing to myself.
|
|
*
|
|
* RETURNS
|
|
* STS_SUCCESS on success
|
|
*/
|
|
int route_purge_recordroute(sip_ticket_t *ticket){
|
|
osip_message_t *mymsg=ticket->sipmsg;
|
|
osip_record_route_t *r_route=NULL;
|
|
int last, i, sts;
|
|
struct in_addr addr1, addr2, addr3;
|
|
|
|
last=osip_list_size(&(mymsg->record_routes))-1;
|
|
/* I *MUST NOT* purge any alien (non-mine) Record-Route headers,
|
|
* only the ones I've put in myself! */
|
|
if (last >= 0) {
|
|
for (i=last; i>=0; i--) {
|
|
r_route = (osip_record_route_t *)
|
|
osip_list_get(&(mymsg->record_routes), i);
|
|
|
|
/* skip empty entries */
|
|
if (r_route == NULL) continue;
|
|
if (r_route->url == NULL) continue;
|
|
if (r_route->url->host == NULL) continue;
|
|
|
|
/* resolve IP addresses (of RR header, inbound & outbound IF) */
|
|
sts = get_ip_by_host(r_route->url->host, &addr1);
|
|
if (get_interface_ip(IF_INBOUND, &addr2) != STS_SUCCESS) {
|
|
return STS_FAILURE;
|
|
}
|
|
if (get_interface_ip(IF_OUTBOUND, &addr3) != STS_SUCCESS) {
|
|
return STS_FAILURE;
|
|
}
|
|
|
|
/* check if my own route header? */
|
|
if ((sts == STS_SUCCESS) &&
|
|
((memcmp(&addr1, &addr2, sizeof(addr1)) == 0) ||
|
|
(memcmp(&addr1, &addr3, sizeof(addr1)) == 0)) &&
|
|
(r_route->url->port ?
|
|
configuration.sip_listen_port == atoi(r_route->url->port):
|
|
configuration.sip_listen_port == SIP_PORT)) {
|
|
|
|
osip_list_remove(&(mymsg->record_routes), i);
|
|
osip_record_route_free(r_route);
|
|
DEBUGC(DBCLASS_PROXY, "removed Record-Route header pointing "
|
|
"to myself");
|
|
}
|
|
} // for
|
|
} // if
|
|
|
|
return STS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* PROXY_DETERMINE_NEXT_HOP
|
|
*
|
|
* check if a route-header exists and give back the next hop
|
|
*
|
|
* RETURNS
|
|
* STS_SUCCESS on success
|
|
*/
|
|
int route_determine_nexthop(sip_ticket_t *ticket,
|
|
struct in_addr *dest, int *port){
|
|
int sts;
|
|
osip_message_t *mymsg=ticket->sipmsg;
|
|
osip_route_t *route=NULL;
|
|
|
|
/*
|
|
* Check for existing route header. If so, the topmost will be
|
|
* the next hop.
|
|
*
|
|
* If this route header does NOT have a lr parameter set, rewrite
|
|
* the SIP URI to point to the destination of the route (NOT IMPLEMENTED)
|
|
*/
|
|
if (!osip_list_eol(&(mymsg->routes), 0)) {
|
|
|
|
/* get the destination from the Route Header */
|
|
route = (osip_route_t *) osip_list_get(&(mymsg->routes), 0);
|
|
if (route==NULL || route->url==NULL || route->url->host==NULL) {
|
|
DEBUGC(DBCLASS_PROXY, "route_determine_nexthop: got broken Route "
|
|
"header - discarding packet");
|
|
return STS_FAILURE;
|
|
}
|
|
|
|
sts = get_ip_by_host(route->url->host, dest);
|
|
if (sts == STS_FAILURE) {
|
|
DEBUGC(DBCLASS_PROXY, "route_determine_nexthop: cannot resolve "
|
|
"Route URI [%s]", route->url->host);
|
|
return STS_FAILURE;
|
|
}
|
|
|
|
if (route->url->port) {
|
|
*port=atoi(route->url->port);
|
|
if ((*port<=0) || (*port>65535)) *port=SIP_PORT;
|
|
} else {
|
|
*port=SIP_PORT;
|
|
}
|
|
}
|
|
|
|
return STS_SUCCESS;
|
|
}
|