888ca09d |
/*
* $Id$
|
7dd0b342 |
*
* Copyright (C) 2001-2003 Fhg Fokus
*
* This file is part of ser, a free SIP server.
*
* ser 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
*
* For a license to use the ser software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* ser 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
2d4b798e |
*
* History:
* -------
|
e1e6c914 |
* 2003-01-23 support for determination of outbound interface added :
|
2d4b798e |
* get_out_socket (jiri)
|
e1e6c914 |
* 2003-01-24 reply to rport support added, contributed by
* Maxim Sobolev <sobomax@FreeBSD.org> and modified by andrei
|
2d4b798e |
*
|
888ca09d |
*/
|
7dd0b342 |
|
888ca09d |
#include <string.h>
|
3e429f5c |
#include <stdio.h>
#include <stdlib.h>
|
9a3dc64b |
#include <sys/types.h>
#include <sys/socket.h>
|
e60a9728 |
#include <netdb.h>
#include <netinet/in.h>
|
1b1b19d8 |
#include <arpa/inet.h>
|
888ca09d |
#include "forward.h"
|
caf80ae6 |
#include "hash_func.h"
|
e60a9728 |
#include "config.h"
|
3881f12c |
#include "parser/msg_parser.h"
|
888ca09d |
#include "route.h"
#include "dprint.h"
|
e60a9728 |
#include "udp_server.h"
|
1b1b19d8 |
#include "globals.h"
|
831faabf |
#include "data_lump.h"
|
104316b6 |
#include "ut.h"
|
dda9dab1 |
#include "mem/mem.h"
|
c2c6a6bf |
#include "msg_translator.h"
|
031e278e |
#include "sr_module.h"
|
dda9dab1 |
#include "stats.h"
|
4e2fdd79 |
#include "ip_addr.h"
#include "resolve.h"
|
e278821b |
#include "name_alias.h"
|
f2f969dd |
#ifdef USE_TCP
#include "tcp_server.h"
#endif
|
888ca09d |
|
03150098 |
#ifdef DEBUG_DMALLOC
#include <dmalloc.h>
#endif
|
2d4b798e |
/* return a socket_info_pointer to the sending socket; as opposed to
* get_send_socket, which returns process's default socket, get_out_socket
* attempts to determine the outbound interface which will be used;
* it creates a temporary connected socket to determine it; it will
* be very likely noticeably slower, but it can deal better with
* multihomed hosts
*/
struct socket_info* get_out_socket(union sockaddr_union* to, int proto)
{
int temp_sock;
socklen_t len;
int r;
union sockaddr_union from;
if (proto!=PROTO_UDP) {
LOG(L_CRIT, "BUG: get_out_socket can only be called for UDP\n");
return 0;
}
r=-1;
temp_sock=socket(to->s.sa_family, SOCK_DGRAM, 0 );
if (temp_sock==-1) {
LOG(L_ERR, "ERROR: get_out_socket: socket() failed: %s\n",
strerror(errno));
return 0;
}
if (connect(temp_sock, &to->s, sockaddru_len(*to))==-1) {
LOG(L_ERR, "ERROR: get_out_socket: connect failed: %s\n",
strerror(errno));
goto error;
}
len=sockaddru_len(from);
if (getsockname(temp_sock, &from.s, &len)==-1) {
LOG(L_ERR, "ERROR: get_out_socket: getsockname failed: %s\n",
strerror(errno));
goto error;
}
for (r=0; r<sock_no; r++) {
switch(from.s.sa_family) {
case AF_INET:
if (sock_info[r].address.af!=AF_INET)
continue;
if (memcmp(&sock_info[r].address.u,
&from.sin.sin_addr,
sock_info[r].address.len)==0)
goto error; /* it is actually success */
break;
case AF_INET6:
if (sock_info[r].address.af!=AF_INET6)
continue;
if (memcmp(&sock_info[r].address.u,
&from.sin6.sin6_addr, len)==0)
goto error;
continue;
default: LOG(L_ERR, "ERROR: get_out_socket: "
"unknown family: %d\n",
from.s.sa_family);
r=-1;
goto error;
}
}
LOG(L_ERR, "ERROR: get_out_socket: no socket found\n");
r=-1;
error:
close(temp_sock);
DBG("DEBUG: get_out_socket: socket determined: %d\n", r );
return r==-1? 0: &sock_info[r];
}
|
b2e71d5b |
|
1b1b19d8 |
|
36ef0329 |
/* returns a socket_info pointer to the sending socket or 0 on error
|
1f3deacf |
* params: destination socket_union pointer
|
36ef0329 |
*/
|
f2f969dd |
struct socket_info* get_send_socket(union sockaddr_union* to, int proto)
|
36ef0329 |
{
struct socket_info* send_sock;
|
2d4b798e |
if (mhomed && proto==PROTO_UDP) return get_out_socket(to, proto);
|
36ef0329 |
send_sock=0;
/* check if we need to change the socket (different address families -
* eg: ipv4 -> ipv6 or ipv6 -> ipv4) */
|
f2f969dd |
#ifdef USE_TCP
if (proto==PROTO_TCP){
/* on tcp just use the "main address", we don't really now the
|
e1e6c914 |
* sending address (we can find it out, but we'll need also to see
|
f2f969dd |
* if we listen on it, and if yes on which port -> too complicated*/
switch(to->s.sa_family){
case AF_INET: send_sock=sendipv4_tcp;
break;
#ifdef USE_IPV6
case AF_INET6: send_sock=sendipv6_tcp;
break;
#endif
default: LOG(L_ERR, "get_send_socket: BUG: don't know how"
" to forward to af %d\n", to->s.sa_family);
}
}else
#endif
if ((bind_address==0) ||(to->s.sa_family!=bind_address->address.af)){
|
36ef0329 |
switch(to->s.sa_family){
case AF_INET: send_sock=sendipv4;
break;
#ifdef USE_IPV6
case AF_INET6: send_sock=sendipv6;
break;
#endif
default: LOG(L_ERR, "get_send_socket: BUG: don't know how"
" to forward to af %d\n", to->s.sa_family);
}
}else send_sock=bind_address;
return send_sock;
}
|
1baa06b5 |
/* checks if the host:port is one of the address we listen on;
* if port==0, the port number is ignored
* returns 1 if true, 0 if false, -1 on error
|
855c2e68 |
*/
|
1baa06b5 |
int check_self(str* host, unsigned short port)
|
855c2e68 |
{
int r;
for (r=0; r<sock_no; r++){
DBG("check_self - checking if host==us: %d==%d && "
" [%.*s] == [%.*s]\n",
host->len,
sock_info[r].name.len,
host->len, host->s,
sock_info[r].name.len, sock_info[r].name.s
);
|
1baa06b5 |
if ((port)&&(sock_info[r].port_no!=port)) continue;
|
855c2e68 |
if ( (host->len==sock_info[r].name.len) &&
#ifdef USE_IPV6
(strncasecmp(host->s, sock_info[r].name.s,
sock_info[r].name.len)==0) /*slower*/
#else
(memcmp(host->s, sock_info[r].name.s,
sock_info[r].name.len)==0)
#endif
)
break;
/* check if host == ip address */
if ( (!sock_info[r].is_ip) &&
(host->len==sock_info[r].address_str.len) &&
#ifdef USE_IPV6
(strncasecmp(host->s, sock_info[r].address_str.s,
sock_info[r].address_str.len)==0) /*slower*/
#else
(memcmp(host->s, sock_info[r].address_str.s,
sock_info[r].address_str.len)==0)
#endif
)
break;
}
if (r==sock_no){
|
e278821b |
/* try to look into the aliases*/
|
1baa06b5 |
if (grep_aliases(host->s, host->len, port)==0){
|
e278821b |
DBG("check_self: host != me\n");
return 0;
}
|
855c2e68 |
}
return 1;
}
|
e278821b |
|
f2f969dd |
int forward_request( struct sip_msg* msg, struct proxy_l * p, int proto)
|
888ca09d |
{
|
c2c6a6bf |
unsigned int len;
|
ac79e819 |
char* buf;
|
4e2fdd79 |
union sockaddr_union* to;
|
36ef0329 |
struct socket_info* send_sock;
|
caf80ae6 |
char md5[MD5_LEN];
|
f2f969dd |
int id; /* used as branch for tcp! */
|
36ef0329 |
|
ac79e819 |
to=0;
|
36ef0329 |
buf=0;
|
f2f969dd |
id=0;
|
36ef0329 |
|
4e2fdd79 |
to=(union sockaddr_union*)malloc(sizeof(union sockaddr_union));
|
dc862225 |
if (to==0){
|
1400b772 |
ser_error=E_OUT_OF_MEM;
|
68a3fc65 |
LOG(L_ERR, "ERROR: forward_request: out of memory\n");
|
dc862225 |
goto error;
}
|
36ef0329 |
|
e60a9728 |
/* if error try next ip address if possible */
|
4ac74c03 |
if (p->ok==0){
if (p->host.h_addr_list[p->addr_idx+1])
p->addr_idx++;
|
4e2fdd79 |
else p->addr_idx=0;
|
4ac74c03 |
p->ok=1;
|
e60a9728 |
}
|
36ef0329 |
|
4e2fdd79 |
hostent2su(to, &p->host, p->addr_idx,
(p->port)?htons(p->port):htons(SIP_PORT));
|
4ac74c03 |
p->tx++;
|
c2c6a6bf |
p->tx_bytes+=len;
|
36ef0329 |
|
5b253cc6 |
|
f2f969dd |
send_sock=get_send_socket(to, proto);
|
36ef0329 |
if (send_sock==0){
LOG(L_ERR, "forward_req: ERROR: cannot forward to af %d "
"no coresponding listening socket\n", to->s.sa_family);
|
1400b772 |
ser_error=E_NO_SOCKET;
|
caf80ae6 |
goto error1;
|
36ef0329 |
}
|
caf80ae6 |
/* calculate branch for outbound request; if syn_branch is turned off,
calculate is from transaction key, i.e., as an md5 of From/To/CallID/
CSeq exactly the same way as TM does; good for reboot -- than messages
belonging to transaction lost due to reboot will still be forwarded
with the same branch parameter and will be match-able downstream
if it is turned on, we don't care about reboot; we simply put a simple
value in there; better for performance
*/
if (syn_branch ) {
*msg->add_to_branch_s='0';
msg->add_to_branch_len=1;
} else {
if (!char_msg_val( msg, md5 )) { /* parses transaction key */
LOG(L_ERR, "ERROR: forward_request: char_msg_val failed\n");
goto error1;
}
msg->hash_index=hash( msg->callid->body, get_cseq(msg)->number);
|
f2f969dd |
if (!branch_builder( msg->hash_index, 0, md5, id /* 0-th branch */,
|
caf80ae6 |
msg->add_to_branch_s, &msg->add_to_branch_len )) {
LOG(L_ERR, "ERROR: forward_request: branch_builder failed\n");
goto error1;
}
}
|
f2f969dd |
buf = build_req_buf_from_sip_req( msg, &len, send_sock, proto);
|
36ef0329 |
if (!buf){
|
caf80ae6 |
LOG(L_ERR, "ERROR: forward_request: building failed\n");
goto error1;
|
36ef0329 |
}
/* send it! */
|
ab130758 |
DBG("Sending:\n%.*s.\n", (int)len, buf);
|
f2f969dd |
DBG("orig. len=%d, new_len=%d, proto=%d\n", msg->len, len, proto );
|
36ef0329 |
|
f2f969dd |
if (proto==PROTO_UDP){
if (udp_send(send_sock, buf, len, to)==-1){
ser_error=E_SEND;
p->errors++;
p->ok=0;
STATS_TX_DROPS;
goto error1;
}
}
#ifdef USE_TCP
else if (proto==PROTO_TCP){
|
534092ba |
if (tcp_send(buf, len, to, 0)<0){
|
f2f969dd |
ser_error=E_SEND;
p->errors++;
p->ok=0;
STATS_TX_DROPS;
goto error1;
}
}
#endif
else{
LOG(L_CRIT, "BUG: forward_request: unknown proto %d\n", proto);
ser_error=E_SEND;
STATS_TX_DROPS;
goto error1;
|
c2c6a6bf |
}
|
f2f969dd |
|
f571aa35 |
/* sent requests stats */
|
f2f969dd |
STATS_TX_REQUEST( msg->first_line.u.request.method_value );
|
6ee62314 |
|
e22bbdb8 |
pkg_free(buf);
|
dc862225 |
free(to);
|
6ee62314 |
/* received_buf & line_buf will be freed in receive_msg by free_lump_list*/
|
888ca09d |
return 0;
|
caf80ae6 |
error1:
free(to);
|
888ca09d |
error:
|
e22bbdb8 |
if (buf) pkg_free(buf);
|
888ca09d |
return -1;
}
|
caf80ae6 |
|
92b6f40f |
int update_sock_struct_from_via( union sockaddr_union* to,
|
4e2fdd79 |
struct via_body* via )
|
68a3fc65 |
{
|
ac79e819 |
struct hostent* he;
|
5bc22c81 |
str* name;
|
e1e6c914 |
int err;
|
5bc22c81 |
unsigned short port;
|
68a3fc65 |
|
e1e6c914 |
port=0;
if (via->rport && via->rport->value.s){
port=str2s(via->rport->value.s, via->rport->value.len, &err);
if (err){
LOG(L_NOTICE, "ERROR: forward_reply: bad rport value(%.*s)\n",
via->rport->value.len, via->rport->value.s);
port=0;
}
}
|
5bc22c81 |
if (via->received){
DBG("update_sock_struct_from_via: using 'received'\n");
name=&(via->received->value);
/* making sure that we won't do SRV lookup on "received"
* (possible if no DNS_IP_HACK is used)*/
|
e1e6c914 |
if (port==0) port=via->port?via->port:SIP_PORT;
|
5bc22c81 |
}else{
DBG("update_sock_struct_from_via: using via host\n");
name=&(via->host);
|
e1e6c914 |
if (port==0) port=via->port;
|
5bc22c81 |
}
|
4c9f3fde |
/* we do now a malloc/memcpy because gethostbyname loves \0-terminated
strings; -jiri
but only if host is not null terminated
(host.s[len] will always be ok for a via)
BTW: when is via->host.s non null terminated? tm copy? - andrei
Yes -- it happened on generating a 408 by TM; -jiri
|
a6982b85 |
sip_resolvehost now accepts str -janakj
|
4c9f3fde |
*/
|
a6982b85 |
DBG("update_sock_struct_from_via: trying SRV lookup\n");
he=sip_resolvehost(name, &port);
|
4c9f3fde |
if (he==0){
|
a6982b85 |
LOG(L_NOTICE, "ERROR:forward_reply:resolve_host(%.*s) failure\n",
name->len, name->s);
|
4c9f3fde |
return -1;
|
68a3fc65 |
}
|
e1e6c914 |
|
4c9f3fde |
hostent2su(to, he, 0, htons(port));
|
68a3fc65 |
return 1;
}
|
888ca09d |
/* removes first via & sends msg to the second */
|
4ac74c03 |
int forward_reply(struct sip_msg* msg)
|
888ca09d |
{
|
ac79e819 |
char* new_buf;
|
4e2fdd79 |
union sockaddr_union* to;
|
36ef0329 |
struct socket_info* send_sock;
|
8cc98445 |
unsigned int new_len;
|
031e278e |
struct sr_module *mod;
|
f2f969dd |
int proto;
#ifdef USE_TCP
char* s;
int len;
int id;
#endif
|
e22bbdb8 |
|
ac79e819 |
to=0;
new_buf=0;
|
1b1b19d8 |
/*check if first via host = us */
if (check_via){
|
1baa06b5 |
if (check_self(&msg->via1->host,
msg->via1->port?msg->via1->port:SIP_PORT)!=1){
|
f8d46776 |
LOG(L_NOTICE, "ERROR: forward_reply: host in first via!=me :"
|
faa9e46f |
" %.*s:%d\n", msg->via1->host.len, msg->via1->host.s,
|
1baa06b5 |
msg->via1->port);
|
1b1b19d8 |
/* send error msg back? */
goto error;
}
}
|
031e278e |
/* quick hack, slower for mutliple modules*/
for (mod=modules;mod;mod=mod->next){
if ((mod->exports) && (mod->exports->response_f)){
DBG("forward_reply: found module %s, passing reply to it\n",
mod->exports->name);
if (mod->exports->response_f(msg)==0) goto skip;
}
}
|
caf80ae6 |
|
b908cdeb |
/* we have to forward the reply stateless, so we need second via -bogdan*/
|
caf80ae6 |
if (parse_headers( msg, HDR_VIA2, 0 )==-1
|| (msg->via2==0) || (msg->via2->error!=PARSE_OK))
|
b908cdeb |
{
/* no second via => error */
LOG(L_ERR, "ERROR: forward_msg: no 2nd via found in reply\n");
goto error;
}
|
4e2fdd79 |
to=(union sockaddr_union*)malloc(sizeof(union sockaddr_union));
|
031e278e |
if (to==0){
LOG(L_ERR, "ERROR: forward_reply: out of memory\n");
goto error;
}
|
8cc98445 |
|
5843cd1b |
new_buf = build_res_buf_from_sip_res( msg, &new_len);
|
8cc98445 |
if (!new_buf){
LOG(L_ERR, "ERROR: forward_reply: building failed\n");
|
888ca09d |
goto error;
}
|
8cc98445 |
|
de38488e |
proto=msg->via2->proto;
|
68a3fc65 |
if (update_sock_struct_from_via( to, msg->via2 )==-1) goto error;
|
de38488e |
send_sock=get_send_socket(to, proto);
|
36ef0329 |
if (send_sock==0){
LOG(L_ERR, "forward_reply: ERROR: no sending socket found\n");
goto error;
}
|
5b253cc6 |
|
f2f969dd |
if (proto==PROTO_UDP){
if (udp_send(send_sock, new_buf,new_len, to)==-1)
{
STATS_TX_DROPS;
goto error;
}
}
#ifdef USE_TCP
else if (proto==PROTO_TCP){
id=0;
|
e1e6c914 |
/* find id in i param if it exists */
if (msg->via1->i&&msg->via1->i->value.s){
s=msg->via1->i->value.s;
len=msg->via1->i->value.len;
DBG("forward_reply: i=%.*s\n",len, s);
id=reverse_hex2int(s, len);
DBG("forward_reply: id= %x\n", id);
|
f2f969dd |
}
|
534092ba |
if (tcp_send(new_buf, new_len, to, id)<0)
|
f2f969dd |
{
STATS_TX_DROPS;
goto error;
}
}
#endif
else{
LOG(L_CRIT, "BUG: forward_reply: unknown proto %d\n", proto);
|
e60a9728 |
goto error;
|
f2f969dd |
}
|
9598488f |
#ifdef STATS
|
f2f969dd |
STATS_TX_RESPONSE( (msg->first_line.u.reply.statuscode/100) );
|
9598488f |
#endif
|
b908cdeb |
|
ab130758 |
DBG(" reply forwarded to %.*s:%d\n",
msg->via2->host.len, msg->via2->host.s,
(unsigned short) msg->via2->port);
|
b908cdeb |
|
e22bbdb8 |
pkg_free(new_buf);
|
dc862225 |
free(to);
|
031e278e |
skip:
|
888ca09d |
return 0;
error:
|
e22bbdb8 |
if (new_buf) pkg_free(new_buf);
|
dc862225 |
if (to) free(to);
|
888ca09d |
return -1;
}
|